이메일 중복 검사와 검증 상태 관리: 사용자 실수 방지 가이드

oversleep·2025년 2월 28일
0

app-development

목록 보기
31/38
post-thumbnail

목차

이메일 검증의 중요성

이메일은 대부분의 웹 서비스와 앱에서 사용자 식별 및 인증의 핵심 요소입니다.
회원가입 과정에서 이메일 주소가 유효하고 중복되지 않는지 확인하는 것은 매우 중요합니다.
하지만 많은 애플리케이션에서 이메일 검증 과정에 허점이 있어 사용자 경험을 저하시키고 보안 위험을 초래할 수 있습니다.

일반적인 이메일 검증 과정의 문제점

기존의 많은 이메일 검증 시스템은 다음과 같은 흐름으로 동작합니다:

  1. 사용자가 이메일 입력
  2. '중복 확인' 버튼을 클릭
  3. 시스템이 이메일 유효성 및 중복 여부 확인
  4. 결과 표시

하지만 여기에는 중요한 보안 허점이 존재합니다:

사용자가 중복 확인 후 이메일을 변경하는 경우, 시스템은 이를 감지하지 못하고 이미 검증된 것으로 간주할 수 있습니다. 이로 인해:

  • 실제로는 검증되지 않은 이메일로 가입이 완료될 위험
  • 중복된 이메일 주소가 시스템에 등록될 가능성
  • 사용자 오류로 인한 계정 접근 문제

개선된 이메일 검증 시스템 구현하기

이러한 문제를 해결하기 위해, 개선된 이메일 검증 시스템은 다음 기능을 포함해야 합니다:

  1. 이메일 변경 감지: 검증 후 이메일이 변경되면 검증 상태 초기화
  2. 검증된 이메일 저장: 검증에 성공한 이메일 값을 별도로 저장
  3. 상태 비교 로직: 제출 시 현재 이메일과 검증된 이메일 비교
  4. 시각적 피드백: 검증 상태를 사용자에게 명확히 표시

코드 분석: EmailVerificationInput 컴포넌트

이제 이러한 개선 사항을 구현한 React Native 컴포넌트를 살펴보겠습니다:

const EmailVerificationInput = ({
  email,
  onEmailChange,
  onVerificationStatusChange,
}: EmailVerificationInputProps) => {
  const [emailCheckMessage, setEmailCheckMessage] = useState<string>("");
  const [isEmailVerified, setIsEmailVerified] = useState(false);
  const [verifiedEmail, setVerifiedEmail] = useState("");

  const handleEmailChange = (text: string) => {
    onEmailChange(text);

    // 이메일이 변경되면 검증 상태 초기화
    if (text !== verifiedEmail) {
      setIsEmailVerified(false);
      setEmailCheckMessage("");
      onVerificationStatusChange(false, "");
    }
  };

  const handleCheckEmailDuplicate = async () => {
    // 이메일 형식 유효성 검사
    const formatResult = validateEmailFormat(email);
    if (!formatResult.isValid) {
      setEmailCheckMessage(formatResult.message);
      return;
    }

    // 이메일 중복 검사
    const result = await checkEmailDuplicate(email);
    setEmailCheckMessage(result.message);

    if (result.isValid) {
      setIsEmailVerified(true);
      setVerifiedEmail(email);
      onVerificationStatusChange(true, email);
    } else {
      setIsEmailVerified(false);
      onVerificationStatusChange(false, "");
    }
  };

  return (
    <>
      <View style={styles.emailInputContainer}>
        <TextInput
          style={[
            styles.emailInput,
            isEmailVerified && { borderColor: "green", borderWidth: 1 },
          ]}
          value={email}
          onChangeText={handleEmailChange}
          placeholder="example@gmail.com"
          placeholderTextColor="#666"
        />
        <TouchableOpacity
          style={[
            styles.button,
            isEmailVerified && email === verifiedEmail
              ? { backgroundColor: "green" }
              : { backgroundColor: colors.primary },
          ]}
          onPress={handleCheckEmailDuplicate}
        >
          <Text style={styles.buttonText}>
            {isEmailVerified && email === verifiedEmail
              ? "✓ 확인됨"
              : "중복 확인"}
          </Text>
        </TouchableOpacity>
      </View>
      {emailCheckMessage ? (
        <Text style={styles.errorText}>{emailCheckMessage}</Text>
      ) : null}
    </>
  );
};

