학습 문서/api reference/config/next config js

taint#

Experimental: 이 기능은 현재 experimental 상태이며 변경될 수 있다. production 환경에는 권장하지 않는다. 사용해 보고 GitHub에 피드백을 남길 수 있다.

학습 목표#

  • experimental.taint를 활성화해 React의 experimental_taintObjectReference와 experimental_taintUniqueValue를 사용하는 방법을 익힌다.
  • 민감한 데이터가 Server Component와 Client Component 사이의 경계를 넘어가지 못하게 taint가 막는 방식을 이해한다.
  • 객체 참조, 고유 값, taint된 값에서 파생한 값이 각각 어떻게 처리되는지 구분한다.
  • experimental 기능의 제한과 process.env에 적용되는 기본 taint의 범위를 확인한다.

핵심 개념 및 설명#

사용법 (Usage)#

taint 옵션은 객체와 값에 taint를 적용하는 React experimental API를 활성화한다. 이 기능은 민감한 데이터가 실수로 클라이언트에 전달되는 일을 막는 데 도움이 된다. 활성화하면 다음 API를 사용할 수 있다.

알아두면 좋은 점: 이 flag를 활성화하면 app 디렉토리에서 React experimental 채널도 활성화한다. process.env에도 taint를 적용하므로 process.env 전체를 Client Component에 전달할 수 없다.
next.config.tsts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  experimental: {
    taint: true,
  },
}

export default nextConfig
경고: 클라이언트에 민감한 데이터가 노출되는 것을 막는 수단으로 taint API에만 의존하지 않는다. 보안 권장 사항을 확인한다.

taint API를 쓰면 클라이언트로 전달해서는 안 되는 데이터를 선언적으로 명시해 방어적으로 처리할 수 있다. 객체나 값이 Server Component와 Client Component 사이의 경계를 넘어가면 React가 오류를 발생시킨다.

다음 상황에서 유용하다.

  • 데이터를 읽는 메서드를 제어할 수 없다.
  • 직접 정의하지 않은 민감한 데이터 형태를 다뤄야 한다.
  • Server Component를 렌더링하는 중에 민감한 데이터에 접근한다.

민감한 데이터가 필요하지 않은 컨텍스트로 반환되지 않도록 데이터와 API를 설계하는 편이 낫다.

주의사항 (Caveats)#

  • taint는 객체를 참조로만 추적한다. 객체를 복사하면 taint가 적용되지 않은 객체가 생기므로 API가 제공하던 보장도 사라진다. 복사한 객체에는 taint를 다시 적용해야 한다.
  • 기본 제공되는 process.env taint는 객체 참조에만 적용된다. process.env.MY_VAR처럼 개별 변수를 읽어 얻은 문자열을 Client Component에 전달할 때는 영향을 받지 않고, { ...process.env }처럼 복사한 값을 전달할 때도 마찬가지다.
  • taint는 taint된 값에서 파생된 데이터를 추적할 수 없다. 파생한 값에도 taint를 적용해야 한다.
  • 값은 수명 참조(lifetime reference)가 범위 안에 있는 동안 taint된 상태로 유지된다. 자세한 내용은 `experimental_taintUniqueValue` 매개변수 참고 문서를 확인한다.

예제 (Examples)#

객체 참조에 taint 적용 (Tainting an object reference)#

getUserDetails 함수는 특정 사용자의 데이터를 반환한다. 사용자 객체 참조에 taint를 적용하면 해당 객체는 Server Component와 Client Component 사이의 경계를 넘어가지 못한다. 여기서는 UserCard가 Client Component라고 가정한다.

ts
import { experimental_taintObjectReference } from 'react'

async function getUserDetails(id: string): Promise<UserDetails> {
  const user = await db.queryUserById(id)

  experimental_taintObjectReference(
    'Do not use the entire user info object. Instead, select only the fields you need.',
    user
  )

  return user
}

taint된 userDetails 객체의 개별 필드는 계속 읽을 수 있다.

app/contact/page.tsxtsx
export async function ContactPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const userDetails = await getUserDetails(id)

  return (
    <UserCard
      firstName={userDetails.firstName}
      lastName={userDetails.lastName}
    />
  )
}

객체 전체를 Client Component에 전달하면 오류가 발생한다.

tsx
export async function ContactPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const userDetails = await getUserDetails(id)

  // 오류가 발생한다
  return <UserCard user={userDetails} />
}

고유 값에 taint 적용 (Tainting a unique value)#

configService.getConfigDetails 호출을 기다리면 서버 설정을 읽을 수 있다고 가정한다. 이 시스템 설정에는 클라이언트에 노출하지 않아야 하는 SERVICE_API_KEY가 들어 있다.

config.SERVICE_API_KEY 값에 taint를 적용할 수 있다.

ts
import { experimental_taintUniqueValue } from 'react'

async function getSystemConfig(): Promise<SystemConfig> {
  const config = await configService.getConfigDetails()

  experimental_taintUniqueValue(
    'Do not pass configuration tokens to the client',
    config,
    config.SERVICE_API_KEY
  )

  return config
}

