티스토리 뷰
JWT가 등장하게 된 배경
초기의 웹 서비스에서는 사용자가 로그인하면 서버가 사용자 정보를 세션에 저장하고 클라이언트에게는 세션 ID를 쿠키에 담아 전달해 인증 상태를 유지했습니다.
하지만 최근 들어
- MSA 아키텍처의 도입
- 프론트엔드와 백엔드 서버의 분리
- 모바일 앱 및 다양한 디바이스의 등장
으로 인해 API 서버가 여러 클라이언트의 요청을 받아야 하는 환경이 되었습니다.
결국 서버가 인증 상태를 관리하지 않고 매 요청마다 독립적으로 인증할 수 있는 Stateless한 방식이 필요해졌습니다.
이 문제를 해결하기 위해 등장한 것이 바로 JWT(Json Web Token) 입니다.
세션(Session) 동작 원리
- 사용자가 로그인하면 서버는 세션 ID를 생성하고 세션 DB(또는 Redis)에 사용자 정보를 저장합니다.
- 서버는 클라이언트에게 세션 ID를 쿠키에 담아 내려줍니다.
- 클라이언트는 이후 요청마다 쿠키가 서버로 전송이 되므로 세션 ID가 자동으로 서버에 보내집니다.
- 서버는 세션 저장소를 조회해 해당 세션 ID에 매핑된 사용자 정보를 읽어 인증합니다.
Tip) 세션 DB는 따로 있는 DB가 아니고 메모리 장소를 지칭하는 것입니다.
솔직히 되게 편하게 사용할 수 있고 안전하게 사용할 수 있어서 좋습니다.
근데 왜?! JWT가 뜨고 처음 배우는 신입 개발자들이 JWT를 찬양하는 것일까...?
세션도 편하고 좋은디..?
JWT 동작 원리
- 사용자가 로그인하면 서버는 JWT 토큰을 발급합니다.
- 서버는 클라이언트에게 JWT 토큰을 쿠키 또는 헤더에 내려줍니다.
- 클라이언트는 토큰을 보관하고 이후 요청마다 쿠키 또는 HTTP Header 등에 담아 보냅니다.
- 서버는 토큰을 검증만 한다. (DB/메모리 접근 없이!)
이 구조를 보고 생각난 것이 자유이용권 티켓!! 입니다.
매표소, 입장소?는 "난 당신이 누구인지 알 필요 없고, 티켓이 유효한지 검사 할거야!" 라는 입장을 가지는 것이랑 동일하게 생각됩니다.
그래서 티켓(JWT)이 유효하면! 입장소?(서버)는 그냥 통과를 해주면 됩니다.
자유이용권 구매한 사람이 자기 티켓 잃어버리면 지 책임이지 뭐...라고 생각했는데 이게 엄청난 단점이네.. 해커들 날뛰겠네...
JWT의 구조
왼쪽으로 보이는 이상한 문자열이 JWT이고, 세부 내용은 크게 3 부분으로 나뉩니다.
- Header : 타입(JWT)과 해시 알고리즘 정보
- Payload : 사용자 정보(Claims)
→ 다른 사람이 볼 수 있기 때문에 엄청 중요한 민감한 정보는 넣지 말아야 합니다. - Signature : Header + Payload를 비밀키로 서명한 것
Payload에는 이런 정보가 주로 들어갑니다.
- sub (subject) : 사용자 고유 식별자(ID)
- iss (issuer) : 토큰 발급자
- aud (audience) : 토큰을 사용할 대상 (ex. API 서버이름)
- exp (expiration time) : 토큰 만료 시간
- iat (issued at) : 토큰 발급 시간
- roles : 사용자의 권한
JWT의 장점
- 이제 사용자가 "내 거 유효한지 검사해 줘" 요청이 올 것이고 그럼 서버는 사용자 인증 상태를 기억할 필요가 없습니다
따라서 여러 대의 서버가 그냥 JWT 유효한지 검사만 하면 되므로 Scale-out에 유리하게 됩니다. - MSA 아키텍처의 도입, 프론트엔드와 백엔드 서버의 분리, 모바일 앱으로 JWT가 등장하고 유리하게 되었다고 말했었습니다.
따라서 클라이언트인 앱, 웹과 쉽게 연동이 가능하므로 편리하여 서버 확장이 쉽습니다. - DB를 1번이라도 더 안 타게 할라는 목적이 있습니다.
- 세션 저장소처럼 별도의 저장소가 필요 없습니다. (표 검사 하는 사람이 모든 사람의 정보를 들고 다니진 않으니까요 ㅎㅎ)
Tip) QR 체크인도 JWT로 구현된 것이다.
JWT의 문제점
- 최신 버전의 라이브러리면 괜찮은데 예전 버전이면 알고리즘에 none을 넣고 뚫을 수 있다고 합니다. (
유튜브 코딩애플님이..) - JWT 본문인 Payload에 암호화하지 않고 정보를 넣으면 노출될 수 있기 때문에 많은 정보나 민감한 정보를 저장하여 사용하기 어렵습니다.
- JWT는 발급이 되면 만료시간이 될 때까지 유효한 티켓이 됩니다. 즉, 누가 훔쳐가도 사용할 수 있다는 것입니다.
근데 누가 카드를 훔쳐가면 카드 도난 신고를 하는데 JWT도 도난 신고를 하면 되지 않느냐라고 할 수 있을 텐데.. 이건 말이 안 됩니다..
그럼 해결책 및 차선책으로 토큰 블랙리스트를 관리하거나 짧은 만료시간의 전략으로 가져가는 것도 방법입니다.
여기서 토큰 블랙리스트는 결국 어딘가 메모리에 저장되거나 DB에 저장되어야 하는데 이건 세션과 별 차이가 없어서 잘 생각해야 할 것입니다. - 이건 사소 한 건데 JWT 시크릿 키를 "secret" 이런 식으로 하여 때려 맞추기 쉽게 만들면 바로 뚫립니다..
- 잘못 로직을 짜면 세션과 더불어 DB를 여러 번 타는 불상사가 생길 수 있습니다.
- 정말로 제대로 알지 않고 사용하면 인증, 보안에서 정말
ㅈ될 수도 있습니다.
세션(Session)은?
세션 방식도 여전히 좋은 선택지입니다. 왜냐? 처음 개발자가 배울 때 세션을 배우 듯 정말 편합니다.
서버가 인증 상태를 직.접 관리해서 그런지 인증된 사용자를 강제 만료, 로그아웃 처리할 수 있습니다.
그 기능을 찾아본다면 다른 기기 로그아웃 기능입니다.
그리고 서버에 저장되므로 민감한 정보를 편하게~ 보관이 가능합니다.
클라이언트 단에도 굳이 정보를 저장하지 않아도 되므로 화면단 로직은 편해질 것입니다.
세션도 단점이 있다.
- 서버에 세션 저장공간(Redis, 서버 내 세션 DB)이 필요합니다.
- 사용자가 많아져서 서버 추가 시 세션 저장소1에는 저장되었지만 세션 저장소2에는 저장이 안 되어 있는 세션 동기화 문제가 발생할 수 있습니다.
이 문제를 해결하기 위해서는 Redis를 사용하거나 세션 클러스터링이라는 기술로 해결합니다.
JWT + Redis가 빛나는 별인 줄 알았어요.
JWT와 Redis가 완벽한 조합인 줄 알았는데 실제로 사용해 보니 몇 가지 중요 포인트를 깨닫게 되었습니다.
저는 로그인을 하면 AccessToken을 발급하고 RefreshToken은 Redis에 저장하여 관리하는 방식으로 토큰 기반 인증을 구현했었습니다.
- Redis의 데이터 시간과 RefreshToken 만료시간을 똑같이 지정하여 관리하기 쉽게 만들었습니다.
- Redis의 값과 사용자의 RefreshToken 값을 비교해 변조를 방지했습니다.
이 이유로 사용하게 되었습니다.
AccessToken은 주기를 짧게 가져가고 재발급을 진행하면 RefreshToken도 재발급이라는 로직을 녹이게 되었는데, RefreshToken 자체에는 최소한의 정보만 포함되어 있어, 재발급을 위해서는 사용자의 상태나 권한 정보를 DB에서 조회해야 했었습니다.
그럼 이러한 궁금증이 생기기 시작했습니다.
- 어차피 DB 조회를 하는 것인데 이게 맞는가?
- Redis가 죽으면 항상 DB 탈 텐데 JWT 쓰는 이유가 무엇인가?
- 그럴 거면 접근할 때마다 JWT 생성해주는 건 어떤가?
→ 엥? 그건 아니지 세션하고 별 다를 게 없잖아.
많은 생각이 스쳐 지나가게 되었습니다.
실제로 사용해 보니 사용자 정보를 최소한으로 해서 넣어서 그런지 DB를 탈 수밖에 없는 구조이거나 민감한 정보가 꼭 들어가게 되었습니다. 트레이드 오프해야 하는 상황도 많들어졌습니다. 그래서인지 완벽할 것 같았던 JWT + Redis는 막을 내리게 되었습니다.
Tip) 로그아웃할 때 토큰 다 없애야 합니다.
그런데 왜 세션 + Redis를 많이 쓸까?
유튜브 한 영상의 댓글 중 1개입니다.
세션 + Redis? 처음에는 엥? 이라고 생각했습니다.
이후에 제가 지하철에서 핸드폰을 안 보니 그냥 쓸데없는 잡생각을 하는데 퇴근하는 시간에 한번 생각하게 되었습니다.
- 운영 환경은 서버가 여러 대이기 때문에 서버 간 세션 공유 문제를 해결하려고 세션 저장소로 Redis 같은 메모리 DB를 사용
- 세션 스토리지가 중앙 집중화되면서 서버 확장(Scale-out)이 용이
- 메모리 DB이기 때문에 빠른 인증, 성능
조금 더 검색해 보니 분산 환경에서 세션 관리를 효율적으로 처리할 수 있는 강력한 조합이라는 결론을 내리게 되었습니다.이게 바로 세션 + Redis 이다!
그렇다면 당신은 무엇을 선택할 것인가?
모든 사람이 그러듯 정답은 없습니다.
시스템 특성과 상황을 정확히 파악하고, 그에 맞는 방법을 선택하는 것이 진짜 실력이라고 합니다.
저는 대규모 API 서비스를 해보는 것, 앱을 만들어서 운영하는 것이 꿈이기 때문에 JWT를 선택하여 진행하겠습니다.
(프론트와 백 나눠져 있으니 이젠 JWT로 내려줄래)

