* 2022년 9월 16일 velog에 작성했던 게시글을 옮겨온 글입니다.
소셜 로그인, 그중에서도 카카오 로그인을 구현하기로 마음을 먹고 공식 문서를 펼쳤다. 처음엔 이해가 잘 안되는 게 당연하다고 생각하며 두세 번을 더 읽어봤지만 어떤 부분을 프론트엔드가 처리하고 어떤 부분을 백엔드가 처리해야 하는 건지 감이 오지 않았다. 일단 공식 문서는 덮어두고 구글링을 해봤지만 현 상황에 필요한 설명은 없었다. 그래서 카카오 로그인을 성공하고 나면 반드시 블로그에 정리하여 공유하겠다고 다짐했다.
답은 공식 문서에 다 있다.
정말 진부하지만 당연하게도 API를 배포한 사람들이 만든 공식 문서에 답이 다 있다. 이해하기 전까진 대체 무슨 소리인가 싶었지만 공식 문서만큼 친절한 자료도 없다. 그렇지만 외부 API 자체를 처음 써보거나 Node.js로 구현하는 방법을 찾고 있는 사람들에게 조금의 도움이라도 되고자 AtoZ 기록을 하려고 한다.
1. Kakao Developer에 로그인하여 내 애플리케이션에서 애플리케이션을 추가한다.
애플리케이션 추가하기를 누르면 아래와 같은 창이 뜬다.
일반적으로 나는 앱 이름에는 프로젝트명을 사업자명에는 팀명이 있다면 팀명을, 아니면 프로젝트명을 쓴다. 이때 주의할 점은 앱 아이콘을 반드시 추가해줘야 한다는 것이다. 프로젝트 로고가 있다면 해당 로고 이미지를 쓰는 걸 추천한다.
실제로 카카오 로그인시 이미지로 뜰뿐만 아니라 유저의 이메일, 성별, 생일 등이 데이터로 필요하다면 반드시 앱 아이콘을 추가해야 한다. 그 이유는 개인 개발자 비즈 앱 전환시에 필수 요소이기 때문이다.
비즈앱이란 "자사 앱이나 웹에서 카카오의 API를 사용하기 위해 카카오 디벨로퍼스에 개설하는 애플리케이션을 의미합니다. 카카오 디벨로퍼스에서 앱을 등록하신 후 사업자 정보를 등록하면 비즈 앱으로 전환됩니다. 앱의 오너만 사업자 정보를 등록할 수 있습니다." 라고 Kakao측에서 설명하고 있다.
정리하자면 사업자등록을 하라는 말이고, 사업자 번호가 없다면(개인 프로젝트용) 본인인증과 카카오비즈니스 통합 서비스 약관 동의를 해야 한다. 더 간단하게 말하면 우리 서비스를 사용하려면 당신을 신뢰할 수 있게 본인 인증하는 절차이다.
이 절차를 무시해도 카카오 로그인 서비스를 이용할 수 있지만 보통 소셜 로그인을 하는 이유는 아이디대신 이메일을 사용하려는 경우가 많기 때문에 하는 편이 좋다. 만약 인증하지 않으면 카카오 로그인 시 사용자가 필수로 동의해야 합니다. (검수 필요) 라는 문구와 함께 비활성화된 라디오 버튼을 볼 수 있을 것이다.
2. 왼쪽 앱 설정 항목에서 비지니스에 접속하여 비즈 앱 전환을 한다.
개인 개발자 비즈 앱 전환을 누르면 아래와 같이 목적을 적는 항목이 나오는데 본인의 목적에 따라 알맞게 작성해주면 된다. 일반적으로 이메일 필수 동의를 위해 사용하므로 해당 항목에 체크하고 전환하도록 했다.
비즈 앱으로 전환하면 개인 개발자 비즈 앱 전환탭이 카카오비지니스 관리자센터에서 비즈니스 채널 연결로 바뀌게 된다. 이제 다시 왼쪽 메뉴 탭에서 제품 설정 > 동의 항목을 눌러 이메일 설정을 눌러보면 아래와 같이 필수 동의 라디오 버튼이 활성화된 것을 확인할 수 있다.
3. 왼쪽 제품 설정 항목에서 동의 항목을 눌러 원하는 정보를 선택한다.
적절한 동의 목적을 작성해주고 필수 동의, 선택 동의든 원하는 서비스 로직대로 저장을 한다.
닉네임, 프로필 사진, 카카오계정(이메일)을 필수 동의 항목으로 선택했다. 예시로 보여주기 위함이며 내가 진행한 실제 프로젝트에선 닉네임과, 이메일만 수집했다. 예시 코드에서 프로필 사진을 처리하는 로직이 없더라도 본인이 원하는 서비스에서 필요하다면 추가해주면 된다.
제품 설정 > 카카오 로그인에 들어가 카카오 로그인을 활성화 설정해준다.
위와 같은 모달창이 뜨면서 동의 항목이 설정되지 않으면 회원식별값만 전달된다고 안내한다. 여기서 회원식별값은 카카오아이디를 의미한다. 카카오 서비스를 이용하기 위한 이메일이 아니고 1, 2, 3 과 같은 숫자로 이루어진 유저마다 가진 고유한 값이다.
회원번호라고 해서 int로 자료형을 설정해서 데이터 모델링을 하게 되면 어떤 경우엔 회원번호가 INSERT되지 않을 것이다. 공식 문서에도 기재되어 있듯이 Long 타입 즉, int 범위를 초과하는 회원번호가 발급될 수도 있다. 고로 int보다는 bigint로 자료형을 택해야 한다. Mysql, Oracle 등 본인이 사용하는 SQL에 맞게 설정해주면 된다. 회원번호의 개념에 대해 알았으니 이제부터 회원번호를 직관적으로 카카오 아이디라고 하겠다.
4. 왼쪽 앱 설정 항목에서 요약 정보를 눌러 플랫폼을 선택한다.
목적에 따라 선택해준다. 웹 프로젝트용이었기 때문에 Web을 선택했다.
사이트 도메인 주소가 없다면 http://localhost:3000을 사용한다. 저장하면 아래와 같이 Redirect_URI를 설정하라는 안내가 뜬다.
카카오 로그인은 서비스 로그인 과정에서 Redirect URI를 통해 서비스에서 요청한 인가 코드와 토큰을 전달하기 때문에 반드시 등록해줘야 한다. 그렇지 않으면 KOE006 에러가 발생한다. 쉽게 말하자면 카카오 로그인에 필요한 인가 코드와 토큰을 전달하기 위해 필요한 링크라고 생각하면 된다. 인가 코드와 토큰이 무엇인지는 뒤에서 이야기 하도록 하겠다.
어떤 URI를 사용해도 상관없지만 직관적으로 내가 설정한 도메인 주소 + /auth/kakao/callback을 사용하는 편이다. 콜백 주소까지 설정했다면 이제 카카오 로그인의 원리에 대해 이해해야 한다.
카카오 로그인의 원리
전체 로직은 위 그림과 같다. 소셜 로그인의 인가 방식은 OAuth를 채택하여 사용하고 있는데 해당 개념만 보는데도 시간이 꽤 걸리기 때문에 원한다면 OAuth 2.0 생활코딩 강의를 보는 것을 추천한다.
사실 처음 소셜 로그인을 공부할 때 프론트엔드가 인가 코드를 카카오 측으로부터 받아와 백엔드에게 넘겨주고, 다시 백엔드가 해당 인가 코드를 가지고 카카오 측에게 토큰을 받아오는 방식으로 학습을 했다. 백엔드에서 유저 정보를 가져오기 위해서 한번은 카카오 측에게 요청을 보내야 하는데 토큰까지 받아오는 요청을 하는 게 번거로워 프론트에게 토큰까지 받아달라고 요청을 했고 합의가 되었다.
즉, 위의 작성한 그림이 누군가에겐 보편적인 방식이 아닐 수도 있다는 의미이다. 개인적으로는 프론트가 토큰까지 받아와 토큰만 넘겨주는 게 편했기 때문에 이러한 방식을 채택하게 되었다.
인가코드란 동의 화면을 통해 인가받은 동의 항목 정보를 가진 코드를 말하며, 인가 코드를 사용해 토큰 받기를 요청할 수 있습니다.
5. 인가 코드 요청하기 (프론트엔드 업무)
이제 본격적으로 카카오 로그인을 하기 위한 과정을 프론트엔드, 백엔드 나눠서 설명할 것이다. 프론트엔드가 없어도 POSTMAN을 통하여 백엔드 혼자 인가 코드, 토큰을 발급 받아볼 수 있다.
인가 코드 받기 공식문서에도 친절하게 나와있듯이 해당 주소로 GET요청을 보내라고 한다.
GET /oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code HTTP/1.1
Host: kauth.kakao.com
👉 적용 예시
https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=어쩌고 저쩌고&redirect_uri=http://localhost:3000/oauth/callback/kakao
앱 설정 > 요약 정보를 누르면 앱 키를 확인할 수 있는데 REST API키가 바로 웹 상에서 카카오 로그인 하기 위한 유형중 하나이다. 다른 이들에게 노출되면 절대 안되니 주의하자. 해당 정보가 담긴 .env파일 혹은 config파일은 .gitignore에 넣어야 한다. 알아보기 쉽게 REST API 키를 어쩌고 저쩌고 라고 하겠다.
여기서 알아야 할 것은 각각
- client_id 즉, REST API 키인 어쩌고 저쩌고
- redirect_uri 즉, 사전에 우리가 설정한 Redirect URI
- response_type=code 즉, 응답을 어떻게 받을 것이냐
를 뜻한다는 것이다.
GET 요청을 보내면 위와 같은 응답이 출력된다. 이때 인가 코드를 받기 위해서는 GET 요청을 보낸 URI를 그대로 복사하여 브라우저에 입력해야 한다. 그럼 위에서 봤던 카카오 로그인 화면이 실제로 뜰 것이다.
혹시 카카오 로그인을 해놨다면 안 뜨고 바로 코드가 뜰 수도 있다. 카카오 로그인을 진행하면 아래와 같은 화면이 뜨면서 URI 입력란이 변한 것을 알 수 있다.
Redirect URI 뒤에 붙은 code=에 성공적으로 인가 코드가 발급되었다. 위에선 /auth/kakao/callback라고 설정했는데 POSTMAN 예시에선 /oauth/callback/kakao라고 되어 있어 헷갈릴 수도 있을 것 같아 덧붙이자면 예전에 만들어 놨던 애플리케이션으로 테스트 중이라 그렇다. 그냥 본인이 설정한 Redirect URI로 응답이 올 것이다.
6. 인가 코드로 토큰 요청하기 (프론트엔드 혹은 백엔드 업무)
URI에서 얻은 인가 코드를 복사해두고 토큰 받기 공식문서를 살펴보자. 단, 인가 코드는 매 요청마다 바뀌기 때문에 인가 코드를 여러번 요청했다면 가장 마지막의 인가 코드를 가지고 토큰을 요청해야 한다. 그렇지 않으면 유효하지 않은 인가 코드라고 에러가 뜰 것이다.
POST /oauth/token HTTP/1.1
Host: kauth.kakao.com
Content-type: application/x-www-form-urlencoded;charset=utf-8
👉 적용 예시
https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id=어쩌고 저쩌고&redirect_uri=http://localhost:3000/oauth/callback/kakao&code=복사한 인가 코드
여기서 알아야 할 것은 각각
- client_id 즉, REST API 키인 어쩌고 저쩌고
- redirect_uri 즉, 사전에 우리가 설정한 Redirect URI
- grant_type=authorization_code 즉, 인가 코드를 넘기겠다
를 뜻한다는 것이다.
POSTMAN을 이용하여 POST 요청을 보내면 성공적으로 access_token이 발급된다. 프론트 엔드측 로직이 현재 없기 때문에 POSTMAN을 통하여 요청하고 있지만 만약 프론트 엔드라면 해당 URI로 fetch혹은 axios 요청을 보내서 받아오면 된다. 이제 토큰까지 받았으니 유저의 정보를 얻을 수 있다.
7. 토큰으로 사용자 정보 가져오기 (백엔드 업무)
사용자 정보 가져오기 공식문서를 살펴보자. 이제부터 백엔드 업무이니 POSTMAN이 아닌 코드로 예시를 들 것이다.
GET/POST /v2/user/me HTTP/1.1
Host: kapi.kakao.com
Authorization: Bearer ${ACCESS_TOKEN}/KakaoAK ${APP_ADMIN_KEY}
Content-type: application/x-www-form-urlencoded;charset=utf-8
👉 적용 예시
// Controller단
const kakaoSignIn = async (req, res) => {
const headers = req.headers["authorization"];
const kakaoToken = headers.split(" ")[1];
const accessToken = await authService.signInWithKakao(kakaoToken);
return res.status(200).json({ accessToken: accessToken });
};
프론트엔드 측에서 발급 받은 카카오 토큰을 headers에 넣어 전달해주면 객체의 형태로 오기 때문에 key값인 authorization으로 읽어와 " "으로 잘라주고 배열에 담긴 요소중 1번째 index를 택하면 순수한 카카오 토큰만을 얻을 수 있다. 이렇게 공백으로 잘라주는 이유는 토큰을 전달할 때 Bearer kakaoToken
의 형태로 받기로 약속했기 때문이다. 토큰을 전달하는 규약이라고 생각하면 된다. 이렇게 얻어온 카카오 토큰을 서비스단의 함수로 전달한다.
// Service단
const signInWithKakao = async (kakaoToken) => {
const result = await axios.get("https://kapi.kakao.com/v2/user/me", {
headers: {
Authorization: `Bearer ${kakaoToken}`,
},
});
const nickname = result.data.kakao_account.profile.nickname;
const email = result.data.kakao_account.email;
const kakaoId = result.data.id;
if (!nickname || !email || !kakaoId) throw new error("KEY_ERROR", 400);
const user = await authDao.getUserByEmail(email);
if (!user) {
await authDao.signUp(nickname, email, kakaoId);
}
return jwt.sign({ sub: kakaoId }, process.env.JWT_SECRET);
};
카카오 토큰을 인자로 받아 위의 공식 문서대로 https://kapi.kakao.com/v2/user/me로 axios GET 요청을 보낸다. 백엔드에선 카카오 로그인과 동시에 유저의 정보를 데이터 베이스에 INSERT해야 하므로 함수 Router 자체는 POST 요청이지만 서비스 단에서 한번은 우리가 클라이언트가 되어 카카오 측 서버에 GET 요청을 보내야 한다.
axios 요청을 보내면 객체 형태로 응답을 받을 수 있다. 카카오 토큰을 담아 해당 API를 실행시키고 console.log(result.data)은 다음과 같이 출력된다.
const email = result.data.kakao_account.email
원하는 정보를 객체 접근법으로 접근하여 변수에 담아두면 데이터 베이스에 저장하기 훨씬 수월하다. 내가 짠 로직은 기존의 유저인지 검증하고 유저가 아니라면 데이터 베이스에 INSERT하여 우리 서비스의 토큰을 발급해주고, 이미 존재하는 유저라면 그냥 토큰을 발급해준다.
// Dao단
const signUp = async (nickname, email, kakaoId) => {
const result = await database.query(
`INSERT INTO users (
name,
email,
kakao_id
) VALUES (?,?,?)`,
[nickname, email, kakaoId]
);
return result.getLastInsertedId();
};
변수에 담아둔 정보들을 Dao단에서 INSERT 해주면 성공적으로 데이터 베이스에 저장이 된다. 이제 kakaoId를 payload에 담아 jwt를 발급하여 프론트엔드에게 전달해주면 유저는 이 토큰을 가지고 우리 서비스를 이용할 수 있다.
카카오 로그인 끝!
'JavaScript' 카테고리의 다른 글
Node.js 카카오 로그인 유닛테스트 Unit Test (mockReturnValue) (0) | 2022.12.31 |
---|---|
[]===[], {}==={} 는 거짓(false)이다 (0) | 2022.12.31 |
if문과 true&false (0) | 2022.12.31 |
배열의 typeof는 Object이다 (0) | 2022.12.31 |
댓글