Browser도 만들었고 OAuth 연결도 했으니 이제 UI를 만들어본다. UI는 당연하게도 html/css로 다 만들면된다. 물론 canvas로 그려도 되고 web-gl로도 그려도 된다. 중요한 것은 electron에서 사용하는 Chromium버전에만 동작하면 되니 다른 브라우저 신경쓰지 않고 맘껏 만들 수 있다는 점이다.
그동안 잉여력으로 테스트해온 Chromium의 극단을 끄집어낼 기회다. Electron으로 앱을 만들면서 가장 좋았던 점이 이 점이었다. polyfill도 필요없고 변환작업없이 ES6를 사용할 수도 있다.
React
여러 UI 라이브러리 중에서 React를 선택했다. 이전에 사용해봐서 익숙하기도 하고 사용자가 많아서 귀찮은 작업을 대신해 줄 외부 라이브러리도 많다. 초반에 webpack2 설정하는 것이 조금 귀찮지만 그 부분만 잘해두면 Component를 쌓아가면서 개발하기 좋다고 생각한다.
class Blog extends Component {
render() {
const { blog, posts, postContent } = this.props
return (
<div>
<Sidebar blog={blog} posts={posts} />
<Content blog={blog} post={postContent} />
</div>
)
}
}
Sidebar, Content로 화면을 나누고 Sidebar에는 post list, Content에는 본문과 에디터를 넣었다.
Redux, ipc
react의 state를 관리하기 편하게 도와주는 redux가 있다. Component가 state를 관리하는 대신 reducer가 state를 관리하고 Component는 prop로 데이터를 받아서 변경이 되면 dispatch만 해주면 된다. 여기에 ipc를 사용하게 되면 이 과정이 더 편해진다. 더이상 Component는 서버에 데이터를 받아와서 reducer에 전달해줄 필요가 없다. 데이터 요청만 하고 값을 받아서 dispatch 하는 것은 최상위 Component에서 몰아서 하면 된다.
ipcRenderer.on("receive-blog-info", (e, info) => {
store.dispatch(actions.receiveBlog(info))
store.dispatch(actions.resetPosts())
store.dispatch(actions.receivePostContent(null))
})
ipcRenderer.on("receive-posts", (e, posts) => {
store.dispatch(actions.receivePosts(posts))
})
ipcRenderer.on("receive-post-content", (e, postContent) => {
store.dispatch(actions.receivePostContent(postContent))
})
material-ui
UI에서 가장 중요한 것. 바로 디자인이다. 특히 앱을 만드는 것이니까 네이티브 Component같은 느낌을 줄 디자인이 필요하다. Chromium은 기본적으로 material ui를 사용하니 material ui를 사용하면 잘 맞을 것 같다. react용으로 잘 만들어진 material-ui 라이브러리가 있다.
<List>
{posts.map(item =>
<ListItem
key={item.postId}
primaryText={item.title}
secondaryText={item.date}
onClick={e => {this.handleSelectPost(item.postId)}}
/>
)}
</List>
List, ListItem 같은 편리한 Component가 다수 있다. 그리고 Icon도 제공을 하고 있어서 버튼만들 때도 참 편하다.
이렇게 앱이 대략의 모양새를 갖추게 되었다. 이제 다음차례는 packaging 이다.