FirefoxOS 앱 개발하기

이 글은 Luca Greco 의 Hacking Firefox OS이라는 글을 번역 편집한 것입니다.
이 글은 자바스크립트와 웹 관련 기술 개발에 관심이 많은 Mozilla 커뮤니티 멤버인 Luca Greco 에 의해 쓰여졌습니다.

벌써 많은 개발자들은 크로스 플랫폼 애플리케이션을 개발하거나 현재 코드/전문지식에 더 좋은 결과를 거두기 위해 Phonegap/Cordova같은 웹 기술을 사용해서 모바일 애플리케이션을 만들고 있습니다.

Firefox OS가 매우 흥미로운 프로젝트인 몇 가지 이유:

  • 웹앱들은 플랫폼에 접근하는 최상위 계층을 가지고 있다
  • 웹앱은 직관적입니다(낮은 추상화 단계와 더 나은 성능을 가지고 있습니다)
  • 플랫폼 자체가 웹을 기반으로 합니다. (웹기술을 이용해서 사용자가 변경 가능합니다)

웹기술을 기반으로 한 모바일 플랫폼은 미래가 될 수 있습니다. 그리고, 우리는 지금 만져볼 수 있고, 더욱 중요하게 우리가 이것이 정의되는 과정에 기여할 수 있으며, 완전히 개방되어 개발되는 플랫폼(Firefox OS)에 감사인사를 보낼 수 있습니다. 이러한 유혹에 저항하지 않았습니다. 그래서 Firefox OS 코드속에 빠져서 MXR을 사용해서 스크랩하고 위키에 있는 문서를 공부하기 시작했습니다.

Firefox OS에서 개발하기

지난 2주일 동안 나는 지역 LinuxDay 2012에서 발표한 주제에 대해 예제앱과 비공식적인 발표자료를 준비했습니다.



Slides: “Hack On Firefox OS”

예제 앱: Gaia(Firefox OS의 사용자 인터페이스) 에서 구현한 Chrono

발표내내 나는 Firefox OS와 Firefox가 공통적으로 가지고 있다고 믿는 중요한 점을 강조하려고 노력했습니다.

“이것은 고정된 형태가 아니고, 이것은 진짜 대화형 환경인 웹처럼 살아있습니다.”

나는 새로운 WebAPI에 대한 상호작용하는 경험을 위해 B2G 인스턴스에 접근하고 흥미로운 점을 찾아 돌아다니거나  Javascript snippets를 실행해 볼 수 있습니다. B2G안에 원격 Javascript 쉘을 작동하는 하나 이상의 선택이 있습니다:

  • Marionette, 자동화된 테스트
  • B2G Remote JS Shell, 선택적으로 tcp를 통해 사용할 수 있는 경량 Javascript 쉡 (내 생각에는 이후에는 사용중지될 것 같습니다.)

불행히도 이러한 도구들은 현재 통합 검사 도구들이 부족합니다. (예를 들어 console.log/console.dir 혹은 MozRepl repl.inspect/repl.search) 그래서, 발표동안 나는 B2G 시뮬레이터 위에 MozRepl 를 설치했지만 지난 주에 Firefox Nightly에 원격 웹콘솔이 탑재되었습니다.

확실하게 원격웹콘솔은 아직 완전하지 않습니다. 그래서 우리는 버그들과 표시되지 않는 에러들을 대비할 필요가 있습니다. (예를 들어 우리는 B2G는 원격 디버거가 가능하도록 설정함을 확인하거나, 그것은 에러없이 실패할 수 있음을 인지해야 합니다.)

Firefox OS 앱 개발하기

나의 경험상 Firefox OS에 OpenWebApps를 개발한다는 것은 정말로 Phonegap같은 기술로 이루어진 hybrid 애플리케이션과 다르지 않습니다:

우리는 기본 기능 대신에 mockups/shims를 사용하여 데스크톱 브라우저와 개발도구에서 애플리케이션의 중요 부분을 개발하고 테스트하려 합니다. 그러나 2주 동안 공유하기에 적합한 많은 양의 작업노트를 수집했습니다. 다음단계는:

  • 개발 진행순서와 도구들에 대한 설명하기
  • 유용한 팁과 우회 방법을 합치기
  • 공개하기 (독립적으로 그리고, Mozilla Marketplace 안에)

