임통 아이콘

임통 블로그

[PORKO] 로그인 및 회원가입

기술

2024년 05월 27일

[PORKO] 로그인 및 회원가입의 썸네일

여러 서비스들을 사용하다보면 회원가입 시 여러 정보들을 수집하는 경우가 있습니다.
아래 이미지를 통해 쉽게 이해할 수 있습니다.

위와 같은 UI패턴을 구현하기 위해 FunnelMulti-Step-Form을 학습하며 개발한 경험을 포스팅하고자 합니다.

Funnel

Funnel은 사용자가 특정 목표에 도달하는 과정에서 거치는 단계들을 의미합니다.
회원가입이나 온보딩 과정에서 사용자가 각 단계를 거치며 이탈하지 않도록 설계하는 것이 핵심입니다.

Funnel의 중요성

각 단계에서 얼마나 많은 사용자가 이탈하는지 분석함으로써, 어느 단계가 문제인지 식별할 수 있습니다. 이탈률이 많은 단계가 있다면 해당 단계를 더 간소화하거나 UX를 개선할 필요가 있습니다. 이를 통해 사용자의 흐름을 분석하고 최적화하여 보다 더 많은 사용자가 회원가입을 완료할 수 있게 합니다.

주의할 점

사용자가 어느 단계에서 무엇을 해야 하는지 명확하게 제시해 주고, 이전 단계로 돌아가는 옵션도 제공해줘야 사용자가 혼란을 느끼지 않고 계속 진행할 수 있습니다.

Multi-Step-Form

Multi-Step-Form은 하나의 긴 폼을 여러 단계로 나누어 사용자에게 부담을 줄여주는 방식입니다.
회원가입이나 온보딩 과정에서 복잡한 정보를 한 번에 다 입력하게 하는 대신, 몇 가지 중요한 정보만 한 단계씩 입력하게 함으로써 사용자 경험을 개선합니다.

Multi-Step-Form의 중요성

  • 이탈률 감소: 긴 폼이 한 번에 보여질 때보다 이탈률이 적어집니다. 각 단계가 짧고 명확하기 때문에, 사용자는 완료할 수 있을 것이라는 자신감을 느끼고 계속 진행하게 됩니다.

  • 데이터 수집 전략: 사용자가 중간에 이탈하더라도, 이미 입력한 정보를 저장해 놓으면 다음에 이어서 진행할 수 있게 설계할 수 있습니다.

그렇다면 위의 전략들을 어떻게 코드로 구현해낼 수 있을 지 알아보도록 합시다.

우선 저는 사용자가 회원가입 시 입력해야하는 많은 정보를 수집하고 유효성을 검증하기 위해 react-hook-formzod 라이브러리를 채택했습니다.

기획된 회원가입의 단계는 총 4단계로 이루어져 있습니다.
각 단계에 입력된 정보를 수집하기 위해 SignipFormProvider 컴포넌트를 작성했습니다.

"use client";
 
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import {
  SignupInputsValues,
  defaultValues,
  signupSchema,
} from "../../schema/signupSchema";
import { Form } from "@/components/ui/form";
import { signup } from "@/service/api/auth";
import { useState } from "react";
import { useRouter } from "next/navigation";
import MessagePopup from "@/components/MessagePopup";
 
const SignUpFormProvider = ({ children }: { children: React.ReactNode }) => {
  const form = useForm<SignupInputsValues>({
    resolver: zodResolver(signupSchema),
    defaultValues,
    mode: "all",
  });
  const router = useRouter();
 
  const [error, setError] = useState<string | undefined>("");
 
  const { handleSubmit } = form;
 
  const onSubmit = handleSubmit(async (formValues) => {
    setError("");
    const result = await signup(formValues);
 
    if (result?.error) {
      setError(result.error);
      setTimeout(() => setError(""), 2000);
      return;
    }
    router.push("/auth/login");
    sessionStorage.removeItem("signup-storage");
  });
 
  return (
    <Form {...form}>
      <form onSubmit={onSubmit} className="px-20">
        {children}
 
        <MessagePopup isView={!!error}>{error}</MessagePopup>
      </form>
    </Form>
  );
};
export default SignUpFormProvider;

수집된 정보를 세션 스토리지에 저장하여 브라우저 탭이 닫히지 않는 동안 데이터를 유지하기 위해
zustandpersist를 사용했습니다.

// signup.ts
 
export const useSignupStore = create<SignupState>()(
  persist(
    (set) => {
      return {
        email: "",
        checkEmail: false,
        password: "",
        confirmPassword: "",
        name: "",
        phoneNumber: "",
        roadName: "",
        detail: "",
        gender: "",
        setStorage: (field, value) => set({ [field]: value }),
      };
    },
    {
      name: "signup-storage",
      storage: createJSONStorage(() => sessionStorage),
    }
  )
);

회원가입 페이지의 최상단에서 SignupFormProvider를 사용하여 각 단계에 해당하는 정보를 수집하고, 새로고침을 하더라도 브라우저 탭만 닫지 않는다면 데이터가 유실되지 않도록 회원가입 기능을 구현할 수 있었습니다.

해당 글은 목차가 없습니다.