🎨 로그인/회원가입 페이지 UX 리뉴얼: 이탈률 감소를 위한 6가지 핵심 개선

프로젝트 목표: UI/UX 개선을 통한 회원가입 이탈률 감소 및 사용자 편의성 향상

📊 프로젝트 배경

쏠브북스 서비스의 회원가입 과정에서 높은 이탈률이 발생하고 있었습니다. 사용자 행동 분석 결과, 불편한 UI/UX 요소들이 주요 원인으로 파악되어 6가지 핵심 영역을 중심으로 전면적인 개선 작업을 진행했습니다.


🎯 6가지 핵심 개선사항

1. 🏷️ 마지막 로그인 방식 기억 기능

기존 문제점

  • 재방문 시 매번 로그인 방식을 선택해야 하는 불편함
  • 사용자가 선호하는 로그인 방식을 기억하지 못함
  • 로그인 시간 증가로 인한 사용자 피로도 상승

로그인 페이지 - 개선 전 ▲ 개선 전: 모든 로그인 버튼이 동일하게 표시됨

개선 방안

// localStorage 기반 마지막 로그인 방식 저장
export const useLastLoginMethod = () => {
  const [lastLoginMethod, setLastLoginMethod] = useState<LoginMethod | null>(
    null
  );

  const saveLastLoginMethod = (method: LoginMethod) => {
    localStorage.setItem(LAST_LOGIN_METHOD_KEY, method);
    setLastLoginMethod(method);
  };

  return { lastLoginMethod, saveLastLoginMethod };
};

// SNS 로그인 버튼에 "최근" 태그 표시
<SSOLoginButton
  src={"/assets/png/login-google.png"}
  alt={"구글 로그인"}
  onClick={googleLogin}
  isLastUsed={lastLoginMethod === "GOOGLE"}
/>;

로그인 페이지 - 개선 후 ▲ 개선 후: 구글 로그인 버튼에 “최근” 태그 표시

🎉 기대 효과:

  • ✅ 재방문 시 로그인 방식 선택 시간 단축
  • ✅ 로그아웃 후에도 정보 유지로 지속적 편의성 제공
  • ✅ 직관적인 “최근” 태그로 사용자 가이드

2. 📐 회원가입 폼 크기 최적화

기존 문제점

  • 필요한 입력 항목에 비해 폼이 시각적으로 과도하게 넓음
  • 화면 공간 낭비로 인한 산만함
  • 모바일/데스크톱에서 일관성 없는 레이아웃

개선 방안

/* 기존 - 과도하게 넓은 폼 */
.signup-form {
  width: 100%;
  max-width: 800px; /* 너무 넓음 */
}

/* 개선 - 적절한 크기로 조정 */
.signup-form {
  width: 100%;
  max-width: 400px; /* 최적화된 크기 */
  margin: 0 auto;
}

/* 반응형 최적화 */
@media (min-width: 640px) {
  .signup-form {
    max-width: 448px; /* sm:max-w-md */
  }
}

@media (min-width: 1024px) {
  .signup-form {
    max-width: 512px; /* lg:max-w-lg */
  }
}

🎉 기대 효과:

  • ✅ 집중도 향상으로 폼 완성 시간 단축 기대
  • ✅ 모든 디바이스에서 일관된 사용자 경험
  • ✅ 시각적 밀도 개선으로 전문성 향상

3. 🎯 약관 동의 클릭 영역 확장

기존 문제점

  • 작은 체크박스만 클릭 가능하여 조작이 어려움
  • 특히 모바일에서 터치 타겟이 너무 작음
  • 약관 텍스트 클릭 시 반응 없어 사용자 혼란

개선 방안

// 기존 - 체크박스만 클릭 가능
<input type="checkbox" id={checkboxId} />
<label htmlFor={checkboxId}>약관 텍스트</label>

// 개선 - 전체 영역 클릭 가능
<label htmlFor={checkboxId} className="cursor-pointer flex items-center gap-3 p-4 hover:bg-blue-50 transition-colors">
    <input
        type="checkbox"
        id={checkboxId}
        className="sr-only"
        onChange={handleChange}
    />
    {/* 커스텀 체크박스 UI */}
    <div className={`w-5 h-5 rounded border-2 ${isChecked ? 'bg-blue-600 border-blue-600' : 'border-gray-300'}`}>
        {isChecked && <CheckIcon />}
    </div>
    <span className="text-sm text-gray-700 flex-1">
        약관 텍스트 전체가 클릭 가능
    </span>
</label>

🎉 기대 효과:

  • ✅ 클릭 성공률 향상 기대
  • ✅ 모바일 사용성 대폭 개선
  • ✅ 직관적인 상호작용으로 사용자 만족도 상승

4. 📁 선택 약관 접기/펼치기 기능

기존 문제점

  • 모든 약관이 한 번에 노출되어 화면이 복잡함
  • 필수/선택 약관의 구분이 어려움
  • 긴 약관 리스트로 인한 시각적 피로도 증가

개선 방안

