Firebase, Redux 를 이용한 로그인 구현

Seungmin Shin·2021년 9월 20일
2
post-custom-banner

Login with Firebase && Redux

Firebase는 프론트엔드 개발자가 사용자 인증, 저장 및 데이터베이스 생성과 같은
일반적인 백엔드 작업을 구현할 수 있는 꽤 인기있는 BaaS (backend-as-a-service) 입니다.

그리고 Redux 는 JavaScript Application 을 위한 예측가능한 상태 컨테이너이며, Application 의
상태를 보다 효율적으로 관리하는데 사용됩니다, 요즘은 React 와 함께 사용되고 있죠, 그렇다고 해서
React 하고만 사용되는것이 아닌 다른 프레임워크와도 충분히 사용할 수 있습니다.

React-Redux-Firebase는 Firebase용 Redux 바인딩을 제공하여 Firebase를 Redux 및 React와
함께 더 쉽게 사용할 수 있도록 하는 라이브러리입니다.

또한 일부 Firebase 기능을 더 쉽게 구현할 수 있도록 몇 가지 Hooks가 함께 제공되기도 합니다.

Prerequisites ( 전제조건 )

이 로직을 구현하기 위해서는 몇가지 전제조건이 필요합니다.

  1. 기본적으로 리액트를 이용할 줄 알아야합니다.
  2. 리액트와 리덕스에 관한 지식이 있어야 합니다.
  3. Firebase 에 대한 지식이 있어야 합니다.
  4. 리액트 라우터에 관한 기본지식이 있어야 합니다.

네가지 조건중에 하나라도 만족하지 못한다면 해당 글을 읽는데 어려움이 있을 수 있으니, 먼저 선행학습 후
이 글을 읽는것을 추천합니다. (저도 이 글 쓰려고 공부했습니다.. 하하)

React 설정

리액트 프로젝트를 만들어줍니다.

npx create-react-app login-test --template redux

를 터미널에 입력하고 실행하면, 기존 리액트에 리덕스까지 동시에 설치됩니다.
리액트 폴더가 만들어지면, cd 로 해당 폴더로 진입합니다. 그리고 추가로 몇가지를 더 설치하겠습니다.

npm install --save react-redux-firebase firebase
npm install --save redux react-redux
npm install --save redux-firestore@latest
npm install react-router-dom

위의 입력들은 내용을 보면 알겠지만, react - redux - firebase 간의 연결을 위한 설정이라고
이해하면 될것 같습니다.

Firebase 설정

위의 설치가 모두 끝났다면, 이번에는 firebase 를 세팅해 보겠습니다.

Thanks to. ( firebase 설치 안내 블로그 )

이곳에서 설명하는대로 따라서 새로운 프로젝트를 하나 작성해 보겠습니다.

이런 페이지로 이동했다면, 성공적으로 프로젝트를 생성한것입니다.
이제, 왼쪽의 사이드바에서 Authentication 을 찾아 이동하겠습니다.

시작하기 버튼을 클릭합니다.

해당 화면에서, 우리는 구글로그인과 일반 이메일 로그인을 구현할 예정이니까 이메일/비밀번호
Google 을 클릭하여 사용권한을 설정하겠습니다.

구글탭에서 프로젝트 지원 이메일은 자신의 이메일을 적으면 됩니다.

이렇게 사용권한 설정이 끝났습니다.
이제 다음으로 FireStore 로 이동하겠습니다.

데이터베이스 만들기 를 클릭합니다.

프로덕션 모드와 테스트 모드가 있는데, 지금은 테스트 모드를 선택하겠습니다.
이후에 코드를 변경해서 프로덕션 모드로도 변화를 줄 수 있다고 합니다.

성공적으로 firestore 가 생성이 되었습니다. 그 다음 규칙 으로 이동하겠습니다.

해당 드래그되어있는 부분을 지우고 나타나는 게시를 눌러 수정을 해줍니다.
다음, 좌측 상단에 있는 옵션 아이콘을 클릭해서 프로젝트설정 으로 들어갑니다.

이곳에서는 우리가 만든 firebase 가 어디서 작동할지를 설정합니다.
하단의 내 앱으로 가서, 안드로이드 ,ios, 웹 중 을 클릭하겠습니다. </> 표시 입니다.

진행창이 보이면, 만들 앱의 닉네임을 설정 하고 체크박스를 클릭하고 계속해서 진행합니다.
콘솔로 이동될때까지 '계속' 버튼을 클릭 해주면 됩니다.

콘솔로 이동이 되고난 후 하단으로 스크롤을 하면 보이는 화면이

