experimental_taintObjectReference

๊ฐœ๋ฐœ์ค‘์ด์—์š”

์ด API๋Š” ์‹คํ—˜์ ์ด๋ฉฐ React ์•ˆ์ • ๋ฒ„์ „์—์„œ๋Š” ์•„์ง ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ด API๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด React ํŒจํ‚ค์ง€๋ฅผ ๊ฐ€์žฅ ์ตœ๊ทผ์˜ ์‹คํ—˜์ ์ธ ๋ฒ„์ „์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • react@experimental
  • react-dom@experimental
  • eslint-plugin-react-hooks@experimental

์‹คํ—˜์ ์ธ ๋ฒ„์ „์˜ React์—๋Š” ๋ฒ„๊ทธ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”.

์ด API๋Š” React ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

taintObjectReference๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด user ๊ฐ์ฒด์™€ ๊ฐ™์€ ํŠน์ •ํ•œ ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „์†กํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

experimental_taintObjectReference(message, object);

ํ‚ค, ํ•ด์‹œ ๋˜๋Š” ํ† ํฐ์ด ์ „๋‹ฌ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ taintUniqueValue๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.


๋ ˆํผ๋Ÿฐ์Šค

taintObjectReference(message, object)

ํด๋ผ์ด์–ธํŠธ๋กœ ์ „๋‹ฌ๋˜์ง€ ์•Š์•„์•ผ ํ•  ๊ฐ์ฒด๋ฅผ taintObjectReference์™€ ํ•จ๊ป˜ ํ˜ธ์ถœํ•˜์—ฌ React์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

import {experimental_taintObjectReference} from 'react';

experimental_taintObjectReference(
'ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋Š” ํด๋ผ์ด์–ธํŠธ๋กœ ์ „๋‹ฌํ•˜์ง€ ๋งˆ์„ธ์š”.',
process.env
);

๋” ๋งŽ์€ ์˜ˆ์ œ๋ฅผ ์•„๋ž˜์—์„œ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

  • message: ๊ฐ์ฒด๊ฐ€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ๋  ๋•Œ ํ‘œ์‹œํ•  ๋ฉ”์‹œ์ง€. ๊ฐ์ฒด๊ฐ€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ๋  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ ๊ฐ์ฒด์— ํฌํ•จ๋˜์–ด ๋‚˜ํƒ€๋‚˜๋Š” ๋ฉ”์‹œ์ง€์ž…๋‹ˆ๋‹ค.

  • object: ์˜ค์—ผ(taint)๋  ๊ฐ์ฒด. ํ•จ์ˆ˜์™€ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค๋„ object๋กœ์„œ taintObjectReference์— ์ „๋‹ฌ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜์™€ ํด๋ž˜์Šค๋Š” ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ๋˜์ง€ ์•Š๋„๋ก ์ด๋ฏธ ๋ง‰ํ˜€์žˆ์ง€๋งŒ React์˜ ๊ธฐ๋ณธ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋Œ€์‹  message์— ์„ค์ •ํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํƒ€์ž… ๋ฐฐ์—ด(Typed Array)์˜ ์ธ์Šคํ„ด์Šค๋ฅผ object๋กœ์„œ taintObjectReference์— ์ „๋‹ฌํ•˜๋ฉด ๊ฐ™์€ ํƒ€์ž… ๋ฐฐ์—ด์˜ ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค๊ฐ€ ์˜ค์—ผ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

experimental_taintObjectReference๋Š” undefined๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์˜์‚ฌํ•ญ

  • ์˜ค์—ผ๋œ ๊ฐ์ฒด๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ๋ณต์ œํ•˜๋ฉด ์˜ค์—ผ๋˜์ง€ ์•Š์€ ๊ฐ์ฒด๊ฐ€ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค. ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด๋Š” ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์˜ค์—ผ๋œ user ๊ฐ์ฒด๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•  ๋•Œ, const userInfo = {name: user.name, ssn: user.ssn} ํ˜น์€ {...user}๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์˜ค์—ผ๋˜์ง€ ์•Š์€ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. taintObjectReference๋Š” ๊ฐ์ฒด๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ทธ๋Œ€๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฒƒ๋งŒ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

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

