Vue.js 개발자를 위한 Ember.js Octane

ChangJoo Park
16 min readMay 13, 2020
Ember.js의 마스코트 Tomster

⛔ 이 글은 Ember.js를 홍보하는 글이 아닙니다.
⛔ 특정 라이브러리, 프레임워크를 옹호하지 않습니다.

Ember.js에서 Vue.js로 넘어가다

2015년 정도에 Ruby on Rails를 한창 하던 시기에 Ember.js를 함께 사용하였습니다. Ember.js가 Ruby on Rails 를 사용하는 개발자들에 의해 만들어져 같은 구성을 가지고 있어 통합이 꽤 괜찮았고, 별 다른 설정 없이 json-api 를 지원하는 것도 큰 장점이었습니다.

프론트엔드의 웹 컴포넌트와 구글의 Polymer의 출현으로 컴포넌트 개발 방식에 관심을 가지기 시작했고 Router와 Controller를 기준으로 많은 작업이 이루어졌던 Ember.js도 컴포넌트 방식으로 점차 변화되고 있었습니다.

2016년 경 Vue.js를 알게 되었고, 모든 것들을 컴포넌트를 기반으로 만들 수 있다는 컨셉에 수긍해서 조금씩 Vue.js를 사용하게 되었고 꽤 만족감이 높았습니다.

어쩌다보니 Vue.js가 주로 사용하는 라이브러리가 되었고, 공식문서 번역 및 커뮤니티 참여 등을 하면서 점점 더 Ember.js를 사용할 일이 없었습니다. 솔직하게 말하면 굳이 러닝커브가 가파른 프레임워크를 사용할 필요가 없는 것에 가깝습니다.

다시 Ember.js 를 사용한다면?

🔔 실제로 회사 프로덕트에서 사용할 생각은 없습니다.
🔔 스타트업 기술 블로그를 Ember Octane으로 바꿀 생각은 있습니다.

그럼에도 불구하고 Ember.js에는 꾸준히 관심을 가졌는데 2019년에 와서야 최근의 라이브러리 스타일에 가까워졌습니다. 장황한 내장 메소드들을 제거하고 네이티브 클래스를 적극 활용하는 방식으로 바뀌어 Plain JavaScript에 가까워지고 있습니다.

이전에는 데이터 하나를 변경하는 경우에도 this.set('counter', 1) 같이 사용하고, 가져올때는 this.get('counter') 를 이용하곤 했습니다. 최신 자바스크립트 라이브러리에서는 당연히 this.counter = 1 같이 사용하고 있습니다.

최근 Ember.js 는 Ember.js Octane Edition(엠버 옥테인 으로 발음)이라는 새로운 릴리즈를 하였는데, 그동안의 컴포넌트 기반 라이브러리에 많은 영향을 받은 것으로 보입니다. 정확하게 말하자면 Glimmer.js를 Ember.js가 정식으로 품게 되면서 Ember Octane가 되었습니다.

회사에서는 Vue.js를 사용하고 있고, 요즘 프론트엔드 라이브러리의 선택지는 React.js, Vue.js 가 주도하고 있고 조금 더 과감하면 Svelte나 Lit를 사용하지 않을까 싶습니다.

React.js는 이미 Hook 기반으로 사용되고 있고, Vue.js도 3버전에 오면서 같은 흐름을 가져가고 있습니다. 이러한 흐름이 있지만 꼭 그래야할 필요는 없다고 생각해서 다시 Ember.js 를 돌아보려고 합니다.

알아볼 것들

Ember.js 의 큰 변화를 만들어낸 Glimmer.js에서 제공하는 Glimmer Component와 Vue.js를 비교하고 Vue.js를 쓰는 사용자가 Ember.js를 사용한다면 어떻게 해야하는지 정리해 보겠습니다. Ember.js는 프레임워크이고 Vue.js는 라이브러리이기 때문에 Vue.js의 컴포넌트를 기준으로 Ember.js를 어떻게 바라봐야하는지 살펴봅니다.

