CSS Gradient Editor — DOM 이벤트 시스템 연구

easylogic
7 min readFeb 20, 2019

--

에디터를 만들게 되면서 가장 심혈을 기울인 기능은 DOM 이벤트 시스템이다.

우리는 DOM 이벤트 정의 하는 것을 언제나 어려워 하고 있다. 왜냐하며 기존의 Class 시스템이랑 맞지 않기 때문이다.

이벤트 정의는 불편하다

간단히 DOM 이벤트를 정의 하는 방식을 보자.

jQuery 형태로 가면 조금 더 심플하게 바뀐다.

일반적인 형태로 관리할 때는 이렇게 해도 별 문제 없을 것 같은데 Component 시스템으로 들어오면 scope 를 매번 바꿔줘야 하는 이슈가 생긴다.

이러한 문제 때문에 보통은 아래 처럼 scope 를 주는 방식으로 재작성한다.

이렇게 DOM 자체를 다루는 방식으로 정의를 하게 되면 DOM 에 의존하는 형태가 되서 같은 작업을 계속 반복해야 하는 이슈들이 있다.

그래서 요즘 나오는 Component 시스템들은 좀 더 이벤트를 편리하게 설정할 수 있는 방법을 제시한다.

VueJS 는 아래와 같이 특정 속성에 method 를 지정하면 자동으로 매칭시켜준다. 아래 코드를 보면 scope 도 유지된다.

ReactJS 는 어떻게 하고 있을까?

React 의 경우도 Class 시스템을 그대로 사용하기 때문에 scope 문제가 발생하는데 JSX 에서 함수도 바로 생성할 수 있는 문법으로 인해서 익명함수를 만들고 arrow function 으로 scope 를 유지 해준다.

이벤트 정의의 관점을 바꾸다.

위에 나오는 모든 예제들은 DOM 을 기준으로 이벤트를 설정한다. 어떻게든 DOM 과 연결된 메소드를 만드는게 핵심이다.

그런데 그러한 부분들이 하지 않아도 되는 여러가지 구문들을 자꾸 만들어내게 되는데 그걸 하지 않기 위해서 여러 고민을 하게 된다. 그러던 와중에 DOM 관점에서 이벤트를 정의하는 관점을 Class 기준으로 바꾸게 됐다.

이렇게 한 이유는 혼자서 거대한 어플을 계속 만들 수 있는 심플한 방법이 필요했는데, 그렇게 될려면 문법을 최소한으로 가져가야 하는 부분도 있었다. 이것은 함수를 잘 만들면 되는 부분이기도 하다. 그렇지만 타이핑도 극도로 줄여보고 싶었기 때문에 기존의 문법체계를 해치지 않고 Class 기준으로 생각할 수 있는 방식이 필요했다.

아래 예제를 한 번 보자.

template 으로 정의한 <button> 은 자동으로 this.$el 로 매칭되고

[CLICK()] (e) {} 으로 정의한 메소드는 자동으로 this.$el 에 scope 변경없이 addEventListener 함수를 통해서 정의된다.

이렇게 함으로써 생기는 이득이 있다.

  1. 이벤트 함수 정의 할 때 scope 를 신경 쓰지 않아도 된다. (scope 는 항상 class 의 this 가 된다. )
  2. 이벤트 함수 이름을 정의하고 해당 메소드랑 매칭하는 코드가 빠진다. (예를 들어 <div onClick=”clickSample”></div> 이런 부가코드를 작성할 필요가 없다.)
  3. 모든 함수는 class 기준으로 생각해서 처리하면 된다. 즉, 생각의 범위가 단순해진다. (이제부터 이벤트 정의 하는 것은 죽 메소드만 나열하면 되는 것이다. )

메소드로 모드 분리가 가능하고 해당 메소드는 특정한 이름을 가지는게 아니라 행위만 가지기 때문에 행위를 정의 할 수만 있으면 독특한 형태를 가질 수 있다.

메소드 정의의 규칙을 바꾸다.

일반적으로 하나의 메소드는 보통 하나의 이름을 가지고 하나의 일을 수행한다.

하지만 여기서는 메소드의 행위에 대해서 기술 하기 때문에 정의한 메소드가 실행될지 안될지도 설정할 수 있다.

예를 들어 특정 키를 클릭했을 때만 이벤트가 실행되게 하고 싶을 때는 아래 처럼 할 수 있다.

여기서 if 를 빼고 특정 키를 클릭한 시점에 메소드를 실행할 수 있다면 어떨까? 그럼 이벤트 메소드 정의는 어떻게 해야할까?

그래서 나온 방법이 아래와 같다.

이미 누군가가 정의 해놓은 체크 방식을 메타 데이타 처럼 빼는 것이다.

[CLICK() + ALT] 라고 정의된 메소드 명의 최종 결과물은 ‘click $el | alt’ 로 바뀐다. 내부적으로 정의된 시스템에 의해 메소드명을 분리해서 최종 실행 함수로 만들어준다.

