될때까지

카카오 소셜로그인 Node.js로 구현하기 본문

학습/Node.js

카카오 소셜로그인 Node.js로 구현하기

랖니 2022. 9. 20. 10:43
728x90

1. KaKao Developers에서 등록하기

https://developers.kakao.com/에서 시작하기 버튼 눌러서 개발자 계정 가입 후, 맨 위 오른쪽의 '내 어플리케이션' 클릭

애플리케이션 추가하기를 누르고, 앱 이름과 사업자명을 입력한다.

생성한 앱 이름을 선택하면, 

앱키가 자동 발급된다. 여기서 REST API키는 환경변수로 등록할 예정!

그다음에는 플랫폼으로 이동해서 Web 플랫폼 등록을 누르고 사이트 도메인을 입력한다.

그러면 Web하단에 Redirect URI를 등록하라고 한다. 등록해준다.
카카오 서버에서 어느 주소로 REST API GET요청을 보내줄까? 적는 곳이라고 생각하자.
주소 뒤에 /auth/kakao/callback이라고 적어주자.

나는 포스트맨으로 테스트할 예정이기 때문에 포스트맨 주소도 추가해줬다.

Redirect URI를 입력하고 나면, 위에 활성화 설정이 있는데 on으로 활성화 시켜준다.

카카오 로그인 > 동의항목으로 이동해서 내 서비스에서 필요한 유저의 정보들을 선택한다.

여기까지 하면 카카오 개발자 사이트에서 해야할 일은 끝났다.
Redirect URL, REST API를 기억해두자.

 

2.  Kakao REST API 구현법

먼저 사이트에서 카카오 로그인 REST API 구현법을 살펴봐야한다.

FROM KAKAO DEVELOPERS

step1 : 인가 코드 받기

  1. 서비스 서버가 카카오 인증 서버로 인가 코드 받기를 요청한다.
  2. 카카오 인증 서버가 사용자에게 카카오 계정 로그인을 통한 인증을 요청한다.
  3. 사용자가 카카오 계정으로 로그인한다.
  4. 카카오 인증 서버가 사용자에게 동의 화면을 출력하여 인가를 위한 사용자 동의를 요청한다.
  5. 사용자가 필수 동의 항목, 이 외 원하는 동의 항목에 동의한 뒤 계속하기 버튼을 누른다.
  6. 카카오 인증 서버는 서비스 서버의 Redirect URI로 인가 코드를 전달한다.

step2 : 토큰 받기

  1. 서비스 서버가 Redirect URI로 전달받은 인가 코드로 토큰 받기를 요청한다.
  2. 카카오 인증 서버가 토큰을 발급해 서비스 서버에 전달한다.

step3 : 사용자 로그인 처리

  • 서비스의 사용자 로그인 처리는 서비스에서 자체 구현해야한다.

 

3.  Node.js로 서버 구현하기

인가코드 받기

먼저 카카오 로그인을 위한 url을 열어줘야한다.
나는 localhost:3000/users/kakao/signup으로 주소를 생성했고, 해당 경로로 들어오면 userController.kakao로 넘겨준다.

// routes > userRouter.js

const router = require("express").Router();
const userController = require("../controllers/userController");

router.post("/signup", userController.signUp);
router.post("/signin", userController.signIn);
router.get("/kakao/signup", userController.kakao);

module.exports = {
  router,
};

 

컨트롤러는 req, res (요청과 응답을) 처리하는 부분이다. 

카카오 로그인 인가 코드 받기의 기본 정보는 아래와 같다고 사이트에 기재되어 있다.

// controllers > userController.js

const config      = require("../config/config");
const userService = require("../services/userService")

...생략...

const kakao = async (req, res) => {
    try { 
        const baseUrl = 'https://kauth.kakao.com/oauth/authorize';

        const kakaoConfig = {
            client_id    : config.client_id,
            redirect_uri : config.redirect_uri,
            response_type: 'code'
        };

        const params = new URLSearchParams(kakaoConfig).toString(); 
        const finalURI = `${baseUrl}?${params}`;

        res.redirect(finalURI);
    } catch (e) {
        console.log(e);
        res.status(e.status || 500).json({message:e.message || "서버에러"})
    }
}

module.exports = {
    signUp,
    signIn,
    kakao
}

