블로그 이미지
Peter Note
Web & LLM FullStacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2016. 4. 13. 13:31 Electron & Ionic

Ionic 프레임워크는 하이브리드 웹앱을 만들기 위한 프레임워크이다. 웹앱이기에 웹을 위한 프레임워크로 Angular를 사용하고 있다. Ionic v1에서는 Angular v1Ionic v2에서는 Angular v2를 사용하고 있다. 







Ionic은 하이브리드 웹앱 프레임워크외에 다양한 서비스를 제공하고 있다. Ionic 플랫폼 상에서 화면을 디자인하고 배포하고 테스트할 수 있는 서비스들을 제공한다. Ionic.io 는 Ionic 프레임워크를 이용해 만든 앱을 배포 관리하는 곳이다. Ionic Creator는 화면을 디자인하는 서비스이며, Ionic View는 앱을 앱스토어에 배포하거나 로컬에 USB로 설치하지 않고 Ionic View앱을 설치하면 Ionic 프레임워크로 만든 모든 앱들을 iOS 또는 Android 상에서 바로 볼 수 있는 서비스이다. 


위의 그림 Ionic는 Cordova를 기반으로 하고 CLI(Command Line Interface)를 제공하며 CLI는 두가지를 통합하고 있다. 첫째는 Cordova의 plugin에 대한 install/uninstall을 위한 명령이고 두번째는 Gulp를 이용해서 웹파일(Sass, Html, Scripts)을 위한 명령 Task에 대해 Gulp의 환경파일인 gulpfile.js에 정의하고 있다. Vinyl은 Gulp에서 사용되는 모듈로 다양한 OS에 상관없이 File Stream을 지원하기 위한것으로 개념은 링크를 참조한다. Ionic은 iOS와 Android Native UI에 가깝게 Angular 기반으로 컴포넌트를 제공하고 있다. 즉, Angular의 Directive(지시자, 컴포넌트)를 이용해 화면을 만드는 방식이다. 또한 Cordova의 기능을 Angular의 서비스로 사용하기 위해 ngCordova도 제공하고 있다. 


Ionic을 사용하기 위해서는 다음과 같은 사전지식이 필요하다. 

  - Node.js 그리고 NPM 사용법

  - 단순 테스트 목적이 아니라면 반드시 Angular 프레임워크 사용경험이 필요하다  

  - XCode 또는 Android Studio에서 간혹 Cordova의 Plugin을 수정할 때도 있다. 

  - 기본 XCode, Android Studio 사용법은 알아두는게 좋다. 직접 툴에서 빌드할 경우가 많다. 


현재 Ionic v2는 Angular v2를 기반으로 하고 아직 beta 버전이다 (2016.4.12).  2016년 상반기에 Angular v2 정식버전이 나오면 비슷한 시기에 정식버전이 나오리라 기대해 본다. Angular v2가 정식버전하에 하반기부터 본격적으로 쓰일 것으로 보이기때문에 Ionic도 v2를 사용하고 준비하면 좋을 것같다. Ionic v2를 사용하기 위해서는 따라서 다음과 같은 기초 지식도 필요하다. 

  - ES2015 JavaScript 스팩 상의 문법 추가 사항을 숙지해야 한다. OOP 스타일의 Syntax로 바뀌었다고 보면 된다.  

  - Angular v2 가이드문서 (반드시 TypeScript 기반으로 참조)를 최소 한번쯤은 보도록 한다. 

  - TypeScript 기반 Cordova 서비스를 통해 Native 접근이 필요할 것이다.  


알아야 할 것은 많지만 일단 설치부터 실행까지 보도록 한다. Ionic v2 CLI 명령어Cordova 공식 CLI 명령어도 한번 훑어보는게 좋다. Cordova는 현재 6.* 버전이 최신이다.





1. 설치하기 


먼저 Node.js를 설치한다. NPM(Node Package Manager)를 통해 Cordova와 ionic 프레임워크를 설치하고 Gulp 수행의 기반을 제공한다. 

  - typescript 컴파일러 설치

  - typings 는 typescript의 definition 파일을 관리하는 메니저이다. typings 폴더에서 관리된다. 

  - cordova는 iOS와 Android Native 접근을 위한 Gateway library라고 보면 된다. 

