본문으로 바로가기

타입스크립트에서는 객체의 특정 key에 대한 값을 변경할 때, 타입 안전성을 유지하는 것이 중요합니다.
이번 글에서는 타입 안전성을 유지하면서 객체의 값을 수정하는 두 가지 방법을 알아보겠습니다.

방법 1: 객체 확장 방식

아래 코드는 객체의 특정 key에 따라 값을 수정하는 예제입니다.

interface User {
  addr: string;
  name: string;
  age: number;
  gender: "M" | "W";
}

type UserKey = keyof User;
type UserValue = User[UserKey];

const user: User = {
  addr: "",
  name: "",
  age: 0,
  gender: "M",
};

const onChangeUser = (user: User, key: UserKey, value: UserValue) => {
  return {
    ...user,
    [key]: value,
  };
};

이 코드에서는 keyvalue를 받아 객체를 반환하는데, 타입 안전성에 문제가 있습니다.
예를 들어, 아래와 같이 addrnumber 타입이 잘못 들어가도 타입 오류가 발생하지 않습니다.

onChangeUser(user, "addr", 1);  // 'addr'은 string이어야 하지만, number가 들어감

방법 2: 제네릭을 활용한 타입 안전성 강화

타입 안전성을 강화하기 위해 제네릭을 사용할 수 있습니다.
제네릭을 활용하면 key가 설정될 때 해당 key에 맞는 타입이 자동으로 추론되므로, 잘못된 타입의 값이 들어갈 수 없게 됩니다.

interface User {
  addr: string;
  name: string;
  age: number;
  gender: "M" | "W";
}

interface ObjectHandler<T> {
  <K extends keyof T>(obj: T, key: K, value: T[K]): void;
}

const user: User = {
  addr: "",
  name: "",
  age: 0,
  gender: "M",
};

const onChangeUser: ObjectHandler<User> = (obj, key, value) => {
  return {
    ...obj,
    [key]: value,
  };
};

이렇게 작성하면 addr 필드에는 string만 들어갈 수 있으므로, onChangeUser(user, "addr", 1)처럼 잘못된 타입이 들어가는 경우 타입 에러가 발생합니다.

결론

제네릭을 활용하여 객체의 keyvalue 타입을 강제하면 타입 안전성을 확보할 수 있습니다.
특히, 리액트에서 객체 상태를 업데이트할 때 이 방식을 사용하면 타입 오류를 방지하고 코드의 가독성을 높일 수 있습니다.

리액트에서는 아래와 같이 사용할 수 있습니다.

const onChangeUser: ObjectHandler<User> = (key, value) => {
  setUser(prev => ({
    ...prev,
    [key]: value,
  }));
};

이 방법을 통해 객체의 상태를 안전하고 일관되게 관리할 수 있습니다.

반응형