React로 앱을 설계하는 방법

박은정·2021년 8월 23일
0

프로젝트

목록 보기
2/34

React

자바스크립트로 규모가 크고 빠른 웹 어플리케이션을 만드는 가장 좋은 방법
React를 이용해서 페이스북과 인스타그램을 만들었다

목업으로 시작하는데 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"}
];

React로 앱을 설계하는 방법

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

하위 컴포넌트를 비롯한 모든 컴포넌트의 주변에, 박스 및 각각의 이름을 붙인다
디자이너의 Photoshop Layer 이름 = React Component 이름

단일 책임 원칙으로 함수나 객체를 만들 때처럼 컴포넌트화 처리를 한다

단일 책임 원칙

하나의 컴포넌트는 한 가지 일을 하는 것이 이상적이라는 원칙이다
이러한 원칙으로, 하나의 컴포넌트가 커지게 된다면 → 보다 작은 하위 컴포넌트로 분리해야 한다

UI와 데이터 모델이, 동일한 Infomation Architecture을 가지는 경향이 있기 때문에
주로 JSON 데이터를 유저에게 보여주기 때문에 데이터 모델이 적절하게 만들어졌다면,
UI(컴포넌트 구조)가 잘 연결될 것이다

Infomation Architecture (=정보 구조도)

서비스 목차 역할
1. 웹 혹은 애플리케이션이 어떻게 구성되는지 보여주며
어떤 기능의 화면으로 보여주는지를 전체적으로 보여주는 도구
2. 이를 통해, 개발자와 디자이너가 편하게 작업할 수 있도록 만든 문서이다
3. 기존 웹 사이트처럼 복잡한 구조에서 사이트의 틀을 짜고
콘텐츠 구성 및 디자인, 개발의 일정관리도 통해서 진행하도록 도와준다
4. 최근 모바일 서비스가 많아지면서 한 페이지가 다수의 페이지 역할을 대신하면서
개발 페이지의 목록의 역할이 강해지는 추세이다
출처 : https://plavement.tistory.com/27

각 컴포넌트가 데이터 모델의 한 조각을 나타내도록 분리해야 한다

이처럼 5개의 컴포넌트로 이루어진 앱이 있다고 하면

  1. FilterableProductTable(노란색)
    예시 전체를 포괄한다

  2. SearchBar(파란색)
    모든 유저의 입력(user input)을 받는다.

  3. ProductTable(연두색)
    유저의 입력(user input)을 기반으로 데이터 콜렉션(data collection)을 필터링 해서 보여준다.

  4. ProductCategoryRow(하늘색)
    각 카테고리(category)의 헤더를 보여준다.

  5. ProductRow(빨강색)
    각각의 제품(product)에 해당하는 행을 보여준다.

이 중 3번 ProductTable을 보면
“Name” 과 “Price” 레이블을 포함한 테이블 헤더만을 가진 컴포넌트는 없는데
이 때, 데이터를 위한 독립된 컴포넌트를 생성할지의 여부는 선택이다

ProductTable의 책임인 데이터 컬렉션이 랜더링의 일부이기 때문에
ProductTable을 남겨두었지만
정렬을 위한 기능을 추가하는 등, 이 헤더가 복잡해진다면
ProductTableHeader 컴포넌트를 새로 만드는 것이 합리적이다

이렇게 목업을 통해 컴포넌트를 확인했으므로
이를 계층구조로 나열해보면 아래와 같이 나타낼 수 있다

* FilterableProductTable
	* SearchBar
    * ProductTable
    	* ProductCategoryRow
        * ProductRow

2. React로 정적인 버전 만들기

컴포넌트 계층구조가 만들어졌으니 → 앱을 실제로 구현해야 한다
가장 쉬운 방법으로는, 데이터 모델을 가지고 UI를 렌더링은 되지만 아무 동작이 없는(=정적인) 버전을 만들 수 있다
이처럼 과정을 나누는 것이 좋은데
정적 버전을 만드는 것은 → 생각은 적게 필요하지만 타이핑은 많이 필요로 하고
상호작용을 만드는 것은 → 생각은 많이 해야 하지만 타이핑은 적게 필요로 한다

