[스터디] 리팩터링_chapter 8_기능 이동

김하은·2024년 7월 1일
0
post-thumbnail

요즘 "코스메이커" 프로젝트에서 리팩터링을 진행하고 있는데, 이번 챕터에서 나온 기술들을 직접 적용해 보기로 했다.

8.1 함수 옮기기

함수를 옮길지 말지를 정하기란 쉽지 않다. 그럴 땐 대상 함수의 현재 컨텍스트와 후보 컨텍스르틑 둘러보면 도움이 된다.

  • 좋은 소프트웨어 설계의 핵심은 모듈화가 얼마나 잘 되어 있느냐를 뜻하는 모듈성이다. 모듈성이란 프로그램의 어딘가를 수정하려 할 때 해당 기능과 깊이 관련된 작은 일부만 이해해도 가능하게 해주는 능력이다.

실제 코드에 적용하기

1. 선택한 함수가 현재 컨텍스트에서 사용 중인 모든 프로그램 요소를 살펴본다. 이 요소들 중에도 함께 옮겨야 할 게 있는지 고민해 본다.

  • 선택한 함수: groupTagsByDescription함수로 src\components\domains\spotRegister\BadgeLists.tsxsrc\pages\SearchPage\index.tsx에서 사용되고 있는 함수이다.
  const groupTagsByDescription = (tags: tagResponseDto[]) => {
    return tags.reduce(
      (acc, tag) => {
        if (!acc[tag.description]) {
          acc[tag.description] = [];
        }
        acc[tag.description].push(tag);
        return acc;
      },
      {} as Record<string, tagResponseDto[]>,
    );
  };
  • 함께 옮겨야 할 요소는 따로 없다.

2. 선택한 함수가 다형 메서드인지 확인한다.

  • 다형 메서드(Polymorphic Function):
    • 다형 메서드는 함수의 인수 타입에 따라 다르게 동작하는 함수를 의미함
    • 즉, 같은 이름의 함수라도 인수 타입이 다르면 다른 동작을 할 수 있음
    • 이를 통해 코드의 유연성과 재사용성을 높일 수 있음
  • groupTagsByDescription 함수는 tagResponseDto[] 타입의 배열을 받아 description 속성을 기준으로 그룹화하여 반환하는 함수임
    • 이 함수는 인수 타입이 고정되어 있으므로 다형 메서드라고 볼 수 없음

3. 선택한 함수를 타깃 컨텍스트로 복사한다. 타깃 함수가 새로운 터전에 잘 자리 잡도록 다듬는다.

  • src\utils\groupTags.ts파일을 만들고 함수를 소스 함수를 복사함
import { tagResponseDto } from "@/api/tag/type";

const groupTags = (tags: tagResponseDto[]): Record<string, tagResponseDto[]> => {
  return tags.reduce(
    (acc, tag) => {
      if (!acc[tag.description]) {
        acc[tag.description] = [];
      }
      acc[tag.description].push(tag);
      return acc;
    },
    {} as Record<string, tagResponseDto[]>,
  );
};

export default groupTags;

4. 정적 분석을 수행한다.

  • 정적 분석
    • 코드를 실행하지 않고 소스 코드를 분석하여 잠재적인 문제점을 찾는 방법
    • 이를 통해 보안 취약점, 성능 문제 등을 사전에 발견할 수 있음

5. 소스 컨텍스트에서 타깃 함수를 참조할 방법을 찾아 반영한다.

  • 모듈 문법을 사용하여 타깃 함수를 import해옴
import groupTags from "@/utils/groupTags";
//중략

const BadgeLists = ({ selectedBadges, onChange }: BadgeListProps) => {
 //중략
  useEffect(() => {
    const fetchLists = async () => {
      try {
        const response = await getTag();
        setTagsData(response);
        // console.log(response.data);
      } catch (error) {
        console.error("Error fetching data:", error);
      }
    };

    fetchLists();
  }, []);

  const groupedTags = groupTags(tagsData);
  
  return (
    <>
		//중략
    </>
  );
};

export default BadgeLists;

6. 소스 함수를 타깃 함수의 위임 함수가 되도록 수정한다.

  • 간단한 경우여서 위임 함수를 만들지 않고 바로 사용함

7. 테스트 한다.

  • tag 목록이 화면에 잘 렌더링 됐고, submit 시에도 tag 데이터가 잘 출력되는 것을 확인함

8. 소스 함수를 인라인할지 고민해본다.

  • 함수를 인라인한 후 마무리 했다.
  • 최종 코드
import { getTag } from "@/api/tag";
import { tagResponseDto } from "@/api/tag/type";
import BadgeList from "@/components/commons/BadgeList/BadgeList";
import groupTags from "@/utils/groupTags";
import { useEffect, useState } from "react";

interface BadgeListProps {
  selectedBadges: tagResponseDto[];
  onChange: (updatedDestinationBadges: tagResponseDto[]) => void;
}

const BadgeLists = ({ selectedBadges, onChange }: BadgeListProps) => {
  const [tagsData, setTagsData] = useState<tagResponseDto[]>([]);
  const [selectedDestinationBadges, setSelectedDestinationBadges] = useState<tagResponseDto[]>(selectedBadges);

  useEffect(() => {
    onChange(selectedDestinationBadges);
  }, [selectedDestinationBadges, onChange]);

  useEffect(() => {
    const fetchLists = async () => {
      try {
        const response = await getTag();
        setTagsData(response);
      } catch (error) {
        console.error("Error fetching data:", error);
      }
    };

    fetchLists();
  }, []);

  const groupedTags = groupTags(tagsData);
  return (
    <>
      {Object.entries(groupedTags).map(([description, tags]) => (
        <BadgeList
          key={description}
          title={description}
          tags={tags}
          selectedBadges={selectedDestinationBadges}
          setSelectedBadges={setSelectedDestinationBadges}
        />
      ))}
    </>
  );
};

export default BadgeLists;
profile
아이디어와 구현을 좋아합니다!

0개의 댓글