블로그 목록
인증

2026 카카오 로그인 Next.js 연동 가이드 (Supabase Auth)

Layered Labs|

한국 SaaS에서 가장 높은 전환율을 보이는 로그인 방식은 단연 카카오 로그인입니다. 카카오톡 MAU 4,800만 시대에, 이메일/비밀번호 대신 카카오 버튼 하나로 가입 허들을 낮출 수 있습니다.

이 글에서는 Next.js App Router + Supabase Auth를 활용해서 카카오 앱 등록 → OAuth 설정 → 로그인 구현 → 콜백 처리 → 미들웨어 인증 보호까지 실제 프로덕션 코드로 보여드립니다.

Supabase Auth를 쓰면 JWT 관리, 세션 갱신, PKCE 플로우를 직접 구현할 필요가 없습니다. 카카오 디벨로퍼스에서 앱을 등록하고, Supabase 대시보드에서 Client ID/Secret만 넣으면 끝입니다.


1단계: 카카오 디벨로퍼스에서 앱 등록

먼저 카카오 디벨로퍼스에 접속해서 애플리케이션을 등록합니다.

  1. 내 애플리케이션 → 애플리케이션 추가하기를 클릭
  2. 앱 이름, 사업자 정보를 입력하고 저장
  3. 앱 키 탭에서 REST API 키를 복사 (이것이 Client ID)
  4. 보안 탭에서 Client Secret 코드를 생성하고 복사

그 다음, 카카오 로그인 설정을 합니다:

  1. 카카오 로그인 → 활성화 설정을 ON으로 변경
  2. Redirect URI에 Supabase 콜백 URL을 등록:
https://<your-project-ref>.supabase.co/auth/v1/callback

Redirect URI를 정확히 입력하세요. 끝에 슬래시(/)가 있으면 안 됩니다. Supabase 대시보드 → Authentication → Providers → Kakao에서 정확한 URL을 확인할 수 있습니다.

마지막으로 동의항목을 설정합니다:

  • 닉네임 — 필수 동의 (프로필 표시용)
  • 카카오계정(이메일) — 선택 동의 (로그인 식별용, 비즈앱 전환 시 필수 가능)
  • 프로필 이미지 — 선택 동의 (아바타 표시용)

비즈 앱 전환을 권장합니다. 개인 앱은 이메일 동의항목을 "선택"으로만 설정할 수 있어서, 사용자가 이메일 제공을 거부할 수 있습니다. 비즈 앱으로 전환하면 이메일을 "필수"로 받을 수 있어 사용자 식별이 확실해집니다.


2단계: Supabase에서 카카오 OAuth 설정

Supabase 대시보드에서 카카오 프로바이더를 활성화합니다:

  1. Authentication → Providers → Kakao로 이동
  2. Enable Kakao provider를 ON
  3. Client ID에 카카오 REST API 키 입력
  4. Client Secret에 카카오 Client Secret 입력
  5. 저장

그리고 .env.local에 Supabase 환경변수를 설정합니다:

.env.local
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://<your-project-ref>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...your-anon-key

이 두 값은 Supabase 대시보드 → Settings → API에서 확인할 수 있습니다. NEXT_PUBLIC_ 접두사가 붙어야 브라우저에서 접근 가능합니다.


3단계: Supabase 클라이언트 만들기

먼저 브라우저에서 사용할 Supabase 클라이언트를 만듭니다. @supabase/ssr 패키지의 createBrowserClient를 사용합니다:

src/lib/supabase/client.ts
import { createBrowserClient } from "@supabase/ssr";

export function createSupabaseBrowserClient() {
  const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
  const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

  if (!url || !key) {
    throw new Error(
      "NEXT_PUBLIC_SUPABASE_URL과 NEXT_PUBLIC_SUPABASE_ANON_KEY를 " +
      ".env.local에 설정하세요."
    );
  }

  return createBrowserClient(url, key);
}

