일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- kaggle
- 정치인
- 강화학습 기초
- React
- Instagrame clone
- coding
- App
- redux
- 사이드프로젝트
- expo
- JavaScript
- 머신러닝
- 카트폴
- 딥러닝
- 크롤링
- 데이터분석
- 앱개발
- selenium
- 조코딩
- TeachagleMachine
- Ros
- 리액트네이티브
- python
- FirebaseV9
- 강화학습
- 클론코딩
- ReactNative
- clone coding
- 전국국밥
- pandas
- Today
- Total
qcoding
[ReactNative Instagram clone #3_로그인 및 Auth , Redux user 상태관리 본문
* 이번 글에서는 Firebase에서 제공하는 email 회원가입 기능을 통해 회원가입 / 로그인을 진행하고, user의 정보가 존재하는 것과 하지 않는 것에 따라서 navigation이 어떻게 분류되며, Redux를 통해 user정보와 user의 post를 받아오는 것을 정리하려고 한다.
* 해당 포스트에서 사용하는 라이브러리를 사용하기 위하여 아래와 같이 설치를 진행한다.
// navigation을 사용하기 위한 라이브러리
npm install @react-navigation/native
expo install react-native-screens react-native-safe-area-context
npm install @react-navigation/stack
expo install react-native-gesture-handler
npm install @react-navigation/bottom-tabs
npm install @react-navigation/material-bottom-tabs react-native-paper react-native-vector-icons
// firebase를 사용하기 위한 라이브러리
expo install firebase // https://docs.expo.dev/guides/using-firebase/
expo install @react-native-async-storage/async-storage
// redux를 사용하기 위한 라이브러리
npm install --save redux react-redux
npm i redux-thunk
1) 로그인 및 회원가입 , 로그인 여부에 따른 Navigation 분류
Register.js
--> Firebase V9 문법이 변경됨에 따라 moule 형태로 import 해 와서 사용하는 것으로 변경되었다. 기본적으로 getAuth() 함수를 사용하여 유저 auth 정보를 받아오게 되는데 createUserWithEmailAndPassword() 함수를 사용하여 email과 password 정보를 받아서 FireStore에 저장하게 된다.
--> Firestore에 저장되는 형태는 users colleciion --> doc.id : userUid 의 형태로 저장된다. 이 값이 저장되면 로그인할 때 해당 collection에서 user 정보를 받아올 수가 있다.
import { View,TextInput,Button} from 'react-native';
import React,{useState}from 'react';
import {
getAuth,
createUserWithEmailAndPassword,
} from "firebase/auth";
import { getFirestore, setDoc, doc } from 'firebase/firestore';
const Register = () => {
const [userInfo,setUserInfo]=useState({
email:"",
password:"",
name:""
})
;
const SignUp=async()=>{
console.log("회원가입")
const {email,password,name}=userInfo;
// Firebase V9 문법이 많이 바뀜
const auth =await getAuth();
const db = await getFirestore()
// 회원가입을 진행한 후 collection에 추가함.
await createUserWithEmailAndPassword(auth,email,password).then( async (result)=>{
const userUID=result.user.uid
// console.log("result user : ",userUID)
const docRef = await setDoc(doc(db, "users",userUID), {
uid:userUID,
name,
email
});
}).catch((error)=>{
console.log(error)
});
}
return (
<View style={{flex:1,justifyContent:"center"}}>
<TextInput
placeholder='name'
// 아래 처럼 prevState를 가져와서 처리함.
onChangeText={(txt)=>{setUserInfo({...userInfo,name:txt})}}
/>
<TextInput
placeholder='email'
onChangeText={(txt)=>{setUserInfo({...userInfo,email:txt})}}
/>
<TextInput
placeholder='password'
secureTextEntry={true}
onChangeText={(txt)=>{setUserInfo({...userInfo,password:txt})}}
/>
<Button
title="Signup"
onPress={()=>{SignUp()}}
></Button>
</View>
)
}
export default Register
import { View,TextInput,Button} from 'react-native';
import React,{useState}from 'react';
import {
getAuth,
signInWithEmailAndPassword,
} from "firebase/auth";
export default function Login() {
const [userInfo,setUserInfo]=useState({
email:"",
password:"",
})
const auth = getAuth();
const SignIN=async()=>{
console.log("로그인")
const {email,password}=userInfo;
await signInWithEmailAndPassword(auth,email,password).then((result)=>{
console.log(result)
}).catch((error)=>{
console.log(error)
});
}
return (
<View style={{flex:1,justifyContent:"center"}}>
<TextInput
placeholder='email'
onChangeText={(txt)=>{setUserInfo({...userInfo,email:txt})}}
/>
<TextInput
placeholder='password'
secureTextEntry={true}
onChangeText={(txt)=>{setUserInfo({...userInfo,password:txt})}}
/>
<Button
title="SignIn"
onPress={()=>{SignIN()}}
></Button>
</View>
)
}
App.js
--> 위에서 언급한 것처럼 onAuthStateChanged()가 유저의 로그인을 감지하게 되고 user 가 있을 경우 loggedIn 상태를 true로 하여 Main.js가 있는 Stack으로 보내주게 된다.
import React,{useState,useEffect}from 'react';
import { StyleSheet, Text, View,Button,LogBox,StatusBar } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
export default function App() {
const [loaded,setLoaded]=useState(true)
const [loggdIn,setloggdIn]=useState(false)
const auth = getAuth();
const chkSigned = async ()=>{
await onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
setloggdIn(true)
setLoaded(false)
// ...
} else {
console.log("로그인 안됨")
setloggdIn(false)
setLoaded(false)
}
})
};
useEffect(()=>{
chkSigned();
},[])
if (loaded){
return(
<>
<StatusBar></StatusBar>
<View style={{flex:1,justifyContent:"center"}}>
<Text>Loading...</Text>
</View>
</>
)
}
// LoggIn이 되면 Main으로 들어가는데, 여기서는 tab navigator를 써서 이동가능하게함.
// 아래 페이지를 두고 안에서 이동가능하게 함.
// LoggIn이 안되면 Lading page로 들어가서 Register 또는 Login 페이지로 이동가능함.
return (loggdIn?
<>
<StatusBar></StatusBar>
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator initialRouteName="Main">
<Stack.Screen name="Main" component={Main} options={{headerShown:false}}></Stack.Screen>
<Stack.Screen name="Add" component={Add}></Stack.Screen>
<Stack.Screen name="Save" component={Save}></Stack.Screen>
<Stack.Screen name="Comment" component={Comment}></Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
</Provider>
</>:<>
<StatusBar></StatusBar>
<NavigationContainer>
<Stack.Navigator initialRouteName="Landing">
<Stack.Screen name="Lading" component={Landing} options={{headerShown:false}}></Stack.Screen>
<Stack.Screen name="Register" component={Register} options={{headerShown:false}}></Stack.Screen>
<Stack.Screen name="Login" component={Login} options={{headerShown:false}}></Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
</>
);
}
2) 로그인 시 redux에서 user 및 user Posts 받아오기
2-1) redux 파일 구조
-> redux 폴더안에는 위와 같이 Action creator / Action / Reducer로 분리 되어있다. 해당 프로젝트에서는 reducer를 user / users 2개를 사용하고, index.js에서 통합시켜 하나의 store를 만들어서 사용한다. 각 파일 내용을 통해서 어떤식으로 구조화 되어 있는 지 확인해 보자.
2-2) redux 코드
App.js
-> App.js에서는 store를 실제 컴포넌트들로 전달하는 역활을 한다. reducer를 받아오고 미들웨어로 비동기처리를 위해 thunk를 사용하여 store를 생성한 뒤 Provider 태그를 통해 전달한다.
// redux
import { Provider } from 'react-redux'
import { createStore,applyMiddleware } from 'redux';
import rootReducer from './redux/reducers'
import thunk from 'redux-thunk'
import Save from './components/main/Save';
// store 생성
const store=createStore(rootReducer,applyMiddleware(thunk))
// <Provider> 태그로 store를 전달함
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator initialRouteName="Main">
<Stack.Screen name="Main" component={Main} options={{headerShown:false}}></Stack.Screen>
<Stack.Screen name="Add" component={Add}></Stack.Screen>
<Stack.Screen name="Save" component={Save}></Stack.Screen>
<Stack.Screen name="Comment" component={Comment}></Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
</Provider>
action/index.js ( = Action Creator)
-> user 정보를 가져오기 위한 action 생성함수를 정의한다. Firebase V9 문법을 사용해서 users collection 내에 접속한 사용자의 uid로 정보를 조회해서 가져온다. 아래 코드에서 action 객체를 의미하는 것이
{type: USER_STATE_CHANGE,currentUser:docSnap.data()} 이며 USER_STATE_CHANGE의 action은 constant/index.js에 정의 되어 있고, 실제 dispatch를 통해 reducer로 해당 action이 전달되어 처리된다.
import { getFirestore, collection, getDocs, getDoc, doc, query, orderBy, onSnapshot } from 'firebase/firestore';
import { getAuth } from "firebase/auth";
import { USER_STATE_CHANGE} from '../constant';
// paramsUid는 다른 사람의 id를 클릭해서 profile로 연결할 때 생기는 값
export const fetchUsers=(paramsUid)=>{
return(
async(dispatch)=>{
// user정보
const auth = await getAuth();
const {uid} = auth.currentUser;
// console.log("userID는",uid)
// FireStore DB
const db = await getFirestore();
// data 가져오기(db,collection이름,docID)
const docRef = doc(db, "users", paramsUid?paramsUid:uid);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
// console.log("user정보가 있음")
// dispatch ( action생성함수 ) 이 들어간다.
// action생성함수는 actions/index.js에 정의되어 있고
// 실제 action에 대한 수행은 reducers/user
dispatch({type: USER_STATE_CHANGE,currentUser:docSnap.data()})
} else {
// doc.data() will be undefined in this case
console.log("user정보가 존재하지 않음")
}
}
)
}
constant/index.js ( = Action )
-> Action에 대한 정의가 되어 있으며 해당 정보는 reducer에서 switch 문에 case별로 분류하기 위해 전달된다.
export const USER_STATE_CHANGE='USER_STATE_CHANGE'
reducers/index.js ( = rootReducer)
-> reducer가 분리 되었을 경우 각각을 합쳐주는 rootReduer 형태가 존재한다. 합칠 때는 combineReducers() 함수를 사용하여 합쳐준다.
import { combineReducers } from "redux";
import {user} from './user'
const rootReducer=combineReducers({
userState:user,
})
export default rootReducer
reducers/user.js ( = Reducer)
-> 실제 action이 동작하는 부분으로 state 관리가 실제 일어나는 부분이다. 여기서는 로그인한 user의 정보를 Firebase에서 가져와서 currentUser의 state에 담아 두고 사용할 수 있게한다.
import {USER_STATE_CHANGE,USER_POSTS_STATE_CHANGE,USER_FOLLOWING_STATE_CHANGE,CLEAR_DATA} from "../constant"
const initialState={
currentUser:null,
}
export const user =(state=initialState,action)=>{
switch(action.type){
case USER_STATE_CHANGE:
return {
...state,
currentUser: action.currentUser
}
default:
return state;
}
}
Main.js
--> redux에 있는 정보를 실제 사용할 때 hook을 사용하여 가져온다. useEffect를 통해 렌더링 될때 바로 dispatch를 통해 action creator를 호출하여 action을 생성하고 생성된 action을 reducer로 처리하여 변경된 cuurnetUser state를 useSelector hook을 통해서 가져온다. 정보를 가져올 수 있는 이유는 App.js에서 provider 태그를 통해서 store 정보를 하위 컴포넌트에게 전달하고 있기 때문이다.
import React,{useEffect} from 'react'
import { useSelector,useDispatch} from 'react-redux'
import { fetchUsers} from '../redux/actions'
const {currentUser}= useSelector(state => state.userState);
useEffect(()=>{
// user 정보
dispatch(fetchUsers());
},[])
'ReactNative' 카테고리의 다른 글
[ReactNative Instagram clone #4_카메라 / 갤러리 사진 업로드 / Firebase Storage ] (0) | 2022.05.11 |
---|---|
[ReactNative Instagram clone #2_파일구조 및 DB 구조 (0) | 2022.05.11 |
[ReactNative Instagram clone #1_소개 ( redux / FireBase] (0) | 2022.05.11 |
[React Navigation]Drawer Navigator 설치 및 적용 *Realm 과 에러 (0) | 2022.01.26 |
[React_Native_study_16]useAsset 사용법 (0) | 2021.12.30 |