(Javascript | mithril) 단일페이지 애플리케이션 및 라우팅 규칙

m.route – 단일페이지 애플리케이션 및 라우팅 규칙

mithril 관련 글

  1. mithril 앱의 기본 구성 및 m()
  2. m.render – 가상 DOM 렌더링하기
  3. m.mountmithril을 이용한 양방향 바인딩 & 템플릿 렌더링
  4. m.prop 양방향 바인딩을 위한 데이터 래퍼
  5. m.withAttr 양방향 바인딩을 위한 이벤트 매퍼
  6. m.componentmithril 앱을 컴포넌트화하기
  7. Todo
  8. \* m.route – 단일페이지 애플리케이션 및 라우팅 규칙
  9. m.request – 서버 API와 통신하기

mithril의 라우팅은 단일 페이지 애플리케이션(Single Page Application, SPA)을 만들 수 있게 해주는 시스템으로 개별 페이지에 대한 북마크 및 브라우저의 히스토리 메카니즘을 그대로 사용할 수 있게도 해준다. 따라서 하나의 HTML 파일 및 스크립트 파일 기반으로 여러 페이지를 구현하는 것이다.

m.route()는 라우팅 시스템을 총괄하는 함수로 , 현재 페이지에서 사용가능한 URL을 정의하고, 특정 URL로 리다이렉팅하거나 현재 경로를 리턴하는 등의 기능을 수행한다.

  • m.route(rootElement, defaultRoute, routes) – 각 루트를 정의하고 루트의 URL 패턴별 대응하는 앱을 지정한다.
  • m.route(path) – 주어진 경로로 리다이렉트한다.
  • m.route() – 현재 경로를 리턴한다.

라우팅 규칙 정의

특정 HTML 요소를 경로의 패턴에 대해서 각 패턴마다 어떤 앱을 마운트할 것인지를 정의한다. 이 앱들은 현재 스코프에서 액세스할 수 있어야 한다. 다음은 간단한 라우팅 규칙 패턴이다. 만약 flask나 aiohttp와 같이 서버 사이드 프레임워크를 접한 경험이 있다면 이 부분은 좀 더 이해가 쉬울 것이다.

m.route document.body, '/', do
  '/': Home
  '/login': Login
  '/dashboard': Dashboard

기본적으로 이렇게 정의한 루트들은 다음과 같은 식으로 GET 파라미터로 루트가 전달된다.

/somepage.html?/ => '/'
/somepage.html?/login => '/login'
/somePage.html?/dashboard => '/dashboard'

mithril은 이렇게 접근한 경로에 대해서 하위 경로에 해당하는 패턴을 찾고, 각 패턴에 매치되는 라우팅 규칙이 있으면 해당 규칙이 지정한 앱을 마운팅하며, 그렇지 않은 경우에는 디폴트 루트로 리다이렉팅하는 것이다.

라우팅 주소의 변수화

라우팅 패턴은 추가적으로 변수를 URL의 일부로부터 취할 수 있다. :변수명의 패턴을 쓰면 해당 패턴은 그 이름에 맵핑되며, 이는 뒤에서 다시 설명할 m.route.param(변수명)함수를 통해서 얻을 수 있다. 변수 맵핑 패턴은 반드시 루트의 맨 마지막일 필요는 없으며 중간 부분이어도 된다.

m.route document.body, '/dashboard/defaultUser', do
  '/dashboard/:userID' : Dashboard
  ...

# ?/dashboard/johndoe -> Dashboard, m.route.param('userID') -> 'johndoe'

하위 경로를 변수로 갖는법

특정 패턴이 일부분이 아니라, 하위 경로 패턴 전체를 변수화하는 것도 가능하다. :변수명... 으로 정의하면 된다. 역시 마찬가지로 뒤쪽에 제한자 패턴이 붙으면 루트의 일부분을 변수에 맵핑하게 된다.

m.route document.body, '/files/pictures/pic01.jpg', do
  "/files/:file..." : gallery-app
