본문 바로가기

TIL(Today I Learned)

TIL-230826(항해99 실전 프로젝트-행동대장(17))

📝오늘 공부한 것

  • 실전프로젝트 - '행동대장' 카카오로그인 구현
  • 인프런 김영한의 스프링 강의 섹션5

 

⛔문제점

[에러 메시지]

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request: "{"error":"invalid_grant","error_description":"authorization code not found for code=v7B6seKGSDCRLKpOvIwAfATwHgLiGfeddI7QNVmuXFkHrn6AZi5GY-n1D7ycl5VKre4tTAorDSAAAAGKMInl3A","error_code":"KOE320"}"] with root cause

 

-> 인증코드가 잘못 되었다는 에러가 뜸!

 

시도해 본 것들💦

✔ KakaoService

    private String getToken(String code) throws JsonProcessingException {
        URI uri = UriComponentsBuilder
                .fromUriString("https://kauth.kakao.com")
                .path("/oauth/token")
                .encode()
                .build()
                .toUri();

        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "authorization_code");
        body.add("client_id", kakaoClientId);
        body.add("redirect_uri", "http://localhost:3000/oauth/callback");
        body.add("code", code);

        RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
                .post(uri)
                .headers(headers)
                .body(body);

        ResponseEntity<String> response = restTemplate.exchange(
                requestEntity,
                String.class
        );

        JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
        return jsonNode.get("access_token").asText();
    }

프론트에서 넘겨주는 인가코드로 카카오에 엑세스 토큰을 요청하는 메서드이다.

이때 HTTP Body에 client_id와 redirect_uri를 넘겨줘야 한다.

이때 프론트의 rest api키와 도메인 주소를 넣어주었다. 그런데 계속 code에 관한 에러가 났다. 그래서 혹시나 하고 client_id와 redirect_uri 모두 백엔드것을 넣어보았다. 그런데도 해결되지 않아 로그를 찍어보았다.

 

 

💯해결

[프론트엔드 에러 메시지]

AxiosError {message: 'Request failed with status code 403', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …} code : "ERR_BAD_REQUEST" config : {transitional: {…}, adapter: Array(2), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …} message : "Request failed with status code 403" name : "AxiosError" request : XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …} response : {data: '', status: 403, statusText: '', headers: AxiosHeaders, config: {…}, …} stack : "AxiosError: Request failed with status code 403\n at settle (http://localhost:3000/static/js/bundle.js:98671:12)\n at XMLHttpRequest.onloadend (http://localhost:3000/static/js/bundle.js:97371:66)" [[Prototype]] : Error


{data: {…}, status: 200, statusText: '', headers: AxiosHeaders, config: {…}, …} config : {transitional: {…}, adapter: Array(2), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …} data : {msg: '로그인에 성공하였습니다.'} headers : AxiosHeaders {authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJjaGFsazMyM0…jc4fQ.3DjnyJmRr8Bb4taXJ25lo_nmBJULqNSh9W-YLuOPr8w', cache-control: 'no-cache, no-store, max-age=0, must-revalidate', content-type: 'application/json', expires: '0', pragma: 'no-cache'} request : XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …} status : 200 statusText : "" [[Prototype]] : Object

 

 

심지어 프론트에서는 이렇게 에러와 성공이 같이떴다. 그리고 엑세스 토큰도 잘 전달되었다.

그래서 어떻게 요청이 오는데 내가 그 요청에 대해서 response를 두번 날리게 되는거지?하고 생각을 해보았다.

 

또, KOE320이라는 에러가 계속나와서 인가코드 관련 문제가 뭐가 있을까 생각해봤다.

 

https://developers.kakao.com/docs/latest/ko/kakaologin/trouble-shooting

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

그런데 서버의 로그를 다시보니까 로그가 계속 두번씩 찍히고 있는 것이었다!!!

혹시나...하고 프론트분에게 '서버로 요청이 두번씩 온다 그래서 코드 에러가 나는 것같다. 프론트에서 서버로 요청을 어떻게 보내는지 알려주실 수 있나'하고 물어보았다.

프론트분이 확인을 하고 알려주신다 했다. 알고보니까 정말로 프론트에서 요청이 두번씩 날라오는 것이었다! 그게 React에서 그렇게 보낸다(?)고 하셨다. 

암튼!!또 이렇게 무사히 에러를 해결할 수 있었다!

 

알게 된 점

✔ KakaoService

        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "authorization_code");
        body.add("client_id", 프론트 키 적기);
        body.add("redirect_uri", 프론트주소 적기);
        body.add("code", code);

프론트의 주소를 적어야 내가 프론트로부터 code를 받을 수 있음!

 

✔ OAuth 

 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는, 접근 위임을 위한 개방형 표준이다. 

사용자가 애플리케이션에게 모든 권한을 넘기지 않고 사용자 대신 서비스를 이용할 수 있게 해주는 HTTP기반의 보안 프로토콜이다.

OAuth를 사용하는 서비스 제공자는 대표적으로 구글, 페이스북, 카카오, 네이버 등이 있다.

 

개선할 점💪🏻

오늘 멘토님의 피드백이 있었다.

Q. 목표로 했던 기능들이 모두 완료되어가고 있다. 앞으로 남은 기간동안 백엔드에서는 무엇을 하면 좋을까?

A.

 - 백엔드는 계속 개선할 것들이 생긴다. 성능테스트를 계속 해봐라.

 - 스프링배치에 대해서 찾아볼 것. JMeter test 계속 해볼 것.

 - https://spring.io/projects/spring-hateoas/ 를 사용해봐라.  (API tool)

 

Spring HATEOAS

Spring HATEOAS provides some APIs to ease creating REST representations that follow the HATEOAS principle when working with Spring and especially Spring MVC. The core problem it tries to address is link creation and representation assembly. Features Model

spring.io

 - 더미데이터 생성 시 spring commandline runner 사용해 봐라.

 

Q. 테스트코드 작성을 해보면 프로젝트에 도움이 될 지?

A.

 - 많은 도움이 됨. 꼭 해봤으면 좋겠다.

 - 많이 어렵다면 https://symflower.com/en/ 를 써보는 것도 좋을 것.

 

마지막으로 

 

😀 : 조만간 유저피드백을 진행하면 사용자들은 우리가 원하는데로만 사용하지 않는다. 따라서 버그가 많이 날 것이다. 우리가 발견하지 못했던 exception을 잡아주는 것이라고 생각해라.

 

라는 조언이 있으셨다. 기능 구현들이 거의 다 끝나간다. 테스트코드 작성의 중요성을 다시 한번 느끼고 빨리 작성해보고 싶다는 생각이 들었다.