
※ 가장 중요한 카카오 공식문서 ※
카카오 로그인 > REST API
https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api

<a th:href="@{/oauth/kakao}">
<img th:src="@{/img/button/kakao_login_medium_narrow.png}" style="width:150px; height:40px;">
</a>
*img 다운로드 링크
https://developers.kakao.com/docs/latest/ko/kakaologin/design-guide
@Getter
@NoArgsConstructor
public class KakaoInfo {
private Long id;
private String nickname;
private String email;
public KakaoInfo(Long id, String nickname, String email) {
this.id = id;
this.nickname = nickname;
this.email = email;
}
}



@Controller
@RequiredArgsConstructor
@RequestMapping("/oauth")
public class OAuthController {
@Value("${kakao.client.id}")
String clientId;
@Value("${kakao.redirect.uri}")
String redirectUri;
@Value("${kakao.client.secret}")
String clientSecret;
/**
* 카카오 로그인 요청
* @return
*/
@GetMapping(value="/kakao")
public String kakaoConnect() {
StringBuffer url = new StringBuffer();
url.append("https://kauth.kakao.com/oauth/authorize?");
url.append("client_id="+clientId);
url.append("&redirect_uri="+redirectUri);
url.append("&response_type=code");
return "redirect:" + url.toString();
}
}
[카카오 로그인] img를 누르면..!
기본 정보에 작성된대로 Get 방식을 사용하여 URL(https://kauth.kakao.com/oauth/authorize)을 통해 카카오에 요청을 보낸다.
쿼리 파라미터로 전달해야하는 client_id, redirect_uri 은 내 애플리케이션에 설정한 값으로 변경해서 요청해야 한다.
해당 값은 application.properties에 별도로 저장한 뒤 가져오는 방식으로 구현했다. application.properties를 사용하는 법은 추가로 포스팅!
카카오에서 전달해준 인가 코드로 토큰을 받는다.



public String getAccessToken(String code) throws JsonProcessingException {
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", clientId);
body.add("redirect_uri", redirectUri);
body.add("code", code);
body.add("client_secret", clientSecret);
// HTTP 요청 보내기
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest = new HttpEntity<>(body, headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kauth.kakao.com/oauth/token",
HttpMethod.POST,
kakaoTokenRequest,
String.class
);
// HTTP 응답 (JSON) -> 액세스 토큰 파싱
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
return jsonNode.get("access_token").asText();
}
HTTP Body의 code부분에 바로 얻은 인가 코드를 작성줘야 하는 것이다.
카카오에서 응답으로 온 정보들 중 access_token 값만 꺼내서 리턴해줬다.


public KakaoInfo getKakaoInfo(String accessToken) throws JsonProcessingException {
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP 요청 보내기
HttpEntity<MultiValueMap<String, String>> kakaoUserInfoRequest = new HttpEntity<>(headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
kakaoUserInfoRequest,
String.class
);
// responseBody에 있는 정보 꺼내기
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
Long id = jsonNode.get("id").asLong();
String email = jsonNode.get("kakao_account").get("email").asText();
String nickname = jsonNode.get("properties")
.get("nickname").asText();
return new KakaoInfo(id, nickname, email);
}
카카오에서 보내준 정보를 만들어 둔 KakaoInfo에 담아 return 했다.
카카오 사용자 정보를 바탕으로 member 테이블을 확인하여, 가입된 내역이 있는지 확인했다. 가입된 정보가 없으면 이메일로 임시 id를 만들고 비밀번호는 랜덤으로 생성하여 가입되도록 처리했다.
public MemberResponse ifNeedKakaoInfo (KakaoInfo kakaoInfo) {
// DB에 중복되는 email이 있는지 확인
String kakaoEmail = kakaoInfo.getEmail();
MemberResponse kakaoMember = memberService.findMemberEmail(kakaoEmail);
// 회원가입
if (kakaoMember == null) {
String kakaoNickname = kakaoInfo.getNickname();
// 이메일로 임시 id 발급
int idx= kakaoEmail.indexOf("@");
String kakaoId = kakaoEmail.substring(0, idx);
// 임시 password 발급 - random UUID
String tempPassword = UUID.randomUUID().toString();
RegisterRequest registerMember = new RegisterRequest();
registerMember.setId(kakaoId);
registerMember.setPassword(tempPassword);
registerMember.setNickname(kakaoNickname);
registerMember.setEmail(kakaoEmail);
memberService.saveMember(registerMember);
// DB 재조회
kakaoMember = memberService.findMemberEmail(kakaoEmail);
}
return kakaoMember;
}
Service에 작성한 함수를 바탕으로 Controller를 작성하고, 세션에 값을 담아 로그인을 구현했다.
GetMapping에 작성한 주소는 애플리케이션에서 등록한 Redirect URI로 설정해야한다.

/**
* 카카오 로그인
* @return
*/
@GetMapping("/kakao/callback")
public String kakaoCallback(String code, HttpSession session) {
// SETP1 : 인가코드 받기
// (카카오 인증 서버는 서비스 서버의 Redirect URI로 인가 코드를 전달합니다.)
// System.out.println(code);
// STEP2: 인가코드를 기반으로 토큰(Access Token) 발급
String accessToken = null;
try {
accessToken = oAuthService.getAccessToken(code);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
//System.out.println("엑세스 토큰 "+accessToken);
// STEP3: 토큰를 통해 사용자 정보 조회
KakaoInfo kakaoInfo = null;
try {
kakaoInfo = oAuthService.getKakaoInfo(accessToken);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
//System.out.println("이메일 확인 "+kakaoInfo.getEmail());
// STEP4: 카카오 사용자 정보 확인
MemberResponse kakaoMember = oAuthService.ifNeedKakaoInfo(kakaoInfo);
// STEP5: 강제 로그인
// 세션에 회원 정보 저장 & 세션 유지 시간 설정
if (kakaoMember != null) {
session.setAttribute("loginMember", kakaoMember);
// session.setMaxInactiveInterval( ) : 세션 타임아웃을 설정하는 메서드
// 로그인 유지 시간 설정 (1800초 == 30분)
session.setMaxInactiveInterval(60 * 30);
// 로그아웃 시 사용할 카카오토큰 추가
session.setAttribute("kakaoToken", accessToken);
}
return "redirect:/";
}



로그인 시 로그아웃 때 사용하기 위해 생성해둔 kakaoToken을 먼저 확인한다.
/**
* 카카오 로그아웃
* @return
*/
@GetMapping("/kakao/logout")
public String kakaoLogout(HttpSession session) {
String accessToken = (String) session.getAttribute("kakaoToken");
if(accessToken != null && !"".equals(accessToken)){
try {
oAuthService.kakaoDisconnect(accessToken);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
session.removeAttribute("kakaoToken");
session.removeAttribute("loginMember");
}else{
System.out.println("accessToken is null");
}
return "redirect:/";
}
public void kakaoDisconnect(String accessToken) throws JsonProcessingException {
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded");
// HTTP 요청 보내기
HttpEntity<MultiValueMap<String, String>> kakaoLogoutRequest = new HttpEntity<>(headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kapi.kakao.com/v1/user/logout",
HttpMethod.POST,
kakaoLogoutRequest,
String.class
);
// responseBody에 있는 정보를 꺼냄
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
Long id = jsonNode.get("id").asLong();
System.out.println("반환된 id: "+id);
}
로그아웃 시 카카오에서 응답으로 id를 보내주므로 id까지 받으면 로그아웃이 잘된 것을 확인할 수 있다.