JWT는 오늘 날 대부분의 소프트웨어에서 로그인과 회원가입 등 인증에서 자주 사용된다. 그렇다면 JWT는 도대체 어떻게 생겼고, 또 어떻게 작동하는지 알아보자.
아마 Chrome이나 Safari등 다양한 웹 브라우저를 사용하다 보면 "쿠키", "세션"이라는 단어를 심심치 않게 접했을 것이다.
그러면 이 "쿠키", "세션" 이라는 것은 뭘까? 둘 다 모두 JWT가 나오기 전까지 사용되던 인증 방식이다.
쿠키는 쉽게 말해 사용자의 정보를 임의로 저장하고 있다가 필요할 때 서버로 던져주는 것을 말한다.
로그인을 하면 Set-Cookie를 받아와 가지고 있다가 로그인이 필요한 API를 요청할 때 담아서 보내는 것이다.
이렇게 하면 유저가 어떤 요청을 보낼 때 마다 일일히 ID나 PW를 보낼 필요가 없어져서 편리한 서비스를 제공받을 수 있다.
현재도 쿠키를 활용해서 인증을 하지만, 아래의 문제점들로 인해 쿠키로만 인증하지는 않고 있다.
가장 큰 문제는 쿠키가 통신 과정에 노출되거나 후킹 당했을 경우 ID와 PW가 그대로 유출된다는 것이다.
같은 이유로 중간에 정보가 변형될 가능성도 무시할 수 없으며 서버에서 이를 방지할 방법이 없다.
또한 서버에서는 매번 쿠키를 받아올 때마다 ID와 PW를 통한 로그인 인증 로직을 진행해야 한다는 번거로움이 존재한다.
마지막으로 쿠키의 크기가 4KB 정도로 작은 편이기 때문에 필요한 정보를 한 번에 다 담지 못한다는 단점도 있다.
위에서 말한 쿠키의 단점 중 하나인 ID와 PW의 노출을 방지하기 위해 나온 것이 바로 세션(Session)이다.
세션이라고 해서 쿠키를 이용해서 정보를 주고 받는다는 사실은 바뀌지 않는다. 다만 로그인과 기타 인증 API 통신에서 발생하는 민감한 정보의 유출이나 갈취를 막기를 위해 고안된 것이 세션이다.
세션의 주요 개념은 로그인 시에 ID와 PW를 주고받지 말고 인증 정보 자체를 서버나 특정 세션 저장소에 저장하고 클라이언트가 쿠키를 요청할 때 저장소에 있는 인증 정보인 세션ID 와 대조하여 로그인을 확인한다는 것이 핵심이다.
순수 쿠키만을 이용해 직접 로그인 정보를 주고 받는 것이 아닌, 로그인 유저를 식별할 있는 특정 값을 대조한다는 점에서 보안성이 올라간 것이다.
만약 세션ID를 탈취 당한다면 어떻게 될까? 단순하다, 세션 저장소를 초기화 하면 그만이다. 탈취를 당했다고 해도 해당 세션ID가 저장된 저장소가 초기화되면 의미가 없어지기 때문이다.
하지만 이것을 다르게 얘기하면 세션 저장소에 에러가 발생하여 작동하지 못한다면 유저는 로그인 작업을 수행할 수 없다는 것이다.
또한 인증 정보를 서버에 따로 저장한다는 점이 문제가 되어서 나중에 기능 확장 또는 서버 이전 시에 세션ID를 따로 다시 만들어줘야 한다.
가장 큰 문제점은 결국 로그인 로직을 수행하기 위해서는 DB에서 세션ID를 조회해야하는 과정을 한 번 거쳐야 한다는 번거로움이 있다.
위에서 소개한 쿠키와 세션의 문제점을 보완하고자 나온 것이 JWT(Json Web Token)이다.
JWT는 말 그대로 Token을 이용한 인증 방법인데, 사용자의 정보와 권한 등을 Token에 암호화 하여 담는다.
기본적으로 특정 매체에 정보를 담아 주고받는 다는 점에서는 쿠키와 유사하지만, JWT의 가장 큰 특징은 서명된 토큰이라는 것이다.
기본적으로 공개 키와 개인 키 두 개의 키가 쌍방을 이루는데, 공개 키는 클라이언트가 가지고 있다.
개인 키는 서버가 가지고 있다가 클라이언트에서 공개 키를 토큰에 담아서 주면 이를 서로 대조하여 유효성을 검사하는 방식이다.
이러한 방식으로 탈취를 당할 일도, 유저 정보가 변형되어 들어올 일도 없어졌다.
개인/공개 키가 쌍을 이루고 있어 정보가 변형되었다고 해도 서버에서 거를 수 있어졌으며, 공개 키를 탈취 당해도 재발급하면 된다.
JWT는 " . " 을 기준으로 세 개의 구조로 이루어져 있다.

