Skip to content
Mithril.js 2
Main Navigation 가이드API

한국어

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
Italiano
Polski
Türkçe
čeština
magyar

한국어

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
Italiano
Polski
Türkçe
čeština
magyar

외관

Sidebar Navigation

API

코어 API

m(selector, attributes, children)

render(element, vnodes)

mount(root, component)

route(root, defaultRoute, routes)

request(options)

parseQueryString(string)

buildQueryString(object)

buildPathname(객체)

parsePathname(string)

trust(html)

fragment(attrs, children)

redraw()

censor(object, extra)

선택적 API

stream()

가이드

이 페이지에서

route(root, defaultRoute, routes) ​

Description ​

애플리케이션 내에서 페이지 간의 이동을 가능하게 합니다.

javascript
var Home = {
  view: function () {
    return 'Welcome';
  },
};

m.route(document.body, '/home', {
  '/home': Home, // `https://localhost/#!/home` 으로 정의
});

애플리케이션당 m.route는 한 번만 호출해야 합니다.

Signature ​

m.route(root, defaultRoute, routes)

인수타입필수설명
rootElement예서브 트리의 부모 노드가 될 DOM 엘리먼트
defaultRouteString예현재 URL이 어떤 경로와도 일치하지 않을 경우 리디렉션할 경로입니다. 초기 경로와는 다릅니다.
routesObject<String,Component|RouteResolver>예경로 문자열을 키로, 컴포넌트 또는 RouteResolver를 값으로 가지는 객체
반환 값undefined 반환

시그니처 읽는 방법

Static members ​

m.route.set ​

일치하는 경로로 리디렉션하거나, 일치하는 경로를 찾을 수 없는 경우 기본 경로로 리디렉션합니다. 모든 마운트 지점에서 비동기 리드로우를 트리거합니다.

m.route.set(path, params, options)

인수타입필수설명
pathString예접두사가 없는 라우팅할 경로 이름입니다. 경로는 params의 값으로 보간된 파라미터를 포함할 수 있습니다.
paramsObject아니요라우팅 파라미터입니다. path에 라우팅 파라미터 슬롯이 있는 경우, 이 객체의 속성이 경로 문자열로 보간됩니다.
options.replaceBoolean아니요새로운 히스토리 항목을 만들지, 현재 항목을 대체할지 여부입니다. 기본값은 false입니다.
options.stateObject아니요기본 history.pushState / history.replaceState 호출에 전달할 state 객체입니다. 이 state 객체는 history.state 속성에서 사용할 수 있으며, 라우팅 파라미터 객체와 병합됩니다. 이 옵션은 pushState API를 사용할 때만 작동하며, 라우터가 hashchange 모드로 폴백하는 경우(즉, pushState API를 사용할 수 없는 경우) 무시됩니다.
options.titleString아니요기본 history.pushState / history.replaceState 호출에 전달할 title 문자열입니다.
반환 값undefined 반환

.set을 params와 함께 사용하려면 반드시 경로를 정의해야 합니다.

javascript
var Article = {
  view: function (vnode) {
    return 'This is article ' + vnode.attrs.articleid;
  },
};

m.route(document.body, {
  '/article/:articleid': Article,
});
m.route.set('/article/:articleid', { articleid: 1 });

m.route.get ​

접두사 없이 마지막으로 완전히 확인된 라우팅 경로를 반환합니다. 비동기 경로가 해결 중일 때는 주소 표시줄에 보이는 경로와 다를 수 있습니다.

path = m.route.get()

인수타입필수설명
반환 값String마지막으로 완전히 확인된 경로를 반환합니다.

m.route.prefix ​

라우터 접두사를 정의합니다. 라우터 접두사는 라우터가 사용하는 기본 전략을 결정하는 URL의 일부입니다.

m.route.prefix = prefix

인수타입필수설명
prefixString예Mithril이 사용하는 기본 라우팅 전략을 제어하는 접두사입니다.

이 속성은 읽고 쓸 수 있습니다.

m.route.Link ​

이 컴포넌트는 동적으로 라우팅되는 링크를 생성합니다. 주요 기능은 로컬 href가 경로 접두사를 반영하도록 변환된 <a> 링크를 생성하는 것입니다.

javascript
m(m.route.Link, { href: '/foo' }, 'foo');

// m.route.prefix가 기본 전략에서 변경되지 않은 경우 다음으로 렌더링됩니다.
// <a href="#!/foo">foo</a>

링크는 다음과 같은 특별한 속성들을 선택적으로 사용할 수 있습니다.

  • selector는 m 함수의 첫 번째 인수로 전달되는 값입니다. <a> 엘리먼트를 포함하여 모든 CSS 선택자가 유효합니다.
  • params & options는 m.route.set에 정의된 것과 동일한 인수입니다.
  • disabled가 true이면 라우팅 동작과 바인딩된 onclick 핸들러를 비활성화하고 접근성 힌트를 위해 data-disabled="true" 속성을 추가합니다. 엘리먼트가 a인 경우 href가 제거됩니다.

