주요 내용으로 건너뛰기

React 앱을 만드는 흐름

- React 공식 페이지의 Thinking in React 번역

0. Start With a Mock

디자이너들로부터 받은 샘플과 JSON API가 미리 갖춰져 있다고 생각해보자.

그것들은 다음과 같다.



디자이너의 샘플
디자이너의 샘플



1. Break The UI Into A Component Hierarchy


 UI === 하나의 컴포넌트!
 UI === 하나의 컴포넌트!


React에서 UI는 하나의 컴포넌트이다. 그리고 하나의 컴포넌트는 하나의 기능만을 수행해야 한다. 즉, 하나의 컴포넌트가 필요 이상  으로 많은 일들을 하게 된다면 이는 그 컴포넌트를 다수의 하위 컴포넌트로 쪼개야함을 의미한다. 그리고 컴포넌트를 나누는 과정은  포토샵에서 레이어처리를 하는 것과 비슷한데, 이는 하나의 레이어가 디자인에서 최소 단위가 되듯이 하나의 컴포넌트도 기능적인 면에서  최소 단위이기 때문이다.  이제 샘플을 살펴보자.


이 간단한 앱에는 다섯 가지 컴포넌트가 있음을 알 수 있다. 이탤릭체는 각 컴포넌트가 다루는 데이터를 의미한다.

FilterableProductTable (orange)

- 앱의 전체를 포함하는 컴포넌트

SearchBar (blue)

-유저의 입력값을 전달받는 컴포넌트

ProductTable (green)

-유저의 입력값을 바탕으로 테이블을 필터링하는 컴포넌트

ProductCategoryRow (turquoise)

- 각 카테고리의 제목을 나타내는 컴포넌트

ProductRow (red)

 -각 제품의 열을 나타내는 컴포넌트


샘플에서 컴포넌트들을 추출해냈으니 이제 앱의 전체 구조를 짜보자.

굉장히 간단하게도 샘플에서 내부에 있는 컴포넌트가 child 컴포넌트가 된다.

전체 구조는 이런 식일 것이다.

FilterableProductTable

    -SearchBar

    -ProductTable

         --ProductCategoryRow

         --ProductRow


2. Build A Static Version in React


React는 State와 Props를 통해 흐름을 관리한다. 이 말은 적절하게 그것들이 적절하게 설계되지 않으면 개발자가 의도한 대로 움직이지 않을  수도 있고 비효율적인 데이터 흐름을 초래할 수도 있다는 말이다. 그렇다면 그것들을 적절하게 통제하기 전에 해야 할 일은  

데이터 흐름이 거의 없는 정적인 버전의 컴포넌트들을 만드는 일일 것이다. 일단 정적인 컴포넌트들이 갖춰지고 밑그림이 갖춰지면 그 때서야

state와 props를 통해 데이터 흐름을 통제하는 일을 시작할 수 있다.


3. Identify The Minimal (but complete) Representation Of UI State


React에서 props와 state를 정하는 것을 간단한 TODO앱을 통해 살펴보면


먼저 State가 될 수 있는 후보군을 살펴보자.


1. The original list of products 기존의 할 일 리스트  

2. The search text the user has entered 유저가 입력한 키워드

3. The value of the checkbox 체크박스 값

4. The filtered list of products 키워드를 통해 필터링 된 리스트


State가 되려면 세 가지 조건을 만족해야 한다.


Is it passed in from a parent via props? If so, it probably isn't state.

1. 부모 컴포넌트로부터 props의 형태로 전달되는가? 만약 그렇다면 state가 아니다.


Does it remain unchanged over time? If so, it probably isn't state.

2. 계속해서 변하지 않는가? 그렇다면 이것도 state가 아니다.


Can you compute it based on any other state or props in your component? If so, it isn't state.

3. 컴포넌트의 다른 state나 props를 통해 계산가능한가? 그렇다면 state가 아니다.


기존의 할 일 리스트는 props를 통해 넘겨질 수 있기 때문에 state가 아니다.

반면 유저가 입력한 키워드나 체크박스 값은 수시로 변하며 컴포넌트의 다른 state나 props를 통해 계산되지 않는다.

그러므로 state가 된다고 할 수 있다.

키워드를 통해 필터링 된 리스트는 충분히 계산 가능하므로 역시 state가 아니다.


그러므로 state는 두 개가 된다.


4. Identify Where Your State Should Live


이제 state가 결정되었다. 그렇다면 이 state를 어느 컴포넌트에 배치해야 되는가?


각 state들을 살펴보자.


1. 먼저 그 state를 이용해서 렌더링되는 모든 컴포넌트를 확인하자.


2. 이제 common owner component를 확인하자.  


3. common owner가 state를 가질 수도 있고 아니면 더 상위의 부모 컴포넌트가 state를 가질 수도 있다.  


4. 만약 어떤 컴포넌트가 state를 가져야할 지 감이 오지 않는다면 common owner 컴포넌트 상위에 state를 가지는 컴포넌트를 만들자.


이제 ㅅ에서 state를 가질 common owner를 가려보자.


1. ProductTable은 productlist를 state를 이용해 필터해야하고, SearchBar는 키워드를 보여주고 체크 상태를 보여주기 위해 state가 필요하다.


The common owner component is FilterableProductTable.

2. common owner는 FilterableProductTable이 된다.


It conceptually makes sense for the filter text and checked value to live in FilterableProductTable

3. filter text와 checked value가 FilterableProductTable 안에 state로서 존재한다는 것이 가장 타당할 것이다.


이제 state가 있어야하는 컴포넌트를 정했으므로 initial state를 정하자.  

this.state = {filterText: '', inStockOnly: false} to FilterableProductTable's constructor

이런 식으로 filterText는 빈 스트링으로, checked value는 inStockOnly 라는 이름의 boolean으로 저장했다.

마침내 이 props들을 이용해 ProductTable을 필터링하고 SearchBar 폼의 값을 보여주게 될 것이다.


5. Add Inverse Data Flow


이전의 앱은 이런 구조를 가지고 있었다.

반면 지금은 이런 구조를 가지고 있다.


이전의 앱은 state가 FileterableProductTable에만 있었기 때문에 유저가 인풋 값에 값을 입력하려고 하거나 체크박스 값을 변경하려해도 최초의 state인 빈 스트링과 빈 체크박스만 렌더링했다.
그러나 이제 constructor를 통해 props를 받아오고  FilterText와 InStockInput의 변경에 대한 이벤트를 핸들하고 바인드하며, 부모 컴포넌트 내부에서는 SearchBar 컴포넌트에서 전해주는 값을 통해 state값이 업데이트 되게 이벤트를 핸들하고 바인드함으로써 자연스럽게 둘이 연결된다.
그럼으로써 유저의 입력값이 앱 전체에 반영된다.

And That's It

이 글이 react를 통해 컴포넌트와 앱을 만드는 것이 어떤 것인지에 대한 아이디어를 제공할 수 있다면 좋겠다.
물론 react way로 코드를 짜는 일이 예전보다 더 수고스러울 수 있지만 코드는 쓸 때보다 읽을 때가 더 많으며 이렇게 모듈화되어 있고, 명시적인 코드는 굉장히 읽기 쉬움을 감안할 필요는 있을 것이다.
여러 컴포넌트들로 이루어진 거대한 라이브러리들을 만들기 시작하면서, 당신은 아마 이러한 명시성과 모듈성에 감사할 것이다. 또한 코드를 재사용함으로써 전체 코드의 양도 감소할 것이다.

이준형 님의 창작활동을 응원하고 싶으세요?

댓글