[웨일마켓] 회원가입 이메일 검증

Moon Hee·2022년 7월 11일
0

트러블 슈팅

목록 보기
16/26

📰 명세 내용

이메일 주소 또는 비밀번호를 입력하고 입력창에서 포커스를 잃으면 바로 유효성 검사가 진행되고 통과하지 못한 경우 경고 문구가 각 입력창 하단에 표시됩니다.

🤔 어떻게 이메일 검증했더라

중복된 이메일을 회원가입 폼에서 체크하고 사용 가능한지도 알려주는 일반적인 회원가입 폼이다. 포커스를 잃으면 바로 사용 가능한지 여부를 알려주는 기능을 넣어야 한다. 버튼을 클릭했을 때는 이미 검증완료된 값을 서버에 전달해야 한다.

그래서 onSubmit이 아니라 onBlur에서 HTTP 요청을 보내면 인풋에서 포커스 잃을 때 검증가능하다. 반응이 좀 느린 것 같은 느낌이 든다..?
로그인 폼에서는 onSubmit 속성에서 HTTP 요청을 보냈어서 쉬울 줄 알았는데 API 연결은 한번에 잘 안되는 것 같다.

이메일 input에만 onBlur에 HTTP요청 코드를 줬는데, 비밀번호 input에도 똑같이 적용할까 고민했다. 내 생각에는 보통 비밀번호까지 입력하면 바로 엔터를 누르거나 버튼을 누르기도 하고 처음부터 비밀번호가 몇자 이상이어야 하는지 유저에게 정보를 제공하는 편이 더 나은것 같아서 onBlur를 주지 않았다. 하지만 명세에는 비밀번호 input에도 포커스를 잃으면 검증결과가 나오게 만들어 달라고 나와있다. 어떤 결정이 더 나은지는 회원가입 기능을 더 업그레이드 시키면서 생각해봐야될 것 같다.

import { useRef, useState, useEffect, useContext } from 'react';
import AuthContext from "../../context/AuthProvider";
import axios from 'axios';
import { API_URL } from '../../constants/defaultUrl';
import styled from 'styled-components';
import Button from '../../components/login/Button';

const Join = (props) => {
    const { setAuth } = useContext(AuthContext);
    const emailRef = useRef();
    const errorRef = useRef();

    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [errorMessage, setErrorMessage] = useState('');
    const [success, setSuccess] = useState(false);
    const [notMatchError, setNotMatchError] = useState('');
    
    useEffect(() => {
        emailRef.current.focus();
    }, []);

    useEffect(() => {
        setErrorMessage('');
    }, [email, password]);

    const handleSubmit = async (event) => {
        event.preventDefault();
        try {
            const reqData = {
                user: { 
                    email: email
                }
            };
            const config = {
                headers: {
                    "Content-Type": "application/json",
                },
            };
            const response = await axios.post(
                `${API_URL}/user/emailvalid`,
                reqData,
                config
            );

            const accessToken = response?.data?.accessToken;
            const roles = response?.data?.roles;
            setAuth({ email, password, roles, accessToken });
            setEmail('');
            setPassword('');
            setSuccess(true);

            if (response?.data?.status === 422) {
                setSuccess(false);
                setNotMatchError(response.data.message);
            }

        } catch (error) {
            if (!error?.response) {
                setErrorMessage('서버가 응답하지 않습니다.');
            } else if (error.response?.status === 400) {
                setErrorMessage('이메일 또는 비밀번호가 일치하지 않습니다.');
            } else if (error.response?.status === 401) {
                setErrorMessage('Unauthorized');
            } else {
                setErrorMessage('로그인 실패');
            }
            errorRef.current.focus();
        }
    };

    const handleOnBlur = async (event) => {
        try {
            const reqData = {
                user: { 
                    email: email
                }
            };
            const config = {
                headers: {
                    "Content-Type": "application/json",
                },
            };
            const response = await axios.post(
                `${API_URL}/user/emailvalid`,
                reqData,
                config
            );
            setSuccess(true);
            if (response?.data?.message === "이미 가입된 이메일 주소 입니다.") {
                setSuccess(false);
                setNotMatchError('*' + response.data.message);
            } else if (response?.data?.message === "사용 가능한 이메일 입니다.") {
                setSuccess(false);
                setNotMatchError('*' + response.data.message);
            }
        } catch (error) {
            console.error(error);
            errorRef.current.focus();
        }
    };

    // 버튼 활성상태 관리
    const [buttonOn, setButtonOn] = useState(false);
    const emailRegex = /^[a-zA-Z0-9+-\_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/;

    const isPassedJoin = () => {
        return emailRegex.test(email) && password.length > 5 ? setButtonOn(true) : setButtonOn(false);
    };

    return (
        <>
            <Wrapper>
                <Title>이메일로 회원가입</Title>
                <Form onSubmit={handleSubmit}>
                    <div>
                            <Label htmlFor='email'>이메일
                                <Input
                                    type='email'
                                    id='email'
                                    ref={emailRef}
                                    autoComplete='off'
                                    onChange={(event) => setEmail(event.target.value)}
                                    required
                                    onKeyUp={isPassedJoin}
                                    onBlur={handleOnBlur}
                                />
                                {(email.length > 5) 
                                && !emailRegex.test(email) 
                                && <ErrorMessage>*올바르지 않은 이메일 형식입니다.</ErrorMessage>
                                }
                                {notMatchError && <ErrorMessage>{notMatchError}</ErrorMessage>}
                            </Label>
                    </div>
                    <div>
                            <Label htmlFor='password'>비밀번호
                                <Input
                                    type='password'
                                    id='password'
                                    onChange={(event) => setPassword(event.target.value)}
                                    require
                                    value={password}
                                    required
                                    onKeyUp={isPassedJoin}
                                />
                                {(password.length < 6) && <ErrorMessage>*비밀번호는 6자 이상이어야 합니다.</ErrorMessage>}
                            </Label>
                    </div>
                    <Button 
                        type='submit' 
                        text='다음'
                        style={{ backgroundColor: buttonOn ? '#00BCD4' : '#B2EBF2' , border: '0px', fontWeight: '500', fontSize: '14px', color: 'white'}}
                    />
                </Form>
            </Wrapper>
        </>
    );
}

export default Join;

🔫 고쳐야할 점

  • handleSubmit과 handleOnBlur의 공통 코드는 묶어서 처리할 예정
  • 버튼 컴포넌트를 무조건 하나로 가져가려고 했다가 코드가 너무 복잡해졌다. 버튼 컴포넌트를 페이지별로 따로 가져갈지 하나를 잘 만들어서 계속해서 쓸지(가능?) 고민이 필요하다! 부끄러우니까 컴포넌트 좀 정갈하게 만들어놓고 멘토님께 여쭤봐야 겠다.
profile
프론트엔드 개발하는 사람

0개의 댓글