Elm과 Electron으로 데스크톱 앱을 만들어 보았습니다.

이것은 Elm Advent Calendar 2017의 15 일째 기사입니다.

소개



ElmElectron을 사용하여 Twitter 해시 태그 인 #elm을 스트리밍하는 앱을 만들어 보았습니다. 완성된 것은 GitHub - calmery/elm-advent-calendar-2017 에서 볼 수 있습니다.


구현



커밋마다 정리해 갑니다.

Hello World



GitHub - calmery/elm-advent-calendar-2017 at 6490533e51c1fb4afea9e03aa9562e4762f52207


Elm이 undefined되었습니다.



조속히라고 할까. Electron에서 Elm을 참조하면 undefined가되었습니다.
Elm.Main.fullscreen() // Uncaught ReferenceError: Elm is not defined

실제로 생성 된 코드를 보면 module.exports가 우선되는 것 같습니다.
if (typeof module === "object")
{
  module['exports'] = Elm;
  return;
}

var globalElm = this['Elm'];
if (typeof globalElm === "undefined")
{
  this['Elm'] = Elm;
  return;
}

그래서 module.exports에서 직접 참조하도록했습니다.
const Elm = module.exports

// require を使って読み込むこともできる
const Elm = require( './app.js' )

이 문제는 webpack을 사용하면 걱정할 필요가 없습니다.

webpack 추가



GitHub - calmery/elm-advent-calendar-2017 at 55e80b6a4e1e11bb8713177fcbbaabc13d90732d
webpack 그 1 · Elm Tutorial 의 설명이 참고가 되었습니다.

트위터에서 얻은 트윗을 프로세스 간 통신 및 포트로 보내기



GitHub - calmery/elm-advent-calendar-2017 at 45fff5fea0170ef369d8554a4dbcbe0f020abe81
Electron은 메인 프로세스와 렌더러 프로세스가 분리되어 있습니다. 그러므로, 그 사이에서 교환을 실시하기 위해서 프로세스간 통신을 실시할 필요가 있습니다. 여기는 ipcMain | Electron를 보면 좋을까 생각합니다.
// src/entry.js
// In main process.
stream.on( 'data', event => {
  const tweet = event.text
  window.webContents.send( 'newTweet', tweet )
} )
// src/public/entry.js
// In renderer process.
ipcRenderer.on( 'newTweet', ( _, tweet ) => {
  // do something ...
} )

일단 트윗 본문만 렌더러 프로세스에 전달하도록 했습니다. 여기에서 받은 데이터를 Elm에 Port를 사용하여 전달합니다. 여기는 JavaScript · An Introduction to Elm 의 Step 2: Talk to JavaScript 라든지 Elm의 Port에서 JS를 사용한다. - Qiita 가 참고가 됩니다.
// src/public/entry.js
ipcRenderer.on( 'newTweet', ( _, tweet ) => {
  app.ports.newTweet.send( tweet )
} )
-- src/public/elm/Main.elm
port newTweet : (String -> msg) -> Sub msg

type Msg
    = NewTweet String

subscriptions : Model -> Sub Msg
subscriptions model =
    newTweet NewTweet

JSON 디코딩



GitHub - calmery/elm-advent-calendar-2017 at 39ab6b701fcfbbf29a46ec706286c2f4c76a5f1f
트윗의 본문만으로는 부족하기 때문에 유저의 정보등도 함께 건네주게 했습니다.
// src/entry.js
stream.on( 'data', event => {
  const tweet = {
    text: event.text,
    created_at: event.created_at,
    user: {
      profile_image_url: event.user.profile_image_url,
      name: event.user.name,
      screen_name: event.user.screen_name,
    }
  }

  window.webContents.send( 'newTweet', JSON.stringify( tweet ) )
} )

Elm에서 JSON을 디코딩합니다. JSON · An Introduction to Elm 이나 [Elm] Decoder a에서 여러가지 이해해 버리자 - Qiita 가 참고가 됩니다.
-- src/public/elm/Main.elm
type alias Tweet =
    { user : User
    , text : String
    , created_at : String
    }

decodeTweet : String -> Result String Tweet
decodeTweet response =
    decodeString tweetDecoder response

tweetDecoder : Decoder Tweet
tweetDecoder =
    map3 Tweet
        (field "user" userDecoder)
        (field "text" string)
        (field "created_at" string)

알림



GitHub - calmery/elm-advent-calendar-2017 at 5ee82825bd10cd5c3b0705f21b0804ba33455ca3
Elm에서 Port를 통해 Notification - Web API 인터페이스 | MDN을 호출하여 트윗을 받으면 알림을 표시합니다.
-- src/public/elm/Main.elm
port notification : String -> Cmd msg

...

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
  ...
    Ok tweet ->
      ( List.append [ tweet ] <| List.take 100 model, notification tweet.text )
// src/public/entry.js
app.ports.notification.subscribe( message => {
  new Notification( message )
} )

외형을 정돈하다



GitHub - calmery/elm-advent-calendar-2017 at c72da54e9bfb6d0aff18446d4313bfd31af246c5
Elm 에도 elm-styled라든지 style-elements라든지 있는 것 같습니다만, 이번은 보통으로 CSS를 사용했습니다.

요약



망설이는 곳도 몇 개 있었지만, 생각하고 있었던 것보다 순조롭게 생겼을까라고 생각합니다. Elm은 아직 모르는 것이 많기 때문에 여러가지 만들면서 시험해 나가면 좋겠다고 생각합니다.

좋은 웹페이지 즐겨찾기