모방은 창조의 어머니라고 하지 않았던가. 코딩 설계도 다 모방에서 시작되는 법. 최근에 재밌게 읽은 글이 있는데
Start naming your useEffect functions, you will thank me later
이 블로그였다. 뭐랄까, thank me later할 정도로 자신 넘치는 애티튜드가 느껴졌달까? useEffect 팁(내가 해주는 팁은 아니지만)을 정리해보려고 한다.
근데 왜 useEffect가 읽기 어렵냐면
저자 Dan이 동료 PR을 열었는데, 200줄짜리 컴포넌트에 useEffect가 4개 있었다고 한다. 코드 자체는 잘 짜여 있었는데, 각 effect가 뭘 하는지 파악하려고 한 줄 한 줄 다 읽어야 했다는 것.
useEffect(() => { 이 시작 문장은 언제 실행되는지는 알려주지만, 왜 실행되는지는 전혀 말해주지 않기 때문이다.
해결책은 단순하다 — 이름을 붙여라
// 기존 방식
useEffect(() => {
document.title = `${count} items`;
}, [count]);
// 이름 붙인 버전
useEffect(function updateDocumentTitle() {
document.title = `${count} items`;
}, [count]);
문법 딱 하나 바꾼 것뿐인데, effect가 4개 있는 컴포넌트라면 이름만 죽 훑어도 전체 데이터 흐름이 한눈에 들어온다.
useEffect(function connectToInventoryWebSocket() { ... })
useEffect(function fetchInitialStock() { ... })
useEffect(function resetStockOnLocationChange() { ... })
useEffect(function notifyParentOfStockUpdate() { ... })
코드 한 줄도 안 읽고도 이 컴포넌트가 뭘 하는지 파악 완료.
디버깅할 때도 빛을 발한다
익명 함수일 때 에러 나면 콘솔에 이렇게 뜬다:
at (anonymous) @ InventorySync.tsx:14
effect가 4개면 어디서 터진 건지 알 수가 없다. 이름을 붙이면?
at connectToInventoryWebSocket @ InventorySync.tsx:14
Sentry 같은 모니터링 툴에서 폰으로 에러 확인할 때 특히 차이가 크다.
이름을 못 짓겠다면, 그게 신호다
이름 붙이려다 보면 이런 상황이 온다:
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
if (user?.preferences?.theme) {
document.body.className = user.preferences.theme;
}
return () => window.removeEventListener('resize', handleResize);
}, [user?.preferences?.theme]);
이걸 뭐라고 부르지? syncWidthAndApplyTheme? "and"가 들어가는 순간 경고 신호다. 관련 없는 두 가지 일을 하나의 effect에 욱여넣고 있다는 뜻이니까, 그냥 쪼개면 된다.
useEffect(function trackWindowWidth() {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
useEffect(function applyUserTheme() {
if (user?.preferences?.theme) {
document.body.className = user.preferences.theme;
}
}, [user?.preferences?.theme]);
심지어 "이 effect 자체가 필요 없다"는 것도 알 수 있다
useEffect(function syncFullName() {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
이름 붙이고 보니... 이거 그냥 derived value잖아?
const fullName = `${firstName} ${lastName}`;
effect 버전은 렌더링을 두 번 일으킨다. 이름을 붙이는 행위 자체가 "이 코드가 effect에 있을 필요가 있나?"를 다시 생각하게 만드는 것이다.
Dan의 실제 경험담
1년 전에 Mapbox 인스턴스를 앱 상태와 동기화하는 컴포넌트를 작업했는데, effect가 5개였다고 한다. 파일 열 때마다 30초씩 다시 파악해야 했던 것.
이름을 붙였더니:
- cleanupStaleMarkerListeners가 사실 handleMarkerInteractions의 cleanup이었다는 걸 발견 → 합침
- synchronizeZoomLevel이랑 synchronizeCenterPosition이 항상 같이 실행된다는 걸 발견 → synchronizeMapViewport로 합침
effect 5개가 3개로 줄었다. 이름이 구조를 보이게 만든 것이다.
cleanup 함수도 이름 붙일 수 있다
useEffect(function pollServerForUpdates() {
const intervalId = setInterval(() => {
fetch(`/api/status/${serverId}`)
.then(res => res.json())
.then(setServerStatus);
}, 5000);
return function stopPollingServer() {
clearInterval(intervalId);
};
}, [serverId]);
pollServerForUpdates로 시작해서 stopPollingServer로 끝나는 대칭 구조. 읽으면서 자연스럽게 흐름이 잡힌다.
코드 한 글자도 안 바꾸고, 라이브러리도 필요 없고, 그냥 이름 하나 붙이는 것뿐인데. 앞으로 useEffect 쓸 때마다 이름 꼭 붙여봐야겠다.
! 요약 !
useEffect에 이름을 붙이면
- 에러 추적이 쉬워진다. 에러가 나면 (anonymous) 대신 connectToInventoryWebSocket이 뜬다.
- 자연히 클린코드를 적게된다. 이름을 못 짓겠으면 그 effect가 너무 많은 일을 하고 있다는 신호다.
- cleanup 함수 시작과 끝이 한눈에 보인다.
'Dev Log' 카테고리의 다른 글
| "보고 있나 삼전닉스?" — AI 반도체 판 뒤흔들 빅테크의 승부수 (0) | 2026.05.17 |
|---|---|
| GitHub PR이 생성됐는데 목록에 안 보이는 버그 (1) | 2026.05.10 |
| Git Worktree 마스터하기X 알아보기O (2) | 2026.04.26 |
| IntelliJ에서는 안 되고, Gradle에서는 되는 이유 (PKIX 에러 해결기) (0) | 2026.04.15 |
| 크롬 익스텐션 만들기 2 (1) | 2026.04.09 |