22sook00 logo
SookDev
🌳

TypeError: Cannot assign to read only property '0' of object '[object Array]'

 
목차

 
리코일에서 데이터를 담아 sorting 하던 중 다음과 같은 에러를 직면했다. 콘솔을 찍어볼땐 제대로 값을 받아오는데 sort 가 불가능한 read only (읽기전용) 데이터라고 한다. 이를 해결하기위해 리코일에서 받아온 데이터를 “깊은복사” 하여 해결해야 한다.
 
notion image

1. 문제 발생 이유

🌿 얕은 복사 & 깊은복사

얕은 복사는 객체의 참조값(주소 값)을 복사하고, 깊은 복사는 객체의 실제 값을 복사한다.
자바스크립트에서 값은 원시값과 참조값 두 가지 데이터 타입의 값이 존재한다.
자바스크립트에서 원시 타입(primitive type)의 값은 새로운 메모리 공간에 독립적인 값을 저장하기 때문에
깊은 복사가 되고 참조 타입(reference type)값은 얕은 복사가 된다.
원시 타입과 참조 타입의 가장 큰 차이점은 원본이 바뀌면 참조 타입은 복사본도 같이 변경되지만,
원시 타입은 변경되지 않는다는 점이 큰 차이점이다.
Recoil은 애초에 불변성을 유지하도록 설계되었다고 한다.

2. 깊은복사 방법

🌱 JSON.parse(JSON.stringify)

자바스크립트에서 배열을 정렬할 때 발생하는 TypeError: Cannot assign to read only property '0' of object '[object Array]' 오류는, 배열이 변경 불가능(immutable) 상태로 설정되어 있을 때 발생할 수 있습니다. 이는 특히 React의 상태 관리 라이브러리인 Recoil을 사용할 때 발생할 수 있는데, Recoil 상태(selectors 또는 atoms)로부터 반환된 배열을 직접 변경하려고 할 때 발생할 수 있습니다.
이 문제를 해결하기 위해, 원본 배열을 직접 변경하지 않고 깊은 복사(deep copy)를 수행한 후에 정렬 작업을 진행해야 합니다. 깊은 복사는 원본 객체의 완전한 복사본을 생성하여, 원본 객체와는 완전히 독립된 새 객체를 만들게 합니다.
깊은 복사를 수행하는 방법 중 하나는 JSON.parse(JSON.stringify(object))를 사용하는 것입니다. 하지만 이 방법은 함수, Date 객체, undefined, RegExp 객체 등 일부 데이터 타입에 대해서는 올바르게 작동하지 않을 수 있습니다. 다행히 여기서는 배열 내 객체들에 대해 단순한 구조를 다루고 있으므로, 이 방법을 사용할 수 있습니다.
다음은 깊은 복사를 이용하여 sort 함수를 사용하기 전에 원본 배열을 복사하는 방법을 보여줍니다:
 
이렇게 하면 원본 list.groupList는 변경되지 않으므로, Recoil 상태를 직접 변경하는 것과 관련된 오류를 피할 수 있

🌿 재귀 함수 사용

깊은 복사(deep copy)를 수행하는 다른 방법들이 있습니다.
JSON.parse(JSON.stringify(object)) 방법은 매우 간단하고 널리 사용되지만,
몇 가지 한계가 있기 때문에 다른 상황에서는 다른 방법들을 고려할 수 있다.
객체나 배열 내부의 중첩된 객체나 배열까지 깊은 복사를 하기 위해 재귀 함수를 사용할 수 있습니다. 이 방법은 모든 종류의 데이터 타입과 순환 참조를 처리할 수 있도록 조정할 수 있습니다.

🪴  lodash 라이브러리의 cloneDeep 함수 사용

lodash는 JavaScript에서 널리 사용되는 유틸리티 라이브러리입니다. lodashcloneDeep 함수를 사용하면 객체의 깊은 복사를 매우 쉽게 할 수 있습니다. 이 방법은 성능도 좋고 다양한 데이터 타입과 복잡한 객체 구조에도 잘 작동합니다.

🌳 structuredClone 함수 사용

structuredClone는 HTML Living Standard에 추가된 API로, 깊은 복사를 지원합니다.
이 함수는 다양한 내장 타입, 포함하여 순환 참조와 같은 복잡한 데이터 구조를 복사할 수 있습니다.
structuredClone는 모던 브라우저에서 지원됩니다.
 
🍃
방법 선택 기준
  • 재귀 함수 사용: 커스텀 로직이 필요하거나 특정 타입의 객체를 다르게 처리해야 할 때 유용합니다.
  • lodashcloneDeep: 대규모 프로젝트나 복잡한 데이터 구조를 다룰 때, 이미 lodash를 사용하고 있다면 편리합니다.
  • structuredClone: 최신 브라우저에서 사용 가능하며, 다양한 타입과 복잡한 구조를 갖는 데이터를 복사해야 할 때 매우 유용합니다. 하지만 구형 브라우저에서는 지원되지 않을 수 있습니다.
 
 

참조
🔗 
🔗