cache - This feature is available in the latest Canary

Canary

  • cache๋Š” ์˜ค์ง React ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. React ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง€์›ํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ™•์ธํ•ด ๋ณด์„ธ์š”.

  • cache๋Š” Canary์™€ ์‹คํ—˜ ์ฑ„๋„์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ cache๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ์ด ํ•œ๊ณ„์ ์— ๋Œ€ํ•ด ์ธ์ง€ํ•˜๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. React์˜ ๋ฆด๋ฆฌ์ฆˆ ์ฑ„๋„์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

cache๋Š” ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ๋‚˜ ์—ฐ์‚ฐ์˜ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹ฑํ•˜๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

const cachedFn = cache(fn);

์ฐธ์กฐ

cache(fn)

์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์—์„œ cache๋ฅผ ํ˜ธ์ถœํ•ด ์บ์‹ฑ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ํ•จ์ˆ˜์˜ ํ•œ ๋ฒ„์ „์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import {cache} from 'react';
import calculateMetrics from 'lib/metrics';

const getMetrics = cache(calculateMetrics);

function Chart({data}) {
const report = getMetrics(data);
// ...
}

getMetrics๊ฐ€ ์ฒ˜์Œ data๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, getMetrics๋Š” calculateMetrics(data)๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์บ์‹œ์— ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. getMetrics๊ฐ€ ๊ฐ™์€ data์™€ ํ•จ๊ป˜ ๋‹ค์‹œ ํ˜ธ์ถœ๋˜๋ฉด, calculateMetrics(data)๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜๋Š” ๋Œ€์‹ ์— ์บ์‹ฑ ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜์— ์žˆ๋Š” ์˜ˆ์‹œ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

๋งค๊ฐœ๋ณ€์ˆ˜

  • fn: ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•˜๊ณ  ์‹ถ์€ ํ•จ์ˆ˜. fn๋Š” ์–ด๋–ค ์ธ์ž๊ฐ’๋„ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ณ  ์–ด๋–ค ๊ฒฐ๊ณผ๋„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

cache๋Š” ๊ฐ™์€ ํƒ€์ž… ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๊ฐ€์ง„ fn์˜ ์บ์‹ฑ ๋œ ๋ฒ„์ „์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ fn๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ฃผ์–ด์ง„ ์ธ์ž๊ฐ’๊ณผ ํ•จ๊ป˜ cachedFn๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, ์บ์‹œ์— ์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ๋จผ์ € ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋‹ค๋ฉด, ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์—†๋‹ค๋ฉด, ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ํ•จ๊ป˜ fn์„ ํ˜ธ์ถœํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œ์— ์ €์žฅํ•˜๊ณ  ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. fn๊ฐ€ ์œ ์ผํ•˜๊ฒŒ ํ˜ธ์ถœ๋˜๋Š” ๊ฒฝ์šฐ๋Š” ์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.

์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค!

์ž…๋ ฅ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ˜ํ™˜ ๊ฐ’ ์บ์‹ฑ์„ ์ตœ์ ํ™”ํ•˜๋Š” ๊ฒƒ์„ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. cache์—์„œ ๋ฐ˜ํ™˜๋˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์˜

  • React๋Š” ์„œ๋ฒ„ ์š”์ฒญ๋งˆ๋‹ค ๋ชจ๋“  ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋“ค์„ ์œ„ํ•ด ์บ์‹œ๋ฅผ ๋ฌดํšจํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • cache๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ ํ•จ์ˆ˜๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋™์ผํ•œ ํ•จ์ˆ˜๋กœ cache๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•˜๋ฉด ๋™์ผํ•œ ์บ์‹œ๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š๋Š” ๋‹ค๋ฅธ ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.
  • cachedFn ๋˜ํ•œ ์บ์‹œ ์—๋Ÿฌ๋ฅผ ์žก์•„๋ƒ…๋‹ˆ๋‹ค. fn๊ฐ€ ํŠน์ • ์ธ์ˆ˜์— ๋Œ€ํ•ด ์—๋Ÿฌ๋ฅผ ๋˜์ง€๋ฉด ์บ์‹ฑ ๋˜๊ณ , ๋™์ผํ•œ ์ธ์ˆ˜๋กœ cachedFn๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋™์ผํ•œ ์—๋Ÿฌ๊ฐ€ ๋‹ค์‹œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • cache๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ๋ฒ•

