Early Riser
생각정리
Early Riser
전체 방문자
오늘
어제
  • 분류 전체보기 (128)
    • JS (19)
    • React (33)
    • React Native (2)
    • Library, Tool (13)
    • CSS (2)
    • Algorithm (40)
    • Computer Science (3)
    • 회고 (3)
    • AI (13)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • 글쓰기

공지사항

인기 글

태그

  • 프로그래머스
  • js
  • 백준
  • 비동기
  • 딥러닝
  • 백트래킹
  • 부스트캠프 합격
  • 파이썬
  • 자바스크립트
  • LGBM
  • global minima
  • useEffect
  • useState
  • 부스트캠프 합격 후기
  • 손실함수
  • 논문리뷰
  • 알고리즘
  • react
  • dfs
  • RNN
  • 구현
  • javascript
  • 완전탐색
  • lightgbm
  • 부스트캠프 9기
  • 밑바닥
  • 오늘의불경
  • local minima
  • BFS
  • boosting

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Early Riser

생각정리

JS

함수의 호출과 참조 전달의 차이(feat: React use)

2026. 1. 8. 20:33

서론

React의 use Hook을 공부하던 중 Promise를 cache하는 법에 대해 살펴보다 함수의 호출과 실행에 대해 의문을 가지게 되었다. 함수의 호출과 참조를 구분하는 것은 react application 최적화에 매우 중요하다

eg:

const [a,seta] = usestate(function())
// Below: lazy Initialization(both works same)
const [a,seta] = usestate(function)
const [a,seta] = usestate(()=>{})

 이 글에서는 함수의 참조와 실행의 차이에 대해 알아보고, use hook Promise cache에 대해 간략하게 알아본다.

함수의 호출과 실행의 차이

 먼저 JavaScript에서 참조를 전달하는 것과 함수 호출을 전달하는 것이 Runtime 동작에서 어떤 차이가 있나를 생각해보자. 함수의 참조를 전달하는 행위는 Memory에 적재된 Function Object의 주소값을 전달하는 것이다. 실행은 되지 않는다. 반면, 함수 호출을 전달하는 것(엄밀히 말하면 전달이 아니라, 실행(evaluate)일 것이다)은 Interpreter가 해당 라인을 읽는 즉시 함수를 실행할 것이다. 즉, 전달된 호출을 Stack에 Push하고 실행(pop)하며, 그 결과로 반환된 값을 전달하는 것이다. 만약 해당 함수가 Object나 Promise 같은 Reference Type을 반환한다면, 함수가 호출될 때마다 Heap Memory에는 새로운 Instance가 생성되며, 이는 매번 고유한 Memory Address를 갖게 된다.

 이 배경지식을 가지고 react use hook의 사용을 생각해보자. react는 컴포넌트가 리렌더링 될때 render phase에서 reconcilation전에 component를 호출하여 host instance를 반환하고 그 host instance로 reconcilation 과정을 거친다.

 따라서 Component가 Rendering될 때마다 use hook 역시 매번 실행된다. 이 과정에서 오류가 발생한다. 아래의 코드를 보자.

import { use } from "react";

// ⚠️ Anti-Pattern Warning
function Note({ id }) {
  // 함수의 실행을 전달하고 있다
  // (runtime때, component가 render phase진입하여 호출되면, 함수의 return값이 use로 전달될 것이다.)
  const note = use(fetchNote(id)); 

  return (
    <div>
      <h1>{note.title}</h1>
      <section>{note.body}</section>
    </div>
  );
}

 비동기 함수가 재실행될 때마다 내용은 동일할지라도 Memory Address가 다른 새로운 Promise Object가 생성된다. React의 use Hook과 Suspense Mechanism은 전달받은 Promise의 Reference 값을 기준으로 작업의 완료 여부를 추적한다. 첫 Rendering 시 생성된 Promise로 인해 Component가 대기 상태에 들어갔다가 데이터가 로드되어 다시 Rendering을 시도할 때, Component 내부의 Logic이 재실행되면서 또다시 새로운 Promise Object가 생성된다. React는 이를 이전과 다른 새로운 요청으로 인식하여 다시 대기 상태로 전환하고, 이 과정이 반복되면서 Infinite Loop에 빠지게 된다.

문제 해결

 따라서 이러한 문제를 해결하기 위해서는 Promise의 Referential Transparency를 보장해야 한다. 즉, Rendering Cycle이 반복되더라도 비동기 요청이 중복으로 발생하지 않고, 이미 생성된 Promise Object의 Memory Address를 유지할 수 있어야 한다. 이를 위해 Server Component 환경에서는 요청 함수를 Caching할 수 있다.

 Client 환경에서는 아래 코드와 같이 Promise 생성의 시점을 Component Rendering 외부로 위임하거나 상위 Component에서 생성된 Promise Instance를 Prop으로 주입받는 구조를 채택해야 한다. 다만 외부로 위임한다면 js파일이 실행될 때 한번만 평가되므로 동적인 값을 전달할 수는 없을 것이다. (use는 함수의 참조가 아닌 이미 실행된 promise를 전달해야 하기에)

 다만 아래 코드의 문제는 Page 컴포넌트가 리렌더링 되면 Promise cache가 무효화 된다는 점이다.

export default function Page({ id }) {
  const notePromise = fetchNote(id); 

  return (
    <Suspense fallback={<p>Loading...</p>}>
      {/* Promise 객체를 그대로 prop으로 전달 */}
      <Note notePromise={notePromise} />
    </Suspense>
  );
}


function Note({ notePromise }) {
  const note = use(notePromise); 

  return (
    <div>
      <h1>{note.title}</h1>
      {/* ... */}
    </div>
  );
}

 혹은 promise를 caching할수도 있다. useMemo나 useCallback을 사용할 수 없는 이유는 Suspense는 render phase에 thorw된 promise를 catch하면 그 컴포넌트를 버리고 fallback을 rendering(fiber화)하기 때문이다.

import { Suspense, use } from 'react';

// Promise를 저장할 객체
const promiseMap = new Map();

function getNotePromise(id) {
  if (!promiseMap.has(id)) {
    const promise = fetch(`https://api.example.com/notes/${id}`).then(res => res.json());
    promiseMap.set(id, promise);
  }
  return promiseMap.get(id);
}

export default function Page({ id }) {
  return (
    <Suspense fallback={<p>Loading...</p>}>
      <Note id={id}/>
    </Suspense>
  );
}

function Note({id}) {
  const notePromise = getNotePromise(id);
  const note = use(notePromise);
  return <h1>{note.title}</h1>;
}

'JS' 카테고리의 다른 글

웹 프론트엔드 성능 지표: RAIL, Core Web Vitals  (0) 2025.12.17
TypedArray에 대하여  (0) 2025.11.24
브라우저에게 양보하기  (0) 2025.09.28
[JS] Javascript에서의 Closures  (0) 2025.09.19
debounce와 throttle의 차이점  (0) 2025.05.29
    'JS' 카테고리의 다른 글
    • 웹 프론트엔드 성능 지표: RAIL, Core Web Vitals
    • TypedArray에 대하여
    • 브라우저에게 양보하기
    • [JS] Javascript에서의 Closures
    Early Riser
    Early Riser
    2년차 프론트엔드 개발자입니다. https://github.com/EarlyRiser42

    티스토리툴바