간단한 애플리케이션
Mithril을 사용하여 주요 작업을 어떻게 처리하는지 보여주는 간단한 애플리케이션을 만들어 보겠습니다.
_최종 결과의 대화형 예제는 여기_에서 확인해 볼 수 있습니다.
먼저 애플리케이션의 진입점을 만들어 보겠습니다. index.html
파일을 생성합니다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>나의 애플리케이션</title>
</head>
<body>
<script src="bin/app.js"></script>
</body>
</html>
<!doctype html>
은 HTML5 문서임을 나타냅니다. 첫 번째 charset
메타 태그는 문서의 문자 인코딩을 지정하고, viewport
메타 태그는 모바일 브라우저가 페이지 크기를 조정하는 방식을 제어합니다. title
태그는 브라우저 탭에 표시될 애플리케이션 제목을 정의하며, script
태그는 애플리케이션 로직을 담은 JavaScript 파일의 경로를 나타냅니다.
애플리케이션 전체를 하나의 JavaScript 파일로 만들 수도 있지만, 코드베이스가 커지면 유지보수가 어려워집니다. 따라서 코드를 _모듈_로 분리하고, 이러한 모듈을 _번들러_를 사용하여 bin/app.js
파일 하나로 합쳐 보겠습니다.
번들러 도구를 설정하는 방법은 여러 가지가 있지만, 대부분 npm을 통해 배포됩니다. 실제로 Mithril을 포함한 대부분의 최신 JavaScript 라이브러리와 도구는 npm을 통해 배포됩니다. npm을 사용하려면 Node.js를 설치해야 합니다. Node.js를 설치하면 npm도 자동으로 함께 설치됩니다. Node.js와 npm이 설치되면 명령줄에서 다음 명령을 실행합니다.
npm init -y
npm이 올바르게 설치되었다면 package.json
파일이 생성됩니다. 이 파일은 프로젝트 메타 정보를 담고 있는 스켈레톤 파일입니다. 필요에 따라 프로젝트 정보와 작성자 정보를 수정하십시오.
Mithril.js를 설치하려면 설치 페이지의 안내를 따르세요. Mithril.js가 설치된 프로젝트 스켈레톤이 준비되면 애플리케이션을 만들 수 있습니다.
먼저 애플리케이션의 상태를 저장할 모듈을 만들어 보겠습니다. src/models/User.js
파일을 생성합니다.
// src/models/User.js
var User = {
list: [],
};
module.exports = User;
이제 서버에서 데이터를 가져오는 코드를 추가해 보겠습니다. 서버와 통신하기 위해 Mithril.js의 XHR 유틸리티인 m.request
를 사용할 수 있습니다. 먼저 Mithril.js를 모듈에 포함합니다.
// src/models/User.js
var m = require('mithril');
var User = {
list: [],
};
module.exports = User;
다음으로 XHR 호출을 실행하는 함수를 만듭니다. loadList
라고 이름을 짓겠습니다.
// src/models/User.js
var m = require('mithril');
var User = {
list: [],
loadList: function () {
// TODO: make XHR call
},
};
module.exports = User;
이제 m.request
를 사용하여 실제 XHR 요청을 수행합니다. 이 튜토리얼에서는 REM (DEAD LINK, FIXME: https //rem-rest-api.herokuapp.com/) API를 사용합니다. 이 API는 빠른 프로토타입 제작을 위해 설계된 모의 REST API입니다. GET https://mithril-rem.fly.dev/api/users
엔드포인트에서 사용자 목록을 반환합니다. m.request
를 사용하여 XHR 요청을 보내고, 응답 데이터를 가져와서 사용자 목록을 채워 보겠습니다.
참고: REM 엔드포인트가 작동하려면 타사 쿠키를 활성화해야 할 수 있습니다.
// src/models/User.js
var m = require('mithril');
var User = {
list: [],
loadList: function () {
return m
.request({
method: 'GET',
url: 'https://mithril-rem.fly.dev/api/users',
withCredentials: true,
})
.then(function (result) {
User.list = result.data;
});
},
};
module.exports = User;
method
옵션은 HTTP 메서드를 지정합니다. 서버에 영향을 주지 않고 데이터를 가져오기만 할 때는 GET
메서드를 사용해야 합니다. url
은 API 엔드포인트 주소를 나타냅니다. withCredentials: true
는 쿠키를 사용하고 있음을 나타냅니다 (REM API의 필수 사항).
m.request
호출은 Promise를 반환합니다. 이 Promise는 엔드포인트에서 받은 데이터로 resolve됩니다. Mithril.js는 기본적으로 HTTP 응답 본문이 JSON 형식이라고 가정하고, 자동으로 JavaScript 객체 또는 배열로 파싱합니다. .then
콜백은 XHR 요청이 완료된 후에 실행됩니다. 이 경우 콜백은 result.data
배열을 User.list
에 할당합니다.
loadList
함수에 return
문이 있는 것을 주목하세요. Promise를 사용할 때는 이렇게 하는 것이 일반적인 방법이며, XHR 요청이 완료된 후 실행할 추가 콜백을 등록할 수 있습니다.
이 간단한 모델은 두 가지 멤버를 제공합니다. User.list
(사용자 객체의 배열)와 User.loadList
(서버에서 데이터를 가져와 User.list
를 채우는 메서드)입니다.
이제 User 모델 모듈의 데이터를 표시하기 위한 뷰 모듈을 만들어 보겠습니다.
src/views/UserList.js
파일을 생성합니다. 먼저 Mithril.js와 User 모델을 포함합니다. 둘 다 필요하기 때문입니다.
// src/views/UserList.js
var m = require('mithril');
var User = require('../models/User');
다음으로 Mithril.js 컴포넌트를 생성합니다. 컴포넌트는 view
메서드를 가진 객체입니다.
// src/views/UserList.js
var m = require('mithril');
var User = require('../models/User');
module.exports = {
view: function () {
// TODO add code here
},
};
Mithril.js 뷰는 기본적으로 hyperscript를 사용하여 정의됩니다. Hyperscript는 복잡한 태그 구조를 HTML보다 더 자연스럽게 들여쓸 수 있는 간결한 구문을 제공하며, JavaScript 구문을 사용하므로 JavaScript 도구 생태계를 활용할 수 있다는 장점이 있습니다. 예를 들어:
- Babel을 사용하여 ES6+ 코드를 IE를 위한 ES5 코드로 트랜스파일하고, JSX (인라인 HTML과 유사한 구문 확장)를 하이퍼스크립트 호출로 변환할 수 있습니다.
- ESLint를 사용하여 코드 스타일을 검사할 수 있습니다. 특별한 플러그인 없이도 쉽게 린팅할 수 있습니다.
- Terser 또는 UglifyJS (ES5만 해당)를 사용하여 코드를 축소할 수 있습니다.
- 코드 커버리지를 위해 Istanbul을 사용할 수 있습니다.
- 쉬운 코드 분석을 위해 TypeScript를 사용할 수 있습니다. 직접 타입 정의를 만들 필요 없이 커뮤니티에서 제공하는 타입 정의를 사용할 수 있습니다.
하이퍼스크립트를 사용하여 항목 목록을 만들어 보겠습니다. 하이퍼스크립트는 Mithril.js에서 일반적으로 사용되는 방식이지만, JSX도 매우 유사하게 작동합니다.
// src/views/UserList.js
var m = require('mithril');
var User = require('../models/User');
module.exports = {
view: function () {
return m('.user-list');
},
};
".user-list"
문자열은 CSS 선택자이며, .user-list
는 클래스를 나타냅니다. 태그 이름이 생략되면 div
가 기본값으로 사용됩니다. 따라서 이 뷰는 <div class="user-list"></div>
와 같습니다.
이제 이전에 만든 모델 (User.list
)에서 사용자 목록을 가져와 데이터를 동적으로 반복하여 표시해 보겠습니다.
// src/views/UserList.js
var m = require('mithril');
var User = require('../models/User');
module.exports = {
view: function () {
return m(
'.user-list',
User.list.map(function (user) {
return m('.user-list-item', user.firstName + ' ' + user.lastName);
})
);
},
};
User.list
는 JavaScript 배열이고, 하이퍼스크립트 뷰는 JavaScript 코드이므로 .map
메서드를 사용하여 배열을 반복할 수 있습니다. 이렇게 하면 각 사용자 이름이 포함된 div
목록을 나타내는 vnode 배열이 생성됩니다.
하지만 아직 User.loadList
함수를 호출하지 않았습니다. 따라서 User.list
는 여전히 빈 배열이므로 이 뷰는 빈 페이지를 렌더링합니다. 컴포넌트를 렌더링할 때 User.loadList
를 호출하고 싶으므로 컴포넌트 수명 주기 메서드를 활용할 수 있습니다.
// src/views/UserList.js
var m = require('mithril');
var User = require('../models/User');
module.exports = {
oninit: User.loadList,
view: function () {
return m(
'.user-list',
User.list.map(function (user) {
return m('.user-list-item', user.firstName + ' ' + user.lastName);
})
);
},
};
컴포넌트에 oninit
메서드를 추가하고, User.loadList
를 할당했습니다. 즉, 컴포넌트가 초기화될 때 User.loadList
가 호출되어 XHR 요청이 실행됩니다. 서버에서 응답을 받으면 User.list
가 채워집니다.
또한 oninit: User.loadList()
(괄호 포함)를 사용하지 않았다는 점에 유의하세요. oninit: User.loadList()
는 함수를 즉시 한 번 호출하지만, oninit: User.loadList
는 컴포넌트가 렌더링될 때만 해당 함수를 호출합니다. 이는 중요한 차이점이며, JavaScript 초보 개발자가 흔히 실수하는 부분입니다. 함수를 즉시 호출하면 컴포넌트가 렌더링되지 않더라도 소스 코드가 평가되는 즉시 XHR 요청이 실행됩니다. 또한 컴포넌트가 다시 생성될 때 (예: 애플리케이션을 앞뒤로 탐색할 때) 예상대로 함수가 다시 호출되지 않을 수 있습니다.
이전에 만든 진입점 파일 src/index.js
에서 뷰를 렌더링해 보겠습니다.
// src/index.js
var m = require('mithril');
var UserList = require('./views/UserList');
m.mount(document.body, UserList);
m.mount
호출은 지정된 컴포넌트 (UserList
)를 DOM 요소 (document.body
)에 렌더링하여 기존 DOM을 대체합니다. 브라우저에서 HTML 파일을 열면 사용자 이름 목록이 표시됩니다.
지금은 스타일을 정의하지 않았기 때문에 목록이 다소 평범해 보입니다. 몇 가지 스타일을 추가해 보겠습니다. 먼저 styles.css
파일을 만들고 index.html
파일에 포함합니다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>나의 애플리케이션</title>
<link href="styles.css" rel="stylesheet" />
</head>
<body>
<script src="bin/app.js"></script>
</body>
</html>
이제 UserList
컴포넌트의 스타일을 지정할 수 있습니다.
.user-list {
list-style: none;
margin: 0 0 10px;
padding: 0;
}
.user-list-item {
background: #fafafa;
border: 1px solid #ddd;
color: #333;
display: block;
margin: 0 0 1px;
padding: 8px 15px;
text-decoration: none;
}
.user-list-item:hover {
text-decoration: underline;
}
브라우저 창을 다시 로드하면 스타일이 적용된 요소가 표시됩니다.
애플리케이션에 라우팅 기능을 추가해 보겠습니다.
라우팅은 화면을 고유한 URL에 연결하여 애플리케이션 내에서 "페이지" 간의 이동을 가능하게 하는 기능입니다. Mithril.js는 단일 페이지 애플리케이션(SPA)을 위해 설계되었으므로, 여기서 "페이지"는 전통적인 의미의 HTML 파일이 아닙니다. 대신, SPA의 라우팅은 JavaScript를 사용하여 애플리케이션의 상태를 변경하고, 동일한 HTML 파일을 유지하면서 화면을 업데이트합니다. 클라이언트 측 라우팅은 페이지 전환 시 깜빡이는 빈 화면을 방지하고, 웹 서비스 기반 아키텍처와 함께 사용하면 서버에서 전송해야 하는 데이터 양을 줄일 수 있습니다 (예: 미리 렌더링된 HTML 대신 JSON 데이터를 다운로드하는 애플리케이션).
m.mount
호출을 m.route
호출로 변경하여 라우팅을 추가할 수 있습니다.
// src/index.js
var m = require('mithril');
var UserList = require('./views/UserList');
m.route(document.body, '/list', {
'/list': UserList,
});
m.route
호출은 애플리케이션이 document.body
에 렌더링됨을 지정합니다. "/list"
인수는 기본 경로입니다. 즉, 사용자가 존재하지 않는 경로로 접속하면 이 경로로 리디렉션됩니다. {"/list": UserList}
객체는 경로와 해당 경로에 표시될 컴포넌트의 매핑을 정의합니다.
브라우저에서 페이지를 새로 고치면 URL에 #!/list
가 추가되어 라우팅이 작동 중임을 알 수 있습니다. 해당 경로에서 UserList
컴포넌트를 렌더링하므로, 이전과 같이 화면에 사용자 목록이 표시됩니다.
#!
스니펫은 해시뱅으로 알려져 있으며, 클라이언트 측 라우팅을 구현하는 데 일반적으로 사용되는 문자열입니다. m.route.prefix
를 통해 이 문자열을 변경할 수 있습니다. 일부 구성에서는 서버 측 변경이 필요하므로, 이 튜토리얼에서는 해시뱅을 계속 사용합니다.
사용자 편집을 위한 또 다른 경로를 애플리케이션에 추가해 보겠습니다. 먼저 views/UserForm.js
모듈을 생성합니다.
// src/views/UserForm.js
module.exports = {
view: function () {
// TODO implement view
},
};
그런 다음 src/index.js
에서 이 새 모듈을 require
합니다.
// src/index.js
var m = require('mithril');
var UserList = require('./views/UserList');
var UserForm = require('./views/UserForm');
m.route(document.body, '/list', {
'/list': UserList,
});
마지막으로, 이 모듈을 참조하는 경로를 만듭니다.
// src/index.js
var m = require('mithril');
var UserList = require('./views/UserList');
var UserForm = require('./views/UserForm');
m.route(document.body, '/list', {
'/list': UserList,
'/edit/:id': UserForm,
});
새 경로에 :id
가 있습니다. 이것은 경로 매개변수입니다. 와일드카드처럼 생각하면 됩니다. /edit/1
경로는 id
가 "1"
인 UserForm
으로 연결됩니다. /edit/2
도 UserForm
으로 연결되지만, id
는 "2"
입니다. 이런 식으로 작동합니다.
해당 경로 매개변수에 응답할 수 있도록 UserForm
컴포넌트를 구현해 보겠습니다.
// src/views/UserForm.js
var m = require('mithril');
module.exports = {
view: function () {
return m('form', [
m('label.label', 'First name'),
m('input.input[type=text][placeholder=First name]'),
m('label.label', 'Last name'),
m('input.input[placeholder=Last name]'),
m('button.button[type=submit]', 'Save'),
]);
},
};
styles.css
에 스타일을 추가합니다.
/* styles.css */
body,
.input,
.button {
font: normal 16px Verdana;
margin: 0;
}
.user-list {
list-style: none;
margin: 0 0 10px;
padding: 0;
}
.user-list-item {
background: #fafafa;
border: 1px solid #ddd;
color: #333;
display: block;
margin: 0 0 1px;
padding: 8px 15px;
text-decoration: none;
}
.user-list-item:hover {
text-decoration: underline;
}
.label {
display: block;
margin: 0 0 5px;
}
.input {
border: 1px solid #ddd;
border-radius: 3px;
box-sizing: border-box;
display: block;
margin: 0 0 10px;
padding: 10px 15px;
width: 100%;
}
.button {
background: #eee;
border: 1px solid #ddd;
border-radius: 3px;
color: #333;
display: inline-block;
margin: 0 0 10px;
padding: 10px 15px;
text-decoration: none;
}
.button:hover {
background: #e8e8e8;
}
지금은 이 컴포넌트가 사용자 이벤트에 응답하지 않습니다. src/models/User.js
의 User
모델에 코드를 추가해 보겠습니다. 현재 코드는 다음과 같습니다.
// src/models/User.js
var m = require('mithril');
var User = {
list: [],
loadList: function () {
return m
.request({
method: 'GET',
url: 'https://mithril-rem.fly.dev/api/users',
withCredentials: true,
})
.then(function (result) {
User.list = result.data;
});
},
};
module.exports = User;
단일 사용자 정보를 로드할 수 있도록 코드를 추가합니다.
// src/models/User.js
var m = require('mithril');
var User = {
list: [],
loadList: function () {
return m
.request({
method: 'GET',
url: 'https://mithril-rem.fly.dev/api/users',
withCredentials: true,
})
.then(function (result) {
User.list = result.data;
});
},
current: {},
load: function (id) {
return m
.request({
method: 'GET',
url: 'https://mithril-rem.fly.dev/api/users/' + id,
withCredentials: true,
})
.then(function (result) {
User.current = result;
});
},
};
module.exports = User;
User.current
속성과 해당 속성을 초기화하는 User.load(id)
메서드를 추가했습니다. 이제 이 새 메서드를 사용하여 UserForm
뷰를 채울 수 있습니다.
// src/views/UserForm.js
var m = require('mithril');
var User = require('../models/User');
module.exports = {
oninit: function (vnode) {
User.load(vnode.attrs.id);
},
view: function () {
return m('form', [
m('label.label', 'First name'),
m('input.input[type=text][placeholder=First name]', {
value: User.current.firstName,
}),
m('label.label', 'Last name'),
m('input.input[placeholder=Last name]', { value: User.current.lastName }),
m('button.button[type=submit]', 'Save'),
]);
},
};
UserList
컴포넌트와 마찬가지로 oninit
메서드에서 User.load()
를 호출합니다. "/edit/:id": UserForm
경로에 :id
라는 경로 매개변수가 있습니다. 경로 매개변수는 UserForm
컴포넌트 vnode의 속성이 되므로, /edit/1
로 접속하면 vnode.attrs.id
값은 "1"
이 됩니다.
이제 UserList
뷰에서 UserForm
으로 이동할 수 있도록 수정해 보겠습니다.
// src/views/UserList.js
var m = require('mithril');
var User = require('../models/User');
module.exports = {
oninit: User.loadList,
view: function () {
return m(
'.user-list',
User.list.map(function (user) {
return m(
m.route.Link,
{
class: 'user-list-item',
href: '/edit/' + user.id,
},
user.firstName + ' ' + user.lastName
);
})
);
},
};
여기서 .user-list-item
vnode를 해당 클래스와 동일한 자식을 가진 m.route.Link
로 바꿨습니다. 원하는 경로를 참조하는 href
속성을 추가했습니다. 즉, 링크를 클릭하면 해시뱅 #!
뒤의 URL 부분이 변경됩니다 (따라서 현재 HTML 페이지를 언로드하지 않고 경로가 변경됨). 내부적으로는 <a>
태그를 사용하여 링크를 구현하므로 정상적으로 작동합니다.
브라우저에서 페이지를 새로 고치면 이제 사람 이름을 클릭하여 해당 사용자의 편집 양식으로 이동할 수 있습니다. 또한 브라우저의 뒤로 버튼을 눌러 양식에서 사용자 목록으로 돌아갈 수 있습니다.
폼 자체는 "저장" 버튼을 눌러도 아직 저장되지 않습니다. 이 폼이 실제로 작동하도록 만들어 보겠습니다.
// src/views/UserForm.js
var m = require('mithril');
var User = require('../models/User');
module.exports = {
oninit: function (vnode) {
User.load(vnode.attrs.id);
},
view: function () {
return m(
'form',
{
onsubmit: function (e) {
e.preventDefault();
User.save();
},
},
[
m('label.label', 'First name'),
m('input.input[type=text][placeholder=First name]', {
oninput: function (e) {
User.current.firstName = e.target.value;
},
value: User.current.firstName,
}),
m('label.label', 'Last name'),
m('input.input[placeholder=Last name]', {
oninput: function (e) {
User.current.lastName = e.target.value;
},
value: User.current.lastName,
}),
m('button.button[type=submit]', 'Save'),
]
);
},
};
사용자가 입력하는 내용이 User.current.firstName
및 User.current.lastName
속성에 즉시 반영되도록 각 입력 필드에 oninput
이벤트를 추가했습니다.
또한 "저장" 버튼을 클릭하면 User.save
메서드가 호출되도록 설정했습니다. 이제 실제로 데이터를 저장하는 User.save
메서드를 구현해 보겠습니다.
// src/models/User.js
var m = require('mithril');
var User = {
list: [],
loadList: function () {
return m
.request({
method: 'GET',
url: 'https://mithril-rem.fly.dev/api/users',
withCredentials: true,
})
.then(function (result) {
User.list = result.data;
});
},
current: {},
load: function (id) {
return m
.request({
method: 'GET',
url: 'https://mithril-rem.fly.dev/api/users/' + id,
withCredentials: true,
})
.then(function (result) {
User.current = result;
});
},
save: function () {
return m.request({
method: 'PUT',
url: 'https://mithril-rem.fly.dev/api/users/' + User.current.id,
body: User.current,
withCredentials: true,
});
},
};
module.exports = User;
위의 save
메서드에서는 PUT
HTTP 메서드를 사용하여 서버의 데이터를 갱신(수정)하고 있습니다. 만약 해당 ID의 데이터가 없다면, 서버 API 구현에 따라 새로운 데이터를 삽입할 수도 있습니다.
이제 애플리케이션에서 사용자 이름을 수정하고 변경 사항을 저장해 보세요. 사용자 목록에 변경 사항이 반영되는 것을 확인할 수 있습니다.
현재는 브라우저의 뒤로 가기 버튼을 통해서만 사용자 목록으로 돌아갈 수 있습니다. 일반적으로 메뉴와 같은 전역 UI 요소를 배치할 수 있는 레이아웃 컴포넌트가 있으면 더 편리할 것입니다.
src/views/Layout.js
파일을 만들어 레이아웃 컴포넌트를 정의해 보겠습니다.
// src/views/Layout.js
var m = require('mithril');
module.exports = {
view: function (vnode) {
return m('main.layout', [
m('nav.menu', [m(m.route.Link, { href: '/list' }, 'Users')]),
m('section', vnode.children),
]);
},
};
이 컴포넌트는 매우 단순하며, 사용자 목록으로 연결되는 링크가 있는 <nav>
요소를 포함합니다. /edit
링크에서 사용했던 것과 마찬가지로, 이 링크는 m.route.Link
를 사용하여 Mithril 라우터에 의해 처리되는 링크를 생성합니다.
또한 vnode.children
을 자식 요소로 가지는 <section>
요소도 있습니다. vnode
는 Layout 컴포넌트의 인스턴스를 나타내는 가상 노드(vnode)를 참조합니다 (예: m(Layout)
호출에서 반환된 vnode). 따라서 vnode.children
은 해당 vnode의 모든 자식 노드를 나타냅니다.
스타일을 약간 업데이트해 보겠습니다.
/* styles.css */
body,
.input,
.button {
font: normal 16px Verdana;
margin: 0;
}
.layout {
margin: 10px auto;
max-width: 1000px;
}
.menu {
margin: 0 0 30px;
}
.user-list {
list-style: none;
margin: 0 0 10px;
padding: 0;
}
.user-list-item {
background: #fafafa;
border: 1px solid #ddd;
color: #333;
display: block;
margin: 0 0 1px;
padding: 8px 15px;
text-decoration: none;
}
.user-list-item:hover {
text-decoration: underline;
}
.label {
display: block;
margin: 0 0 5px;
}
.input {
border: 1px solid #ddd;
border-radius: 3px;
box-sizing: border-box;
display: block;
margin: 0 0 10px;
padding: 10px 15px;
width: 100%;
}
.button {
background: #eee;
border: 1px solid #ddd;
border-radius: 3px;
color: #333;
display: inline-block;
margin: 0 0 10px;
padding: 10px 15px;
text-decoration: none;
}
.button:hover {
background: #e8e8e8;
}
src/index.js
에서 라우터를 수정하여 레이아웃을 적용해 보겠습니다.
// src/index.js
var m = require('mithril');
var UserList = require('./views/UserList');
var UserForm = require('./views/UserForm');
var Layout = require('./views/Layout');
m.route(document.body, '/list', {
'/list': {
render: function () {
return m(Layout, m(UserList));
},
},
'/edit/:id': {
render: function (vnode) {
return m(Layout, m(UserForm, vnode.attrs));
},
},
});
이제 각 컴포넌트를 RouteResolver(기본적으로 render
메서드를 가진 객체)로 대체했습니다. render
메서드는 m()
호출을 중첩하는 방식으로 일반 컴포넌트의 view
메서드와 동일하게 작성할 수 있습니다.
주목할 점은 m()
호출에서 선택자 문자열 대신 컴포넌트를 직접 사용할 수 있다는 것입니다. /list
경로의 경우 m(Layout, m(UserList))
는 Layout
컴포넌트의 인스턴스를 나타내는 루트 vnode를 생성하고, UserList
vnode를 해당 루트 vnode의 유일한 자식으로 설정합니다.
/edit/:id
경로에는 경로 매개변수를 UserForm
컴포넌트로 전달하는 vnode
인수가 있습니다. 따라서 URL이 /edit/1
인 경우, vnode.attrs
는 {id: 1}
이 되고, m(UserForm, vnode.attrs)
는 m(UserForm, {id: 1})
과 동일하게 동작합니다. JSX로 표현하면 <UserForm id={vnode.attrs.id} />
와 같습니다.
브라우저에서 페이지를 새로 고치면 이제 앱의 모든 페이지에 전역 탐색 메뉴가 표시됩니다.
이것으로 튜토리얼을 마칩니다.
이 튜토리얼에서는 서버에서 사용자 목록을 가져오고 개별 사용자를 편집할 수 있는 간단한 애플리케이션을 만드는 과정을 살펴보았습니다. 추가 연습으로 사용자 생성 및 삭제 기능을 직접 구현해 보십시오.
더 많은 Mithril.js 코드 예제를 보려면 예제 페이지를 확인하십시오. 질문이 있으시면 Mithril.js 채팅방을 방문해 주세요.