Hengxi's 개발 블로그

[React] 이메일 자동완성 기능 구현하기 본문

개발/React

[React] 이메일 자동완성 기능 구현하기

HENGXI 2022. 10. 9. 14:20

현재 진행하는 프로젝트에서 이메일 자동완성 기능을 필요로 하여 기능 구현을 해보았다.
처음에는 구글링을 하면 간단하게 구현할 수 있을 것이라고 생각했지만, 이메일 자동 완성 기능을 구현해 놓은 코드는 없었다...
그래서 HTML에 datalist 태그를 이용하여 간단하게 구현을 했는데, 안타깝게도 스타일을 줄 수 없어 빠꾸 먹고... javascript로 열심히 기능을 구현했다.

아래는 쿠팡의 로그인 화면으로 이메일 자동완성 기능을 쿠팡처럼 구현하고자 했다.

쿠팡 로그인 이메일 자동완성 기능

자주 사용하는 이메일 리스트와 기능 구현에 필요한 state 선언

  const FrequencyEmails = [
    '@naver.com',
    '@gmail.com',
    '@daum.net',
    '@hanmail.net',
    '@yahoo.com',
    '@outlook.com',
    '@nate.com',
    '@kakao.com',
  ];
  const [email, setEmail] = useState(defaultEmail); //이메일 input 값
  const [emailList, setEmailList] = useState(FrequencyEmails); //추천 이메일 리스트를 확인, 이메일 리스트 상태 관리
  const [selected, setSelected] = useState(-1); //키보드 선택
  const [isDrobBox, setIsDropbox] = useState(false); // 드롭박스 유무
  const inputRef = useRef(); //외부클릭 감지 확인

함수 만들기

외부 클릭 감지

  useEffect(() => {
    const handleClickOutside = (e) => {
      if (
        inputRef.current &&
        !inputRef.current.contains(e.target)
      ) {
        setIsDropbox(false);
      }
    };

    document.addEventListener('mousedown', handleClickOutside);
  }, [inputRef]);

input 태그와 dropbox를 감싸고 있는 div에 ref를 주었다.(input에 ref를 주고 dropbox를 선택하려 하면 dropbox가 사라짐)
useEffect를 활용하여 외부 클릭(mousedown) 감지, ref를 벗어난 클릭이면 isDropBox를 false로 바꿔주었다.

이메일 값 담기 드롭박스 상태 관리

  const onChangeEmail = (e) => {
    setEmail(e.target.value);

    if (e.target.value.includes('@')) {
      setIsDropbox(true);
      setEmailList(
        FrequencyEmails.filter((el) =>
          el.includes(e.target.value.split('@')[1]),
        ),
      );
    } else {
      setIsDropbox(false);
      setSelected(-1);
    }
  };

input에 onChange이벤트로 email state에 값을 담아 주는 동시에 isDropBox를 true로 바꿔주었다. @를 입력해야 이메일 리스트가 나오도록 구현하기 위해 if문을 사용하여 분기 처리를 했다.
input창에 한 글자만 써도 이메일을 리스트업 해주는 사이트들이 있는데, 이러한 구현을 원한다면 if문의 조건을 조금만 수정해주면 될 듯
emailList에는 입력된 값을 포함하는 List만을 걸러주기 위해 filter와 includes를 사용, 입력 값 중 @ 이후만 필요하니 split을 사용하였다.

드롭박스 클릭 선택

  const handleDropDownClick = (first, second) => {
    setEmail(`${first.split('@')[0]}${second}`);
    setIsDropbox(false);
    setSelected(-1);
  };

드롭박스(이메일 리스트) 마우스로 클릭하면 input에 클릭 값이 들어가는 함수이다. 2개의 파라미터를 받아 합쳐 주고 있는데 첫 번째가 전체 input값이기 때문에 @ 앞부분만 가져오기 위하여 split을 사용('test@na'라는 값을 input에 입력 후 드롭박스 선택했을 때 첫 번째 값이 test@na가 되기때문), 두 번째 파라미터는 선택한 emailList 값이 들어가게 된다.
클릭하면 input에 입력 후 드롭박스를 제거해주기 위해 dropBox 상태를 false로 바꿔주고, selected 값도 원위치시켜준다.(아래에서 추가 설명)

키보드로 드롭박스 선택

  const handleKeyUp = (e) => {
    if (isDrobBox) {
      if (e.key === 'ArrowDown' && emailList.length - 1 > selected) {
        setSelected(selected + 1);
      }
      //emailList.length에 -1을 해주는 이유는 selected의 최대값을 맞춰주기 위해서이다.
      //예를들어 밑에 emailList 2개가 나왔다고 가정했을 때, selected값이 최대 1까지 변할 수 있게 해줘야한다. 
      //'ArrowDown'키를 누르면 selected는 0이 되고, 한번 더 누르면 1이 되고, 그 다음은 더이상 옵션이 없기 때문에 키가 안먹히게 해주는 것이다.

      if (e.key === 'ArrowUp' && selected >= 0) {
        setSelected(selected - 1);
      }
      if (e.key === 'Enter' && selected >= 0) {
        handleDropDownClick(email, emailList[selected]);
      }
    }
  };

input 값에 @가 있을 때(isDropBox가 true일 경우 / 드롭박스가 보일 경우), 키보드로 emailList를 선택할 수 있게 해주는 함수이다.
selected에 대한 설명은 코드 부분에 적어 두었다. 처음 조건을 이해하였다면 그다음은 바로 이해가 될 것이다.
Enter키를 누르면 handleDropDownClick 함수가 불리기 때문에 인자로 email과 선택된 emailList의 값을 보내준다.

전체 코드

<div ref={inputRef}>
    <input
        type="email"
        placeholder="이메일(아이디) 입력"
        value={email}
        onChange={(e) => {
        onChangeEmail(e);
        }}
        onKeyUp={handleKeyUp}
    />
    {isDrobBox && (
      <MailTipUl>
        {emailList.map((item, idx) => (
          <MailTipLi
            key={idx}
            onMouseOver={() => setSelected(idx)}
            onClick={() => handleDropDownClick(email, item)}
            selected={selected === idx}
          >
            {email.split('@')[0]}
            {item}
          </MailTipLi>
        ))}
      </MailTipUl>
    )}
</div>

드롭박스 li 태그 css

const MailTipLi = styled.li`
  background-color: ${({ selected }) => (selected ? '#f5f5f5' : '')};
  color: ${({ selected }) => (selected ? 'var(--zu--m4-color)' : '')};
`;

관련없는 코드는 지우고 올렸습니다.

isDropBox가 true일 경우 드롭박스가 보이도록 설정했다. 처음에는 hover로도 css를 주었는데, 키보드로 List를 고를 때와 마우스로 고를 때 스타일이 둘 다 먹어 고민하다 onMouseOver 이벤트를 이용하여 selected 상태를 바꿔주도록 했다.
selected가 idx와 같을 경우 스타일이 먹도록 설정!
그리고 li 태그의 값이 {email}이 아니고 {email.split('@')[0]}{item} 인 것은 위에서 설명하였으니 패스! 쳐보면 바로 알 수 있다.

완성된 이메일 자동 완성
Comments