next.js로 “카카오 로그인, 카카오 카카오 친구목록 조회, 메시지 발송” 테스트 해볼 수 있는 간단한 예제입니다.
[실행방법]
Git clone 후, npm install 합니다.
git clone https://github.com/kakao-tam/-Example-next.js.git
npm install
- 내 애플리케이션 > 앱 설정 > 요약 정보 > "REST API 키"를 복사해서 .env.local 파일 CLIENT_ID 항목에 설정합니다.
- 내 애플리케이션>제품 설정>카카오 로그인 > Redirect URI에 http://localhost:3000/redirect 주소를 설정합니다.
- npm run dev 로 실행합니다.
[실행결과]
/next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
images: {
remotePatterns: [
{
protocol: "https",
hostname: "k.kakaocdn.net",
pathname: "/14/dn/btqCn0WEmI3/nijroPfbpCa4at5EIsjyf0/o.jpg",
}
]
},
};
export default nextConfig;
/.env.local
CLIENT_ID=your_kakao_developers_rest_api_key
CLIENT_SECRET=your_kakao_client_secret
REDIRECT_URI=http://localhost:3000/redirect
KAUTH_HOST=https://kauth.kakao.com
KAPI_HOST=https://kapi.kakao.com
SECRET_COOKIE_PASSWORD=your_secret_cookie_password_must_be_32_characters
DOMAIN=localhost
/app/redirect/route.ts
import {getAccessToken, setSessionAccessToken} from "@/app/kakao";
import {redirect} from "next/navigation";
import {NextRequest} from "next/server";
export async function GET(req:NextRequest) {
const code = req.nextUrl.searchParams.get('code') || '';
const access_token = await getAccessToken(code);
await setSessionAccessToken(access_token);
redirect('/');
}
/app/kakao.ts
'use server';
import {redirect} from "next/navigation";
import {cookies} from "next/headers";
import {getIronSession} from "iron-session";
export type SessionData = {
token: string
}
export async function sessionOptions() {
return {
password: process.env.SECRET_COOKIE_PASSWORD || '',
cookieName: 'exampleCookieName',
cookieOptions: {
maxAge: 60 * 60 * 2, // 2h
secure: process.env.PHASE === 'production',
domain: process.env.DOMAIN
}
}
}
export async function authorize(scope?: string) {
let scope_param = '';
if (scope) {
scope_param = `&scope=${scope}`;
}
redirect(`${process.env.KAUTH_HOST}/oauth/authorize?client_id=${process.env.CLIENT_ID}&redirect_uri=${process.env.REDIRECT_URI}&response_type=code${scope_param}&client_secret=${process.env.CLIENT_SECRET}`);
}
export async function getAccessToken(code: string) {
const response = await fetch(`${process.env.KAUTH_HOST}/oauth/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `client_id=${process.env.CLIENT_ID}&client_secret=${process.env.CLIENT_SECRET}&code=${code}&grant_type=authorization_code&redirect_uri=${process.env.REDIRECT_URI}`,
});
const json = await response.json();
return json?.access_token.toString();
}
export async function setSessionAccessToken(access_token: string) {
const session = await getIronSession<SessionData>(
await cookies(),
await sessionOptions()
)
session.token = access_token
await session.save()
}
export async function getSessionAccessToken() {
const session = await getIronSession<SessionData>(
await cookies(),
await sessionOptions()
)
return session.token;
}
export async function profile() {
const access_token = await getSessionAccessToken();
const response = await fetch(`${process.env.KAPI_HOST}/v2/user/me`, {
headers: {
'Authorization': `Bearer ${access_token}`
}
});
return JSON.stringify(await response.json());
}
export async function friends() {
const access_token = await getSessionAccessToken();
const response = await fetch(`${process.env.KAPI_HOST}/v1/api/talk/friends`, {
headers: {
'Authorization': `Bearer ${access_token}`
}
});
return JSON.stringify(await response.json());
}
export async function message() {
const access_token = await getSessionAccessToken();
const param = 'template_object={"object_type":"text","text":"Hello, world!","link":{"web_url":"https://developers.kakao.com","mobile_web_url":"https://developers.kakao.com"}}'
const response = await fetch(`${process.env.KAPI_HOST}/v2/api/talk/memo/default/send`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: param
});
return JSON.stringify(await response.json());
}
export async function friends_message(uuids: string) {
const access_token = await getSessionAccessToken();
const uuid_param = encodeURIComponent(`${uuids}`);
const param = `receiver_uuids=[${uuid_param}]&template_object={"object_type":"text","text":"Hello, world!","link":{"web_url":"https://developers.kakao.com","mobile_web_url":"https://developers.kakao.com"}}`
const response = await fetch(`${process.env.KAPI_HOST}/v1/api/talk/friends/message/default/send`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: param
});
return JSON.stringify(await response.json());
}
export async function logout() {
const access_token = await getSessionAccessToken();
const response = await fetch(`${process.env.KAPI_HOST}/v1/user/logout`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`
}
});
return JSON.stringify(await response.json());
}
export async function unlink() {
const access_token = await getSessionAccessToken();
const response = await fetch(`${process.env.KAPI_HOST}/v1/user/unlink`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`
}
});
return JSON.stringify(await response.json());
}
/app/page.tsx
'use client';
import {useState} from "react";
import Image from "next/image";
import {authorize, friends, friends_message, logout, message, profile, unlink} from "@/app/kakao";
export default function Home() {
const [data, setData] = useState('');
const [uuid, setUuid] = useState('');
return (
<div className="grid items-center justify-items-center gap-5 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
<p>카카오 로그인 및 프로필 조회 예제</p>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
[KOE101, KOE004] 내 애플리케이션 > 제품 설정 > 카카오 로그인 > 활성화 설정 : ON
</li>
<li>
[KOE006] 내 애플리케이션 > 제품 설정 > 카카오 로그인 > Redirect URI : http://localhost:3000/redirect
</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<button onClick={() => authorize()}
className="flex items-center justify-center text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
>
<Image
src={"https://k.kakaocdn.net/14/dn/btqCn0WEmI3/nijroPfbpCa4at5EIsjyf0/o.jpg"}
width={222}
height={49}
alt="kakao login"
/>
</button>
>
<button onClick={() => authorize('friends,talk_message')}
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
>
추가 항목 동의 받기 - 친구목록 조회와 메세지 발송 권한 획득
</button>
</div>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<textarea
className="bg-gray-200 rounded-lg shadow border p-2"
rows={10}
cols={80}
id="contents"
readOnly={true}
value={data}
>
</textarea>
</div>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<button onClick={async () => setData(await profile())}
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
>
사용자 정보 가져오기
</button>
</div>
<ol start={3}
className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li>
친구 목록 조회로 UUID 가져와 메시지 보내기 : 친구도 동의 해야 조회 가능
</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<button onClick={async () => setData(await friends())}
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
>
친구 목록 가져오기
</button>
<button onClick={async () => setData(await message())}
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
>
기본 템플릿으로 메시지 보내기 - 나에게 보내기
</button>
</div>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<input onChange={(e) => setUuid(e.target.value)}
type="text" value={uuid}
placeholder="UUID 입력 ex) "AAA","BBB" 쌍따옴표 포함"
className="bg-white rounded-lg shadow border p-2 w-80"
/>
<button onClick={async () => setData(await friends_message(uuid))}
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
>
기본 템플릿으로 메시지 보내기 - 친구에게 보내기
</button>
</div>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<button onClick={async () => setData(await logout())}
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
>
로그 아웃
</button>
<button onClick={async () => setData(await unlink())}
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
>
연결 끊기
</button>
</div>
</main>
</div>
);
}
로그인에 관한 가이드 : REST API | Kakao Developers REST API
친구목록 관한 가이드 : REST API | Kakao Developers REST API
메시지에 관한 가이드 : 카카오톡 메시지: REST API | Kakao Developers 카카오톡 메시지: REST API
KOE006 에러 : Koe006 에러가 발생할 때
친구 api, 메시지 api 사용을 위한 체크 리스트 : 친구 api와 피커, 메시지 api 사용을 위한 체크 리스트
친구목록, 메시지 API 자주 겪는 에러 : [faq] 친구 목록 api, 메시지 전송 api를 연동하는 과정에서 자주 겪는 에러
메시지 API 권한 신청 방법 : '메시지 API' 사용 신청 방법 / How to request permission for 'Messaging API'
'언어, 환경별 예제 코드' 카테고리의 다른 글
[rest api 예제] node.js- 카카오 로그인, 카카오 친구목록 조회, 메시지 발송 (0) | 2025.02.26 |
---|---|
[rest api 예제] python (Flask) - 카카오 로그인, 카카오 친구목록 조회, 메시지 발송 (0) | 2025.02.26 |
Java에서 API 호출 후, Error Body 조회하는 방법 (0) | 2024.10.17 |
iOS objective-c Kakao SDK 지원 종료에 따른 임시 처리 방안 (1) | 2023.09.19 |
Android 카카오 SDK 사용, 공식 예제 실행해보기 (0) | 2022.11.17 |
댓글