예제로 나는 간단한 초시계를 만들기로 결정했습니다:

개발순서와 개발도구

진행하는 동안 나는 다음 도구를 사용했습니다:

  • VoloJS – 개발서버와 자동화된 개발 빌드(js/css 압축, manifest.appcache 생성)
  • Firefox Nightly 웹개발자도구 – 구조 보기, tilt, 응답형 디자인 뷰, 웹콘솔
  • R2D2B2G – Firefox 애드온으로 B2G와 통합/상호운영

Volo를 사용하면 앱을 http 서버를 탑재한 volo에서 테스트 할 수 있고 Require.js를 사용하여 module단위로 Javascript code를 분리하고, 마지막으로 압축되고 자동으로 생성된 manifest.appcache를 포함한 제품을 만들 수 있습니다.

개발주기동안 나는 반복적으로:

  • 변경사항을 적용했습니다
  • 데스크톱 브라우저에서 불러들이고고 변경사항을 검사했습니다
  • R2D2B2G를 사용한 b2g 시뮬레이터에서 변경사항을 확인했습니다
  • 데스크톱 브라우저나 b2g 시뮬레이터에서 원격으로 디버깅했습니다
  • 이런 과정을 처음부터 다시 시작했습니다 🙂

제일 좋아하는 데스크톱 브라우저(당연히 Firefox :-P)를 사용하여 일반적으로 모바일웹 런타임에서 사용할 수 없는 강력한 조사/디버깅 도구를 사용할 있는 기회를 가졌습니다:

  • DOM 계층을 확인하고 변경하기 위한 마크업 뷰어
  • CSS 속성을 확인하고 변경하기 위한 스타일 에디터
  • 안보이는 영역의 DOM 요소들이 어디에 위치해있는지 확인하기 위한 Tilt
  • Javascript 환경을 확인하고 변경하기 위한 웹콘솔

“Firefox OS” 와 “Firefox for Android” 같은 새로운 Mozilla 프로젝트 덕분에 더 많은 도구들이 “원격 웹 도구”로 사용할 수 있고 원격 인스턴스에 접근할 수 있습니다.

팁과 우회방법

Gaia 유저인터페이지 빌딩 블럭

Gaia는 단지 B2G 유저인터페이스 도구가 아닙니다. 가이드를 수행하는 디자인 스타일 가이드와 미리 만들어진 CSS 스타일의 집합입니다:

우리는 Firefox OS에서 진정 멋지고 고유한 룩앤필(Look&Feel)을 보여주기 위해 저장소에서 콤포넌트 스타일을 가져올 수 있고 앱에 적용할 수 있습니다. 몇몇 콤퍼넌트는 안정적이지 않습니다. 이것은 그들은 다른 콤포넌트와 상호작용이 원할하지 못하거나 모든 플랫폼(예를 들어, Firefox 데스크톱 이나 Firefox 안드로이드 버전)에서 작동하지 않을 수 있음을 의미합니다. 그러나 대부분 우리는 몇몇 특정 상황에 맞추거나 특별한 CSS 형태를 사용하여 수정할 필요는 없습니다.

이것은 완성도 높고 완전한 CSS 프레임(예를 들어 Bootstrap )이라고 느끼지 못할 수 있습니다. 그러나 점점 더 나아질 거라 믿고, 나아질 겁니다.

반응형 디자인 뷰를 사용하여 우리는 다른 화면크기 (그리고 방향)에서 시험할 수 있습니다. 이것은 Firefox OS 혹은 Firefox Android 버전에서 시험할 필요없이 완성도가 높고 일관된 결과에 도달하도록 도와 줍니다. 그러나 우리는 기능향상을 위한 조정에 관련된 인치당 도트수(DPI)을 눈여겨 봐야 합니다. 왜냐하면 우리는 현재 그것이 데스크톱 브라우저를 사용했을 때는 어떻게 보일지 완전히 알지 못하기 때문입니다.

앱 패널

많은 앱은 한 개보다 많은 패널이 필요합니다. 그래서 기본 앱이 필수 기능을 구현하는 방법을 이해하기 위해 공식 Gaia 애플리케이션의 내부를 보았습니다. 이것은 Gaia 시계 애플리케이션이 “Tilt” 시선로부터 표시되는 방법입니다:

