Jieunny์˜ ๋ธ”๋กœ๊ทธ

S3) Unit 7. [์‹ค์Šต] Cookie๋กœ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ ๋ณธ๋ฌธ

CodeStates/Training

S3) Unit 7. [์‹ค์Šต] Cookie๋กœ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ

Jieunny 2023. 3. 7. 15:00

๐Ÿ“ฃ  Cookie ์‚ฌ์šฉํ•ด์„œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ

 

๏ผž Client 

๐Ÿญ. ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค

Auth - Client
    โœ“ ๐Ÿงฉ ์œ ์ € ์ •๋ณด๊ฐ€ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ์‹œ ์—๋Ÿฌ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜ํƒ€๋‚˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. (361 ms)
    โœ“ ๐Ÿงฉ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์„ฑ๊ณต์ ์œผ๋กœ ๋กœ๊ทธ์ธ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (204 ms)
    โœ“ ๐Ÿงฉ ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์„ฑ๊ณต์ ์œผ๋กœ ๋กœ๊ทธ์•„์›ƒ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (144 ms)
    โœ“ ๐Ÿงฉ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์œ ์ €์˜ ์ •๋ณด๋กœ ๋กœ๊ทธ์ธํ•  ์‹œ ์—๋Ÿฌ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜ํƒ€๋‚˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. (66 ms)

 

๐Ÿฎ. ํŒŒ์ผ ๊ตฌ์กฐ

src

ใ„ด  pages

      ใ„ด Login.js : ์„œ๋ฒ„๋กœ ๋กœ๊ทธ์ธ ์š”์ฒญ ๋ณด๋‚ด๊ธฐ

      ใ„ด MyPage.js : ์„œ๋ฒ„๋กœ ๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ ๋ณด๋‚ด๊ธฐ

ใ„ด  App.js : ์ดˆ๊ธฐํ™”๋ฉด ๋ Œ๋”๋ง(Login ํŽ˜์ด์ง€์ธ์ง€ MyPage ์ธ์ง€)

 

๐Ÿฏ. ์ฝ”๋“œ ๊ตฌํ˜„

๐Ÿ“Œ App.js

import './App.css';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Login from './pages/Login';
import Mypage from './pages/Mypage';
import React, { useEffect, useState } from 'react';
import axios from 'axios';

// ๋ชจ๋“  ์š”์ฒญ์— withCredentials๊ฐ€ true๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค.
axios.defaults.withCredentials = true;

