서버가 클라이언트를 식별하는 방법
HTTP는 기본적으로 stateless다. HTTP 요청에 포함되는 정보들은 현재 요청을 처리하기 위한 정보들만을 제공할 뿐, 그 이전 또는 이후의 요청에 대한 정보는 제공하지 않는다.
그러나 이러한 정보만으로는 적절한 응답을 내려주기 어려운 요청들이 있다. 대표적인 예시로 사용자의 인증(로그인된 사용자인가?) 여부에 따라 허용을 달리하는 API로의 요청이 있다.
이러한 API를 호출하기 위해, 통상적으로 두 가지 방법이 사용된다. 하나는 서버가 부가적인 정보를 소유하는 세션 방법이고, 다른 하나는 클라이언트가 부가적인 정보를 소유하는 토큰 방법이다.
두 가지 모두 활발하게 사용되고 있고, 나름의 장단점을 가진다. 본 글에서는 두 가지의 특징을 분석하고 어떤 상황에서 어떤 방법을 사용하면 좋을지 평가해본다.
세션
세션 방식으로 사용자를 식별하는 방법은 다음의 특징을 갖는다.
- 인증 정보 발급 요청(ex: login) 시, 서버는 클라이언트가 세션 키(Session Key)라는 임의의 문자열 값을 쿠키에 저장하도록 한다.
- HTTP Response의 Set-Cookie 헤더를 이용하면 클라이언트가 특정 쿠키를 생성하도록 할 수 있다.
- 쿠키는 포함되어야 할 도메인에 속한다면 항상 요청에 동봉된다. 서버는 쿠키에 담긴 세션 키를 서버의 데이터베이스에서 조회해 사용자 데이터를 반환한다.
- 이 방식은 서버가 인증된 사용자의 데이터를 소유한다는 점에서 Stateful한 통신 방식이라고 볼 수 있다.
- 이 경우 사용자 데이터의 소유 책임은 서버에게 있으므로, 많은 사용자가 active 상태(세션 db에 정보를 저장한 상태)라면 서버의 부하가 커질 수 있다.
토큰
토큰 방식으로 사용자를 식별하는 방법은 다음의 특징을 갖는다.
- 인증 정보 발급 요청 시, 서버는 사용자 데이터를 토큰 형태로 인코딩하여 응답 바디에 담아 제공한다.
- 토큰은 다양한 포맷의 토큰이 존재한다. 주로 사용되는 토큰 중 하나인 jwt(json web token)의 경우 공식문서에서 확인할 수 있듯 payload 라는 필드에 사용자 데이터를 포함한다.
- 토큰을 받은 클라이언트는 브라우저 내 저장소(주로 로컬 스토리지)에 이를 저장한 뒤, 인증이 필요한 요청을 할 때 헤더에 이 토큰을 포함시킨다.
- 서버는 받은 토큰을 디코딩하여 사용자 데이터를 얻는다.
- 이 방식은 서버가 클라이언트 정보를 소유하지 않고 클라이언트가 보낸 토큰 정보만을 통해 사용자를 식별한다는 점에서 Stateless한 통신 방식이라고 볼 수 있다.
- 이 경우 사용자 데이터의 소유 책임은 클라이언트에게 있으므로, 토큰을 탈취당해 사용자 정보를 유출당할 수 있으며, 토큰 자체가 많은 정보를 저장할 수록 그 크기가 커지기 때문에, 인증 요청을 처리하기 위해 더 큰 트래픽을 감당해야 하게 된다는 단점이 있다.
결론
세션 방식의 장점은 다음과 같다.
- 세션 키는 일정한 길이로, 그 크기가 크지 않다.
- 서버 db에 대한 접근만 잘 방어하면 사용자 정보가 유출될 일은 없다.
세션 방식의 단점은 다음과 같다.
- 동시 접속 사용자가 많다면 서버에 부하가 올 수 있다.
- 서버의 대수를 늘리는 Scale-Out을 수행할 경우, 세션 동기화 문제를 다루어야 한다.
- 일반적으로 서버가 1대일 때에는 서버 내부의 로컬 메모리에 세션을 저장한다. 하지만 서버가 여러 대가 된다면 어떤 서버에 요청이 도착할지 모르므로, 접속한 사용자의 정보를 모든 서버가 공유할 필요가 있게 된다.
토큰 방식의 장점은 다음과 같다.
- 인증 절차를 도입해도 HTTP 통신을 Stateless하게 유지할 수 있도록 하기 때문에, 서버 확장에 용이하다.
토큰 방식의 단점은 다음과 같다.
- 쿠키보다 그 크기가 더 크다.
- 토큰은 서버에서 decode해서 사용해야 하기 때문에, 단방향 암호화 알고리즘으로 암호화할 수 없다. 때문에 외부에 유출됐을 때 사용자 정보가 유출될 수 있다.
최근에는 주로 Redis와 같은 NoSQL DBMS를 세션DB로 사용하여 여러 대의 서버가 세션 조회를 위해 이 Redis DB를 바라보게 하는 방식으로 Scale-Out 시의 세션 동기화 문제의 해결이 한층 간단해졌다. 그러나 이 방법은 또 Redis가 단일 장애 지점이 되는 만큼 Redis DB에 장애가 발생하면 현재 로그인 중인 사용자 정보가 전부 휘발된다는 문제점을 야기하기도 한다.
취사선택을 잘 해야 할 것 같다. 현재로써는 동시 접속자가 그리 많지 않은 서비스나 인증을 위해 사용자의 민감 정보가 요구되는 서비스에는 세션 방식을, 이외에는 토큰 방식을 사용하는 것이 가장 좋은 안으로 보인다.