공부/Spring Boot

Spring Boot) 백 & 프론트 카카오 로그인 API 구현하기

son_i 2024. 8. 24. 21:20
728x90

카카오 공식문서에 있는 로그인 과정이다.

처음부터 보고 로직 이해를 하면 구현이 한결 쉬워진다. 공식문서의 위력을 다시 한 번 느꼈다.

 

간단하게 정리해보면 다음과 같다.

1. 카카오 로그인 완료 후 Redirect URI로 인가 코드 발급됨.
2. 인가 코드를 통해 카카오 서버로 토큰 요청.
3. 토큰을 통해 카카오 로그인 한 사용자 정보를 받아올 수 있음.
4. 해당 사용자 정보를 이용해 회원가입 or 로그인 처리.

 

 

로직을 토대로 우리 프로젝트에서 적용한 플로우는 다음과 같다.

1. 프론트-> 카카오
kakao.Auth.Authorize()이용해 카카오 로그인 화면 요청

2. 카카오-> 백
로그인 완료되면 code와 함께 자동으로 백 리다이렉트 url로 넘어감 (~~/oauth/kakao/callback?code=~~~)

3. 백->카카오
POST ~~/oauth/token 으로 인가 코드와 함께 요청.
그럼 나오는게 “카카오”가 발급한 토큰

4. 프론트->백
3에서 리턴값으로 넘어온 카카오 token + 사용자에게 추가로 받은 email, phone을
백 post ~~~/oauth/kakao/extraInfo로 보내준다.

5. 백 -> 카카오서버
Get or Post로 ~~/v2/user/me + 4에서 프론트가 넘겨준 카카오 토큰으로 로그인 한 사용자 정보 요청

6. 카카오 정보 (이름, 사진) 사용자 직접입력(이메일, 전번) 이용해
디비에서 사용자 정보 조회, 없으면 새로 저장 , 있으면 해당 회원 가입돼있는 것으로 인지

-> 완료되면 백에서 직접 jwt토큰 atk rtk만들어서 내려줌
(일반로그인 응답처럼 !! 그리고 이때의 atk를 로컬스토리지에 저장해놓고 이제 로그인 필요한 api접근 시 헤더에 Authorization으로 넣어서 요청 보내야 함)

 

 

개발 과정

1. 카카오 developers에서 애플리케이션 등록, Redirect URL 설정

https://developers.kakao.com/

 

Kakao Developers

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

developers.kakao.com

위 사이트에 들어가서 애플리케이션을 만들어야 한다.

 

추가하는 과정은 어렵지 않다.

기본 정보 입력 후 생성해주고 카카오 로그인 기능을 사용할 것이기 때문에 활성화 설정에 ON으로 바꿔준다.

 

그리고 동의항목에 들어가서 닉네임과 프로필 사진의 상태를 설정 버튼을 눌러 변경해준다.

카카오 비즈회원이 아닌 상태로 받을 수 있는 정보는 아래 두 가지가 최선이다.

 

 

가장 중요한 게 카카오 로그인 완료 후 code를 받을 리다이렉트 URI를 실제 인가 코드를 받아 사용할 URI로 설정해줘야 한다.

카카오 로그인 활성화 설정을 한 아래 부분에서 설정할 수 있다.

리다이렉트 URI에 작성한 첫 번째 주소는 로컬에서 테스트 하기 위한 주소이고 

두 번째는 배포 환경에서 실제 리다이렉트 될 주소이다.

세 번째는 포스트맨에서 테스트 하기 위해 작성했다.

 

이렇게 등록해두고  백에서 컨트롤러 코드는 아래와 같이 작성해주었다.

 등록해둔 부분과 똑같이 /oauth/kakao/callback?code=code 형식으로 받는다.

 

 

2. 프론트 -> 카카오 서버로 카카오 로그인 창 요청

Step 1 과정을 진행하기 위해 프론트에서 카카오 로그인 요청이 필요하다.

공식 문서에서는 다음과 같이 안내되어있다.

 

REST API

 

 

JavaScript

 

