04.03 TIL

2023. 4. 3. 14:44개발일지

카카오 소셜로그인 삽질 6일차....

카카오 맘편히 쉽겠다 생각하고 시작한게 문제의 시작
첫날에 localhost:8080에서 index파일로 테스트를 했을 때 성공해서 프론트 분들께 넘겼드렸으나, 로컬에서는 토큰을 받지만 프론트에서는 토큰을 못받으셨음 그래서 리다이렉트uri를 프론트서버 localhost:3000으로 맞춰드리고 했으나 토큰을 2번 요청으로 오류가 발생하지만 실행은 되었습니다. 그래서 팀원 회의를 통해 어느정도 기능구현과 CSS가 자리잡고 다시 해보기로 했습니다 그래서 어느 정도 구현이 완료되고 프론트 서버 배포 후 거기에 리다이렉트uri를 프론트 서버와 맞추자마자 500오류의 시작 여기서부터 삽질 시작...
우선 카카오 Service 로직

@Slf4j
@Service
@RequiredArgsConstructor
public class KakaoService {

    private final PasswordEncoder passwordEncoder;
    private final MemberRepository memberRepository;
    private final RefreshTokenRepository refreshTokenRepository;
    private final JwtUtil jwtUtil;


    public ApiResponseDto<SuccessResponse> kakaoLogin(String code, HttpServletResponse response) throws JsonProcessingException {
        log.info("로그인 service 진입");
        // 1. "인가 코드"로 "액세스 토큰" 요청
        String accessToken = getToken(code);
        System.out.println("code = " + code);
        log.info("인가코드 요청 완료");
        System.out.println("accessToken = " + accessToken);

        // 2. 토큰으로 카카오 API 호출 : "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
        SocialUserInfoDto userInfo = getKakaoUserInfo(accessToken);
        log.info("accessToken 성공");
        // 3. 필요시에 회원가입
        Member member = registerKakaoUserIfNeeded(userInfo);
        System.out.println("userInfo = " + userInfo);
        System.out.println("accessToken = " + accessToken);
        // 4. JWT 토큰 반환
        TokenDto tokenDto = jwtUtil.createAllToken(member.getEmail());

        //  Member Email값으로 refreshToken을 찾는다
        Optional<RefreshToken> refreshToken = refreshTokenRepository.findAllByMemberId(member.getEmail());

        if(refreshToken.isPresent()) {
            refreshTokenRepository.save(refreshToken.get().updateToken(tokenDto.getRefresh_Token()));
        }else {
            RefreshToken newToken = new RefreshToken(tokenDto.getRefresh_Token(), member.getEmail());
            refreshTokenRepository.save(newToken);
        }

        System.out.println("accessToken = " + accessToken);
        return ResponseUtils.ok(SuccessResponse.of(HttpStatus.OK,"로그인 성공"));
    }


    // 1. "인가 코드"로 "액세스 토큰" 요청
    private String getToken(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");
        log.info("grant_type 인증 성공");
        //REST API KEY
        body.add("client_id", "bdb9f0d03a95450cca094def1b12464f");
        log.info("client_id 인증 성공");
        body.add("redirect_uri", "https://fe-fawn.vercel.app/kakao/callback");
        log.info("redirect_uri 인증 성공");
        body.add("code", code);
        log.info("code 인증 성공");
        log.info("여기는 코드 " + code);


        // HTTP 요청 보내기
        HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest = new HttpEntity<>(body, headers);
        RestTemplate rt = new RestTemplate();
        ResponseEntity<String> response = null;
        log.info(response + "null");
        System.out.println("response = " + response);
        try {
            System.out.println("try문 진입");
            log.info("try문 안 진입입");
            response = rt.exchange(
                    "https://kauth.kakao.com/oauth/token",
                    HttpMethod.POST,
                    kakaoTokenRequest,
                    String.class
            );
        } catch (HttpClientErrorException e) {
            System.out.println("catch문 진입");
            log.info("catch문 안 진입");
            log.error("Kakao API authentication failed with status code " + e.getRawStatusCode());
            log.info("getRawStatusCode 오류 패스");
            log.error("Response headers: " + e.getResponseHeaders());
            log.info("getResponseHeaders 오류 패스");
            log.error("Response body: " + e.getResponseBodyAsString());
            log.info("getResponseBodyAsString 오류 패스");
            throw e;
        }
//        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);
        log.info("responseBody 입니다.");
        System.out.println("responseBody = " + responseBody);
        return jsonNode.get("access_token").asText();
    }


    // 2. 토큰으로 카카오 API 호출 : "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
    private SocialUserInfoDto getKakaoUserInfo(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
        );

        String responseBody = response.getBody();
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jsonNode = objectMapper.readTree(responseBody);

        Long id = jsonNode.get("id").asLong();
        String nickname = jsonNode.get("properties")
                .get("nickname").asText();
        String email = jsonNode.get("kakao_account")
                .get("email").asText();

        log.info("카카오 사용자 정보: " + id + ", " + nickname + ", " + email);
        return new SocialUserInfoDto(id, nickname, email);
    }


    // 3. 필요시에 회원가입
    private Member registerKakaoUserIfNeeded(SocialUserInfoDto userInfo) {
        // 로그인 타입 && 사용자 EMAIL로 회원 유무 확인
        Member findUser = memberRepository.findByEmail(userInfo.getEmail())
                .orElse(null);
        if(findUser == null){
            findUser = memberRepository.save(Member.builder()
                    .nickname(userInfo.getNickname())
                    .password(passwordEncoder.encode(UUID.randomUUID().toString()))
                    .email(userInfo.getEmail())
                    .loginType(LoginType.KAKAO_USER)
                    .build());
        }else {
            findUser.updateLoginStatus(LoginType.KAKAO_USER);
        }
        return findUser;
    }

}

그리고 Controller 로직

// 소셜로그인
@GetMapping("/kakao/callback")
public ApiResponseDto<SuccessResponse> kakaoLogin(@RequestParam String code, HttpServletResponse response)throws JsonProcessingException {
    return kakaoService.kakaoLogin(code, response);
}

이렇게 짜고 프론트분들 로직도 보았으나 아무리 코드를 뒤집어 엎고 다시짜도 계속 오류...

이렇게 4일을 삽질을 하다가 허용IP를 추가하라는 글에 카카오 내 애플리케이션 -> 허용 IP주소 들어가보니.... 저번에 기능과 CSS잡기 전에 토큰 2번발급되는거 잡으려고 허용IP주소를 설정해놨던걸 잊고있었습니다.. 그래서 그걸 지우자마자 200.... 이렇게 허무할수가... 그래도 성공했으니 기분은 좋다 ㅎ

'개발일지' 카테고리의 다른 글

04.06 TIL (Nginx)  (0) 2023.04.07
04.04 TIL (SSE)  (0) 2023.04.07
03.27 Redis 사용하는 이유  (0) 2023.03.27
03.25 TIL  (0) 2023.03.25
03.24 이메일 인증(feat. 네이버,구글)  (0) 2023.03.24