리액트에서 useCallback을 정확히 이해하고 활용하는 능력을 테스트하는 5가지 문제입니다. 각 문제에서 가장 적절한 답변을 선택하고, 그 이유를 함께 작성해보세요.
1. 불필요한 리렌더링 방지하기
|
|
Q1. 위 코드에서 발생하는 문제점과 가장 적절한 해결책은 무엇인가요?
A. 문제 없음, 코드가 이미 최적화되어 있다.
B. handleSearch를 useCallback으로 감싸고 의존성 배열에 query와 onSearch를 추가한다.
C. MemoizedSearchButton에 key 속성을 추가하여 강제로 리렌더링한다.
D. handleSearch를 컴포넌트 외부로 이동시킨다.
정답 확인하기
정답: B
handleSearch 함수는 컴포넌트가 리렌더링될 때마다 새로 생성됩니다. 이로 인해 React.memo로 감싼 MemoizedSearchButton 컴포넌트가 불필요하게 다시 렌더링됩니다. React.memo는 props가 변경되지 않으면 리렌더링을 방지하는데, 매번 새로운 함수 참조가 전달되므로 최적화 효과가 사라집니다.
useCallback을 사용하면 이 문제를 해결할 수 있습니다.
|
|
이렇게 하면 query나 onSearch가 변경되지 않는 한 handleSearch 함수의 참조가 유지되어, MemoizedSearchButton이 불필요하게 리렌더링되는 것을 방지할 수 있습니다.
2. 의존성 배열 문제
|
|
Q2. 이 코드의 문제점과 올바른 해결책은 무엇인가요?
A. 문제 없음, 코드가 이미 최적화되어 있다.
B. useCallback 대신 useMemo를 사용해야 한다.
C. fetchUserData의 의존성 배열에 userId를 추가해야 한다.
D. useEffect의 의존성 배열에 userId를 직접 추가해야 한다.
정답 확인하기
정답: C
fetchUserData 함수는 내부에서 userId를 사용하지만, 의존성 배열에는 포함되어 있지 않습니다. 이로 인해 userId가 변경되어도 함수는 새로 생성되지 않고, 항상 처음 마운트될 때의 userId 값만 사용합니다. 따라서 다른 사용자 ID로 변경되어도 항상 첫 번째 사용자의 데이터만 가져오는 버그가 발생합니다.
올바른 해결책은 fetchUserData의 의존성 배열에 userId를 추가하는 것입니다:
|
|
이렇게 하면 userId가 변경될 때마다 fetchUserData 함수가 새로 생성되어 최신 userId 값을 사용하게 됩니다.
3. 카운터 업데이트 문제
|
|
Q3. 위 코드에서 발생하는 문제점과 적절한 해결책은 무엇인가요?
A. 의존성 배열에 count를 추가한다
B. 함수형 업데이트(prevCount => prevCount + 1)를 사용한다
C. useCallback 대신 일반 함수를 사용한다
D. useCallback 대신 useMemo를 사용한다
정답 확인하기
정답: B
현재 코드에서는 increment 함수가 빈 의존성 배열을 사용해 컴포넌트가 처음 마운트될 때의 count 값(0)만 계속 참조합니다. 이로 인해 버튼을 여러 번 클릭해도 count가 항상 1로만 증가하는 문제가 발생합니다.
함수형 업데이트를 사용하면 이 문제를 해결할 수 있습니다.
|
|
함수형 업데이트 방식은 외부 count 값을 직접 참조하지 않고 리액트가 제공하는 최신 상태값을 기반으로 업데이트합니다. 따라서 의존성 배열을 비워도 항상 현재 count 값에서 1씩 정확하게 증가시킬 수 있습니다.
선택지 A(의존성 배열에 count 추가)도 문제를 해결할 수 있지만, count가 변경될 때마다 함수가 새로 생성되므로 useCallback의 메모이제이션 효과가 사라진다는 단점이 있습니다.
4. 무한 루프 문제
|
|
Q4. 위 코드에서 발생할 수 있는 문제와 가장 적절한 해결책은 무엇인가요?
A. useCallback을 제거하고 일반 함수 사용하기
B. useEffect 의존성 배열에서 fetchData 제거하기
C. useEffect에 디바운스(debounce) 로직 추가하기
D. filter 상태 대신 useRef 사용하기
정답 확인하기
정답: C
현재 코드에서는 사용자가 입력할 때마다 다음과 같은 연쇄 반응이 일어납니다.
- 사용자가 입력 → filter 상태 변경
- filter 변경 → fetchData 함수 재생성
- fetchData 재생성 → useEffect 트리거
- API 호출 후 데이터 설정 → 컴포넌트 리렌더링
사용자가 빠르게 타이핑하면 모든 키 입력마다 API 호출이 발생하여 불필요한 네트워크 요청이 많아지고 성능이 저하됩니다.
디바운스 패턴을 적용하면 이 문제를 효과적으로 해결할 수 있습니다.
|
|
이렇게 하면 사용자가 타이핑을 멈춘 후 일정 시간(500ms)이 지난 후에만 API 호출이 발생하므로, 불필요한 API 호출을 줄이고 성능을 개선할 수 있습니다.
5. 최적화 전략 평가
|
|
Q5. 위 코드에서 useCallback 사용에 대한 가장 적절한 평가는 무엇인가요?
A. 모든 함수에 useCallback 사용이 적절하다
B. handleNameChange와 handleEmailChange는 useCallback이 불필요하다
C. handleSubmit에만 useCallback이 불필요하다
D. 모든 함수에 useCallback 대신 useMemo를 사용해야 한다
정답 확인하기
정답: B
handleNameChange와 handleEmailChange 함수는 단순히 입력값을 상태에 저장하는 간단한 함수입니다. 이 함수들은 자식 컴포넌트에 props로 전달되지 않고 현재 컴포넌트 내부에서만 사용되므로 useCallback으로 메모이제이션할 필요가 없습니다.
이러한 경우 다음과 같이 인라인 함수를 사용하는 것이 더 간결하고 가독성이 좋습니다.
|
|
반면, handleSubmit 함수는 form 제출 시 사용되는 함수로, 실제 애플리케이션에서는 API 호출이나 데이터 검증 등 복잡한 로직이 포함될 수 있어 useCallback을 유지하는 것이 적절할 수 있습니다. 또한 이 함수는 name과 email 값에 의존하므로 의존성 배열에 이들을 포함시키는 것이 중요합니다.
과도한 최적화는 코드를 더 복잡하게 만들 수 있으므로, 실제로 성능 문제가 발생하는 경우에만 useCallback을 적용하는 것이 좋은 방법입니다.