요청을 보내야하는 주소를 baseUrl에 저장했다.
해당 주소로 요청을 보내면 끝나는 게 아니라,url뒤에 ? 쿼리 파라미터로 필요한 카카오 설정들을 덧붙여야한다.

kakaoConfig라는 객체 변수를 생성하고, 안에 해당 정보들을 기재한다.
여기서 키값은 카카오에서 요구한 대로 기재해야한다는 점 주의!
나는 dotenv를 사용하고 있기 때문에, config파일에서 내보낸 환경설정을 해당 코드에서 가져다 사용하게끔 작성했다.

URLSearchParams.toString()은 URL에 사용할 수 있는 쿼리 문자열을 반환하는 메소드다.
kakaoConfig객체에 나누어져있는 문자열을 URL처럼 뭉쳐준다.
URLSearchParams의 실행 결과는 객체로 반환하는데, 이를 문자열로 바꾸기 위해 뒤에 toString()을 붙였다.

해당 메소드를 사용해서 반환된 쿼리 문자열을 params변수에 저장하고,
baseUrl뒤에 덧붙여주면 요청보낼 URI가 카카오에서 요구한 주소와 일치하게 된다.

res.redirect() 메소드는 인자로 들어온 URL로 페이지를 이동시킨다.

그다음 내가 생성했던 주소 url로 이동하면 카카오 로그인 창이 뜬다.
전체 동의 후 동의하고 계속하기버튼을 누르면 카카오 개발자 사이트에서 설정했던 Redirect URI로 이동하고
params에 인가코드를 담아 반환을 해준다.

그럼 이제 전달받은 인가코드를 카카오로 보내서 토큰을 요청해보자.

 

인가코드로 토큰 요청하기

토큰을 요청하기 위한 주소는 위와 같은데, 내가 만든 사이트에서는 /users로 들어와야 userRouter에서 처리하도록 작성해놨다.
마음에는 안들지만, 앞에 /users를 붙이지 않기 위해 index.js에서 url을 작성했다.

// routes > index.js

const router = require("express").Router();
const userRouter = require("./userRouter");
const userController = require("../controllers/userController");

router.get("/auth/kakao/callback", userController.kakaoSignIn);
router.use("/users", userRouter.router);

module.exports = router;

/auth/kakao/callback으로 get 요청 시 userController.kakaoSignIn에서 처리하게 했다.

const config      = require("../config/config");
const userService = require("../services/userService")

...생략...

const kakaoSignIn = async (req, res) => {
    try {
        const code = req.query.code; 
        const {accessToken, } = await userService.kakaoSignIn(code);
        res.status(201).json({ message : "로그인 성공", accessToken : accessToken})
    } catch (e) {
        console.log(e);
        res.status(e.status || 500).json({message:e.message || "서버에러"})
    }
}

module.exports = {
    kakaoSignIn
}

req의 query에 code라는 이름에 인가코드가 담겨있다는 걸 카카오사이트에서 확인했다.
req.query.code를 사용해 인가코드를 code라는 변수에 저장하고, 비즈니스 로직을 처리하는 서비스단으로 code를 넘기게 작성했다.

개인적으로 코드를 작성할 때 가장 어려웠던 서비스부분.. ㅜ_ㅜ

const userDao                                 = require("../models/userDao")
const {createAccessToken, createRefreshToken} = require("../utils/jwt");
const axios                                   = require("axios");
const config                                  = require("../config/config");

...생략...

const kakaoSignIn = async (code) => {
    const userInfo = await getKakaoToken(code);

    const email        = userInfo.data.kakao_account.email;
    const nickname     = userInfo.data.properties.nickname;
    const profileImage = userInfo.data.properties.profile_image;
    const kakao_id     = userInfo.data.id

    const user = await userDao.getUserByEmail(email);

    const userDto = {
        email,
        nickname,
        profileImage,
        kakao_id
    }

    if (user.length === 0 ) {
        await userDao.createKakaoUser(userDto)
    } else {
        const accessToken  = createAccessToken(user)
        const refreshToken = createRefreshToken()

        await userDao.updateUserRefreshToken(user[0].id, refreshToken)
        return {accessToken : accessToken};
    }
}

kakaoSignIn으로 전달된 인가코드 code를 가지고 카카오 서버에 보내고 토큰을 받아야한다. 
그리고 해당 토큰을 사용해 유저 정보를 추출하고
그 뒤에 카카오 로그인이 진행될 수 있다.