๊ณ ๋น„์šฉ ์—ฐ์‚ฐ ์บ์‹ฑํ•˜๊ธฐ

๋ฐ˜๋ณต ์ž‘์—…์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด cache๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

import {cache} from 'react';
import calculateUserMetrics from 'lib/user';

const getUserMetrics = cache(calculateUserMetrics);

function Profile({user}) {
const metrics = getUserMetrics(user);
// ...
}

function TeamReport({users}) {
for (let user in users) {
const metrics = getUserMetrics(user);
// ...
}
// ...
}

๊ฐ™์€ user ๊ฐ์ฒด๊ฐ€ Profile๊ณผ TeamReport์—์„œ ๋ Œ๋”๋  ๋•Œ, ๋‘ ์ปดํฌ๋„ŒํŠธ๋Š” ์ผ์„ ๊ณต์œ ํ•˜๊ณ , user๋ฅผ ์œ„ํ•œ calculateUserMetrics๋ฅผ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

Profile์ด ๋จผ์ € ๋ Œ๋”๋œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ด…์‹œ๋‹ค. Profile์€ getUserMetrics๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ์บ์‹ฑ ๋œ ๊ฒฐ๊ณผ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. user์™€ ํ•จ๊ป˜ getUserMetrics๋ฅผ ์ฒ˜์Œ ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํ˜„์žฌ ์ €์žฅ๋œ ์บ์‹œ๋Š” ์—†์Šต๋‹ˆ๋‹ค. getUserMetrics๋Š” user์™€ ํ•จ๊ป˜ calculateUserMetrics๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์บ์‹œ์— ๊ฒฐ๊ด๊ฐ’์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

TeamReport๊ฐ€ users ๋ชฉ๋ก๊ณผ ํ•จ๊ป˜ ๋ Œ๋”๋  ๋•Œ ๊ฐ™์€ user ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๊ณ , ์ด๋Š” getUserMetrics๋ฅผ ํ˜ธ์ถœํ•ด ์บ์‹œ์—์„œ ๊ฒฐ๊ด๊ฐ’์„ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.

์ฃผ์˜ํ•˜์„ธ์š”!

๋‹ค๋ฅธ ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋‹ค๋ฅธ ์บ์‹œ์—์„œ ์ฝ์Šต๋‹ˆ๋‹ค.

๊ฐ™์€ ์บ์‹œ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„ , ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฐ˜๋“œ์‹œ ๊ฐ™์€ ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// Temperature.js
import {cache} from 'react';
import {calculateWeekReport} from './report';

export function Temperature({cityData}) {
// ๐Ÿšฉ Wrong: ์ปดํฌ๋„ŒํŠธ์—์„œ `cache`๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๊ฐ ๋ Œ๋”๋ง์— ๋Œ€ํ•ด `getWeekReport`๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
const getWeekReport = cache(calculateWeekReport);
const report = getWeekReport(cityData);
// ...
}
// Precipitation.js
import {cache} from 'react';
import {calculateWeekReport} from './report';

// ๐Ÿšฉ Wrong: `getWeekReport`๋Š” `Precipitation` ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
const getWeekReport = cache(calculateWeekReport);

export function Precipitation({cityData}) {
const report = getWeekReport(cityData);
// ...
}

์œ„์˜ ์˜ˆ์‹œ์—์„œ, Precipitation์™€ Temperature๋Š” ๊ฐ๊ฐ cache๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ž์ฒด ์บ์‹œ ์กฐํšŒ๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ๋ƒ…๋‹ˆ๋‹ค. ๋‘ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ™์€ cityData๋ฅผ ๋ Œ๋”ํ•œ๋‹ค๋ฉด, calculateWeekReport๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐ˜๋ณต ์ž‘์—…์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ฒŒ๋‹ค๊ฐ€, Temperature๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋  ๋•Œ๋งˆ๋‹ค ์–ด๋–ค ์บ์‹œ ๊ณต์œ ๋„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š” ์ƒˆ๋กœ์šด ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์บ์‹œ ์‚ฌ์šฉ์„ ๋Š˜๋ฆฌ๊ณ  ์ผ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด์„œ ๋‘ ์ปดํฌ๋„ŒํŠธ๋Š” ๊ฐ™์€ ์บ์‹œ์— ์ ‘๊ทผํ•˜๋Š” ๊ฐ™์€ ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€์‹ , ์ปดํฌ๋„ŒํŠธ๋ผ๋ฆฌ import ํ•  ์ˆ˜ ์žˆ๋Š” ์ „์šฉ ๋ชจ๋“ˆ์— ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜์„ธ์š”.

