본문 바로가기
Node.js

익스프레스 웹 서버 만들기 - 미들웨어(session)

by Programmer.Junny 2025. 1. 14.

1. session 미들웨어

1.1 정의

  • 세션(Session)은 사용자별로 상태를 유지하기 위해 사용되는 메커니즘이다.
  • Express.js에서 세션을 관리하기 위해 주로 express-session 패키지를 사용한다.
  • 세션 미들웨어는 클라이언트가 서버에 요청할 때마다 세션 ID를 쿠키에 저장하고, 서버는 이 ID를 통해 사용자 상태를 관리한다.

1.2 필요성

  • 로그인 상태 유지: 사용자가 로그인한 상태를 유지하기 위해.
  • 사용자 데이터 저장: 장바구니 정보, 사용자 설정 등.
  • 보안 관리: CSRF 보호, 인증 등.

1.3 동작 방식

  1. 클라이언트 요청: 클라이언트가 서버에 요청을 보낸다.
  2. 세션 ID 확인: 서버는 요청에 포함된 세션 ID를 확인한다.
    • 세션 ID 존재 시: 해당 세션 데이터를 로드한다.
    • 세션 ID 미존재 시: 새로운 세션을 생성하고 세션 ID를 쿠키에 저장한다.
  3. 세션 데이터 접근 및 수정: req.session 객체를 통해 세션 데이터를 읽고 쓸 수 있다.
  4. 응답 전송: 서버는 응답과 함께 세션 ID를 쿠키에 설정하여 클라이언트로 전송한다.

2. 설치

npm install express-session

3. 사용 예시

const express = require('express');
const session = require('express-session');

const app = express();

// 세션 미들웨어 설정
app.use(session({
  secret: 'yourSecretKey', // 세션 ID를 서명하기 위한 비밀 키
  resave: false,           // 세션을 항상 저장할지 여부
  saveUninitialized: true, // 초기화되지 않은 세션을 저장할지 여부
  cookie: { secure: false } // HTTPS를 사용하는 경우 true로 설정
}));

// 라우트 예시
app.get('/', (req, res) => {
  if (req.session.views) {
    req.session.views++;
    res.send(`총 ${req.session.views}번 방문하셨습니다.`);
  } else {
    req.session.views = 1;
    res.send('처음 방문하셨습니다. 세션이 설정되었습니다.');
  }
});

app.listen(3000, () => console.log('서버가 포트 3000에서 실행 중입니다.'));

app.use(session({ settings }); 로 미들웨어를 설정하며, 각 라우트에서 session에 대한 부분을 설정하거나 받아올 수 있다.

위 코드에서 쿠키를 세팅하였기 때문에 따로 cookieParser를 사용하지 않아도 쿠키가 저장된다.

 

  • secret: 세션 ID를 서명하는 데 사용되는 비밀 키이다. 강력하고 예측 불가능한 문자열을 사용해야 한다.
  • resave: 세션을 수정하지 않아도 매 요청마다 세션을 저장할지 여부를 결정한다.
    • false: 세션이 변경되지 않은 경우 저장하지 않습니다.
    • true: 세션이 변경되지 않아도 저장합니다.
  • saveUninitialized초기화되지 않은 세션을 저장할지 여부를 결정한다.
    • true: 새로운 세션이 변경되지 않아도 저장된다.
    • false: 새로운 세션이 변경될 때만 저장된다.
  • cookie: 세션 쿠키에 대한 옵션을 설정한다.
    • secure: true로 설정하면, HTTPS 연결에서만 쿠키가 전송된다. **개발 환경에서는 false**로 설정하고, **프로덕션 환경에서는 true**로 설정하는 것이 일반적이다.

 

4. session 미들웨어 주요 옵션 및 설정

참고로 기본적으로 메모리 저장소가 사용되지만, 프로덕션 환경에서는 Redis, MongoDB 등 외부 저장소를 사용하는 것이 권장된다.

5. 실무 예시

4.1 사용자 로그인 상태 유지

목적: 사용자가 로그인하면 세션에 사용자 정보를 저장하여, 이후 요청에서 로그인 상태를 유지.

const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');

const app = express();

// 세션 미들웨어 설정
app.use(session({
  secret: 'yourSecretKey',
  resave: false,
  saveUninitialized: false,
  cookie: { secure: false } // HTTPS 사용 시 true
}));

app.use(bodyParser.urlencoded({ extended: true }));

// 로그인 폼 라우트
app.get('/login', (req, res) => {
  res.send(`
    <form method="POST" action="/login">
      <label>사용자 이름: <input type="text" name="username"></label><br>
      <button type="submit">로그인</button>
    </form>
  `);
});

// 로그인 처리 라우트
app.post('/login', (req, res) => {
  const { username } = req.body;
  // 실제로는 데이터베이스 검증 등이 필요합니다.
  req.session.user = { username };
  res.redirect('/dashboard');
});

// 대시보드 라우트 (로그인 상태 확인)
app.get('/dashboard', (req, res) => {
  if (req.session.user) {
    res.send(`환영합니다, ${req.session.user.username}! <a href="/logout">로그아웃</a>`);
  } else {
    res.redirect('/login');
  }
});

// 로그아웃 라우트
app.get('/logout', (req, res) => {
  req.session.destroy(err => {
    if (err) {
      return res.status(500).send('로그아웃 중 에러가 발생했습니다.');
    }
    res.redirect('/login');
  });
});

app.listen(3000, () => console.log('서버가 포트 3000에서 실행 중입니다.'));

4.2 쇼핑 카트 기능

목적: 사용자가 상품을 장바구니에 담을 때, 세션에 장바구니 정보를 저장.

const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');

const app = express();

// 세션 미들웨어 설정
app.use(session({
  secret: 'yourSecretKey',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: false } // HTTPS 사용 시 true
}));

