신뢰할 수 없는 코드와 불변성
- 7.1 신뢰할 수 없는 코드란
- 7.2 방어적 복사
- 7.3 깊은 복사
- 7.4 카피-온-라이트 vs 방어적 복사
- 7.5 실전에서의 방어적 복사
안에서는 카피-온-라이트, 밖과의 경계에서는 방어적 복사 — 이 조합이 현실적인 불변성 전략이야.
현실에서는 우리 코드만 있는 게 아니거든. 레거시 코드, 서드파티 라이브러리, 다른 팀이 짠 코드처럼 카피-온-라이트를 지키는지 보장할 수 없는 코드와 상호작용해야 해. **신뢰할 수 없는 코드(untrusted code)**란 카피-온-라이트 규율을 지키는지 확신할 수 없는 코드를 말하는데, 예전에 짠 레거시 코드는 불변성 따위 신경 안 쓴 코드고, 서드파티 라이브러리는 내부 구현을 모르니 데이터를 수정할지 안 할지 모르고, 다른 팀의 코드도 카피-온-라이트를 쓴다는 보장이 없잖아. 이런 코드한테 우리의 불변 데이터를 넘기면 뭘 할지 몰라.
이 문제의 해법이 **방어적 복사(defensive copy)**야. 규칙은 두 가지인데, 신뢰할 수 없는 코드로 데이터를 보낼 때 깊은 복사본을 만들어서 보내고, 신뢰할 수 없는 코드에서 데이터를 받을 때도 받은 데이터의 깊은 복사본을 만들어서 쓰는 거야. 즉, 신뢰할 수 없는 코드와의 경계에서 들어오는 것도 복사, 나가는 것도 복사. 양방향으로 방어해.
function add_item_to_cart(cart, item) {
// 우리 코드 안에서는 카피-온-라이트 사용
var new_cart = add_item(cart, item);
// 신뢰할 수 없는 레거시 함수에 넘길 때
var cart_copy = deepCopy(new_cart); // 나갈 때 복사
legacy_update_shipping(cart_copy);
// 레거시 함수에서 받은 결과도 복사
var result = deepCopy(cart_copy); // 들어올 때 복사
return result;
}
카피-온-라이트와 방어적 복사의 조합으로 **안전한 불변성 영역(safe zone)**을 만드는 거야. 안전한 영역 안에서는 카피-온-라이트로 효율적으로, 영역 경계에서는 방어적 복사로 확실하게.
방어적 복사에는 **깊은 복사(deep copy)**가 필요해. 얕은 복사로는 안 돼 — 내부 중첩 객체가 여전히 공유되니까. 자바스크립트에서 깊은 복사하는 방법은 JSON.parse(JSON.stringify(obj))(간단하지만 함수, undefined, Date 등이 날아감), Lodash의 _.cloneDeep()(실무에서 가장 많이 쓰는 방법), structuredClone()(모던 브라우저/Node.js 지원) 같은 게 있어. 깊은 복사는 비용이 크거든. 중첩 구조가 깊고 데이터가 크면 시간과 메모리를 많이 써. 그래서 카피-온-라이트(얕은 복사)를 기본으로 쓰고, 방어적 복사(깊은 복사)는 경계에서만 쓰는 거야.
두 방식을 비교 정리하면, 카피-온-라이트는 얕은 복사 사용, 비용 적음(구조적 공유), 우리가 관리하는 코드 안에서 사용, 모든 코드가 규율을 지켜야 안전해. 방어적 복사는 깊은 복사 사용, 비용 큼, 신뢰할 수 없는 코드와의 경계에서 사용, 상대방이 뭘 하든 상관없이 안전해. 둘은 대립하는 게 아니라 보완 관계야.
방어적 복사가 실제로 쓰이는 곳들도 많아. 웹 API 호출할 때 서버에서 받은 JSON 데이터를 그대로 쓰지 말고 복사해서 쓰기, 이벤트 핸들러에서 이벤트 객체를 저장해야 할 때 복사해서 저장, 라이브러리가 넘겨주는 데이터를 콜백 밖에서 쓸 때 복사하는 것 같은 거지. 저자가 강조하는 것 — 방어적 복사를 어디서나 쓰면 성능이 죽어. 꼭 필요한 경계에서만 써야 하고, 그 경계를 잘 파악하는 게 설계 능력이야. 사실 많은 언어와 프레임워크가 이미 방어적 복사를 쓰고 있거든. Erlang은 프로세스 간 메시지 전달할 때 깊은 복사를 하고, 자바의 Collections.unmodifiableList()도 비슷한 맥락이야. 새로운 개념이 아니라 이미 검증된 패턴이라는 거지.
정리
7장 읽고 기억할 거 세 가지:
- 방어적 복사 = 경계에서 깊은 복사. 신뢰할 수 없는 코드로 보낼 때도, 받을 때도 복사
- 안에서는 카피-온-라이트, 밖과의 경계에서는 방어적 복사. 이 조합이 현실적 전략
- 깊은 복사는 비싸다. 그래서 경계에서만 쓰고, 내부에서는 얕은 복사(구조적 공유)로 효율적으로