Search
Duplicate
📒

[React 완벽 가이드] 12-2. Redux 스타일가이드 ⭐

상태
완료
수업
React 완벽 가이드
주제
연관 노트
3 more properties
참고

리덕스 스타일 가이드

NOTE
이 문서는 리덕스 코드를 작성하는 공식적인 스타일 가이드이며 추천하는 패턴과 최전의 방식 및 리덕스 어플리케이션을 만드는 접근법들을 설명한다!
‘올바르게’ 사용하는 방법은 없다. 하지만 오랜시간을 분석한 결과 특정한 방법들이 다른 것들보다 더 좋다는것을 알 수 있다.
에러와 쓸데없는 시간 낭비 및 안티-패턴들을 피하기 위한 권장사항을 모아서 작성했다.
하지만 이는 절대적이지는 않으므로, 자신의 상황에 맞추어서 적용해야 한다.

참고

원래 상당한 양의 내용의 스타일 가이드가 있지만 RTK(Redux Toolkit)이 대부분 해결해줌
고로 RTK에서 자동으로 적용되는건 제외하고 작성한다.
이번 챕터 결과물

우선순위 A의 규칙: 필수

상태를 직접 변경하지 마라.

NOTE
상태를 직접 바꾸는 행위리덕스에서 발생하는 가장 흔한 버그 중 하나로, 컴포넌트로 하여금 다시 렌더링 되는 것을 막거나, 디버깅을 고장낼 수 있다.
복사한 값을 변경하는 것은 괜찮다.

해결방법

상태변화를 감지하기 위해 redux-immutable-state-invariant를 사용
우발적인 상태변경을 막기 위해 Immer를 사용하자.
RTK를 사용하면 자동 Immer을 사용해서 막아줌!

리듀서들은 부수효과를 가지면 안된다.

NOTE
리듀서의 함수들은 오직 자신의 stateaction 인자들에만 의존해야 하고, 그 인자들을 기반으로 계산한 새로운 상태만을 반환해야 한다.
비동기 로직(Ajax 호출, TimeOut, Promise 등), 랜덤 값 생성, 리듀서 밖의 값 변경 또는 리듀서 함수의 외부 스코프에 있는 것들을 실행해서는 안된다!
이 규칙의 목적은 리듀서가 호출됬을 때, 예측가능하게 만들기 위함이다.

직렬화-불가능한 값들을 상태나 액션에 넣지 마라.

NOTE
Promise, Symbols, Maps/Sets, 함수, 클래스 인스턴스와 같이 직렬화 불가능 한 것들을 리덕스 스토어의 상태나 디스패치되는 액션에 넣는것을 피하자
이렇게 해야, 리덕스 DevTool 디버깅 같은 기능이 제대로 동작한다

예외

리듀서에 도달하기 전, 액션을 미들웨어가 가로채서 중지시킨다면, 직렬화 불가능한 값들을 액션에 넣을 수 있다.
redux-thunk, redux-promice같은 것들이 있다.

우선순위 B의 규칙: 강력 추천

리듀서에 가능한 많은 로직을 넣자.

NOTE
새로운 상태를 계산하는 로직을, 액션을 준비하고 디스패치하는 코드 (클릭 핸들러와 같은)가 아니라 해당 로직에 접한한 리듀서에 많이 넣도록 하자.
리덕스는 새로운 상태 값이 reducer안에서 실행되는지, 액션 로직에서 계산되는지 신경쓰지 않는다.
ex) Todo List앱의 경우, “할일 토글” 액션을 위한 로직은 ToDo목록의 배열을 업데이하는걸 요구한다.
// 클릭 핸들러: const onTodoClicked = (id) => { dispatch({type: "todos/toggleTodo", payload: {id}}) } // 리듀서: case "todos/toggleTodo": { return state.map(todo => { if(todo.id !== action.payload.id) return todo; return {...todo, id: action.payload.id}; }) }
JavaScript
복사
권장하는 방법
// 클릭 핸들러: const onTodoClicked = id => { const newTodos = todos.map(todo => { if (todo.id !== id) return todo return { ...todo, id } }) dispatch({ type: 'todos/toggleTodo', payload: { todos: newTodos } }) } // 리듀서: case "todos/toggleTodo": return action.payload.todos;
JavaScript
복사
권장하지 않음

이유

리듀서는 순수함수이기 때문에 항상 테스트하기가 쉽다.
const result = reducer(testState, action)만 호출하고, 결과를 원하면 값으로 assert하면 된다. 즉 테스트하기 쉬운 로직이 많아진다.
RTK를 사용한다면, 리듀서 안에서 불변 업데이트 로직을 작성하기 쉽다.
RTK DevTool에서 디버깅하기가 쉽다.

리듀서는 상태모양(State Shape)을 가져야 한다. (정규화)

NOTE
스토어의 중첩되거나 연관된 데이터 처리시 스토어데이터베이스의 일부처럼 정규화된 형태로 유지하는 방식을 권장한다
정규화 되지 않은 코드
정규화된 코드
각 데이터 타입은 자신의 테이블을 가진다.
데이터 테이블은 항목의 아이디를 Key로, 항목들을 값으로 가지는 개별 항목 아이템을 저장한다.
개별 항목에 대한 참조는 항목의 ID를 저장하여 수행한다.
배열의 ID는 순서를 나타낸다.

리듀서를 상태기계(State Machine)처럼 다루자.

NOTE
액션자체를 조건없이 다루지 말고, 현재 상태 그리고 디스패치된 액션 모두의 조합이 새로운 상태 값이 실제로 계산되어야 하는지 여부를 결정하는 “상태 기계” 처럼 다뤄야 한다.

자세한 설명

const IDLE_STATUS = 'idle'; const LOADING_STATUS = 'loading'; const SUCCESS_STATUS = 'success'; const FAILURE_STATUS = 'failure'; const initialUserState = { status: 'idle', // 명시적인 유한 상태 user: null, error: null }
JavaScript
복사
"idle" (시작하지 않은 상태)
"loading" (사용자 정보를 가져오고 있는 상태)
"success" (사용자 정보를 성공적으로 가져옴)
"failure" (사용자 정보를 가져오는데 실패함)