처음 글에 이어 css gradient editor 를 만들면서 생각했던 것들을 이야기 해보자.
처음부터 생각하다.
이 전의 나는 프로그램을 시작하면 항상 라이브러리 기준으로 생각을 했다. 예를 들어 jquery 를 사용할 때는 jquery 의 플러그인을 만드는 것에 집중을 했고 vuejs 나 react 를 사용할 때는 그 들이 만들어놓은 나름의 규칙 안에서 생각을 했다. 생각하지 않았던 라이프 사이클에 대해서 고민하게 됐고 이벤트 감지와 변경 시점에 대한 변화 등 데이타 기준으로 고민하게 되었다.
하지만 이런 것들이 나의 프로그램들이 안전하다거나 더욱 잘 만들수 있게 해준다던가 하는 기준보다는 항상 하는 프로그램을 조금 다르게 만든다는 느낌이 많이 들었다. (내가 이해를 잘 못해서 그럴 수도 있다.)
css gradient editor 를 만들기로 마음 먹고 난 이후 시중에 유통되는 대부분의 프로토타이핑 툴의 메뉴얼들을 한 번씩은 다 본 것 같다.
그러면서 든 생각은 이게 CSS와 JS 로 만들어도 되는 스펙인가?에 대한 의문이었다. 이렇게 큰 스펙을 혼자서 만들어본 적이 없기 때문에 오는 불안함일 수도 있고 그렇게 만들어진 제품이 생각보다 많이 없었고 있다 하더라도 대부분 회사 단위에서 나오고 있던 것들이라 사람이 많이 필요하지 않을까 생각했다.
결론적으로 말씀 드리면 나름 큰 규모의 JS 로 만들어진 어플도 구조만 잘 잡으면 혼자서도 어느 정도까지는 가능하겠다는 생각이 들었다. 나머지는 규모의 경제(즉, 기획하고 개발하는 방향에 여러명이 동시에 개발 하는 회사 같은)에 들어 가는 것 같다.
어플리케이션 만드는 구조를 생각해보자.
아무런 라이브러리 도움 없이 만들어야 했기 때문에 사실 막막했다. 처음부터 큰 어플을 만들 때 어떻게 해야하는지 몰랐었다. 그래도 이왕 시작 한거니 깐 해보자는 심정으로 시작 했다.
일단 Color Picker 를 만들면서 나름 template, component, dom event 에 대한 구조에 대한 정의를 했기 때문에 이걸 기준으로 생각했다. 여기에 ColorPicker 를 만들면서 부족한 부분을 채워넣기로 했다.
- 부모/자식 Component 끼리의 메세지 전송
- 부모/자식 관계를 넘어선 Component 끼리의 메세지 전송
- Component 와 상관 없는 행위들과 그것들에 반응하는 Component 설계
어플리케이션을 하나의 UI 객체 처럼 생각했다. 그래서 지금도 실제 코드가 하나의 라이브러리가 실행되는 방식이다.
생각하다 보니 메세지를 전송하고 메세지를 받고 그것에 반응하는 체계가 가장 필요했다. 요즘으로 생각하면 React redux 나 VueJS 의 Vuex 같은 공통의 저장소와 어떤 규칙이 정해진 룰이 필요한 것이다. 하지만 결국은 라이브러리를 쓰지 않기 때문에 최소한의 스펙으로 만들어야 했다.
간단하게 몇가지를 다시 정리 한다.
- 어플리케이션을 위한 중간 저장소($store) 를 만든다.
- 저장소를 관리하는 Module 을 만든다. (Module 은 Component 와 상관 없는 어떤 행위를 모아 놓은 것이다.)
- Module 은 저장소의 데이타를 가공(Action)하거나 조회(Getter) 하는 기능을 넣는다.
3. Getter 는 순수하게 데이타를 반환하는 역할을 하고
4. Action 은 데이타를 저장하고 가공하는 역할을 한다.
5. CLICK 처럼 메소드가 하나의 행위를 할 수 있도록 역할을 부여한다. 예를 들어 아래와 같이 동작한다.
[ACTION('/item/set')] ($store, id, obj) { }
[GETTER('/item/get')] ($store, id) { return ....; }
6. async, await 또는 Promise 같은 스펙은 사용하지 않고 순수 함수 입장에서 처리한다. 이렇게 하는 이유는 말 그대로 하나의 라이브러리르 만드는 방법으로 제안되어야 하는 것도 있어서 이걸 가지고 외부에서 라이브러리를 만드는 스펙을 최소화 한 것도 있다.
7. 이러한 일련의 과정들이 끝나면 Module 에서는 메세지를 보낼 수 있다.
8. Component 는 메세지를 받고 자신을 업데이트 한다.
9. 가장 중요한 지점인데 이 시스템은 양방향 바인딩이나 특정 데이타를 변경할 때 바로 변경이 일어나는 형태를 가지고 있지 않다. 세부적으로 변경이 되는 데이타가 있으면 하나의 메세지로서 전달만 가능하다.
이 부분이 중요했던 이유는 내가 변경하는 데이타가 정확히 어떤 것인줄 아는 것이 나에게는 혼자서 하는 디버깅에 너무 중요했기 때문이다.
이렇게 나열하고 보니 구조가 생각보다 복잡해 보였다. 그런데 신기하게도 $store 기준으로 생각해보니 생각의 범위가 달라진다.
- $store 에 데이타와 메세지 전달 구조, GETTER와 ACTION 을 등록해놓는다.
- Module 과 Component 는 모두 동일한 $store 를 바라 본다.
- Module 은 ACTION 을 통해서 메세지를 전달하는 역할만 하다. 특정 메세지를 받을 수 없다.
- 서로 간의 모든 메세지는 $store 를 통해서 이루어진다.
이렇게 생각하다 보니 현대 프레임워크랑 별반 다를 바 없는 형태가 되는 것 같은 느낌이 있다. (어떻게 보면 사람들이 생각하는 범위가 비슷할 수도 있다)
실제 메세지 시스템을 만들어보자.
먼저 Module 은 GETTER, ACTION 을 정의한다. 두 가지 형태는 DOM Event 와 같은 방식으로 하나의 PREFIX 로 정의되며 메소드의 목적을 알려준다. 그리고 $store 를 조작하는 방식에 대한 정의를 한다.
이제 Component 에서 사용해보자.
이 컴포넌트는 간단하게 버튼을 클릭하면 어떤 변화가 일어나는지 보여준다.
먼저 버튼을 클릭하면 /item/set 이라는 ACTION 을 실행한다. dispatch 후에 Module 에서는 CHANGE_EDITOR 이벤트를 날리게 되고 Component 에서 그 이벤트를 받은 후에 refresh 메소드를 통해 현재 상태를 GETTER 로 가지고 온 다음 최종 생성된 DOM 을 업데이트 한다.
Virtual DOM 을 쓰지 않기 때문에 특정 데이타 변경 이벤트를 받았을 때 전체 DOM 을 업데이트 하는 구조를 사용하지 않고 내가 필요한 지점만 업데이트 하도록 한다.
그리고 기존 template 로 작성된 html 객체들이 모두 변경되는 방식이라 gc 가 많이 일어날 수가 있다. 그래서 이미 생성된 DOM을 최대한 활용할 수 있게 원하는 DOM 의 속성만 바꿔주는 방식을 코딩을 한다.
한가지 더 이슈가 있는데 혹시나 메세지가 ping-pong 치는 현상을 막기 위해서 내가 만든 emit() 메세지는 나한테 다시 돌아오지 않는다. 내가 보낸 메세지가 나한테 다시 돌아오게 되면 무한 루프나 다른 이상행동의 원인이 될 수 있어서 미리 제한을 걸어두었다.
어떻게 보면 코드가 좀 길어질 수 있는데 데이타가 흘러가는 구조를 명확히 하기 위해서는 어쩔 수가 없는 선택이었던 것 같다. (아마도 이 것보다 더 심플하면서 좋은 구조가 있지 않을까 한다. 하지만 현재는 이 정도 수준에서도 나름 만족할 만한 결과가 나오고 있는 상태다. )
Module 뿐만 아니라 Component 도 $store 객체를 통해 통신 하기 때문에 Component 끼리도 emit 과 EVENT() 정의로 서로 데이타를 주고 받을 수 있다.
규칙을 완성하다.
- Component 기반으로 만들어 질 수 있고
- HTML 문자열로 객체를 만들 수 있고
- DOM Event 도 하나의 메서드로 정의 할 수 있고
- 중간 Store 로 데이타를 관리 하고
- Module 과 Component 또는 Component 끼리 메세지를 주고 받을 수 있다.
이로서 어플리케이션 만드는 기본 구조가 완성이 되었다. 이것이 얼마나 대규모의 어플을 작성할 수 있을지 알수는 없지만 최소한의 스펙은 되는 것 같다. 무엇보다 이러한 것들을 정의 하면서 메세드의 이름 정의에 대해서 다시 생각해보게 된 것 같다.
마무리
나름 간단한 규칙을 만들고 실행한다고 생각했지만 어플리케이션의 덩치가 커지고 기능이 많아 질 수록 비대해지는건 어쩔 수 없는 것 같다. 그럴 수록 더욱 더 심플한 방법이 필요할 수도 있다.
현재 컴포넌트 파일이 100여개가 넘어가고 있어서 점점 이것을 관리하는 방식도 필요한 것 같다.
그리고 나도 모르는 사이에 튜닝 해야 하는 시점이 너무 많다. 그런 것도 신경 안 쓸 수 있도록 구조를 잘 잡아야 할 듯 하다.
사실 난 기본중에 기본을 이제서야 하고 있는지도 모르겠다. 이제부터 코딩이 훨씬 재밌어지는 듯 하다.
ps.
누군가가 그랬다.
기술이 별거 있냐고
빠르고 힘이 좋으면 된다고.
언제나 그렇듯 기본에 목 마른 사람이 되면 좋겠다.