Ch7 에서는 방어적 복사를 통해 레거시 코드를 사용하면서 어떻게 불변성을 유지할 수 있는지 알려주고 있다.
Ch7 요약
카피 온 라이트는 불변성이 확보된 안전지대 안에서 데이터를 주고받는다면 방어적 복사 defensive copy 는 신뢰할 수 없는 코드와 데이터를 주고받을 때 복사본을 만들어 전달한다.
방어적 복사는 깊은 복사를 하여 모든 것을 복사하기에 카피 온 라이트에 비해 비용이 많이 든다. 신뢰할 수 없는 코드와 함께 사용할 때만 DC 를 사용한다.
신뢰할 수 없는 코드가 있다면 방어적 복사를 사용해 감싸자
웹 기반 API 도 암묵적으로 방어적 복사를 한다. request, response 를 주고받을때 JSON 형식으로 직렬화하여 데이터를 전송하는 것도 방어적 복사로 데이터를 복사해서 복사본을 전달하는 것이다.
방어적 복사는 비공유 아키텍처를 구현하기 좋다.
// 원본
function add_item_to_cart(name, price) {
var item = make_cart_item(name, price);
shopping_cart = add_item(shopping_cart, item);
var total = calc_total(shopping_cart);
set_cart_total_dom(total);
update_shipping_icons(shopping_cart);
update_tax_dom(total);
black_friday_promotion(shopping_cart);
}
책에서는 black_friday_promotion
가 레거시 코드이며 신뢰할 수 없는 코드라 불변성을 유지하기 위해 방어적 복사 기법을 사용할 수 있다고 소개하고 있다.
책의 내용대로 방어적 복사는 데이터를 가져오고 내보낼 때 복사본을 만들어 전달한다.
/// Copy before sharing data
function add_item_to_cart(name, price) {
var item = make_cart_item(name, price);
shopping_cart = add_item(shopping_cart, item);
var total = calc_total(shopping_cart);
set_cart_total_dom(total);
update_shipping_icons(shopping_cart);
update_tax_dom(total);
var cart_copy = deepCopy(shopping_cart);
black_friday_promotion(cart_copy);
}
/// Copy before and after sharing data
function add_item_to_cart(name, price) {
var item = make_cart_item(name, price);
shopping_cart = add_item(shopping_cart, item);
var total = calc_total(shopping_cart);
set_cart_total_dom(total);
update_shipping_icons(shopping_cart);
update_tax_dom(total);
var cart_copy = deepCopy(shopping_cart);
black_friday_promotion(cart_copy);
shopping_cart = deepCopy(cart_copy);
}
주제 | 카피 온 라이트 | 방어적 복사 |
---|---|---|
언제 사용하나요? | 통제할 수 있는 데이터를 바꿀 때 | 신뢰 할 수 없는 코드와 데이터를 주고받을 때 |
어디서 쓰나요? | 안전지대 어디서나 사용 카피 온 라이트가 불변성을 가진 안전지대를 만든다. | 안전지대의 경계에서 데이터가 오고 갈 때 |
복사 방식은? | 얕은 복사 | 깊은 복사 |
규칙 | 1. 바꿀 데이터의 얕은 복사본을 생성 2. 복사본 변경 3. 복사본 리턴 | 1. 안전지대로 들어오는 데이터에 깊은 복사를 만든다. 2. 안전지대에서 나가는 데이터에 깊은 복사를 만든다. |
자바스크립트에서는 표준 라이브러리가 좋지 않아 깊은 복사를 만들기가 어렵다.. 깊은 복사를 하고 싶을때는 아래와 같은 방법을 활용할 수 있다.
- JSON.parse(JSON.stringify(obj))
- structuredClone(obj)
- lodash 라이브러리의 .cloneDeep()
카피 온 라이트 패턴은 무엇이 바뀌는지 알기 때문에 무엇을 복사해야 할지 예상할 수 있습니다.
신뢰할 수 없는 레거시 코드는 어떤 일이 일어날지 정확히 알 수 없어 데이터가 바뀌는 것을 완전히 막아주는 원칙이 필요하며 이 원칙을 방어적 복사라고 한다. - 149p
생각
막연히 카피 온 라이트가 얕은 복사고 방어적 복사가 깊은 복사라고 생각했는데 그렇게 이분법적으로 분리할 수 없다는 생각이 들었다. 카피 온 라이트는 신뢰할 수 있도록 코드를 변경하기 전에 어떻게 변경될지 예측이 가능하므로 복사본을 만들고 그 뒤에 복사본을 수정한다.
카피 온 라이트는 변경 가능한 쓰기를 불변의 읽기로 전환시켜주니 신뢰할 수 있는 코드를 만들어준다.
들어오고 나가는 데이터의 복사본을 만드는 것이 방어적 복사가 동작하는 방식이다. 방어적 복사를 사용하면 데이터가 바뀌는 것을 막아 불변성을 지킬 수 있다. - 151 p
생각
방어적 복사는 신뢰할 수 있는 코드로 만들 수 없는 코드를 사용해야 할 때 변경될 수 있는 데이터를 그대로 사용하지 않고 입력과 출력으로 해당 데이터가 넘어올 때 그대로 사용하지 말고 깊은 복사를 하여 원본을 건드리지 않고 복사본만으로 작업을 진행하는 것으로 이해했다.
카피 온 라이트는 복사본을 만들어 복사본에 쓰기를 하니 카피 - 온 - 라이트인 것 같고 방어적 복사는 원본이 바뀌지 않도록 방어해서 방어적 복사라고 하는것 같다.
함수형 프로그래밍에서는 유일한 객체로 사용자를 표현하지 않는다. 데이터는 이벤트에 대한 사실이다. 이벤트가 발생할 때처럼 필요할 때마다 여러 번 복사할 수 있다. - 157p
생각
책에서 이런 부분이 참 마음에 들었다. 마치 여러 사람들과 같이 책을 읽으며 새로운 관점에서 내용을 다시 바라볼 수 있는 부분이 좋았다. 방어적 복사니 카피 온 라이트니 계속 뭘 복사하는데 복사본이 여러개면 문제가 발생하지 않을까 하는 염려에 대해 애초에 데이터는 이벤트에 대한 사실이니 여러 번 복사해서 사용할 수 있다는 답변을 제공한다.
레거시 코드 legacy code - 오래 전에 만들어 지금 당장 고칠 수 없어 그대로 사용해야 하는 코드
깊은 복사 deep copy - 위에서 아래로 모든 계층에 있는 중첩된 데이터 구조를 복사한다.
앞에 함수형 코딩 Ch4 에서 map 이 얕은 복사를 한다고 생각했다.
그런데 다시 코드를 좀 수정해보니 깊은 복사를 한다는 것을 알 수 있었다. 사실 깊은 복사, 얕은 복사라고 구분하기는 어렵고 배열의 각 요소를 돌면서 콜백 함수를 실행하고 그 결과를 배열로 반환해주는데 반환할때 처음 예시는 수정된 바가 없으니 원본을 참조하고 있어 변경사항이 원본과 공유된 것이고 두번째 예시코드의 경우 spread 문법을 사용하는 순간이 깊은 복사가 일어났을 때인데 spread 는 기본적으로 깊은 복사를 수행하며 2차원 이상에서 얕은 복사를 수행한다. 다만 저 코드에서는 깊은 복사를 수행하여 원본과 복사본이 다른 참조를 가리키게 되어 복사본에 수정을 해도 원본에 영향이 없던 것이다.