> sudo npm install -g   typings  typescript  cordova ionic@beta



다음으로 ionic에서 제공하는 샘플 파일을 자동 다운로드 받아 설치한다. ionic v2 에 typescript 버전으로 설치하는 명령어이다. 자동으로 typings폴더에 typescript definition 파일 및 package.json에 정의된 모듈도 node_modules 밑으로 자동 설치된다.

프로젝트생성) ionic start <projectName> <templateType> --v2 --ts

예) ionic start myFirst blank --v2 --ts


blank 템플릿 타입을 주면 https://github.com/driftyco/ionic2-starter-blank/archive/typescript.zip 에서 typescript 버전을 자동 다운로드해 설치한다.

templateType에는 tutorial, tabs, sidemenu, blank가 있다. 설치를 하면 다음과 같은 안내글이 나온다. 


Make sure to cd into your new app directory:

  cd ionic2-tutorial-github


To run your app in the browser (great for initial development):

  ionic serve


To run on iOS:

  ionic run ios


To run on Android:

  ionic run android


To test your app on a device easily, try Ionic View:

  http://view.ionic.io


New! Add push notifications, update your app remotely, and package iOS and Android apps with the Ionic Platform!

  https://apps.ionic.io/signup


New to Ionic? Get started here: http://ionicframework.com/docs/v2/getting-started


설명대로 ionic2-tutorial-github 폴더로 이동후 ioinic serve 명령을 수행하면 Gulp의 serve 태스커가 수행된다. 수행 결과로 ionic $ 명령콘솔이 활성화 된다.

> ionic serve 

WARN: ionic.config.js has been deprecated, you can remove it.

Running live reload server: http://localhost:35729

Watching: www/**/*, !www/lib/**/*

√ Running dev server:  http://localhost:8100

Ionic server commands, enter:

  restart or r to restart the client app from the root

  goto or g and a url to have the app navigate to the given url

  consolelogs or c to enable/disable console log output

  serverlogs or s to enable/disable server log output

  quit or q to shutdown the server and exit


ionic $





2. ionic 폴더 구조


ionic의 폴더구조 및 환경설정 파일은 다음과 같다. 


 app

 개발자가 작성하는 모든 애플리케이션 코드가 위치한다. 

 hooks (cordova)

 Cordova 빌드과정의 일부로서 동작될 수 있는 스크립트를 포함하고 있다. 앱을 패키지 할때 필요하다면 언제든 커스터마이징 할 수 있다.  

 node_modules

 npm을 통해 설치된 모듈들이 있다.

 platforms (cordova)

 ionic platform 으로 ios, android를 설치하면 하위 폴더로 생기고, XCode 또는 Android Studio에서 import할 수 있다.

 plugins (cordova)

 ionic platform 선택시 Cordova의 플러그인이 설치되는 폴더이다. 

 resources 

 앱을 위한 icon과 splash image를 해상도가 틀린 모바일 기기별로 놓는 곳이다.  

 typings

 Typescript로 쓰여지지 않는 JS 라이브러리의 타입정의를 한 type definition 파일이 있다.  

 www (cordova)

 index.html를 포함한다. 이곳은 빌드될 때 사용되는 것으로 애플리케이션 코드가 위치하는 곳이 아님을 주의하자.  "ionic build" 를 하면 "cordova build"가 수행되어 www 해당 디렉토리에 app의 코드가 위치하고 다시 platforms/ios 또는 android의 www 폴더에 copy된다. 따라서 최종 사용되는 파일은 platforms/ios (또는 android) /www/* 에 위치한다. 

 config.xml (cordova)

 앱 패키지를 만들때 사용하기 위해 Cordova의 환경설정이 존재한다. 

 ionic.config.js

 not used로 앞으로 없어질 것이다. ionic.config.json 파일은 버전 정보만 전달

 package.json

 npm 으로 설치되는 모든 모듈에 대한 설정 

tsconfig.json / typings.json

 TypeScript 환경 설정 / type definition file 환경 설정


크게 "환경파일", "Cordova", "애플리케이션" 부분으로 나뉠 수 있다. 최초 템플릿이 생성된 이후 개발자는 "애플리케이션"폴더인 "app"를 사용하면 된다. blank타입으로 만들었을 때 platforms 폴더에는 ios 플랫폼이 기본 설치된다. 







3. TypeScript 기반 개발


최근(2016.4.8) Beta버전에 CLI 를 통해 TypeScript 기반의 Angular v2 페이지와 서비스를 만들 수 있는 명령어를 공개했다. 파일이름은 kebob-case로 my-page와 같이 되고, ES6/TypeScript기반의 class는 PascalCase로 MyData로 처럼 이름을 준다. 

> ionic generate ( 또는 축약해서 g ) <page 또는 provider> <Name>


예)

> ionic g page myPage

create app/pages/my-page/my-page.html (.js, .css)


> ionic g provider MyData

create app/providers/my-data/my-data.js 


생성된 my-page.ts 소스는 다음과 같다. @Page는 TypeScript에서 제공하는 Decorator를 이용해 Class Decorator를 Ionic을 위해 만든 것이다. 

  - TypeScript의 Decorator에 대해 자세히 알고 싶다면 링크를 참조한다. 

import {Page, NavController} from 'ionic-angular';


/*

  Generated class for the MyPagePage page.


  See http://ionicframework.com/docs/v2/components/#navigation for more info on

  Ionic pages and navigation.

*/

