인스타그램클론코딩

인스타그램클론코딩(react-native, firebase, expo, Formik, Yup) 02

lian_is_clone 2022. 1. 13. 18:33

 기본셋팅 및 버전

- Visual Studio Code v1.63.2

- nodeJS 14.16

- expo 5.0.1

- react-native 0.66.4

- firebase 9.6.2

- expo 44.0.4

- @react-navigation/native 6.0.6

- @react-navigation/native-stack 6.2.5

- react-native-safe-area-context

- formik 2.2.9

- yup 0.32.11

- email-validator 2.0.4

나머지 라이브러리들은 쓰면서 추가하겠습니다.

 

이어서 Login 및 Signup 페이지 를 제작해 보겠습니다.

 

Signup.js

import React from 'react'
import { View, Text, StyleSheet, TextInput, TouchableOpacity, Pressable, Image } from 'react-native'
import { getAuth, createUserWithEmailAndPassword } from 'firebase/auth'
import { getFirestore, doc, setDoc } from 'firebase/firestore'

import { Formik } from 'formik';
import * as Yup from 'yup'

const Signup = ({ navigation }) => {

    const signUpFormSchema = Yup.object().shape({
        email: Yup.string().email().required('정확한 이메일형식으로 작성해주세요'),
        password: Yup.string().required().min(6, '비밀번호 6자리 이상 입력하세요'),
        name: Yup.string().required().min(2, '적합하지않은 이름입니다.')
    })

    const db = getFirestore();
    const auth = getAuth();
    const onSignUp = (email, password, name) => {
        try {
            // console.log(email, password, name)
            createUserWithEmailAndPassword(auth, email, password)
                .then((result) => {
                    setDoc(doc(db, 'users', auth.currentUser.uid), { email, name })
                })
        } catch (error) {
            console.log(error.message)
        }
    }

    return (
        <View style={styles.container}>
            <Image style={styles.logo} source={require(`../../assets/instagramlogo.png`)} />
            <View style={styles.formik}>
                <Formik
                    initialValues={{ email: '', password:'', name: '' }}
                    onSubmit={values => onSignUp(values.email, values.password, values.name)}
                    validationSchema={signUpFormSchema}
                    validateOnMount={true}
                >
                    {({ handleChange, handleBlur, handleSubmit, values, isValid }) => (
                        <>
                            <View >
                                <TextInput
                                    style={styles.inputs}
                                    placeholder='이메일'
                                    autoCorrect={false} // 자동수정 비활성화
                                    autoFocus={true} // 페이지오픈시 포커스
                                    autoCapitalize='none'
                                    textContentType='emailAddress'
                                    onChangeText={handleChange('email')}
                                    onBlur={handleBlur('email')}
                                    value={values.email}
                                    />
                            </View>
                            <View>
                                <TextInput
                                    style={styles.inputs}
                                    placeholder='비밀번호'
                                    autoCorrect={false}
                                    secureTextEntry={true} // 비밀번호 *** 처리
                                    autoCapitalize='none'
                                    textContentType='password'
                                    onChangeText={handleChange('password')}
                                    onBlur={handleBlur('password')}
                                    value={values.password}
                                    />
                            </View>
                            <View>
                                <TextInput
                                    style={styles.inputs}
                                    placeholder='이름'
                                    autoCorrect={false}
                                    autoCapitalize='none'
                                    textContentType='text'
                                    onChangeText={handleChange('name')}
                                    onBlur={handleBlur('name')}
                                    value={values.name}
                                />
                            </View>
                            <Pressable style={[styles.signupbtn,{ backgroundColor: isValid ? '#6BB0F5' : '#9ACAF7', }]} onPress={handleSubmit}>
                                <Text style={styles.signupbtnText}>회원가입</Text>
                            </Pressable>
                        </>
                    )}
                </Formik>
            </View>

            <TouchableOpacity style={[styles.moveContainer, styles.loginBtn]} onPress={() => navigation.goBack()}>
                <Text style={[styles.moveText]}>이미 계정이 있으신가요?</Text>
                <Text style={[styles.moveText, styles.moveTextBold]}>로그인하기</Text>
            </TouchableOpacity>
        </View>
    );
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: 'white'
    },
    logo: {
        width: 160,
        height: 45,
        marginBottom: 30
    },
    formik:{
        width:'100%',
        paddingHorizontal:'5%'
    },
    inputs: {
        backgroundColor: 'whitesmoke',
        padding: 10,
        borderWidth: 1,
        borderColor: 'gainsboro',
        borderRadius: 5,
        marginBottom: 15,
        fontSize: '.7rem',
        outlineStyle: 'none'
    },
    signupbtn: {
        width: '100%',
        height: 40,
        backgroundColor: '#99c4e9',
        borderRadius: 5,
        alignItems: 'center',
        justifyContent: 'center',
    },
    signupbtnText: {
        color: 'white',
        fontSize: '.7rem',
    },
    moveContainer: { flexDirection: 'row' },
    moveText: { fontSize: '.6rem' },
    moveTextBold: { fontWeight: '600' },
    helpBtn: { marginVertical: 10 },
    loginBtn: {
        position: 'fixed',
        bottom: 0,
        left: 0, right: 0,
        justifyContent: 'center',
        alignItems: 'center',
        height: 40,
        borderTopWidth: 2,
        borderTopColor: 'whitesmoke'
    }
});


