| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- 'use client';
- import './style.scss';
- import Link from 'next/link';
- import { useRouter } from 'next/navigation';
- import { useState, useEffect, useRef } from 'react';
- import { VerificationType } from '@/constants/common';
- import { VerifyEmailRequest, ResendEmailRequest } from '@/types/request/auth';
- import { fetchApi, throwError } from '@/lib/utils/client';
- import Loading from '@/app/component/Loading';
- export default function Approval()
- {
- const router = useRouter();
- const [loading, setLoading] = useState<boolean>(false);
- const [error, setError] = useState<string>('');
- const [verifyCode, setVerifyCode] = useState<string>('');
- const verifyCodeRef = useRef<HTMLInputElement>(null);
- const type: string|null = sessionStorage.getItem('type');
- const email: string = (sessionStorage.getItem('email') || '');
- const expiration: string|null = sessionStorage.getItem('expiration');
- const callbackURL: string = (sessionStorage.getItem('callbackURL') || '/');
- // 인증번호 유지 시간 조회
- const getResendApprovalSecond = (): number => {
- let remainResendApprovalSecond: string | null = sessionStorage.getItem('remainResendApprovalSecond');
- if (!remainResendApprovalSecond) {
- remainResendApprovalSecond = '120'; // 2분 후 만료
- sessionStorage.setItem('remainResendApprovalSecond', remainResendApprovalSecond);
- }
- return Number(remainResendApprovalSecond);
- };
- const [resendApprovalSecond, setResendApprovalSecond] = useState(
- getResendApprovalSecond()
- );
- // 다시 보내기 여부
- const [canResend, setCanResend] = useState(false);
- useEffect(() => {
- if (error) {
- alert(error);
- setError('');
- }
- }, [error]);
- // 페이지 벗어날 때 세션 정리
- useEffect(() => {
- if (!email || email == '') {
- alert('잘못된 접근입니다.');
- location.replace('/');
- return;
- }
- // 인증 시간 만료 확인
- if (type == null || type == "") {
- alert('다시 시도해주세요.');
- router.push('/');
- return;
- }
- if (!expiration || Date.now() > Number(expiration)) {
- alert('인증 시간이 만료되었습니다. 다시 시도해주세요.');
- router.push(callbackURL);
- return;
- }
- const handleUnload = (e: BeforeUnloadEvent) => {
- const isReload = (e.type === 'beforeunload' || (performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming)?.type === 'reload' || performance.navigation.type === 1);
- if (isReload) {
- e.preventDefault();
- return;
- }
- sessionStorage.clear();
- }
- window.addEventListener('beforeunload', handleUnload);
- return () => window.removeEventListener('beforeunload', handleUnload);
- }, [expiration, router]);
- // 다시 보내기 타이머 설정
- useEffect(() => {
- if (resendApprovalSecond <= 0) {
- setCanResend(true);
- sessionStorage.setItem('expiration', '0');
- sessionStorage.removeItem('remainResendApprovalSecond');
- return;
- }
- const timer = setInterval(() => {
- setResendApprovalSecond((prev) => {
- if (prev <= 1) {
- clearInterval(timer);
- setCanResend(true);
- return 0;
- }
- return prev - 1;
- });
- }, 1000);
- sessionStorage.setItem('remainResendApprovalSecond', resendApprovalSecond.toString());
- return () => clearInterval(timer);
- }, [resendApprovalSecond]);
- // 인증번호 검증
- const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
- e.preventDefault();
- setLoading(true);
- try {
- if (!verifyCode) {
- verifyCodeRef.current?.focus();
- throw new Error('인증번호를 입력하세요.');
- }
- await new Promise(resolve => setTimeout(resolve, 500));
- const res = await fetchApi('/api/auth/verify-email', {
- method: 'POST',
- body: { Email: email, Code: verifyCode, Type: Number(type) } as VerifyEmailRequest
- });
- if (!res.success) {
- throwError(res);
- }
- sessionStorage.setItem('email', email);
- switch (type) {
- // 회원가입 완료
- case VerificationType.Registration.toString():
- router.push('/welcome');
- break;
- // 비밀번호 변경 페이지 이동
- case VerificationType.ForgotPassword.toString():
- router.push('/reset-password');
- break;
- }
- } catch (err) {
- if (err instanceof Error) {
- setError(err.message);
- }
- } finally {
- setLoading(false);
- }
- };
- // 인증번호 다시 보내기
- const handleResend = async () => {
- try {
- await fetchApi('/api/auth/resend-email', {
- method: 'POST',
- body: { Email: email, Type: Number(type) } as ResendEmailRequest
- });
- setCanResend(false);
- setResendApprovalSecond(getResendApprovalSecond());
- setVerifyCode("");
- } catch (err: any) {
- setError(err.message);
- }
- };
- return (
- <>
- {loading && <Loading />}
- <div id="approvalForm" className="row-start-2 flex flex-row flex-wrap gap-2">
- <fieldset className="grow">
- <legend>인증번호 확인</legend>
- <form id="fApproval" method="post" acceptCharset="utf-8" autoComplete="off" className="grid p-4" onSubmit={handleSubmit}>
- <p>보안을 위해 인증번호를 발송했습니다. <br />수신된 인증번호를 입력해주세요.</p>
- <br />
- <label htmlFor="email">수신 이메일</label>
- <input type="email" name="email" id="email" value={email} disabled />
- <div className="flex flex-row flex-1">
- <label htmlFor="verifyCode" className="flex-auto">인증번호</label>
- <label className="text-right">
- {canResend ? (
- <button type="button" className="text-blue-600 no-underline hover:underline" onClick={handleResend}>
- 다시 받기
- </button>
- ) : (
- <><span className="text-green-700">유효시간:(<em>{resendApprovalSecond}</em>초)</span></>
- )}
- </label>
- </div>
- <input type="number" name="verify_code" id="verifyCode" ref={verifyCodeRef} minLength={6} maxLength={10} onChange={e => setVerifyCode(e.target.value)} autoComplete="off" />
- <div className="grid grid-cols-2 gap-2">
- <button type="submit" className="btn btn-submit" disabled={loading}>
- {loading ? "확인 중..." : "확인"}
- </button>
- <button type="button" className="btn btn-default" onClick={() => router.push(callbackURL)}>취소</button>
- </div>
- <hr />
- <dl>
- <dt>도움이 필요하신가요?</dt>
- <dd>인증번호를 받을 수 없거나 오류가 발생한 경우 <Link href="/support/qna">운영자에게 문의</Link>하세요.</dd>
- </dl>
- </form>
- </fieldset>
- </div>
- </>
- );
- }
|