핵심 포인트:

  • 환경변수 검증 — 클라이언트 생성 전에 반드시 환경변수가 있는지 확인합니다. 없으면 명확한 에러 메시지로 빠르게 디버깅할 수 있습니다.
  • @supabase/ssr — Next.js App Router에서는 @supabase/supabase-js createClient 대신 SSR 전용 패키지를 사용해야 쿠키 기반 세션이 올바르게 동작합니다.

4단계: signInWithOAuth로 카카오 로그인 구현

이제 실제 로그인 페이지를 만듭니다. Supabase의 signInWithOAuth 하나로 카카오 로그인이 완성됩니다:

src/app/(auth)/login/page.tsx
"use client";

import { useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { createSupabaseBrowserClient } from "@/lib/supabase/client";

export default function LoginPage() {
  const router = useRouter();
  const [error, setError] = useState("");

  const supabase = createSupabaseBrowserClient();

  const handleOAuthLogin = async (
    provider: "kakao" | "google"
  ) => {
    setError("");
    const { error: authError } =
      await supabase.auth.signInWithOAuth({
        provider,
        options: {
          redirectTo:
            window.location.origin + "/auth/callback",
        },
      });

    if (authError) {
      setError(authError.message);
    }
  };

  return (
    <div className="flex min-h-screen items-center justify-center">
      <div className="w-full max-w-sm">
        <button
          type="button"
          onClick={() => handleOAuthLogin("kakao")}
          className="w-full rounded-lg bg-[#FEE500] px-4 py-2.5
                     text-sm font-medium text-[#191919]"
        >
          카카오로 로그인
        </button>

        <button
          type="button"
          onClick={() => handleOAuthLogin("google")}
          className="mt-3 w-full rounded-lg border
                     border-gray-300 bg-white px-4 py-2.5
                     text-sm font-medium text-gray-700"
        >
          구글로 로그인
        </button>

        {error && (
          <p className="mt-4 text-sm text-red-600">
            {error}
          </p>
        )}
      </div>
    </div>
  );
}

코드에서 주목할 부분:

  • signInWithOAuth — provider에 "kakao"를 넘기면 Supabase가 자동으로 카카오 OAuth 플로우를 시작합니다. 카카오 로그인 팝업이 뜨고, 사용자가 동의하면 redirectTo로 돌아옵니다.
  • redirectTo window.location.origin + "/auth/callback"을 지정합니다. 이 URL에서 인증 코드를 세션으로 교환합니다.
  • 카카오 노란색 — 카카오 브랜드 가이드라인에 따라 배경색 #FEE500, 글자색 #191919를 사용합니다.

Google 로그인도 같은 코드입니다. provider만 "google"로 바꾸면 됩니다. Supabase Auth의 장점은 프로바이더 추가가 코드 변경 없이 대시보드 설정만으로 가능하다는 점입니다. Apple, GitHub, Naver도 같은 패턴입니다.


5단계: Auth Callback 라우트 처리

카카오 로그인 후 Supabase가 /auth/callback으로 리다이렉트합니다. 여기서 인증 코드를 세션으로 교환해야 합니다:

src/app/auth/callback/route.ts
import { NextResponse } from "next/server";
import { createSupabaseServerClient } from "@/lib/supabase/server";

export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url);
  const code = searchParams.get("code");
  const next = searchParams.get("next") ?? "/dashboard";

  // Open Redirect 방지
  const safePath =
    next.startsWith("/") && !next.startsWith("//")
      ? next
      : "/dashboard";

  if (code) {
    const supabase = await createSupabaseServerClient();
    const { error } =
      await supabase.auth.exchangeCodeForSession(code);
    if (!error) {
      return NextResponse.redirect(
        `${origin}${safePath}`
      );
    }
  }

  // 인증 실패 시 로그인 페이지로
  return NextResponse.redirect(`${origin}/login`);
}

이 라우트가 하는 일:

  1. URL에서 code 파라미터를 추출합니다. Supabase가 카카오에서 받은 인증 코드를 여기에 담아줍니다.
  2. exchangeCodeForSession으로 코드를 JWT 세션으로 교환합니다. 이 과정에서 Supabase가 카카오 Access Token을 받아오고, auth.users 테이블에 사용자를 생성하거나 업데이트합니다.
  3. 성공하면 /dashboard로, 실패하면 /login으로 리다이렉트합니다.

