컨텐츠로 건너뛰기

Inspector to Debug Mobile Browser

2013/08/16

모바일 웹페이지를 개발하거나 테스트를 하기위해서 꼭 필요한 툴에 대해 정리해볼까 합니다.

Safari 6.0 이상버전

=> 상세 설명 또는 그림 참고 (http://webdesign.tutsplus.com/tutorials/workflow-tutorials/quick-tip-using-web-inspector-to-debug-mobile-safari/)

Safari 나 Chrome 이나 webkit engine 에서 구동되기 때문에 거의 비슷하다

Mobile Device에서 렌더링된 페이지도 거의 동일하다. => windows 크롬에서 해도 큰 차이는 없다는 이야기임.

하지만, 분명 모바일과 데스크 탑은 틀린 부분있다.

Apple은 iOS 6와 Safari 6를 발표하면서 debugging tool을 발표했다.

하지만, Mac 상에서 remote web inspector 만 디버깅이 가능하며 Windows에서는 사용 불가하다.

사용 방법

  1. 최신버젼 확인

    1. Safari 6
    2. Mac OSX Lion (10.7.4) or higher
    3. Xcode 4.5 with iOS 6 SDK or higher
  2. Developer Tools 실행

    1. Virtual Device
      1. Settings > Safari > Advenced > turn on “Web Inspector”
    2. Desktop Safari
      1. Safari > Preferences > Advanced > check “Show develop menu in menu bar”
  3. Inspect Your Website

    1. Desktop Safari
      1. Develop > iPhone Simulator > 페이지 선택
  4. 할 수 있는 것들

    1. Make live changes to HTML and CSS.
    2. See how your website/application is performing including seeing details for JavaScript events and network requests.
    3. Debug JavaScript using breakpoints and other tools.
    4. View warnings and errors.
    5. Access the console.
    6. Search the DOM.
    7. Access and view site storage.

Weinre

http://people.apache.org/~pmuellr/weinre/docs/latest/

http://elegantcoder.com/weinre <- 한글 사이트

 

요약 :

내장 디버깅 툴이 없는 모바일 브라우저를 위한 원격 디버깅 툴.

Webkit 계열의 개발자 도구인 Web Inspector 를 사용할 수 있도록 해준다.

세가지 파트로 나뉜다.

  • 서버 : java기반 (Jetty)의 http 서버 기존에는 java 였으나 node.js 로 바뀐듯??
  • 대상 : 디버깅할 모바일 디바이스 브라우저
  • 클라이언트 : Safari, Chrome 등에 내장된 Web Inspector와 비슷한 외관의 클라이언트 페이지

 

사용방법

  1. 설치(http://people.apache.org/~pmuellr/weinre/docs/latest/Installing.html)
    1. npm 설치
      1. 최신 node.js 설치하면됨.(http://nodejs.org/download/)
    2. weinre 설치
      > npm -g install weinre
  2. 서버 실행(http://people.apache.org/~pmuellr/weinre/docs/latest/Running.html)
    ipconfig  로 자신의 ip 확인후

    > weinre --httpPort 8081 --boundHoost 192.168.xxx.xxx(자신의 ip)

    ※ boundHost 의 기본 값은 localhost이나 다른 기기(Android Simulator 등)에서 접속할 수 없다

    ※ 다른 상세 옵션은 : http://elegantcoder.com/weinre

  3. 클라이언트 실행
    1. 데스크탑에서 http://192.168.xxx.xxx:8081/client/ 접속
    2. 시뮬레이터에서 http://192.168.xxx.xxx:8081/demo/weinre-demo.html 접속

 

weinre_run

 

모바일 웹페이지 테스트 자동화

2013/07/15
tags: ,

지난 6월 부터 모바일 웹페이지에 대한 테스트 자동화 프로젝트를 시작했습니다.

 

여러가지 정리해 둔것이 있지만 옮기기에는 시간이 없고 게을러서;;;^^;

우선 맛배기로 몇가지 링크를 공유해 볼까 합니다.

 

Webdriver : http://www.w3.org/TR/2013/WD-webdriver-20130117/

Selenium : https://code.google.com/p/selenium/

Getting Started With Android Driver  : https://code.google.com/p/selenium/wiki/AndroidDriver

IPhoneDriver : https://code.google.com/p/selenium/wiki/IPhoneDriver

How To

http://android-developers.blogspot.kr/2011/10/introducing-android-webdriver.html

http://abodeqa.wordpress.com/2013/05/08/configuring-android-webdriver-in-eclipse/

Hybrid

IPhone & Android : http://appium.io/

IPhone : 내장되어있는 Safari를 통해 직접 테스트 가능

Android : Chromedriver를 통해 chrome을 따로 설치해서 테스트 가능 (크롬브라우저 app이 Intel x86 에서는 실행되지 않음.)

Android : http://dominikdary.github.io/selendroid/

Mobile Browser Inspector

Weinre : http://people.apache.org/~pmuellr/weinre/docs/latest/

QA 자동화를 통해 얻을 수 있는 이점은

모바일 웹페이지는 물론 네이티브 앱에 대해 배포시마다 반복되는 테스트에 대해 스크립트를 작성해두면 컴퓨터로 자동화 할수 있기 때문에

많은 인력 리소스를 절감 할 수 있고 좀더 믿을 만한 결과를 얻을 수 있다고 생각합니다.

몇년 전 모바일 어플 개발할때 출시때부터 업데이트 할때마다 반복적은 테스트로 몇시간씩 하면서 야근했던걸 떠올리면…ㅠ_ㅠ

다음 언젠가.. 안드로이드 / 아이폰에 대해 실제 시스템에 대한 구축방법을 적어보겠습니다.^^

 

Fileupload(swfupload, html5, jquery-fileupload)

2013/03/29

IE 10과 win8 이 나오면서 flash player 로 된 swfupload 나

activeX upload 에 대한 대안으로 html5 fileupload 방법을 찾게되었고

추가적으로 jQuery-File-Upload 를 찾게 되었다.

HTML5 를 이용한 파일 업로드의 경우 아래 블로그에 아주 자세히 나와있다.

html5-file-upload-with-progress

위 블로그에서 설명한 기능은

html5 progress event 를 이용한 % 숫자로 진행상태 보여주기

XMLHttpRequest 를 이용한 업로드

FormData 를 이용한 fileSize, fileType 출력 이다.

영어로 되어 있지만 소스 코드만 붙여넣기 하더라도 동작할 정도의 소스기 때문에 이해하는데는 어렵지 않다.

fileUpload 받는 서버의 경우는 기존 포스트에 node-fileupload 소스를 간단히 수정해서 쓸수도 있고

xamp 설치후 php로 간단히 파일을 받는 서버를 구현할수도 있다. (file-upload.post-method)

추가적으로 이 포스트에서는 다음과 같은 기능을 구현 및 변경했다.

1. 파일 선택 버튼은 이미지로 변경

2. 파일 선택시 목록에 들어가고 submit 버튼을 이용해 파일리스트를 전송

3. 총 파일 크기 제한

위 기능은 swfupload에서 기본?으로 제공하는 기능으로

</pre>
$(document).ready(function() {

var swfu = new SWFUpload({
'flash_url' : '/resource/swf/swfupload.swf'
, 'file_post_name' : 'uploadedFile'
//, 'post_params' : {'name': 't.png'}
, 'upload_url' : '/upload/exec'
, 'file_types' : '*.*'
, 'file_types_description' : 'allFile'
, 'file_size_limit' : maxFileSize
, 'file_queue_error_handler' : uploadHandleErrors
, 'upload_error_handler' : uploadHandleErrors
, 'file_queued_handler' : uploadQueued
, 'upload_success_handler' : uploadComplete
, 'button_placeholder_id' : 'fileToUpload'
, 'button_image_url' : '/resource/img/browse_bg.gif'
, 'button_text' : '<a href="#" onclick="return false;">' + '파일찾기' + '</a>'
, 'button_text_style' : ' a { fontFamily:gulim,arial; } '
, 'button_text_left_padding' : 6
, 'button_text_top_padding' : 2
, 'button_width' : 66
, 'button_height' : 22
, 'button_action' : SWFUpload.BUTTON_ACTION.SELECT_FILE
, 'button_disable' : false
});

&nbsp;

$('#btnSubmit').click(function() {
 swfu.startUpload();
 });

 function uploadQueued( fileObj ) {
 $('#uploadFileName').append('<li>' + fileObj.name + '</li>');
 }

function uploadHandleErrors( fileObj , error_code , message ) {
 switch( error_code )
 {
 default: return;
 case SWFUpload.ERROR_CODE_QUEUE_LIMIT_EXCEEDED: alert( message ); break;
 case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE: alert( 'FILE_INCORRECT' ); break;
 case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT: alert('FILE_SIZE_TOO_BIG'); break;
 case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE: alert('NOT_CSV_FILE'); break;
 case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED: alert( message ); break;
 case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED: alert( message ); break;
 }
 }

 function uploadComplete( fileObj , data ) {
 var result = $.parseJSON(data);
 if( result.msg == '[SUCCESS]' ) {
 $( '#uploadFileName' ).html('');
 alert('registComplete');
 //swfu = null;
 return;
 }
 else {
 //swfu = null;
 alert('registFail');
 }
 }
<pre>});

대충 저런식으로 처리를 해주면 된다.

HTML5 를 이용하는 방법은…

</pre>
<form id="form3" enctype="multipart/form-data" method="post" action="">
<div class="row">
 <input type="hidden" name="MAX_FILE_SIZE" value="3000000" />
 <label for="fileToUpload3">HTML5 Select a File to Upload</label><br />
 <img src="/resource/img/button.png" onclick="document.getElementById('fileToUpload3').click();" />
 <input type="file" size="30" name="uploadedFile" id="fileToUpload3" style="display:none;"/>
 <input type="text" name="userId" id="textTest3" value="111" />
</div>

<div><ul id='uploadFileName3'></ul></div>
<div id="fileSize3"></div>
<div id="fileType3"></div>
<div class="row">
 <input type="button" value="Upload" id="btnSubmit3"/>
</div>
<div id="progressNumber3"></div>
</form>

위와 같이 우선 버튼을 이미지로 바꾼 form 을 선언하고

</pre>
$(document).ready(function() {

var fd = new FormData();

$('form:#form3').change(function() {
fileSelected('fileToUpload3', fd);
 });

 $('#btnSubmit3').click(function() {
uploadFile(fd);
 });

&nbsp;

function fileSelected(sInputId, fd) {
 var file = document.getElementById(sInputId).files[0];
 if (file) {
 var fileSize = 0;
 if (!fd.curFileSize) {
 fd.curFileSize = 0;
 }
 fd.curFileSize += file.size;

 if (fd.curFileSize > maxFileSize) {
 alert('FILE_SIZE_TOO_BIG');
 return false;
 }

 if (file.size > 1024 * 1024) {
 fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
 } else {
 fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
 }

$('#uploadFileName3').append('<li>' + file.name + '</li>');
 $('#fileSize3').html('Size: ' + fileSize);
 $('#fileType3').html('Type: ' + file.type);

 fd.append('uploadedFile', file);
 }
 }
function uploadFile (fd) {
 var xhr = new XMLHttpRequest();

fd.append('userId', $('textTest3').val());

/* event listners */
 xhr.upload.addEventListener("progress", contMainPage.uploadProgress, false);
 xhr.addEventListener("load", contMainPage.uploadComplete, false);
 xhr.addEventListener("error", contMainPage.uploadFailed, false);
 xhr.addEventListener("abort", contMainPage.uploadCanceled, false);
 /* Be sure to change the url below to the url of your upload server side script */
 xhr.open("POST", "/main/exec");
 xhr.send(fd);
 }

});

위와 같이 파일을 선택할때마다 파일 사이즈의 합을 체크하고 formData에 append 시켜주면 된다.

 

jquery fileupload 의 경우 크로스 브라우징을 지원하기 때문에 아예 swfupload를 대체해서 사용할려고 했으나

결국 파일 사이즈 체크등에 대한 기능은 html5를 사용하는 등 제한이 있기때문에 테스트만 해보았으며

</pre>
$("#form2").fileupload({
 // dataType: 'text',
 url: '/main/exec',
 fileInput: $('#fileToUpload2'),
 // formAcceptCharset: 'utf-8',
 // singleFileUploads: true,
 // replaceFileInput: false,
 // forceIframeTransport: true,
 add: function (e, data) {
 if (!data.curFileSize) {
 data.curFileSize = 0;
 }
 data.curFileSize += data.files[0].size;
 if (data.curFileSize > maxFileSize) {
 alert('FILE_SIZE_TOO_BIG');
 return false;
 }
 $('#uploadFileName2').append('<li>' + data.files[0].name + '</li>');

 $('#btnSubmit2').click(function() {
 data.submit();
 });
 },
 done: function (e, data) {
 alert(data.result.msg);
 }
 ,failed: function (e, data) {
alert('registFail');
 }
 });
<pre>

위와 같이 선언해주면 된다.

jQuery .on() 사용법

2013/03/08

http://www.mimul.com/pebble/default/2013/03/06/1362559765579.html

위 블로그에 보면 .bind(), .live(), .delegate() 대신 .on() 의 사용을 권고하고 있습니다.^^

근데 live 대신 쓰기 위해서는 아래와 같은 방법으로 작성해야된다고 해서 실제로 테스트해보았습니다.

$(document).on("click", ".foo", handler);
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
  <input type="button" value="추가" />
  <div id="parent"></div>
  
</body>
</html>
$(document).ready(function(){
  var i = 0;
  
  $('input').on('click', function(){
    $('#parent').append('<ul class="child">test = '+i+'</ul>');
    i++;
  });
  
  $(document).on('click', '.child', function(){
      alert($(this).html());
  });
  /* 작동 안함.
  $('.child').on(function() {
      alert($(this).html());
  });
  */
});

Gearman 모니터링 with PHP

2013/02/01
tags:

1. 따로 웹서버 올리기.

https://github.com/yugene/Gearman-Monitor

따로 올릴때는 위에 소스를 clone해서 아파치 설정과

_config,php 파일에 아래와 같이 추가하면 바로 사용 가능

<?php
/**
 * Gearman Monitor configuration file
 *
 * The following server fields are available:
 *  - address: Gearman server address, hostname and port
 *  - name: Gearman server name to display in Gearman Monitor interface
 *
 * Example:
 * $cfgServers[$i]['address'] = '192.168.0.10:4730';
 * $cfgServers[$i]['name'] = 'Gearman server 1';
 * ++ $i;
 *
 * $cfgServers[$i]['address'] = '192.168.1.1:7003';
 * $cfgServers[$i]['name'] = 'Gearman server 2';
 * ++ $i;
 */
$i = 0;
$cfgServers = array();
$cfgServers[$i]['address'] = '192.168.0.1:4730';
$cfgServers[$i]['name'] = 'Gearman Job Server 1';
++ $i;
$cfgServers[$i]['address'] = 'gearman-job-001.test.com:4730';
$cfgServers[$i]['name'] = 'Gearman Job Server 1';
++ $i;
$cfgServers[$i]['address'] = 'gearman-job-002.test.com:4731';
$cfgServers[$i]['name'] = 'Gearman Job Server 1';
++ $i;
?>

실행 화면

gearman_monitor

서버별 job function, queue에 쌓인 job 개수, worker 개수 등을 확인할 수 있다.

2. Net_Gearman(http://pear.php.net/package/Net_Gearman) 사용하기


<?php
@include_once('Net/Gearman/Manager.php');
class testClass
{
    public function test()
    {
        try {
            echo var_dump($this->getWorkerInfo());
            echo var_dump($this->getGearmanStatus());
        } catch (Exception $e) {
            return $e;
        }
    }
    public function getWorkerInfo($sServerIp='localhost', $sServerPort='4730')
    {
        try {
            if(class_exists('Net_Gearman_Manager', true)) {
                $gearman = new Net_Gearman_Manager($sServerIp.':'.$sServerPort);
                $oWorkers = $gearman->workers();
                $gearman->disconnect();
                unset($gearman);
                return $oWorkers;
            }
        }
        catch (Exception $e) {
            return $e;
        }
    }
    public function getGearmanStatus($sServerIp='localhost', $sServerPort='4730')
    {
        try {
            if(class_exists('Net_Gearman_Manager', true)) {
                $gearman = new Net_Gearman_Manager($sServerIp.':'.$sServerPort);
                $oStatus = $gearman->status();
                $gearman->disconnect();
                unset($gearman);
                return $oStatus;
            }
        }
        catch (Exception $e) {
            return $e;
        }
    }
}
$gearman = new testClass();
$gearman->test();

gearman_monitor2

[Node.js] MySQL Driver Tests

2012/11/14

추가 테스트~

사내 테스트용 가상서버를 받아서 아래 내용을 추가로 테스트 해보았습니다.
가상서버의 스팩은 아래와 같습니다.
CentOS 6.x
Intel® Xeon® Processor E5506
(4M Cache, 2.13 GHz, 4.80 GT/s Intel® QPI)

결과는

| Driver/ORM           | Type | Run 1 | Run 2 | Run 3 | Average|
+----------------------+--------+--------+--------+--------+--------+
| NodeJS_mysql         | driver | 12.035 | 12.081 | 12.024 | 12.04 |
| NodeJS_sequelize_1   | orm    | 32.908 | 30.707 | 31.118 | 31.57 |
| NodeJS_sequelize_10  | orm    | 29.983 | 29.593 | 29.832 | 29.80 |
| NodeJS_mysql_queue   | trans  | 23.666 | 11.337 | 11.689 | 15.56 |
| PHP_PDO              | driver | 2.889  | 2.939  | 2.984  | 2.93 |
| NodeJS_mysql         | driver | 0.021  | 0.022  | 0.018  | .02 |
| NodeJS_sequelize     | orm    | 0.076  | 0.064  | 0.049  | .06 |
| PHP_PDO              | driver | 0.004  | 0.003  | 0.004  | 0 |
+----------------------+--------+--------+--------+--------+--------+

와 같습니다. insert시 순위가 바뀌었는데요;;
확인해 본결과 CPU 때문이 않을까 싶습니다.
node 같은경우 CPU가 100% 까지도 올라갔었습니다.;;
php는 상대적으로 20%까지 올라가고 끝났습니다.

아래 테스트를 진행했던 개인 PC의 사양은
Intel® Core™ i5-760 Processor
(8M Cache, 2.80 GHz)
입니다.

http://www.cpubenchmark.net를 참고해보니
i5-760 = 4000
Xeon E5506 = 3189
이네요.

마지막 추가로
node.js에 node-db-mysql 을 테스트 해보았습니다.
node-db-mysql는 libmysql을 사용합니다.
결과는 같은 가상서버에서 PHP_PDO와 insert시 같은 성능을 내주었습니다만
select시에는 이상하게 NodeJS_mysql와 비슷하거나 약간 나은 성능을 보여주네요;
———————————————————————————–

node.js 로 새로운 프로젝트 하는중에 mysql driver가 필요해서 검색하던중 아래와 같은 글을 찾았다.
For MySQL: Goodbye NodeJS, Welcome Back PHP
저자는 node.js 팬이기는 하지만 node.js와 mysql 간에는 궁합이 안맞다고.. php가 성능이 훨씬 빠르다는 것이다.
그러면서 테스트 결과와 git repo를 올려줘서 궁금한김에 직접 테스트 하다가 이것 저것 고쳐보니 의외의 결과가 나왔다.

우선 php는 잘 할줄 모르고 귀찮아 php용 orm 은 설치하지 않음.

https://github.com/teragoon/MySQL-Driver-ORM-Tests

원 저작자(David)의 결과

| Driver/ORM           | Type   | Run 1 | Run 2 | Run 3 |Average|
+----------------------+--------+-------+-------+-------+-------+
| NodeJS_mysql         | driver | 4.53  | 4.58  | 4.65  | 4.58  |
| NodeJS_sequelize     | orm    | 11.64 | 11.70 | 11.74 | 11.69 |
| PHP_PDO              | driver | 2.14  | 2.21  | 2.21  | 2.18  |
| PHP_laravel_raw      | driver | 3.33  | 3.37  | 3.38  | 3.36  |
| PHP_laravel_eloquent | orm    | 4.52  | 4.38  | 4.37  | 4.42  |
| PHP_doctrine         | orm    | 2.43  | 2.44  | 2.54  | 2.47  |
| PHP_zenddb           | driver | 2.39  | 2.48  | 2.48  | 2.45  |
+----------------------+--------+-------+-------+-------+-------+

내가 해본 결과

| Driver/ORM           | Type | Run 1 | Run 2 | Run 3 | Average|
+----------------------+--------+--------+--------+--------+--------+
| NodeJS_mysql         | driver | 43.148 | 34.969 | 34.792 | |
| NodeJS_sequelize_1   | orm    | 46.830 | 52.933 | 49.480 | |
| NodeJS_sequelize_10  | orm    | 13.408 | 13.241 | 12.648 | |
| NodeJS_mysql_queue   | trans  | 5.358  | 4.880  | 4.741  | |
| PHP_PDO              | driver | 32.905 | 36.375 | 31.431 | |
+----------------------+--------+--------+--------+--------+--------+

일단 전체적으로 David의 결과가 빠른 이유는 ssd상에(ㅠㅠ) 테스트 한 결과임

그리고 실제로 node-mysql 과 php_pdo 를 비교했을때 pdo가 빠름(심하게는 10초). 또한 node용 orm 의 경우 그 차이가 더 심함

하지만 sequelize의 경우 connection pool 을 지원하기 때문에
connection pool을 1개에서 10개로 늘리면 pdo의 1/3수준.
거기에 node-mysql의 queue 및 transaction 기능 확장 모듈인 node-mysql-queue 를 이용해 insert시 transaction을 사용시 1/6로 줄어듬.

php도 connection pool이나 transction을 지원하는 드라이버나 pdo로 사용하는 방법이 있는지는 모르겠지만..^^;
혹시 가능하신분은 git fork 해서 php 구현해주시면 감사.^^

결론은 driver 성능도 중요하지만 insert의 경우 connection pool이나 transaction 같은 기능이 가장 중요하다는 엉뚱한? 결과를 도출.

David가 마지막 결론에 Pick the right tool for the right job 이라고 했는데 right way 라는 말을 추가하고 싶음.

추가
순수 select 성능 비교

| NodeJS_mysql | driver | 0.108 | 0.188 | 0.196 | |
| NodeJS_sequelize | orm | 0.133 | 0.132 | 0.132 | |
| PHP_PDO | driver | 0.004 | 0.004 | 0.003 | |

PDO 압승!

P.S.
node.js의 mysql driver중에 node-db-mysql 가 있는데
http://nodejsdb.org/
node-db is developed with C++ 라는 문구가 있음.
실제 mysql client libraries 가 있어야 함.
windows가 설치 못함.
추후 비교 예정.

[Mysql] PROCEDURE를 이용한 loop insert

2012/11/08

개발하다 보면 테스트를 위해 더미 데이터를 insert 해야 하는 경우가 자주 생긴다

그때 많은 데이터를 넣어야 하는 경우 다음과 같이 sql을 작성하면 편하다.

참고로 AES_ENCRYPT 는 암호화 function이고 CONCAT은 string 연결 function이다.

 

DELIMITER $$

CREATE PROCEDURE myFunction()
BEGIN

    DECLARE i INT DEFAULT 1;

    WHILE (i < 500000) DO
        INSERT INTO `test table` (num, name, email)
        VALUE (i, CONCAT(‘test’, i), HEX(AES_ENCRYPT(CONCAT(‘test’, i, ‘@test.com’), ‘key’)));
        SET i = i + 1;
    END WHILE;
END$$

DELIMITER ;
CALL myFunction();

 

위에 처럼 PROCEDURE를 만들고 

while문을 이용해서 반복해주면 됨다.

팔로우

모든 새 글을 수신함으로 전달 받으세요.