[SpringBoot + React] 카카오 소셜 로그인 REST API 방식 구현 - 3. 카카오 소셜 로그인 구현
💡 목차
1. 카카오 소셜 로그인 사전 준비사항
2. 카카오 소셜 로그인 구조 분석
3. 구현 단계
글을 읽기 앞서 저는 React와 SpringBoot를 이용하여 카카오 소셜 로그인을 구현하는 과정을 담았습니다.
SpringBoot에서만 카카오 소셜 로그인을 수행하는 과정을 필요로 하신다면 제 글을 읽으실 필요는 없습니다.
프론트와 서버와의 일렬의 동작 과정을 보시고자 하신다면 첫 글부터 하나하나 읽어 주시면 감사하겠습니다.
⚠️ 참고로 저는 백엔드 쪽 작업을 담당하고 있어 백엔드 위주의 설명을 진행할 예정입니다.
프론트의 설명을 보고 싶다면
👀 프론트 (React 엿보고 오기)
프론트 카카오 소셜 로그인 구현 과정 (그림 포함)
이번 카카오 소셜 로그인은 보면서 여러 블로그들을 참고했었는데 OAuth2를 이용해서 CustomUserDetails을 만들어 진행하는 것 과, Kakao REST API를 이용하는 두 가지가 있는 것 같았습니다.
OAuth2를 이용하면 편하게 소셜 로그인을 처리할 수 있다고 하지만, OAuth2에 대한 충분한 지식이 없다면 사용하기 어려우니 먼저 Kakao REST API를 이용하여 작업해 보시는 것을 추천드립니다.
아래는 제가 참고한 카카오 로그인 REST API 공식 문서입니다.
카카오 소셜 로그인 과정은 초보자에게는 굉장히 어렵다고 느껴집니다. 제가 초보라서...
또한 카카오 로그인 REST API문서는 솔직히 조금 읽기가 불편합니다만... 꾸준히 읽다 보면 읽는 방법을 깨달으실 겁니다 ㅎㅎ
REST API 문서를 꼭 읽어 보시는 것을 추천드리며, 이제 포스팅을 시작해 보도록 하겠습니다.
3. 카카오 소셜 로그인 구현
앞서 이전 포스트를 보고 마무리하고 오셨다고 가정한 후에 진행하는 포스트입니다.
💡 목차
1. 카카오 소셜 로그인 사전 준비사항
2. 카카오 소셜 로그인 구조 분석
3. 구현 단계
주의사항
먼저 주의사항을 말씀드리겠습니다.
1. 카카오 서버에 인증 토큰을 받아올 수 있는 code는 노출을 최대한 피하자!
카카오 서버에 토큰을 받아올 수 있게 하기 때문에 노출을 하면 카카오 리소스 서버에 접근할 수 있게 만들어 주의를 해야 합니다.
2. 카카오 리소스 서버에 접근할 수 있는 카카오 엑세스 토큰은 노출되지 않도록 하자!
카카오 엑세스 토큰을 가지고 카카오 리소스 서버에 요청을 보내면 유저 정보를 받아올 수 있기 때문에 외부로 노출되면 개인 정보를 탈취당할 위험이 있습니다.
3. 프로젝트는 Spring Boot Gradle를 이용합니다.
백엔드 서버가 할 일
1. 프론트에서 받아온 인가 코드(code)를 이용해 카카오 엑세스 토큰을 발급받는다.
2. 카카오 엑세스 토큰을 이용해 카카오 리소스 서버에 유저 정보를 조회한다.
3. 카카오 리소스 서버에서 받아온 response 데이터를 DTO 클래스에 넣어둔 후
4. response 데이터를 가진 DTO를 이용해 로그인, 회원가입의 로직을 만들고 DB에 저장한다.
Controller
@GetMapping("/login/oauth2/callback/kakao")
public ResponseEntity<LoginResponseDto> kakaoLogin(HttpServletRequest request) {
String code = request.getParameter("code");
String kakaoAccessToken = authService.getKakaoAccessToken(code);
return authService.kakaoLogin(kakaoAccessToken);
}
Service
컨트롤러의 authService.getKakaoAccessToken(code) 처리
@Transactional
public KakaoTokenDto getKakaoAccessToken(String code) {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// Http Response Body 객체 생성
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "authorization_code"); //카카오 공식문서 기준 authorization_code 로 고정
params.add("client_id", KAKAO_CLIENT_ID); // 카카오 Dev 앱 REST API 키
params.add("redirect_uri", KAKAO_REDIRECT_URI); // 카카오 Dev redirect uri
params.add("code", code); // 프론트에서 인가 코드 요청시 받은 인가 코드값
params.add("client_secret", KAKAO_CLIENT_SECRET); // 카카오 Dev 카카오 로그인 Client Secret
// 헤더와 바디 합치기 위해 Http Entity 객체 생성
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest = new HttpEntity<>(params, headers);
// 카카오로부터 Access token 받아오기
RestTemplate rt = new RestTemplate();
ResponseEntity<String> accessTokenResponse = rt.exchange(
KAKAO_TOKEN_URI, // "https://kauth.kakao.com/oauth/token"
HttpMethod.POST,
kakaoTokenRequest,
String.class
);
// JSON Parsing (-> KakaoTokenDto)
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
KakaoTokenDto kakaoTokenDto = null;
try {
kakaoTokenDto = objectMapper.readValue(accessTokenResponse.getBody(), KakaoTokenDto.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return kakaoTokenDto;
}
컨트롤러의 return authService.kakaoLogin(kakaoAccessToken) 처리
public ResponseEntity<LoginResponseDto> kakaoLogin(String kakaoAccessToken) {
Account account = getKakaoInfo(kakaoAccessToken);
LoginResponseDto loginResponseDto = new LoginResponseDto();
loginResponseDto.setLoginSuccess(true);
loginResponseDto.setAccount(account);
Account existOwner = accountRepository.findById(account.getId()).orElse(null);
try {
if (existOwner == null) {
System.out.println("처음 로그인 하는 회원입니다.");
accountRepository.save(account);
}
loginResponseDto.setLoginSuccess(true);
return ResponseEntity.ok().headers(headers).body(loginResponseDto);
} catch (Exception e) {
loginResponseDto.setLoginSuccess(false);
return ResponseEntity.badRequest().body(loginResponseDto);
}
}
Service의 kakaoLogin.getKakaoInfo(kakaoAccessToken) 처리
public Account getKakaoInfo(String kakaoAccessToken) {
RestTemplate rt = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + kakaoAccessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
HttpEntity<MultiValueMap<String, String>> accountInfoRequest = new HttpEntity<>(headers);
// POST 방식으로 API 서버에 요청 후 response 받아옴
ResponseEntity<String> accountInfoResponse = rt.exchange(
KAKAO_USER_INFO_URI, // "https://kapi.kakao.com/v2/user/me"
HttpMethod.POST,
accountInfoRequest,
String.class
);
// JSON Parsing (-> kakaoAccountDto)
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
KakaoAccountDto kakaoAccountDto = null;
try {
kakaoAccountDto = objectMapper.readValue(accountInfoResponse.getBody(), KakaoAccountDto.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// 회원가입 처리하기
Long kakaoId = kakaoAccountDto.getId();
Account existOwner = accountRepository.findById(kakaoId).orElse(null);
// 처음 로그인이 아닌 경우
if (existOwner != null) {
return Account.builder()
.id(kakaoAccountDto.getId())
.email(kakaoAccountDto.getKakao_account().getEmail())
.kakaoName(kakaoAccountDto.getKakao_account().getProfile().getNickname())
.build();
}
// 처음 로그인 하는 경우
else {
return Account.builder()
.id(kakaoAccountDto.getId())
.email(kakaoAccountDto.getKakao_account().getEmail())
.kakaoName(kakaoAccountDto.getKakao_account().getProfile().getNickname())
.build();
}
}
DTO
@Data
public class LoginResponseDto {
public boolean loginSuccess;
public Account account;
}
@Data
public class KakaoTokenDto {
private String access_token;
private String token_type;
private String refresh_token;
private String id_token;
private int expires_in;
private int refresh_token_expires_in;
private String scope;
}
위 과정은 회원가입 후 로그인 처리입니다. 처음 로그인 하면 회원가입을 진행하면서 DB에 저장하고, 처음 로그인이 아니라면 DB에서 정보를 가져와 로그인을 수행합니다.
수행하는 과정을 잘 마쳤다면 로그인 및 회원가입을 할 수 있을 겁니다!!
DB나 다른 과정이 궁금하시다면 댓글 달아주시면 감사하겠습니다!
💡 목차
1. 카카오 소셜 로그인 사전 준비사항
2. 카카오 소셜 로그인 구조 분석
3. 구현 단계
'Spring > Kakao Social Login' 카테고리의 다른 글
[Spring Boot] 502 Error - 502 에러는 Redirect URI를 잘못 지정한 것이다... + 카카오 소셜 로그인 (0) | 2023.03.31 |
---|---|
[Spring Boot] JWT에 대하여 (0) | 2023.03.30 |
[SpringBoot + React] 카카오 소셜 로그인 REST API 방식 구현 - 2. 카카오 소셜 로그인 구조 분석 (0) | 2023.03.30 |
[SpringBoot + React] 카카오 소셜 로그인 REST API 방식 구현 - 1. 카카오 소셜 로그인 사전 준비사항 (1) | 2023.03.30 |