Open Redirect 취약점에 주의하세요.

next 파라미터를 그대로 리다이렉트에 사용하면, 공격자가 ?next=https://evil.com으로 사용자를 외부 사이트로 보낼 수 있습니다. 반드시 /로 시작하고 //로 시작하지 않는지 검증하세요.


6단계: 미들웨어로 인증 보호

로그인 후 보호가 필요한 페이지(대시보드, 설정 등)에 미인증 사용자가 접근하지 못하도록 미들웨어에서 처리합니다:

src/lib/supabase/middleware.ts
import { createServerClient } from "@supabase/ssr";
import {
  NextResponse,
  type NextRequest,
} from "next/server";

/** 인증 불필요한 공개 라우트 */
const PUBLIC_ROUTES = [
  "/",
  "/login",
  "/signup",
  "/auth",
  "/api",
  "/terms",
  "/privacy",
  "/refund",
  "/pricing",
  "/thank-you",
];

function isPublicRoute(pathname: string): boolean {
  return PUBLIC_ROUTES.some(
    (route) =>
      pathname === route ||
      pathname.startsWith(`${route}/`)
  ) ||
    pathname === "/robots.txt" ||
    pathname === "/sitemap.xml";
}

export async function updateSession(
  request: NextRequest
) {
  const supabaseUrl =
    process.env.NEXT_PUBLIC_SUPABASE_URL;
  const supabaseAnonKey =
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

  // 환경변수 미설정 시 스킵 (초기 배포/데모)
  if (!supabaseUrl || !supabaseAnonKey) {
    return NextResponse.next({ request });
  }

  let supabaseResponse = NextResponse.next({ request });

  const supabase = createServerClient(
    supabaseUrl,
    supabaseAnonKey,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll();
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value }) =>
            request.cookies.set(name, value)
          );
          supabaseResponse = NextResponse.next({
            request,
          });
          cookiesToSet.forEach(
            ({ name, value, options }) =>
              supabaseResponse.cookies.set(
                name,
                value,
                options
              )
          );
        },
      },
    }
  );

  const {
    data: { user },
  } = await supabase.auth.getUser();

  const pathname = request.nextUrl.pathname;

  // 미인증 → 로그인 페이지로 리다이렉트
  if (!isPublicRoute(pathname) && !user) {
    const url = request.nextUrl.clone();
    url.pathname = "/login";
    url.searchParams.set("redirectTo", pathname);
    return NextResponse.redirect(url);
  }

  // 이미 로그인한 사용자 → 대시보드로
  if (
    user &&
    (pathname === "/login" || pathname === "/signup")
  ) {
    const url = request.nextUrl.clone();
    url.pathname = "/dashboard";
    return NextResponse.redirect(url);
  }

  return supabaseResponse;
}

미들웨어의 핵심 로직:

  • PUBLIC_ROUTES 화이트리스트 — 인증 없이 접근 가능한 경로를 명시적으로 정의합니다. 새 공개 페이지를 추가할 때마다 여기에 등록하세요.
  • 쿠키 기반 세션 — Supabase SSR 패키지가 요청/응답의 쿠키를 자동으로 관리합니다. 미들웨어에서 getUser()를 호출하면 쿠키에서 세션을 읽어 사용자 인증 여부를 확인합니다.
  • 리다이렉트 체인 — 미인증 사용자는 /login?redirectTo=/dashboard처럼 원래 가려던 경로를 쿼리 파라미터로 보존합니다. 로그인 성공 후 원래 페이지로 돌아갈 수 있습니다.
  • 환경변수 미설정 시 스킵 — Supabase를 아직 연결하지 않은 초기 배포 상태에서도 앱이 정상 동작합니다.

이 미들웨어를 src/middleware.ts에서 호출합니다:

src/middleware.ts
import { type NextRequest } from "next/server";
import { updateSession } from "@/lib/supabase/middleware";

export async function middleware(request: NextRequest) {
  return await updateSession(request);
}

