@ List
** 8.2.5 Firebase로 회원 인증하기
** 8.2.6 오류 예외 처리하기
** 8.2.7 사용자 프로필 Firebase에 담기
@ Note
1. React Native Firebase 회원 인증
- Firebase에서 제공하는 함수들을 컴포넌트에 바로 사용하지 않고 임의의 함수를 따로 만들어서 호출함
- 함수를 감싸는 작업을 한 번 하면 추후 firebase를 사용하지 않고 다른 방식으로 인증할 경우에도 코드를 쉽게 전환 가능
> reference : https://rnfirebase.io/auth/usage
2. loading 을 통한 상태 관리
- async/await 문법을 사용한 비동기 처리 시, loading & setLoading 을 적절히 사용
3. 오류 처리
- e 객체의 code 를 통해 어떤 에러인지 판별 가능
> 로그인 : https://firebase.google.com/docs/reference/js/v8/firebase.auth.Auth#signinwithemailandpassword
> 회원가입 : https://firebase.google.com/docs/reference/js/v8/firebase.auth.Auth#createuserwithemailandpassword
4. AND 연산자, OR 연산자
- AND 연산자 : A && B
> ex) return { flag && "안녕하세요"} ;
A, B 모두 true 일 때 B를 반환
A, B 중 하나라도 false 인 경우 아무것도 반환하지 않음
- OR 연산자 : A || B
> ex) return { "flag" || "오랜만이네요"} ;
A가 true 인 경우 A 를 반환
A가 false, B가 true인 경우 B를 반환
A, B가 false인 경우 아무것도 반환하지 않음
5. Firebase Authentication
- Firebase 콘솔을 설정하고 App에서 회원 가입 시, 정상적으로 Authentication 페이지의 Users 탭에 등록됨
6. Firestore
- Firebase에서 제공하는 NoSQL 베이스로 아래와 같이 생성함
@ Result
** 오류 출력 관련 추가된 내용 : https://bocoder.tistory.com/88
@ Git
- https://github.com/eunbok-bocoder/PublicGalleryBocoder/commits/main
# Source tree
* AppDelegate.m 수정 이유 : https://bocoder.tistory.com/77
# components > SignButton.js
import React from 'react';
import {ActivityIndicator, StyleSheet, View} from 'react-native';
import CustomButton from '../components/CustomButton';
import {useNavigation} from '@react-navigation/native';
function SignButtons({isSignUp, onSubmit, loading}) {
const navigation = useNavigation();
const primaryTitle = isSignUp ? '회원가입' : '로그인';
const secondaryTitle = isSignUp ? '로그인' : '회원가입';
const onSecondaryButtonPress = () => {
if (isSignUp) {
navigation.goBack();
} else {
navigation.push('SignIn', {isSignUp: true});
}
};
if (loading) {
return (
<View style={styles.spinnerWrapper}>
<ActivityIndicator size={32} color="#6200ee" />
</View>
);
}
return (
<View style={styles.buttons}>
<CustomButton title={primaryTitle} hasMarginBottom onPress={onSubmit} />
<CustomButton
title={secondaryTitle}
theme="secondary"
onPress={onSecondaryButtonPress}
/>
</View>
);
}
const styles = StyleSheet.create({
spinnerWrapper: {
maginTop: 64,
height: 104,
justifyContent: 'center',
alignItems: 'center',
},
buttons: {
marginTop: 64,
},
});
export default SignButtons;
# lib > auth.js
import auth from '@react-native-firebase/auth';
// 로그인
export function signIn({email, password}) {
return auth().signInWithEmailAndPassword(email, password);
}
// 회원가입
export function signUp({email, password}) {
return auth().createUserWithEmailAndPassword(email, password);
}
// 앱 가동 or 로그인 상태 변경 시, 현재 사용자의 정보를 파라미터로 받아오는 콜백함수
export function subscribeAuth(callback) {
return auth().onAuthStateChanged(callback);
}
// 로그아웃
export function signOut() {
return auth().signOut();
}
# lib > users.js
import firestore from '@react-native-firebase/firestore';
export const usersCollection = firestore().collection('users');
export function createUser({id, displayName, photoURL}) {
return usersCollection.doc(id).set({
id,
displayName,
photoURL,
});
}
export async function getUser(id) {
const doc = await usersCollection.doc(id).get();
return doc.data(); // 다음 블로그 글인 #18 단계에서 추가함, gibhub 이용 시 참고
}
# screens > SignInScreen.js
import React, {useState} from 'react';
import {
Alert,
Keyboard,
KeyboardAvoidingView,
Platform,
StyleSheet,
Text,
View,
} from 'react-native';
import {SafeAreaView} from 'react-native-safe-area-context';
import SignButtons from '../components/SignButton';
import SignForm from '../components/SignForm';
import {signIn, signUp} from '../lib/auth';
function SignInScreen({navigation, route}) {
const {isSignUp} = route.params ?? {}; // null 병합 연산자
const [form, setForm] = useState({
email: '',
password: '',
confirmPassword: '',
});
const [loading, setLoading] = useState();
const createChangeTextHandler = name => value => {
setForm({...form, [name]: value});
};
const onSubmit = async () => {
Keyboard.dismiss();
console.log('[Log] form : ', form);
const {email, password, confirmPassword} = form;
if (isSignUp && password !== confirmPassword) {
Alert.alert('실패', '비밀번호가 일치하지 않습니다.');
return;
}
setLoading(true);
const info = {email, password};
try {
const {user} = isSignUp ? await signUp(info) : await signIn(info);
console.log('[LOG] user : ', user);
} catch (e) {
const message = {
'auth/email-already-in-use': '이미 가입된 이메일입니다.',
'auth/wrong-password': '잘못된 비밀번호입니다.',
'auth/user-not-found': '존재하지 않는 계정입니다.',
'auth/invalid-email': '유효하지 않은 이메일 주소입니다.',
};
const msg = message[e.code] || `${isSignUp ? '가입' : '로그인'} 실패`;
Alert.alert('실패', msg);
console.log('[Log] error :', e);
} finally {
setLoading(false);
}
};
return (
<KeyboardAvoidingView
style={styles.KeyboardAvoidingView}
behavior={Platform.select({ios: 'padding'})}>
<SafeAreaView style={styles.fullscreen}>
<Text style={styles.text}>PublicGallery</Text>
<View style={styles.form}>
<SignForm
isSignUp={isSignUp}
onSubmit={onSubmit}
form={form}
createChangeTextHandler={createChangeTextHandler}
/>
<SignButtons
isSignUp={isSignUp}
onSubmit={onSubmit}
loading={loading}
/>
</View>
</SafeAreaView>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
KeyboardAvoidingView: {
flex: 1,
},
fullscreen: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 32,
fontWeight: 'bold',
},
form: {
marginTop: 64,
width: '100%',
paddingHorizontal: 16,
},
// buttons: {
// marginTop: 64,
// },
});
export default SignInScreen;
'React Native > React Native_study' 카테고리의 다른 글
[리액트 네이티브를 다루는 기술 #19] 8장 Firebase로 사진 공유 앱 만들기1 (p .466 ~ 478) (0) | 2022.03.16 |
---|---|
[리액트 네이티브를 다루는 기술 #18] 8장 Firebase로 사진 공유 앱 만들기1 (p .453 ~ 465) (0) | 2022.02.18 |
[리액트 네이티브를 다루는 기술 #16] 8장 Firebase로 사진 공유 앱 만들기1 (p .419 ~ 444) (0) | 2022.01.26 |
[리액트 네이티브를 다루는 기술 #15] 8장 Firebase로 사진 공유 앱 만들기1 (p .407 ~ 418) (0) | 2022.01.20 |
[리액트 네이티브를 다루는 기술 #14] 7장 다이어리 앱 만들기2 (p .399 ~ 406) (0) | 2022.01.17 |