@Page({

  templateUrl: 'build/pages/my-page/my-page.html',

})

export class MyPagePage {

  constructor(public nav: NavController) {

    this.nav = nav;

  }

}


.ts가 .js코드로 컴파일된 내역을 보고 싶다면 app 폴더로 이동해서 TypeScript 컴파일 명령어인 "tsc"를 수행한다. @Page는 __decorate({ ... }) 안에서 "ionic-angular"의 decorators/page.js 의 Class Decorator가 수행되는 것이다. ionic_angular_1.Page( <config object> ); 결국, @Page는 구현한 decorator 펑션을 호출해 주는 역할을 수행할 뿐이다. TypeScript Decorator 공식문서를 통해 개념을 이해하자. @Page는 Angular v2에서 제공하는 데코레이터가 아니라 Ionic에서 제공하는 것이다. 내부 소스를 보면 @Page 클래스는 ion-page selector를 사용하고 결국 Angular v2 core에 있는 @Component를 호출하고 있을 뿐이다. 즉, 굳이 @Page를 쓰지 않아되 되는 Decorating이다. 

// app/pages/my-page/my-page.js TypeScript 컴파일된 소스


"use strict";

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {

    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;

    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);

    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;

    return c > 3 && r && Object.defineProperty(target, key, r), r;

};

var __metadata = (this && this.__metadata) || function (k, v) {

    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);

};

var ionic_angular_1 = require('ionic-angular');

/*

  Generated class for the MyPagePage page.


  See http://ionicframework.com/docs/v2/components/#navigation for more info on

  Ionic pages and navigation.

*/

// 자동으로 postfix로 Page가 붙는다. 

var MyPagePage = (function () {

    function MyPagePage(nav) {

        this.nav = nav;

    }

    MyPagePage = __decorate([

        ionic_angular_1.Page({

            templateUrl: 'build/pages/my-page/my-page.html',

        }), 

        __metadata('design:paramtypes', [ionic_angular_1.NavController])

    ], MyPagePage);

    return MyPagePage;

}());

exports.MyPagePage = MyPagePage;



// node_modules/ionic-angular/decorators/page.js 데코레이터 구현 소스


