Flux 글에서 「값을 바꾸고 싶으면 전표(Action)를 내고, 고치는 건 값을 가진 Store가 한다」를 공부했다. 그다음에 궁금했던 건 두 가지였다. Flux와 Redux는 같이 나온 건가, 다른 때 나온 건가. 그리고 Redux는 Flux와 무엇이 다른가.
이 글은 내가 AI 대화 상대인 Claude(Anthropic이 만든 AI)와 대화하며 공부한 것을 옮긴 것이다. 공부 중에 「새 페이지를 만든다」는 비유 하나 때문에 한참 헤맸는데, 그 헤맨 자리도 그대로 남긴다.
이 글에 옮긴 Redux 문서 인용은 전부 Redux 공식 저장소 reduxjs/redux의 docs/understanding/ 아래 문서(history-and-design/history-of-redux.md, history-and-design/PriorArt.md, thinking-in-redux/ThreePrinciples.md, 커밋 56abca4 판)에서 직접 확인했다.
먼저: 이 글에 나오는 「상태」와 「우리 앱」
상태(state)는 화면이 지금 보여줘야 할 값을 말한다. 좋아요 수 13, 「내가 하트를 눌렀다」 같은 값이다. 값이 바뀌면 화면도 따라 바뀌어야 한다.
우리 앱은 우리 client 앱, 방문자가 보는 회사 공개 사이트다. Next.js로 만든 React 앱(React는 화면을 컴포넌트라는 조각으로 나눠 만드는 자바스크립트 도구다)이고, 후기 상세 화면에 하트 모양 좋아요 버튼과 좋아요 수가 있다. 이 버튼 코드(components/ReviewActions.js)가 이 글과 앞 두 글의 공통 예제다. 하트를 누르면 화면이 서버(좋아요 수를 세는 우리 자바 백엔드)에 요청을 보내고, 서버가 돌려준 새 수로 화면을 다시 그린다.
먼저: 이 글의 자바스크립트 코드는 여섯 가지만 알면 읽힌다
이 글의 예제 중 일부는 자바스크립트다. 자바와 이름만 비슷한 다른 언어라서, 자바 독자가 처음 보면 걸리는 문법이 여섯 가지 있다. 미리 풀어 둔다.
{ type: '좋아요_반영됨', count: 13 }: 클래스 없이 중괄호만으로 객체를 바로 만든다.이름: 값짝을 쉼표로 늘어놓고,obj.type처럼 꺼낸다. 자바의Map<String, Object>에 값을 넣어 둔 것과 비슷하다{ reviewId, count: updated }: 이름만 있고 콜론이 없는reviewId는reviewId: reviewId를 줄여 쓴 것이다. 같은 이름의 변수 값을 그대로 넣는다const x = ...: 변수를 만든다. 다시 대입할 수 없다는 점이 자바의final변수와 같다. 타입은 적지 않는다function subscribe(onChange) { ... }: 함수를 만든다. 자바 메서드와 같은데, 매개변수와 반환값의 타입을 적지 않는다(x) => ...,x => ...,() => ...: 함수를 짧게 쓴 화살표 함수다. 자바의 람다x -> ...와 같다. 자바스크립트에서는 함수도 값이라서, 변수에 담고, 다른 함수에 넘기고(그렇게 넘긴 함수가 콜백이다), 반환값으로 돌려줄 수 있다. 변수에 담긴 함수는onChange()처럼 괄호를 붙여 부른다a === b: 「같은가」를 비교한다. 이 글에서는action.type === '좋아요_반영됨'처럼 글자 둘이 같은지 볼 때만 쓰고, 그때는 자바의"좋아요_반영됨".equals(action.type)과 같다고 보면 된다
Redux는 Flux보다 1년 늦게 나온 후속작이다
Redux 문서에는 Redux의 역사를 정리한 글이 따로 있다. 그 글의 연표를 따라가면 이렇다.
2014년, Flux. Facebook이 「Flux 아키텍처」라는 생각을 발표했다. 그런데 그 생각을 다 구현한 도구는 주지 않았다. 문서는 이렇게 적었다.
Facebook announced this “Flux Architecture” concept around 2014, but didn’t provide a full library that implemented that pattern. That led the React community to build dozens of Flux-inspired libraries with variations on the pattern.
Facebook은 2014년쯤 「Flux 아키텍처」라는 개념을 발표했지만, 그 패턴을 다 구현한 라이브러리는 내놓지 않았다. 그래서 React를 쓰는 개발자들이 Flux를 본뜬 라이브러리를 수십 개 만들었다.
2015년 중반, Redux. Dan Abramov라는 개발자가 그 수십 개 중 하나로 Redux를 만들었다. 원래 목적은 학회 발표에서 「시간 되돌리기 디버깅」(지나간 상태로 거꾸로 돌아가 화면을 다시 보기)을 보여주는 것이었다. Redux는 나오자마자 다른 Flux 흉내 도구들을 거의 다 밀어냈고, 문서는 2016년쯤에는 「React를 쓰면 Redux도 꼭 써야 한다」는 말까지 돌았다고 적었다.
2017~2018년, 경쟁자. 관심이 「화면 상태를 어떻게 관리하나」에서 「서버 데이터를 어떻게 가져오고 저장해 두나」로 옮겨 갔다. 화면 상태가 「메뉴가 열렸나」처럼 화면 안에서만 생기는 값이라면, 서버 데이터는 후기 목록처럼 서버에서 받아 오는 값이다. 받아 온 걸 잠시 저장해 두었다가 다시 쓰고, 오래되면 새로 받아 오는 일을 맡는 도구들(React Query 등)이 이때 나왔다.
2019년, Redux Toolkit. 같은 Redux를 더 적은 코드로 쓰게 만든 공식 도구가 나왔다.
그래서 순서는 MVC(1970년대 말) → Flux(2014) → Redux(2015)다. 셋은 같은 문제, 「값이 바뀌는 길을 어떻게 다스리나」를 점점 다르게 푼 흐름이다.
Redux는 Flux의 생각을 이어받되 창구를 없애고 값을 고치지 않는다
Redux 문서의 「Prior Art(앞선 작업)」 글이 Flux와 직접 비교한다. 첫 문장이 관계를 정리한다.
Redux evolves the ideas of Flux, but avoids its complexity.
Redux는 Flux의 생각을 발전시키되, 그 복잡함은 피한다.
같은 글이 꼽은 차이 중 둘이 핵심이다.
Unlike Flux, Redux does not have the concept of a Dispatcher.
Flux와 달리 Redux에는 Dispatcher라는 개념이 없다.
Dispatcher는 Flux에서 전표(Action)를 받아 모든 Store에 똑같이 돌리는 창구다.
Another important difference from Flux is that Redux assumes you never mutate your data.
Flux와 또 하나 중요한 차이는, Redux는 데이터를 절대 고쳐 쓰지 않는다고 전제한다는 점이다.
Flux 글의 은행으로 옮기면 이렇다.
Flux 은행은 통장 담당(Store)이 여럿이었다. 후기 좋아요를 예로 들면, 후기마다 좋아요 수를 들고 있는 담당, 「내가 눌렀는지」를 들고 있는 담당, 화면 맨 위에 띄울 「오늘 받은 좋아요 합계」를 들고 있는 담당이 따로 있는 식이다. Flux는 업무별로 Store를 나눴기 때문이다. 그래서 전표를 나눠 줄 창구가 필요했다. 각 담당은 전표를 받으면 자기 통장의 숫자를 지우고 고쳐 썼다.
Redux 은행은 통장이 딱 한 권이다. 모든 값이 이 한 권에 들어 있다. 통장이 하나니까 전표를 나눠 줄 창구가 필요 없다. 그리고 전표가 오면 숫자를 지우고 고쳐 쓰지 않는다. 계산 규칙을 보고 바뀐 값을 담은 것을 새로 만든다.
재미있는 건 문서에 Flux를 만든 사람들도 Redux를 인정했다는 기록이 남아 있다는 점이다. 누군가는 「Flux를 안 하는 방법으로 더 나은 Flux를 만들었다(inventing a better Flux by not doing Flux at all)」고 평했다.
Redux의 원칙 셋은 Store 하나, 전표만으로 변경, 순수 함수로 계산이다
Redux 문서는 원칙을 셋으로 정리한다.
Single source of truth. The global state of your application is stored in an object tree within a single store.
하나뿐인 원본. 앱 전체 상태는 Store 하나 안의 객체 트리에 담긴다.
객체 트리는 객체 안에 객체가 들어 있는 모양이다. 예를 들면 { review: { count: 13, liked: true }, todayTotal: 42 }처럼, 앱의 모든 값이 큰 객체 하나 안에 가지를 치며 들어 있다.
State is read-only. The only way to change the state is to emit an action, an object describing what happened.
상태는 읽기 전용이다. 상태를 바꾸는 유일한 방법은 「무엇이 일어났는지」를 적은 객체인 Action을 내는 것이다.
Changes are made with pure functions. To specify how the state tree is transformed by actions, you write pure reducers.
변경은 순수 함수로 한다. Action에 따라 상태가 어떻게 바뀌는지는 순수한 Reducer로 적는다.
Action은 Flux 글과 같은 평범한 객체다. 좋아요가 반영됐다는 Action은 이렇게 생겼다.
{ type: '좋아요_반영됨', count: 13, liked: true }
type이 전표의 종류이고, 나머지는 함께 전하는 값(새 좋아요 수와 눌렀는지)이다.
첫째 원칙이 「통장 한 권」이다. 둘째는 Flux와 같다. 전표 말고는 값을 바꿀 길이 없다. 셋째가 Redux만의 것이다. Reducer는 「지금 상태」와 「전표」를 받아 「새 상태」를 돌려주는 함수다.
function reducer(state, action) {
if (action.type === '좋아요_반영됨') {
return { ...state, count: action.count, liked: action.liked };
}
return state;
}
한 줄씩 보면 이렇다.
function reducer(state, action): 지금 상태와 들어온 전표를 받는다. 자바스크립트라 타입을 적지 않았지만,state는{ count: 12, liked: false }같은 상태 객체이고action은 위의 Action 객체다if (action.type === '좋아요_반영됨'): 전표 종류를 보고 자기가 처리할 일인지 가린다return { ...state, count: action.count, liked: action.liked }:...state는 지금 상태의 값을 전부 베낀다는 뜻이다. 거기서count와liked만 바꾼 새 객체를 만들어 돌려준다. 받은state는 건드리지 않는다return state: 상관없는 전표면 아무것도 바꾸지 않고 지금 상태를 그대로 돌려준다
이 예제는 쉽게 보려고 후기 하나의 상태 { count, liked }만 받는다. 앞에서 본 큰 트리 { review: {...}, todayTotal: 42 } 전체를 함수 하나가 다 받는 게 아니다. Redux 문서도 「처음엔 Reducer 하나로 시작하고, 앱이 커지면 상태 트리의 특정 부분을 맡는 작은 Reducer들로 나누라」고 한다. review 가지는 이 Reducer가, todayTotal 가지는 다른 Reducer가 맡고, Redux가 둘의 결과를 합쳐 큰 트리를 만든다. 실제 앱에서 후기가 여러 개라면 어느 후기의 좋아요인지 알리도록 전표에 reviewId도 넣는다.
순수 함수라는 건 같은 입력이면 언제나 같은 결과가 나오고, 함수 바깥의 아무것도 바꾸지 않는다는 뜻이다. Reducer는 받은 상태 객체조차 고치지 않는다. 그래서 같은 상태에 같은 전표를 넣으면 언제나 같은 새 상태가 나온다.
통장(Store)이 하는 일은 셋뿐이다. Redux 소스(src/createStore.ts)에서 Store 객체가 내놓는 것을 확인했다.
통장(Store)은 Redux가 주는 createStore 함수에 앞의 Reducer를 넘겨서 만든다. const store = createStore(reducer); 한 줄이다. 그래서 Store는 전표가 올 때마다 이때 받은 Reducer를 부른다. (지금은 같은 일을 더 짧게 해 주는 Redux Toolkit의 함수를 주로 쓰지만, 「Store를 만들 때 Reducer를 넘긴다」는 점은 같다.)
getState(): 지금 상태를 돌려준다dispatch(action): 전표를 받아 Reducer로 새 상태를 만들고, 그걸 지금 상태로 바꿔 끼운다subscribe(listener): 「바뀌면 알려 주세요」 명단에 등록한다.listener는 「바뀌면 이걸 불러 줘」라며 넘기는 함수다
마지막 subscribe는 Flux 글에서 본 그 명단이다. Redux에서도 화면이 미리 등록하고, Store가 「바뀌었다」고 알리는 건 똑같다. 화면 쪽에서는 React가 화면이 나타날 때 subscribe로 등록하고, 알림이 오면 getState()로 새 상태를 읽어 다시 그린다.
하트 클릭부터 화면이 바뀔 때까지를 한 줄로 이으면 이렇다.
하트 클릭 → 화면 코드가 서버에 요청 → 응답(새 수 13)이 오면 화면 코드가
store.dispatch({ type: '좋아요_반영됨', count: 13, liked: true }) 를 부른다
→ Store가 Reducer로 새 상태 객체를 만든다 → 명단에 「바뀌었다」 알림
→ React가 getState()로 새 상태를 읽어 다시 그린다
dispatch를 부르는 쪽은 서버 응답을 받은 화면 코드다. 여기서 「화면 코드」는 우리가 쓴 컴포넌트 안의 클릭 처리 함수를 말한다. 컴포넌트는 우리가 쓰지만, 컴포넌트 함수를 다시 불러 화면을 새로 그리는 일은 React가 한다. 그래서 「전표를 내는 것」은 우리 코드가, 「다시 그리는 것」은 React가 맡는다.
「새 페이지」라는 비유가 오해를 만들었고, 새로 만드는 건 상태 객체다
처음 들은 설명은 Redux를 이렇게 그렸다. 「전표가 오면 통장 숫자를 고쳐 쓰지 않고, 계산 규칙표를 보고 새 페이지를 한 장 새로 쓴다. 지난 페이지는 그대로 남는다.」
나는 「새 페이지」에서 막혔다. 웹 이야기를 하던 중이라 「페이지」가 웹 페이지(화면)로 들렸다. Redux가 전표마다 화면을 새로 만든다는 말인가 싶었다. 「페이지를 만든다고 했잖아」라고 되물었고, 그건 통장의 한 장을 뜻하는 비유였다는 답을 들었다. Redux는 웹 페이지를 새로 만들지 않는다. 화면은 그대로이고, 새로 만드는 건 값을 담은 객체 하나다. 이 객체를 Redux에서는 상태(state)라고 부른다.
비유를 빼고 자바로 보니 바로 잡혔다. 좋아요 정보를 담는 객체가 있다고 하자.
LikeState state = new LikeState(12, false); // count=12, liked=false
값을 13으로 바꾸는 방법은 둘이다.
// 방법 1: 고쳐 쓰기 (Flux의 Store가 하던 방식)
state.setCount(13);
state.setLiked(true);
// 객체는 하나다. 12였던 그 객체가 13이 됐다. 12였던 모습은 어디에도 없다
// 방법 2: 새로 만들기 (Redux 방식)
LikeState newState = new LikeState(13, true);
// 객체가 둘이다. state는 여전히 (12, false), newState가 (13, true)다
// 앞으로는 newState를 「지금 상태」로 쓴다
자바의 String이 이미 방법 2로 움직인다. "hello".toUpperCase()는 원래 문자열을 고치지 않고 "HELLO"라는 새 String을 돌려준다. 이렇게 한 번 만들면 내용을 바꾸지 않는 객체를 불변 객체라고 한다.
그러니 Redux의 흐름은 이렇게 그려야 정확하다.
지금 상태 객체 {12, false} + 전표 「좋아요_반영됨」
│
▼
Reducer(계산 규칙 함수)
│
▼
새 상태 객체 {13, true} ← 이걸 「지금 상태」로 바꿔 끼운다
(이전 객체 {12, false}는 고치지 않는다)
│
▼
「바뀌었다」 알림 → 화면이 새 상태를 읽어 숫자 13을 그린다
화면(웹 페이지)은 하나 그대로이고, 그 안의 숫자만 12에서 13으로 바뀐다. 바뀌는 건 화면 뒤에서 값을 들고 있는 객체다.
이전 객체를 고치지 않는 첫째 이유는 == 한 번으로 「바뀌었다」를 알기 위해서다
그다음 질문은 「이전 객체를 그대로 둬? 이유가 뭐야?」였다. 먼저 오해 하나를 풀었다. 「그대로 둔다」가 「일부러 쌓아 둔다」는 뜻은 아니다.
LikeState state = new LikeState(12, false);
state = new LikeState(13, true); // state가 새 객체를 가리킨다
// 이제 (12, false) 객체를 가리키는 변수가 아무도 없다
// → 자바의 가비지 컬렉터가 나중에 알아서 치운다
아무도 안 쓰는 이전 객체는 자바든 자바스크립트든 가비지 컬렉터가 알아서 치운다. 핵심은 「남겨 둔다」가 아니라 「건드리지 않는다」다.
건드리지 않는 첫째 이유는 바뀌었는지를 알아내는 비용이다. 고쳐 쓰면 이런 일이 생긴다.
LikeState before = state;
state.setCount(13); // 같은 객체를 고친다
before == state // true. 같은 객체라서 「안 바뀌었다」로 보인다
before와 state가 같은 객체를 가리키니, 주소만 비교해서는 바뀐 걸 알 수 없다. 알려면 안의 값을 하나하나 다 비교해야 한다(==와 equals()의 차이). 값이 수백 개면 그것도 일이다. 새로 만들면 이렇다.
LikeState before = state;
state = new LikeState(13, true); // 새 객체
before == state // false. 「바뀌었다」를 바로 안다
== 한 번이면 끝난다. 이 비교를 하는 쪽은 Redux가 아니라 React다. 왜 Store가 알렸는데 React가 또 비교할까. Redux 소스(src/createStore.ts)를 보면 dispatch는 Reducer를 부른 뒤 결과가 바뀌었든 아니든 명단 전부에게 알린다. 상관없는 전표가 와서 Reducer가 상태를 그대로 돌려줘도 알림은 간다. 그래서 알림은 「바뀌었을 수도 있다」는 뜻일 뿐이고, 정말 바뀌었는지는 React가 새 상태를 읽어 이전에 읽은 것과 같은 객체인지 비교해서 가린다. 다르면 화면을 다시 그린다. MVC 글에서 확인한 React 공식 문서의 useSyncExternalStore 설명이 그렇다. useSyncExternalStore는 컴포넌트를 바깥 보관함(Redux Store 같은 것)에 연결하는 React 함수로, 「등록하는 함수」와 「지금 값을 읽는 함수」를 넘겨받는다. 문서는 그 「지금 값을 읽는 함수」가 돌려준 결과가 이전과 다른지 Object.is(자바의 ==에 가까운 비교)로 확인해서 다르면 다시 그리고, 그 값은 고쳐 쓰지 말고 바뀌었으면 새로 만들어 돌려주라고 적혀 있다. 고쳐 쓰면 같은 객체라서 React가 바뀐 줄 모른다. 숫자는 13인데 화면에는 12가 남는 버그가 생긴다.
둘째 이유는 지난 상태를 그대로 꺼내 볼 수 있어서다
평소에는 이전 객체가 치워진다. 그런데 개발할 때만 쓰는 도구가 이전 객체들을 일부러 붙잡아 두면 이야기가 달라진다. Redux에는 브라우저에 설치해 쓰는 개발 도구가 있어서, 들어온 Action과 그때마다 만들어진 상태 객체를 목록으로 모아 둔다. 그러면 이렇게 된다.
상태1 {12, false} ──전표A──▶ 상태2 {13, true} ──전표B──▶ 상태3 {12, false}
이전 객체를 고친 적이 없으니 상태1, 상태2가 그때 모습 그대로 남아 있다. 「전표 A 직후 화면은 어땠지?」를 보려면 개발 도구 목록에서 상태2를 고르면 된다. 도구가 상태2를 지금 상태로 다시 끼워 넣고, 화면이 그 상태로 다시 그려진다. 고쳐 쓰는 방식이었다면 상태1은 이미 13으로 덮여서 사라졌을 것이다.
이게 Dan Abramov가 Redux를 만들며 보여주려던 「시간 되돌리기 디버깅」이다. Redux 문서도 세 원칙 중 앞의 둘에 이 이득을 붙여 두었다. 「하나뿐인 원본」에는 상태가 트리 하나에 다 있으면 되돌리기(Undo)·다시하기(Redo)처럼 원래 만들기 어려웠던 기능이 「갑자기 쉬워진다」고 적었고, 「상태는 읽기 전용」에는 Action이 평범한 객체라서 기록해 뒀다가 다시 재생할 수 있다고 적었다. 평범한 객체라는 건 { type: '좋아요_반영됨', count: 13 }처럼 글자와 숫자 같은 값만 들어 있고, 함수나 서버 연결 같은 「살아 있는 것」은 들어 있지 않다는 뜻이다. 값만 있으니 그대로 글자로 바꿔 파일에 적어 둘 수 있고, 나중에 그 글자를 읽어 똑같은 전표로 되살릴 수 있다. 전표 안에 함수가 들어 있었다면 글자로 적어 둘 수가 없어서 기록도 재생도 안 된다. 다시 재생한다는 건, 처음 상태에서 기록해 둔 전표들을 차례로 Reducer에 다시 넣는 것이다. Reducer는 같은 입력이면 같은 결과를 내니, 그 시점의 상태가 똑같이 다시 나온다.
평소에 이득을 보는 건 첫째 이유이고, 둘째는 디버깅할 때 따라오는 덤이다.
Redux와 FSD는 답하는 질문이 달라서 함께 쓸 수 있다
Redux를 다 보고 나서 「그럼 FSD와 Redux는 공존할 수 있는 거구나?」라고 물었다. 그렇다. 둘은 답하는 질문이 다르다.
- FSD는 「파일을 어느 폴더에 두고, 누가 누구를 불러 쓸 수 있나」에 답한다. 다루는 것은 폴더와 import 방향이다
- Redux는 「화면의 값이 어떤 길로 바뀌나」에 답한다. 다루는 것은 상태 객체와 전표의 흐름이다
FSD 공식 문서에도 Redux 같은 도구가 들어갈 자리가 이미 적혀 있다. FSD 글을 쓸 때 받아 둔 FSD 문서 원본에서 확인했다.
app층의store칸은 「global store configuration」, 앱 전체에 하나뿐인 Store를 만드는 설정 코드다- 각 조각의
model칸은 「schemas, interfaces, stores, and business logic」, 그 업무에 딸린 상태와 규칙이다
예를 들면 Redux의 「통장 한 권」을 만드는 코드는 app/store/에 두고, 좋아요 상태를 계산하는 Reducer는 entities/review/model/에 두는 식이다. FSD는 어디에 둘지를, Redux는 어떻게 바뀔지를 정한다.
참고로 우리 client 앱은 지금 Redux를 쓰지 않는다. 좋아요 버튼 파일 components/ReviewActions.js가 Redux 없이 작은 보관함을 직접 만들어 쓴다. 좋아요 수를 적어 두는 Map(counts)과 「바뀌면 알려 주세요」 명단(listeners), 명단에 등록하는 subscribe와 명단 전부에게 알리는 notify 함수가 전부다. Redux Store가 하는 일 중 「값을 들고 있다가 바뀌면 알린다」만 손으로 만든 셈이고, Action과 Reducer는 없다. Flux 글에서 본 대로, 좋아요 수를 바꾸는 곳이 한 곳뿐이라 그걸로 충분하다.
판단 기준 정리
| 질문 | 답 | 근거 |
|---|---|---|
| Flux와 Redux는 같이 나왔나 | 아니다. Flux 2014, Redux 2015 | Redux는 Flux를 본뜬 수십 개 도구 중 하나로 나와 나머지를 밀어냈다 |
| Redux는 Flux에서 무엇을 덜어 냈나 | Dispatcher(창구) | Store가 하나라 전표를 나눠 줄 필요가 없다 |
| Redux에서 값은 어떻게 바뀌나 | Action을 내면 Reducer가 새 상태를 만든다 | 상태는 읽기 전용이고, Reducer는 순수 함수다 |
| 「새로 만든다」는 무엇을 새로 만드나 | 상태 객체 하나. 화면(웹 페이지)이 아니다 | 화면은 그대로이고 숫자만 바뀐다 |
| 이전 객체는 남겨 두나 | 고치지 않을 뿐이다. 아무도 안 쓰면 치워진다 | 핵심은 「건드리지 않는다」 |
| 왜 고치지 않나 (1) | == 한 번으로 바뀐 걸 안다 |
React도 Object.is로 비교해 다시 그린다 |
| 왜 고치지 않나 (2) | 지난 상태를 그대로 꺼내 볼 수 있다 | 시간 되돌리기 디버깅, Undo/Redo |
| FSD와 함께 쓸 수 있나 | 있다 | FSD는 어디에 둘지, Redux는 어떻게 바뀔지를 정한다 |
이 정리에 닿기까지
시작은 「Flux와 Redux는 다른 시대에 나온 거야?」였다. Redux 문서의 역사 글로 Flux 2014, Redux 2015를 확인했다. Redux는 Flux를 본뜬 수십 개 도구 중 하나였고, 원래 목적은 시간 되돌리기 디버깅 시연이었다.
Redux가 Flux에서 덜어 낸 것은 창구였다. 통장 담당이 여럿이던 Flux와 달리 통장을 한 권으로 합치니, 전표를 나눠 줄 창구가 필요 없어졌다. 그리고 값을 고쳐 쓰지 않는다는 전제가 하나 더 붙었다.
「새 페이지」에서 막혔다. 통장의 한 장을 뜻한 비유였는데, 웹 이야기 중이라 화면을 새로 만든다는 말로 들렸다. 비유를 버리고 자바의 「고쳐 쓰기 vs 새로 만들기」로 보니 바로 잡혔다. 새로 만드는 건 상태 객체 하나였다. 비유가 이해를 돕다가 오히려 막을 수 있다는 걸 이때 알았다.
「이전 객체를 왜 그대로 둬?」를 물었다. 일부러 쌓아 두는 게 아니라 건드리지 않을 뿐이고, 아무도 안 쓰면 치워진다는 것부터 바로잡았다. 이유는 둘이었다. == 한 번으로 바뀐 걸 알 수 있고, 지난 상태를 그대로 꺼내 볼 수 있다. 첫째 이유가 MVC 글에서 본 React 문서의 「Object.is로 비교하니 고쳐 쓰지 말고 새로 만들어라」와 같은 이야기였다.
마지막으로 FSD와의 관계를 물었다. 둘은 답하는 질문이 달라서 함께 쓸 수 있었고, FSD 문서에도 Store가 들어갈 자리(app/store, 각 조각의 model)가 이미 있었다.
정리
- Redux는 Flux보다 1년 늦게(2015) 나온 후속작이다. Flux를 본뜬 수십 개 도구 중 하나였고, 나머지를 거의 다 밀어냈다
- Redux는 Store를 하나로 합쳐 Dispatcher를 없앴다. 상태를 바꾸는 유일한 길은 Action이고, 새 상태는 Reducer라는 순수 함수가 만든다
- 새로 만드는 건 화면이 아니라 상태 객체 하나다. 이전 객체는 고치지 않을 뿐, 아무도 안 쓰면 치워진다
- 이전 객체를 고치지 않으면
==한 번으로 「바뀌었다」를 안다. React도 이 방법으로 다시 그릴지 정한다. 덤으로 지난 상태를 꺼내 볼 수 있다 - FSD와 Redux는 함께 쓸 수 있다. FSD는 어디에 둘지를, Redux는 어떻게 바뀔지를 정한다
자신만의 철학을 만들어가는 중입니다.
댓글남기기