https://ko.reactjs.org/docs/thinking-in-react.html
목업으로 시작하기
JSON API와 목업을 받았다고 가정하면 다음과 같을 것이다.
[
{category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
{category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
{category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
{category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
{category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
{category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
];
Step 1. Component 계층 구조로 나누기
처음으로 할 일은 모든 컴포넌트의 주변에 박스를 그리고 그 각가에 이름을 붙이는 것이다. 디자이너와 함께 일한다면, 이것들을 이미 정해두었을 수도 있다. 디자이너의 Photoshop 레이어 이름이 React 컴포넌트의 이름이 될 수 있다.
객체 지향 프로그래밍 원칙 중, 단일 책임 원칙이 있다.
- 모든 클래스는 하나의 책임만 가지며, 클래스는 그 책임을 완전히 캡슐화해야 한다.
- 즉, 하나의 컴포넌트는 한 가지의 일만 하게끔 모듈화하라는 뜻이다.
- 만약 하나의 컴포넌트가 커지게 된다면, 이는 보다 작은 하위 컴포넌트로 분리되어야 한다.
주로 JSON 데이터를 유저에게 보여주기 때문에, 데이터 모델이 적절하게 만들어졌다면, UI가 잘 연결될 것이다.
위의 그림은 다섯 개의 컴포넌트로 이루어진 앱이다.
- FilterableProductTable(노란색): 예시 전체를 포괄한다.
- SearchBar(파란색): 모든 유저의 입력을 받는다.
- ProductTable(연두색): 유저의 입력을 기반으로 데이터 콜렉션을 필터링 해서 보여준다.
- ProductCategoryRow(하늘색): 각 카테고리의 헤더를 보여준다.
- ProductRow(빨간색): 각각의 제품에 해당하는 행을 보여준다.
- FilterableProductTable
- SearchBar
- ProductTable
- ProductCategoryRow
- ProductRow
Step 2. React로 정적인 버전 만들기
컴포넌트 계층 구조가 만들어졌다면, 데이터 모델을 가지고 UI 렌더링만 되는 정적인 버전을 만들어보는 것이다. 정적인 버전을 만들기 위해 state는 사용할 필요가 없다. state는 오직 상호작용을 위해, 즉 시간이 지남에 따라 데이터가 바뀌는 것에 사용한다. 정적인 버전을 만들 때는 필요하지 않다.
Top-Bottom & Bottom-Up
앱을 만들 때 하향식(Top-Bottom)이나 상향식(Bottom-Up)으로 만들 수 있다. 간단한 프로젝트라면 하향식으로 만드는게 쉽지만 프로젝트가 커지면 상향식으로 만들고 테스트를 작성하면 개발하기에 더 쉽다.
Step 3. UI에서 최소한의 State
UI를 상호작용하게 만드려면 데이터 모델을 변경할 수 있는 방법이 있어야 한다. React에서는 state를 통해서 변경한다.
최소한의 state를 찾고, 나머지 모든 것들이 필요에 따라 state를 그때그때 계산되도록 만들어야 한다.
예시) 애플리케이션 내 데이터
- 제품의 원본 목록
- 유저가 입력한 검색어
- 체크박스의 값
- 필터링 된 제품들의 목록
위 목록을 살펴보고 어떤 것이 state가 되어야 하는지 살펴보자. 이는 아래의 세 가지 질문을 통해 결정할 수 있다.
- 상위에서부터 props를 통해 전달 되는가? -> X
- 시간이 지나도 변하지 않는가? -> X
- 컴포넌트 안의 다른 state나 props를 가지고 계산 가능한가? -> X
위 세 가지 질문 중 하나라도 X라는 대답이 나온다면 그것은 state가 아니다.
- 제품의 원본 목록: 부모로부터 props를 통해 전달되므로 state가 아니다.
- 유저가 입력한 검색어, 체크박스의 값: props로 전달되지 않는 값이며, 시간이 지남에 따라 변하기도 하며, 다른 것들로부터 계산이 되지 않으므로 state가 맞다.
- 필터링 된 제품들의 목록: 제품의 원본 목록과 검색어, 체크박스의 값을 가지고 계산 가능하므로 state가 아니다.
Step 4. state가 어디에 있어야 할지 찾기
state로 정할 데이터를 찾았으면, 어떤 컴포넌트가 state를 변경하거나 소유할지 찾아야 한다.
우선 공통 소유 컴포넌트를 찾아야 한다. 계층 구조 내에서 특정 state가 있어야 하는 모든 컴포넌트들의 상위에 있는 하나의 컴포넌트를 말한다. state를 소유할 적절한 컴포넌트를 찾지 못하였다면, state를 소유하는 컴포넌트를 하나 만들어서 공통 오너 컴포넌트의 상위 계층에 추가해야 한다.
- FilterableProductTable(노란색) 이 공통 소유 컴포넌트라고 할 수 있다.
- FilterableProductTable(노란색) 이 검색어와 체크박스의 체크 여부를 state로 가지는 것이 의미상으로도 맞다.
// FilterableProductTable
constructor() {
super();
this.state = {
filterText: '',
isStockOnly: false,
}
} // 애플리션의 초기 상태 반영
const { filterText, isStockOnly } = this.state;
<ProductTable filterText={filterText} isStockOnly={isStockOnly} />
<SearchBar filterText={filterText} isStockOnly={isStockOnly} />
React는 항상 컴포넌트 계층구조를 따라 아래로 내려가는 단방향 데이터 흐름을 따른다. 어떤 컴포넌트가 어떤 state를 가져야 하는지 바로 결정하기 어려울 수 있다.
아래 과정을 따라 결정해 보자.
- state를 기반으로 렌더링하는 모든 컴포넌트를 찾자.
- 공통 소유 컴포넌트를 찾자.
- 공통 혹은 더 상위에 있는 컴포넌트가 state를 가져야 한다.
Step 5. 역방향 데이터 흐름
계층 구조 하단에 있는 폼 컴포넌트에서, 가장 상위에 있는 공통 컴포넌트의 state를 업데이트 할 수 있다.
우리는 보통 사용자의 입력을 반영할 수 있도록 state를 업데이트 하기 원한다. 컴포넌트는 그 자신의 state만 변경할 수 있기 때문에, FilterableProductTable은 SearchBar에 props로 자기의 메서드를 전달하고, SearchBar는 그 응답으로 인자를 전달해 줄 것이다. 보통 <input>에 onChange 이벤트를 사용할 수 있다. FiterableProduct에서 전달된 콜백은 setState()를 호출하고 앱이 업데이트 될 것이다.