라우팅 동작은 이벤트 처리 API를 사용하여 막을 수 없습니다. 대신 disabled를 사용하십시오.

javascript
m(
  m.route.Link,
  {
    href: '/foo',
    selector: 'button.large',
    disabled: true,
    params: { key: 'value' },
    options: { replace: true },
  },
  'link name'
);

// 다음으로 렌더링됩니다.
// <button disabled aria-disabled="true" class="large">link name</button>

vnode = m(m.route.Link, attributes, children)

인수타입필수설명
attributes.hrefObject예탐색할 대상 경로입니다.
attributes.disabledBoolean아니요해당 엘리먼트의 접근성을 비활성화합니다.
attributes.selectorString|Object|Function아니요m에 대한 선택자입니다. 기본값은 "a"입니다.
attributes.optionsObject아니요m.route.set에 전달되는 options를 설정합니다.
attributes.paramsObject아니요m.route.set에 전달되는 params를 설정합니다.
attributesObject아니요m으로 전달될 다른 속성입니다.
childrenArray<Vnode>|String|Number|Boolean아니요이 링크에 대한 자식 vnode입니다.
반환 값Vnodevnode입니다.

m.route.param ​

마지막으로 완전히 확인된 경로에서 경로 파라미터를 검색합니다. 경로 파라미터는 키-값 쌍입니다. 경로 파라미터는 다음 위치에서 가져올 수 있습니다.

  • 경로 보간 (예: 경로가 /users/:id이고 /users/1로 확인되면 경로 파라미터는 키 id와 값 "1"을 가짐)
  • 라우터 쿼리 문자열 (예: 경로가 /users?page=1이면 경로 파라미터는 키 page와 값 "1"을 가짐)
  • history.state (예: history.state가 {foo: "bar"}이면 경로 파라미터는 키 foo와 값 "bar"을 가짐)

value = m.route.param(key)

인수타입필수설명
keyString아니요경로 파라미터 이름 (예: 경로 /users/:id의 id, 또는 경로 /users/1?page=3의 page, 또는 history.state의 키)
반환 값String|Object지정된 키에 대한 값을 반환합니다. 키가 지정되지 않은 경우 모든 보간된 키를 포함하는 객체를 반환합니다.

RouteResolver의 onmatch 함수에서 새 경로는 아직 완전히 확인되지 않았으며 m.route.param()은 이전 경로의 파라미터를 반환합니다(있는 경우). onmatch는 새 경로의 파라미터를 인수로 받습니다.

m.route.SKIP ​

다음 경로로 건너뛰기 위해 경로 해결사의 onmatch에서 반환할 수 있는 특수 값입니다.

RouteResolver ​

RouteResolver는 onmatch 메서드 및/또는 render 메서드를 포함하는 컴포넌트가 아닌 객체입니다. 두 메서드는 선택 사항이지만 하나 이상은 있어야 합니다.

객체가 컴포넌트로 인식될 수 있는 경우(view 메서드가 있거나 function/class인 경우) onmatch 또는 render 메서드가 있더라도 컴포넌트로 처리됩니다. RouteResolver는 컴포넌트가 아니므로 라이프사이클 메서드가 없습니다.

일반적으로 RouteResolver는 m.route 호출과 동일한 파일에 있어야 하고, 컴포넌트 정의는 자체 모듈에 있어야 합니다.

routeResolver = {onmatch, render}

컴포넌트를 사용하는 경우, 컴포넌트가 Home이라고 가정할 때, 이 RouteResolver는 다음과 같이 syntactic sugar로 표현될 수 있습니다.

javascript
var routeResolver = {
  onmatch: function () {
    return Home;
  },
  render: function (vnode) {
    return [vnode];
  },
};

routeResolver.onmatch ​

onmatch 훅은 라우터가 렌더링할 컴포넌트를 찾아야 할 때 호출됩니다. 라우터 경로가 변경될 때마다 한 번씩 호출되지만, 같은 경로에서 리드로우가 발생할 때는 호출되지 않습니다. 컴포넌트가 초기화되기 전에 로직을 실행하는 데 사용할 수 있습니다(예: 인증 로직, 데이터 미리 로드, 리디렉션 분석 추적 등).

이 메서드를 사용하면 컴포넌트를 비동기적으로 정의할 수 있어 코드 분할과 비동기 모듈 로딩에 적합합니다. 컴포넌트를 비동기적으로 렌더링하려면 컴포넌트 인스턴스로 resolve되는 Promise를 반환합니다.

onmatch에 대한 자세한 내용은 고급 컴포넌트 해결 섹션을 참조하십시오.

routeResolver.onmatch(args, requestedPath, route)