export default Signup

시그업 하는데 두가지의 방법을 확인하였습니다. 

 

Formik으로 값확인하고 Yup으로 유효성검사를 하는 법

 

react의 useState를 활용하여 인풋값을 onChangeText로 setState 가져와서 submit할 때 

유효성검사 하는법 

 

npm 인스톨 수 보니 Formik도 사람들이 많이 쓰는거 같아서 Formik방법을 채택하였다.

 

Login.js

import { Pressable, Image, StyleSheet, TextInput, View, Text, TouchableOpacity } from 'react-native';

import { Formik } from 'formik';
import * as Yup from 'yup'

import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';



const Login = ({ navigation }) => {

    const auth = getAuth()

    const signInFormSchema = Yup.object().shape({
        email: Yup.string().email().required(''),
        password: Yup.string().required().min(6, '')
    })

    const onSignIn = (email, password) => {
        signInWithEmailAndPassword(auth, email, password)
            .then()
            .catch(err => {console.log('이메일 혹은 패스워드 확인 부탁드립니다.')})
    }

    return (
        <View style={styles.container}>
            <Image style={styles.logo} source={require(`../../assets/instagramlogo.png`)} />
            <View style={styles.formik}>
                <Formik
                    initialValues={{ email: '', password: '' }}
                    onSubmit={values => onSignIn(values.email, values.password)}
                    validationSchema={signInFormSchema}
                    validateOnMount={true}
                >
                    {({ handleChange, handleBlur, handleSubmit, values, isValid }) => (
                        <>
                            <View >
                                <TextInput
                                    style={styles.inputs}
                                    placeholder='이메일'
                                    autoCorrect={false} // 자동수정 비활성화
                                    autoFocus={true} // 페이지오픈시 포커스
                                    autoCapitalize='none'
                                    textContentType='emailAddress'
                                    onChangeText={handleChange('email')}
                                    onBlur={handleBlur('email')}
                                    value={values.email}
                                />
                            </View>
                            <View>
                                <TextInput
                                    style={styles.inputs}
                                    placeholder='비밀번호'
                                    autoCorrect={false}
                                    autoCapitalize='none'
                                    textContentType='password'
                                    secureTextEntry={true}
                                    onChangeText={handleChange('password')}
                                    onBlur={handleBlur('password')}
                                    value={values.password}
                                />
                            </View>
                            <Pressable style={[styles.loginbtn, { backgroundColor: isValid ? '#6BB0F5' : '#9ACAF7', }]} onPress={handleSubmit} disabled={!isValid}>
                                <Text style={styles.loginbtnText}>로그인</Text>
                            </Pressable>
                        </>
                    )}
                </Formik>
            </View>

            <TouchableOpacity onPress={() => console.log('fortget-passward')} style={[styles.moveContainer, styles.helpBtn]}>
                <Text style={[styles.moveText]}>로그인 상세 정보를 잊으셨나요?</Text>
                <Text style={[styles.moveText, styles.moveTextBold]}>로그인 도움말 보기.</Text>
            </TouchableOpacity>

            <TouchableOpacity style={[styles.moveContainer, styles.signupBtn]} onPress={() => navigation.navigate('Signup')}>
                <Text style={[styles.moveText]}>계정이 없으신가요?</Text>
                <Text style={[styles.moveText, styles.moveTextBold]}>가입하기</Text>
            </TouchableOpacity>
        </View>
    );
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        paddingHorizontal: '5%',
        backgroundColor: 'white'
    },
    logo: {
        width: 160,
        height: 45,
        marginBottom: 30
    },
    formik:{
        width:'100%',
        paddingHorizontal:'5%'
    },
    inputs: {
        backgroundColor: 'whitesmoke',
        padding: 10,
        borderWidth: 1,
        borderColor: 'gainsboro',
        borderRadius: 5,
        marginBottom: 15,
        fontSize: '.7rem',
        outlineStyle: 'none'
    },
    loginbtn: {
        width: '100%',
        height: 40,
        backgroundColor: '#99c4e9',
        borderRadius: 5,
        alignItems: 'center',
        justifyContent: 'center',
    },
    loginbtnText: {
        color: 'white',
        fontSize: '.7rem',
    },
    moveContainer: { flexDirection: 'row' },
    moveText: { fontSize: '.6rem' },
    moveTextBold: { fontWeight: '600' },
    helpBtn: { marginVertical: 10 },
    signupBtn: {
        position: 'fixed',
        bottom: 0,
        left: 0, right: 0,
        justifyContent: 'center',
        alignItems: 'center',
        height: 40,
        borderTopWidth: 2,
        borderTopColor: 'whitesmoke'
    }
});


