내 프로젝트에 맞는 사용자 인증 방식은 뭘까? ( 세션 기반 vs 토큰 기반 )

생성일:

5 분 소요

들어가며

사이드 프로젝트를 시작하면 아마도 제일 먼저 구현하는 기능이 회원가입과 로그인일 것이다.
로그인 기능이 왜 필요한지 간단하게 살펴보고 다양한 로그인 방식 중 내 프로젝트의 성격에 적합한 구현 방식은 무엇인지 정리해 보았다.

로그인을 왜 할까?

모든 웹페이지가 로그인을 필요로 하진 않는다. 이벤트 안내 페이지나 간단한 심리 테스트같은 웹페이지를 생각해보면 로그인 없이 바로 해당 페이지에 접속이 가능하다.
이러한 페이지들은 사용자가 회원인지 아닌지에 상관없이 동일한 정보를 보여주면 되기 때문이다.
즉, 로그인은 사이트의 회원과 비회원인 사용자 사이에 제공하는 정보가 다를 경우 어떤 사용자가 회원인지를 확인하고, 해당 사용자가 가진 권한에 맞는 정보를 제공하기 위함이다.
여기서 회원인지 아닌지 사용자를 분별하고 확인하는 것을 인증(Athentication)이라 하며, 인증된 사용자에게 특정 리소스나 기능에 접근할 수 있는 권한을 부여하는 것을 인가(Authorization)라 한다.
페스티벌 티켓으로 예를 들자면, 티켓을 소지했는지 확인하는 행위가 인증이라면, 양일권 티켓인지 하루 티켓인지에 따라 티켓 소지자가 볼 수 있는 공연이 다르므로 인가라 볼 수 있다.

로그인은 어떻게 동작할까?

HTTP의 stateless 특성

요청을 보낸 사용자가 회원인지 서버에서 한 번만 확인하고 모든게 끝나면 좋겠지만 안타깝게도 서버는 매번 요청이 들어올때마다 이 사용자가 누구인지를 알지 못한다.
HTTP가 무상태성(stateless)이란 특징을 가지기 때문이다. 우리가 개발하는 웹사이트는 HTTP 기반에서 동작하기 때문에 한 번 요청과 응답이 완료되면 서버는 클라이언트의 이전 상태를 기억하지 않는다.
매 요청이 독립적이기 때문에 서버를 확장하기에 용이하고 대량의 트래픽을 대처하기 수월하다는 장점이 있다.
하지만 이러한 특성을 로그인에 적용해보면, 사용자가 이미 로그인을 성공해서 인증이 된 상태라 할지라도 서버는 기억하지 못하기 때문에 매 요청마다 사용자 인증을 해야한다는 것이다.

그럼 어떻게 매번 로그인을 하지 않고 로그인 상태를 유지할까?

그렇다고 사용자가 페이지를 이동할 때마다 로그인을 해야할까? 그런 불편한 사이트를 즐겁게 이용하는 사용자는 없을 것이다.
사용자 인증을 거치고 로그인 상태를 유지하기 위한 방법으로 크게 세션 기반 방식토큰 기반 방식이 있다.

세션

세션이란 사용자에 대한 정보를 저장하는 파일로 고유한 세션 ID, 로그인 시간, 만료 시간 등의 정보를 담고있다.
세션을 이용한 로그인 상태 유지 방법은 간단히 말하자면 서버와 클라이언트가 서로 세션 ID를 가지고 사용자를 인식하는 방식이다.
위의 티켓을 다시 예로 들자면, 티켓 발행처와 페스티벌 고객이 서로 티켓 예매번호를 공유하고 발행처는 예매번호 명단에서 해당 예매번호를 찾아 그에 맞는 고객 정보를 대조하여 확인하는 방식이다.

토큰

토큰은 인증된 사용자 정보와 사용자의 접근 권한에 대한 정보가 포함된 암호화 데이터 조각이다.
토큰을 이용한 상태 유지 방법은 간단히 말해 유효한 토큰을 가지고 있는지를 확인하는 방식이다.
토큰의 유효성만 확인하면 되는 이유는, 토큰 안에 사용자 인증에 필요한 모든 정보를 이미 가지고 있기 때문이다.
정확한 예시는 아니지만 위의 예시로 보자면 이미 티켓에 예매자에 대한 정보와 페스티벌 정보 등이 적혀 있어서 고객 정보가 맞는지만 확인하고 정상 티켓이면 바로 통과하는 방식이라 볼 수 있다.

