6-4. 회원가입 완성하기 및 유저정보 담기

이제 회원가입 프로세스는 거의 완성했습니다. 이번 섹션에서는 회원가입 API 를 호출하고, 유저 정보를 스토어와 로컬스토리지안에 담는것을 구현하겠습니다.

회원가입 API 호출

우선, auth 모듈의 result 값을 Register 에 연결시켜주고, handleLocalRegister 메소드를 작성하세요.

실제 API 가 호출 되기 전에, 한번 더 검증하는 작업을 거치고, 요청 후에 만약에 에러가 발생했다면, response 의 상태를 확인하여 코드가 409일 경우에, key 의 값에 따라 이 오류가 이메일 중복때문에 생긴건지, 아니면 아이디 중복때문에 생긴건지 에러를 띄워줍니다.

그리고, 그 외의 상황엔, 알 수 없는 오류가 발생했다고 띄웁니다.

src/containers/Auth/Register.js

(...)

class Register extends Component {
    (...)

    handleLocalRegister = async () => {
        const { form, AuthActions, error, history } = this.props;
        const { email, username, password, passwordConfirm } = form.toJS();

        const { validate } = this;

        if(error) return; // 현재 에러가 있는 상태라면 진행하지 않음
        if(!validate['email'](email) 
            || !validate['username'](username) 
            || !validate['password'](password) 
            || !validate['passwordConfirm'](passwordConfirm)) { 
            // 하나라도 실패하면 진행하지 않음
            return;
        }

        try {
            await AuthActions.localRegister({
                email, username, password
            });
            const loggedInfo = this.props.result.toJS();
            console.log(loggedInfo);
            // TODO: 로그인 정보 저장 (로컬스토리지/스토어)
            history.push('/'); // 회원가입 성공시 홈페이지로 이동
        } catch(e) {
            // 에러 처리하기
            if(e.response.status === 409) {
                const { key } = e.response.data;
                const message = key === 'email' ? '이미 존재하는 이메일입니다.' : '이미 존재하는 아이디입니다.';
                return this.setError(message);
            }
            this.setError('알 수 없는 에러가 발생했습니다.')
        }
    }    

    render() {
        const { error } = this.props;
        const { email, username, password, passwordConfirm } = this.props.form.toJS();
        const { handleChange, handleLocalRegister } = this;

        return (
            <AuthContent title="회원가입">
                (...)
                {
                    error && <AuthError>{error}</AuthError>
                }
                <AuthButton onClick={handleLocalRegister}>회원가입</AuthButton>
                <RightAlignedLink to="/auth/login">로그인</RightAlignedLink>
            </AuthContent>
        );
    }
}

export default connect(
    (state) => ({
        form: state.auth.getIn(['register', 'form']),
        error: state.auth.getIn(['register', 'error']),
        exists: state.auth.getIn(['register', 'exists']),
        result: state.auth.get('result')
    }),
    (dispatch) => ({
        AuthActions: bindActionCreators(authActions, dispatch)
    })
)(Register);

아직은 회원 인증정보를 담는 로직을 작성하지 않았기 때문에, 임시로 콘솔로 프린트만 하도록 작성하였습니다. 이렇게 코드를 완성하고 나면 회원가입 시도를 해보세요.

그리고, 개발자 도구의 콘솔을 열어보면 다음과 같이 기록이 될 것입니다:

Object {thumbnail: "/static/images/default_thumbnail.png", username: "tester"}

유저 리덕스 모듈 만들기

이제 user 라는 모듈을 만들어서 유저가 로그인 했다는 상태를 관리하도록 하겠습니다.

회원가입 혹은 로그인을 성공하면, 유저의 정보를 auth 모듈안의 result 안에 담습니다. 그 다음엔, 회원가입/로그인 메소드에서 그 result 값을 SET_LOGGED_INFO 값을 통하여 user 모듈의 loggedInfo 로 복사합니다. 그 과정에서 validate 와 logged 값은 true 로 전환하게 됩니다.

유저가 로그인을 하게 되면, 해당 정보를 로컬스토리지에 담고 페이지를 새로고침 할 때 해당 데이터를 가져와서 SET_LOGGED_INFO 를 호출하여 저장됐던 로그인 정보를 스토어에 넣어줍니다. 그리고 나서, 해당 인증정보가 유효한지 CHECK_STATUS 를 통하여 검증을 하고, 검증이 끝나면 validated 값을 true 로 설정을 해주고, 만약에 검증에 실패하게 된다면, 로그인 정보를 초기화시킵니다.

src/redux/modules/user.js

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

import { Map } from 'immutable';
import * as AuthAPI from 'lib/api/auth';
import { pender } from 'redux-pender';

const SET_LOGGED_INFO = 'user/SET_LOGGED_INFO'; // 로그인 정보 설정
const SET_VALIDATED = 'user/SET_VALIDATED'; // validated 값 설정
const LOGOUT = 'user/LOGOUT'; // 로그아웃
const CHECK_STATUS = 'user/CHECK_STATUS'; // 현재 로그인상태 확인

