Skip to content

신뢰할 수 없는 코드를 쓰면서 불변성 지키기

불변성을 유지하기 위해 보통은 카피-온-라이트(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를 직접 수정할 수 있습니다.
  • 즉, 안전지대 밖에서 데이터가 변할 수 있고, 그 참조가 안전지대로 흘러 들어올 수도 있습니다.

이런 상황에서는 불변성을 깨는 잠재적 위험이 항상 존재합니다. 이를 방어적 복사를 적용하여 해결할 수 있습니다.

방어적 복사

방어적 복사

  1. 데이터가 안전지대를 벗어날 때 복사합니다.
    • 원본 대신 깊은 복사본을 신뢰할 수 없는 코드로 전달합니다.
  2. 데이터가 안전지대로 들어올 때 복사합니다.
    • 외부에서 들어온 값은 즉시 깊은 복사하여 사용합니다.

이렇게 하면 원본 데이터는 외부 코드에 노출되지 않고 (외부 코드에 의해 변경되지 않고), 외부에서 변형된 값도 안전하게 전달 받을 수 있습니다.

방어적 복사로 개선하기

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 응답 (깊은 복사본 리턴)

즉, 네트워크 통신 과정 자체가 비공유 아키텍처를 만듭니다.

비공유 아키텍처

  • 모듈이 서로 통신하기 위해 방어적 복사를 구현했다면 “비공유 아키텍처”라고 합니다.
  • 모듈이 어떤 데이터의 참조도 공유하고 있지 않기 때문입니다.

정리하기

  • 레거시 코드나 외부 라이브러리는 불변성을 보장하지 않습니다. → 잠재적 위험
  • 이를 막기 위한 핵심 원칙으로 방어적 복사가 있습니다.
    • 안전지대 → 외부 : 깊은 복사본 전달
    • 외부 → 안전지대 : 깊은 복사본 수신
  • 비용이 큰 깊은 복사 대신, 안전지대 내부에서는 카피-온-라이트를 활용합니다.
  • 외부와의 경계를 방어적 복사로 지키면, 불변성 기반의 함수형 코드를 유지할 수 있습니다.