이 화면이고, 현재 선택되어있는 구성 탭 의 코드가 잘 보인다면 성공적으로 끝난것입니다.
이제 나중에 저 구성 탭에 있는 코드를 사용할것입니다. 코드를 잘 복사해 두거나, 해당 페이지를 닫지않습니다.

이제 모든 준비가 끝났고, 에디터로 넘어가겠습니다.

로그인 로직 짜기

1. 작업환경 정리 (삭제)

일단 에디터로 돌아가서 정리를 한번 해주겠습니다. 필요없는 파일들을 삭제 합니다.

App.test.js
logo.svg
serviceWorker.js
setupTests.js
App.js (내부 파일만)

파일을 삭제 해 주겠습니다.
그리고 이 외의 파일들 안에서 삭제된 파일들이 import 되어 사용되고 있는 부분을 전부 삭제해주겠습니다.
그리고 나서 App.js 안의 <div> 태그 안의 코드를 전부 삭제하고 간단한 text 를 작성해서
정상적으로 구현이 되는지를 확인합니다. npm start 로 실행합니다.

정상적으로 출력이 되는것을 확인했다면, 이제 시작할 준비가 되었습니다.
만약, 오류가 뜬다면 해당 오류를 잘 읽어보고 지우지 않은 코드가 있는지 확인합니다.

2. firebase.js

이 프로젝트의 목적은 로그인 구현 이라는 기능이기 때문에, 다른 디자인 없이 간단하게만 구현합니다.

현재 출력이 되는 App.js 는 메인 화면 즉, 로그인이 되면 나오는 화면일테니. 텍스트를 바꿔줍니다.

이제부터 만들 로그인 창에서 로그인이 정상적으로 완료된다면, 이 페이지가 보이게 될겁니다.
firebase.js 파일을 생성합니다. 그리고 아까 복사해 두었던 firebase 의 코드를 복붙합니다.

npm i firebase 를 입력하고 firebase 를 설치합니다.

그리고 firebase 를 import 해주겠습니다.

이 다음에는 기본적인 firebase app 코드와, 우리가 설정했던 기본 이메일,
그리고 구글로 로그인하는 코드를 작성할겁니다.

const firebaseApp = firebase.initializeApp(firebaseConfig);
// firebase app 관련 코드

const auth = firebase.auth()
// 기본 이메일 로그인을 위한 코드

const provider = new firebase.auth.GoogleAuthProvider()
// 구글 로그인을 위한 코드

const db = firebaseApp.firestore()
// 데이터베이스 관련 코드

export {auth, provider}
export default db
// 위에서 설정한 변수들을 export 하는 코드

위의 코드들을 밑에다 붙여넣어줍니다.

시간이 지나면 몇몇 코드들의 text color 가 바뀔겁니다, 아직 firebase 가 적용되지 못한상태입니다.
진행하는데는 크게 문제가 없으니 다음으로 넘어가겠습니다.

(firebase 가 정상적으로 적용된 상태)

3. Redux 구현

일단 features 폴더 안에있는 파일 중

counterSlice.js

만 남기고 모두 삭제하고, 모든 파일을 counter 파일에서 빼 features 폴더에 담습니다.
counter 폴더는 사라집니다.

현재 보이는게 리덕스의 기본 세팅이라고 할 수 있겠습니다. 우리는 여기서 몇가지만 삭제합니다.
아래의 코드로 덮어씌웁니다. 기존 코드에서 몇가지만 삭제하고 정리한 코드입니다.

import { createSlice } from '@reduxjs/toolkit';

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload;
    },
  },
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;

export const selectCount = (state) => state.counter.value;

export default counterSlice.reducer;

이제 여기서 코드를 변경하겠습니다.

  1. counter -> user 로 변경, 파일명도 userSlice.js 로 바꾼다.

  1. createSlice() -> initialState: { user: null }

    로그인의 초기값은 '모르는' 사용자이기 때문에 null 값을 부여합니다.

  1. reducers: increment -> login , decrement -> logout , incrementByAmount 삭제

    로그인의 기능에는 로그인, 로그아웃만 필요하기 때문에 위에서부터 변경하고 마지막 세번째는
    삭제합니다. 내부의 요소도 변경할겁니다.

생성한 login 과 logout 은 reducer 가 생성한 action 입니다. action 이 key 값으로 들어온거죠
바로 createSlice( ) 때문에 가능하게 됬습니다.

