일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 한국은행
- typescript
- react
- next
- javascript
- 경제
- 은행
- 프로젝트 회고
- js
- Next.js
- 내수
- CS
- 기준금리
- 코드잇 스프린트 9기
- 가계대출
- 코드잇
- 가계부채
- 프론트엔드
- 금투세
- 개발자
- FED
- 리엑트 쿼리
- 경제신문읽기
- 미국
- 금리
- 자바스크립트
- 개발
- 금융투자소득세
- API
- 코드잇 스프린트
- Today
- Total
뭉균의 개발일지
[Next.js] Next.js를 이용한 카카오 간편 로그인 구현 본문
🚪들어가며
linkbrary 프로젝트를 진행하며 로그인 및 회원가입 페이지의 디자인과 기능을 구현하는 역할을 맡았습니다.
로그인 및 회원가입 페이지의 기본적인 디자인과 기능 구현을 마무리 한 후, 간편 로그인 / 회원가입 기능을 만들었습니다. 구글과 카카오로 간편 로그인 기능을 구현했는데 이번 포스팅에서는 카카오로 구현하는 방법을 작성했습니다.
🔐 카카오 간편 로그인 과정
저는 애플리케이션 및 앱 키 생성과정은 생략하고 설명을 진행하겠습니다.
자세한 과정은 아래 링크에서 확인하실 수 있습니다 🙂↕️
https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#kakaologin
우선 카카오 소셜 로그인의 흐름을 살펴보겠습니다.
1. 카카오 계정 로그인 후 redirectUri로 인가 코드 받기 (redirectUri를 Next.js로 구축한 서버로 설정)
2. 인가 코드를 이용하여 토큰 받기
3. 카카오 인가 서버로 부터 전달받은 토큰을 활용하여 로그인 완료하기
크게 보면 위와 같은 3가지 과정으로 진행됩니다. 차례대로 자세히 살펴보겠습니다. 앞으로 등장할 코드들은 간편 로그인 기능을 만드는 과정입니다. 회원가입도 비슷하게 구현할 수 있습니다👍
📌 카카오 계정 로그인 후 redirectUri로 인가 코드 받기
먼저, 카카오 소셜 로그인 버튼을 누르면 GET/oauth/authorize 요청을 보내 카카오 계정 인증 과정을 거쳐야 합니다.
GET/oauth/authorize 요청의 양식은 아래와 같습니다.
https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}
카카오 소셜 로그인 버튼을 클릭하면 해당 링크로 들어가 카카오 계정 인증 과정을 거치고, 인증에 성공하면 카카오 인가 서버에서 인가 코드를 저의 redirectUri로 전달해줍니다.
아래 코드는 카카오에 로그인 요청을 보내는 버튼의 코드입니다. client_id와 redirect_uri는 .env.local파일에 따로 보관했습니다.
참고로 client_id는 REST API 키를 사용해주시면 됩니다.
<Link href={`https://kauth.kakao.com/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID}&redirect_uri=${process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI_SIGN_IN}&response_type=code`}>
<Image
src="/icons/kakaotalk.svg"
width="42"
height="42"
alt="카카오톡"
/>
</Link>
또한, next.js의 api 라우팅을 통해 간편 로그인과 간편 회원가입에 대해 각각 다른 서버를 구축하여 사용했고 아래와 같이 작성하여 간편 로그인과 간편 회원가입에서 각각 다른 redirectUri을 사용했습니다.
NEXT_PUBLIC_KAKAO_REDIRECT_URI_SIGN_IN=http://localhost:3000/api/auth/sign-in/kakao
NEXT_PUBLIC_KAKAO_REDIRECT_URI_SIGN_UP=http://localhost:3000/api/auth/sign-up/kakao
다시 말해, 간편 로그인 시에는 인가 코드가 /api/auth/sign-in/kakao로 전달되고 간편 회원가입 시에는 /api/auth/sign-up/kakao으로 전달됩니다.
이 과정에서 주의할 점은 Kakao developers 홈페이지에서도 Redirect URI를 설정해야 합니다.
📌 인가 코드를 이용하여 토큰 받기
(앞으로 등장하는 redirectUri은 /api/auth/sign-in/kakao.ts이고 axiosInstance는 linkbrary서버의 도메인입니다.)
카카오 인증 과정을 통과하면 redirectUri로 인가 코드가 전송됩니다. 이 인가 코드를 이용하여 token을 발급받는 요청을 보내야합니다. 여기서 카카오 인가 서버로 부터 access_token 등을 받아올 수 있습니다. 또한, OpenID Connect 설정 시, id_token도 받아 올 수 있습니다. 코드와 함께 자세히 살펴보겠습니다.
import axiosInstance from "@/lib/api/axiosInstanceApi";
import { NextApiRequest, NextApiResponse } from "next";
import { serialize } from "cookie";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { code } = req.query;
if (!code) {
return res.status(400).json({ message: "인증 코드가 없습니다." });
}
const redirectUri = process.env.KAKAO_REDIRECT_URI || "http://localhost:3000/;
위의 코드와 같이 redirectUri로 설정한 /api/auth/sign-in/kakao.ts 서버로 인가 코드를 받아옵니다.
const tokenUrl = "https://kauth.kakao.com/oauth/token";
const params = new URLSearchParams({
code: code as string,
client_id: clientId,
redirect_uri: redirectUri,
grant_type: "authorization_code",
});
const tokenResponse = await axios.post(tokenUrl, params.toString(), {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
const { id_token } = tokenResponse.data;
if (!id_token) {
return res
.status(401)
.json({ message: "ID 토큰을 가져오지 못했습니다." });
}
...
인가 코드(code), client_id, redirect_uri, grant_type을 request body로 만들어 카카오에서 지정해준 tokenUrl로 POST 요청을 해주면 아래의 객체같은 데이터를 전달 받을 수 있습니다.
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"token_type":"bearer",
"access_token":"${ACCESS_TOKEN}",
"expires_in":43199,
"refresh_token":"${REFRESH_TOKEN}",
"refresh_token_expires_in":5184000,
"scope":"account_email profile"
}
또한, OpenID Connect 활성화 설정을 해주시면 이 객체에 id_token도 담겨서 옵니다.
저는 id_token을 받기 위해 해당 기능을 활성화하였습니다. 이렇게 하면 카카오 인가 서버로 부터 access_token, id_token 등을 불러올 수 있습니다.
📌 카카오 인가 서버로부터 전달받은 토큰을 활용하여 로그인 완료하기
이제 카카오 인가 서버로 부터 받은 토큰을 담아 linkbrary서버로 간편 로그인 요청을 보내야합니다. linkbrary서버로 간편 로그인 요청을 할 때 필요한 request body는 다음과 같습니다.
{
"name": "string",
"token": "string",
"redirectUri": "string"
}
저는 백엔드와 소통할 수 없는 상황에서 SWAGGER문서만 보며 작업을 했기 때문에 여기에 담아야하는 토큰이 access_token인지 id_token인지 알 수 없어서 각각 넣어서 요청을 보내봤지만 요청이 보내지지 않았습니다.
결과적으로, linkbrary서버에서 token에 담아 보내길 원했던 것이 인가 코드임을 알게 되었고 아래와 같이 코드를 수정하여 요청을 보냈습니다. 코드를 보면 아시겠지만 처음에 구현할 때는 간편 로그인을 시도하면 linkbrary에 회원인지 확인하고 회원이 아니라면 간편 회원가입부터하게 구현했었고 이 과정에서 로그인, 회원가입 요청에 각각 token에 인가 코드(code)가 사용되었습니다.
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { code } = req.query;
if (!code) {
return res.status(400).json({ message: "인증 코드가 없습니다." });
}
try {
const loginResponse = await axiosInstance.post("/auth/sign-in/kakao", {
token: code,
redirectUri,
});
...
// 로그인 실패 시 회원가입 시도
try {
const signUpResponse = await axiosInstance.post("/auth/sign-up/kakao", {
name: "사용자",
token: code,
redirectUri,
});
...
이렇게하고 실행해보니 ...
invalid_grant라는 에러 메시지가 왔습니다. 이를 kakao developers에서 원인을 찾아보니 아래와 같이 5가지 경우에 발생할 수 있는 에러였습니다.
그 중 KOE320를 보고 현재 제 코드에서 동일한 인가 코드를 두 번 이상 사용하여 이 에러가 발생함을 깨닫게 되었습니다.
즉, 저는 로그인 요청을 통해 code를 한 번 사용하고 아래 회원가입 요청에서 code를 또 사용했기에 이 에러가 발생했던 것입니다.
이후, 간편 로그인과 간편 회원가입의 서버를 분리하여 redirectUri를 설정해주어 해당 문제를 해결했습니다.
(그래서 시작할 때 보여드린 redirectUri이 2개였습니다😂)
import axiosInstance from "@/lib/api/axiosInstanceApi";
import { NextApiRequest, NextApiResponse } from "next";
import { serialize } from "cookie";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { code } = req.query;
console.log(code);
if (!code) {
return res.status(400).json({ message: "인증 코드가 없습니다." });
}
const redirectUri =
process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI_SIGN_IN ||
"http://localhost:3000/";
try {
const loginResponse = await axiosInstance.post("/auth/sign-in/kakao", {
token: code,
redirectUri,
});
...
이런 식으로 sign-in.ts에서는 library서버로 간편 로그인 POST 요청만 하도록 설정했습니다.
linkbrary서버로 보낸 요청이 성공하면 linkbrary서버에서는 accessToken을 response해주고 이 토큰을 linkbrary브라우저에 저장하면서 간편 로그인 과정은 마무리 됩니다. 아래의 코드와 같이 보안 설정을 하여 브라우저에 저장하면서 간편 로그인 과정을 마무리했습니다.
...
const accessToken = loginResponse.data.access_token;
if (accessToken) {
res.setHeader(
"Set-Cookie",
serialize("accessToken", accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 60 * 60 * 24,
path: "/",
})
);
return res.redirect("http://localhost:3000");
}
} catch (loginError: any) {
console.error(
"로그인 실패:",
loginError.response?.data || loginError.message
);
return res.redirect("signup");
}
} catch (error: any) {
console.error("Error:", error.response?.data || error.message);
return res.status(500).json({
message: "서버 오류",
error: error.response?.data || error.message,
});
}
};
export default handler;
📝 결론
결과적으로, 저는 카카오 인가 서버에서 받은 access_token과 id_token을 사용하지 않았지만, 이를 이용하여 다양한 간편 기능을 구현할 수 있을 것입니다. 또한, 본인의 서비스 서버에서 원하는 token이 이 둘 중 하나일 수도 있기에 인가 서버로 부터 해당 토큰들을 불러오는 과정을 이해하고 알 필요가 있는 것 같습니다.
'Next.js' 카테고리의 다른 글
[Next.js] 카카오 지도 API 사용해보기 (1) | 2024.11.21 |
---|---|
[Next.js] 모달창 만들기(Modal) (2) | 2024.10.22 |
[Next.js] SSR 과정과 hydration에 대하여 (5) | 2024.09.27 |
[Next.js] 리액트만 사용할 때와 비교해 Next.js를 사용하는 이유 (3) | 2024.09.16 |