인수타입설명
argsObject라우팅 파라미터
requestedPathString마지막 라우팅 작업에서 요청한 라우터 경로입니다. 보간된 라우팅 파라미터 값을 포함하지만 접두사는 제외합니다. onmatch가 호출되면 이 경로에 대한 확인이 완료되지 않았으며 m.route.get()은 여전히 이전 경로를 반환합니다.
routeString마지막 라우팅 작업에서 요청한 라우터 경로입니다. 보간된 라우팅 파라미터 값을 제외합니다.
반환 값Component|\Promise<Component>|undefined컴포넌트 또는 컴포넌트로 resolve되는 Promise를 반환합니다.

onmatch가 컴포넌트 또는 컴포넌트로 resolve되는 Promise를 반환하는 경우 이 컴포넌트는 RouteResolver의 render 메서드에서 첫 번째 인수에 대한 vnode.tag로 사용됩니다. 그렇지 않으면 vnode.tag가 "div"로 설정됩니다. 마찬가지로 onmatch 메서드가 생략되면 vnode.tag도 "div"입니다.

onmatch가 거부되는 Promise를 반환하는 경우 라우터는 defaultRoute로 다시 리디렉션합니다. 반환하기 전에 Promise 체인에서 .catch를 호출하여 이 동작을 재정의할 수 있습니다.

routeResolver.render ​

render 메서드는 일치하는 경로에 대한 모든 다시 그리기에 대해 호출됩니다. 컴포넌트의 view 메서드와 유사하며 컴포넌트 구성을 단순화하기 위해 존재합니다. 또한 Mithril.js가 서브트리 전체를 교체하는 기본 동작을 피할 수 있습니다.

vnode = routeResolver.render(vnode)

인수타입설명
vnodeObject속성 객체에 라우팅 파라미터가 포함된 vnode입니다. onmatch가 컴포넌트 또는 컴포넌트로 resolve되는 Promise를 반환하지 않으면 vnode의 tag 필드는 기본적으로 "div"입니다.
vnode.attrsObjectURL 파라미터 값의 맵
반환 값Array<Vnode>|Vnode렌더링할 vnode

vnode 파라미터는 m(Component, m.route.param())입니다. 여기서 Component는 경로에 대해 확인된 컴포넌트이고(routeResolver.onmatch 이후) m.route.param()은 여기에 설명되어 있습니다. 이 메서드를 생략하면 기본 반환 값은 [vnode]이며, key 파라미터를 사용할 수 있도록 fragment로 래핑됩니다. :key 파라미터와 결합하면 단일 엘리먼트 key가 지정된 fragment가 됩니다. 왜냐하면 [m(Component, {key: m.route.param("key"), ...})]와 같이 렌더링되기 때문입니다.

How it works ​

라우팅은 SPA(Single Page Application)를 구축하기 위한 시스템입니다. SPA는 전체 브라우저 새로고침 없이 애플리케이션 내에서 페이지 간 이동을 가능하게 합니다.

각 페이지를 개별적으로 북마크할 수 있는 기능과 브라우저의 히스토리 메커니즘을 통해 애플리케이션을 탐색할 수 있는 기능을 유지하면서 원활한 탐색 기능을 제공합니다.

페이지 새로 고침 없는 라우팅은 부분적으로 history.pushState API에 의해 가능합니다. 이 API를 사용하면 페이지가 로드된 후 브라우저에 표시되는 URL을 프로그래밍 방식으로 변경할 수 있지만, 콜드 상태(예: 새 탭)에서 주어진 URL로 이동하면 적절한 마크업이 렌더링되도록 하는 것은 애플리케이션 개발자의 책임입니다.

Routing strategies ​

라우팅 전략은 라이브러리가 실제로 라우팅을 구현하는 방법을 결정합니다. SPA 라우팅 시스템을 구현하는 데 사용할 수 있는 세 가지 일반적인 전략이 있으며, 각 전략에는 다른 고려 사항이 있습니다.

  • m.route.prefix = '#!' (기본값) – URL의 fragment identifier(해시라고도 함) 부분을 사용합니다. 이 전략을 사용하는 URL은 일반적으로 https://localhost/#!/page1과 같습니다.
  • m.route.prefix = '?' – 쿼리 문자열을 사용합니다. 이 전략을 사용하는 URL은 일반적으로 https://localhost/?/page1과 같습니다.
  • m.route.prefix = '' – 경로 이름을 사용합니다. 이 전략을 사용하는 URL은 일반적으로 https://localhost/page1과 같습니다.

해시 전략을 사용하면 history.pushState를 지원하지 않는 브라우저에서 작동하는 것이 보장됩니다. onhashchange 이벤트 핸들러를 폴백 메커니즘으로 사용할 수 있기 때문입니다. 해시를 순전히 로컬로 유지하려면 이 전략을 사용하십시오.

