프론트엔드에서 “상태 관리”라고 부르는 것과, 데이터·에이전트 쪽에서 “목적성과 품질성을 통과한 상태”라고 부르는 것은 같은 단어를 쓴다. 우연이 아니다. 둘 다 값이 아니라 술어를 다루고, 그 술어가 인자를 하나 더 받는다는 것도 같다.

문제는 여기서부터다. 닮았다는 이유로 React 의 상태 관리 직관을 그대로 옮기면 가장 유명한 규칙 하나가 정확히 반대로 작동한다. 이 글은 두 상태를 축별로 놓고 상관의 부호를 가른다. 같은 해법이 이식되는 축이 넷, 이식하면 사고가 나는 축이 넷이다.

앞선 글(AI ready data 를 상태 관리로 보기)에서 \(\mathrm{ready}(D, P, t)\) 라는 표기를 썼다. 이 글은 그 표기를 React 쪽에 겹쳐 본다.

1. 두 상태는 같은 문법을 쓴다

React 에서 “이 값을 state 로 둘 것인가”의 판정 기준은 값의 성질이 아니다. 렌더가 그것을 읽느냐다. React 공식 문서는 useRef 를 이렇게 소개한다.

useRef렌더링에 필요하지 않은 값을 참조하게 해주는 React Hook 이다. (…) ref 를 바꿔도 리렌더가 일어나지 않는다.react.dev, useRef

즉 같은 값이라도 렌더 트리가 읽으면 state 고, 안 읽으면 ref 다. 값 자체에는 답이 없다.

\[\mathrm{isState}(v) \;\equiv\; \text{렌더가 } v \text{ 를 읽는다}\]

데이터 쪽도 같은 모양이다. 어떤 데이터셋이 “준비되었다”는 판정은 데이터셋의 내재 속성이 아니라 어떤 목적에 대해서인가에 달렸다. EU AI Act 제10조 제2항은 고위험 AI 시스템의 학습·검증·시험 데이터에 대해, 데이터 거버넌스 관행이 “의도된 목적에 적합해야 한다 (appropriate for the intended purpose)”고 규정한다 (Regulation (EU) 2024/1689, Art. 10(2)). 법문에서도 품질은 목적에 상대적인 술어다.

\[\mathrm{ready}(D, P, t)\]

두 술어 모두 인자를 하나 더 받는다. 그리고 실무에서 사고가 나는 방식도 같다 — 그 인자를 빼먹는다. isState(v) 를 값만 보고 정하고, ready(D) 를 목적 없이 인증한다. 상관은 여기서 시작한다.

2. 양의 상관 — 같은 해법이 그대로 이식되는 축

2.1 인자 누락 — 소비자를 안 보고 값을 본다

  • React: 렌더가 읽지 않는 값을 useState 에 넣으면 불필요한 리렌더가 난다. 정답은 ref.
  • Agent: 목적 없이 ready(D) 로 전역 인증하면 그 인증서는 아무것도 보증하지 않는다.

둘 다 “이 값을 상태로 만들 것인가”를 소비자가 아니라 을 보고 정한 결과다. 판정 기준을 소비자 쪽으로 옮기는 것이 양쪽 모두의 해법이다.

2.2 파생 상태 — 저장하지 말고 계산하라

React 문서는 상태 구조의 다섯 원칙 중 셋째로 이렇게 못박는다.

중복 상태를 피하라. 컴포넌트의 props 나 기존 state 변수로부터 렌더링 중에 계산할 수 있는 정보라면, 그 정보를 그 컴포넌트의 state 에 넣지 말아야 한다. — react.dev, Choosing the State Structure

같은 문서는 이 원칙들의 목적을 “실수 없이 상태를 갱신하기 쉽게 만드는 것“이라 밝히고, 데이터베이스 정규화에 빗댄다.

품질 판정도 정확히 같다. 검증 결과를 스냅샷으로 저장해 두면, 원본이 바뀌어도 인증서는 초록불로 남는다. 두 번째 진실원이 생기고 조용히 어긋난다. 저장 대신 평가 시점에 계산하는 것이 해법이라는 점도, 계산이 비싸서 잘 안 지켜진다는 점도 같다.

2.3 신선도 — t 인자를 명시적으로 다루는 법

이 축은 React 진영이 앞서 있다. 서버에서 온 데이터를 “소유한 상태”가 아니라 캐시로 분리하고, 신선도를 옵션으로 노출했기 때문이다. TanStack Query 는 기본 동작을 이렇게 문서화한다.