export const setLoggedInfo = createAction(SET_LOGGED_INFO); // loggedInfo
export const setValidated = createAction(SET_VALIDATED); // validated
export const logout = createAction(LOGOUT, AuthAPI.logout);
export const checkStatus = createAction(CHECK_STATUS, AuthAPI.checkStatus);

const initialState = Map({
    loggedInfo: Map({ // 현재 로그인중인 유저의 정보
        thumbnail: null,
        username: null
    }),
    logged: false, // 현재 로그인중인지 알려준다
    validated: false // 이 값은 현재 로그인중인지 아닌지 한번 서버측에 검증했음을 의미
});

export default handleActions({
    [SET_LOGGED_INFO]: (state, action) => state.set('loggedInfo', Map(action.payload)).set('logged', true),
    [SET_VALIDATED]: (state, action) => state.set('validated', action.payload),
    ...pender({
        type: CHECK_STATUS,
        onSuccess: (state, action) => state.set('loggedInfo', Map(action.payload.data)).set('validated', true), 
        onFailure: (state, action) => initialState
    })
}, initialState);

모듈을 만들고 난 다음엔 모듈 인덱스에서 불러와서 combineReducers 안에 넣으세요.

src/redux/modules/index.js

import { combineReducers } from 'redux';
import base from './base';
import auth from './auth';
import user from './user';

import { penderReducer } from 'redux-pender';

export default combineReducers({
    base,
    auth,
    user,
    pender: penderReducer
});

로컬스토리지 헬퍼 작성하기

로컬스토리지에 데이터를 조금 더 편하게 넣고 조회하기 위해서 헬퍼 모듈을 작성하겠습니다.

src/lib/storage.js

// 로컬 스토리지에 JSON 형태로 저장 / 불러오기 / 삭제 헬퍼
const storage = {
    set: (key, object) => {
        if(!localStorage) return;
        localStorage[key] = (typeof object) === 'string' ? object : JSON.stringify(object);
    },
    get: (key) => {
        if(!localStorage) return null;

        if(!localStorage[key]) {
            return null;
        }

        try {
            const parsed = JSON.parse(localStorage[key]);
            return parsed;
        } catch(e) {
            return localStorage[key];
        }
    },
    remove: (key) => {
        if(!localStorage) return null;

        if(localStorage[key]) {
            localStorage.removeItem(key);
        }
    }
};

export default storage;

이 모듈의 사용법은 다음과 같습니다:

storage.set('foo', 'bar');
storage.set('foobar', { foo: 'bar' });
let foo = storage.get('foo'); // bar
storage.remove('foo');

이 모듈은, 만약에 파라미터가 객체형태로 들어오면 자동으로 JSON.stringify 를 해주고, 일반 문자열이라면 그대로 넣어줍니다. 조회를 할 때도 마찬가지로 객체형태라면 자동으로 파싱을 해줍니다.

유저정보 스토어와 로컬스토리지에 담기

이제 방금 만든 코드들을 사용하여 스토어와 로컬스토리지에 유저 정보를 담아보도록 하겠습니다.

src/containers/Auth/Register.js

(...)
import * as userActions from 'redux/modules/user';
import storage from 'lib/storage';


class Register extends Component {


    (...)

    handleLocalRegister = async () => {
        const { form, AuthActions, UserActions, error, history } = this.props;
        const { email, username, password, passwordConfirm } = form.toJS();

        const { validate } = this;

        if(error) return; // 현재 에러가 있는 상태라면 진행하지 않음
        if(!validate['email'](email) 
            || !validate['username'](username) 
            || !validate['password'](password) 
            || !validate['passwordConfirm'](passwordConfirm)) { 
            // 하나라도 실패하면 진행하지 않음
            return;
        }

        try {
            await AuthActions.localRegister({
                email, username, password
            });
            const loggedInfo = this.props.result.toJS();

            storage.set('loggedInfo', loggedInfo);
            UserActions.setLoggedInfo(loggedInfo);
            UserActions.setValidated(true);
            history.push('/'); // 회원가입 성공시 홈페이지로 이동
        } catch(e) {
            // 에러 처리하기
            if(e.response.status === 409) {
                const { key } = e.response.data;
                const message = key === 'email' ? '이미 존재하는 이메일입니다.' : '이미 존재하는 아이디입니다.';
                return this.setError(message);
            }
            this.setError('알 수 없는 에러가 발생했습니다.')
        }
    }

    (...)
}

export default connect(
    (state) => ({
        form: state.auth.getIn(['register', 'form']),
        error: state.auth.getIn(['register', 'error']),
        exists: state.auth.getIn(['register', 'exists']),
        result: state.auth.get('result')
    }),
    (dispatch) => ({
        AuthActions: bindActionCreators(authActions, dispatch),
        UserActions: bindActionCreators(userActions, dispatch)
    })
)(Register);

이제 회원가입 부분에서 해야할것은 모두 마쳤습니다! 이제 로그인을 구현해볼 차례입니다.

results matching ""

    No results matching ""