쿠키·세션·JWT·OAuth 차이와 변천사 — 패스키까지 한눈에

"로그인하면 그 정보가 대체 어디에 저장되는 거예요?"
"JWT 쓰면 세션 안 써도 된다던데, 무슨 뜻이죠?"
"OAuth랑 그냥 로그인이랑 같은 거 아닌가요?"
"요즘은 비밀번호 없이 로그인한다던데, 그게 패스키라는 거예요?"
주니어 개발자분들에게 가장 많이 받는 질문을 모아봤습니다. 공통점이 하나 있는데요.
전부 "인증을 어떻게 처리하느냐"는 같은 뿌리에서 나온 질문이라는 점입니다.
사실 웹 인증의 역사는 한 줄로 요약됩니다. HTTP는 손님을 기억하지 못한다.
이 한 가지 문제를 시대마다 다르게 풀어온 게 쿠키, 세션, JWT, OAuth, 그리고 요즘의 패스키입니다.
저는 2012년에 웹 개발을 시작했는데,
그때만 해도 쿠키 인증 흔적이 코드 곳곳에 남아 있었습니다.
이후 세션을 파일로 저장하다가 서버를 늘리면서 골치를 앓고,
Redis로 갈아타고, API가 늘어나면서 JWT를 적극적으로 쓰게 되는 과정을 현장에서 직접 겪었어요.
이 글은 그 흐름을 따라가면서 각 방식이 "왜 등장했고,
무엇이 답답해서 다음으로 넘어갔는지"를 정리해 보려고 합니다.
신입 개발자나 인증 구조가 막연하게 느껴지는 분들에게 도움이 됐으면 합니다.
📋 목차
- HTTP는 원래 손님을 기억하지 못합니다
- 쿠키 - 브라우저에 도장을 찍다
- 세션 - 진짜 정보는 서버가 들고 있습니다
- 제가 겪은 세션의 벽 - 서버를 늘리니 문제가 터졌습니다
- JWT - 토큰 안에 정보를 담다
- JWT가 만능은 아니었습니다
- OAuth - 비밀번호를 넘기지 않고 권한만 빌려주다
- OAuth 2.1 - 위험한 것들을 덜어낸 정리판
- 패스키 - 비밀번호 자체를 없애는 방향
- 한눈에 보는 인증 방식 비교
- 자주 묻는 질문 (세션 JWT 차이, OAuth OIDC 차이 등)
- 마무리
1. HTTP는 원래 손님을 기억하지 못합니다
이야기를 시작하려면 HTTP의 성격부터 짚어야 합니다. HTTP는 무상태(stateless) 프로토콜입니다. 요청 하나가 끝나면 그걸로 끝이고, 다음 요청이 들어와도 "아, 아까 그 사람이네" 하고 알아보지 못합니다. 카페 직원이 손님이 나갔다 들어올 때마다 처음 보는 사람처럼 대하는 셈이죠.
그런데 로그인이라는 기능은 정반대를 요구합니다. 한 번 신원을 확인하면 그 뒤로는 계속 "당신이 누구인지" 기억해줘야 합니다. 무상태인 HTTP 위에서 상태를 흉내 내야 하는 모순. 인증 기술의 변천사는 전부 이 모순을 메우려는 시도였습니다.
2. 쿠키 - 브라우저에 도장을 찍다
가장 먼저 나온 답이 쿠키였습니다. 1990년대 중반 넷스케이프 시절에 등장했으니 꽤 오래된 기술이에요. 원리는 단순합니다. 서버가 응답할 때 "이 도장을 들고 다시 오세요" 하고 작은 데이터 조각을 브라우저에 심어둡니다. 브라우저는 같은 도메인에 요청할 때마다 그 도장을 자동으로 들고 갑니다.
문제는 초창기 방식이 그 도장 안에 민감한 정보를 그대로 넣었다는 겁니다. 로그인 여부나 사용자 ID 같은 걸 쿠키에 직접 담아두는 코드가 많았어요. 사용자가 마음만 먹으면 브라우저 개발자 도구로 쿠키 값을 열어보고 고칠 수 있는데 말이죠. 저도 2012년 무렵에 "이거 그냥 쿠키 값 바꾸면 다른 사람으로 로그인되는 거 아닌가?" 싶은 오래된 코드를 보고 식겁한 기억이 있습니다.
📌 쿠키의 한계
쿠키는 클라이언트가 들고 다니는 데이터라서 위변조에 취약합니다.
중요한 정보를 통째로 쿠키에 담는 건 그래서 위험합니다.
이 약점이 다음 단계인 세션을 불러옵니다.
3. 세션 - 진짜 정보는 서버가 들고 있습니다
그래서 발상을 뒤집습니다. 정보를 클라이언트한테 맡기지 말고, 서버가 들고 있자. 대신 클라이언트에게는 "보관함 번호표"만 쥐여주자. 이게 세션입니다.
로그인에 성공하면 서버가 세션을 하나 만들고, 그 안에 사용자 정보를 저장합니다. 그리고 그 세션을 가리키는 식별자(세션 ID)만 쿠키에 담아 브라우저에 보냅니다. 쿠키 안에는 의미 없는 무작위 문자열만 들어 있으니, 사용자가 그 값을 들여다봐도 알 수 있는 게 없죠. 실제 정보는 서버 금고에 있으니까요.
2010년대 초반에 제가 일하던 환경에서도 쿠키 직접 저장 방식을 걷어내고 세션 방식으로 옮기던 시기였습니다. 그땐 "이제 서버가 정보를 들고 있으니 안전하겠구나" 하고 마음이 좀 놓였는데, 정작 진짜 골치 아픈 문제는 그다음에 찾아왔습니다.
4. 제가 겪은 세션의 벽 - 서버를 늘리니 문제가 터졌습니다
세션은 서버에 저장된다는 건 알고 있었지만, 그 시절 대다수 구현은 세션을 파일로 저장하는 방식이었습니다. 서버 한 대일 때는 아무 문제가 없어요. 그런데 트래픽이 늘어서 서버를 두 대, 세 대로 늘리는 순간 이게 발목을 잡습니다.
A 서버에서 로그인한 사용자의 다음 요청이 B 서버로 넘어가면, B 서버에는 그 세션 파일이 없으니 "당신 누구세요?" 하고 다시 묻는 거죠. 로그인이 풀려버리는 겁니다. 당시엔 이걸 해결하려고 서버끼리 세션 파일을 두는 디렉터리를 공유 스토리지에 마운트해서 같이 바라보게 했는데, 솔직히 관리하기가 여간 귀찮은 게 아니었습니다. 마운트가 꼬이거나 느려지면 그대로 장애로 이어지기도 했고요.
그래서 결국 세션 저장소를 파일에서 떼어내 Redis나 memcached 같은 인메모리 저장소에 따로 설정해서 썼습니다. 모든 서버가 같은 세션 저장소 한 곳을 바라보게 하니 서버를 몇 대로 늘리든 로그인이 유지됐어요. 지금 보면 너무 당연한 구조인데, 그땐 직접 부딪히면서 배운 셈입니다.
💡 그때 깨달은 것
세션 방식의 본질적 부담은 "서버가 상태를 들고 있어야 한다"는 점입니다.
서버를 늘릴수록 그 상태를 어떻게 공유할지가 숙제가 됩니다.
이 부담을 아예 없애려는 시도가 다음의 JWT입니다.
5. JWT - 토큰 안에 정보를 담다
JWT(JSON Web Token)는 또 한 번 발상을 뒤집습니다. 서버가 상태를 들고 있는 게 부담이라면, 다시 클라이언트한테 정보를 들려 보내자. 단, 옛날 쿠키처럼 맨몸으로 보내는 게 아니라 서명을 붙여서요.
JWT는 사용자 정보가 담긴 데이터에 서버만 아는 비밀키로 서명을 합니다. 클라이언트가 이 토큰을 들고 와도 내용을 위조하면 서명이 깨지기 때문에 서버가 바로 알아챕니다. 정보는 토큰 안에 들어 있고 서명으로 무결성이 보장되니, 서버는 따로 세션 저장소를 둘 필요가 없어집니다. 토큰만 검증하면 되거든요. RFC 7519로 표준이 정리된 게 2015년입니다.
제가 JWT를 적극적으로 쓰게 된 계기는 API가 늘어나면서였습니다. 서비스가 커지면 도메인이 여러 개로 쪼개집니다. 메인 사이트, API 서버, 별도 도메인의 부가 서비스... 이렇게 도메인이 갈라지면 쿠키 기반 세션은 도메인 경계를 넘기가 까다롭습니다. 쿠키는 기본적으로 도메인에 묶여 있으니까요. 그게 매번 귀찮았는데, JWT는 헤더에 토큰을 실어 보내는 방식이라 도메인이 달라도 인증을 일관되게 처리할 수 있었습니다. 서버 간 세션 공유 고민에서도 벗어났고요.
| 항목 | 세션 방식 | JWT 방식 |
|---|---|---|
| 정보 저장 위치 | 서버 (저장소 필요) | 토큰 자체 (클라이언트가 보관) |
| 서버 확장 | 공유 저장소 필요 (Redis 등) | 저장소 없이 검증만 하면 됨 |
| 도메인 분리 | 쿠키 도메인 제약으로 까다로움 | 헤더로 전달, 비교적 자유로움 |
| 즉시 폐기(로그아웃) | 저장소에서 지우면 끝, 쉬움 | 만료 전 강제 폐기가 어려움 |
6. JWT가 만능은 아니었습니다
JWT를 쓰기 시작하면서 새로운 고민이 생겼습니다. 가장 먼저 부딪히는 게 "토큰을 어디에 보관할 것인가"입니다. 브라우저의 localStorage에 넣으면 자바스크립트로 쉽게 꺼낼 수 있어 편하지만, XSS 공격에 토큰이 통째로 털릴 수 있습니다. 그렇다고 쿠키에 담으면 이번엔 CSRF를 신경 써야 하고요. 정답이 딱 떨어지지 않는 영역이라 지금도 논쟁이 있습니다. 요즘은 httpOnly 속성을 건 쿠키에 담는 방식이 비교적 안전한 선택으로 자주 언급됩니다.
두 번째 문제는 폐기입니다. 세션은 저장소에서 지워버리면 바로 무효가 되는데, JWT는 한 번 발급되면 만료 시각까지 유효합니다. 사용자가 로그아웃을 해도, 계정이 탈취돼서 급히 끊어야 하는 상황이 와도, 이미 나간 토큰을 서버가 곧바로 무력화하기가 어렵습니다. 이걸 메우려고 짧은 수명의 액세스 토큰 + 갱신용 리프레시 토큰을 조합하거나, 블랙리스트를 따로 두기도 하는데, 그러다 보면 "상태를 없애려고 JWT를 썼는데 결국 또 서버에 상태를 두게 되네" 하는 묘한 상황이 벌어지기도 합니다.
📌 정리하면
세션과 JWT는 우열의 문제가 아니라 트레이드오프입니다.
서버가 상태를 들고 있는 부담(세션)이냐,
폐기와 보관이 까다로운 부담(JWT)이냐.
상황에 맞게 고르는 게 맞고,
실제로 둘을 섞어 쓰는 서비스도 많습니다.
7. OAuth - 비밀번호를 넘기지 않고 권한만 빌려주다
여기서 흔한 오해 하나를 짚고 가겠습니다. OAuth는 사실 "로그인 기술"이 아닙니다. 정확히는 권한을 위임하는 프로토콜입니다.
예를 들어 어떤 앱이 여러분의 구글 캘린더를 읽어와야 한다고 해봅시다. 옛날 방식이라면 그 앱한테 구글 아이디와 비밀번호를 그냥 넘겨줘야 했을 겁니다. 생각만 해도 아찔하죠. OAuth는 이 문제를 풀기 위해 나왔습니다. 비밀번호는 구글에만 입력하고, 앱에게는 "이 사람 캘린더를 읽어도 된다"는 제한된 권한 증서(토큰)만 발급해주는 거죠. 우리가 흔히 보는 "구글로 로그인", "카카오로 로그인" 버튼 뒤에서 도는 게 바로 이 흐름입니다. OAuth 2.0이 RFC 6749로 표준이 된 게 2012년입니다.
다만 OAuth 자체는 "권한 위임"이지 "이 사람이 누구인지 확인"하는 일은 아닙니다. 그래서 OAuth 위에 신원 확인 계층을 얹은 OpenID Connect(OIDC)가 따로 있습니다. ID 토큰을 통해 "로그인한 사람이 누구인지"를 표준화된 방식으로 알려주죠. "소셜 로그인"이라고 부르는 것의 실체는 대부분 이 OIDC입니다. OAuth와 OIDC를 뭉뚱그려 부르는 경우가 많은데, 한 번 구분해 두면 머릿속이 한결 정리됩니다.
8. OAuth 2.1 - 위험한 것들을 덜어낸 정리판
OAuth 2.0은 워낙 널리 쓰이다 보니 시간이 지나면서 문제도 드러났습니다. 너무 유연하게 설계한 탓에, 보안에 취약한 방식까지 스펙 안에 함께 들어 있었거든요. 10여 년간 보안 권고들이 여기저기 흩어져 쌓였고, 이걸 한데 모아 정리한 게 OAuth 2.1입니다.
2026년 현재 OAuth 2.1은 아직 IETF 인터넷 드래프트 상태이고 정식 RFC로 확정되진 않았습니다. 그런데도 주요 인증 제공자들과 라이브러리가 이미 그 내용을 사실상 기본값으로 채택한 상태라, 새로 코드를 짠다면 OAuth 2.1 기준으로 가는 게 맞다는 분위기입니다. 주요 변화는 다음과 같습니다.
- 토큰을 URL에 그대로 노출하던 implicit(암묵적) 방식을 제거했습니다. 브라우저 기록이나 로그로 토큰이 새는 위험이 컸기 때문입니다.
- 사용자의 아이디·비밀번호를 앱이 직접 받아 처리하던 password(ROPC) 방식도 제거됐습니다. OAuth가 애초에 막으려던 패턴이었으니까요.
- PKCE를 모든 클라이언트에 의무화했습니다. 예전엔 모바일·SPA 같은 일부에만 권장했는데, 이제는 서버 클라이언트까지 전부 적용합니다.
- 리다이렉트 주소를 와일드카드 없이 정확히 일치하는지 비교하도록 조였습니다.
한마디로 "새로운 걸 추가했다"기보다 "위험했던 걸 덜어냈다"에 가깝습니다. 참고로 OAuth 2.0(RFC 6749)이 폐기된 건 아니고 여전히 유효합니다. 다만 새로 만든다면 더 안전한 기본값을 따르라는 쪽으로 무게가 실린 거죠.
9. 패스키 - 비밀번호 자체를 없애는 방향
지금까지의 변천사는 따지고 보면 전부 "비밀번호로 로그인한 다음, 그 상태를 어떻게 유지하느냐"의 이야기였습니다. 그런데 요즘 흐름은 한 발 더 나아가 비밀번호라는 출발점 자체를 의심합니다. 그 결과가 패스키(Passkey)입니다.
패스키는 FIDO 얼라이언스와 W3C가 만든 WebAuthn 표준을 기반으로 하고, 2022년부터 본격적으로 퍼지기 시작했습니다. 핵심은 공개키 암호화입니다. 사용자의 기기 안에는 개인키가 안전하게 보관되고, 서비스에는 공개키만 등록됩니다. 로그인할 때는 지문이나 얼굴 인식으로 기기를 잠금 해제하면, 개인키로 서명한 값만 서버에 전달됩니다. 비밀번호 자체가 네트워크를 오가지 않으니 유출될 비밀번호가 없는 셈이죠.
특히 강력한 부분은 피싱 내성입니다. 패스키는 등록된 도메인에 묶여 있어서, 가짜 사이트로 유도해도 거기서는 동작하지 않습니다. 사람을 속여서 비밀번호를 빼내는 고전적 수법이 통하지 않는 구조예요. 애플 iCloud 키체인, 구글 비밀번호 관리자, 마이크로소프트 같은 플랫폼을 통해 기기 간 동기화도 됩니다.
💡 한국 상황은 조금 다릅니다
해외에서는 구글, 애플 등 주요 서비스가 패스키를 적극 도입했고,
2026년 기준 글로벌 상위 사이트 상당수가 지원합니다.
다만 한국은 아직 전면 도입이 제한적인 편입니다.
삼성 패스, SK텔레콤 등이 관련 사업을 하고 있고,
은행·증권권의 시범 서비스와 정부 차원의 표준화 작업이 진행되는 단계로 보입니다.
보편화 시점은 좀 더 지켜봐야 할 것 같습니다.
10. 한눈에 보는 인증 방식 비교
지금까지 다룬 다섯 가지 방식을 한 표로 정리하면 흐름이 더 또렷하게 보입니다. 등장 시점과 핵심 아이디어, 그리고 무엇이 아쉬워서 다음 단계로 넘어갔는지를 같이 봤습니다.
| 방식 | 등장·표준화 시점 | 핵심 아이디어 | 주요 한계 |
|---|---|---|---|
| 쿠키 | 1990년대 중반 | 브라우저에 식별 데이터를 심어 둠 | 위변조에 취약, 민감 정보 노출 위험 |
| 세션 | 2000년대 보편화 | 정보는 서버가 보관, 클라이언트엔 번호표만 | 서버 확장 시 저장소 공유 부담 |
| JWT | 2015년 (RFC 7519) | 서명된 토큰에 정보를 담아 클라이언트가 보관 | 즉시 폐기 어려움, 저장 위치 고민 |
| OAuth 2.0 | 2012년 (RFC 6749) | 비밀번호 대신 제한된 권한만 위임 | 유연성이 과해 취약 패턴이 혼재 |
| 패스키 | 2022년~ (FIDO2/WebAuthn) | 공개키 기반으로 비밀번호 자체를 제거 | 도입 초기, 국내 보급은 진행 중 |
표를 보면 한 가지가 눈에 띕니다. 정보를 "클라이언트에 두느냐, 서버에 두느냐"가 쿠키 → 세션 → JWT를 오가며 계속 시소를 탑니다. 결국 어느 쪽도 완벽하지 않고, 환경에 따라 무게추가 옮겨가는 거죠.
11. 자주 묻는 질문 (세션 JWT 차이, OAuth OIDC 차이 등)
세션과 JWT 중 무엇을 써야 하나요?
우열의 문제가 아니라 트레이드오프입니다. 단일 서비스에서 즉시 로그아웃이나 강제 만료가 중요하면 세션이 다루기 편합니다. 반대로 서버를 여러 대로 확장하거나 도메인이 분리된 API 환경이라면 JWT가 유리합니다. 실제 서비스에서는 둘을 섞어 쓰는 경우도 많습니다.
JWT는 어디에 저장하는 게 안전한가요?
localStorage는 다루기 편하지만 XSS 공격에 토큰이 통째로 노출될 수 있습니다. httpOnly 속성을 건 쿠키에 담는 방식이 비교적 안전한 선택으로 자주 권장되는데, 이 경우엔 CSRF 대비가 별도로 필요합니다. 딱 떨어지는 정답이 있는 영역은 아니라서 서비스 환경에 맞춰 고르는 게 맞습니다.
OAuth와 OIDC는 무엇이 다른가요?
OAuth는 "권한 위임"이고, OIDC(OpenID Connect)는 그 위에 "신원 확인"을 얹은 계층입니다. OAuth만으로는 로그인한 사람이 누구인지 표준적으로 알 수 없고, OIDC의 ID 토큰이 그 역할을 합니다. 우리가 흔히 말하는 소셜 로그인의 실체는 대부분 OIDC입니다.
패스키를 쓰면 비밀번호는 완전히 사라지나요?
방향은 그렇지만 당장은 아닙니다. 2026년 기준 해외 주요 서비스는 빠르게 도입하고 있지만, 국내는 아직 보급 초기 단계라 비밀번호와 함께 쓰이는 경우가 많습니다. 한동안은 두 방식이 공존하는 형태가 이어질 가능성이 큽니다.
12. 마무리
쿠키, 세션, JWT, OAuth, 패스키를 쭉 따라왔는데, 다시 보면 이게 서로 갈아치우는 관계가 아니라는 점이 보입니다. 쿠키는 지금도 세션 ID와 토큰을 실어 나르고, 세션은 여전히 수많은 서비스의 기본이며, JWT와 OAuth는 API와 소셜 로그인의 뼈대로 잘 쓰이고 있습니다. 패스키도 당분간은 비밀번호와 공존하겠죠.
결국 이 모든 건 "HTTP는 손님을 기억하지 못한다"는 한 문장에서 출발해, 각 시대의 환경과 위협에 맞춰 답을 다듬어 온 과정이었습니다. 서버 한 대 시절엔 파일 세션으로 충분했고, 서버를 늘리니 Redis가 필요했고, 도메인이 쪼개지니 JWT가 편했고, 비밀번호 유출이 끊이지 않으니 패스키가 나온 거죠. 그래서 어떤 방식이 "정답"이라고 단정하기보다, 내 서비스가 지금 어떤 환경에 있는지를 먼저 보는 게 맞다고 생각합니다. 몇 년 뒤엔 또 지금의 정답이 옛날 방식이 되어 있을지도 모르고요.
📚 함께 보면 좋은 글
API, 아직도 어렵나요? '디지털 웨이터'로 배우는 연결의 철학
새로운 쇼핑몰에 가입할 때마다 아이디와 비밀번호를 새로 만드는 일은 매우 번거롭습니다.그런데 우리는 대부분 '카카오로 3초 만에 시작하기' 버튼을 눌러 이 과정을 건너뜁니다.신기하게도
devwar.tistory.com
REST API와 GraphQL 차이 완벽 정리: 페이스북이 REST를 버린 진짜 이유 (2026 최신)
API(Application Programming Interface)는 현대 웹과 모바일 서비스가 서로 데이터를 주고받는 핵심 통신 수단입니다.지난 10년 이상 API 시장의 절대 강자였던 REST API는 단순함과 직관성으로 전 세계 개발
devwar.tistory.com
네이버가 PHP였다고? 한국 IT 대기업들의 충격적인 초기 기술 스택 (네이버·배민·카카오)
"형, 네이버는 처음부터 Java였죠?""배민도 Spring으로 시작한 거 아니에요?""카카오가 Ruby였다고요? 진짜요?"후배 개발자들과 한국 IT 대기업 이야기를 나누다 보면,거의 모두가 같은 오해를 하고 있
devwar.tistory.com
웹개발자 MBTI 직무 추천 — 프론트엔드·백엔드·DBA 성향 궁합
"너 I라서 백엔드가 딱이야.""제가 P인데 DBA 해도 괜찮을까요?""프론트는 E가 잘한다던데 진짜예요?""성향 따라 직무 갈리는 거, 그거 좀 미신 아닌가요?" 팀에서 한 번쯤 들어보셨을 법한 말들입니
devwar.tistory.com
Apache Nginx Tomcat 차이 완벽 정리 - 웹서버 30년 변천사로 이해하기
Apache, Nginx, Apache Tomcat의 차이를 한 번에 정리합니다. 셋 다 "서버"라는 이름이 붙지만 Apache와 Nginx는 웹서버, Tomcat은 WAS(서블릿 컨테이너)로 역할이 다릅니다. 이 글은 1995년부터 2026년까지 웹서버
devwar.tistory.com
'서버의생각' 카테고리의 다른 글
| 리눅스 배포판 계보에 숨은 57년의 결별사, 유닉스부터 우분투까지 (0) | 2026.08.03 |
|---|---|
| 서버 보안 업데이트가 두렵다면 AWS를 써야 하는 이유 (0) | 2026.07.30 |
| vi vim 차이, 50년 된 편집기를 아직 쓰는 이유 (0) | 2026.07.09 |
| 왜 서버 컴퓨터는 인텔 CPU만 쓸까? - 13년차 개발자가 풀어보는 x86과 ARM의 진짜 차이 (0) | 2026.07.09 |
| 우분투는 어쩌다 서버의 기본값이 됐을까 - CentOS를 떠나 우분투로 온 개발자 이야기 (0) | 2026.07.06 |