React 회원가입 구현하기

피시본·2022년 10월 25일
8

리액트로 회원가입 폼 구현하기

10월 3일부터 10월 21일까지 3주간 2차 프로젝트가 진행되었다.
공통 주제는 환경 문제였고 우리 팀은 여러 논의 끝에 제로웨이스트 샵을 주제로 정했다. 지도로 사용자 주변에 있는 제로웨이스트 샵을 띄우고 가게에 대한 리뷰 작성과 찜하기 등 여러 기능을 구현하기로 했다.

이 프로젝트에서 내가 맡은 부분은 회원가입 / 로그인 / 간편 로그인 구현

그 중 회원가입부터 정리해보려고 한다. 구현하기에 앞서 조건부터 정리했다.

  • 유효성 검사
  • 이메일, 닉네임 중복 확인
  • 약관 동의
  • 위 사항이 모두 통과되어야 가입 버튼 활성화

1-1. 이메일 / 비밀번호 / 닉네임 유효성 검사

  const validateEmail = (email) => {
    return email
      .toLowerCase()
      .match(/([\w-.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/);
  };

  const validatePwd = (password) => {
    return password
      .toLowerCase()
      .match(/^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{10,25}$/);
  }

  const validateNickname = (nickname) => {
    return nickname
      .toLowerCase()
      .match(/^[ㄱ-ㅎ|가-힣|a-z|A-Z|0-9|].{1,8}$/)
  }

1-2. 유효성 검사로 이메일 / 비밀번호 / 닉네임 걸러주기

const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPwd, setConfirmPwd] = useState("");
const [nickname, setNickname] = useState("");

const [emailMsg, setEmailMsg] = useState("");
const [pwdMsg, setPwdMsg] = useState('');
const [confirmPwdMsg, setConfirmPwdMsg]= useState("")
const [nicknameMsg, setNicknameMsg] = useState("")


// 1-1에 잡아뒀던 유효성 검사 함수로 정리하기
const isEmailValid = validateEmail(email);
const isPwdValid = validatePwd(password);
const isConfirmPwd = password === confirmPwd;
const isNicknameValid = validateNickname(nickname);


  //이메일 
  const onChangeEmail = useCallback( async (e) => {
    const currEmail = e.target.value;
    setEmail(currEmail);

    if (!validateEmail(currEmail)) {
      setEmailMsg("이메일 형식이 올바르지 않습니다.")
    } else {
        setEmailMsg("올바른 이메일 형식입니다.")
      }
    })

    //비밀번호
    const onChangePwd = useCallback((e) =>{
      const currPwd = e.target.value;
      setPassword(currPwd);

      if (!validatePwd(currPwd)) {
        setPwdMsg("영문, 숫자, 특수기호 조합으로 10자리 이상 입력해주세요.")
      } else {
        setPwdMsg("안전한 비밀번호입니다.")
      }
    }, [])

    //비밀번호 확인
    const onChangeConfirmPwd = useCallback((e) => {
      const currConfirmPwd = e.target.value;
      setConfirmPwd(currConfirmPwd);

      if (currConfirmPwd !== password) {
        setConfirmPwdMsg("비밀번호가 일치하지 않습니다.")
      } else {
        setConfirmPwdMsg("올바른 비밀번호입니다.")
      }
    }, [password])

    //닉네임
    const onChangeNickname = useCallback((e) => {
      const currNickname = e.target.value;
      setNickname(currNickname);

      if (!validateNickname(currNickname)) {
        setNicknameMsg("1글자 이상 9글자 미만으로 입력해주세요.")
      } else {
        setNicknameMsg("올바른 닉네임 형식입니다.")
      }
    }, []);
      
// 이런 식으로 return 부분에서 삼항 연산자를 이용해 클래스 네임을 지정해주고 
// 클래스네임에 따라 밑에 나오는 문구를 달리 나오게 구현했다. 
<ResisterStyled.OutputText className={isEmailValid ? 'success' : 'error'}>
{emailMsg}</ResisterStyled.OutputText> ...(중략)

  1. 이메일 / 닉네임 중복 확인
    백엔드를 맡으신 분과 이런 저런 논의 끝에 백엔은 프엔에서 axios post로 사용자가 쓴 이메일과 닉네임 값을 보내주면 result 값을 불리언으로 뱉어주는 로직을 짜고 프엔은 사용자가 버튼을 누르면 이 불리언 값으로 중복인지 아닌지 걸러주는 로직을 짜기로 했다.
const [checkMail, setCheckMail] = useState(false)
const [checkNickname, setCheckNickname] = useState(false)

  const onCheckEmail = async (e) => {
    e.preventDefault();

    try { 
      const res = await Api.post("user/register/email", {email});

      const { result } = res.data;

      if (!result) {
          setEmailMsg("이미 등록된 메일입니다. 다시 입력해주세요.");
          setCheckMail(false);
      } else {
        setEmailMsg("사용 가능한 메일입니다.😊");
        setCheckMail(true);
      }

    } catch (err) {
      console.log(err);
    }
  }

  const onCheckNickname = async (e) => {
    e.preventDefault();

    try { 
      const res = await Api.post("user/register/nickname", {nickname});

      const { result } = res.data;

      if (!result) {
          setNicknameMsg("이미 등록된 닉네임입니다. 다시 입력해주세요.");
          setCheckNickname(false);
     } else {
        setNicknameMsg("사용 가능한 닉네임입니다.😊");
        setCheckNickname(true);
      }

    } catch (err) {
      console.log(err);
    }
  }


  1. 약관 동의 모달창
/RegisterForm.jsx

const [isAccepted, setIsAccpted] = useState(false);

const handleCheckAccept = useCallback(() => {
      setIsAccpted(true);
    }, []);
    
// 모달창 컴포넌트를 따로 만들어 주었기에 callback props를 사용했다.


/CheckModal.jsx

import React, {useState} from "react";
import * as ModalStyled from "../StyledComponents/ModalStyled";

function CheckModal ( {onCheckAccept, isAccepted, setIsAccpted }) {
    const [openModal, setOpenModal] = useState(false);

    const showModal = () => {
        setOpenModal(true);
    }
    const closeModal = () => {
        setOpenModal(false);
        setIsAccpted(false);
    }
 
    const onSubmitAccept = () => {
        closeModal();
        onCheckAccept();
    }

    return (
        <>
            <ModalStyled.ModalButton>
              <span onClick={showModal} className={isAccepted? 'yes' : 'no'}>약관 동의 *</span>
            </ModalStyled.ModalButton>
            {openModal ? <ModalStyled.ModalForm>
                <h1>정보 이용 약관 동의</h1>
                <p>1. 탈퇴 시 회원 정보는 보류 처리되어 14일간 보관됩니다. 이 기간 동안 동일한 메일 주소로 회원가입을 할 시 가입이 제한되며, 탈퇴 취소를 원하시는 경우 취소 처리도 가능합니다.</p>
                <p>
                2. 탈퇴하더라도 작성하신 리뷰는 계속 남아 있게 됩니다. 작성한 리뷰를 제외한 모든 회원 정보는 삭제되며 남아 있는 리뷰의 작성자명은 '(익명)'으로 처리됩니다. </p>
                <p>약관에 동의하십니까? </p>
                <button onClick={closeModal} className="left-btn">이전</button>
                <button onClick={onSubmitAccept}>동의</button>
            </ModalStyled.ModalForm> : null}
        </>
    )
}

export default CheckModal;

가입 폼과 마찬가지로 약관 동의를 하면 isAccepted가 true가 되어 className이 'yes'가 되고 아니면 'no'여서 글자 색깔이 동의 여부에 따라 변하게 구현했다.

  1. 가입 버튼 활성화
// 앞에 정리한 유효성 검사를 한번에 묶어주고 
const isAllValid = isEmailValid && isPwdValid && isConfirmPwd && isNicknameValid && isAccepted && checkMail && checkNickname;

// return 부분에서 disabled 값으로 제어해주었다.
<ResisterStyled.FootBtnBox>
<ResisterStyled.FootButton onClick={onSubmit} type="submit" disabled={!isAllValid}> 
	가입하기 
</ResisterStyled.FootButton>
</ResisterStyled.FootBtnBox>

아쉬운 점

  1. 이메일 인증
    진짜 존재하는 이메일인지 이메일 인증을 구현하자고 초반에 이야기가 나왔는데 시간이 없어 방안만 생각하고 직접 코드로 짜지 못했다. 가입 절차 중 사용자가 이메일 인증 버튼을 누르거나 회원가입 절차가 전부 끝났을 때 입력한 주소로 인증 메일이 갔다고 알린다 -> 메일을 눌러 보면 안에 우리 홈페이지로 돌아오는 링크가 걸려 있어 링크를 누르면 인증이 완료되는 식으로 구현해보고 싶었다.
    그런데 의문 🤔
    유효성 검사와 중복 확인, 약관 동의까지 전부 거쳤는데 또 이메일 인증을 하라고 하면 사용자 입장에서 너무 귀찮을 것 같다 . . . 구현할 시간이 있었다면 이건 또 이것대로 팀원들과 충분한 논의를 거쳐야 할 문제로 보인다.

  2. 중복일 때 뜨는 메시지 색깔
    메일이든 닉네임이든 중복일 때 이미 등록된 ~라고 밑에 메시지를 띄워 준다. 색깔은 초록색이다.
    (1) isEmailValid에 조건을 하나 더 걸자니 중복 확인을 하고 다시 메일을 칠 때 나오는 '올바른 이메일 형식입니다.' 문구가 빨간색이다.
    (2) 삼항 연산자를 하나 더 걸어 색을 구분하려 했더니 여러 조건으로 테스트를 해봤을 때 앞 클래스에 따라 뒤 클래스 색이 뒤바뀌어 나오는 등의 문제가 생겼다.
    결국 중복 확인 메시지는 초록색으로 띄워주게 되었다. 중복 확인 로직을 까먹고 있다가 중간에 투입한 거라 이런 문제가 생긴 걸지도 모르겠다. 다음에는 로직을 처음부터 제대로 짜서 이런 문제가 없도록 해봐야겠다.

className={` ${isEmailValid ? 'success' : 'error'} ${checkMail? 'checked' : 'not-checked'}`}

(😢혹시 해결 방안을 아시는 분이 계시다면 댓글 부탁드립니다. 감사합니다.😢)

배운 점

  1. 사용자 입장과 서비스를 제공하는 입장
    사용자가 입력하는 메일 주소가 중복인지 아닌지 실시간으로 걸러주는 기능을 구현해보고 싶었는데 백엔드 파트너 분이 그렇게 하면 한 글자씩 검색해야 해서 서버 비용이 많이 들 거라고 하셨다. 생각해보니 나는 사용자 입장에서 버튼을 누르지 않고도 알아서 걸러주니 간편하다고 생각해서 이 기능을 고려한 거였는데 웹 서비스를 제공하는 입장에서는 최대한 비용을 줄여야 하니 내가 생각한 방식이 쉽게 다른 방식으로 대체될 수 있음을 깨달았다.
    사이 균형을 어떻게 맞추며 서비스를 구현해야할지 여러 각도로 생각해보자 다짐했던 계기다.
profile
Hello, World!

0개의 댓글