← 글 목록

notes

use ??

JavaScript nullish coalescing 연산자 ??의 동작·사용 기준과 주의점 메모.


한 줄로

use(리소스) = Promise 결과나 Context 값을 컴포넌트 안에서 바로 읽는 함수

(조건문·반복문 안에서도 호출 가능 — 일반 훅과 다른 점)


무엇을 읽을 수 있나


1. Promise와 함께 (Suspense 연동)

async function getCamp(seq: number) {
  const res = await fetch(`/api/camp/${seq}`);
  return res.json();
}

function CampDetail({ campPromise }: { campPromise: Promise<Camp> }) {
  const camp = use(campPromise); // resolve될 때까지 Suspense가 대기

  return <h1>{camp.name}</h1>;
}

// 부모
<Suspense fallback={<Skeleton />}>
  <CampDetail campPromise={getCamp(123)} />
</Suspense>

동작

  • Promise가 아직 안 끝나면 → Suspense boundary로 fallback 표시
  • 성공 → use가 결과 반환
  • 실패 → 가까운 Error Boundary로 전달

useEffect + useState로 fetch 하던 패턴을, 서버/부모에서 Promise 넘기고 use로 읽는 방식으로 바꿀 수 있습니다.


2. Context와 함께

const ThemeContext = createContext<'light' | 'dark'>('light');

function Button() {
  const theme = use(ThemeContext); // useContext(ThemeContext)와 동일

  return <button className={theme}>...</button>;
}

useContext와 비슷하지만, use는 조건문 안에서도 호출할 수 있습니다.

function Panel({ showTheme }: { showTheme: boolean }) {
  if (showTheme) {
    const theme = use(ThemeContext); // ✅ useContext는 여기서 불가
    return <div>{theme}</div>;
  }
  return null;
}

일반 훅과의 차이


useEffect + fetch vs use + Promise

기존 (useEffect)

const [data, setData] = useState(null);
useEffect(() => {
  fetch('/api').then(r => r.json()).then(setData);
}, []);
if (!data) return <Loading />;

use + Suspense

const data = use(fetch('/api').then(r => r.json()));
// 로딩은 Suspense fallback이 처리

로딩 UI를 컴포넌트마다 if (!data)로 두지 않고, Suspense 한곳에서 처리할 수 있습니다.


주의할 점

  • Promise는 렌더마다 새로 만들면 안 됨

    매 렌더 use(fetch(...)) 하면 Suspense가 반복됩니다.

    → 부모/cache/useMemo 등으로 안정적인 Promise를 넘기세요.

  • 클라이언트 컴포넌트

    Next.js App Router에서는 "use client" 안에서 사용합니다.

  • 에러 처리

    reject된 Promise는 Error Boundary가 잡아야 합니다.

  • 아직 모든 팀이 React 19는 아님

    thankqcamping이 React 19면 사용 가능, 그 전 버전에는 없습니다.


Next.js에서 흔한 패턴

// Server Component에서 Promise 생성
export default function Page() {
  const campPromise = getCampData(seq); // await 안 함
  return (
    <Suspense fallback={<Skeleton />}>
      <CampClient campPromise={campPromise} />
    </Suspense>
  );
}

// Client Component
'use client';
function CampClient({ campPromise }: { campPromise: Promise<Camp> }) {
  const camp = use(campPromise);
  return <div>{camp.name}</div>;
}

서버에서 시작한 fetch를 클라이언트에서 use로 이어 받는 흐름입니다.


요약

use = “이 Promise/Context 값을 지금 읽어라” — 로딩은 Suspense, 실패는 Error Boundary에 맡기는 스타일입니다.