들어가며
처음 jwt를 접하고, 적용했던 경험은 '직관의 맛' 프로젝트였다.
당시 로그인 시스템을 구현하기 위해 Node.js로 백엔드 서버를 구현하면서 jwt 키도 발급받고, 관련 미들웨어도 구성하는 등 어찌저찌 꾸역꾸역 했던 기억이 있다.
그러다보니 아무래도 깊이 있는 이해가 부족했을 것이고, 나는 JWT를 '안전한 사용자 인증'을 위해 사용한다고만 생각했다.
최근에서야 부트캠프에서의 강의와 이력서 첨삭을 통해 JWT와 이를 사용하는 이유를 보다 깊이 있게 이해하게 되었다. 그 내용을 내가 작성한 코드와 함께 알아보잡!🤔
JWT를 왜 사용할까❓
JWT라는 토큰 기반 인증 방식을 왜 사용하는지에 대해서 알아보려면 기존에 사용하던 서버(세션) 기반 인증 방식에 대해서 알아볼 필요가 있겠다.
차이점
1. 서버(세션) 기반 인증 (Session & Cookie 방식)
- 사용자가 로그인을 하면, 서버는 해당 사용자를 위한 세션을 생성하고, 세션 ID를 발급한다.
- 세션 ID는 클라이언트의 쿠키(Cookie)에 저장된다.
- 이후 클라이언트가 요청을 보낼 때, 쿠키에 저장된 세션 ID를 함께 전송한다.
- 서버는 세션 ID를 확인하여 해당 사용자의 정보를 조회하고 요청을 처리한다.
✅ 장점
- 보안성 → 세션 ID는 서버에서 관리되므로 클라이언트에서 변조가 어렵다.
- 만료 및 관리 용이 → 서버에서 강제로 세션을 삭제하거나 만료시킬 수 있다.
❌ 단점
- 서버 부담 → 모든 사용자의 세션을 서버에서 관리해야 하므로, 사용자가 많아질수록 메모리 부담이 증가한다.
- 확장성 문제 → 서버가 여러 개일 경우(로드 밸런싱), 각 서버가 세션을 공유해야 하는 추가 작업이 필요하다.
- CSRF 취약성 → 쿠키 기반 인증이므로 CSRF(Cross-Site Request Forgery) 공격에 취약할 수 있다.
2. JWT 기반 인증
- 사용자가 로그인을 하면, 서버는 JWT를 생성하여 클라이언트에 반환한다.
- JWT는 클라이언트의 Local Storage 또는 HttpOnly Cookie에 저장된다.
- 이후 클라이언트가 요청을 보낼 때, JWT를 Authorization 헤더에 포함하여 서버에 전달한다.
- 서버는 전달받은 JWT의 서명을 검증하여 요청을 처리한다.

✅ 장점
- Stateless(무상태) → JWT는 자체적으로 정보를 포함하고 있으므로, 서버가 별도로 사용자 정보를 저장하지 않아도 된다.
- Scalability(확장성) → 서버에서 세션을 관리하지 않기 때문에, 로드 밸런싱이 쉬워지고 확장성이 증가.
- 다양한 플랫폼에서 사용 가능 → 쿠키 기반 인증은 브라우저 환경에서 주로 사용되지만, JWT는 모바일 앱, API 서버 등에서도 쉽게 활용 가능.

