
로그인 기능은 겉으로 보면 단순해 보인다.
입력값을 받고, 기존 데이터와 동일할 경우
로그인 성공 처리를 해주면 끝나는 것처럼 보인다.
그런데 여기서 더 깊게 생각해보면 미처 생각하지 못한 부분이 생긴다.
"로그인 성공 이후에는 어떻게 로그인 상태를 유지시키지?"
로그인 요청을 계속 보내는 건 불친절한 기능이다.
한 번의 요청으로도 서비스는 계속 나를
로그인한 사용자로 알아봐야 한다.
그럼 서버가 어떻게 사용자를 기억하게 만들어야 할까?
웹의 기본 통신 방식인 HTTP는
기본적으로 이전 요청을 기억하지 못한다.
즉, 요청 하나가 끝나면
다음 요청은 완전히 새로운 요청처럼 처리된다.
그렇다면 로그인 상태를 유지하기 위해서는
사용자를 기억하기 위한 별도의 방법이 필요하다.
그리고 이 문제를 해결하기 위해 등장한 대표적인 방식이
바로 세션(Session)과 토큰(Token) 기반 인증이다.
로그인 상태를 유지하는 방식을 이해하려면
우선 HTTP의 기본 구조부터 알아야 한다.
웹에서 클라이언트와 서버는
HTTP 요청(Request)와 응답(Response)을 주고받는다.
브라우저가 서버에게 요청을 보내면,
서버는 그에 맞는 응답을 반환한다.
그런데 여기서 중요한 특징이 하나 있다.
HTTP는 기본적으로 상태(State)를 기억하지 않는다.
즉, 요청 하나와 응답 하나가 끝나면 연결은 죵료되고,
다음 요청이 들어왔을 때 서버는
그 요청이 이전 요청과 같은 사용자의 요청인지 알 수 없다.
예를 들어
사용자 입장에서는
"아까 로그인했으니까 당연히 로그인된 상태겠지"라고 생각할 수 있지만,
서버 입장에서는 아니라는 말이다.
새로운 요청이 들어왔을 뿐이고,
그 요청이 이전에 로그인했던 사용자의 요청인지
자동으로 알 수 있는 방법이 없다.
즉, HTTP만으로는
로그인 상태를 유지할 수 없는 구조라는 뜻이다.
이런 특징을 상태(State)를 저장하지 않는다는 의미로 Stateless라고 부른다.
그래서 웹 서비스는
사용자를 기억하기 위한 별도의 방법이 필요했고,
그 과정에서 등장한 대표적인 방식이
세션(Session)과 토큰(Token) 기반 인증이다.
앞서 말했듯, HTTP는 기본적으로 상태를 기억하지 못한다.
즉, 로그인 요청이 끝난 이후에는
서버가 사용자를 계속 기억할 방법이 없다.
그래서 등장한 방식이
바로 세션(Session) 기반 인증이다.
세션 방식의 핵심은 단순하다.
서버가 직접 사용자의 로그인 상태를 기억하는 것
로그인 요청이 성공하면
서버 내부에 사용자의 로그인 정보를 저장한다.
예를 들어
같은 정보들을 서버가 보관하게 된다.
그리고 이때 서버는
해당 사용자를 구분하기 위한 고유한 ID도 함께 만든다.
이 ID를 세션 ID(Session ID)라고 한다.
그런데 문제는 여기서 끝나지 않는다.
서버는 세션 정보를 저장했지만,
다음 요청에서 어떤 사용자가 어떤 세션의 주인인지 다시 알아야 한다.
그래서 브라우저가 세션 ID를 가지고 다니게 된다.
이때 사용되는 것이 쿠키(Cookie)다.
서버는 로그인 성공 이후
세션 ID를 쿠키에 담아 브라우저로 전달하고,
브라우저는 이후 요청마다 해당 쿠키를 함께 전송한다.
그러면 서버는
즉 세션은 서버에 저장되고 쿠키는 세션 ID를 전달하는 역할을 한다.
쿠키와 세션은 같은 개념이 아니라,
쿠키는 단순한 저장 공간이고,
세션은 서버가 사용자를 기억하기 위한 인증 방식에 가깝다.
이 부분이 핵심이자, 많이 헷갈리는 부분이라고 한다.
이렇게 세션 기반 인증은
HTTP가 상태를 기억하지 못한다는 문제를 해결하기 시작했다.
세션 기반 인증은
HTTP가 상태를 기억하지 못한다는 문제를 해결해줬다.
하지만 서비스 규모가 커지기 시작하면서
세션 방식도 점점 한계를 드러내기 시작한다.
가장 큰 이유는 세션 정보가 서버에 저장된다는 점이었는데,
사용자가 늘어날수록 서버가 저장해야 하는 세션 데이터도 함께 증가하게 된다.
즉, 로그인한 사용자가 많아질수록
서버는 더 많은 상태를 직접 관리해야 했다.
처음에는 큰 문제가 아니었지만
서비스 규모가 커지고 트래픽이 많아지면서
상황이 많이 달라지기 시작했다.
특히 서버를 여러 대 운영하기 시작하면 문제가 더 복잡해진다.
예를 들어 A 서버에서 로그인했는데,
다음 요청은 B 서버로 들어가는 상황이 생길 수도 있었다.
그런데 세션 정보가 A 서버에만 저장되어 있다면,
B 서버는 해당 사용자가 로그인한 상태인지 알 수 없다.
결국 여러 서버가 같은 세션 정보를 공유해야 하는 문제가 생긴다.
그래서 세션 저장소를 따로 두거나
Redis 같은 외부 저장소를 사용하거나
세션 클러스터링을 구성하는 방식 등이 등장하게 된다.
즉, 세션 방식을 통해 해결된 인증 자체보다
그 세션들을 관리하기 위한 추가적인 구조가 필요해지기 시작한 것이다.
그리고 웹 환경 자체도 점점 변하기 시작했다.
과거에는 서버가 화면까지 함께 렌더링하는 구조가 많았다면,
점점 프론트엔드와 백엔드가 분리되기 시작했고,
SPA(Single Page Application)나
모바일 앱 환경도 늘어나기 시작했다.
이런 환경에서는 서버가 상태를 직접 기억하는 방식보다,
클라이언트가 직접 인증 정보를 들고 다니는 방식이
더 유리해지기 시작했다.
그래서 이런 흐름 속에서 등장한 방식이
바로 토큰(Token) 기반 인증이다.
세션 방식은 서비스 규모가 커지고, 서버가 여러 대로 분산되고, 프론트와 백엔드가 분리되기 시작하면서
점점 서버가 상태를 직접 저장하는 방식에 부담이 생기기 시작했다.
그래서 등장한 방식이 바로 토큰(Token) 기반 인증이다.
토큰 방식의 핵심은 세션과 반대로,
세션은 서버가 사용자를 기억했다면,
토큰 방식은 클라이언트가 인증 정보를 직접 들고 다닌다.
로그인 요청이 들어오면 서버는 먼저 사용자의 정보를 확인한다.
그리고 로그인이 성공하면 사용자를 증명할 수 있는 토큰(Token)을 발급한다.
브라우저는 이 토큰을 저장하고, 이후 요청마다 함께 전달한다.
대표적으로 Authorization 헤더, Bearer Token 방식 등을 사용한다.
예를 들어 이런 형태다.
Authorization: Bearer eyJhbGciOi...
그러면 서버는 요청이 들어올 때마다 토큰이 유효한지 검증하고,
검증이 완료되면 해당 사용자가 로그인된 사용자라고 판단하게 된다.
즉, 세션 방식처럼 서버가 로그인 상태를 직접 저장하지 않아도 되는 구조다.
특히 서버 입장에서는 세션 저장소를 계속 관리할 필요가 줄어들기 때문에
서버 확장이나 프론트/백엔드 분리 구조에서 더 유리해지기 시작했다.
그리고 이런 흐름 속에서
대표적인 토큰 기반 인증 방식으로 떠오른 것이 JWT(JSON Web Token)다.
JWT는 사용자 정보와 만료 시간 등을 포함한 데이터를
하나의 토큰 형태로 만들어 전달하는 방식이다.
덕분에 서버는 별도의 세션 저장소 없이도
토큰 자체를 검증하는 것만으로도 사용자를 확인할 수 있게 되었다.
하지만 토큰 방식 역시 완벽한 방식은 아니었고,
편리한 만큼 새로운 보안 문제와 관리 문제도 함께 가져오게 된다.
토큰 기반 인증은 세션 방식의 한계를 해결한 건 사실이다.
하지만 이것 역시도 시간이 지나면서 여러 단점이 드러나기 시작했다.
가장 대표적인 문제는 토큰이 탈취되었을 때의 위험성이다.
세션 방식은 서버가 상태를 직접 관리하기 때문에
특정 세션을 서버에서 강제로 만료시키는 것이 비교적 쉽다.
하지만 JWT는 클라이언트가 토큰 자체를 들고 다니는 구조다.
즉, 토큰이 유효한 동안에는 서버 입장에서 해당 사용자를 정상 사용자로 판단하게 된다.
만약 토큰이 외부에 노출되거나 탈취된다면,
다른 사용자가 그대로 인증된 사용자처럼 동작할 수도 있다는 뜻이다.
그리고 JWT는 흔히 오해하는 것처럼 데이터를 암호화하는 방식이 아니다.
JWT의 Payload는 Base64 방식으로 인코딩되어 있을 뿐이며,
토큰 자체를 열어보는 것은 어렵지 않다.
이는 곧 민강한 정보를 토큰 내부에 그대로 담는 것은 위험할 수 도 있다는 문제로 직결된다.
또 다른 문제는 로그아웃 처리다.
세션 방식은 서버에서 세션을 제거하면 즉시 로그아웃 상태로 만들 수 있다.
하지만 JWT는 이미 클라이언트에게 발급된 토큰이다.
서버가 토큰을 강제로 회수하기 어렵기 때문에,
만료 시간이 끝나기 전까지는 계속 사용할 수 있는 문제가 생긴다.
그래서 등장한 개념이 바로 Access Token과 Refresh Token 구조다.
Access Token은 짧은 시간만 사용하고, Refresh Token으로 새로운 토큰을 재발급받는 방식이다.
즉, 토큰 기반 인증은 세션 방식의 한계를 해결하기 위해 등장했지만,
그 과정에서 또 다른 보안 문제와 관리 문제를 함께 안게 된 것이다.
결국 인증 방식은 "무조건 더 좋은 방식"이 등장하는 흐름이라기보다,
환경 변화에 따라 장단점이 다른 방식들이 계속 발전해온 과정에 가깝다고 볼 수 있다.
세션과 토큰은 결국엔 둘 다
HTTP가 상태를 기억하지 못한다는 문제를 해결하기 위해 등장한 방식이다.
세션의 어느 문제가 있고, 토큰에 어느 문제가 있고,
결국 인증의 역할을 똑똑히 하고 있다.
하지만 분명한 차이점이 있다.
먼저 사용자를 기억하는 방식에서부터 차이가 있다.
세션 방식은 서버가 사용자의 로그인 상태를 직접 저장한다.
브라우저는 세션 ID만 전달하고, 실제 로그인 정보는 서버 내부에서 관리된다.
반면 토큰 방식은 클라이언트가 인증 정보를 직접 들고 다닌다.
서버는 별도의 로그인 상태를 저장하기보다, 전달받은 토큰이 유효한지만 검증한다.
| 세션 기반 인증 | 토큰 기반 인증 |
|---|---|
| 서버가 상태 저장 | 클라이언트가 토큰 저장 |
| 서버 메모리/저장소 필요 | Stateless 구조에 유리 |
| 강제 로그아웃 처리 쉬움 | 토큰 만료 전까지 유지 가능 |
| 서버 확장 시 관리 필요 | 분산 환경에 유리 |
| 전통적인 웹 구조에 강함 | SPA / 모바일 환경에 강함 |
이렇게 각각 장단점이 다르기 때문에 서비스 구조에 따라 더 잘 어울리는 환경도 달라진다.
세션 기반 인증은 아래와 같은 서비스에서 여전히 잘 쓰이고 있다.
그런 전통적인 서비스에서 쓰이는 반면 토큰 기반 인증은 비교적 최신 서비스에 쓰인다.