따라서 프론트에서 다음과 같이 코드를 작성했다.

 

눈 여겨 봐야할 것은 loginWithKakao() 메소드이다.

이 안에서 window.Kakao.Auth.authorize() 메소드를 호출한다. 

해당 메소드를 호출할 때 redirectUri 파라미터가 필수이므로 추가해야한다.

import React, { useEffect } from 'react';

function KakaoLogin() {
  const KAKAO_KEY = {카카오 REST API 키};

  useEffect(() => {
    if (!KAKAO_KEY) {
      console.error('KAKAO_KEY를 찾을 수 없습니다.');
      return;
    }
  
    const script = document.createElement('script');
    script.src = "https://t1.kakaocdn.net/kakao_js_sdk/2.7.2/kakao.min.js";
    script.integrity = "sha384-TiCUE00h649CAMonG018J2ujOgDKW/kVWlChEuu4jK2vxfAAD0eZxzCKakxg55G4";
    script.crossOrigin = "anonymous";
    script.async = true;
    script.onload = () => {
      if (window.Kakao) {
        console.log('Kakao SDK 로드 완료');
        window.Kakao.init(KAKAO_KEY); 
        console.log('Kakao SDK 초기화 완료');
      } else {
        console.error('Kakao SDK를 로드할 수 없습니다.');
      }
    };
    script.onerror = () => {
      console.error('Kakao SDK 스크립트 로드 실패');
    };
  
    document.body.appendChild(script);
    
    return () => {
      document.body.removeChild(script);
    };
  }, [KAKAO_KEY]);
  

  const loginWithKakao = () => {
    if (window.Kakao) {
      console.log('카카오 로그인 요청 중');
      window.Kakao.Auth.authorize({
        redirectUri: 'http://{ec2 ip주소}/oauth/kakao/callback',
      });
    } else {
      console.error('카카오 로그인 요청 실패');
    }
  };
  

  return (
    <div>
      <button id="kakao-login-btn" onClick={loginWithKakao} style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer' }}>
        <img 
          src="https://k.kakaocdn.net/14/dn/btroDszwNrM/I6efHub1SN5KCJqLm1Ovx1/o.jpg" 
          width="222" 
          alt="Kakao login button" 
        />
      </button>
    </div>
  );
}

export default KakaoLogin;

 

이렇게 하면 카카오 로그인 버튼을 통해 카카오 로그인을 할 수 있게 된다.

 

 여기서 동의 항목에 체크하고 로그인을 하면 1~5 과정이 완료된 것이다.

 

그리고 6번 과정인 카카오 -> 리다이렉트 URI로 (백에 작성해둔 컨트롤러 메소드로) 인가 코드가 전달되게 된다 !

 

이 부분에서 한참을 해맸었는데 처음에 우리는 프론트에서 요청을 받아 처리하는 줄 알고 계속 Redirect URI로 GET 요청을 백으로 보냈었다. 이 글을 보는 다른 사람들의 삽질 시간이 덜어졌으면 좋겠다.

프론트가 직접 리다이렉트 URI로 요청을 보내는 것이 아니고 로그인이 완료되면 알아서 ! 리다이렉트 URI로 인가 코드가 날아오는 것이다 !ㅜㅜㅜ

 

3. 카카오 -> 백 Redirect URI로 인가 코드 전달

application.yml에 kakao Rest API 키 등록

 

rest API 키는 생성한 애플리케이션 -> 앱 키에서 확인 가능

kakao:
  client_id: {Rest API 키}

 

 

위에서도 썼듯이 컨트롤러를 아래와 같이 작성해주었다.

@GetMapping("/oauth/kakao/callback")//인가코드 발급
    public void callbackKakao(
            @RequestParam("code") String code
            , HttpServletResponse response) throws IOException {

        System.out.println(code);
        String accessTokenFromKakao = kakaoLoginService.getAccessTokenFromKakao(code);

        String redirectUrl = "http://{배포 서버 ip}/inputInfo?token=" + accessTokenFromKakao;

        response.sendRedirect(redirectUrl);
    }