쿼리 문자열 전략을 사용하면 서버 측 감지가 가능하지만 일반적인 경로로 표시되지는 않습니다. 서버 측에서 앵커 링크를 지원하거나 감지해야 하지만, 경로 이름 전략을 사용하기 위한 서버 설정을 변경할 수 없는 경우 (예: Apache 서버에서 .htaccess 파일 수정이 불가능한 경우) 이 전략을 사용하십시오.

경로 이름 전략은 가장 깔끔한 URL을 생성하지만 애플리케이션이 라우팅할 수 있는 모든 URL에서 단일 페이지 애플리케이션 코드를 제공하도록 서버를 설정해야 합니다. 더 깔끔한 URL을 원하면 이 전략을 사용하십시오.

해시 전략을 사용하는 단일 페이지 애플리케이션은 종종 해시 뒤에 느낌표를 사용하여 해시를 앵커에 연결하는 목적이 아닌 라우팅 메커니즘으로 사용하고 있음을 나타내는 규칙을 사용합니다. #! 문자열은 hashbang이라고 합니다.

기본 전략은 해시뱅을 사용합니다.

Typical usage ​

일반적으로 경로에 매핑할 몇 개의 컴포넌트를 생성해야 합니다.

javascript
var Home = {
  view: function () {
    return [m(Menu), m('h1', 'Home')];
  },
};

var Page1 = {
  view: function () {
    return [m(Menu), m('h1', 'Page 1')];
  },
};

위의 예에서는 Home과 Page1이라는 두 개의 컴포넌트가 있습니다. 각 컴포넌트에는 메뉴와 일부 텍스트가 포함되어 있습니다. 메뉴 자체는 반복을 피하기 위해 컴포넌트로 정의됩니다.

javascript
var Menu = {
  view: function () {
    return m('nav', [
      m(m.route.Link, { href: '/' }, 'Home'),
      m(m.route.Link, { href: '/page1' }, 'Page 1'),
    ]);
  },
};

이제 경로를 정의하고 컴포넌트를 매핑할 수 있습니다.

javascript
m.route(document.body, '/', {
  '/': Home,
  '/page1': Page1,
});

여기서는 두 개의 경로인 /와 /page1을 지정합니다. 사용자가 각 URL로 이동할 때 해당 컴포넌트를 렌더링합니다.

Navigating to different routes ​

위의 예에서 Menu 컴포넌트에는 두 개의 m.route.Link가 있습니다. 이는 엘리먼트(기본적으로 <a>)를 만들고 사용자가 클릭하면 현재 애플리케이션 내에서 다른 경로로 이동하도록 설정합니다. 원격 서버로 이동하는 것이 아닙니다.

m.route.set(route)를 통해 프로그래밍 방식으로 이동할 수도 있습니다. 예를 들어 m.route.set("/page1")과 같습니다.

경로 간을 탐색할 때 라우터 접두사가 처리됩니다. 즉, Mithril.js 경로를 연결할 때 m.route.set과 m.route.Link 모두에서 해시뱅 #!(또는 m.route.prefix를 설정한 접두사)을 생략하십시오.

컴포넌트 간을 탐색할 때 전체 서브트리가 대체됩니다. 서브트리만 패치하려면 render 메서드가 있는 경로 해결사를 사용하십시오.

Routing parameters ​

경우에 따라 변수 ID 또는 유사한 데이터를 경로에 표시하고 싶지만 가능한 모든 ID에 대해 별도의 경로를 명시적으로 지정하고 싶지 않습니다. 이를 위해 Mithril.js는 파라미터화된 경로를 지원합니다.

javascript
var Edit = {
  view: function (vnode) {
    return [m(Menu), m('h1', 'Editing ' + vnode.attrs.id)];
  },
};
m.route(document.body, '/edit/1', {
  '/edit/:id': Edit,
});

위의 예에서는 경로 /edit/:id를 정의했습니다. 이는 /edit/로 시작하고 일부 데이터(예: /edit/1, edit/234 등)가 뒤따르는 모든 URL과 일치하는 동적 경로를 만듭니다. 그런 다음 id 값은 컴포넌트의 vnode의 속성으로 매핑됩니다(vnode.attrs.id).

경로에 여러 인수를 가질 수 있습니다. 예를 들어 /edit/:projectID/:userID는 컴포넌트의 vnode 속성 객체에 projectID 및 userID 속성을 생성합니다.

Key parameter ​

사용자가 파라미터화된 경로에서 다른 파라미터가 있는 동일한 경로로 이동할 때(예: 경로 /page/:id가 주어졌을 때 /page/1에서 /page/2로 이동) 두 경로가 동일한 컴포넌트로 확인되므로 컴포넌트가 처음부터 다시 생성되지 않고 가상 DOM이 제자리에서 비교(diff)됩니다. 이로 인해 컴포넌트가 처음 생성될 때 호출되는 oninit 또는 oncreate 라이프사이클 훅 대신, 업데이트 시 호출되는 onupdate 훅이 실행되는 결과가 발생합니다. 그러나 개발자가 컴포넌트의 재생성을 경로 변경 이벤트와 동기화하고 싶어하는 것은 비교적 일반적입니다.