세션 기반 인증 방식

세션 기반 인증 방식의 흐름

세션 기반 인증은 서버가 상태를 저장/기억하는 방식으로 작동한다. 즉, 세션 정보가 서버와 클라이언트 모두에 보관되어야 한다.
서버는 사용자 세션 정보를 메모리 또는 데이터 베이스에 저장하고, 클라이언트인 브라우저는 쿠키에 세션 ID를 저장한다.

  1. 사용자가 로그인 양식에 사용자 자격 증명(id/password)을 입력하고 POST 메소드로 양식을 제출한다.
  2. 서버는 회원인지 여부를 데이터베이스에서 대조하는 등으로 사용자를 확인한다. 인증된 사용자라면 서버는 고유한 세션 ID와 사용자 정보를 가진 세션을 생성하고 이를 서버의 저장소에 저장한다.
  3. 서버는 보통 세션 ID가 포함된 쿠키를 응답 헤더에 넣어 ( Set-Cookie 헤더 ) 브라우저로 보낸다.
  4. 세션 ID가 포함된 쿠키는 브라우저에 저장된다.
  5. 이후 요청부터는 서버로 전송될 때 세션 ID가 들어있는 쿠키가 포함되어 요청이 전송된다.
  6. 요청을 받은 서버는 세션 ID를 확인한다
  7. 세션 ID가 유효하면 서버는 요청된 리소스를 제공한다.
  8. 사용자가 로그아웃하면 서버에서 세션이 삭제되고 브라우저에서 세션 쿠키가 삭제된다.

세션 기반 인증의 장단점

세션 기반 인증의 장점

  • 세션이 서버에 저장되기 때문에 이 세션을 직접 제어할 수 있다. 예를 들어, 계정이 유출된 것으로 의심되는 경우 그 즉시 해당 세션 ID를 무효화해서 로그아웃시킬 수 있다.
  • 서버로 요청을 보낼 때 세션ID만 보내면 되기 때문에 네트워크 부하가 적다.

세션 기반 인증의 단점

  • 서버가 세션을 가지고 있어야 하므로 디폴트인 WAS 메모리에 세션을 저장한다면 WAS를 확장하는 경우에는 세션 데이터를 각 서버들이 가질 수 있도록 동기화를 따로 해줘야 한다. (세션 클러스터링)
  • 디스크를 사용하는 DB에 저장한다면 로그인 요청마다 디스크 I/O가 발생하므로 성능상 이슈가 생길 수 있다.
  • 즉, 사용자가 많고 저장하는 데이터가 많으면 서버 메모리에 부담이 커서 OOM 이슈가 발생할 수 있다. ➡️ 만료시간을 적절히 설정해서 메모리가 누적되지 않도록 관리해야 한다.

토큰 기반 인증 방식

토큰 기반 인증 방식의 흐름

토큰 기반 인증 방식은 사용자 인증에 토큰을 사용하며, 상태를 저장하지 않는 방식으로 작동한다.

  1. 사용자가 로그인 양식에 사용자 자격 증명(id/password)를 입력하고 POST 요청으로 서버에 양식을 제출한다.
  2. 서버는 회원인지 여부를 데이터베이스에서 대조하여 사용자를 확인하고 토큰(주로 JWT)을 생성한다. 이 토큰에는 사용자 정보와 권한에 대한 정보가 담겨있다.
  3. 토큰은 비밀 키로 서명되고 해싱 알고리즘으로 처리되어 해시를 생성한다.
  4. 서버는 토큰을 클라이언트로 보낸다.
  5. 클라이언트는 토큰을 받아 로컬에 저장한다.
  6. 클라이언트는 이후 요청부터는 토큰을 요청 헤더의 Authorizaion 헤더에 함께 서버로 전송한다.
  7. 서버는 각 요청에 대해 비밀키를 사용하여 토큰의 유효성을 검사하고 클레임(사용자 정보)을 추출한다.
  8. 토큰이 유효하면 서버는 요청을 승인하고 리소스를 제공한다.
  9. 사용자가 로그아웃하면 토큰은 클라이언트 측에서 파기된다. ( 서버는 알지 못한다. )