카카오 애플리케이션에 Redirect URI로 등록해둔 주소로 요청을 받고 쿼리 파라미터로 코드가 날아오기 때문에 @RequestParam을 통해 받아준다.

 

이 컨트롤러까지 거치면 Step1의 6번까지의 과정이 끝난다.

 

서비스 코드에서는 이 인가 코드를 통해 Step2 과정인 토큰을 발급받는다.

 

<KakaoLoginService>

 

@RequiredArgsConstructor
@Service
@Slf4j
public class KakaoLoginService {

    @Value("${kakao.client_id}")
    private String clientId;

    private final String KAUTH_TOKEN_URL_HOST = "https://kauth.kakao.com";
    private final String KAUTH_USER_URL_HOST = "https://kapi.kakao.com";

    private final MemberRepository memberRepository;
    private final JwtUtils jwtUtils;


    public String getAccessTokenFromKakao(String code) {

        KakaoTokenResponse kakaoTokenResponseDto =
                WebClient.create(KAUTH_TOKEN_URL_HOST).post()
                        .uri(uriBuilder -> uriBuilder
                                .scheme("https")
                                .path("/oauth/token")
                                .queryParam("grant_type", "authorization_code")
                                .queryParam("client_id", clientId)
                                .queryParam("code", code)
                                .build(true))
                        .header(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString())
                        .retrieve()
                        //TODO : Custom Exception
                        .bodyToMono(KakaoTokenResponse.class)
                        .block();


        log.info(" [Kakao Service] Access Token ------> {}", kakaoTokenResponseDto.getAccessToken());
        log.info(" [Kakao Service] Refresh Token ------> {}", kakaoTokenResponseDto.getRefreshToken());
        log.info(" [Kakao Service] Id Token ------> {}", kakaoTokenResponseDto.getIdToken());
        log.info(" [Kakao Service] Scope ------> {}", kakaoTokenResponseDto.getScope());

		return kakaoTokenResponseDto.getAccessToken();
    }
 }

 

WebClient를 사용하려면 아래의 Webflux의존성이 필요하다. build.gradle에 추가한다.

//webflux
    implementation 'org.springframework.boot:spring-boot-starter-webflux'

 

 

private final로 작성해둔 URL은 모두 공식문서에 있다!

 

POST https://kauth.kakao.com/oauth/token으로 요청 필수 항목인 grant_type, client_id, redirect_uri, code를 uri builder로 생성해서 요청을 보낸다.

 

(음 이제보니까 나 redirect uri를 안 넣었네 그래도 이상은 없었다. ㅋㅋ)

 

.bodyToMono()로 KakaoTokenResponse dto를 통해 응답을 받게 되는데 해당 코드는 다음과 같다.

package com.sj.Petory.OAuth;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class KakaoTokenResponse {

    @JsonProperty("token_type")
    public String tokenType;

    @JsonProperty("access_token")
    public String accessToken;

    @JsonProperty("id_token")
    public String idToken;

    @JsonProperty("expires_in")
    public String expiresIn;

    @JsonProperty("refresh_token")
    public String refreshToken;

    @JsonProperty("refresh_token_expires_in")
    public String refreshTokenExpiresIn;

    @JsonProperty("scope")
    public String scope;
}

 

아래 3개의 어노테이션은 JSON 직렬화, 역직렬화에서 속성을 무시하는데 사용된다.

* 직렬화 ? ) 
  자바에서 사용되는 Object/Data를 다른 컴퓨터의 자바 시스템에서 사용할 수 있도록 바이트 스트림 형태의 연속적인 데이터로 변환하는 포맷 변환 기술.

* 역직렬화 ? )
  바이트로 변환된 데이터를 원래대로 자바 시스템의 Object또는 Data로 변환하는 기술

@JsonIgnore : 클래스의 속성(필드, 멤버 변수) 수준에서 사용
@JsonProperty : 클래스의 속성(필드, 멤버 변수) 수준에서 사용
@JsonIgnoreProperties : 클래스 수준에서 사용
@JsonIgnoreType : 클래스 수준에서 사용. 전체 클래스를 무시

 