// getWeekReport.js
import {cache} from 'react';
import {calculateWeekReport} from './report';

export default cache(calculateWeekReport);
// Temperature.js
import getWeekReport from './getWeekReport';

export default function Temperature({cityData}) {
const report = getWeekReport(cityData);
// ...
}
// Precipitation.js
import getWeekReport from './getWeekReport';

export default function Precipitation({cityData}) {
const report = getWeekReport(cityData);
// ...
}

์—ฌ๊ธฐ ๋‘ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ™์€ ์บ์‹œ๋ฅผ ์ฝ๊ณ  ์“ฐ๊ธฐ ์œ„ํ•ด ./getWeekReport.js๋กœ ๋ถ€ํ„ฐ exportํ•ด ์˜จ ๊ฐ™์€ ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ์˜ ์Šค๋ƒ…์ƒท ๊ณต์œ ํ•˜๊ธฐ

์ปดํฌ๋„ŒํŠธ๋ผ๋ฆฌ ๋ฐ์ดํ„ฐ์˜ ์Šค๋ƒ…์ƒท์„ ๊ณต์œ ํ•˜๊ธฐ ์œ„ํ•ด์„  fetch์™€ ๊ฐ™์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” ํ•จ์ˆ˜์™€ ํ•จ๊ป˜ cache๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ฌ ๋•Œ, ์š”์ฒญ์ด ํ•œ ๋ฒˆ๋งŒ ๋ฐœ์ƒํ•˜๊ณ  ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋Š” ์บ์‹ฑ ๋˜๋ฉฐ ์ปดํฌ๋„ŒํŠธ๋ผ๋ฆฌ ๊ณต์œ ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ์„œ๋ฒ„ ๋ Œ๋”๋ง ์ „๋ฐ˜์— ๊ฑธ์ณ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ ์Šค๋ƒ…์ƒท์„ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.

import {cache} from 'react';
import {fetchTemperature} from './api.js';

const getTemperature = cache(async (city) => {
return await fetchTemperature(city);
});

async function AnimatedWeatherCard({city}) {
const temperature = await getTemperature(city);
// ...
}

async function MinimalWeatherCard({city}) {
const temperature = await getTemperature(city);
// ...
}

AnimatedWeatherCard์™€ MinimalWeatherCard๊ฐ€ ๊ฐ™์€ ๋„์‹œ๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ, ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋กœ ๋ถ€ํ„ฐ ๊ฐ™์€ ๋ฐ์ดํ„ฐ์˜ ์Šค๋ƒ…์ƒท์„ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

AnimatedWeatherCard์™€ MinimalWeatherCard๊ฐ€ ๋‹ค๋ฅธ ๋„์‹œ๋ฅผ getTemperature์˜ ์ธ์ž๋กœ ๋ฐ›๊ฒŒ ๋œ๋‹ค๋ฉด, fetchTemperature๋Š” ๋‘ ๋ฒˆ ํ˜ธ์ถœ๋˜๊ณ  ํ˜ธ์ถœ๋งˆ๋‹ค ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋„์‹œ๊ฐ€ ์บ์‹œ ํ‚ค์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค!

๋น„๋™๊ธฐ ๋ Œ๋”๋ง์€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์ง€์›๋ฉ๋‹ˆ๋‹ค.

async function AnimatedWeatherCard({city}) {
const temperature = await getTemperature(city);
// ...
}

ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด use ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

์‚ฌ์ „์— ๋ฐ์ดํ„ฐ ๋ฐ›์•„๋‘๊ธฐ