function Page(config) {

    return function (cls) {

        // @Page 의 기본 selector

        config.selector = 'ion-page';  

        config.directives = config.directives ? config.directives.concat(directives_1.IONIC_DIRECTIVES) : directives_1.IONIC_DIRECTIVES;

        config.host = config.host || {};

        config.host['[hidden]'] = '_hidden';

        config.host['[class.tab-subpage]'] = '_tabSubPage';

        var annotations = _reflect.getMetadata('annotations', cls) || [];

        // 기본 설정후 Angular v2 core의 @Component를 호출한다. 

        annotations.push(new core_1.Component(config)); 

        _reflect.defineMetadata('annotations', annotations, cls);

        return cls;

    };

}

exports.Page = Page;


provider로 만든 것은 ES2015의 Promise, RxJS, @Injectable이 포함되어 있다. Angular v2에서는 모든 Inject되는 Service Class에는 Class Decorator로 @Injectable를 설정토록 권고한다. 

// app/providers/my-data/my-data.ts 소스 


import {Injectable} from 'angular2/core';

import {Http} from 'angular2/http';

import 'rxjs/add/operator/map';


/*

  Generated class for the MyData provider.


  See https://angular.io/docs/ts/latest/guide/dependency-injection.html

  for more info on providers and Angular 2 DI.

*/

@Injectable()

export class MyData {

  data: any = null;


  constructor(public http: Http) {}


  load() {

    if (this.data) {

      // already loaded data

      return Promise.resolve(this.data);

    }


    // don't have the data yet

    return new Promise(resolve => {

      // We're using Angular Http provider to request the data,

      // then on the response it'll map the JSON data to a parsed JS object.

      // Next we process the data and resolve the promise with the new data.

      this.http.get('path/to/data.json')

        .map(res => res.json())

        .subscribe(data => {

          // we've got back the raw data, now generate the core schedule data

          // and save the data for later reference

          this.data = data;

          resolve(this.data);

        });

    });

  }

}





5. ionic 페이지 만들기 


일단, ionic generate를 통해 생성된 page, provider (서비스)는 참조만 한다. 다음과 같이 서비스를 파일을 만든다. Angular2 http 모듈이 RXJS를 사용하고 있고 return 오브젝트가 Promise가 아니라 Observable이다. RXJS는 Reactive Programming의 자바스크립트 구현체로 링크를 참조한다.

import { Injectable } from 'angular2/core';

import { Http, Headers } from 'angular2/http';


@Injectable()

export class GitHubService {

    constructor(private http: Http) {}


    getRepos(username) {

        let repos = this.http.get(`https://api.github.com/users/${username}/repos`);

        return repos;

    }

}


다음으로 /app/pages/home/home.ts를 수정한다. @Component 대신에 @Page를 사용하고 providers로 GitHubService를 정의한다. 

import {Page} from 'ionic-angular';

import {GitHubService} from '../../providers/github.service';


@Page({

  templateUrl: 'build/pages/home/home.html',

  providers: [GitHubService]

})

export class HomePage {

  public foundRepositories;

  public username;

  constructor(private github: GitHubService) {}

  getRepos() {

    this.github.getRepos(this.username)

      .subscribe(

          data => {

              this.foundRepositories = data.json();

          },

          err => console.error(err),

          () => console.log('getRepos completed')

      );

  }

}


home.html도 다음과 같이 재정의한다. 

<ion-navbar *navbar>

<ion-title>

Home

</ion-title>

</ion-navbar>


<ion-content class="home">

<ion-list inset>

<ion-item>

<ion-label>Username</ion-label>

<ion-input [(ngModel)]="username" type="text"></ion-input>

</ion-item>

</ion-list>

<div padding>

<button block (click)="getRepos()">Search</button>

</div>

<ion-card *ngFor="#repo of foundRepositories">

<ion-card-header>

{{ repo.name }}

</ion-card-header>

<ion-card-content>

{{ repo.git_url }}

</ion-card-content>

</ion-card>

</ion-content>


ionic CLI 명령을 통해 테스트를 해보자. serve 명령을 이용하면 로컬 서버를 띄워서 브라우져상에서 테스트를 해볼 수 있다. 옵션으로 --lab을 주면 ios, android 둘 다 테스트가 가능하다. 

> ionic serve --lab 