systemConfig 객체의 다른 속성은 계속 읽을 수 있다.

tsx
export async function Dashboard() {
  const systemConfig = await getSystemConfig()

  return <ClientDashboard version={systemConfig.SERVICE_API_VERSION} />
}

SERVICE_API_KEY를 ClientDashboard에 전달하면 오류가 발생한다.

tsx
export async function Dashboard() {
  const systemConfig = await getSystemConfig()
  // PR에서 누군가 실수한다
  const version = systemConfig.SERVICE_API_KEY

  return <ClientDashboard version={version} />
}

systemConfig.SERVICE_API_KEY를 새 변수에 다시 할당해도 taint는 유지된다. 따라서 이 값을 Client Component에 전달하면 여전히 오류가 발생한다.

반대로 taint된 고유 값에서 파생한 값은 클라이언트에 노출된다.

tsx
export async function Dashboard() {
  const systemConfig = await getSystemConfig()
  // PR에서 누군가 실수한다
  const version = `version::${systemConfig.SERVICE_API_KEY}`

  return <ClientDashboard version={version} />
}

getSystemConfig가 반환하는 데이터에서 SERVICE_API_KEY를 제거하는 편이 더 낫다.

예제 및 데모 설계#

  • 데모 가능 여부: 가능
  • experimental.taint: true를 설정하고 Server Component에서 사용자 객체의 개별 필드만 Client Component로 전달하는 화면을 구성한다.
  • 객체 전체나 taint된 SERVICE_API_KEY를 전달하면 오류가 발생하고, 개별 필드를 전달하면 정상적으로 렌더링되는 모습을 브라우저에서 비교한다.
  • taint된 고유 값으로 새 문자열을 만들면 클라이언트에 값이 노출될 수 있다는 차이도 같은 화면에서 확인한다.

연습 문제#

  1. experimental_taintObjectReference를 적용한 객체에 대한 설명으로 올바른 것은 무엇인가?
  • A. 객체를 복사해도 원본의 taint가 항상 자동으로 복사된다.
  • B. 객체 전체는 Client Component로 전달할 수 없지만 개별 필드는 읽을 수 있다.
  • C. 객체에서 파생한 모든 값도 자동으로 taint된다.
  • D. process.env.MY_VAR처럼 개별 환경 변수 문자열도 항상 전달할 수 없다.
정답 보기

정답: B 해설: 객체 참조에 taint를 적용하면 객체 전체는 경계를 넘어 전달할 수 없지만 필요한 개별 필드는 사용할 수 있다.

  1. 기본 제공되는 process.env taint의 영향을 받지 않는 동작은 무엇인가? 복수 선택한다.
  • A. process.env 객체 전체를 Client Component에 전달한다.
  • B. process.env 객체 참조를 그대로 다른 변수에 할당한다.
  • C. process.env.MY_VAR를 읽어 얻은 문자열을 Client Component에 전달한다.
  • D. { ...process.env }처럼 복사한 값을 Client Component에 전달한다.
정답 보기

정답: C, D 해설: 기본 process.env taint는 객체 참조에만 적용되므로 개별 변수에서 읽은 문자열과 복사한 값에는 영향을 주지 않는다.

  1. taint된 고유 값을 다루는 방법으로 올바른 것은 무엇인가?
  • A. taint된 값을 새 변수에 할당하면 taint가 사라지므로 그대로 전달한다.
  • B. taint된 고유 값에서 파생한 값도 항상 자동으로 taint된다고 가정한다.
  • C. 원래 taint된 값은 새 변수에 할당해도 보호되며, 민감한 값은 반환 데이터에서 제거하는 편이 낫다.
  • D. experimental.taint를 비활성화하고 오류를 무시한다.
정답 보기

정답: C 해설: taint된 값 자체는 재할당해도 보호되지만 파생한 값은 추적되지 않으므로 민감한 값을 반환 데이터에서 제거하는 편이 낫다.

챕터 요약#

  • experimental.taint는 React의 experimental_taintObjectReference와 experimental_taintUniqueValue를 활성화한다.
  • taint된 객체나 값이 Server Component와 Client Component 사이의 경계를 넘으면 React가 오류를 발생시킨다.
  • 객체를 복사하거나 taint된 값에서 값을 파생하면 taint가 자동으로 이어지지 않으므로 별도로 처리해야 한다.
  • 기본 process.env taint는 객체 참조에만 적용되고 개별 환경 변수 문자열이나 복사본에는 적용되지 않는다.
  • taint API만으로 민감한 데이터 노출을 막지 말고, 필요하지 않은 컨텍스트로 민감한 데이터를 반환하지 않도록 데이터와 API를 설계한다.

이 문서의 실습 데모

이 문서가 도움이 되었나요?

남겨 주신 응답은 문서를 개선하는 데 쓰여요.

이 탭에서는 문서마다 한 번 응답할 수 있어요.