프로젝트 진행하면서 리펙토링을 진행할 때, 느낀 점이 있다.
나는 기능 구현에 집착해서 하드코딩 한 부분들도 많았다.
다시 한번 내 코드를 들여다보고 여유롭게 고민들을 하니,
안보이던 문제들도 잘보였고 나아가 이론으로만 접했던 내용들을
실제 예시와 함께 적용해보니 의도도 더 깊이 와닿았다.
많은 리펙토링이 있었지만 단계별로 구분하여서 나누어보겠다.
먼저 완성된 로직이다.
import SearchBar from "@/components/main/SearchBar";
import SortButtons from "@/components/main/SortButtons";
import WorldcupList from "@/components/main/WorldcupList";
import { useWorldcups } from "@/lib/hooks/useWorldcups";
export default function Home() {
const { containerRef, sort, setSort, setSearch, worldcups, isLoading } =
useWorldcups("/worldcups");
return (
<main
className="flex h-screen flex-col overflow-y-scroll relative pt-24"
ref={containerRef}>
<SearchBar setSearch={setSearch} />
<SortButtons setSort={setSort} sort={sort} />
<WorldcupList worldcups={worldcups} isLoading={isLoading} />
</main>
);
}
커스텀 훅과 기존 로직을 쪼개서 컴포넌트 단위로 나누어 적용하였다.
기존 73줄에서 18줄로 줄일 수 있었다.
단순히 줄을 줄였다는 것이 중요한 것이 아니라 줄임으로서 직관적으로 변했다는 것이
인상 깊었다.
커스텀 훅
을 사용하여서 내가 사용하는 메소드만 나타내는 추상화 시켜서 어떠한 메소드를 쓰는지 알 수 있고 이러한 것들이 적용되는 컴포넌트 단위가 직관적으로 보여서 만족 스러웠다.
위처럼 메인에서 데이터를 받아오고 관리하는 로직과 스크롤 이벤트를 추가하는 로직을 묶어서 커스텀 훅으로 만들었고,
메인 페이지에서 추가된 기능을 하는 나만의 월드컵 페이지에서도 재사용 하였다.
확실히 커스텀훅의 장점을 느낄 수 있었던게 한 페이지 안에 로직과 컴포넌트들이 있으면 많은 정보가 존재하여 온전히 포커싱이 안되었는데
커스텀 훅으로 분리하니 파일마다 각각의 역할이 존재하여서 인지가 잘되었고,
재사용성도 올라가서(나만의 월드컵 페이지와 메인 페이지를 잇는 역할을 한다.)
그래서 유지보수가 더욱 수월해졌다.
메인에서는 card라는 컴포넌트가 존재하였다.
그리고 이 card는 조건부 렌더링을 하였다.
{data ? (
worldcups.length > 0 ? (
worldcups.map((v) => (
<Card
key={v.id}
worldcup={v}
mine={true}
handlerDelete={handlerDelete}
handlerWorldCup={handlerWorldCup}
/>
))
) : (
<div>
<Image
src='/icon/blueDolphin2.svg'
alt='one'
width={150}
height={250}
/>
<h3>
월드컵이 없습니다. <br />
<span
onClick={() => router.push("/editors")}
>
후보 추가
</span>
를 통해 업데이트해주세요
</h3>
</div>
)
) : (
new Array(10).fill(1).map((_, i) => {
return <CardSkeleton key={i} />;
})
)}
</article>
위 로직을 드러내는 방식을 사용하였는데
추상화를 통해서 한눈에 직관적인 파일을 만들고 싶어서 컴포넌트화 하였고,
컴포넌트 화하여서 이 페이지에서는 조건부 렌더링관련해서만 다루도록 하고 싶었다.
그래서 worldcupList 라는 파일을 만들어서
if (isLoading) {
return (
<article className="w-full h-auto ">
{Array.from({ length: 10 }, (_, i) => (
<CardSkeleton key={i} />
))}
</article>
);
}
return (
<article className="w-full h-auto ">
{worldcups && worldcups.length > 0 ? (
worldcups.map((v) => (
<Card
key={v.id}
worldcup={v}
mine={mine}
handlerDelete={handlerDelete}
handlerEditorWorldCup={handlerEditorWorldCup}
/>
))
) : (
<div >
<Image
src="/icon/blueDolphin2.svg"
alt="one"
width={150}
height={250}
/>
<h3>
월드컵이 없습니다. <br />
<span
onClick={() => router.push("/editors")}
>
후보 추가
</span>
를 통해 업데이트해주세요
</h3>
</div>
)}
</article>
);
이 파일에서는 조건부 렌더링에만 집중하면 되어서 관리하기 편했다.
결과적으로 isLoading 값을 이용하여서 로딩중일 떄와
결과에 따른 값을 구분지어서 렌더링 해주는 로직으로 나눌 수 있었다.
처음 로직을 작성할 때는 버튼을 컴포넌트화 하려고 하였는데
내가 어떠한 로직을 넣을거고 뭐가 필요한지 잘몰랐기 때문에
시작부터 컴포넌트화 하기가 어려웠다.
그래서 하드 코딩을 먼저하고 어떠한 데이터를 사용하는지 체크하였고
이를 컴포넌트화 하여서 관리하였다.
const GameButton = ({ icon, alt, label, onClick }: ButtonProps) => {
return (
<button
onClick={onClick}
className='bg-main px-2 h-10 sm:px-4 space-x-2 mr-2 flex items-center rounded-lg hover:scale-110 cursor-pointer text-base'>
<Image src={icon} alt={alt} width={20} height={20} />
<p>{label}</p>
</button>
);
};
지금 보면 간단한 구성인데, 반응형으로 버튼을 만들려고 하다보니 디자인으로 고민이 많았었다. 그래서 자주 구성이 바뀌었고 그로 인해 컴포넌트화보다는 디자인에 우선시 하였던것 같다.
다음에 만들게 된다면 기존에 만들었던 로직들을 재활용하여서 거기에 덫붙히는 형식으로 접근할 것이다.
(공부 한다고 프로젝트마다 로직을 재사용하지 않았다.)
SearchBar.tsx
위 파일에서는 기존에는 image에 onClick 메소드를 넣어서 적용하였다.
return (
<div className="flex items-center justify-center">
<form
className="bg-white w-72 h-10 relative flex items-center justify-center"
onSubmit={handleSearch}
<Image
src="/icon/search.svg"
alt="Search button"
width={25}
height={25}
onClick={handleSearch}
/>
</form>
</div>
);
};
하지만 웹접근성과 유지보수를 고려하여서 이미지에 onClick을 다는 것을 적절하지 않았다.
그래서 위 로직을 2가지 포인트로 수정하였다.
return (
<div className="flex items-center justify-center">
<form
className="bg-white w-72 h-10 relative flex items-center justify-center"
onSubmit={handleSearch}>
<button type="submit" className="absolute right-4 pb-2">
<Image
src="/icon/search.svg"
alt="Search button"
width={25}
height={25}
/>
</button>
</form>
</div>
);
};
1번 button 태그를 넣어서 여기서 버튼 기능을 한다는 것으로 웹접근성을 높였고
2번 버블링을 사용하여서 이벤트를 넣지않고 submit을 통해 연결해주었다.
메인페이지에서 생성하는 이벤트를 하나 아낄 수 있었고
웹 접근성도 개성하여서 좋았다.
처음에 커스텀 훅을 배웠을 때, 큰 그림을 이해하기 어려웠다.
하지만 직접 적용을 해보니 유지보수에 뛰어난 장점이 있었고,
앞으로도 애용할 것이다.
또한 조건부 렌더링도 항상 드러내는 방식을 사용하였는데
앞으로는 조건부 렌더링을 컴포넌트화해서 드러내기보다는
직관적으로 중앙에서 관리할 수 있게 추상화할 것이고,
내부에서 관리하여서 역할을 분명하게 구분짓는 방법을 활용할 것이다.
이전까지 빠르게 기능을 구현하고 페이지 완성하는데만 달려왔기 때문에
로직을 지저분한 부분들이 마음에 쓰였는데
이번을 기회로 수정할 수 있어서 상쾌하였다.
첫 페이지를 끝냈으니 이번을 기회로 성장하고
나머지 페이지들도 여기서 얻었던 아이디어들을 활용하여 리펙토링 하겠다.