이를 위해 경로 파라미터화를 key와 결합하여 매우 편리한 패턴을 만들 수 있습니다.

javascript
m.route(document.body, '/edit/1', {
  '/edit/:key': Edit,
});

이는 경로의 루트 컴포넌트에 대해 생성된 vnode에 경로 파라미터 객체 key가 있음을 의미합니다. 경로 파라미터는 vnode의 attrs 속성으로 전달됩니다. 따라서 한 페이지에서 다른 페이지로 이동할 때 key가 변경되어 컴포넌트가 처음부터 다시 생성됩니다(key는 가상 DOM 엔진에 이전 컴포넌트와 새 컴포넌트가 다른 엔터티임을 알려주기 때문).

이 아이디어를 더 발전시켜 다시 로드될 때 자체적으로 다시 생성되는 컴포넌트를 만들 수 있습니다.

m.route.set(m.route.get(), {key: Date.now()})

또는 history state 기능을 사용하여 URL을 변경하지 않고 다시 로드할 수 있는 컴포넌트를 만들 수도 있습니다.

m.route.set(m.route.get(), null, {state: {key: Date.now()}})

key 파라미터는 컴포넌트 경로에만 작동합니다. 경로 해결사를 사용하는 경우 key: m.route.param("key")를 전달하여 단일 자식 key가 지정된 fragment를 사용하여 동일한 작업을 수행해야 합니다.

Variadic routes ​

가변 경로, 즉 슬래시가 포함된 URL 경로 이름을 포함하는 인수가 있는 경로를 가질 수도 있습니다.

javascript
m.route(document.body, '/edit/pictures/image.jpg', {
  '/edit/:file...': Edit,
});

Handling 404s ​

Isomorphic/Universal JavaScript 애플리케이션에서, URL 파라미터와 가변 경로를 함께 사용하면 사용자 정의 404 오류 페이지를 표시하는 데 유용합니다.

404 Not Found 오류의 경우 서버는 사용자 지정 페이지를 클라이언트로 다시 보냅니다. Mithril.js가 로드되면 해당 경로를 알 수 없으므로 클라이언트를 기본 경로로 리디렉션합니다.

javascript
m.route(document.body, '/', {
  '/': homeComponent,
  // [...]
  '/:404...': errorPageComponent,
});

History state ​

기본적으로 제공되는 history.pushState API를 활용하여 사용자 탐색 경험을 향상시킬 수 있습니다. 예를 들어 애플리케이션은 사용자가 페이지를 떠날 때 큰 양식의 상태를 "기억"하여 사용자가 브라우저에서 뒤로 버튼을 누르면 빈 양식이 아닌 양식이 채워지도록 할 수 있습니다.

예를 들어 다음과 같은 양식을 만들 수 있습니다.

javascript
var state = {
  term: '',
  search: function () {
    // save the state for this route
    // this is equivalent to `history.replaceState({term: state.term}, null, location.href)`
    m.route.set(m.route.get(), null, {
      replace: true,
      state: { term: state.term },
    });

    // navigate away
    location.href = 'https://google.com/?q=' + state.term;
  },
};

var Form = {
  oninit: function (vnode) {
    state.term = vnode.attrs.term || ''; // populated from the `history.state` property if the user presses the back button
  },
  view: function () {
    return m('form', [
      m("input[placeholder='Search']", {
        oninput: function (e) {
          state.term = e.target.value;
        },
        value: state.term,
      }),
      m('button', { onclick: state.search }, 'Search'),
    ]);
  },
};

m.route(document.body, '/', {
  '/': Form,
});

이렇게 하면 사용자가 검색 후 뒤로 버튼을 눌러 애플리케이션으로 돌아와도 입력란이 검색어로 채워진 상태를 유지합니다. 사용자가 직접 입력하기 번거로운 비-영구적인 상태를 가지는 대규모 폼이나 다른 애플리케이션의 사용자 경험을 개선할 수 있습니다.

Changing router prefix ​

라우터 접두사는 라우터가 사용하는 기본 전략을 결정하는 URL의 일부입니다.

javascript
// pathname 전략으로 설정
m.route.prefix = '';

// querystring 전략으로 설정
m.route.prefix = '?';

// 해시뱅 없는 해시 전략으로 설정
m.route.prefix = '#';

// 루트 URL이 아닌 하위 URL에서 pathname 전략을 사용할 때
// 예: 앱이 `https://localhost/my-app` 아래에 있고 다른 것이
// `https://localhost` 아래에 있는 경우
m.route.prefix = '/my-app';

