React
가 상태 관리를 위한 라이브러리는 아니지만 상태 관리의 주요 원칙을 배우면 컴포넌트 간 서로 느슨하게 결합된(loose coupled), 구조적으로 아름다운 코드를 작성 가능
UI, 프론트엔드 개발에서 상태는 "동적으로 표현되는 데이터"
상태를 다룰 때에 주요 고려대상인 Side Effect
는 "함수의 입력 외에도 함수의 결과에 영향을 미치는 요인"
대표적으로 네트워크 요청, API 호출이 Side Effect
Side Effect
를 최대한 배제하는 컴포넌트 우선 개발 방식은 어떤 데이터가 들어오는지 상관하지 않고 설사 데이터가 가짜 데이터라 할지라도 컴포넌트는 표현(presentation) 그 자체에 집중하는 것
로컬 상태는 특정 컴포넌트 안에서만 관리되는 상태
다른 컴포넌트와 데이터를 공유하지 않는 폼(form) 데이터(input box, select box 등과 같이 입력값을 받는 경우)는 대부분 로컬 상태
전역 상태는 프로덕트 전체 혹은 여러가지 컴포넌트가 동시에 관리하는 상태
다른 컴포넌트와 상태를 공유하고 영향을 끼치는 상태
서로 다른 컴포넌트가 동일한 상태를 다룬다면 출처는 오직 한 곳이어야 함
=> '전역 공간'
데이터 무결성을 위해 동일한 데이터는 항상 같은 곳에서 데이터를 가지고 오도록 해야함
Single source of truth(신뢰할 수 있는 단일 출처) 원칙은 프론트엔드 뿐만 아니라 다양한 곳에서 언급되는 원칙
전역 상태 관리 예시 : 브라우저 테마 / 언어 / 히스토리(Undo/Redo) 등
상태 관리를 위한 각종 툴 : React Context
, Redux
, Mobx
=> 전역 상태 저장소 제공, Props drilling(프로퍼티 내려꽂기) 이슈 해결
Redux
란 JS
에서 예측 가능한 상태 관리를 할 수 있게 해주는 컨테이너이며 React
없이도 사용할 수 있는 상태 관련 라이브러리
Redux
는 컴포넌트와 상태를 분리하는 패턴
=> 상태 변경 로직을 컴포넌트로부터 분리하면 표현에 집중한 보다 단순한 함수 컴포넌트로 만들 수 있다.
Action
은 어떤 액션을 취할 것인지 정의해 놓은 객체
{ type: ‘ADD_TO_CART’, payload: request }
type
은 필수로 지정을 해주어야 하며 그 외의 것들은 선택적으로 사용 가능
모든 변화를 action
을 통해 취하는 것은 우리가 만드는 앱에서 무슨 일이 일어나고 있는지 직관적으로 알기 쉽게 하는 역할
Dispatch
는 Action을 전달하는 메소드
dispatch
의 전달인자로 Action 객체가 전달되고 reducer
를 호출해 state
의 값을 바꾸는 역할
state
가 관리되는 오직 하나뿐인 저장소의 역할
const store = createStore(rootReducer)
미들웨어와 Redux devtools 지원을 위해 두번째 인자에 추가적인 내용 작성 가능
현재의 state
와 Action
을 사용해서 새로운 state
를 만들어 내는 pure function(순수 함수)
Reducer
의 Immutability(불변성)=> Redux
의 state
업데이트는 immutable한 방식으로 변경해야함
=> 변경된 state
를 로그로 남기기 위해서 꼭 필요한 작업
=> Object.assign()
을 통해 새로운 객체를 만들어 리턴
shallow equality checking을 위해서 주소값 비교(성능, 시간복잡도)
상태가 관리되는 오직 하나의 공간 => store
simple js object (action)
store에게 app에 필요한 데이터를 운반해주는 역할
상태는 불변(읽기 전용) 데이터이며 오직 액션 만이 상태 교체 가능
reducer
는 순수 함수
현재 상태와 action
을 사용해 다음 상태를 만들어 냄
action
객체가 dispatch
에 전달되고 reducer
를 호출해서 새로운 state
를 생성
상태를 예측 가능하게 만들어 줌
유지 보수에 용이함
디버깅에 유리 (action과 state log 기록 시)
Redux dev tools 사용
순수 함수를 사용하기 때문에 테스트를 붙이기 쉬움
useSelector()
는 컴포넌트와 state를 연결하는 역할
=> 컴포넌트에서 useSelector
메소드를 통해 store의 state에 접근 가능
useSelector
의 전달인자로는 콜백 함수를 받으며 콜백 함수의 전달인자로는 state 값이 들어감
useDispatch()
는 Action 객체를 Reducer로 전달해주는 메소드
Action이 일어나는 곳은 클릭 등의 이벤트가 일어나는 컴포넌트
리덕스에서 비동기 작업을 처리할 때는 redux-thunk
라는 미들웨어를 많이 사용
비동기 액션 생산자는 상태(비동기 요청 시작/완료/실패 등)에 따라 동기 액션 생산자를 호출해줌
비동기 액션 생산자는 리듀서로 연결되지 않고 직접 dispatcher
를 통해 스토어로 새로운 상태를 보내줌
함수를 dispatch할 때에는 해당 함수에서 dispatch
와 getState
를 파라미터로 받아와야함
=> 이 함수를 만들어주는 함수를 우리는 thunk라고 부름
동기 액션 | 비동기 액션 | |
---|---|---|
구현 방법 | 액션 생산자를 만들어 액션 객체를 리턴 | 액션을 구현한 후 dispatch를 사용해 다른 동기 액션 호출 |
Presentational 컴포넌트는 어떻게 보여지는가에 초점을 맞춘 컴포넌트 => props
Container 컴포넌트는 어떻게 동작하는가에 초점을 맞춘 컴포넌트 => useSelector
Presentational | Container | |
---|---|---|
기능 | 어떻게 보여지는가 | 어떻게 동작하는가 |
Redux와 연관성 | 없음 | 있음 |
Read data | Props에서 data를 읽음 | Redux의 State에 접근(useSelector) |
Change data | Props에서 콜백을 호출 | Redux Action |