- Published on
2024.01.16
[함수형 코딩] - 07. 신뢰할 수 없는 코드를 쓰면서 불변성 지키기
카피-온-라이트 패턴은 데이터를 바꾸기 전에 복사한다.
무엇이 바뀌는지 알기 때문에 무엇을 복사할지 예측 가능.
그러나 레거시 코드는 어떤 일이 일어날지 예측이 불가해 데이터가 바뀌는 것을 완벽히 막아줄 원칙이 필요
이 원칙이 방어적 복사
방어적 복사
깊은 복사를 통해 데이터가 바뀌어도 영향을 받지 않도록 한다.
방어적 복사를 사용하면 원본의 변경을 방지해 불변성을 지킬 수 있다.
방어적 복사에는 두 가지 규칙이 있다.
- 데이터가 안전한 코드에서 나갈 때 복사하기
변경 불가능한 데이터가 신뢰할 수 없는 코드로 나갈 때,- 불변성 데이터를 위한 깊은 복사본을 만든다.
- 신뢰할 수 없는 코드로 복사본을 전달한다.
- 안전한 코드로 데이터가 들어올 때 복사하기
신뢰할 수 없는 코드에서 변경될 수도 있는 데이터가 들어올 때,- 변경될 수도 있는 데이터가 들어오면 바로 깊은 복사본을 만들어 안전한 코드로 전달
- 복사본을 안전한 코드에서 사용
연습문제 푸는데 생각보다 안 풀린다... 일단 저 방어적 복사 규칙이 아직 낯설어서 그런지 규칙 그냥 생각도 안남. 근데 정답 보면 너무 단순한걸 보면 내가 어렵게 생각하나 싶기도 하다..
- | 카피-온-라이트 | 방어적 복사 |
---|---|---|
언제 | 통제할 수 있는 데이터를 바꿀 때 | 신뢰할 수 없는 코드와 데이터를 주고받을 때 |
어디서 | 안전지대 어디서나 | 안전지대의 경계에서 |
복사 방식 | 얕은 복사(상대적으로 적은 비용) | 깊은 복사(상대적으로 많은 비용) |
규칙 | 1. 바꿀 데이터의 얕은 복사를 만든다. 2. 복사본을 변경한다. 3. 복사본을 리턴한다. | 1. 안전지대로 들어오는 데이터에 깊은 복사를 만든다. 2. 안전지대에서 나가는 데이터에 깊은 복사를 만든다. |
자바스크립트에서 깊은 복사를 구현하는 것은 어렵다.
자바스크립트의 표준 라이브러리가 좋지 않기 때문.
깊은 복사의 동작은 배열과 객체의 복사를 위해 모든 항목을 재귀적으로 반복하며 복사한다.
깊은 복사가 필요하다면 Lodash
의 .cloneDeep( )
을 추천한다.
해당 공식문서
원래부터 lodash를 쓰지 않았어서 깊은 복사가 필요하다면 함수를 하나 만들어서 재활용하면 좋을 것 같다. 이것뿐만 아니라 카피-온-라이트나 이 책에서 알려주는 예시들을 따로 구현해놓고 쓰면 편할 것 같다.
카피-온-라이트와 방어적 복사는 각각의 장점이 있으므로, 어느 하나가 아니라 둘을 적절한 곳에서 사용하여 불변성 원칙을 지키도록 하는 것이 중요한 것 같다.
깊은 복사와 관련해서 따로 정리
JavaScript에서, 모든 표준 내장 객체의 복사 작업(전개 구문, Array.prototype.concat(), Array.prototype.slice(), Array.from(), Object.assign(), Object.create())은 깊은 복사본을 생성하지 않습니다 (대신, 얕은 복사본을 생성합니다).
직렬화가 가능하다면 JavaScript 객체의 깊은 복사를 만드는 한 가지 방법은, JSON.stringify()를 사용하여 객체를 JSON 문자열로 변환한 다음, JSON.parse()로 문자열을 다시 (완전히 새로운) JavaScript 객체로 변환하는 것입니다.
const ingredientsList = ['noodles', { list: ['eggs', 'flour', 'water'] }]
const ingredientsListDeepCopy = JSON.parse(JSON.stringify(ingredientsList))
위 코드의 객체는 직렬화할 수 있을 만큼 단순하지만, 많은 JavaScript 객체는 직렬화할 수 없습니다.
예를 들어, 함수 (클로저 포함), 심볼, HTML DOM API에서 HTML 요소를 나타내는 객체, 재귀 데이터 등등.
이 경우 JSON.stringify()를 호출하면 실패합니다.
Web API structuredClone()은 또한 깊은 복사본을 만들 수 있습니다. 또한 Error와 같은 더 많은 데이터 타입을 처리합니다. 그러나 structuredClone()은 JavaScript 언어 자체의 기능이 아니라 web API를 실행하는 브라우저 및 다른 JavaScript 호스트의 기능입니다. 그리고 직렬화할 수 없는 객체에 structuredClone()을 호출하면, JSON.stringify()과 같이실패할 것입니다.
JavaScript의 새로운 객체 복사 방법 - Structured Clone 블로그 글
lodash의 clonedeep 함수
var objects = [{ a: 1 }, { b: 2 }]
var deep = _.cloneDeep(objects)
이게 제일 편해보이긴 함