데이터와 디자인을 컴포넌트로 구성하기

1
2
3
4
5
6
7
8
[
{ "category": "과일", "price": 1920, "stocked": true, "name": "사과" },
{ "category": "과일", "price": 1640, "stocked": true, "name": "두리안" },
{ "category": "과일", "price": 2480, "stocked": false, "name": "아보카도" },
{ "category": "채소", "price": 1700, "stocked": true, "name": "시금치" },
{ "category": "채소", "price": 2800, "stocked": false, "name": "호박" },
{ "category": "채소", "price": 1330, "stocked": true, "name": "완도콩" }
]

design

위와 같이 데이터와 디자인 시안이 제시되었을 때, 컴포넌트를 어떻게 구성하는 것이 좋을지 생각해본다.

UI와 데이터는 주로 동일한 모양을 갖기 때문에 데이터 구조와 컴포넌트 구조가 자연스럽게 매핑된다.

그러므로 다음과 같이 컴포넌트를 구성할 수 있을 것이다.

component

1
2
3
4
5
FilterableProductTable
├── SearchBar
└── ProductTable
├── ProductCategoryRow
└── ProductRow

폴더 구조는 위와 같이 구성할 수 있을 것이다.

이상적인 컴포넌트 설계는 한가지만 책임하는 것이다. 만약 한가지 이상의 처리가 필요하다면 컴포넌트를 더 작은 컴포넌트로 나눠야한다.

React 정적 컴포넌트 구성

컴포넌트에 대한 구상이 끝났으므로 React로 앱을 빌드해보자.

이 때 가장 간단한 접근법은 사용자와의 상호작용은 고려하지 않고 데이터 모델에서 UI를 렌더링 하는 버전으로 작성하는 것이다.

데이터 모델을 렌더링하는 앱을 만들고자 한다면 props와 state를 사용하고 싶을 것이다. 하지만 현재 단계에서는 state를 사용하지 않는다.
왜냐하면 state는 사용자와 상호작용할 때 사용하기 때문에 아직 단계가 아니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import PRODUCTS from "../../api/products.json";

function FilterableProductTable() {
const products = [...PRODUCTS];

return (
<div className="filterableProductTable">
<SearchBar />
<ProductTable products={products} />
</div>
);
}

export default FilterableProductTable;

정적 빌드된 컴포넌트는 JSX만을 반환한다. 그리고 컴포넌트 트리 최상위 컴포넌트에서 데이터 모델을 하위 props로 전달하는 “단방향 데이터 흐름”을 구현한다.

컴포넌트 UI 대화형으로 만들기

UI를 대화형으로 만들려면 사용자가 기본 데이터를 변경할 수 있도록 해야한다. 이러한 상호작용이 필요할 때, state를 사용한다.

state는 앱이 기억하고 변경해야 할 데이터의 최소 집합이라고 생각할 수 있다.

이 때 상태를 구성하는 가장 중요한 원칙은 DRY(Don’t Repeat Yourself)이다. state는 최소한으로만 필요해야하며 나머지는 필요할 때 다시 고민한다.

그렇다면 위 컴포넌트에서는 state는 무엇이 될 수 있을까?

state와 props 구분하기

1. 시간이 지남에도 변경이 없다면?

=> 이는 state가 아니다. state는 사용자와 상호작용해야하는 요소이기 때문이다.

2. props를 통해 상위 컴포넌트로 부터 전달될 수 있다면?

=> 이는 state가 아니다.

3. 컴포넌트의 기존 state 또는 props를 기반으로 계산할 수 있다면?

=> 이는 state가 아니다.

그러므로 위 예시에서 state는 검색 텍스트 값과 체크박스 값이 될 수 있다.

앱 상태 관리 하기

자 이제 앱이 가지는 최소한의 state를 파악했다. 그러면 이 state를 변경하고 소유하는 컴포넌트는 누가 할 것인지를 정해줘야한다.

React는 단방향 데이터 흐름을 사용하기 때문에 상위 컴포넌트에서 하위 컴포넌트로 데이터가 전달된다.

컴포넌트 상태 관리 전략

  1. state를 기반으로 무언가를 렌더링하는 모든 컴포넌트 확인한다.
  2. 해당 컴포넌트와 가장 가까운 공통 상위 컴포넌트를 찾는다.

이제 state를 어디서 관리할지 정해줘야한다. 경우의 수는 3가지가 있다.

  • state를 공통 상위 컴포넌트에서 관리
  • state를 공통 상위 컴포넌트보다 상위에서 관리
  • state를 소유하는 것이 타당한 컴포넌트를 찾지 못한 경우, state를 유지하기 위한 새로운 컴포넌트 만들고 공통 상위 컴포넌트 보다 위 어딘가에 추가하여 관리

위 예시에서 검색 텍스트 값과 체크박스 값이 state라고 하였다.

컴포넌트 상태 관리 단계

  1. 상태를 사용하는 컴포넌트 식별
  • SearchBar - 검색 텍스트 state 관리
  • ProductTable - 체크박스 유무 state 관리
  1. 공통 상위 컴포넌트 찾기

SearchBar, ProductTable 의 공통 상위 컴포넌트는 FilterableProductTable 컴포넌트이다.

  1. state가 있는 위치 결정

공통 상위 컴포넌트인 FilterableProductTable 컴포넌트에서 관리한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import PRODUCTS from "../../api/products.json";
import SearchBar from "./SearchBar/SearchBar";
import ProductTable from "./ProductTable/ProductTable";
import { useState } from "react";

function FilterableProductTable() {
const products = [...PRODUCTS];

const [searchText, setSearchText] = useState("");
const [inStockOnly, setInStockOnly] = useState(false);

const handeChangeSearchText = (newSearchText: string) =>
setSearchText(newSearchText);

const handeChangeInStockOnly = (inStockOnlyState: boolean) =>
setInStockOnly(inStockOnlyState);

return (
<div className="filterableProductTable">
<SearchBar
searchText={searchText}
inStockOnly={inStockOnly}
onChangeSearchText={handeChangeSearchText}
onChangeInStockOnly={handeChangeInStockOnly}
/>
<ProductTable
products={products}
searchText={searchText}
inStockOnly={inStockOnly}
/>
</div>
);
}

export default FilterableProductTable;

해당 컴포넌트에서 state를 관리하고 props로 전달하는 것은 완료했으니 state를 관리하는 컴포넌트에서 state 변경함수까지 props 전달하여 사용자가 UI를 업데이트 할 수 있도록 구현해주면 완성이다.

댓글 공유

  • page 1 of 1

loco9939

author.bio


author.job