차후(이제 곧이겠다..) 백엔드 분들과 협업을 하게 된다면, 서버↔클라이언트 구조로 정보를 주고받게 될 것이다. 이를 대비하기 위해 공부한 걸 정리해 보자.
인터넷 통신에 사용되는 HTTP 프로토콜은 기본적으로 무상태(Stateless)이다. 즉, 서버는 클라이언트로부터 요청을 받을 때마다 이전 요청이나 클라이언트의 상태(로그인 여부)를 전혀 기억하지 못한다. 이로 인해, 사용자가 로그인을 한 후에도 서버에 정보를 송신할 때마다 "나 사용자 OOO이야!" 라고 신원을 매번 밝혀야 하는 비효율적인 문제가 생긴다. 이를 해결하기 위해 서버와 클라이언트가 '상태'를 공유할 수 있는 인증 방식이 필요하다. 가장 대표적인 인증 수단이라고 하면, ID나 PW가 있다. 그러나 이를 매번 보내는 것은 보안상 매우 위험하고 탈취 위험이 높다.
따라서 대부분 로그인을 하면 주로 토큰(Token)이라는 임시 신분증을 발급받아, 이후 통신에서 이 토큰을 이용해 자신을 인증한다.
인증 수단들
1. 쿠키(Cookie)
다들 인터넷을 이용하시다 보면, "쿠키 사용을 허용하시겠습니까?"라는 창을 본 적이 있을 것이다. 쿠키(Cookie)는 서버가 클라이언트(웹 브라우저) 측에 저장하는 작은 기록 정보 파일이며, Key-Value 형태의 문자열로 존재한다. 쿠키는 사용자마다의 브라우저에 정보가 저장되기 때문에, 각 사용자별로 개개인의 쿠키를 가지게 된다.
클라이언트는 서버에 요청을 할 때, 요청 헤더에 쿠키를 담아 서버에 전송한다. 서버는 이 쿠키를 통해 클라이언트의 이전 접속을 토대로 바로 특정 사용자임을 확인함으로써, 맞춤 정보 제공 등의 편의를 제공한다.
쿠키의 사용 과정은 아래와 같다.

- 사용자가 서버로 요청을 보냅니다.
- 서버는 응답을 작성할 때, 클라이언트 쪽에 저장하고 싶은 정보들을 쿠키 헤더(쿠키들을 한 줄로 묶어서 전송하는 HTTP 헤더)에 담아 전송합니다.
- 사용자는 전달받은 쿠키를 저장하고, 앞으로 서버로 요청할 때마다 요청값의 쿠키 헤더에 쿠키를 동봉해서 전송합니다.
- 서버는 전달받은 쿠키를 바탕으로 사용자를 인증하고 사용자 맞춤 요청을 처리합니다.
쿠키의 장점은 정보를 클라이언트가 가지고 있다 보니 서버의 부하가 적다는 점입니다. 그러나 쿠키 전체를 전송하기 때문에 용량 제한이 있고, 쿠키에 민감한 정보를 담을 경우 탈취 시 개인정보가 그대로 노출될 수 있다는 보안 위험이 있습니다.
2. 세션(Session)

세션(Session)은 쿠키의 보안 취약성을 극복하기 위해 등장했습니다. 민감한 정보를 클라이언트에 모두 담는 것이 위험하기 때문에, 세션은 클라이언트의 민감한 정보를 서버에 저장하고 관리합니다.
세션은 서버가 Session ID라는 것을 발급한 뒤, 이를 쿠키에 넣어 클라이언트한테 전달합니다. 클라이언트가 서버에게 쿠키를 보내면, 서버는 쿠키에 있는 세션 ID를 바탕으로 서버의 세션 저장소(Session Storage)에 있는 사용자 정보를 찾아 인증을 수행합니다.
즉, 민감한 정보는 클라이언트한테 보내지 않고, 서버에서만 관리한다는 것이 쿠키와 세션의 큰 차이입니다.
핵심 정리
- 클라이언트는 세션 ID를 쿠키를 통해서 기억합니다.
- 클라이언트는 다음 요청 때마다 헤더의 쿠키에 세션 ID를 담아 전송합니다.
- 서버는 클라이언트가 보낸 세션 ID와 세션 저장소에 담긴 사용자 정보를 대조해 인증 상태를 판단합니다.
장점으로는, 세션 ID 자체로는 유의미한 정보를 가지고 있지 않으므로 보안성이 강화되지만, 역시 해커가 세션 ID 자체를 탈취하여 클라이언트로 위장할 수 있는 위험이 있습니다. 또한, 서버에 요청이 많아질수록 세션 정보를 유지하기 위해, 서버 부하가 심화되고 서버 확장이 어렵다는(Stateful) 문제가 있습니다.
3. 토큰(Token)

