| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Spring API
- 예외
- 백엔드스쿨
- 엔티티 생명주기
- html
- 백엔드공부
- Java
- Docker
- jenkins
- 카카오인증토큰받기
- spring
- 카카오인가코드받기
- 제로베이스
- 엔티티 매니저
- 카카오사용자정보가져오기
- ci/cd
- 백엔드 로드맵
- 어떤 개발자?
- GitHub_Actions
- static
- 인스턴스
- button
- feignClient
- form
- tag
- oAuth2
- MIND 2023 #후기
- 상속
- input
- Interface
- Today
- Total
HiDevelop
OAuth2 ( 실습 편) 본문
카카오 로그인 API 구현하기!
전 포스트에서 OAuth2에 대한 이론에 대해 공부해보았습니다. 이제 한 번 서비스와 카카오 소셜 로그인 API를 연동해보겠습니다.
(https://hidevelop.tistory.com/79)
OAuth2 (이론 및 준비)
사용자 편리하게 어느 앱이나 웹에서 편리하게 이용할 수 있는 소셜로그인 기능! 이 기능은 어떻게 가능한 걸까요..? 목차 1. OAuth란? 2. OAuth 구성 요소 3. OAuth 2구현을 위한 준비 설계 OAuth란? 앱을
hidevelop.tistory.com
목차
1. 카카오 API 연동을 위한 사전 준비!
2. 카카오 인가코드 요청해서 redirect uri로 받아오기!
3. 카카오에서 AccessToken 받아오기!
4. 카카오에서 사용자 정보 저장하기!
1. 카카오 API 연동을 위한 사전 준비.
카카오 소셜 로그인을 사용하기 위해서는 몇 가지 준비해야할 것이 있습니다.
소셜 로그인을 구현하는 방법에는 크게 REST API, JavaScript, Android, iOS, Flutter 등으로 나뉘는데요. 저 같은 경우에는 App과 Web에서 모두 사용할 수 있는 REST API 방벙을 사용하겠습니다.
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
필요한 준비물 : REST API KEY, rediret URI, 동의항목
1.1 REST API KEY 발급받기
카카오 디벨롭퍼의 홈페이지에 들어가게 되면 오른쪽 상단에 내 어플리케이션을 클릭해줍니다.

클릭하게 되면 다음과 같은 화면이 나오는데, 여기서 애플리케이션 추가하기를 누른 후 나만의 어플리케이션을 만들어줍니다. 테스트 용이나 토이 프로젝트로 만들어본다면, 기입칸에 맘에 드시는 걸로 작성하셔도 상관없습니다.

추가를 하시게 되면 어플리케이션 목록의 자신이 추가한 목록이 뜨시게 됩니다. 클릭해서 들어가보면 다음과 같은 정보가 제공됩니다!

1.2 동의항목
저는 REST API KEY를 사용할 것 입니다. KEY가 준비되었으면 다음으로는 동의항목으로 가보시죠

동의항목을 보시면 다음과 같은 화면이 나오는데 거기서 필요한 항목들만 "설정" 버튼을 눌러 필수, 선택 동의로 만들어줍시다.

저의 경우에는 "닉네임" 과 "카카오계정(이메일)" 정도만 동의 항목으로 체크해주었습니다. 😀
1.3 redirect url
redirect url의 경우, 저희 서버에서 인가코드를 요청했을 때, 카카오서버에서 인가코드를 보내줄 url을 뜻합니다.
쉽게 말하자면
서비스 : 카카오얌 인가코드좀 줘!
카카오 : 어디에 줘야하는데? redirect urI로 보낼건데 등록했어..? 등록했으면 보내줄게~!
요런 상황이 되는거죠.

이제 돌아와서 위에 사진처럼 카카오 로그인을 클릭해봅시다. 클릭하면 요런화면이 나오는데


활성화 설정에서 상태를 ON 으로 바꿔주시면 아래 Redirect URI 구역이 나오게 됩니다. Redirect URI 등록을 눌러주시면
위와 같은 창이 뜨는데 원하시는 인가코드를 받을 uri를 등록해주시면 됩니다.
자 이제 카카오 서버와 통신할 모든 준비물 맞췄습니다. 이제 본격적으로 구현으로 들어가보시죠!
2. 카카오 인가코드 요청해서 redirect uri로 받아오기!

저희는 위 사진에 있는 부분을 시도해 볼 거에요! 원래는 인가코드를 받아서 redirecturl로 넘기는 부분은 FE쪽에서 처리하면 좋지만, 저 같은 경우에는 일단 FE분이 없으니 혼자서 한번 해보겠습니다. 😂
2.1 build.gradle
//feign-client
ext {
springCloudVersion = '2021.0.3'
}
//feign-client
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//yml에서 속성불러오기
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
//feign-client
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
}
저의 경우에는 카카오 인증서버, 자원서버와 통신하기위해서 FeignClient를 사용하기위해 관련 의존성을
추가해줬습니다😀
FeignClient에 대해 알아보실려면 아래 링크를 통해 한 번 보시고 오는 것을 추천해드립니다😁
https://hidevelop.tistory.com/80
간단한 서버 간 통신 @FeignClient
인턴 기간 중에 새로운 어플리케이션 서버 구축 업무를 받게 되었어요!! 흠 일단는 어느 정도 설계를 잡아가고 있는 중에 개발하는 어플리케이션의 예상 사용자들이 40대 이상이 가장 많을 것 같
hidevelop.tistory.com
2.2 인가 코드 요청 보내기
인가코드를 보내기위해서는 다음과 같은 녀석들이 필요합니다.

