NesJS 카카오 로그인 구현하기

권태형·2023년 5월 10일
3

NestJS 연습

목록 보기
16/19
post-thumbnail

😀이번엔 소셜로그인을 구현해 보자!

시작하기전 사전설정

카카오톡 로그인을 구현하기 앞서 사전설정이 필요하다.

  1. 카카오 개발탭 회원가입
    카카오 디벨로퍼스에서 카카오 아이디와 이름만으로 간단하게 가입하면된다.

  2. 플랫폼 등록 및 설정

    카카오 로그인을 적용한 웹 or 앱 의 대표아이콘과 이름, 대표자 이름을 작성 이후 이 부분에 등록한 내용이 권한 인증 탭에 표출된다.

    등록을 완료하면 왼쪽 아래와 같은 창이 뜨는데 여기서 플렛폼 설정을 통해 본인의 프로젝트에 맞는 설정을 해주면된다.
    😀필자는 web사이트에서 사용할 것이기 때문에 web플랫폼 등록으로 진행하였다.

  1. 카카오로그인 활성화

    이전 단계에서 만들었던 플랫폼등록후 좌측 제품 설정에서 카카오 로그인을 활성화 시켜준다.

  2. 리다이렉트 URI등록
    같은 플랫폼 제품설정에서 등록할 수 있다.
    이 리다이렉트 URI는 실질적으로 카카오 로그인 요청을 진행할 때 GET요청에 의한 실제우리가 아는 카카오유저가 ID와 PASSWORD로 로그인한 후 응답값으로 받는 쿼리스트링으로 code를 받으면서 동작할 라우터의 URI를 의미한다.

  1. 동의항목 설정하기
    동의 항목 또한 설정탭에서 카카오아이디에서 수집할 정보에 대해서 사용자에게 고지할 목록을 설정해준다.

    이렇게 5가지 필수 항목을 완료하고 나서 카카오 로그인 구현을 시작할 수 있다.


카카오 로그인 흐름


구현시작

😀위의 흐름에 따라서 구현을 시작해보자. 흐름을 이미지라고 넘기지 말고 꼭 한번 읽어보길 바란다.

1. 클라이언트에서 로그인요청을 시도한다.

😀즉 웹페이지나, 어플리케이션에서 카카로톡으로 로그인하기 버튼을 클릭하는 시점이라고 표현할 수 있겠다.
이 요청을 클릭했을 때 우리는 카카오 로그인페이지로 넘겨줘야한다.

필요정보


위의 기본정보에 포함된 주소로 GET요청을 보내면 아래 response_type의 code를 받을 수 있다. 일반적으로 HTTP요청을 하는 방법은 여러가지가 있고 axios요청을 자주 사용하는 것을 많이 봤는데, 필자는 카카오 로그인 버튼 클릭 요청 API를 진행시 바로 다음 라우터에 실행될 수 있도록 리다이렉트를 줘서 코드를 작성했다. 또한 NestJS는 axios를 포함한 HTTP모듈을 따로 내장하고 있기때문에 이 모듈을 임포트하여 간단하게 사용할 수 있다.

로직작성


@Get('kakao-login-page')
  @Header('Content-Type', 'text/html')
  async kakaoRedirect(@Res() res: Response): Promise<void> {
    const url = `https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${KAKAO_API_KEY}&redirect_uri=${CODE_REDIRECT_URI}`;
    res.redirect(url);
  }

이처럼 작성하고 domain/path/kakao-login-page(필자: localhost:4000/auth/kakao-login-page) 로 들어가보면 로그인이 되어있지 않다면 로그인 ID, PASSWORD입력창으로 이동하고 입력완료 후 동의화면으로 이어지게 된다.

😀동의하고 계속하기를 진행하면 에러메시지가 출력될 것이다. 아직 CODE_REDIRECT_URI에 해당하는 로직을 작성하지 않았기 때문이다. 만약 잘 동작하고 있는지 의심이 된다면 간단하게 아래와 같이 콘솔만 찍어주는 로직을 만들어서 실행해 보면 알아볼 수 있다.

