TIL 57์ผ์ฐจ Cookie / Session ๐Ÿช

10512 ๋‹จ์–ด TILCODESTATESdeepdiveCODESTATES

๋“ค์–ด๊ฐ€๋ฉฐ

๋ณด์•ˆ ์ˆ˜์—… ์ฒซ ๋‚ . Section3์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์ˆ˜์—…์„ ํ•˜๋‚˜ ๊ณ ๋ฅด๋ฉด ๋ˆ„๊ตฌ๋“  ์ด ์ˆ˜์—…์„ ๊ณ ๋ฅธ๋‹ค๊ณ  ํ•  ์ •๋„๋กœ ์ค‘์š”ํ•œ ์ˆ˜์—…์ด๋‹ค. ์ด๋ฒˆ์ฃผ๋Š” ํŠน๋ณ„ ์ฃผ๊ฐ„์œผ๋กœ ๋ถ€๋ฅผ ์ •๋„๋กœ ์ค‘์š”ํ•œ ๋‚ด์šฉ๋“ค์ด ๋ชฐ๋ ค์žˆ๋‹ค. Cookie์™€ Session. ์—”์ง€๋‹ˆ์–ด ์ƒํ™œํ•˜๋ฉด์„œ ์–ด๋ ดํ’‹ํ•œ ๊ฐœ๋…์€ ์•Œ์•˜์ง€๋งŒ ์ง์ ‘ ๋‹ค๋ฃจ๋Š”๊ฑด ์‰ฝ์ง€ ์•Š๊ตฌ๋‚˜. ๋ช…๋ น์–ด๋„ ์–ด์ƒ‰ํ•˜๊ณ  MVC ํŒจํ„ด๋„ ์•„์ง ์‚ด์ง ์–ด์ƒ‰ํ•˜๋‹ค. ๊ทธ ์ƒํƒœ์—์„œ ๋ณด์•ˆ์„ ๋•Œ๋ ค๋„ฃ์œผ๋‹ˆ.. ์™€์šฐ! ํ•˜๋ฃจ ๋ฐฐ์šฐ๊ณ  ๋„˜์–ด๊ฐ„ Sequelize๋„ ํŠ€์–ด๋‚˜์˜จ๋‹ค. ์ด๊ฑด ๋ณต์Šต์„ ์•ˆํ•˜๋ฉด ๋„์ €ํžˆ ๋”ฐ๋ผ๊ฐˆ ์ˆ˜ ์—†๋‹ค.

์Šคํ”„๋ฆฐํŠธ๋„ ์‰ฝ์ง€ ์•Š์•˜๋‹ค. ๋ณ€๋ช…์„ ํ•˜์ž๋ฉด, ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ๋Š์Šจํ•˜๊ฒŒ ์งœ์—ฌ์žˆ์–ด์„œ ์‹ค์ œ๋กœ Header์— Cookie๊ฐ€ ๋“ค์–ด์žˆ์ง€ ์•Š์•„๋„ ๋“ค์–ด์žˆ๋‹ค๊ณ  ํ†ต๊ณผ๊ฐ€ ๋œ๋‹ค. ์ด๊ฑธ ๋ชฐ๋ผ์„œ ํ•œ~์ฐธ์„ ํ•ด๋งธ๋‹ค.
axios ์‚ฌ์šฉ์— ๋Œ€ํ•ด์„œ๋„ ๊ท€๋” ํ•ด์คฌ๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ํ•ด๋งค์ง„ ์•Š์•˜๋‹ค. ์˜ค๋Š˜์€ ํŽ˜์–ด์™€ ๊ฐ™์ด ๋„ˆ๋ฌด ํ•ด๋งค์„œ ์ •์‹ ์ ์œผ๋กœ ์ง€์น˜๋Š” ํ•˜๋ฃจ๋‹ค. ๊ทธ๋ž˜๋„ ํ•ด๋งค๋ฉด์„œ ๋ฐฐ์šด๋‹ค๊ณ .. ์ฐจ๋ผ๋ฆฌ ํ•œ ๋ฒˆ์— ์„ฑ๊ณตํ•˜์ง€ ๋ชปํ•œ๊ฒŒ ์˜คํžˆ๋ ค ๋“์ด ๋˜์ง€ ์•Š์„๊นŒ? ๋‚ด๊ฐ€ ๋งŽ์ด ๋ถ€์กฑํ•˜๊ณ  ๋ณด์ถฉํ•ด์•ผ ๋œ๋‹ค๊ณ  ๋Š๋ผ๋Š” ๊ณ„๊ธฐ๊ฐ€ ๋˜์ง€ ์•Š์„๊นŒ? ์กฐ๊ธˆ์€ ๋‚˜์—๊ฒŒ ์œ„๋กœ๋ฅผ ํ•ด๋ณธ๋‹ค.
์ด๋ฒˆ ํฌ์ŠคํŒ…์€ ์‹ค์Šต ๊ณผ์ •์—์„œ ๋†“์ณค๋˜ ๋ถ€๋ถ„์„ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค. ๋ถ€์กฑํ•œ ๋ถ€๋ถ„์€ ๋ช‡๋ฒˆ์”ฉ ๋ฐ˜๋ณตํ•ด์•ผ๊ฒ ๋‹ค.

