BioPlayground

🧬
목록으로

세션과 인증 — Express Session

Express에서 세션 기반 인증이 어떻게 작동하는지, 쿠키와의 관계, 세션 스토어 개념을 배웁니다.

중급
|
12
|
검증 완료 (2026-07)
세션express-session인증로그인세션 스토어
진행률0/23 (0%)

세션과 인증 — Express Session

이 토픽을 마치면

세션이 왜 필요한지 이해하고, express-session으로 로그인/로그아웃을 구현할 수 있으며, 세션 스토어의 개념과 보안 설정을 알게 됩니다.


왜 세션이 필요한가

쿠키 토픽에서 "HTTP는 무상태"라고 배웠습니다. 쿠키로 상태를 유지할 수 있지만, 쿠키에는 치명적인 한계가 있습니다.

javascript
// Bad — 사용자 정보를 쿠키에 직접 저장
res.cookie('user', JSON.stringify({ id: 1, role: 'admin' }));

클라이언트(브라우저)가 쿠키를 자유롭게 수정할 수 있습니다. 사용자가 role: 'admin'role: 'superadmin'으로 바꿔서 보내면? 서버는 이를 구분할 수 없습니다.

세션은 이 문제를 해결합니다. 민감한 데이터는 서버에 저장하고, 클라이언트에게는 **식별 키(세션 ID)**만 전달합니다.

text
쿠키 방식:  클라이언트에 "이름=Hoon, 역할=admin" 저장 (위변조 가능)
세션 방식:  클라이언트에 "세션ID=abc123"만 저장 → 서버가 abc123으로 사용자 정보 조회

세션의 작동 원리

text
1. 로그인 요청
   Client → Server: POST /login {username: "Hoon", password: "****"}

2. 서버가 세션 생성
   Server: 세션 저장소에 {userId: 1, role: "admin"} 저장
   Server: 세션 ID = "abc123" 생성
   Server → Client: Set-Cookie: connect.sid=abc123

3. 이후 요청
   Client → Server: GET /dashboard (Cookie: connect.sid=abc123)
   Server: abc123으로 세션 조회 → {userId: 1, role: "admin"} 확인
   Server → Client: 대시보드 데이터

클라이언트는 세션 ID만 알고 있고, 실제 데이터(userId, role)는 서버 메모리나 데이터베이스에 있습니다. 클라이언트가 세션 ID를 위조해도, 서버의 세션 저장소에 해당 ID가 없으면 인증이 실패합니다.


express-session 설치와 설정

bash
npm install express-session
javascript
const express = require('express');
const session = require('express-session');
const app = express();

app.use(express.json());
app.use(session({
  secret: 'my-secret-key-change-this',
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,
    secure: false,      // 프로덕션에서는 true
    maxAge: 3600000      // 1 hour
  }
}));
옵션설명
secret세션 ID를 암호화하는 키. 길고 랜덤하게 설정
resave변경 없어도 매 요청마다 세션 저장할지. false 권장
saveUninitialized빈 세션도 저장할지. false로 하면 로그인 전에는 쿠키 미발행
cookie세션 쿠키 옵션 (httpOnly, secure, maxAge 등)

secret절대 코드에 하드코딩하면 안 됩니다. 환경 변수로 관리합니다:

javascript
secret: process.env.SESSION_SECRET || 'fallback-dev-only'

로그인/로그아웃 구현

javascript
const users = [
  { id: 1, username: 'alice', password: 'pass123', role: 'admin' },
  { id: 2, username: 'bob', password: 'pass456', role: 'user' }
];

// 로그인
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username && u.password === password);

  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // 세션에 사용자 정보 저장
  req.session.userId = user.id;
  req.session.role = user.role;

  res.json({ message: `Welcome, ${user.username}` });
});

// 로그아웃
app.post('/logout', (req, res) => {
  req.session.destroy(err => {
    if (err) {
      return res.status(500).json({ error: 'Logout failed' });
    }
    res.clearCookie('connect.sid');
    res.json({ message: 'Logged out' });
  });
});

req.session은 객체입니다. 어떤 속성이든 자유롭게 저장할 수 있습니다. req.session.destroy()는 서버의 세션 데이터를 삭제합니다.


인증 미들웨어

보호된 라우트에 접근할 때마다 "로그인했는가?"를 확인해야 합니다. 이것을 미들웨어로 만들면 모든 라우트에 재사용할 수 있습니다.

javascript
function requireAuth(req, res, next) {
  if (!req.session.userId) {
    return res.status(401).json({ error: 'Login required' });
  }
  next();
}

function requireAdmin(req, res, next) {
  if (req.session.role !== 'admin') {
    return res.status(403).json({ error: 'Admin access required' });
  }
  next();
}

// 사용
app.get('/profile', requireAuth, (req, res) => {
  res.json({ userId: req.session.userId, role: req.session.role });
});

app.get('/admin/dashboard', requireAuth, requireAdmin, (req, res) => {
  res.json({ message: 'Admin dashboard' });
});

미들웨어를 체이닝해서 "로그인 + 관리자"처럼 복합 조건을 만들 수 있습니다.


세션 스토어 — 메모리 vs 외부 저장소

기본적으로 express-session은 서버 메모리에 세션을 저장합니다. 개발에는 충분하지만 프로덕션에서는 문제가 됩니다.

문제설명
서버 재시작메모리가 초기화되므로 모든 사용자가 로그아웃됨
메모리 누수세션이 쌓이면 서버 메모리가 부족해짐
다중 서버서버 A의 메모리에 있는 세션을 서버 B가 모름

프로덕션에서는 Redis, MongoDB, PostgreSQL 같은 외부 저장소를 사용합니다:

bash
npm install connect-redis redis
javascript
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');

const redisClient = createClient();
redisClient.connect();

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false
}));

Redis에 세션을 저장하면 서버가 재시작되어도 세션이 유지되고, 여러 서버가 같은 Redis를 바라보면 세션을 공유할 수 있습니다.


세션 vs JWT — 언제 무엇을

세션JWT
상태 저장서버 (stateful)클라이언트 (stateless)
스케일링Redis 등 공유 저장소 필요서버 간 공유 불필요
로그아웃destroy() 즉시 무효화토큰 만료까지 유효 (블랙리스트 필요)
보안서버가 데이터 관리토큰 탈취 시 대응 어려움
적합한 사례전통적 웹 앱, 관리자 페이지API 서버, 모바일 앱, 마이크로서비스
text
전통적 웹 앱 (SSR) → 세션이 자연스러움
SPA + API 서버 → JWT가 편리함
모바일 앱 → JWT (쿠키 관리가 복잡)

핵심 정리

개념정리
세션사용자 데이터를 서버에 저장, 클라이언트에는 ID만 전달
세션 ID쿠키로 전달되는 식별 키 (connect.sid)
req.session세션 데이터 읽기/쓰기 객체
destroy()로그아웃 — 세션 데이터 삭제
세션 스토어프로덕션에서는 Redis 등 외부 저장소 사용

세션 기반 인증의 핵심: 민감한 데이터는 서버에, 클라이언트에는 열쇠(세션 ID)만. 이 원칙을 이해하면, 토큰 기반 인증(JWT)과의 차이도 자연스럽게 파악할 수 있습니다.