신뢰할 수 없는 코드를 쓰면서 불변성 지키기
불변성을 유지하기 위해 보통은 카피-온-라이트(copy-on-write) 를 사용합니다.
하지만 레거시 코드나 외부 라이브러리처럼 우리가 통제할 수 없는 영역에서는 이 방식이 통하지 않습니다.
그럴 때 필요한 것이 바로 방어적 복사(defensive copy) 입니다.
스터디 회차: 6회차 (2025년 8월 25일)
레거시 코드와 불변성
예제: 블랙 프라이데이 행사 코드 추가
tsx
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
은 레거시 코드로, 내부에서shopping_cart
를 직접 수정할 수 있습니다. - 즉, 안전지대 밖에서 데이터가 변할 수 있고, 그 참조가 안전지대로 흘러 들어올 수도 있습니다.
이런 상황에서는 불변성을 깨는 잠재적 위험이 항상 존재합니다. 이를 방어적 복사를 적용하여 해결할 수 있습니다.
방어적 복사
- 데이터가 안전지대를 벗어날 때 복사합니다.
- 원본 대신 깊은 복사본을 신뢰할 수 없는 코드로 전달합니다.
- 데이터가 안전지대로 들어올 때 복사합니다.
- 외부에서 들어온 값은 즉시 깊은 복사하여 사용합니다.
이렇게 하면 원본 데이터는 외부 코드에 노출되지 않고 (외부 코드에 의해 변경되지 않고), 외부에서 변형된 값도 안전하게 전달 받을 수 있습니다.
방어적 복사로 개선하기
tsx
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); // 결과 값을 깊은 복사로 반영
}
shopping_cart
원본은 외부 코드에 직접 전달되지 않습니다.- 외부 코드 실행 전/후 모두 deepCopy를 거치므로 안전성이 보장됩니다.
신뢰할 수 없는 코드 감싸기
매번 deepCopy
를 작성하는 대신, 안전한 래퍼 함수로 감싸 중복을 제거할 수 있습니다.
tsx
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);
shopping_cart = black_friday_promotion_safe(shopping_cart);
}
function black_friday_promotion_safe(cart) {
var cart_copy = deepCopy(cart);
black_friday_promotion(cart_copy);
return deepCopy(cart_copy);
}
black_friday_promotion_safe
가 항상 방어적 복사를 보장합니다.- 안전지대 코드에서는 안전한 래퍼 함수만 사용하면 됩니다.
카피온 라이트와 방어적 복사 비교하기
카피-온-라이트 | 방어적 복사 | |
---|---|---|
언제 | 통제할 수 있는 데이터를 바꿀때 | 신뢰할 수 없는 코드와 데이터를 주고 받아야 할 때 |
어디서 | 안전지대 어디서나 쓸 수 있습니다. | 안전지대의 경계에서 데이터가 오고 갈 때 사용합니다. |
복사 방식 | 얕은 복사 | 깊은 복사 |
규칙 | 1. 바꿀 데이터의 얕은 복사를 만듭니다. 2. 복사본을 변경합니다. 3. 복사본을 리턴합니다. | 1. 안전지대로 들어오는 데이터에 깊은 복사를 만듭니다. 2. 안전지대에서 나가는 데이터에 깊은 복사를 만듭니다. |
웹 API와 비공유 아키텍처
웹 API속에 방어적 복사
- 웹 기반 API는 이미 방어적 복사를 내장하고 있습니다.
- 클라이언트 → 서버: 객체를 JSON으로 직렬화 (깊은 복사본 생성)
- 서버 → 클라이언트: JSON 응답 (깊은 복사본 리턴)
즉, 네트워크 통신 과정 자체가 비공유 아키텍처를 만듭니다.
비공유 아키텍처
- 모듈이 서로 통신하기 위해 방어적 복사를 구현했다면 “비공유 아키텍처”라고 합니다.
- 모듈이 어떤 데이터의 참조도 공유하고 있지 않기 때문입니다.
정리하기
- 레거시 코드나 외부 라이브러리는 불변성을 보장하지 않습니다. → 잠재적 위험
- 이를 막기 위한 핵심 원칙으로 방어적 복사가 있습니다.
- 안전지대 → 외부 : 깊은 복사본 전달
- 외부 → 안전지대 : 깊은 복사본 수신
- 비용이 큰 깊은 복사 대신, 안전지대 내부에서는 카피-온-라이트를 활용합니다.
- 외부와의 경계를 방어적 복사로 지키면, 불변성 기반의 함수형 코드를 유지할 수 있습니다.