일단 위와 같이 해당 URL로 GET HTTP Method를 통해 보내야합니다. 그 외에 필요한 정보는

client_id ( 저희가 아까 발급받은 REST API KEY), redirect_uri 와 response_type이 필요하네요!
그럼 이 녀석들을 어떻게 보내냐 하면,
https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}
요렇게 url에 담아서 보내면 되겠습니다 response_type은 code로 client_id와 redirect uri는 괄호와 $를 제거하고 넣어주면 됩니다. 그럼 인가코드를 보내기위한 클래스를 만들어보겠습니다.
2.2.1 인가코드를 보내기 위한 클래스 KakaAuthCode와 application.yml
application.yml
oauth2:
kakao:
infoUrl: https://kapi.kakao.com
baseUrl: https://kauth.kakao.com
clientId: 1111111111111111111111111 (REST_API_KEY)
redirectUri: http://localhost:8080/user/kakao/oauth
저의 경우에는 카카오 관련된 개인정보를 담고 있다보니 yml에 저장하여 사용했습니다. 😃
KakaoAuthCode.class
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "oauth2.kakao")
public class KakaoAuthCode {
private String baseUrl;
private String clientId;
private String redirectUri;
public String kakaoUrlInit(){
Map<String, Object> params = new HashMap<>();
params.put("response_type", "code");
params.put("client_id", getClientId());
params.put("redirect_uri", getRedirectUri());
String paramStr = params.entrySet().stream()
.map(param->param.getKey() + "=" + param.getValue())
.collect(Collectors.joining("&"));
return getBaseUrl() + "/oauth/auhtorize" + "?" +paramStr;
}
}
@ConfigurationProperties(prefix="~~~") 사용하게되면 각 변수에 맞게 Spring에서 값을 넣어줍니다. 당연히 그렇게 하기위해서는 @Component를 통해 Bean으로 등록을 해줘야 합니다 😁
kakaoUrlInit( )에 대해 간단히 설명해 드리자면, 저 메소드를 통과하면,
https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}
요렇게 uri이 만들어 지게 됩니다. 이제 URI 객체에 이 문자열을 넣어서 보내주기만 하면됩니다.
KakaoService.class
import com.example.feignclientstudy.dto.KakaoAuthCode;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import java.net.URI;
import java.net.URISyntaxException;
@Service
@RequiredArgsConstructor
public class KakaoService {
private final KakaoAuthCode kakaoAuthCode;
public HttpHeaders getKakaoAuthCode(){
return createHttpHeader(kakaoAuthCode.kakaoUrlInit());
}
private HttpHeaders createHttpHeader(String str){
try {
URI uri = new URI(str);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(uri);
return httpHeaders;
} catch (URISyntaxException e){
e.printStackTrace();
}
return null;
}
}
KakaoController.class
@RestController
@RequiredArgsConstructor
public class KakaoController {
private final KakaoService kakaoService;
@GetMapping("/user/kakao/login")
public ResponseEntity<?> kakaoLogin(){
HttpHeaders httpHeaders = kakaoService.getKakaoAuthCode();
return httpHeaders != null ? new ResponseEntity<>(httpHeaders, HttpStatus.SEE_OTHER) : ResponseEntity.badRequest().build();
}
}
createHttpHeader 메소드를 통해 저희가 kakaoUrlInit으로만든 문자열을 uri 객체 넣어 HttpHeader에 넣어줍니다. 그리고 getKakaoAuthCode 메소드를 통해 컨트롤러가 가게되면서 저희 카카오 인증 서버로 요청이 보내지게 되는겁니다 😉
자 그럼 실행되는지 확인을 해보아야곘죠?
View
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<a href="http://localhost:8080/user/kakao/login"><img src="image/kakao_login_medium.png" alt="카카오 로그인"></a>
</body>
</html>
테스트를 위해 정말 간단하 이미지를 만들어봤어여!
테스트에 사용한 이미지는 카카오 리소스 부분에 다운받아 사용하실수 있습니다.