감사합니다.
'백엔드 > 🎃CS' 카테고리의 다른 글
[디자인패턴] Proxy 프록시 패턴 : 코드 보안과 성능 향상을 위한 패턴 (0) | 2024.05.30 |
---|---|
[디자인패턴] Strategy 전략 패턴 : 효율적인 코드 재사용을 위한 전략 패턴 (0) | 2024.05.23 |
[디자인패턴] Template Method 템플릿 메서드 : 모순없는 상태 보장하기 (0) | 2024.05.16 |
[Process] 더보기 : 컨텐츠 더 보기 기능을 구현할 때 고려해야할 것 (0) | 2024.03.14 |
[디자인패턴] Singleton 싱글톤 : 프로그래밍 세계의 유일무이한 인스턴스 (0) | 2024.02.21 |
- Total
- Today
- Yesterday
- Cors
- JavaScript
- Front
- 개발환경
- Spring Security
- 트랜잭션
- 깃허브 액션
- 개발블로그
- AJAX
- 인증
- Fetch
- 실시간 채팅
- spring
- DBeaver
- 네트워크
- 디자인패턴
- 데이터 베이스
- java
- 개발
- 프론트
- 계단 오르기
- 코딩테스트
- aws
- 비동기
- 개발자
- 오라클
- 로그
- 템플릿
- jvm
- 프로세스
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |