본문 바로가기
내일배움 강의/강의- Node.js 입문, 숙련

Node.js 숙련주차 3.5 쿠키(Cookie)와 세션(Session)

by GREEN나무 2024. 12. 1.
728x90


01. 쿠키와 세션

1) 쿠키와 세션이란?

◆ 쿠키(Cookie): 브라우저가 서버로부터 응답으로 Set-Cookie 헤더를 받은 경우 해당 데이터를 저장한 뒤 모든 요청에 포함하여 보냅니다.

쿠키는 사용자가 naver.com과 같은 웹 사이트를 방문할 때마다 이전에 방문했던 정보를 기억하는 데이터 파일입니다.
▶ 데이터를 여러 사이트에 공유할 수 있기 때문에 보안에 취약할 수 있습니다.
▶ 쿠키는 userId=user-1321;userName=sparta 와 같이 문자열 형식으로 존재하며 쿠키 간에는 세미콜론(;) 으로 구분됩니다.

세션(Session): 쿠키를 기반으로 구성된 기술입니다. 단, 클라이언트가 마음대로 데이터를 확인 할 수 있던 쿠키와는 다르게 세션은 데이터를 서버에만 저장합니다.

▶ 세션은 일반적으로 세션 Id를 쿠키를 이용해 클라이언트에게 전달하여, 서버는 이 세션 Id를 사용해 저장된 세션 데이터를 조회합니다.
▶ 세션을 통해 사용자의 상태 정보를 서버에 저장하면, 서버는 사용자의 상태를 추적할 수 있게 됩니다.
보안성은 좋으나, 반대로 사용자가 많은 경우 서버에 저장해야 할 데이터가 많아져서 서버 컴퓨터가 감당하지 못하는 문제가 생기기 쉽습니다.
▶ 쿠키와 마찬가지로 세션 역시 만료 기간이 있습니다.

2) 쿠키(Cookie) 만들어보기

쿠키의 동작 방식. 출처: https://blog.naver.com/suin2_91

◆ 쿠키가 없는 경우 :

쿠키없음  → 서버에 요청( Set-Cookie) : 일반적으로 쿠키를 발하는 조건은 로그인   → 로그인성공 시 서버에서 클라이언트에게 쿠키 발급해줌(Response)

 

◆ 쿠키가 있는 경우 :

클라이언트가 서버에 Request를 보낼 때 마다 쿠키정보를 가진 채로 계석 서버로 전달 → 서버는 쿠키를 가자고 있을 때에만 수행할 수 있는 여러가지 결과를 return합니다.

 

서버가 클라이언트의 HTTP 요청(Request)을 수신할 때, 서버는 응답(Response)과 함께 Set-Cookie 라는 헤더를 함께 전송할 수 있습니다. 그 후 쿠키는 해당 서버에 의해 만들어진 응답(Response)과 함께 Cookie HTTP 헤더안에 포함되어 전달받습니다.

 

쿠키-세션 템플릿, app.js

// app.js

import express from 'express';

const app = express();
const PORT = 5001;

app.use(express.json());

app.listen(PORT, () => {
  console.log(PORT, '포트로 서버가 열렸어요!');
});

 

터미널에서 초기화

yarn init -y

package.json에 추가

"type": "module",

express 의존성 설치

yarn add express

 

node app.js (실행하기)

 

Set-Cookie 를 이용하여 쿠키 할당하기 (클라이언트에게 쿠키가 없을 때 쿠키 발행하기)

// 'Set-Cookie'를 이용하여 쿠키를 할당하는 API
app.get("/set-cookie", (req, res) => {
  let expire = new Date();
  expire.setMinutes(expire.getMinutes() + 60); // 만료 시간을 60분으로 설정합니다.

  res.writeHead(200, { // 상태코드 200번
    'Set-Cookie': `name=sparta; Expires=${expire.toGMTString()}; HttpOnly; Path=/`,
  });
  return res.end();
});

http://localhost:5001/set-cookie

개발자도구(F12) → Application → Storage → Cookies → localhost:5001

Max-Age가 한시간으로 설정됨.


res.cookie()를 이용하여 쿠키 할당하기

// 'res.cookie()'를 이용하여 쿠키를 할당하는 API
app.get("/set-cookie", (req, res) => {
  let expires = new Date();
  expires.setMinutes(expires.getMinutes() + 60); // 만료 시간을 60분으로 설정합니다.

  res.cookie('name', 'sparta', {
    expires: expires
  });
  return res.end();
});