export default Login

 

이렇게 되면 잘 들어온다.

 

그럼 이제 엡 오픈 시 로그인되었는지 확인 후 메인페이지로 이동하는 엡으로 이동해야지요.

 

App.js

import { useEffect, useState } from 'react'
import { Text, View, TouchableOpacity, StyleSheet } from 'react-native';

import AuthScreen from './screens/AuthScreen';
import MainScreen from './screens/MainScreen'

import { initializeApp, getApps } from "firebase/app";
import { getAuth, onAuthStateChanged } from 'firebase/auth'

const firebaseConfig = {
  apiKey: "yuor code",
  authDomain: "yuor code",
  projectId: "yuor code",
  storageBucket: "yuor code",
  messagingSenderId: "yuor code",
  appId: "yuor code",
  measurementId: "yuor code"
};

getApps.length === 0 ? initializeApp(firebaseConfig) : '';



export default function App() {
  const [currentUser, setCurrentUser] = useState(null)

  const useHandler = user => user ? setCurrentUser(user) : setCurrentUser(null)
  useEffect(() => {
    const auth = getAuth();
    onAuthStateChanged(auth, (user) => useHandler(user))
  }, [])

  return (
    <View style={styles.container}>
      { currentUser? <MainScreen />:<AuthScreen /> } 
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1
  }
})

useEffect로 엡이 구동 될 때 firebase 에 로그인되어있는지 확인하고 아니면 로그인화면으로 이동하게 합니다. 

( 먼처 로그인화면으로 이동했다가 갑자기 메인화면으로 넘어가는 현상이 있던데... 흐흠)

 

간략하게 메인페이지는 이렇게 구현하였다.

 

MainScreen.js

import React from 'react'
import { View, Text, TouchableOpacity } from 'react-native'
import { getAuth, signOut } from 'firebase/auth'
const MainScreen = () => {
    const signOutBtn = () =>{
        signOut(getAuth())
    }
    return (
        <View>
            <Text>{ getAuth().currentUser.uid }</Text>
            <TouchableOpacity onPress={signOutBtn}>
                <Text>logout</Text>
            </TouchableOpacity>
        </View>
    )
}

export default MainScreen

 

 

이제 메인스크린을 작성해보자 이건 03으로 넘어갑니다.