Auth.js로 인증 전면 개편하며 익명 계정 병합 구현
목차
최근 인증 시스템 전체를 Auth.js 기반으로 개편하면서 Google 로그인을 추가하고, 익명 사용자를 기존 계정으로 병합하는 기능을 넣었다. 동시에 초기 설정 자동화를 위해 Polar setup 스크립트도 구성했는데, 이 작업이 생각보다 복잡했던 이유와 의사결정 과정을 정리해본다.
OAuth 기반 로그인, 왜 Auth.js인가
솔직히 처음엔 간단하게 생각했다. Google OAuth만 추가하면 되겠지 싶었는데, 막상 구현하다 보니 회원가입/로그인/토큰 갱신/세션 관리가 얽혀있었다. 처음부터 모든 인증을 직접 짜기보다 Auth.js(Next.js용 인증 라이브러리)를 도입하는 게 낫겠다고 판단했다. 이게 바로 큰 변경을 미리 예측하고 한 번에 처리하는 의사결정이다.
왜 이 선택이 중요했는지:
| 항목 | 직접 구현 | Auth.js |
|---|---|---|
| Google OAuth 콜백 | 수동 토큰 처리 | 빌트인 프로바이더 |
| 세션/토큰 갱신 | 별도 미들웨어 | 통합 미들웨어 |
| 유지보수 부담 | 높음 (보안 패치) | Auth.js 커뮤니티 |
| 다른 OAuth 추가 | 매번 새로 작성 | 프로바이더만 추가 |
package.json에 Auth.js 관련 의존성이 들어가는 것도 이 결정의 결과물이다.
익명→계정 병합: 왜 어려운가
이게 핵심이었다. 앱을 처음 방문한 사용자는 계정 없이 익명으로 데이터를 쌓는다. 나중에 Google로 로그인하면 그 익명 데이터를 새 계정과 연결해야 한다. 단순히 "새 계정 만들고 끝"이 아니란 뜻이다.
이 상황이 실무에서 흔한 이유:
- 모바일 앱이나 웹 앱에서 회원가입 없이 사용 경험을 주고 싶을 때
- 사용자가 "아, 좋네. 이제 계정 만들어서 동기화하고 싶다" 할 때
- 쿠폰/포인트/구매 이력 같은 데이터가 이미 익명 ID로 쌓여있을 때
src/app/api/auth/[...nextauth]/route.ts 파일에서 이 로직을 처리한다. Google OAuth 콜백에서 사용자 정보를 받으면:
1. DB에 있는 기존 Google ID인가? → 기존 계정 로그인
2. 없는 Google ID인가? → 새 계정 생성
2-1. 이 세션에 익명 ID가 있는가? → 그 익명 데이터를 새 계정으로 병합
2-2. 익명 ID 없음 → 빈 상태에서 새 계정 시작
이 흐름에서 주의할 점은 원자성이다. 병합 중간에 실패하면 데이터가 반쪽만 옮겨갈 수 있다. 트랜잭션으로 보호하거나, 최소한 멱등성을 보장해야 한다.
UI 관점: login-bar.tsx의 역할
src/app/login-bar.tsx는 단순한 UI 컴포넌트지만, 실제로는 이 전체 흐름의 진입점이다. "Google로 로그인" 버튼을 클릭하면 Auth.js의 signIn 함수를 호출하고, 콜백이 route.ts의 로직으로 흘러간다.
사용자 입장에서:
익명 사용 중 → "Google로 로그인" 클릭 → Google 동의 화면 →
자동으로 익명 데이터 병합 → 로그인 완료
사용자는 별도의 "데이터 병합" 버튼이나 확인창을 보지 않는다. 자동으로 처리되어야 한다. 이게 UX와 백엔드 로직이 조화해야 하는 부분이다.
Polar setup 스크립트: 운영 자동화
scripts/polar-setup.mjs는 초기 설정을 자동화하는 스크립트다. Polar(아마 외부 결제/구독 서비스)를 연동할 때 필요한 키, URL, 웹훅 설정 같은 것들을 한 번에 구성한다.
왜 이게 중요한가? 팀원이 새로운 환경(로컬, 스테이징, 프로덕션)을 셋업할 때마다 수동으로 하나하나 설정하는 건 비효율적이다. 특히 개발/스테이징/프로덕션마다 다른 Polar 계정/키를 써야 할 때 더욱 그렇다.
// 예상 구조
1. .env.example에서 필수 변수 읽기
2. 사용자 입력 받기 (API 키, 웹훅 URL 등)
3. .env.local 또는 환경별 설정 파일 생성
4. 검증 (Polar 연동 테스트, 웹훅 정상 작동 확인)
.env.example 파일에 이번에 새로 추가된 환경 변수들(Google OAuth 키, Polar 키 등)이 나열되어 있고, 스크립트가 이를 참고해서 설정하는 구조다.
종합하면, 왜 이렇게 설계했는가
팀 관점에서 본다면:
- Auth.js 도입: 보안 취약점 줄이고, 다음에 다른 OAuth(카카오, GitHub 등)를 추가할 때 확장 가능하게
- 익명→계정 병합: 사용자 이탈 줄이고, 온보딩 마찰 최소화
- Polar setup 스크립트: 개발/배포 속도 올리고, 환경 설정 실수 방지
이번 작업을 하면서 느낀 가장 큰 교훈은, "OAuth 추가"라는 단순한 요구사항이 실제로는 인증/인가 시스템 전체의 재설계였다는 것이다. 처음 계획할 때는 1주일 정도 예상했지만, 익명 병합과 여러 엣지 케이스를 처리하다 보니 훨씬 커졌다.
다음번엔 비슷한 "OAuth 추가" 요청이 들어오면, 팀에게 "실제로는 이 정도의 스코프"라고 미리 얘기하는 게 중요하겠다. 프로젝트 관리 관점에서도 그렇고, 예상 외 복잡성이 나중에 터지지 않도록.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.