관리 메뉴

거니의 velog

(5) 프론트엔드 프로젝트 : 시작 및 회원 인증 구현 5 본문

React_프론트엔드 프로젝트

(5) 프론트엔드 프로젝트 : 시작 및 회원 인증 구현 5

Unlimited00 2024. 2. 22. 20:24

(4) 회원가입 구현

* 바로 회원가입 기능을 구현해 보자. 오류 처리는 나중에 할 것이므로, 지금은 신경 쓰지 말고 코드를 작성해 보자.

[containers/auth/RegisterForm.js]

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { changeField, initializeForm, register } from '../../modules/auth';
import AuthForm from '../../components/auth/AuthForm';

const RegisterForm = () => {
  const dispatch = useDispatch();
  const { form, auth, authError } = useSelector(({ auth }) => ({
    form: auth.register,
    auth: auth.auth,
    authError: auth.authError,
  }));

  // 인풋 변경 이벤트 핸들러
  const onChange = (e) => {
    const { value, name } = e.target;
    dispatch(
      changeField({
        form: 'register',
        key: name,
        value,
      }),
    );
  };

  // 폼 등록 이벤트 핸들러
  const onSubmit = (e) => {
    e.preventDefault();
    const { username, password, passwordConfirm } = form;
    if (password !== passwordConfirm) {
      // TODO: 오류 처리
      return;
    }
    dispatch(register({ username, password }));
  };

  // 컴포넌트가 처음 렌더링될 때 form을 초기화함
  useEffect(() => {
    dispatch(initializeForm('register'));
  }, [dispatch]);

  // 회원가입 성공 / 실패 처리
  useEffect(() => {
    if (authError) {
      console.log('오류 발생');
      console.log(authError);
      return;
    }
    if (auth) {
      console.log('회원가입 성공');
      console.log(auth);
    }
  }, [auth, authError]);

  return (
    <AuthForm
      type="register"
      form={form}
      onChange={onChange}
      onSubmit={onSubmit}
    />
  );
};

export default RegisterForm;

* 위 코드에서는 onSubmit 이벤트가 발생했을 때 register 함수에 현재 username과 password를 파라미터로 넣어서 액션을 디스패치해 주었다. 그리고 사가에서 API 요청을 처리하고, 이에 대한 결과는 auth/authError를 통해 조회할 수 있다.

* 또한, 결과를 얻었을 때 특정 작업을 하기 위해 useEffect를 사용했다. useEffect에 넣어 준 함수는 auth 값 혹은 authError 값 중에서 무엇이 유효한지에 따라 다른 작업을 한다.

* http://localhost:3000/register 회원가입 페이지에서 아무 계정 정보나 입력하고 회원 가입을 한 번 시도해 보자. 이어서 개발자 도구의 콘솔에 무엇이 찍히는지 확인하고, 버튼을 다시 한 번 눌러 보자.

* 만약 blog-backend 디렉터리에 만들었던 서버가 실행 중이 아니라면 해당 서버를 실행해 주자.

백엔드 서버도 동시에 실행된 상태여야 한다.

