notes
Flux , Proxy ,Atomic 상태관리(뭘쓰지 ?)
Flux·Proxy·Atomic 상태관리 선택지와 적용 방향을 비교한 메모.
프로젝트를 진행하면서 redux 나 recoil 등 전역관리상태 라이브러리를 사용하고
react-query 등을 토이프로젝트나 react native로 앱을 개발 할때 클라이언트의 의존성을 낮추는데 사용했었다.
알고 쓰니? 😅
대답은 아니요…
context api 혹은 recoil , redux 와같은 전역 관리 라이브러리를 사용하면서
해당 부분에 패턴 , 동작원리 보단 구현위주의 개발을 진행하다 보니 사용법은 어느정도 이해하는데 내부 동작원리에 대한 이해도가 떨어졌다.
참고 : compound pattern
그러다 vercel git에 올라온 프로젝트를 찾아보며 분석 하는 도중에 해당 패턴을 반영한 로직을 봤다.
사람들은 이런식으로 설계를 하는구나.
코드를 짤때 , 설계할때 패턴이 정말 중요하구나.
더나아가서,코드 동작원리를 알아야 이슈를 잡을때 원만하게 해결해 나갈수 있겠구나. 라는 생각이 들었다.
그럼 내가 사용할 라이브러리도 특정한 패턴에 의해 설계되고 동작원리는 어떻게 될까 ? 찾아보았다.
크게 전역관리 라이브러리의 패턴은 크게 3가지가 있는것을 확인했다.
Flux
참고:
Flux는 Facebook에서 클라이언트-사이드 웹 어플리케이션을 만들기 위해 사용하는 어플리케이션 아키텍쳐라고 한다.
단방향 데이터 흐름을 활용해 뷰 컴포넌트를 구성하는 React를 보완하는 역할을 한다.(이는 라이프 사이클을 확인 하며 더 알아보자.)
Flux 어플리케이션은 다음 핵심적인 세가지 부분으로 구성되어 있다
Dispatcher - 데이터 흐름을 통제하는 중앙 통제 장치
Stores - 특정한 action이 들어옴에 따라 그에 맞는 type에 맞는 행동에 대한 콜백
Views - React 컴포넌트
여기서 Model-View-Controller ( 속칭 mvc )와 혼동을 하면 안된다.
Flux는 MVC와 다르게 단방향으로 데이터가 흐른다.
flux 패턴의 구조와 데이터 흐름을 살펴보자
Flux 어플리케이션에서의 데이터는 단방향으로 흐른다:
action을 정의 → 이를 dispatcher 에 전달(이렇게 바꿔라) → store에서 데이터를 변경 → view에 변경된 데이터를 반영
여기서 action은 새로운 데이터를 포함하고 있는 간단한 객체로 type 프로퍼티로 구분할 수 있다.
이게 무슨말이냐
action type를 지칭하여 나의 행동에 대한 정의를 내린다.
그 행동에 대한 데이터가 변경( data payload )
(data)=> {
type:"RUN",
state:{
calorie:data.calorie - 35
}
}
위와 같은 형식으로 action type이 해당 과 같은 값이 들어오면 해당에 맞는 data 변경이 일어나는것에 대한 정의 object를 의미
view는 사용자의 상호작용에 응답하기 위해 새로운 action을 만들어 시스템에 전파한다.
1.모든 데이터는 중앙 허브인 dispatcher를 경유하여 동작한다.
2.최초 action 에서 단방향으로 dispatcher 에 action creator
메소드를 제공한다.(action creator 는 행동에 대한 부분을 정의 한 object)
3.dispatcher는 store를 등록하기 위한 콜백을 실행한 이후에 action을 모든 store로 전달한다.(이로써 초기값이 할당이 된다.)
4.store는 change 이벤트를 views에게 알려주고 그 결과가 view
에 출력 된다.
view에서는 이벤트 핸들러로(버튼 을 딸깍! 한다던지 스크롤을 내린다 던지) 새로운actiontype 값을 넘겨주면 2-3-4 의 행위가 다시 일어나면데이터가 변동한다.
Proxy
참고 :
대리자 라고 ?
그럼 대신 시키는 건가 ?
프록시는
프록시가 대체재가 되어 proxy 에게 일을 대신처리 하게 시킬수 있다 는 것으로 이해하면 된다. (뭔가 미들웨어 와 개념이 비슷하다.)
프록시는 원래 객체에 대한 접근을 제어한다 즉, 객체 자체에 직접 접근 하는것 이 아닌 프록시를 경유 해서 , 당신의 요청이 원래 객체에 전달되기 전 또는 후에 무언가를 수행할 수 있도록 한다.
사진 참고 : https://ko.wikipedia.org/wiki/프록시_패턴
참고 사진을 보면 Real subject 라는 메소드에 접근시 클라이언트가 다이렉트로 해당 메소드에 접근을 하는것이 아닌 Proxy를 통해
💁♂️여기서 잠깐
프록시는 데이터의 흐름만 제어해야 하며 결과 값을 직접 변경,조작하는 행위는 하지 말아야한다.
예를 들어 A가 나 B 가 프록시이다.
B는 나의 대리인이다.
내가 B에게 마트에서 사과를 사오라고 시켰다.
그런데 B가 사과가 아닌 배를 사와버렸다…
이러한 데이터를 직접 조작 하는 행위자체를 프록시에선 하지말아야 한다.
참고:
// 여기 misub 이라는 객체가 있다.
const minsub = {
age:28,
weight:68,
height:176
}
// minsub 객체를 수정할수 있는 프록시를 생성
const minsubProxy = new Proxy(minsub, {
get: (obj, prop) => {
console.group('------------------')
console.log(`object:`,obj)
console.log(`properties:`,prop)
console.groupEnd()
},
set: (obj, prop, value) => {
console.group('------------------')
console.log(`properties:${prop}`)
console.log(`data:${obj[prop]} `)
console.log(`change data:${value} `)
console.groupEnd()
obj[prop] = value;
},
});
//프록시로 접근
minsubProxy.weight;
minsubProxy.age = 50;
위와 같이 minsub 이라는 객체 자체에 접근을 하는것이 아닌 생성자 프록시를 호출 해당 Proxy를 경유하여 값을 가져오거나 수정하는 패턴을 의미 한다.
Atomic
아토믹이라는 단어는 컴포넌트를 나눌때도 많이 쓰여 알고 있다.
원자라는 뜻의 단어로
컴포넌트UI를 나눌때는 인터페이스를 작고요소로 잘개 나누어 분리 하는 개념을 말한다.
상태 관리에서도 이와 같이 패턴을 정의 한다.
참고 :recoil 공식 사이트 , jotai 공식사이트
상태값을 atom인 원자의 형태로 나누어 기본값(default) 을 정의하고
해당 상태값을 특정 key 값으로 분류하여 atom 단위의 불변성을 체크
해당 개념을 이해 하기전에 Provider 패턴에 대해 먼저 알고 가면 좋다
참고 :
https://twitter.com/dai_shi/status/1380885195549597696
https://techblog.yogiyo.co.kr/recoil을-이용한-손쉬운-상태관리-b70b32650582
위는 recoil의 life cycle과 조타이의 life cycle을 개발자 올려놓은건데 그림을 보면 이해 하기 쉽다.
우선recoil같은 경우
import React from 'react';
import {
RecoilRoot,
atom,
selector,
useRecoilState,
useRecoilValue,
} from 'recoil';
function App() {
return (
// root 에 provider를 랩핑 jotai 와 달리 store 와같은개념의 weak map을 주입 하진 않는다
<RecoilRoot>
<CharacterCounter />
</RecoilRoot>
);
}
// atom을 정의 key를 기반으로 해당 원자단위로 쪼갠 값을 인식 하는 것은 동일하다
const textState = atom({
key: 'textState', // unique ID (with respect to other atoms/selectors)
default: '', // default value (aka initial value)
});
// hook을 호출
function TextInput() {
const [text, setText] = useRecoilState(textState);
const onChange = (event) => {
setText(event.target.value);
};
return (
<div>
<input type="text" value={text} onChange={onChange} />
<br />
Echo: {text}
</div>
);
}
흐름도는 flux 패턴과 동일 → 단방향으로 흐르고 selectors 라는 context 로 데이터를 의존 관리 한다.
const [text, setText] = useRecoilState(textState);
selectors 의 argument 로 atom 을 주입
comoponent 에서 text(state), setText(dispath)
jotai 같은 경우
1.atom 은 위크 맵에 정의 한다.
2.정의 한 위크 맵 Provider에 주입한다
3.provider rapping 후 component에 atom 을 호출할 context를 선언,Provider 를 통해 변경된 데이터를 주고 받는다.