Mithril을 이용해서 간단한 메모앱을 만들어보자. 서버 사이드까지 만들 건 아니고 브라우저의 로컬 저장소를 이용해서 간단하게 메모들의 제목과 내용을 기록하고 보고, 편집할 수 있는 정도로 구현해보자. 사용언어는 Mithril하고 특별히 잘 어울린다 생각되는 LiveScript이며, 전체 코드 분량은 50줄 내외이다.
먼저 개별 메모(포스트)를 디자인한다. 제목과 본문 정도의 프로퍼티만 있으면 되는데, 추가적인 키 값을 하나 추가한다. 이 키 값은 포스트가 생성된 시점의 시간값으로 개별 포스트를 구분하는 식별자로 사용할 수 있게 한다.
class Post
(args) ->
@title = args?.title || ''
@content = args?.title || ''
@key = args?.key || new Date!get-time!
이렇게 작성된 포스트는 브라우저의 로컬 스토리지에 저장된다. 입출력을 담당할 객체를 하나 만들도록 하자. 키 값을 이용해서 저장된 내용을 불러오거나, 주어진 포스트를 저장할 수 있는 정도면 되기 때문에 다음과 같이 매우 간단하게 작성할 수 있다.
storage = do
save: (post) !-> local-storage.set-item post.key, JSON.stringify post
load: (key) -> new Post JSON.parse local-storate.get-item key
작성 UI
작성을 위한 페이지는 다음과 같이 구성하기로 한다.
- 페이지의 경로는 /edit/<key>가 된다.
- 키 값이 없으면 신규 포스트를 생성하고 키 값이 있으면 해당 키에 대한 포스트를 로딩해온다.
- 제목과 본문을 편집할 필드가 각각 있고
- 저장, 취소 버튼을 추가한다. (취소버튼은 단순히 리스팅 페이지로 이동시키도록 하자.)
코드는 다음과 같다. 사실 바인딩 처리가 들어가기 때문에 작성 UI가 가장 코드가 길 것이다.
writer = do
oninit: (vnode) !->
vnode.state = if k = m.route.param \key then post: stroage.load k else new Post!
view: (vnode) ->
m \.writer,
* \title,
m \input, do
oninput: m.with-attr \value, !~> vnode.state.post.title = it
value: vnode.state.post.title
m \br
m \textarea, do
onkeyup: m.with-attr \value, !~> vnode.state.post.content = it
value: vnode.state.post.content
m \br
m \button, do
onclick: !~>
storage.save vnode.state.post
m.route.set \/
, \save
m \button, {onclick: !-> m.route.set \/}, \cancel
상세 뷰 보기
이번에는 특정 키를 사용해서 해당 키가 가리키는 뷰의 내용을 보는 컴포넌트이다.
- 페이지 경로는 /view/<key> 가 되게 한다.
- 버튼은 edit, list 두 개를 추가한다. edit 버튼은 /edit/<key>로,리스트 버튼은 / 로 이동시킨다.
코드는 다음과 같다.
viewer = do
oninit: (vnode) !->
## 키가 있으면 로드하고 없으면 루트로 리다이렉트
if k = m.route.param \key then vnode.state.post = storage.load k
else m.route.set \/
view: (vnode) ->
post = vnode.state.post
m \.viewer,
* m \h1, post.title
m \.content post.content
m \button {onclick: !~> m.route.set "/edit/#{vnode.state.post.key}"}, \edit
m \button {onclick: !-> m.route.set \/}, \list
리스팅
이번에는 리스팅이다.
- 로컬스토리지의 length 속성을 이용해서 리스트를 만든다. (length 속성은 있지만 순회는 불가하다.) 각 키는
.key(i)
를 통해서 얻을 수 있다. - 이 기능을 이용해서 모든 키에 대해서 키 값과 제목을 얻어서 링크를 만들 수 있다.
참고로 링크를 만들 때 “/view/<key>”를 그냥 그대로 쓰면 해당 호스트의 루트로부터 시작하는 주소를 참조하기 때문에 이 SPA 페이지를 벗어나게 된다. 따라서 링크를 만들 때에는 {oncreate: m.route.link}
를 이용해서 라우터가 인식하는 경로로 변환처리되도록 해야 한다. 또 새로운 페이지를 만들기 위해서는 키 값을 숫자가 아닌 아무거나 줘서 load를 실패하게 만들면 된다.
lister = do
view: (vnode) ->
m \.lister,
m \ul, [0 til local-storage.length].map (i) ->
key = local-storage.key(i)
title = storage.load key .title
m \li, m \a {href: "/view/#key", oncreate: m.route.link}, title
m \button {onclick: !-> m.route.set \/edit/_}, \write
모든 컴포넌트의 준비가 완료되었다. 라우팅 규칙을 세팅하면 끝이다.
m.route document.body, \/, do
\/ : lister,
"/view/:key" : viewer,
"/edit/:key" : writer
HTML 페이지 준비
필요한 라이브러리들과 작성한 라이스브크립트 코드를 로딩하고 실행할 HTML페이지를 준비한다. 로컬에서 돌려보면 UI는 허접하나마 실제 작동하는 앱임을 확인할 수 있다.
<!doctype html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mithril/1.1.6/mithril.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/livescript/1.6.0/livescript-min.js"></script>
<script src="./blog.ls" type="text/ls"></script>
<script>require('LiveScript').go();</script>
</head>
<body>
</body>
</html>