app.use(bodyParser.urlencoded({ extended: true }));

// 상품 목록 라우트
app.get('/products', (req, res) => {
  const products = [
    { id: 1, name: '상품 A', price: 1000 },
    { id: 2, name: '상품 B', price: 2000 },
    { id: 3, name: '상품 C', price: 3000 },
  ];

  let productList = '<h1>상품 목록</h1><ul>';
  products.forEach(product => {
    productList += `
      <li>
        ${product.name} - ${product.price}원
        <form method="POST" action="/add-to-cart">
          <input type="hidden" name="id" value="${product.id}">
          <button type="submit">장바구니에 담기</button>
        </form>
      </li>
    `;
  });
  productList += '</ul><a href="/cart">장바구니 보기</a>';

  res.send(productList);
});

// 장바구니에 상품 추가 라우트
app.post('/add-to-cart', (req, res) => {
  const { id } = req.body;
  const product = { id, name: `상품 ${id}`, price: id * 1000 };

  if (!req.session.cart) {
    req.session.cart = [];
  }

  req.session.cart.push(product);
  res.redirect('/cart');
});

// 장바구니 보기 라우트
app.get('/cart', (req, res) => {
  const cart = req.session.cart || [];
  let cartList = '<h1>장바구니</h1><ul>';

  cart.forEach((item, index) => {
    cartList += `
      <li>
        ${item.name} - ${item.price}원
        <form method="POST" action="/remove-from-cart">
          <input type="hidden" name="index" value="${index}">
          <button type="submit">제거</button>
        </form>
      </li>
    `;
  });

  cartList += '</ul><a href="/products">상품 목록으로 돌아가기</a>';

  res.send(cartList);
});

// 장바구니에서 상품 제거 라우트
app.post('/remove-from-cart', (req, res) => {
  const { index } = req.body;
  if (req.session.cart && req.session.cart[index]) {
    req.session.cart.splice(index, 1);
  }
  res.redirect('/cart');
});

app.listen(3000, () => console.log('서버가 포트 3000에서 실행 중입니다.'));

6. cookie-parser와 express-session

6.1. cookie-parser express-session의 관계

6.1.1. express-session이 쿠키를 처리하는 방식

  • **express-session**은 세션 ID 쿠키에 저장하여 사용자별 세션을 관리한다.
  • **express-session**은 내부적으로 쿠키를 파싱하고 설정하는 기능을 제공한다.
  • 따라서, **express-session**을 사용할 때는 별도로 cookie-parser를 사용할 필요가 없다.
  • 과거 버전의 **express-session**은 **cookie-parser**에 의존하여 쿠키를 파싱했다.
  • 최근 버전에서는 **express-session**이 자체적으로 쿠키를 처리하게 되어, **cookie-parser**의 필요성이 줄어들었다.

6.2. 언제 cookie-parser를 사용해야 하는가?

6.2.1. 다른 쿠키를 파싱할 필요가 있을 때

  • express-session 외에 추가적인 쿠키 파싱하여 사용해야 하는 경우 **cookie-parser**를 사용한다.
  • 예를 들어, 사용자 선호 설정이나 추적 쿠키 등을 처리할 때 유용하다.

6.2.2. 특정 쿠키 옵션 설정이 필요할 때

  • **cookie-parser**는 쿠키의 옵션을 더 세밀하게 설정하거나 암호화할 때 사용될 수 있다.
  • 예시: 서명된 쿠키 암호화된 쿠키를 사용할 때.

6.2.3. 보안 강화가 필요할 때

  • **cookie-parser**를 사용하여 쿠키의 보안 속성(예: httpOnly, secure, sameSite)을 더 세밀하게 관리할 수 있다.

최근댓글

최근글

skin by © 2024 ttuttak