ABOUT ME

-

  • [ Vite + React ] Tag 구현하기 ( onCompositionStart , onCompositionEnd )
    Front/React 2025. 3. 1. 12:22
    반응형

    티스토리와 같은 방식의 태그를 구현


    Input Tag

    onKeyDown함수를 활용하여 Enter와 Tab이 눌렀을때 input을 다시 빈값으로 초기화 해준다.

        const [tag, setTag] = useState("")
    
        const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
            if (e.key === "Enter" || e.key === "Tab") {
                e.preventDefault();
                setTag("");
    
            }
        }

    우선 해시태그와 input으로 태그를 입력할 수 있도록 작성

                <span>#</span>
                <input
                    className="input_tag"
                    placeholder="태그입력"
                    maxLength={10}
                    value={tag}
                    onChange={(e) => setTag(e.target.value)}
                    onKeyDown={onKeyDown}
                />

     

    이후 css로 모양을 잡아준다.

    .tag {
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 3px;
      max-width: 400px;
      flex-wrap: wrap;
    
      span {
        color: #909090;
      }
    
      .input_tag {
        margin-left: 5px;
        border: none;
    
        &:focus {
          outline: none;
        }
      }
    }

    Tag List

    Enter 혹은 Tab을 눌렀을때 입력한 태그를 TagList에 값을 추가해준다.

        const [tag, setTag] = useState("")
        const [tagList, setTagList] = useState<string[]>([])
    
        const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
            if (e.key === "Enter" || e.key === "Tab") {
                e.preventDefault();
                setTagList((prev) => [...prev, tag]);
                setTag("");
    
            }
        }

     

    이후 tagList 의 길이가 0보다 클때 tag list를 보여준다.

    그럼 태그입력 후 Enter혹은 Tab을 누를때마다 tagList에 값이 생성이 되고 입력한 태그가 보이게 된다.

    filter를 활용하여 현재 x 버튼을 누르면 누른 index값을 제외하여 다시 리스트를 만들어서 넣어준다.

        const onClickDelete = (index: number) => {
            const newTagList = tagList.filter((_, idx) => idx !== index);
            setTagList(newTagList);
        }
    tagList.length > 0 && (
                        <div className="tag_list">
                            {tagList.map((tag, index) => (
                                <div className="tag_item" key={index}>
                                    <span>#</span>
                                    <span key={index} className="tag">{tag}</span>
                                    <div className="tag_delete" onClick={() => onClickDelete(index)}>
                                        <CloseIcon/>
                                    </div>
                                </div>
                            ))}
                        </div>
                    )

    Problem

    태그 기능을 구현하면서 한글을 입력하면 onKeyDown이 두번 실행되면서 이전 값이 추가로 들어가게 되었다.

    ex) 안녕 -> #안녕 #녕 이런식으로 값이 들어가게 된다.

     

    이는 IME 입력 방식이 원인이다. IME는 글자를 조합하여 문자를 반드는 방식이다.

    더보기

     

    • 영어 입력: "Enter" 키를 누를 때 한 번만 onKeyDown이 실행됨.
    • 한글 입력: "한" + "ㄱ" -> "한글" 같은 조합 과정이 있기 때문에 onKeyDown이 두 번 실행될 수 있음.

     

    React에서는 onCompositionStart와 onCompositionEnd를 활용하면 IME 입력 중인지 감지할 수 있다.

    이를 활용하여

     

    Input 태그에 onCompositionStart와 End를 활용하여 현재 입력중인지를 판단한다.

    <input
                    className="input_tag"
                    placeholder="태그입력"
                    maxLength={10}
                    value={tag}
                    onChange={(e) => setTag(e.target.value)}
                    onKeyDown={onKeyDown}
                    onCompositionStart={() => setIsComposing(true)} 
                    onCompositionEnd={() => setIsComposing(false)}
                />

     

    이후 입력중이 아닐때만 onKeyDown을 실행시켜준다.

       const [isComposing, setIsComposing] = useState(false);
    
        const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
            if (isComposing) return;
    
            if (e.key === "Enter" || e.key === "Tab") {
                e.preventDefault();
                setTagList((prev) => [...prev, tag]);
                setTag("");
    
            }
        }
    728x90
    반응형