JWT, 모던 웹의 표준 인증 방식
JWT(JSON Web Token)는 사용자 인증 정보를 안전하게 전달하는 방식입니다. 세션 기반 인증의 한계를 극복하고, 마이크로서비스, 모바일 앱, API 서버 간 통신을 가능하게 합니다.
Google, GitHub, Auth0 등 주요 서비스들이 모두 JWT를 사용합니다. 이 가이드에서는 JWT의 모든 것을 배웁니다.
1부: JWT vs 세션 기반 인증
세션 기반 인증의 한계
클라이언트 → 로그인 → 서버 (세션 저장)
← 세션 ID 쿠키
문제:
1. 서버가 모든 세션 데이터 저장 필요
2. 서버 확장 시 세션 동기화 복잡
3. 도메인 간 공유 어려움
4. 모바일 앱에서 쿠키 사용 어려움JWT 기반 인증의 장점
클라이언트 → 로그인 → 서버 (검증)
← JWT 토큰
장점:
1. 서버는 토큰 검증만 함 (저장 불필요)
2. 무상태(Stateless) - 서버 확장 용이
3. 도메인/도메인 간 공유 가능
4. 모바일, SPA에 적합2부: JWT 구조
JWT의 3부 구조
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
구조: Header.Payload.Signature1. Header (헤더)
Base64 디코딩하면:
{
"alg": "HS256", // 서명 알고리즘
"typ": "JWT" // 토큰 타입
}2. Payload (페이로드)
Base64 디코딩하면:
{
"sub": "1234567890", // Subject (사용자 ID)
"name": "John Doe", // 사용자 이름
"iat": 1516239022, // Issued At (발급 시간)
"exp": 1516242622 // Expiration (만료 시간)
}표준 Claims
- iss: Issuer (발급자)
- sub: Subject (주체, 보통 사용자 ID)
- aud: Audience (대상)
- exp: Expiration Time (만료 시간, Unix timestamp)
- iat: Issued At (발급 시간)
- nbf: Not Before (유효 시작 시간)
3. Signature (서명)
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
용도:
- 토큰이 변조되지 않았음을 보장
- 발급자가 신뢰할 수 있음을 증명3부: JWT 만드는 과정
Step 1: Header 생성
const header = {
alg: "HS256",
typ: "JWT"
};
const encodedHeader = base64url(JSON.stringify(header));Step 2: Payload 생성
const payload = {
sub: "user123",
name: "John Doe",
email: "john@example.com",
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24) // 1일 후
};
const encodedPayload = base64url(JSON.stringify(payload));Step 3: 서명 생성
const secret = "your-secret-key";
const signature = HMACSHA256(
encodedHeader + "." + encodedPayload,
secret
);
const encodedSignature = base64url(signature);Step 4: 최종 JWT
const jwt = encodedHeader + "." + encodedPayload + "." + encodedSignature;4부: JWT 검증
토큰 검증 절차
클라이언트가 보낸 JWT 수신
Step 1: 형식 확인
→ Header.Payload.Signature 형식인가?
Step 2: Signature 재검증
→ 저장된 secret으로 서명 재계산
→ 보낸 서명과 일치하는가?
Step 3: Payload 검증
→ 만료 시간(exp) 확인
→ 필수 정보 확인
검증 성공 → 토큰 신뢰 가능JavaScript 예시
function verifyJWT(token, secret) {
const parts = token.split('.');
if (parts.length !== 3) return false;
const [headerEnc, payloadEnc, signatureEnc] = parts;
// 서명 재검증
const expectedSignature = HMACSHA256(
headerEnc + "." + payloadEnc,
secret
);
if (signatureEnc !== base64url(expectedSignature)) {
return false; // 변조됨
}
// Payload 디코딩
const payload = JSON.parse(base64urlDecode(payloadEnc));
// 만료 시간 확인
if (payload.exp < Math.floor(Date.now() / 1000)) {
return false; // 만료됨
}
return payload;
}5부: JWT 보안
1. Secret Key 관리
❌ 하드코딩
const secret = "mysecretkey";
✅ 환경 변수
const secret = process.env.JWT_SECRET;2. 토큰 저장 위치
❌ localStorage (XSS 공격에 취약)
localStorage.setItem('token', jwt);
✅ HttpOnly 쿠키 (XSS 방지)
res.cookie('token', jwt, {
httpOnly: true,
secure: true,
sameSite: 'strict'
});3. 만료 시간 설정
❌ 무제한 토큰
exp: null
✅ 짧은 만료 시간
exp: Math.floor(Date.now() / 1000) + (15 * 60) // 15분4. Refresh Token
Access Token (15분):
- 짧은 만료 시간
- API 요청에 사용
Refresh Token (7일):
- 긴 만료 시간
- 새 Access Token 발급에만 사용
- 더 안전하게 저장
장점: 공격자가 Access Token 탈취해도 제한적 피해6부: JWT 실제 활용
로그인 플로우
1. 클라이언트: 이메일/비밀번호 전송
2. 서버: 검증 후 JWT 발급
- Access Token
- Refresh Token
3. 클라이언트: 토큰 저장 (쿠키 또는 메모리)
4. API 요청: Authorization 헤더에 토큰 포함
GET /api/profile
Authorization: Bearer eyJhbGc...API 서버에서 검증
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, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
app.get('/api/profile', authenticateToken, (req, res) => {
res.json({ user: req.user });
});7부: JWT 디코더로 확인
JWT 디코더를 사용하면:
- Header, Payload, Signature 각각 확인
- 만료 시간 확인
- Payload의 정보 확인
- 서명 검증 (secret 필요)
자주 하는 실수
1. Secret을 공개 저장소에 노출
❌ GitHub에 secret 업로드
✅ .env 파일 (.gitignore에 포함)
2. 만료 시간 미설정
❌ 토큰에 만료 시간이 없음
✅ 반드시 exp 설정
3. HTTP로 토큰 전송
❌ http://example.com으로 토큰 전송 (MITM 공격)
✅ HTTPS 사용 필수
4. 너무 많은 정보 포함
❌ 토큰에 모든 사용자 정보 포함 (토큰 크기 증가)
✅ 필수 정보만 포함 (ID, 역할 등)
마무리
JWT는 현대 웹 개발의 필수 요소입니다. 올바르게 구현하면 안전하고 확장 가능한 인증 시스템을 만들 수 있습니다.
JWT 디코더로 토큰을 분석해보고, 보안 가이드를 참고하여 안전한 인증을 구현하세요!