# ?/files/documents/presentation001.pdf
# m.route.param('file') => 'documents/presentation001.pdf'

m.route document.body, '/blog/2016/11/articles', do
  '/blog/:date/articles': article-list-app
# ?/blog/2016/12/articles
# m.route.param('date') => '2016/12'

리다이렉트

m.route(path)를 통해서 특정 주소로 리다이렉트하는 것이 가능하다. 이 때 루트의 범위는 싱글페이지 애플리케이션 이내의 위치이며, 완전히 다른 페이지로 보내려면 document.location 속성을 쓰거나 <a> 태그를 만들어서 써야 한다.

리다이렉트할 경로에는 쿼리 스트링을 포함하는 것이 가능하며, 경로 외의 쿼리스트링을 포함하는 경우에 m.route.param은 이들의 값도 키-값 쌍으로 가지고 있다.

m.route('/grid?sortBy=date&dir=desc')
# ---
sort-by = m.route.param \sortBy # "date"
dir = m.route.param \dir # "desc"

언로딩

앱 내에서 리다이렉팅이 일어나는 경우에, 앱은 unload 이벤트를 받게 된다. 따라서 마운트된 앱의 컨트롤러가 onunload 이벤트 핸들러를 정의하고 있다면, 리다이렉트 되기 직전에 정리 작업을 할 수 있다. (혹은 변경 사항등을 로컬 스토리지에 저장하거나 하는 등의 작업을 이 시점에 해도 된다.)

Home = do
  controller: ->
    onunload: -> console.log "unloading home component"
  view: (ctrl) ->
    m \div, \home

Dashboard = do
  controller: -> {}
  view: -> {}

m.route document.body, '/', do
  '/': Home
  '/dashboard': Dashboard

m.route('/dashboard') # Home -> Dashboard로 바뀌기 전에 로그를 남긴다.

현재 경로 획득 및 쿼리 파라미터 확인

m.route()를 그냥 호출하면 현재 경로를 획득할 수 있다. m.route.param() 함수는 쿼리 스트링 및 라우팅 규칙에 정의한 변수값을 파싱하여 리턴해준다.

  • m.route.param(key) – 주어진 키(변수명)에 대한 파라미터 값을 리턴한다.
  • m.route.param() – 키를 주지 않은 경우, 파싱된 전체 파라미터의 키-값쌍을 포함하는 객체를 리턴한다.

쿼리 스트링 빌드/파싱

m.route.parseQuery(), m.route.buildQueryString()은 키-값 쌍으로 이루어진 객체를 쿼리스트링으로 직렬화하거나, 반대로 쿼리스트링을 파싱해서 객체로 변환한다. 변환시의 규칙은 URI.js의 컨벤션을 따른다.

라우팅 모드

라우팅 모드는 m.route.mode 값을 변경하는 것으로 세팅할 수 있다. 형식은 문자열이며, 기본값은 search로 되어 있다. 사용가능한 모드는 아래와 같이 정의된다.

  1. search : 단일페이지내의 라우팅 경로를 쿼리스트링으로 사용한다. 따라서 페이지 내 앵커를 적용할 수 있다. 다만 이 모드는 IE8에서 히스토리 관리와 관련된 기능이 미비한 관계로 라우팅시에 페이지 리프레시가 일어날 수 있다.
  2. hash : #를 기준으로 라우팅 경로를 쓴다. 즉, 각 루트가 앵커를 기준으로 식별되기 때문에 페이지 내의 앵커를 쓸 수 없다는 단점이 있다. 대신에 모든 브라우저에서 리프레시없이 라우팅이 가능하다.
  3. pathname : 이는 URL 상으로는 완전히 다른 URL로 인식하게끔하는 것이다. 대신에 이는 웹서버에서 URL을 rewrite하는 기능을 지원하고, 또 자신의 서비스의 라우팅 규칙에 맞게 서버의 설정을 미리 맞춰줘야 한다는 단점이 있다. (또한 앱은 반드시 루트 주소에서만 시작할 수 있다.)