Flux란? Flux vs MVC

Flux란? Flux vs MVC

Tags
React.js
Published
January 15, 2025
Author
Seongbin Kim
작성 중
 
  • 이 글의 내용
    • Flux가 무엇이고, Redux와의 차이점을 이해합니다.
    • Flux를 실제로 사용해보고 구체적으로 각 구성 요소를 이해합니다.
 

1. Flux란

  • Flux는 Facebook 개발자들이 React로 View를 구축할 때 애플리케이션 상태를 관리하기 위해 개발한 라이브러리이며, 당시 Facebook에서 실제로 사용된 라이브러리입니다.
  • 특히 상태 간에 뷰를 자동으로 re-render하는 React의 선언적 프로그래밍 스타일과 잘 어울립니다.
  • (개인적으로는 React를 단순한 View로 활용할 때 쓰기 좋게 MVC를 개량한 모델이라는 생각이 듭니다.)
 

1-1. 역사

  • Flux는 F8 2014 컨퍼런스에서 페이스북 개발자들이 오픈소스로 공개하고 발표했습니다. (10:50 ~ 24:00)
    • Video preview
  • (tmi) React는 JSConf US 2013 컨퍼런스에서 페이스북 개발자들이 오픈소스로 공개하고 발표했습니다.
    • Video preview
 

1-2. 요약

 

해결하고자 한 문제점

  • 채팅 회귀 버그들이 자주 발생
    • 소스 코드의 다른 부분을 수정했을 때 채팅 회귀가 발생
 

원인

  • 예측하기 어려운 렌더링 결과(코드 실행 결과)
    • 좋지 못한 설계 - 과도하게 복잡한 Controller 코드
      • Controller는 이벤트, API 응답을 처리
      • 특정 이벤트, API 응답을 처리할 때, 갱신할 Model이 많아질수록, Controller가 복잡해짐
      • Controller가 복잡하므로 실행 결과를 예측하기 어려워짐
      • 연관 Model 간 연쇄 갱신 사례:
        • DM 목록 모델 에서 특정 DM을 읽음으로 갱신하는 경우 읽지 않은 개수 모델 에서 개수를 갱신
        • (MVC 참고)
 

해결 방법

  • 렌더링 결과를 더 쉽게 예측할 수 있게 설계
    • Action → Dispatcher → Store → View
    • Controller의 책임을 Model과 View로 이동
      • Model의 public setter 제거
      • Model의 setter를 활용하던 Controller 제거
      • Model이 Event를 수신하고 직접 처리
      • View는 Model의 변경 사항을 수신해 직접 처리 (React)
    • 렌더링 원인을 명확화
      • 상태 변경은 Action을 발행해야만 가능하게 변경
    • 렌더링 과정을 단순화
      • 각 렌더링의 원인은 하나의 Action이도록
        • (prevState, action) ⇒ nextState
        • State 마다 렌더링 수행
          • 상태 반영(reduce) 도중에는 Action 발행을 금지 (Store → Action ❌)
          • 별도의 State 변경이 필요한 경우, 새로운 Action → … → View 단계를 거치도록.
 

결과

  • 단방향 데이터 흐름
    • Action → Dispatcher → Store → View (→ Action)
      • notion image
      • Event, Web API를 처리할 때 Controller가 하지 않고, Action 발행만 하는 모습.
 

효과

  • 원인이었던 채팅 회귀 버그 해소
  • 버그의 원인 파악이 더 쉬워짐
  • 단위 테스트 작성이 더 쉬워짐
 

1-3. 해결하고자 한 문제점

  • 계속 반복되는 채팅 버그의 회귀때문에 구조적인 문제가 있다고 판단하고, 구조를 개선하고자 했다고 합니다.
 

사례: 채팅 버그

  • 당시 facebook에는 계속해서 채팅 회귀 버그들이 있었다고 합니다.
  • 가장 유명한 것이 읽지 않은 메시지 개수 버그라고 합니다.
notion image
notion image
  • 채팅 기능에는 Model이 여러 개가 있어 Controller에서 여러 개의 Model, View를 다루고 있었습니다.
    • notion image
 

설계 개선: Client MVC

  • 처음에는 문제가 없었지만, Model, View가 많아지고 서로 연관될수록 기능을 추가하는 과정이 어려워졌고, 전체적으로 동작의 흐름을 예측할 수 없어졌다고 합니다.
    • notion image
 

1-4. 해결 방법

 

Controller 사용 → Controller 제거, Event 기반으로

  • AS-IS: 이벤트, API 응답 처리 시 Controller(MVC)로 처리
    • (ex) new message API Handler
      • ChatTab, MainMessages, UnseenThreads 모델의 상태를 갱신
      • 화면을 리렌더링
  • TO-BE: 각 UI 요소에서 응답해야 하는 Event를 직접 처리
    • API 응답 수신 시 Action이 발행되고, 관련된 Model들에게 전달
    • 각 Model이 독립적으로 Action을 처리
      • (ex) 신규 메시지가 있는 경우
        • new message Action을 발행
          • ChatTab, MainMessages 모델은 UI 상에 메시지 추가
          • UnseenThreads 모델은 조회하지 않은 메시지 목록에 추가
      • (ex) 메시지를 읽은 경우
        • mark seen Action을 발행
          • UnseenThreads 모델은 조회하지 않은 메시지 목록에서 해당 메시지를 제거
 

