기본 개념

  • JWT (JSON Web Token)
    JWT는 당사자 간 정보를 안전하게 JSON 객체로 전송하기 위한 컴팩트하고 독립적인 방식이다. 이 정보는 디지털 서명되어 있어 신뢰할 수 있다. JWT는 HMAC 알고리즘이나 RSA/ECDSA와 같은 공개/개인 키 쌍을 사용하여 서명할 수 있다.

  • 쿠키 기반 인증
    쿠키 기반 인증은 사용자가 로그인할 때 서버가 세션 ID를 생성하고, 이 세션 ID를 쿠키에 저장하여 클라이언트에게 전송하는 방식이다. 모든 후속 요청에서 클라이언트는 이 쿠키를 서버에 보내고, 서버는 세션 ID를 확인하여 사용자를 인증한다.

작동 원리

  • JWT 작동 원리
    1. 사용자가 로그인하면 서버는 JWT를 생성한다.
    2. JWT는 헤더(알고리즘 및 토큰 타입), 페이로드(클레임), 서명으로 구성된다.
    3. 서버는 이 토큰을 클라이언트에게 반환한다.
    4. 클라이언트는 이 토큰을 로컬 스토리지나 쿠키에 저장한다.
    5. 이후 요청 시 클라이언트는 Authorization 헤더에 이 토큰을 포함시킨다.
    6. 서버는 토큰을 검증하고 요청을 처리한다.
  • 쿠키 기반 인증 작동 원리
    1. 사용자가 로그인하면 서버는 세션 ID를 생성한다.
    2. 서버는 이 세션 ID를 서버 측 데이터베이스(세션 저장소)에 저장한다.
    3. 서버는 이 세션 ID를 쿠키에 담아 클라이언트에게 전송한다.
    4. 클라이언트는 이 쿠키를 브라우저에 저장한다.
    5. 이후 요청 시 브라우저는 자동으로 이 쿠키를 요청에 포함시킨다.
    6. 서버는 쿠키 내 세션 ID를 확인하여 사용자를 인증한다.

주요 차이점

  • 상태 관리
    • JWT: 상태 비저장(Stateless) 방식으로, 서버는 클라이언트 상태를 저장하지 않는다.
    • 쿠키 기반: 상태 저장(Stateful) 방식으로, 서버는 세션 데이터를 저장한다.
  • 데이터 저장 위치
    • JWT: 클라이언트 측에 모든 필요한 정보를 저장한다.
    • 쿠키 기반: 세션 ID만 클라이언트에 저장하고, 실제 데이터는 서버에 저장한다.
  • 확장성
    • JWT: 분산 시스템에서 확장이 용이하다.
    • 쿠키 기반: 세션 정보를 공유하기 위한 추가 메커니즘이 필요하다.
  • 보안
    • JWT: 토큰 자체에 정보가 포함되어 있어, 토큰 탈취 시 정보 노출 위험이 있다.
    • 쿠키 기반: 세션 ID만 노출되며, 실제 데이터는 서버에 안전하게 보관된다.

장단점

JWT

  • 장점
    • 서버 부하 감소: 세션 저장소가 필요 없다.
    • 확장성: 분산 시스템에서 효율적이다.
    • 교차 도메인 지원: 다양한 도메인 간 인증이 용이하다.
    • 모바일 애플리케이션 지원: 네이티브 앱에서 사용하기 좋다.
  • 단점
    • 토큰 크기: 많은 정보를 담을수록 토큰 크기가 커진다.
    • 보안 위험: 클라이언트에 저장된 정보는 탈취될 수 있다.
    • 토큰 무효화: 발급된 토큰을 무효화하기 어렵다.
    • 갱신 메커니즘: 토큰 만료 시 갱신 프로세스가 필요하다.
  • 장점
    • 즉각적인 무효화: 세션을 언제든지 서버에서 삭제할 수 있다.
    • 데이터 보안: 민감한 정보는 서버에만 저장된다.
    • 메모리 효율성: 클라이언트는 작은 세션 ID만 저장한다.
    • 익숙한 구현: 많은 웹 프레임워크가 기본적으로 지원한다.
  • 단점
    • 서버 부하: 각 사용자 세션을 서버에 저장해야 한다.
    • 확장성 문제: 여러 서버 간 세션 공유 메커니즘이 필요하다.
    • CSRF 취약점: 적절한 보호가 없으면 CSRF 공격에 취약하다.
    • 쿠키 제한: 일부 환경에서는 쿠키 사용이 제한될 수 있다.