ionic의 ion-* 시작하는 Angular component를 사용하면 mobile 플랫폼에 따라 Look and feel이 자동으로 맞춰진다. 이를 원하지 않을 때는 최소한의 ion-* 태그만을 사용하면 된다. ion-navbar 와 ion-content 태그만을 사용하고 나머지는 서비스에 맞게 HTML을 작성해도 무방하다.





6. ionic Navigation 추가하기 


네비게이션을 위해 메인 컴포넌트인 app.ts를 살펴보면, ionic에서 제공하는 @App 데코레이터를 통해서 HomePage 를 최상위 페이지로 설정하고 있다.

import 'es6-shim';

import {App, Platform} from 'ionic-angular';

import {StatusBar} from 'ionic-native';

import {HomePage} from './pages/home/home';


@App({

  template: '<ion-nav [root]="rootPage"></ion-nav>',

  config: {} // http://ionicframework.com/docs/v2/api/config/Config/

})

export class MyApp {

  rootPage: any = HomePage;


  constructor(platform: Platform) {

    platform.ready().then(() => {

      // Cordova 준비

      StatusBar.styleDefault();

    });

  }

}


GitHub 저장소 목록에서 상세페이지 이동을 위한 detail 페이지를 만들고, 일단 home.html 과 .ts 파일을 수정한다.

// home.html 에서 click 이벤트를 추가한다. 

<ion-card *ngFor="#repo of foundRepositories" (click)="goDetail(repo)">


// home.ts 에 Navigation을 위한 controller와 추가 메소드를 정의한다. 

import {Page, NavController} from 'ionic-angular';

import {GitHubService} from '../../providers/github.service';

import {DetailPage} from '../detail/detail'


@Page({

  templateUrl: 'build/pages/home/home.html',

  providers: [GitHubService]

})

export class HomePage {

  public foundRepositories;

  public username;

  constructor(private github: GitHubService, private nav: NavController) {}

  getRepos() {

    this.github.getRepos(this.username)

      .subscribe(

          data => {

              this.foundRepositories = data.json();

          },

          err => console.error(err),

          () => console.log('getRepos completed')

      );

  }

  goDetail(repo) {

      // 페이지를 Stack에 추가하는 것이다. 

      this.nav.push(DetailPage, { repo: repo });

  }

}


home.html 상단의 ion-navbar 태그는 pagenavigation하면 <Back 버튼이 자동으로 생긴다. 즉, <Back 버튼을 클릭하는 순간 이동한 페이지를 pop 하는 것이 자동으로 이루어 지기때문에 명시적으로 해줄 필요가 없다. 다음으로 저장소의 README 파일 내역을 HTML 포멧으로 가져오는 메소드를 GitHubService에 추가한다. 

// github.service.ts


getDetail(repo) {

        let headers = new Headers();

        headers.append('Accept', 'application/vnd.github.VERSION.html');

        return this.http.get(`${repo.url}/readme`, {headers: headers});

}


상세내역을 detail.ts를 통해 상세정보를 가져오고 다시 detail.html에 뿌려준다. NavParams를 통해 전달받은 repo 객체의 url로 README 값을 받아온다. detail.html에서는 아래 소스처럼 {{ readme }} 표현식을 사용하면 HTML 태그가 text로 뿌려질 뿐이다. 따라서 innerHTML 속성을 이용한다. 

// detail.ts 


import {Page, NavController, NavParams} from 'ionic-angular';

import {GitHubService} from '../../providers/github.service';


@Page({

  templateUrl: 'build/pages/detail/detail.html',

  providers: [GitHubService]

})

export class DetailPage {

  public readme = '';

  public repo;

  constructor(private nav: NavController, private github: GitHubService, private navParams: NavParams) {

      this.repo = navParams.get('repo');

      this.github.getDetail(this.repo)

        .subscribe(

            data => this.readme = data.text(),

            err => {

                if(err.status == 404) {

                    this.readme = 'This repo does not have a README file';

                } else {

                    console.error(err);

                }

            },

            () => console.log('getDetail completed')

        );

  }

}



