Ember

Ember - Tutorial(Routing System)

모_아이 2024. 4. 24. 17:17

업무 진행을 위해 Ember를 처음부터 배워보고 있는데 단순히 보고 Tutorial 을 따라한 것으로는 부족한 것 같아 정리 차 작성해보려 한다

모든 내용은 Ember 공식문서의 Tutorial과 예전 버전이긴 하지만 2022 ember beginners라는 외국 유튜브를 보고 적절히 섞어 작성하였다.
물론 Test에 관련된 부분은 적당히 제거했다

먼저 tutorial에서 만들어지는 웹사이트는 공식문석에 실제로 netlify를 통해 배포되어 있으니 직접 확인해 봐도 된다
Tutorial완성페이지 확인하기


Ember CLI 설치

먼저 Ember를 쉽게 사용하기 위한 CLI를 먼저 설치 했다

npm install -g ember-cli

설치가 잘 완료되었는지 확인해보자

ember --version

입력시 이런식으로 출력이 잘 되는지 확인해보자

EmberCLI로 새로운 Ember앱 만들기

EmberCLI 명령을 사용해서 새 프로젝트를 사용할 수 있다. create react-app처럼 간단히 만들 수 있는 명령어인데 패턴을 살펴보자면
ember new <project-name>으로 project-name 부분에 원하는 프로젝트 이름을 작성시 자동적으로 만들어 진다.
이때 공식문서에는 나와있는데로 뒤에 --lane en을 통해 앱의 기본 언어를 영어롤 설정시키자. 웹 사이트의 접근성이 향상된다

ember new test-project --lang en

그렇다면 새로 생긴 test-project폴더에 ember 프로젝트가 생긴 걸 확인할 수 있다.
이후 해당 폴더로 이동하여 npm start를 실행할 경우 아주 기초적인 웹사이트가 하나 뜨는 걸 확인할 수 있다
주소는 http://localhost:4200 으로 기본 설정 되어 있다


파일 구조 살펴보기

처음 만들어질 때 파일 구조를 잘 살펴보면 아래와 같이 만들어져 있을 것이다.

test-project
├── .github
│   └── workflows
│       └── ci.yml
├── app
│   ├── components
│   │   └── .gitkeep
│   ├── controllers
│   │   └── .gitkeep
│   ├── helpers
│   │   └── .gitkeep
│   ├── models
│   │   └── .gitkeep
│   ├── routes
│   │   └── .gitkeep
│   ├── styles
│   │   └── app.css
│   ├── templates
│   │   └── application.hbs
│   ├── app.js
│   ├── index.html
│   └── router.js
├── config
│   ├── ember-cli-update.json
│   ├── environment.js
│   ├── optional-features.json
│   └── targets.js
├── public
│   └── robots.txt
├── tests
│   ├── helpers
│   │   └── index.js
│   ├── integration
│   │   └── .gitkeep
│   ├── unit
│   │   └── .gitkeep
│   ├── index.html
│   └── test-helper.js
├── .editorconfig
├── .ember-cli
├── .eslintcache
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── .stylelintignore
├── .stylelintrc.js
├── .template-lintrc.js
├── .watchmanconfig
├── README.md
├── ember-cli-build.js
├── package.json
├── package-lock.json
└── testem.js

16 directories, 37 files

하나씩 살펴보자면

  • app 폴더
    • 기본 폴더로 우리가 주로 봐야할 소스 파일들이 존재하는 곳이다(templates,components 등)
  • config 폴더
    • 환경 변수가 여기에 설정되는 것과 같은 일부 구성 정보가 포함되어 있다
  • public 폴더
    • 이미지나 일부 문서와 같은 모든 정적파일을 저장하는 공용 폴더
  • tests 폴더
    • 모든 test파일

라우팅 시스템

ember에는 브라우저 URL과 통합되는 라우팅 시스템이 함께 제공된다.
예를 들면 우리가 localhost:4200/cart로 가려 한다면 실제 app/router.js 내부에서 카트경로를 검색 하기 시작한다.

// ...app/router.js

Router.map(function () {
  this.route('cart');
});

이때 찾고있던 관련 항목을 찾으면 전체 앱이 어떻게 구성되어 있는지 빠르게 확인할 수 있도록 사이트맵 역할을 한다
이때 구동되는게 app/routes/cart.jsapp/controllers/cart.js이고 이를 통해 우리가 만든 app/templates/cart.hbs가 템플릿 렌더링을 시작하게 된다.

라우팅에도 여러가지가 있는데

  • Application route
  • Basic route
  • Nested route
  • Index route
  • Dynamic route
  • 404 not found route

등이 있다.
하나씩살펴보자

Application route

