next.js에서 쿠키로 로그인 유지

채희태·2022년 10월 10일
3

프론트 서버에서 쿠키로 로그인 유지하기

http 통신은 stateless, connectless의 특성을 갖고 있기 때문에 로그인을 한 후 페이지를 이동할 때 마다 쿠키를 서버에 보내 서버에 자신의 정보를 알려주고 다시 서버에서 자신의 정보에 맞는 데이터를 받아와 로그인 해야한다.

로그인을 할 때 쿠키로 사용자의 토큰을 보내주고, 로그아웃 할 때 쿠키를 삭제함으로써 쿠키를 생성 / 삭제하는 것은 어렵지 않게 구현할 수 있었다.
하지만 NEXT.js 환경에서 페이지를 이동할 때 쿠키를 프론트 서버에서 백 서버로 전송하며 로그인을 유지하는데에서 어려움을 겪었다.

로그인해서 브라우저에 쿠키 담기

로그인을 하면 쿠키를 서버에서 생성하여 브라우저로 보내준다.
이를 application cookie탭에서 확인할 수 있다.

router.post("/login", (req, res) => {
  User.findOne({ email: req.body.email }, (err, user) => {
    if (!user) {
      res.status(401).json({ success: false, message: "이메일이 틀림" });
    }
    user.comparePassword(req.body.password, (err, isMatch) => {
      if (!isMatch) {
        res.status(401).json({ success: false, message: "비밀번호가 틀림" });
      }
      user.generateToken((err, user) => {
        res
          .cookie("token", user.token)
          .status(200)
          .json({ success: true, doc: user });
      });
    });
  });
});

res.cookie("쿠키name", "쿠키value")로 쿠키를 생성할 수 있다.
나는 쿠키 name으로 "token"을 주었고, 쿠키 value로 user데이터의 token을 주었다.

application을 확인하면 위와 같이 볼 수 있다.
(이 때, 요청시 withCredentials를 설정하는 것을 꼭 잊지 말자!)

이제, 브라우저는 쿠키를 서버에서 전달 받아 해당 데이터의 토큰을 쿠키 값으로 갖고 있는 상황이다.
이를 이용해서 로그인유지를 구현할 것이다.

쿠키를 이용해 로그인 유지

브라우저에서 쿠키를 서버에 전송하는 것은 그닥 어렵지 않다.
서버에서 cookie-parser와 withCredentials를 사용한 상황에서, 브라우저에서 서버로 자동으로 쿠키를 전송해 주기 때문이다.

하지만 NEXT환경에서는 프론트 서버에서 getServerSideProps로 데이터를 fetch해 렌더링 한 후 브라우저로 해당 파일을 넘겨준다. 즉, 쿠키를 프론트 서버에서 백엔드 서버로 전송해야 한다.

프론트 서버에서는 자동으로 쿠키를 넘겨주지 않기 때문에 따로 설정을 해야한다.

export const getServerSideProps = wrapper.getServerSideProps(
  (store) =>
    async ({ req }) => {
      const cookie = req?.headers.cookie;
      console.log(cookie);
    }
);

req가 존재한다는 것은 곧 현재 프론트 서버라는 뜻이다.
req.headers.cookie를 콘솔로 찍어보면 다음과 같이 쿠키의 name=value 쌍을 확인할 수 있다.
token=eyJhbGciOiJIUzI1NiJ9.NjM0NDQwZGFlMDQzOWNjOGVjOWQzNjc2.hhcg_ApVv7pix9OwjyyZbt4hAmufTjFDsy49RzFzaDQ

export const getServerSideProps = wrapper.getServerSideProps(
  (store) =>
    async ({ req }) => {
      const cookie = req?.headers.cookie;
      if (req && cookie) {
        axios.defaults.headers.Cookie = cookie;
      }
      try {
        const res = await axios("http://localhost:7000/api/user/getUser", {
          withCredentials: true,
        });
        const payload = await res.data.doc;
        store.dispatch({ type: LOG_IN, payload });
      } catch (err) {
        console.log(err);
      }
    }
);

위의 코드 처럼 req.headers.cookie로 브라우저의 쿠키를 프론트 서버로 갖고 와 cookie변수에 담고 이를 axios에 담아 백엔드 서버로 보내준다.

router.get("/getUser", (req, res) => {
  console.log(req.cookies);
  const token = req.cookies.token;
  User.findOne({ token }, (err, doc) => {
    return res.status(200).json({ success: true, doc });
  });
});
{
  token: 'eyJhbGciOiJIUzI1NiJ9.NjM0NDQwNjRhYmE2MmMwMWQ5NDg3OGU5.VyySYxLacGf4fiW2ljdPwghjGhuzJd4t5xJNoDDkdXM'
}

백엔드 서버에서는 req.cookies로 쿠키 값을 불러올 수 있다.

내가 선택한 방식은 쿠키에 담긴 cookie value를 유저의 token 데이터와 비교하여 해당 쿠키를 가진 유저를 찾아내고 그 유저의 데이터를 프론트 서버로 보내주는 것이다.

그럼 프론트 서버에서는 백엔드에서 받은 해당 유저의 데이터를 redux로 보내 유저의 데이터 state를 유지시킨다.

로그아웃으로 쿠키 삭제

로그아웃 버튼을 클릭하면 브라우저에서 쿠키를 서버로 자동으로 보내준다.

router.get("/logout", (req, res) => {
  const token = req.cookies.token;
  User.findOne({ token }, (err, doc) => {
    if (err) return res.json({ success: false, err });
    return res.clearCookie("token").status(200).send({ success: true, doc });
  });
});

서버에서는 쿠키를 받아 쿠키에 담긴 cookie value를 유저의 token 데이터와 비교하여 해당 쿠키를 가진 유저를 찾아내고

res.clearCookie("쿠키name")로 해당 쿠키를 삭제한다.


쿠키가 말끔하게 사라지며 로그아웃 되었다.


참고한 문서 목록

쿠키 전송 (6버전 이후)
https://intrepidgeeks.com/tutorial/how-does-getserversideprops-transfer-cookies-to-the-server
쿠키 전송 (6버전 이전)
https://hhyemi.github.io/2021/05/04/ssrprops.html
axios 관련
https://chaeyoung2.tistory.com/53

profile
기록, 공부, 활용

0개의 댓글