Thinkreals에서 앱개발을 담당하는 소프트웨어 엔지니어 @muik 입니다. 개발한 앱으로는 쿠폰모아, 포켓스타일 안드로이드앱이 있는데요. 지금까지 앱개발을 하며 하이브리드앱 필요성을 많이 느껴왔습니다. 아마 비슷한 분들이 많을꺼라 생각되어 이번에 하이브리드앱 도입 준비를 하며 경험한 것을 공유해보려 합니다.
Thinkreals에서 앱개발을 담당하는 소프트웨어 엔지니어 @muik 입니다. 개발한 앱으로는 쿠폰모아, 포켓스타일 안드로이드앱이 있는데요. 지금까지 앱개발을 하며 하이브리드앱 필요성을 많이 느껴왔습니다. 아마 비슷한 분들이 많을꺼라 생각되어 이번에 하이브리드앱 도입 준비를 하며 경험한 것을 공유해보려 합니다.
The comprehensive guide for optimizing the performance of mobile HTML5 Web ap...Sang Seok Lim
The deck includes a set of techniques and knowledge that can be used when you try to optimize the performance HTML5 app, mobile Web site, JavaScript application running on top of a browser or WebView
The comprehensive guide for optimizing the performance of mobile HTML5 Web ap...Sang Seok Lim
The deck includes a set of techniques and knowledge that can be used when you try to optimize the performance HTML5 app, mobile Web site, JavaScript application running on top of a browser or WebView
Create-React-App으로 SSR을 구현하며 배운 점 (feat. TypeScript)LanarkSeung
프로덕션 환경에서 클라이언트 사이드 렌더링을 고집하기란 힘든 일입니다. 서버를 통해 웹사이트를 제공하면서도 React의 편리함을 누리려면 서버 사이드 렌더링(SSR)을 구현해야 하는데요. Create-React-App을 그대로 유지하면서 SSR을 구현하는 과정을 보여드리고자 합니다. TypeScript로도 가능합니다!
Similar to 캠프앱 개발 사례를 통해 본 하이브리드앱 어디까지 | Devon 2012 (20)
24. Native to Web Call
• Android
- webview.loadUrl("http://camp.daum.net")
- webview.loadUrl("javascript:hello('world')")
• iOS
- [webview loadRequest:]
- [webview stringByEvaluatingJavaScriptFromString:]
25. Web to Native Call
• location.href
- WebViewClient.shouldOverrideUrlLoading()
- webView:shouldStartLoadWithRequest:navigationType:
• window.prompt
- Android에서만 사용
- WebChromeClient.onJsPrompt()
• Android - webview.addJavaScriptInterface()
- Java 객체를 JavaScript 객체로 삽입하여 public method 호출 가능
- iOS, Android 동일 인터페이스로 제공 위해 미사용
26. Internal URL Scheme
• Web to Native 명령 규칙
scheme://namespace/action/param1/...
ex) daumcamp://daum.camp/camp/10
27. location.href
• URL 변경을 연속해서 발생시키면 마지막 하나만 호출
• 해결책
- setTimeout : 호출 시점 보장 못함
- iframe
function callByIframe(scheme) {
var iframe = document.createElement('iframe');
iframe.src = scheme;
document.body.appendChild(iframe);
setTimeout(function() {
document.body.removeChild(iframe);
}, 100);
};
28. Web to Native Callback
• Native 호출 후 응답이 필요한 경우
location.href = 'daumcamp://daum.camp/post/postComplete';
callback JS 함수
44. 주의 사항
• html 파일은 항상 캐쉬
• 업데이트는 리소스 파일이 아닌 Manifest 파일의 변경
• Application cache 제거시 http 응답은 404
- obsolete 이벤트 처리 필요
• Android 2.1은 Network 섹션에서 * 선택자 미지원
- 명시되지 않은 네트워크 요청은 모두 취소
78. JS 코드 줄이기
• Google Closure Compiler
- Minify & Code Merging
• SIMPLE_OPTIMIZATIONS
- 공백, 주석 삭제, Local 변수를 짧은 변수명으로 변경
- 도달하지 않는 코드 제거 / 미사용 변수 제거
79. JS 코드 줄이기
var isDev = false;
function hello(name) {
if (isDev) { console.log('hello called'); }
return 'Hello, ' + name;
}
hello('New user');
80. JS 코드 줄이기
var isDev = false;
function hello(name) {
if (isDev) { console.log('hello called'); }
return 'Hello, ' + name;
}
hello('New user');
SIMPLE_OPTIMIZATION
var isDev=!1;function hello(a)
{isDev&&console.log("hello called");return"Hello, "+a}
hello("New user");
81. JS 코드 줄이기
var isDev = false;
function hello(name) {
if (false) { console.log('hello called'); }
return 'Hello, ' + name;
}
hello('New user');
82. JS 코드 줄이기
var isDev = false;
function hello(name) {
if (false) { console.log('hello called'); }
return 'Hello, ' + name;
}
hello('New user');
SIMPLE_OPTIMIZATION
function hello(a){return"Hello, "+a}hello("New user");
83. JS Pre-Processor
• Idea
- 플랫폼 구분자를 상수로 치환하여 컴파일러가 제거 가능하게 하자
- 플랫폼 별로 별도의 JavaScript 파일 생성
• JavaScript Parser
- Esprima : Source Code > Abstract Syntax Tree
- Escodegen : Abstract Syntax Tree > Source Code
84. Source Code
if (camp.isApp) {
" camp.Util.installExtAnchorHandler();
" if (camp.os === camp.OS.IOS) {
" " camp.UI.navi.init();
" " camp.Util.hideAddressBar();
" }
} else {
" // 메뉴, 네비 적용
" camp.UI.navi.init();
" camp.UI.menu.init();
" // body의 높이를 window.height에 맞춤
" camp.Util.hideAddressBar();
" // daum id 저장
" camp.Env.uid = camp.Util.getUserId();
" // 로그인 인증 쿠키 갱신 타이머 시작
" camp.Function.startLoginCheckTimer();
}
85. Pre-processed Code
if (true) {
아이폰 앱인 경우
camp.Util.installExtAnchorHandler();
if ('ios' === 'ios') {
camp.UI.navi.init();
camp.Util.hideAddressBar();
}
} else {
camp.UI.navi.init();
camp.UI.menu.init();
camp.Util.hideAddressBar();
camp.Env.uid = camp.Util.getUserId();
camp.Function.startLoginCheckTimer();
}
86. Compiled Code
• 전처리기 처리후 컴파일된 코드 (83 byte)
camp.Util.installExtAnchorHandler();camp.UI.navi.init(
);camp.Util.hideAddressBar();
• 원 소스 코드로 컴파일된 코드 (262 byte)
camp.isApp?
(camp.Util.installExtAnchorHandler(),camp.os===camp.OS
.IOS&&(camp.UI.navi.init(),camp.Util.hideAddressBar())
):
(camp.UI.navi.init(),camp.UI.menu.init(),camp.Util.hid
eAddressBar(),camp.Env.uid=camp.Util.getUserId(),camp.
Function.startLoginCheckTimer());
94. Ghost Click
• Touch 이후에 click 발생
• Clickable Element
- Link
- Form Button
- onclick 이벤트 등록 element
95. Ghost Click
• Touch 이후에 click 발생
• Clickable Element
- Link
- Form Button
- onclick 이벤트 등록 element
96. Ghost Click
• event.preventDefault()로 마우스 이벤트 제거
- iOS 만 가능
- Android 는 불가
• 해결책
- Busting Ghost Clicks - touch와 click 동시 발생 제거
- 모든 Clickable element의 click 이벤트 제거하고 touch 사용
- jQuery Mobile 등 라이브러리 사용
100. 지금까지 언급한 내용
• 화면 영역별 구현 방법
• WebView & Web-App Bridge
• Application Cache
• 버전 관리
• WebView Request Intercept
• JS 크기 줄이기
• Touch 이벤트
101. 캠프 하이브리드 앱
• 개발 속도
- 상대적으로 빠르다
- Mobile Web Application의 Front-End 개발 역량에 따름
• 실행 성능
- WebView 성능 한계 존재
- Native 성능 최적화 간과하면 안됨
• 운영
- One Source Multi Use로 인해 테스트 부담 증가
- 앱/웹 버전 관리의 Best Practice 찾기 쉽지 않다
102. 마지막으로
• 하이브리드 앱의 성능은 Web Application의 성능에 좌우
• 하지만, 아무리 튜닝해도 2% 부족한 느낌
• Facebook이 Native로 바꾸는 이 시점에...
103. WebView 이해를 위한 추천 강의
• Android
- Google I/O 2012 : Android WebView
• iOS
- WWDC 2012 : Optimizing Web Content in UIWebViews and
Websites on iOS
- WWDC 2012 : Debugging UIWebViews and Websites on iOS
104. 참고
• Cross-Platform Tools: Build Once and Run Everywhere
http://www.infoq.com/presentations/Cross-Platform-Mobile-Tools
• Sunspider JavaScript Benchmark
http://www.webkit.org/perf/sunspider/sunspider.html
• Creating Fast Buttons for Mobile Web Applications
https://developers.google.com/mobile/articles/fast_buttons
• Substituting local data for remote UIWebView requests
http://www.cocoawithlove.com/2010/09/substituting-local-data-for-remote.html
• Esprima & Escodegen