View는 Model의 상태를 구독하고 자동 반영

  • AS-IS: Controller가 직접 View의 갱신을 호출
    • (ex) 신규 메시지가 있는 경우
      • messagesView.appendMessage(newMessage);
  • TO-BE: View는 Model을 구독하고 변경 사항이 있으면 스스로 re-render 수행
    • (ex) 신규 메시지가 있는 경우
      • ChatTab, MainMessages 모델은 UI 상에 메시지 추가
      • messagesViewmessages를 받아 화면을 스스로 갱신
 

API 데이터 그대로 사용 → 클라이언트 상태 모델 추가

  • AS-IS: 신규 메시지 알람 기능의 경우 new message 이벤트만 존재
    • 이벤트마다 그 개수만 더하고 빼는 방식을 사용
    • (도메인 계층이 별도로 없고 API 반환 값을 그대로 사용하는 코드만 있는 방식)
  • TO-BE: unseen messages(조회하지 않은 메시지) 라는 클라이언트 상태를 새로 정의
    • “조회하지 않은 메시지 목록”을 명시적으로 관리
    • 조회하지 않은 메시지의 개수는 이 목록의 길이를 활용
    • (클라이언트에 도메인 계층이 생겨 코드의 의도가 더 명확해지는 방식)
    • notion image
 

렌더링 과정 단순화 (렌더링 원인 명확화)

  • AS-IS: Controller에서 View에 반영되기 전에 여러 Model을 갱신
    • 여러 단계가 한 번의 렌더링 사이클에서 실행되므로 복잡
      • (ex) new message, mark unseen이 한 번에 실행되고 화면에 반영
  • TO-BE: 한 단계에 한 번의 렌더링으로 단순화
    • 신규 상태를 View가 렌더링하기 전까지 상태 갱신을 금지
      • Store에서 스스로 신규 Action을 발행할 수 없도록 제한
      • 항상 (prevState, action) => nextState인 구조 (action[] ❌)
        • (ex) new message 발행 이후 렌더링, 이후 mark unseen 발행
      • Action → Dispatcher → Store → View 순서를 항상 지켜야 함
        • notion image
 

1-5. 도입 효과

  • 채팅 버그 해소
  • 신규 입사자의 코드 파악 시간 감소
  • 버그 원인을 더 쉽게 찾을 수 있게 개선
 

2. (WIP) Flux 도입

 

Flux를 사용하기 적절한 경우

       

      Flux가 부적절한 경우

           

          3. (WIP) Redux와의 차이점

           

          동일한 점

          • Action이라는 Event 기반입니다.
          • 단방향 데이터 흐름입니다.

          유사점

          • reduce 함수가 있습니다.
            • Redux에서 reducer는 함수 형태로 구성합니다.
            • Flux에서 reduce는 Store의 메소드입니다.
          • connect 함수와 같은 create/createfunctional 함수가 있습니다.
            • 일종의 mapToStateToProps가 있습니다.
            • Flux는 별도로 구독할 Store의 배열을 추가로 입력받습니다. (Redux는 단일 스토어이므로 필요 없습니다)

          차이점

          • 기능 마다의 Store를 사용하며, Multi Store로 앱이 구성됩니다.
            • Dispatcher라는 구성 요소가 있습니다.
              • Multi Store때문에 필요한 요소입니다.
              • 모든 Store는 Dispatcher에 등록됩니다.
              • Dispatcher는 Action 실행의 단일 진입점입니다.
              • Dispatcher는 등록된 모든 Store에 Action을 전달합니다.
            • 특정 Action 처리 시 Store 간의 순서를 명시적으로 결정해야 합니다.
              • 이를 waitFor라는 헬퍼 함수를 호출함으로써 구현합니다.
          • Store 정의 시에는 Store를 상속받는 class로 정의해야 합니다. (React의 Component class 같이)
          • mapDispatchToProps가 없습니다. 대신 Action Creator가 순수 함수가 아니라, dispatch 호출까지 수행합니다.

          부족한 점

          • Middleware 정도의 확장성은 없습니다.
            • 대신 Store에서 비동기적으로 Action을 발행합니다.
            • (Middleware는 대부분 비동기 동작을 하기 때문에 문제가 없어보입니다.)
           

          4. (WIP) Flux 예제 실습 - Todo List

          • 출처
            • MVC 예제
            • Chat 예제 (3.1.0 이후에 제거됨)
              • 멀티 스토어의 waitFor 사용 예시를 보기 위해 확인 필요
           

          4-1. Todolist 기능 목록

          TodoList의 기능은 아래와 같습니다.
          • list 조회
          • 신규 item 생성 (텍스트 컨텐츠 등록)
          • item 제거
          • item의 완료 여부 toggle
          • item의 텍스트 컨텐츠 수정
           

          4-2. list, delete, toggle 기능 구현 - 실습 내용

           

          addTodo 구현

          • 단일 todoStore에 addTodo 액션을 처리할 수 있게 구현한 모습
            • 마운트 후 addTodo를 단순하게 호출해서 화면만 구성
            • notion image
          • flux가 Redux와 유사한 점
            • Store 선언이 조금 다르지만, reduce = (state, action) => state인 메소드가 존재
              • reduce 메소드로 action을 반영한 상태를 반환
          • flux가 Redux와 다른 점
            • Dispatcher의 존재
              • Dispatcher의 인스턴스를 싱글톤으로 사용
              • Store에 Dispatcher를 등록
              • Action Creator가 있는데, Dispatcher를 직접 import해서 호출
                • redux에서는 actionCreator는 단순히 action 객체를 반환
                • 컴포넌트에서 mapDispatchToProps으로 dispatch를 실행
            • Store를 선언하며, class로 구성
            • Store의 인스턴스를 싱글톤으로 사용
           

          delete, toggle 구현

          notion image