데이터 모델을 렌더링하는 앱의 정적 버전을 만들기 위해
다른 컴포넌트를 재사용하는 컴포넌트를 만들고
props를 이용해서 데이터를 전달한다

props는 부모가 자식에게 데이터를 넘겨줄 때 사용할 수있는 방법이고
state는 시간이 지남에 따라 데이터가 바뀌는 것처럼 상호작용을 위해 사용하는 것이기 때문에
정적 버전을 만들 때에는 state를 만들면 안된다

따라서 우리는 정적인 버전을 만드는 것이기 때문에 state는 필요없다


앱을 만들 때 하향식(top-down)이나 상향식(botton-up)으로 만들 수 있다

계층구조의 상층부에 있는 컴포넌트 FilterableProductTable 부터 만들면 하향식
계층구조의 하층부에 있는 컴포넌트 ProductRow부터 만들면 상향식

간단한 예시로는 보통 하향식으로 만드는 것이 쉽지만,
프로젝트가 커지면 상향식으로 만들고 테스트를 작성하면서 개발하기 더 쉽다


이 단계가 끝나면 데이터 렌더링을 위해 만들어진, 재사용이 가능한 컴포넌트의 라이브러리를 가지게 된다
현재는 앱의 정적 버전이기 때문에 → 컴포넌트는 render() 메서드만을 가지고 있을 것이다

계층구조의 최상단 컴포넌트 FilterableProductTable 는 prop으로 데이터 모델을 받는다
데이터 모델이 변경되면 ReactDOM.render() 메서드를 다시 호출해서 UI가 업데이트 된다
따라서 UI가 어떻게 업데이트되고 어디에서 변경해야 할지 알 수 있다

React의 단방향 데이터 흐름(=단방향 바인딩)은 모든 것을 모듈화 하고 빠르게 만들어 준다

단방향 데이터 흐름 (=단방향 바인딩)

one-way-data flow, one-way binding
1. 부모로부터 자식으로만 데이터 전송이 가능하다
2. 유지보수를 좀 더 편리하게 해준다
예) 운전이 익숙하지 않은 초보운전자에게는 중앙선을 침범할 수 없고, 모두가 같은 방향으로 달리기 때문에 일방통행 차선이 좀 더 운전하기 수월하다
3. 가상 돔(Vitual DOM)과 궁합이 잘 맞는다
: 뷰를 통째로 바꾸는 역할을 하기 때문에 단방향 데이터 방식은 DOM을 갱신할 때 편하다
4. 단순한 데이터 흐름은 이해하기 쉽고 관리하기 좋다

3. UI state에 대한 최소한으로 완전하게 표현 찾아내기

UI를 상호작용하게 만들려면 기반 데이터 모델을 변경할 수 있는 방법이 있어야 하는데
이를 React는 state를 통해 변경한다

애플리케이션을 올바르게 만들기 위해서는
애플리케이션에서 필요로 하는, 변경 가능한 state 의 최소 집합을 중복배제원칙을 가지고 생각해야 한다

애플리케이션이 필요호 하는 가상 최소한의 state를 찾고,
이를 통해 나머지 모든 것들이 필요에 따라 그때그때 계산되도록 만든다

예를 들어 TODO 리스트를 만든다고 하면
TODO 아이템을 저장하는 배열만 유지하고
TODO 아이템의 개수를 표현하는 state를 별도로 만들지 말고
대신 TODO 아이템 배열의 길이를 가지고 오면 된다


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

애플리 케이션에 이러한 데이터들이 있다고 가정하면
아래 3가지 질문을 통해 어떤 데이터가 state가 되어야 하는지 결정할 수 있다

  1. 부모로부터 props가 전달되는가?
    -> Yes : 확실히 state가 아니다
    -> No

  2. 시간이 지나도 변하는가?
    -> Yes : 확실히 state가 아니다
    -> No

  3. 컴포넌트 안의 다른 state나 props를 가지고 계산이 가능한가?
    -> Yes : 확실히 state가 아니다
    -> No