Glimmer.js는 Ember.js를 만든 Tom Dale이 만든 초경량화 컴포넌트 라이브러리입니다. Glimmer.js만 단독으로 사용할 수 있고 Glimmer.js로 만든 컴포넌트를 웹 컴포넌트로 빌드해서 사용할 수 도 있습니다.

Ember Octane에서는 기존의 컴포넌트(클래식 컴포넌트)와 함께 Glimmer.js의 컴포넌트를 함께 지원합니다. 최신 버전 ember-cli(vue cli와 유사)를 사용한다면 컴포넌트 생성 터미널 명령어를 이용하여 Glimmer Component를 만들 수 있습니다.

이 글은 Vue.js를 이미 사용하고 있는 개발자를 대상으로 Ember Octane을 소개하는 것이기 때문에 어느정도 Vue.js에 대한 이해가 있어야 합니다. Vue.js에 생소한 경우에는 공식문서 등을 보고 오시면 좋습니다.

Vue 3가 베타 단계이고 앞으로는 Vue 3를 사용하겠지만 기본적으로 Vue.js 2 를 기준으로 다룹니다. 그리고 실제로 Vue.js를 이용해 개발하면서 사용하는 여러가지 함수들 중 자주 사용하는 내용만 살펴보겠습니다.

Nuxt의 경우는 Ember.js의 fastboot 패키지와 1:1로 대응합니다. 이 글을 읽고 Ember.js에 관심이 있으신 분은 fastboot 도 알아보시면 좋습니다.

라이프사이클 훅

Vue.js는 거의 대부분의 상태 사이사이마다 라이프사이클 훅을 제공합니다. 그러나 기본적으로 Ember Octane은 컴포넌트 클래스의 constructor 밖에 제공하지 않습니다. Vue.js의 beforeCreate 시점에 호출됩니다.

Vue.js에서 제공하는 엘리먼트와 관련된 상태의 훅을 사용하려면 https://github.com/emberjs/ember-render-modifiers 를 사용해야하는데 이 라이브러리를 사용하면 mounted(did-render) 훅과 updated(did-update) beforeDestroy(willDestroy)훅을 사용할 수 있습니다.

Ember Octane은 네이티브 클래스의 라이프사이클 훅인 constructor 만 컴포넌트 클래스코드 안에서 정의하고, ember-render-modifiers에서 제공하는 did-render, did-update, will-destroy는 컴포넌트의 DOM 엘리먼트에서 호출해야합니다.

다양한 훅을 제공하지 않지만 자주 사용하는 최소한의 컴포넌트 라이프사이클 훅을 가지고 있고 추가로 modifier를 이용해 확장하는 것을 권장gkqslek. Ember Octane 이전에는 Vue.js 에서 제공하는 대부분의 라이프사이클 훅을 가지고 있었으나 Glimmer.js의 컴포넌트를 사용하는 시점에서 모두 제거되고 constructor만 남게 되었다.

Ember Octane에 Glimmer.js를 통합하면서 발생하는 차이점은 Glimmer.js는 didInsertElement 훅을 제공하지만 Ember Octane에서 이 훅을 선언하면 Ember.js 애플리케이션에서는 didInsertElement 훅을 지원하지 않으니 @ember/render-modifiers 를 사용하라는 에러 메시지를 보여줍니다.

Vue.js 3 부터는 라이프사이클 훅의 이름에 onCreated 처럼 on 을 붙여서 사용합니다.

데이터

Ember Octane은 네이티브 클래스를 활용하는 것을 큰 특징으로 내세우고 있어 Vue.js 처럼 명시적으로 기능에 따라 코드 블럭을 나누지 않습니다. 다만 데코레이터를 적극 활용하여 어떤 프로퍼티가 옵저버(Ember Octane은 tracked라고 함)를 가지는지 알 수 있습니다.

구분된 코드 블럭은 가독성에 큰 장점이 있으나 네이티브 클래스를 이용하는 경우 클래스 변수의 경우는 변수처럼, 메소드의 경우는 메소드처럼 보이기 때문에 특별히 data, computed, methods 등의 구분을 할 필요가 없습니다.