useQuery 를 통한 쿼리 인스턴스는 기본적으로 캐시된 데이터를 stale 로 간주한다. (…) staleTime 이 설정된 쿼리는 그 시간이 지나기 전까지 fresh 로 간주된다. — TanStack Query, Important Defaults (벤더 1차 문서)

기본값이 “신선함”이 아니라 “낡음“이라는 선택이 핵심이다. 신선하다고 주장하려면 기간을 명시해야 한다. 같은 문서는 비활성 쿼리의 기본 GC 시간을 5분(gcTime)으로, 그리고 staleTime: Infinity'static' 을 구분한다 — 전자는 수동 무효화가 통하고 후자는 통하지 않는다.

데이터 준비 완료 인증서에는 대개 이 대응물이 없다. “2026-08-31 검증 통과” 라고만 적히고 언제까지 유효한지, 무엇이 바뀌면 무효인지가 없다. staleTimeinvalidateQueries 에 해당하는 두 장치를 인증서에 붙이는 것이 이 축에서 가져올 것이다.

2.4 스냅샷 일관성 — tearing 은 에이전트에도 있다

React 18 의 동시성 렌더링에서, 외부 스토어를 구독하는 화면이 부분마다 서로 다른 버전을 읽을 수 있다. useSyncExternalStore 는 이걸 막으려고 존재한다. 문서의 주의사항이 그 장치를 직접 설명한다.

트랜지션 갱신마다 React 는 DOM 에 변경을 적용하기 직전에 getSnapshot한 번 더 호출한다. 원래 호출 때와 다른 값을 반환하면, React 는 갱신을 처음부터 다시 시작하되 이번엔 블로킹 갱신으로 적용한다 — 화면의 모든 컴포넌트가 스토어의 같은 버전을 반영하도록 보장하기 위해서다.react.dev, useSyncExternalStore

멀티스텝 에이전트에 그대로 대응한다. 3단계에서 원문을 읽고 7단계에서 그 원문으로 만든 파생 인덱스를 읽는데 그사이 원본이 바뀌었다면, 에이전트는 서로 다른 버전을 섞어 추론한 것이다. 결론은 그럴듯하고, 각 단계의 로그도 정상이다.

차이는 React 에는 이 문제에 이름과 훅이 있고, 에이전트 쪽에는 둘 다 없다는 점이다.

3. 음의 상관 — 이식하면 사고가 나는 축

여기서부터는 표준 문서가 규정한 내용이 아니라 필자의 종합이다.

3.1 전이의 성질 — 가역 대 단조

setState 는 싸고, 가역적이고, 어디로든 간다. 상태 그래프에 방향 제약이 없다. 준비 완료 판정은 비싸고, 증거 기반이고, 사실상 단조롭다. 인증을 “되돌린다”는 개념이 없다 — 무효화하고 재검증할 뿐이다. React 의 상태 전이 도식을 그대로 옮기면 “롤백” 이라는 존재하지 않는 연산을 설계에 넣게 된다.

3.2 권위의 위치 — 스토어는 틀릴 수 없고, 인증서는 틀릴 수 있다

React 는 자기 상태에 대해 틀릴 수 없다. 스토어가 정의상 진실이고, 화면은 그 함수다. 불일치는 스토어와 화면 사이에서만 생기며, 그건 React 가 책임지는 영역이다.

ready 는 다르다. 바깥 세계에 대한 주장이다. 목적 \(P\) 를 정하는 것은 규제·사업·사람이지 데이터를 들고 있는 시스템이 아니다. 그래서 인증서는 시스템이 완벽하게 동작해도 세계에 대해 틀릴 수 있다. 이 비대칭이 두 상태 사이의 가장 큰 간극이다.

3.3 실패 신호 — 시끄러움 대 조용함

React 상태 관리가 다룰 만한 이유의 절반은 틀리면 즉시 터진다는 점이다. 잘못된 렌더, 무한 루프, 콘솔 경고가 바로 나온다. React 는 이걸 더 시끄럽게 만들려고 일부러 장치를 넣기도 한다 — Strict Mode 는 “우발적 불순성을 찾도록 돕기 위해 컴포넌트 함수를 두 번 호출한다” (react.dev, useRef).

준비 완료 판정의 실패는 반대다. 조용하고 지연된다. 학습은 잘 돌아가고, 응답도 그럴듯하고, 그저 틀렸을 뿐이다. React 의 방법론을 옮길 때 함께 옮겨지지 않는 것이 바로 이 즉각적 피드백이고, 그게 없으면 나머지 규율도 무너진다.

3.4 ★ 가장 유명한 규칙이 뒤집힌다

React 에서 배우는 첫 원칙 중 하나가 single source of truth 다. 진실은 한 곳에 있어야 하고, 중복은 어긋남의 원천이다(§2.2 의 문서가 바로 그 얘기다).

