💡 오늘 학습 키워드
- OAuth(Kakao 연동)
🎯 학습 내용 정리
1. OAuth(Kakao 연동)
OAuth는 Open Authorization의 약자로 사용자의 비밀번호를 직접 전달하지 않고, 제3자 서비스의 인증을 이용하여 로그인할 수 있도록 해주는 인증 프로토콜이다.
카카오 로그인을 예로 들면 다음과 같은 흐름으로 동작한다.
Client
↓
Server
↓
Kakao Authorization Server
↓
Server
↓
Client
즉,
- 사용자가 카카오 로그인을 요청한다.
- 카카오 로그인 페이지에서 인증을 수행한다.
- 카카오가 인가 코드(Authorization Code)를 서버로 전달한다.
- 서버는 인가 코드로 액세스 토큰을 요청한다.
- 액세스 토큰을 이용해 사용자 정보를 조회한다.
- 회원가입 또는 로그인을 수행한다.
- JWT를 발급하여 클라이언트에게 전달한다.
이번 구현에서는 Server에서 수행하는 OAuth 인증 과정을 직접 구현하였다.
먼저 카카오 로그인에 성공하면 등록한 Redirect URI로 인가 코드가 전달된다.
@GetMapping("/user/kakao/callback")
public ResponseEntity<Void> kakaoLogin(@RequestParam String code) throws JsonProcessingException {
String jwt = kakaoService.kakaoLogin(code);
return ResponseEntity.ok()
.header(HttpHeaders.AUTHORIZATION, "Bearer " + jwt)
.build();
}
여기서 전달받는 code가 바로 Authorization Code(인가 코드) 이다. 이 코드는 매우 중요한 값이며 1회만 사용할 수 있다.
이후 모든 인증 과정은 Service에서 수행한다.
KakaoService에서는 크게 두 가지 작업을 수행한다.
public String kakaoLogin(String code) throws JsonProcessingException {
// 1. 인가 코드 -> 액세스 토큰
String accessToken = getToken(code);
// 2. 액세스 토큰 -> 사용자 정보 조회
KakaoUserInfoDto kakaoUserInfo = getKakaoUserInfo(accessToken);
return null;
}
OAuth 인증 과정은 생각보다 단순하다.
Authorization Code
↓
Access Token
↓
User Info
이후 조회한 사용자 정보를 이용하여 회원가입 또는 로그인을 수행하면 된다.
OAuth에서는 외부 API를 호출해야 한다. 그렇기 때문에 RestTemplate을 Bean으로 등록하여 사용하였다.
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(5))
.build();
}
}
왜 Timeout을 설정해야 할까?
외부 서버가 장애가 발생하거나 응답하지 않는 경우,
Server
↓
Kakao API
↓
응답 없음
서버는 계속 대기하게 된다.
Timeout을 지정하면 일정 시간이 지나면 예외가 발생하여 서버가 무한정 대기하는 상황을 방지할 수 있다.
OAuth의 첫 번째 과정은 인가 코드를 액세스 토큰으로 교환하는 것이다.
private String getToken(String code)
1. 요청 URL 생성
URI uri = UriComponentsBuilder
.fromUriString("https://kauth.kakao.com")
.path("/oauth/token")
.build()
.toUri();
Spring에서는 UriComponentsBuilder를 이용하여 URL을 안전하게 생성할 수 있다.
2. Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add(
"Content-type",
"application/x-www-form-urlencoded;charset=utf-8"
);
카카오 토큰 API는 Form 데이터 방식만 지원한다.
3. Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", REST_API_KEY);
body.add("redirect_uri", REDIRECT_URI);
body.add("code", code);
body.add("client_secret", CLIENT_SECRET);
여기서 중요한 값은 다음과 같다.
- grant_type
- client_id
- redirect_uri
- code
- client_secret
특히 redirect_uri는 카카오 개발자 사이트에 등록한 URI와 반드시 동일해야 한다.
4. HTTP 요청
RequestEntity<MultiValueMap<String, String>> requestEntity =
RequestEntity.post(uri)
.headers(headers)
.body(body);
ResponseEntity<String> response =
restTemplate.exchange(
requestEntity,
String.class
);
RestTemplate.exchange()를 이용하면 Header와 Body를 함께 전송할 수 있다.
5. JSON 파싱
응답은 JSON으로 내려온다.
{
"access_token":"xxxxxxxx",
"refresh_token":"xxxxxxxx"
}
이를 Jackson을 이용하여 파싱한다.
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
return jsonNode.get("access_token").asText();
액세스 토큰을 얻었다면 사용자 정보를 요청할 수 있다.
private KakaoUserInfoDto getKakaoUserInfo(String accessToken)
이번에는 Authorization Header가 필요하다.
headers.add(
"Authorization",
"Bearer " + accessToken
);
카카오는 Bearer Token 방식으로 인증한다.
요청 후 응답은 다음과 같다.
{
"id":1632335751,
"properties":{
"nickname":"르탄이"
},
"kakao_account":{
"email":"letan@sparta.com"
}
}
필요한 값만 추출한다.
Long id = jsonNode.get("id").asLong();
String nickname = jsonNode.get("properties")
.get("nickname")
.asText();
String email = jsonNode.get("kakao_account")
.get("email")
.asText();
이후 DTO에 저장하여 반환한다.
return new KakaoUserInfoDto(
id,
nickname,
email
);
현재 코드에서는 사용자 정보 조회까지만 구현되어 있다.
실제 서비스에서는 다음 과정이 추가되어야 한다.
사용자 정보 조회
↓
기존 회원 조회
↓
없으면 회원가입
↓
JWT 생성
↓
Client 전달
즉,
User user = userRepository.findByEmail(...);
if (user == null) {
회원가입
}
JWT 생성
Response 반환
까지 구현되어야 OAuth 로그인이 완성된다.
그렇다면 구현하면서 주의해야 할 점에는 어떠한 것들이 있을까?
1. Secret Key를 코드에 작성하지 않기
예제에서는 이해를 위해
body.add("client_secret", "...");
body.add("client_id", "...");
를 직접 작성했지만 실제 프로젝트에서는 절대 이렇게 작성하지 않는다.
application.yml 또는 환경 변수로 관리하는 것이 안전하다.
kakao:
client-id:
client-secret:
2. Redirect URI는 반드시 동일해야 한다
카카오 개발자 사이트
↓
http://localhost:8080/api/user/kakao/callback
↓
Spring 코드
↓
http://localhost:8080/api/user/kakao/callback
한 글자라도 다르면 인증이 실패한다.
3. 이메일 제공 여부 확인
모든 사용자가 이메일 제공에 동의하는 것은 아니다.
현재 코드는
jsonNode.get("kakao_account")
.get("email")
.asText();
로 바로 접근한다.
하지만 이메일 제공에 동의하지 않은 경우 NullPointerException이 발생할 수 있으므로
JsonNode account = jsonNode.path("kakao_account");
String email = account.path("email").asText(null);
처럼 안전하게 처리하는 것이 좋다.
4. RestTemplate 대신 WebClient
Spring Boot 3.x 이후에는
RestTemplate
보다
WebClient
사용이 권장된다.
📚 한줄 정리
이번에는 OAuth 2.0을 이용하여 카카오 로그인 과정을 직접 구현해보았다. 단순히 로그인 버튼을 연동하는 것이 아니라, 인가 코드로 액세스 토큰을 발급받고, 액세스 토큰을 이용해 사용자 정보를 조회하는 전체 인증 흐름을 이해할 수 있었다.
또한 외부 API를 호출하는 과정에서 RestTemplate의 사용법과 HTTP Header, Body 구성 방식, JSON 응답 파싱 방법까지 함께 학습할 수 있었다. 앞으로는 여기서 한 단계 더 나아가 사용자 정보를 기반으로 회원가입과 로그인을 처리하고, JWT를 발급하여 Spring Security와 연계한 완전한 소셜 로그인 기능까지 구현해보며 인증 시스템에 대한 이해를 더욱 깊게 다져나갈 예정이다.
'Bootcamp > Implementation' 카테고리의 다른 글
| UserDetails와 UserDetailsService (0) | 2026.07.09 |
|---|---|
| Authentication과 SecurityContextHolder (0) | 2026.07.08 |
| JWT 기반 로그인 구현과 Access Token 발급 (0) | 2026.07.07 |
| Spring Security 구조 설계 (0) | 2026.07.06 |
| Spring Security + JWT (0) | 2026.06.27 |