토큰 기반 인증의 장단점

토큰 기반 인증의 장점

  • 인증 정보를 저장할 필요가 없으므로 서버의 부하 감소하여 인증처리가 빨라 사용자 경험이 원활하다.
  • 상태 유지없이 서버끼리 비밀키만 공유하면 되기 때문에 서버 다중화에 유리한다.
  • 모바일 환경에서도 동작이 잘 된다.
  • Signature로 토큰의 위변조 확인이 가능하다.

토큰 기반 인증의 단점

  • 서버가 상태를 저장하지 않으므로 토큰 탈취 등의 상황에서 제어할 수 없다. ➡️ 만료 시간을 짧게 설정하고 refresh token을 함께 사용해서 access token이 탈취되어도 금방 토큰을 사용할 수 없도록 할 수 있다.
  • 클라이언트가 모든 인증 정보를 가지고 있으므로 보안면에서 불리하다 ➡️ 토큰을 HttpOnly, SameSite strict, Cookie secure 보안 설정을 활성화하여 쿠키에 저장하는 것이 권장된다.
  • 토큰의 payload에 인증 정보를 담고 있어 JWT 길이가 길다. 따라서 로그인 요청이 많으면 네트워크 부하가 심하다.
  • payload는 BASE64 인코딩된 데이터이기 때문에 디코딩하면 내용을 그대로 볼 수 있다. ➡️ 중요한 데이터는 payload에 담아선 안된다.

세션 기반 인증과 토큰 기반 인증의 주요 차이점

  • 저장 위치 : 세션은 서버에 저장되지만 토큰은 클라이언트에서만 저장한다.
  • 요청시 서버에 보내는 정보 : 세션은 쿠키를 보내고 토큰은 토큰 그 자체를 전송한다.
  • stateful vs stateless : 세션은 서버에 저장하기 때문에 상태를 유지하지만, 토큰은 서버가 정보를 저장하지 않으므로 무상태성을 띈다. 상태를 유지시키기 위한 작업이 없으면 서버를 여러대로 수평 확장하기에 더 유리하다.
  • 만료 제어 : 세션은 서버에서 무효화시키는 등의 관리를 할 수 있지만 토큰은 자체적으로 만료시간을 가지고 있으며 제어할 수 없다.

내가 사이드 프로젝트에서 선택한 방식

지금 만들고 있는 뮤직소울메이트는 결론부터 말하자면 토큰 기반 인증을 사용하기로 했다.
토큰 방식을 선택한 이유는 다음과 같은 프로젝트의 성격 때문이었다.

  • 대규모 시스템 설계를 연습하기 위해 사이드 프로젝트이지만 많은 사용자가 이용하는 서비스라 가정한다.
    • 서버를 여러 대로 스케일 아웃하기에 편해야 한다. 즉, 확장성이 좋아야 한다.
  • 서비스에서 요구하는 사용자 정보는 많지 않다. (이메일, 권한 )
    • payload에 실을 민감 데이터가 많지 않다.
  • 규모가 커지면 MSA로 변경하는 것을 염두하고 있다.
  • 음악 스트리밍 기능도 있기 때문에 앱 서비스를 제공하는 것을 염두하고 있다.

결론

프로젝트에서 로그인 기능을 구현함에 있어 세션 기반 인증과 토큰 기반 인증은 각각의 장단점을 가지고 있으며, 프로젝트의 요구사항과 목표에 따라 적합한 방식을 선택해야 한다.
세션 기반 인증은 상태를 서버에서 관리하는 전통적인 방식으로, 안전한 인증 관리가 가능하지만 서버의 부하와 확장성에 제한을 받을 수 있다.
반면, 토큰 기반 인증은 상태를 클라이언트에 저장하여 서버의 부담을 줄이고 확장성을 높이지만, 토큰 관리와 보안에 주의가 필요하다.

참고

  • https://www.criipto.com/blog/session-token-based-authentication

댓글남기기