export const config = {
  matcher: [
    // 정적 파일과 _next 제외
    "/((?!_next/static|_next/image|favicon.ico|.*\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
  ],
};

프로덕션 체크리스트

개발 환경에서 잘 되는데 프로덕션에서 안 되는 경우가 많습니다. 배포 전에 반드시 확인하세요:

  • 카카오 Redirect URI: 프로덕션 도메인의 Supabase 콜백 URL이 카카오 디벨로퍼스에 등록되어 있는지 확인. 개발용 localhost와 프로덕션 URL 두 개 다 등록하세요.
  • 비즈 앱 전환: 개인 앱은 하루 카카오 로그인 요청 제한이 있습니다. 서비스 출시 전에 비즈 앱으로 전환하세요.
  • 이메일 필수 동의: 비즈 앱 전환 후 카카오계정(이메일) 동의항목을 "필수"로 변경. 이메일 없이 가입된 사용자를 처리하는 로직이 없다면 반드시 필수로 설정하세요.
  • Supabase Site URL: Supabase 대시보드 → Authentication → URL Configuration에서 Site URL을 프로덕션 도메인으로 설정. 이걸 빠뜨리면 콜백 후 localhost로 리다이렉트됩니다.
  • Redirect URLs 화이트리스트: 같은 설정 페이지에서 Redirect URLs에 프로덕션 도메인 https://yourdomain.com/**을 추가하세요.
  • 에러 모니터링: 카카오 로그인 실패를 Sentry 등으로 모니터링. 토큰 만료, 네트워크 오류, 동의 철회 등 다양한 실패 케이스가 있습니다.
  • 로그아웃 시 카카오 연결 해제: 사용자가 서비스를 탈퇴하면 카카오 연결 해제 API도 호출해야 합니다. 안 하면 사용자의 카카오 설정에 앱이 계속 남아있습니다.

전체 파일 구조 정리

src/
├── lib/
│   └── supabase/
│       ├── client.ts          # 브라우저용 Supabase 클라이언트
│       ├── server.ts          # 서버용 Supabase 클라이언트
│       └── middleware.ts      # 인증 미들웨어 로직
├── app/
│   ├── (auth)/
│   │   └── login/
│   │       └── page.tsx       # 카카오/구글 로그인 페이지
│   ├── auth/
│   │   └── callback/
│   │       └── route.ts       # OAuth 콜백 처리
│   └── middleware.ts          # Next.js 미들웨어 진입점
└── .env.local                 # Supabase 환경변수

마무리

여기까지 따라오셨다면 카카오 로그인 + Supabase Auth + Next.js App Router로 인증 시스템의 전체 흐름을 이해하셨을 겁니다. 핵심을 정리하면:

  • 카카오 디벨로퍼스에서 앱을 등록하고, Redirect URI를 정확히 설정할 것
  • signInWithOAuth 한 줄이면 카카오 로그인이 완성되지만, 콜백 라우트와 미들웨어가 반드시 있어야 세션이 유지됨
  • 프로덕션 배포 전에 비즈 앱 전환, Site URL 설정, 이메일 필수 동의를 꼭 확인할 것

하지만 인증은 SaaS의 시작일 뿐입니다. 실제로는 결제 연동(토스페이먼츠), DB 설계, API 라우팅, 관리자 대시보드, 이메일 발송, 알림톡 등등 해야 할 것들이 산더미입니다.

이 글에서 보여드린 코드는 실제로 한국형 SaaS 보일러플레이트를 만들면서 작성한 코드입니다. 카카오 로그인뿐 아니라, 토스페이먼츠 결제, Supabase, tRPC, Drizzle ORM, 알림톡까지 모두 포함된 Next.js 스타터킷이 있습니다.

카카오 로그인 + 토스 결제가 모두 포함된 한국형 SaaS 보일러플레이트

Supabase Auth + 토스페이먼츠 + tRPC + 알림톡 — 설정 없이 바로 시작

가격 보기
Written by 레이어드 랩스 | 한국 개발자를 위한 SaaS 인프라