OAuth2 쉽게 이해하기 - 초보자를 위한 쉬운 가이드
by Jeongjin Kim
🎯 시작하기 전에: 인증 vs 인가
보안을 이해하려면 먼저 이 두 개념을 구분해야 합니다.
인증 (Authentication) - “너 누구야?”
아파트 출입문에서 주민등록증을 확인하는 것과 같습니다.
- “당신이 정말 김철수가 맞나요?”
- 로그인할 때 아이디/비밀번호를 입력하는 과정
인가 (Authorization) - “이거 해도 돼?”
아파트 입주민이라고 모든 집에 들어갈 수 있는 건 아니죠.
- “당신은 303호만 출입 가능합니다”
- 로그인 후 특정 기능이나 데이터에 접근할 권한이 있는지 확인
핵심: OAuth2는 원래 인가(권한 위임)를 위한 도구입니다. 하지만 OpenID Connect를 더하면 인증(로그인)도 할 수 있습니다.
📖 이야기로 이해하는 OAuth2
상황: 당신이 사진 인쇄 서비스를 이용하려고 합니다
옛날 방식 (문제 많음):
인쇄 서비스: "구글 포토에서 사진 가져올게요. 구글 아이디랑 비밀번호 알려주세요!"
당신: "아... 여기에 내 구글 비밀번호를 줘야 하나?" 😰
문제점:
- 인쇄 서비스가 내 구글 계정을 마음대로 쓸 수 있어요
- 비밀번호를 바꾸기 전까지 계속 접근 가능해요
- 사진만 보면 되는데 이메일, 캘린더까지 다 볼 수 있어요
OAuth2 방식 (똑똑함):
인쇄 서비스: "구글에 로그인해서 '사진만 볼 수 있는 허가증'을 받아올게요"
당신: 구글 로그인 → "이 앱이 내 사진만 보도록 허락할래요?" 클릭
구글: "여기 임시 출입증(토큰)이요. 사진 폴더만 볼 수 있어요"
장점:
- 비밀번호를 알려주지 않아요
- 사진만 볼 수 있도록 권한을 제한해요
- 언제든지 허가를 취소할 수 있어요
🏗️ OAuth2의 4명의 등장인물
실제 서비스에서 이 4명이 어떻게 협력하는지 봅시다.
1. Resource Owner (자원 소유자) - 당신
- 구글 포토에 있는 사진의 주인
- 권한을 줄지 말지 결정하는 사람
2. Client (클라이언트) - 인쇄 서비스
- 당신의 사진을 가져오고 싶은 앱
- 당신 대신 구글에 접근하려는 애플리케이션
3. Authorization Server (인가 서버) - 구글 로그인 서버
- 당신이 정말 구글 계정 주인인지 확인
- “사진 폴더 읽기 권한”이 담긴 토큰을 발급
4. Resource Server (자원 서버) - 구글 포토 API
- 실제 사진이 저장된 곳
- 토큰을 확인하고 사진을 제공
🎫 토큰의 종류
Access Token - “출입증”
"이 토큰을 가진 사람은 사진 폴더를 30일간 읽을 수 있습니다"
- API를 호출할 때 사용
- 유효기간이 있음 (보통 1시간 ~ 1일)
- Bearer Token 방식: 가진 사람이 곧 권한자
실제 사용 예시:
GET /photos/album1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
ID Token - “신분증” (OIDC에서만)
"이 사람은 user@gmail.com이고, 이름은 김철수입니다"
- 로그인한 사용자가 누구인지 알려줌
- API 호출에는 사용하지 않음
- 클라이언트가 사용자 정보를 확인할 때만 사용
Refresh Token - “재발급권”
"출입증이 만료되면 이걸로 새 출입증을 받으세요"
- Access Token이 만료되면 새로 받을 때 사용
- 유효기간이 매우 김 (보통 30일 ~ 1년)
- 안전하게 보관해야 함
🔑 토큰의 형식
JWT (JSON Web Token) - “자가 검증 가능한 토큰”
eyJhbGci.eyJzdWIi.SflKxwRJ
↑헤더 ↑내용 ↑서명
특징:
- 토큰 안에 정보가 모두 들어있어요
- 서버가 DB 조회 없이 바로 검증 가능
- 빠르고 효율적
예시 내용:
{
"sub": "user123",
"scope": "photos:read",
"exp": 1735905600
}
장점: ⚡ 빠름, 서버 부담 적음
단점: ❌ 한 번 발급하면 취소하기 어려움
Opaque Token - “불투명한 토큰”
d4f9a8b3c2e1
(의미 없는 랜덤 문자열)
특징:
- 토큰만 봐서는 아무것도 모름
- 매번 서버에 “이 토큰 유효해?” 물어봐야 함(물어보는 과정을 ‘Introspection’이라고 불러요)
- 즉시 취소 가능
장점: 🔒 보안성 높음, 즉시 취소 가능
단점: 🐌 느림, 서버 부담 증가
🔄 토큰 받는 방법 (Grant Types)
1. Authorization Code - “사용자 로그인 방식”
언제 사용: 웹사이트, 모바일 앱에서 사용자 로그인
과정:
1. 사용자: "구글로 로그인" 버튼 클릭
2. 구글: "이 앱이 사진 보는 거 허락할래?" 물어봄
3. 사용자: "허락" 클릭
4. 구글: 임시 코드 발급 → 앱으로 전달
5. 앱: 코드를 토큰으로 교환 (서버끼리 비밀 통신)
가장 안전한 방식이고, OIDC 로그인에서 표준입니다.
최근에는 보안 강화를 위해 Authorization Code 방식에 PKCE라는 보안 장치를 더하는 것이 표준(OAuth 2.1)입니다. Spring Security는 이를 자동으로 지원합니다.
2. Client Credentials - “서버 간 통신 방식”
언제 사용: 사용자 없이 서버끼리 통신 (예: 정기 데이터 동기화)
과정:
1. 백엔드 서버: "나 정식 등록된 앱이야" + 비밀키 제출
2. 인가 서버: "확인했어" + 토큰 발급
3. 백엔드 서버: 토큰으로 API 호출
특징:
- 사용자 로그인 없음
- ID Token 없음 (사용자가 없으니까)
- Access Token만 받음
🌐 OpenID Connect (OIDC)
OAuth2의 한계
OAuth2만으로는 “이 사용자가 누구인지” 표준적으로 알 수 없었습니다.
OIDC = OAuth2 + 로그인
| 기능 | OAuth2 | OIDC |
|---|---|---|
| 목적 | 권한 위임 | 로그인 + 권한 위임 |
| 토큰 | Access Token | Access Token + ID Token |
| 사용자 정보 | 별도 API 호출 | ID Token에 포함 |
OIDC 사용 예:
"구글로 로그인" = OAuth2 + OIDC
→ Access Token (구글 API 호출용)
→ ID Token (사용자 정보: 이메일, 이름)
💻 Spring Security 7으로 구현하기
1. Resource Server (내 API 보호)
상황: 내가 만든 API를 토큰으로 보호하고 싶어요.
설정 (application.yml):
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://auth.mycompany.com
Spring Security 설정:
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(Customizer.withDefaults())
);
return http.build();
}
}
동작:
- 클라이언트가 API 호출 시 토큰을 헤더에 넣어 보냄
- Spring Security가 자동으로 토큰 검증
- 유효하면 요청 처리, 아니면 401 에러
2. OAuth2 Login (소셜 로그인)
상황: 내 서비스에 “구글로 로그인” 버튼을 넣고 싶어요.
설정 (application.yml):
spring:
security:
oauth2:
client:
registration:
google:
client-id: your-client-id
client-secret: your-secret
scope: openid, profile, email
Spring Security 설정:
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(Customizer.withDefaults());
return http.build();
}
}
동작:
- 사용자가
/oauth2/authorization/google접속 - 구글 로그인 페이지로 리다이렉트
- 로그인 후 우리 서비스로 돌아옴
- Spring Security가 자동으로 세션 생성
3. Client Credentials (서버 간 통신)
상황: 내 백엔드 서버에서 다른 회사 API를 주기적으로 호출해야 해요.
설정 (application.yml):
spring:
security:
oauth2:
client:
registration:
partner-api:
client-id: my-app-id
client-secret: my-secret
authorization-grant-type: client_credentials
scope: api:read
provider:
partner-api:
token-uri: https://partner.com/oauth/token
사용 코드:
@Service
public class PartnerApiService {
private final RestClient restClient;
public PartnerApiService(RestClient.Builder builder) {
this.restClient = builder
.requestInterceptor(new OAuth2ClientHttpRequestInterceptor())
.build();
}
public String getData() {
// 토큰이 자동으로 헤더에 추가됨
return restClient.get()
.uri("https://partner.com/api/data")
.retrieve()
.body(String.class);
}
}
🎓 핵심 정리
OAuth2 vs OIDC 선택 가이드
| 목적 | 사용 |
|---|---|
| 사용자 로그인 기능 | OIDC (OAuth2 + openid scope) |
| 제3자 API 호출 권한 | OAuth2 |
| 사용자 정보 필요 | OIDC (ID Token 사용) |
| 서버 간 통신 | OAuth2 (Client Credentials) |
토큰 선택 가이드
| 상황 | 추천 |
|---|---|
| 마이크로서비스 환경 | JWT (빠르고 독립적) |
| 엄격한 보안 필요 | Opaque Token (즉시 취소 가능) |
| 외부 API 연동 | 상대방이 정한 방식 따르기 |
Spring Security 7 특징
- Bean 중심 설정: 필요한 기능을 Bean으로 등록하면 자동 감지
- 간소화된 코드: 기본 설정만으로도 대부분 동작
- RestClient 지원: 동기 방식 HTTP 클라이언트 공식 지원
❓ 자주 묻는 질문
Q: JWT가 탈취당하면 어떻게 하나요?
A: JWT는 만료 전까지 취소가 어렵습니다. 그래서 유효기간을 짧게 (1시간 이하) 설정하고, Refresh Token으로 자주 갱신하는 방식을 사용합니다. JWT 탈취가 걱정된다면, Refresh Token을 한 번만 사용 가능하게 만드는 ‘Refresh Token Rotation’ 기법을 사용하면 더욱 안전합니다.
Q: ID Token으로 API를 호출해도 되나요?
A: 안 됩니다. ID Token은 “로그인한 사용자 정보 확인”용입니다. API 호출은 반드시 Access Token을 사용하세요.
Q: OAuth2 없이 JWT만 쓰면 안 되나요?
A: 가능하지만, OAuth2를 쓰면 토큰 발급/갱신/취소 등의 표준 방식을 제공받아 더 안전하고 편리합니다.
Q: 어떤 Grant Type을 써야 하나요?
- 웹/모바일 로그인 → Authorization Code
- 서버 간 통신 → Client Credentials
- 기타 방식은 보안 이슈가 있어 비추천
🚀 마무리
OAuth2와 OIDC는 처음엔 복잡해 보이지만, 핵심은 간단합니다:
- 비밀번호를 직접 주고받지 않기
- 필요한 권한만 제한적으로 주기
- 언제든지 권한을 취소할 수 있게 하기
Spring Security 7은 이 모든 과정을 최대한 자동화해서, 개발자는 비즈니스 로직에만 집중할 수 있게 도와줍니다.
시작 팁: 먼저 Google OAuth2로 로그인 기능을 만들어보세요.
spring-boot-starter-oauth2-client만 추가하고client-id와client-secret만 설정하면 바로 작동합니다!
Subscribe via RSS