상똥이의 Back-End 공부방
[Nest.js] 카카오 로그인 API (회원가입, 로그인, 카카오 프로필 가져오기) 본문
목차
0. 사전 준비 : 내 애플리케이션 등록
1. 카카오 로그인 로직 설명
2. 로직 구현 1 - 인가 코드 받아오기
3. 로직 구현 2 - 토큰 받아오기
4. 로직 구현 3 - 회원가입 및 프로필 생성
5. 전체 코드
[0. 사전 준비 : 내 애플리케이션 등록]
1. 실제 사업자 등록은 필요 없고 대충 작성만 하면 됨
2. https://developers.kakao.com/ 에 들어가서 로그인
3. 상단 목록 중 '내 애플리케이션'으로 이동
4. '내 애플리케이션 추가하기' 클릭
5. 앱 이름, 사업자 명, 카테고리 작성 및 동의사항 체크 후 저장 누르면 생성됨
6. 생성된 애플리케이션을 클릭하고, 제품 설정 > 카카오 로그인 으로 들어가 환경설정
- '활성화 설정'의 상태를 ON으로 바꾸기
- 'OpenId Connect 활성화 설정'의 상태를 ON으로 바꾸기
- 'Redirect URI' 등록하기 (카카오 로그인 후 랜딩 페이지로 이동할 수 있도록 함 (나는 'http://localhost:3000/kakao-auth/callback' 으로 입력했음)
- '동의항목'으로 이동하여 개인정보 중 필요한 항목(이 글에서는 닉네임)에 대한 상태를 '필수 동의'로 변경
[1. 카카오 로그인 로직 설명]
※ https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api 에 접속해서 API설명을 볼 수 있음
0. 로그인 요청
- 서비스 클라이언트에서 서비스 서버로 카카오 로그인 요청한다.
1. 인가 코드 받기
- 서비스 서버에서 카카오 인증 서버에 인가 코드를 요청한다 (Get 메서드)
- 카카오 인증 서버는 서비스 클라이언트에 카카오계정 로그인 요청 한다.
- 카카오 인증 서버는 서비스 클라이언트에 동의 화면을 출력하고,
- 서비스 클라이언트에서 동의하면 카카오 인증 서버에서 서비스 서버에 302 Redirect URI로 인가 코드를 전달한다
2. 토큰 받기
- 서비스 서버에서 POST 메서드로 카카오 인증 서버에 토큰을 요청한다. 경로는 /oauth/token
- 카카오 인증 서버에서 서비스 서버로 토큰을 발급한다.
3. 사용자 로그인 처리
- 서비스 서버에서 ID 토큰 유효성 검사를 진행한다.
- 서비스 서버에서 발급받은 사용자 토큰으로 사용자 정보를 조회한다.서비스 회원정보 확인 또는 회원가입 처리한다.
- 로그인 완료
[2. 로직 구현 1 - 인가 코드 받아오기]
0. 초기 설정
폴더 구조
database(prisma)
model User {
id String @id
userProfile UserProfile?
}
model UserProfile {
id String @id
user User @relation(fields: [id], references: [id])
nickname String
}
1. .env파일 설정
- REST_API_KEY는 내 애플리케이션 > 앱 설정 > 요약 정보에서 확인 가능
- REDIRECT_URI는 로그인 완료 후 랜딩 페이지로 설정
- REST_API_KEY와 REDIRECT_URI는 .env파일에 담아 관리한다.
REST_API_KEY = 내_애플리케이션_요약정보에서_받아온_REST_API_키
REDIRECT_URI = https://localhost:3000/kakao-auth/callback
- 잘 설정되었는지 확인하기 위해, 아래 url에 본인의 REST_API_키와 REDIRECT_URI를 적절히 삽입해서 사이트로 이동해보자
https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI} |
- 나는 잘 이동됨
2. 로직 구현
- kakao-auth.service.ts에 url을 반환하는 로직을 작성한다.
- 작성된 링크는 인가 코드를 받아오는 url+필수 파라미터를 조합한 것
// kakao-auth.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PrismaService } from 'src/database/prisma/prisma.service';
@Injectable()
export class KakaoAuthService {
private readonly restApiKey: string;
private readonly redirectUri: string;
constructor(
private readonly configService: ConfigService,
private readonly prismaService: PrismaService,
) {}
getCode() {
return `https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${this.restApiKey}&redirect_uri=${this.redirectUri}`;
}
}
- kakao-auth.controller.ts를 통해 인가 코드를 받아온다.
// kakao-auth.controller.ts
import { Controller, Get, Res } from '@nestjs/common';
import { KakaoAuthService } from './kakao-auth.service';
import { Response } from 'express';
@Controller('kakao-auth')
export class KakaoAuthController {
constructor(private readonly kakaoAuthService: KakaoAuthService) {}
@Get()
getKakakoCode(@Res() res: Response) {
const url = this.kakaoAuthService.getCode();
res.redirect(url);
}
@Get('callback')
kakaoCallback() {
return 'callback page';
}
}
- kakao-auth 경로에 직접 접근하면 아래와 같이 로그인 화면이 뜨고
- 로그인하면 자동으로 callback 페이지로 이동하며 uri에 코드가 포함되어 있는 것을 확인할 수 있다.
- 카카오 API 설명 보기 (접은 글)
[3. 로직 구현2 - 토큰 받아오기]
1. 로직 설정
- 서비스 로직에서 url, param, header를 설정해서 조합한 다음, post메서드를 통해 엑세스 토큰을 가져와야 한다.
- url : 카카오 API에 나와있는대로 작성한다
- params : 카카오 API에 나와있는대로 작성한다. client_id는 REST_API 키와 같고 redirect uri는 REDIRECT_URI와 같다.
- header : 카카오 API에 나와있는대로 작성한다.
- response의 데이터 안에 들어있는 엑세스토큰을 반환한다.
// kakao-auth.service.ts
async getKakaoAccessToken(code: string) {
const url = 'https://kauth.kakao.com/oauth/token';
const params = new URLSearchParams({
grant_type: 'authorization_code',
client_id: this.restApiKey,
redirect_id: this.redirectUri,
code,
});
const header = {
'Content-type': 'application/x-www-form-urlencoded;charset=utf-8',
};
const response = await axios.post(url, params, {
headers: header,
});
return response.data.access_token;
}
- 컨트롤러에서 엑세스 토큰을 반환한다.
// kakao-auth.controller.ts
@Get('callback')
async kakaoCallback(@Query('code') code: string, @Res() res: Response) {
const accessToken = await this.kakaoAuthService.getKakaoAccessToken(code);
return res.send({ accessToken }); //화면에 액세스토큰 출력
}
- 화면에 엑세스토큰이 출력된다
- 카카오 API 설명 보기 (접은 글)
[4. 로직구현3 - 회원가입 및 프로필 생성]
1. 카카오 프로필을 가져온다
- controller의 kakaoCallback에 사용자 정보(userInfo)를 가져오는 로직을 추가한다
- 아이디: userInfo.data.id를 통해 호출 가능하다
- 닉네임: userInfo.data.properties.nickname (properties 안에 프로필사진과 썸네일이 들어있음)
// kakao-auth.controller.ts
@Get('callback')
async kakaoCallback(@Query('code') code: string, @Res() res: Response) {
const accessToken = await this.kakaoAuthService.getKakaoAccessToken(code);
const url = 'https://kapi.kakao.com/v2/user/me';
const userInfo = await axios.get(url, {
headers: {
Authorization: `Bearer ${kakaoAccessToken}`,
},
});
return res.send({ accessToken }); //화면에 액세스토큰 출력
}
- 회원가입과 동시에 프로필을 생성하는 로직을 service에 작성한다
// kakao-auth.service.ts
async createUser(kakaoId: number, nickname: string) {
await this.prismaService.user.upsert({
where: { id: kakaoId },
update: {},
create: {
id: kakaoId,
userProfile: {
create: {
nickname: nickname,
},
},
},
include: { userProfile: true },
});
}
- kakaoCallback에서 createUser를 호출하여 액세스토큰 발급과 동시에 회원가입이 가능해지도록 한다
// kakao-auth.controller.ts
@Get('callback')
async kakaoCallback(@Query('code') code: string, @Res() res: Response) {
const accessToken = await this.kakaoAuthService.getKakaoAccessToken(code);
const url = 'https://kapi.kakao.com/v2/user/me';
const userInfo = await axios.get(url, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const newUser = await this.kakaoAuthService.createUser(
userInfo.data.id.toString(),
userInfo.data.properties.nickname,
);
console.log(newUser);
return res.send({ accessToken });
}
- console 결과
- 이로써 카카오를 통한 회원가입, 프로필 호출까지 성공하였습니다
[5. 전체 코드]
1. kakao-auth.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import axios from 'axios';
import { PrismaService } from 'src/database/prisma/prisma.service';
@Injectable()
export class KakaoAuthService {
private readonly restApiKey: string;
private readonly redirectUri: string;
constructor(
private readonly configService: ConfigService,
private readonly prismaService: PrismaService,
) {
this.restApiKey = this.configService.getOrThrow('REST_API_KEY');
this.redirectUri = this.configService.getOrThrow('REDIRECT_URI');
}
getCode() {
return `https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${this.restApiKey}&redirect_uri=${this.redirectUri}`;
}
async getKakaoAccessToken(code: string) {
const url = 'https://kauth.kakao.com/oauth/token';
const params = new URLSearchParams({
grant_type: 'authorization_code',
client_id: this.restApiKey,
redirect_id: this.redirectUri,
code,
});
const header = {
'Content-type': 'application/x-www-form-urlencoded;charset=utf-8',
};
const response = await axios.post(url, params, {
headers: header,
});
return response.data.access_token;
}
async createUser(kakaoId: string, nickname: string) {
return await this.prismaService.user.upsert({
where: { id: kakaoId },
update: {},
create: {
id: kakaoId,
userProfile: {
create: {
nickname: nickname,
},
},
},
include: { userProfile: true },
});
}
}
2. kakao-auth.controller.ts
import { Controller, Get, Post, Query, Res } from '@nestjs/common';
import { KakaoAuthService } from './kakao-auth.service';
import { Response } from 'express';
import axios from 'axios';
@Controller('kakao-auth')
export class KakaoAuthController {
constructor(private readonly kakaoAuthService: KakaoAuthService) {}
@Get()
getKakakoCode(@Res() res: Response) {
const url = this.kakaoAuthService.getCode();
res.redirect(url);
}
@Get('callback')
async kakaoCallback(@Query('code') code: string, @Res() res: Response) {
const accessToken = await this.kakaoAuthService.getKakaoAccessToken(code);
const url = 'https://kapi.kakao.com/v2/user/me';
const userInfo = await axios.get(url, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const newUser = await this.kakaoAuthService.createUser(
userInfo.data.id.toString(),
userInfo.data.properties.nickname,
);
console.log(newUser);
return res.send({ accessToken });
}
}
'Nest.JS' 카테고리의 다른 글
[Nest.js] REST API 방식으로 소셜로그인 구현 샘플 코드 (naver, google, kakao) (2) | 2024.10.16 |
---|---|
[Nest.js] 프로젝트 초기 설정하는 법 (0) | 2024.09.25 |
[Nest.js] 라이프사이클 정리 (0) | 2024.09.08 |
[Nest.js] 페이지네이션 백엔드 구현 (prisma) + 프론트 구현 (0) | 2024.07.04 |
[Nest.js, AWS S3] S3 버켓에 이미지 업로드하기 (1) | 2024.06.13 |