์žฅ์‹œ๊ฐ„ ์‹คํ–‰๋˜๋Š” ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์บ์‹ฑํ•˜๋ฉด, ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๊ธฐ ์ „์— ๋น„๋™๊ธฐ ์ž‘์—…์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const getUser = cache(async (id) => {
return await db.user.query(id);
}

async function Profile({id}) {
const user = await getUser(id);
return (
<section>
<img src={user.profilePic} />
<h2>{user.name}</h2>
</section>
);
}

function Page({id}) {
// โœ… Good: ์œ ์ € ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
getUser(id);
// ... ๋ช‡๋ช‡์˜ ๊ณ„์‚ฐ ์ž‘์—…๋“ค
return (
<>
<Profile id={id} />
</>
);
}

Page๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ, ์ปดํฌ๋„ŒํŠธ๋Š” getUser๋ฅผ ํ˜ธ์ถœํ•˜์ง€๋งŒ, ๋ฐ˜ํ™˜๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜์„ธ์š”. ์ด ์ดˆ๊ธฐ getUser ํ˜ธ์ถœ์€ ํŽ˜์ด์ง€๊ฐ€ ๋‹ค๋ฅธ ๊ณ„์‚ฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ž์‹์„ ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ ๋ฐœ์ƒํ•˜๋Š”, ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

Profile์„ ๋ Œ๋”๋งํ•  ๋•Œ, getUser๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ดˆ๊ธฐ getUser ํ˜ธ์ถœ์ด ์ด๋ฏธ ์œ ์ € ๋ฐ์ดํ„ฐ์— ๋ฐ˜ํ™˜๋˜๊ณ  ์บ์‹ฑ ๋˜์—ˆ๋‹ค๋ฉด, Profile์ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๊ณ  ๊ธฐ๋‹ค๋ฆด ๋•Œ, ๋‹ค๋ฅธ ์›๊ฒฉ ํ”„๋กœ์‹œ์ € ํ˜ธ์ถœ ์—†์ด ์‰ฝ๊ฒŒ ์บ์‹œ์—์„œ ์ฝ์–ด์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ์š”์ฒญ์ด ์™„๋ฃŒ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ, ์ด ํŒจํ„ด์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋กœ๋“œํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ฌ ๋•Œ ์ƒ๊ธฐ๋Š” ์ง€์—ฐ์ด ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.

Deep Dive

๋น„๋™๊ธฐ ์ž‘์—… ์บ์‹ฑํ•˜๊ธฐ

๋น„๋™๊ธฐ ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด, Promise๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์ด Promise๋Š” ์ž‘์—…์— ๋Œ€ํ•œ ์ƒํƒœ(๋ณด๋ฅ˜ ์ค‘, ์™„๋ฃŒ๋จ, ์‹คํŒจํ•จ)์™€ ์ตœ์ข…์ ์œผ๋กœ ํ™•์ •๋œ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์˜ˆ์‹œ์—์„œ, ๋น„๋™๊ธฐ ํ•จ์ˆ˜ fetchData๋Š” fetch๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” Promise๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

async function fetchData() {
return await fetch(`https://...`);
}

const getData = cache(fetchData);

async function MyComponent() {
getData();
// ... some computational work
await getData();
// ...
}

getData๋ฅผ ์ฒ˜์Œ ํ˜ธ์ถœํ•  ๋•Œ, fetchData์—์„œ ๋ฐ˜ํ™˜๋œ Promise๊ฐ€ ์บ์‹ฑ ๋ฉ๋‹ˆ๋‹ค. ์ดํ›„ ์กฐํšŒ ์‹œ, ๊ฐ™์€ Promise๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ getData ํ˜ธ์ถœ์€ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์ง€๋งŒ(await) ๋‘ ๋ฒˆ์งธ๋Š” ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. await ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—ฐ์‚ฐ์ž๋กœ, ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ํ™•์ •๋œ Promise์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ getData์€ ๋‹จ์ˆœํžˆ ์กฐํšŒํ•  ๋‘ ๋ฒˆ์งธ getData์— ๋Œ€ํ•œ Promise๋ฅผ ์บ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด fetch๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ ํ˜ธ์ถœ์—์„œ Promise๊ฐ€ ์—ฌ์ „ํžˆ _๋ณด๋ฅ˜ ์ค‘_์ด๋ฉด, ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ await๊ฐ€ ์ผ์‹œ ์ค‘์ง€๋ฉ๋‹ˆ๋‹ค. ์ด ์ตœ์ ํ™”๋Š” ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ React๊ฐ€ ๊ณ„์‚ฐ ์ž‘์—…์„ ๊ณ„์†ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ๋‘ ๋ฒˆ์งธ ํ˜ธ์ถœ์— ๋Œ€ํ•œ ๋Œ€๊ธฐ ์‹œ๊ฐ„์„ ์ค„์ผ ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

์™„๋ฃŒ๋œ ๊ฒฐ๊ณผ๋‚˜ ์—๋Ÿฌ์— ๋Œ€ํ•œ Promise๊ฐ€ ์ด๋ฏธ ์ •ํ•ด์ง„ ๊ฒฝ์šฐ, await๋Š” ์ฆ‰์‹œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๊ฒฐ๊ณผ ๋ชจ๋‘ ์„ฑ๋Šฅ์ƒ์˜ ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ฃผ์˜ํ•˜์„ธ์š”!

์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์—์„œ ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์บ์‹œ๊ฐ€ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
import {cache} from 'react';

const getUser = cache(async (userId) => {
return await db.user.query(userId);
});

// ๐Ÿšฉ Wrong: ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์—์„œ ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋ฉ”๋ชจํ™”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
getUser('demo-id');

async function DemoProfile() {
// โœ… Good: `getUser`๋Š” ๋ฉ”๋ชจํ™” ๋ฉ๋‹ˆ๋‹ค.
const user = await getUser('demo-id');
return <Profile user={user} />;
}

React๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜์˜ ์บ์‹œ ์ ‘๊ทผ๋งŒ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์—์„œ getUser๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์—ฌ์ „ํžˆ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜์ง€๋งŒ, ์บ์‹œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

์ด๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์ปจํ…์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์บ์‹œ ์ ‘๊ทผ์ด ์ œ๊ณต๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

Deep Dive

cache, memo, or useMemo ์ค‘ ์–ธ์ œ ์–ด๋–ค ๊ฑธ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋‚˜์š”?

์–ธ๊ธ‰๋œ ๋ชจ๋“  API๋“ค์€ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ๋ฉ”๋ชจํ™” ๋Œ€์ƒ, ์บ์‹œ ์ ‘๊ทผ ๊ถŒํ•œ, ์บ์‹œ ๋ฌดํšจํ™” ์‹œ์ ์— ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

useMemo

์ผ๋ฐ˜์ ์œผ๋กœ useMemo๋Š” ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ Œ๋”๋ง์— ๊ฑธ์ณ ๊ณ ๋น„์šฉ์˜ ๊ณ„์‚ฐ์„ ์บ์‹ฑํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™˜์„ ๋ฉ”๋ชจํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

'use client';

function WeatherReport({record}) {
const avgTemp = useMemo(() => calculateAvg(record)), record);
// ...
}

