Thinking in React

Bewell·2023년 8월 28일
1
post-thumbnail

들어가기

  • React는 디자인을 바라보는 방식, 앱을 빌드하는 방식을 바꿀 수 있다
  • React눈 사용자 인터페이스를 빌드 할때 먼저 컴포넌트 라는 조각으로 분해하고, 서로 다른 시각적 상태를 기술한다
  • 마지막으로 컴포넌트를 서로 연결해 데이터가 흐르도록 한다



지금부터 React에서 UI를 구현하는 일반적인 5단계를 설명한다


Step1. UI를 컴포넌트 계층 구조로 나누기

  • 목업의 모든 컴포넌트와 하위 컴포넌트 주위에 상자를 그리고 이름을 지정
    • 프로그래밍 => 단일 책임 원칙으로 컴포넌트는 이상적으로는 한 가지 일만 수행해야 한다. 만약 컴포넌트가 늘어나면 더 작은 하위 컴포넌트로 분해해야 한다
    • CSS => 클래스 선택자를 만들 때 무엇을 위해 만들 것인지 생각해보자
    • 디자인 => 디자인 레이어를 어떻게 구성할지 고려해보자
  • JSON이 잘 구조화 되어 있으면 UI에 자연스럽게 매핑된다



  • FilterableProductTable (회색)에는 전체 앱이 포함됩니다.
  • SearchBar (파란색)는 사용자 입력을 수신합니다.
  • ProductTable (보라색)은 사용자 입력에 따라 목록을 표시하고 필터링합니다.
  • ProductCategoryRow (녹색)는 각 카테고리에 대한 제목을 표시합니다.
  • ProductRow (노란색)는 각 상품에 대한 행을 표시합니다.

(취향에 따라 ProductTableHeader 컴포넌트도 추가할 수 있다)




이제 목업에서 컴포넌트를 식별했으므로 계층 구조를 정렬한다

- FilterableProductTable
	- SearchBar
	- ProductTable
		- ProductCategoryRow
		- ProductRow








Step2. React로 정적인 UI 만들기

  • 컴포넌트 계층 구조가 완성되었으니 이제 앱을 구현

  • 가장 간단한 접근은 상호작용을 추가하지 않고 데이터 모델에서 UI렌더링 하는 버전을 만드는 것
    정적 버전을 먼저 만든 다음 상호작용(interactivity)을 별도로 추가하는것이 쉽다

  • 앱의 정적 버전을 만들기 위해서는 다른 컴포넌트를 재사용하는 component를 만들고 props를 사용하여 데이터 전달

  • state는 상호작용, 시간이 지남에 따라 변하는 데이터에만 사용!

  • 하향식 : 상위 컴포넌트 -> 하위 컴포넌트 -> 간단한 예시에 좋고
  • 상향식 : 하위 컴포넌트 -> 상위 컴포넌트 -> 대규모 프로젝트에 좋다
function ProductCategoryRow({ category }) {
  return (
    <tr>
      <th colSpan="2">
        {category}
      </th>
    </tr>
  );
}

function ProductRow({ product }) {
  const name = product.stocked ? product.name :
    <span style={{ color: 'red' }}>
      {product.name}
    </span>;

  return (
    <tr>
      <td>{name}</td>
      <td>{product.price}</td>
    </tr>
  );
}

function ProductTable({ products }) {
  const rows = [];
  let lastCategory = null;

  products.forEach((product) => {
    if (product.category !== lastCategory) {
      rows.push(
        <ProductCategoryRow
          category={product.category}
          key={product.category} />
      );
    }
    rows.push(
      <ProductRow
        product={product}
        key={product.name} />
    );
    lastCategory = product.category;
  });

  return (
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Price</th>
        </tr>
      </thead>
      <tbody>{rows}</tbody>
    </table>
  );
}

function SearchBar() {
  return (
    <form>
      <input type="text" placeholder="Search..." />
      <label>
        <input type="checkbox" />
        {' '}
        Only show products in stock
      </label>
    </form>
  );
}

function FilterableProductTable({ products }) {
  return (
    <div>
      <SearchBar />
      <ProductTable products={products} />
    </div>
  );
}

