[React] 리액트로 사고하기

ryeoni·2022년 8월 22일

React

목록 보기
2/9

개요

리액트를 사용할 때 올바른 방향으로 하고있는지 더 나은 방법은 없는지
의구심이 들었는데 예전에 읽으려고 했던 공문을 발견했다.
왜 이제야 봤는지 조금 후회가 됐지만.. 앞으로 개발하면서 참고하기 위해 요약해보았다!


본문


목업으로 시작하기

  • JSON API와 목업을 받았을 때 목업은 다음과 같다.
  • 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"}
    ];


1단계: UI를 컴포넌트 계층 구조로 나누기

단일 책임 원칙(SRP: Single Responsibility Principle)

  • 컴포넌트를 나눌 때 하나의 컴포넌트는 한 가지 일을 하는 게 이상적이다.
  • 하나의 컴포넌트가 커지게 된다면 보다 작은 하위 컴포넌트로 분리되어야 한다.

  1. FilterableProductTable(노란색) : 전체를 포괄한다.
  2. SearchBar(파란색) : 모든 유저의 입력을 받는다.
  3. ProductTable(연두색) : 유저의 입력을 기반으로 데이터 콜렉션을 필터링 해서 보여준다.
  4. ProductCategoryRow(하늘색) : 각 카테고리의 헤더를 보여준다.
  5. ProductRow(빨강색) : 각각의 제품에 해당하는 행을 보여준다.

  • ProductTable 을 보면 테이블 헤더만을 가진 컴포넌트는 없다.
  • 이 예시에서는 데이터 컬렉션이 렌더링을 일부이기 때문에 남겨뒀지만 헤더가 복잡해지면 (정렬을 위한 기능을 추가하는 등) ProductTableHeader 컴포넌트를 만드는 것이 더 합리적이다.

  • FilterableProductTable
    • SearchBar
    • ProductTable
      • ProductCategoryRow
      • ProductRow


2단계: 리액트로 정적인 버전 만들기

Thinking In React: Step 2

  • 정적 버전은 UI는 렌더링되지만 아무 동작도 없는 버전이다.
  • 다른 컴포넌트를 재사용하는 컴포넌트를 만들고 props를 이용해 데이터를 전달한다.
  • state는 데이터가 바뀌는 것에 사용하기 때문에 정적 버전은 state를 사용하지 않는다.
  • 앱을 만들 때 하향식(top-down)이나 상향식(bottom-up)으로 만들 수 있다.
    • 계층 구조의 상층부에 있는 컴포넌트(FilterableProductTable) 부터 만들거나 하층부에 있는 컴포넌트(ProductRow) 부터 만들 수 있다.
    • 간단한 예시에서는 하향식으로 만드는 게 쉽지만 프로젝트가 커지면 상향식으로 만들고 테스트를 작성하면서 개발하기가 더 쉽다.

React’s one-way data flow (단방향 데이터 흐름)

  • 위 단계가 끝나면 데이터 렌더링을 위해 만들어진 재사용 가능한 컴포넌트들의 라이브러리를 가지게 된다.
  • 현재는 정적 버전이기 때문에 render() 메서드만 가지고 있다.
    • 계증구조의 최상단 컴포넌트(FilterableProductTable)는 prop으로 데이터 모델을 받는다.
    • 데이터 모델이 변경되면 ReactDOM.render()를 다시 호출해서 UI가 업데이트 된다.


3단계: UI state에 완전하고 최소한의 표현 찾아내기

중복배제원칙(Don't repeat yourself)

애플리케이션에서 필요로 하는 가장 최소한의 state를 찾고 이를 통해 나머지 모든 것들이 필요에 따라 그때그때 계산되도록 만든다. 예를 들어 TODO 리스트를 만든다면, TODO 아이템을 저장하는 배열만 유지하고 TODO 아이템의 개수를 표현하는 state를 별도로 만들지 않는다. (배열의 길이를 가져오면 되기 때문)

예시 애플리케이션은 다음과 같은 데이터를 가지고 있다.

  • 제품의 원복 목록
  • 유저가 입력한 검색어
  • 체크박스의 값
  • 필터링 된 제품들의 목록

아래 세 가지 중 하나에 해당되면 state가 아니다.

  1. 부로로부터 props를 통해 전달되는 경우
  2. 시간이 지나도 변하지 않는 경우
  3. 컴포넌트 안의 다른 state나 props를 가지고 계산이 가능한 경우

예시 애플리케이션의 state는 다음과 같다.

  • 유저가 입력한 검색어
  • 체크박스의 값

제품의 원본 목록은 props를 통해 전달되며 필터링 된 목록은 다른 것들로부터 계산될 수 있기 때문에 state가 아니다.


4단계: state가 어디에 있어야 할 지 찾기

Thinking In React: Step 4

  1. state를 기반으로 렌더링하는 모든 컴포넌트를 찾는다.
  2. 공통 소유 컴포넌트를 찾는다. (계층 구조 내에서 특정 state가 있어야 하는 모든 컴포넌트들의 상위에 있는 하나의 컴포넌트).
  3. 공통 혹은 더 상위에 있는 컴포넌트가 state를 가져야 한다.
  4. state를 소유할 적절한 컴포넌트를 찾지 못하였다면, state를 소유하는 컴포넌트를 하나 만들어서 공통 소유 컴포넌트의 상위 계층에 추가한다.

  • ProductTable 은 state에 의존한 상품 리스트의 필터링해야 하고 SerarchBar 는 검색어와 체크박스 상태를 표시해야 한다.
  • 공통 소유 컴포넌트는 FilterableProductTable 이며 검색어와 체크 여부 state를 가지는 것이 타당하다.
    1. this.state = {filterText: '', inStockOnly: false} 를 FilterableProductTable의 constructor에 추가하여 초기 상태를 반영한다.
    2. filterTextinStockOnlyProductTableSearchBar에 prop으로 전달한다.
    3. 이 props를 이용하여 ProductTable의 행을 정렬하고 SearchBar의 폼 필드 값을 설정한다.

5단계: 역방향 데이터 흐름 추가하기

Thinking In React: Step 5

  • 이전 단계까지는 계층 구조 아래로 흐르는 props와 statae의 함수로써 앱을 만들었다.
  • 이제 계층 구조 하단에 있는 폼 컴포넌트에서 FilterableProductTable 의 state를 업데이트할 수 있어야 한다.
  • 이전 단계에서 input 태그의 value 속성이 항상 props와 동일하도록 설정했기 때문에 체크하거나 타이핑할 경우 입력이 무시된다.
  • 사용자가 폼을 변경할 때마다 사용자의 입력을 반영할 수 있도록 state를 업데이트하려면 컴포넌트는 그 자신의 state만 변경할 수 있기 때문에 FilterableProductTableSearchBar 에 콜백을 넘겨서 state가 업데이트되어야 할 때마다 호출되도록 한다.
  • FilterableProductTable에서 전달된 콜백은 setState()를 호출하고 앱이 업데이트될 것이다.

참고

React로 사고하기

profile
기록하는 습관 ✏️ 공유하고 싶은 정보들 🔎

1개의 댓글

comment-user-thumbnail
2022년 8월 24일

👍

답글 달기