All Articles

[Project-lib] PubSub 함수형 상태관리 패턴

<작성중>

PubSub-State

PubSub 패턴에 영감을 받은 앱의 데이터 상태를 관리하는 라이브러리


다중 함수가 같은 출처의 동적 데이터에 접근하고 있다면, 어떻게 이 데이터의 변화를 추적하며 관리를 하면 좋을까요. 
  • 우선 데이터를 만들어내는 주체와, 그 데이터를 사용하며, 의존하는 주체를 분리하여 커플링이 일어나지 않아야 한다고 생각합니다.
  • 다중 함수가 오리진이 같은 데이터를 사용한다면, 그 말은 곧 함수는 동기화 된 최신의 데이터를 가지거나, 일회성으로 Props형식으로 데이터를 자식으로 넘겨줍니다.
  • 특정 주제로 묶은 데이터의 집합을 원하는 함수에게만 전달하고, 데이터가 함수에 전달 된 후, 함수가 호출되는 파이프를 만들어, 중간에 불필요한 기능적 간섭을 줄입니다.
  • 위에서 언급한 바와 같이 데이터를 발행하는 주체는 데이터를 받는 주체를 몰라도 됩니다. 단지 발행자와 구독자 사이에는 데이터를 저장하는 상태 저장소와 메세지를 전달하는 브로커가 존재할 뿐입니다.
  • 구독자 함수는 그저 함수의 기능에만 집중합니다 (Reactive), 구독하는 데이터가 변경되었을 시 알아차리기만 하면 됩니다. (Ack)
  • 추가적으로 함수가 데이터를 구독하고, 메세지를 수신하고, 수신한 데이터를 사용하고, 새로운 데이터를 요청하는 상태를 저장소에 함께 저장한다면, 더 세밀하게 데이터의 변화주기를 추적할 수 있다고 생각합니다.

전역 변수를 사용하지 않기 위해, 그리고 공유하는 자원의 변화를 추적하는 여러 방법들을 찾아 보던 중,
네트워크 방법론의 뿌리를 가진 PubSub Messaging 패턴을 알게 되었습니다. ( 이는 프론트엔드 프레임워크/라이브러리 상태관리 패턴인 Flux의 기반이 됩니다. )

5월 23일을 기준으로 작성한 코드입니다. 최신 버전은 블로그 글 마무리 전까지는 여기서 확인 부탁드립니다.

PubSub-State 저장소

const { pipe } = require('./helper.js')
/**
 * 
 * @param {*} msg 개수에 상관없이 [] 배열에 초기에 저장할 객체 전달
 * @param {*} sub 스토어를 구독할 구독자(함수)를 배열에 담아 전달
 */
const Store = (msg, sub) =>
{

    // 메세지 저장 객체
    let _store = {}
    const ack = ( {topics} ) => topics.reduce((_, topic) =>   pipe(_store[topic].data , ..._store[topic].subs), -1 )
    const subscribe = ({ topics, sub }) =>
    {
        for (const topic of topics)
        {
            _store[topic].subs.push(...sub)
        }
        return { topics }
    }
    const getData = (store) => (topic) => store[topic].data
    const setData = (msgs, sub) => 
    {
        const topics = msgs.reduce((acc, cur) =>
        {
            const prevStore = _store[cur.topic]

            const temp = {
                [cur.topic]: {
                    data: prevStore ? { ...prevStore['data'], ...cur.data } : { ...cur.data },
                    subs: prevStore ? [...prevStore['subs']] : []
                }
            }

            Object.assign(_store, temp)
            acc.push(cur.topic)

            return acc

        }, [])

        return {topics, sub}
    }
    

    const pubSubPipe = (msgs, subs) => [ pipe( setData(msgs, subs), subscribe, ack ) ]


    return { setData, pubSubPipe, subscribe, ack, getData: getData(_store) }

}
/**
 * Store 생성자를 초기화 때와 분리한다
 * @param {*} msgs 데이터 정보
 * @param {*} subs 데이터를 구독하는 함수
 *   
 */

const store = (msgs, subs) =>
{
    const pubSubStore = Store(msgs, subs)
    pubSubStore.pubSubPipe(msgs, subs)

    return pubSubStore

}