Redux에 대해 학습했다.
엔지니어님께서 Advanced한 도구라 짧게 학습하고 넘어가게 되었지만, 중요한 도구라고도 하셨기 때문에 개념적인 부분이라도 정리하고 넘어가려 한다.
Redux는 JavaScript 웹/앱을 위한 상태 관련 라이브러리이다.
UI(User Interface)는 컴포넌트로 이루어진 Tree로 구성된다.
컴포넌트는 props와 state 두 가지 속성을 가지는데 React 블로깅에서도 언급했듯,
props는 불변 속성(ex 인간, 여자, 남자)이고, state는 가변 속성(ex 나이, 직업)이다.
Redux를 사용하기 전, 우리는 최상단의 컴포넌트에서 state를 상속받는 아주 밑(depth)에 있는 컴포넌트에서 trigger가 발생해 state 속성값이 바뀌는 상황이 일어나면, 우리는 state 끌어올리기를 이용해 최상단 state값을 바꿔줬다.
하지만, 아주아주 복잡하게 구성되어있는 UI라면 state 끌어올리기에'만' 의지하기에는 꽤 벅찰 것이다.
Redux를 사용하면 독립된 Store에 state를 저장하고 관리할 수 있어,
어느 depth에 있는 컴포넌트라도 Store에 접근해 state를 읽을 수 있고, action을 통해 update 할 수 있게 된다. 또한, 이전 상태들이 history로 저장도 된다.
이것이 우리가 Redux를 배워야하는 이유라고 할 수 있겠다.
React와 함께 많이 사용되어 React의 하위 라이브러리라는 오해를 많이 하지만,
Redux는 컴포넌트로 이루어진 UI 구성에 사용할 수 있는, React 없이도 사용할 수 있는 독립적인 도구이다.
하지만, 이번 스프린트에서는 React를 Redux를 이용해 리팩토링 했기 때문에 React와 연관지어 얘기해야할 것 같다.
둘은 서로 독립적이라는 것만 잘 알아두자!
Redux의 작동 방식 (출처 : Redux 공식 홈페이지)
Action은 Store로 보내지게 되는 데이터 묶음이다.
추가될 새로운 state값이 들어있는 단순 JS객체로 작성되어야 하며, 어떤 형태의 액션이 실행될지 나타내는 type 속성값은 필수 포함, 나머지 값들은 사용자 정의에 따른다. 또한, 필요에 따라 액션 생성 함수를 만들어 쓰면 편리하다.
기본적인 액션의 권장 구조는 아래처럼 정의되어 있다.
{
type: 'ADD_TODO', // 필수
payload: { // 사용자 정의
text: 'Do something.'
}
}
dispatch는 생성된 액션을 Reducer로 보내는 메서드이다. 액션을 첫 번째 인자로 담아 전달된다.
dispatch(action); // --> Reducer가 받는 action으로 보냄
Reducer는 이전 상태와 액션을 받아서 새로운(update) 상태를 반환하는 순수 함수이다. 순수 함수처럼 작동 해야하기 때문에 Reducer는 side effect 금지, API 호출 금지, 인수 변경 금지!
이 3가지는 Reducer 내에서 절대 금지된다.
그리고 Reducer는 세팅 카테고리에 따라 여러 개를 만들어서 사용가능 하다.
한 번 user정보를 세팅하는 Reducer를 만들어보자.
const userInfoReducer = function(state = initialState, action) {
switch (action.type) {
case SET_USERNAME:
return Object.assign({}, state, { // 여기서, state를 직접 변경하지 않았다. 빈 객체에 state를 복사해서, currentUser만 새 값으로 덮어씌웠다. 절대 원래 state를 직접 변경하면 안된다. 이유는 Redux의 3가지 원칙에서 알 수 있다.
currentUser: { name: action.name }
})
case SET_USERGRADE:
return Object.assign({}, state, {
userGrade: { grade: action.grade }
})
...
default:
return state;
}
};
이렇게 위의 흐름으로, Redux를 사용하면 각 컴포넌트들에서 action을 날려(dispatch), 각각의 reducer로 새 state를 갱신하는 단일 Store를 운영할 수 있게된다.
어느 하위 컴포넌트들이든 Store에 접근해 state의 값들을 읽을 수 있으니 복잡한 App을 운영할 수록 필수적인 라이브러리가 될 것이다.
1. Single source of truth
: Redux는 단일 저장소(Store)를 가진다. 클래스 컴포넌트에서처럼 트리거를 추적해 state를 끌어올릴 필요가 없다.
2. State is read-only
: state는 읽기 전용이다. action를 이용한 Reducer로만 갱신을 해야한다. 절대 개인의 컴포넌트가 직접 state를 update해서는 안된다.
3. Changes are made with pure functions
: Reducer를 통한 state 변경은 순수 함수처럼 되어야한다. 위의 Reducer 예시에서 주석부분을 살펴보자.
이전 state를 직접적으로 변경하지 않고(history이기 때문), 복사를 해와서 덮어쓰는 방식이어야 한다.
React와 Redux를 학습했다면, 뭐가 무엇이 Presentational하고, 무엇이 Container인지 감이 올 것이다.
말 그대로 Presentational Component(event, DOM조작)는 화면에 보여지는 것에 집중하는 것이고,
Container Component는 그 Component가 어떤 동작(상태관리)을 하는지에 대한 기능을 관리한다.
컴포넌트 하나의 파일에 두 개의 성질이 섞여있으면 엄청나게 복잡하고 가독성이 떨어지게 된다.
파일을 따로 구분해서, 각 컴포넌트들의 Presentational과 Container를 분리해서 구축하는 것이 유지보수에 도움이 될 것이다.
이렇게 Redux 환경에 대한 개념들을 간단하게나마 정리해보았다. React로 구축한 파일을 Redux를 이용해 Container Component를 구분해서 리팩토링하는 것은 쉬운 작업이 아니었다. TT
class Component에 state를 만들어 관리를 하는 것보다 훨씬 복잡한 작업이었지만, 하나의 웹 서비스를 구축한다고 생각하면 수 백, 수 천개의 Component들이 생성될텐데 그렇다면 상태관리 라이브러리는 필수조건이 될 수 밖에 없다는 것을 피부로 느낄 수 있었다.
비록 이번에는 Redux를 다룰 수 있는 날이 짧았지만, 프로젝트를 시작하면 충분히 학습하고 사용해 볼 수 있을 것 같다. 잘 다룰 수 있으면 정말 좋은 친구가 될 수 있다. 😂