[React]간단한 회원가입 및 유효성 검사

이보아·2024년 6월 13일

유효성 검사란?

사용자가 입력한 정보가 올바른지 확인하는 과정

유효성 검사 중요한 이유

  • 안전성: 비밀번호가 너무 짧거나 간단하면 해킹에 취약해질 수 있다. 그래서 유효성 검사를 통해 안전한 비밀번호를 입력 유도한다.
  • 정확성: 잘못된 정보를 미리 걸러내서 서버에 이상한 정보가 저장되는 것을 막을 수 있다.
  • 사용자 경험: 사용자가 올바른 형식으로 정보를 입력하도록 도와준다.

들어가기에 앞서

아주 간단한 방식으로 구현했고, 시간이 더 있었다면 조금 더 세부적인 유효성 검사를 도전해 봐야겠다.🤓


회원가입 유효성 검사 추가하기

1.회원가입 폼 만들기

기본적인 회원가입을 먼저 작성한다.

import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { loginApi } from '../axios/api';
import styled from 'styled-components';

const SignUpForm = styled.div`
    // 스타일 코드 생략
`;

const Signup = () => {
    // 닉네임, 아이디, 비밀번호, 비밀번호 확인의 상태를 관리
    const [nickname, setNickname] = useState('');
    const [id, setId] = useState('');
    const [password, setPassword] = useState('');
    const [confirmPassword, setConfirmPassword] = useState('');
    const navigate = useNavigate();

    const handleSignUp = async (e) => {
        e.preventDefault(); // 폼 제출 새로고침 방지
      
        // 비밀번호와 비밀번호 확인 내용이 일치하는지 확인
        if (password !== confirmPassword) {
            alert('비밀번호가 일치하지 않습니다. 다시 확인해주세요.');
            return;
        }

        try {
           // 회원가입 API를 호출하여 서버에 데이터를 전송
            const response = await loginApi.post('/register', {
                id,
                password,
                nickname,
            });
          // 회원가입 성공 시 로그인 페이지로 이동
            const data = response.data;
            if (data.success) {
                navigate('/login');
            } else {
                // 실패 시 사용자에게 알림을 표시
                alert('회원가입 실패하였습니다.'); 
            }
        } catch (error) {
            // 에러가 발생하면 콘솔에 출력
            console.error('Signup error:', error);
            // 사용자에게 에러 메시지를 표시
            alert('회원가입 실패하였습니다.');
        }
    };
   // 회원가입 폼을 렌더링
    return (
        <SignUpForm>
            <h1>
                <img src="/logo.png" alt="로고" />
            </h1>
            <form onSubmit={handleSignUp}>
                <label htmlFor="nickname">닉네임</label>
                <input
                    type="text"
                    value={nickname}
                    onChange={(e) => setNickname(e.target.value)}
                    placeholder="닉네임"
                />
                <label htmlFor="id">아이디</label>
                <input type="text" value={id} onChange={(e) => setId(e.target.value)} placeholder="ID" />
                <label htmlFor="password">비밀번호</label>
                <input
                    type="password"
                    value={password}
                    onChange={(e) => setPassword(e.target.value)}
                    placeholder="비밀번호"
                />
                <label htmlFor="confirmPassword">비밀번호 확인</label>
                <input
                    type="password"
                    value={confirmPassword}
                    onChange={(e) => setConfirmPassword(e.target.value)}
                    placeholder="비밀번호 확인"
                />
                <button type="submit">회원가입</button>
            </form>
        </SignUpForm>
    );
};

export default Signup;

2. 유효성 검사 추가하기⭐

닉네임, 아이디, 비밀번호에 대한 유효성 검사를 추가한다.

// 입력된 폼 데이터를 유효성 검사하는 함수 생성
const validateForm = () => {
    const errors = {}; // 오류 메시지를 저장할 객체

    // 닉네임이 입력되지 않았을 경우 오류 메시지를 추가
    if (!nickname.trim()) {
        errors.nickname = '닉네임을 입력해주세요.';
    }

    // 아이디가 입력되지 않았을 경우 오류 메시지를 추가
    if (!id.trim()) {
        errors.id = '아이디를 입력해주세요.';
    }

    // 비밀번호가 입력되지 않았을 경우 오류 메시지를 추가
    if (!password.trim()) {
        errors.password = '비밀번호를 입력해주세요.';
    } else {
        // 비밀번호에 최소 두 개의 영문자가 포함되어 있는지 확인
        const hasTwoLetters = /[a-zA-Z].*[a-zA-Z]/.test(password);
        if (!hasTwoLetters) {
            errors.password = '비밀번호에는 최소 두 개의 영문자가 포함되어야 합니다.';
        }

        // 비밀번호가 최소 6자리 이상인지 확인
        if (password.length < 6) {
            errors.password = '비밀번호는 최소 6자리여야 합니다.';
        }
    }

    // 비밀번호와 비밀번호 확인이 일치하는지 확인
    if (password !== confirmPassword) {
        errors.confirmPassword = '비밀번호가 일치하지 않습니다.';
    }

    return errors; // 오류 메시지를 반환
};

3. 유효성 검사 결과 처리하기⭐

validateForm 함수에서 발생한 오류를 처리하고, 오류가 있을 때는 회원가입 요청을 보내지 않도록 한다.

