HooneyLog
© 2026 Seunghoon Shin. All rights reserved.
모든 게시글
typescript
2022. 5. 20.•
1

TypeScript 객체 업데이트의 정석: Spread 연산자와 Utility Types(Pick, Partial) 활용하기

Seunghoon Shin
작성자 Seunghoon Shin풀스택 개발자

TypeScript 객체 업데이트의 정석: Spread 연산자와 Utility Types(Pick, Partial) 활용하기

1. 문제의 배경

자바스크립트와 타입스크립트에서 객체 데이터를 다루는 것은 일상적인 작업입니다. 특히 React와 같은 현대적인 프레임워크를 사용할 때는 불변성(Immutability)을 유지하며 객체를 업데이트하는 것이 매우 중요합니다.

단순히 obj.key = value와 같이 직접 수정하는 방식은 사이드 이펙트를 유발하고, 리액트의 상태 변경 감지를 방해할 수 있습니다. 또한, 대규모 프로젝트에서 객체의 특정 속성만 안전하게 업데이트(Partial Update)하기 위해서는 타입 시스템의 도움 없이는 런타임 에러를 피하기 어렵습니다.

2. 해결 방안 탐색

객체를 안전하게 업데이트하기 위해 우리는 크게 두 가지 도구를 조합합니다.

  1. ES6 Spread 연산자 (...): 기존 객체를 복사하면서 새로운 값을 덮어씌워 불변성을 보장합니다.
  2. TypeScript Utility Types (Partial, Pick): 업데이트에 필요한 속성만 타입 수준에서 안전하게 추출하여 관리합니다.

3. 핵심 개념 및 아키텍처

불변 객체 업데이트 흐름

타입 시스템의 역할

  • Record: 유니온 타입을 기반으로 일관된 구조의 객체 타입을 정의합니다.
  • Pick: 특정 키들만 선택하여 새로운 타입을 만듭니다.
  • Partial: 모든 속성을 선택 사항(Optional)으로 만들어 '일부 수정' 로직에 최적화된 타입을 만듭니다.

4. 구현 및 트러블슈팅

Spread 연산자를 이용한 기본 업데이트

type AccountKey = 'id' | 'pwd' | 'nickname'; let myAccount: Record<AccountKey, string> = { id: 'hooney', pwd: '1234aa', nickname: 'hoon' }; // 업데이트할 데이터 (Pick 활용) const dataForUpdating: Pick<Record<AccountKey, string>, 'nickname'> = { nickname: '후니' }; // 💡 새로운 객체를 생성하며 업데이트 myAccount = { ...myAccount, ...dataForUpdating };

트러블슈팅: Partial을 활용한 유연한 업데이트 함수

실무에서는 어떤 값이 들어올지 모르는 동적인 업데이트 함수를 만들어야 할 때가 많습니다. 이때 Partial<T>는 필수적입니다.

interface UserProfile { id: string; email: string; age: number; bio: string; } /** * Partial을 사용하여 프로필의 일부만 업데이트하는 함수 */ const updateUser = (current: UserProfile, updates: Partial<UserProfile>): UserProfile => { return { ...current, ...updates }; }; const user: UserProfile = { id: '1', email: 'test@test.com', age: 20, bio: 'hello' }; const updated = updateUser(user, { age: 21, bio: 'growing up!' });

Partial<UserProfile>을 사용하면 { age: 21 }만 넘겨도 타입 에러가 발생하지 않으며, 자동 완성과 타입 검사의 이점을 그대로 누릴 수 있습니다.

5. 결과 및 Trade-off

성과

  • 타입 안정성: 잘못된 키 값을 업데이트하려는 시도를 컴파일 타임에 차단합니다.
  • 코드 간결성: 수동으로 하나씩 값을 할당할 필요 없이 한 줄의 Spread 연산으로 해결됩니다.
  • 예측 가능성: 원본 데이터를 보존하므로 디버깅이 쉬워지고 React의 최적화 도구들과 잘 맞물립니다.

Trade-off

  • 얕은 복사(Shallow Copy)의 한계: Spread 연산자는 1단계 깊이만 복사합니다. 중첩된 객체(Nested Object)를 업데이트할 때는 Immer와 같은 라이브러리를 고려하거나 더 복잡한 Spread 구조가 필요합니다.
  • 가비지 컬렉션: 매번 새로운 객체를 생성하므로 아주 빈번한 업데이트가 발생하는 경우 메모리 사용량이 늘어날 수 있습니다.

6. 마치며

TypeScript의 유틸리티 타입과 ES6의 문법을 적절히 조합하면, 복잡한 데이터 업데이트 로직도 간결하고 안전하게 작성할 수 있습니다. Pick과 Partial의 차이를 명확히 이해하고 상황에 맞는 도구를 선택하는 습관을 들여보세요!

참고 자료:

  • TypeScript Handbook: Utility Types
  • MDN: Spread syntax
← 이전 글Interface에서 어떠한 key의 value를 타입으로 만드는 방법
다음 글 →Nest.js에서 dto를 사용하여 validation과 데이터 변형 시켜보기