์“ฐ๋ฉด์„œ ๊ถ๊ธˆํ•ด์กŒ๋Š”๋ฐ ์—ฌ๋Ÿฌ๊ฐœ์˜ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์žˆ์„ ๋•Œ, ์ „์ฒด ์„ธ์…˜์„ ์กฐํšŒํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ๋ ๊นŒ?

์„ธ์…˜๊ณผ ์ฟ ํ‚ค

์„ธ์…˜ ๋“ฑ๋ก๊ณผ ์ฟ ํ‚ค๋ฅผ ์ „์†กํ•˜๋Š” ๋ถ€๋ถ„์€ ํ—ˆ๋ฌดํ•  ์ •๋„๋กœ ์‰ฝ๋‹ค. sessionId๋Š” userId๋กœ ์ง€์ •ํ•˜๊ณ  ๊ฐ’ ํฌํ•จํ•ด์„œ ๋ณด๋‚ด์ฃผ๋ฉด ๋.

req.session.userId = req.body.userId;
res.status(200).json({ data: userInfo, message: 'ok' }).end();

๋ ˆํผ๋Ÿฐ์Šค ์ฝ”๋“œ๋Š” save() ๋ฉ”์†Œ๋“œ์˜ ์ฝœ๋ฐฑํ•จ์ˆ˜๋กœ ๋“ค์–ด๊ฐ„๋‹ค. ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋Š” HTTP ์‘๋‹ต์ด ๋๋‚  ๋•Œ ์ž๋™์œผ๋กœ ํ˜ธ์ถœ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ ๋ถ€๋ฅผ ํ•„์š”๊ฐ€ ์—†๋‹ค.

This method is automatically called at the end of the HTTP response if the session data has been altered (though this behavior can be altered with various options in the middleware constructor). Because of this, typically this method does not need to be called.
session - expressjs

req.session.save(function () {
        req.session.userId = userInfo.userId;
        res.json({ data: userInfo, message: 'ok' });
      });

์„ธ์…˜ ํŒŒ๊ธฐ

req.session ๋‚ด์˜ ํ‚ค๊ฐ’์„ ์ง€์šฐ๋Š” ๊ฒƒ๋ณด๋‹ค (delete ๋ฉ”์†Œ๋“œ) req.session.destroy() ๋ฉ”์†Œ๋“œ๋กœ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์šธ ์ˆ˜ ์žˆ๋‹ค. ๊ธฐ๋ณธ ๊ฐ’์€ keep()์ด๋ฉฐ ์„ธ์…˜์„ ์œ ์ง€ํ•œ๋‹ค.

req.session.destroy();
res.json({ data: null, message: 'ok' });