Vue 3 도 Ember Octane 과 마찬가지의 흐름을 보이는데 setup 메소드 안에서 메소드나 데이터, computed properties를 정의하고 메소드 마지막에 리턴하는 방법을 사용합니다.

data

Vue.js 의 data는 옵저버를 가진 프로퍼티를 만드는 역할을 하는데, Ember Octane에서 @tracked 데코레이터를 가진 클래스 변수와 동일한 역할을 합니다. 값이 변경되면 template에 변경사항을 자동으로 반영합니다.

Vue.js 3 부터는 Ref, Reactive 두가지 메소드를 이용해 옵저버를 가진 데이터 속성을 정의 할 수 있다. 자세한 내용은 Vue Composition API 를 확인하세요

props

Vue.js의 props는 외부로부터 전달받는 속성을 의미하는데 Ember Octane은 this.args 를 사용합니다. 기본값이나 타입체크, 검증 등의 기능은 제공하지 않습니다. 만약 데이터의 검증이 필요하면 직접 구현하거나 Ember Data를 사용합니다. Vue.js와 Ember Octane 모두 외부에서 전달 받은 값의 경우에는 불변(immutable) 속성입니다.

Vue.js는 props를 정의하여야 사용할 수 있으나 Ember Octane은 추가적인 정의 없이 템플릿의 컴포넌트 정의에서 지정된 내용을 this.args 로 접근합니다.

컴포넌트 클래스 메소드에서 this.args.title 와 같이 사용합니다. 모든 원시 속성들과 메소드까지 전달할 수 있습니다.

Vue.js 3 에서는 setup 메소드의 파라미터로 props를 사용합니다 setup(props) 와 같이 선언하면 됩니다.

watch

Vue.js에는 watch 를 제공하여 데이터의 변화를 추적할 수 있는데, Ember Octane은 네이티브 클래스를 활용하기 떄문에 명시적인 watch 기능을 제공하지 않습니다. 대신 setter를 선언하여 값이 변화하는 시점에 원하는 작업을 할 수 있습니다.

computed properties

Ember Octane은 computed properties를 네이티브 클래스의 getter, setter를 이용해서 해결하는데, firstName, lastName을 연결해서 보여주는 fullName 이라는 속성이 있을 때 아래와 같이 표현합니다.

Vue.js에서 제공하는 캐시는 제공하지 않습니다.

Vue.js 3는 const myComputed = computed(() => 1) 과 같이 computed 메소드를 이용하여 선언합니다.

methods

Vue.js는 methods 를 메소드 선언에 사용하는데 선언한 함수는 템플릿에서 함수 이름으로 호출할 수 있습니다. Ember Octane에서는 네이티브 클래스의 함수 선언을 하고 템플릿에서 호출하는 경우 @action 데코레이터를 추가하면 됩니다. @action 데코레이터를 가지지 않은 함수는 자동으로 컴포넌트 클래스 안에서만 호출하는 함수로 간주됩니다.

위 예제에서 this.increment 함수의 기본 파라미터는 event 이고, increment 메소드에 amount 라는 증가량을 전달하고 싶으면 템플릿과 메소드를 다음과 같이 변경해야 합니다.

파라미터를 추가하려면 fn modifier를 이용합니다. fn modifier에 이어 사용할 함수와 전달할 값을 지정하면 됩니다.

템플릿

Vue.js는 Single File Component를 큰 장점으로 가지는 라이브러리입니다. 이 때문에 한 컴포넌트 안에서 일어나는 일들을 한 파일만 열어봐도 알 수 있는 장점이 있습니다.

template에 선언된 템플릿에는 script 영역에 있는 data, computed properties, methods를 사용할 수 있습니다. Ember Octane도 같습니다. 데이터 섹션에서 살펴본 것과 같이, firstName, lastName과 fullName 을 템플릿에 mustache 를 이용해 표현할 수 있다.

Vue.js 2,3 그리고 Ember Octane 모두 같습니다.