카카오 응답으로 JSON 형태가 바이트 형태로 넘어오는데 이것을 우리 프로젝트에서 객체로 변환해서 써야한다.

여기서 역직렬화가 사용되는데 그에 따른 설정을 해주는 것이다.

 

@NoArgsConstructor

: 역직렬화를 위한 기본 생성자 설정

 

@JsonIgnoreProperties(ignoreUnknown = false)

: 기본값 false. 알 수 없는 프로퍼티를 무시하지 않도록 설정

 

@JsonProperty

: JSON을 객체로 변환할 때 key 이름을 설정할 수 있다.

위와 같이 설정해주면 카카오 응답으로 오는 token_type json의 값을 내가 설정한 이름인 tokenType으로 받을 수 있다.

 

 

-----

해당 과정을 거치면 서비스 메소드에서 return 값으로 카카오에서 발급해준 AccessToken을 넘겨준다.

 

이 token으로 사용자 정보를 얻어올 수 있는데 그 전에 사용자에게 받아야 할 추가 정보들이 있다.

 

카카오에서 제공하는 정보가 이름과 프로필 사진 뿐이라 우리는 이메일과 전화번호가 추가로 필요했기 때문에 프론트의 입력 화면으로 리다이렉트 되게 해야했다. 

@GetMapping("/oauth/kakao/callback")//인가코드 발급
    public void callbackKakao(
            @RequestParam("code") String code
            , HttpServletResponse response) throws IOException {

        System.out.println(code);
        String accessTokenFromKakao = kakaoLoginService.getAccessTokenFromKakao(code);

        String redirectUrl = "http://{배포 서버 ip}/inputInfo?token=" + accessTokenFromKakao;

        response.sendRedirect(redirectUrl);
    }

HttpServletResponse를 이용해 HTTP 응답을 클라이언트에게 보내줄 수 있는데 그 중 sendRedirect 메소드를 이용해 클라이언트가 특정 페이지로 이동할 수 있게 한다.

 

카카오 로그인을 완료하면 카카오에서 발급한 accesstoken과 함께 이메일과 전화번호를 작성할 수 있는 페이지로 이동!

이렇게 하면 Step2의 토큰 받기 작업까지 완료 !

 

3. 프론트 -> 백으로 추가 정보 입력, 백 -> 카카오로 카카오 토큰을 통한 사용자 정보 받아온 후 함께 처리

좀 복잡해 보일 수 있는데 별 거 없다.

 

a. "완료"를 누르면 프론트에서 백으로 입력한 추가 정보와 2에서 백이 내려준 카카오 토큰을 헤더에 함께 실어 백 컨트롤러로 요청을 보내게 된다.

 

b. 백에서는 받은 accessToken으로 카카오 서버에 사용자 정보 요청을 한 뒤 입력한 정보들과 버무려

가입되지 않은 사용자라면 -> DB 저장 후 atk, rtk 발급

이미 가입된 사용자면 -> atk, rtk 발급

 

a. Front-End

1. 페이지 라우팅 설정

import { Route, Routes } from 'react-router-dom';

import HomePage from '../pages/homePage.jsx';
import LoginPage from '../pages/loginPage.jsx';
import SignUpPage from '../pages/signUpPage.jsx';
import MainPage from '../pages/mainPage.jsx';
import MyPage from '../pages/myPage.jsx';
import PetPage from '../pages/petPage.jsx';  
import KakaoCallback from '../utils/kakaoCallback.jsx'; 
import InputInfo from '../pages/inputInfo.jsx';

const Routing = () => {

	return (
		<Routes>
            <Route path='/login' element={<LoginPage/>} />
			<Route path='/inputInfo' element={<InputInfo/>} />
            <Route path='/signUp' element={<SignUpPage/>} />
			<Route path='/oauth/kakao/callback' element={<KakaoCallback />} /> 
			<Route path='/mainPage' element={<MainPage/>} />
			<Route path='/myPage' element={<MyPage/>} />
			<Route path='/petPage' element={<PetPage/>} />
			<Route path='/' element={<HomePage/>} />
            
		</Routes>
	);
};