function App() {
  const [isLogin, setIsLogin] = useState(false);
  const [userInfo, setUserInfo] = useState(null);

  const authHandler = () => {
  // ์„œ๋ฒ„์˜ userInfo์—์„œ ๋ฐ›์•„์˜จ ์œ ์ € ์ •๋ณด๊ฐ€ ์žˆ์œผ๋ฉด 
  // ์ƒˆ๋กœ ๊ณ ์นจํ•ด๋„ ๋กœ๊ทธ์ธ์ด ์œ ์ง€๋˜๋„๋ก ํ•œ๋‹ค(๋กœ๊ทธ์ธ ์œ ์ง€ ์ฒดํฌํ–ˆ์„ ๋•Œ)
    /*
    TODO: ์ดˆ๊ธฐ ํ™”๋ฉด ๋ Œ๋”๋ง์‹œ, ์„œ๋ฒ„์— ์œ ์ € ์ •๋ณด๋ฅผ ์š”์ฒญํ•˜์—ฌ Login ๋˜๋Š” Mypage๊ฐ€ ๋ Œ๋”๋ง๋˜๋„๋ก ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
    return axios
      .get(์œ ์ €์˜ ์ •๋ณด๋ฅผ ๋‹ด๋‹นํ•˜๋Š” endpoint)
      .then((res) => {
        ์ธ์ฆ์— ์„ฑ๊ณตํ–ˆ๋‹ค๋ฉด ์‘๋‹ต์œผ๋กœ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๊ฐ€ Mypage์— ๋ Œ๋”๋ง๋˜๋„๋ก State๋ฅผ ๋ณ€๊ฒฝํ•˜์„ธ์š”.
      })
      .catch((err) => {
        ์ธ์ฆ์— ์‹คํŒจํ–ˆ๋‹ค๋ฉด ๊ทธ์— ๋Œ€ํ•œ ์—๋Ÿฌ ํ•ธ๋“ค๋ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”. 
      });
    */
      return axios
      .get("http://localhost:4000/userinfo");
      // ์„œ๋ฒ„์˜ userinfo๋กœ ์œ ์ € ์ •๋ณด๋ฅผ ์š”์ฒญํ•œ๋‹ค.
      .then((res) => {
      // ์œ ์ € ์ •๋ณด๋ฅผ ์ž˜ ๋ฐ›์•„์™”์„ ๊ฒฝ์šฐ
        setIsLogin(true)
        // ๋กœ๊ทธ์ธ ์„ฑ๊ณต -> ์ƒˆ๋กœ๊ณ ์นจํ•ด๋„ ๋กœ๊ทธ์•„์›ƒ ๋˜์ง€ ์•Š๋Š”๋‹ค.
        setUserInfo(res.data)
        // ์œ ์ € ์ •๋ณด ๋ฐ”๊ฟ”์ฃผ๊ธฐ
      })
      .catch((err) => {
      // ์œ ์ € ์ •๋ณด๋ฅผ ์ œ๋Œ€๋กœ ๋ฐ›์•„์˜ค์ง€ ๋ชปํ–ˆ์„ ๊ฒฝ์šฐ
        console.log(err.response.data)
      });
  };

  useEffect(() => {
    // ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ ์‹œ ์•„๋ž˜ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
    authHandler();
  }, []);
  
  return(
    ...
    // ๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ์— ์•Œ๋งž์€ props๋ฅผ ๋‚ด๋ ค์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  );
}

 

๐Ÿ“Œ Login.js

import React, { useState } from 'react';
import axios from 'axios';

export default function Login({setUserInfo, setIsLogin}) {
  const [loginInfo, setLoginInfo] = useState({
  // input ์ฐฝ์— ์•„์ด๋””, ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค.
    userId: '',
    password: '',
  });
  const [checkedKeepLogin, setCheckedKeepLogin] = useState(false);
  // '๋กœ๊ทธ์ธ ์œ ์ง€ํ•˜๊ธฐ' ์ฒดํฌ ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
  const [errorMessage, setErrorMessage] = useState('');
  // ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ๋ฐ‘์— ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.
  const handleInputValue = (key) => (e) => {
  // key๋กœ id์™€ password๋ฅผ ๊ตฌ๋ถ„ํ•˜๊ณ , input์ด ๋ณ€ํ•  ๋•Œ ๋งˆ๋‹ค ๋ฐ›์•„์˜จ๋‹ค.
    setLoginInfo({ ...loginInfo, [key]: e.target.value });
  };
  const loginRequestHandler = () => {
    /*
    TODO: Login ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” state๋ฅผ ์ด์šฉํ•ด ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
    ๋กœ๊ทธ์ธ์— ํ•„์š”ํ•œ ์œ ์ €์ •๋ณด๊ฐ€ ์ถฉ๋ถ„ํžˆ ์ œ๊ณต๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด ์—๋Ÿฌ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜ํƒ€๋‚˜๋„๋ก ๊ตฌํ˜„ํ•˜์„ธ์š”.
    return axios
      .post(login์„ ๋‹ด๋‹นํ•˜๋Š” endpoint)
      .then((res) => {
        ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ–ˆ๋‹ค๋ฉด ์‘๋‹ต์œผ๋กœ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๊ฐ€ Mypage์— ๋ Œ๋”๋ง๋˜๋„๋ก State๋ฅผ ๋ณ€๊ฒฝํ•˜์„ธ์š”.
      })
      .catch((err) => {
        ๋กœ๊ทธ์ธ์— ์‹คํŒจํ–ˆ๋‹ค๋ฉด ๊ทธ์— ๋Œ€ํ•œ ์—๋Ÿฌ ํ•ธ๋“ค๋ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”. 
      });
    */
   if(!loginInfo.userId || !loginInfo.password){
   // ์•„์ด๋””๋‚˜ ํŒจ์Šค์›Œ๋“œ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ์ž…๋ ฅ๋˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ
    setErrorMessage('์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.');
    // ์š”์ฒญ ๋ณด๋‚ผ ํ•„์š” ์—†์ด ๋ฐ”๋กœ ๋ฆฌํ„ด(์—๋Ÿฌ๋‹ˆ๊นŒ)
    return;
   }
   return axios
          .post("http://localhost:4000/login", {loginInfo, checkedKeepLogin})
          // ๋กœ๊ทธ์ธ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋ณด๋‚ด์ค€๋‹ค.
          // ์—”๋“œํฌ์ธํŠธ ๋‹ค์Œ ์ธ์ž๋กœ req.body๋ฅผ ๋ณด๋‚ด์ค€๋‹ค -> ์„œ๋ฒ„์˜ login.js์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์–ด์ฃผ๋Š” ๊ฒƒ
          .then((res) => {
          // ์‘๋‹ต์„ ์ž˜ ๋ฐ›์•„์™”์„ ๋•Œ
            setUserInfo(res.data)
            setIsLogin(true)
            console.log(res.data);

            // ๋กœ๊ทธ์ธ์„ ์„ฑ๊ณตํ–ˆ์„ ๊ฒฝ์šฐ ์—๋Ÿฌ๋ฉ”์‹œ์ง€๋ฅผ ์ง€์›Œ์ค€๋‹ค.
            setErrorMessage("")
          })
          .catch((err) => {
          // ์‘๋‹ต์„ ์ œ๋Œ€๋กœ ๋ฐ›์•„์˜ค์ง€ ๋ชปํ–ˆ์„ ๋•Œ
            console.log(err.response.data)
            setErrorMessage("๋กœ๊ทธ์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.")
          })
  };
  
  return (
    ...
  );
}

 

๐Ÿ“Œ MyPage.js

import axios from 'axios';
import React from 'react';

export default function Mypage({ userInfo, setIsLogin ,setUserInfo }) {
// ๋กœ๊ทธ์ธ์ด ์„ฑ๊ณตํ–ˆ์„ ๋•Œ ๋œจ๋Š” ํŽ˜์ด์ง€(๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ์ด ์žˆ๋‹ค)
  const logoutHandler = () => {
  // ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ ์ž‘๋™ํ•  ํ•จ์ˆ˜
    /*
    TODO: Logout ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ์‹œ Login ํŽ˜์ด์ง€๋กœ ๋Œ์•„๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•˜์„ธ์š”. 
    return axios
      .post(logout์„ ๋‹ด๋‹นํ•˜๋Š” endpoint)
      .then((res) => {
        ๋กœ๊ทธ์•„์›ƒ์— ์„ฑ๊ณตํ–ˆ๋‹ค๋ฉด App์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์„ธ์š”.
      })
      .catch((err) => {
        ๋กœ๊ทธ์•„์›ƒ์— ์‹คํŒจํ–ˆ๋‹ค๋ฉด ๊ทธ์— ๋Œ€ํ•œ ์—๋Ÿฌ ํ•ธ๋“ค๋ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”. 
      });
    */
   return axios
          .post("http://localhost:4000/logout")
          // ์„œ๋ฒ„์˜ logout์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๊ฒŒ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ž‘์„ฑํ•ด์ค€๋‹ค.
          .then((res) => {
            setIsLogin(false);
            setUserInfo(null);
          })
          .catch((err) => {
            console.log(err);
          })
  };
  
  return(
    ...
  );
}

 


๏ผž Server

๐Ÿญ. ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค

POST /login

 โœ“ ๐Ÿšฉ db์— ์กด์žฌํ•˜๋Š” ์œ ์ €๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด, ์ƒํƒœ ์ฝ”๋“œ 401์™€ ํ•จ๊ป˜ Not Authorized๋ผ๋Š” ๋ฉ”์„ธ์ง€๊ฐ€ ์‘๋‹ต์— ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
      โœ“ ๐Ÿšฉ ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ–ˆ๋‹ค๋ฉด /userinfo๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
      โœ“ ๐Ÿšฉ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์ผ์‹œ์ ์œผ๋กœ ์œ ์ง€ํ•˜๋Š” ์š”์ฒญ์ด๋ผ๋ฉด Session Cookie๋ฅผ ๋ณด๋‚ด์•ผํ•ฉ๋‹ˆ๋‹ค.
POST /login 
      โœ“ ๐Ÿšฉ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ์š”์ฒญ์ด๋ผ๋ฉด Session Cookie๊ฐ€ ์•„๋‹Œ Persistent Cookie๋ฅผ ๋ณด๋‚ด์•ผํ•ฉ๋‹ˆ๋‹ค.
      Cookie Option
        โœ“ ์ฟ ํ‚ค ์˜ต์…˜์ค‘ Domain ์˜ต์…˜์€ `localhost`๋กœ ์„ค์ •๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
        โœ“ ์ฟ ํ‚ค ์˜ต์…˜์ค‘ Path ์˜ต์…˜์€ `/`๋กœ ์„ค์ •๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
        โœ“ ์ฟ ํ‚ค ์˜ต์…˜์ค‘ HttpOnly ์˜ต์…˜์ด ์„ค์ •๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
        โœ“ ์ฟ ํ‚ค ์˜ต์…˜์ค‘ Secure ์˜ต์…˜์ด ์„ค์ •๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
        โœ“ ์ฟ ํ‚ค ์˜ต์…˜์ค‘ SameSite ์˜ต์…˜์€ `none`์œผ๋กœ ์„ค์ •๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

GET /userinfo
GET /userinfo 401 1.372 ms - 14
    โœ“ ๐Ÿšฉ ์ฟ ํ‚ค์— cookieId๊ฐ€ ์—†๋‹ค๋ฉด, ์ƒํƒœ ์ฝ”๋“œ 401์™€ ํ•จ๊ป˜ Not Authorized๋ผ๋Š” ๋ฉ”์„ธ์ง€๊ฐ€ ์‘๋‹ต์— ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
GET /userinfo 401 0.806 ms - 14
    โœ“ ๐Ÿšฉ ์ฟ ํ‚ค์— ์ €์žฅ๋œ cookieId๊ฐ€ ์œ ์ €์ •๋ณด์™€ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์ƒํƒœ ์ฝ”๋“œ 401์™€ ํ•จ๊ป˜ Not Authorized๋ผ๋Š” ๋ฉ”์„ธ์ง€๊ฐ€ ์‘๋‹ต์— ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
GET /userinfo 200 1.657 ms - 295
    โœ“ ๐Ÿšฉ ์ฟ ํ‚ค์— ์ €์žฅ๋œ cookieId๊ฐ€ ์œ ์ €์ •๋ณด์™€ ์ผ์น˜ํ•œ๋‹ค๋ฉด, ์œ ์ €์ •๋ณด๊ฐ€ ์‘๋‹ต์— ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
GET /userinfo 200 0.819 ms - 295
    โœ“ ๐Ÿšฉ ์‘๋‹ต์— ํฌํ•จ๋œ ์œ ์ €์ •๋ณด์— ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋‹ด๊ฒจ์žˆ์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

POST /logout
POST /logout 205 1.049 ms - 0
    โœ“ ๐Ÿšฉ ๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ ์‹œ 205 ์ƒํƒœ์ฝ”๋“œ๋กœ ์‘๋‹ตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    โœ“ ๐Ÿšฉ ๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ ์‹œ ์ฟ ํ‚ค๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

๐Ÿฎ. ํŒŒ์ผ ๊ตฌ์กฐ

src

ใ„ด  controllers

      ใ„ด users

          ใ„ด login.js : ์ฟ ํ‚ค๋ฅผ ๋ฐœ๊ธ‰ํ•œ๋‹ค.

          ใ„ด logout.js : ์ฟ ํ‚ค๋ฅผ ์‚ญ์ œํ•˜๊ณ , ์ƒํƒœ์ฝ”๋“œ๋กœ 205๋ฅผ ์ „๋‹ฌํ•œ๋‹ค(205 : Reset Content)

          ใ„ด userinfo.js : ์ฟ ํ‚ค๋ฅผ ๊ฒ€์ฆํ•ด์„œ ์œ ์ € ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

      ใ„ด index.js

ใ„ด  controllers

      ใ„ด users

          ใ„ดdata.js : ๋กœ๊ทธ์ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ํŒŒ์ผ

 

๐Ÿฏ. ์ฝ”๋“œ ๊ตฌํ˜„

๐Ÿ“Œ login.js

const { USER_DATA } = require('../../db/data');

//๋กœ๊ทธ์ธ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ด๋ฃจ์–ด์ ธ์•ผ ์ฟ ๊ธฐ๊ฐ€ ๋งŒ๋“ค์–ด์ง„๋‹ค!!

module.exports = (req, res) => {
  const { userId, password } = req.body.loginInfo;
  const { checkedKeepLogin } = req.body;
  const userInfo = {
    ...USER_DATA.filter((user) => user.userId === userId && user.password === password)[0],
    // data.js์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์„œ req.body๋กœ ๋“ค์–ด์˜จ ์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ์™€ ๊ฐ™์€ ์• ๋“ค๋งŒ ๋‚จ๊ธฐ๊ณ  ์žˆ๋‹ค.
    // ์—†์œผ๋ฉฐ ๋นˆ ๊ฐ์ฒด ๋ฆฌํ„ด
  };

  const cookiesOption = {
    domain: 'localhost',
    path: '/',
    // ์œ ํšจ๊ธฐ๊ฐ„์€ ๋กœ๊ทธ์ธ ์ฒดํฌ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆํ…Œ๋‹ˆ ์ผ๋‹จ ํŒจ์Šคํ•ฉ๋‹ˆ๋‹ค.
    // secure๋„ ์ผ๋‹จ ํŒจ์Šคํ•˜๊ณ , sameSite ์˜ต์…˜์„ ์ž‘์„ฑํ•  ๋•Œ ๋‹ค์‹œ ํ™•์ธํ•ฉ์‹œ๋‹ค.
    httpOnly: true,
    
    // sameSite ์˜ต์…˜์˜ ์„ค์ • ๊ฐ’์„ ํ™•์ธํ•˜๋ฉด์„œ ์–ด๋–ค ์˜ต์…˜์„ ์„ค์ •ํ•˜๋ฉด ์ข‹์„์ง€ ์‚ดํŽด๋ด…์‹œ๋‹ค.
      // Lax : Get ์š”์ฒญ๋งŒ ์ฟ ํ‚ค ์ „์†ก. ๊ทธ๋Ÿฐ๋ฐ ํด๋ผ์ด์–ธํŠธ์—์„œ ์•„์ด๋””๋ž‘ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ POST๋กœ ๋ณด๋‚ด์ฃผ๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์ง€๊ธˆ ์‚ฌ์šฉํ•˜๊ธฐ์—” ๋ถ€์ ํ•ฉํ•ด์š”.
      // Strict : same-site๋งŒ ๊ฐ€๋Šฅ. ๊ทธ๋Ÿฐ๋ฐ ํด๋ผ์ด์–ธํŠธ๋Š” http ํ”„๋กœํ† ์ฝœ + 3000๋ฒˆ ํฌํŠธ, ์„œ๋ฒ„๋Š” https ํ”„๋กœํ† ์ฝœ + 4000๋ฒˆ ํฌํŠธ๋กœ same-site๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ์˜ต์…˜๋„ ๋ถ€์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.
      // ๋”ฐ๋ผ์„œ 'none'์ด ๊ฐ€์žฅ ์ ํ•ฉํ•œ ์˜ต์…˜๊ฐ’์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด ์˜ต์…˜์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ์—๋Š”  secure ์˜ต์…˜์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ํ•ด์š”. secure ์˜ต์…˜๋„ ๊ฐ™์ด ์ž‘์„ฑํ•ด์ค์‹œ๋‹ค.
    sameSite: 'none',
    secure: true,
  }

  if(userInfo.id === undefined){
    // ๋กœ๊ทธ์ธ ์‹คํŒจํ•˜๋ฉด(๋กœ๊ทธ์ธ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ) ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ณ 
    // userInfo๋Š” ์ผ์น˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค๋ฉด ๋นˆ ๊ฐ์ฒด๊ฐ€ ๋“ค์–ด์˜ค๋ฏ€๋กœ ๋ถ„๊ธฐ๋ฅผ ๋‚˜๋ˆ ์„œ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.
    res.status(401).send('Not Authorized');
  } else if (checkedKeepLogin === true) {
    // ๋กœ๊ทธ์ธ์„ ์œ ์ง€ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ, cookiesOption์— max-age ๋˜๋Š” expires ์˜ต์…˜์„ ์ถ”๊ฐ€๋กœ ์„ค์ •ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.
  
    // max-age ์˜ต์…˜์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒฝ์šฐ
    cookiesOption.maxAge = 1000 * 60 * 30
    // ๋‹จ์œ„๋Š” ms(๋ฐ€๋ฆฌ์„ธ์ปจ๋“œ === 0.001์ดˆ)์ด๋‹ˆ ์ฃผ์˜ํ•˜์„ธ์š”! -> ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•  ๊ฒฝ์šฐ 30๋ถ„๋™์•ˆ ์ฟ ํ‚ค๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  
    // expires ์˜ต์…˜์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š”, ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
    cookiesOption.expires = new Date(Date.now() + (1000 * 60 * 30) )
    // ์ง€๊ธˆ ์‹œ๊ฐ„ + 30๋ถ„ ํ›„์— ์ฟ ํ‚ค๋ฅผ ์‚ญ์ œํ•œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.
  
    res.cookie('cookieId', userInfo.id, cookiesOption);
    // express ์„œ๋ฒ„์— ์ฟ ํ‚ค ๋ณด๋‚ด๋Š” ๋ฐฉ๋ฒ•(์ฟ ํ‚ค ์ด๋ฆ„, ์ฟ ํ‚ค ๊ฐ’, ์ฟ ํ‚ค ์˜ต์…˜)
  
  } else {
    // ๋กœ๊ทธ์ธ์„ ์œ ์ง€ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์€ ๊ฒฝ์šฐ, max-age ๋˜๋Š” expires ์˜ต์…˜์„ ์ž‘์„ฑํ•˜์ง€ ์•Š์€ ์ƒํƒœ ๊ทธ๋Œ€๋กœ ์ฟ ํ‚ค๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    res.cookie('cookieId', userInfo.id, cookiesOption)
  }

  res.redirect("/userinfo")
  // ์—ฌ๊ธฐ์„œ๋Š” ์ฟ ํ‚ค๋ฅผ ๋ฐœ๊ธ‰ํ•˜๊ธฐ๋งŒ ํ•˜๊ณ  userInfo์—์„œ ํšŒ์›์ •๋ณด๋ฅผ ๋ณด๋‚ด์ฃผ๋ฏ€๋กœ
  // userInfo๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ•ด์ค€๋‹ค
  /*
   * TODO: ๋กœ๊ทธ์ธ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.
   *
   * userInfo์—๋Š” ์š”์ฒญ์˜ ๋ฐ”๋””๋ฅผ ์ด์šฉํ•ด db์—์„œ ์กฐํšŒํ•œ ์œ ์ €์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ˜์†”์—์„œ userInfo๋ฅผ ์ถœ๋ ฅํ•ด๋ณด์„ธ์š”.
   * ์œ ์ €์˜ ์ •๋ณด๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค๋ฉด ํ•ด๋‹น ์œ ์ €๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒƒ์ž„์œผ๋กœ ๋กœ๊ทธ์ธ ์„ฑ๊ณต์— ๋Œ€ํ•œ ์‘๋‹ต์„ ์ „์†กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
   * ๋งŒ์•ฝ undefined๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค๋ฉด ํ•ด๋‹นํ•˜๋Š” ์œ ์ €๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ž„์œผ๋กœ ๋กœ๊ทธ์ธ ์‹คํŒจ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ์ „์†กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
   *
   * ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ์—๋Š” ํด๋ผ์ด์–ธํŠธ์— ์ฟ ํ‚ค๋ฅผ ์ „์†กํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ฟ ํ‚ค์˜ cookieId์—๋Š” userInfo.id๊ฐ€ ๋‹ด๊ฒจ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
   * ํ…Œ์ŠคํŠธ์ผ€์ด์Šค์—์„œ ์š”๊ตฌํ•˜๋Š” ์ฟ ํ‚ค ์˜ต์…˜์„ ๋ชจ๋‘ ์„ค์ •ํ•˜์„ธ์š”.
   * ์˜์†์„ฑ์žˆ๋Š” ์ฟ ํ‚ค๋ฅผ ๋ณด๋‚ด๋ ค๋ฉด max-age ๋˜๋Š” expires ์˜ต์…˜์„ ์„ค์ •ํ•˜์„ธ์š”.
   *
   * ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฐ”๋กœ ์‘๋‹ต์„ ๋ณด๋‚ด์ง€์•Š๊ณ  ์„œ๋ฒ„์˜ /useinfo๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
   * express์˜ res.redirect ๋ฉ”์„œ๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์„œ๋ฒ„์˜ /userinfo๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•˜์„ธ์š”.
   */
};

 

๐Ÿ“Œ userInfo.js

const { USER_DATA } = require('../../db/data');

module.exports = (req, res) => {
// login์—์„œ ์ฟ ํ‚ค๊ฐ€ ์ž˜ ๋งŒ๋“ค์–ด์กŒ๋‹ค๋ฉด, ์ฟ ํ‚ค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ๋„ฃ์–ด ์ค€ userInfo.id๊ฐ€ ๋“ค์–ด์žˆ๋‹ค.
// ์ด ๊ฐ’์„ ์‚ฌ์šฉํ•ด์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „๋‹ฌํ•ด์ค„ ์œ ์ € ์ •๋ณด๋ฅผ ํ•„ํ„ฐ๋งํ•œ๋‹ค.
  const cookieId = req.cookies.cookieId
  const userInfo = {
      ...USER_DATA.filter((user) => user.id === cookieId)[0],
  };
  if (!cookieId || !userInfo.id){
  // ์œ ํšจํ•œ ์•„์ด๋””๊ฐ€ ์•„๋‹๊ฒฝ์šฐ userInfo๊ฐ€ ๋น„์–ด์žˆ์„ ์ˆ˜๋„ ์žˆ๊ณ 
  // ์ฟ ํ‚ค๊ฐ€ ์—†์„ ๋•Œ๋„ userInfo ๊ฐ’์ด ์ œ๋Œ€๋กœ ๋“ค์–ด์˜ค์ง€ ์•Š๋Š”๋‹ค. -> ๋กœ๊ทธ์ธ ์‹คํŒจ
    res.status(401).send('Not Authorized');
  } else {
      // ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ผ์„œ ์‚ญ์ œ ํ›„์— ๋ณด๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
      delete userInfo.password
      res.send(userInfo)
  }
  /*
   * TODO: ์ฟ ํ‚ค ๊ฒ€์ฆ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์œ ์ € ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.
   *
   * ๋กœ๊ทธ์ธ ์‹œ ์„ค์ •ํ•œ ์ฟ ํ‚ค๊ฐ€ ์กด์žฌํ•˜๋Š” ์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
   * ์•„์ง ๋กœ๊ทธ์ธ์„ ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ์ฟ ํ‚ค๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
   * ์ฟ ํ‚ค์— ์œ ์ €์˜ id๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ฝ˜์†”์— req.cookies๋ฅผ ์ถœ๋ ฅํ•ด๋ณด์„ธ์š”.
   */
};

โžฐ console.log(req.cookies)๋กœ ํ™•์ธํ•œ ์ฟ ํ‚ค

 

๐Ÿ“Œ logout.js

module.exports = (req, res) => {
  /*
   * TODO: ๋กœ๊ทธ์•„์›ƒ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.
   *
   * cookie-parser์˜ clearCookie('์ฟ ํ‚ค์˜ ํ‚ค', cookieOption) ๋ฉ”์„œ๋“œ๋กœ ํ•ด๋‹น ํ‚ค๋ฅผ ๊ฐ€์ง„ ์ฟ ํ‚ค๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
   * ๋งŒ์•ฝ res.clearCookie('user', cookieOption) ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค๋ฉด `user=....` ์ฟ ํ‚ค๊ฐ€ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.
   * ๋กœ๊ทธ์•„์›ƒ ์„ฑ๊ณต์— ๋Œ€ํ•œ ์ƒํƒœ ์ฝ”๋“œ๋Š” 205๊ฐ€ ๋˜์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.
   */
  const cookiesOption = {
    domain: 'localhost',
    path: '/',
    httpOnly: true,
    sameSite: 'none',
    secure: true,
  }

  res.status(205).clearCookie('cookieId', cookiesOption).send("logout");
  // ์ฟ ํ‚ค๋ฅผ ์‚ญ์ œํ•  ๋•Œ๋Š” rea.clearCookie๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
};