❌ 단점
- 보안 → JWT는 기본적으로 암호화되지 않으므로, 탈취되면 누구나 내용을 확인할 수 있다.
- ⁉️당연히 암호화 된다고 생각했었다...;; 하지만 이 곳에서 얼마든지 decoding이 가능했다는 것을 깨달아버렸다.(링크)
- 토큰 강제 만료시키기 어려움 → 서버에서 특정 사용자의 토큰을 폐기하려면 별도의 관리가 필요하다.
- 토큰 크기가 큼 → JWT는 사용자 정보(Claims)를 포함하기 때문에 세션 ID보다 크기가 커질 수 있다.
🌱 정리
- 사용 목적에 알맞게 사용하면 좋겠다.
- 상대적으로 JWT는 토큰 유출 시 누구나 해독할 수 있지만 변조로 인한 위험 발생 가능성이 적다. (참고)
- 그리고 서버에서 상태를 관리하지 않기에 부담이 적고, 세션을 관리하지 않으므로 확성성도 높다.
Stateless(무상태)한 인증 방식의 필요성
Stateless(무상태)란, 서버가 클라이언트의 요청 정보를 저장하지 않는 방식을 의미한다. 즉, 서버는 클라이언트의 상태(Session)를 기억하지 않고, 각 요청을 독립적으로 처리한다는 뜻이다.
위에서도 언급된 내용이지만 자꾸 반복되는 것은 JWT를 사용함에 있어 핵심적인 요소라고 판단되기 때문이다.
✔️ 따라서, 서버는 요청을 받을 때마다 JWT의 서명을 검증하고, 그 안의 정보를 읽어 처리하기 때문에 서버가 로그인 상태를 저장할 필요 없다. -> Stateless 하다!
Stateless 인증 방식의 장점은 다음과 같다.
✅ 서버 부담 감소 → 클라이언트가 인증 정보를 저장하고 요청할 때마다 전달하므로, 서버는 세션을 관리할 필요가 없다.
✅ 확장성 증가 → 여러 서버가 존재하는 환경(로드 밸런싱)에서도 별도의 세션 공유 없이 인증 가능하다.
✅ 빠른 인증 처리 → 요청 시 서버가 DB에서 세션 정보를 조회할 필요 없이, 토큰의 서명 검증만 하면 된다.
그렇다면 JWT가 완벽한 솔루션일까?🤔🤔
JWT는 Stateless 인증 방식으로 확장성과 성능이 뛰어나지만, 보안 문제를 고려해야 한다.
위 JWT 단점에서 살펴본 것과 같이,
- JWT는 기본적으로 암호화되지 않으므로, 노출될 경우 누구나 토큰을 해독할 수 있다.
- 세션 기반 인증과 달리, 토큰을 강제로 만료시키기 어렵다. (이를 해결하기 위해 블랙리스트, Refresh Token을 사용)
🌱 Stateless 방식은 확장성과 성능 면에서 강력한 장점이 있지만, 보안을 위해 추가적인 조치를 고려해야 한다!
JWT는 암호화되지 않는다⁉️
앞서 살펴본 바와 같이,
JWT는 기본적으로 암호화(Encryption)가 아닌 서명(Signature)이 목적이다. (위조 방지! 인증!)
서명된 토큰은 무결성을 보장하지만, 토큰 자체는 누구나 디코딩 가능하다. 그렇기에 JWT가 노출될 경우 보안 문제 발생 가능하므로, 민감한 정보는 Payload에 담지 않는다.
결국, Signature에 사용된 비밀키가 노출되지 않는다면 데이터를 위조해도 여기서 걸러질 것이기에 위와 같은 목적이 가능하다고 본다.
JWT를 안전하게 사용하려면❓
- Access Token과 Refresh Token을 분리하여 사용
- JWT도 제 3자에게 토큰 탈취의 위험성이 있기 때문에, 그대로 사용하는것이 아닌 Access Token, Refresh Token 으로 이중으로 나누어 인증을 하는 방식이 필요하다.
- Access Token : 클라이언트가 갖고있는 실제로 유저의 정보가 담긴 토큰으로, 클라이언트에서 요청을 보내면, 서버에서 해당 토큰에 있는 정보를 활용하여 응답.
- Refresh Token: 새로운 Access Token을 발급해주기 위해 사용하는 토큰으로 짧은 수명을 가지는 Access Token에게 새로운 토큰을 발급해주기 위해 사용
- 즉, Access Token 만을 이용하면, 토큰이 탈취될 경우에는 토큰이 만료되기 전 까지 토큰을 획득한 사람은 누구나 권한 접근이 가능해지는 문제점이 있다.
- 그래서 토큰의 유효 시간을 짧게 부여하여 탈취 문제에 대해 대응을 하기도 한다.
- 하지만 유효 기간이 짧을 경우 그만큼 사용자는 로그인을 자주해야 하는 번거로움이 있다.
- 따라서 이러한 문제를 해결하기 위해 Refresh Token 이라는 추가적인 토큰을 활용하여 토큰을 이중으로 보호한다고 생각하면 좋겠다.
- HTTPS를 통한 안전한 전송
- HTTPS를 사용해서 전송 중간에 토큰이 탈취되는 것을 방지한다.
- JWT는 자체적으로 암호화되지 않기에, 네트워크에서 탈취될 경우 누구든지 해당 토큰을 사용할 수 있다.
- 토큰 저장 방식 (Local Storage vs. HttpOnly Cookie)
가. Local Storage (권장하지 않음)
-
JavaScript로 접근 가능하기 때문에 XSS(Cross-Site Scripting) 공격에 취약하다.
나. HttpOnly Cookie ✅ (권장)
-
HttpOnly, Secure 속성을 추가하여 JavaScript에서 접근하지 못하도록 설정한다.

마무리
🌱 JWT를 선택한 이유
- 이제는 JWT의 본질에 가까운 답변을 할 수 있겠다.
- '안전한' 보다는 Stateless 한 특징을 활용해서 성능적으로 이점을 가져갈 수 있다.
- 클라이언트가 모바일과 같은 다양한 환경에서도 인증을 할 수 있다.
- 다른 서버스와 연동이 용이하며, 여러 서버를 활용할 경우 인증을 위해 서버를 특정할 필요가 없기에 확장성 측면에서도 이점이 있다.
🤔 개선해야할 부분
- Refresh Token 활용
- 내가 JWT를 적용한 프로젝트에서는 Access Token만 활용했다. 그래서 토큰의 유효기간 또한 별 생각없이 적당히 이 정도면 되겠다! 하고 부여했었다.
- 이 기간을 짧게 하고, Refresh Token을 활용해서 보안성을 강화해야겠다.
- HttpOnly Cookie 활용
- 위 코드처럼 내가 직접 백엔드 서버를 구현한 프로젝트에서는 HttpOnly Cookie를 활용할 수 있었지만,
- 부트캠프에서 제시된 서버에서는 Local Storage 나 Session Storage를 활용했었다.
- 당시에는 서버를 손댈 수 없었고 개념적으로도 명확하지 않았으나, 이제는 어떤 방식이 보안 측면을 보완할 수 있을지에 대해 명확히 알게 되었다.
참고한 사이트 (감사합니다)
1. https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-JWTjson-web-token-%EB%9E%80-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC#jwt_json_web_token_%EC%9D%B4%EB%9E%80
2. https://velopert.com/2350
3. https://cbw1030.tistory.com/331
4. https://auth0.com/learn/json-web-tokens?_gl=1*1jrh5v*_gcl_au*MTg5ODc2NDE4OC4xNzQyMzQ4Mzkx*_ga*NDk4NDA3MTIyLjE3MzM1NzQ2OTc.*_ga_QKMSDV5369*MTc0MjM0ODM5MS4yLjEuMTc0MjM0OTQyOC42MC4wLjA.