props는 절대 바꿀 수 없다 (불변성)

512025년 10월 05일4

"props는 읽기 전용이다."

이 한 문장의 규칙은 팀 내에서 작은 논쟁을 불러일으켰다. 실용성을 중시하는 몇몇 엔지니어들은 이 제약이 너무 과도하다고 느꼈다.

"이해는 합니다만, 이건 너무 빡빡한 규칙 아닙니까?" 크리스가 문제를 제기했다. "때로는 자식 컴포넌트에서 받은 props를 약간 가공해서 사용해야 할 때가 있습니다. 예를 들어, props로 숫자 값을 받아서, 그 값에 10을 더한 결과를 화면에 표시해야 한다면요?"

그는 화이트보드에 코드를 적었다.

// 안티 패턴 (잘못된 사용법)
var MyComponent = React.createClass({
  render: function() {
    // 받은 props를 직접 수정하려는 시도
    this.props.value = this.props.value + 10; 
    
    return React.createElement('div', null, this.props.value);
  }
});

"이런 코드를 원천적으로 막는다는 건가요? 개발자의 편의성을 너무 해치는 것 같은데요."

조던 워크가 그의 질문에 대답했다.
"그 코드는 리액트가 가장 피해야 할 안티 패턴입니다. 그 이유는 단 하나, 예측 가능성을 파괴하기 때문입니다."

그는 크리스가 작성한 코드의 문제점을 조목조목 짚었다.
"만약 MyComponentthis.props.value를 직접 수정했다고 가정해 봅시다. 이 컴포넌트를 렌더링한 부모 컴포넌트는 그 사실을 전혀 알지 못합니다. 부모는 자신이 value: 5를 전달했다고 굳게 믿고 있지만, 실제로는 자식이 그 값을 멋대로 15로 바꿔버린 거죠."

그는 더 심각한 상황을 가정했다.
"만약 다른 형제 컴포넌트도 같은 props 객체를 공유하고 있다면 어떻게 될까요? MyComponent가 일으킨 예기치 않은 부작용(Side Effect)이, 아무 상관없는 형제 컴포넌트의 렌더링 결과에까지 영향을 미치게 됩니다. 우리는 다시 ‘데이터가 어디서 바뀌었지?’를 추적하는 지옥으로 돌아가게 되는 겁니다."

스탠다드 ML에서 깊은 영감을 받은 조던에게 불변성은 타협할 수 없는 원칙이었다. 데이터는 한 곳, 즉 데이터를 소유한 부모 컴포넌트에서만 변경되어야 했다. 자식은 그저 전달받은 데이터를 화면에 표시하는 역할에만 충실해야 했다.

"자식 컴포넌트가 받은 props를 가공해야 한다면, props 자체를 수정하는 대신, 새로운 지역 변수를 만들어서 사용하면 됩니다."

그는 코드를 올바르게 수정했다.

// 올바른 사용법
var MyComponent = React.createClass({
  render: function() {
    // props를 수정하는 대신, 새로운 변수에 가공된 값을 담는다.
    var processedValue = this.props.value + 10; 
    
    return React.createElement('div', null, processedValue);
  }
});

"이러면 this.props.value 원본은 그대로 유지되면서, 원하는 결과를 안전하게 얻을 수 있습니다. 부작용이 전혀 없죠."

props의 불변성 원칙은 팀 내에서 공식적으로 확립되었다. 리액트는 기술적으로 this.props 객체를 얼려서(freeze) 수정을 막는 등의 강제적인 조치를 취하지는 않기로 했다. 그것은 성능에 미미한 영향을 줄 수 있었기 때문이다. 대신, 이것을 모든 리액트 개발자가 반드시 지켜야 할 가장 중요한 ‘규약’으로 삼기로 했다.

'순수 함수는 입력값을 수정하지 않는다.'
함수형 프로그래밍의 이 기본 원칙이 리액트의 props 시스템에 그대로 녹아든 것이다. 컴포넌트는 props를 입력으로 받아 버추얼 DOM을 출력하는 순수 함수와 같아야 했다.

이 엄격한 규칙은 리액트 애플리케이션에 놀라운 안정성을 가져다줄 터였다. 데이터의 흐름은 언제나 하향식으로 투명하게 유지될 것이고, 개발자는 더 이상 보이지 않는 곳에서 데이터가 오염될 것을 걱정하지 않아도 되었다.

이제 props에 대한 정의는 완벽해졌다. 다음 과제는 이 불변성의 원칙을 뚫고, 컴포넌트가 스스로의 상태를 '변경'해야 하는 불가피한 상황들을 어떻게 다룰 것인가에 대한 문제였다.