Redux 도입이 필요해진 이유
학교에서 준 외주 프로젝트를 하고 있는데, 프론트엔드로 리액트를 사용했다. 처음엔 GraphQL 서버로 필요한 값을 요청하고, 받고, 그 결과를 적당한 컴포넌트에 넘겨주고, 컴포넌트는 또 다른 컴포넌트에 넘겨주는 방식으로 만들고 있었다. 부모 컴포넌트에서 자식 컴포넌트로 값이던 함수던 넘기는 것은 어렵지 않았다. 하지만 자식 컴포넌트에서 부모 컴포넌트로 뭔가를 보낼 때 문제가 생기고, Context API를 사용하게 된다. 지금 프로젝트는 꽤 많은 상태를 오직 GraphQL 서버를 통해 받아서 가공해준다. 관리할 상태가 더 많아지니 스트레스는 더 늘어갔다. Redux를 도입할 필요성을 느꼈다.
사실 규모가 크거나 로직이 복잡한 프로젝트가 아니면 오직 GraphQL에서 상태를 관리할 수 있고, 아니면 Context API를 통해 충분히 해결할 수 있다. 하지만 한계가 있다. 그 때 리덕스를 도입하는 순간 상태 관리가 정말 쉬워진단다. 노가다로 컴포넌트끼리 상태 관리를 한번 해봤으니 공부해서 새로운 기술을 넣어보는 것도 충분한 가치가 있다고 생각한다..
리덕스의 3가지 규칙
리덕스를 사용하면서 꼭 지켜줘야 할 규칙이 3가지 있다.
- 하나의 애플리케이션 안에는 하나의 스토어만 있다.
- 상태는 읽기 전용이다.
- 변화를 일으키는 함수, 리듀서는 순수한 함수여야 한다.
3번에서 “순수한 함수”라는 말은 동일한 인풋 -> 동일한 아웃풋이어야 하고 네트워크 요청을 보내는 등 불순한(?) 작업을 하면 안된다는 뜻이다. 자세한 설명은 Velopert Gitbooks에 잘 정리되어 있다.
Action, Reducer, Store
리덕스에는 Action, Reducer, Store라는 키워드가 나온다.
Action (액션)
상태에 어떠한 변화가 필요할 때 액션을 발생시킨다. type
필드는 필수이고, 나머지는 개발자 마음이다.
1 | { |
Action Creator (액션 생성함수)
말 그대로 액션을 만드는 함수이다. 파라미터를 받아서 Action 객체 형태로 반환한다.
1 | export const changeText = text => ({ |
Reducer (리듀서)
리듀서는 변화를 일으키는 함수이다.
1 | export default function counter(state = initialState, action) { |
여기서 왜 액션 생성함수는
export const
로, 리듀서는export default
로 내보냈는지 의아할 수 있다. Redux에서 자주 쓰이는 Duck 패턴이고, 한 파일에 리듀서, 액션, 액션 생성함수를 저렇게 모두 넣어넣고 한 파일에서 가져오기가 편하고 직관적이다.
Store (스토어)
리덕스는 한 애플리케이션 당 한 스토어를 만든다. (2개 이상이 안되는 건 아니지만 절대 권장하지 않는다) 스토어 안에는 애플리케이션의 상태와, 리듀서, 내장 메소드들이 들어있다.
Dispatch (디스패치)
디스패치는 Store 내장 함수 중 하나이다. 액션을 발생시킨다. 따라서 dispatch의 파라미터로는 액션이 들어간다. dispatch(action)
으로 정의된 action
을 리듀서 함수를 통해 실행한다.
Subscribe (구독)
구독도 Store 내장 함수 중 하나이다. 디스패치가 발생할 때마다 호출된다. subscribe
에 특정 함수를 파라미터로 넣어주면 된다. console.log(store.getState())
등 디스패치를 통해 상태가 변경될 때마다 스토어의 상태를 가져오는 등의 작업을 할 수 있는데, 나중에 Hook을 사용하여 스토어에 알아서 구독시켜주므로 직접 사용할 일은 거의 없다.
지금 간단한 TodoList를 리덕스를 통해 만들어보고 있는데, 확실히 직관적이고 상태 관리가 편하긴 하다. 하지만 아직 리덕스 미들웨어를 사용하지 않아서 Context API의 dispatch와 크게 다른 점을 못 느꼈다. 더 해봐야 알 것 같다.