적합한 사용 시나리오

  • JWT에 적합한 시나리오
    • 마이크로서비스 아키텍처
    • 모바일 애플리케이션
    • 서버리스 환경
    • 단기 인증이 필요한 경우
    • 분산 시스템
  • 쿠키 기반에 적합한 시나리오
    • 전통적인 웹 애플리케이션
    • 높은 보안이 필요한 환경
    • 사용자 세션을 빠르게 무효화해야 하는 경우
    • 사용자 데이터가 자주 변경되는 경우
    • 단일 서버 환경
특성JWT쿠키 기반 인증
상태 관리Stateless (상태 비저장)Stateful (상태 저장)
데이터 저장 위치클라이언트 측서버 측 (세션 ID만 클라이언트)
확장성높음낮음 (세션 공유 필요)
서버 부하낮음높음
토큰/세션 크기클 수 있음 (데이터에 따라)작음 (세션 ID만)
보안중간 (토큰 탈취 위험)높음 (데이터는 서버에만)
즉시 무효화 가능성어려움쉬움
CSRF 취약성낮음 (헤더 사용 시)높음 (보호 필요)
구현 복잡성중간낮음 (프레임워크 지원)
모바일 지원우수함제한적
교차 도메인 지원우수함제한적 (CORS 설정 필요)
만료 처리내장 기능별도 구현 필요
메모리 사용량클라이언트: 높음, 서버: 낮음클라이언트: 낮음, 서버: 높음
적합한 아키텍처마이크로서비스, 분산 시스템모놀리식, 단일 서버 환경

실제 구현 예시

JWT 구현 예시 (Node.js)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// JWT 인증 구현 예시
const jwt = require('jsonwebtoken');
const express = require('express');
const app = express();

app.use(express.json());

// 로그인 라우트
app.post('/login', (req, res) => {
  // 사용자 검증 (데이터베이스 조회 등)
  const user = { id: 1, username: 'test_user' };
  
  // JWT 생성
  const token = jwt.sign(
    { userId: user.id, username: user.username },
    'your_jwt_secret',  // 실제로는 환경 변수에서 가져와야 함
    { expiresIn: '1h' }  // 토큰 만료 시간
  );
  
  // 클라이언트에 토큰 반환
  res.json({ token });
});

// 인증 미들웨어
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  
  if (!token) return res.sendStatus(401);
  
  jwt.verify(token, 'your_jwt_secret', (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user;
    next();
  });
}

// 인증된 라우트
app.get('/protected', authenticateToken, (req, res) => {
  res.json({ message: '인증된 사용자입니다!', user: req.user });
});

app.listen(3000);

쿠키 기반 인증 구현 예시 (Node.js)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 쿠키 기반 인증 구현 예시
const express = require('express');
const session = require('express-session');
const app = express();

app.use(express.json());

// 세션 미들웨어 설정
app.use(session({
  secret: 'your_session_secret',  // 실제로는 환경 변수에서 가져와야 함
  resave: false,
  saveUninitialized: false,
  cookie: { 
    secure: process.env.NODE_ENV === 'production',  // HTTPS에서만 쿠키 전송
    httpOnly: true,  // JS에서 쿠키 접근 방지
    maxAge: 3600000  // 1시간 후 만료
  }
}));

// 로그인 라우트
app.post('/login', (req, res) => {
  // 사용자 검증 (데이터베이스 조회 등)
  const user = { id: 1, username: 'test_user' };
  
  // 세션에 사용자 정보 저장
  req.session.user = user;
  
  res.json({ message: '로그인 성공!' });
});

// 인증 미들웨어
function authenticate(req, res, next) {
  if (!req.session.user) {
    return res.status(401).json({ message: '인증 필요' });
  }
  next();
}

// 인증된 라우트
app.get('/protected', authenticate, (req, res) => {
  res.json({ message: '인증된 사용자입니다!', user: req.session.user });
});

// 로그아웃 라우트
app.post('/logout', (req, res) => {
  req.session.destroy();
  res.json({ message: '로그아웃 성공!' });
});

app.listen(3000);

용어 정리

용어설명

참고 및 출처