어플리케이션이 커질수록, 상태가 어떻게 정리되고 컴포넌트간에 어떻게 데이터가 흐르는지에 대해 더욱 의도적으로 생각하면 도움이 돼요. 불필요하고 중복된 상태는 버그를 발생시켜요. 이번 챕터에서는 어떻게 상태를 작 구조화하고 어떻게 상태의 업데이트 로직을 지속가능하게 유지하는지 그리고 거리가 먼 컴포넌트 간에 상태를 어떻게 공유하는지를 알아볼 거예요.
콘텐츠 목록
- 상태 변화 측면에서 UI 변화를 생각하는 방법
- 상태를 잘 구조화 시키는 방법
- 컴포넌트 간 상태를 공유하기 위해 "상태를 끌어올리는" 방법
- 상태가 보존되는지 혹은 삭제되는지를 조정하는 방법
- 복잡한 상태 로직을 함수에 통합하는 방법
- "prop drilling" 없이 정보를 전달하는 방법
- 앱이 커지면 상태 관리를 확장하는 방법
Reacting to input with state | 상태로 입력창에 반응하기
리액트로는 코드에서 직접 UI를 수정하지 않아요. 예를 들어, "버튼 비활성화하기", "버튼 활성화하기", "성공 메시지 보내기" 처럼 명령을 작성할 수 없어요. 대신에, 여러분은 컴포넌트다른 시각적 상태("초기 상태", "입력 상태", "성공 상태")에 따라 주고 싶은 UI를 설명하고 사용자의 입력에 반응하여 상태 변화를 발생시켜요. 이 방법은 디자이너가 UI에 대해 생각하는 방법과 비슷해요.
리액트를 사용하여 빌드한 퀴즈 폼이 있어요. 제출 버튼을 활성화 시킬 것인지 혹은 비활성화 시킬 것인지, 성공 메시지를 보여줄 것인지를 결정하는 status
라는 상태 변수를 사용하는 방법을 확인하세요.
이 주제를 더 알아볼 준비가 되었나요?
상태 주도 마인드셋으로 상호작용에 접근하는 방법을 알고 싶다면 상태로 입력창에 반응하기 문서를 읽어보세요.
읽으러 가기
Choosing the state structure | 상태 구조 정하기
잘 구조화된 상태는 수정하고 디버깅하기 좋은 컴포넌트와 버그의 상습적 근원이 되는 컴포넌트의 차이를 만들어줘요. 가장 중요한 원칙은 상태는 불필요하고 중복되는 정보를 포함해서는 안된다는 원칙이에요. 만약 불필요한 상태가 있따면 업데이트하는 것을 잊어버리기 쉽고 버그를 유발해요.
예를 들어, 이 폼은 불필요한 상태 변수인 fullName
을 갖고 있어요.
이 상태를 제거하고 컴포넌트가 렌더링되는 동안 fullName
을 계산하여 코드를 간단하게 만들 수 있어요.
작은 변화처럼 보이겠지만 리액트 앱에서 발생하는 많은 버그가 이런 방법으로 해결돼요.
이 주제를 더 알아볼 준비가 되었나요?
버그를 피하기 위한 상태의 형태를 디자인하는 방법을 알고 싶다면 상태 구조 정하기 문서를 읽어보세요.
읽으러 가기
Sharing state between components | 컴포넌트 간 상태 공유하기
때때로 항상 같이 변하는 두 컴포넌트의 상태를 만들어야해요. 양 컴포넌트에서 상태를 지우고 가장 가까운 공통 부모로 이들을 옮긴 후 props를 통해 상태를 아래로 전달하면 이를 구현할 수 있어요. 이러한 방법을 "상태 끌어올리기" 라고 하며 리액트 코드를 작성하는 가장 일반적인 방법 중 하나에요.
이 예시에서 하 번에 하나의 패널만 활성화 되어야해요. 그러기 위해서는 개별 패널 안에서 활성화된 상태를 유지하는 대신 부모 컴포넌트는 상태를 갖고 자식 컴포넌트로 props를 지정해주어야해요.
이 주제를 더 알아볼 준비가 되었나요?
상태를 끌어올려서 컴포넌트를 동기화하는 방법을 알고 싶다면 컴포넌트 간 상태 공유하기 문서를 읽어보세요.
읽으러 가기
Preserving and resetting state | 상태 보존 및 초기화하기
컴포넌트를 리렌더링할 때, 리액트는 트리의 어떤 부분이 유지(되고 업데이트)되는지와 어떤 부분이 스크래치에서 제거되고 재생성되는지를 결정해요. 대부분의 경우에서 리액트의 자동화된 동작은 충분히 잘 작동해요. 기본적으로 리액트는 트리에서 이전에 렌더링 된 컴포넌트 트리와 "일치하는" 부분은 보존해요.
하지만 때때로 이렇게 동작하기를 바라지 않을 수도 있어요. 이 채팅 앱에서 메시지를 타이핑한 후 수신인을 바꾸면 입력창은 초기화되지 않아요. 이렇게 된다면 사용자가 잘못된 사람에게 메시지를 실수로 전달할 수도 있어요.
리액트는 기본 동작을 오버라이드하는 것을 허용하고 컴포넌트가 <Chat key={email} />
처럼 다른 key
를 전달하여 상태를 초기화하도록 강제해요. 이는 리액트에게 만약 수신인이 다르다면 새로운 데이터(와 입력창과 같은 UI)로 스크래치에서 재생성해야하는 다른 Chat
컴포넌트를 고려하라고 말해줘요. 이제 같은 컴포넌트를 렌더링 하더라도 수신인을 바꾸면 입력창이 초기화돼요.
이 주제를 더 알아볼 준비가 되었나요?
상태의 생애 주기와 상태를 제어하는 방법을 알고 싶다면 상태 보존 및 초기화하기 문서를 읽어보세요.
읽으러 가기
Extracting state logic into a reducer | 리듀서로 상태 로직 추출하기
여러 이벤트 핸들러에 여러 상태 업데이트가 뿌려진 컴포넌트는 압도적이에요. 이러한 경우에는 모든 상태 업데이트 로직을 컴포넌트 외부에서 하나의 함수로 통합하고 이를 "리듀서"라고 부르세요. 이벤트 핸들러는 사용자의 "행동"만을 지정하기 떄문이 이벤트 핸들러는 간결해져요. 파일의 맨 아래쪽에 리듀서 함수는 각 액션에 반응하여 상태가 어떻게 업데이트되는지를 지정해줘요.
이 주제를 더 알아볼 준비가 되었나요?
상리듀서 함수에서 로직을 통합하는 방법을 알고 싶다면 리듀서로 상태 로직 추출하기 문서를 읽어보세요.
읽으러 가기
Passing data deeply with context | 컨텍스트를 활용하여 데이터를 깊은 곳까지 전달하기
때때로 props를 통해 부모 컴포넌트에서 자식 컴포넌트로 정보를 전달해요. 그러나 props 전달은 많은 컴포넌트를 통해서 prop을 전달하거나 많은 컴포넌트가 동일한 정보를 필요로 할 떄는 불편해요. 컨텍스트는 명시적으로 props를 전달하지 않아도 부모 컴포넌트가 트리 안에 있는 어떤 컴포넌트에게 깊이 상관 없이 정보를 전달할 수 있도록 만들어줘요.
여기서 Heading
컴포넌트는 가장 가까운 Section
에 해당 섹션의 레벨을 요청하여 해딜 레벨을 결정해요. 각 Section
트랙은 부모 Section
에 레벨을 요청하여 자체 레벨을 추척하고 그 레벨에 1을 더해요. 모든 Section
은 props를 전달하지 않고 하위에 있는 모든 컴포넌트에 정보를 제공해요. 이는 컨텍스트를 통해서 진행돼요.
이 주제를 더 알아볼 준비가 되었나요?
props 전달의 대안으로 컨텍스트를 사용하는 방법을 알고 싶다면 컨텍스트를 활용하여 데이터를 깊은 곳까지 전달하기 문서를 읽어보세요.
읽으러 가기
Scaling up with reducer and context | 리듀서와 컨텍스트로 확장하기
리듀서는 컴포넌트의 상태 업데이트 로직을 통합시켜줘요. 컨텍스트는 정보를 다른 컴포넌트까지 전달해줘요. 여러분은 복잡한 화면의 상태를 관리하기 위해 리듀서와 컨텍스트를 함께 사용할 수 있어요.
이 방법으로 복잡한 상태를 가진 부모 컴포넌트는 리듀서로 상태를 관리할 수 있어요. 트리의 어느 깊이에 있는 다른 컴포넌트는 컨텍스트를 통하여 상태를 읽을 수 있어요. 이 컴포넌트들은 상태를 업데이트하는 액션을 디스패치할 수도 있어요.
이 주제를 더 알아볼 준비가 되었나요?
규모가 커지는 앱에서 상태 관리가 확장되는 방식을 알고 싶다면 리듀서와 컨텍스트로 확장하기 문서를 읽어보세요.
읽으러 가기
What’s next? | 다음 챕터
상태로 입력창에 반응하기부터 시작하여 이 챕터를 하나하나 읽어보세요!
또는, 만약에 이 주제가 이미 익숙하다면 해치 탈출하기를 읽는 건 어떨까요?