๋‹ค๋ฅธ ๋„๋ฉ”์ธ๊ฐ„ ์ฟ ํ‚ค ์ „์†กํ•˜๊ธฐ

Frontend์™€ Backend ๋„๋ฉ”์ธ ์ฃผ์†Œ๊ฐ€ ๋‹ค๋ฅธ ๊ฒฝ์šฐ Cookie ์ „๋‹ฌ์ด ๋˜์ง€ ์•Š๋Š”๋‹ค. ํŠนํžˆ Network ํƒญ์—์„œ Response Header์— Set-Cookie๋Š” ์žˆ๋Š”๋ฐ Application ํƒญ์—์„œ Cookie๊ฐ€ ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค. ํ—ค๋”์˜ ์˜ต์…˜ ์กฐ์ •์œผ๋กœ ์ด๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

client / withCredentials: true

ํ—ค๋”์— withCredentials: true ์˜ต์…˜์œผ๋กœ ๋ณด์•ˆ ํ†ต์‹  ์ค‘ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ(Cross) ์‚ฌ์ด์˜ ์ฟ ํ‚ค ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•œ๋‹ค. ํ˜„์žฌ ์‹ค์Šต ํ™˜๊ฒฝ์€ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„์˜ ๋„๋ฉ”์ธ์ด ๋‹ค๋ฅธ ์ƒํ™ฉ์ด๋‹ค.

XMLHttpRequest.withCredentials ์†์„ฑ์€ ์ฟ ํ‚ค, ๊ถŒํ•œ ๋ถ€์—ฌ ํ—ค๋” ๋˜๋Š” TLS ํด๋ผ์ด์–ธํŠธ ์ธ์ฆ์„œ์™€ ๊ฐ™์€ ์ž๊ฒฉ ์ฆ๋ช…์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์ดํŠธ ๊ฐ„ ์•ก์„ธ์Šค ์ œ์–ด ์š”์ฒญ์„ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ถ€์šธ ๊ฐ’์ž…๋‹ˆ๋‹ค.
withCredentials ์„ค์ •์€ ๋™์ผ ์‚ฌ์ดํŠธ ์š”์ฒญ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
withCredentials๊ฐ€ true๋กœ ์„ค์ •๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ์ž์‹ ์˜ ๋„๋ฉ”์ธ์— ๋Œ€ํ•œ ์ฟ ํ‚ค ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. withCredentials๋ฅผ true๋กœ ์„ค์ •ํ•˜์—ฌ ์–ป์€ ํƒ€์‚ฌ ์ฟ ํ‚ค๋Š” ์—ฌ์ „ํžˆ ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…์„ ๋”ฐ๋ฅด๋ฏ€๋กœ document.cookie ๋˜๋Š” ์‘๋‹ต ํ—ค๋”๋ฅผ ํ†ตํ•ด ์š”์ฒญํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ์—์„œ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
XMLHttpRequest.withCredentials - mozilla

axios
      .post(
        'https://localhost:4000/users/login',
        {
          userId: this.state.username,
          password: this.state.password,
        },
        { 'Content-Type': 'application/json', withCredentials: true } // <------
      )

server / credentials: true

์ด ์˜ต์…˜์„ ๋„๋ฉด ์ฒซ๋ฒˆ์งธ ๋กœ๊ทธ์ธ ์š”์ฒญ์—์„œ ์•„๋ž˜์˜ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ž์ฃผ ๋ณด๋˜ ์—๋Ÿฌ๋‹ค. CORS ์ •์ฑ…์— ์œ„๋ฐ˜๋œ๋‹ค๋Š” ๋‚ด์šฉ์ด๋ฉฐ 'XMLHttpRequest์— ์˜ํ•ด ์‹œ์ž‘๋œ ์š”์ฒญ์˜ ์ž๊ฒฉ ์ฆ๋ช… ๋ชจ๋“œ๋Š” withCredentials ์†์„ฑ์— ์˜ํ•ด ์ œ์–ด๋œ๋‹ค'๊ณ  ํ•œ๋‹ค.

