-
[Supabase][Auth] auth.admin.createUser와 signUp을 세션 반환과 확인 메일 기준으로 나누는 법기타개발지식/풀스택개발 2026. 7. 3. 09:16
IT 리서치 노트
[Supabase][Auth] auth.admin.createUser와 signUp을 세션 반환과 확인 메일 기준으로 나누는 법
Supabase에서 사용자를 만든다는 이유만으로 `auth.admin.createUser()`와 `auth.signUp()`을 같은 범주로 취급하면 가입 흐름이 금방 꼬인다. 2026년 7월 3일 기준 Supabase 공식 문서를 다시 보면 `createUser()`는 service_role 기반 서버 관리 API이고, `signUp()`은 PKCE·redirect·확인 메일 같은 사용자 가입 흐름과 맞물린다. 이 글은 두 API를 세션 반환과 확인 메일 기준으로 어디서 갈라 봐야 하는지 정리한 것이다.
1. 개요
결론부터 말하면
auth.admin.createUser()는 관리자 서버가 계정을 공급하는 경로,auth.signUp()은 사용자가 자신의 세션을 시작하는 경로에 더 가깝다. 전자는 service_role과 별도 admin client 분리가 핵심이고, 후자는 Confirm email 설정과 redirect 흐름이 핵심이다. 둘을 같은 helper 뒤에 넣으면 세션 기대치와 메일 흐름이 충돌하기 쉽다.이미 service_role client에서 RLS error가 나는 글이 관리자 client 분리를 다뤘다면, 이번 글은 그 분리를 가입 플로우 선택까지 확장한 내용이다. 또 Authorization 헤더와 service_role을 먼저 보는 글과 같이 보면 왜 가입 API도 관리자 경로와 사용자 경로를 섞으면 안 되는지가 더 분명해진다.
관리자 경로 안에서 초대 메일을 보내는 flow와 계정만 먼저 만드는 flow를 다시 자르고 싶다면 inviteUserByEmail와 createUser를 메일 발송과 세션 비생성 기준으로 나누는 글을 이어서 보는 편이 좋다.
2. 어디서 실제로 막히는가
실무에서 흔한 실패는 세 가지다. 첫째, 백오피스에서 계정을 선생성해야 하는데
signUp()을 써서 확인 메일과 redirect 처리까지 떠안는다. 둘째, 공개 회원가입인데createUser()를 써서 browser 세션이 당연히 생길 것처럼 기대한다. 셋째, admin client를 SSR helper와 섞어 써서 service_role 경로와 사용자 세션 경로가 한 코드 안에서 뒤섞인다.공식 문서는
createUser()를 서버 전용 관리 API로 소개하고, secret key guide에서는 별도 admin client를 만들며 세션 저장과 자동 갱신을 끄라고 안내한다. 반면signUp()문서는 PKCE email signup과 autoconfirm 제약을 설명하고 있어 사용자 redirect 흐름에 더 가까운 API라는 점이 드러난다. 이 차이를 놓치면 '왜 세션이 없지', '왜 메일 확인이 안 오지', '왜 SSR 쿠키가 섞이지' 같은 문제가 한꺼번에 붙는다.signUp()에서 세션 반환이 언제 생기는지까지 헷갈린다면 Confirm email 설정을 먼저 봐야 한다. Supabase의 현재 공식 레퍼런스는 이 동작을 언어별 reference와 v1 JavaScript reference에서 일관되게 설명하고 있다. 여기서는 그 문서를 바탕으로 '공개 가입은 설정에 따라 session이 갈리고, 관리 생성은 service_role 서버 경로에서 확인 상태를 따로 제어한다'는 운영 기준을 잡는 편이 안전하다고 본다.- 증상: 가입 API는 성공했는데 브라우저 세션이 기대와 다르다.
- 실패: 공개 signup과 관리자 provision을 같은 helper로 묶는다.
- 막힘: Confirm email 설정을 안 보고 signUp 결과만 해석한다.
- 누락: service_role admin client를 SSR/user client와 분리하지 않는다.
상황 먼저 볼 것 판단 기준 관리자 계정 선생성 service_role admin client createUser 쪽이 더 자연스럽다 공개 회원가입 Confirm email, redirect, PKCE signUp 흐름을 먼저 본다 세션이 예상과 다르다 프로젝트 confirm 설정과 client 종류 signup 결과를 무조건 동일하게 기대하지 않는다 3. 실무에서 적용하는 순서
가장 실용적인 분리 순서는 네 단계다. 먼저 이 경로가 관리자 서버에서만 도는지, 사용자가 브라우저에서 직접 들어오는지 나눈다. 두 번째로 service_role admin client와 user/SSR client를 코드에서 분리한다. 세 번째로 공개 signup 경로라면 Confirm email과 redirect 설정을 먼저 확인한다. 마지막으로 관리자 생성 경로라면
email_confirm같은 확인 상태와 후속 초대/비밀번호 설정 절차를 별도로 정리한다.- 관리자 경로인지 공개 가입 경로인지 먼저 나눈다.
- service_role admin client를 별도로 만든다.
- signUp 경로는 Confirm email과 redirect 설정을 먼저 본다.
- createUser 경로는 확인 상태와 후속 온보딩 절차를 따로 설계한다.
핵심은 "사용자를 만든다"보다 "누가 세션을 시작하는가"를 기준으로 API를 고르는 것이다. 사용자가 자신의 브라우저에서 세션을 가져가야 한다면 signUp 쪽 흐름이 더 맞고, 서버가 내부적으로 계정 공급을 해야 한다면 createUser 쪽이 더 단순하다. 이 차이는 문서에 직접 적힌 서버 전용 제약과 signup flow 제약에서 나온 운영 해석이다.
이렇게 자르면 세션 반환 기대치, 메일 확인 경로, 서버 권한 범위가 자연스럽게 분리된다. 특히 이후에 admin invite, password reset, user self-service를 확장할 때도 구조가 덜 흔들린다.
4. 공식 문서와 예시 화면으로 확인하기
첫 자료는 `auth.admin.createUser()` 문서의 핵심 전제다. 이 API는 서버에서만 호출해야 하고 browser에 service_role을 노출하면 안 된다고 못 박는다.
즉 이 함수는 가입 폼에서 바로 쓰는 public signup 경로가 아니라, 관리자 백오피스나 내부 배치 같은 trusted server 경로에 맞는 도구다. 이 전제를 빼먹으면 세션 기대치도 함께 어긋난다.
두 번째 화면은 `createUser()` 예시에서 `email_confirm: true`를 따로 넘기는 부분이다. 문서 예시만 봐도 관리 API는 확인 상태를 서버가 결정할 수 있다는 점이 드러난다.
반대로 말하면 기본 public signup처럼 '사용자가 확인 메일을 받고 직접 세션을 만든다'는 흐름과는 목적이 다르다. 관리자 생성과 셀프 가입을 같은 버튼 뒤에 섞으면 운영 규칙이 꼬이기 쉽다.
세 번째 자료는 `signUp()` 문서의 주의 문구다. Supabase는 email signup에서 PKCE를 지원하지만 autoconfirm이 켜지면 이 흐름을 같이 쓰지 못한다고 설명한다.
이 문장은 `signUp()`이 사용자 브라우저와 redirect, 확인 메일, 세션 수립 흐름에 연결된 API라는 뜻이다. 같은 '사용자 생성'이더라도 createUser와 signUp의 운영 자리가 다른 이유가 여기서 갈린다.
네 번째 화면은 Supabase의 server-side secret key 가이드다. 별도 admin client를 만들고 `persistSession: false`, `autoRefreshToken: false`, `detectSessionInUrl: false`를 끄라고 적는다.
이 설정은 createUser 같은 관리자 경로를 사용자 세션 경로와 섞지 말라는 실무 신호로 읽는 편이 맞다. 이미 SSR 쿠키와 세션 덮어쓰기 글을 봤다면, 왜 admin client를 분리해야 하는지가 더 선명해진다.
비교표로 보면 두 API의 자리가 더 빨리 잡힌다. 특히 누가 호출하는지, 세션을 어디서 기대하는지, 확인 메일을 누가 책임지는지를 나눠 적는 편이 실용적이다.
팀 문서에 이 표를 남겨 두면 백오피스 생성, 관리자 초대, 공개 회원가입을 하나의 helper로 우겨 넣는 실수를 줄일 수 있다.
현장에서 자주 꼬이는 포인트는 API 선택 자체보다 기대하는 결과가 다르다는 점이다. 체크리스트로 정리해 두면 가입 흐름 설계가 빨라진다.
특히 '관리자가 계정을 먼저 만들고 사용자는 나중에 비밀번호를 잡는다' 같은 흐름은 signUp 감각으로 설계하면 어긋난다. 반대로 일반 회원가입을 createUser로 밀면 확인 메일과 세션 흐름을 직접 더 많이 떠안게 된다.
마지막 자료는 팀 메모 예시다. 어떤 경로가 admin client를 쓰고 어떤 경로가 browser signup을 쓰는지 로그 키를 고정하면 나중에 장애 대응이 쉬워진다.
이미 service_role key 노출 금지 글을 읽었다면, 이 메모는 그 원칙을 가입 플로우 설계에 적용한 형태다.
5. 주의사항과 리스크
첫 번째 리스크는
createUser()를 signup shortcut처럼 써서 service_role 경로를 넓혀 버리는 것이다. 두 번째 리스크는signUp()을 관리자 provisioning 용도로 써서 확인 메일, redirect, 세션 생성 타이밍이 설계와 어긋나는 것이다. 세 번째 리스크는 Confirm email 설정을 환경별로 다르게 두고도 결과를 같은 것으로 기대하는 것이다.운영 전에 최소한 public signup 경로, backoffice admin create 경로, SSR helper 경로를 따로 테스트하는 편이 좋다. 같은 프로젝트라도 이 셋의 세션과 메일 동작은 의도적으로 달라야 한다.
- signup과 admin provision은 목적부터 다르다.
- service_role client는 user/SSR client와 분리한다.
- 세션 기대치는 Confirm email 설정과 함께 읽는다.
6. 결론
Supabase에서
auth.admin.createUser()와auth.signUp()은 같은 사용자 생성처럼 보여도 세션과 메일 흐름이 다르다. 관리자 서버가 계정을 공급하는지, 사용자가 자기 브라우저에서 가입하는지부터 나눈 뒤 client와 설정을 분리하면 가입 흐름이 훨씬 덜 꼬인다.- 관리자 경로는 createUser와 admin client를 본다.
- 공개 가입은 signUp과 Confirm email을 본다.
- 세션 기대치는 API 이름보다 호출 위치와 설정에서 갈린다.
7. 참고 링크
- https://supabase.com/docs/reference/javascript/auth-admin-createuser
- https://supabase.com/docs/reference/javascript/auth-signup
- https://supabase.com/docs/guides/troubleshooting/performing-administration-tasks-on-the-server-side-with-the-servicerole-secret-BYM4Fa
- https://supabase.com/docs/reference/javascript/v1
- https://supabase.com/docs/guides/auth/server-side/creating-a-client
- https://supabase.com/docs/guides/auth/auth-email-templates
'기타개발지식 > 풀스택개발' 카테고리의 다른 글