“name”이란, 이름을 가지고, “sparta”라는 데이터를 가진 쿠키가 생성되었습니다!

 

api url이 동일하면 호출 시 404(페이지 없음)오류가 뜹니다. 주소를 다르게 만듭시다.

app.js 코드

더보기
// app.js

import express from "express";

const app = express();
const PORT = 5001;

app.use(express.json());

app.listen(PORT, () => {
    console.log(PORT, "포트로 서버가 열렸어요!");
});

/*
// 'Set-Cookie'를 이용하여 쿠키를 할당하는 API
app.get("/set-cookie", (req, res) => {
    let expire = new Date();
    // 만료 시간을 60분으로 설정합니다.
    // 1이 1분
    expire.setMinutes(expire.getMinutes() + 60);

    // 상태넘버 200
    res.writeHead(200, {
        // 쿠키 이름이 sparta
        // 시간제한
        "Set-Cookie": `name=sparta; Expires=${expire.toGMTString()}; HttpOnly; Path=/`,
    });
    return res.end();
});
*/

// 'res.cookie()'를 이용하여 쿠키를 할당하는 API
app.get("/set-cookie1", (req, res) => {
    let expires = new Date();
    expires.setMinutes(expires.getMinutes() + 60); // 만료 시간을 60분으로 설정합니다.

    // name이라는 key에 "sparta" 사용
    // 만료시간은 위에서 정의한 expires변수값(60분)
    // 을 반려함
    res.cookie("name", "sparta1", {
        expires: expires,
    });
    return res.end();
});

3) req를 이용하여 쿠키 접근하기

클라이언트

클라이언트는 서버에 요청(Request)을 보낼 때 자신이 보유하고 있는 쿠키를 자동으로 서버에 전달하게 됩니다. 여기서 클라이언트가 전달하는 쿠키 정보는 Request header에 포함되어 서버에 전달되게 됩니다.


서버
일반적으로 쿠키는 req.headers.cookie에 들어있습니다. req.headers는 클라이언트가 요청한 Request의 헤더(header)를 의미합니다. Reqeust의 헤더는 수 많은 정보를 담고있습니다.
헤더는 요청을 보낸 클라이언트에 관한 상세한 정보를 담고있습니다.
이 정보에는 사용자의 브라우저 유형, 운영 체제, 데스크탑이나 모바일의 사용 유무 등이 포함됩니다.

req.headers.cookie를 이용하여 모든 쿠키 조회하기

// 'req.headers.cookie'를 이용하여 클라이언트의 모든 쿠키를 조회하는 API
app.get('/get-cookie', (req, res) => {
  const cookie = req.headers.cookie;
  console.log(cookie); // name=sparta
  return res.status(200).json({ cookie });
});

4) cookie-parser 미들웨어 적용하기

cookie-parser 미들웨어는 요청에 추가된 쿠키를 req.cookies 객체로 만들어 줍니다. 더이상 req.headers.cookie와 같이 번거롭게 사용하지 않아도 되겠죠
이전에는 req.headers.cookie와 같이 여러 프로퍼티를 넘어서야만 쿠키를 사용할 수 있었습니다. 하지만,  cookie-parser 미들웨어를 이용하면 더욱 간편하게 쿠키를 관리할 수 있습니다.


cookie-parser 설치하기

# yarn을 이용해 cookie-parser를 설치합니다.
yarn add cookie-parser

​cookie-parser는 단순히 쿠키를 req.cookies로 변환해주는 것 외에도, 더욱 쿠키를 손쉽게 사용할 수 있도록 여러 기능을 제공하는 라이브러리 입니다. 
npm 문서 : https://www.npmjs.com/package/cookie-parser

 

cookie-parser 미들웨어를 전역으로 사용하려면 다음과 같이 사용합니다.

// app.js에 추가
import cookieParser from "cookie-parser";
app.use(cookieParser());


​cookie-parser 등록하기, app.js

import cookieParser from 'cookie-parser';

app.use(cookieParser());

// 'req.cookies'를 이용하여 클라이언트의 모든 쿠키를 조회하는 API
app.get('/get-cookie', (req, res) => {
  const cookies = req.cookies;
  console.log(cookies);
  return res.status(200).json({ cookie: cookies });
});


쿠키 조회 부분을 req.cookies로 변경하였고, 쿠키의 형태가 name=sparta에서 { name: 'sparta' } 형태의 객체로 변환된 것을 확인할 수 있습니다. 쿠키를 사용하기 좀 더 쉬워졌습니다.