어디선가 객체로 dispatch 되어 온 action 을 createSlice 가 받게 됩니다. 그리고 reducer 는
들어온 객체가 login 인지, logout 인지 구별해서 해당 기능을 실행하게 됩니다.

  1. login, logout 내부 변경

    login 부분의 인자에 action 을 추가해 준다. 그리고 state.value 가 아닌, state.user 로
    변경해준다, login 에는 그 값을 action.payload 를 할당한다.

action.payload 는 특정 컴포넌트에서 받아온 정보들을 현재 user의 상태에 추가하겠다는 의미입니다.
logout 은 user 의 상태를 초기상태로 되돌리면 됩니다. null 을 할당시킵니다.

  1. userSlice.actions 에 login 과 logout 을 할당한다.

  1. selectCount -> selectUser 로 변경, state.user.value -> state.user.user 변경

state.user 의 user 정보를 전달해야한다. 나중에 userSelector 로 상태를 변경할겁니다.
이제 store 로 이동합니다.

  1. app -> store.js 로 이동 후 코드작성

  • store 란, state 값을 저장하는 장소입니다. reducer 에서 전달받은 action 을 redux 의
    dispatch 함수를 사용하여 store에 전달합니다, createStore 를 통해서 만들 수 있고,
    createStore 안의 reducer 역할을 하는 함수가 들어가야 합니다.

4. 페이지 구성 ( App.js, Main.js )

삼항연산자를 이용해서 로그인 화면과, 로그인 성공시 보이는 화면을 구별시켜보겠습니다.
App.js 로 이동합니다.

user의 상태가 존재한다면 로그인 성공페이지를, 그렇지 않다면 로그인 화면을 보이게 해야합니다.
그 전에, 로그인 성공페이지를 App.js 에 단순한 h1 태그로 작성되있어서 복잡해 보일 수 있으니.
Main.js 파일을 만들어 그 안에 로그인 성공 페이지를 만들어줍니다.

Main.js 파일 생성

App.js 파일에서 삼항연산자를 이용해, user 가 존재하면 Main.js 로, 그렇지 않다면 Login.js
이동하게 합니다, 그리고 user의 출처는 useSelector 를 이용해 userSlice 에서 만들어놓은
selectUser 를 가져온것입니다.

그럼 이제 Login.js 로 이동해 로그인 페이지를 구현하겠습니다.

5. 페이지 구성 ( Login.js, Login.css )

Login.js 의 구성은 꽤 길기때문에 코드에 주석을 달아 설명을 하겠습니다.

import React, { useState } from 'react';
import { auth, provider } from "./firebase";
// firebase 에서 export 한 두가지의 변수 (일반, 구글) 를 가져옵니다.
import './Login.css'

function Login() {
    const [email, setEmail] = useState("");
    // email 값의 변화를 useState 로 저장합니다. 
    const [password, setPassword] = useState("");
    // password 값의 변화를 useState 로 저장합니다. 
  
    const signIn = () => {
        auth.signInWithPopup(provider).catch((e) => alert(e.message));
    };
    // 로그인창 을 호출하는 코드

    const handleLogin = (e) => {
        e.preventDefault();

        auth.signInWithEmailAndPassword(email, password)
          .then((auth) => {
             console.log(auth);
           })
          .catch((e) => alert(e.message))

        setEmail("");
        setPassword("");
    }
    // 로그인 버튼 클릭시 실행되는 함수

    const handleRegister = (e) => {
        e.preventDefault();

        auth.createUserWithEmailAndPassword(email, password)
          .then((auth) => {
            if (auth) {
                console.log(auth)
            }
           })
          .catch((e) => alert(e.message))
      
        setEmail("");
        setPassword("");
    };
   // 회원가입 버튼 클릭시 실행되는 함수

    return (
        <div className="login">
            <div className="login_container">
                <div className="login_desc">
                    <p> 로그인 화면입니다</p>
                </div>
                <div className="login_auth">
                    <div className="login_authOptions">
                        <div className="login_authOption">
                            <img className="login_gooogleAuth"
                                src="https://media-public.canva.com/MADnBiAubGA/3/screen.svg"
                                alt="" />
                            <p onClick={signIn}> 구글 아이디로 로그인 </p>
                        </div>
                    </div>
                    <div className="login_emailPass">
                        <div className="login_label">
                            <h4> 로그인 </h4>
                        </div>
                        <div className="login_inputFields">
                            <div className="login_inputField">
                                <input type="text" placeholder="이메일"
                                    value={email} onChange={(e) => setEmail(e.target.value)} />
                            </div>
                            <div className="login_inputField">
                                <input type="password" placeholder="비밀번호" value={password} onChange={(e) => setPassword(e.target.value)} />
                            </div>
                        </div>
                        <div className="login_forgButt">
                            <small> 비밀번호 찾기</small>
                            <button type="submit" onClick={handleLogin}>로그인</button>
                        </div>
                        <button onClick={handleRegister}>회원가입</button>
                    </div>
                </div>
            </div>
        </div>
    );
}

