๐Ÿ”ฅ Trouble Shooting - async/await ๋ง๊ณ  use()๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์ž

์Š˜ยท2025๋…„ 3์›” 13์ผ

๐Ÿ”ฅ Trouble Shooting

๋ชฉ๋ก ๋ณด๊ธฐ
12/23

ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค๊ฐ€ ๋ฆฌํŒฉํ† ๋ง์œผ๋กœ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ async/await์œผ๋กœ ์‚ฌ์šฉ์ค‘์ธ ๊ฒƒ๋“ค์„ use()๋กœ ๋ณ€๊ฒฝ์„ ํ•˜๋˜ ์™€์ค‘์— use()๊ฐ€ ์ ์šฉ์ด ๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋Š”๋ฐ,
๊ทธ๊ฑด ๋ฐ”๋กœ metadata๋ฅผ ์„ค์ •ํ•˜๊ณ  ์žˆ๋Š” generateMetadata()์—์„œ์˜ ์‚ฌ์šฉ์ด์˜€๋‹ค...

๐Ÿšจ ๋ฌธ์ œ ์ฝ”๋“œ

export function generateMetadata({
  params,
}: {
  params: { id: string };
}): Promise<Metadata> {
  const id = params?.id;
  const champion = use(fetchChampionDetail(id));
  if (!champion) {
    return {
      title: "์•Œ ์ˆ˜ ์—†๋Š” ์ฑ”ํ”ผ์–ธ",
      description: "์ฑ”ํ”ผ์–ธ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค",
    };
  }

  return {
    title: `${champion.title + " " + champion.name}์˜ ์ƒ์„ธ ํŽ˜์ด์ง€`,
    description: `${champion.lore}`,
  };
}

use()๋ถ€๋ถ„์— ์•„๋ž˜์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ฒŒ ๋˜์—ˆ๋Š”๋ฐ..
์™œ ์ด๋Ÿฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๊ฒƒ์ธ์ง€
์šฐ์„  use()์— ๋Œ€ํ•ด์„œ ์ข€๋” ์•Œ์•„๋ณด์ž!

React Hook "use" is called in function "generateMetadata" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"

๐Ÿš€ use()๋ž€ ๋ฌด์—‡์ผ๊นŒ?

React์˜ use()๋Š” Next.js์˜ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ ํŒจ์นญ ๋ฉ”์„œ๋“œ๋กœ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” async/await์™€ ๋น„์Šทํ•œ ์—ญํ• ์„ ํ•˜์ง€๋งŒ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•๊ณผ ๋™์ž‘ ๋ฐฉ์‹์ด ๋‹ค๋ฅด๋‹ค.

โ“ use()์™€ async/await์˜ ์ฐจ์ด

๊ตฌ๋ถ„use(fetch())await fetch()
์‚ฌ์šฉ ์œ„์น˜์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ๋งŒ ๊ฐ€๋Šฅ์–ด๋””์„œ๋“  ์‚ฌ์šฉ ๊ฐ€๋Šฅ
๋ Œ๋”๋ง ์‹œ์ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ์ค‘ ์‹คํ–‰๋จ๋ช…์‹œ์ ์œผ๋กœ ํ˜ธ์ถœํ•ด์•ผ ์‹คํ–‰๋จ
Suspense ์ง€์›โœ… ์ž๋™ ๋กœ๋”ฉ ์ƒํƒœ ๊ด€๋ฆฌโŒ ์ง์ ‘ ๋กœ๋”ฉ ์ƒํƒœ ๊ด€๋ฆฌ ํ•„์š”
์ž๋™ ์บ์‹ฑโœ… RSC ๊ธฐ๋Šฅ์œผ๋กœ ์ž๋™ ์บ์‹ฑโŒ ๊ธฐ๋ณธ์ ์œผ๋กœ ์บ์‹ฑ ์•ˆ ๋จ
์ฝ”๋“œ ๊ฐ„๊ฒฐํ•จโœ… ์„ ์–ธ์ ์ด๊ณ  ๊ฐ„๊ฒฐโŒ ์ถ”๊ฐ€์ ์ธ ์ƒํƒœ ๊ด€๋ฆฌ ํ•„์š”

๐Ÿ”ฅ use()๋Š” ๋ฐ˜๋“œ์‹œ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค!

"use server";

export async function fetchChampion() {
	const res = await fetch("xxxx")
    return res.json()
}

// โŒ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ์—๋Ÿฌ ๋ฐœ์ƒ!
const champion = use(fetchChampion()); // โŒ ์—๋Ÿฌ!

๐Ÿ’ฅ ์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑ์‹œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ

๐Ÿšจ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ 

  • use()๋Š” React์˜ ๋ Œ๋”๋ง ์ค‘์— ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•จ
  • ์ปดํฌ๋„ŒํŠธ ๋ฐ”๊นฅ์—์„œ๋Š” React์˜ ๋ Œ๋”๋ง ์ปจํ…์ŠคํŠธ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ

โœ… ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ ์˜ˆ์‹œ

