방명록
- [React.js] React useCallback, 함수 인수는 어떻게 동작할까? (feat. 빈 의존성 배열 `[]`)2025년 04월 04일 17시 30분 18초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
React useCallback, 함수 인수는 어떻게 동작할까?
React 개발 중 성능 최적화를 위해
useCallback
훅을 자주 사용하게 된다. 그런데useCallback(fn, [])
처럼 빈 의존성 배열을 사용하면, 혹시 함수가 처음 호출될 때 사용된 인수가 고정되어 버리는 건 아닐까 하는 의문이 들 때가 있다. 마치 스냅샷처럼.그래서 이
useCallback
과 함수 인수 전달의 관계에 대해 명확하게 알아고자 한다. 결론부터 말하면, 빈 의존성 배열은 인수를 고정시키지 않는다!1. useCallback이란?
- 개념: 함수 자체를 기억(메모이제이션)하여, 특정 조건 하에서 함수의 재생성을 방지하는 React 훅이다.
- 목적: 주로 자식 컴포넌트에 props로 함수를 전달하거나
useEffect
의 의존성 배열에 함수를 포함할 때, 불필요한 리렌더링 또는useEffect
재실행을 막아 성능 최적화에 기여한다. - 동작:
useCallback(fn, deps)
형태로 사용한다.deps
배열 안의 의존성 값들이 변경되지 않는 한,useCallback
은 이전에 생성했던 동일한 함수 인스턴스를 반환한다.deps
가 빈 배열[]
이면, 컴포넌트가 처음 마운트될 때 단 한 번만 함수를 생성하고 이후에는 계속 그 함수를 재사용한다.
2. 흔한 오해: "빈 배열
[]
이 인수를 고정시킨다?"- 가끔
useCallback(fn, [])
을 쓰면,fn
함수가 처음 호출될 때 사용된 인수(예:fn('첫번째 인수')
)가 그대로 기억되어, 나중에fn('두번째 인수')
처럼 다른 인수로 호출해도 계속 '첫번째 인수'가 사용될 거라고 생각하는 경우가 있다. - 하지만 이것은 잘못된 이해이다. useCallback은 함수 호출 시점의 인수를 저장하지 않는다.
3. 실제 동작 방식과 예제 코드
useCallback
은 함수 정의(코드 자체)를 메모리에 저장해두고 재사용하는 것이다.- 메모이제이션된 함수가 호출되는 시점에 어떤 인수가 전달되었는지에 따라, 함수 내부에서는 그때 전달된 인수를 사용한다.
- 예제 코드를 통해 직접 확인해 보자!
import React, { useState, useCallback, useEffect } from 'react'; function CallbackArgDemo() { const [text, setText] = useState(''); // useCallback으로 메모이제이션된 함수 (인수: prefix) // 빈 의존성 배열을 사용 -> 이 함수 인스턴스는 한 번만 생성됨 const logMessage = useCallback((prefix) => { // 함수 본문에서는 호출 시점에 전달된 'prefix' 인수를 사용함 // text 상태는 사용하지 않음 (인수 전달에 집중하기 위함) console.log(`[Callback] ${prefix}: 이 메시지는 useCallback 함수로부터 왔습니다.`); }, []); // 의존성 없음 // 비교: useCallback 없이 정의된 함수 const logMessagePlain = (prefix) => { // 이 함수는 리렌더링될 때마다 새로 생성됨 console.log(`[Plain] ${prefix}: 이 메시지는 일반 함수로부터 왔습니다.`); }; console.log("--- 컴포넌트 리렌더링 ---"); // useEffect에서 logMessage 함수 참조 (참조 안정성 테스트용) useEffect(() => { console.log("logMessage 함수 참조 변경됨 (useCallback)"); // 이 로그는 logMessage 함수 참조가 변경될 때만 출력됨 // 빈 배열 useCallback 덕분에 마운트 시 1회만 출력될 것임 }, [logMessage]); useEffect(() => { console.log("logMessagePlain 함수 참조 변경됨 (Plain)"); // 이 로그는 리렌더링 시마다 매번 출력될 것임 }, [logMessagePlain]); return ( <div> <h1>useCallback 인수 전달 테스트</h1> <input type="text" value={text} onChange={(e) => setText(e.target.value)} placeholder="리렌더링 유발용 입력창" /> <hr /> <h2>useCallback 함수 호출</h2> {/* 동일한 logMessage 함수를 다른 인수로 호출 */} <button onClick={() => logMessage('INFO')}>정보 로그 (Callback)</button> <button onClick={() => logMessage('WARN')}>경고 로그 (Callback)</button> <hr /> <h2>일반 함수 호출</h2> <button onClick={() => logMessagePlain('INFO')}>정보 로그 (Plain)</button> <button onClick={() => logMessagePlain('WARN')}>경고 로그 (Plain)</button> </div> ); } export default CallbackArgDemo;
4. 예제 코드 분석
CallbackArgDemo
컴포넌트는 입력(text
) 상태를 가지고 있어, 입력할 때마다 리렌더링이 발생한다.logMessage
함수는useCallback(..., [])
으로 정의되어, 컴포넌트가 처음 렌더링될 때 단 한 번만 생성된다. 이후 리렌더링 시에도 동일한 함수 인스턴스가 유지된다. (useEffect
로그 참조)logMessagePlain
함수는useCallback
없이 정의되어, 컴포넌트가 리렌더링될 때마다 매번 새로운 함수 인스턴스가 생성된다. (useEffect
로그 참조)- 핵심 포인트:
- "정보 로그 (Callback)" 버튼과 "경고 로그 (Callback)" 버튼은 동일한
logMessage
함수 인스턴스를 호출한다. - 하지만 버튼을 클릭할 때 각각 다른
prefix
인수('INFO', 'WARN')를 전달한다. logMessage
함수는 호출될 때 전달받은 그prefix
인수를 사용하여 콘솔에 로그를 정확히 출력한다. 이는useCallback
이 인수를 고정하지 않음을 보여준다.
- "정보 로그 (Callback)" 버튼과 "경고 로그 (Callback)" 버튼은 동일한
5. 그럼 useCallback은 왜 사용할까?
- 주된 이유는 참조 안정성(Referential Stability) 확보이다.
- 언제 유용한가?
React.memo
로 감싸진 자식 컴포넌트에 함수를 props로 내려줄 때: 함수 참조가 동일하게 유지되므로 자식 컴포넌트의 불필요한 리렌더링을 방지할 수 있다.useEffect
의 의존성 배열에 함수를 포함할 때: 함수 참조가 변경되지 않으면useEffect
가 불필요하게 재실행되는 것을 막을 수 있다. (예제 코드의useEffect
참고)
6. 결론
useCallback(fn, [])
은 함수fn
의 정의를 메모이제이션하여 참조 안정성을 제공하는 것이지, 함수 호출 시의 인수를 저장하거나 고정하는 것이 아니다.- 메모이제이션된 함수도 일반 함수와 마찬가지로, 호출될 때 전달받는 인수를 사용하여 동작한다.
- 그러니 안심하고
useCallback
으로 메모이제이션된 함수에 다양한 인수를 전달하여 사용해 보자!
728x90반응형'언어·프레임워크 > React.js' 카테고리의 다른 글
[React.js] React 개발, 더 쉽고 빠르게! React Haiku 소개 및 사용법 (0) 2025.03.14 [React.js] 컴포넌트 내부 vs. 외부 함수 정의: 성능 최적화 가이드 (0) 2025.03.12 [React.js] React에서 useRef를 활용한 안정적인 소켓 통신 방법 (0) 2025.02.28 [React.js] "Blocked aria-hidden on an element because its descendant retained focus." 오류 해결 (0) 2025.02.28 [React.js] 리액트 프로젝트 우분투 서버에서 구동하는 방법(npx, nginx 사용) (0) 2025.02.10 다음글이 없습니다.이전글이 없습니다.댓글