(연재) m.request – 서버와의 통신 – Mithril

서버와의 통신

SPA를 만드는 과정에서 필수적인 기능 중 하나는 서버와의 통신이다. 대부분의 웹앱은 어떤 식으로든 1)서버로부터 데이터를 내려 받아 표시하고, 2)사용자가 작성/변경한 내용을 서버로 전송하거나 3)경우에 따라서는 파일 업로드도 지원해야 한다. 서버와의 통신을 위해서는 form을 쓰거나, XMLHttpRequst 같은 걸 사용해서 구현하는 방법이 있다. Mithril은 기본적으로 XMLHttpRequest를 사용해서 서버와 비동기 통신하는 것을 간단하게 처리할 수 있는 API를 제공한다.

기본적인 GET 요청

m.request() 함수가 서버 통신을 처리하는 메인 플레이어가 된다.  이 함수는 요청을 전달할 URL이나 객체형식의 옵션 정보를 인자로 받은 후, 서버 응답을 감싸고 있는 Promise 객체를 리턴한다.

Promise 객체라고 해서 긴장할 필요는 없고, .then() 메소드를 사용해서 콜백을 받고, 완료 시에 콜백에 래핑된 데이터를 넣어 호출해주는 타입을 생각하면 된다.

m.request()는 기본적으로 서버 응답이 JSON 형식으로 돌아오는 것을 가정한다. JSON 형식의 데이터는 자동으로 내부에서 JSON.parse를 사용해서 자바 스크립트 객체로 변환되고 이를 감싼 Promise로 변경된다. 0.2.x 버전에서는 자동으로 prop()으로 감싸졌는데, 더 이상 prop으로는 감싸지지 않는다. 아래 예제는 특정 주소로 GET 요청을 보내고, 응답으로 돌아온 JSON 데이터를 파싱하여 출력하는 예를 보여준다.

m.request do
  method: \GET
  url: \/api/v1/users
.then (users) -> console.log users

인자로 넘겨지는 부분에서 여러가지 옵션을 줄 수 있다.

옵션 키 필수여부 설명
url Yes 요청을 보낼 서버의 주소
method HTTP 메소드 타입. 기본적으로 GET으로 간주한다.
data 서버로 전송될 데이터. GET타입인 경우 쿼리 스트링으로 변환되어 URL에 추가되며, POST인 경우 페이로드로 업로드된다.
async 기본값은 true로 요청이 비동기로 전송되는 것을 의미한다.
user 인증을 위한 사용자 계정 필드
password 인증을 위한 패스워드 필드
withCredentials 제3자 도메인으로 쿠키를 전송할 것인지를 결정한다. 기본적으로 false
config xhr 객체를 받는 함수를 세팅할 수 있다. 흔히 프로그레스 바를 구현할 때 쓴다.
headers 헤더 항목에 임의의 키-값쌍을 전송할 수 있다.
type 특정한 생성자를 넘겨주면 리턴된 데이터를 해당 타입으로 만들어준다.
serialize 데이터를 직렬화하는 방법. 기본적으로 JSON.stringify를 사용한다.
deserialize 응답페이로드를 역직렬화하는 방법. 기본적으로 JSON.parse를 사용한다. 만약 파일을 받은 경우라면 항등 함수를 넣어줄 것
extract xhr, option을 받는 함수를 통해서 응답을 추출하는 방식을 컨트롤할 수 있다. 기본적으로 xhr.responseText를 취하게 된다.
useBody GET요청에서 데이터를 주소가 아닌 data 섹션에 담아서 실어보내도록 강제한다.
background 기본값은 false이고, 이는 통신이 완료되었을 때 모든 마운트되지 않은 컴포넌트를 새로 그리도록 한다.  true로 변경하면 이 동작을 하지 않는다.

Promise 처리

Promise 객체는 .then()을 통해서 두 개의 콜백을 처리할 수 있다. 해당 Promise가 resolve될 때 호출될 콜백과 reject될 때 호출될 콜백을 각각 받을 수 있다. 혹은 .catch()를 이용해서 에러를 처리할 수 있다. 다음은 데이터 통신을 통해서 응답에 따라 UI를 달리 렌더링하는 일반적인 예를 보여주고 있다.

Data = do
  todo:
    list: null
    error: ''
    fetch: !->
      m.request do
        url: \/api/v1/todos
      .then (items) !->
        Data.todos.list = items
      .catch (e) !->
        Data.todos.error = e.message

Todo = do
  oninit: Data.todo.fetch
  view: (vnode) ->
    if Data.todo.error then
      m \.error Data.todo.error
    else if Data.todo.list then
            Data.todo.list.map -> m \div it.title
         else '.loading-icon'

m.route document.body, \/, { \/ : Todos }

파일 업로드 구현하기

파일 업로드를 구현하는 방법은 다음과 같다.

  1. 드래그 이벤트나 파일 타입의 input 요소로부터 파일 객체를 얻고
  2. 이를 FormData로 래핑한다. 이 데이터를 전송하면 된다.

대략 다음과 같은 식으로 처리할 수 있다.

m.render document.bdoy, m 'input[type=file]', {onchange:upload}

!function upload e
  file = e.target.files.0
  data = new FormData!
    ..append \file file
  m.request do
    method: \POST
    url: \/api/v1/upload
    data: data             ## 폼데이터를 실어보내고
    serialize: (v) -> v    ## 이때 데이터는 그대로 보낸다.

파일 다운로드

파일 데이터를 내려받는 경우에는 deserialze 옵션을 정의해주어야 한다. 다음은 비동기로 svg 파일을 받아와서 렌더링해주는 예이다.

m.request do
  method: \GET
  url: \/files/icon.svg
  deserialize: -> it
.then !-> m.render document.body, m.trust it

관련 글 목차

  1.  mithril 앱의 기본 구성 및 m()
  2.  m.render() – 가상 DOM 렌더링하기
  3. m.mount – 가상 노드를 마운트하기
  4. m.prop() – 양방향 바인딩을 위한 데이터 래퍼 – deprecated
  5. m.withAttr() 이벤트 핸들러 처리
  6. m.component – 가상노드를 컴포넌트로 사용하기
  7. Todo 앱 예제
  8. m.route() – 단일페이지 애플리케이션 및 라우팅 규칙
  9. m.request – 서버와의 통신