// 회원가입 버튼이 눌렸을 때 실행되는 함수
const handleSignUp = async (e) => {
    e.preventDefault(); 

    const validationErrors = validateForm(); // 폼 데이터를 유효성 검사

    // 유효성 검사에서 오류가 발견되면 오류 메시지를 상태로 설정하고 함수를 종료함
    if (Object.keys(validationErrors).length > 0) {
        setErrors(validationErrors);
        return;
    }

    try {
        // 서버에 회원가입 요청을 보냄
        const response = await loginApi.post('/register', {
            id, // 입력된 아이디
            password, // 입력된 비밀번호
            nickname, // 입력된 닉네임
        });

        const data = response.data; // 서버로부터의 응답 데이터를 저장

        // 회원가입이 성공하면 로그인 페이지로 이동
        if (data.success) {
            navigate('/login');
        } else {
            // 회원가입이 실패시 경고 메시지
            alert('회원가입 실패하였습니다.');
        }
    } catch (error) {
        // 회원가입 요청 중 오류가 발생하면 콘솔에 오류를 출력하고 경고 메시지 띄움
        console.error('Signup error:', error);
        alert('회원가입 실패하였습니다.');
    }
};

4. 완성된 전체 코드

import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { loginApi } from '../axios/api';
import styled from 'styled-components';

const SignUpForm = styled.div`
// 스타일 코드 생략
`;

const Signup = () => {
    const [nickname, setNickname] = useState(''); // 닉네임 상태
    const [id, setId] = useState(''); // 아이디 상태
    const [password, setPassword] = useState(''); // 비밀번호 상태
    const [confirmPassword, setConfirmPassword] = useState(''); // 비밀번호 확인 상태
    const [errors, setErrors] = useState({}); // 유효성 검사 오류 메시지 상태
    const navigate = useNavigate(); // 페이지 이동을 위한 네비게이트 훅

    // 폼 유효성 검사 함수
    const validateForm = () => {
        const errors = {}; // 오류 메시지를 담을 객체

        // 닉네임이 비어 있는지 확인
        if (!nickname.trim()) {
            errors.nickname = '닉네임을 입력해주세요.';
        }

        // 아이디가 비어 있는지 확인
        if (!id.trim()) {
            errors.id = '아이디를 입력해주세요.';
        }

        // 비밀번호가 비어 있는지 확인
        if (!password.trim()) {
            errors.password = '비밀번호를 입력해주세요.';
        } else {
            // 비밀번호에 최소 두 개의 영문자가 포함되어 있는지 확인
            const hasTwoLetters = /[a-zA-Z].*[a-zA-Z]/.test(password);
            if (!hasTwoLetters) {
                errors.password = '비밀번호에는 최소 두 개의 영문자가 포함되어야 합니다.';
            }
            // 비밀번호가 최소 6자리 이상인지 확인
            if (password.length < 6) {
                errors.password = '비밀번호는 최소 6자리여야 합니다.';
            }
        }

        // 비밀번호와 비밀번호 확인이 일치하는지 확인
        if (password !== confirmPassword) {
            errors.confirmPassword = '비밀번호가 일치하지 않습니다.';
        }

        return errors; // 오류 메시지 객체 반환
    };

    // 회원가입 버튼이 눌렸을 때 실행되는 함수
    const handleSignUp = async (e) => {
        e.preventDefault(); // 폼의 기본 제출 동작을 막음

        const validationErrors = validateForm(); // 폼 데이터를 유효성 검사
        // 유효성 검사에서 오류가 발견되면 오류 메시지를 상태로 설정하고 함수를 종료
        if (Object.keys(validationErrors).length > 0) {
            setErrors(validationErrors);
            return;
        }

        try {
            // 서버에 회원가입 요청을 보냄
            const response = await loginApi.post(
                '/register',
                {
                    id, // 입력된 아이디
                    password, // 입력된 비밀번호
                    nickname, // 입력된 닉네임
                },
                {
                    headers: {
                        'Content-Type': 'application/json',
                    },
                }
            );

            const data = response.data; // 서버로부터의 응답 데이터를 저장

            // 회원가입이 성공하면 로그인 페이지로 이동
            if (data.success) {
                navigate('/login');
            } else {
                // 회원가입이 실패하면 경고 메시지를 띄움
                alert('회원가입 실패하였습니다.');
            }
        } catch (error) {
            // 회원가입 요청 중 오류가 발생하면 콘솔에 오류를 출력하고 경고 메시지를 띄움
            console.error('Signup error:', error);
            alert('회원가입 실패하였습니다.');
        }
    };

    return (
        <SignUpForm>
            <h1>
                <img src="/logo.png" alt="로고" />
            </h1>
            <form onSubmit={handleSignUp}>
                <label htmlFor="nickname">닉네임</label>
                <input
                    type="text"
                    value={nickname}
                    onChange={(e) => setNickname(e.target.value)}
                    placeholder="닉네임"
                />
                {errors.nickname && <p className="notice">{errors.nickname}</p>}
                <label htmlFor="id">아이디</label>
                <input type="text" value={id} onChange={(e) => setId(e.target.value)} placeholder="ID" />
                {errors.id && <p className="notice">{errors.id}</p>}
                <label htmlFor="password">비밀번호</label>
                <input
                    type="password"
                    value={password}
                    onChange={(e) => setPassword(e.target.value)}
                    placeholder="비밀번호"
                />
                {errors.password && <p className="notice">{errors.password}</p>}
                <label htmlFor="confirmPassword">비밀번호 확인</label>
                <input
                    type="password"
                    value={confirmPassword}
                    onChange={(e) => setConfirmPassword(e.target.value)}
                    placeholder="비밀번호 확인"
                />
                {errors.confirmPassword && <p className="notice">{errors.confirmPassword}</p>}
                <button type="submit">회원가입</button>
            </form>
        </SignUpForm>
    );
};

export default Signup;

profile
매일매일 틀깨기

0개의 댓글