export default Routing;

 

2. 백에서 response.sendDirect로 전달 된 요청을 받아 쿼리 파라미터로 넘겨준 토큰을 프론트에서 로컬스토리지에 저장.

3. navigate() 이용해 inputInfo로 이동

import React, { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

function Callback() {
  const navigate = useNavigate();

  useEffect(() => {
    
    const urlParams = new URLSearchParams(window.location.search);
    const Token = urlParams.get('token');

    if (Token) {
      localStorage.setItem('accessToken', Token);

      navigate('/inputInfo');
    } else {
      console.error('액세스 토큰이 쿼리 파라미터에서 발견되지 않았습니다.');
    }
  }, [navigate]);

  return <div>로딩 중입니다.</div>;
}

export default Callback;

 

4. InputInfo 페이지

  2에서 로컬스토리지에 토큰을 저장했기 때문에 userEffect의 첫 번째 if에서 쿼리 파라미터 토큰에 걸리지 않고

  else 안에 있는 localStroage.getItem() 으로 토큰을 가져오게 된다.

(코드 리팩토링 필요)

 

그리고 POST ~~/oauth/kakao/extraInfo로 헤더에 카카오 토큰, RequestBody로 이메일과 전화번호를 포함한 요청을 보낸다.

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';

function InputInfo() {
  const [userData, setUserData] = useState({ email: '', phone: '' });
  const [accessToken, setAccessToken] = useState(null);
  const navigate = useNavigate();

  useEffect(() => {
    const urlParams = new URLSearchParams(window.location.search);
    const token = urlParams.get('token');

    if (token) {
      console.log('쿼리 파라미터에서 가져온 토큰:', token);
      
      localStorage.setItem('accessToken', token);
      setAccessToken(token);
    } else {
      
      const storedToken = localStorage.getItem('accessToken');
      if (storedToken) {
        console.log('로컬 스토리지에서 가져온 토큰:', storedToken);
        setAccessToken(storedToken);
      } else {
        console.error('로컬 스토리지에서 액세스 토큰을 찾을 수 없습니다.');
      }
    }
  }, []);

  const handleSubmit = async (event) => {
    event.preventDefault();

    if (!accessToken) {
      console.error('액세스 토큰이 없습니다.');
      return;
    }

    try {
      await axios.post('http://{배포 서버 ip}/oauth/kakao/extraInfo', 
        {
          email: userData.email,
          phone: userData.phone,
        }, 
        {
          headers: {
            'Content-Type': 'application/json',
            Authorization: `${accessToken}`
          }
        }
      );
      alert('서버로 데이터 전송 성공');
      
      navigate('/mainPage');
    } catch (error) {
      console.error('서버 전송 실패:', error);
    }
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <label>
          이메일 :
          <input 
            type="email" 
            value={userData.email}
            onChange={(e) => setUserData({ ...userData, email: e.target.value })}
            required
          />
        </label>
        <br />
        <label>
          전화번호 :
          <input 
            type="tel" 
            value={userData.phone}
            onChange={(e) => setUserData({ ...userData, phone: e.target.value })}
            required
          />
        </label>
        <br />
        <button type="submit">완료</button>
      </form>
    </div>
  );
}

export default InputInfo;

 

 

b. Back-End

추가 입력 정보를 받기 위한 ExtraUserInfo dto

package com.sj.Petory.OAuth;

import jakarta.validation.constraints.NotBlank;
import lombok.*;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ExtraUserInfo {

    @NotBlank
    private String email;
    @NotBlank
    private String phone;
}

 

컨트롤러

헤더로 카카오 토큰, RequestBody로 ExtraUserInfo를 받음 

    @PostMapping("/oauth/kakao/extraInfo")
    public ResponseEntity<SignIn.Response> kakaoExtraInfo(
            @RequestHeader("Authorization") String accessToken
            , @RequestBody @Valid ExtraUserInfo extraUserInfo) {

        return ResponseEntity.ok(
                kakaoLoginService.kakaoExtraInfo(accessToken, extraUserInfo));
    }

 

서비스

 

GET or POST https://kapi.kakao.com/v2/user/me  로 헤더에 토큰과 contentType을 넣어 사용자 정보 요청을 한다.

public SignIn.Response kakaoExtraInfo(
            final String accessToken
            , final ExtraUserInfo extraUserInfo) {

        UserInfoResponse userInfoResponse = WebClient.create(KAUTH_USER_URL_HOST).get()
                .uri(uriBuilder -> uriBuilder
                        .scheme("https")
                        .path("/v2/user/me")
                        .build()
                )
                .header(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString())
                .header("Authorization", "Bearer " + accessToken)
                .retrieve()
                .bodyToMono(UserInfoResponse.class)
                .block();
        //회원 존재하는지 확인 -> 이메일
        memberRepository.save(findOrCreateMember(userInfoResponse, extraUserInfo));

        System.out.println(userInfoResponse.getKakaoAcount().getProfile().getNickName());
        System.out.println(userInfoResponse.getKakaoAcount().getProfile().getProfileImageUrl());

        return new SignIn.Response(
                jwtUtils.generateToken(extraUserInfo.getEmail(), "ATK")
                , jwtUtils.generateToken(extraUserInfo.getEmail(), "RTK")
        );
    }

    //findOrCreateMember()
    //존재하면 -> 로그인 성공 ATK, RTK 발급
    //존재하지 않으면 -> 회원 정보 저장
    public Member findOrCreateMember(UserInfoResponse userInfoResponse, ExtraUserInfo extraUserInfo) {
        return memberRepository.findByEmail(extraUserInfo.getEmail())
                .orElseGet(() -> createMember(userInfoResponse, extraUserInfo));
    }

    private Member createMember(UserInfoResponse userInfoResponse, ExtraUserInfo extraUserInfo) {
        return Member.builder()
                .name(userInfoResponse.getKakaoAcount().getProfile().getNickName())
                .email(extraUserInfo.getEmail())
                .phone(extraUserInfo.getPhone())
                .image(userInfoResponse.getKakaoAcount().getProfile().getProfileImageUrl())
                .build();
    }

 

응답으로 받는 UserInfoResponse dto는 다음과 같다.

(사용자의 이름과 프로필 이미지를 받아오게 하기 위해 작성한 것인데 잘 모르겠으면 공식문서 응답을 참고)

package com.sj.Petory.OAuth;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.HashMap;

@Getter
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class UserInfoResponse {
    @JsonProperty("id")
    private long id;

    @JsonProperty("properties")
    private HashMap<String, String> properties;

    @JsonProperty("kakao_account")
    private KakaoAcount kakaoAcount;

    @Getter
    @NoArgsConstructor
    @JsonIgnoreProperties(ignoreUnknown = true)
    public class KakaoAcount {

        @JsonProperty("profile")
        public ProfileInfo profile;

        @JsonProperty("profile_nickname_needs_agreement")
        public boolean nickNameAgree;

        @JsonProperty("profile_image_needs_agreement")
        public boolean profileImageAgree;

        @Getter
        @NoArgsConstructor
        @JsonIgnoreProperties(ignoreUnknown = true)
        public class ProfileInfo {

            @JsonProperty("nickname")
            public String nickName;

            @JsonProperty("profile_image_url")
            public String profileImageUrl;
        }
    }
}

 

findOrCreateMember() 메소드를 통해 InputInfo페이지에서 사용자가 입력한 이메일로 회원 DB에서 사용자를 찾는다.

 

있으면 해당 회원 엔티티를 가져오고

없으면 createMember() 메소드를 통해 카카오 제공 이름, 이미지와 사용자가 입력한 이메일, 전화번호로 DB에 저장한다.

 

그리고 최종 리턴 값은 일반 로그인 한 회원과 마찬가지로 우리 프로젝트의 JWT util를 이용해 ATK와 RTK를 발급해서 내려주게 된다.

 

이렇게 하면 Step3의 사용자 로그인 처리! 까지 다 됐다 😁😁😁

 

여기서 애먹었던 부분이 진즉 카카오 로그인은 완료했는데 추가 정보를 입력 받기 위해 inputInfo페이지로 이동했던 것이었다.

 

Trouble Shooting

1. 백에서 리다이렉트 URI로 날아온 코드를 통해 발급받은 카카오 토큰을 꼭 ! 프론트로 내려줬어야 했는데 

window.Kakao.Auth.Authorize()로 프론트에서 로그인 요청을 하고 로그인 요청 완료와 동시에 백 리다이렉트 URI 컨트롤러로 코드 요청이 들어가버리니까 대체 어디서 토큰을 받아와야하는 지를 몇 시간 동안 삽질했다.

 

해결했던 과정은 

리다이렉트 URI 백 컨트롤러에서 HttpServletResponse.sendRedirct(프론트 추가 입력 페이지 + token)을 통해 프론트 추가 입력 페이지로 리턴을 해줘서 토큰 값을 받아올 수 있었다 !

 

 

2. inputInfo에서 추가 정보 입력 후 완료 버튼을 누르면 404 NotFound가 떴음

 

라우팅 설정도 잘 해주었었다.

 

이거랑 비슷한 느낌으로 메인 페이지에서부터 하나하나 이동하면 잘 되던 것들이 주소창에 직접 입력하고 들어가려면 404 오류가 났다.

 

ex) 메인 -> 로그인 버튼 눌러서 ~~/login 은 이상 없는데 직접 ~~/login 입력하면 404 오류

 

