[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-10 22:08:15 +09:00
parent 21ac3ed255
commit 504fd5fb42
3011 changed files with 380280 additions and 206977 deletions
+143 -92
View File
@@ -2,118 +2,169 @@
id: wiki-2026-0508-deepreadonly
title: DeepReadonly
category: 10_Wiki/Topics
status: needs_review
status: verified
canonical_id: self
aliases: []
aliases: [deep-readonly, recursive-readonly, immutable-type]
duplicate_of: none
source_trust_level: A
confidence_score: 0.92
tags: [auto-consolidated, technical-documentation]
confidence_score: 0.9
verification_status: applied
tags: [typescript, type-utility, immutability]
raw_sources: []
last_reinforced: 2026-05-08
last_reinforced: 2026-05-10
github_commit: pending
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
tech_stack:
language: unspecified
framework: unspecified
language: typescript
framework: typescript-5.x
---
# [[DeepReadonly|DeepReadonly]]
# DeepReadonly
## 📌 한 줄 통찰 (The Karpathy Summary)
> DeepReadonly는 TypeScript에서 객체의 모든 중첩된 프로퍼티에 재귀적으로 `readonly`를 적용하여 데이터 구조 전체를 완전한 불변(immutable) 상태로 만드는 사용자 정의 유틸리티 타입이다 [1, 2]. 기본 내장된 `Readonly<T>` 유틸리티가 객체의 최상위 속성만 보호하는 얕은(shallow) 불변성만을 제공한다는 한계를 극복하기 위해 고안되었다 [1-3]. 상태 관리나 설정 객체와 같이, 객체 생성 이후 내부의 단 하나의 속성도 수정되지 않아야 함을 엄격하게 보장해야 할 때 주로 사용된다 [1, 4].
## 한 줄
> **"매 `Readonly<T>` 의 surface-only — 매 nested mutation 가 still possible"**. 매 DeepReadonly<T> 가 매 recursively 의 every property 의 readonly 의 mark — 매 redux state, config object, frozen domain model 의 essential. TS 5.x 의 매 `const` type parameter + DeepReadonly 가 매 powerful combo.
---
## 매 핵심
> 재귀적 불변성(DeepReadonly)은 TypeScript에서 최상위 속성뿐만 아니라 중첩된 내부 객체까지 모두 불변(immutable) 상태로 만드는 커스텀 유틸리티 타입 기법입니다 [1-3]. 내장 `Readonly<T>` 타입이나 `readonly` 수식어가 제공하는 얕은(shallow) 수준의 보호 한계를 극복하기 위해 매핑 타입과 조건부 타입을 결합하여 구현합니다 [1, 3-5]. 전체 데이터 구조의 변경을 방지하여 트리 구조나 복잡한 중첩 데이터를 다루는 상태 관리 및 설정 객체 등에서 데이터 무결성을 보장하는 강력한 방어책 역할을 합니다 [1, 3].
### 매 vs Readonly
- `Readonly<T>` — 매 top-level only. 매 `obj.nested.mutate = X` 가 still allowed.
- `DeepReadonly<T>` — 매 every level 의 recursive freeze.
## 📖 구조화된 지식 (Synthesized Content)
- **얕은 불변성의 한계 극복:** TypeScript가 기본으로 제공하는 `Readonly<T>` 타입은 객체의 최상위 수준(top-level) 프로퍼티만 읽기 전용으로 제어한다 [1, 2]. 따라서 내부의 중첩된 객체는 여전히 수정 가능한 상태로 남게 되어 데이터 오염의 위험이 존재하는데, 깊은 수준의 불변성을 강제하기 위해서는 `DeepReadonly` 타입이 필요하다 [1-3].
- **재귀적 구현 방식:** `DeepReadonly`는 매핑 타입(Mapped Types)과 조건부 타입(Conditional Types)을 결합하여 재귀적으로 정의된다 [4]. 일반적으로 `type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> };`와 같은 형태로 작성되어, 중첩된 데이터 트리 구조의 모든 하위 속성에 `readonly` 수식어가 빠짐없이 적용되도록 만든다 [1, 2, 4].
- **적용 및 가용성:** 이 타입은 데이터 무결성을 최우선으로 하는 프론트엔드 아키텍처나 복잡한 시스템에서 예기치 않은 데이터 변경을 차단하는 강력한 방패 역할을 수행한다 [4]. 하지만 `DeepReadonly`는 현재 TypeScript 언어 자체에 내장되어 있지 않다 [5]. 따라서 개발자가 프로젝트 내에서 직접 재귀적 유틸리티 타입을 선언하거나, `ts-essentials`와 같이 이를 제공하는 서드파티 라이브러리를 가져와 사용해야 한다 [5].
### 매 type-only vs runtime
- DeepReadonly 의 매 compile-time type guarantee — 매 runtime 의 mutation 의 X protect.
- 매 runtime freeze 의 `Object.freeze` (shallow) 또는 `deepFreeze` helper 사용.
- TypeScript 5.0+ `as const` 의 매 literal-level deep readonly 의 produce.
---
### 매 사용처
1. Redux/Zustand state shape — 매 mutation prevent.
2. Configuration schemas (env config, feature flags).
3. API response DTOs after parse.
4. Domain entities in DDD value objects.
5. Test fixtures (prevent accidental modification).
- **얕은 불변성의 한계:** TypeScript의 내장 `Readonly<T>` 유틸리티 타입이나 기본적인 `readonly` 수식어는 객체의 최상위(top-level) 속성만 읽기 전용으로 만든다 [1, 2, 5]. 이로 인해 중첩된 객체(nested objects)나 배열의 깊은 내부 속성들은 여전히 변경 가능한(mutable) 상태로 남아 예기치 않은 데이터 오염에 취약해진다 [1, 2, 5].
- **DeepReadonly의 동작 원리:** 이러한 한계를 극복하기 위해 모든 내부 계층을 보호할 수 있는 `DeepReadonly` 재귀적 타입(Recursive Type)을 정의한다 [1, 2]. 이 타입은 매핑 타입(Mapped Types)과 조건부 타입(Conditional Types)을 결합하여 구성하며, 객체의 프로퍼티를 순회하며 중첩된 모든 속성에 재귀적으로 `readonly`를 강제하여 전체 구조를 동결시킨다 [1, 3].
- **구현 및 라이브러리 의존성:** `DeepReadonly`는 현재 TypeScript 언어에 기본적으로 내장된 유틸리티 타입이 아니다 [6]. 따라서 시스템 무결성을 위해 개발자가 직접 재귀적 헬퍼 타입을 정의하거나, `ts-essentials`와 같이 이를 제공하는 외부 라이브러리에 의존해야 한다 [6]. 반대로 불변성을 해제해야 하는 경우에는 `Mutable` 헬퍼 타입을 만들어 모든 속성의 `readonly`를 제거할 수도 있다 [2, 7].
- **핵심 활용 사례:** 트리 구조나 복잡한 중첩 데이터를 다룰 때 단 하나의 속성도 변경되지 않도록 보장한다 [1, 3]. 생성 후 구조가 수정되어서는 안 되는 설정 객체(Configuration objects), 상태 관리 아키텍처, 그리고 데이터 무결성이 치명적으로 중요한 금융 시스템 등에서 필수적인 보호 요소로 자리 잡고 있다 [1, 3, 8].
## 💻 패턴
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
- **과거 데이터와의 충돌:** 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
- **정책 변화:** Programming & Language 분야의 자동 자산화 수행.
### Basic DeepReadonly
```ts
type DeepReadonly<T> = T extends (...args: any[]) => any
? T
: T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T;
---
- **과거 데이터와의 충돌:** 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
- **정책 변화:** Programming & Language 분야의 자동 자산화 수행.
## 🔗 지식 연결 (Graph)
- **Related Topics:** [[readonly|readonly]], Readonly<T>, Mapped Types, Conditional Types
- **Projects/Contexts:** [[상태 관리(State Management)|상태 관리(State Management]], 설정 객체(Configuration Objects), ts-essentials
- **Contradictions/Notes:** 깊은 수준의 불변성을 보장하는 기능이 실무적으로 널리 요구되었음에도 불구하고 `DeepReadonly`는 TypeScript에 공식적으로 기본 내장되어 있지 않다는 점이 특징적이다 [5]. 이로 인해 추가적인 구현이나 외부 라이브러리 의존성이 요구된다 [5].
---
*Last updated: 2026-04-18*
---
---
- **Related Topics:** [[Readonly 유틸리티 타입|Readonly]], 매핑 타입 (Mapped Types), 조건부 타입 (Conditional Types), 유틸리티 타입 (Utility Types)
- **Projects/Contexts:** ts-essentials, 프론트엔드 상태 관리 ([[State|State]] [[Management|Management]]), 설정 객체 (Configuration objects)
- **Contradictions/Notes:** `Readonly<T>`는 기본 제공되나 `DeepReadonly`는 TypeScript에 내장되어 있지 않다는 점이 특징입니다 [6]. 또한 런타임에 성능 오버헤드를 일으키는 `Object.freeze()`의 얕은 동결과 달리, 이 방식은 컴파일 타임에 타입 레벨에서만 불변성을 검사하고 강제하므로 훨씬 효율적입니다 [5].
---
*Last updated: 2026-04-18*
---
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
**언제 이 지식을 쓰는가:**
- *(TODO)*
**언제 쓰면 안 되는가:**
- *(TODO)*
## 🧪 검증 상태 (Validation)
- **정보 상태:** needs_review
- **출처 신뢰도:** A
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
## 🧬 중복 검사 (Duplicate Check)
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
- **처리 방식:** UPDATE (자동 정규화)
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
## 🕓 변경 이력 (Changelog)
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|------|-----------|-----------|--------|
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
## 💻 코드 패턴 (Code Patterns)
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
```text
# TODO
interface User {
id: string;
profile: { name: string; tags: string[] };
}
const u: DeepReadonly<User> = { id: '1', profile: { name: 'a', tags: ['x'] } };
// u.profile.name = 'b'; // ❌ Cannot assign
// u.profile.tags.push('y'); // ❌ readonly array
```
## 🤔 의사결정 기준 (Decision Criteria)
### Handles Map / Set / Array
```ts
type DeepReadonly<T> =
T extends (infer U)[] ? readonly DeepReadonly<U>[]
: T extends Map<infer K, infer V> ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
: T extends Set<infer U> ? ReadonlySet<DeepReadonly<U>>
: T extends Promise<infer U> ? Promise<DeepReadonly<U>>
: T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T;
```
**선택 A를 써야 할 때:**
- *(TODO)*
### Brand-aware (preserve nominal types)
```ts
type Brand<T, B> = T & { readonly __brand: B };
type DeepReadonly<T> = T extends Brand<infer U, infer B>
? Brand<DeepReadonly<U>, B>
: T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T;
```
**선택 B를 써야 할 때:**
- *(TODO)*
### Runtime deepFreeze pair
```ts
export function deepFreeze<T>(obj: T): DeepReadonly<T> {
if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {
Object.freeze(obj);
for (const key of Object.keys(obj)) deepFreeze((obj as any)[key]);
}
return obj as DeepReadonly<T>;
}
```
**기본값:**
> *(TODO)*
### `as const` + DeepReadonly
```ts
const config = {
features: { newCheckout: true, beta: ['user1', 'user2'] },
limits: { rpm: 100 },
} as const;
// 매 `as const` 가 매 every literal 의 deeply readonly + literal-typed 로 만듦.
type Config = typeof config;
// Config['features']['beta'] === readonly ['user1', 'user2']
```
## ❌ 안티패턴 (Anti-Patterns)
### Mutable inverse (DeepWritable)
```ts
type DeepWritable<T> = T extends (...args: any[]) => any
? T
: T extends object
? { -readonly [K in keyof T]: DeepWritable<T[K]> }
: T;
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
function clone<T>(x: DeepReadonly<T>): DeepWritable<T> {
return structuredClone(x as T) as DeepWritable<T>;
}
```
### Zod + DeepReadonly
```ts
import { z } from 'zod';
const UserSchema = z.object({ id: z.string(), tags: z.array(z.string()) }).readonly();
type User = DeepReadonly<z.infer<typeof UserSchema>>;
```
### Function args (Redux reducer)
```ts
function reducer<S, A>(state: DeepReadonly<S>, action: A): S {
// state.x = ... // ❌ compile error
return { ...(state as S), updated: true } as S;
}
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| Literal config | `as const` |
| Generic state shape | `DeepReadonly<T>` utility |
| Runtime guarantee 필요 | `deepFreeze` + DeepReadonly |
| Performance hot path | 매 type-only DeepReadonly (no runtime cost) |
| Library author 가 expose | DeepReadonly + readonly arrays in public API |
**기본값**: type-only DeepReadonly + `as const` for literals; runtime `deepFreeze` only for security-sensitive boundaries.
## 🔗 Graph
- 부모: [[TypeScript-Type-System]] · [[Immutability]]
- 변형: [[Readonly]] · [[as-const]] · [[DeepWritable]]
- 응용: [[Redux-State]] · [[Zustand]] · [[Domain-Driven-Design]]
- Adjacent: [[Branded-Types]] · [[Zod]] · [[Structural-Sharing]]
## 🤖 LLM 활용
**언제**: 매 utility type 의 design / extension, 매 type error explanation, 매 readonly violation 의 codemod 작성.
**언제 X**: 매 runtime data validation (Zod 사용). 매 hot-path performance tuning (TS types 가 erased — runtime cost 0).
## ❌ 안티패턴
- **함수 type 의 readonly 적용**: 매 `(...args) => any` 가 readonly 의 의미 X — special-case 필요.
- **Date / RegExp 의 recurse**: 매 built-in instances 가 깨짐 — exclude 의 type guard.
- **DeepReadonly + cast away**: `state as Mutable` 가 매 type safety 의 destroy.
- **Runtime mutation through cast**: 매 `(state as any).x = 1` — 매 type lie 의 propagate.
- **Naive `keyof T` on union**: distributive conditional 의 사용.
## 🧪 검증 / 중복
- Verified (TypeScript 5.x docs, type-fest library, ts-toolbelt).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — DeepReadonly utility type variants and patterns |