Clerk 완벽 가이드 | 인증·사용자 관리·OAuth·MFA·Next.js·실전 활용
이 글의 핵심
Clerk로 완벽한 인증 시스템을 구축하는 완벽 가이드입니다. 이메일/비밀번호, OAuth, MFA, 사용자 관리, Next.js 통합까지 실전 예제로 정리했습니다.
실무 경험 공유: 자체 인증 시스템을 Clerk로 전환하면서, 개발 시간이 90% 단축되고 보안이 크게 향상된 경험을 공유합니다.
들어가며: “인증 구현이 복잡해요”
실무 문제 시나리오
시나리오 1: 보안이 걱정돼요
직접 구현은 위험합니다. Clerk는 엔터프라이즈급 보안을 제공합니다.
시나리오 2: OAuth 연동이 어려워요
각 플랫폼마다 다릅니다. Clerk는 통합 API를 제공합니다.
시나리오 3: 사용자 관리가 번거로워요
Admin 패널이 필요합니다. Clerk는 Dashboard를 제공합니다.
1. Clerk란?
핵심 특징
Clerk는 완벽한 인증 및 사용자 관리 플랫폼입니다.
주요 기능:
- 다양한 인증: 이메일, OAuth, Magic Link
- MFA: 2단계 인증
- 사용자 관리: Dashboard
- 조직 관리: Multi-tenancy
- 세션 관리: 자동
2. Next.js 설정
설치
npm install @clerk/nextjs
환경 변수
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
Middleware
// middleware.ts
import { authMiddleware } from '@clerk/nextjs';
export default authMiddleware({
publicRoutes: ['/', '/api/webhook'],
});
export const config = {
matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
};
Provider
// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<ClerkProvider>
<html lang="ko">
<body>{children}</body>
</html>
</ClerkProvider>
);
}
3. 인증 컴포넌트
Sign In / Sign Up
// app/sign-in/[[...sign-in]]/page.tsx
import { SignIn } from '@clerk/nextjs';
export default function SignInPage() {
return (
<div className="flex justify-center items-center min-h-screen">
<SignIn />
</div>
);
}
// app/sign-up/[[...sign-up]]/page.tsx
import { SignUp } from '@clerk/nextjs';
export default function SignUpPage() {
return (
<div className="flex justify-center items-center min-h-screen">
<SignUp />
</div>
);
}
User Button
// components/Header.tsx
import { UserButton, SignedIn, SignedOut, SignInButton } from '@clerk/nextjs';
export default function Header() {
return (
<header>
<SignedIn>
<UserButton afterSignOutUrl="/" />
</SignedIn>
<SignedOut>
<SignInButton mode="modal">
<button>Sign In</button>
</SignInButton>
</SignedOut>
</header>
);
}
4. 보호된 페이지
클라이언트 컴포넌트
'use client';
import { useUser } from '@clerk/nextjs';
import { redirect } from 'next/navigation';
export default function DashboardPage() {
const { isLoaded, isSignedIn, user } = useUser();
if (!isLoaded) return <div>Loading...</div>;
if (!isSignedIn) return redirect('/sign-in');
return (
<div>
<h1>Welcome, {user.firstName}!</h1>
</div>
);
}
서버 컴포넌트
import { currentUser } from '@clerk/nextjs';
import { redirect } from 'next/navigation';
export default async function DashboardPage() {
const user = await currentUser();
if (!user) {
redirect('/sign-in');
}
return (
<div>
<h1>Welcome, {user.firstName}!</h1>
</div>
);
}
5. API 보호
API Route
// app/api/protected/route.ts
import { auth } from '@clerk/nextjs';
import { NextResponse } from 'next/server';
export async function GET() {
const { userId } = auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const data = await getProtectedData(userId);
return NextResponse.json(data);
}
6. Webhook
설정
// app/api/webhook/route.ts
import { Webhook } from 'svix';
import { headers } from 'next/headers';
export async function POST(req: Request) {
const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET!;
const headerPayload = headers();
const svixId = headerPayload.get('svix-id');
const svixTimestamp = headerPayload.get('svix-timestamp');
const svixSignature = headerPayload.get('svix-signature');
const body = await req.text();
const wh = new Webhook(WEBHOOK_SECRET);
let evt;
try {
evt = wh.verify(body, {
'svix-id': svixId!,
'svix-timestamp': svixTimestamp!,
'svix-signature': svixSignature!,
});
} catch (err) {
return new Response('Webhook verification failed', { status: 400 });
}
const { type, data } = evt;
switch (type) {
case 'user.created':
await createUserInDatabase(data);
break;
case 'user.updated':
await updateUserInDatabase(data);
break;
case 'user.deleted':
await deleteUserFromDatabase(data);
break;
}
return new Response('Webhook received', { status: 200 });
}
7. 조직 관리
조직 생성
import { OrganizationSwitcher, OrganizationProfile } from '@clerk/nextjs';
export default function OrganizationPage() {
return (
<div>
<OrganizationSwitcher />
<OrganizationProfile />
</div>
);
}
권한 확인
import { auth } from '@clerk/nextjs';
export default async function AdminPage() {
const { userId, orgRole } = auth();
if (orgRole !== 'admin') {
return <div>Access Denied</div>;
}
return <div>Admin Panel</div>;
}
정리 및 체크리스트
핵심 요약
- Clerk: 인증 및 사용자 관리
- 다양한 인증: 이메일, OAuth, Magic Link
- MFA: 2단계 인증
- 사용자 관리: Dashboard
- 조직 관리: Multi-tenancy
- Next.js: 완벽한 통합
구현 체크리스트
- Clerk 계정 생성
- SDK 설치
- Middleware 설정
- 인증 컴포넌트 추가
- 보호된 페이지 구현
- API 보호
- Webhook 설정
같이 보면 좋은 글
- Supabase 완벽 가이드
- Next.js App Router 가이드
- tRPC 완벽 가이드
이 글에서 다루는 키워드
Clerk, Authentication, OAuth, MFA, User Management, Next.js, Backend
자주 묻는 질문 (FAQ)
Q. Auth0와 비교하면 어떤가요?
A. Clerk가 DX가 더 좋고 Next.js 통합이 완벽합니다. Auth0는 더 많은 기능을 제공합니다.
Q. 무료로 사용할 수 있나요?
A. 네, 10K MAU까지 무료입니다.
Q. 커스터마이징이 가능한가요?
A. 네, 컴포넌트 스타일링과 로직을 커스터마이징할 수 있습니다.
Q. 프로덕션에서 사용해도 되나요?
A. 네, 많은 스타트업과 기업에서 안정적으로 사용하고 있습니다.