고급 컴포넌트 처리 ​

컴포넌트를 경로에 매핑하는 대신 RouteResolver 객체를 지정할 수 있습니다. RouteResolver 객체는 onmatch() 및/또는 render() 메서드를 포함합니다. 두 메서드는 선택 사항이지만, 적어도 하나는 반드시 존재해야 합니다.

javascript
m.route(document.body, '/', {
  '/': {
    onmatch: function (args, requestedPath, route) {
      return Home;
    },
    render: function (vnode) {
      return vnode; // m(Home)과 동일합니다.
    },
  },
});

RouteResolver는 다양한 고급 라우팅 시나리오를 구현하는 데 유용합니다.

레이아웃 컴포넌트 감싸기 ​

라우팅된 컴포넌트의 전부 또는 대부분을 재사용 가능한 틀(일반적으로 "레이아웃"이라고 함)로 감싸는 것이 좋습니다. 이를 위해 먼저 다양한 컴포넌트를 감싸는 공통 마크업을 포함하는 컴포넌트를 만들어야 합니다.

javascript
var Layout = {
  view: function (vnode) {
    return m('.layout', vnode.children);
  },
};

위 예제에서 레이아웃은 컴포넌트에 전달된 자식을 포함하는 <div class="layout">으로 구성되지만, 실제 시나리오에서는 필요에 따라 더 복잡하게 구성할 수 있습니다.

레이아웃을 감싸는 한 가지 방법은 경로 맵에서 익명 컴포넌트를 정의하는 것입니다.

javascript
// 예시 1
m.route(document.body, '/', {
  '/': {
    view: function () {
      return m(Layout, m(Home));
    },
  },
  '/form': {
    view: function () {
      return m(Layout, m(Form));
    },
  },
});

그러나 최상위 컴포넌트가 익명 컴포넌트이기 때문에 / 경로에서 /form 경로로 (또는 그 반대로) 이동하면 익명 컴포넌트가 해체되고 DOM이 처음부터 다시 생성됩니다. Layout 컴포넌트에 라이프사이클 메서드가 정의되어 있다면 oninit 및 oncreate 훅이 모든 경로 변경 시 실행됩니다. 애플리케이션에 따라 이것이 바람직할 수도 있고 그렇지 않을 수도 있습니다.

Layout 컴포넌트가 처음부터 다시 생성되지 않고, diff 알고리즘에 의해 변경 사항만 반영되도록 하려면 RouteResolver를 루트 객체로 사용해야 합니다.

javascript
// 예시 2
m.route(document.body, '/', {
  '/': {
    render: function () {
      return m(Layout, m(Home));
    },
  },
  '/form': {
    render: function () {
      return m(Layout, m(Form));
    },
  },
});

이 경우 Layout 컴포넌트에 oninit 및 oncreate 라이프사이클 메서드가 있다면 첫 번째 경로 변경 시에만 실행됩니다 (모든 경로가 동일한 레이아웃을 사용한다고 가정).

두 예시의 차이점을 명확히 하기 위해 예시 1은 다음 코드와 같습니다.

javascript
// 예시 1과 기능적으로 동일
var Anon1 = {
  view: function () {
    return m(Layout, m(Home));
  },
};
var Anon2 = {
  view: function () {
    return m(Layout, m(Form));
  },
};

m.route(document.body, '/', {
  '/': {
    render: function () {
      return m(Anon1);
    },
  },
  '/form': {
    render: function () {
      return m(Anon2);
    },
  },
});

Anon1과 Anon2는 서로 다른 컴포넌트이므로 해당 하위 트리 (Layout 포함)가 처음부터 다시 생성됩니다. 이는 컴포넌트가 RouteResolver 없이 직접 사용될 때도 발생합니다.

예시 2에서 Layout은 두 경로 모두에서 최상위 컴포넌트이므로 Layout 컴포넌트의 DOM이 비교(diff)됩니다 (즉, 변경 사항이 없으면 그대로 유지됨). Home에서 Form으로의 변경만 해당 DOM 섹션의 재생성을 트리거합니다.

경로 재지정 ​

RouteResolver의 onmatch 훅은 경로의 최상위 컴포넌트가 초기화되기 전에 로직을 실행하는 데 사용할 수 있습니다. Mithril의 m.route.set() 또는 네이티브 HTML의 history API를 사용할 수 있습니다. history API를 사용하여 리디렉션할 때, onmatch 훅은 일치하는 경로의 처리가 완료되지 않도록 영원히 resolve 되지 않는 Promise를 반환해야 합니다. m.route.set()은 내부적으로 해당 경로의 처리를 취소하므로, 별도로 Promise를 반환할 필요가 없습니다.

예시: 인증 ​