template 구성에 큰 차이가 있다면 Ember Octane은 Handlebars 문법을 적극적으로 활용합니다. 정확히는 Handlebars 문법을 처리하는 glimmer-vm을 이용합니다.. Handlebars 문법을 차용하므로 v-bind라거나 v-on같은 키워드가 없습니다. DOM 엘리먼트 텍스트에 적는 것 처럼 mustache를 바로 사용합니다.

Vue의 경우에는 v-bind을 이용해 보간을 하지만 Ember Octane은 Handlebars 템플릿을 처리할때 DOM 엘리먼트 텍스트와 동일하게 mustache가 있으면 해당 데이터를 이용하여 렌더링합니다.

메소드의 경우에 v-on 혹은 @ 를 사용하는 Vue가 훨씬 간단하게 표현하는 것으로 보입니다. Ember Octane의 경우에는 Handlebars 문법의 한계인지 조금은 장황합니다.

Ember Octane의 메소드 호출은 on, “이벤트” 함수 순으로 호출합니다. Ember Octane의 경우가 이렇고 Glimmer.js는 아래와 같이 작성해야합니다.

아직 통합이 완전하지는 않아 서로 다른 문법을 가지고 있으므로 주의해야합니다.

Vue.js 에서 가능한 v-on에서 연산을 하는 코드는 Ember Octane에서는 불가능합니다.

이런 코드는 Ember Octane에서 할 수 없으니 반드시 increment 와 같은 함수를 선언하고 호출해야합니다.

조건부 렌더링

Vue.js와 Ember Octane 모두 if를 이용한 조건부 렌더링을 할 수 있으나 문법이 약간 다릅니다. v-if를 쓰거나, {{ if }} 를 쓰는 차이 정도가 있습니다. Ember Octane은 v-show같은 역할을 하는 기능은 제공하지 않습니다.

Ember Octane은 if를 이용해 Vue.js에서 class를 바인딩 하는 것 처럼 만들 수 있습니다.

Ember Octane은 if를 이용한 삼항연산을 하는 방식으로 class에 바로 mustache문법을 사용할 수 있습니다

루프

