무작위 노트 표시 로직 개선하기

최경락 (K_ROCK_)·2022년 7월 15일
0

개요

  • 현재 진행 중인 토이 프로젝트의 Memory 파트는 암기노트를 컨셉으로 하고있다.
  • 단순히 노트를 작성하는 것만 아니라, 암기를 위한 기능이 필요하다고 생각하여 무작위의 질문을 제시하되, 작성한 키워드와 본문을 선택하에 볼 수 있는 컴포넌트를 제작하였다.
  • 그리고 간단하게 무작위 인덱스를 생성하여 해당하는 노트를 보여주는 로직으로 작성하였다.
    → 중복된 경우가 생김
  • 최근 면접에서 프로젝트에 대해 주셨던 질문 중, 무작위 기능이 중복된 내용이 다시 표시되는 것에 대해 어떻게 생각하는 지에 대한 질문을 받았고, 기존 로직을 수정하여 개선해보기로 하였다.

기존 로직

// 랜덤 인덱스 로직
function getRandomIdx(num: number) {
    return Math.floor(Math.random() * num);
  }
  • num 이라는 매개변수에 노트의 총 갯수(배열의 길이) 를 전달하여 정수인 인덱스를 구하는 방식으로 작성하였다.
  • 당연히 중복이 발생하는 경우가 생긴다.
  • 표시되는 노트의 데이터를 currentQuestion 라는 상태에 저장하여 사용하고 있는데, 다음 질문 버튼을 누를 때 마다 아래의 갱신함수를 실행하여 무작위 인덱스의 위치에 있는 노트를 현재 질문으로 설정하는 방식으로 구현하였다.
<button
  onClick={setCurrentQuestion(notesSlice[getRandomIdx(notesSlice.length)])}>
  • 인덱스가 전적으로 무작위로 나오다 보니 동일한 질문이 연달아 나오거나 하는 경우가 발생하기도 했다.

개선 로직

  • 작성된 노트의 인덱스를 가진 배열을 만들고, 해당 인덱스 배열을 뒤섞는 방식으로 진행해보기로 하였다.
  • 이를 이용하면 인덱스 배열의 인덱스를 이동해가며 무작위 인덱스를 전달해 줄 수 있고, 총 질문이 몇개인지, 얼만큼 진행 했는지의 여부도 간단하게 표시해 줄 수 있었다.

무작위 인덱스 배열 만들기

  • 일단 노트가 10개라고 가정했을 때, 0~9 까지의 값을 가진 인덱스 배열을 만들어야한다.
    → 그리고, 노트의 갯수에 따라 가변적이여야 했다.
  • 그리고 해당 배열을 무작위로 순서로 정렬해야한다.
  • 이를 종합하면 아래와 같다.
const getRandomIdxArr = (num: number) => {
    const idxArr = Array(num)
      .fill(null)
      .map((el, idx) => idx);
      // 노트의 길이만큼을 가지는 배열을 만들고, null 로 채운다.
      // 해당 요소들을 각각의 인덱스로 치환한다.

    return idxArr.sort(() => Math.random() - 0.5);
    // sort 함수는 반환하는 값이 양수인 경우 현재 요소를 다음 요소 앞에 두고, 
    // 음수인 경우 다음 요소를 현재 요소 앞에 배치한다.
    // 이를 Math.random() - 0.5 를 이용하여 음수 혹은 양수가 무작위로 나오게 함으로써
    // 무작위 정렬을 구현한다.
  };

기능 구현

  • 기존 로직의 경우 RandomNote 컴포넌트 내부에서 무작위 숫자를 생성하고, 인덱스에 접근하는 방식을 사용했지만, 개선 로직의 경우 상위 컴포넌트에서 인덱스 배열을 전달 해주어야 했다.
    RandomNote 컴포넌트에서 생성시, 다음 버튼을 누를때마다 새로운 인덱스가 생성된다.
<RandomNote
  // ...props
  idxArr={getRandomIdxArr(notesSlice.length)}
/>
  • props 로 전달받은 인덱스 배열을 RandomNote 컴포넌트에서 currentQuestionIdx 상태를 이용하여 탐색하고, 초기값을 0으로 지정하여 상태갱신을 통해 증감하는 것으로 인덱스 배열 탐색을 구현했다.
  • 이전에는 구현하지 않았던 이전 질문으로 이동하는 것도 쉽게 구현 할 수 있었다.

const [currentQuestionIdx, setCurrentQuestionIdx] = useState<number>(0);

const handleNextBtn = () => {
  if (currentQuestionIdx < notesSlice.length - 1) {
    setCurrentQuestionIdx(currentQuestionIdx + 1);
  }
};

const handlePrevBtn = () => {
  if (currentQuestionIdx > 0) {
    setCurrentQuestionIdx(currentQuestionIdx - 1);
  }
};
// 버튼이 클릭될 때 위의 함수를 실행하여 전달받은 배열의 인덱스를 증감한다.

useEffect(() => {
    setCurrentQuestion(notesSlice[idxArr[currentQuestionIdx]]);
  }, [currentQuestionIdx]);
// 질문의 인덱스가 변경될 때 마다 현재 질문을 갱신한다.

진행도 표시하기

  • 위 처럼 로직을 수정함에 따라, 진행도를 아래와 같이 간단하게 표시 해 줄 수 있게 되었다.
<div className="indicator">
  {currentQuestionIdx + 1} / {notesSlice.length}
</div>

결과

  • 현재 진행도와 이전 질문 버튼이 추가되었다.

0개의 댓글