page.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. 'use client';
  2. import './style.scss';
  3. import Link from 'next/link';
  4. import { useRouter } from 'next/navigation';
  5. import { useState, useEffect, useRef } from 'react';
  6. import { Checkbox } from '@/components/ui/checkbox';
  7. import { Dialog, DialogTrigger } from '@/components/ui/dialog';
  8. import TermsDialog from '@/app/component/TermsDialog';
  9. import { VerificationType } from '@/constants/common';
  10. import { fetchApi, throwError } from '@/lib/utils/client';
  11. import { RegisterRequest } from '@/dtos/request/auth';
  12. import { RegisterResponse } from '@/dtos/response/auth';
  13. export default function Page()
  14. {
  15. const router = useRouter();
  16. const [error, setError] = useState<string>('');
  17. const [loading, setLoading] = useState<boolean>(false);
  18. const [email, setEmail] = useState<string>('');
  19. const [password, setPassword] = useState<string>('');
  20. const [rePassword, setRePassword] = useState<string>('');
  21. const [agree1, setAgree1] = useState<boolean>(false);
  22. const [agree2, setAgree2] = useState<boolean>(false);
  23. const emailRef = useRef<HTMLInputElement>(null);
  24. const passwordRef = useRef<HTMLInputElement>(null);
  25. const rePasswordRef = useRef<HTMLInputElement>(null);
  26. useEffect(() => {
  27. if (error) {
  28. alert(error);
  29. setError('');
  30. }
  31. }, [error]);
  32. const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
  33. e.preventDefault();
  34. try {
  35. if (email.length < 1) {
  36. emailRef.current?.focus();
  37. throw new Error('이메일을 입력해주세요.');
  38. }
  39. if (password.length < 1) {
  40. passwordRef.current?.focus();
  41. throw new Error('비밀번호를 입력해주세요.');
  42. }
  43. if (rePassword.length < 1) {
  44. rePasswordRef.current?.focus();
  45. throw new Error('비밀번호 확인을 입력해주세요.');
  46. }
  47. if (password !== rePassword) {
  48. throw new Error('비밀번호가 서로 일치하지 않습니다.');
  49. }
  50. if (!agree1 || !agree2) {
  51. throw new Error('모든 필수 약관에 동의해주세요.');
  52. }
  53. await new Promise(resolve => setTimeout(resolve, 500));
  54. const res = await fetchApi<RegisterResponse>('/api/auth/register', {
  55. method: 'POST',
  56. body: {
  57. Email: email,
  58. Password: password,
  59. IsPolicyAgree: agree1,
  60. IsPrivacyAgree: agree2
  61. } as RegisterRequest
  62. });
  63. throwError(res);
  64. // 시간 제한 생성
  65. const expiration: string = (Date.now() + 10 * 60 * 1000).toString();
  66. const callbackURL: string = location.pathname;
  67. sessionStorage.setItem('type', VerificationType.Registration.toString());
  68. sessionStorage.setItem('expiration', expiration);
  69. sessionStorage.setItem('callbackURL', callbackURL);
  70. sessionStorage.setItem('email', email);
  71. if (res.data!.isRegisterEmailAuth) {
  72. // 이메일 인증 필요
  73. router.push('/approval');
  74. } else {
  75. // 회원가입 완료
  76. router.push('/welcome');
  77. }
  78. } catch (err) {
  79. if (err instanceof Error) {
  80. setError(err.message);
  81. }
  82. } finally {
  83. setLoading(false);
  84. }
  85. }
  86. return (
  87. <>
  88. <div id="registForm" className="row-start-2">
  89. <fieldset>
  90. <legend>회원가입</legend>
  91. <form method="post" acceptCharset="utf-8" autoComplete="off" onSubmit={handleSubmit}>
  92. <div className="flex flex-row flex-wrap">
  93. <section className="grow sm:pt-4 sm:pb-4">
  94. <dl>
  95. <dt hidden>&nbsp;</dt>
  96. <dd>{ process.env.NEXT_PUBLIC_SITE_NAME } 에 오신 것을 환영합니다.</dd>
  97. <dd>빠르고 간단하게 회원가입을 진행하세요.</dd>
  98. </dl>
  99. <br/>
  100. <dl>
  101. <dt hidden>&nbsp;</dt>
  102. <dd>가입 확인을 위해 유효한 이메일을 입력해주세요.</dd>
  103. <dd>비밀번호는 최소 8자 이상 입력해주세요.</dd>
  104. </dl>
  105. </section>
  106. <section className="grow sm:pt-4">
  107. <article className="grid">
  108. <label htmlFor="email">이메일</label>
  109. <input type="email" name="email" id="email" ref={emailRef} maxLength={30} onChange={e => setEmail(e.target.value)} autoComplete="off" />
  110. <label htmlFor="password">비밀번호</label>
  111. <input type="password" name="password" id="password" ref={passwordRef} maxLength={20} onChange={e => setPassword(e.target.value)} />
  112. <label htmlFor="rePassword">비밀번호 확인</label>
  113. <input type="password" name="re_password" id="rePassword" ref={rePasswordRef} maxLength={20} onChange={e => setRePassword(e.target.value)} />
  114. </article>
  115. </section>
  116. </div>
  117. <hr />
  118. <div className="flex flex-row flex-wrap">
  119. <section className="grow">
  120. <dl>
  121. <dt>회원가입 약관</dt>
  122. <dd>원활한 서비스 이용을 위해 약관 동의가 필요합니다.</dd>
  123. <dd>약관 내용을 자세히 확인하신 후 동의해주세요.</dd>
  124. </dl>
  125. </section>
  126. <section className="grow pt-4 sm:pt-0">
  127. <p>
  128. <Checkbox name="agree_1" id="agree_1" onCheckedChange={(checked) => setAgree1(Boolean(checked))} />
  129. <label htmlFor="agree_1">
  130. <Dialog>
  131. <DialogTrigger>이용약관</DialogTrigger>
  132. <TermsDialog subject="이용약관" description="" />
  133. </Dialog>
  134. 에 동의합니다.
  135. </label>
  136. </p>
  137. <p>
  138. <Checkbox name="agree_2" id="agree_2" onCheckedChange={(checked) => setAgree2(Boolean(checked))} />
  139. <label htmlFor="agree_2">
  140. <Dialog>
  141. <DialogTrigger>개인정보처리방침</DialogTrigger>
  142. <TermsDialog subject="개인정보처리방침" description="" />
  143. </Dialog>
  144. 에 동의합니다.
  145. </label>
  146. </p>
  147. <div className="grid grid-cols-2 gap-2 mt-3">
  148. <button type="submit" className="btn btn-submit" disabled={loading}>
  149. {loading ? "회원가입 중..." : "회원가입"}
  150. </button>
  151. <Link href="/login" className="btn btn-default">취소</Link>
  152. </div>
  153. </section>
  154. </div>
  155. </form>
  156. </fieldset>
  157. </div>
  158. </>
  159. );
  160. }