// 접기/펼치기 상태 관리
const [isMarketingExpanded, setIsMarketingExpanded] = useState(false);

// 스마트한 자동 펼치기 로직
const handleMarketingClick = (checked: boolean) => {
  setMarketingValueAll(checked);
  if (checked) {
    setIsMarketingExpanded(true); // 체크 시 자동 펼치기
  }
};

// UI 구현
<div className="border-b border-gray-200">
  <div
    className="flex items-center justify-between p-4 hover:bg-blue-50 transition-colors cursor-pointer"
    onClick={() => setIsMarketingExpanded(!isMarketingExpanded)}
  >
    <label className="flex items-center gap-3 cursor-pointer flex-1">
      {/* 마케팅 동의 체크박스 */}
    </label>

    {/* 화살표 아이콘 */}
    <div
      className={`transition-transform duration-200 ${
        isMarketingExpanded ? "rotate-90" : ""
      }`}
    >
      <ArrowRightIcon />
    </div>
  </div>

  {/* 접기/펼치기 애니메이션 */}
  <div
    className={`bg-gray-50 overflow-hidden transition-all duration-300 ease-in-out ${
      isMarketingExpanded ? "max-h-96 opacity-100" : "max-h-0 opacity-0"
    }`}
  >
    {/* 세부 마케팅 약관들 (SMS, 푸시, 이메일) */}
  </div>
</div>;

🎉 기대 효과:

  • ✅ 화면 복잡도 대폭 감소
  • ✅ 약관 동의 완료율 향상 기대
  • ✅ 필요한 정보만 선택적 노출로 인지 부담 감소

5. ✅ 휴대폰 인증 완료 상태 표시

기존 문제점

  • 인증 완료 후 명확한 피드백 부족
  • 사용자가 인증 성공 여부를 확신하지 못함
  • 불안감으로 인한 재인증 시도나 이탈 발생

개선 방안

// 조건부 렌더링으로 인증 상태 표시
<If
  condition={!isMobileNumberAccess}
  Component={
    // 인증 진행 중 UI
    <Stack direction="column" gap={16}>
      <MobileNumberFormItem {...props} />
      <AuthCodeFormItem {...props} />
    </Stack>
  }
  ElseComponent={
    // 인증 완료 UI
    <div className="flex items-center gap-2 py-3 px-4 bg-green-50 rounded-lg border border-green-200">
      <svg
        className="w-5 h-5 text-green-600"
        fill="currentColor"
        viewBox="0 0 20 20"
      >
        <path
          fillRule="evenodd"
          d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
          clipRule="evenodd"
        />
      </svg>
      <span className="text-sm font-medium text-green-800">
        휴대폰 인증 완료
      </span>
    </div>
  }
/>

회원가입 페이지 - 인증 전 ▲ 개선 전: 휴대폰 인증 진행 중 상태

회원가입 페이지 - 인증 완료 ▲ 개선 후: 명확한 “휴대폰 인증 완료” 상태 표시

🎉 기대 효과:

  • ✅ 인증 재시도율 감소 기대
  • ✅ 명확한 성공 피드백으로 사용자 안심
  • ✅ 다음 단계 진행에 대한 확신 제공

6. 🔄 로그인/회원가입 영역 명확한 구분

기존 문제점

  • 로그인과 회원가입 영역이 시각적으로 구분되지 않음
  • 사용자가 현재 어떤 과정에 있는지 혼란
  • 잘못된 페이지 접근으로 인한 추가적인 네비게이션 필요

개선 방안

// 탭 기반 네비게이션으로 명확한 구분
const SigninPage = () => {
  return (
    <div className="min-h-screen bg-gradient-to-br from-blue-50 via-indigo-50 to-purple-50">
      {/* 헤더 영역 - 현재 위치 명시 */}
      <div className="text-center mb-8">
        <h1 className="text-2xl font-bold text-gray-900 mb-2">로그인</h1>
        <p className="text-gray-600">계정에 로그인하세요</p>
      </div>

      {/* 로그인 폼 영역 */}
      <div className="bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl p-8">
        {/* 로그인 컨텐츠 */}
      </div>

      {/* 회원가입 링크 - 명확한 구분 */}
      <div className="mt-6 text-center">
        <p className="text-gray-600 text-sm mb-3">아직 계정이 없으신가요?</p>
        <button className="text-blue-500 hover:text-blue-700 font-semibold transition-all">
          회원가입하기 
        </button>
      </div>
    </div>
  );
};

const SignupPage = () => {
  return (
    <div className="min-h-screen bg-gradient-to-br from-purple-50 via-blue-50 to-indigo-50">
      {/* 헤더 영역 - 가치 중심 메시지 */}
      <div className="text-center mb-8">
        <h1 className="text-2xl font-bold text-gray-900 mb-2">
          지금 가입하고 바로{" "}
          <span className="text-blue-600">무료 기출문제</span> 풀어보        </h1>
      </div>

      {/* 회원가입 폼 영역 */}
      <div className="bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl p-8">
        {/* 회원가입 컨텐츠 */}
      </div>
    </div>
  );
};