Access to XMLHttpRequest at 'https://localhost:4000/users/login' from origin 'https://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

์•„๋ž˜๋Š” ๊ณต์‹ ๋ฌธ์„œ์—์„œ ์–ธ๊ธ‰ํ•˜๋Š” Access-Control-Allow-Credentials ์˜ต์…˜์— ๋Œ€ํ•œ ์„ค๋ช…์ด๋‹ค. ์š”์•ฝํ•˜๋ฉด ๋‹ค๋ฅธ ๋„๋ฉ”์ธ๊ฐ„ ํ—ค๋”๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

์‘๋‹ตํ—ค๋” Access-Control-Allow-Credentials ๋Š” ์š”์ฒญ์˜ ์ž๊ฒฉ์ฆ๋ช… ๋ชจ๋“œ(Request.credentials)๊ฐ€ "include" ์ผ๋•Œ, ๋ธŒ๋ผ์šฐ์ €๋“ค์ด ์‘๋‹ต์„ ํ”„๋กœํŠธ์—”๋“œ ์ž๋ฐ”์ŠคํŠธ๋ฆฝํŠธ ์ฝ”๋“œ์— ๋…ธ์ถœํ• ์ง€์— ๋Œ€ํ•ด ์•Œ๋ ค์ค๋‹ˆ๋‹ค.
์š”์ฒญ์˜ ์ž๊ฒฉ์ฆ๋ช… ๋ชจ๋“œ๊ฐ€ (Request.credentials)๊ฐ€ "include" ์ผ ๋•Œ, Access-Control-Allow-Credentials ๊ฐ’์ด true ์ผ ๊ฒฝ์šฐ์—๋งŒ ๋ธŒ๋ผ์šฐ์ €๋“ค์€ ํ”„๋กœํŠธ์—”๋“œ ์ž๋ฐ”์ŠคํŠธ๋ฆฝํŠธ์— ์‘๋‹ต์„ ๋…ธ์ถœ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
Access-Control-Allow-Credentials - mozilla

credentials: Configures the Access-Control-Allow-Credentials CORS header. Set to true to pass the header, otherwise it is omitted.
cors - express

server / origin: 'https://localhost:3000' or true

origin์„ *๋กœ ์ ์œผ๋ฉด ์•ˆ๋œ๋‹ค. ์ •ํ™•ํ•˜๊ฒŒ ์ ์–ด์•ผ ์ฟ ํ‚ค๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค. express์—์„  true๋„ ๋ฐ›์•„์ค€๋‹ค.
๋กœ๊ทธ์ธ ์š”์ฒญ์ด ์ฒด์ธ์œผ๋กœ ์—ฐ๊ฒฐ๋˜์–ด์„œ 2๋ฒˆ ์ด๋ฃจ์–ด์ง€๋Š”๋ฐ ๊ทธ ๊ณผ์ •์—์„œ ์ž„์‹œ ํ—ค๋”๊ฐ€ ๋งŒ๋“ค์–ด์ ธ CORS ์ •์ฑ…์— ์œ„๋ฐ˜ํ•œ๋‹ค. ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ๋ณด๋ฉด ์ฒซ Login Post ์š”์ฒญ์€ ๋„˜์–ด๊ฐ€์ง€๋งŒ ์ด์–ด์ง€๋Š” userinfo Get ์š”์ฒญ์€ CORS ์—์„œ ๋ง‰ํžŒ๋‹ค.
๊ทธ ์ด์œ ๋Š” origin ์˜ต์…˜์ด *์ผ ๋•Œ๋Š” 'Access-Control-Allow-Origin' ์˜ต์…˜์ด ์ง€์›๋˜์ง€ ์•Š๋Š”๋‹ค.
Reason: Credential is not supported if the CORS header โ€˜Access-Control-Allow-Originโ€™ is *

app.use(
  cors({
    origin: 'https://localhost:3000', // <-----
    methods: ['GET', 'POST', 'OPTIONS'],
    credentials: true, // <------
  })
);

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