Skip to content

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

앞서 6장에서는 카피-온-라이트(Copy-on-Write) 를 통해 중첩된 데이터에서도 불변성을 지키는 방법을 살펴보았습니다. 새로운 기능을 새로 만들 때라면 이 방식을 적용하기 쉽습니다.

하지만 실제 프로젝트에서 이미 존재하는 레거시 코드와 함께 작업해야 하는 경우가 많습니다. 이때 사용할 수 있는 방어적 복사(Defensive Copy) 에 대해 알아봅니다.

1. 레거시 코드와 불변성

레거시 코드가 다루는 데이터는 불변성을 깨뜨릴 수 있습니다. 이런 상황에서 중요한 것은 “신뢰할 수 있는 코드”와 “신뢰할 수 없는 코드”의 경계를 명확히 하는 것입니다.

2. 안전지대 개념

불변성을 철저히 지키며 작성된 코드들의 영역을 안전지대라고 부를 수 있습니다. 반면 레거시 코드는 안전지대 바깥에 있습니다.

따라서 안전지대의 경계를 넘나드는 데이터는 잠재적으로 위험합니다. 이때 필요한 것이 바로 방어적 복사입니다.

3. 방어적 복사

방어적 복사는 데이터가

  1. 안전지대로 들어올 때
  2. 안전지대에서 나갈 때

깊은 복사본을 만드는 것을 의미합니다.

예를 들어, 장바구니에 담긴 상품 가격을 레거시 함수로 변경해야 한다고 할 때,

tsx
function add_item_to_cart(item, price) {
    // 안전지대 밖으로 나갈 때 깊은 복사
    var cart_copy = deepCopy(shopping_cart);
    legacy_set_promotion_price(cart_copy);

    // 안전지대로 들어올 때 다시 깊은 복사
    shopping_cart = deepCopy(cart_copy);
}

TIP

API 요청을 통해 서버에서 직렬화된 JSON 데이터를 받는 과정도, 일종의 방어적 복사라고 볼 수 있습니다. 데이터를 직렬화-역직렬화하는 순간, 원본과는 독립된 복사본이 만들어지기 때문입니다.

4. Copy-on-Write vs 방어적 복사

구분카피-온-라이트 (Copy-on-Write)방어적 복사 (Defensive Copy)
언제 쓰나요?통제할 수 있는 데이터를 바꿀 때신뢰할 수 없는 코드/데이터와 주고받을 때
어디서 쓰나요?안전지대 내부 어디서든안전지대의 경계
복사 방식얕은 복사 (비용 적음)깊은 복사 (비용 큼)
규칙1. 얕은 복사본 생성
2. 복사본 변경
3. 복사본 반환
1. 안전지대 입장 시 깊은 복사
2. 안전지대 퇴장 시 깊은 복사

5. 얕은 복사 vs 깊은 복사

얕은 복사는 1단계만 복사하기 때문에 내부 참조는 여전히 공유됩니다.

tsx
let arr1 = [1, [2, 3]];
let arr2 = arr1.slice(); // 얕은 복사

arr2[0] = 99; // 원본은 영향 없음
arr2[1][0] = 88; // 내부 배열 요소 수정 → 원본도 변경됨

console.log(arr1); // [1, [88, 3]]

깊은 복사는 내부 구조까지 모두 복제해 원본과 완전히 독립시킵니다.

tsx
let arr1 = [1, [2, 3]];
let arr2 = JSON.parse(JSON.stringify(arr1)); // 깊은 복사

arr2[1][0] = 88;

console.log(arr1); // [1, [2, 3]]
console.log(arr2); // [1, [88, 3]]

정리하며

  • 카피-온-라이트는 우리가 직접 통제할 수 있는 코드 내부에서 불변성을 지킬 때 유용합니다.
  • 방어적 복사는 안전지대와 레거시 코드처럼 신뢰할 수 없는 코드 사이에서 데이터를 주고받을 때 필요합니다.
  • 깊은 복사는 비용이 크기 때문에 꼭 필요한 순간, 즉 안전지대의 경계에서만 사용하는 것이 바람직합니다.