조건부 렌더링과 같이 Ember Octane도 v-for에 해당하는 each 키워드를 제공하는데 이는 역시 Handlebars의 문법을 따릅니다. {{#each items as |item| }} {{/each}} 와 같이 사용합니다.

지시어

DOM 엘리먼트를 확장하는 directives는 Ember Octane에도 있습니다. ember-modifier 를 이용합니다.

조금 장황하지만 modifier 함수를 선언하고 element와 eventName 등을 넘겨 받아 처리할 수 있습니다. 위에서 말한 ember-render-modifier도 ember-modifier를 기반으로 만들어진 라이브러리입니다.

Ember Octane은 모든 컴포넌트가 글로벌 컴포넌트입니다. 따로 스크립트 코드로 컴포넌트를 등록하고, import를 이용해 가져올 필요가 없습니다. 컴포넌트를 만들고 필요한 곳에서 사용하면됩니다.

컴포넌트를 import 후 등록하여 사용하는 것이 트리-쉐이킹에 큰 장점을 가지지만 Ember Octane의 빌드 과정을 자세히 알지 못하는 상황이라 비교는 하지 못합니다.

Vue.js는 Webpack을 사용하고, Ember Octane은 Brocolli를 사용하여 두 빌드 도구의 차이를 살펴보면 알 수 있을 것으로 생각합니다.

Ember Octane도 Vue.js와 같이 “Data Down, Action Up” 이라는 기본 룰을 따릅니다. 부모 컴포넌트는 자식 컴포넌트에 데이터를 넘기고 자식 컴포넌트는 변경된 결과를 부모에게 알려주는 방식을 말합니다.

이렇게 Parent 컴포넌트 안에 Child 컴포넌트를 정의할 수 있습니다. 이 예제는 yield (Vue.js라면 slot)를 이용한 것이므로 Child 컴포넌트로 action을 주입하려면 Parent와 Child 컴포넌트를 모두 가지고 있는 컨트롤러에 action 메소드를 정의해야 합니다. 이 글의 범위인 컴포넌트 범위 비교에서 벗어나므로 Parent 컴포넌트가 Child 컴포넌트를 바로 가지도록 바꿉니다.

Parent 컴포넌트에 callParent 메소드를 선언하고, Child 컴포넌트에 전달하면, Child 컴포넌트에서 사용할 수 있습니다.

당연하게도 if를 이용한 조건부 렌더링, each를 이용한 반복문도 가능합니다.

그밖에

Vue.js는 컴패니언 라이브러리로 라우팅을 위한 vue-router와 Vuex를 함께 제공합니다. 선택적으로 사용하겠지만 프로젝트 규모가 조금만 커져도 사용을 피할 수 없습니다. 저는 개발하는 거의 모든 프로젝트에서 사용하고 있습니다. Vue.js가 View 레이어를 담당한다고 하지만 실제로 추가로 설치하는 의존성들이 많습니다.

Ember.js는 프레임워크이기 때문에 컴포넌트, 라우터, 서비스(글로벌 상태관리로 사용)를 제공하고 있고, 추가적으로 데이터 모델 유틸리티와 API 호출을 담당하는 Ember Data라는 도구도 포함되어있습니다.

이전 Ember.js에서 Ember Octane으로 업데이트 되면서 API가 많이 간소화 되었습니다.

Ember.js Octane vs Classic Cheat Sheet 에서 클래식 컴포넌트와 Ember Octane 버전의 차이를 보실 수 있습니다.

Glimmer.js API는 더욱 놀라운데 외부에 노출된 기능은 두가지 밖에 없습니다. Ember Octane으로 넘어오면서 컴포넌트 기반 개발에 필요한 것만 남기고 모두 Plain JavaScript를 사용하자고 작정이라도 한 느낌이 듭니다.

  • @glimmer/component
  • @glimmer/tracking

그러면 Ember Octane을 써야만 하는가?

Ember.js를 써야하는가? 라고 질문을 하는 분이이 있다면 조금 회의적입니다. 오히려 이렇게 질문 하고 싶습니다.

  • 최근 프론트엔드 뉴스 혹은 커뮤니티에서 Ember.js, Ember Octane, Glimmer.js를 들어본 적 있나요?
  • Ember.js 개발자를 찾는다는 이야기를 들어본적 있나요?

이 질문들은 Vue.js를 처음 사용할 때 면접관에게 들었던 질문입니다.
Ember.js를 Vue.js로만 바꾼채로 말이죠

다만 추천한다면 주변인들 그리고 커뮤니티의 의견에 별로 영향을 받지 않고 자신에게 맞는 프론트엔드 프레임워크를 찾는 경험을 하고 싶다면 추천하고 싶습니다. 다른 진영의 개발 방식은 원래 사용하는 개발 방식을 변화하는 아이디어를 주곤 합니다. Ember.js를 쓰다가 Vue.js를 몇년동안 쓰고 있는 것 처럼 큰 변화를 줄 수도 있습니다. 그리고 Ember.js의 기본인 Convention over Configuration 에 공감하는 개발자라면 더할나위없이 좋은 도구라고 생각합니다.

생태계는 Vue.js에 비해서 말도 못하게 열악한데, Ember Observer 에 올라온 Ember.js 관련 라이브러리만 보아도 알 수 있습니다. UI 라이브러리만 해도 몇 페이지가 나오는 Vue.js에 비하면 거의 지원이 없다고 생각이 들 정도입니다. 그나마 위안이 되는 것은 최근에는 tailwind.css가 있어서 외부 UI 라이브러리를 쓰는 일이 적다는 점입니다.

그래도 Ember.js를 해보고 싶다면

약간은 완만해진 러닝커브와 괜찮은 개발 경험을 가지는 Ember.js를 해보고싶다면 아래 웹사이트를 추천합니다.

- https://www.emberjs.com/ : 공식 가이드
- https://embermap.com/ : Ember.js를 이용한 프로젝트 가이드
- https://yoember.com/ : 실제 프로젝트를 만들며 배우는 Ember.js

구글에 검색할 때 Ember Octane 이라고 검색해야 최근의 정보를 얻으 실 수 있습니다.

--

--