이제 한 번 테스트를 실행해보겠습니다.

로그인 버튼을 누르면 로그인 페이지가 뜨게 되고, 요런식으로 동의항목이 나오게됩니다.

동의하고 계속하기로 누르면, 아직 저희가 redirectUrl API를 만들지 않아 저렇게 뜨지만 위에 도메인을 보시면, code = "~~"가 뜨신게 보일 겁니다. 이렇게되면 인가코드를 받는 부분이 완성된 거에요!

3. 카카오에서 AccessToken 받아오기
이제 저희가 받은 인가코드를 통해 카카오에 AccessToken을 받아와 볼거에요!

일단 Accesstoken을 받기위해 요청을 보낼 때, 필요한 데이터들을 준비해봅시다.

일단 HTTP Method POST이고 URL: https://kauth.kakao.com/oauth/token 으로 보내면 되겠네요!

필요한 정보는 이렇게 되네요! 요청을 보내기위해서는 Header의 Content-type을 application/x-www-form-urlencoded:charset=utf-8로 보내고 Body에는 grant_type, client_id, redirect_uri, code로 보내면 될 것같아요!!(2차인증을 해두신상태라면 secret_key도 필요해요! 카카오 로그인 2차를 해두신 분들이라면, 자세한사항은 카카오 문서를 참고해주세요! )
일단 요청을 보내기위해서는 요청을 보내는 데이터 클래스와 응답을 받는 데이터 클래스를가 필요합니다.
요청 데이터클래스

요청에는 위에 녀석들이 필요하군요 이걸 데이터 클래스로 만들어봅시다.
KakaoDto.KAccessToken
public class KakaoDto {
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class KAccessTokenReq {
private String code;
private String client_id;
private String redirect_uri;
private final String grant_type = "authorization_code";
public static KAccessTokenReq newInstance(String code, String clientId, String redirectUri) {
return KAccessTokenReq.builder()
.code(code)
.client_id(clientId)
.redirect_uri(redirectUri)
.build();
}
}
}
저의 경우에는 Kakao 관련된 데이터 클래스를 한 클래스에서 관리하기 위해 Inner 클래스로 KAccessTokenReq 클래스를 선언해주었습니다.
newInstance( )의 경우에는 요청을 보내기 위해 필요한 데이터를 넣은 메소드라고 보시면됩니다. 이제 요청을 보낼 데이터클래스를 만들었으니 응답을 받을 데이터 클래스를 만들어야겠죠?
응답 데이터 클래스

