Kakao Social Login 구현하기 (feat. Redux)

IRE_0546·2021년 7월 23일
1

창업 동아리에서 일을 하면서, 소셜 로그인을 구현할 일이 생겼었다. 그런데... 생각만큼 쉽지 않았다. 그냥 공식문서 조금 보고 참고해서 만들면 되는거지! 라고 생각했던 나의 바람과는 달리 꽤나 이해가 필요한 작업이었다.

나는 React를 이용하여 웹 페이지를 기반으로 작업을 했다. React에 Kakao Login을 구현해 놓은 여러 사례를 찾아보고 적용해 보려고 노력을 해 보았는데, 이것도 역시 바로 되지 않았다. 그럼 그렇지. 역시 날로 먹을 생각은 하지 않는 편이 좋다. 그래서 결국은 카카오 공식 문서를 참고하고 다른 분이 써 놓은 Login 구현하는 방법의 글을 참고하여 로그인을 구현해 보았다.

참고한 글에 기본적인 소셜 로그인 구현에 대해 잘 설명이 되어있으니, 나는 이 글에서 다루지 않았던 기본적인 Redux 측면을 추가로 공부하며 적용해 보았던 것을 적어보려고 한다.

사실상 Redux Basic에 대한 내용이니 소셜 로그인은 다른 글을 참고해 주시길 바라면서. Redux가 어떤 건지부터 정확히 파악을 못하고 있는 나에게는 참 어려운 과제였다. 그래서 처음부터 공부하면서 찾아봤다. (사실 굳이 굳이 로그인 기능에서 Redux를 사용할 필요는 없었지만 쓰는 편이 배우는 것도 많고 편리할 것 같아서.)

1. Redux의 구조

사실 처음 접하는 거라 무슨 말인지도 이것저것 뒤져보다 보니 헷갈리다가, 참고 자료를 보고 어떤 이유에서 Redux의 구조와 필요가 있는 건지 정확하게 이해를 하게 되었다.

간단하게 정리해보면 아래와 같다.

  • Redux는 FLUX 패턴이다.
    -> FLUX 패턴이란?
    그림과 같이 시스템에서 어떠한 Action을 받았을 때, Dispatcher가 받은 Action들을 통제하여 Store에 있는 데이터를 업데이트 한다. 그리고 변동된 데이터를 View에 리렌더링 한다.

Dispatcher: 작업이 중첩되지 않도록 해준다.
Action: Dispatcher를 통하여 Store에 있는 데이터를 처리한다.

  • Redux의 3원칙
  1. Single Source of Truth
    state는 단 하나의 store로만 사용) → 보통 nested 구조로 이뤄져 있다.
  2. State is read-only
    state를 직접 변경할 수 없으며, state를 변경하기 위해서는 action이 dispatch 되어야 한다.
  3. Changes are made with Pure Function
    action을 dispatch하여 상태값 변경 → action 객체를 처리하는 함수를 Reducer라고 한다.
    → action을 받고 어떻게 상태를 바꿀 지 정의하는 함수이다.
    순수 함수로 작성 되어야 한다:

Redux의 기본적인 내용을 알고 나서, 차근히 나고 있는 오류를 해결해 나가는 형식으로 카카오 소셜 로그인에 포함되어있는 Redux 코드를 만져보기 시작했다.

2. Error 해결 (Redux 파악)

오류 1) actionCreator가 export 되지 않았습니다.

src/pages/Login.js

import React from 'react';
import { useDispatch } from 'react-redux';
import { actionCreators as userActions } from "../redux/modules/user";
import { Spinner } from 'react-bootstrap';

const Login = (props) => {
  //Dispatch: store에 있는 데이터를 컨트롤 하기 위함
  const dispatch = useDispatch();
  //인가 코드
  let code = new URL(window.location.href).searchParams.get("code");
  console.log(code);

  React.useEffect(async () => {
    await dispatch(userActions.kakaoLogin(code));
  }, []);
  
  return <Spinner/>;
}

export default Login;

리다이렉트 URL에 해당하는 페이지이다. 이 페이지로 이동하여 인가 코드를 넘기고 Token을 받는 동작을 하려고 하는데, 이와 같은 에러가 발생하였다. 아래의 페이지와 연결되는 문제였는데 코드는 다음과 같다.
src/redux/modules/user.js

import axios from "axios";

const kakaoLogin = (code) => {
    return function (dispatch, getState) {
        axios({
            method: "GET",
            url: `http://localhost:3000/?code=${code}`,
        })
        .then((res) => {
            //get token
            console.log(res);
            
            const ACCESS_TOKEN = res.data.accessToken;

            //local store (temp)
            localStorage.setItem("token", ACCESS_TOKEN);

            window.alert("Login success...");
            //get token -> change page to HOME
            //history.replace("/home"); 
        
        }).catch((err) => {
            console.log("Login error", err);
            window.alert("Login failed...");
            //history.replace("/home");
        });
    }
};

const actionCreators = { kakaoLogin }; 

export { actionCreators }