* 처음에는 성공하고, 두 번째 요청은 실패로 처리되었는가? (계정 정보는 무엇이든 원하는 값을 넣으면 되는데, 비밀번호와 비밀번호 확인 필드는 서로 일치해야 한다.

회원가입 API 호출

* API가 성공하거나 실패했을 때 콘솔에 결과가 잘 나타나는가?

* 이제 사용자의 상태를 담을 user 라는 리덕스 모듈을 만들어 보자.

[modules/user.js]

import { createAction, handleActions } from 'redux-actions';
import { takeLatest } from 'redux-saga/effects';
import * as authAPI from '../lib/api/auth';
import createRequestSaga, {
  createRequestActionTypes,
} from '../lib/createRequestSaga';

const TEMP_SET_USER = 'user/TEMP_SET_USER'; // 새로고침 이후 임시 로그인 처리
// 회원 정보 확인
const [CHECK, CHECK_SUCCESS, CHECK_FAILURE] =
  createRequestActionTypes('user/CHECK');

export const tempSetUser = createAction(TEMP_SET_USER, (user) => user);
export const check = createAction(CHECK);

const checkSaga = createRequestSaga(CHECK, authAPI.check);
export function* userSaga() {
  yield takeLatest(CHECK, checkSaga);
}

const initialState = {
  user: null,
  checkError: null,
};

export default handleActions(
  {
    [TEMP_SET_USER]: (state, { payload: user }) => ({
      ...state,
      user,
    }),
    [CHECK_SUCCESS]: (state, { payload: user }) => ({
      ...state,
      user,
      checkError: null,
    }),
    [CHECK_FAILURE]: (state, { payload: error }) => ({
      ...state,
      user: null,
      checkError: error,
    }),
  },
  initialState,
);

* 새 모듈을 만들었으니, 루트 리듀서에 표함시켜 주어야 한다.

[modules/index.js]

import { combineReducers } from 'redux';
import { all } from 'redux-saga/effects';
import auth, { authSaga } from './auth';
import loading from './loading';
import user, { userSaga } from './user';

const rootReducer = combineReducers({
  auth,
  loading,
  user,
});

export function* rootSaga() {
  yield all([authSaga(), userSaga()]);
}

export default rootReducer;

* 리덕스 모듈을 다 작성했으면, 회원가입 성공 후 check를 호출하여 현재 사용자가 로그인 상태가 되었는지 확인해 보자.

[containers/auth/RegisterForm.js]

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { changeField, initializeForm, register } from '../../modules/auth';
import AuthForm from '../../components/auth/AuthForm';
import { check } from '../../modules/user';

const RegisterForm = () => {
  const dispatch = useDispatch();
  const { form, auth, authError, user } = useSelector(({ auth, user }) => ({
    form: auth.register,
    auth: auth.auth,
    authError: auth.authError,
    user: user.user,
  }));

  // 인풋 변경 이벤트 핸들러
  const onChange = (e) => {
    const { value, name } = e.target;
    dispatch(
      changeField({
        form: 'register',
        key: name,
        value,
      }),
    );
  };

  // 폼 등록 이벤트 핸들러
  const onSubmit = (e) => {
    e.preventDefault();
    const { username, password, passwordConfirm } = form;
    if (password !== passwordConfirm) {
      // TODO: 오류 처리
      return;
    }
    dispatch(register({ username, password }));
  };

  // 컴포넌트가 처음 렌더링될 때 form을 초기화함
  useEffect(() => {
    dispatch(initializeForm('register'));
  }, [dispatch]);

  // 회원가입 성공 / 실패 처리
  useEffect(() => {
    if (authError) {
      console.log('오류 발생');
      console.log(authError);
      return;
    }
    if (auth) {
      console.log('회원가입 성공');
      console.log(auth);
      dispatch(check());
    }
  }, [auth, authError, dispatch]);

  // user 값이 잘 설정되었는지 확인
  useEffect(() => {
    if (user) {
      console.log('check API 성공');
      console.log(user);
    }
  }, [user]);

  return (
    <AuthForm
      type="register"
      form={form}
      onChange={onChange}
      onSubmit={onSubmit}
    />
  );
};

export default RegisterForm;

* 이제 다시 회원가입을 시도해 보자. 회원가입에 성공한 뒤에는 리덕스 개발자 도구를 열어서 user 안에 값이 잘 들어가 있는지 확인하자.

회원가입 성공 후 user 상태

* 회원가입에 성공했다면 홈 화면으로 라우트를 이동시켜 보자. RegisterForm 에서 history 객체를 사용하려면 withRouter 로 컴포넌트를 감싸주면 된다.

[containers/auth/RegisterForm.js]

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { changeField, initializeForm, register } from '../../modules/auth';
import AuthForm from '../../components/auth/AuthForm';
import { check } from '../../modules/user';
import { withRouter } from 'react-router-dom';

const RegisterForm = ({ history }) => {
  (...)

  // user 값이 잘 설정되었는지 확인
  useEffect(() => {
    if (user) {
      console.log('check API 성공');
      console.log(user);
      history.push('/'); // 홈 화면으로 이동
    }
  }, [user, history]);

  return (...);
};

export default withRouter(RegisterForm);

* 여기까지 구현했는가? 이제 회원가입에 성공하면 http://localhost:3000/ 으로 이동할 것이다. 잘 이동되는지 확인해 보자.

회원가입을 하면?
루트 경로로 잘 이동한다.