const ChampionComponent = () => {
  const champion = use(fetchChampion()); // โœ… ์ •์ƒ ์ž‘๋™!
  return <h1>{champion.name}</h1>;
};

๐Ÿ”น ์œ„์™€ ๊ฐ™์ด ์ปดํฌ๋„ŒํŠธ ์•ˆ use client๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•ด์•ผ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•จ


๐Ÿค” ๊ทธ๋ ‡๋‹ค๋ฉด, generateMetadata()์—์„œ use()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์™œ ์•ˆ ๋ ๊นŒ?

๐Ÿ’ฅ ์—๋Ÿฌ๊ฐ€ ๋‚˜๋Š” ์ด์œ 

  • gernerateMetadata()๋Š” React ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ Next.js์˜ ์„œ๋ฒ„ ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ!
  • ์ฆ‰, React์˜ ๋ Œ๋”๋ง ์ปจํ…์ŠคํŠธ์—์„œ ์‹คํ–‰๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ use()๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค.

๐Ÿ“Œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: await์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ

๊ฒฐ๊ตญ์€ ๋Œ๊ณ  ๋Œ์•„์„œ async/await ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉ์„ ํ•˜์˜€๋‹ค.

export async function generateMetadata({
  params,
}: {
  params: { id: string };
}): Promise<Metadata> {
  const id = params?.id;
  const champion = await fetchChampionDetail(id);
  if (!champion) {
    return {
      title: "์•Œ ์ˆ˜ ์—†๋Š” ์ฑ”ํ”ผ์–ธ",
      description: "์ฑ”ํ”ผ์–ธ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค",
    };
  }

โžก ์ด๋ ‡๊ฒŒ await์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋ฉด ๋ฌธ์ œ์—†์ด ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค

โš ๏ธ ์ž ๊น

๊ทธ๋ ‡๋‹ค๋ฉด ์–ธ์ œ use()๋ฅผ ์“ฐ๊ณ , ์–ธ์ œ await fetch()๋ฅผ ์จ์•ผํ• ๊นŒ?

๐ŸŽฏ use()์™€ await fetch()๊ฐ๊ฐ ์ ํ•ฉํ•œ ๊ฒฝ์šฐ๋Š” ์–ธ์ œ์ผ๊นŒ?

โœ… use(fetch())๊ฐ€ ์ ํ•ฉํ•œ ๊ฒฝ์šฐ

  • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ๋•Œ
  • Suspense๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ž๋™์œผ๋กœ ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ
  • ์ž๋™ ์บ์‹ฑ์„ ์›ํ•  ๋•Œ (๋™์ผํ•œ ์š”์ฒญ์„ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ณด๋‚ด์ง€ ์•Š๋„๋ก)

โœ… await fetch()๊ฐ€ ์ ํ•ฉํ•œ ๊ฒฝ์šฐ

  • ์„œ๋ฒ„ ์•ก์…˜ (use server) ๋˜๋Š” API ๋ผ์šฐํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ
  • React ์ปดํฌ๋„ŒํŠธ ๋ฐ”๊นฅ์—์„œ ์‹คํ–‰ํ•ด์•ผ ํ•  ๋•Œ (generateMetadata, API ํ•ธ๋“ค๋Ÿฌ ๋“ฑ)
  • ๋งค ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•  ๋•Œ (์บ์‹ฑ์ด ํ•„์š” ์—†๋Š” ๊ฒฝ์šฐ)

๐Ÿš€ ์ •๋ฆฌ: use() vs await fetch() ํ•œ๋ˆˆ์— ๋น„๊ต

ํ•ญ๋ชฉuse(fetch())await fetch()
์‚ฌ์šฉ๋ฒ•use(fetch())await fetch()
์‚ฌ์šฉ ์œ„์น˜์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์–ด๋””์„œ๋“  ์‚ฌ์šฉ ๊ฐ€๋Šฅ
๋ Œ๋”๋ง ๋ฐฉ์‹React ๋ Œ๋”๋ง ์ค‘ ์‹คํ–‰๋ช…์‹œ์ ์œผ๋กœ ์‹คํ–‰
Suspense ์ง€์›โœ… ๊ฐ€๋ŠฅโŒ ์ง์ ‘ ๊ด€๋ฆฌ ํ•„์š”
์ž๋™ ์บ์‹ฑโœ… ์ง€์›โŒ ๊ธฐ๋ณธ์ ์œผ๋กœ ์—†์Œ

๐Ÿ’ก ํ•œ๋งˆ๋””๋กœ:

  • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ use()๋ฅผ ์“ฐ๋ฉด ๋” ๊น”๋”ํ•˜๊ณ  ํšจ์œจ์ 
  • ๊ทธ ์™ธ์˜ ๊ฒฝ์šฐ์—๋Š” await fetch()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋จ
profile
์ฃผ๋‹ˆ์–ด ํ”„๋ก ํŠธ์—”๋“œ ์„ฑ์žฅ๊ธฐ ๊ธฐ๋ก๊ธฐ๋ก

0๊ฐœ์˜ ๋Œ“๊ธ€