패널은 처음엔 스크린 밖에 있고 CSS 변경을 사용하여 스크린 위로 이동하는 간단한 DOM 요소들(예를 들어 section이라 div 태그)입니다:

“Chrono” 앱에서 여러분은 Drawer(불안정한 Gaia 유저인터페이스 빌딩 블럭)에서 이러한 방법을 찾을 수 있습니다:

그리고 Laps과 About 패널에서 (:target pseudo class 와 결합되어 있습니다):

매혹적인 -moz-element 기술

Firefox OS에 있는 시간 선택 콤퍼넌트에서 사용된 재미있는 기술입니다:

그리고 Firefox Nightly 데스크톱의 마크업 뷰에서 사용되었습니다.(지금은 불가능합니다)

표준적이지 않은 CSS 특징 덕분에 우리는 또 다른 DOM 요소 위에 배경 이미지로 DOM 요소를 사용할 수 있습니다. 예를 들어 복잡한 화면 밖 비쥬얼 콤퍼넌트를 독립된 DOM 요소인 볼 수 있는 영역 안으로 통합할 수 있습니다.

index.html 부분 소스

chrono.css 부분 소스

chrono.js 부분 소스

Using -moz-element and -moz-calc (compute components size into CSS rules and already included into CSS3 as calc) is really simple, but you can learn more on the subject on MDN:
-moz-element-moz-calc (calc 처럼 CSS3에 이미 들어 있고 CSS rules 안에서 콤퍼넌트 길이를 계산하는) 는 정말 간단합니다. 그러나 우리는 MDN에서 더 많은 것을 배울 수 있습니다:

공개하기

웹앱 포함 목록(WebApp Manifest)

개발 주기동안 우리는 애플리케이션을 R2D2B2G 메뉴옵션을 사용해서 B2G 시뮬레이터에 설치하였습니다. 그래서 우리는 실질적인 manifest.webapp이 필요하지 않았습니다. 그러나 테스트 유저에게 공개하거나 일반 유저에서 공개하려고 할려면 manifest.webapp 이 필요합니다. manifest.webapp은 어렵지 않게 만들 수 있습니다. 매우 단순하며, json구조를 가지고 있습니다: App/Manifest on MDN. manifest 파일과 관련한 디버깅은 아직 알려지지 않은 분야이고, 다음 팁들은 유용합니다:

  • manifest 파일이 구문 오류를 가지고 있거나 내려받을 수 없다면 에러는 이전 에러 콘솔에 남겨질 것입니다(그들은 새로운 웹 콘솔에 남겨지지 않습니다)
  • 만약 애플리케이션이 서브디렉토리처럼 인식된다면, 여러분은 manifest 안에 리소스(launch_path, appcache_path, icon 등)에 경로를 포함해야 합니다
  • 여러분은 앱을 플랫폼 독립적으로 제거하는 개발자(그리고, 테스트 유저)로써 여러분을 돕기 위해 제거버튼을 넣을 수 있습니다(설치된 웹앱을 “제거하는 방법”은 데스크톱,안드로이드나 Firefox OS에서 다를 수 있기 때문입니다)

OpenWebApps API를 사용해서 나는 “Chrono”에 유저들이 자체적으로 설치할 수 있도록 몇가지 코드를 넣었습니다:

브라우저를 사용해서 데스크톱에 설치하기

웹앱 런타임이나 브라우저탭안의 셀프서비스 인스톨러로 이미 설치되어 있는지 확인합니다:

리눅스 데스크톱에서 여러분이 OpenWebApp를 Firefox로 설치할 때 새로운 런처(“.desktop”) 를 “.local/share/applications”라는 숨겨진 디렉토리에 만들 것입니다.

$ cat ~/.local/share/applications/owa-http\;alcacoop.github.com.desktop
[Desktop Entry]
Name=Chrono
Comment=Gaia Chronometer Example App
Exec="/home/rpl/.http;alcacoop.github.com/webapprt-stub"
Icon=/home/rpl/.http;alcacoop.github.com/icon.png
Type=Application
Terminal=false
Actions=Uninstall;
[Desktop Action Uninstall]
Name=Uninstall App
Exec=/home/rpl/.http;alcacoop.github.com/webapprt-stub -remove

