Outsider's Dev Story: Mobile/Mobile Web 카테고리 글 목록https://blog.outsider.ne.kr/Stay Hungry. Stay Foolish. Don't Be Satisfied.2024-03-15T10:00:26+09:00Textcube 1.10.7 : Tempo primo나는 AMP를 좋아하지 않는다.Outsiderhttps://blog.outsider.ne.kr/12852017-04-22T17:02:17+09:002017-04-22T09:30:00+09:00<p>AMP가 처음 등장했을 때 꽤 관심이 있었고 그동안 모바일 최적화에서 느끼던 어려움을 해결해 줄 것으로 생각했지만, 자세한 내용을 보면 볼수록 내가 생각하던 웹의 방향과는 달라서 지금은 전체적인 추세만 보고 있을 뿐 내가 직접 구현할 것 같지는 않다. 정말 큰 흐름이 된다면 거스를 수는 없겠지만 내가 먼저 구현해서 그 흐름에 참여할 생각은 별로 없다.<br />
<br></p>
<h1>AMP란 무엇인가?</h1>
<p><a href="https://www.ampproject.org/ko">AMP</a>는 Google에서 시작한 프로젝트로 Accelerated Mobile Pages의 약자이다. 즉, 모바일전용 빠른 웹페이지를 의미한다. 요즘은 하나의 웹사이트를 만들고 미디어쿼리로 큰 해상도부터 작은 해상도까지 모두 대응하고 있다. 하나의 문서를 가지고 다양한 클라이언트 환경에 맞추어서 보여주는 이 방향은 맞는다고 생각하지만, 현실에서 이를 제대로 구현하기는 쉽지 않다. 미디어쿼리로 구현한 경우 어쩔 수 없이 성능이나 기능에서 손해를 볼 수밖에 없는데 몇 가지 예를 들면 <a href="http://caniuse.com/#search=srcset">srcset을 아직 현실에서 사용할 수 없는 상황</a>에서 이미지 등을 상황에 맞게 사용하려면 이미지를 CSS에서 미디어쿼리로 지정해야 하는데 이렇게 하면 검색엔진에서는 이미지가 제대로 수집되지 않는다. 그리고 자바스크립트를 상황에 따라 동적으로 로딩하는 수준으로 구현하는 게 아니라면 데스크톱에서만 필요한 스크립트를 모바일에서도 다운받아야 하고 그 반대의 경우가 생기기도 한다. 이는 복잡한 사이트일수록 모바일에서 성능 저하가 생길 수밖에 없게 된다.</p>
<p>이런 문제를 해결하려고 AMP가 나왔다고 생각하는데 모바일에서 빠른 속도를 보장하기 위해 AMP는 몇 가지 제약사항을 가지고 있다.</p>
<ul>
<li>CSS는 모두 인라인으로 지정해야 하며 50KB를 넘을 수 없다.</li>
<li>스크립트는 <code><script async src="https://cdn.ampproject.org/v0.js"></script></code>처럼 AMP에서 제공하는 스크립트 외에는 외부 JavaScript를 허용하지 않는다.</li>
<li>기본 HTML 태그 대신 <code><amp-img></code>, <code><amp-anim></code>, <code><amp-video></code>같은 <a href="https://www.ampproject.org/ko/docs/reference/components">AMP 전용 태그</a>를 사용한다.</li>
<li>모든 리소스는 크기를 지정해서 리소스를 다운로드 한 후에 브라우저가 레이아웃을 다시 그리지 않도록 한다.</li>
</ul>
<p>이 외에도 브라우저 속도에 영향을 줄 수 있는 요소를 차단하는 다양한 제약사항이 있다. 이를 통해서 아주 빠른 모바일 전용 페이지를 제공하고 있다. AMP에 대해서는 검색하면 자세히 설명하는 다양한 글을 볼 수 있다.</p>
<p>웹사이트가 모바일에서 느리다는 것은 모두가 겪는 문제인데 각 서비스에서는 이를 해결하려는 움직임을 보였다. Apple에서는 <a href="https://www.apple.com/news/">News</a>를 공개했고 Facebook은 <a href="https://instantarticles.fb.com/">Instant Article</a>을 공개했다. Apple과 Facebook이 모바일에서 빠른 속도를 보여줄 수 있는 환경을 만들자 여기에 위기를 느낀 구글이 AMP를 만들었다고 생각하고 있다.(이런 배경을 설명하진 않으므로 이건 내 추정일 뿐이다.) News와 Instant Article이 완전히 자사 플랫폼 전용임과 비교하면 AMP는 좀 더 범용적인 포맷을 만들어서 반격을 꾀했다고 생각하고 현재까지는 꽤 괜찮은 분위기를 만들고 있다고 본다.<br />
<br></p>
<h1>하지만 나는 AMP가 잘못된 방향이라고 생각한다.</h1>
<p>이렇게 생각하는 이유가 몇 가지 있는데 AMP는 그동안 웹이 발전해 온 방향과는 다른 방향이라고 느끼기 때문이다. 웹이 계속 발전해도 나는 웹은 그 근간이 문서라는 개념에 무게를 크게 두고 있고 이게 웹의 핵심이라고 생각하기에 이를 거스르는 느낌이 들면 거부감이 드는 편이긴 하다.<br />
<br></p>
<h2>AMP는 모바일 전용 페이지 시대로의 회귀이다.</h2>
<p>스마트폰이 처음 등장했을 때 웹에는 모바일 전용 페이지라는 개념이 있었다. outsider.ne.kr이 웹사이트면 모바일은 m.outsider.ne.kr 같은 식으로 만들었다. 이는 서버에서 User Agent를 확인해서 모바일 브라우저라고 판단되면 모바일 전용 페이지로 리다이렉트 시키는 방식으로 동작했고 이 페이지는 작은 해상도에서 최적화된 레이아웃으로 구성된 웹페이지를 제공했고 하단에는 보통 "PC 버전 보기" 같은 버튼이 있어서 둘 사이를 오갈 수 있도록 구현했다.</p>
<p>이 모바일 전용 페이지는 현재 국내에도 많이 남아있지만 각 모바일 페이지가 상당히 형편없게 만들어진 것과 상관없이 이는 하나의 페이지가 두 가지 주소를 가지고 있다는 문제를 가지고 있었다. 이는 <strong>웹 문서의 퍼머링크라는 개념과 맞지 않았고(요즘은 이런 용어를 잘 안 쓰지만) 결국 2개의 페이지가 존재하는 것과 다름없게 되었다.</strong></p>
<p>그리고 <strong>모바일 페이지가 잘못된 방향이라고 내가 생각하는 이유 중 하나는 모바일과 데스크톱의 경계가 모호해졌기 때문이다.</strong> 데스크톱과 모바일은 얼핏 보면 명확한 구분 같지만 모바일 디바이스가 점점 발전하면서 이 경계는 무너져버렸다. 예를 들어 iPad Pro는 1024x1366의 해상도를 가지고 있다. 그러면 여기서 iPad Pro는 모바일로 구분해야 하는가 데스크톱으로 구분해야 하는가? 이는 쉽지 않은 문제다. 만약 1280을 기준으로 모바일과 데스크톱을 나눈다고 하더라도 1280 해상도가 안되는 노트북도 많이 존재하고 iPad Pro같은 경우 가로로 보면 폭이 1366이 되어 버린다. 같은 장비가 세로로 보냐 가로로 보냐에 따라 모바일이냐 데스크톱이냐를 다르게 구분할 수는 없다. 이런 문제는 모바일 전용 페이지로 해결할 수 있는 문제가 아니라 미디어쿼리로 각 상황에 맞게 모든 픽셀에 대응할 수 있는 게 더 옮은 방향이라고 생각한다.(아직 부족한 점이 있다고 하더라도...)</p>
<p>다시 AMP로 돌아가 보자.</p>
<p><a href="http://www.theverge.com/2017/4/14/15303338/apple-autonomous-vehicle-testing-permit-california">Apple just received a permit to test self-driving cars in California</a>라는 The Verge의 기사를 보자. 이 기사의 주소는 <a href="http://www.theverge.com/2017/4/14/15303338/apple-autonomous-vehicle-testing-permit-california">http://www.theverge.com/2017/4/14/15303338/apple-autonomous-vehicle-testing-permit-california</a>이고 이 페이지는 큰 해상도에서 보면 다음과 같은 디자인으로 되어 있고 당연히 모바일에서도 잘 볼 수 있도록 반응형 디자인으로 만들어져 있다.</p>
<p style="text-align: center;"><img src="//blog.outsider.ne.kr/attach/1/8329990877.jpg" width="750" height="477" alt="The Verge의 뉴스 기사" title="" /></p>
<p>이 페이지의 소스를 보면 다음과 같은 HTML 태그가 존재한다.</p>
<pre class="line-numbers"><code class="language-html"><link rel="amphtml" href="http://www.theverge.com/platform/amp/2017/4/14/15303338/apple-autonomous-vehicle-testing-permit-california">
</code></pre>
<p>이 태그가 이 페이지의 AMP 페이지 주소를 알려주는 역할을 하고 그 주소는 <a href="http://www.theverge.com/platform/amp/2017/4/14/15303338/apple-autonomous-vehicle-testing-permit-california">http://www.theverge.com/platform/amp/2017/4/14/15303338/apple-autonomous-vehicle-testing-permit-california</a>가 되고 이는 큰 해상도에서 접속해 들어가더라도 다음과 같이 모바일 전용 레이아웃으로 보이게 된다.</p>
<p style="text-align: center;"><img src="//blog.outsider.ne.kr/attach/1/8470806628.jpg" width="750" height="644" alt="The Verge의 뉴스 기사의 AMP 페이지" title="" /></p>
<p>나는 이 구조가 앞에서 본 모바일 전용 페이지와 별로 달라 보이지 않는다. 서버에서 자동 리다이렉트를 안 해준다는 것과 서로 간에 가시적인 이동 버튼이 없는 것 말고는 나한테는 똑같아 보인다. 이 AMP 구조가 모바일에 최적화 사이트이므로 좋은 방식이라면 m.도메인으로 시작하는 모바일 전용 페이지도 괜찮은 접근이어야 한다. 물론 AMP가 여러 방향에서 더 좋은 기술적인 요소를 가진 것은 맞지만 큰 흐름의 방향은 거의 비슷하다고 본다.<br />
<br></p>
<h2>AMP는 검색엔진 중심이다.</h2>
<p>위에서 간단히 AMP의 구조를 설명했지만 실제로 사용자들이 이용하는 방식을 살펴보자. AMP가 위에서 보았듯이 <code><link></code> 태그로 표시되어 있고 UI에 버튼이 없으면 사용자는 어떻게 AMP 페이지를 이용하는가? 구글에서 검색하면 AMP를 지원하는 웹페이지 즉, 위처럼 <code><link rel="amphtml" href=""></code>를 제공하는 웹페이지는 다음과 같이 AMP 페이지라고 보여준다.</p>
<p style="text-align: center;"><img src="//blog.outsider.ne.kr/attach/1/3677289634.gif" width="422" height="750" alt="구글 검색에 표시된 AMP 페이지" title="" /></p>
<p>이 페이지를 클릭하면 <code>https://www.google.co.kr/amp/www.theverge.com/platform/amp/2017/4/14/15303338/apple-autonomous-vehicle-testing-permit-california</code>같은 주소로 접속이 된다. 이 주소는 스마트폰 등에서 열면 아래와 같이 열린다.</p>
<p style="text-align: center;"><img src="//blog.outsider.ne.kr/attach/1/1597279244.gif" width="422" height="750" alt="구글에서 보여주는 AMP 페이지" title="" /></p>
<p>좀 더 설명을 하면 다른 환경에서 접속하면 원래의 주소인 <a href="http://www.theverge.com/2017/4/14/15303338/apple-autonomous-vehicle-testing-permit-calif">http://www.theverge.com/2017/4/14/15303338/apple-autonomous-vehicle-testing-permit-calif</a>로 리다이렉트된다. 기술적으로 설명하면 AMP 페이지에는 <code><link rel="canonical" href="http://www.theverge.com/2017/4/14/15303338/apple-autonomous-vehicle-testing-permit-california"></code>처럼 원본 페이지에 대한 주소 정보를 알려주고 있고 기술적으로 말하면 크롤러가 이 모바일 페이지에 오게 되면 canonical 주소를 찾아가므로 실제 크롤링을 원본 문서만 크롤링 된다.</p>
<p>다시 위의 구글에서 접속한 AMP 페이지로 돌아가 보자. 이 페이지는 주소에서 볼 수 있듯이 google의 사이트이다. 구글에서 AMP 전용 사이트를 가지고 있고 뒤의 주소를 이용해서 아이프레임으로 해당 페이지를 AMP로 보여주고 있다. 상단에 <code>www.theverge.com</code>이라고 표시된 헤더 부분은 구글의 사이트이고 그 아래 <code><iframe></code>으로 AMP 페이지를 보여주는 방식을 취하고 있다. 이렇게 하는 경우 해당 문서에 대한 URL(여기서는 the verge의 기사) 대신 구글 주소가 주소창에 표시되므로 구글에서는 이를 조금이라도 완화하기 위해 상단 헤더에서 원본 주소를 복사하고 AMP에 대한 정보를 얻을 수 있는 버튼을 제공하고 있다.</p>
<p>그리고 AMP를 서비스해서 테스트해보진 않았지만 내가 아는 지식 선에서는 이 AMP 페이지를 더 빠르게 제공하려고 <a href="https://developers.google.com/amp/cache/">Google에서 AMP 캐시</a>를 제공하고 있다. AMP 페이지를 접속할 때마다 해당 서버에서 가져오는 것이 아니라 구글이 제공하는 AMP 캐시에 저장해 뒀다가 사용자에게 빠르게 제공하는 것이다. 물론 구글 캐시서버의 성능과 인프라는 신뢰할만하니 사용자는 빠르게 페이지를 볼 수 있지만, 사용자는 해당 서버에 접속하지 않는다.(물론 콘텐츠 내에 광고로 수익을 내거나 로깅을 하려는 방법 등은 존재한다)</p>
<p>나는 이 구조가 아주 이상하게 느껴진다. 내가 알던 모든 웹 기술은 해당 서비스 프로바이더 중심이었고 웹브라우저 기반이었다. 계속 예시로 사용한 The Verge를 계속 얘기하면 사용자가 어떤 페이지를 보고 어떻게 보여줄지는 The Verge 사이트 더 정확히는 The Verge 서버에 HTTP 요청이 왔을 때 The Verge가 결정한다. 이를 동적으로 처리해서 보여주던 정적페이지로 캐시 해서 CDN으로 배포하던 어쨌든 The Verge의 서버가 결정하고 표준에 맞춰서 구현된 웹브라우저가 그것에 맞게 처리해준다. 처음 AMP를 보여줬을 때 나는 당연히 여태 하던 이 개념 내에서 AMP를 이해하려고 했고 수많은 문서를 보았지만 내 사이트에 사용자가 접속했을 때 어떻게 빠른 AMP 페이지를 이용할 수 있게 하는지 이해하지 못했다. 그래서 <a href="http://stackoverflow.com/questions/35502996/how-do-i-support-amp-html-and-desktop-html-simultaneously">Stackoverflow에 질문</a>도 올렸다가 AMP가 동작하는 방식을 이해하게 되었다.</p>
<p>AMP는 기존에 우리가 알고 있던 웹사이트가 동작하는 방식대로 돌아가는 게 아니라 검색엔진 중심으로 동작한다. 내 블로그가 AMP를 제공한다고 하면 사용자가 AMP를 이용하려면 구글 검색을 통해서 들어와야 한다. 내 블로그에 접속해서는 AMP를 이용할 수 없다. AMP가 보급되면서 구글뿐 아니라 다른 검색엔진에도 AMP 지원이 확대되고 있고 더 발전하면 페이스북이나 트위터 같은 대형서비스에서도 AMP를 지원할 수도 있지만 내 웹사이트에서 내가 통제권을 가지고 있지 않다는 것은 여전히 같다. A 사이트가 내 AMP를 이상하게 제공한다면 그냥 이상하게 제공되는 거다.</p>
<p>이건 내가 여태까지 이해하고 공부하던 웹이 나아가던 방향과는 완전히 다르고 극단적으로 말하면 페이스북 내에 페이스북 페이지를 만드는 것과 크게 다를 바가 없다고 느껴진다. 구글은 그나마 이런 방향 내에서도 최대한 웹의 방향을 적게 헤치려고 노력을 하고 있지만 다른 서비스도 똑같이 할 거라는 보장은 전혀 없다. 나는 이게 완전히 잘못된 방향이라고 생각한다.<br />
<br></p>
<h2>AMP는 기술적으로 새로울 것이 전혀 없다.</h2>
<p>물론 기술이란 것은 항상 새롭고 혁신적인 것만 만들어야 하는 것은 아니다. 이미 존재하는 기술을 새롭게 연동하거나 새로운 개념을 부여하는 것도 충분히 혁신적인 일이다. 그렇긴 하지만 앞에서 설명한 방향적인 문제를 포함해서 생각할 때 AMP가 제공하는 기술적인 혁신은 전혀 없다. AMP는 모바일에서 웹페이지를 더 빠르게 보여주겠다는 것이 목표이다. 내가 AMP 제약조건에 맞추어서 간단한 웹페이지를 만들어서 CDN으로 제공하면 AMP보다 느릴까? 물론 구글 캐시 서버나 CDN보다는 느릴 수도 있지만 비싼 CDN을 쓰지 못하더라도 요즘은 저렴한 CloudFront나 CloudFlare 등 저렴한 CDN을 쓸 방법이 많이 있다. AMP에서 다수의 대단한 사람들이 좋은 라이브러리를 빠르게 만들고 잘 모르는 개발자라도 실수하지 않게 좋은 제약사항을 제공한 것은 이해하지만, 오픈소스로 누군가 yet-another-amp.js같은 라이브러리를 만들어서 가져다 쓸 수 있게 한다면 다를 건 하나도 없다.</p>
<p>기존에 모바일에서 웹사이트가 느렸던 이유는 기술적으로 모바일에서 빠른 전용 웹사이트를 만들 수 없었기 때문이 아니다. 현재 웹 기술로 데스크톱 웹을 제공하면서 모바일에도 최적화할 기반 기술이 안 만들어졌기 때문이다. 맨 앞에서 얘기한 대로 이런 방향이면 나로서는 다시 m.도메인 사이트를 모바일 전용으로 만들어서 사용자에게 빠르게 제공하는 게 훨씬 더 낫다고 본다.</p>
<p>그리고 대부분 웹사이트가 점점 느려지는 이유는 서비스마다 다양한 요구사항이 있기 때문이다. 모든 사이트가 뉴스 사이트라서 간단한 텍스트만 제공하면 끝나는 것이 아니다. 콘텐츠 사이트라고 하더라도 사용자 온보딩을 위해서 여러 요소를 넣어야 하고 광고도 그냥 보여주는 게 아니라 다양한 실험을 해야 하고 애플리케이션 적이 고급 요소도 제공해야 한다. 실제로 이런 요구사항을 맞추기 위해서 AMP에도 새로운 컴포넌트가 계속 추가되고 있다. 이게 과연 모두의 요구사항을 맞출정도로 다양한 컴포넌트가 갖춰진다면 여전히 지금처럼 빠를까? 나는 AMP도 결국 요구사항을 맞추기 위해서 점점 느려지거나 요구사항을 맞춰주지 못할 것이라고 본다. 물론 AMP는 뉴스사이트 같은 문서에 기반을 둔 웹사이트를 타게팅하고 있는 것은 확실하지만, 시간이 지나면서 이는 점점 흐려질 것으로 생각한다.<br />
<br></p>
<h1>결론</h1>
<p>구글은 AMP를 지원하면 구글 검색결과에 더 높은 중요도를 배정하면서 웹사이트 운영자에게 AMP는 달콤한 유혹이 되었다. AMP라는 요소만으로 검색 결과에 상위로 올라갈 정도로 검색 알고리즘이 간단하지는 않지만, 조금이라도 상위에 올리고 싶은 웹사이트 운영자들에게 AMP 지원은 할 수 있다면 안 할 이유가 없는 부분이다. 물론 난 이 검색 결과에 적용하는 방식은 기술의 제대로 된 검증 없이 구글이 점유율을 이용해서 기술을 강요했다는 거부감이 훨씬 더 크다.</p>
<p>난 <a href="https://developers.google.com/web/progressive-web-apps/">PWA</a>이 웹의 방향성에서는 훨씬 좋은 방향이라고 보고 있는 편인데 자연스럽게 AMP가 PWA와 같이 어울리면서(둘 다 구글이 주도하다 보니) 섞여 들어오는 게 더 싫기는 하다. 물론 내가 이렇게 생각한다고 AMP가 안된다는 것은 아니다. 보통은 오히려 내가 비관적으로 보는 기술이나 회사는 더 잘되는 경향이 있지만....</p>
<p><strong><a href="https://blog.outsider.ne.kr/1285?commentInput=true#entry1285WriteComment">댓글 쓰기</a></strong></p>iPhone 5에서 웹앱을 전체화면으로 실행하기Outsiderhttps://blog.outsider.ne.kr/9762013-09-05T01:47:10+09:002013-09-05T01:47:10+09:00<h1>viewport 메타 태그</h1>
<p>모바일용 웹페이지를 만들때 일반적으로 <code>viewport</code> 메타태그를 사용해서 페이지가 모바일에서도 잘 보이도록 합니다.</p>
<p style="text-align: center;"><img src="//blog.outsider.ne.kr/attach/1/9527280283.gif" width="400" height="710" alt="모바일 사파리에서 viewport를 지정하지 않아서 작게 나오는 화면" title="" /></p>
<p>위처럼 <code>viewport</code> 메타태그를 사용하지 않으면 원래의 사이즈로 나와서 모바일에서 보기에 좋지 않은 페이지에 다음과 같이 <code>viewport</code>를 지정하면 아래 화면과 같이 모바일 사이즈에 맞춰서 나온다.</p>
<pre class="line-numbers"><code class="language-html"><meta name="viewport" content="width=device-width,
initial-scale=1.0,
maximum-scale=1.0,
user-scalable=no">
</code></pre>
<p style="text-align: center;"><img src="//blog.outsider.ne.kr/attach/1/7868208111.gif" width="400" height="710" alt="모바일 사파리에서 viewport지정으로 모바일에 맞게 나오는 화면" title="" /></p>
<p><a href="https://developer.apple.com/library/safari/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html">Safari 개발자 문서</a>를 보면 <code>viewport</code>에서 사용할 수 있는 프로퍼티에 대한 설명이 나와 있다. 이 프로퍼티는 모두 iOS 1.0부터 지원한다.</p>
<ul>
<li><strong>width</strong>: 뷰포트의 넓이이고 픽셀값이다. 기본값은 980이고 200부터 10,000까지 지정할 수 있다. 기기의 넓이를 의미하는 상수인 <code>device-width</code>나 높이를 의미하는 <code>device-height</code>를 사용할 수도 있다.</li>
<li><strong>height</strong>: 뷰포트의 높이이고 픽셀값이다. 기본값은 <code>width</code> 프로퍼티에 기반해서 화면 비율에 따라 계산한다. 223부터 10,000 픽셀까지 지정할 수 있으면 <code>device-width</code>나 <code>device-height</code> 상수를 사용할 수 있다.</li>
<li><strong>initial-scale</strong>: 뷰포트의 기본 비율이다. 기본값은 보이는 영역에 화면을 맞춰주고 <code>minimum-scale</code>과 <code>maximum-scale</code> 프로퍼티로 범위가 결정된다.</li>
<li><strong>minimum-scale</strong>: 뷰포트의 최소 비율을 지정한다. 기본값은 0.25이고 0부터 10.0까지 지정할 수 있다.</li>
<li><strong>maximum-scale</strong>: 뷰포트의 최대 비율을 지정한다. 기본값은 5.0이고 0부터 10.0까지 지정할 수 있다.</li>
<li><strong>user-scalable</strong>: 사용자가 뷰포트를 축소/확대 할 수 있는지를 지정한다. <code>yes</code>로 지정하면 축소/확대할 수 있고 <code>no</code>로 지정하면 축소/확대할 수 없다. 기본값은 <code>yes</code>다. 이 값은 <code>no</code>로 지정하면 인풋필드에 글자를 입력할 때 웹페이지가 스크롤되는 것을 막는다.</li>
</ul>
<p><br></p>
<h1>아이폰 5에서 웹앱으로 전체화면 사용하기</h1>
<p>모바일 사파리에서 사용할 때는 상관없지만 iOS에는 웹페이지를 앱처럼 홈화면에 추가하는 기능이 있고 <a href="http://rainygirl.com/">rainygirl님</a>이 올려주신 <a href="http://blog.rainygirl.com/?p=612">iOS Web-App 용 몇 가지 스킬</a>에 나와있는 <code>apple-mobile-web-app-capable</code> 메타태그를 사용하면 모바일 사파리의 주소창같은 것 없이 풀 스크린으로 띄울 수 있다. 이 부분은 웹을 페이지가 아닌 웹앱형태로 구성했을 때 유용한 기능인데 iPhone 5에서 위의 <code>viewport</code> 메타태그를 사용해서 홈화면에서 앱처럼 실행하면 다음과 같이 나온다.</p>
<p style="text-align: center;"><img src="//blog.outsider.ne.kr/attach/1/5799396497.gif" width="400" height="710" alt="웹앱으로 실행했을때 위아래가 잘려서 나오는 화면" title="" /></p>
<p>(빈공간을 알 수 있도록 테두리를 추가했다.)</p>
<p>아이폰은 세로가 더 길기 때문에 전체화면으로 나타나지 않고 위아래에 공백이 나타난다. 아이폰4는 640x960인데 반해서 아이폰 5는 640x1136인데 여기서 상단의 상태바가 40픽셀이므로 실제 화면에 사용하는 높이는 640x1096이 된다. <code>viewport</code> 메타태그의 height는 <code>width</code>에 기반해서 적용되는데 <code>initial-scale</code>를 1로 지정한 경우에 <code>width</code>가 320픽셀이 된다. 그래서 비율에 따라 <code>height</code>가 480픽셀이 되고 실제 기기의 픽셀로 보면 960픽셀이 되므로 176픽셀(1136-960)만큼 비게 되는 것이다. width가 320이니까 height는 568픽셀이 되어야 전체화면이 다 채워지는데 이 문제를 해결하는 방법은 아주 쉽다.(<a href="https://gist.github.com/burin/3840737">Burin Asavesna가 올려준 코드</a>를 참고했다.)</p>
<pre class="line-numbers"><code class="language-html"><meta name="viewport" content="width=device-width,
initial-scale=1.0,
maximum-scale=1.0,
user-scalable=no">
<meta name="viewport" content="initial-scale=1.0,
maximum-scale=1.0,
user-scalable=no"
media="(device-height: 568px)">
</code></pre>
<p>위와 같이 메타태그를 2개 지정하고 미디어쿼리를 사용해서 기기의 높이가 568픽셀인 경우에는 <code>viewport</code>에서 width를 지정하지 않으면 다음과 같이 전체화면으로 웹앱이 나타나게 된다.</p>
<p style="text-align: center;"><img src="//blog.outsider.ne.kr/attach/1/3356408135.gif" width="400" height="710" alt="아이폰 5에서 웹앱이 전체화면으로 나온 화면" title="" /></p>
<p><strong><a href="https://blog.outsider.ne.kr/976?commentInput=true#entry976WriteComment">댓글 쓰기</a></strong></p>모바일 사파리에서 스크롤 구현과 관련된 문제Outsiderhttps://blog.outsider.ne.kr/9712013-08-18T16:52:56+09:002013-08-18T01:51:57+09:00<p>HTML5가 나오고 나서 웹에도 엄청난 변화가 일어났지만 초반에 열심히 쫓아가다가 너무 많이 쏟아져 나와서 한참동안은 너무 깊게 파지는 않고 있었고 모바일은 딱히 만질일도 없었기 때문에 기본적인 수준 이상으로는 크게 관심을 두지 않았었다. 지금은 프론트앤드쪽 작업을 하고 있으므로 모바일도 좀 알아야 되겠다 싶어서 모바일웹쪽을 만져보고 있는 중이다. 개인 프로젝트에서 모바일 웹을 작업하고 있는데 스크롤을 구현하는게 상당히 골치가 아팠다. 스크롤은 어디나 들어가는 아주 일반적인 기능이라고 생각하고 있었음에도 구현하는데 여러가지 어려움이 상당히 많았다.</p>
<p>모바일에서 스크롤 관련 라이브러리로 가장 유명한 것은 <a href="http://cubiq.org/iscroll-4">iScroll</a>이다. 현재는 4버전이 최신 버전이고 <a href="http://cubiq.org/iscroll-5-ready-for-beta-test">iScroll 5가 베타상태</a>에 있다. 처음에는 iScroll로 작업을 했는데 처음 사용해봐서 제대로 구현못한 건지 모르겠지만 아주 깔끔하게 안되는 부분이 좀 있었고 안드로이드 기기로 넘어가니 퍼포먼스가 너무 안나왔다. 그래서 OS 탑재된 네이티브 스크롤을 직접 이용하기로 하고 iScroll을 제거하고 직접 구현하고 있는데 그 가운데 스크롤에 관련된 내용을 좀 정리해야 좀더 다양한 기능 구현 및 최적화를 할 수 있을것 같아서 정리를 한다.(이미 다 아는 내용인데 나만 이제 확인한 것 같긴 하지만)</p>
<p>일단 내가 가진게 iOS라서 iOS에 맞추어서 작업을 하고 있다. 안드로이드에서도 동작하리라고 생각하지만 많은 테스트를 해보진 않았다. 예제는 <a href="http://angularjs.org/">Angular.js</a>를 사용했는데 이벤트나 좌표를 추적해서 보여주기 위한 것이므로 스크롤과는 상관이 없다. 그리고 스크롤을 설명하는 예시일 뿐이므로 퍼포먼스가 많이 고려되어 있지는 않다.(누가 잘아시는 분이 설명을 해주시면 감사.)<br />
<br></p>
<h1>스크롤</h1>
<p>스크롤 영역으로 지정하는 것은 딱히 모바일 웹에 특화된 것은 아니다. 스크롤을 사용할 곳의 크기를 지정하고 CSS 속성에서 <code>overflow: auto;</code>로 지정하면 된다. 이러면 해당 영역보다 작을때는 상관없지만 해당 영역보다 내용이 많아지면 자동으로 스크롤이 동작하게 된다. 가로나 세로만 하고 싶다면 <code>overflow-x: auto;</code>, <code>overflow-y: auto;</code> 같은 식으로 작성하면 된다. 다음 예제를 보자.(스크롤은 스샷을 찍어서 설명하기가 어렵고 또 동영상을 찍기고 쉽지 않아서 실행해 볼 수 있는 예제를 만들었다. 이벤트가 다르므로 데스크탑에서는 제대로 동작하지 않을 수 있으므로 모바일장비에서 테스트해봐야 한다.)</p>
<iframe width="100%" height="300" src="http://jsfiddle.net/outsider/hBc7g/embedded/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<p><a href="http://jsfiddle.net/outsider/hBc7g/show/">실행가능한 예제 링크</a></p>
<p>각 동작을 한눈에 볼 수 있도록 예제를 구성했다.(폭이 좁아서 블로그내에서는 예제를 실행해보기 어려우므로 모바일에서 띄워보는게 확인하기 좋다.) 상단에는 좌표관련 정보를 표시하고 좌측에는 예제의 스크롤 영역이 있다. 우측에는 스크롤 동작과 관련한 이벤트가 출력되도록 했다. 모바일에서는 마우스가 없으므로 터치이벤트를 통해서 스크롤이 동작하게 되는데 터치할 때 <code>touchstart</code>이벤트가 발생하고 움직이면 <code>touchmove</code>가 발생하고 손가락을 뗄 때 <code>touch</code>가 발생한다. 움직이지 않고 바로 떼면 <code>touchmove</code>가 발생하지 않고 바로 <code>touchend</code>가 발생한다. 여기서는 스크롤 영역에 대한 테스트이므로 <code>touchmove</code>를 통해서 스크롤이 될 때 <code>scroll</code>이벤트도 함께 발생한다.</p>
<p>좌표와 관련해서는 스크롤 영역에는 <code>scrollTop</code>이라는 속성이 존재한다. 가장 최상위에 있을 때는 <code>scrollTop</code>이 0이고 스크롤을 내릴 수록 값이 커지게 된다. 각 이벤트에 대한 좌표를 알 수 있도록 <code>touchstart</code>, <code>touchmove</code>, touchend`의 좌표를 따로 표시하도록 했다. 예제소스는 jsFiddle에서 볼 수 있으므로 따로 전체소스를 담지는 않겠다.<br />
<br></p>
<h1>모멘텀(momentum) 스크롤</h1>
<p>앞에서 본게 기본 스크롤 영역이지만 이 스크롤을 실제로는 사용할 수 없다. 모바일에서 스크롤을 할 경우에는 가속도가 먹어서 손가락을 튕기면 스크롤이 빠르게 이동하는 스크롤을 사용하고 사용자에게도 이게 당연한 동작이다. 이를 모멘텀 스크롤이라고 부르는데 앞에서 본 예제는 손가락을 떼는 순간 스크롤이 멈추기 때문에 사용하기가 아주 불편하다.</p>
<p>iOS 즉 모바일 사파리(웹뷰 포함)에서 모멘텀 스크롤을 사용하려면 스크롤 영역에 <code>-webkit-overflow-scrolling: touch;</code> CSS 속성을 지정한다. 이 스타일만 지정하면 스크롤이 자연스러운 모멘텀 스크롤로 동작하게 된다.</p>
<iframe width="100%" height="300" src="http://jsfiddle.net/outsider/hBc7g/1/embedded/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<p><a href="http://jsfiddle.net/outsider/1/hBc7g/show/">실행가능한 예제 링크</a></p>
<p>이제 스크롤이 아주 자연스러워 진 것을 볼 수 있다. 하지만 모멘텀 스크롤에는 몇가지 문제가 좀 있다.(이는 iOS의 버그수준이라고 생각하는데 어쨌든 문제가 있다.)</p>
<p><strong>모멘텀 스크롤 중에는 <code>scrollTop</code>이 업데이트되지 않는다.</strong> 모멘텀 스크롤을 손가락으로 스크롤 영역을 튕겨서 동작하므로 즉 <code>touchend</code>이벤트가 발생한 이후에도 스크롤이 계속 이동하게 되는데 <code>tocuchend</code>이벤트까지는 <code>scrollTop</code>이 갱신되지지만 <code>touchend</code>이벤트가 발생한 이후에 모멘텀 스크롤가 동작하는 동안에는 <code>scrollTop</code>이 변하지 않고 모멘텀 스크롤이 시간이 지나서 자동으로 멈춘 순간 <code>scroll</code>이벤트가 발생하면서 <code>scrollTop</code>이 갱신된다. 그래서 현재 스크롤이 어느 위치에 있는지 찾을 수가 없고 심지어 모멘텀 스크롤이 동작하고 있다는 것 조차 알 수가 없다.(나중에 <code>scroll</code>이벤트가 발생하므로 모멘텀 스크롤이 동작했었구나 정도만 알 수 있다.)<br />
<br></p>
<h1>모멘텀 스크롤에서 선택영역 사용하기</h1>
<p>모멘텀 스크롤에서 선택영역을 함께 사용하지 않는다면 큰 문제는 없다. 아주 자연스럽게 스크롤을 할 수 있지만 나는 다음과 같이 하고 싶었고 어플리케이션에 따라 다르겠지만 당연한 동작이라고 생각했다.</p>
<ul>
<li>스크롤을 자연스럽게 사용할 수 있어야 한다.(이건 당연히...)</li>
<li>리스트를 터치하면 선택했음을 알 수 있도록 색상을 변환한다.</li>
<li>터치한 상태에서 스크롤을 하기 시작하면 선택하고자 한 것이 아니므로 선택은 취소되고 스크롤이 동작한다.</li>
<li>스크롤도중에 클릭한 것은 선택하기 위한 것이 아니라 스크롤을 멈추고자 하는 것이므로 선택이 되지 않는다.</li>
<li>리스트를 선택한 후에 손가락을 떼면 선택으로 동작한다.</li>
</ul>
<p><strong><code>scrollTop</code>이 업데이트되지 않으므로 모멘텀 스크롤중의 터치도 잘못된 좌표가 인식된다.</strong> 말로 설명하려니 어려운데 스크롤 영역에 <code>li</code>같은 태그에 클릭 효과가 나도록 <code>:active</code>로 스타일을 주었을 경우 모멘텀 스크롤 중에(멈춘뒤 말고) 클릭을 해서 멈춘다면 현재 클릭한 부분이 선택영역으로 잡히지 않고 모멘텀 스크롤을 시작하기 전에 해당 위치에 있던 엘리먼트가 선택된 것으로 표시가 된다. <code>:active</code> 스타일이 다른 위치에 가서 먹고 실제 이벤트도 여기서 발생한다. 이는 다음 예제에서 확인해 볼 수 있다.</p>
<iframe width="100%" height="300" src="http://jsfiddle.net/outsider/hBc7g/2/embedded/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<p><a href="http://jsfiddle.net/outsider/2/hBc7g/show/">실행가능한 예제 링크</a></p>
<p>그래서 맨 앞에 얘기한 조건대로 클릭할 경우 리스트의 스타일을 변경해 주기 위해서 <code>:active</code>상태를 쓸 수 없다고 생각했다. 내가 생각한 방법은 선택에 대한 CSS 클래스를 주어서 터치이벤트에 따라 조건에 따라 클래스를 넣었다 빼었다 하는 것이다. 이렇게 하려면 지저분한 플래그가 꽤 필요하고 그래도 해결이 되지 않아서 스크롤에 대한 가속도를 측정하는 방법을 시도했다. 마지막 4개의 이벤트의 시간간격을 측정해서 모멘텀 스크롤처럼 손가락을 튕긴 것인지 아니면 그냥 스크롤을 하다가 손가락을 뗀 것인지를 판단하려고 한 것인데 일단 내 개인 프로젝트에서는 잘 동작하고 있다. 완벽하진 않지만 한 95%의 경우에서는 제대로 동작하는 것으로 보인다. 사실 이 부분도 예제로 만들려고 했지만 이건 잘 안됐다. 내가 만든것에서는 여러가지 UI가 섞여있어서인지 가속도 측정이 예제로 다시 구성하니 전혀 다르게 나와서 구성할 수가 없었다.(어째서!!!) 일단 내가 시도한 방법이 범용적으로는 쓸 수 없다고 생각해서 좀더 고민을 해봐야겠다. 정말 이 버그가 iOS 7에서는 해결이 되었으면 좋겠다. ㅠ</p>
<p><strong><a href="https://blog.outsider.ne.kr/971?commentInput=true#entry971WriteComment">댓글 쓰기</a></strong></p>Mobile Safari에서 아이폰 회전 이벤트 다루기Outsiderhttps://blog.outsider.ne.kr/4652010-05-04T03:40:44+09:002010-05-04T03:40:44+09:00iPhone뿐만아니라 최근의 스마트폰들에서는 모두 중력계를 가지고 있어서 세로로 보기와 가로로 볼때 자동으로 화면이 전환이 됩니다. 사용자 입장에선 무척 편리하죠. 하지만 세로/가로 모드에서 해상도가 달라지기 때문에 보는 모드에 따라서 처리를 해주어야 할 필요가 있습니다. 왠지 있을것 같기는 했지만 하드웨어에서 발생시키는 이벤트라 좀 긴가민가했는데 찾아보니 있더군요.<br><br>보통은 Rotate라고 부르는 아이폰의 기기를 회전시키는 것은 Orientaion이라고 부르는데 Mobile Safari에서는 <a href="http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html#//apple_ref/doc/uid/TP40006511-SW16" target="_blank">Oeientaion에 대한 이벤트를 지원</a>하고 있었고<span style="color: rgb(204, 153, 0);"> iPhone OS 1.1.1이후부터 orientationchange라는 이벤트명으로 지원하고 있었습니다.</span><br><br><pre class="line-numbers"><code class="language-javascript">
function updateOrientation()
{
switch(window.orientation)
{
case 0:
alert("0");
break;
case -90:
alert("-90");
break;
case 90:
alert("90");
break;
case 180:
alert("180");
break;
}
}
window.onload = function() {
document.body.onorientationchange = updateOrientation;
}
</code></pre><br>위와같이 처리하면 orientation에 대한 이벤트를 처리할 수 있습니다. <a href="http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html#//apple_ref/doc/uid/TP40006511-SW16" target="_blank">Safari의 레퍼런스문서</a>에서는 <body>태그에 인라인으로 이벤트를 정의했지만 요즘은 보통 이벤트처리를 HTML에서 분리해서 하기 때문에 위와같이 처리했습니다. onload될때 body의 onorientationchange이벤트에 updateOrientation라는 함수를 리스너로 지정해 준것입니다. 이렇게 하면 <span style="color: rgb(204, 153, 0);">orientaion의 변화가 있을때마다 함수가 호출되고 window.orientation의 값을 통해 현재 어느정도 회전된 상태인지를 확인할 수 있습니다.</span> onorientationchange는 window에 등록해도 되지만 여기서는 body에 걸었습니다.<br><br>레퍼런스 문서에도 세로로 뒤집은 180에 대한 값이 나타나있는데 이상하게 저의 iPod Touch에서는 180에 대한 이벤트는 발생하지 않았습니다.(아이폰에서는 어떤지 잘 모르겠네요.) 실제로도 세로로 뒤집는다고 모바일 사파리가 뒤집어 지지는 않죠.<br> <br><br><br>추가로 테스트를 해보니<span style="color: rgb(204, 153, 0);"> 넥서스원의 Android 2.1에서는 onorientationchange이벤트가 지원되지 않고 있었습니다. 안드로이드에서는 별수 없이 resize를 사용해야 합니다.</span><br><br><pre class="line-numbers"><code class="language-javascript">
if ("onorientationchange" in window) {
document.body.onorientationchange = updateOrientation;
} else {
window.onresize = updateOrientation;
}
</code></pre><br><span style="color: rgb(204, 153, 0);">onorientationchange를 검사해서 있으면 사용하고 아니면 resize이벤트로 사용하면 됩니다. 양쪽다 resize로 사용해도 가능하긴 하지만 지원되는 이벤트가 있으면 사용하는게 맞을듯합니다.</span> 그리고 사실 안드로이드에서는 resize에 걸었기 때문에 회전시키지 않아도 중간중간 위의 이벤트가 실행되기는 합니다 ㅡㅡ;;<br><p><strong><a href="https://blog.outsider.ne.kr/465?commentInput=true#entry465WriteComment">댓글 쓰기</a></strong></p>Desktop용 Opera Mobile Emulator 발표Outsiderhttps://blog.outsider.ne.kr/4592010-04-23T18:33:52+09:002010-04-23T02:53:02+09:00약 한달정도 전에 Windows Mobile과 Symbian을 위한 <a href="http://www.opera.com/mobile/" target="_blank">Opera Mobile 10</a>이 출시된 이후 Opera Labs에서 오페라 모바일에서의 개발테스트를 쉽게 할 수 있도록 테스크탑용 <a href="http://www.opera.com/developer/tools/" target="_blank">Opera Mobile Emulator</a>를 릴리즈했습니다. 이제 오페라 모바일용 웹사이트 개발을 위해서 기기를 굳이 구입하지 않아도 되고 데스크탑에서 쉽게 테스트를 할 수 있습니다. <br><br><div class="imageblock triple" style="text-align: center"><table cellspacing="5" cellpadding="0" border="0" style="margin: 0 auto;"><tr><td><img src="//blog.outsider.ne.kr/attach/1/1013386677.gif" alt="사용자 삽입 이미지" height="313" width="183" /></td><td><img src="//blog.outsider.ne.kr/attach/1/1148093777.gif" alt="사용자 삽입 이미지" height="313" width="183" /></td><td><img src="//blog.outsider.ne.kr/attach/1/1354723106.gif" alt="사용자 삽입 이미지" height="313" width="183" /></td></tr></table></div><br>에뮬레이터는 Windows, Mac, Linux용을 모두 제공하고 있습니다. Windows에서는 무척 잘 동작하고 있습니다. 아직 오페라 모바일을 실제로 사용해 보지는 못했지만 위 화면처럼 데스크탑 상에서 모바일 브라우저로 아주 잘 돌아갑니다. 마우스커서를 손가란처럼 드래그해서 이동해서 볼 수 있습니다. 마우스휠이나 멀티터치같은 것은 안됩니다. ㅎ<br><br><div class="imageblock triple" style="text-align: center"><table cellspacing="5" cellpadding="0" border="0" style="margin: 0 auto;"><tr><td><img src="//blog.outsider.ne.kr/attach/1/1238483023.gif" alt="사용자 삽입 이미지" height="313" width="183" /></td><td><img src="//blog.outsider.ne.kr/attach/1/1039996560.gif" alt="사용자 삽입 이미지" height="313" width="183" /></td><td><img src="//blog.outsider.ne.kr/attach/1/1333769748.gif" alt="사용자 삽입 이미지" height="313" width="183" /></td></tr></table></div><br>설정화면도 동일하게 나오고 스마트폰에서 처럼 더블클릭을 하면 해당 부분이 확대되는 기능도 동일합니다.(테스트 해보니 이 기능은 글자가 잘 안보일 정도로 화면이 클 경우에만 동작하는것 같습니다. 안동작하는 페이지도 있더군요.) 글자를 입력하는 부분에 커서를 두면 자동으로 키보드도 나타나고(당연히 한글키보드는 없습니다.) 화면상의 가상키보드말고 실제 키보드로도 타이핑 가능합니다.<br><br><div class="imageblock dual" style="text-align: center;"><table cellspacing="5" cellpadding="0" border="0" style="margin: 0 auto;"><tr><td><img src="//blog.outsider.ne.kr/attach/1/1399139161.gif" alt="사용자 삽입 이미지" height="470" width="275" /></td><td><img src="//blog.outsider.ne.kr/attach/1/1138131303.gif" alt="사용자 삽입 이미지" height="470" width="275" /></td></tr></table></div><br>텍스트부분에 마우스클릭을 누르고 있으면 위의 왼쪽 화면처럼 메뉴가 나오고 Select Text를 클릭하면 영역을 마우스커서로 선택할 수 있게 되고 선택하면 추가적인 메뉴가 나타납니다. 잠깐 써보니 스마트폰상에서 사용하는 것과 거의 동일한 UX로 사용할 수 있었습니다. <br><br><div class="imageblock center" style="text-align: center; clear: both;"><img src="//blog.outsider.ne.kr/attach/1/1124136159.gif" alt="사용자 삽입 이미지" height="414" width="422" /></div><br>설치한 메뉴를 보면 여러가지 해상도의 가로/세로모드로의 에뮬레이터를 제공하고 있기 때문에 원하는대로 빠르게 띄워서 테스트를 해 볼 수 있습니다. 물론 이미 띄워놓은 에뮬레이터도 창의 크기가 변경이 가능하고 크기를 변경할 경우 창상단에 현재의 사이즈가 표시되기 때문에 쉽게 원하는 해상도의 웹사이트를 오페라 모바일에서 테스트해 볼 수 있습니다.<br><br><div class="imageblock center" style="text-align: center; clear: both;"><img src="//blog.outsider.ne.kr/attach/1/1403283536.gif" alt="사용자 삽입 이미지" height="450" width="550" /></div><br>이렇게 여러개를 동시에 띄우는 것도 가능합니다. <br><br>그리고 이것은 오페라 모바일입니다. 최근에 아이폰에 런칭되면서 주목을 받았던 오페라 미니와는 생긴건 동일하지만 약간 다릅니다. 제가 아는 범위에서는 <span style="color: rgb(204, 153, 0);">오페라 모바일은 흔히 얘기하는 모바일 사파리같은 웹브라우저이고 데스크탑의 웹브라우저랑 동일하지만 모바일에서 동작하는 웹브라우저입니다. 하지만 오페라 미니는 웹페이지를 웹브라우저가 직접 접속해서 렌더링하는 것이 아닌 중간에 중앙서버가 존재해서 중앙서버에서 페이지를 받아서 이미지등을 모바일에 맞게 경량화 시키고 렌더링도 어느정도는 해준뒤에 오페라 미니에 내려주면 간단한 처리만 수행하는 것입니다.</span> 때문에 오페라 미니가 빠르다는 얘기가 있는 것입니다.(국내에서는 프록시서버가 존재하지 않아서인지 오히려 더 느린것 같은 느껴지기도 합니다.) 때문에 오페라 미니에서는 레이아웃이 깨지는 경우를 많이 보게 되는데 오페라 모바일에서는 그런 현상이 (아직 많이 테스트는 못해봤지만) 보이지 않는것 같습니다.<br><br><br>User-Agent는 아래와 같이 찍히는군요.<br><br><blockquote style="color: rgb(255, 118, 53);">Opera/9.80 (Windows NT 5.1; Opera Mobi/49; U; en) Presto/2.4.18 Version/10.00</blockquote><br><br>꼭 오페라 모바일용이 아니더라도 모바일 웹을 개발하는데 해상도등을 테스트해 볼 수 있다는 면에서 큰 도움이 될듯 합니다. 여기에
모바일 사파리 에뮤레이터만 있으면 금상첨화겠네요. ㅎ<br><p><strong><a href="https://blog.outsider.ne.kr/459?commentInput=true#entry459WriteComment">댓글 쓰기</a></strong></p>