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);
이제 회원가입 부분에서 해야할것은 모두 마쳤습니다! 이제 로그인을 구현해볼 차례입니다.