목차
- Before/After — 자체 구현 1,200줄에서 NextAuth 280줄로
- 비교 기준 다섯 가지 — 한국 프로바이더부터 본다
- 항목별 비교 — NextAuth와 Passport는 결이 다르다
- 카카오·네이버 프로바이더 등록 시 주의점
- OAuth 콜백 보안 처리 — state, PKCE, redirect_uri
- 벤치마크와 운영 후 체감
- 결론과 한계
Before/After — 자체 구현 1,200줄에서 NextAuth 280줄로
소셜 로그인 구현 Next.js 환경에서 가장 큰 고민은 NextAuth.js와 Passport.js 사이 선택이다. 자체 구현을 유지하던 서비스에서 카카오·구글·네이버를 한 번에 붙여야 했고, 두 라이브러리를 모두 검토한 결과를 정리한다.
변경 전 상태는 이렇다. Next.js 13 App Router 위에 이메일+비밀번호 로그인을 직접 짰다. bcrypt 해싱, JWT 발급, refresh 토큰 회전, 비밀번호 잠금 정책, 메일 인증, 비밀번호 재설정까지. 인증 관련 코드만 약 1,200줄. 라이브러리는 jose, bcryptjs, nodemailer 조합이다. 보안 이슈 리포트가 올라올 때마다 직접 패치해야 했다.
반면, 변경 후 상태는 NextAuth.js v5(Auth.js로 리브랜딩)로 정리했다. 카카오·구글·네이버 + 이메일 매직링크 4개 채널. 인증 관련 코드 약 280줄. 콜백 라우트는 라이브러리가 처리하고, 세션은 JWT 기본, 민감 액션은 DB 세션으로 분리했다.
그러나, 코드량만 보면 NextAuth 압승처럼 보인다. 다만 Passport.js를 Express에서 쓰던 팀이라면 이야기가 다르다. 항목별 비교 기준을 먼저 정리한다.
비교 기준 다섯 가지 — 한국 프로바이더부터 본다
비교 기준은 다섯 개로 좁혔다.
| 기준 | 왜 중요한가 |
|---|---|
| 한국 프로바이더 지원 | 카카오·네이버는 OAuth 2.0 표준에서 살짝 벗어난다 |
| 보안 기본값 | state, PKCE, nonce 검증을 라이브러리가 알아서 하는지 |
| 콜백 처리 비용 | 라우트 작성, 리다이렉트 처리, 에러 핸들링 |
| 세션 전략 유연성 | JWT ↔ DB 세션 전환, 토큰 회전 |
| Next.js App Router 통합 | Server Component, Middleware에서 세션 접근 |
이 다섯 가지는 결국 "얼마나 적게 짜고 얼마나 안전하게 가져갈 수 있느냐"로 수렴한다. 자체 구현이라는 선택지를 후보에서 일찍 제거한 이유도 여기 있다. OAuth 2.0 + OIDC + PKCE + state 검증을 직접 짜면 검토 비용이 너무 크다. 5년차 기준으로 봐도 이건 라이브러리 위에서 가는 쪽이 합리적이다.
항목별 비교 — NextAuth와 Passport는 결이 다르다
프로바이더 지원과 한국 OAuth
그러나, NextAuth.js v5는 80개 이상의 프로바이더가 코어에 들어 있다. 구글·깃허브·디스코드 같은 글로벌 서비스는 한 줄 설정으로 끝난다. 카카오와 네이버도 공식 프로바이더로 포함되어 있다 (next-auth/providers/kakao, next-auth/providers/naver). 작성 시점 기준 Auth.js 공식 문서에서 확인 가능하다.
Passport.js는 전략(Strategy) 단위 플러그인 방식이다. passport-kakao, passport-naver는 커뮤니티 패키지로 존재한다. 단, 관리 주체가 다르고 passport-naver는 최근 업데이트가 뜸한 편이다 (2025년 초 기준 main 브랜치 커밋이 1년 이상 멈춰 있는 것으로 보인다). 직접 포크해서 쓰는 팀도 있다.
그러나, 한국 프로바이더만 놓고 보면 NextAuth가 유지보수 부담이 더 낮다.
보안 기본값과 콜백 검증
OAuth 2.0 보안 체크리스트는 길다. state 파라미터로 CSRF 방어, PKCE로 인가 코드 가로채기 방어, nonce로 ID 토큰 재전송 방어, redirect_uri 정확한 매칭.
이처럼, NextAuth.js v5는 이걸 전부 기본값으로 처리한다. PKCE는 OAuth 2.1 권고를 따라 지원하는 프로바이더에 자동으로 적용된다. state와 PKCE 검증 실패 시 OAuthCallbackError로 떨어진다 (출처: Auth.js v5 Reference / Errors).
Passport.js는 전략마다 다르다. passport-oauth2는 옵션으로 state: true, pkce: true를 켜야 한다. 끄고 쓰는 예제 코드가 인터넷에 너무 많다. 카카오·네이버처럼 PKCE를 공식 지원하지 않는 프로바이더는 state만으로 가야 한다 (2025년 6월 기준 카카오 OAuth 문서, 네이버 개발자센터 문서 확인).
반면, 이 차이는 작아 보여도 보안 감사에서 크게 작동한다. "기본값이 안전한가"가 라이브러리 평가의 첫 번째 항목이다.
세션 전략과 토큰 보관
그러나, NextAuth의 기본 세션은 JWT다. 쿠키에 암호화된 JWT가 들어간다. DB 세션으로 바꾸려면 session: { strategy: "database" }로 한 줄 변경하면 된다. Prisma, Drizzle, MikroORM 어댑터가 공식이다.
실제로, Passport.js의 세션은 express-session에 위임한다. 보통 Redis나 Memcached 스토어를 붙인다. 세션 키 관리, TTL, 재발급은 직접 짜야 한다. 유연성은 높지만 그만큼 코드가 늘어난다.
리프레시 토큰 회전은 둘 다 직접 구현이다. NextAuth는 jwt 콜백 안에서 만료 검사 → refresh API 호출 → 새 토큰 반환 패턴을 공식 예제로 제공한다. Passport는 전략 옵션과 미들웨어 조합으로 작성한다.
카카오·네이버 프로바이더 등록 시 주의점
따라서, NextAuth로 가기로 한 뒤 실제로 부딪힌 부분을 정리한다.
카카오는 "동의 항목" 화면에서 닉네임, 프로필 이미지, 이메일을 따로 설정한다. 이메일은 사업자 검수가 필요한 항목이다. 검수 전에는 테스트 계정만 받아온다. 등록 후 콜백 URL은 /api/auth/callback/kakao 형식으로 정확히 일치해야 한다. 끝에 슬래시 하나 더 붙으면 그대로 실패한다.
물론, 네이버는 응답 데이터 구조가 표준과 약간 다르다. response 객체 안에 id, email, name이 중첩되어 있다. NextAuth profile 콜백에서 평탄화가 필요하다.
// auth.config.ts (NextAuth.js v5)
import Google from "next-auth/providers/google"
import Kakao from "next-auth/providers/kakao"
import Naver from "next-auth/providers/naver"
export const authConfig = {
providers: [
Google({
clientId: process.env.GOOGLE_ID!,
clientSecret: process.env.GOOGLE_SECRET!,
}),
Kakao({
clientId: process.env.KAKAO_ID!,
clientSecret: process.env.KAKAO_SECRET!,
}),
Naver({
clientId: process.env.NAVER_ID!,
clientSecret: process.env.NAVER_SECRET!,
profile(profile) {
// 네이버는 response 안에 데이터가 중첩됨
return {
id: profile.response.id,
name: profile.response.nickname,
email: profile.response.email,
image: profile.response.profile_image,
}
},
}),
],
}
구글은 사실상 설정이 없다. 클라이언트 ID/Secret만 넣으면 PKCE, state, nonce가 다 켜진다. 카카오와 네이버는 콘솔에서 콜백 URL 등록, 동의 항목 검수, 비즈 앱 전환 같은 추가 단계가 더 든다. 이 부분은 코드보다 행정 비용이 크다.
OAuth 콜백 보안 처리 — state, PKCE, redirect_uri
콜백 보안은 직접 검증하지 않아도 라이브러리가 처리한다. 그래도 어떤 검증이 일어나는지는 알고 있어야 한다.
state 파라미터는 인가 요청 시 생성한 랜덤 값을 쿠키에 저장하고, 콜백에서 받은 값과 비교한다. NextAuth는 이걸 암호화된 쿠키 (__Secure-authjs.state.*)로 저장한다. 같은 도메인이 아니면 검증이 실패한다. 프록시 뒤에 있으면 trustHost: true와 AUTH_URL 환경변수 설정이 사실상 필수다.
PKCE는 인가 코드 가로채기 공격을 막는다. 클라이언트가 code_verifier를 생성해 해시 (code_challenge)를 인가 요청에 함께 보내고, 토큰 교환 시 원본 verifier를 같이 보낸다. 카카오·네이버는 공식 PKCE 지원이 없어 NextAuth가 PKCE를 자동으로 켜지 않는다. 구글은 자동 적용된다.
그래서, redirect_uri는 OAuth 콘솔에 등록한 값과 정확히 일치해야 한다. 와일드카드 미지원. 개발용 http://localhost:3000과 운영용 https://prod.example.com은 별도 등록이다. 프리뷰 배포(Vercel 등)는 별도 클라이언트 ID를 따로 두는 쪽이 안전해 보인다.
그러나, 콜백 에러 처리는 NextAuth가 ?error= 쿼리로 리다이렉트한다. OAuthCallbackError, AccessDenied, Verification 같은 표준화된 에러 코드가 있다. 커스텀 에러 페이지 (pages.error)에서 분기 처리하면 된다.
벤치마크와 운영 후 체감
그러나, 빌드 사이즈와 콜드 스타트 차이를 측정해봤다. 측정 환경은 Next.js 14.2, Node.js 20.11, Vercel Node 런타임이다. 수치는 동일 프로젝트를 두 가지 구성으로 빌드해본 체감 기준이다.
| 항목 | NextAuth v5 | Passport.js + Express |
|---|---|---|
| 인증 관련 코드량 | 약 280줄 | 약 850줄 |
| 빌드 사이즈 증가 | 약 +180KB | 약 +95KB |
| 콜드 스타트 (p50, 체감) | 약 410ms | 약 320ms |
| 카카오/네이버 추가 시간 | 두 줄 + profile 콜백 | 별도 전략 패키지 설치 + 라우트 |
이처럼, Passport가 빌드 사이즈와 콜드 스타트에서 약간 유리한 것으로 보인다. 단, Next.js App Router 안에서 Express를 같이 끌고 가는 비용이 더 크다. 별도 인증 서버를 두는 구조라면 Passport가 자연스럽다.
특히, 체감상 가장 큰 차이는 "콜백 라우트를 한 번도 안 만져도 된다"는 점이었다. /app/api/auth/[...nextauth]/route.ts 한 파일로 끝난다. Passport는 라우트, 미들웨어, 세션 스토어, 에러 핸들러까지 손이 간다.
결론과 한계
반면, Next.js 14 이상에서 새로 짠다면 NextAuth.js v5가 기본 선택이라는 게 결론이다. 한국 프로바이더가 코어에 있고, 보안 기본값이 안전하며, 콜백 라우트 작성이 사실상 없다. Express 기반 백엔드를 따로 운영하고 인증을 그쪽에 두는 구조라면 Passport.js가 자연스럽다. Next.js는 프론트만, 인증 서버는 분리하는 패턴이라면 굳이 NextAuth로 갈 이유가 없다.
당장 실행할 수 있는 세 가지는 이렇다. 첫째, 카카오·네이버 콘솔에서 콜백 URL을 정확히 등록하고 동의 항목 검수를 신청한다 (이메일 항목은 사업자 검수 필요). 둘째, NextAuth v5 설치 후 auth.config.ts에 세 프로바이더를 추가하고 profile 콜백에서 네이버 응답을 평탄화한다. 셋째, 운영 환경에 AUTH_URL과 AUTH_SECRET을 설정하고 콜백 쿠키 도메인을 확인한다.
다만 NextAuth.js v5는 작성 시점(2026년 기준) 기준 Auth.js로 리브랜딩 후 일부 어댑터 API가 여전히 변경 중이라, 프로덕션 배포 전 사용 중인 ORM 어댑터(Prisma, Drizzle 등) 버전 호환성은 더 지켜봐야 한다.
관련 글
- TOTP 2단계 인증 구현 완전 가이드: Python·Node.js 백업 코드와 복구 플로우 – TOTP 2단계 인증 구현은 함수 두 줄로 끝나지 않는다. 서버 NTP 시간이 어긋나면 사용자가 락아웃되고, 백업 코드가 없으면 폰을 잃어…
- JWT RS256 HS256 차이, 환경별로 다른 답이 나오는 이유 – RS256이 무조건 정답이라는 통념을 깬다. 환경별로 답이 다르다. HS256, RS256, ES256을 키 관리와 검증 비용 기준으로 다…
- 세션 JWT 인증 비교: 로그아웃 안 되는 토큰 때문에 밤샌 이야기 (2026) – 세션 JWT 인증 비교를 다룬다. JWT로 갈아탔다가 로그아웃이 안 되는 문제로 헤맸던 경험, 그리고 어떤 기준으로 다시 결정했는지 정리한다.