본문 바로가기

TIL

TIL - 퍼널 구조 개선

회원가입 퍼널 구조 구현

오늘은 회원가입 퍼널 구조(이메일 -> 닉네임 -> 비밀번호 -> 성공)를 다음과 같이 재구성했습니다:

  • 회원가입 (이메일, 닉네임, 비밀번호 입력)
  • 유저 추가 정보 입력 (키, 몸무게, 프로필 이미지)
  • 회원가입 성공 페이지

주요 작업 내용

  1. 회원가입 퍼널 구조 변경
    • 기존의 회원가입 프로세스를 재구성하여 필수 입력 사항(이메일, 닉네임, 비밀번호)과 선택 입력 사항(추가 정보 입력)으로 나누었습니다.
  2. 이메일 및 닉네임 중복 확인 기능 구현
    • 입력한 이메일과 닉네임이 중복되지 않도록 서버와 통신하여 중복 여부를 확인하는 기능을 추가했습니다.
  3. 리팩토링 작업
    • 이메일과 닉네임 중복 확인 기능을 한 파일로 합쳐 공통된 부분을 리팩토링했습니다.
  4. 초기 데이터 폴더 구조 개선
    • data/authInitialState.ts 파일을 만들어 초기 폼 상태를 관리하도록 구조를 개선했습니다.

초기 폼 상태

  • data/authInitialState.ts 파일을 만들어 폼 상태를 초기화했습니다.
import { FormState } from '@/types/auth';

export const initialFormState: FormState = {
  email: { value: '', error: null, successMessage: null, isVerified: false, isChecking: false },
  nickname: { value: '', error: null, successMessage: null, isVerified: false, isChecking: false },
  password: { value: '', error: null },
  confirmPassword: { value: '', error: null },
  height: { value: '', error: null },
  weight: { value: '', error: null },
  profileImage: { value: null, error: null },
};

 

필수 입력 사항 폼 (EssentialInfoForm)

  • 이메일, 닉네임, 비밀번호 입력을 위한 폼을 작성하고 중복 확인 버튼을 추가했습니다
import { EssentialInfoFormProps } from '@/types/auth';

const EssentialInfoForm = ({ formState, handleChange, handleCheckDuplicate, onSubmit }: EssentialInfoFormProps) => {
  return (
    <form onSubmit={onSubmit} className="flex flex-col gap-4 items-center justify-center w-full max-w-[960px]">
      {/* 이메일 입력 폼 */}
      <div>
        <label htmlFor="email">이메일:</label>
        <div>
          <input type="email" id="email" name="email" value={formState.email.value} onChange={handleChange} required />
          <button type="button" onClick={() => handleCheckDuplicate('email')} disabled={formState.email.isChecking}>
            {formState.email.isChecking ? '확인 중' : '확인'}
          </button>
        </div>
        {formState.email.error && <p>{formState.email.error}</p>}
        {formState.email.successMessage && <p>{formState.email.successMessage}</p>}
      </div>

      {/* 닉네임 입력 폼 */}
      <div>
        <label htmlFor="nickname">닉네임:</label>
        <div>
          <input type="text" id="nickname" name="nickname" value={formState.nickname.value} onChange={handleChange} required />
          <button type="button" onClick={() => handleCheckDuplicate('nickname')} disabled={formState.nickname.isChecking}>
            {formState.nickname.isChecking ? '확인 중' : '확인'}
          </button>
        </div>
        {formState.nickname.error && <p>{formState.nickname.error}</p>}
        {formState.nickname.successMessage && <p>{formState.nickname.successMessage}</p>}
      </div>

      {/* 비밀번호 입력 폼 */}
      <div>
        <label htmlFor="password">비밀번호:</label>
        <input type="password" id="password" name="password" value={formState.password.value} onChange={handleChange} required />
        {formState.password.error && <p>{formState.password.error}</p>}
      </div>

      {/* 비밀번호 확인 입력 폼 */}
      <div>
        <label htmlFor="confirmPassword">비밀번호 확인:</label>
        <input type="password" id="confirmPassword" name="confirmPassword" value={formState.confirmPassword.value} onChange={handleChange} required />
        {formState.confirmPassword.error && <p>{formState.confirmPassword.error}</p>}
      </div>

      <button type="submit">다음</button>
    </form>
  );
};

export default EssentialInfoForm;

중복 확인 기능

  • 중복 확인을 위한 서버 통신 로직을 구현했습니다.

import { createClient } from '@/supabase/server';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const { field, value } = await request.json();

  if (field !== 'email' && field !== 'nickname') {
    return NextResponse.json({ error: 'Invalid field' }, { status: 400 });
  }

  try {
    const supabase = createClient();
    const { data, error: queryError } = await supabase.from('users').select('*').eq(field, value).single();

    if (queryError) {
      if (queryError.code === 'PGRST116') {
        return NextResponse.json({ available: true });
      }
      return NextResponse.json({ error: queryError.message }, { status: 500 });
    }

    return NextResponse.json({ available: false });
  } catch (error) {
    return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
  }
}

 

결론

오늘은 회원가입 프로세스를 재구성하고, 이메일 및 닉네임 중복 확인 기능을 추가했으며, 초기 데이터 폴더 구조를 개선했습니다. 특히, 중복된 코드를 리팩토링하여 코드의 가독성과 유지 보수성을 높이는 작업을 했습니다.