function App() {
const record = getRecord();
return (
<>
<WeatherReport record={record} />
<WeatherReport record={record} />
</>
);
}

์ด ์˜ˆ์‹œ์—์„œ App์€ ๋‘ ๊ฐœ์˜ WeatherReport๋ฅผ ๊ฐ™์€ ๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ ๋ Œ๋”ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‘ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ–ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์„œ๋กœ ์ž‘์—…์„ ๊ณต์œ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. useMemo์˜ ์บ์‹œ๋Š” ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ useMemo๋Š” App์ด ๋‹ค์‹œ ๋ Œ๋”๋ง ๋˜๊ณ  record ๊ฐ์ฒด๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, ๊ฐ ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ž‘์—…์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๋ฉ”๋ชจํ™”๋œ avgTemp์˜ ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. useMemo๋Š” ์ฃผ์–ด์ง„ ์ข…์†์„ฑ์„ ๊ฐ€์ง„ avgTemp์˜ ๋งˆ์ง€๋ง‰ ๊ณ„์‚ฐ๋งŒ ์บ์‹ฑํ•ฉ๋‹ˆ๋‹ค.

cache

์ผ๋ฐ˜์ ์œผ๋กœ cache๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ปดํฌ๋„ŒํŠธ ๊ฐ„์— ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์„ ๋ฉ”๋ชจํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

