[OAuth 2.0&Session] 카카오 API를 활용한 로그인 구현
OAuth (Open Authorization)란?
구글, 페이스북, 트위터와 같은 다양한 플랫폼의 특정한 사용자 데이터에 접근하기 위해 제3자 클라이언트(우리의 서비스)가 사용자의 접근 권한을 위임(Delegated Authorization)받을 수 있는 표준 프로토콜이다.
쉽게 말하자면, 우리의 서비스가 우리 서비스를 이용하는 유저의 타사 플랫폼 정보에 접근하기 위해서 권한을 타사 플랫폼으로부터 위임 받는 것 이다.
출처: https://hudi.blog/oauth-2.0/
카카오 API 인증 흐름 (로그인)
- 사용자 인증 요청: 사용자가 카카오 로그인 버튼을 클릭하면, 카카오 인증 페이지로 리디렉션됩니다.
- 사용자 동의: 사용자가 카카오 인증 페이지에서 권한 요청에 동의합니다.
- Authorization Code 발급: 카카오가 인가 코드를 서비스에 전달합니다.
- Access Token 발급 요청: 서비스는 인가 코드를 사용하여 카카오에 액세스 토큰을 요청합니다.
- Access Token 발급: 카카오가 액세스 토큰을 서비스에 전달합니다.
- 사용자 정보 요청: 서비스는 액세스 토큰을 사용하여 카카오로부터 사용자 정보를 가져옵니다.
- 로그인 완료: 서비스가 받은 사용자 정보를 기반으로 로그인 절차를 완료합니다.
상세 설계
프론트엔드 (React):
- 카카오 로그인 버튼: 사용자가 클릭하면 카카오 인증 페이지로 이동하도록 설정.
- 콜백 페이지: 인증 완료 후 리디렉션될 페이지로, 인가 코드를 처리.
- 프로필 페이지: 세션에서 받아온 유저 정보를 화면에 보여줌.
백엔드 (Java Spring):
- 인가 코드 처리 엔드포인트: 콜백 페이지에서 받은 인가 코드를 처리하고, 액세스 토큰을 요청.
- 액세스 토큰 처리: 액세스 토큰을 받아 사용자 정보를 요청.
- 사용자 정보 저장: 세션을 생성.
- + 사용자 정보를 데이터베이스에 저장
개발하기
1. 카카오 디벨로퍼스에서 카카오 API 사용을 위한 Key 발급
Kakao Developers: https://developers.kakao.com/console/app
참고 문서 : Kakao i 기술문서
1) 애플리케이션 추가하기
2) 두 가지 Key 발급
- REST API Key ⇒
Client ID
로 사용 - Secret Key ⇒
Client Secret
로 사용- 보안을 강화하기 위해 선택적으로 Client Secret 값을 사용
⇒ 탈취 방지를 위해 환경 변수로 노출을 방지한다.
3) Redirect URL 설정
- 카카오 로그인 탭 → 활성화 → Redirect URL 등록
- 사용자가 카카오 로그인을 성공하면, 카카오 측에서 Client를 Redirect 해줄 URI를 설정
!에러 주의!
💡 React 연동시 localhost:3000 으로 redirect url을 설정
- React 애플리케이션은 프론트엔드 클라이언트이며, 보통 개발 중에
http://localhost:3000
에서 실행됩니다. - Spring Boot 애플리케이션은 백엔드 서버이며, 보통 개발 중에
http://localhost:8080
에서 실행됩니다. - React 애플리케이션에서 카카오 로그인 프로세스를 처리
- URL에서 인증 코드를 추출하여 백엔드 서버(
http://localhost:8080/callback
)로 전송 (POST)
- URL에서 인증 코드를 추출하여 백엔드 서버(
- API 요청을 받은 백엔드 서버(
http://localhost:8080/callback
)는 토큰 교환 및 사용자 정보를 처리합니다.- 카카오 API를 호출하여 액세스 토큰 및 사용자 정보를 받아와 세션에 저장하거나 데이터베이스에 저장
추가
-
동의항목 설정
카카오 로그인 과정에서 사용자로부터 받을 정보를 설정
2. 로그인 페이지 구현
http://localhost:3000 으로 접속시 React 기본 화면으로 이동
- 로그인 버튼을 간단히 구현하고 버튼 클릭시 로그인 요청을 보낼 URL을 연결
-
URL 형식
private final static String KAUTH_TOKEN_URL_HOST = "https://kauth.kakao.com"; public String getKakaoLogin() { return KAUTH_TOKEN_URL_HOST + "/oauth/authorize" + "?client_id=" + KAKAO_CLIENT_ID + "&redirect_uri=" + KAKAO_REDIRECT_URL + "&response_type=code"; }
- 백에서
/login
엔드 포인트로 GetMapping -
프론트에서 axios를 통해 URL을 get
axios.get('http://localhost:8080/login') .then(response => { setLoginUrl(response.data.location); }) .catch(error => { console.error('There was an error fetching the login URL!', error); });
- 프론트엔드에서 Axios 또는 Fetch API를 사용하여 데이터를 가져올 때 JSON 형식의 응답을 처리하는 것이 일반적
- 확장성, 유연성 : JSON 형식을 사용하면 추가적인 정보를 쉽게 포함할 수 있습니다. 예를 들어, 로그인 URL 외에도 메시지, 상태 코드 등 다른 데이터를 함께 전송할 수 있습니다.
3. Callback 페이지 구현
엑세스 토큰 요청
- HTTP 요청 (POST)
https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={KAKAO_CLIENT_ID}&client_secret={KAKAO_CLIENT_SECRET}&redirect_uri={KAKAO_REDIRECT_URL}&code={code}
Postman으로 사전 확인
1. WebClient를 사용한 POST 요청
KakaoTokenResponseDto kakaoTokenResponseDto = WebClient.create(KAUTH_TOKEN_URL_HOST).post()
.uri(uriBuilder -> uriBuilder
.scheme("https")
.path("/oauth/token")
- Kakao 인증 서버의 토큰 발급 URL에 POST 요청을 보냅니다.
2. HTTP 헤더 및 요청 본문 설정
.queryParam("grant_type", "authorization_code")
.queryParam("client_id", KAKAO_CLIENT_ID)
.queryParam("client_secret", KAKAO_CLIENT_SECRET)
.queryParam("redirect_uri", KAKAO_REDIRECT_URL)
.queryParam("code", code)
.build(true))
.header(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString())
- 요청의 콘텐츠 타입을
application/x-www-form-urlencoded
로 설정하고, 요청 본문에 인증 코드 및 기타 필요한 정보를 포함합니다.
3. 응답 처리 및 예외 처리
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, clientResponse -> {
log.error("Client error while requesting token: {}", clientResponse.statusCode());
return Mono.error(new RuntimeException("Invalid authorization code or parameters."));
})
.onStatus(HttpStatusCode::is5xxServerError, clientResponse -> {
log.error("Server error while requesting token: {}", clientResponse.statusCode());
return Mono.error(new RuntimeException("Kakao server error. Please try again later."));
})
.bodyToMono(KakaoTokenResponseDto.class)
.block();
- 서버의 응답 상태 코드를 확인하고, 4xx 오류가 발생하면 클라이언트 측 오류로 간주하여 에러 로그를 남기고 사용자 정의 예외를 발생시킵니다.
- 5xx 오류가 발생하면 서버 측 문제로 간주하여 다른 오류 메시지를 로그에 남깁니다.
- 최종적으로 응답 본문을
KakaoTokenResponseDto
객체로 변환합니다.
4. 응답 처리
if(kakaoTokenResponseDto != null) {
log.info(" [Kakao Service] Access Token ------> {}", kakaoTokenResponseDto.getAccessToken());
log.info(" [Kakao Service] Refresh Token ------> {}", kakaoTokenResponseDto.getRefreshToken());
log.info(" [Kakao Service] Id Token ------> {}", kakaoTokenResponseDto.getIdToken());
log.info(" [Kakao Service] Scope ------> {}", kakaoTokenResponseDto.getScope());
} else {
throw new RuntimeException("Failed to obtain access token");
}
- 액세스 토큰 수신 여부를 확인하고, 성공적으로 수신한 경우 각 토큰 및 정보(리프레시 토큰, ID 토큰, 스코프)를 로그에 남깁니다.
- 만약 응답이 null이라면, 액세스 토큰을 얻는 데 실패했다는 예외를 발생시킵니다.
5. 액세스 토큰 반환
return kakaoTokenResponseDto.getAccessToken();
- 성공적으로 수신한 액세스 토큰을 반환합니다.
사용자 정보 요청
- HTTP 요청 (GET)
https://kapi.kakao.com/v2/user/me?access_token={access_token}
세션에 사용자 정보 저장 (HttpSession)
session.setAttribute("userId", userInfo.getId()); //key, value
session.setAttribute("userInfo", userInfo);
- 인가 코드의 경우 단일 사용, 즉 단 한 번만 사용할 수 있다. 그래서 새로고침을 할 경우 유저 정보가 사라지는 이슈가 있다.
- 단일 사용: 인증 코드는 한 번만 사용할 수 있습니다. 이미 액세스 토큰을 요청하는 데 사용된 경우, 해당 코드는 더 이상 유효하지 않습니다. 새로운 인증 코드를 사용해야 합니다.
- 그렇기 때문에 로그인 유지하는 방법을 적용해야 한다.
- 로컬 스토리지 or 세션
세션 → 서버에서 저장 (서버 구축x, DBx)
- 별도로 저장해주지 않는다면 저장하지 않는다.
쿠키 → 클라이언트에 저장
- 도메인별로 쿠키 저장
- /user 에서 쿠키를 받는다하면 아래 저장
4. Profile 페이지 구현
Java Spring
KakaoUserInfoResponseDto userInfo = (KakaoUserInfoResponseDto) session.getAttribute("userInfo");
- session에 저장한 user 정보를 key 값을 이용해서 가져오기
React
axios.get('http://localhost:8080/profile', { withCredentials: true })
.then(response => {
setUserInfo(response.data);
console.log("프로필 정보 가져오기 성공:", response.data);
})
.catch(error => {
console.error('프로필 정보 가져오기 중 오류 발생!', error);
});
- 프로필을 get 방식으로 프론트에서 받아오기
참고 블로그
https://ddonghyeo.tistory.com/16
https://shxrecord.tistory.com/290
코드 및 시연 영상
백엔드 : https://github.com/KimGyeongLock/kakao_login_backend
프론트엔드 : https://github.com/KimGyeongLock/kakao_login_frontend