const PRODUCTS = [
  {category: "Fruits", price: "$1", stocked: true, name: "Apple"},
  {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"},
  {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"},
  {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"},
  {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"},
  {category: "Vegetables", price: "$1", stocked: true, name: "Peas"}
];

export default function App() {
  return <FilterableProductTable products={PRODUCTS} />;
}
  • 데이터가 최상위 컴포넌트에서 트리 하단에 있는 컴포넌트로 흘러내리기 때문에 이를 단방향 데이터 흐름이라고 한다









Step3. 최소한의 완전한 UI state 찾기

  • UI 데이터 모델을 변경하기 위해 state를 사용하자
  • state를 앱이 기억해야 하는 최소한의 변화하는 데이터 집합으로 생각하기
  • state를 구조화할 때 가장 중요한 원칙은 DRY (Dont Repeat Yourself)
  • 앱에서 필요한 최소한의 state를 파악하고 그외의 모든 값은 필요할 때 계산하자



그럼 이 앱의 모든 데이터 조각을 생각해보고, 그중 무엇이 state인지 식별해보자

1. 제품의 원본 목록
2. 사용자가 입력한 검색어
3. 체크박스의 값
4. 필터링된 제품 목록

state가 되지 않는 조건
1. 시간이 지나도 변하지 않나요?
2. 부모로부터 props를 통해 전달되나요?
3. 컴포넌트의 기존 state 또는 props를 가지고 계산할 수 있나요?

1. 제품의 원본 목록
	=> 제품의 원본 목록은 props로 전달되었으므로 state가 아닙니다.
2. 사용자가 입력한 검색어
	=> 시간에 따라 변하기 때문에 state입니다.
3. 체크박스의 
	=> 시간에 따라 바뀌고, 다른 것으로 부터 계산할 수 없으므로 state입니다
4. 필터링된 제품 목록
	=> 원복 목록으로 부터 검색어 및 체크박스 값을 조합하여 계산할 수 있으므로 state가 아닙니다

즉 검색어와 체크박스의 값만 state이다











Step4. state가 어디에 있어야 할지 파악하기

  • React컴포넌트는 부모 -> 자식 컴포넌트로 아래로 내려가는 단방향 데이터 흐름이기 때문에, state를 소유하는 컴포넌트를 잘 식별해야 한다
1. 해당 state를 기반으로 렌더링하는 모든 컴포넌트를 찾자
2. 가장 가까운 공통 상위 컴포넌트, 즉 계층상 그 state의 영향을 받는 모든 컴포넌트들의 위에 있는 컴포넌트를 찾자
3. state가 어디 위치할지 결정하자
4. 대게 공통 부모 state를 그대로 둘 수 있다
5. 혹은 공통 부모보다 더 상위 컴포넌트에 state를 둘 수 있다
6. state를 소유할 적절한 컴포넌트가 없다면, 상위에 추가하자




  1. state를 사용하는 컴포넌들 식별하기
  • ProductTable은 state(검색어 및 체크박스 값)으로 필터링 해야한다
  • SearchBar은 해당 state(검색어 및 체크박스 값)을 표시해야 한다
  1. 공통 부모 찾기
  2. state를 어디 둘지 결정하기
//	두 개의 state 변수를 추가
function FilterableProductTable({ products }) {
  const [filterText, setFilterText] = useState('');
  const [inStockOnly, setInStockOnly] = useState(false);
//	filterText와 inStockOnly를 ProductTable과 SearchBar에 props로 전달
<div>
  <SearchBar 
    filterText={filterText} 
    inStockOnly={inStockOnly} />
  <ProductTable 
    products={products}
    filterText={filterText}
    inStockOnly={inStockOnly} />
</div>










Step5. 역방향 데이터 흐름 추가하기

  • react의 state데이터는 계층 구조 아래로 흐르며 올바르게 렌더링된다
  • 사용자 입력에 따라 state를 변경하기 위해서는 역방향으로도 데이터가 흘러야 한다
  • state를 업데이트 할 수 있는 hook을 하위 컴포넌트로 전달한다










느낀점

  • 디자인시스템 selector에서 props로 받는 option값들을 조작을 위해 state로 변경했는데, 위 이론에 근거하면 잘못된 방식이다.
    왜 state로 했는지, 그랬을때 발생할 수 있는 이슈, 그리고 props로 변경한다면 어떻게 동작할지 고민이 필요하다

참고: https://react-ko.dev/learn/thinking-in-react#start-with-the-mockup

0개의 댓글