
우리는 다양한 사이트를 방문하다 보면 아래와 같은 알림을 받을 때가 있다.

사이트에서 Cookie에 대한 내용은 2가지만 설명하고 넘어가겠다.
- 해외 사이트에서 유독 물어보는 이유는 유럽 연합의 GDPR(General Data Protection Regulation) 때문이다.
법적 규제로 사용자의 개인 데이터를 수집하기 전 동의를 얻는 과정이다. - 개인 정보 보호 : 쿠키는 사용자의 방문 기록, 로그인 정보 등등에 대한 내용을 저장할 수 있다.
2번째 내용이 이 글의 중요한 주제이므로 설명하자면,
Cookie란 무엇인가?
정의 : 쿠키는 웹 브라우저에서 저장되는 작은 데이터 파일이다. 서버가 사용자의 브라우저에 전송하며 이후 사용자가 재 방문하였을 때 쿠키에 담긴 정보를 다시 서버로 전송할 수 있다.
그렇다면 왜 사용하는가?
정의의 내용 중 작은 데이터파일의 역할 이므로 쿠키에 사용자의 로그인 상태를 유지하기 위한 정보를 담고 있을 수 있다.
여기까지만 듣는다면 쿠키로 사용자 정보를 관리하는구나 라고 생각할 수 있다.
그렇지만 쿠키에는 가장 큰 문제점이 있었다.
- 보안문제 : 쿠키는 사용자의 브라우저에 저장되므로 민감한 정보를 저장하기에는 위험하다.
만약 쿠키가 탈취 또는 위변조 되어 접근한다면 사용자의 개인정보가 노출될 수 있다!
그렇다면 Session은 어떨까?
Session은 무엇인가?
정의 : 서버 측에서 사용자 별로 상태를 관리하는 방법이다. 사용자가 웹사이트를 방문하는 동안 서버에 사용자 정보를 저장하고, 사용자가 사이트를 떠나거나 일정 시간이 지나면 세션이 종료된다.
세션은 보안을 생각한다면 좋은 선택지라고 생각한다.
하지만 세션방식을 사용하게 된다면 사용자 세션 데이터가 모두 서버에 저장된다.
이는 곧 서버의 자원을 많이 소모하게 된다.
그렇다면 JWT도 알아보자.
JWT란 무엇인가?
JWT는 JSON Web Token의 약자로, JSON 객체를 사용하여 두 개체 간에 정보를 안전하게 전송하기 위한 방식이다.
JWT는 크게 세 부분으로 구성되어있다.
Header (헤더): 헤더는 토큰의 유형(JWT)과 해싱 알고리즘(예: HMAC SHA256 또는 RSA)을 지정한다.
{
"alg": "HS256",
"typ": "JWT"
}
Payload (페이로드): 페이로드는 클레임(claims)을 포함하고 있으며, 클레임은 엔티티(일반적으로 사용자)와 추가 데이터를 나타내는 키-값 쌍이다. 클레임에는 등록된 클레임(registered claims), 공개 클레임(public claims), 비공개 클레임(private claims)이 있다.
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Signature (서명): 서명은 헤더와 페이로드를 인코딩한 후, 지정된 알고리즘을 사용하여 비밀 키로 서명한 것이다. 이는 토큰의 무결성을 검증하는 데 사용된다.
예시: HMAC SHA256 알고리즘을 사용할 때는 다음과 같이 서명한다.
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
JWT의 구조는 아래와 같은 형태로 이루어져 있다.
xxxxx.yyyyy.zzzzz
xxxxx: Base64 Url로 인코딩 된 헤더
yyyyy: Base64Url로 인코딩된 페이로드
zzzzz: 서명
그렇다면 JWT Token 하나로 사용자 로그인 정보를 관리할 수 있을까?
가능하다. 다만 Token 하나로 사용자를 관리하게 된다면 Token의 유효기간이 길어지게 된다.
유효기간이 길어진다면, Token을 가지고 있다가 Cookie처럼 토큰이 탈취되면 보안상 문제가 발생하게 된다.
그렇다면 더 나은 방법은?
Token을 한 개가 아닌 Refresh Token과 Access Token 2가지로 나눠서 관리하는 것이다.
Access Token : 사용자가 서버에 요청을 보낼 때 인증을 위해 사용된다. 일반적으로 짧은 수명을 가지게 되며 이유는 토큰이 탈취되어도 짧은 시간 내로 설정한다면 기간이 만료된 토큰으로 만들기 때문이다.
Refresh Token : Access Token이 만료되었을 때, 새로운 Access Token을 발급한다. Access Token에 비해 유효기간이 긴 편이다.
JWT를 활용한 로그인 인증 로직 flow는 아래와 같다.

JWT를 RDBMS 또는 NoSQL에 저장하기에 디스크 기반 스토리지를 사용하여 읽기 쓰기 속도가 느리다.
그래서 사용하게 된 것이 Redis이다.
Redis에 대해 자세한 내용은 추후 정리하여 게시글을 작성할 계획이므로 간단하게 알아보자.
Redis는 메모리 기반 데이터 저장소로, 빠른 읽기, 쓰기 성능을 가지고 있다.
데이터를 저장할 때 Key, Value 형태로 저장하기 때문에 복잡한 데이터 모델링이 필요하지 않다.
또한 TTL(Time-To-Live) 기능을 지원하기 때문에 Access Token과 Refresh Token의 유효기간을 쉽게 관리할 수 있다.
이러한 이점을 생각하여 Redis를 사용하게 되었다.
다만 프로젝트에서 요구하는 기능 중에서 회원의 로그아웃, 전체 기기 로그아웃을 요구하는 내용이 있었다.
구현에 대해 생각해 보니 로그아웃을 처리할 때 발급한 Refresh Token을 무효화시켜도 기존에 발급한 Access Token을 어떻게 관리해야 하나 고민을 하였고 2가지의 방법을 찾았다.
- Access Token의 유효기간을 매우 짧게 설정한다.
- Access Token을 블랙리스트로 관리한다.
2가지 방법 모두 채택하여 프로젝트에 적용하기로 하였다.
1번의 경우 보안을 위해 토큰이 탈취되어도 짧은 유효기간으로 만료가 되기에 선택하였고,
2번의 경우 Token이 탈취되었거나 사용자가 로그아웃 이후 발급된 Access Token으로 접근하여도 이를 사전에 방지하기 위해 선택하였다.
그렇다면 JWT의 장점인 Stateless는..?
Stateless의 장점을 포기하여도 선택한 이유는 대규모 애플리케이션에서는 JWT의 확장성과 분산 처리의 이점을 살릴 수 있다.
그러나 소규모 애플리케이션에서는 세션 기반 인증이 더 단순하고 저렴할 수 있다.
따라서 애플리케이션의 특성, 사용자 수, 서버 인프라 등을 고려하여 적절한 방식을 선택해야 한다.
- 출처