JWT
JSON Web Token
도구
누구인지와 언제까지 유효한지를 적고 서명을 붙인 문자열로, 요청할 때마다 신원을 증명하는 데 씁니다.
등장 배경
웹은 요청 하나가 끝나면 서버가 상대를 잊어버리도록 만들어졌습니다. 로그인을 한 번 했다고 해서 다음 요청이 저절로 그 사람의 것이 되지는 않습니다. 화면을 한 번 여는 동안에도 요청은 수십 번 오가므로, 매번 누구인지를 다시 밝혀야 합니다. 비밀번호를 매번 보낼 수는 없으니 대신 로그인에 성공한 사람에게 임시 표를 하나 주는 방식이 굳어졌습니다.
오래 쓰인 방식은 서버가 기억하는 쪽이었습니다. 로그인하면 서버가 접속 기록을 자기 메모리에 적어 두고 그 기록의 번호만 브라우저에 넘깁니다. 브라우저가 그 번호를 들고 오면 서버는 자기 메모에서 번호를 찾아 누구인지 확인합니다. 서버가 한 대일 때는 잘 돌아갑니다.
페이지덱이 서버를 두 대로 늘린 날 문제가 드러났습니다. 사용자가 새로고침을 할 때마다 로그인이 풀렸습니다. 요청이 1번 서버로 가면 기록이 있는데 2번 서버로 가면 없었습니다. 태오가 확인해 보니 접속 기록이 각 서버의 메모리에만 있었습니다. 기록을 모아 둘 저장소를 따로 세우는 것이 한 가지 답이고, 서버가 아예 기억하지 않게 만드는 것이 다른 답입니다. JWT 는 뒤쪽을 고른 결과입니다.
해결하는 문제
기억하는 쪽을 서버에서 표 자체로 옮깁니다. 서버가 목록을 뒤져 확인하는 대신, 표에 적힌 내용이 위조되지 않았다는 것만 확인하면 되게 만듭니다.
표는 점 두 개로 이어진 문자열이고 세 토막입니다.
- 헤더 — 이 표에 어떤 방식으로 서명했는지 적습니다. 받는 쪽이 같은 방식으로 다시 계산하려면 필요합니다.
- 페이로드 — 누구인지, 어떤 역할인지, 언제까지 유효한지를 적습니다. 한 칸 한 칸을 클레임이라고 부릅니다. 사용자 번호가 1024 이고 역할이 편집자이며 만료가 15분 뒤라는 식입니다.
- 서명 — 앞의 두 토막을 서버만 아는 열쇠와 함께 계산해 얻은 값입니다.
요청이 들어오면 서버가 앞의 두 토막을 자기 열쇠로 다시 계산해 보고, 나온 값이 세 번째 토막과 같으면 통과시킵니다. 누군가 페이로드에서 역할을 편집자에서 관리자로 고치면 계산 값이 달라지므로 그 자리에서 걸립니다. 저장소를 한 번도 들여다보지 않고, 서버가 몇 대든 같은 열쇠만 갖고 있으면 됩니다.
앞의 두 토막은 잠겨 있지 않습니다. 읽기 편한 글자로 바꿔 둔 것일 뿐이라 표를 손에 넣은 사람은 누구나 내용을 그대로 펼쳐 볼 수 있습니다. 서명이 막는 것은 읽는 일이 아니라 고치는 일입니다. 내용을 감춰야 한다면 암호화를 따로 얹어야 합니다. 페이로드에 전화번호나 주소를 넣지 않는 이유가 이것입니다.
대가도 분명합니다. 서버가 기억하지 않으므로 한 번 나간 표를 도중에 무를 방법이 마땅치 않습니다. 계정을 정지시켜도 이미 나간 표는 만료 시각까지 그대로 통합니다. 그래서 유효기간을 15분처럼 짧게 두고, 그 표가 만료되면 따로 보관한 재발급용 표로 새 표를 받아 오는 구조를 함께 씁니다. 재발급용 표는 서버가 기록해 두므로 그쪽은 즉시 막을 수 있습니다.
대안
표를 주고받는 방식은 셋 중에 고릅니다.
- 세션 방식 — 앞서 나온 서버가 기억하는 방식입니다. 기록이 한곳에 있으니 계정을 정지시키는 즉시 접속이 끊깁니다. 대신 요청마다 저장소를 한 번씩 조회해야 하고, 그 저장소가 멈추면 전체 로그인이 함께 멈춥니다.
- 내용 없는 토큰 — 표에는 아무 뜻 없는 문자열만 적고, 그 문자열이 누구 것인지는 발급한 쪽에 물어봅니다. 내용이 밖으로 새지 않고 즉시 취소도 되지만 확인할 때마다 왕복이 한 번 더 붙습니다.
- JWT — 확인에 왕복이 없고 서버를 늘리기 쉽습니다. 취소가 늦고 내용이 노출된다는 점을 감수합니다.
사용자 수가 적고 즉시 차단이 중요한 사내 도구라면 세션 방식이 편합니다. 서비스가 여러 개로 쪼개져 있고 각자 따로 확인해야 하면 JWT 쪽이 유리합니다. 같은 이유로 싱글 사인온, 곧 한 번 로그인으로 여러 서비스를 함께 쓰게 하는 구조에서 JWT 가 자주 보입니다. 서비스마다 로그인 서버에 물어보지 않고 표 하나만 들고 다니면 되기 때문입니다.
실무 용례
"401 떨어지면 리프레시 한 번 태우고 같은 요청을 다시 보내게 해 주세요."
표가 만료됐다는 응답이 오면 새 표를 받아 재시도하도록 처리해 달라는 요청입니다.
"페이로드에 이메일까지는 넣어도 되는데 전화번호는 빼죠."
표 안은 누구나 열어 볼 수 있으니 민감한 값을 담지 말자는 말입니다.
"정지시킨 계정인데 15분 동안은 그대로 들어와져요."
이미 나간 표가 살아 있어 만료 전까지는 막히지 않는다는 뜻입니다.
페이지덱이 기업 고객을 처음 받던 달의 장면입니다. 고객사 보안 담당이 퇴사자 계정을 즉시 차단할 수 있느냐고 물었고, 태오는 최대 15분이 걸린다고 답했습니다. 그대로는 계약 조건에 못 미쳤습니다. 표의 유효기간을 1분으로 줄이는 안이 먼저 나왔지만 재발급 요청이 열다섯 배로 늘어나는 문제가 있었습니다. 결국 정지된 계정 목록만 따로 두고 중요한 요청에서만 그 목록을 확인하는 쪽으로 정리했습니다.
여기서 인증과 인가가 갈립니다. 표가 위조되지 않았다는 확인이 앞의 것이고, 그 사람이 이 문서를 지워도 되는지 보는 것이 뒤의 것입니다. 표에 역할이 적혀 있다고 해서 뒤쪽 확인이 없어지지는 않습니다.
흔한 오해
표 안의 내용이 암호화돼 있다고 여기는 경우가 가장 많습니다. 알아볼 수 없는 문자열이 길게 이어지니 잠겨 있는 것처럼 보입니다. 실제로는 읽기 편한 글자로 바꿔 둔 것뿐이고, 되돌리는 도구는 웹에 널려 있습니다. 표를 붙여 넣으면 안에 적힌 사용자 번호와 역할과 만료 시각이 그대로 나옵니다. 이 오해를 그대로 두면 주소나 생년월일을 페이로드에 넣게 되고, 브라우저 저장소에 남은 표 하나가 그대로 개인정보 유출 경로가 됩니다.
JWT 를 쓰면 로그인 기능이 완성된다고 읽는 경우도 있습니다. 로그인 관련 글에서 가장 자주 보이는 말이라 그렇게 느끼기 쉽습니다. JWT 가 정하는 것은 표를 어떤 모양으로 적고 어떻게 위조를 가려내느냐까지입니다. 비밀번호를 어떻게 보관할지, 표를 브라우저 어디에 둘지, 만료된 뒤 어떻게 재발급할지, 기기를 잃어버렸을 때 무엇을 막을지는 전부 따로 정해야 합니다. 형식 하나를 골랐을 뿐인데 설계가 끝났다고 여기면 그 빈자리가 나중에 사고로 돌아옵니다.
자주 헷갈리는 용어
| 용어 | 무엇을 정하는가 | JWT와의 차이 |
|---|---|---|
| OAuth | 권한을 빌려주는 절차 | 누가 어떤 순서를 거쳐 허가를 받아 내는지를 정하는 규약입니다. 그 결과로 손에 쥐는 표를 어떤 형식으로 적을지는 정하지 않아서, OAuth 로 받은 표가 JWT 일 수도 있고 아무 뜻 없는 문자열일 수도 있습니다 |
같이 보면 좋은 페이지
마지막 업데이트 2026-09-16