🎉 기대 효과:

  • ✅ 사용자 혼란 대폭 감소
  • ✅ 잘못된 페이지 접근률 감소
  • ✅ 각 페이지별 명확한 목적 전달

📈 전체 개선 결과

🖼️ Before & After 비교

🔐 로그인 페이지

로그인 페이지 - 개선 전 로그인 페이지 - 개선 후 ▲ 마지막 로그인 방식 기억 기능으로 재방문 편의성 향상

📝 회원가입 페이지

회원가입 페이지 - 개선 전 회원가입 페이지 - 개선 후 ▲ 폼 최적화, 인증 완료 표시, 약관 접기/펼치기 등 종합적 UX 개선

📊 주요 개선사항 요약

개선 영역 구현 완료 기대 효과
마지막 로그인 기억 재방문 로그인 편의성 향상
폼 크기 최적화 집중도 향상 및 완성도 개선
클릭 영역 확장 터치 조작 편의성 향상
약관 접기/펼치기 시각적 복잡도 감소
인증 완료 표시 명확한 상태 피드백 제공
영역 구분 개선 사용자 혼란 감소

💭 개선 후 기대되는 사용자 반응

  • 클릭 영역 확장: 모바일에서 약관 동의 시 터치 조작이 더 쉬워질 것으로 예상
  • 인증 완료 표시: 휴대폰 인증 후 명확한 피드백으로 사용자 불안감 해소 기대
  • 마지막 로그인 기억: 재방문 사용자의 로그인 편의성 대폭 향상 예상
  • 약관 접기/펼치기: 초기 화면 복잡도 감소로 가입 의지 향상 기대

🛠 기술적 구현 하이라이트

1. localStorage 활용한 사용자 기억 기능

// 브라우저 세션 유지와 관계없이 사용자 편의성 제공
const LAST_LOGIN_METHOD_KEY = "solve-books-last-login-method";

export const useLastLoginMethod = () => {
  const [lastLoginMethod, setLastLoginMethod] = useState<LoginMethod | null>(
    null
  );

  useEffect(() => {
    const stored = localStorage.getItem(LAST_LOGIN_METHOD_KEY);
    if (stored) setLastLoginMethod(stored as LoginMethod);
  }, []);

  const saveLastLoginMethod = (method: LoginMethod) => {
    localStorage.setItem(LAST_LOGIN_METHOD_KEY, method);
    setLastLoginMethod(method);
  };

  return { lastLoginMethod, saveLastLoginMethod };
};

2. 접근성을 고려한 커스텀 체크박스

// 스크린 리더와 키보드 네비게이션 완전 지원
<label
  htmlFor={uniqueId}
  className="cursor-pointer hover:bg-blue-50 transition-colors"
>
  <input
    type="checkbox"
    id={uniqueId}
    className="sr-only" // 시각적으로 숨김, 스크린 리더에서는 접근 가능
    checked={isChecked}
    onChange={handleChange}
  />
  {/* 커스텀 UI */}
</label>

3. 반응형 애니메이션

/* 부드러운 접기/펼치기 애니메이션 */
.collapsible-content {
  overflow: hidden;
  transition: all 0.3s ease-in-out;
}

.expanded {
  max-height: 24rem; /* max-h-96 */
  opacity: 1;
}

.collapsed {
  max-height: 0;
  opacity: 0;
}

💡 핵심 인사이트

🎯 1. 사용자 중심 사고의 중요성

  • 발견: 개발자가 생각하는 “논리적 구조”와 사용자가 느끼는 “직관적 구조”는 다름
  • 적용: 기능 단위가 아닌 사용자 작업 단위로 UI 그룹화
  • 결과: 인지 부담 감소로 완료율 향상

🔍 2. 미세한 상호작용의 큰 영향

  • 발견: 클릭 영역 확장 같은 작은 개선이 큰 사용성 향상을 가져옴
  • 적용: 터치 타겟 최소 44px, 호버 효과, 트랜지션 추가
  • 결과: 모바일 사용성 대폭 개선

📊 3. 점진적 정보 공개의 효과

  • 발견: 한 번에 많은 정보를 보여주면 사용자가 압도당함
  • 적용: 접기/펼치기, 단계별 정보 공개
  • 결과: 완료율 상승과 이탈률 감소

🎨 4. 시각적 피드백의 필수성

  • 발견: 사용자는 자신의 행동에 대한 명확한 결과를 원함
  • 적용: 상태별 시각적 구분, 성공/실패 피드백
  • 결과: 사용자 안정감 증가, 재시도 감소

📚 참고 자료


💻 기술 스택: React 18 + Next.js 15 + TypeScript + TailwindCSS + React Hook Form
📅 프로젝트 기간: 2025년 7월
🎯 핵심 성과: 6가지 UX 개선사항 구현 완료, 사용자 편의성 향상
👥 팀 구성: Frontend Developer 1명 (개인 프로젝트)

Updated: