React로 개발하다 보면 커스텀 훅(Custom Hook)을 만들고, 그걸 부모 컴포넌트에서 호출해서 자식에 props로 내려줄지, 혹은 자식 컴포넌트 각각에서 호출할지 고민되는 경우가 있다. 어디서 호출하는지에 따라 API 호출의 중복, 렌더링 성능, 컴포넌트의 재사용성 등에 직접적인 영향을 줄 수 있기 때문에 상황에 맞게 적절한 위치에서 호출할 필요가 있다.
💁🏻 적용 가이드
부모 컴포넌트에서 호출하는 게 좋은 경우
- 여러 자식 컴포넌트가 같은 데이터나 상태를 공유해야 할 때
- API 호출을 한 번만 실행하고 싶을 때
- 데이터를 제어하는 책임이 부모 컴포넌트에 있어야 할 때
- 자식 컴포넌트는 해당 데이터를 읽기 전용으로만 사용할 때
자식 컴포넌트에서 각각 호출하는 게 좋은 경우
- 자식마다 서로 다른 데이터를 사용하거나 호출 파라미터가 다를 때
- 자식 컴포넌트가 독립적으로 재사용되어야 할 때
- 각 자식이 자체적으로 훅을 통해 상태나 데이터를 관리해야 할 때
- 커스텀 훅이 특정 자식 컴포넌트의 내부 로직에 강하게 결합되어 있을 때
✅ 부모에서 커스텀 훅 호출 후 props로 자식에게 전달
- 하나의 공통된 상태나 데이터를 여러 자식 컴포넌트가 공유할 때
- 동일한 API 요청이나 로직이 중복 호출되지 않도록 할 때
- 부모가 데이터를 제어하고, 자식은 읽기 전용으로 사용할 때
[예시]
서버에서 에러 코드 목록을 한 번만 받아오고, 이를 기반으로 필터 드롭다운과 에러 리스트 양쪽에 동일하게 사용하는 상황
import { useErrorCodes } from './hooks/useErrorCodes';
import ErrorFilter from './ErrorFilter';
import ErrorList from './ErrorList';
export default function Parent() {
const errorCodes = useErrorCodes(); // 한 번만 호출
if (!errorCodes) return <div>로딩 중...</div>;
return (
<div>
<ErrorFilter errorCodes={errorCodes} />
<ErrorList errorCodes={errorCodes} />
</div>
);
}
- 에러 코드 API는 한 번만 호출되고, 그 결과는 재사용됨
- 필터와 리스트 컴포넌트는 불필요한 데이터 요청 없이 동일한 소스 데이터 기반으로 동작
- 성능 최적화는 물론, 데이터 일관성도 보장됨
✅ 자식 컴포넌트에서 각각 커스텀 훅 호출
- 자식 컴포넌트가 각기 다른 데이터나 상태를 가져야 할 때
- 자식이 독립적으로 동작해야 할 때
- 자식에서 훅을 더 쉽게 재사용하고 싶을 때
[예시]
여러 명의 사용자 정보를 각자 다른 ID로 개별적으로 불러와야 하는 상황
// Parent.tsx
import UserBox from './UserBox';
export default function Parent() {
return (
<>
<UserBox userId={1} />
<UserBox userId={2} />
</>
);
}
// UserBox.tsx
import { useUser } from './hooks/useUser';
export default function UserBox({ userId }) {
const user = useUser(userId); // 자식에서 각각 호출
return <div>{user ? `${user.name} 님의 정보` : 'Loading...'}</div>;
- 자식 컴포넌트가 독립적으로 동작
- 훅 자체만으로도 재사용성 높음
✅ 커스텀 훅 설계 시 주의해야 할 점
1. 불필요한 중복 호출 방지
- 동일한 데이터를 자식마다 훅으로 개별 호출하면 API 요청이 반복될 수 있음
- 공통 데이터를 여러 자식이 사용할 경우, 부모에서 한 번만 호출해 props로 전달하는 구조가 유리
2. 컴포넌트 재사용성 고려
- 자식 컴포넌트가 훅에 직접 의존하지 않고 props로만 데이터를 받으면 재사용이 어려워질 수 있음
- 자식이 독립적으로 사용될 가능성이 있다면 내부에서 훅을 호출하도록 설계하는 것이 유리
3. 상태 책임의 분리
- 모든 상태를 부모에서 관리하면 컴포넌트 구조가 무겁고 단단해짐
- 공통 상태만 부모가 책임지고, 개별 UI 상태는 자식에게 맡겨 책임을 분리하는 것이 좋음
4. props drilling 최소화
- 중첩된 자식에게 props를 계속 넘기면 가독성과 유지보수성 저하
- 상황에 따라 React Context나 전역 상태 관리 도구(zustand, recoil 등) 고려
5. 데이터 로딩/에러 상태 명확히 처리
- 부모에서 데이터를 가져올 경우, 자식은 데이터가 null/undefined일 가능성을 항상 염두에 둬야 함
- 로딩, 에러, 빈 상태를 처리하는 UI 패턴을 구조적으로 함께 설계
6. 렌더링 최적화
- 부모에서 상태가 바뀔 때 자식에게 내려주는 props가 새로 생성되면 불필요한 리렌더링 발생
- useMemo, React.memo 등을 활용해 성능 최적화 고려
커스텀 훅은 재사용성과 코드 구조를 깔끔하게 만들어주는 강력한 도구지만, "어디서 호출하느냐"에 따라 성능과 유지보수성에 큰 차이가 생길 수 있다. 불필요한 API 호출을 줄이고 싶다면 공통 데이터를 부모에서 한 번만 가져와 자식에게 전달하는 방식이 유리하고, 반대로 각 자식이 독립적인 데이터를 가져야 하는 경우에는 자식 내부에서 훅을 호출하는 방식이 더 적합하다. 실무에서는 이 두 가지 패턴을 상황에 맞게 조합해서 쓰는 경우도 많기 때문에, "무조건 이게 맞다"는 정답보다는 구조와 데이터 흐름에 맞는 최적의 방식을 선택하는게 중요하다.
'Frontend' 카테고리의 다른 글
| React input 포커스 풀림: key 관리 (2) | 2025.08.27 |
|---|---|
| Zod z.preprocess 활용 (1) | 2025.08.14 |
| Skeleton UI를 이용한 로딩 상태 개선 (1) | 2025.06.16 |
| react-hook-form으로 수정된 필드만 추려내기 (0) | 2025.06.09 |
| 자바스크립트 비동기 처리의 핵심, 이벤트 루프 (3) | 2025.06.02 |