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