여러분이 알다시피 현재 규칙(그리고 구현)은 도메인당 한 개만 지원하고 있습니다. 만약 여러분이 숨겨진 디렉토리의 webapp파일을 들여다 본다면 여러분은 단 한개의 webapp.json 설정 파일을 발견하게 될 것입니다:

$ ls /home/rpl/.http;alcacoop.github.com/
Crash Reports  icon.png  profiles.ini  webapp.ini  webapp.json  webapprt-stub
z8dvpe0j.default

이러한 제한에 대한 이유는 MDN에 설명되어 있습니다: app manifest에 대한 자주하는 질문들 웹앱 런타임에서 앱이 작동하고 있을 때 디버깅 문제를 해결하기 위해서 명령어로 직접 구동시킬 수 있으며, 이전(그러나 여전히 유용한) 에러 콘솔을 사용할 수 있습니다:

$ ~/.http\;alcacoop.github.com/webapprt-srt -jsconsole

OpenWebApp를 제거하는 방법은 매우 간단합니다. 여러분은 이것을 “OpenWebApp 숨겨진 디렉토리” 에서 실행할 수 있는 “wbapprt-stub”를 사용하여 수동으로 삭제할 수 있습니다 (플랫폼에 의존적인 방법) :

$ ~/.http\;alcacoop.github.com/webapprt-stub -remove

자바스크립트 코드에서 “Chrono” 안에서 Firefox Browser에서 앱을 제거하는 방법을 제공할 수 있습니다:

앱캐시 목록

이것은 오래 전에 주요 브라우저에 구현되었지만 지금은 OpenWebApps 덕분에 필수요소가 되었습니다: manifest.appcache 없이, 그리고 업그레이드하기 위한 올바른 자바스크립트없이 웹앱은 오프라인에서 제대로 작동하지 않을 것이며, 진정으로 설치된 애플리케이션이라고 느껴지지 않을 것입니다.

현재 앱캐시 흑마법 중 한가지이고, 척 노리스처럼 “Facts Page”를 부여받을만 합니다: AppCacheFacts.

volo-appcache명령 덕분에 manifest.appcache를 생성하는 것은 한 라인 명령으로 간단히 실행할 수 있습니다:

  $ volo appache
  ...
  $ ls -l www-build/manifest.appcache
  ...
  $ tar cjvf release.tar.bz2 www-build
  ...

불행히도 여러분이 manifest.appcache를 디버깅/테스트할 필요가 있을 때 여러분은 스스로 해결해야 합니다. 왜냐하면 Firefox에는 현재 유용한 디버깅툴이 없습니다:

  • 앱캐시 다운로딩 상황(그리고 에러)는 현재 웹콘솔에 나타나지 않습니다.
  • 앱캐시 에러는 에러 메세지/설명이 포함되어 있지 않습니다
  • Firefox 안드로이드버전과 Firefox OS는 애플리케이션캐시를 지울 수 있는 UI를 가지고 있지 않습니다

