1. 도입: 인증 방식, 왜 중요한가요?
웹 애플리케이션에서 사용자가 로그인한 후 다음 페이지로 이동하거나 데이터를 요청할 때, 서버는 해당 요청이 '누구로부터 왔는지' 알아야 합니다. HTTP 프로토콜은 기본적으로 무상태(Stateless)입니다. 즉, 서버는 이전 요청과 현재 요청 사이에 어떤 연결고리도 기억하지 못합니다.
- 1.1. 웹 애플리케이션의 기본 과제: 상태 유지가 필요한 이유
- 인증(Authentication)이란, 로그인한 사용자가 매번 요청을 보낼 때마다 자신의 신원을 증명하고, 서버가 그 신원을 일관성 있게 유지(Session/State Management)하는 과정을 말합니다. 이 상태 유지를 위해 '쿠키 기반 세션' 또는 '토큰(Bearer)' 방식이 사용됩니다.
2. 쿠키 기반 세션 인증 방식 해부
가장 전통적인 방식이며, 서버가 사용자의 상태를 직접 관리합니다.
2.1. 작동 원리
- 로그인 요청: 클라이언트가 ID/PW를 서버에 전송합니다.
- 세션 생성: 서버는 인증 성공 후 서버 메모리/DB에 세션(Session)을 생성하고, 고유한 세션 ID를 부여합니다.
- 쿠키 발급: 서버는 이 세션 ID를 담은 세션 쿠키를 응답 헤더(Set-Cookie)에 실어 클라이언트에게 보냅니다.
- 자동 전송: 클라이언트(브라우저)는 이 쿠키를 저장하고 있다가, 이후 동일한 도메인으로 요청을 보낼 때마다 해당 쿠키를 자동으로 요청 헤더에 포함하여 전송합니다.
- 인증 확인: 서버는 전송된 세션 ID를 이용해 저장된 세션 정보를 조회하고 사용자를 식별합니다.
2.2. 장점
- 서버 주도 관리: 사용자의 모든 상태 정보를 서버가 관리하므로, 클라이언트가 복잡한 인증 정보를 저장할 필요가 없습니다.
- 세션 무효화 용이: 서버에서 세션 정보를 삭제하는 즉시 사용자 인증을 강제로 만료시킬 수 있습니다 (예: 강제 로그아웃).
2.3. 단점
- 확장성(Scalability) 문제: 서버를 여러 대로 증설(Scale-out)할 경우, 모든 서버가 동일한 세션 정보를 공유하도록 별도의 중앙 집중식 세션 스토리지(Redis 등)를 구성해야 하는 복잡성이 발생합니다. (Stateful)
- CSRF(Cross-Site Request Forgery) 취약점: 쿠키가 요청 시 자동으로 전송되기 때문에, 공격자가 사용자의 세션 쿠키를 이용해 악성 요청을 위조하기 쉽습니다.
3. Bearer 토큰 (JWT) 인증 방식 해부
쿠키의 확장성 문제를 해결하기 위해 등장했으며, 특히 API 서버나 SPA(Single Page Application) 환경에서 선호됩니다.
3.1. 작동 원리
- 로그인 요청: 클라이언트가 ID/PW를 서버에 전송합니다.
- 토큰 생성: 서버는 인증 성공 후 사용자의 정보(Payload)와 서명(Signature)을 포함하는 JWT (JSON Web Token)를 생성합니다.
- 토큰 발급: 서버는 생성된 JWT를 응답 본문(Body)에 담아 클라이언트에게 보냅니다.
- 토큰 저장: 과거에는 Local Storage에 저장하는 경우가 많았으나, 현재는 XSS 방어를 위해 Refresh Token은 HttpOnly 쿠키에, Access Token은 메모리에 저장하는 방식이 표준으로 자리 잡았습니다.
- 수동 전송: 이후 요청 시 클라이언트가 JavaScript를 이용해 토큰을 꺼내 Authorization 헤더에 Bearer <token> 형태로 직접 삽입하여 전송합니다.
- 인증 확인: 서버는 토큰의 서명(Signature)을 검증하여 토큰의 위변조 여부를 확인하고, Payload에서 사용자 정보를 추출하여 인증합니다.
3.2. 장점
- 무상태(Stateless): 서버는 토큰 자체의 유효성만 검증할 뿐, 별도로 세션 정보를 저장하지 않습니다. 서버 확장(Scale-out)이 매우 용이합니다.
- SPA/모바일 환경 최적화: 웹뿐만 아니라 모바일 앱 등 다양한 클라이언트에서 공통된 인증 메커니즘을 사용할 수 있으며, 도메인이 달라도 CORS(Cross-Origin Resource Sharing) 문제없이 인증이 가능합니다.
3.3. 단점
- 보안 문제 (XSS): 토큰을 Local Storage 등에 저장할 경우, XSS(Cross-Site Scripting) 공격에 의해 저장된 토큰이 탈취될 위험이 매우 큽니다.
- 토큰 만료 관리의 복잡성: 한 번 발급된 토큰은 만료되기 전까지는 유효합니다.한 번 발급된 토큰은 만료 전까지 서버에서 강제로 무효화하기 어렵습니다(블랙리스트나 짧은 만료 시간 + Refresh Token 전략으로 보완 가능).
4. 쿠키 vs Bearer: 핵심 비교 분석
| 특징 | 쿠키 기반 세션 | Bearer 토큰 (JWT) |
| 상태 관리 | Stateful (서버가 상태 기억) | Stateless (서버가 상태 기억 안 함) |
| 확장성 | 낮음 (세션 동기화 필요) | 높음 (서버 확장 용이) |
| 주요 취약점 | CSRF (쿠키 자동 전송) | XSS (토큰 탈취 위험) |
| 전송 위치 | Cookie 헤더 (브라우저 자동 전송) | Authorization: Bearer 헤더 (클라이언트 수동 삽입) |
| 사용 환경 | 전통적인 웹, 세션 관리가 중요한 환경 | SPA, API 서버, 모바일 앱, 마이크로서비스 |
5. 실무 적용을 위한 최적화 전략
두 방식 모두 장단점이 명확하므로, 실무에서는 두 방식의 장점을 결합한 하이브리드 전략을 많이 사용합니다.
5.1. 토큰의 안전한 관리 (권장 방안)
Bearer 토큰의 가장 큰 취약점인 XSS 공격 위험을 줄이면서 확장성을 유지하는 방법입니다.
- Access Token / Refresh Token 모델:
- Access Token (짧은 만료 시간): 실제 리소스 접근에 사용되며, 탈취되어도 피해를 최소화하기 위해 만료 시간을 짧게 설정합니다.
- Refresh Token (긴 만료 시간): Access Token이 만료되었을 때 새 Access Token을 발급받는 데만 사용됩니다.
- HttpOnly Secure Cookie 활용:
- Refresh Token을 HttpOnly 및 Secure 옵션이 설정된 쿠키에 저장합니다. HttpOnly 옵션 덕분에 JavaScript로 토큰에 접근하는 것이 불가능해져 XSS 공격으로부터 안전합니다.
- Access Token은 메모리(In-Memory)에 저장하거나, 여전히 Authorization 헤더를 통해 전송하는 방식을 유지합니다.
5.2. 인증 방식 선택 가이드라인
- 확장성(Scalability)이 최우선이고 서버 분리가 필수적이라면: JWT (Bearer 토큰) 방식을 기본으로 채택하고, 토큰 관리에 대한 보안 전략(HttpOnly 쿠키, Refresh Token)을 반드시 추가합니다.
- 전통적인 웹 애플리케이션(SSR)이고 보안에 더 중점을 둔다면: 쿠키 기반 세션 방식을 사용하되, 반드시 CSRF 방어 메커니즘(CSRF 토큰)을 적용해야 합니다.
5.3. 취약점별 방어 전략 심화
| 취약점 | 발생 방식 | 설명 | 방어 전략 |
| CSRF (Cross-Site Request Forgery) | 쿠키 기반 세션 | 브라우저가 사용자 모르게 쿠키를 자동으로 포함하여 악성 사이트의 요청을 서버에 보냄. | SameSite 속성 및 CSRF 토큰 사용 |
| XSS (Cross-Site Scripting) | Bearer 토큰 (Local Storage 저장 시) | 공격자가 주입한 스크립트가 Local Storage의 토큰을 읽어내 탈취함. | HttpOnly 쿠키에 저장하거나 메모리 저장 |
1. CSRF (쿠키) 방어 전략
쿠키가 자동 전송되는 것을 막거나, 자동 전송된 요청이 위조된 것임을 서버가 식별하도록 합니다.
- 쿠키 SameSite 속성 사용:
- 쿠키 전송 규칙을 정의합니다. Strict를 설정하면 타 도메인 요청에 쿠키가 전송되지 않아 CSRF 방어에 효과적입니다.
Set-Cookie: sessionID=abc1234; HttpOnly; Secure; SameSite=Strict
- 쿠키 전송 규칙을 정의합니다. Strict를 설정하면 타 도메인 요청에 쿠키가 전송되지 않아 CSRF 방어에 효과적입니다.
- CSRF 토큰 사용 (더 강력한 방어):
- 서버가 요청 시마다 무작위로 생성한 토큰을 클라이언트에게 발급하고, 클라이언트는 이 토큰을 요청 헤더나 바디에 담아 서버로 다시 보냅니다.
- 쿠키와 달리 이 토큰은 자동 전송되지 않으므로, 악성 사이트는 이 토큰 값을 알 수 없어 요청 위조가 불가능해집니다.
2. XSS (Bearer 토큰) 방어 전략
XSS는 공격자가 JavaScript를 실행하여 저장소에 접근하는 것이 핵심입니다. 이를 막는 가장 효과적인 방법은 토큰을 JS가 접근할 수 없는 곳에 두는 것입니다.
- Refresh Token을 HttpOnly 쿠키에 저장:
- 가장 민감하고 만료 기간이 긴 Refresh Token을 HttpOnly 옵션이 설정된 쿠키에 저장합니다.
- 이 쿠키는 HTTP 요청에만 포함되며, 브라우저의 JavaScript API (document.cookie)를 통해 접근하는 것이 원천적으로 불가능합니다.
-
Set-Cookie: refreshToken=xyz987; **HttpOnly**; Secure; SameSite=Strict
- Access Token은 메모리에 저장:
- 상대적으로 만료 기간이 짧은 Access Token은 브라우저의 전역 변수나 클로저 등 메모리상에만 저장합니다. 탭을 닫으면 자동으로 사라지므로 영구적인 탈취를 막을 수 있습니다.
6. 결론: 개발 환경에 맞는 인증 방식 선택하기
웹 애플리케이션의 인증은 성능과 보안을 동시에 고려해야 하는 가장 중요한 영역입니다. 무조건 한 가지 방식만을 고수하기보다는, 서비스의 규모, 환경(SPA, 모바일), 그리고 보안 요구 사항을 종합적으로 고려하여 두 방식의 장점을 결합한 최적의 하이브리드 전략을 선택하는 것이 가장 현명합니다.
→ Refresh Token은 HttpOnly + Secure + SameSite=Strict/Lax 쿠키에 저장
→ Access Token은 짧은 만료 시간(5~15분) + 메모리 저장 또는 Bearer 헤더 전송
→ 현재 이 조합이 보안과 확장성을 거의 완벽하게 만족합니다.
대규모 SPA/모바일 동시 서비스 시에는 BFF 서버에서 HttpOnly 쿠키 세션을 유지하고, 외부 API 서버로는 Bearer 토큰을 발급하는 방식도 많이 사용됩니다.
결론적으로 '무조건 JWT가 좋다/세션이 낡았다'는 말은 지양해야 합니다. 보안은 쿠키가, 확장성은 JWT가 강점이므로, 결국 'Refresh Token은 HttpOnly 쿠키로, Access Token은 짧은 만료 시간의 JWT로 메모리에 저장'하는 하이브리드 조합이 현재의 보안과 확장성을 거의 완벽하게 만족하는 최선의 답입니다.
'Backend' 카테고리의 다른 글
| NPE는 왜 런타임에 터질까? — Modern Null Safety와 가드 패턴(Guard Pattern) (0) | 2026.09.19 |
|---|---|
| BFS vs DFS: 단순한 탐색이 아니라 '비용'을 설계하는 기술 (0) | 2026.02.04 |
| DB 연결 오류: Docker Desktop과 로컬 DB의 포트 충돌 해결 (WSL2) (0) | 2025.12.30 |
| 조회 성능 100배 올리기: 백엔드 개발자의 필수 소양, 인덱스(Index) 완벽 가이드 (0) | 2025.12.23 |
| 로컬 캐시(Caffeine)와 분산 캐시(Redis) (0) | 2025.09.20 |