1. 하이브리드
앱 제작 사례 공유
푸딩얼굴인식 3.0
앱스프레소랩 | 박종순
1
12년 10월 29일 월요일
2. Agenda
1. 하이브리드 앱과 프레임워크들
2. 단일페이지 인터페이스
3. 푸딩 얼굴인식 3.0 기술요소
Javascript Library
Appspresso Built-in API
Custom Plugins
4. 실전 앱 제작 테크닉
“웹 개발”스러운 “앱 개발” - 앱스프레소 On The Fly
Fast button click
iScroll 터치 이벤트 처리를 위한 레퍼
button 이벤트를 담당하는 하나의 이벤트 delegate
초기 앱 로딩 속도 개선
...
2
12년 10월 29일 월요일
3. 하이브리드 앱과 프레임워크들
하이브리드 앱 프레임워크
Javascript Framework
3
12년 10월 29일 월요일
4. 하이브리드 앱과 프레임워크들
하이브리드 앱 프레임워크
PhoneGap, Appcelerator, Appspresso
플랫폼(iOS/Android/Etc.) 별 앱 빌드기능 제공
카메라 등 장치 접근을 위한 Device API 제공
4
12년 10월 29일 월요일
5. 하이브리드 앱과 프레임워크들
Javascript Framework
jQueryMobile, Sencha Touch, JQTocuh, Jo
모바일 웹브라우저용 웹앱 제작을 위한 프레임워크
순수 웹 기반 (카메라 등 장치접근 불가)
5
12년 10월 29일 월요일
6. 단일 페이지 인터페이스
단일 페이지 인터페이스
(SPI: Single page interface)
show/hide
하나의 HTML파일로 페이지 구성
6
12년 10월 29일 월요일
7. 푸딩 얼굴인식 3.0 기술요소
Javascript Library
앱스프레소 내장 API
앱스프레소 커스텀 플러그인
7
12년 10월 29일 월요일
8. 푸딩 얼굴인식 3.0 기술요소
Javascript Library
Appspresso
jQuery 1.6 KitchenSink
소스코드 공개
xx - 자체 제작 페이지 라이브러리
기존 플랫폼: 퍼포먼스 이슈, 커스터마이징 어려움
iScroll4
jpeg encoder
Android Canvas를 이미지 파일로 저장하기 위함
oauth.js, sha1.js
Facebook, Twitter OAuth 인증용
8
12년 10월 29일 월요일
9. 푸딩 얼굴인식 3.0 기술요소
Built-in Plugin
filesystem - 히스토리 이미지, json파일저장 등
device status - 네트워크 상태 체크 등
ax.ext.ios - iOS 고유 네이티브 API
ax.ext.android - Android 고유 네이티브 API
ax.ext.ga - Google Analytics API
ax.ext.media - 화면캡쳐 API
ax.ext.net - cross domain 우회용 네트워크 API
ax.ext.ui - 네이티브 다이얼로그 API
9
12년 10월 29일 월요일
10. 푸딩 얼굴인식 3.0 기술요소
Custom Plugin
iOS / Android Camera
Push notification
Admob
Media Share - 카카오톡 등 공유
etc...
10
12년 10월 29일 월요일
11. 실전 앱 제작 테크닉
실전 앱 제작 테크닉
1. 하이브리드앱 주의사항
2. “웹 개발”스러운 “앱 개발” - On The Fly
3. Fast button click
4. iScroll 터치 이벤트 처리를 위한 레퍼
5. 버튼 이벤트는 한곳에 모아 처리
6. 초기 앱 구동 속도 개선
7. 페이지 전환 효과
8. DEMO
9. CSS 개발자 협업
10.다국어 처리
11.파일시스템 레퍼
11
12년 10월 29일 월요일
23. Fast button click - 터치 반응성 향상 기법
왜그럴까요??
Click event VS Touch event
브라우저는 터치이벤트인지를 판단할 시간이 필요
약 300ms 의 delay time
https://developers.google.com/mobile/articles/fast_buttons
http://cubiq.org/remove-onclick-delay-on-webkit-for-iphone
23
12년 10월 29일 월요일
24. Fast button click - 터치 반응성 향상 기법
이렇게 해결했습니다 touchstart
사용
var touchEvent = ‘touchstart’;
// touch event를 지원하지 않는 브라우저에서는 mousedown을 이용
if (typeof document.body.ontouchstart === 'undefined' ) {
touchEvent = ‘mousedown’;
}
document.getElementById(‘someElement’)
.addEventListener(touchEvent, function(e){
// do something
}, false);
24
12년 10월 29일 월요일
25. 실전 앱 제작 테크닉
iScroll 터치 이벤트
처리를 위한 레퍼
25
12년 10월 29일 월요일
26. iScroll 터치 이벤트 처리를 위한 레퍼
iScroll
헤더고정 바디영역 스크롤 지원
회전목마(Carousel) 지원
iOS/Android 모두 지원
크로스플랫폼 스크롤 library 중
가장 빠른 속도
터치이벤트 처리의 어려움
플랫폼에 따른 옵션변경 필요
26
12년 10월 29일 월요일
27. iScroll 터치 이벤트 처리를 위한 레퍼
이렇게 해결했습니다
touchTime을 계산하여 300ms 미만인 경우
클릭을 위한 터치이벤트로 간주
iScroll 직접수정 대신 wrapper 모듈 작성 - xx.scroll
개발자는 new iScroll(args) 대신
xx.scroll.addScroll(args) 사용
클릭을 위한 터치이벤트에 대해서만 onTouchEnd
콜백 호출
플랫폼 별 옵션 분기처리 자동화
27
12년 10월 29일 월요일
28. iScroll 터치 이벤트 처리를 위한 레퍼
touchTime 계산
var touchTime = 0,
var touchTime = 0,
touchStart = 0,
touchStart = 0,
isScrolling = false;
isScrolling = false;
document.addEventListener('DOMContentLoaded', function(){
var body = document.body;
document.addEventListener('DOMContentLoaded', function(){
body.addEventListener(‘touchstart’, function(e){
var body = document.body;
touchStart = new Date().getTime();
}, false);
body.addEventListener(‘touchmove’, function(e){
body.addEventListener(‘touchstart’, function(e){
isScrolling = true;
var myScroll = new iScroll(‘scrollWrapper’, {
}, false);
touchStart = new Date().getTime();
body.addEventListener(‘touchend’, function(e){
}, false);
...,
isScrolling = false;
}, false);
onBeforeScrollEnd: function(){
});
body.addEventListener(‘touchmove’, function(e){
var myScroll = new iScroll(‘scrollWrapper’, { Date().getTime()) - touchStart;
touchTime = (new
isScrolling = true;
...,
},onBeforeScrollEnd: function(){
}, false); = (new Date().getTime()) - touchStart;
touchTime
onTouchEnd: onTouchEnd,
},
onTouchEnd: onTouchEnd,
...
...
body.addEventListener(‘touchend’, function(e){
});
});
isScrolling = false;
...
}, false);
});
28
12년 10월 29일 월요일
29. iScroll 터치 이벤트 처리를 위한 레퍼
클릭을 위한 터치이벤트 판단
function onTouchEnd(e){
var args = this.args,
target = e.target,
eventPage,
i = 10;
if (isScrolling) {
isScrolling = false;
} else {
while (i-‐-‐) {
if($(target).hasClass('page')) { 300ms
eventPage = $(target).attr('id');
break; 이상인 경우만
}
target = target.parentNode; 버튼 이벤트
}
if (!xx.page[eventPage] || xx.page[eventPage].info.isShown) {
if(touchTime < 300 && touchTime > 1) {
if (args.onTouchEnd) { args.onTouchEnd(e); }
}
}
}
}
29
12년 10월 29일 월요일
30. iScroll 터치 이벤트 처리를 위한 레퍼
푸딩얼굴인식 3.0 사용 예
var onOptionTouchEnd = function(e) {
// 클릭이라 판단된 경우에만 호출됨
// do something
};
if(typeof xx.scroll.wrapperSetting === 'undefined'){
var scrollOption = {
wrapperId: 'wrapperSetting',
scrollbarClass: 'noScroll',
onTouchEnd: onOptionTouchEnd
};
xx.scroll.addScroll(scrollOption);
}
30
12년 10월 29일 월요일
31. 실전 앱 제작 테크닉
버튼 이벤트는
한곳에 모아 처리
31
12년 10월 29일 월요일
32. 버튼 이벤트는 한곳에 모아 처리
일반적인 이벤트 처리
document.getElementById(‘someElement’)
.addEventListener(‘touchstart’, function(e){
// do something
}, false);
$(‘#someElement’).bind(‘click’, function(e){
// do something
}, false);
<div onclick=‘alert(“aa”);’>BUTTON</div>
32
12년 10월 29일 월요일
33. 버튼 이벤트는 한곳에 모아 처리
버튼 HTML
<div class="header">
<button data-role="button" title="btnSettings" class="back">
<img src="image/icon_option.png" alt="setting" />
<div class="imgfix"> </div>
<span class="bg"></span>
<img class="new hide" src="image/main_btn_new.png" alt="new" />
</button>
<div class="title">
<h1><img id="mainTitle" src="image/beacon.png" alt="얼굴인식"/></h1>
<div class="imgfix"> </div>
</div>
<button data-role="button" title="btnMy" class="login">
<img src="image/icon_login.png" alt="my" />
<div class="imgfix"> </div>
<span class="bg"></span>
</button>
</div>
33
12년 10월 29일 월요일
34. 버튼 이벤트는 한곳에 모아 처리
하나의 이벤트 delegate
document.body.addEventListener('touchstart', function(e) {
setTimeout(function() {
document.body.addEventListener('touchstart', function(e) {
var target = e.target,
// 버튼 누름효과 제거
var target = e.target,
btnName,
44. if (target.getAttribute('data-‐role') === 'button') {
target = target.parentNode;
}
45.
46.
47.
48. // 실제 버튼 element 찾기
while (i-‐-‐) { // 이벤트가 발생한 페이지 찾기
while(i-‐-‐) { if (target.getAttribute('data-‐role') === 'button') {
if($(target).hasClass('page')) { // 버튼 이름 가져오기
if($(target).attr('data-‐role') === 'button') { btnName = $(target).attr('title');
$(target).addClass('pressed');
eventPage = $(target).attr('id');
110. 버튼 이벤트는 한곳에 모아 처리
onButtonTouched - 2
...
switch (eventPage) { // 이벤트 페이지에 따라 분기 처리
switch (eventPage) {
case 'pageMain':
case 'pageMain':
switch (btnName) {
switch (btnName) { // 버튼 이름에 따라 분기 처리
case 'btnFind':
xx.page.pageFind.show();
case 'btnFind':
break;
case 'btnBattle':
xx.page.pageFind.show();
xx.page.pageBattle.show();
break;
break;
}
case 'btnBattle':
break;
// 중복 이벤트방지 플래그가 켜진 경우 1추 후 플래그 해지
case '...':
xx.page.pageBattle.show();
if (_btnGlobalTouchActionFlag === true) {
}
break;
_btnGlobalTouchActionTimer = setTimeout(function(){
if (_btnGlobalTouchActionFlag === true) {
}
_btnGlobalTouchActionFlag = false;
_btnGlobalTouchActionTimer = setTimeout(function(){
break;
_btnGlobalTouchActionFlag = false;
_btnGlobalTouchActionTimer = undefined;
_btnGlobalTouchActionTimer = undefined;
case 'pageBattle':
}, 1000);
}, 1000);
}
} // do something
}
break;
36
12년 10월 29일 월요일
111. 버튼 이벤트는 한곳에 모아 처리
효과
하나의 이벤트 리스너 정의
앱 전역에 걸친 일관된 이벤트 처리
CSS 개발과 무관한 버튼 액션 처리
버튼누름 효과 처리 자동화
이벤트 코드를 한곳에 모음으로서 유지보수 용이
37
12년 10월 29일 월요일
113. 초기 앱 구동 속도 개선
하이브리드 앱은 느려요~
단일페이지 인터페이스로 구성된 앱
페이지 리소스는 앱 구동시에 로딩?
jQueryMobile의 멀티페이지 템플릿
리소스의 lazy loading이 답이다!
39
12년 10월 29일 월요일
114. 초기 앱 구동 속도 개선
싱글 페이지 템플릿
!-- main page --
div id=pageMain class=page style=display: none;
div class=header
...
img class=new hide src=image/main_btn_new.png alt=new /
...
img class=main_event_ball1 src=locales/ko/image/main_event1.png/
img class=hide main_event_ball2 src=locales/ko/image/main_event2.png/
img class=hide main_event_ball1 src=locales/ko/image/main_event1.png/
메인 페이지
img class=hide main_event_ball2 src=locales/ko/image/main_event2.png/
div class=imgfixnbsp;/div
...
/div
div class=body
...
img class=type1 o1 src=locales/ko/image/main_frame03.png/
img class=type1 o2 src=locales/ko/image/main_frame04.png/
...
/div !-- page pageBattleSearch --
/div div id=pageBattleSearch class=page hide ui
...
!-- page pageFind -- img src=image/find_icon_guide.png /
div id=pageFind class=page hide ui ...
img src=image/find_guide1.png alt=선글라스NO /
서브2
img src=image/battle_guide1.png /
서브1
... img src=image/battle_guide_icon.png /
img src=image/find_guide2.png alt=모자NO / img src=locales/ko/image/battle_guide2.png/
... img src=image/battle_guide_icon.png /
img src=image/find_guide3.png alt=옆모습NO / img src=image/battle_guide3.png /
/div ...
... /div
40
12년 10월 29일 월요일
115. 초기 앱 구동 속도 개선
이렇게 해결했습니다
앱 실행시 불필요한 리소스 Lazy Loading
image, javascript, html 은 모두 Lazy Loading
(단, CSS는 앱 시작시 full loading)
멀티페이지 템플릿, 페이지 모듈 JS 분리
Lazy Loading을 담당하는 xx.page 라이브러리
(개발자와 사용자는 Lazy Loading이 되는지도 몰라요)
41
12년 10월 29일 월요일
116. 초기 앱 구동 속도 개선
멀티페이지 템플릿
!-- main page --
!-- page pageBattle --
div id=pageMain class=page style=display: none;
div class=header
div id=pageBattle class=page hide ui/div
...
img class=new hide src=image/main_btn_new.png alt=new /
...
HTML
img class=main_event_ball1 src=locales/ko/image/main_event1.png/ Javascript
!-- page pageBattle --
img class=hide main_event_ball2 src=locales/ko/image/main_event2.png/
img class=hide main_event_ball1 src=locales/ko/image/main_event1.png/
메인 페이지
img class=hide main_event_ball2 src=locales/ko/image/main_event2.png/
div id=pageBattleResult
...
div class=imgfixnbsp;/div
class=page hide ui/div
/div
div class=body
...
!-- page whois (finding) --
img class=type1 o1 src=locales/ko/image/main_frame03.png/
img class=type1 o2 src=locales/ko/image/main_frame04.png/
div id=pageFinding class=page hide/div
/div
...
/div !-- page pageBattle --
div id=pageBattle class=page hide ui/div
!-- page pageFind --
!-- page whois result -- !-- page pageBattle --
div id=pageFind class=page hide ui/div
div id=pageBattleResult class=page hide ui/div
div id=pageFound class=page pageBattleSearch --
!-- page whois (finding) --
div id=pageFinding class=page hide/div
hide ui/div
!-- page
... whois result --
!-- page
div id=pageBattleSearch class=page hide ui/div
div id=pageFound class=page hide ui/div !-- page pageBattleSearchStar --
div id=pageBattleSearchStar class=page hide ui/div
42
12년 10월 29일 월요일
117. 초기 앱 구동 속도 개선
HTML lazy loading
if (this.innerHTML === '') {
...
var targetHTML = arg.id + '.html?' + (new Date().getTime());
if (arg.useLocale === true) {
targetHTML = 'locales/' + _lang + '/' + targetHTML;
}
// 내용이 없는 경우 ajax를 이용한 lazy loading
$.ajax({
url: targetHTML,
context: body
}).done(function(data) {
$(self).html(data).after(function(){
loadContentCallback();
});
});
...
}
43
12년 10월 29일 월요일
118. 초기 앱 구동 속도 개선
Javascript lazy loading
function addScript(url, callback) {
if (_DEBUG) { console.log('addScript', url); }
if (typeof lazyScriptLoading[url] === 'undefined') {
119.
120.
121.
122.
123.
124.
125.
126.
127.
128. // bookmarklet 기법을 활용한 스크립트 lazy loading
(function(e) {
e.onload = callback;
e.setAttribute(src, url);
document.getElementsByTagName(body)[0].appendChild(e);
}(document.createElement(script)));
!-- page pageBattle --
lazyScriptLoading[url] = true; div id=pageBattle class=page hide ui/div
} else { !-- page pageBattle --
if (typeof callback === 'function') { class=page hide ui/div div id=pageBattleResult
callback(); !-- page pageBattleSearch --
div id=pageBattleSearch class=page hide ui/div
}
!-- page pageBattleSearchStar --
} div id=pageBattleSearchStar class=page hide ui/div
}
44
12년 10월 29일 월요일
129. 초기 앱 구동 속도 개선
푸딩얼굴인식 3.0 사용 예 - 1
xx.page.add({
id: 'pageMy',
onPageBeforeLoadForInit: function(){
var self = this;
app.common.addScript('js/page_my.js', function(){
self.info.onPageLoad = function(){
app.pageMy.showScroll();
};
});
}, !-- page pageBattle --
div id=pageBattle class=page hide ui/div
onPageHide: function() { !-- page pageBattle --
app.pageMy.resetStatus(); id=pageBattleResult class=page hide ui/div
div
}, !-- page pageBattleSearch --
div id=pageBattleSearch class=page hide ui/div
useLocale: true !-- page pageBattleSearchStar --
div id=pageBattleSearchStar class=page hide ui/div
});
45
12년 10월 29일 월요일
130. 초기 앱 구동 속도 개선
푸딩얼굴인식 3.0 사용 예 - 2
xx.page.add({
id: 'pageSetting',
onPageBeforeLoadForInit: function(){
var self = this;
app.common.addScript('js/page_setting.js', function(){
self.info.onPageLoad = function(){
app.pageSetting.updateUI();
};
});
}, !-- page pageBattle --
div id=pageBattle class=page hide ui/div
onPageBeforeHide: function(){ pageBattle --
!-- page
app.common.removeWebView();
div id=pageBattleResult class=page hide ui/div
}, !-- page pageBattleSearch --
div id=pageBattleSearch class=page hide ui/div
type: 'vertical' !-- page pageBattleSearchStar --
div id=pageBattleSearchStar class=page hide ui/div
});
46
12년 10월 29일 월요일
131. 초기 앱 구동 속도 개선
효과
네이티브앱 수준의 앱 시작 속도
앱 메모리 사용 최소화
멀티페이지 템플릿을 활용한 다국어 처리 용이
CSS 개발자와 협업 시 코드 충돌 최소화
47
12년 10월 29일 월요일
133. 페이지 전환 효과
HTML
!-- page pageBattle --
div id=pageBattle class=page ui.../div
!-- page pageBattle --
div id=pageBattleResult class=page ui.../div
!-- page whois (finding) --
div id=pageFinding class=page.../div
!-- page whois result --
div id=pageFound class=page ui.../div
49
12년 10월 29일 월요일
134. 페이지 전환 효과
CSS
.page{
width: 100%;
height: 100%;
left: 0;
top: 0;
position: absolute;
overflow: hidden;
opacity: 0;
-webkit-transition: all 0.3s ease-out;
}
50
12년 10월 29일 월요일
135. 페이지 전환 효과
Javascript
-webkit-transition: all 0.3s ease-out;
// 배틀페이지 보이기
$('#pageBattle').css({opacity: 1});
// 배틀페이지 슬라이드 다운
$('#pageBattle').css({top: window.innerHeight});
// 배틀페이지 슬라이드 업
$('#pageBattle').css({top: 0});
51
12년 10월 29일 월요일
136. 페이지 전환 효과
CSS3 Transforms
http://www.w3.org/TR/css3-transforms/
http://www.the-art-of-web.com/css/css-
animation/
http://www.the-art-of-web.com/css/
timing-function/
52
12년 10월 29일 월요일
139. CSS 개발자와 협업
data-role=”button”
div data-role=button title=btnBattle
img id=main_btn_frame3 class=type1 o1
src=locales/ko/image/main_frame03.png alt=닮은꼴 배틀 /
div class=imgfixnbsp;/div
div class=tit t1/div
img id=main_btn_frame3_desc class=battle-msg msg-left
src=locales/ko/image/main_battle_msg2.png
alt=누가 더 연예인을 닮았을까? /
/div
div data-role=button title=btnFind
img id=main_btn_frame4 class=type1 o2
src=locales/ko/image/main_frame04.png alt=닮은꼴연예인찾기 /
div class=imgfixnbsp;/div
div class=tit t2/div
/div
55
12년 10월 29일 월요일
140. CSS 개발자와 협업
효과
data-role=”button” 을 이용하므로 하위 마크업
과 무관한 버튼영역 처리
멀티페이지 템플릿 사용을 통한 CSS 개발자와 협업
시 코드 충돌 최소화
56
12년 10월 29일 월요일