프론트엔드를 공부하면서 개념은 대부분 익숙해졌지만, 최근 실무형 프로젝트들을 진행하며 판단의 기준이 애매한 지점들을 자주 마주하게 되었다.
이 글은 그중에서도 가장 고민이 컸던 영역들인 컴포넌트 추상화, 상태 위치, 렌더링 최적화에 대한 나의 흔들림을 돌아보고, 프로젝트를 통해 개선 방향을 정리한 글이다.
코어 개념은 알고 있지만, 연결 지점에서 약간의 흔들림이 있음
Button, Modal, Card 등의 UI 컴포넌트를 설계할 때
| 잘못된 판단 예 | 문제점 |
|---|---|
BaseButton 하나로 다 통일 | variant, loading, icon 등 props 폭발 |
상황마다 버튼 만들기 (SubmitButton, LoginButton) | 중복 코드 증가, 유지보수 어려움 |
나의 문제
언제 추상화하고 언제 그냥 둘 것인지 기준이 없었다.
- 개선 방안
- 예를들어 카드 컴포넌트가 있다면 타이틀, 이미지, 설명 등으로 쪼갬
- 샤드씨엔처럼 가져다 쓰면 조금씩 다른 모양의 카드에 대응 가능
type, tag, order)를 Zustand에 넣었지만,| 상태 위치 | 특징 |
|---|---|
local (useState) | 간단하지만 페이지 이동 시 초기화됨 |
global (Zustand 등) | 재사용 가능하지만 과도한 전역화 위험 |
URL (searchParams) | 공유, 리프레시에 유리하나 조작 복잡 |
server state (TanStack Query) | API 동기화에 유리하나 local과 혼용 시 혼란 |
나의 문제
이 상태가 진짜 전역이어야 하는가?"를 항상 의심해야 한다.
setQueryData()로 직접 조작
useState() 한 줄로도 충분했음 queryClient.setQueryData(logKeys.detail(logId), (old: ApiResponse<DetailLog>) => {
if (!old?.success) return old;
const updatedPlaces = old.data.place.map((place) => {
if (place.place_id === placeId) {
const currentCount = place._count?.place_bookmark ?? 0;
return {
...place,
_count: {
...place._count,
place_bookmark: currentCount + (isBookmark ? -1 : 1),
},
};
}
return place;
});
return {
...old,
data: {
...old.data,
place: updatedPlaces,
},
};
});
place._count.place_bookmark처럼 중첩 객체 수정이 반복export default function PlaceBookmarkWithCount({
placeId,
placeBookmarkCount = 0,
}: PlaceBookmarkWithCountProps) {
const [bookmarkCount, setBookmarkCount] = useState(placeBookmarkCount);
const handleToggle = (newStatus: boolean) => {
setBookmarkCount((prev) => prev + (newStatus ? 1 : -1));
};
return (
<section className="absolute top-0 right-0 flex flex-col items-center">
<PlaceBookMarkButton // 장소 낙관적 업데이트 코드가 들어있는 컴포넌트
placeId={placeId}
onToggle={handleToggle}
className="!top-0 !right-0 !relative w-9 h-9"
/>
<span className="font-medium text-text-sm text-light-300">
{formatCount(bookmarkCount)} //북마크 카운트
</span>
</section>
);
}
bookmarkCount는 단순 UI 표시이므로 useState로 관리개발 실력은 개념 암기보다 상황에서의 선택 기준이 핵심이다.
이번 사례를 통해 흔들렸던 판단 지점을 돌아봤고, 앞으로 더 명확한 기준을 갖고 설계할 수 있도록 꾸준히 개선해 나갈 예정이다.