먼저 ember에는 HCBS의 app/template/application.hbs 으로 시작되는 기본 애플리케이션 경로가 있다
이는 웹 앱의 시작점이기도 한데 최상위에 존재하는 react로 보자면 main이나 next의 최상위 layout이라고 생각하면 편할 것 같다.
모든 곳에서 쓰이는 부분을 이곳에 적용시키면 모두 적용된다.
Tutorial에선 nav-barapplication.hbs에 적용한 다음 {{outlet}}을 통해 하위 요소들을 렌더링 시킨다.
마치 react 최상위 요소에서 navbar컴포넌트를 넣고 아래에 {children}을 통해 각 페이지들이 렌더링 되도록 하는 것과 같았다

Basic route

기본 경로이다.
ember cli를 통해 만들어 낼 수도 있고 직접 파일을 만들 수도 있는데 ember cli가 편한 방법 같기도 하다.
아직 ember에 대해 부족한 부분이 많아 CLI로 만드는게 좋은것인지 직접 만드는 게 좋은것인지 잘 모르겠지만 일단 Tutorial과 beginner 강의가 알려준대로 따라가보자.

ember g route clothes
// 또는
ember generate route clothes

이렇게 작성할 경우 저절로 template에 관련 파일이 생성되고 app/router.jsRouter.map 함수 안에도 저절로 입력되어 있을것이다.

//...app/router.js
Router.map(function(){
    this.route('clothes')
})

이러면 localhost:4200/clothes로 이동할 경우 app/templates/clothes.hbs 에 입력한 요소들이 출력되게 된다.

Nested route

중첩된 경로로 /clothes/t-shirt와 같이 경로에 경로로 들어가는 경우이다.
예로 현재 clothes라는 옷 경로를 사용중인데 가장 가까운 하위 카테고리로 티셔츠로 들어가야 한다. 이때는 어떻게 구성을 하고 만들어야 할까??
생각보다 간단한데 이전 clothes경로를 만들 때처럼 ember CLI를 사용하면 된다

ember g route clothes/t-shirt

clothes는 이미 만들어져 있고 그에 중첩된 경로를 /를 통해 작성해주면 되는데 이러면 app/tmeplates폴더에 clothes라는 폴더가 생성되게 되고 그안에

그리고 router.js내부 사항도 변경되게 되는데

Router.map(function(){
    this.route('clothes',function(){
      this.route('t-shirt')
    })
})

처럼 변하게 된다.

