웹 인증, 세션과 쿠키
들어가며
프로젝트 진행 중 별도 Web API 프로젝트에서 관리자 권한을 체크한 뒤, 다른 Web 프로젝트에서 관리자 요청들을 필터링해서 수행하고 이 권한에 따라 클라이언트 측에서도 화면 구분이 필요한 상황에 마주했다.
권한 데이터를 어떻게 저장하고 관리할지 고민하던 중, /{Area}/{Controller}/{Action} 같은 URL 형태로 수백 개의 권한을 관리해야했다.
이걸 어디에 저장해야 하지? 라는 고민부터 어떻게 관리할지 고민이 됐다.
처음에는 단순하게 생각했다. 사용자가 로그인하면 권한 목록을 Claim에 넣어서 관리하면 되지 않을까? 하지만 곧 문제를 발견했다. 권한 URL 하나가 약 4~50 bytes, 300개면 12~15KB다. 일반적인 브라주어 쿠키 제한인 4KB를 생각하면 훨씬 초과하는 수치다.
LocalStorage에 넣으면 크기 문제는 해결되지만 JavaScript로 접근 가능하다는 보안 위험이 있다.
결국 세션 을 선택했다. 이 글에서는 어떤 사고 과정을 거쳐 이 결론에 도달했는지 정리하고자 한다.
Claim이란
먼저 Claim이 무엇인지 명확히 해야 한다. Claim ≠ 쿠키다.
Claim은 사용자 정보의 한 조각일 뿐이다. “이름”, “이메일”, “역할” 같은 key-value 데이터 구조다.
1
2
3
4
5
6
7
8
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, "12345"),
new Claim(ClaimTypes.Name, "홍길동"),
new Claim(ClaimTypes.Role, "Admin"),
new Claim("Permission", "/Area1/Controller1/Action1"),
new Claim("Permission", "/Area2/Controller2/Action2")
};
이 Claim들을 어디에 저장하느냐 가 핵심이다. 저장 위치에 따라 쿠키 인증, JWT 인증, 세션 인증으로 나뉜다.
저장 방식 비교
쿠키 인증
Claim 데이터를 암호화하여 쿠키에 저장한다.
1
2
3
4
5
6
var claims = new List<Claim> { /* 수백 개 권한 */ };
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(principal);
// 브라우저 쿠키: .AspNetCore.Cookies = [암호화된 Claim 데이터]
문제:
- 쿠키 크기 제한: 4KB
- 수백 개 권한은 물리적으로 불가능
JWT (JSON Web Token)
Claim을 JWT로 인코딩하여 클라이언트에 전달한다.
1
2
3
var claims = new List<Claim> { /* 수백 개 권한 */ };
var token = new JwtSecurityToken(claims: claims, ...);
var jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
저장 위치 선택:
- 쿠키: 4KB 초과 → 불가능
- LocalStorage: XSS 공격 위험
- Authorization 헤더: 매 요청마다 20~30KB 전송
문제:
- 여전히 크기가 큼
- 권한 변경 시 새 토큰 발급 필요 (기존 토큰은 만료 전까지 유효)
세션
서버에 데이터를 저장하고, 클라이언트에는 식별자만 전달한다.
1
2
3
4
5
6
// 로그인 시
var claims = new List<Claim> { /* 수백 개 권한 */ };
HttpContext.Session.SetString("claims", JsonSerializer.Serialize(claims));
// 쿠키에는 세션 ID만 저장 (약 20 bytes)
// .AspNetCore.Session = abc123xyz
장점:
- 클라이언트 전송 크기: 20 bytes
- 서버에서 실시간 권한 변경 가능
- 클라이언트가 데이터 조작 불가능
세션을 선택했던 사고 흐름
1. 권한 데이터 저장 위치 후보
“권한 데이터를 어디에 저장할까?”
선택지:
- 클라이언트 (브라우저)
- 서버
처음에는 클라이언트 저장을 고려했다. 매번 서버에 물어볼 필요 없이 빠르게 확인할 수 있을 것 같았다.
2. 클라이언트 저장의 문제점
쿠키
- 크기: 15KB
- 제한: 4KB
LocalStorage
- 크기: 5~10MB (충분함)
- 보안: JavaScript 접근 가능 → XSS 공격 위험
- 조작: 클라이언트에서 권한 수정 가능
- 위험함
그래서 클라이언트 저장 포기했다.
3. 서버 저장
서버에 저장 후 어떻게 매번 식별하지?
=> 식별자를 클라이언트에 주고 그 식별자를 쓰면 되겠다.
4. 식별자의 크기는?
A) 권한 데이터를 암호화해서 토큰으로 (JWT)
1
2
3
권한 300개 → JSON 직렬화 → 15KB
→ JWT 암호화 → 20KB
→ Base64 인코딩 → 약 27KB
문제: 여전히 크다. 매 요청마다 20~30KB
B) 식별자만 주고 실제 데이터는 서버 보관 (세션)
1
2
3
권한 300개 → 서버 메모리/Redis 저장
세션 ID 생성 → "abc123xyz" → 약 20 bytes
→ 쿠키에 저장 가능
장점:
- 쿠키 크기: 20 bytes « 4KB
- 매 요청마다 자동 전송
- 클라이언트 조작 불가능
- 서버에서 실시간 권한 변경 가능
5. 세션 ID
세션 특징
크기 문제
| 방식 | 클라이언트 → 서버 전송량 |
|---|---|
| JWT | 매 요청마다 27KB |
| 세션 | 매 요청마다 20 bytes |
| 차이 | 1,350배 |
보안 문제
| 방식 | 데이터 위치 | 조작 가능성 |
|---|---|---|
| JWT | 클라이언트 | 디코딩 가능 (내용 볼 수 있음) |
| 세션 | 서버 | ID만 알고 내용 모름 |
브라우저 저장소 크기 비교
실제로 각 저장소의 크기를 확인해보자.
| 저장소 | 크기 제한 | 특징 |
|---|---|---|
| 쿠키 | 4KB | 매 요청마다 자동 전송 |
| LocalStorage | 5~10MB | JavaScript 접근 가능 (XSS 위험) |
| SessionStorage | 5~10MB | 탭 종료 시 삭제, XSS 위험 동일 |
| 서버 세션 | 서버 메모리 한계 | 클라이언트는 ID만 보유 |
권한 URL 크기 계산:
1
2
3
권한 하나: "/Qna/QnaTicket/Update" = 23 bytes (영문 기준)
Claim 래핑: JSON 직렬화 시 약 50~60 bytes
300개 권한: 50 × 300 = 15,000 bytes = 15KB
쿠키 제한:
- 1개당: 4KB
- 도메인당: 50~180개
- 15KB는 물리적으로 불가능
마치며
수백 개의 권한을 관리하는 문제를 통해 세션 기반 인증의 필요성을 깨달았다.
핵심 원칙:
| 개념 | 설명 |
|---|---|
| Claim | 데이터 구조일 뿐, 저장 위치가 중요 |
| 쿠키 인증 | 4KB 제한으로 대량 데이터 불가능 |
| JWT | 여전히 크고, 실시간 변경 불가 |
| 세션 | 서버 저장 + 식별자 전달 = 최적 |
실무에서 “왜 이 방식을 사용하는가?”를 이해하는 것이 중요하다.
단순히 “세션이 좋다더라”가 아니라, 크기 제한과 보안, 유연성의 트레이드오프를 직접 계산하고 비교해보면 자연스럽게 답이 나온다.
하지만 항상 이게 정답이다 라고 단정지을 순 없으므로 과감하게 코드를 바꾸기도 할 것 같다.
References
- Cookies, Tokens, or JWTs? The ASP.NET Core Identity Dilemma - Auth0
- Session vs JWT Authentication in .NET Core - Medium
- Storage quotas and eviction criteria - MDN Web Docs
- Please Stop Using Local Storage - Randall Degges
- ASP.NET Core Claims Based Authentication - Eddie Abbondanz
- ClaimsPrincipal Class - Microsoft Learn