app.js 코드

더보기
// app.js

import express from "express";
import cookieParser from "cookie-parser";

const app = express();
const PORT = 5001;

app.use(express.json());
app.use(cookieParser());

/*
// 'Set-Cookie'를 이용하여 쿠키를 할당하는 API
app.get("/set-cookie", (req, res) => {
    let expire = new Date();
    // 만료 시간을 60분으로 설정합니다.
    // 1이 1분
    expire.setMinutes(expire.getMinutes() + 60);

    // 상태넘버 200
    res.writeHead(200, {
        // 쿠키 이름이 sparta
        // 시간제한
        "Set-Cookie": `name=sparta; Expires=${expire.toGMTString()}; HttpOnly; Path=/`,
    });
    return res.end();
});
*/

// 'res.cookie()'를 이용하여 쿠키를 할당하는 API
app.get("/set-cookie", (req, res) => {
    let expires = new Date();
    expires.setMinutes(expires.getMinutes() + 60); // 만료 시간을 60분으로 설정합니다.

    // name이라는 key에 "sparta" 사용
    // 만료시간은 위에서 정의한 expires변수값(60분)
    // 을 반려함
    res.cookie("name", "sparta1", {
        expires: expires,
    });
    return res.end();
});

/*
// 'req.headers.cookie'를 이용하여 클라이언트의 모든 쿠키를 조회하는 API
app.get("/get-cookie", (req, res) => {
    const cookie = req.headers.cookie;
    console.log(cookie); // name=sparta
    return res.status(200).json({ cookie });
});
*/

// 'req.cookies'를 이용하여 클라이언트의 모든 쿠키를 조회하는 API
app.get("/get-cookie", (req, res) => {
    const cookies = req.cookies;
    console.log(cookies);
    return res.status(200).json({ cookie: cookies });
});

app.listen(PORT, () => {
    console.log(PORT, "포트로 서버가 열렸어요!");
});

5) 세션(Session) 만들어보기

Cookie-Session 흐름 : https://www.wisecleaner.com/think-tank

 

쿠키의 경우 서버를 재시작하거나 새로고침을 하더라도 로그인이 유지됩니다. 이는 사용자에게는 편리하지만, 서버의 입장에서는 보안 문제가 발생할 수 있습니다. 왜냐하면 쿠키가 조작되거나 노출된다면 해당 권한을 탈취당해, 악의적인 공격을 받을 수 있게 되는 것이죠.
쿠키에는 사용자가 누구인지 확실하게 구분할 수 있는 정보를 넣어주어야 합니다. 그래야 민감한 정보는 서버에서만 관리하고, 사용자 식별 정보를 통해 사용자의 정보를 반환할 수 있게 되겠죠. 

 

세션도 쿠키와 비슷합니다.

세션은 쿠키에 있는 데이를 바탕으로 실제 해당하는 비지니스 로직을 처리하는 건 동일하나

실제 가지고 있는 데이터의 중요한 데이터를 클라이언트에게 전달하는 것이 아니라 서버에만 가지고 있습니다.

즉, 세션을 사용하게 되면 사용자에게는 단순하게 세션을 열 수 있는 키를 줄 뿐이고 

실제 중요한 내용(자물쇠 같은 것)은 서버에서 관리합니다.


▶ /set-session API를 호출했을 때 name=sparta 의 정보를 서버에 저장하고, 저장한 시간 정보를 쿠키로 반환받는 API
▶ /get-session API를 호출했을 때 쿠키의 시간 정보를 이용하여 서버에 저장된 name 정보(🔒)를 출력하는 API
→ 이렇게 구현한다면, 해당 시간을 기준으로 사용자가 누구인지 확실하게 구분할 수 있겠죠? 

 

/set-session API 만들기

let session = {};
app.get('/set-session', function (req, res, next) {
  // 현재는 sparta라는 이름으로 저장하지만, 나중에는 복잡한 사용자의 정보로 변경될 수 있습니다.
  const name = 'sparta';
  const uniqueInt = Date.now();
  // 세션에 사용자의 시간 정보 저장
  session[uniqueInt] = { name };

  res.cookie('sessionKey', uniqueInt);
  return res.status(200).end();
});

서버에서 해당 사용자의 정보를 저장하기 위해 session 객체를 생성합니다.
/set-session API가 호출되면 name=sparta의 정보를 세션에 삽입하고, 해당하는 데이터를 검색하기 위한 시간 정보를 쿠키로 반환합니다.