응답 데이터에는 위와 같은 녀석들이 있습니다. 데이터에 매핑되는 변수들을 선언해주죠.
KakaoDto.KAccessTokenRes
public class KakaoDto {
//생략
@Getter
@Setter
public static class KAccessTokenRes {
private String token_type;
private String access_token;
private String expires_in;
private String refresh_token;
private String refresh_token_expires_in;
@Override
public String toString() {
return "code=" + code + '&' +
"client_id=" + client_id + '&' +
"redirect_uri=" + redirect_uri + '&' +
"grant_type=" + grant_type;
}
}
}
응답 받을 데이터 값들을 선언해주었습니다. 여기서 주의할점 KAccessTokenRes만 작성해준다면, 서버로 요청을 보낼 때, Json형식 데이터로 값이 보내지게 됩니다. 카카오서버와 통신하기 위해서는 Content_Type : application/x-www-form-urlencoded 이기 때문에 toString을 오버라이드해서 x-www-form-urlencoded에 맞는 요청 바디 형식으로 보내줍시다.
x-www-form-urlencoded에 맞는 요청 바디 형식
code=sadsadlfj1234&client_id=1234dlfk34o5lm&redirect_uri=~~~~~~&grant_type=authorization_code
이제 본격적으로 카카오 서버와 통신을 해보죠 저 같은 경우에는 FeignClient를 이용해서 카카오 서버와 통신할겁니다. FeignClient에 대해 잘모르시면 아래 포스트를 읽어보시기를 권장해드려요.
https://hidevelop.tistory.com/80
간단한 서버 간 통신 @FeignClient
인턴 기간 중에 새로운 어플리케이션 서버 구축 업무를 받게 되었어요!! 흠 일단는 어느 정도 설계를 잡아가고 있는 중에 개발하는 어플리케이션의 예상 사용자들이 40대 이상이 가장 많을 것 같
hidevelop.tistory.com
KakaoAccessTokenFeignClient
@FeignClient(name = "kakaoAccessTokenFeignClient", url = "${oauth2.kakao.baseUrl}", configuration = KakaoFeignConfiguration.class)
public interface KakaoAccessTokenFeignClient {
@PostMapping(value = "/oauth/token")
KakaoDto.KAccessTokenRes getKakaoAccessToken(@RequestBody String kAccessTokenReq);
}
FeignClient를 사용해 카카오 서버와 통신할 인터페이스를 선언해주었습니다.
여기서 살펴보아야 할 것들을 보자면
첫번째로 url로, 현재 저 같은 경우에는 yml에서 불러오기 때문에 ${oauth2.kakao.baseUrl}로 되어있는데 yml파일을 보면
oauth2:
kakao:
infoUrl: https://kapi.kakao.com
baseUrl: https://kauth.kakao.com
clientId: 비밀비밀비밀비밀비밀비밀
redirectUri: http://localhost:8080/user/kakao/oauth
요런식으로 되어있으니 url에 들어가는 값은 https://kauth.kakao.com이 되겠네요. 그리고 @PostMaping에 있는 /oauth/token으로 되어있어 통신할 서버의 uri인 https://kauth.kakao.com/oauth/token이 되는 거에요.
그 다음 살펴 본것은 configuration입니다.
보시면 "configuration = KakaoFeignConfiguration.class" 이렇게 되어있는데, 제가 아까 카카오 서버와 통신하기 위해서는 Content-Type : application/x-www-form-urlencoded 로 되어있어야 된다고 말씀 드렸죠. 그러한 설정을 configuration에서 하는 겁니다. 해당 클래스를 보면
KakaoFeignConfiguration
@Configuration
public class KakaoFeignConfiguration {
@Bean
public RequestInterceptor requestInterceptor() {
return template -> template.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
}
@Bean
public ErrorDecoder errorDecoder() {
return new KakaoFeignClientErrorDecoder();
}
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
여기서 RequestInterceptor에서 원하는 Header값을 설정해주면 되는거죠.
또한 FeignClient에대해서 짧게 설명드리자면 FeignClient에서는 자동으로 등록해주는 Bean들이 있지만, 그렇지 않은 Bean들이 있습니다. 그 경우 대표적인 예로 ErrorDecoder와 Logger입니다. 그래서 따로 Bean으로 등록해주었습니다. 자세한 내용은 위에 올려드린 FeignClient 포스트에 적혀있으니 참고해주요.
자 이제 테스트를 한 번 진행해 보아야겠죠?
일단 통신할 수 있도록 Service와 Controller를 만들어줍시다.
KakaoService
@Service
@RequiredArgsConstructor
public class KakaoService {
@Value("${oauth2.kakao.clientId}")
private String clientId;
@Value("${oauth2.kakao.redirectUri}")
private String redirectUri;
private final KakaoAuthCode kakaoAuthCode;
private final KakaoAccessTokenFeignClient kakaoAccessTokenFeignClient;
public HttpHeaders getKakaoAuthCode(){
//생략
}
private HttpHeaders createHttpHeader(String str){
//생략
}
public KakaoDto.KAccessTokenRes getKakaoAccessToken(String code){
return kakaoAccessTokenFeignClient.getKakaoAccessToken(
KakaoDto.KAccessTokenReq.newInstance(code,clientId,redirectUri).toString()
);
}
}
getKakaoAccessToken을 보시면 code를 매개변수로 받아 KAccessTokenReq.newInstance로 넘겨 요청에 필요한 데이터 를 넣어준뒤 toString을 통해서 kakaoAccessTokenFeignClient.getKakaoAccessToken으로 넘겨 카카오 서버로 요청을 보내고 있습니다.
KaKaoController
@RestController
@RequiredArgsConstructor
public class KakaoController {
private final KakaoService kakaoService;
@GetMapping("/user/kakao/login")
public ResponseEntity<?> kakaoLogin(){
//생략
}
@GetMapping("/user/kakao/oauth")
public ResponseEntity<?> kakaoToken(@RequestParam String code){
var result = kakaoService.getKakaoAccessToken(code);
return ResponseEntity.ok(result);
}
}
이제 Controller계층인데요. 여기서 신경써야할 부분이 바로 uri 부분입니다. 저희가 아까 설정한 redirect_uri가 token을 받아오는 api의 uri가 되어야 카카오 서버로 부터 code를 받아올 수 있겠죠?
자 그럼 모든 테스를 진행해보면,

다음과 같이 성공하실 걸 보실 수 있습니다! 이제 마지막 단계만 남았습니다. 여기까지하면 카카오 로그인을 성공하였지만, 저희가 필요한 것 카카오 서버에 있는 사용자의 정보입니다!
4. 카카오에서 사용자 정보 저장하기
사용자 정보를 가져오기 위해서는 2가지 방식을 카카오에서 제공하고 있습니다. 저희의 경우에는 액세스 토큰을 통해서 사용자 정보를 가져와야합니다.
저희의 경우 필요한 데이터 (카카오 닉네임, 이메일)만 요청할 거기 때문에 그에 필요한 요청 데이터 클래스도 만들어줘야해요.


요청 데이터 클래스에는 'property_keys=["kakao_account.email"] 요런식으로 데이터를 넣어주면 되는 것 같습니다.
KInfoReq
@Getter
@Setter
public static class KInfoReq {
private final List<String> property_keys = List.of("kakao_account.email", "kakao_account.profile.nickname");
@Override
public String toString() {
return "property_keys=" + property_keys;
}
}
저의 경우 닉네임과 이메일만 받아올거기 때문에, List를 선언과 동시에 초기화해주었습니다.
이제 응답을 받을 데이터 클래스를 만들어보죠.
응답 데이터 클래스

응답 데이터들을 보니 저희가 필요한 데이터는 id와 kakao_account정도인 것 같습니다. kakao_account에 대해서 한번 살펴보죠.

저 같은 경우에는 닉네임을 필수동의항목으로 요청했기 때문에 profile 안에있는

닉네임을 가져올겁니다. 또 이메일도 요청했으니 확인해보죠.

이메일은 KakaoAccount에서 바로 가져오면되겠네요.

이런 형식으로 데이터가 오다하니 여기에 맞춰서 데이클래스를 작성해보죠. 값들이 안에안에 있으니 많은 데이터 클래스가 필요할 것 같습니다. ㅠㅠ
KInfoRes
@Getter
@Setter
public static class KInfoRes {
private Long id;
private KakaoAccount kakao_account;
}
KakaoAccount
@Getter
@Setter
public static class KakaoAccount{
private String email;
private Profile profile;
}
Profile
@Setter
@Getter
public static class Profile {
private String nickname;
}
아무래도 요청받는 값들이 많이 감싸줘있다보니 클래스가 3개를 구현했습니다.
이제 통신을 보낼 인터페이스를 선언해주기 전에 Configuration을 만들어 보겠습니다.
KakaoInfoFeignClient
@FeignClient(name = "KakaoInfoFeignClient", url = "${oauth2.kakao.infoUrl}", configuration = KakaoFeignConfiguration.class)
public interface KakaoInfoFeignClient {
@PostMapping("/v2/user/me")
KakaoDto.KInfoRes getKakaoInfo(@RequestHeader(name ="Authorization") String Authorization,
@RequestBody String kInfoReq);
}
Content-Type은 token 받아오는 것과 동일한 값이기 때문에 똑같이 설정해주었습니다.
카카오서버에 사용자의 정보를 가져올려면 발급받은 토큰값을 넣어줘야 하기 때문에 토큰값을 넣어주었습니다.
Header의 키 값이 Authorization이어서 name을 Authorization으로 설정해 주었습니다. 😁
그 다음은 이제 Service계층을 수정해봅시다. 기존의 kakaoService 클래스의 getKakaoAccessToken을 getKakaoInfo로 수정해주었어요!
kakaoService 클래스의 메소드 getKakaoAccessToken(String code) > getKakaoInfo(String code) 수정
public KakaoDto.KInfoRes getKakaoInfo(String code){
return kakaoInfoFeignClient.getKakaoInfo(
"Bearer "+
kakaoAccessTokenFeignClient.getKakaoAccessToken(
KakaoDto.KAccessTokenReq.newInstance(code,clientId,redirectUri).toString()
).getAccess_token(), KakaoDto.KInfoReq.class.toString());
}
토큰을 보낼 때 앞에 Bearer하고 띄어쓰기를 한 뒤에 토큰을 보내줘야하기 때문에, 앞에 "Bearer "을 넣어주었습니다. 그리고, 요청을 보낼 값(KakaoDto.KInfoReq.class.toString())을 넣어 보내줬어요!
자 그럼 테스트를 해보겠습니다.

값이 들어온 것을 확인하실 수 있습니다. 이제 이 정보를 토대로 사용자를 자사 어플의 DB에 넣으시거나 해서 활용하시면 될 것 같습니다. 😆
긴 글 읽어주셔서 감사합니다. 😌
'Spring' 카테고리의 다른 글
| 간단한 서버 간 통신 @FeignClient (0) | 2023.08.03 |
|---|---|
| OAuth2 (이론 및 준비) (0) | 2023.07.20 |
| ORM(Object Relational Mapping) (0) | 2023.04.09 |
| API 작성 (0) | 2023.03.19 |
| Maven pom.xml? (0) | 2023.03.19 |