준비 완료 상태에서는 이게 정확히 반대다. 같은 데이터셋이 같은 시점에

\[\mathrm{ready}(D, P_{\text{사기탐지}}, t) = \text{참} \qquad\land\qquad \mathrm{ready}(D, P_{\text{신용평가}}, t) = \text{거짓}\]

일 수 있고, 이건 모순이 아니라 정상이다. 목적이 다르면 요구 사항이 다르기 때문이다.

그래서 전역 is_ready 불리언 하나를 두는 것 — React 직관대로라면 모범인 그 설계 — 이 여기서는 바로 그 버그다. §2.2 에서 “중복을 없애라”가 옳았던 것과, 여기서 “판정을 목적별로 쪼개라”가 옳은 것은 충돌하지 않는다. 중복(같은 사실의 두 사본)과 다가성(다른 질문의 다른 답)은 다르다. 이 둘을 구분하지 못한 채 정규화 직관만 옮기면 목적 인자를 접어버리게 된다.

4. 축별 상관의 부호

React 준비 완료 상태 상관
술어의 인자 렌더가 읽는가 목적 \(P\) 에 맞는가 + 같은 누락, 같은 해법
파생 상태 저장 말고 렌더 중 계산 저장 말고 평가 시점 계산 +
신선도 staleTime·무효화 대개 부재 + (React 쪽이 앞섬)
스냅샷 일관성 useSyncExternalStore 대응물 없음 + (문제는 같고 장치가 없음)
전이 가역·임의 그래프 단조·재검증
권위 스토어가 정의상 진실 세계에 대한 주장이라 틀릴 수 있음
실패 신호 즉시·시끄러움 지연·조용함
진실원 단일 진실원 목적별 다가 판정 정반대

앞의 넷은 React 의 해법을 문장만 바꿔 쓰면 그대로 작동한다. 뒤의 넷은 옮기면 설계가 망가진다.

5. 이식 가이드

가져올 것 (양의 상관 축)

  • 판정 기준을 값이 아니라 소비자에 둔다 — “누가 이걸 읽는가 / 어떤 목적에 쓰는가”
  • 파생 판정을 저장하지 않는다. 저장해야 한다면 원본 버전을 함께 박아 둔다
  • 인증서에 staleTime 을 붙인다 — 유효 기간과 무효화 트리거를 함께 적는다
  • 멀티스텝 파이프라인이 읽는 원본에 스냅샷 버전을 고정한다

버릴 것 (음의 상관 축)

  • 전역 is_ready 불리언. 목적별 판정 테이블로 바꾼다
  • “롤백” 이라는 전이. 무효화 + 재검증이 맞는 이름이다
  • 시스템이 초록불이면 사실도 맞다는 가정

아직 없어서 만들어야 하는 것

에이전트 쪽에 대응물이 없는 세 가지가 그대로 할 일 목록이다.

  1. useSyncExternalStore 에 해당하는 스냅샷 일관성 장치
  2. Strict Mode 에 해당하는 일부러 시끄럽게 만드는 장치
  3. staleTime 에 해당하는 명시적 신선도 계약

React 가 10여 년에 걸쳐 덧붙여 온 것이 결국 저 셋이다. 조용히 틀리는 상태를 시끄럽게 만드는 장치들. 데이터·에이전트 쪽의 준비 완료 상태에는 아직 그게 없다.

References

1차·공식

벤더 1차 문서 (재현 가능 여부는 각자 확인 필요)

관련 글

근거의 한계

  • 3절 전체는 필자의 종합이다. 전이의 성질·권위의 위치·실패 신호·진실원에 대한 대비는 표준이나 문헌이 규정한 분류가 아니라 두 영역을 겹쳐 본 결과다. 4절의 상관 부호 표도 같다.
  • AI Act 제10조 제2항의 “appropriate for the intended purpose” 는 조문 재현 사이트에서 확인한 문구다. 집필 시점에 EUR-Lex 원문 HTML 을 직접 내려받으려 했으나 응답을 받지 못해, 공식 원문과의 대조는 하지 못했다. 링크는 공식 ELI 주소를 걸어 둔다.
  • React 와 준비 완료 상태를 대응시킨 것은 유비이지 동형사상이 아니다. 4절에서 부호가 나뉜다는 것 자체가, 이 대응이 어느 지점에서 논증의 근거로 쓰일 수 없는지를 보여준다.
  • 에이전트 쪽에 “대응물이 없다”는 진술은 필자가 아는 범위의 부재이지, 부재의 증명이 아니다.

</content>