단순히 export를 다른 이름으로 잘못 해주고 있어서 발생하는 오류였다. 항상 의문이 나는 것이, export와 export default 차이였는데, 해당 링크의 글을 참고하여 의문을 해결하였다.

오류 2) Provider로 감싸주세요.

이 부분에서 꽤 애를 먹었는데, Provider? 라는 개념을 모르고 있었다. 이 부분은 공식 문서를 참고 하여 알아내었다. 이와 같은 구조를 가지도록 index.js를 수정해야 했다. 수정한 코드는 아래와 같았다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import * as serviceWorker from './serviceWorker';
import store from './store/exportStore';

ReactDOM.render(
  <Provider store={store}>
    <App/>
  </Provider>,
  document.getElementById('root')
);

오류 3) Store 선언이 제대로 되어있지 않습니다.

Store라는 개념을 처음에 모르고 있었는데, 결국 그냥 Dispatcher가 Action을 취해서 Store에 있는 데이터를 바꾸도록 동작하는 과정에서 필요한 것이라고 이해했다. 이 Store는 오직 하나만 가질 수 있으며, 보동 JS로 생각을 해보면 {{},{},{}} 와 같이 중첩된 구조를 가지고 있는 경우가 많다고 한다. 또한 순수 함수로 이뤄져야 하는 부분을 명심하자.

그래서 이 부분을 선언해서 위의 코드와 같이 Provider의 속성으로 주어야 하는데, 기존의 소셜 로그인 코드를 작성한 분은 다른 Redux 코드가 있었기 때문에 나와 같은 오류가 발생하지 않았을 것으로 보인다. 그러나 나는 소셜 로그인을 하면서 처음 도입한 Redux였기 때문에 구조를 만들 필요가 있었다.

Store를 만드는 부분에 있어서는 이 글을 참고했다. 예제는 카운터를 만드는 것이었는데 많은 도움이 되었다. 기존에 소셜 로그인에서는 딱히 필요한 동작이 없어 보여서 임의로 상태를 설정하고 store를 만드는 것에 의의를 두었다.

구조를 파악하고 나서, 나와 같은 경우에는 후에 추가할 것이 생길 수도 있으니 각각의 페이지에 대한 모듈을 만들고, 그 모듈을 합쳐서 store를 만들어 export했다.


파일 구조는 위의 그림과 같다.

LoginReducer.js

import { createAction, handleActions } from 'redux-actions';

const CODE = 'login/CODE';

export const code = createAction(CODE);

const initialState = {
    code : ''
}

export default handleActions( {
    [CODE]: (state, action) => {
        return { code : state.code + 'hello'};
    },
}, initialState);

임의의 Reducer를 정의해 주었다. HandleActions를 쓴 이유는, 저렇게 해야 나중에 다른 Action들이 많아질 때 중첩된 구조로 한꺼번에 Handle을 할 수 있도록 만들어 주기 때문이다. 음... 비슷한 것으로 Switch-Case문을 생각하면 편할 것 같다.

index.js

import { combineReducers } from "redux";
import LoginReducer from "./LoginReducer";

export default combineReducers({
    LoginReducer
});

이 부분에는 위의 코드를 Reducer로 만들어 주도록 한다. 이렇게 선언한 이유는 마찬가지로 후에 관리가 편하게 하도록 하기 위해서이다.

configure.js

import { createStore, applyMiddleware } from "redux";
import modules from './modules';
import thunk from "redux-thunk"

const configure = () => {
    const store = createStore(modules, applyMiddleware(thunk));
    return store;
}

export default configure;

modules를 import 하여 이제 store를 만들어준다. 여기서 잠깐! 비동기 메소드를 쓰기 위해서는 후에 middleware가 필요하다! 라는 오류가 나는데 이 createStore 부분에서 'applyMiddleware'를 추가해주면 오류가 해결되었다. 혹시 같은 오류가 나는 사람이 있다면 이 부분을 참고했으면 좋겠다.

exportStore.js

import configure from './configure';
export default configure();

마지막으로 만든 Store를 export 해준다. 이것을 이제 최상단의 index.js에서 import 해줘서 store로 사용하면 된다. 이렇게 하면 오류가 해결되었다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import * as serviceWorker from './serviceWorker';
import store from './store/exportStore';

ReactDOM.render(
  <Provider store={store}>
    <App/>
  </Provider>,
  document.getElementById('root')
);

이렇게 Redux를 적용하고 나니 더이상 오류는 나지 않고 페이지도 잘 로드 되며 코드가 실행이 되었다. 야메로 Redux를 급하게 적용한 것이다 보니 정확한 이해도 아직은 아니고, 틀린 부분이 있을 수 있다. 이번 주말에는 Redux 공식 문서와 예시를 참고하여 제대로 배워 보아야겠다.

그리고 Redux 부분에서는 오류가 나지 않는데 카카오 로그인 부분에서 Network 오류가 나서 그 부분도 다시 한 번 보아야겠다. 그래도 내 힘으로 처음 보는 기술을 적용해보고 오류를 해결해서 뿌듯했다.

profile
Front-end developer, Time is flying never to return ✨

0개의 댓글