현재 나는 학원 동료들과 간단한 웹앱을 react(이하 리액트)로 만들려고 하고 있다. 그러다 보니 리액트로는 어떻게 설계를 해야 되고 어떤 구조로 프로젝트를 만들어야 하는지 궁금증이 생겼다. 그래서 이곳 저곳 블로그를 보다가 일단 공부를 좀 더 하자 하고 공식문서를 봤는데 'React로 사고하기' 페이지가 있었다. 쭉 읽어봤는데 처음 리액트를 시작할 때 도움이 될 거 같아 간단하게 나마 블로그로 남긴다.
구현하고자 하는 UI를 컴포넌트 계층으로 나눈다. 컴포넌트는 어떻게 나눠야 될까?
<출처> 리액트 공식문서 이미지
위에 색깔네모로 그려저 있는 것이 컴포넌트라고 보면 된다.
저것들을 각각 컴포넌트로 만들어야 될 거 같다.
단순히 UI(정적 버전)를 만든다. 정적인 UI를 만들 때는 타이핑이 많이 필요하고 고민은 크게 필요하지 않은 반면, 사용자와의 상호작용 기능을 추가할 때는 타이핑보다 고민이 많이 필요하다고 공식문서가 그런다ㅎㅎ
정적 버전을 만들기 위해서는 재사용되는 components를 만들고 props를 이용해서 데이터를 전달해야 한다고 한다. props는 부모에서 자식으로 데이터를 전달하는 방식이고 정적 버전을 만들 때는 state를 아예 사용하지 말라고 한다! 뒤에 가면 state를 최소화하라고 나온다. state가 바뀔 때마다 재렌더링되니 비용이 비싸서 그런 거 같다.
또 큰 컴포넌트부터 만들 것인지 작은 컴포넌트부터 만들 것인지 정해야 하는데 간단한 예시는 하향식(큰 컴포넌트부터, top-bottom)으로 진행하는 것이 더 쉽고, 대규모 프로젝트에서는 상향식(작은 컴포넌트부터, bottom-top)으로 진행하는 것이 더 쉽다고 한다. 나는 평소 하향식이 익숙하나 공식문서의 조언대로 이번엔 상향으로 해봐야겠다.
또 재사용 가능한 컴포넌트를 갖게 되는데 데이터는 맨 위에 있는 컴포넌트가 prop으로 전달한다고 한다. 아마 위에 나와 있는 것처럼 단일 책임을 주려고 그러는 거 같다. (흠.. 이러면 뭔가 prop chain이 발생할 거 같은데.. 해결방법을 어디서 본 거 같은데 찾아봐야겠다..!)
state를 앱이 기억해야 하는 최소한의 변화하는 데이터의 집합으로 보라고 한다. state를 계획할 때 가장 중요한 원칙은 DRY(Don't Repeat Yourself) 라고 한다. 사용할 수 있는 최소한으로 state를 사용하고 state를 이용해 계산할 수 있는(활용할 수 있는) 데이터들은 state로 두지 말라는 말인 거 같다. 예를 들어, 게시판에서 게시글을 state배열로 두면 게시글의 개수는 state로 두는 것이 아니라 게시글배열.length를 이용해 구하면 된다.
아래는 공식문서 예시가 너무 좋아서 직접 인용하겠다.
이제 이 예제 애플리케이션의 모든 데이터 조각을 생각해 보세요:
- 제품의 원본 목록
- 사용자가 입력한 검색어
- 체크박스의 값
- 필터링된 제품 목록
다음 중 어떤 것이 state인가요? 그렇지 않은 것을 식별합니다:
- 시간이 지나도 변하지 않나요? 그렇다면 state가 아닙니다.
- 부모로부터 props를 통해 전달되나요? 그렇다면 state가 아닙니다.
- 컴포넌트의 기존 state 또는 props를 가지고 계산할 수 있나요? 그렇다면 당연히 state가 아닙니다!
남은 것이 아마도 state일 것입니다.
다시 한 번 하나씩 살펴봅시다:
- 제품 원본 목록은 props로 전달되었으므로 state가 아닙니다.
- 검색어는 시간에 따라 바뀌고 다른 것으로부터 계산할 수 없으므로 state로 볼 수 있습니다.
- 체크박스의 값은 시간에 따라 바뀌고 다른 것으로부터 계산할 수 없으므로 state로 볼 수 있습니다.
- 필터링된 제품 목록은 원본 목록으로부터 검색어 및 체크박스 값을 조합하여 계산할 수 있으므로 state가 아닙니다.
즉, 검색어와 체크박스의 값만 state입니다! 멋지네요!
💡 다 읽고 나니 뭔가 사용자의 의해 데이터가 바뀔 때 state를 쓰는 것 같다.
props와 state 차이
state를 최소한으로 정했다면 state가 어느 컴포넌트에 있어야 할 지 정해야 한다. 아직 처음이라 익숙하지 않을 수 있지만 다음 단계를 따르면 이해할 수 있을 거라고 한다.
위의 공식문서의 UI를 참고하며 다음 글을 읽어보자.
이제 state 값은 FilterableProductTable에 있습니다.
위의 상태대로라면 부모에서 state를 관리하고 자식에게 props로 데이터를 전달해주는 단방향 구조다. 그러나 자식에서 props 데이터가 변경되었을 때 state를 업데이트 해야 하므로 역방향으로도 데이터가 흐를 수 있게 해줘야 한다.
현재 setState 함수는 부모만 갖고 있으므로 자식에게 props로 전달해준다.
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
{/* 아래 setState 함수들을 전달해준다. */}
return (
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly}
onFilterTextChange={setFilterText}
onInStockOnlyChange={setInStockOnly} />
자식은 이 함수를 받아서 사용자에 의해 값이 바뀔 때마다 state를 업데이트해준다.
<input
type="text"
value={filterText}
placeholder="Search..."
onChange={(e) => onFilterTextChange(e.target.value)} />
여기가 'React로 사고하기' 공식문서 끝이다.
여기서 눈여겨봐야할 점은
1. UI를 컴포넌트로 쪼개기
2. state는 최소한으로
3. state관리는 같이 쓰이는 자식들의 부모가, 없다면 부모를 만들어서 거기서 관리하고 state, setState는 props로 자식에게 전달한다.
이 정도일 거 같다. 공식문서가 잘 되어있어서 읽기도 좋고 재밌다ㅎㅎ 개발하다가 중간중간 돌아와서 보면 도움이 많이 될 거 같다!!