상위 루트의 복사 기능 내에 루트 설정이 추가되는 것이다.(말이 번역본이라 어렵긴 한데 javascript 문법적으로만 보자면 Router라는 class는 app/router.js 안에서 선언된 클래스로 EmberRouter를 상속받고 있다.
그리고 Router 내부에서는 위에서 얘기했던 config폴더의 환경변수 파일 environment.js에서 locationtyperootURL을 덮어 씌우고 있다.

이후 Router클래스의 map메서드를 실행시키는데 map 메서드는 RouterDSL이라는 콜백함수를 받는다. 해당 콜백함수 안에는 route라는 메서드가 존재하고 route메서드는 우리가 연결한 url경로 이름을 첫번째 매개변수로 받고 option(경로 이름과 파일 이름을 서로 다른걸 사용하고 싶을때)과 또 같은 RouterDSL을 콜백함수로 가지는 콜백함수를 옵셔널로 실행할 수 있다.

여기서 중첩 라우팅일 경우 route안에 route로 중첩해서 사용하는 방식으로 this.route를 통해 clothes 경로를 만든뒤 바로 위에서 설명한 옵셔널한 콜백함수를 다시 작성하여 this.route t-shirt를 넣는것이다.

이후 app/templates/clothes/t-shirt로 들어가면 t-shirt에서 작성한 HTML 요소들이 보일 것이다.

Index route

각 슬래시 마다 기본적으로 보여주는 페이지들이 있는데 이를 index를 통해 관리 할 수 있다.
예를 들어 clothes라는 경로로 이동했을 때 물론 clothes.hbs라는 파일 내에서도 작성할 수 있지만 무언가 기본적으로 표시되고 싶은 걸 파일로 나누고 싶다면 index.hbstemplates/clothes폴더안에 생성할 경우 자동적으로 clothes.hbs{{outlet}}부분에 표시된다.
이때 경로를 clothes/t-shirt로 들어갈 경우 index.hbs에서 작성한 요소는 표기되지 않고 t-shirt.hbs에서 작성한 요소만 표기되며
/clothes만 들어갈 경우 clothes.hbs{{outlet}}부분에 index.hbs의 요소들이 자동적으로 표기되게 된다

각 라우팅들에서 표시될 수 있는 기본 값이라고 생각하면 편할 것 같다.

Dynamic route

동적 라우팅으로 동적실행 또는 동적 세그먼트이다

주로 사용 되는게 게시물이나 상품의 ID를 경로에 넣는 것이 있다. item/123이면 123번의 item을 보여달라는 것과 같다
지금 item/123의 경우 URL의 마지막 세그먼트 인스턴트인데 한번만들어보자
똑같이 emberCLI를 통해 만든다면

ember g route item

으로 한번 만들어보자 이경우 위에서 router.js 에 새로 만들어지는 것처럼

Router.map(function(){
    this.route('clothes',function(){
      this.route('t-shirt')
    })
    this.route('item')
})

가 생성되는데 조금 변경시켜보자

Router.map(function(){
    this.route('clothes',function(){
      this.route('t-shirt')
    })
    this.route('item',{path:"/item/:item_id"})
})

아까 말했던 옵셔널로 주어지는 인자중에 표기와 이름을 다르게 하는 옵션인자를사용해 {path:"/item/:item_id"}를 통해 실제 url은 item/과 마지막에 :item_id가 동적 세그먼트로 들어올수 있도록 한다

이후 실제로 /items/1으로 경로를 이동해서 실제테스트 해보자.

화면은 잘 뜨는데 그럼 동적 라우팅한 값은 어디서 얻을 수 있을까?

app/routes 폴더로 이동해보면 emberCLI를 통해 generate로 만들 때 함께 만들어진 clothes.jsitem.js가 자동적으로 만들어져 있을 것이다.
이때 item에 대해서 동적으로 받는 것에 대한 모델링이 필요함으로

import Route from '@ember/routing/route'

export default class ItemRoute extends Route {
    model(params) {
      const {item_id} = params;
      return item_id;
    }
}

이렇게 작성하면 이젠 정상적으로 렌더링 되는 것을 확인할 수 있다
그럼 이 모델에서 returnitem_id는 어디서 어떻게 받을 수 있을까? 하고 의문을 가지는데 이는 item.hbs에서 사용할 수 있다

// app/templates/item.hbs
<h1> {{this.model}} </h1>

이렇게 작성할 경우 this.model부분이 아까 return item_id부분이 되게 된다.

이를 살펴볼때 위에서 설명한 ember의 라우팅 부분에서 마지막 hbs파일에 도달해서 렌더링 하기 전에 route폴더와 controller 폴더에서 해당되는 js들을 실행시키고 오는 것 같다.
마치 class형 react를 했을 때 처럼 사용되는 함수들은 route폴더 안의 js파일에 메서드들로 작성하고 실행은 hbs에서 {{this.함수명}}으로 실행시키는 것이다.

이때 주의할 것으로 실제로 에러를 겪었던 부분인데 이름의 중요성이다. item이라는 경로를 사용 했는데 templates에서도 item.hbs여야 하고 routes에서도 item.js 그리고 실행되는 class도 ItemRoute로 대소문자까지 잘 지켜줘야 실행되는 걸 볼 수 있다.
때문에 직접 만들 경우 오타의 문제가 생길지 모르니 emberCLI를 사용하는 게 더 좋은 것 같다는 생각이 든다

404 not found route

우리가 흔히 보는 에러페이지로 next 등에는 404.js사용하는 것처럼 사용할 수 있다.
예를 들어 우리가 지정하지 않은 경로로 사용자가 이동 하였을 때 사용자는 개발자가 아니기에 콘솔창을 확인해 보지 않는다.
이로 인해 비어 있는 페이지가 사용자를 반기게 되고 사용자는 페이지가 로딩중인지 에러가 났는지 전혀 알 수가 없는 상태가 된다.
이러면 유저 입장을 전혀 고려하지 않은 설계가 됨으로 이러한 에러 페이지는 필수라고 생각한다.

ebmerCLI를 통해 만들어보자

ember g route not-found

이후 작성된 경로부분을 수정한다.

Router.map(function(){
  this.route('clothes',function(){
    this.route('t-shirt');
  });
  this.route('item',{path:"/item/:item_id"});
  this.route('not-found',{path:'/*path'});
});

이렇게 저장할 경우 허용되지 않은 URL로 들어간다 하더라도 콘솔 오류는 사라지고 application.hbs에서 작성되어 있는 요소는 그대로 출력되는 것을 확인할 수 있다.


이후 아래엔 not-found.hbs에 작성한 에러임을 알리는 요소들이 출력되는 것을 볼 수 있다.

이에 에러 페이지에 다시 되돌아가는 버튼을 만들어 놓는다면 사용자 측면에서 이 페이지가 에러고 다시 돌아가면 되겠다라는걸 명확하게 보여줄 수 있게된다.


여기까지가 ember에 있는 라우팅 시스템이고 더욱 자세하게 알아본다면 라우팅 시스템 안에 리디렉션과 전환 방지나 재시도 등 여러가지가 있는데 tutorial이 끝나면 또 하나씩 정리 해봐야겠다.

반응형