2015년 7월 6일 월요일

스터디서치에 적용한 Flux

최근에 스터디서치 웹을 React 프레임워크와 Flux 구조를 사용해 리뉴얼했다. Flux 구조를 사용하면서 느낀 Flux의 장점과 겪었던 문제, 해결방법을 짧게나마 공유해보려고 한다.

Flux의 장점

웹은 모바일 앱처럼 한 화면에 하나의 MVC셋이 아닌 한 화면에 여러 컴포넌트들이 존재할 여지가 있다. Flux는 이러한 웹 특성에 잘 맞는 구조다.

1. Single source of truth

위 사진에서 내 프로필 정보를 사용하고 있는 컴포넌트는 오른쪽 위의 마이페이지 링크와 중간 위쯤 있는 마이페이지 내에서의 네비게이션, 그리고 프로필 수정 페이지 총 세개다. 이 세개의 컴포넌트는 각기 다른 모델 객체를 사용하는 것이 아닌, 스토어에서 제공하는 하나의 데이터만을 사용한다.

2. Event handling

프로필 사진이나 이름을 변경하게되면 스토어에 변경사항이 저장되고, 스토어는 변경사항을 알림받아야 할 컴포넌트들에 데이터가 변경되었음을 알려준다. 따라서 한 컴포넌트에서 변경 이벤트가 발생할지라도, 이 컴포넌트가 데이터 변경을 알리기 위해 다른 컴포넌트들을 참조할 필요가 없다.

부수적인 이벤트 처리

Flux구조의 장점을 지키려면 데이터는 단방향으로 흘러야하며, 컴포넌트가 데이터를 직접 참조하거나 변경해서는 안된다. 그렇다면 이벤트 완료 후 완료 알림과 같은 부수적인 이벤트들은 어떻게 처리할까? 처음에는 별 고민없이 ChangeListener에서 처리를 해주었다.
...
componentDidMount: function() {
  MySelfStore.addPhotoChangeListener(this._onPhotoChange);
  MySelfStore.addNameChangeListener(this._onNameChange);
  ...
},
componentWillUnmount: function() {
  MySelfStore.removePhotoChangeListener(this._onPhotoChange);
  MySelfStore.removeNameChangeListener(this._onNamgeChange);
  ...
},
_onPhotoInput: function() {
  MySelfActions.changePhoto(...);
},
...
_onPhotoChange: function() {
  ToastActions.show("사진이 변경되었습니다");
  this.setState({mySelf: MySelfStore.getMySelf()});
}
...
이런 구조는 몇가지 단점이 있는데, 우선 데이터 변경이 해당 컴포넌트에서 발생된 이벤트에 의해 이뤄진 것인지 확인하지 못한다. 또 이벤트 발생 부분과 완료부분이 떨어져있어 코드의 가독성도 떨어진다. 마지막으로 저렇게 하다간 수많은 ChangeListener가 존재해 코드가 상당히 무거워질 것이다. 이를 해결하기 위해 Promise 패턴을 이용하였다.
...
componentDidMount: function() {
  MySelfStore.addChangeListener(this._onMySelfChange);
},
componentWillUnmount: function() {
  MySelfStore.removeChangeListener(this._onMySelfChange);
},
_onBookmarkButtonClick: function() {
  MySelfActions.changePhoto(...).done(function() {
    ToastActions.show("사진이 변경되었습니다");
  }
},
_onMySelfChange: function() {
  this.setState({mySelf: MySelfStore.getMySelf()});
}
...
이렇게 함으로써 onPhotoChange, onNameChange와 같은 ChangeListener들을 줄여 코드를 가볍게 할 수 있었고, 이벤트 완료와 데이터 변경을 나누어 처리할 수 있어 가독성도 높힐 수 있었다.

댓글 없음:

댓글 쓰기