위에 사진처럼 각각 header, payload, signature로 이루어져 있다. 지금부터 각 부분의 역할과 기능에 대해서 알아보자.
Header는 토큰의 타입과 토큰을 생성할 때 사용된 알고리즘이 들어가있다.

여기서 토큰 타입은 "JWT" 그리고 암호화 알고리즘은 HS512가 사용되었다는 것을 알 수 있다.
Payload는 이용자의 일부 정보, 또는 토큰 정보의 일부가 들어가 있다, 이를 Claim이라고 부른다. 또한 코드에서 개발자가 어떤 정보를 넣느냐에 따라 구성이 달라지고는 한다.

Claim은 크게 등록된 클레임, 공개 클레임, 비공개 클레임으로 나뉜다.
등록된 클레임은 토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터이며, 모두 선택적으로 작성이 가능하지만 사용할 것을 권장하고 있다. 간결하게 하기 위해 key는 모두 길이가 3으로 통일되어 있다.
iss: 토큰 발급자(issuer)
sub: 토큰 제목(subject)
aud: 토큰 대상자(audience)
exp: 토큰 만료 시간(expiration)
nbf: 토큰 활성 날짜(not before)
iat: 토큰 발급 시간(issued at)
jti: JWT 토큰 식별자(JWT ID)
이렇게 총 7개의 클레임이 있다, 그러나 모두를 반드시 사용할 필요는 없다. 참고로 subject는 unique한 값을 이용하며 보통의 경우 사용자의 이메일을 사용한다.
공개 / 비공개 클레임은 사용자 정의 클레임, 즉 개발자가 직접 커스텀한 클레임이다.
공개 클레임은 공개용 정보를 위해 사용되며 비공개 클레임은 서버와 클라이언트 사이에 임의로 지정한 정보를 저장한다.
Signature은 JWT에서 가장 중요한 부분으로 Header와 Payload의 값을 모두 인코딩한 후에 합쳐 놓는다.
거기에 서버에 있는 개인 키를 이용하여 암호화를 시킨다. 그렇기에 이 부분은 서버에 있는 개인 키 없이는 디코딩이 불가능 하다.

위 이미지에서 header, 그리고 payload를 인코딩하여 합친 것을 볼 수 있다.
이처럼 signature에서 암호화 및 복호화가 이루어지기 때문에 설령 JWT header, payload가 변질된 상태로 통신을 시도한다고 해도 signature가 서버에서 발급한 것과 다를 것이기에 인증이 불가능 해진다.
JWT의 인증 단계는 크게 회원가입과 로그인, 그리고 토큰 발급과 재발급. 이렇게 4가지 단계로 이루어져 있다.
회원가입:
다들 해봤을 회원가입이다. 서버에서 요청하는 정보를 담아서 보낸다, 그러면 서버는 특별한 인증 절차 없이 유저 중복 확인 후 DB에 유저 권한을 부여 후 저장한다.
로그인:
보통의 경우 회원가입 후 다시 로그인 화면으로 되돌아온다. 그 이유는 로그인을 통해 본인 인증을 하고 토큰을 발급 받기 위해서다.
로그인 할 때 요구하는 정보를 입력 후 (예시 - 비밀번호와 이메일) 정보가 일치할 경우 로그인이 된다.
토큰 발급:
로그인이 되는 것과 동시에 아래 예시와 같이 access token과 refresh token이 발급된다.

해당 토큰은 필자가 로컬에서 돌린 테스트용 서버에서 발급 받은 토큰이다. 여기서 유저는 access token을 이용해 인증이 필요한 기타 API를 이용한다.
그러나 토큰은 항상 만료 시간이 존재한다. 위에 토큰에 경우 설정 파일에 12시간이 만료 기간이었다.
12시간이 지나면 access token은 유효하지 않기 때문에 사용할 수 없다, 그렇다면 또 다시 로그인을 해야 하는가? 그건 아니다.
토큰 재발급:
아래에 있는 refresh token을 이용해 access token을 재발급 받아서 다시 재사용하면 된다.
그러나 refresh token이라고 만료 기간이 없는 것은 아니다. refresh token 까지 만료된다면 그때에는 재 로그인을 통해서 토큰을 전부 다시 발급 받아야 한다.
JWT 토큰은 개발을 하면서 한 번 정도는 마주치게 되는 녀석이다. 처음에는 다루기 까다롭고 뭐가 뭔지 모를 정도로 복잡하다고 느낄 수도 있다.

하지만 뭐든 익숙해지면 쉬워진다, 반복적인 코드 복습과 공부만이 살 길인 것 같다. 다들 즐거운 코딩을 하기를 바라며 글을 마치겠다
아래는 글에 미처 담지 못한 JWT로 만든 Auth 템플릿이다. 한 번씩 참고해 보기를 바란다.