Learning Docs 0.0.1 버전

Sign-up + 이메일 인증

회원가입 → 이메일 코드 발송 → 코드 확인 → 그 다음에야 다른 API 사용 가능.

흐름

1. ClientAuth.signUp(body)
     → 200 + accessToken (sign-in 과 동일)
     → user.emailVerifiedAt 은 null
     → 다른 endpoint 호출 시 403 (이메일 인증 강제)
2. ClientEmailVerification.sendVerification({ email })
     → user.email 로 6자리 OTP 발송
     → 응답에 expiresAt + nextResendAvailableAt
3. ClientEmailVerification.verifyEmail({ code })
     → user.email / user.emailVerifiedAt 갱신
     → 이후 모든 endpoint 호출 가능

1. 회원가입

const res = await ClientAuth.signUp({
  body: {
    tenantKey: 'classum',
    userId: 'newbie',
    password: 'pw-12345678',
    displayName: 'Newbie',
    email: 'newbie@classum.com',
  },
})
hooks.setAccessToken(res.data!.accessToken, res.data!.accessTokenExpiresAt)

에러 코드

status code 의미
400 signUp/EmailDomainNotAllowed tenant 의 허용 이메일 도메인 정책에 없는 도메인
400 (zod) password min(8) / email format / userId regex
404 tenant/NotFound tenant 없음 / soft-deleted
409 user/UserIdDuplicated 같은 tenant 안 userId 중복

2. 이메일 인증 코드 발송

const res = await ClientEmailVerification.sendVerification({
  body: { email: 'newbie@classum.com' },
})
// 200 — { expiresAt, nextResendAvailableAt }
  • OTP 만료: 10분
  • 시도 횟수: 5회 (틀리면 코드 무효화)
  • 재발송 cooldown: 60초

에러 코드

status code 의미
400 (zod) email format
429 emailVerification/CooldownActive 60초 cooldown 중

3. 코드 검증

const res = await ClientEmailVerification.verifyEmail({ body: { code: '123456' } })
// 200 — { user: { ..., email, emailVerifiedAt } }

이 시점부터 다른 모든 client_auth endpoint 호출 가능.

에러 코드

status code 의미
400 (zod) code 가 6자리 숫자 아님
401 emailVerification/InvalidCode 만료 / 시도초과 / 불일치 — 모두 같은 응답

4. React + TanStack Query 예시

function SignUpFlow() {
  const [stage, setStage] = useState<'form' | 'verify'>('form')
  const signUpMutation = useMutation({
    mutationFn: async (input) => {
      const res = await ClientAuth.signUp({ body: input })
      if (res.error) throw res.error
      return res.data!
    },
    onSuccess: (data) => {
      hooks.setAccessToken(data.accessToken, data.accessTokenExpiresAt)
      ClientEmailVerification.sendVerification({ body: { email: data.user.email! } })
      setStage('verify')
    },
  })

  const verifyMutation = useMutation({
    mutationFn: async (code: string) => {
      const res = await ClientEmailVerification.verifyEmail({ body: { code } })
      if (res.error) throw res.error
      return res.data!
    },
    onSuccess: () => navigate('/'),
  })

  return stage === 'form' ? <SignUpForm onSubmit={signUpMutation.mutate} /> : <VerifyCode onSubmit={verifyMutation.mutate} />
}

주의 — admin_issued user 는 별도

BackOffice 가 발급한 매니저 (source=admin_issued) 는 이메일 인증 강제 안 됨. sign-in 후 바로 모든 endpoint 사용 가능. 임시 비밀번호는 별도 변경 흐름 권장.