지난 번에 nginx가  index.html 경로를 찾지 못 해서 ec2 서버에 /etc/nginx/nginx.conf에서 아래와 같이 지정해주었었다.

index.html을 찾지 못 해서 발생하는 문제는 아니고

 

아래 코드를 추가해줘서 해결했다. 

# React Router를 지원하기 위해 모든 경로를 index.html로 라우팅
location / { try_files $uri /index.html; }

 

 

마치며 ,,

백&프론트 연동을 하기 위해 정말 많이 삽질하고 오래 걸렸떤 ㅠㅠ 카카오 로그인 구현하기를 완료했다.

항상 완벽하게 이해하지 못 한 채로 구현한다는 느낌이 들었는데 이번에 구현하면서 로직이 정말 완벽하게 이해가 됐다 ,,,,

 

 

프론트는 무지해서 잘 몰랐는데 이번 기회에 과정 같은 것들이 좀 익숙해진 거 같다 !

백 얼른 개발 하고 프론트도 공부해서 풀스택을 다룰 수 있는 개발자가 되어야지..,...


참고

 

카카오 Developers 공식문서

https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#get-token-info

 

Kakao Developers

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

developers.kakao.com

 

전체적인 큰 도움 받았습니다.

https://ddonghyeo.tistory.com/16#hELLO

 

[Spring] REST API 카카오 로그인 구현하기

카카오 공식 문서를 기반으로 작성https://developers.kakao.com/docs/latest/ko/kakaologin/common구현 코드https://github.com/DDonghyeo/kakao-login참고이 글은 절차에 따라 코드를 완성해 나가는 과정입니다. 전체 코드

ddonghyeo.tistory.com

 

KOE010 오류 해결

https://velog.io/@denmark-choco/kakao-Login-API-KOE010401

 

kakao Login API - KOE010(401)

시작전!! 카카오 login api는 인가코드를 받은 후, 토큰을 발급받을 수 있습니다.(카카오 디벨로퍼에서 로그인 로직 확인하기)에러 발생상황은 인가코드를 받은 후 axios를 이용해 토큰을 받기위한 p

velog.io

 

백 -> 카카오 서버로 WebClient 이용한 요청에서 WebClient 기본 지식 학습

https://blog.naver.com/seek316/223337685249

 

[Java] Spring Boot - WebClient 사용하기

WebClient란? WebClient는 Spring Boot 애플리케이션에서 HTTP 요청을 만드는 데 사용되는 강력...

blog.naver.com

 

 

728x90