const cachedFetchReport = cache(fetchReport);

function WeatherReport({city}) {
const report = cachedFetchReport(city);
// ...
}

function App() {
const city = "Los Angeles";
return (
<>
<WeatherReport city={city} />
<WeatherReport city={city} />
</>
);
}

์ด์ „ ์˜ˆ์ œ๋ฅผ cache๋ฅผ ์ด์šฉํ•ด ์žฌ์ž‘์„ฑํ•˜๋ฉด, ์ด ๊ฒฝ์šฐ์— WeatherReport์˜ ๋‘ ๋ฒˆ์งธ ์ธ์Šคํ„ด์Šค๋Š” ์ค‘๋ณต ์ž‘์—…์„ ์ƒ๋žตํ•˜๊ณ  ์ฒซ ๋ฒˆ์งธ WeatherReport์™€ ๊ฐ™์€ ์บ์‹œ๋ฅผ ์ฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด์ „ ์˜ˆ์ œ์™€ ๋‹ค๋ฅธ ์ ์€ ๊ณ„์‚ฐ์—๋งŒ ์‚ฌ์šฉ๋˜๋Š” useMemo์™€ ๋‹ฌ๋ฆฌ cache๋Š” ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ๋ฉ”๋ชจํ™”ํ•˜๋Š” ๋ฐ๋„ ๊ถŒ์žฅ๋œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

์ด๋•Œ, cache๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฉฐ ์บ์‹œ๋Š” ์„œ๋ฒ„ ์š”์ฒญ ์ „์ฒด์—์„œ ๋ฌดํšจํ™”๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

memo

memo๋Š” ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜์„ ๋•Œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žฌ ๋ Œ๋”๋ง ๋˜๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

'use client';

function WeatherReport({record}) {
const avgTemp = calculateAvg(record);
// ...
}

const MemoWeatherReport = memo(WeatherReport);

function App() {
const record = getRecord();
return (
<>
<MemoWeatherReport record={record} />
<MemoWeatherReport record={record} />
</>
);
}

์˜ˆ์ œ์—์„œ MemoWeatherReport ์ปดํฌ๋„ŒํŠธ ๋ชจ๋‘ ์ฒซ ๋ฒˆ์งธ ๋ Œ๋”์—์„œ calculateAvg๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ App์ด ์žฌ ๋ Œ๋”๋ง ๋  ๋•Œ record์˜ ๋ณ€๊ฒฝ์ด ์—†๋‹ค๋ฉด ํ”„๋กœํผํ‹ฐ์˜ ๋ณ€๊ฒฝ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— MemoWeatherReport๊ฐ€ ๋‹ค์‹œ ๋ Œ๋”๋ง ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

useMemo์™€ ๋น„๊ตํ•˜๋ฉด memo๋Š” ํ”„๋กœํผํ‹ฐ์™€ ํŠน์ • ๊ณ„์‚ฐ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง์„ ๋ฉ”๋ชจํ™”ํ•ฉ๋‹ˆ๋‹ค. useMemo์™€ ์œ ์‚ฌํ•˜๊ฒŒ, ๋ฉ”๋ชจํ™”๋œ ์ปดํฌ๋„ŒํŠธ๋Š” ๋งˆ์ง€๋ง‰ ํ”„๋กœํผํ‹ฐ ๊ฐ’์— ๋Œ€ํ•œ ๋งˆ์ง€๋ง‰ ๋ Œ๋”๋ง์„ ์บ์‹ฑํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด, ์บ์‰ฌ๋Š” ๋ฌดํšจํ™”๋˜๊ณ  ์ปดํฌ๋„ŒํŠธ๋Š” ์žฌ ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค.


๋ฌธ์ œ ํ•ด๊ฒฐ

๋™์ผํ•œ ์ธ์ˆ˜๋กœ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด๋„ ๋ฉ”๋ชจ๋œ ํ•จ์ˆ˜๊ฐ€ ๊ณ„์† ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์•ž์„œ ์–ธ๊ธ‰๋œ ์ฃผ์˜ ์‚ฌํ•ญ๋“ค์„ ํ™•์ธํ•˜์„ธ์š”.

