실무에서 발생하는 많은 버그는 의도하지 않은 값 변경에서 시작됩니다. TypeScript는 이러한 문제를 컴파일 단계에서 막을 수 있도록 Readonly 유틸리티 타입과 const assertion(as const)이라는 강력한 기능을 제공합니다.
이 두 가지를 제대로 이해하면, 설정 값·상태 값·상수 데이터·도메인 모델을 안전하게 고정된 형태로 설계할 수 있습니다.
Readonly란?
Readonly<T>는 객체 타입의 모든 프로퍼티를 읽기 전용(readonly)으로 변환하는 유틸리티 타입입니다.
type User = {
id: number;
name: string;
};
type ReadonlyUser = Readonly<User>;
// {
// readonly id: number;
// readonly name: string;
// }
Readonly로 변환된 타입은 할당 이후 수정이 불가능합니다.
const user: ReadonlyUser = { id: 1, name: "Alice" };
user.name = "Bob"; // 컴파일 에러
주요 사용 사례:
- 변경되면 안 되는 도메인 데이터
- 초기화 이후 고정되는 설정 정보
- 외부에 노출되는 불변 객체
ReadonlyArray — 배열 불변성
배열에 대해서는 ReadonlyArray<T>를 사용합니다.
const roles: ReadonlyArray<string> = ["admin", "user"];
roles.push("guest"); // X
roles[0] = "guest"; // X
단, map, filter, concat처럼 새 배열을 반환하는 메서드는 정상적으로 사용할 수 있습니다.
const assertion(as const)이란?
as const는 값 전체를 가장 좁은 리터럴 타입 + readonly로 고정하는 문법입니다.
const status = "success" as const;
// 타입: "success"
일반적인 경우라면 status: string으로 추론되지만, const assertion을 사용하면 리터럴 타입으로 고정됩니다.
객체에 const assertion 적용
const config = {
retry: 3,
mode: "debug",
} as const;
위 객체의 타입은 다음과 같습니다.
{
readonly retry: 3;
readonly mode: "debug";
}
특징 정리:
- 모든 프로퍼티가 readonly
- number/string이 아닌 리터럴 타입으로 고정
- 값과 타입이 완전히 일치
const assertion과 유니온 타입 생성
실무에서 가장 많이 사용되는 패턴 중 하나입니다.
const STATES = ["idle", "loading", "success", "error"] as const;
type State = typeof STATES[number];
// "idle" | "loading" | "success" | "error"
enum 없이도 타입 안전한 상태 모델을 만들 수 있으며, Discriminated Union과 함께 사용하면 매우 강력합니다.
Readonly vs const assertion 차이
| 구분 | Readonly<T> | as const |
|---|---|---|
| 적용 대상 | 타입 | 값 |
| 불변성 범위 | 프로퍼티 readonly | 값 + 타입 전체 고정 |
| 리터럴 타입화 | X | O |
| 주 사용 목적 | API/도메인 타입 보호 | 상수·상태·옵션 정의 |
요약하면 다음과 같습니다.
- 값 자체가 상수다 → as const
- 타입 구조만 불변으로 만들고 싶다 → Readonly
실무에서 자주 쓰는 패턴
설정 객체 보호
const APP_CONFIG = {
apiTimeout: 3000,
env: "production",
} as const;
옵션 목록 + 타입 자동 생성
const ROLES = ["ADMIN", "USER"] as const;
type Role = typeof ROLES[number];
함수 인자 보호
function process(user: Readonly<User>) {
// user는 읽기 전용
}
주의사항
- as const는 변경 가능해야 하는 객체에 사용하면 오히려 불편
- React state, mutable 도메인에는 신중히 적용
- 불변성이 필요한 경계(boundary)에만 사용하는 것이 이상적
불변성은 강력하지만, 모든 곳에 적용하는 것은 오히려 독이 될 수 있습니다.
Readonly와 const assertion은 TypeScript에서 의도를 타입으로 표현하는 가장 중요한 수단입니다. 이 두 가지를 잘 활용하면 코드의 신뢰도와 유지보수성이 크게 향상됩니다.
- Readonly → 타입 레벨 불변성
- as const → 값 + 리터럴 타입 + 불변성
- 상태·설정·상수 모델링의 핵심 도구
'개발 > Typescript' 카테고리의 다른 글
| [TYPESCRIPT] this 타입과 함수 오버로드 — 호출 맥락을 타입으로 표현하는 고급 패턴 (0) | 2025.12.21 |
|---|---|
| [TYPESCRIPT] Index Signature와 동적 객체 타입 - 키가 정해지지 않은 객체를 안전하게 다루는 방법 (0) | 2025.12.20 |
| [TYPESCRIPT] Discriminated Union 패턴으로 안전한 분기 처리 - 타입으로 보장하는 조건 분기 (0) | 2025.12.18 |
| [TYPESCRIPT] 조건부 타입(Conditional Type) — 타입에 따라 분기하는 고급 타입 설계 (0) | 2025.12.15 |
| [TYPESCRIPT] infer 키워드로 타입 추론하기 — 조건부 타입의 핵심 메커니즘 (0) | 2025.12.14 |
