본문 바로가기

Frontend/Next.js

[Next.js] 카카오 소셜 로그인 구현하기

728x90
반응형

카카오 소셜 로그인을 구현하기 위해서 먼저 kakao developers에서 설정을 해주어야 한다.

 

먼저, 내 애플리케이션 > 애플리케이션 추가하기를 클릭하고, 앱 이름과 사업자 정보를 입력하여 애플리케이션 생성한다.

생성된 애플리케이션에 들어가서, 앱 설정 > 플랫폼에서 웹 플랫폼을 추가한다.

 

다음으로, 앱 설정 > 카카오 로그인에 들어가서 활성화 설정을 ON으로 하고, http://localhost:3000/api/auth/callback/kakao를 Redirect URI로 추가한다. ( 프로덕션 환경에서는 프로덕션 URL을 사용 )

 

.env 파일을 만들어준다. .env 파일에는 앞에서 추가한 REDIRECT URI와 REST API KEY를 설정해주어야 한다. REST API KEY는 앱 설정 > 앱 키에서 확인할 수 있다.

KAKAO_REST_API_KEY={본인의 키 확인해서 복붙!}
KAKAO_REDIRECT_URI=http://localhost:3000/api/auth/kakao/callback

 

 

이제 코드를 작성할건데, 폴더 구조는 다음과 같다.

src/
  ├── app/
  │   ├── layout.tsx          # RootLayout
  │   ├── page.tsx            # 로그인 버튼 포함 메인 페이지
  │   ├── mybag/
  │   │   └── page.tsx        # 사용자 정보를 보여주는 페이지
  ├── pages/
  │   └── api/
  │       └── auth/
  │           ├── kakao/
  │           │   ├── login.ts      # 카카오 인증 요청
  │           │   └── callback.ts   # 카카오 인증 후 처리

 

참고로, App Router(app/ 폴더)는 API Routes를 지원하지 않기 때문에 API 라우트는 반드시 src/pages/api 아래에 위치해야 한다. src/api 아래에 있으면 라우팅이 제대로 동작하지 않는다.

 

 

사용자가 "카카오로 시작하기" 버튼을 누르면 카카오 로그인 페이지로 리다이렉트된다.

// page.tsx
const handleKakaoLogin = () => {
  window.location.href = "/api/auth/kakao/login"; // 서버에서 카카오 인증 URL 생성
};

...
<button
    onClick={handleKakaoLogin}
    className="font-nanum bg-[#F9E000] text-[#333B58] py-3 rounded-md w-full text-center flex flex-row items-center justify-center gap-3 absolute bottom-5 left-0 right-0 mx-auto max-w-[calc(100%-2rem)] text-[16px] font-bold"
>
    <IoChatbubbleSharp />
    카카오로 시작하기
</button>

 

카카오로 인증 요청을 보내기 위해 서버에서 OAuth 인증 URL을 생성한다.

// login.ts

const kakaoAuthURL = `https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${KAKAO_REST_API_KEY}&redirect_uri=${KAKAO_REDIRECT_URI}`;
res.redirect(kakaoAuthURL);

 

사용자가 카카오 로그인 페이지에서 인증을 완료하면, 카카오는 Redirect URI로 인증 코드를 전달한다.

아래 코드에서 code에 인증 코드가 저장되고, 이 코드는 Access Token을 요청하는 데 사용된다.

// callback.ts

export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
  const { code } = req.query;

  if (!code) {
    res.status(400).json({ error: 'Authorization code not found' });
    return;
  }

  try {
    const tokenResponse = await axios.post<TokenResponse>(
      'https://kauth.kakao.com/oauth/token',
      new URLSearchParams({
        grant_type: 'authorization_code',
        client_id: process.env.KAKAO_REST_API_KEY || '',
        redirect_uri: process.env.KAKAO_REDIRECT_URI || '',
        code: code as string,
      }),
      { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
    );

    const { access_token } = tokenResponse.data;

    const userResponse = await axios.get<KakaoUser>('https://kapi.kakao.com/v2/user/me', {
      headers: {
        Authorization: `Bearer ${access_token}`,
      },
    });

    const userData = userResponse.data;

    res.redirect(`/mybag?user=${encodeURIComponent(JSON.stringify(userData))}`);
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: 'Failed to fetch access token or user info' });
  }
}

 

 

728x90
반응형