앱캐시 디버깅 문제는 매우 기술적이며, 경험적으로 얻은 몇 가지 기술이 있습니다:

  • 모든 window.applicationCache 이벤트 ('error', 'checking', 'noupdate', 'progress', 'downloading', 'cached' , 'updateready' 등)을 구독하십시오. 그리고 개발과 디버깅하는 동안 모든 수신 이벤트/에러 메세지를 기록하세요.
  • 여러분의 첫 릴리즈안에 업그레이드를 할 수 있는 코드를 추가하세요.(또는 업그레이드하기 위해 집집마다 돌아다닐 준비를 하세요 :-D)
  • Firefox 데스크톱 버전에서는 설정창을 통해 applicationCache를 제거할 수 있습니다
  • “appcache updating”이 어디서 문제가 있는지 이해하기 위해 서버 로그를 분석하세요
  • Firefox 나 B2G를 실행할 떄 왜 문제가 생기는지 이해하기 위해 applicationCache 내부의 로그를 활성화하세요 (https://mxr.mozilla.org/mozilla-central/source/uriloader/prefetch/nsOfflineCacheUpdate.cpp#62):
    export NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
    export NSPR_LOG_FILE=offlineupdate.log
    firefox -no-remote -ProfileManager &
    
    tail -f offlineupdate.log
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::Init [7fc55959ce50]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::AddObserver [7fc56a9fcc08] to update [7fc55959ce50]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::AddObserver [7fc55c3264d8] [7fc55959ce50]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::Schedule [7fc55959ce50]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdateService::Schedule [7fc57428dac0, update=7fc55959ce50]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdateService::ProcessNextUpdate [7fc57428dac0, num=1]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::Begin [7fc55959ce50]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::NotifyState [7fc55959ce50, 2]
    -1614710976[7fc59e91f590]: 7fc559d0df00: Opening channel for http://html5dev:8888/gaia-chrono-app/manifest.appcache
    -1614710976[7fc59e91f590]: loaded 3981 bytes into offline cache [offset=0]
    -1614710976[7fc59e91f590]: Update not needed, downloaded manifest content is byte-for-byte identical
    -1614710976[7fc59e91f590]: done fetching offline item [status=0]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::LoadCompleted [7fc55959ce50]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::NotifyState [7fc55959ce50, 3]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::Finish [7fc55959ce50]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdateService::UpdateFinished [7fc57428dac0, update=7fc55959ce50]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdateService::ProcessNextUpdate [7fc57428dac0, num=0]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::NotifyState [7fc55959ce50, 10]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::RemoveObserver [7fc56a9fcc08] from update [7fc55959ce50]
    -1614710976[7fc59e91f590]: nsOfflineCacheUpdate::RemoveObserver [7fc55c3264d8] from update [7fc55959ce50]

“Gaia Chrono App”에 applicationCache를 지원하도록 추가했을 때 나는 Firefox 가 나에게 “updateready” 이벤트를 보내지 않음을 발견하기 위해 모든 기술을 동원했습니다. 그래서 나는 새로운(그리고 이미 캐싱된) 버전을 사용하기 위해 페이지를 새로 로딩하라고 말할 수 없었습니다. 문제를 이해하고 MXR에서 코드와 Bugzilla에서 버그사항을 찾아보면서 나는 마침내 버그 트래커안에 있는 버그사항을 찾았습니다:Bug 683794: html5 앱캐시앱이 업그레이드 되었을 때 onupdateready 이벤트가 뜨지 않습니다..
이 버그의 회피방법은 매우 간단합니다.(버그를 쫒는 것보다 간단합니다): 실제로는 기동하지 않는 “updateready” 리스너를 applicationCache 개체 위에 스크립트 태그 안에서 작동해야 하는 위치를 확인하고 추가합니다:

여러분이 곧 혹은 나중에 이 기능을 사용해서 시작하려면 다음을 준비해야 합니다:

  • 표준에서 제안된 형태의 구현
  • 왜 이것이 정상적으로 작동하지 않는지 디버깅하기
  • 이미 존재하는 버그나 파일에 대한 검색과 보고되지 않은 버그에 대한 보고(이것은 정말 중요합니다!!! :-D)
  • 회피방법 찾기

이것은 웹 개발 도구들이 더 많은 지원이 필요합니다. 왜냐하면 일반적인 웹 개발자는 웹앱을 보이는 화면을 “브라우저 내부” 에서 디버깅하길 원하지 않기 때문입니다.

Firefox 안드로이드 버전으로 변환하기

OpenWebApps의 흥미로운 기능은 “거의 변경사항없이 어떠한 플랫폼위에 설치된다”입니다. 예를 들어 우리는 “Chrono”앱을 데스크톱에 Firefox Nightly와 Android Nightly에 설치할 수 있습니다. 내 생각에는 Firefox Android버전에는 OpenWebApps과 Firefox OS의 미래를 위한 전략적 플랫폼이 될 수 있습니다: Android는 벌써 유명한 모바일 플랫폼이고, 개발자들이 Firefox OS와 Android를 하나의 코드로 앱을 출시할 수 있도록 해줍니다. “Chrono”앱을 Android에 옮길 때 직면한 단하나의 문제는 Firefox Android버전의 표현하는 방식과 다르다는 것입니다 (그리고 우리 애플리케이션을 담고 있는 WebAppRT안에서의 결과처럼): GekoScreenshot 서비스는 그것이 변경을 감지한 순간과 감지된 위치를 다시 그릴 것입니다. 이 기능은 -moz-element 기능과 상호작용하기 어렵고, 그것은 정말로 다시 그릴 필요가 있는 것이 무엇인지 이해하는데 약간의 도움이 필요합니다:

공개하기

GitHub pages are a rapid and simple option to release our app to the public, even simpler thanks to the volo-ghdeploy command:
volo-ghdeploy명령 덕분에 GitHub 페이지를 통해 우리의 앱을 빠르고 간단하게 공개할 수 있습니다:

  $ volo appcache && volo ghdeploy
  ...

여러분의 OpenWebApp를 주어진 도메인의 하위디렉토리(예를 들어 GitHub 페이지를 이용한)에 배포할 때 여러분은 현재 웹주소가 아니라 원본(프로토콜+호스트+포트)에 기준으로 한 manifest.webapp의 경로를 고려해야 합니다:

우리는 단하나의 OpenWebApp을 어떤 곳에서든지 설치할 수 있습니다. 그래서 여러분이 Github 페이지에서 하나보다 더 많은 앱을 배포할 때 Github 페이지를 사용자 도메인을 사용하여 설정해놔야 합니다: Github 설명 – page와 함께 사용자 도메인 설정하기.


우리의 앱이 마침내 온라인에 공개되고, 누구나 설치할 수 있게 되었을 때 우리는 Mozilla Marketplace 에 올리고, 더 많이 노출하도록 할 수 있습니다.

앱 제출이 진행되는 동안, manifest.webapp는 검증되고, 여러러분이 제출을 마치기 위해 조정해야 할 경우와 방법이 여러분에게 경고메세지로 표시될 것입니다:

  • 정보 부족에 관련된 에러 (예를 들어 이름과 아이콘 등)
  • 잘못된 값에 관련된 에러 (예를 들어 형식,형태 등)

다른 모바일 장터에서 여러분이 수집하고 채워넣어야 합니다:

  • manifest.webapp 주소 (주의: 그것은 개발자 패널에서 읽기전용이며, 여러분은 수정하실 수 없습니다)
  • 긴 설명문과 주요기능 목록
  • 짧은 릴리즈에 대한 설명
  • 하나 혹은 많은 예시화면

소규모 장터(Mini Market)

Mozilla Marketplace 목표는 에코시스템을 행하는 다른 모바일 장터처럼 OpenWebApps를 좀 더 많이 노출할 수 있도록 도와주는 것입니다. 그러나 다음과 같은 이유로 OpenWebApps에 Mozilla가 소규모 장터(Mini Market)라고 이름을 붙였습니다:
Mozilla는 OpenWebApps만을 위한 공간이 아닙니다. Mozilla는 우리가 웹에서 누릴 수 있는 동일한 자유를 가지도록 장터를 설계했습니다.

개발자에게 흥미로운 상황을 만들 수 있는 매우 강력한 기능이 있습니다:

  • Firefox OS 기기위에 탑재되는 통신사앱
  • 비공개 앱의 설치/관리자
  • 인트라넷 앱의 설치/관리자

결론

말할 필요도 없이 Firefox OS와 OpenWebApps는 현재 완벽하진 않고(그러나 매우 빠른 속도로 개선되고 있습니다), Firefox OS는 공식적인 SDK를 가지고 있지 않지만 웹은 공식적인 SDK가 필요없고, 우리는 벌써 사용하고 있습니다.

그래서 여러분은 모바일 플랫폼에 관심이 있고 어떻게 모바일 플랫폼이 만들어지고 성장하는지 알고 싶거나 웹 개발자와 여러분이 모바일 에코시스템안에서 더 많은 웹 기술을 알기 윈한다면…

여러분은 진심으로 Firefox OS를 주목해야 합니다:

우리는 더 많은 공개형 모바일 에코시스템을 가질 자격이 있습니다. 이것을 추진하기 위해 바로 시작하세요, OpenWebApps와 Firefox OS가 새롭고 강력한 도구가 될 수 있도록 도와주세요!

즐겁게 개발하시길!

작성자: Seol

Seol가 작성한 문서들…


댓글이 없습니다.

댓글 쓰기