@Get('kakao')//< 여기는 필자가 작성해둔 CODE_REDIRECT_URI의 라우터 경로이다.
  async getKakaoInfo(@Query() code: string) {
    console.log(code);
  }

2. 받은 코드로 토큰을 받아보자.

😀이제 코드를 받고, 토큰을 요청하는 라우터 로직을 작성하자. 우리에게 필요한건 사용자 정보인데, 이 사용자 정보를 얻고 싶다면 토큰을 가지고 한번더 요청을 보낼 필요가 있다.

필요정보

또 POST메소드를 이용해서 정보를 담아 보내면 토큰을 얻을 수 있다. 샘플 예시를 들여다보자.

curl -v -X POST "https://kauth.kakao.com/oauth/token" \
 -H "Content-Type: application/x-www-form-urlencoded" \
 -d "grant_type=authorization_code" \
 -d "client_id=${REST_API_KEY}" \
 --data-urlencode "redirect_uri=${REDIRECT_URI}" \
 -d "code=${AUTHORIZE_CODE}"

생플 예시에서 나타내는 용어를 보자

-v 는 verbose 모드를 나타내며, HTTP 요청에 대한 상세 정보를 보여주도록 설정하는 옵션이다.
-X POST 는 HTTP 요청 메소드를 POST로 설정하는 옵션이다.
-H 는 HTTP 요청 헤더를 설정하는 옵션으로, Content-Type 헤더를 application/x-www-form-urlencoded로 설정한다.
-d 는 HTTP 요청 데이터를 설정하는 옵션으로, grant_type, client_id, code와 같은 파라미터들을 설정한다.
--data-urlencode 는 HTTP 요청 데이터를 url-encode 형식으로 설정하는 옵션으로, redirect_uri 값을 설정한다.

한마디로 POST요청으로 Headers에 Content-Type 헤더를 application/x-www-form-urlencoded를 넣어주고, parameter에 grant_type, client_id, code와 인코딩한 redirect_uri 값을 넣어주라는 말이다.

로직작성

😀자 이제 다시 로직을 돌아가자

위와 똑같이 또 컨트롤러에서 리다이렉트를 시켜서 진행할 수도 있으나, 그러면 컨트롤러에 쓸데없는 로직이 많아지는 것 같아 이번에는 서비스 로직에서 axios요청을 진행해 보자.

// controller
@Get('kakao')
  async getKakaoInfo(@Query() query: { code }) {
    const apikey = KAKAO_API_KEY;
    const redirectUri = CODE_REDIRECT_URI;
    await this.authService.kakaoLogin(apikey, redirectUri, query.code);
  }

KAKAO_API_KEYCODE_REDIRECT_URI는 컨트롤러 단에서 위에서 한번 썻으니까 서비스에서 다시 임포트하기 싫어서 kakaoLogin()의 인자로 변수에 담아 보내버렸다. 여기서 코드만 보내고 서비스 로직에서 process.env에서 꺼내서 써도 무방하다.

//service
async kakaoLogin(apikey: string, redirectUri: string, code: string) {
    const config = {
      grant_type: 'authorization_code',
      client_id: apikey,
      redirect_uri: redirectUri,
      code,
    };
    const params = new URLSearchParams(config).toString();
  	const tokenHeaders = {
      'Content-type': 'application/x-www-form-urlencoded;charset=utf-8',
    };
    const tokenUrl = `https://kauth.kakao.com/oauth/token?${params}`;
    

    const res = await firstValueFrom(
      this.http.post(tokenUrl, '', { headers: tokenHeaders }),
    );
    console.log(res.data);
  
  	// url을 다음과 같이 바꾸면 아래의 요청방식 3가지 중 하나로 요청할 수 있다.
	// const tokenUrl = `https://kauth.kakao.com/oauth/token?`  
  
	//아래의 3가지 방법은 2번째 인자로 ''(빈문자열)이 아닌 params를 받는다.  
    //2번 const res = await firstValueFrom(
    // this.http.post(tokenUrl, params, { headers: tokenHeaders }),
    // );

    //3번 const res = await this.http.post(tokenUrl, params, { headers: tokenHeaders }).toPromise()

    //4번 await axios.post(tokenUrl, params, { headers: tokenHeaders }).then((res) => {
    //   console.log(res.data);
    // });
  }

