render 메서드가 의존해야 할 첫 번째 데이터 소스, props.
팀은 이 개념을 어떻게 개발자들에게 명확하게 전달할지 논의하기 시작했다. props는 'properties(속성들)'의 줄임말이었지만, 그 이름만으로는 그 역할과 제약을 충분히 설명하기 어려웠다.
조던 워크는 XHP 시절의 컴포넌트를 예시로 들었다.
// XHP 컴포넌트 사용 예시
<ui:avatar imageUrl={$user->avatarUrl} size="small" />
“XHP에서 우리는 컴포넌트에 데이터를 전달하기 위해 XML 어트리뷰트(attribute), 즉 속성을 사용했습니다. imageUrl과 size가 바로 그것이죠. 리액트의 props는 바로 이 개념을 자바스크립트 세계로 가져온 것입니다.”
그는 React.createElement 함수를 다시 한번 짚었다.
// React.createElement(컴포넌트, props객체, ...자식들)
React.createElement(Avatar, { imageUrl: user.avatarUrl, size: 'small' });
“createElement 함수의 두 번째 인자가 바로 props 객체입니다. 우리는 여기에 컴포넌트에 전달하고 싶은 데이터를 키-값 쌍으로 담아 넘겨주면 됩니다. 그러면 리액트는 이 객체를 자식 컴포넌트 내부로 전달해주고, 자식 컴포넌트는 this.props라는 이름으로 그 데이터에 접근할 수 있게 되는 거죠.”
이 구조는 데이터가 위에서 아래로 흐른다는 단방향 데이터 흐름의 원칙을 코드로 구현한 것이었다. 부모 컴포넌트가 createElement를 통해 자식 컴포넌트를 만들 때, 필요한 데이터를 props라는 꾸러미에 담아 함께 내려보내는 것이다.
맷이 질문했다.
“그럼 <Avatar> 컴포넌트는, 자신을 렌더링한 부모가 누구인지, 또는 그 imageUrl 데이터가 원래 어디서 왔는지 전혀 알 수 없겠네요?”
“바로 그겁니다.” 조던이 강조했다. “<Avatar> 컴포넌트에게 props는 그냥 하늘에서 뚝 떨어진 선물 같은 겁니다. 누가, 왜 줬는지는 알 필요도, 알 수도 없죠. 그저 주어진 props를 보고 자신의 render 메서드를 실행할 뿐입니다. 이것이 컴포넌트의 독립성과 재사용성을 보장하는 핵심적인 장치입니다.”
크리스는 백본과의 차이점을 생각하며 고개를 끄덕였다.
“백본 뷰는 생성될 때 model 자체를 통째로 주입받는 경우가 많았습니다. 그래서 뷰가 모델의 모든 데이터에 접근하고, 심지어 모델의 메서드를 호출할 수도 있었죠. 하지만 리액트의 props는 부모가 자식에게 필요한 데이터만 명시적으로 골라서 전달하는 방식이군요. 훨씬 더 통제되고 예측 가능하네요.”
props는 부모와 자식 컴포넌트 사이의 명확한 계약(Contract)이었다.
부모는 자식에게 필요한 데이터를 props로 전달할 것을 약속하고, 자식은 props로 받은 데이터만 사용하여 자신을 렌더링할 것을 약속한다. 이 계약 덕분에, 개발자는 어떤 컴포넌트를 사용할 때 그 컴포넌트의 render 함수를 들여다볼 필요 없이, 어떤 props를 넘겨줘야 하는지만 확인하면 되었다.
팀은 props의 개념을 명확히 정의했다.
props는 부모 컴포넌트가 자식 컴포넌트에게 전달하는 데이터이다.props는 자식 컴포넌트 내부에서this.props를 통해 읽을 수 있는 읽기 전용(read-only) 객체이다.
이 ‘읽기 전용’이라는 마지막 규칙은 매우 중요했다. 그것은 리액트가 스탠다드 ML로부터 물려받은 ‘불변성’ 철학을 구현하는 첫 번째 단추였기 때문이다. props의 정체가 명확해지자, 팀의 다음 논의는 자연스럽게 이 불변성 원칙으로 이어졌다.


