Skip to content

먼저 할 일과 이어서 할 일을 자동완성으로 추가해보자 😌

NaGyeong Park edited this page Dec 19, 2022 · 2 revisions

🥰 완성본

검색기능구현


자동완성 검색창을 구현하게 된 이유

스크린샷 2022-12-20 오전 1 14 31

우리 Todo 어플리케이션은 Todo간의 선후관계를 지정해 줄 수 있다.

  • 사용자에게 직접 todo의 uuid를 보여주고, 복사하게 만들어 텍스트형태로 입력하게 만들었다.
  • 여러 uuid를 집어넣을 땐 ,를 통해 구분하게 했었다.

개선 전 선후관계 지정 기능의 문제점

사용자 측면

  • uuid를 사용해 선후관계를 추가한다는 개념 자체가 납득이 잘 안된다.
  • uuid를 복사했을 때 어떤 Todo였는지 기억이 안난다.
  • uuid를 직접 복사해 붙여넣는 행위가 귀찮다

개발자 측면

  • id를 사용자에게 노출하는 점이 굉장히 불쾌하다
  • ,로 구분하기 때문에 입력을 받았을 때 유효성검증 과정이 이유없이 길고 복잡하다.

이러한 문제들을 해결하기 위해서 Todo들의 제목을 입력하여 검색하고, 검색한 Todo를 추가하는 방식을 생각하게 되었다.


첫 구현

검색 API를 만들고, SearchBar 컴포넌트를 만들었다. 이에 마우스 이벤트와 키보드 이벤트를 적용시켜 현재 있는 Todo들의 제목을 검색하여 먼저 할 일과 나중에 할 일에 추가 할 수 있도록 하였다. 검색한 Todo 중 하나를 click하거나 enter했을 때 SearchBar를 이용하는 컴포넌트에서 내려준 props Fuction에 선택한 todo의 정보를 보내주는 식으로 구현하였다.


포커싱된 list에 스크롤을 맞추기 위해서 Ref 여러개를 관리해보자.

검색된 Todo가 5개가 넘어가면 더이상 검색리스트의 높이가 커지지않고 스크롤로 전환된다. 그때 keyboard 이벤트로 Todo를 선택할 때 현재 포커싱된 Todo에 스크롤이 가게하고 싶었다. 이때 스크롤 위치를 변환하기 위해서 두가지 DOM 조작이 생각났다.

  • getElementById를 이용해 현재 포커싱한 Todo에 스크롤 이동
  • useRef를 이용해 현재 포커싱한 Todo에 스크롤 이동 두가지 중에서 id를 사용하지 않는(리액트는 중복된 id를 사용할 수 있기 때문에 id를 사용하지 않는 편이 좋다.) useRef를 사용해 구현하기로 결정하였다. 사실 React를 사용할 때는 ref도 지양하는 편이 좋지만, 스크롤을 제어하기 위해선 어쩔 수 없이 사용해야했었다.

일단 구현을 하기로 마음은 먹었는데,.. useRef의 경우에는 element 하나하나에 useRef를 달아서 구현했었기에 개수가 변화하는 리스트 엘리먼트에 어떻게 적용할지 고민했었다.


Callback ref를 이용해 ref object를 저장하는 객체 만들기

const Search = (): ReactElement => {
  const liRef = useRef<LiRefList>({});

...

return (
...
{searchTodoList.map((todo) => {
          return (
            <li key={todo.id}
              ref={(el) => {
                if (el !== null) {
                  liRef.current[todo.id] = el;
                }
              }}>
            </li>
          );
        })}
...)
}

여러 고민을 한 결과, Callback ref를 이용해 ref Object를 인자로 받아 저장해주고, 리스트가 포커싱 되었을 경우 해당 ref object의 scrollIntoView 메서드를 호출해 해당 엘리먼트가 보이도록 ul태그를 스크롤하게 하였다. 나는 id의 값으로 ref를 찾고싶어서 object로 관리해주었지만 array로 관리해도 무방하다.


컴포넌트 분리의 필요성 인식

첫 구현을 마치고 코드리뷰를 했을 때 가독성이 떨어진다는 피드백을 받았다. UI와 비지니스 로직이 섞여 알아보기 힘들었던 것이다. 그렇기 때문에 기존에는 SearchBar라는 컴포넌트에 UI와 로직을 통째로 넣어놓았었는데 이를 Search라는 컴포넌트의 SearchBar(검색창), SearchListContent(검색결과 리스트 내용) 컴포넌트로 분리하여 UI와 로직을 분리해주었다.
image

  • Search에서 검색API를 호출하고 SearchListContent에는 props로 전달하여 Search에서만 비지니스 로직을 관리하게 한다.
  • Click, Keyboard 등 이벤트가 발생하였을 때의 로직을 Search에서 관리하여 SearchBar와 SearchListContent는 UI 기능만 수행하도록 수정한다.

분리하고 느낀 점

  • Search에서만 로직들을 관리하게 되어서 편했다.
  • SearchBar와 SearchListContent는 UI 기능만 수행하기 때문에 UI 수정이 필요할 때 빠르게 해당 부분을 찾아 수정할 수 있었다.

자동완성 검색기능 기능 추가 후 돌아온 피드백

사용자 측면

  • todo의 제목으로 검색하기 때문에 편리하게 선후관계를 추가할 수 있었다.
  • 선후관계를 추가할 때 어떻게 해야하는지 이해하기 쉬웠다.
  • 못생긴 id를 보지않게되어 기쁘다.

개발자 측면

  • 입력 받은 todo들에 대한 유효성 검증 부분이 줄어들었다.
  • id를 사용자에게 노출하지않아 편-안해졌다.


💊 비타500

📌 프로젝트

🐾 개발 일지

🥑 그룹활동

🌴 멘토링
🥕 데일리 스크럼
🍒 데일리 개인 회고
🐥 주간 회고
👯 발표 자료
Clone this wiki locally