먼저 카카오 서버에 보내고 토큰을 받는 로직을 작성했고, 해당 로직은 getKakaoToken 함수로 따로 만들었다.
카카오 토큰을 요청하기 위해 위에 캡처한 카카오 서버대로 코드를 작성했다.

axios를 사용했는데, axios는 프론트엔드, 백엔드가 통신을 쉽게 하기 위해 Promise API를 활용하는 HTTP 비동기 통신 라이브러리다.
axios는 비교적 컴팩트하다고 하던데, 자세한 사항은 해당 블로그 참고

일단은 API통신을 하게끔 도와주는 라이브러리라고 생각하자.😄
필요 정보들을 담아 axios를 사용해 비동기 통신을 보내면, result안에 json객체로 응답이 담겨있다.
콘솔에 찍어서 result를 확인했고, 내가 필요한 accessToken은 result.data.access_token에 담겨있어서 추출하여 변수로 저장해줬다.

const getKakaoToken = async (code) => {
    const tokenUrl = 'https://kauth.kakao.com/oauth/token';
    let accessToken;
    try {
        const result = await axios({
            method : 'POST',
            url    : tokenUrl,
            headers: {
                "Content-Type" : "application/x-www-form-urlencoded;charset=utf-8"
            },
            params : {
                grant_type  : "authorization_code",
                client_id   : config.client_id,
                redirect_uri: config.redirect_uri,
                code
            }
        });
        accessToken = result.data.access_token;
    } catch (e) {
        console.log(e.response.data)
        throw {status:400, message:"카카오 로그인 요청 중 에러 발생"}
    }
    const userInfo = await getUserInfoByKakaoToken(accessToken)
    return userInfo;
}

토큰을 받았으니 이제?
유저 정보를 요청해야한다.

상세하게 설명이 잘 되어있는 카카오.. 갓카오👍

유저정보를 요청하는 함수도 새로 만들었다.

const getUserInfoByKakaoToken = async (accessToken) => {
    const userInfoUrl = `https://kapi.kakao.com/v2/user/me`;

    let userInfo = await axios ({
        method : 'GET',
        url : userInfoUrl,
        headers : {
            Authorization : `Bearer ${accessToken}`
        }
    });
    return userInfo;
}

module.exports = {
    signUp,
    signIn,
    kakaoSignIn
}

위의 카카오에서 요구한 대로 똑같이 axios를 사용해 요청을 보냈고, 결과값인 유저정보를 반환해서 타고타고 다시 kakaoSignIn에서 사용하게 된다.

const userDao                                 = require("../models/userDao")
const {createAccessToken, createRefreshToken} = require("../utils/jwt");
const axios                                   = require("axios");
const config                                  = require("../config/config");

...생략...

const kakaoSignIn = async (code) => {
    const userInfo = await getKakaoToken(code);

    const email        = userInfo.data.kakao_account.email;
    const nickname     = userInfo.data.properties.nickname;
    const profileImage = userInfo.data.properties.profile_image;
    const kakao_id     = userInfo.data.id

    const user = await userDao.getUserByEmail(email);

    const userDto = {
        email,
        nickname,
        profileImage,
        kakao_id
    }

    if (user.length === 0 ) {
        await userDao.createKakaoUser(userDto)
    } else {
        const accessToken  = createAccessToken(user)
        const refreshToken = createRefreshToken()

        await userDao.updateUserRefreshToken(user[0].id, refreshToken)
        return {accessToken : accessToken};
    }
}

회원가입을 진행하기 위해 필요한 정보들을 userDto에 저장했다. 

  • DTO : Data Transfer Object - 로직이 없는 순수한 데이터 객체
  • DAO : Data Access Object - 서비스와 DB를 연결하는 고리 역할

userDao.getUserByEmail을 통해 이미 가입된 유저인지 확인한다.
가입이 되어있는 경우에는 내 사이트의 accessToken, refreshToken을 발급한다.

처음 이용하는 유저라면, userDao.createKakaoUser에 userDto를 사용해 DB에 저장한다.

 

해당 진행과정을 포스트맨으로 테스트할 수 있는데, 이는 다른 포스팅에 이미 정리해놨으니 그걸로 대체!
🎉 이렇게 하면 카카오 소셜 로그인 노드로 구현하기 완료 🎉

728x90