핵심 개선 사항 해설

  1. 이중 상태 관리:

    • isEmailVerified: 검증 완료 여부 (boolean)
    • verifiedEmail: 검증에 성공한 이메일 값 (string)
  2. 이메일 변경 감지 로직:

    // 이메일이 변경되면 검증 상태 초기화
    if (text !== verifiedEmail) {
      setIsEmailVerified(false);
      setEmailCheckMessage("");
      onVerificationStatusChange(false, "");
    }

    이메일 값이 변경되면 즉시 검증 상태를 초기화합니다.

  3. 검증 성공 시 이메일 저장:

    if (result.isValid) {
      setIsEmailVerified(true);
      setVerifiedEmail(email);
      onVerificationStatusChange(true, email);
    }

    검증에 성공한 경우에만 해당 이메일을 verifiedEmail 상태에 저장합니다.

  4. 부모 컴포넌트 상태 업데이트:

    onVerificationStatusChange(true, email);

    부모 컴포넌트에 검증 상태와 검증된 이메일을 함께 전달합니다.

  5. 시각적 피드백:

    style={[
      styles.emailInput,
      isEmailVerified && { borderColor: "green", borderWidth: 1 },
    ]}

    검증 완료 시 입력 필드 테두리를 녹색으로 변경하여 사용자에게 시각적 피드백을 제공합니다.

이메일 유효성 검증 및 중복 확인 로직

이 컴포넌트는 두 가지 유틸리티 함수에 의존합니다:

  1. validateEmailFormat: 이메일 형식 유효성 검사
  2. checkEmailDuplicate: 서버 API를 통한 이메일 중복 확인
// 이메일 형식 검증 함수
export const validateEmailFormat = (email: string): EmailVerificationResult => {
  if (!email) {
    return {
      isValid: false,
      message: "이메일을 입력해주세요.",
    };
  }

  const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
  if (!emailRegex.test(email)) {
    return {
      isValid: false,
      message: "유효한 이메일 형식이 아닙니다.",
    };
  }

  return {
    isValid: true,
    message: "",
  };
};

// 이메일 중복 검사 함수
export const checkEmailDuplicate = async (
  email: string
): Promise<EmailVerificationResult> => {
  try {
    const response = await axiosInstance.post("/member/check-email", {
      email: email,
    });

    if (response.data === false) {
      return {
        isValid: false,
        message: "이미 사용 중인 이메일입니다.",
      };
    } else {
      return {
        isValid: true,
        message: "사용 가능한 이메일입니다.",
      };
    }
  } catch (error) {
    // 오류 처리 로직
    // ...
  }
};

실제 구현 시 고려사항

이 개선된 이메일 검증 시스템을 구현할 때 고려해야 할 추가 사항들:

  1. 디바운스(Debounce) 적용:
    이메일 입력 시 매 타이핑마다 상태를 변경하기보다는 약간의 지연을 두어 성능 최적화

  2. 토큰 기반 검증:
    서버에서 검증된 이메일에 대한 임시 토큰을 발급하고, 회원가입 완료 시 함께 전송하여 추가적인 보안 강화

  3. 이메일 자동 완성 처리:
    브라우저/기기의 자동 완성 기능이 작동할 때도 검증 상태가 올바르게 초기화되도록 처리

  4. 네트워크 오류 처리:
    네트워크 불안정으로 인한 중복 검사 실패 시 적절한 오류 메시지와 재시도 옵션 제공

  5. 접근성 고려:
    시각적 피드백 외에도 스크린 리더 사용자를 위한 접근성 메시지 제공


이러한 개선된 이메일 검증 시스템을 구현함으로써, 사용자가 중복 확인 후 이메일을 변경하는 실수를 방지하고, 더 안전하고 신뢰할 수 있는 회원가입 프로세스를 제공할 수 있습니다.

profile
궁금한 것, 했던 것, 시행착오 그리고 기억하고 싶은 것들을 기록합니다.

0개의 댓글