Oauth 2.0
NCP에서는 Oauth 2.0에 대해 다음과 같이 설명하고 있습니다.
Open Authorization 2.0 혹은 OAuth2.0은 웹 및 애플리케이션 인증 및 권한 부여를 위한 개방형 표준 프로토콜입니다. 이 프로토콜에서는 third-party 애플리케이션이 사용자의 리소스에 접근하기 위한 절차를 정의하고 서비스 제공자의 API를 사용할 수 있는 권한을 부여합니다.
쉽게 이야기하면, 클라이언트가 서버의 특정 리소스에 대한 접근 권한을 얻기 위해 사용하는 방식입니다. 즉 API를 호출할 때 서버로부터 발급받은 Access Token을 헤더에 넣어 보내면 서버에서 권한을 확인하고 리소스를 제공해주는 것입니다. 그래서 인증이 아니라 인가 프레임워크이고, 권한을 확인하는 것이지 '이 사람이 누구인지'를 알기 위해 사용하는 것이 아닙니다.
OIDC(OpenID Connect), JWT
OIDC는 Oauth 2.0 위에 인증 기능을 표준화한 것이고, 이때 인증의 결과로 ID Token이라는 것이 나옵니다. ID Token은 JWT로 표준화되어 있습니다. 그럼 JWT는 무엇일까요?
JWT는 json-web-token으로, "claims(사용자/권한/만료시간 등의 정보)를 JSON으로 담고, 서명과 암호화로 보호할 수 있는 compact 토큰"이라고 정의됩니다. 이러한 JWT를 사용하면, 세션 관리가 어려운 모바일 앱에서도 인증/인가를 구현할 수 있고 브라우저에서도 서버가 세션 상태를 저장 및 관리하지 않아도 됩니다. JWT는 xxx.yyy.zzz와 같이 점(.)으로 구분된 문자열이고, 각 부분은 Base64URL로 인코딩됩니다. 암호화가 아니라 인코딩되는 것이므로 누구나 디코딩 가능하다는 것에 주의해야 합니다. 문자열의 3부분은 각각 Header, Claims, Signature 입니다.
- Header: 토큰 타입, 서명 알고리즘 (예: RS256)
- Claims(Payload): 사용자/권한/만료시간 같은 정보
- sub: subject (대게 userId)
- iat: 발급 시각
- exp: 만료 시각
- iss: 발급자 (예: google.com, sia)
- aud: 대상 (예: sia-mobile)
- Signature: 이 토큰이 서버가 발급한 게 맞고 중간에 바뀌지 않았다고 증명하는 서명
그럼 다시 돌아와서, ID Token이 정확히 무엇일까요? Access Token과는 어떻게 다를까요? Refresh Token, Authorization Code 개념과 함께 인증 프로세스를 살펴봅시다.
- Access Token: 인증이 성공했을 때 서버로부터 발급되는 토큰입니다. 이 토큰은 앞으로 클라이언트(앱)이 서버로 API를 호출할 때 인증하기 위해 사용하는 토큰입니다. 즉 서버는 Redis 등으로 관리하고, 앱 또한 안전한 스토리지에 저장해두어야 합니다. Access Token은 만료 기간이 매우 짧습니다. 민감한 곳에서는 5~15분, 일반적인 서비스에서도 30분~1시간입니다.
- Refresh Token: 만료기간이 지나면 해당 Access Token은 사용하지 못하고 서버가 401 에러를 뱉을 것입니다. 그러면 Refresh Token으로 서버에게 토큰 발급을 재요청해야 하며, Refresh Token 정보는 서버도 redis 등에 보관하고 있다가 올바른 값인지 비교한 후 access token을 발급해줍니다. refresh token은 일반적으로 2주~30일 정도의 만료기간을 가집니다. 참고로 이렇게 다시 access token을 재발급하면 refresh token도 그때 재발급하여 서버와 클라이언트 모두 refresh token을 업데이트하는 것이 보안상 안전합니다. 또한, JWT 형식인 Access Token과 다르게 Refresh Token은 랜덤 문자열로 저장되는 경우가 많습니다.
- Id Token: Access Token과 마찬가지로 인증에 성공했을 때 서버로부터 발급되는 토큰입니다. 역시나 JWT 형식입니다. 하지만 Access Token과는 역할이 다릅니다. 오직 클라이언트에서 인증된 사용자의 정보(이메일 등)를 필요로 할 때 사용하는 값입니다.
- Authorization Code: 사용자가 로그인을 완료했을 때, 서버가 브라우저나 앱(Client)에 즉시 토큰을 주지 않고 대신 주는 임시 코드입니다. 실제 토큰(Access Token, ID Token)을 안전하게 받기 위한 중간 매개체로, 1~10분 이내로 수명이 짧고 한 번 사용한 이후 바로 폐기되는 일회성 값입니다. 모바일 기준에서 설명하자면, 이 code는 브라우저가 열리는 로그인, 즉 외부 소셜 로그인을 할 때만 등장하는 값이고, 서비스 자체 로그인을 진행할 때는 사용될 필요 없는 값입니다. 정확히 어떻게 사용되는 값인지, 왜 필요한지 아래 인증 흐름으로 이해해봅시다.
인증 흐름
먼저 모바일 앱의 소셜 로그인 인증 흐름을 간단하게 시퀀스 다이어그램으로 살펴보면 아래와 같습니다.
- 앱에서 '구글/애플 로그인' 버튼을 클릭하여 로그인 시작
- 앱이 브라우저를 열어 인증 서버의 /authorize로 보냅니다.
- 이때 state, code_challenge 값을 같이 보냅니다. state는 클라이언트가 이때 만들어서 보내면, 인증 서버가 나중에 그 값을 그대로 돌려주어서 클라이언트 입장에서 "내가 시작한 로그인 플로우가 맞는지"를 확인합니다. code_challenge는, code_verifier 로부터 만들어지는 값으로, 인증 서버가 보관해놓았다가 인증 서버에서 생성한 Authorization code와 매치시켜놓고 클라이언트에게 code를 보내줍니다. 그럼 클라이언트가 code와 code_verifier를 가지고 서버에게 최종적으로 토큰 요청을 보냅니다. 즉 code는 인증 서버가 만들어서 클라이언트에게 주는 값이며, 바로 토큰을 주기보다 code라는 티켓으로 인증하도록 하는 것입니다. 이 과정에서 해커가 code를 탈취하더라도 처음 요청했던 클라이언트가 아니면 토큰을 받을 수 없게 하기 위해 사용하는 값이 code_verifier, code_challenge 값입니다.
- 브라우저에서 로그인/동의
- 인증 서버가 로그인 화면 제공
- 사용자가 계정 인증 + 권한 동의
- 외부 인증 서버가 Authorization Code를 앱으로 돌려줌
- 로그인/동의가 성공하면 인증서버가 redirect_uri 로 리다이렉트하면서 code=...&state=... 를 포함해 앱으로 전달합니다.
- 앱이 code를 토큰으로 교환 요청
- 앱이 서버의 /token에 code를 보내서 토큰을 받습니다.
- 이때 PKCE가 있으면 code_verifier도 같이 보내야 합니다.
- 인증 서버가 토큰 발급
- Access Token, Id Token, Refresh Token을 발급하여 앱으로 전달
- 앱이 토큰을 받아서 저장
- Access Token으로 API 호출
- Id Token으로 사용자 정보 확인
- Refresh Token으로 Access Token 재발급