export default Login;

컴포넌트를 꽤 많이 쪼갠것을 볼 수 있는데, 이것은 화면을 디자인할 때 조각이 많아야 더욱 디테일한
디자인이 가능하기 때문입니다, 위 코드를 보면 거의 버튼하나하나, input창 하나하나를 쪼갠걸 볼 수 있는데,
큰 덩어리부터 보게 되면 그다지 어려운 코드가 아닌걸 알 수 있습니다.

그리고 이 코드에서 동작을 수행하는것은 회원가입, 로그인, 구글 로그인정도이기 때문에
그것들에 대한 핸들러 함수를 상부에 작성하였습니다. 이 중에는 firebase 기본 메서드들 도 있습니다.
훑어보면서 공부하는것을 추천합니다.

Login.css 도 보겠습니다.

.login {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
}

.login_container {
    display: flex;
    flex-direction: column;
    padding: 20px;
    width: 700px;
    background-color: rgb(255, 255, 255);
    border-radius: 10px;
}

.login_desc > p {
    text-align: center;
    font-weight: 500;
    font-size: 20px;
}

.login_auth {
    display: flex;
    margin-top: 50px;
}

.login_authOptions {
    display: flex;
    flex: 0.5;
    flex-direction: column;
    padding: 20px;
    border-right: 1px solid lightgray;
}

.login_authOption {
    display: flex;
    align-items: center;
    padding: 7px;
    margin-bottom: 15px;
    border: 1px solid lightgray;
    border-radius: 5px;
    cursor: pointer;
}

.login_authOption:hover {
    background-color: rgb(226, 226, 226);
}

.login_authOption > p {
    margin-left: 11px;
}

.login_emailPass {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 20px;
    flex: 0.5;
    margin-left: 20px;
}

.login_inputFields {
    display: flex;
    flex-direction: column;
    padding: 5px;
}

.login_inputField {
    padding: 5px;
}

.login_forgButt {
    padding-left: 10px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.login_forgButt > button {
    padding: 9px;
    cursor: pointer;
}

.login_emailPass > button {
    padding: 9px;
    cursor: pointer;
    margin-top: 10px;
}

크게 많은것을 구현하지 않을려고 했지만, 보기에는 좋아야 하니. 최소한의 css 를 구현했습니다.
이제 이렇게 작성한 후 출력되는 화면을 보겠습니다.

로그인 화면이 구현되었습니다. 구글로그인, 일반 로그인 두가지로 나뉘어졌습니다.

6. 로그인, 로그아웃

일단 먼저 구글 로그인을 해보겠습니다. 구글 아이디로 로그인 버튼 을 클릭합니다.

구글 계정을 선택하는 창이 뜹니다. 내 계정을 클릭해봅시다.

성공적으로 로그인 성공 창이 띄워졌습니다. 아래에 있는 로그아웃 버튼을 만들어야 합니다.
Main.js 로 갑니다.

해당 text 가 있는 하부에 버튼태그를 만들어 준 후

<button onClick={() => auth.signOut()}>Logout</button>

을 추가 해 줍니다. 그리고 상부에서 auth도 import 해줍니다. firebase.js 에서 가져오는겁니다.
signOut 메서드를 통해 인증을 해제시켜 user를 null 로 만들어 Login.js 로 보내는겁니다.

이렇게 버튼을 만들어 클릭을 하면 다시 로그인 창으로 돌아갑니다.
이번엔 이메일, 비밀번호로 들어가보겠습니다. 순서는 이러합니다.

  1. 이메일과 비밀번호를 작성한다. (비밀번호는 6자리 이상)
  2. 회원가입 버튼을 클릭한다.
  3. 다시 이메일과 비밀번호를 작성한다.
  4. 로그인버튼을 클릭한다.

이 순서대로 작성한 후 결과를 확인하자. 똑같은 결과가 나왔다. 두 방법 모두 성공했다.


정리

firebase 로 간단한 백엔드를 구현해서 이메일, 구글로그인이 가능하게 구현해보았다.
리덕스로 사용자의 정보를 가져와서 로그인과 로그아웃의 기능이 가능하게 구현해보았다.

profile
Frontend Developer
post-custom-banner

0개의 댓글