JavaScript에서 Spread 연산자 (...)는 객체와 배열을 복사하고 확장하는 데 편리함을 제공하기 위해 도입되었습니다. ES2018 이후로는 객체에도 사용되고 있습니다. TypeScript에서는 Spread가 단순히 문법적 개선이 아닌 객체 조작 시 정교한 타입화를 위한 도구가 되었습니다.
역사적으로 JavaScript에서 객체를 복제하거나 확장하는 방법은 Object.assign을 통해 이루어졌으나, 이러한 접근법은 타입 안전성을 잃고 복잡한 객체 구조일 경우 키 충돌이 발생하기 쉽게 했습니다.
문제는 Spread를 사용하여 객체를 병합/확장할 때 TypeScript가 입력 구조를 기반으로 새 타입을 유추하고 키 충돌을 처리하는 과정에서 (‘마지막 것이 이긴다’) 개발자가 의도했던 것과 다를 수 있다는 것입니다. optional 필드, readonly 및 클래스의 프라이빗 속성과의 교차점에 주의할 필요가 있습니다.
해결책: Spread를 사용하는 것을 허용하여 TypeScript 컴파일러가 결과를 자동으로 유추하도록 합니다. 복잡한 케이스에서는 입력 구조의 타입을 명시적으로 제한하고 타입 구조의 변경 사항을 면밀히 살펴보는 것이 중요합니다.
코드 예시:
interface User { name: string; age: number; } interface Extra { isAdmin?: boolean; readonly city: string; } const base: User = { name: 'Ivan', age: 28 }; const extended: User & Extra = { ...base, city: 'Moscow', isAdmin: true };
주요 특징:
Spread 상속을 통해 TypeScript에서 객체의 "deep copy"를 얻을 수 있습니까?
답변: 아닙니다. Spread는 단지 얕은 복사(shallow copy)만 수행하며, 중첩된 객체는 참조로 남습니다.
const original = { user: { name: 'Anna' } }; const cloned = { ...original }; cloned.user.name = 'Maria'; // original.user.name도 변경됩니다
Spread를 사용할 때 객체의 교차하는 키가 있을 경우 타입이 축소되거나 확장되며, 이것이 변수 주석에 어떻게 영향을 미칩니까?
답변: Spread에서는 변수의 타입이 확장되며, 만약 키가 겹치는 경우 오른쪽 속성이 이깁니다; 명시적인 타입 주석이 있는 경우 타입 불일치로 인한 오류가 발생할 수 있습니다.
const a = { id: 4, value: "abc" }; const b = { value: 123 }; const c: { id: number; value: number } = { ...a, ...b }; // ok
Spread는 클래스에 적용될 수 있으며, 프라이빗 속성은 어떻게 됩니까?
답변: Spread는 클래스의 퍼블릭 속성에만 적용 가능하며, 프라이빗 (private/#) 및 보호된 protected 필드는 결과 객체에 포함되지 않습니다.
class Person { private id = 77; name = "Bob"; } const p = new Person(); const spreaded = { ...p }; // spreaded: { name: string }
개발자가 사용자 옵션을 확장하기 위해 settings 객체 값을 Spread합니다:
const common = { theme: 'light', notifications: true }; const user = { notifications: false, signature: 'Sasha' }; const merged = { ...common, ...user };
merged가 타입적으로 일관되기를 기대했으나, 실수로 signature 필드의 타입에 대한 오류를 간과하여 Spread가 단순히 키를 복사할 뿐 그 값을 검증하지 않음을 잊었습니다.
장점:
단점:
구성을 병합하기 위해 Spread를 사용하기 전에 검증 후 반환 타입 주석과 함께 사용합니다:
interface Settings { theme: "light" | "dark"; notifications: boolean; signature?: string; } function getSettings(common: Settings, specific: Partial<Settings>): Settings { return { ...common, ...specific }; }
장점:
단점: