Effect가 필요 없을 수도 있어요
Effect가 필요 없을 수도 있어요
Effect는 React 패러다임을 벗어나는 탈출구예요. React가 아닌 위젯이나 네트워크, 브라우저 DOM 같은 외부 시스템과 컴포넌트를 동기화하기 위해 쓰죠. 그런데 외부 시스템이 관여하지 않는 경우 — 가령 props나 state가 바뀌었을 때 state를 갱신하려는 경우라면 Effect가 필요 없어요. 불필요한 Effect를 제거하면 코드가 따라 읽기 쉬워지고, 더 빨리 돌고, 오류도 줄어들어요.
Effect를 걷어내는 두 원칙
첫째, 렌더링을 위한 데이터 변환엔 Effect가 필요 없어요. 목록을 필터링해서 보여주고 싶다고 가정해볼게요. 목록이 바뀔 때 state를 갱신하는 Effect를 떠올릴 수 있지만, 그건 비효율적이에요. React 렌더링은 컴포넌트 함수로 화면을 계산한 뒤 커밋하고, 마지막에 Effect를 실행하는 순서로 흘러가요. 그렇다면 렌더링 중에 바로 계산하면 돼요.
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
// 🔴 불필요한 state와 Effect
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
// ...
}
대신 props나 state가 바뀌면 자동으로 다시 계산되는 렌더링 시간에 계산해요.
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
// ✅ 렌더링 중 계산
const fullName = firstName + ' ' + lastName;
// ...
}
둘째, 사용자 이벤트 처리에는 Effect가 필요 없어요. 버튼 클릭 시 /api/buy POST를 보내고 알림을 띄운다고 해볼게요. 클릭 이벤트 핸들러에서는 사용자가 정확히 무엇을 했는지 알지만, Effect가 실행될 즈음이면 어떤 버튼이 눌렸는지 알 수 없어요. 그래서 사용자 이벤트는 이벤트 핸들러에서 처리하는 게 원칙이에요. '코드를 어디에 둘지' 헷갈리면, 그 코드가 왜 실행돼야 하는지 물어보세요 — 특정 상호작용 때문에 실행된다면 핸들러, 화면에 컴포넌트가 보이는 것이 원인이라면 Effect예요.
Effect를 대체하는 기법들
몇 가지 흔한 패턴을 바꿔쓰는 방법을 알아둘수록 좋아요.
- 비싼 계산 캐싱 — 연산 결과를 state에 두고 Effect로 갱신하기보다
useMemo로 감싸요.useMemo는 렌더링 중에 실행되므로 순수 계산에만 써야 해요. React Compiler가 자동으로 메모이제이션해 주면 손으로 쓴useMemo가 필요 없어지는 경우도 많아요. - state 전체 초기화 — prop이 바뀔 때 컴포넌트 트리 전체를 리셋하고 싶다면, Effect 대신 다른
key를 넘겨 리셋해요. - 부모에 state 변화 알리기 — Effect로 부모의 state를 바꾸기보다, prop을 받거나 이벤트 핸들러로 직접 notify 하는 흐름이 더 예측 가능해요.
- 외부 저장소 구독 — React가 모르는 데이터가 바뀔 때는
useSyncExternalStore같은 방법으로 구독하고, 보통useOnlineStatus처럼 커스텀 Hook으로 묶어 재사용해요.
예외도 있어요 — 데이터 fetch. 검색어가 바뀔 때 결과를 fetch 하는 코드는 이벤트 핸들러로 옮길 필요가 없어요. 검색 입력은 URL에서 채워지고, 사용자가 입력을 건드리지 않고도 앞·뒤로 이동할 수 있기 때문이에요. 화면에 컴포넌트가 있는 동안 현재 page·query에 맞는 결과를 네트워크와 동기화해야 하니 Effect가 맞는 자리예요. 다만 응답이 역순으로 도착하는 경쟁 조건을 막으려면 정리 함수로 예전 응답을 무시해야 해요. 그리고 프레임워크가 제공하는 더 효율적인 데이터 fetch 수단이 있다면 그것부터 쓰는 걸 권장해요.
컴포넌트에 놓인 원본 useEffect 호출이 적을수록 앱을 유지보수하기 쉬워요. 꼭 필요한 동기화에만 Effect를 아껴 쓰세요.
더 알아보기
- SyncEffect로 동기화하기 — Effect가 진짜 필요한 영역을 배워요
- useMemo 레퍼런스 — 비싼 계산을 캐싱하는 확정 API예요
- 컴포넌트를 순수하게 유지하기 — 렌더링 중 계산이 왜 순수해야 하는지 다뤄요