// detail.html 

<ion-navbar *navbar>

  <ion-title>{{ repo.name }}</ion-title>

</ion-navbar>


<ion-content padding class="detail">

  <div padding>{{ readme }}</div>

</ion-content>



detail.html을 text가 아니 DOM으로 넣기 위해 innerHTML속성으로 변경하고 최종 테스트 한다. 파일을 변경하면 ionic은 파일을 watch하고 있다가 자동으로 refresh 해준다. (ionic serve 경우)

<ion-content padding class="detail">

  <div padding [innerHTML]="readme"></div>

  <!-- <div padding>{{ readme }}</div> -->

</ion-content>


ios와 Android 플랫폼에 맞는 "< Back" 버튼이 생성된다. github 사용자 이름을 입력하고 목록이 나오면 목록중의 카드하나를 클릭하면 다음과 같이 Navigation되는 것을 볼 수 있다. ionic은 Tabs 형태 또는 Menus형태의 UI 컨테이너를 통해 Navigation을 Mobile UX에 맞게 변경할 수 있다. 





7. ionic 배포와 테스트 


ionic serve 명령으로 로컬 웹서버를 띄워 로컬 웹 브라우져를 통해 기능을 테스트 했다면 emulate 또는 기기에 App을 배포해서 테스트 한다. 브라우져에서 보는 것과 실 기기에서 테스트하는 것은 하늘과 땅 차이다. 희소식은 XCode 7 부터는 Developer Account(유료)없이도 USB를 통해 iPhone에 App을 배포 테스트할 수 있다. 

// 로컬에서 xcode emulate을 띄워준다. 

> ionic emulate ios


// 연결된 기기에서 수행할 수 있다

> ionic run ios 


MacBook 상에서 ionic run을 위해서는 "npm install -g ios-deploy" 사전에 설치가 필요하다. 



또는 ionic viewer를 모바일 기기에 설치하고 프로젝트를 ionic.io 서비스에 자신의 계정에 upload한 후 작동 여부를 테스트 할 수 있다. 하지만 작동중 디버깅에 대한 부분은 보다 많은 지면을 필요로 하기에 다음에 언급한다. upload시에 로그인 안되어 있으면 등록한 id/pwd를 물어본다. 

> ionic upload 

WARN: No 'upload:before' gulp task found!

If your app requires a build step, you may want to ensure it runs before upload.


Uploading app....

Saved app_id, writing to ionic.io.bundle.min.js...

Successfully uploaded (f3ded32c)


Share your beautiful app with someone:


$ ionic share EMAIL

Saved api_key, writing to ionic.io.bundle.min.js...


최근에는 Xamarin이나 React Native, NativeScript 같은 Native Code로 전환해 주는 프레임워크가 인기를 끌고 있다. 모바일만을 고려하지 않는다면 ionic은 웹표준 기술을 통해 빠른 기능개발과 Angular2기반의 프론트앤드의 서비스와 컴포넌트(만일 Web을 Angular2로 개발한다면)를 공유할 수 있다. 즉, Native Mobile UX/UI 특성과 빌드 환경의 편의성은 Ionic 프레임워크를 사용하면서 웹개발도 도모할 수 있는 Hybrid WebApp + Web 개발이 가능하다 판단된다. 


향후 angular2-seedNativescriptElectron을 접목한 angular2-seed-advanced가 나왔듯이, angular2-seed에 Ionic2를 접목한 seed를 만들 예정이다. 





참조


  - ionic v2 공식 문서

  - Vinyl 개념 이해하기 & Stream Handbook

  - Cordova 공식 홈페이지 문서

  - TypeScript Decorator 만들기

  - 블로깅 참조 

  - 다양한 모바일 기기 테스트 방법들

  - Ionic2 Conference App 소스

  - Ionic.io 의 다양한 서비스들: Push, Deploy, Analytics, User

  - Crosswalk



자료


  - Ionic Advantures

  - Ionic Collection

  - Ionic Resources

  - AppCamp

  - play.ionic.io: playground


     


posted by Peter Note