์œ„์˜ ์–ด๋Š ๊ฒƒ๋„ ํ•ด๋‹นํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, React๊ฐ€ ์บ์‹œ์— ๋ฌด์—‡์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ์‹์— ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ธ์ž๊ฐ€ ์›์‹œ ๊ฐ’(๊ฐ์ฒด, ํ•จ์ˆ˜, ๋ฐฐ์—ด ๋“ฑ) ์ด ์•„๋‹ˆ๋ผ๋ฉด, ๊ฐ™์€ ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ๋„˜๊ฒผ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

๋ฉ”๋ชจํ™”๋œ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ, React๋Š” ์ž…๋ ฅ๋œ ์ธ์ž๊ฐ’์„ ์กฐํšŒํ•ด ๊ฒฐ๊ณผ๊ฐ€ ์ด๋ฏธ ์บ์‹ฑ ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. React๋Š” ์ธ์ˆ˜์˜ ์–•์€ ๋™๋“ฑ์„ฑ์„ ์‚ฌ์šฉํ•ด ์บ์‹œ ํžˆํŠธ๊ฐ€ ์žˆ๋Š”์ง€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

import {cache} from 'react';

const calculateNorm = cache((vector) => {
// ...
});

function MapMarker(props) {
// ๐Ÿšฉ Wrong: ์ธ์ž๊ฐ€ ๋งค ๋ Œ๋”๋ง๋งˆ๋‹ค ๋ณ€๊ฒฝ๋˜๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
const length = calculateNorm(props);
// ...
}

function App() {
return (
<>
<MapMarker x={10} y={10} z={10} />
<MapMarker x={10} y={10} z={10} />
</>
);
}

์ด ๊ฒฝ์šฐ ๋‘ MapMarker๋Š” ๋™์ผํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋™์ผํ•œ ๊ฐ’์ธ {x: 10, y: 10, z:10}์™€ ํ•จ๊ป˜ calculateNorm๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋“ฏ ๋ณด์ž…๋‹ˆ๋‹ค. ๊ฐ์ฒด์— ๋™์ผํ•œ ๊ฐ’์ด ํฌํ•จ๋˜์–ด ์žˆ๋”๋ผ๋„ ๊ฐ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์ฒด ํ”„๋กœํผํ‹ฐ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ, ๋™์ผํ•œ ๊ฐ์ฒด ์ฐธ์กฐ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

React๋Š” ์ž…๋ ฅ์—์„œ Object.is๋ฅผ ํ˜ธ์ถœํ•ด ์บ์‹œ ํžˆํŠธ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

import {cache} from 'react';

const calculateNorm = cache((x, y, z) => {
// ...
});

function MapMarker(props) {
// โœ… Good: ๋ฉ”๋ชจํ™” ํ•จ์ˆ˜์— ์ธ์ž๋กœ ์›์‹œ๊ฐ’ ์ œ๊ณตํ•˜๊ธฐ
const length = calculateNorm(props.x, props.y, props.z);
// ...
}

function App() {
return (
<>
<MapMarker x={10} y={10} z={10} />
<MapMarker x={10} y={10} z={10} />
</>
);
}

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ํ•œ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ๋ฒกํ„ฐ ์ฐจ์›์„ calculateNorm์— ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฐจ์› ์ž์ฒด๊ฐ€ ์›์‹œ ๊ฐ’์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ ๋ฒกํ„ฐ ๊ฐ์ฒด๋ฅผ ์ปดํฌ๋„ŒํŠธ์˜ ํ”„๋กœํผํ‹ฐ๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋‘ ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค์— ๋™์ผํ•œ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

import {cache} from 'react';

const calculateNorm = cache((vector) => {
// ...
});

function MapMarker(props) {
// โœ… Good: ๋™์ผํ•œ `vector` ๊ฐ์ฒด๋ฅผ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค.
const length = calculateNorm(props.vector);
// ...
}

function App() {
const vector = [10, 10, 10];
return (
<>
<MapMarker vector={vector} />
<MapMarker vector={vector} />
</>
);
}