이러한 질문을 토대로 분류를 하자면

  • 제품의 원본 목록
    -> state 아님 : props를 통해 전달된다

  • 유저가 입력한 검색어
    -> state 맞음 : 시간의 지남에 따라 변하면서 다른 것들로부터 계산될 수 없다

  • 체크박스의 값
    -> state 맞음 : 시간의 지남에 따라 변하면서 다른 것들로부터 계산될 수 없다

  • 필터링 된 제품들의 목록
    -> state 아님 : 제품의 원본 목록검색어, 체크박스 의 값을 조합해서 계산된다

따라서 애플리케이션은 2개의 state만을 가지게 된다

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

4. state가 어디에 있어야 할지 찾기

어떤 컴포너트가 state를 변경하거나 소유할지 찾아야 한다

React는 항상 컴포넌트 계층구조를 따라 아래로 내려가는 단방향 데이터 흐름을 따르기 때문에

애플리케이션이 가지는 각각의 state에 대해서
1. state를 기반으로 렌더링하는 모든 컴포넌트를 찾고
2. 공통 소유 컴포넌트 common owner component를 찾아보는데
: 계층 구조 내에서 특정 state가 있어야 하는 모든 컴포넌트들의 상위에 있는 하나의 컴포넌트
3. 공통 혹은 더 상위에 있는 컴포넌트가 state를 가져야 한다
4. state를 소유할 적절한 컴포넌트를 찾지 못했다면,
state를 소유하는 컴포넌트를 하나 만들어서 → 공통 오너 컴포넌트의 상위 계층에 추가해야 한다

이러한 전략을 애플리케이션에 적용해서 FilterableProductTable에 state를 두기로 했다

ProductTable : state에 의존한 상품 리스트의 필터링을 해야 하고
SearchBar : 검색어와 체크박스의 상태를 표시해야 한다
FilterableProductTable : 공통 소유 컴포넌트
의미상으로도 FilterableProductTable 이 검색어와 체크박스의 체크 여부를 가지는 것이 제일 타당하다
  1. 아래와 같은 인스턴스 속성을 FilterableProductTable의 constructor에 추가하여 애플리케이션의 초기상태를 반영한다
this.state = {filterText: '', inStockOnly: false}
  1. filterText, inStockOnly → ProductTable, SearchBar에 props로 전달한다

  2. props로 전달된 filterText, inStockOnly을 이용해서
    ProductTable의 행을 정렬하고, SearchBar의 폼 필드 값을 설정한다

  3. 애플리케이션의 동작 확인할 수 있다
    filterText = 'ball'로 설정하고 앱 새로고침

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

지금까지는 하향식으로 props나 state의 함수로 앱을 만들었지만

이제는 다른 방향의 데이터 흐름을 만들어본다
계층 구조의 하단에 있는 폼 컴포넌트에서 → FilterableProductTable의 state를 업데이트 한다

React
전통적인 양방향 데이터 바인딩과 비교하면 더 많은 타이핑을 필요로 했지만
데이터 흐름을 명시적으로 보이게 만들어서 → 프로그램이 어떻게 동작하는지 파악할 수 있게 도와주웠다

input요소의 value 속성이 항상 FilterableProductTable 에서 전달된 state와 동일하게 설정했기 때문에
위에서 만든 상태에서 inlut box를 체크하거나 타이핑할 때 React가 입력을 무시하는 것을 확인할 수 있다


우리는 사용자가 폼을 변경할 때마다 사용자의 입력을 반영할 수 있도록
state를 업데이트하기를 원하는데

컴포넌트는 그 자신의 state만 변경할 수 있기 때문에

FilterableProductTable 는 SearchBar 에 콜백을 넘겨서
state가 업데이트되어야 할 때마다 호출되도록 할 것이다

그래서 input요소에 onChange 이벤트를 사용해서 알림을 받을 수 있고
FilterableProductTable 에서 전달된 콜백은 setState()을 호출하고 앱이 업데이트 된다


이전보다 더 많은 타이핑을 해야 할 수 있지만 우리는 코드를 쓸 일보다 읽을 일이 더 많다

  • 모듈화되고 명시적인 코드는 읽을 때 덜 어려워서 큰 컴포넌트 라이브러리를 사용할 때 많이 유용하고
  • 코드 재사용성을 통해 코드 라인이 줄어들 것이다
profile
새로운 것을 도전하고 노력한다

0개의 댓글