토큰(Token)은 서버가 클라이언트를 인증한 후 발급해주는 신분증 같은 겁니다. 토큰 인증 방식은 서버가 클라이언트에게 토큰을 부여하고, 클라이언트는 서버에 요청을 보낼 때 해당 토큰을 Authorization 헤더에 담아 전송합니다.
기존 세션 기반 인증은 서버가 파일이나 데이터베이스에 세션 정보를 가지고 있어야 하고 이를 조회하는 과정이 필요하기 때문에 많은 오버헤드가 발생합니다.
하지만 토큰은 세션과는 달리 서버가 아닌 클라이언트에 저장되기 때문에 서버의 부담이 감소합니다. 토큰 자체에 데이터가 들어있기 때문에, 서버는 클라이언트에서 받은 토큰이 위조되었는지 판별만 하면 되기 때문입니다.
이 덕분에, 세션 방식보다 데이터의 오버헤드도 적고, 쿠키 방식처럼 사용자에게 정보를 갖고 있게 함으로써 서버는 무상태(Stateless) 상태를 유지할 수 있습니다.
JWT(Json Web Token)

JWT(Json Web Token)는 토큰 인증 방식의 사실상 표준이며, 사용자 인증에 필요한 정보를 JSON 형식으로 구조화하고 암호화시킨 토큰입니다. JWT 기반 인증은 JWT 토큰(일반적으로 Access Token)을 HTTP 헤더에 실어 서버가 클라이언트를 식별하는 방식입니다.

JWT는 JSON 데이터를 Base64 Encode 등를 통해 인코딩하여 직렬화한 것이며, 토큰 내부에는 위변조 방지를 위해 개인 키를 통한 전자 서명도 들어 있습니다. 따라서 사용자가 JWT를 서버로 전송하면 서버는 서명을 검증하는 과정을 거치며, 검증이 완료되면 요청한 응답을 돌려줍니다.
JWT 구조
JWT는 Header, Payload, Signature 세 부분으로 나뉘며, 각 부분은 Base64 인코딩되어 점(.)으로 연결된다.

- Header: 토큰의 타입(typ) 및 서명에 사용된 암호화 방식(alg)이 정의되어 있습니다.
- Payload: 토큰에서 사용할 정보들이 정의되어 있으며, 서버와 클라이언트가 주고받는 실질적인 정보가 담겨 있습니다. 또한, 이 부분에 토큰 유효시간을 추가합니다.
- Signature: 토큰의 위변조 여부를 검증하는 부분입니다. 헤더와 페이로드, 그리고 서버의 비밀 키를 이용해 암호화 알고리즘(alg)을 적용하여 생성됩니다.
Access Token과 Refresh Token을 이용한 보안 강화
JWT는 내부에 유효 시간(Expiration Time) 정보를 포함하고 있습니다. 이 유효 시간을 짧게 설정함으로써, 설사 토큰이 악의적인 사용자에게 탈취되더라도 토큰이 만료되는 즉시 무효화되어 서버 접근을 막을 수 있습니다.
하지만, 토큰의 유효 시간이 종료될 때마다 새로운 토큰을 발급해야 하는데, 매번 발급 과정에서 서버 부하(오버헤드)가 발생하고, 짧은 만료 시간은 사용자에게 불편함을 줄 수 있습니다.
이 문제를 해결하고 보안을 강화하기 위해 Access Token과 Refresh Token 방식을 함께 사용합니다.
둘은 서로 다른 유효 기간을 가지며, Access Token은 약 1시간 정도로 짧게 설정하며, Refresh Token은 2주 정도로 넉넉하게 보통 설정합니다.

- 사용자가 로그인을 통해 서버에 인증을 요청합니다.
- 서버는 인증 요청을 받으면, Access Token과 Refresh Token을 각각 생성하고 이를 클라이언트에게 발급합니다.
- 클라이언트는 이 토큰들을 안전한 스토리지(ex. SharedPreference)에 저장합니다.
- 클라이언트가 서버에게 요청 시, Access Token을 Authorization 헤더에 넣어 전송하고, 서버는 이 토큰의 서명 일치 여부와 만료 여부를 확인하여, 통과 시 응답을 전송합니다.
- 만약 Access Token이 만료되었으면, 클라이언트는 서버에게 Refresh Token을 이용하여 새로운 Access Token을 재발급받습니다.
아마 백엔드 분들과 협업을 하게 되면,
[ 앱 ]
소셜 SDK 로그인
↓
소셜 토큰
↓
[ 우리 API(백엔드) ]
소셜 토큰 검증
JWT 발급
↓
[ 앱 ]
JWT 저장(아마 SharedPreference에..?)
↓
모든 API 요청
Authorization: Bearer JWT
이런 식으로 되지 않을까 싶다.
그렇다면 내가 해야하는 건,
- 로그인하면 SDK로 토큰 받기
- 그 토큰을 API로 전달
- 응답으로 받은 JWT 저장( Access Token + Refresh Token )
- 이후 모든 요청에 JWT 첨부
를 구현하는게 아닐까 싶다.
'안드로이드 스터디' 카테고리의 다른 글
| JetPack Compose 2편 : Row, Column과 기본 크기 지정 (0) | 2025.12.30 |
|---|---|
| JetPack Compose - 1 (0) | 2025.12.30 |
| Retrofit과 백엔드와의 연동 (0) | 2025.12.30 |
| RecyclerView CRUD 구현하기 (2) | 2025.11.02 |
| RecyclerView 클릭 이벤트 구현 (0) | 2025.11.02 |