7.8 KiB
category, tags, title, last_updated
| category | tags | title | last_updated | ||||
|---|---|---|---|---|---|---|---|
| Unified |
|
|
2026-05-02 |
Readonly 유틸리티 타입
📌 Brief Summary
Readonly 유틸리티 타입(
Readonly<T>)은 TypeScript에서 특정 객체 타입의 모든 속성에readonly수식어를 추가하여 초기화 이후 값을 재할당할 수 없도록 변환하는 기능입니다[1, 2]. 이는 런타임 성능 저하 없이 컴파일 타임에만 엄격하게 불변성을 강제하여, 의도치 않은 데이터 변형으로 인한 버그를 사전에 차단합니다[3, 4]. 단, 최상위 속성에만 적용되는 얕은(shallow) 불변성만을 제공하므로, 중첩된 객체를 완전히 동결하려면 재귀적인 형태의 딥 리드온리(Deep Readonly) 패턴이 별도로 필요합니다[5, 6].
readonly는 TypeScript에서 객체의 속성이나 배열, 튜플이 초기화된 이후에 수정되지 않도록 방지하는 수식어이자 유틸리티 타입입니다 [1-3]. 변수의 재할당을 막는const와 달리 객체 내부 구조의 불변성을 제어하며, 런타임 오버헤드 없이 컴파일 타임에 오류를 잡아내어 코드의 안정성과 예측 가능성을 높입니다 [2, 4, 5].
📖 Core Content
-
기본 작동 원리와 문법:
Readonly<T>는 주어진 타입T의 모든 프로퍼티를 읽기 전용으로 매핑합니다[1, 2]. 설정이나 API 응답, 상태 관리 리듀서 등 앱 내내 값이 유지되어야 하는 객체를 보호할 때 주로 사용됩니다[7, 8]. TypeScript 코드가 컴파일된 후에는 관련 어노테이션이 모두 사라지므로 실행 시점(Runtime)에는 어떠한 오버헤드도 발생시키지 않습니다[3, 9]. -
유사 개념과의 차이점:
const와의 차이:const는 변수 자체의 재할당을 막지만 참조된 객체의 내부 속성 변경은 막지 못합니다. 반면readonly는 변수 바인딩이 아닌 객체 속성이나 배열 요소 자체의 변경을 제한합니다[10, 11].Object.freeze()와의 차이:Object.freeze()는 런타임에 객체를 동결하며 실행 성능에 비용을 청구하지만,readonly는 컴파일 타임에 타입 시스템을 통해서만 수정을 금지합니다[4, 12].
-
배열에서의 활용: 객체뿐만 아니라 배열에도
ReadonlyArray<T>또는readonly T[]형태로 사용할 수 있습니다[13, 14]. 이렇게 선언된 배열은 요소의 재할당이 불가능할 뿐만 아니라,push(),pop()등 원본을 수정하는 메서드가 타입 정의에서 완전히 제거됩니다[15, 16]. -
한계점 및 우회 취약점 (Gotcha):
- 얕은 불변성(Shallow Immutability):
Readonly<T>는 1단계 깊이의 속성에만 작용합니다. 객체 내부의 중첩된 객체 속성은 여전히 수정이 가능하며, 이를 해결하기 위해서는 매핑된 타입과 조건부 타입을 결합한 커스텀[[DeepReadonly|DeepReadonly]]<T>유틸리티를 구현해야 합니다[5, 6, 17]. - 에일리어싱(Aliasing) 문제:
readonly타입의 데이터를, 수정 가능한 타입(mutable)을 매개변수로 받는 함수에 전달할 경우 타입 호환성 규칙에 의해 통과될 수 있습니다. 이로 인해 함수 내부에서 원본 데이터가 변경되는 우회 돌연변이가 발생할 수 있습니다[18, 19].
- 얕은 불변성(Shallow Immutability):
-
작동 방식 및 성능:
readonly는 전적으로 컴파일 타임에만 동작하는 기능으로, 컴파일 후 생성된 JavaScript 코드에서는 흔적이 사라집니다 [2, 6]. 따라서 성능에 어떠한 런타임 오버헤드도 주지 않으면서 속성의 불변성을 강제할 수 있습니다 [2, 5, 6]. -
객체 및 클래스에서의 활용: 인터페이스나 타입 별칭, 클래스의 속성에
readonly를 지정할 수 있습니다. 클래스에서는 선언 시점이나 생성자(constructor) 내부에서만 초기화가 가능하며, 그 이후에 값을 수정하려고 시도하면 컴파일러가 에러를 발생시킵니다 [7, 8]. 이는 시스템의 고유 식별자(ID)나 환경 설정 값처럼 생명 주기 동안 변하지 않아야 하는 데이터를 보호할 때 유용합니다 [7, 9]. -
배열과 튜플의 보호: 배열을
readonly T[]또는ReadonlyArray<T>로 선언하면push,pop, 요소 직접 재할당 등 배열을 변이시키는 모든 메서드와 작업이 타입 시스템에서 제거되어 안전하게 보호됩니다 [10-12]. 튜플 역시 각 위치의 값을 고정하기 위해readonly를 적용할 수 있습니다 [10]. -
const 및 Object.freeze()와의 비교:
const: 런타임에 변수 자체의 재할당을 방지하지만, 참조된 객체 내부 속성의 변경은 막지 못합니다 [4, 13, 14].Object.freeze(): 런타임에 객체의 얕은 수정을 차단하며 성능 비용이 발생합니다 [5, 14, 15].readonly: 컴파일 타임에 객체/배열 내부 속성의 변경을 타입 레벨에서 차단하여, 실행 전 코드 작성 단계에서 안전을 보장합니다 [4, 5, 14].
-
얕은 불변성(Shallow Immutability)의 한계: 기본적인
readonly수식어나Readonly<T>유틸리티 타입은 최상위 레벨의 속성에만 얕게 적용됩니다 [16-18]. 즉, 중첩된 내부 객체는 여전히 수정될 수 있습니다 [17, 18]. 완벽한 깊은 불변성을 원한다면 재귀적으로 속성을 잠그는 커스텀[[DeepReadonly|DeepReadonly]]유틸리티 타입을 별도로 구현해야 합니다 [17-19].
⚖️ Trade-offs & Caveats
- 과거 데이터와의 충돌: 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
- 정책 변화: Programming & Language 분야의 자동 자산화 수행.
- 과거 데이터와의 충돌: 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
- 정책 변화: Programming & Language 분야의 자동 자산화 수행.
🔗 Knowledge Connections
- Related Topics: 불변성 (Immutability), 매핑된 타입(Mapped Types), DeepReadonly
- Projects/Contexts: 변경 불가한 외부 API 응답 데이터 모델링, 상태 관리 시스템(Redux 리듀서 등)의 데이터 무결성 보장, 그리고 애플리케이션의 전역 환경 설정(Configuration) 객체 보호 맥락에서 광범위하게 쓰입니다[8, 17].
- Contradictions/Notes: TypeScript의 에일리어싱 한계로 인해
readonly데이터가mutable타입을 요구하는 함수로 전달되어 내부에서 값이 변경될 위험이 존재하므로, 완전한 불변성을 지키려면 함수 시그니처 전반에 걸쳐 읽기 전용 파라미터를 강제하거나 데이터의 복사본을 넘기는 설계가 필요합니다[18, 19]. 또한, 모든readonly속성을 다시 수정 가능하게 되돌려야 할 때는Mutable이라는 커스텀 헬퍼 타입을 만들어 매핑 수식어를 제거(-readonly)하는 방식으로 해결할 수 있습니다[6].
Last updated: 2026-04-18
- Related Topics: const, Object.freeze(), ReadonlyArray, DeepReadonly, Utility Types
- Projects/Contexts: State Management, Configuration Objects, API Responses
- Contradictions/Notes:
readonly는 데이터 변경을 막는 훌륭한 타입 제어 장치지만, 컴파일러는readonly데이터를 변경 가능(mutable)한 매개변수를 받는 함수에 전달하는 것(Aliasing)을 허용하므로 이로 인한 우회적 변이(mutation)가 발생할 수 있다는 구조적 맹점을 주의해야 합니다 [20, 21].
Last updated: 2026-04-18