๋ณด์•ˆ์„ ์˜ค์—ผ์—๋งŒ ์˜์กดํ•˜์ง€ ๋งˆ์„ธ์š”. ๊ฐ์ฒด๋ฅผ ์˜ค์—ผ์‹œ์ผฐ๋‹ค๊ณ  ํ•ด์„œ ๋ชจ๋“  ๋ˆ„์ถœ ๊ฐ€๋Šฅ์„ฑ์„ ๋ง‰์„ ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์˜ค์—ผ๋œ ๊ฐ์ฒด๋ฅผ ๋ณต์ œํ•˜๋ฉด ์˜ค์—ผ๋˜์ง€ ์•Š์€ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค. ์˜ค์—ผ๋œ ๊ฐ์ฒด์—์„œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ(์˜ˆ. {secret: taintedObj.secret}) ์ž‘์„ฑ๋œ ์ƒˆ ๊ฐ’์ด๋‚˜ ๊ฐ์ฒด๋Š” ์˜ค์—ผ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ค์—ผ์€ ํ•œ ๊ฒน์˜ ๋ณดํ˜ธ ์žฅ์น˜์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. ๋ณด์•ˆ์„ฑ์ด ๋†’์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์—ฌ๋Ÿฌ ๊ฒน์˜ ๋ณดํ˜ธ ์žฅ์น˜์™€ ์ž˜ ์„ค๊ณ„๋œ API๋ฅผ ๋งˆ๋ จํ•ด ๋‘๊ณ  ๊ฒฉ๋ฆฌ ํŒจํ„ด์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.


์‚ฌ์šฉ๋ฒ•

์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๊ฐ€ ์˜๋„ํ•˜์ง€ ์•Š๊ฒŒ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ

ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—๋Š” ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์€ ๊ฐ์ฒด๊ฐ€ ์ „๋‹ฌ๋˜์–ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์ด์ƒ์ ์œผ๋กœ, ๋ฐ์ดํ„ฐ ํŽ˜์น˜ ํ•จ์ˆ˜๋Š” ํ˜„์žฌ ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋…ธ์ถœํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฆฌํŒฉํ† ๋ง ๋„์ค‘ ๊ฐ€๋” ์‹ค์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ API์—์„œ ์‚ฌ์šฉ์ž ๊ฐ์ฒด๋ฅผ โ€œ์˜ค์—ผ(taint)โ€œ์‹œ์ผœ์„œ ์ด๋Ÿฌํ•œ ์‹ค์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import {experimental_taintObjectReference} from 'react';

export async function getUser(id) {
const user = await db`SELECT * FROM users WHERE id = ${id}`;
experimental_taintObjectReference(
'user ๊ฐ์ฒด ์ „์ฒด๋ฅผ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „๋‹ฌํ•˜์ง€ ๋งˆ์„ธ์š”.' +
'ํ•„์š”ํ•˜๋‹ค๋ฉด ์ผ๋ถ€ ํŠน์ •ํ•œ ํ”„๋กœํผํ‹ฐ๋งŒ ๋ฝ‘์•„์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.',
user,
);
return user;
}

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

Deep Dive

๋ฐ์ดํ„ฐ ํŽ˜์น˜์—์„œ ๋ˆ„์ถœ ๋ฐฉ์ง€ํ•˜๊ธฐ

๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ํ™˜๊ฒฝ์„ ์‹คํ–‰ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ์ „๋‹ฌํ•  ๋•Œ ์ฃผ์˜๋ฅผ ๊ธฐ์šธ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// api.js
export async function getUser(id) {
const user = await db`SELECT * FROM users WHERE id = ${id}`;
return user;
}
import { getUser } from 'api.js';
import { InfoCard } from 'components.js';

export async function Profile(props) {
const user = await getUser(props.userId);
// DO NOT DO THIS
return <InfoCard user={user} />;
}
// components.js
"use client";

export async function InfoCard({ user }) {
return <div>{user.name}</div>;
}

์ด์ƒ์ ์œผ๋กœ, getUser๋Š” ํ˜„์žฌ ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋…ธ์ถœํ•˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. user ๊ฐ์ฒด๊ฐ€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด ์‚ฌ์šฉ์ž ๊ฐ์ฒด๋ฅผ โ€œ์˜ค์—ผ(taint)โ€œ์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// api.js
import {experimental_taintObjectReference} from 'react';

export async function getUser(id) {
const user = await db`SELECT * FROM users WHERE id = ${id}`;
experimental_taintObjectReference(
'user ๊ฐ์ฒด ์ „์ฒด๋ฅผ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „๋‹ฌํ•˜์ง€ ๋งˆ์„ธ์š”. ' +
'ํ•„์š”ํ•˜๋‹ค๋ฉด ์ผ๋ถ€ ํŠน์ •ํ•œ ํ”„๋กœํผํ‹ฐ๋งŒ ๋ฝ‘์•„์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.',
user,
);
return user;
}

์ด์ œ ๋ˆ„๊ตฐ๊ฐ€ user ๊ฐ์ฒด๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์„ค์ •ํ•œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.