회원가입 및 로그인 구현 #4

chu·2021년 4월 9일
0

이번 시간에는 로그인 후 지속적으로 로그인을 유지할 수 있도록
프론트와 백에 코드를 추가하여 마무리할 예정입니다.


Back

routes/user.js

로그인을 유지할 수 있는 라우트는 user로 정해서 진행했습니다.

const express = require('express');
const router = express.Router();
const { User } = require('../models');
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');

// 로그인 사용자 정보 가져오기 (계속 로그인 상태를 만들기 위한)
// GET /user
// isLoggedIn 발견되면 인증 체크 후 다음 미들웨어를 실행시킨다.
router.get('/', isLoggedIn, async (req, res, next) => {
  try {
    // 로그인 인증이 되었다면, req.user에서 유저 정보 확인 가능
    if (req.user) {
      // 로그인 인증된 유저의 id 가져오기
      const user = await User.findOne({
        where: { id: req.user.id }
      });
      // 그 유저의 정보 중 비밀번호를 제외한 정보 가져오기
      const fullUserWithoutPassword = await User.findOne({
        where: { id: user.id },
        attributes: {
          exclude: ['password'], // exclude: 제외한 나머지 정보 가져오기
        },
      });
      res.status(200).json(fullUserWithoutPassword);
    } else {
      res.status(200).json(null);
    }
  } catch(error) {
    console.error(error);
    next(error);
  }
});

isLoggedIn / isNotLoggedIn 뭐지?

로그인을 했는지 체크해주는 미들웨어다.
그 안에는 아래처럼 작성하였다.

// 인증이 되어있는가? true
exports.isLoggedIn = (req, res, next) => {
  if (req.isAuthenticated()) {
    next();
  } else {
    res.status(401).send('로그인이 필요합니다.');
  }
};

// 인증이 되지 않았는가? false
exports.isNotLoggedIn = (req, res, next) => {
  if (!req.isAuthenticated()) {
    next();
  } else {
    res.status(401).send('로그아웃 후 접근이 가능합니다.');
  }
};

여기서 isAuthenticated() 메소드는 서버에 요청을 보낸 사용자가 인증 유무를 boolean 값으로 알려준다.

로그인과 회원가입은 코드도 중요하지만, 어떻게 미들웨어가 흘러가는지도 파악해야한다.

Front

slices/index.ts

import { combineReducers } from 'redux';
import { HYDRATE } from 'next-redux-wrapper';

import userSlice from './user';

const rootReducer = (state: any, action: any) => {
  switch (action.type) {
    case HYDRATE:
      console.log('HYDRATE', action);
      return action.payload;
    default: {
      const combineReducer = combineReducers({
        user: userSlice.reducer,
      });
      return combineReducer(state, action);
    }
  }
};

// 기존 아래 코드를 제거하고, 위 코드를 넣는다.
// const rootReducer = combineReducers({
//   user: userSlice.reducer,
// });

export type RootState = ReturnType<typeof rootReducer>;

export default rootReducer;

여기서는 HYDRATE의 액션을 추가하기 위해 위 처럼 switch문을 사용했다.

HYDRATE란?

서버에서 생성된 상태를 클라이언트로 전달해주는 역할을 한다.
콘솔로 찍어보면, 현재 상태에 대한 내용이 찍혀 나온다.

pages/index.tsx

import React from 'react';
import { useSelector } from 'react-redux';
import axios from 'axios';

import { Container, ProfileWrapper } from '../styles/style';

import LoginForm from '../component/loginForm';
import { RootState } from '../slices';
import { loadUser } from 'actions/user';
import wrapper from 'store/configureStore';
import Link from 'next/Link';

const Home = () => {
  const isLoggedIn = useSelector((state: RootState) => state.user.isLoggedIn);
  const { email, nickname } = useSelector(
    (state: RootState) => state.user.user
  );
  return (
    <Container>
      {isLoggedIn ? (
        <ProfileWrapper>
          <div>
            <h1>
              <strong>{nickname}</strong> 환영합니다.
            </h1>
            <p>{email}</p>
          </div>
          <Link href="/profile">마이페이지</Link>
        </ProfileWrapper>
      ) : (
        <LoginForm />
      )}
    </Container>
  );
};

// SSR은 프론트서버에서 진행되기 때문에 브라우저에서는 개입할 수 없다.
// SSR은 프론트서버에서 백엔드서버로 데이터를 요청하고, 받은 후 브라우저로 데이터와 렌더링을 한번에 보낸다.
// 서버에서 진행이 되기 떄문에 쿠키를 넣어서 직접 보내줘야한다.
export const getServerSideProps = wrapper.getServerSideProps(async context => {
  // back 서버로 쿠키 전달
  // 로그인을 하게되면 context에서 req를 사용 가능
  // 프론트서버에서 쿠키가 공유되는걸 방지하기 위해 if문으로 조건 작업 진행
  console.log('context', context);
  const cookie = context.req ? context.req.headers.cookie : '';
  axios.defaults.headers.Cookie = '';
  if (context.req && cookie) {
    axios.defaults.headers.Cookie = cookie;
  }
  await context.store.dispatch(loadUser());
});

export default Home;

여기서 context는 무엇일까? 콘솔로 찍어보면 많은 내용이 찍혀나오지만,
결과로는 개발자 도구에서 확인할 수 있는 headers에 대한 내용이 있다.

또한 getServerSideProps는 맨 하단에 있는 export default 위에 위치하고 있어야한다.

기존에는 dispatch(loadUser()) 는 컴포넌트 내부에서 진행을 했었지만,
SSR을 하기 위해서는 컴포넌트 내부가 아닌 getServerSideProps에서 호출을 해야한다.

SSR은 브라우저 개입을 못하기 때문에 먼저 실행하여 유저가 로그인을 했는지 파악한 뒤
인증서를 쿠키에 담아지게 되기 때문에 로그인을 한 상태를 새로고침, 페이지 이동을 해도 유지할 수 있다.


이렇게 React , Next.js , Node.js , MySQL 을 사용하여 로그인 및 회원가입을
구현해보았다. 양심상 타입스크립크는 아닌 것 같아서 안했다고 말을 하자...

다음 시간에는 로컬 회원가입이 아닌 SNS로 회원가입에 대해서 정리를 하도록 하겠다.

잘못된 설명이 있다면 피드백 부탁드릴게요 😔

profile
한 걸음 한걸음 / 현재는 알고리즘 공부 중!

0개의 댓글