if 를 안쓰게 되면 메소드가 훨씬 간결해지고 하고자 하는 방향도 정확하게 기술 할 수 있다. 최종적으로는 메소드가 해야하는 행위를 잘 기술하게 하면 되는 것이다.

일단 코드량도 줄었고 의미상으로도 명확히 확장되는 결과를 되었다.

규칙을 확장하다.

이제 메소드를 어떻게 정의해야될지 알았으니 규칙을 좀 더확장해보자.

화면상에 드래그 해야 하는 객체가 있을 때 보통은 mousedown, mousemove, mouseup 함수 3가지를 사용해서 핸들링 한다. (모바일은 touchstart, touchmove, touchend 함수를 사용할 수 있다. )

일단 3가지 메소드를 정의해보자.

  1. mousedown 이 일어날 때 this.isDown 을 설정해서 마우스를 눌렀다는 표시를 하고
  2. mousemove 가 일어 날 때 this.isDown 가 true 일 때만 메소드가 실행되게 하고
  3. mouseup 을 하게 되면 this.isDown 을 false 로 지정해서 더이상 mousemove 가 실행되지 않도록 한다.

사실 이것만 해도 심플하긴 하지만 저기에 isDown 이 아니라 다른 조건이 들어가면 결국은 복잡한 if 를 계속 나열 해야한다.

그래서 애초에 if 가 되는 부분을 다른 메소드로 분리하고 체크할 수 있도록 메소드를 재정의 할 수 있다. 아래 코드를 보자.

CHECKER 라는 메소드를 통해서 특정 이벤트 함수가 실행되기 이전에 미리 상태를 체크 해줄 수 있도록 한다. 즉, 실행되는 행위랑 상관 없는 부분은 다른 메소드로 분리 할 수 있는 것이다. (많은 if 를 줄일 수 있게 되었다. )

이로써 이벤트 정의 하는 방법이 끝났다. 기본적인 문법은 모두 메소드 명이 문자열이 될 수 있기에 가능한 스펙이다. 그렇게 보면 문자열로 특정 메소드 정의를 좀 더 쉽게 할 수 있다면 어떠한 스펙도 넣을 수 있게 된다. 예를 들어 debounce 옵션도 쉽게 넣을 수 있다.

DOM 을 확장하다.

이벤트 정의 하는 방식은 모두 알아봤다. 그 외에도 옵션들이 몇가지 있지만 시스템 자체는 이 틀에서 바뀌지 않는다. 그저 나머지를 정의만 하면되기 때문이다.

지금까지 만들어진 예제들은 모두 this.$el 이라는 각 Component 의 root element 를 대상으로 적용되었다.

하나의 template 에는 root 만 있는 것이 아니기 때문에 내가 원하는 DOM 에 이벤트를 적용 시켜 줄 수 있어야 한다.

그래서 DOM 을 선택할 수 있는 선택자를 확장한다.

template 를 정의 할 때 개별 element 들은 ref 속성을 가질 수 있는데 이것은 vuejs 나 reactjs 처럼 dom 을 미리 캐슁하기 위해서 사용되어진다. 이렇게 캐슁된 dom 에 이벤트를 적용할 수 있게 된다.

위 소스는 $targetElement 에 CLICK 이벤트를 지정한 상태이다. 기존과 별반 다를게 없는 형태이다.

이렇게 간단한건 잘 되는데, 실제로 엄청나게 많은 아이템을 가진 리스트에는 이벤트를 어떻게 적용할까? 단순히 ref 로 이름을 주기에는 너무 많은 element 를 캐싱해야 할 수도 있다.

일단 여기서는 jquery 에서 주로 사용하던 delegate 방식을 적극 활용한다. 즉 부모 element 에 이벤트를 걸고 이벤트가 발생된 target element 를 체크해서 처리하는 것이다.

[CLICK(‘$el button’)] (e) { }

this.$el 의 하위에 있는 button element 가 Click 됐을 때만 메소드를 실행한다. 클릭된 button element 는 e.$delegateTarget 이라는 객체로 가지고 올 수 있다.

하위의 element 에도 이런 식으로 쉽게 이벤트 정의를 할 수 있게 된다.

마무리

DOM 을 기존의 방식대로 처리 해도 되지만 그렇게 되면 불필요한 코드가 너무 많아진다.

그러한 것들을 문법이 허용하는 한 심플한 구조로 만들 수 있다면 하나의 메소드가 추구해야 하는 목적에 좀 더 다가갈 수 있지 않을까 한다.

DOM 이벤트 시스템을 구축하면서 클래스를 활용하는 방식을 다시 생각하게 된게 좋았고 언어의 스펙을 좀 더 자유롭게 사용할 수 있게 되었다.

기존의 방식에서 새로운 방식으로 나아가는건 힘들지만 그만한 가치가 있는 것 같다. 생각하는 방식이 완전히 달라지고 있다.

--

--

easylogic
easylogic

Written by easylogic

걸작을 만드는 사람. 에디터를 만드는 사람.

No responses yet