Tech/ReactJS

[리액트/React] 리액트 태그 등록 기능 만들기

닝닝깅 2023. 11. 30. 20:15

구현 화면

태그를 입력한 뒤 엔터를 누르면 등록되는 방식으로 구현하였다. 

태그의 X표시를 누르면 태그가 삭제된다.

 

구현과정

1. 기본 구조 생성

우선 기본적인 디자인을 잡아보자.

입력한 태그를 담을 tags state를 만들어 map 함수를 활용해 뿌린다.

const [tags, setTags] = useState([]);

<TagInputContainer>
  {tags.map((tag, idx) => (
    <TagItem key={idx}>
      <span>{tag}</span>
      <span>&times;</span>
    </TagItem>
  ))}
  <TagInput
    placeholder={tags.length == 0 ? "엔터를 입력하여 태그를 등록해주세요" : ""}
  />
</TagInputContainer>;

 

사용한 스타일 컴포넌트의 css는 다음과 같다. 

const TagInputContainer = styled.div`
  ${flexICenter}
  flex-wrap: wrap;
  gap: 0.5em;
  background-color: ${(props) => props.theme.colors.gray.xxs};
  padding: 0.4rem 1rem;
  border-radius: 0.7rem;
`;

const TagItem = styled.div`
  display: inline-block;
  padding: 0.5em 0.75em;
  border-radius: 20px;
  background-color: ${(props) => props.theme.colors.gray.xs};
  font-size: ${(props) => props.theme.sizes.xs};

  & > span:last-child {
    display: inline-flex;
    justify-content: center;
    align-items: center;
    background-color: rgb(48, 48, 48);
    border-radius: 50%;
    margin-left: 0.3em;
    font-size: ${(props) => props.theme.sizes.xxs};
    color: #fff;
    cursor: pointer;
  }
`;

const TagInput = styled.input`
  flex-grow: 1;
  padding: 0.5em 0;
  background-color: ${(props) => props.theme.colors.gray.xxs};
  border: none;
  outline: none;
  font-family: "Noto Sans KR", sans-serif;
`;

 

2. 태그 등록 함수 구현

 

태그 등록 함수는 엔터를 누르면 tags 배열에 새 태그가 추가되는 방식으로 구현하였다.

 

우선 예외처리를 해야한다.

키보드 이벤트가 엔터가 아닐 경우와 입력 값이 비어있을 경우에는 바로 리턴하여 태그가 추가되지 않도록 하였다.

 

예외 처리를 통과한 값은 태그로 추가하고

다른 태그를 입력 받을 수 있게 input의 값이 ""으로 초기화 시켰다.

const handleKeyDown = (e) => {
  if (e.key != "Enter") return;
  const value = e.target.value;
  if (!value.trim()) return;
  setTags([...tags, value]);
  e.target.value = "";
};

 

 

이 함수를 input의 onKeyDown 이벤트 리스너로 추가한다.

<TagInput
  onKeyDown={handleKeyDown}
  placeholder={tags.length == 0 ? "엔터를 입력하여 태그를 등록해주세요" : ""}
/>;

 

3. 태그 삭제 함수 구현

태그 삭제 함수는 태그의 X버튼을 클릭하면 태그가 tags 배열에서 삭제되는 방식으로 구현하였다.

 

tag의 인덱스값을 인수로 받고

filter함수를 사용하여 해당 인덱스의 항목을 태그 배열에서 제거하였다.

const removeTag = (tagIdx) => {
  setTags(tags.filter((tag, idx) => idx != tagIdx));
};

 

이 함수는 X버튼의 onClick 이벤트 리스너로 달아주었다.

<TagItem key={idx}>
  <span>{tag}</span>
  <span onClick={() => removeTag(idx)}>
    &times;
  </span>
</TagItem>;

 

+ 태그 개수 제한

tags의 길이가 최대 설정 개수보다 커지면 input창이 띄워지지 않도록 하여 개수를 제한하였다.

{tags.length <= MAX_TAG_NUM && (
    <TagInput
      onKeyDown={handleKeyDown}
      placeholder={
        tags.length == 0 ? "엔터를 입력하여 태그를 등록해주세요" : ""
      }
    />
  );
}

 

전체코드

앞서 설명한 함수와 스타일 컴포넌트를 제외한 핵심 전체 코드이다.

<TagInputContainer>
  {tags.map((tag, idx) => (
    <TagItem key={idx}>
      <span>{tag}</span>
      <span onClick={() => removeTag(idx)}>
        &times;
      </span>
    </TagItem>
  ))}
  {tags.length <= MAX_TAG_NUM && (
    <TagInput
      onKeyDown={handleKeyDown}
      placeholder={
        tags.length == 0 ? "엔터를 입력하여 태그를 등록해주세요" : ""
      }
    />
  )}
</TagInputContainer>;

 

더 생각해볼점

입력창보다 긴 태그를 입력했을 때 태그의 형태가 망가진다.!

이렇게 까지 긴 태그를 입력할 일이 없을 것 같기는 하지만,,, 만약 태그 창 자체가 작은 경우라면 길지 않은 태그인 경우에도 사진과 같은 경우가 나타날 수 있기 때문에 고려해볼 필요가 있다고 생각한다.

 


참고

Let's create an Add Tags input with REACT JS