/get-session API 만들기

app.get('/get-session', function (req, res, next) {
  const { sessionKey } = req.cookies;
  // 클라이언트의 쿠키에 저장된 세션키로 서버의 세션 정보를 조회합니다.
  const name = session[sessionKey];
  return res.status(200).json({ name });
});

쿠키에 저장된 sessionKey를 이용하여 session에 저장된 데이터를 불러옵니다.

 


세션 API 전체 코드, app.js

// app.js
import express from "express";
import cookieParser from "cookie-parser";

const app = express();
const PORT = 5001;

app.use(express.json());
app.use(cookieParser());

// 'req.cookies'를 이용하여 클라이언트의 모든 쿠키를 조회하는 API
app.get("/get-cookie", (req, res) => {
    const cookies = req.cookies;
    console.log(cookies);
    return res.status(200).json({ cookie: cookies });
});

let session = {};
app.get("/set-session", function (req, res, next) {
    // 현재는 sparta라는 이름으로 저장하지만, 나중에는 복잡한 사용자의 정보로 변경될 수 있습니다.
    const name = "sparta";
    const uniqueInt = Date.now();
    // 세션에 사용자의 시간 정보 저장
    session[uniqueInt] = { name };

    res.cookie("sessionKey", uniqueInt);
    return res.status(200).end();
});

app.get("/get-session", function (req, res, next) {
    const { sessionKey } = req.cookies;
    // 클라이언트의 쿠키에 저장된 세션키로 서버의 세션 정보를 조회합니다.
    const name = session[sessionKey];
    return res.status(200).json({ name });
});

app.listen(PORT, () => {
    console.log(PORT, "포트로 서버가 열렸어요!");
});

 

http://localhost:5001/get-cookie

 

http://localhost:5001/get-session


6) 연습문제

요구사항

1. cookie-parser 미들웨어를 적용하세요
2 . GET Method로 http://localhost:5001/set을 호출했을 때, name이라는 이름을 가진 “nodejs” 문자열을 저장한 쿠키를 반환하세요.
3 . GET Method로 http://localhost:5001/get을 호출했을 때, 클라이언트에게 전달받은 모든 쿠키 정보들이 반환되는 API를 만드세요


답안 확인하기

더보기
import express from 'express';
import cookieParser from 'cookie-parser';

const app = express();
const PORT = 5001;

app.use(express.json());
app.use(cookieParser());

app.get("/set", (req, res) => {
  res.cookie('name', 'nodejs');
  return res.status(200).end();
});

app.get("/get", (req, res) => {
  const cookies = req.cookies;
  return res.status(200).json({ cookie: cookies });
});

app.listen(PORT, () => {
  console.log(PORT, '포트로 서버가 열렸어요!');
});

 

import express from "express";
// 1. cookie-parser 미들웨어를 적용하세요
import cookieParser from "cookie-parser";

const app = express();
const PORT = 5002;

app.use(express.json());
app.use(cookieParser());

// cookey
// 2. GET Method로 http://localhost:5001/set1 호출 시 name이라는 이름의 “nodejs_c” 문자열을 저장한 쿠키를 반환
app.get("/set1", (req, res) => {
    res.cookie("name", "nodejs_c");
    return res.status(200).end();
});

// 3. GET Method로 http://localhost:5001/get1 호출 시 모든 쿠키 정보를 반환
app.get("/get1", (req, res) => {
    const cookies = req.cookies;
    return res.status(200).json({ cookies });
});

// session
let session = {};

// 2. GET Method로 http://localhost:5001/set2 호출 시 name이라는 이름의 “nodejs_s” 문자열을 저장한 세션 키를 반환
app.get("/set2", (req, res) => {
    const name = "nodejs_s";
    const uniqueInt = Date.now();
    session[uniqueInt] = { name };

    res.cookie("sessionKey", uniqueInt);
    return res.status(200).end();
});

// 3. GET Method로 http://localhost:5001/get2 호출 시 클라이언트로부터 전달받은 sessionKey 쿠키 값을 기반으로 세션 정보를 반환
app.get("/get2", (req, res) => {
    const sessionKey = req.cookies.sessionKey;

    if (!sessionKey || !session[sessionKey]) {
        return res.status(404).json({ error: "Session not found" });
    }

    const name = session[sessionKey];
    return res.status(200).json({ name });
});

app.listen(PORT, () => {
    console.log(`${PORT} 포트로 서버가 열렸습니다.`);
});