토큰(Token) 기반 인증은 웹에서 가장 널리 사용되는 인증 방식 중 하나이다. 토큰을 이해하는 것은 웹 및 모바일 애플리케이션에서 인증을 구현하는 방법을 이해하는 데 중요하다.
토큰 인증이란 사용자 인증을 위해 서버가 생성한 암호화된 문자열을 의미한다. 토큰은 사용자의 ID와 같은 정보를 포함하고, 사용자가 이후 요청을 할 때마다 이 토큰을 첨부하여 서버에 전송한다. 서버는 이 토큰을 검증하여 사용자가 누구인지 확인하고, 해당 사용자에 대한 요청을 처리한다.
- 상태가 없다(Stateless): 토큰은 필요한 모든 정보를 자체적으로 포함하고 있으므로, 서버는 사용자의 상태를 기억할 필요가 없다. 이는 서버의 부하를 줄이고 확장성을 향상시킨다.
- 모바일 친화적이다: 쿠키는 웹 브라우저에 의존적인 기술이지만, 토큰은 HTTP 헤더를 통해 전송되므로 모바일과 같은 다양한 플랫폼에서도 작동한다.
- 보안: 토큰은 일반적으로 암호화되어 있으므로, 중간에서 토큰을 탈취하더라도 이를 복호화하지 않는 한 사용자 정보를 노출시키지 않는다.
- 만료 기간 설정: 토큰에는 만료 시간을 설정할 수 있으므로, 일정 시간이 지나면 자동으로 사용할 수 없게 만들 수 있다.
- 토큰에는 일반적으로 사용자 ID, 유효 기간, 권한 등의 정보가 JSON 형태로 포함되며, 이 정보는 서버에서 필요에 따라 읽어올 수 있다. 이러한 방식을 JWT (JSON Web Token)라고 부른다.
토큰을 활용한 인증 방식 중 대표적으로 JWT(JSON Web Token)가 있다.
JWT는 JSON 객체를 사용하여 토큰을 생성하고, 이를 URL 안전 문자열로 인코딩하여 클라이언트에 전송한다.
클라이언트는 이후 요청에 이 JWT를 첨부하여 서버에 전송한다.
JWT는 일반적으로 세 가지의 부분으로 구성되어 있다.
Header: Header는 일반적으로 토큰의 유형 (JWT)과 서명 알고리즘 (예: HMAC SHA256 또는 RSA)을 포함하는 두 가지 부분으로 구성된다.
Payload: Payload 부분은 토큰에서 실제 전달하려는 데이터를 포함하며, 이 데이터를 클레임 (Claims)라고 부른다. 클레임은 세 가지 유형 (Registered, Public, Private claims)으로 나뉜다.
Signature: 서명 부분에서는 Header의 인코딩 값, Payload의 인코딩 값, 그리고 비밀 키를 사용해 생성된다. 이 서명을 통해 토큰의 무결성이 보장되며, 서버는 이 서명을 확인하여 요청이 중간에 변조되지 않았는지 확인할 수 있다.
이 세 부분을 합치면 'header.payload.signature' 형식의 긴 문자열이 생성되며, 이 문자열이 바로 JWT이다. 각 부분은 Base64Url로 인코딩되어 표현되며, 부분과 부분 사이는 '.' 문자로 구분된다.
React에서 JWT 인증 방식을 사용하면 사용자의 상태를 서버에서 관리하는 대신 클라이언트에서 관리할 수 있다. 이를 위해 우선적으로 사용자의 로그인 정보를 서버에 보내고, 그 결과로 JWT를 받아와서 이를 클라이언트 측에서 저장해두는 작업이 필요하다.
그런 후, 사용자가 인증이 필요한 요청을 할 때마다 이 토큰을 헤더에 첨부해서 보내면, 서버는 이 토큰을 검증하여 요청이 유효한지 아닌지를 판단하게 된다. 이렇게 하면 세션 관리와 같은 추가적인 작업 없이도 사용자 인증이 가능하다.
로그인: 사용자가 로그인 폼에 자신의 정보를 입력하고 로그인 요청을 보낸다. 이 요청에는 일반적으로 사용자 이름과 비밀번호가 포함되어 있다.
토큰 발급: 서버는 로그인 요청을 받아 사용자를 검증하고, 검증이 완료되면 JWT를 생성하여 클라이언트에게 응답으로 보낸다. 이 토큰에는 사용자를 식별할 수 있는 정보와 토큰의 유효기간 등이 포함되어 있다.
토큰 저장: 클라이언트는 응답으로 받은 토큰을 로컬 저장소나 세션 저장소에 저장한다. 이렇게 하면 토큰은 사용자가 브라우저를 닫거나 재시작해도 유지되며, 사용자가 다시 로그인할 필요 없이 서버에 요청을 보낼 수 있다.
인증된 요청: 클라이언트는 서버에 요청을 보낼 때마다 저장해 둔 토큰을 'Authorization' 헤더에 첨부해서 보냅니다. 서버는 이 헤더를 확인하여 토큰이 유효한지 검증하고, 토큰의 소유자에게 요청에 대한 권한이 있는지 확인한다.
로그아웃: 사용자가 로그아웃을 요청하면 클라이언트는 저장해 둔 토큰을 제거한다. 이후에는 인증이 필요한 요청을 보낼 수 없게 된다.
이러한 JWT 인증 방식의 장점 중 하나는 클라이언트와 서버가 완전히 분리되어 있을 수 있다는 점이다. 즉, 서버는 상태를 저장할 필요 없이 요청이 유효한지만 판단하면 되므로, 서버의 로직이 단순해지고 확장성이 높아진다. 또한, 서버와 클라이언트가 분리되어 있으면 프론트엔드와 백엔드를 개발하는 팀이 독립적으로 작업할 수 있으므로 개발 과정이 더 유연해진다.
설명도 이상하고 코드도 이상한 글임에도 읽어주셔서 감사합니다. : )