다음은 자체 이메일/비밀번호 로그인의 인증 흐름입니다. (브라우저 없음, 커스텀 로그인 API)
- 앱에서 이메일/비밀번호 입력
- 사용자가 앱 UI에서 이메일/비밀번호 입력
- 앱이 서버 로그인 API 호출
- HTTPS로 POST 요청
- 서버가 자격증명 검증
- DB에서 사용자를 조회하여 비밀번호 해시 비교, 계정 상태 확인 등을 수행합니다.
- 서버가 토큰 발급
- Access Token, Refresh Token 을 만들어서 응답으로 보내줍니다.
- 앱이 토큰을 받아서 저장 및 관리
- Access Token, Refresh Token을 보안상 안전한 스토리지에 저장해두고 axios interceptor 등에서 access token 재발급 로직을 수행하도록 구현합니다.

모바일 자체 로그인은 앱이 서버에 직접(email/password) HTTPS 요청을 보내서 인증을 끝내기 때문에, 브라우저 리다이렉트 기반 흐름에서만 필요한 Authorization Code가 등장할 이유가 없습니다. 또 ID Token은 OIDC에서 “클라이언트가 로그인 결과를 표준 방식으로 검증”하려고 쓰는 토큰이라, 자체 로그인에선 보통 access/refresh 토큰만으로 충분해서 굳이 분리하지 않습니다.
'development' 카테고리의 다른 글
| React Native - Redux, useEffect (0) | 2026.01.06 |
|---|---|
| Docker: 헬스 체크와 디펜던시 체크로 신뢰성 확보하기 (0) | 2025.09.14 |
| Dockerfile과 Docker Compose (0) | 2025.09.10 |
| 서버 환경 구성하기4 - 성능과 가용성을 보장하기 (0) | 2025.09.07 |
| 서버 환경 구성하기3 - 정적 콘텐츠를 낮은 비용으로 (0) | 2025.09.07 |