아래 예는 사용자가 로그인하지 않으면 /secret 페이지를 볼 수 없도록 하는 로그인 장벽을 구현하는 방법을 보여줍니다.

javascript
var isLoggedIn = false;

var Login = {
  view: function () {
    return m('form', [
      m(
        'button[type=button]',
        {
          onclick: function () {
            isLoggedIn = true;
            m.route.set('/secret');
          },
        },
        'Login'
      ),
    ]);
  },
};

m.route(document.body, '/secret', {
  '/secret': {
    onmatch: function () {
      if (!isLoggedIn) m.route.set('/login');
      else return Home;
    },
  },
  '/login': Login,
});

애플리케이션이 로드되면 onmatch가 호출되고 isLoggedIn이 false이므로 애플리케이션은 /login으로 리디렉션됩니다. 사용자가 로그인 버튼을 누르면 isLoggedIn이 true로 설정되고 애플리케이션은 /secret으로 리디렉션됩니다. onmatch 훅이 다시 실행되고 이번에는 isLoggedIn이 true이므로 애플리케이션은 Home 컴포넌트를 렌더링합니다.

위 예제에서는 단순함을 위해 사용자의 로그인 상태를 전역 변수에 저장하고, 로그인 버튼 클릭 시 해당 변수의 값을 변경하는 방식으로 구현했습니다. 실제 애플리케이션에서는 사용자가 올바른 로그인 정보를 입력해야 하며, 로그인 버튼을 클릭하면 서버에 인증 요청을 보내 사용자를 인증하는 과정을 거칩니다.

javascript
var Auth = {
  username: '',
  password: '',

  setUsername: function (value) {
    Auth.username = value;
  },
  setPassword: function (value) {
    Auth.password = value;
  },
  login: function () {
    m.request({
      url: '/api/v1/auth',
      params: { username: Auth.username, password: Auth.password },
    }).then(function (data) {
      localStorage.setItem('auth-token', data.token);
      m.route.set('/secret');
    });
  },
};

var Login = {
  view: function () {
    return m('form', [
      m('input[type=text]', {
        oninput: function (e) {
          Auth.setUsername(e.target.value);
        },
        value: Auth.username,
      }),
      m('input[type=password]', {
        oninput: function (e) {
          Auth.setPassword(e.target.value);
        },
        value: Auth.password,
      }),
      m('button[type=button]', { onclick: Auth.login }, 'Login'),
    ]);
  },
};

m.route(document.body, '/secret', {
  '/secret': {
    onmatch: function () {
      if (!localStorage.getItem('auth-token')) m.route.set('/login');
      else return Home;
    },
  },
  '/login': Login,
});

데이터 미리 로드 ​

일반적으로 컴포넌트는 초기화될 때 데이터를 로드할 수 있습니다. 이런 방식으로 데이터를 로드하면 컴포넌트가 초기 렌더링과 데이터 로드 후 렌더링, 총 두 번 렌더링됩니다. loadUsers()가 Promise를 반환하지만 oninit에서 반환된 Promise는 현재 무시됩니다. 두 번째 렌더링은 m.request의 background 옵션에서 비롯됩니다.

javascript
var state = {
  users: [],
  loadUsers: function () {
    return m.request('/api/v1/users').then(function (users) {
      state.users = users;
    });
  },
};

m.route(document.body, '/user/list', {
  '/user/list': {
    oninit: state.loadUsers,
    view: function () {
      return state.users.length > 0
        ? state.users.map(function (user) {
            return m('div', user.id);
          })
        : 'loading';
    },
  },
});

위 예제에서 첫 번째 렌더링 시에는 요청이 완료되기 전에 state.users가 빈 배열이므로 UI에 "loading"이 표시됩니다. 그런 다음 데이터가 사용 가능해지면 UI가 다시 그려지고 사용자 ID 목록이 표시됩니다.

RouteResolver를 사용하면 컴포넌트 렌더링 전에 데이터를 미리 로드하여 화면 깜빡임 현상을 줄이고, 로딩 표시기를 표시할 필요성을 없앨 수 있습니다.

javascript
var state = {
  users: [],
  loadUsers: function () {
    return m.request('/api/v1/users').then(function (users) {
      state.users = users;
    });
  },
};

m.route(document.body, '/user/list', {
  '/user/list': {
    onmatch: state.loadUsers,
    render: function () {
      return state.users.map(function (user) {
        return m('div', user.id);
      });
    },
  },
});

위 코드에서 render는 요청이 완료된 후에만 실행되므로 삼항 연산자는 불필요합니다.

코드 분할 ​

대규모 애플리케이션에서는 모든 경로에 대한 코드를 미리 로드하는 대신, 필요할 때 해당 경로의 코드만 다운로드하는 것이 효율적일 수 있습니다. 이러한 방식으로 코드베이스를 나누는 것을 코드 분할 또는 지연 로딩이라고 합니다. Mithril.js에서는 onmatch 훅에서 promise를 반환하여 이를 수행할 수 있습니다.

