Error

Error | 중첩된 객체(객체 안의 객체) 형태의 상태 onChange 함수 구현하기

3jun 2023. 8. 31. 18:34

중첩된 객체, 즉 객체 안의 객체 형태의 state 가 존재할 때, 이 state 를 업데이트 하기 위한 onChange 함수를 구현하는 방법을 공유하고자 한다. 

에러 발생 상황

제목 그대로 중첩된 객체 형태의 상태가 존재하고, 이를 업데이트 하기 위한 onChange 함수를 구현하는 과정에서 지속적으로 타입에러가 발생했다. 

 

아래 예시코드에서와 같이 state 라는 객체 안에 gym, address 라는 2개의 key가 존재하고, address key는 하위에 city 라는 key와 province 라는 key를 가진 객체를 value로 갖고 있다. 

 

이 때 onChange 이벤트를 활용하여 input에 입력이 발생하면 그에 해당하는 key 의 value 값을 업데이트 하는 기능을 구현하고자 했다. 

 

예시코드

interface OverLappedState {
  gym: string,
  address: Address
}

interface Address {
  city: string,
  province: string
}

const [state, setState] = useState<OverLappedState>({
  gym: '', 
  address: {
  	city: '',
  	province:''
  }
}) 

const overlappedState:OverLappedState = {
  gym: '체육관',
  address: {
    city: '서울',
    province: '경기도'
  }
}

const key = 'address'
const innerKey = 'province'

// 타입에러 발생
// onChange 함수를 구현하기에 앞서 에러를 먼저 확인하기 위한 코드
console.log(overlappedState[key][innerKey])

실제 프로젝트 코드에서 발생한 에러는 아래와 같다. 

시도했던 방법

console.log(overlappedState[key as keyof OverLappedState])

기존에 위와 같이 key 로 사용하려는 string 값이 해당 타입의 key 값임을 선언해주면 정상적으로 구현이 가능했다. 위 코드 역시 에러 없이 콘솔이 정상 출력되었다.

console.log(overlappedState[key as keyof OverLappedState][innerKey as keyof Address])

문제가 된 부분은 위 코드였다. 

onChange 이벤트에서 state의 object 뿐만 아니라 object의 object를 수정하는 기능 역시 구현해야 했기에 innerKey를 사용한 state 접근 역시 가능해야 했던 상황이었지만 도저히 해결방법을 찾을 수 없었다. 

 

결국 커뮤니티의 도움을 받아 해결했다. (역시 혼자보단 여럿이 낫다.. 개발자 커뮤니티 만세!!)

해결방법

앞 부분 `overlappedState[key as keyof OverLappedState]` 의 타입 정보가 innerKey를 사용하여 호출하기 전까지 유지되지 않는게 원인으로 추측되었다.

console.log((overlappedState[key as keyof OverLappedState]as Address)[innerKey as keyof Address])

결국 위와 같이 첫 번째 key 변수를 사용하여 가져온 값의 return 타입을 다시 한번 지정해주고 그 객체를 다시 innerKey를 활용하여 다시 한번 가져옴으로써 해결했다. 

 

이렇게 구현한 onChange 함수는 아래와 같다.

const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = event.target;
    const [key, innerKey] = name.split('-');  // name = 'address-city' , 'address-province'

   const updatedState = {...state};
   (updatedState[key as keyof OverLappedState] as Address)[innerKey as keyof Address] = value
 };

더 나은 해결 방법

커뮤니티에서 해결방법을 논의하던 중에 지나친 Assertion 사용은 any 타입을 사용하는 것과 마찬가지로 지양해야 한다는 말을 들었다.

생각해보니 아직 타입스크립트를 처음 사용하기 시작하면서 "타입스크립트가 익숙치 않은 사람은 any를 사용하지 않는 것부터 시작하라" 라는 말을 인상깊게 들었는데 이 문장에 너무 매몰되어 Assertion을 사용하는 것에 대해서는 전혀 거리낌 없이 사용했고, 그 장단점에 대해서는 전혀 고려하지 않고 사용하고 있었다. 

 

이번 기회에 올바른 타입 단언을 사용 방법에 대해 한번 더 고민해보았다.

 

더나은 해결 방법과 타입 단언에 대해 더 알아보고 싶다면 아래 포스팅으로 이동해주세요.

 

Type Assertion, 타입 단언의 올바른 사용법 고민하기

타입 단언은 타입에 확신이 있을 때 사용하는 타입 지정 방식이다. 나 같은 경우에는 `Unsafe member access ~~` 에러가 발생했을 때 주로 타입 단언을 통해 해결했었다. 타입 단언 사용방법 타입 단언

kk3june.tistory.com