74일차 - Spring (카카오 로그인)

Yohan·2024년 6월 10일
0

코딩기록

목록 보기
114/156
post-custom-banner

카카오 로그인 API 받기

https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api

  • https://developers.xxxxx.com
    -> 구글, 네이버 등도 xxx 자리에 넣어서 API 이용가능

1. 인가 코드 받기


@Controller
@Slf4j
public class SnsLoginController {

    @Value("${sns.kakao.app-key}")
    private String appKey;
    @Value("${sns.kakao.redirect-uri}")
    private String redirectUri;

    @GetMapping("/kakao/login")
    public String kakaoLogin() {

        // 카카오 서버로 인가코드발급 통신을 해야 함.
        // 아래 3개는 필수적으로 받아야함
        String uri = "https://kauth.kakao.com/oauth/authorize";
        uri += "?client_id=" + appKey;
        uri += "&redirect_uri=" + redirectUri;
        uri += "&response_type=code";
        return "redirect:" + uri;
    }

    // 인가코드를 받는 요청 메서드
    @GetMapping("/oauth/kakao")
    public String kakaoCode(String code) {
        log.info("카카오 인가코드 발급 - {}", code);
        return "";
    }
}

2. 토큰을 받아서 사용자 정보 가져오기


SnsLoginController

	// 인가코드를 받는 요청 메서드
    @GetMapping("/oauth/kakao")
    public String kakaoCode(String code, HttpSession session) {
        log.info("카카오 인가코드 발급 - {}", code);

        // 토큰 발급에 필요한 파라미터 만들기
        HashMap<String, Object> requsetParams = new HashMap<>();
        requsetParams.put("appKey", appKey);
        requsetParams.put("redirect", redirectUri);
        requsetParams.put("code", code);

        // 인증 액세스 토큰 발급 요청
        snsLoginService.kakaoLogin(requsetParams, session);

        return "redirect:/";
    }
  • 인가 코드를 받는 메서드에서 토큰 발급에 필요한 파라미터도 만들어준다.

SnsLoginService

  • 요청 URI, 요청 헤더
@Service
@Slf4j
public class SnsLoginService {

    // 카카오 로그인 처리 서비스 로직
    public void kakaoLogin(Map<String, Object> requestParams) {

        // 토큰 발급 요청
        String accessToken = getKakaoAccessToken(requestParams);

        // 발급받은 토큰으로 사용자 정보 가져오기
        getKakaoUserInfo(accessToken);


    }

    // 토큰으로 사용자 정보 요청
    private void getKakaoUserInfo(String accessToken) {

        // request uri
        String requestUri = "https://kapi.kakao.com/v2/user/me";

        // 헤더 설정
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Bearer " + accessToken);
        headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");


        // 요청 보내기
        RestTemplate template = new RestTemplate();
        ResponseEntity<Map> response = template.exchange(
                requestUri
                , HttpMethod.POST
                , new HttpEntity<>(headers)
                , Map.class
        );

        // 응답 정보 JSON 꺼내기
        Map json = response.getBody();
        log.debug("user profile: {}", json);

    }

    // 인가코드로 토큰 발급 요청
    private String getKakaoAccessToken(Map<String, Object> requestParams) {

        // 요청 URI
        String requestUri = "https://kauth.kakao.com/oauth/token";

        // 요청 헤더 설정
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");

        // POST요청은 쿼리 파라미터를 URI에 붙일 수 없음
        // 요청 바디에 쿼리 파라미터를 넣어야 함
        LinkedMultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        params.add("grant_type", "authorization_code");
        params.add("client_id", requestParams.get("appKey"));
        params.add("redirect_uri", requestParams.get("redirect"));
        params.add("code", requestParams.get("code"));

        // 요청헤더와 요청바디를 담을 객체 생성
        HttpEntity<Object> entity = new HttpEntity<>(params, headers);

        // 카카오 인증 서버로 POST요청 보내기
        RestTemplate template = new RestTemplate();

        /**
         - RestTemplate객체가 REST API 통신을 위한 API인데 (자바스크립트 fetch역할)
         - 서버에 통신을 보내면서 응답을 받을 수 있는 메서드가 exchange
         param1: 요청 URL
         param2: 요청 방식 (get, post, put, patch, delete...)
         param3: 요청 헤더와 요청 바디 정보 - HttpEntity로 포장해서 줘야 함
         param4: 응답결과(JSON)를 어떤 타입으로 받아낼 것인지 (ex: DTO로 받을건지 Map으로 받을건지)
         */
        ResponseEntity<AccessTokenDto> response = template.exchange(requestUri, HttpMethod.POST, entity, AccessTokenDto.class);

//        log.debug("response: {}", response);

        AccessTokenDto json = response.getBody();

        log.debug("json: {}", json);

//        String accessToken = (String) json.get("access_token");
        String accessToken = json.getAccessToken();
        String refreshToken = json.getRefreshToken();

        log.debug("token: {}", accessToken);
        log.debug("refreshToken: {}", refreshToken);

        return accessToken;
    }

}

  • 응답에 필요한 access_token, refresh_token은 DTO로 만들어줌

AccessTokenDto

public class AccessTokenDto {

    @JsonProperty("access_token")
    private String accessToken;

    @JsonProperty("refresh_token")
    private String refreshToken;
}
  • 사용자 정보 요청할 때 Map 대신 dto를 사용해서 Service 코드 수정

KakaoUserDto

public class KakaoUserDto {

    private Long id;

    @JsonProperty("connected_at")
    private LocalDateTime connectedAt;

    private Properties properties;

    @Getter @ToString
    public static class Properties {
        private String nickname;
        @JsonProperty("profile_image")
        private String profileImage;
    }
}

SnsLoginService

// 발급받은 토큰으로 사용자 정보 가져오기
        KakaoUserDto.Properties userInfo = getKakaoUserInfo(accessToken);

        // 카카오에서 받은 회원정보로 우리 사이트 회원가입 시키기
        String account = userInfo.getNickname();
        // 회원 중복확인
        if (memberService.checkIdentifier("account", account)) {
            throw new IllegalArgumentException("이미 가입한 회원입니다.");
        }

        memberService.join(
                SignUpDto.builder()
                        .account(account)
                        .password("0000")
                        .name(account)
                        .email(account + "@abc.com")
                        .build(),
                userInfo.getProfileImage()
        );
```
profile
백엔드 개발자
post-custom-banner

0개의 댓글