가장 기본적인 형태는 다음과 같습니다.

javascript
// Home.js
module.export = {
  view: function () {
    return [m(Menu), m('h1', 'Home')];
  },
};
javascript
// index.js
function load(file) {
  return m.request({
    method: 'GET',
    url: file,
    extract: function (xhr) {
      return new Function(
        'var module = {};' + xhr.responseText + ';return module.exports;'
      );
    },
  });
}

m.route(document.body, '/', {
  '/': {
    onmatch: function () {
      return load('Home.js');
    },
  },
});

하지만 실제로 프로덕션 환경에서 사용하려면, Home.js 모듈과 관련된 모든 종속성을 서버에서 제공하는 하나의 파일로 묶어야 합니다 (번들링).

다행히 지연 로딩을 위해 모듈을 번들링하는 작업을 용이하게 하는 여러 도구가 있습니다. 다음은 많은 번들러에서 지원하는 네이티브 동적 import(...)를 사용하는 예입니다.

javascript
m.route(document.body, '/', {
  '/': {
    onmatch: function () {
      return import('./Home.js');
    },
  },
});

유형화된 경로 ​

특정 고급 라우팅 시나리오에서는 경로 자체뿐만 아니라, 숫자 ID와 같은 특정 형식의 값만 매칭되도록 제한하고 싶을 수 있습니다. 경로에서 m.route.SKIP을 반환하여 매우 쉽게 수행할 수 있습니다.

javascript
m.route(document.body, '/', {
  '/view/:id': {
    onmatch: function (args) {
      if (!/^\d+$/.test(args.id)) return m.route.SKIP;
      return ItemView;
    },
  },
  '/view/:name': UserView,
});

숨겨진 경로 ​

드문 경우지만 일부 사용자에 대해서는 특정 경로를 숨기고 다른 사용자에 대해서는 숨기지 않을 수 있습니다. 예를 들어, 특정 사용자가 다른 사용자의 정보를 볼 권한이 없는 경우, 권한 오류를 보여주는 대신 해당 경로가 존재하지 않는 것처럼 처리하고 404 페이지로 리디렉션할 수 있습니다. 이 경우 m.route.SKIP을 사용하여 경로가 존재하지 않는 것처럼 처리할 수 있습니다.

javascript
m.route(document.body, '/', {
  '/user/:id': {
    onmatch: function (args) {
      return Model.checkViewable(args.id).then(function (viewable) {
        return viewable ? UserView : m.route.SKIP;
      });
    },
  },
  '/:404...': PageNotFound,
});

경로 취소/차단 ​

RouteResolver의 onmatch는 절대 해결되지 않는 promise를 반환하여 경로 해결을 방지할 수 있습니다. 이는 중복된 경로 요청이 발생했을 때, 해당 요청을 감지하고 취소하는 데 사용할 수 있습니다.

javascript
m.route(document.body, '/', {
  '/': {
    onmatch: function (args, requestedPath) {
      if (m.route.get() === requestedPath) return new Promise(function () {});
    },
  },
});

서드파티 통합 ​

특정 상황에서는 React와 같은 다른 프레임워크와 연동해야 할 수 있습니다. 방법은 다음과 같습니다.

  • m.route를 사용하여 모든 경로를 정상적으로 정의하되 한 번만 사용해야 합니다. 여러 경로 지점은 지원되지 않습니다.
  • 라우팅 구독을 해제해야 하는 경우, m.mount(root, null)을 호출하여 해제할 수 있습니다. 이때 m.route(root, ...)를 호출할 때 사용했던 동일한 root 엘리먼트를 사용해야 합니다. m.route는 내부적으로 m.mount를 사용하여 모든 것을 연결하므로 마법이 아닙니다.

다음은 React를 사용한 예입니다.

jsx
class Child extends React.Component {
  constructor(props) {
    super(props);
    this.root = React.createRef();
  }

  componentDidMount() {
    m.route(this.root, '/', {
      // ...
    });
  }

  componentDidUnmount() {
    m.mount(this.root, null);
  }

  render() {
    return <div ref={this.root} />;
  }
}

다음은 Vue를 사용한 대략적인 동등물입니다.

html
<div ref="root"></div>
javascript
Vue.component('my-child', {
  template: `<div ref="root"></div>`,
  mounted: function () {
    m.route(this.$refs.root, '/', {
      // ...
    });
  },
  destroyed: function () {
    m.mount(this.$refs.root, null);
  },
});
Pager
이전mount(root, component)
다음request(options)

MIT 라이선스 하에 배포되었습니다.

Copyright (c) 2024 Mithril Contributors

https://mithril.js.org/route.html

MIT 라이선스 하에 배포되었습니다.

Copyright (c) 2024 Mithril Contributors