새로 창을 열어서 로그인을 시도 해 axios요청을 보내서 res에 담아 res.data를 콘솔에 찍어보면 정상 출력되는 것을 확인할 수 있다.

여기서 scope는 우리가 처음에 동의항목에 설정한 값인 것을 확인할 수도 있다.

😀필자는 여기서 AxiosError: Request failed with status code 400 에러를 좀 많이 겪었다. 따라서 여러가지 axios요청보내는 방법을 4가지 알게 되었다. 1~4번 까지(1번은 주석이 안된 로직) 필자와 같은 처지에 있는 친구들은 Axios 에러처리 포스팅을 참고하자.


3. 받은 토큰으로 회원정보를 받아보자

😀본인의 서비스가 반드시 카카오톡 회원만 가입가능하도록 만든다면 위의 과정에서 받은 토큰을 가지고 요청을 진행시켜도 무방할 것이다. 하지만, 카카오에서 회원토큰 인증을 위한 API를 따로 지원해 주지 않기 때문에 이 회원정보조회 API를 이용해 토큰에 대한 유효성을 검증해야 본인 서비스에 권한이 있는 유저만이 필요한 요청을 진행할 수 있게 될 것이다.

또는 본인의 서비스에 다른 로컬 로그인과, 추가적인 타사의 소셜로그인이 존재한다면, 따로 구분해서 본인 서비스의 DB에 저장할 필요가 생길 것이다.

필요정보


필수 정보는 Authorization 헤더에 Bearer ${ACCESS_TOKEN}만을 요구한다.

HTTP GET또는 POST요청으로 kapi.kakao.com으로 Authorization헤더에 토큰을 담아서 보내주면 회원정보를 받을 수 있다.

로직직성

위에서 받은 res.data에 있는 access_token을 headers에 담아서 axios요청을 진행하면 된다.

	// 위의 토큰받기 로직의 마지막 부분
    // await axios.post(tokenUrl, params, { headers: tokenHeaders }).then((res) => {
    //   console.log(res.data);
    // }); 

    const userInfoUrl = `https://kapi.kakao.com/v2/user/me`;
    const userInfoHeaders = {
      Authorization: `Bearer ${res.data.access_token}`,
    };
    const { data } = await firstValueFrom(
      this.http.get(userInfoUrl, { headers: userInfoHeaders }),
    );
    console.log(data);

이전 단계인 토큰받기에서는 res에 axios요청에 대한 응답을 모두 받았다. 이때 내용이 많고 이번 로직에 쓰일 access_token을 꺼낼때 res.data.access_token으로 길게 써야했던 것을 방지하기 위해 이번 axios요청에서는 data만 객체 구조 분해 할당을 통해 data변수만 뽑아쓸 수 있도록 작성하였다.

새로 창을 열어 다시 로그인을 시도해 보니 콘솔에 data가 잘 찍히는 것을 확인할 수 있었다.

해당 로직을 따로분류해서 카카오 인증용 미들웨어를 만들어도 되고, 회원정보를 얻었으니 그 회원정보를 본인 서비스의 DB에 저장하고 관리해도 된다.


이후로 해야될 일은 본인 서비스에 맞춰서 진행하여야한다. 로그아웃도 존재하지만, 로그아웃 로직은 회원정보요청 로직과 매우 유사하기 때문에 KAKAO DEVELOPERS에서 참고해서 만들도록 하자.

profile
22년 12월 개발을 시작한 신입 개발자 ‘권태형’입니다. 포스팅 하나하나 내가 다시보기 위해 쓰는 것이지만, 다른 분들에게도 도움이 되었으면 좋겠습니다. 💯컬러폰트가 잘 안보이실 경우 🌙다크모드를 이용해주세요.😀 지적과 참견은 언제나 환영합니다. 많은 댓글 부탁드립니다.

0개의 댓글