TIL

20221208 TIL 리팩토링을 조금 더 편하게

jiwoosmile 2022. 12. 8. 23:02

2주에 걸쳐 과제 구현을 완료했다!

 

과제를 완료했기 때문에 어제 작업했던 페이지네이션 컴포넌트 리팩토링을 진행하였다.
이때까지 리팩토링을 할 때 리팩토링을 원래 파일에서 하다보면 혼란스러웠던 경우가 많았다.

그리고 기존 파일에서 작업할 때 작업을 하는데도 어려움이 느껴졌었기 때문에

리팩토링을 제대로 하는 경우에 다른 방식으로 접근을 해야겠다는 생각을 했었고,

예전에 홀맨님께 조언을 구했었다.

그런데 그동안 리팩토링을 제대로 할 일이 많지 않았기 때문에 그 방식을 한동안 까먹고 있었다.

 

오늘은 어제 작성한 무지막지한 페이지네이션 컴포넌트 코드를 제대로 리팩토링을 해야됐는데,

어떻게 하면 조금 편하게 리팩토링을 할 수 있을까 고민하는 중에

홀맨님이 말씀해주신 방식이 떠올랐다.

바로 기존 파일을 복사해서 복사한 파일에서 리팩토링을 진행하는 것이었다.

테스트 코드는 이미 있기 때문에 안심하고 리팩토링을 진행할 수 있었고, 파일을 복사해서 중복되는 로직을 분리했다.

 

어제 생각해본 대로 결국 페이지네이션 컴포넌트에서 쓰일 값들을 배열로 뽑아내기만 하면

컴포넌트를 매우 작게 만들 수 있기 때문에 페이지네이션 컴포넌트에서 쓰일 값들(페이지 값들)을 뽑아내는 함수를 만드는 것부터 시작했다.

두 페이지 사이 값이 2이상 차이나는 경우 ... 으로 표시를 해야했는데, 

리액트에서 map을 돌리는 경우 고유한 key가 필요하다.

...는 최대 두 개가 있을 수 있고, 페이지 값들은 양수이기 때문에 key를 -1과 -2를 주었고,

여러 번의 리팩토링 끝에 아래와 같이 리팩토링할 수 있었다.

동기분의 리팩토링 중의 코드를 보고 복잡해보인다는 말을 듣고 rangeOf 메서드를 분리할 수 있었다.

지금도 깔끔하진 않지만 동기분의 말씀 덕분에 메서드를 하나 더 분리할 수 있어 조금 더 깔끔해진 것 같다.

const rangeOf = ({ size, start = 0 }) => [...Array(size).keys()].map((i) => i + start);

const pageValues = ({ totalPages, currentPage }) => {
  if (totalPages < 8) {
    return [...rangeOf({ size: totalPages })];
  }

  if (currentPage < 5) {
    return [...rangeOf({ size: 5 }),
      { key: -1, value: '...' },
      totalPages - 1];
  }

  if (totalPages - currentPage < 4) {
    return [0,
      { key: -1, value: '...' },
      ...rangeOf({ size: 5, start: totalPages - 5 })];
  }

  return [0,
    { key: -1, value: '...' },
    ...rangeOf({ size: 3, start: currentPage - 2 }),
    { key: -2, value: '...' },
    totalPages - 1];
};

export default function Pagination({ url, totalPages, currentPage = 1 }) {
  const navigate = useNavigate();

  const handleClickPrevious = () => {
    navigate(`${url}?page=${currentPage - 1}`);
  };

  const handleClickNext = () => {
    navigate(`${url}?page=${Number(currentPage) + 1}`);
  };

  if (!totalPages) {
    return null;
  }

  const pages = pageValues({
    totalPages: Number(totalPages),
    currentPage: Number(currentPage),
  });

  return (
    <ul>
      <li>
        <button
          type="button"
          title="previous"
          name="previous"
          disabled={currentPage < 2}
          onClick={handleClickPrevious}
        >
          previous
        </button>
      </li>
      {
        pages
          .map((page) => (
            typeof page === 'number'
              ? (
                <li key={page}>
                  <Link
                    to={`${url}?page=${page + 1}`}
                    selected={Number(currentPage) === page + 1}
                  >
                    {page + 1}

                  </Link>
                </li>
              ) : (
                <li key={page.key}>
                  <p>
                    {page.value}
                  </p>
                </li>
              )
          ))
      }
      <li>
        <button
          type="button"
          title="next"
          name="next"
          disabled={currentPage > totalPages - 1}
          onClick={handleClickNext}
        >
          next
        </button>
      </li>
    </ul>
  );
}

항상 복잡해 보이면 분리할 수 있는 메서드가 있는지 꼭 확인하자.

그리고 이번에 리팩토링할 때 복사를 해서 리팩토링하는 것이 매우 편함을 느꼈다.

테스트 코드가 있어서 마음놓고 리팩토링을 할 수 있었고, 기존 코드도 이미 있기 때문에 마음을 놓고 리팩토링을 할 수 있었던 것 같다.

앞으로도 리팩토링할 때 기존 파일을 복사해서 수정해서 테스트가 통과되면

기존 파일을 리팩토링한 파일로 대체하는 방식으로 작업하면 좋을 것 같다!