브라우저 위의 그림판
HTML5 의 많은 새로운 기능 중 가장 자주 언급되어 왔던 것이 아마 Canvas(캔버스) 일 것이다
Canvas 는 Web Workers 나 Web Storage와 같은 다른 HTML5 스펙보다 덜 기술적이며 보다 직관적이다.
개발자 뿐만 아니라 비개발자들 역시 Canvas의 개념을 쉽게 이해할 수 있고 흥미를 보여왔기 때문에 가장 먼저 그리고 가장 흔하게 소개된 HTML5 기술인 것이다

Canvas 는 말 그대로 그림을 그릴 수 있는 화폭, 즉 그림판과 같다
그것도 다른 곳에 아닌 브라우저 위에서 동작하는 그림판인 것이다

Canvas 위에 선, 도형, 텍스트, 이미지와 같은 그래픽을 표현할 수 있고 색깔, 그림자, 패턴과 같은 여러 효과를 적용할 수 있다. 그리고 기본적으로 Canvas 는 2차원 그래픽 표현을 위한 스펙이지만 추가로
WebGL를 기반으로 하는 3D 그래픽용 Canvas 의 스펙 개발이 진행중이다
참고: WebGL은 오픈 그래픽 라이브러리인 OpenGL(OpenGL ES 2.0)에 기반한 웹 표준 그래픽 라이브러리이다. WebGL을 이용하면 브라우저에 별도의 플러그 인 없이 완벽하게 하드웨어 가속되는 3D그래픽을 표현할 수 있게 된다. 주요 브라우저 벤더사인 애플, 구글, 모질라 및 오페라와 하드웨어 업체인 AMD 및 엔비디아 등이 WebGL Working Group 멤버로 활동하고 있다. 그리고 구글의 웹 용 3D API O3D 플러그 인 기술을 포기하고 WebGL 을 선택했다 


브라우저 지원 현황
브라우저 지원 현황 역시 다른 HTML5 기술보다 더 일반적이다
다시 말해 대부분의 브라우저에서 Canvas를 지원한다는 의미이다


그림1. 브라우저별 2D Canvas 지원 현황 (출처: http://caniuse.com/)

IE9 이전 버전에서 Canvas 지원하기
현재까지 출시된 IE8 까지는 HTML5 지원이 거의 되지 않는다
Canvas 역시 IE9 이전 버전에서는 동작하지 않는다
다만 구글에서 제공하는 크롬프레임을 설치하면 IE9 이전버전에서도 HTML5 를 동작시킬 수 있다

그러나 Canvas의 경우 더 쉽게 IE에 적용할 방법이 있다.
ExplorerCanvas 라이브러리를 이용하면 이전버전의 IE에서도 Canvas를 동작시킬 수 있게 된다
(ExplorerCanvas는 이전 버전의 IE에서 Canvas API가 동작할 수 있도록 해 주는 오픈소스 자바스크립트 라이브러리이다)
ExplorerCanvas 다운로드: http://code.google.com/p/explorercanvas/

다운받은 excanvas.js 스크립트 파일을 Canvas가 구현된 HTML 파일의 헤더 영역에 다음과 같이
정의하여 IE일 경우 이 스크립트가 적용되도록 처리하면 된다
<html>
<head>
 <!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]-->
  ...



Canvas 학습 지원 사이트
Canvas로 구현할 수 있는 것은 아주 많다
개념적으로 보편적인 기술인 만큼 그 응용 개발 역시 매우 다양하게 펼칠 수 있다
그러나 응용도 기본이 갖춰져야 제대로 되는 만큼 기본사용법의 정확한 숙지가 필요하다

이 글에서도 기본적인 사용법과 간단한 데모를 작성해 보겠지만,
그전에 Canvas 학습을 도와주는 훌륭한 사이트가 있어 소개한다

이 두 사이트에서 제공하는 Canvas 튜토리얼은 정확한 기본기를 갖추고 응용력을 기르는데 충분한 도움을 줄 것이다. Canvas를 적용할 서비스는 굉장히 많으며 특히 게임과 같은 동적이고 화려한 그래픽이 필요한 서비스에는 활용도가 무궁무진할 것이다. 잘 정리된 기본 학습 도구를 활용하는 것이 매우 중요하므로 위 사이트들을 꼭 참고하기 바란다


Canvas 다루기
지금부터 Canvas를 다루는 기본적인 방법에 대해 알아 보도록 하자

<canvas> 태그와 그리기 컨텍스트
그림을 그리기 위해서는 제일 먼저 브라우저에 화폭이 준비되어 있어야 한다
<canvas>라는 마크업 태그를 이용하여 브라우저에 캔버스 영역을 지정할 수 있다
<canvas id="cv" width="400" height="300"></canvas>

HTML 문서에 <canvas> 태그를 삽입하는 것 만으로 브라우저에 그림판이 준비된다
다만 캔버스는 기본적으로 테투리가 없기 때문에 위 코드만으로는 브라우저에서 캔버스를 눈으로 확인하기는 힘들다. 따라서 스타일을 적용해 캔버스의 테두리를 보이게 하는 것이 좋을 수 있다
<canvas id="cv" width="400" height="300"
    style="position: relative; border: 1px solid #000;"></canvas>

그리고 이 캔버스를 이용해 그리기를 수행하려면 '그리기 컨텍스트'를 얻어야 한다
그리기 컨텍스트는 앞서 정의한 <canvas> DOM 객체의 getContext 메서드로 얻을 수 있다
var canvas = document.getElementById("cv"); //canvas의 DOM 객체를 얻는다
var context = canvas.getContext("2d");         //DOM 객체로부터 2D 컨텍스트를 얻는다

이후 그리기 작업은 '그리기 컨텍스트'를 통해 이루어진다

캔버스의 좌표
캔버스에 각종 도형 및 선과 같은 2차원 그래픽 작업을 할 때 각 점의 위치를 지정해야 하는데
이는 캔버스의 2차원 좌표가 기준이 된다
사각형 모양을 하고 있는 캔버스는 제일 왼쪽 상단 꼭지점이 (0,0) 시작점이 된다
(0,0) 시작점을 기준으로 그리고자 하는 도형의 가로 위치 x 와 세로위치 y 좌표를 지정하면 된다

그림2. Canvas의 좌표 (출처: http://diveintohtml5.org/canvas.html)


그리기 작업

1) 사각형 그리기
그리기의 가장 기본이 되는 도형이다. Canvas API 에서 사각형을 위한 함수를 따로 제공하고 있다
아래와 같은 사각형 함수인데, 모두 같은 매개변수를 취하고 있다
x,y : 사각형의 시작점 좌표(왼쪽 위 모서리 지점), width,height: 사각형의 너비,높이

fillRect(x, y, width, height)       : 색으로 채운 사각형을 그린다
strokeRect(x, y, width, height) : 선만 있는 사각형을 그린다
clearRect(x, y, width, height)   : 사각형 영역을 지운다

아래 그림은 간단한 사각형 그리기 샘플이다
차례로 속이 찬 사각형과, 윤곽만 있는 사각형, 그리고 절반이 지워진 사각형이다
아래 결과 화면과 코드를 참고하기 바란다



<!DOCTYPE html>
<html>
<head></head>
<body>
  <canvas id="cv" width="400" height="300" style="position: relative; border: 1px solid #000;"></canvas
</body>
</html>
<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");          
 
  context.fillRect(10,10,100,100);    //색으로채운 사각형
  context.strokeRect(120,10,100,100); //윤곽만 있는 사각형
 
  context.fillRect(230,10,100,100);   //색으로 채운 사각형
  context.clearRect(230,10,50,50);    //앞 사각형의 절반을 지움
</script> 


2) 각종 도형 및 선 그리기
사각형과는 달리, 삼각형, 오각형, 육각형 및 원과 같은 도형은 선들을 연결하여 직접 그려줘야 한다
완성된 도형은 선들의 집합체이며 각각의 선들을 패스(Path) 라 한다
(엄밀히 말하자면 각 선은 서브패스이며 서브패스 전체를 가리켜 패스라 한다)

결국 도형을 그린다는 것은 각 선을 연결한다는 의미이며 이는 패스(Path)를 그리는 과정인 것이다

패스(Path)그리기 순서
첫째, 패스 그리기를 시작을 알린다
: beginPath() 함수로 canvas에 패스그리기를 알린다. 이전의 패스는 모두 초기화된다

둘째, 패스 경로 즉 시작점과 종료점을 지정한다
: moveTo(x,y) 함수로 패스의 시작점을 지정하고 lineTo(x,y) 함수로 패스가 이어질 점을 지정한다
 
셋째, 패스 경로를 따라 실제로 그린다
: 이전 과정까지는 패스 즉 경로에 대한 정보를 셋팅하는 것이다
  실제 지정된 패스를 따라 선을 그려야 하는데, 이는 stroke(), fill()과 같은 잉크(ink)함수가 이용된다

삼각형 그리기
간단한 삼각형 그리기 샘플을 보자.
아래와 같은 삼각형을 그리기 위해 패스그리기를 수행한 예이다


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");          
 
  context.beginPath(); //패스 그리기 시작  
  context.moveTo(50,10); //패스 시작점 지정  
  context.lineTo(20 ,100); //패스 이동점 지정
  context.lineTo(80 ,100); 
  context.lineTo(50 ,10);  
  context.stroke();    //윤관선 그리기
  //context.fill();   //색 채우기
</script>

beginPath() 로 패스그리기의 시작을 알리고 moveTo()로 패스 시작점을 설정한다
그리고 lineTo() 를 이용하여 패스의 경로를, 그리는 방향 대로 이동한다
마지막으로 stroke()로 패스의 윤곽선을 그린다
주석처리된 fill() 함수로 패스를 그릴 경우 패스 속이 색(기존 검정색)으로 채워지게 된다 

이런식으로 삼각형은 물론, 사각형, 오각형,.. N각형의 도형을 맘껏 그릴 수 있다

원 그리기
앞서 살펴본 삼각형 그리기는 직선을 연결한 도형일 경우에는 적합하지만 곡선이나 원을
그릴 수는 없다. 즉 lineTo() 함수는 직선을 그리는 함수이다

원이나 곡선 역시 패스 그리기의 일종으로 앞서 살펴본 과정을 그대로 따른다
다만 직선을 그리는 함수인 lineTo() 대신 원을 그리는 함수인 arc() 나 배지곡선을 그리는 함수인 quadraticCurveTo() 함수를 이용하면 된다

간단한 원그리기 샘플을 살펴 보자


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");          
 
  context.beginPath(); //패스 그리기 시작   
  context.arc(70,70,50,0, 2*Math.PI, true); //원그리기 
  context.stroke();    //윤관선 그리기
</script>


패스 닫기
패스를 이용해 도형을 마음껏 그릴 수 있으려면 관련 학습을 조금 더 해야 한다
패스와 관련된 추가 학습은 다른 사이트를 참고하도록 하며 이 글에서는 명시적으로 패스를 닫는 closePath() 함수에 대해 알아보면서 패스 정리를 마치고자 한다

closePath()는 패스를 명시적으로 닫는 역할을 한다
반드시 사용해야 하는 것은 아니지만 명시적으로 closePath()를 호출해 주면
마지막 패스의 좌표와 시작좌표를 자동으로 연결해 주기 때문에 편리하다

다음은 앞서 살펴본 삼각형 그리기 예에서 마지막 패스를 생략하고 closePath()를 대신 사용하였다
즉 마지막 lineTo() 함수가 없어도 자동으로 시작점과 연결해 주기 때문에 결과는 동일하다
<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");          
 
  context.beginPath(); //패스 그리기 시작 
  context.moveTo(50,10); //패스 시작점 지정 
  context.lineTo(20 ,100); //패스 이동점 지정
  context.lineTo(80 ,100); 
  context.closePath();     //마지막 패스 이동점 대신 패스를 명시적으로 닫는다
  context.stroke();    //윤관선 그리기
</script>


스타일 및 효과
캔버스에 그리기 작업을 할 경우 별다른 스타일을 지정하지 않으면 기본 스타일이 적용된다
앞서 살펴본 샘플에서 도형의 안,팎의 색이 검정색이었던 것은 기본값이기 때문이다
스타일을 적용하면 도형의 색 지정 뿐만 아니라 그라데이션 효과, 그림자 효과등을 줄 수 있다

색 지정
우선 간단히 색을 지정하는 예를 보자. 앞서 살펴본 사각형 그리기 예에서 색을 지정한다
채우기 스타일은 fillStyle 로 윤곽선 스타일은 strokeStyle 로 색을 지정하면 된다
색을 지정할 때 blue, red 와 같은 문자는 물론 #ffffff 혹은 rgb(255,0,0) 등 모든 CSS 컬러를 사용할 수 있다. 색을 지정하지 않았을 땐 기본값으로 #000000(검정색) 이 된다


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");                 
 
  context.fillStyle = "blue";
  context.fillRect(10,10,100,100);    //파란색으로 채운 사각형
 
  context.strokeStyle = "red";
  context.strokeRect(120,10,100,100); //빨가색 윤곽선 사각형
</script>


그라데이션 효과
fillStyle 이나 strokeStyle를 통해 색깔 지정 뿐만 아니라 그라데이션 효과도 줄 수 있다
그라데이션은 선형그라데이션과 원형그라데이션 두 가지 종류가 있다

선형 그라데이션
: (x,y) 지점에서 (x1,y1) 까지 직선으로 색조가 변화한다
  createLinearGradient(x,y,x1,y1) 함수 이용

원형 그리데이션
: 중심이 (x,y), 반지름 r인 원에서 중심이 (x1,y1), 반지름이 r1 인 원 사이의 색조가 변화한다
  createRadialGradient(x,y,r,x1,y1,r1) 함수 이용

createXXXGradient 함수로 그라데이션의 형태를 지정한 후,
addColorStop(offset, color) 함수를 이용하여 각 지점에 이용될 색을 지정한다
offset는 0.0(시작점) 에서 1.0 (끝점) 사이의 위치 값을 나타내며 해당 위치의 색을 지정한다

사각형에 선형 그라데이션 효과를 준 예를 살펴 보자
두 사각형 모두 선형 그라데이션 효과를 준 것이며 직선 방향을 조정하여 가로,세로 효과를 줬다

(화살표는 색조 변화 방향을 나타냄)

<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");                 
 
  var gradient = context.createLinearGradient(0,0,100,0);
  gradient.addColorStop(0,"white");
  gradient.addColorStop(1,"blue"); 
  context.fillStyle = gradient;   
  context.fillRect(10,10,100,100); 
 
  gradient = context.createLinearGradient(0,0,0,100);
  gradient.addColorStop(0,"white");
  gradient.addColorStop(1,"red"); 
  context.strokeStyle = gradient;
  context.strokeRect(120,10,100,100);      
</script>


그림자 효과
선이나 도형에 그림자 효과를 줄 수 있다.
shadowOffsetX, shadowOffsetY 함수를 이용하여 원본 도형의 위치를 기준으로 그림자의 위치를 지정할 수 있으며 shadowColor 로 그림자 색상 shadowBlur로 그림자의 흐리기를 조정할 수 있다


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");                 
    
  context.shadowColor = "green";
  context.shadowOffsetX = 5;
  context.shadowOffsetY = 5;
  context.shadowBlur = 2; 
 
  context.fillStyle = "blue";
  context.fillRect(10,10,100,100);  
</script>



이미지 및 텍스트 그리기
캔버스에는 선이나 각종 도형 이외에도 이미지 파일을 삽입하거나 텍스트를 직접 그릴 수도 있다
캔버스에 텍스트 그리기를 이용하면 글자에 각종 효과를 줄 수 있는 장점이 있다
그리고 이미지 역시 각종 이미지 처리를 할 수 있다는 장점이 있겠다

텍스트 그리기
우선 캔버스에 텍스트를 그리는 예를 살펴 보자
다른 도형 그리기와 마찬가지로 채워진 텍스트 혹은 윤곽선만 있는 텍스트를 그릴 수 있다


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");                 
    
  var msg = "Hello, Canvas!"
 
  context.font = "20px '맑은 고딕'";
  context.fillText(msg, 10  ,50);
 
  context.font = "40px 'Tahoma'";
  context.strokeText(msg, 10  ,100);
</script>


이미지 그리기
drawImage 함수를 이용하여 캔버스에 이미지 파일을 삽입할 수 있다
주의할 점은 drawImage의 호출 시점이다. 이미지가 모두 로딩된 이후에 이 함수를 호출해야 한다
따라서 이미지 로딩이 완료될 때 발생하는 onload 이벤트에서 이 함수를 사용하는 것이 일반적이다

눈의 즐거움을 위해 손담비 사진을 이용해 샘플을 만들어 보자 ^.^
Image 객체의 onload 이벤트에서 drawImage 함수가 호출되는 것을 눈여겨 보기 바라며
이미지를 이용하여 사각형을 그릴 때 채우기 패턴으로 사용할 수도 있다
예에서는 손담비 이미지를 기반으로 패턴을 생성하여 사각형의 채우기 스타일로 사용하고 있다
그리고 회전효과도 가미했다(이미지 그리기와는 무관하지만...)


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");                 
    
  var image = new Image();
 
  image.onload = function(){
    context.rotate(20*Math.PI/180);       //회전효과
   
    context.drawImage(image,30,10);   //이미지 그리기
   
    var pattern = context.createPattern(image,"repeat"); //반복패턴정의
    context.fillStyle = pattern;                                       //fill 패턴 지정
    context.fillRect(200,10,200,250);                                   //이미지로 사각형을 채운다     
  }
 
  image.src = "sondambi.bmp";
</script>


Canvas 초기화
그림을 그리다 모두 지우고 싶을 때가 있다
캔버스에 각종 그래픽 작업을 하다가 모든 것을 지우고 다시 시작하고 싶은,
일명 리셋(reset)을 하고 싶다면 매우 단순하게 초기화를 할 수 있다

캔버스의 너비나 높이 속성을 다시 설정하는 것만으로 캔버스는 완전 초기화 된다
캔버스에 그려진 내용 뿐만 아니라 그리기컨텍스트의 각종 속성도 기본값으로 돌아 온다

function resetCanvas(){
    canvas.width = canvas.width;       
  }


마무리 데모
이제 이 글을 마치기 전에 마무리 데모를 살펴 보자
사실 이 글에서 소개된 캔버스 다루기 내용은 극히 일부에 지나지 않는다.
2D 그래픽 처리와 관련한 다양한 기법과 API들이 존재한다.

반드시 관련 자료를 참고하여 추가 학습을 하길 권하며 3D 캔버스인 WebGL도 필요하다면 관련 자료를
찾아 보기 바란다

마지막 데모는 마우스로 그림을 그릴 수 있는 그림판이다
캔버스에 마우스를 데고 자유자재로 움직이면, 선이 마우스를 따라 그려지는 샘플이다

아래 데모 실행화면을 보자. 필자가 캔버스에 대고 그린 그림이다 -.-;


전체 소스
<!DOCTYPE html>
<html>
  <head>
    <title>Canvas Paint Example</title>      
    <!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]-->   
  </head>
  <body>
   <div id="container">
     <canvas id="drawCanvas" width="400" height="300"
            style=" position: relative; border: 1px solid #000;"></canvas>
</div>
</body>
</html>
<script type="text/javascript">   
if(window.addEventListener){
    window.addEventListener('load', InitEvent, false);
}
var canvas, context, tool;       
function InitEvent () {                                      
    canvas = document.getElementById('drawCanvas');
    if (!canvas) {
      alert("캔버스 객체를 찾을 수 없음");
      return;
    }
    if (!canvas.getContext) {
      alert("Drawing Contextf를 찾을 수 없음");
      return;
    }       
    context = canvas.getContext('2d');
    if (!context) {
      alert("getContext() 함수를 호출 할 수 없음");
      return;
    }
    // Pencil tool 객체를 생성 한다.
    tool = new tool_pencil();
    canvas.addEventListener('mousedown', ev_canvas, false);
    canvas.addEventListener('mousemove', ev_canvas, false);
    canvas.addEventListener('mouseup',   ev_canvas, false);
}
function tool_pencil ()                                  
{
    var tool = this;
    this.started = false;
    // 마우스를 누르는 순간 그리기 작업을 시작 한다.
    this.mousedown = function (ev)
    {
        context.beginPath();
        context.moveTo(ev._x, ev._y);
        tool.started = true;
    };
   // 마우스가 이동하는 동안 계속 호출하여 Canvas에 Line을 그려 나간다
    this.mousemove = function (ev)
    {
        if (tool.started)
        {
            context.lineTo(ev._x, ev._y);
            context.stroke();
        }
    };
   // 마우스 떼면 그리기 작업을 중단한다
    this.mouseup = function (ev)
    {
      if (tool.started){
            tool.mousemove(ev);
            tool.started = false;
      }
    };
}       
// Canvas요소 내의 좌표를 결정 한다.
function ev_canvas (ev)
{
    if (ev.layerX || ev.layerX == 0)
    { // Firefox 브라우저
      ev._x = ev.layerX;
      ev._y = ev.layerY;
    }
    else if (ev.offsetX || ev.offsetX == 0)
    { // Opera 브라우저
      ev._x = ev.offsetX;
      ev._y = ev.offsetY;
    }
    // tool의 이벤트 핸들러를 호출한다.
    var func = tool[ev.type];       
    if (func) {
        func(ev);
    }
}
</script>

출처 : http://m.mkexdev.net/ 

'Dev > RIA' 카테고리의 다른 글

Canvas2Image: Canvas 데이터를 이미지로 저장  (0) 2011.06.10
HTML5 Guide  (0) 2011.06.06

박용우

이클립스는 앞으로 다가올 대변혁의 시대를 준비하고 있다. 지금까지 진행해왔고, 앞으로 진행할 이클립스 프로젝트들은 엔터프라이즈 개발, 임베디드 및 모바일 개발, 리치 클라이언트 플랫폼, 애플리케이션 프레임워크, 개발 언어 IDE 등으로 세분화되고 있다. 그러나 우리는 과거의 이클립스 IDE만을 생각하며 마냥 시간을 보내고 있는 것 같다. 필자 역시 이번 기고를 진행하면서 까맣게 잊고 있던 무언가를 다시금 떠올릴 만한 계기를 마련한 것 같다. 지금부터 RCP와 함께 새로운 각오를 다져보자.



1990년대 중반부터 두드러진 인터넷과 웹 브라우저의 보급으로 현대인들의 생활 패턴이 완전히 달라졌다. 이제는 모든 생활이 인터넷에서 이뤄질 수 있다고 해도 과언이 아닐 것이다. 그러나 최초의 웹 브라우저인 넷스케이프와 마이크로소프트의 인터넷 익스플로러가 펼친 웹 브라우저 전쟁에서 인터넷 익스플로러가 승리한 이후부터는, 많은 웹 기반 개발자들과 사용자들은 더 이상의 새로운 웹 기술의 발전을 경험하지 못했고, 점점 더 웹의 한계를 느끼기 시작했다. 그나마 리눅스나 파이어폭스와 같은 도전자들이 존재하고 있어 마이크로소프트가 장악하다시피 한 웹 환경에서 다소나마 발전이 이뤄지고 있다고 할 수 있다.



리치 클라이언트의 귀환

웹브라우저를 필두로 하여 웹 페이지, 웹 서버, CGI, 웹 애플리케이션으로 이어지는 씬(thin) 클라이언트가 클라이언트 PC의 기본 플랫폼이 되었고, 많은 유지보수(특히 버그 수정과 버전 갱신) 문제와 무거운 동작성이라는 약점을 안고 있는 리치 클라이언트가 점차 사라지게 되었다.

21세기로 넘어오면서 이처럼 많은 시스템들이 웹으로 이동했고, 웹은 사용자가 처음 익히기에 쉽고 빠르며 이식도 편리한 환경을 제공했다. 그러나 사용자들이 모든 일을 웹 브라우저를 통해 수행하려다 보니, 빈약한 사용자 체험(user experience), 웹 엔트로피의 증가, 다양한 플랫폼 및 다중 웹 브라우저에 대한 지원 부족 등의 문제들이 하나둘 나타나기 시작했다. HTML의 단순한 사용자 인터페이스가 주는 제약을 뛰어넘기 위해 DHTML, 자바스크립트, 액티브X 컨트롤, 플래시가 범람하게 되었고, 이는 결국 웹 브라우저로 대표되는 씬 클라이언트가 다시금 무거워지는 계기로 작용했다. 이러한 혼란의 시대가 계속되자 어디론가 사라졌던 리치 클라이언트가 다시 귀환하게 되었다.

플래시 는 리치 클라이언트의 부활을 알리는 첫 신호탄이었다. 이는 기존의 웹에서 볼 수 없었던 풍부한 사용자 체험(user experience)을 제공했고 나아가 프로그래밍이 가능한 플렉스로 발전했다. 그러나 플렉스가 리치 인터넷 애플리케이션(Rich Internet Application, RIA)이라고 스스로 말하고 있듯이, 웹 브라우저 내에서 실행되는 특성 탓에 웹이라는 태생적인 한계는 벗어나지 못하고 있는 것 같다.

또한 웹2.0을 구현할 기술의 하나로 주목받고 있는 AJAX(Asynchronous JavaScript and XML) 역시 기존의 웹과 HTML이 지닌 웹 엔트로피 과다 생산의 문제점을 보완하기 위해 생겨난 하나의 웹 기술에 불과하다. 이러한 상황에서 이클립스 리치 클라이언트 플랫폼(Rich Client Platform, RCP)은 신선한 충격으로 다가왔다. 다중 크로스 플랫폼 환경에서 성공적으로 상호작용하는 비즈니스 애플리케이션 생성에 필요한 모든 요구사항들을 충분히 만족시키고 있다고 생각한다. 물론, 웹 애플리케이션은 여전히 큰 장점을 갖추고 있고 AJAX나 웹2.0 등의 기술과 함께 진일보하고 있다. 동시에 리치 클라이언트 애플리케이션은 일부 애플리케이션에 훨씬 더 부합한다. 그 예로, 은행 창구(Bank Teller) 애플리케이션이나 콜센터(Call Center) 애플리케이션 등을 꼽을 수 있다. 이 경우의 애플리케이션들은 일반적으로 다음과 같은 특징을 지닌다.

- 대부분 수 백, 수 천명의 사용자들을 타깃으로 한 내부 인트라넷 애플리케이션이다.
- 폰트, 컬러, 위젯 등 UI에 대한 엄격한 룩앤필 요구사항이 적용된다.
- 파워유저가 사용하는 것이므로 단축키를 사용한 빠른 반응을 필요로 한다.
- 매우 큰 데이터 세트를 가지고 있고 스크린에 많은 정보를 디스플레이 한다. 클라이언트 측에 큰 세션 데이터를 관리하여 퍼포먼스를 높인다.
- 데스크탑 및 다른 데스크탑 애플리케이션과 통합한다.
- 일반적으로 워크플로우 유형 또는 협업 유형의 애플리케이션이고 오프라인에서 작업할 수 있는 기능과 태스크 변환 기능이 매우 중요하다.

이 세상에서 인간이 만든 그 어떤 것도 완벽할 수는 없다. 따라서 지금부터는 리치 클라이언트와 씬 클라이언트가 서로의 역할을 명확히 구분하고, 서로를 존중하며 공존해야 할 것이다.



리치 클라이언트 플랫폼

자바를 잘 알지 못하는 주변의 친구들이나 혹은 함께 작업하는 이들에게 이클립스 IDE 프로그램을 보여주고, 그것이 어떤 언어로 개발되었다고 생각하는 지를 물어보길 바란다. 아마 대부분의 사람들은 VB(비주얼 베이직), C++, 혹은 C# 등의 언어로 개발되었다고 답할 것이다. 이들에게 이클립스는 자바로 개발된 애플리케이션임을 알려주면 대다수가 무척 놀랄 것이다.

이 는 자바를 잘 모르는 이들에게만 해당되는 반응은 아니다. 이클립스가 제공하는 리치 클라이언트 플랫폼을 써서 VB, C++, C#, 델파이 등을 이용해 개발한 것과 동일한 윈도우 프로그램을 개발할 수 있고, 더군다나 그 윈도우 프로그램을 윈도우, 리눅스, 맥 OSX 등에서 아무런 변경 없이 실행할 수 있다고 말한다면 자바를 충분히 알고 있는 독자들도 별반 다르지 않은 반응을 보일 것이다.

● 이클립스 RCP

물론 이클립스가 처음부터 그랬던 것은 아니다. 이클립스는 2001년 11월 7일 공식적으로 오픈 프로젝트가 된 후, 2002년 6월 처음으로 2.0 버전을 릴리즈했다. 가장 최근에는 2005년 11월에 3.2 M3 버전이 릴리즈되었다 (http://www.eclipse.org/ community/news/whatsnewhistory.html). 지난 몇 년 동안 이클립스 프로젝트는 극적으로 성장했고, 그 결과 강력한 개발 환경으로 성숙했다. 대다수의 독자들은 이클립스를 단순한 통합 개발 환경(IDE)으로 여길지 모르지만, 이클립스 3.0 버전 이후부터는 더 이상 단순한 IDE가 아니라 소프트웨어 라이프사이클 전반에 걸친 개발 플랫폼으로 자리매김하고 있다.

또한 이클립스 3.0에 와서는 비로소 이클립스 UI의 근간이 되는 핵심 기능들을 비IDE 애플리케이션에서도 쉽게 사용할 수 있도록 리팩토링 한 리치 클라이언트 플랫폼이라는 개념도 소개했다. 개발자들은 비즈니스 애플리케이션을 개발할 때 이클립스 플러그인 아키텍처에 기반해 이클립스 RCP가 제공하는 많은 고급 기능들(플랫폼 독립적인 네이티브 GUI 컴포넌트와 네이티브 룩앤필, 풍부한 사용자 인터페이스, 헬프 시스템 등)을 사용할 수 있게 되었다.

이처럼 많은 이클립스의 기능들은 이클립스 RCP라는 이름으로 비즈니스 애플리케이션 개발에 필요한 공통 프레임워크로 사용되었다. 이로써 개발자들은 코어 컴포넌트 셋을 만들기 위해 시간을 낭비하는 대신에, 각자가 만들고자 하는 애플리케이션의 요구사항 분석이나 실제 개발에 더 많은 시간과 정열을 쏟을 수 있게 되었다. 이클립스의 주요 컴포넌트와 이중에서 이클립스 RCP로 패키징된 컴포넌트들의 구성도를 살펴보면 <그림 1>과 같다.


<그림 1> 이클립스 아키텍처와 이클립스 RCP 구성 컴포넌트

이클립스를 구성하는 전체 컴포넌트 중에서 이클립스 RCP의 기본 구성 컴포넌트를 골라 제시하고 있는데, 그 각각을 살펴보면 다음과 같다.

- UI 워크벤치(Workbench) : 에디터(editors), 뷰(views), 퍼스펙티브(perspectives) 등으로 구성된 UI 워크벤치를 제공한다.
- SWT(Standard Widget Toolkit) : SWT는 운영체제의 네이티브 윈도우 환경과 긴밀하게 통합된 다양한 컴포넌트와 플랫폼 독립적인 API를 제공한다. SWT는 자바 개발자들로 하여금 네이티브 데스크탑 애플리케이션과 동일한 품질의 솔루션을 개발할 수 있게 해준다.
- JFace : JFace는 SWT를 이용해 사용자 인터페이스를 개발할 때 해야 하는 많은 공통 작업들을 간편화 해주는 컴포넌트와 헬퍼 유틸리티 셋을 제공한다. 데이터 뷰, 위자드, 다이얼로그 컴포넌트, 텍스트 관리, 이미지 및 폰트 컴포넌트 등을 제공하기 위해 SWT를 확장하는 많은 유틸리티 클래스들을 포함하고 있다.
- 플랫폼 런타임 : 플러그인과 JIT 레이지 로딩(just-in-time lazy loading) 및 초기화 간에 확장 포인트(extension-point) 모델 기반의 느슨한 결합을 정의하고 있다.
- OSGi(Open Services Gateway Initiative) : 플러그인 라이프사이클 관리 등을 위한 프레임워크이다. 이클립스에서의 플러그인 발견이나 애플리케이션의 재시작 없이 플러그인을 로딩 및 언로딩 하는 동작이 이에 해당된다.



리치 클라이언트 애플리케이션

지금부터는 이클립스 RCP를 써서 개발한 대표적인 리치 클라이언트 애플리케이션을 소개한다. 데모를 다운로드 하기 위한 사이트 주소는 다음과 같다.

http://udig.refractions.net/confluence/display/UDIG/Home

이 사이트에서 데모를 다운로드 해 실행하면, 애플리케이션에서 제공하는 사용자 인터페이스가 이클립스와 별반 다르지 않음을 알 수 있다. <화면 1>에 나타난 모습과 여러분이 현재 사용하고 있는 이클립스를 비교해보길 바란다.


<화면 1> 이클립스 RCP를 이용한 리치 클라이언트 애플리케이션(uDig)

상 단의 메뉴바와 툴바, 왼쪽의 프로젝트와 레이어 탐색기, 컨텐츠 편집기, 그리고 하단의 몇 개 탭으로 구성된 메시지 또는 상태 창 등이 이클립스의 UI와 무척 흡사한 것을 알 수 있다. 게다가 ‘Window’ 및 ‘Help’ 메뉴는 이클립스와 완전히 동일하다. <화면 1>에서 보여주고 있는 uDig(User-friendly Desktop Internet GIS) 프로젝트는 GeoSpatial 애플리케이션이자, 개발자들이 새롭게 혹은 상속해서 애플리케이션을 생성할 수 있는 플랫폼이다. uDig는 인터넷에서 인식할 수 있는 GIS(Geographic Information System)의 핵심 엘리먼트다. uDig와 같은 리치 클라이언트의 특징은 다음과 같다.

- 사용자 편의성을 높인 풍부한 사용자 체험(rich user experience) 제공
- 플랫폼 독립(윈도우, 리눅스, 맥 OSX 등의 운영체제와 무관하게 실행)
- 하부 운영체제의 네이티브 위젯을 사용함으로써 네이티브 룩앤필 제공
- 드래그 & 드롭 기능과 같은 고급 사용자 인터페이스 제공
- 통합된 업데이트 방식 제공
- 확장성 제공



이클립스 플러그인 아키텍처

이클립스의 전신이 IBM의 비주얼 에이지 포 자바(Visual Age for Java)였기 때문에, 이클립스는 플랫폼을 쉽게 확장할 수 있도록 만들어졌다. <그림 2>는 이클립스 플러그인 아키텍처의 주요 컴포넌트들을 보여주고 있는데, 이클립스 RCP의 핵심은 바로 이클립스 워크벤치, SWT, JFace, 헬프 컴포넌트 등이다.


<그림 2> 이클립스 플러그인 아키텍처

이 클립스 플랫폼 런타임을 구성하는 기본 파일들을 제외하면, 이클립스의 모든 기능들은 플러그인을 사용해 구현된다. 플러그인은 개발자들이 이클립스 환경에 새로운 기능과 동작을 추가하기 위해 사용할 수 있는 기본 빌딩 블록이라고 볼 수 있다. 따라서 이클립스 플랫폼 런타임은 워크벤치 내에서 플러그인의 라이프사이클을 관리하는 책임을 지닌다. 이클립스 RCP를 이용하여 개발한 리치 클라이언트 애플리케이션에서 사용하는 모든 플러그인들은 애플리케이션의 디렉토리 구조 내에 있는 plugin 폴더 내에 존재한다. 리치 클라이언트 애플리케이션의 실행 시에 이클립스 런타임은 모든 사용가능한 플러그인을 찾아내고 전역 플러그인 레지스트리를 생성하기 위해 이 정보를 사용한다.

따라서 이클립스 RCP를 이용하여 리치 클라이언트 애플리케이션을 개발하고자 한다면, 메인 프로그램으로서 이클립스 RCP에서 실행될 수 있는 플러그인을 생성해야 한다. 이클립스는 플러그인 개발을 쉽게 할 수 있도록 플러그인 개발환경(Plug-in Development Environment, PDE)도 제공하고 있다. 이클립스 PDE에는 플러그인 개발 과정을 단순화시켜주는 다양한 위자드(Wizard)와 에디터(Editor)들이 포함되어 있다. 따라서 이클립스 SDK에 기본적으로 내장된 PDE를 사용하면 이클립스 RCP를 이용하는 리치 클라이언트 애플리케이션을 더 쉽고 빠르게 개발할 수 있다. <화면 2>는 이클립스 PDE의 실행 화면이다. 이클립스 RCP나 PDE를 이용해 리치 클라이언트 애플리케이션을 개발하는 방법은 이 글이 의도하는 바가 아니라 여기서는 다루지 않기로 한다.


<화면 2> 이클립스 PDE

이클립스 RCP를 이용한 리치 클라이언트 애플리케이션의 개발에 대해서는 마소가 향후에 별도 지면을 통해 연재해주길 기대한다.



플러그인 vs. 제품

이클립스 RCP를 이용해 리치 클라이언트를 개발하고 배포하는 방법에는 두 가지가 있다. 앞에서 이미 언급했듯이 이클립스 RCP는 플러그인 아키텍처에 기반하고 있다. 따라서 이클립스 RCP를 이용해 개발한 리치 클라이언트 애플리케이션은 기본적으로 이클립스의 플러그인처럼 실행된다. 소프트웨어 라이프사이클을 위한 다양한 툴을 개발해 판매하는 것이 목적이라면 이클립스 내에서 플러그인 형태로 실행되더라도 아무런 문제가 없을 것이다. 그러나 회사에 따라서는 서버를 모니터링 하는 리치 클라이언트(윈도우 프로그램)를 만들어 상용화해야 하거나, 또는 콜센터나 일정관리의 경우처럼 이클립스 없이 실행되는 독립(stand-alone) 애플리케이션의 형태로 배포해야 할 수도 있다. 그럼 이런 경우에는 어떻게 해야 할까?

이런 문제에 대비해 이클립스 RCP를 이용해 개발한 리치 클라이언트 애플리케이션은 플러그인이나 독립 애플리케이션(product) 형태로 만들어져 배포될 수 있도록 하고 있다. 이런 두 가지 형태로 배포되는 대표적인 프로그램으로 Adobe사의 ‘플렉스 빌더’를 꼽을 수 있다. <화면 3>과 <화면 4>에서 차례로 소개되는 실행 화면은 각각 플렉스 빌더를 이클립스 플러그인 형태로 설치해 실행한 경우와 독립(stand-alone) 애플리케이션으로 설치해 실행한 경우이다.


<화면 3> 이클립스 플러그인으로 실행한 플레스 빌더


<화면 4> 독립 애플리케이션으로 실행한 플렉스 빌더

< 화면 3>과 <화면 4>가 보여주고 있는 플렉스 빌더의 실행 화면은 서로 별반 다를 게 없어 보인다. 그도 그럴 것이 두 가지 모두 이클립스 RCP를 이용해 개발되었고, 또한 이클립스 RCP의 런타임 환경에서 실행되기 때문이다. 그렇다면 대체 어떤 차이가 있는 것일까? 그렇다. 바로 플러그인(plug-in)과 제품(product)의 차이인 것이다. 지금까지 필자가 독립 애플리케이션(또는 stand-alone 애플리케이션)이라고 부르던 것을 이클립스 RCP에서는 제품이라고 부르고 있는 것이다. 이는 이클립스 RCP 입장에서 볼 때 이클립스 IDE 내에서 실행되는 플러그인이 아니라, 이클립스 IDE와는 별개로 배포되는 제품이라고 생각했기 때문일 것이다. 결국 이 두 화면이 보여주고 있는 것처럼 플러그인이나 제품의 형태로 실행되더라도 모두가 이클립스 RCP 상에서 실행되므로 그 외관상에는 큰 차이가 없다.

그러나 동작의 측면에서 볼 때 아주 큰 차이점이 있다. 플러그인은 이클립스 IDE 내에서 실행되므로 이클립스 IDE의 모든 기능과 퍼스펙티브를 사용할 수 있는 반면에, 제품 형태로 실행되는 경우에는 이클립스 RCP에서 정의하고 있는 기능만 쓸 수 있을 뿐 이클립스의 IDE적인 기능들은 사용할 수 없다는 점이다. 따라서 전체 화면 구성에서 볼 때 둘 모두가 플렉스 퍼스펙티브를 사용하고 있어 거의 동일하지만, 두 개의 실행화면을 자세히 살펴보면 메뉴, 툴바, 사용 가능한 퍼스펙티브 등의 유무에서 다소 차이가 있음을 알 수 있다.

이렇 듯, 이클립스는 이클립스 RCP를 이용해 개발한 리치 클라이언트 애플리케이션을 독립 실행이 가능한 프로그램 형태로 배포할 수 있는 제품(product)의 개념을 지원한다. 이클립스 RCP를 이용해 생성된 제품은 자신이 필요로 하는 다른 플러그인들을 모두 포함하고, 애플리케이션을 실행하기 위한 네이티브 실행 파일(launcher program)과 환경구성 파일(config files) 등을 생성해 포함한다. 또한 실행 윈도우의 타이틀과 여러 윈도우 및 데스크탑 아이콘, 프로그램 시작 화면(Splash Screen), 프로그램 실행 파일의 이름, 어바웃 다이얼로그 정보 등을 변경할 수 있는데, 이를 브랜딩이라 한다. 이렇게 생성한 제품은 InstallShield, NSIS, 인스톨 팩토리 등과 같은 유틸리티 프로그램을 써서 인스톨 프로그램으로 만들고 배포할 수 있다.



이클립스 SWT와 JFace

이클립스 SWT(Standard Widget Toolkit)는 JFace와 함께 다중 플랫폼에서 네이티브 리치 클라이언트 애플리케이션을 개발하기 위한 이클립스의 대표적인 컴포넌트로 일종의 라이브러리이다. 또한 이는 이클립스 RCP의 풍부한 사용자 경험을 제공하기 위한 핵심 컴포넌트라고 할 수 있다. 비록, SWT와 JFace가 같은 목적을 지녔다고는 하지만, 이 두 가지 기술은 사용자 인터페이스를 생성하는 데 있어서 다음과 같이 다른 철학을 가졌다.

먼저 SWT는 시스템 내부 자원에 대한 풍부한 제어와 액세스가 가능하도록 지원하지만, 개발자가 모든 고급 UI 작업을 해주어야 하므로 사용하기가 복잡하다. 반면에 JFace는 모든 GUI 작업을 자동으로 하지만, JFace에서 제공되는 GUI 컴포넌트를 사용해야 하므로 유연성(flexibility)이 부족하다고 할 수 있다.

즉, 이클립스 SWT에서는 GUI 프로그래밍을 위한 풍부한 컴포넌트들을 제공하고, JFace에서는 SWT에서 제공하는 기본 컴포넌트들을 조합해 복잡한 형태의 GUI(즉 마법사 형태의 컴포넌트)를 제공하므로, 두 가지 기술을 적절히 조합해 사용하는 것이 바람직하다(이클립스 SWT에 대한 보다 자세한 내용은 필자가 2006년 9월부터 12월까지 마소에 기고한 이클립스 SWT 관련 연재를 참고하길 바란다).

이클립스 SWT를 이용한 GUI 개발

모든 운영체제는 기본적인 사용자 인터페이스를 개발할 수 있는 수많은 그래픽 컴포넌트를 제공한다. 이러한 그래픽 컴포넌트에는 버튼, 윈도우, 메뉴를 비롯해 모니터 스크린에 나타나는 다른 모든 그래픽 컴포넌트들까지 포함된다.

SWT 의 목적은 자바 프로그래머가 이러한 컴포넌트를 직접 액세스할 수 있게 함으로써 그러한 컴포넌트들을 원하는 대로 구성 및 위치시키고, 이를 통해 GUI 화면을 개발할 수 있도록 하는 것이다. 이클립스 SWT를 이용해 애플리케이션에 SWT 버튼 객체를 사용하는 프로그램을 작성했다면, MS 윈도우 환경에서 실행될 때는 윈도우의 버튼처럼, 매킨토시에서는 매킨토시의 그것처럼 나타난다. 마찬가지로 리눅스나 유닉스 등에서도 그 각각의 버튼처럼 나타나고 동작한다. 이러한 그래픽 컴포넌트와 함께 SWT는 이벤트에 대한 액세스도 제공한다. 이는 사용자가 어떤 버튼을 클릭했고, 어떤 메뉴 아이템이 선택되었는지를 알 수 있게 한다(모든 형태의 사용자 입력 처리 가능). 마지막으로 애플리케이션에서 그래픽스 작업을 해야 하는 경우를 살펴보자. 이 경우에는 SWT가 이미지 생성과 폰트 이용, 그리고 다양한 도형 표현을 위한 방대한 그래픽 툴셋을 제공한다. 이러한 기능은 새로운 그래픽 작업이 가능하도록 할 뿐만 아니라 GUI 내에 언제, 어느 곳에, 어떻게 디스플레이할 지를 제어하도록 지원한다.

이처럼 SWT는 사용자 인터페이스를 생성하는 데 필요한 풍부한 기능들을 제공하지만, 모든 GUI 작업을 개발자가 직접 해야 하므로 프로그램이 길고 복잡해질 수 있다. 이런 까닭에 이클립스는 GUI 개발을 지원하는 복잡한 컴포넌트들을 만들어 JFace라는 이름의 라이브러리로 제공하게 되었다.



이클리스 JFace를 이용한 GUI 개발 단순화

동일한 SWT 코드를 반복 작성하는 수고를 줄여 이를 보다 간편화할 수 있도록 등장한 것이 바로 JFace이다. JFace는 SWT만을 이용해 작업할 때 많은 시간을 소모할 수 있는 다수의 작업들을 간단히 수행할 수 있도록 돕는다. 그러나 JFace가 단순히 SWT를 대체하는 것은 아니다. 대부분의 GUI 프로그램은 SWT와 JFace 두 가지 기능을 적절히 사용해야만 보다 나은 효과를 얻을 수 있다.

JFace의 중요한 예로 이벤트를 꼽을 수 있다. 일반적인 사용자 인터페이스에서는 버튼을 클릭했거나, 키를 눌렀거나, 혹은 메뉴가 선택되는 등의 서로 다른 이벤트를 받을 수 있고, 이 이벤트는 모두 똑같은 기능을 수행해야 한다. 이런 과정을 수행하는 데도 SWT와 JFace 간에는 차이가 있다. 먼저 SWT에서 각 이벤트는 별도로 받아 처리해야 한다. 그러나 JFace는 이 이벤트들을 하나의 객체로 조합해 그 이벤트를 발생시킨 컴포넌트보다 이벤트에 대해 어떤 응답을 할지 자체에 더 많은 관심을 가지도록 해준다. 이런 간단하면서도 강력한 개념 덕분으로 GUI 프로그램에 많은 양의 코드를 작성하지 않더라도 컨텍스트 메뉴, 툴바, 그리고 팔레트 등을 추가할 수 있는 것이다.

JFace 는 이뿐만 아니라 다중 윈도우나 그래픽스를 필요로 하는 방대한 GUI 프로그램을 개발하는 데도 매우 유용하다. SWT 컴포넌트들을 조직하고 컴포넌트들이 사용하는 메모리 할당(allocation)을 도와주는 레지스트리 클래스들도 제공한다. 예를 들면 SWT에서 애플리케이션이 사용할 많은 폰트와 이미지를 생성 및 해제(deallocate)하는 경우가 이에 해당한다. JFace를 이용하면 이런 지루한 작업을 대신하기 위해 빌트인 FontRegistry나 ImageRegistry 객체를 사용할 수 있다.



참고 자료
1. http://udig.refractions.net/confluence/display/UDIG/Home, 이클립스 RCP UDIG 프로젝트
2. http://www.foreflight.com/, 이클립스 RCP ForeFlight 프로젝트
3. http://www.eclipse.org/community/casestudies/RSSfinal.pdf, 이클립스 RCP RSS Solutions 프로젝트
4. http://www.eclipse.org/community/ casestudies/NASAfinal.pdf, 이클립스 RCP Maestro (NASA Space Mission Management)
5. http://www.zdnet.co.kr/builder/dev/java/0,39031622,39139542,00.htm, EJB와 리치 클라이언트 구축
6. http://www.eclipse.org/articles/Article-RCP-1/tutorial1.html, Rich Client Tutorial Part 1
7. http://wiki.eclipse.org/index.php/Rich_Client_Platform, Eclipse Rich Client Platform



제공 : DB포탈사이트 DBguide.net


'Dev > eclipse' 카테고리의 다른 글

이클립스 단축키  (0) 2011.04.30
이클립스 자동 가비지 컬렉션 플러그인  (0) 2011.04.30
이클립스 속도 올리기  (0) 2011.04.29
Boost Eclipse speed performances  (1) 2011.04.29

1. 데몬(daemon) 이란?
주기적인 서비스 요청을 처리하기 위해서 커널상에 백그라운드 모드로 실행되는 프로세스로, 메모리 관리 방법에 따라 단독 데몬과 xinetd로 분리된다.

  • 단독데몬
    항상 백그라운드 모드로 실행되고 메모리를 상대적으로 많이 소비한다. 그러나 서비스(응답속도)가 빠르다. httpd와 같은 웹서비스 데몬이 대표적.
  • xinetd(슈퍼데몬)
    요청이 있을때마다 xinetd가 서비스를 싱행시켜주므로 메모리 소비가 적다. 그러나 단독데몬에 비해 상대적으로 서비스 속도가 느리다.


2. 간단한 자바 데몬 만들기
nohup을 이용해서 java를 실행시킨다.

터미널이 종료될 때(쉘이 종료될 때) 프로세스가 죽는 이유는 해당 프로세스가 쉘의 자식 프로세스 이기 때문이다. 따라서, 부모 프로세스가 죽을대 던지는 SIGHUP을 자식 프로세스가 받게 된다.

nohup은 부모 프로세스가 죽을때 자식 프로세스에게 SIGHUP을 던지지 않는 프로세스를 말한다.

$ nohup java 클래스명 & 
 
사용하기 편한 장점은 있으나, 문제는 중지를 시킬수 없다. 즉, 해당 프로세스 ID를 찾아내 kill하는 수 밖에 없다. 물론 파일 체크 기법, 소켓을 이용한 제어 방법등을 사용할 수 있지만 스스로 구현해야 하는 번거로움이 있다.


3. apache commons daemon 이용하기
Java는 UNIX의 시그널 처리를 할수 없기때문에, 중지 신호(TERM signal)를 처리하기 위해서는 따로 구현을 해야한다. 이런 번거로움을 해결하기 위해 자카르타의 하위 프로젝트중의 commons daemon을 이용한다. commons daemon은 중지 신호를 받으면 미리 지정된 메소드를 실행한다.


UNIX용 Jsvc와 윈도우용 Procrun 있다.
여기서는 Jsvc를 이용해보도록 하겠다.


commons daemon을 다운로드해 압축을 해제하면 위 그림과 같다.
commons-daemon.jar 파일은 Java프로젝트 lib폴더에 복사해둔다.


bin폴더의 압축파일을 해제하면 jsvc-src라는 폴더가 나온다.
폴더의 내용은 위와 같다.
commons daemon을 사용하기 위해서는 바로 여기서 jsvc를 빌드해줘야 한다.
빌드환경은 다음과 같다.(리눅스에서 빌드해야한다.)

  • GNU AutoConf(at least 2.53)
  • ANSI-C compliant compiler(GCC is good)
  • GNU Make
  • A Java Platform 2 compliant SDK

여기서부터는 ubuntu 8.10 환경에서 진행하도록 한다.

먼저 JDK가 설치되어 있지 않다면 JDK를 설치한다.

 $ sudo apt-get install sun-java6-jdk

JDK가 설치되는 경로는 /usr/lib/jvm/java-6-sun 이다.

gcc 및 make 가 설치되있지 않다면 아래 명령를 이용해 한방에 설치한다.

 $ sudo apt-get install build-essential

AutoConf가 설치되있지 않다면 AutoConf를 설치한다.

 $ sudo apt-get install autoconf

Jsvc를 빌드하는 방법은 다음과 같다.

  • support/buildconf.sh
  • ./configure --with-java=/usr/lib/jvm/java-6-sun
  • make


빌드가 성공적으로 이루어졌다면 위 그림과 같이 jsvc가 만들어진것을 확인할 수 있다. 이후 이 파일을 가지고 Java 데몬을 실행한다.

Java 데몬을 만들려면 org.apache.commons.daemon.Daemon 인터페이스의 init, start, stop, destory 메소드를 구현해야 한다.

샘플 코드는 아래와 같다.
  1. import org.apache.commons.daemon.Daemon;   
  2. import org.apache.commons.daemon.DaemonContext;   
  3.   
  4. public class DaemonTest implements Daemon {   
  5.   
  6.     Thread t = null;   
  7.        
  8.     @Override  
  9.     public void init(DaemonContext arg0) throws Exception {   
  10.         System.err.println("daemonTest init");   
  11.         t = new DaemonThread();   
  12.     }   
  13.   
  14.     @Override  
  15.     public void start() throws Exception {   
  16.         System.err.println("daemonTest start");   
  17.         t.start();   
  18.     }   
  19.   
  20.     @Override  
  21.     public void stop() throws Exception {   
  22.         System.err.println("daemonTest stop");   
  23.         t.interrupt();   
  24.     }   
  25.        
  26.     @Override  
  27.     public void destroy() {   
  28.         System.err.println("daemonTest destroy");   
  29.     }   
  30. }  
  1. public class DaemonThread extends Thread {   
  2.   
  3.     @Override  
  4.     public void run() {   
  5.         while( Thread.interrupted() ) {   
  6.             System.err.println("run");   
  7.             try {   
  8.                 Thread.sleep(5000);   
  9.             } catch (InterruptedException e) {   
  10.                 Thread.currentThread().interrupt();   
  11.                 return;   
  12.             }   
  13.         }   
  14.     }      
  15. }  
init에서 초기화, start에서 처리할 작업을 별도의 쓰레드로 생성해서 실행한다. start 메소드 호출후 반드시 return 되어야 데몬이 시그널 처리를 제대로 할 수 있다.
stop, destroy 는 중지 신호가 오면 차례되로 호출된다.

이제 실행/중지 스크립트를 작성한다.
Jsvc를 싱행하는데 필요한것은 실행하는 계정(user), JAVA_HOME(-home), 프로세스ID 파일(-pidfile), 출력지정(-outfile, -errfile), 클래스파일(-cp) 등이 있다.
  1. #!/bin/sh   
  2. JAVA_HOME=/usr/lib/jvm/java-6-sun   
  3. DAEMON_HOME=/home/lyb1495/commons-daemon-1.0.1/bin/jsvc-src   
  4. DAEMON_USER=lyb1495   
  5. DAEMONTEST_HOME=/home/lyb1495/workspace/Daemon   
  6. PID_FILE=$DAEMONTEST_HOME/daemon_test.pid   
  7.   
  8. CLASSPATH=\   
  9. $DAEMONTEST_HOME/bin:\   
  10. $DAEMONTEST_HOME/lib/commons-daemon.jar   
  11.   
  12. case "$1" in   
  13.   
  14.     start)   
  15.     #   
  16.     # Start Daemon   
  17.     #   
  18.     $DAEMON_HOME/jsvc \   
  19.     -user $DAEMON_USER \   
  20.     -home $JAVA_HOME \   
  21.     -wait 10 \   
  22.     -pidfile $PID_FILE \   
  23. -errfile '&1' \   
  24.     -cp $CLASSPATH \   
  25.     DaemonTest   
  26.     #   
  27.     # To get a verbose JVM   
  28.     #-verbose \   
  29.     # To get a debug of jsvc.   
  30.     #-debug \   
  31.     exit $?   
  32.     ;;   
  33.   
  34.   stop)   
  35.     #   
  36.     # Stop PostMan   
  37.     #   
  38.     $DAEMON_HOME/jsvc \   
  39.     -stop \   
  40.     -pidfile $PID_FILE \   
  41.     DaemonTest   
  42.     exit $?   
  43.     ;;   
  44. #   
  45.   *)   
  46.     echo "Usage DaemonTest.sh start/stop"  
  47.     exit 1;;   
  48. esac  
이제 Java 데몬을 실행하기 위한 모든 준비를 마쳤다.


위에서 작성한 스크립트 파일을 이용해 Java 데몬을 실핸한다.
정상적으로 데몬이 시작된다면 PID_FILE에 지정한 파일이 생성된것을 확인할 수 있다.


데몬을 중지하려면 start 대신 stop을 입력으로 스크립트를 실행하면 된다.

'Dev > JAVA' 카테고리의 다른 글

Spring framework 설치  (0) 2012.11.25
Java Simple Daemon  (0) 2011.06.05
자바 이미지 사이즈 추출 예제  (0) 2011.04.23
자바 이미지 리사이즈(썸네일)  (0) 2011.04.06

 글쓴이 : 푸우     날짜 : 08-03-01 19:09     조회 : 486    


요즘 통 글을 안 올렸네요.
요즘 회사일로 정신이 없네요.
죄송하구요. ^^; 
아무튼 이번에는 UNIX/LINUX의 Deamon 프로세스 만드는 것에 대해 정리해 보도록 하겠습니다.
 
1. Daemon이란?
 
뭐 이글을 관심이 있어서 읽고 계시는 분 중에 Daemon이 뭔지를 모르시는 분은 없겠지만...
그래도 한번 정리해 봅니다.
Daemon의 뜻 자체는 "악마", "악령", "귀신"과 같이 좀 무시무시 합니다.
하지만 Daemon의 어원은 신화에서 신과 인간사이의 초자연적인 영적존재로서의 수호신에서 나왔다고 합니다. 뭐 좋은쪽으로 생각하죠.
컴퓨터 상에서 Daemon은 겉으로는 보이지 않지만 항상 뒤에서 뭔가를 계속해서 수행해 주는 프로그램을 말합니다. 예로 httpd, ftpd, telnetd 등...
이런면에서 왜 Daemon이라는 이름 지었는지 이해가 가네요.
윈도우즈에서는 Daemon이라는 용어가 마음에 들지 않았는지 아니면 뭔가 꼭 튀게 해야한다고 생각했는지 몰라도... UNIX/LINUX의 Daemon과 같은 역할을 하는 프로그램을 Service 라고 합니다.
"데몬"은 이름에 뭔가 철학을 담아 지은것 같은데... "서비스"... 참 단순해서 좋긴 좋네요. ㅋㅋ  ^^;
 
Daemon 프로그램이 되려면 몇가지 특징을 갖추어야 하지만 가장 중요한 것은 "OS가 부팅되면 사용자의 개입없이 실행될 수 있을 것"입니다.
이 말은 다시 말해 누군가가 로그인을 하여 굳이 실행하지 않아도 OS의 init프로세스에 의해 실행될 수 있어야 한다는 이야기 이고 이럴려면 프로그램을 제어하는 터미널(tty)를 갖지 말아야 한다는 것 입니다. 반대로 말하면 누군가 로그인 해서 데몬을 시작시켰다면 로그아웃할 때 해당 데몬은 계속해서 살아 있어야 한다는 말도 됩니다.
 
이제 이러한 특징을 갖는 프로그램을 만드는 방법을 단계별로 알아보도록 하죠.
 
 
2. 로그 아웃시 프로그램 종료되지 않게 하기
 
우선 데몬은 아니더라도 데몬과 비슷하게 수행하면 계속해서 작업을 수행 할 수 있도록 하는 방법에 대해서 알아 보겠습니다.
 
기본 소스는 다음과 같습니다.
 
#include <stdio.h>

int work()
{
        FILE *fp;
        unsigned long count=0;

        fp=fopen("/tmp/daemon.out""wt");
        if(fp==NULL) {
                printf("File Open Error\n");
                return -1;
        }

        while(1){
                sleep(1);
                fprintf(fp, "%lu\n", count);
                fflush(fp);
                fprintf(stdout, "%lu\n", count++);
        }
        fclose(fp);

        return 0;
}

int main(int argc, char *argv[])
{
        work();

        return 0;
}
 
 
위의 프로그램은 간단하니깐 보시면 아시겠지만 main()함수가 수행되면 work()라는 함수를 수행하게 됩니다.
work()함수는 무한루프를 돌면서 1초마다 "/tmp/daemon.out"파일에 카운팅을 기록합니다.
또 표준출력으로도 같은 값을 출력하는 프로그램입니다.
위의 소스를 컴파일하여 daemon이라는 실행파일을 생성했다고 하고 다음의 글들을 작성토록 하겠습니다.
 
자 그럼 로그아웃이 될때 어떤 현상이 일어날지에 대해 한번 알아보도록 하죠.
 
로그아웃하거나 해당 터미널이 끊기게 되면 OS 해당 터미널을 제어단말로 사용하던 모든 프로그램에게 특별한 신호를 보내게 됩니다.
이 신호가 SIGHUP, 즉, Hangup 시그널입니다.
일반적인 프로그램은 이 Hangup 시그널을 받으면 정상종료를 하게 됩니다.
뭐 프로세스가 알아서 정상종료 한다기 보다는 이 시그널을 어떻게 처리하라는 루틴을 구현하지 않은 프로세스는 조용히 OS가 죽이는 것이죠.
 
OS에 SIGHUP을 무시하게 해주는 'nohup'이라는 쉘명령어가 있습니다.
일반적인 사용방법은 다음과 같습니다.
 
#nohup daemon&amp;amp;
 
위의 명령어 중 daemon은 데몬 프로그램의 이름이고 마지막 &는 이 글의 주제와는 상관없지만 백그라운드로 실행해서 프롬프트를 다시 얻기 위함입니다.
이렇게 daemon을 실행한 뒤 로그아웃하거나 단말기를 끊고 다시 접속한 다음 "ps -ef | grep daemon" 명령을 통해 보시면 daemon이 살아 있음을 보실 수 있으실 것 입니다.
또한 "tail -f /tmp/daemon.out" 해서 보시면 살아 있을 뿐 더러 원래의 기능도 정상적으로 수행 하고 있음을 알 수 있습니다.
 
그런데 여기서 원래 daemon은 파일 뿐만 아니라 화면에도 카운팅 번호를 출력하도록 되어 있었는데 위의 명령의 수행 후에는 아래와 같은 메시지만 남기고 나타나지 않습니다.
 
appending output to `nohup.out'<br />
 
이는 nohup 명령어가 daemon의 표준 출력을 현재 경로의 'nohup.out'이라는 파일로 리다이렉트 시켰기 때문입니다.
 
어찌되었건 쉘에서 제공하는 명령어를 통해 데몬과 유사한 기능을 하도록 해 보았네요. 
 
자 이번에는 'nohup'과 같은 기능을 시스템 함수로 구현해 보도록 하겠습니다.
 
골자는 프로그램 내에서 SIGHUP시그널을 무시할 수 있도록 코딩하는 것입니다.
 
 위 함수에 #include <signal.h> 를 포함하고 main함수를 다음과 같이 고칩니다.
 
 
int main(int argc, char *argv[])
{
        signal(SIGHUP, SIG_IGN);

        work();

        return 0;
}
 
 
지금 부터는 work()함수의 표준 출력이 귀찮아 지므로 표준 출력으로 내보는 문장은 적절히 삭제하셔도 좋습니다.  
위의 main()함수는 signal()함수를 통해 SIGHUP시그널을 무시하도록 설정 한 뒤 work()함수를 수행한 것입니다.
 
컴파일 후 테스트는 여러분의 몫으로 하구요.
 
SIGHUP을 무시하는 방법은 아니지만 또 다른 방법이 있는데 이는 프로세스 내에서 자신의 그룹아이디를 바꾸는 것입니다.
 
이는 로그 아웃시 현재 터미널을 제어 단말기로 하고 있는 프로세스에 OS가 SIGHUP시그널을 보낼 때 쉘프로세스의 아이디를 그룹아이디로 갖는 모든 프로세스에게 보낸다는 것에 착안한 것입니다.
 
즉, 프로그램은 특정 쉘프로세스에서 수행되기 때문에 일반적으로 프로세스 그룹아이디는 수행 환경이 되었던 쉘프로세스 아이디를 갖게 되는데 이를 프로세스 내에게 바꾸는 것입니다. 
 
int main(int argc, char *argv[])
{
        int newpgid;

        newpgid = setpgrp();

        work();

        return 0;
}
 
위의 소스에 보이는 setpgrp()함수는 자신의 프로세스 그룹을 자신의 프로세스아이디로 바꿔줍니다.
이렇게 하면 로그아웃시 이 프로세스는 아예 SIGHUP시그널을 받지도 않으므로 종료되지 않습니다.
 
자 여기까지는 데몬 프로그램을 만드는 법이라기 보다는 일반적으로 프로세스에서 일어나는 일과 이들을 처리하는 방법, 그리고 간단히 데몬과 유사한 효과 내기라고 생각하시면 될 듯 싶습니다.
 
 
3. fork에 의한 Daemon만들기
 
 
위에서 이야기 한 방법들도 뭐 잘못된 방법들은 아니지만 일반적으로는 이제부터 이야기 하는 방법으로 주로 데몬을 만듭니다.
main()함수를 다음과 같이 바꾸세요.
 
int main(int argc, char *argv[])
{
        int pid;

        pid=fork();
        switch(pid){
        case -1:
                fprintf(stderr, "Fork Error\n");
                break;
        case 0:         // child
                break;
        default:        // parent
                return 0;
        }

        work();

        return 0;
}
 
위의 main()함수는 프로그램이 시작되자 마자 fork()를 수행해서 자식프로세스는 work()함수를 수행하고 부모프로세스는 종료하게 합니다.
그러면 사실 자식프로세스는 아직 살아 있는데 부모프로세스가 터미널과 연결되어 있었는데 사라지게 됩니다. 고로 자식 프로세스는 제어단말기를 가지지 않게 되고 부모가 없으므로 고아프로세스가 되었네요.
이렇게 고아프로세스가 발생하면 OS는 이 고아 프로세스를 init프로세스의 자식으로 만들어 줍니다.
부모,자식,고아를 이야기 했으니 입양이라고 해야 할까요? ㅋㅋㅋ
하지만 원래 init프로세스는 1번 프로세스로서 조상 프로세스에 해당됨으로 입양이라고 하기는 좀 그렇네요.
 
아무튼 위의 프로그램은 아무 문제가 없습니다. 좀비가 발생하지도 않구요.
 
하지만 이런 케이스를 생각해 보죠.
daemon도 프로세스이므로 현재 작업디렉토리를 갖습니다.
그래서 이 daemon이 실행된 후 daemon을 실행 시켰던 디렉토리가 속한 파일시스템을 "umount" 시키려 한다면 OS는 해당 파일시스템이 사용되고 있다고 umount 하지 못한다고 할 것 입니다.
이런 경우 해당 데몬을 내려야 하는데...
더욱 나쁜 경우는 어떤 데몬이 이 파일시스템을 사용하고 있는지를 알기가 힘들다는 것이죠.
그래서 데몬은 데몬이 되기 전에 현재 경로를 "/'와 같이 "umount"될 수 없는 곳으로 옮기는 것이 좋습니다.
 
또 한가지 고려해야 할 것은 현재 daemon은 뭐 파일에 숫자만 적고 있지만 여러분이 만드는 deamon은 어떤 일을 하게 될 지 사실 모릅니다.
예를 들어 daemon이 또 fork를 해서 자식 프로세스들을 갖을 수도 있고 exec()함수나 system()함수에 의해 아예 다른 프로그램을 실행 시킬 수도 있을 것입니다.
이때 새로 생성되는 프로세스를 현재 daemon이 제어 할 수 없다면 이 또한 문제일 수 있겠죠?
그래서 데몬프로세스는 데몬이 된 직 후 자신이 속한 프로세스 그룹의 리더가 아니라면 새로운 프로세스 세션을 만들고 자신이 리더가 된 후 다음 작업들을 하게 됩니다.  이러한 기능을 하는 함수가 바로 setsid()라는 함수 입니다. (참조: http://teamblog.joinc.co.kr/yundream/226)
 아래 소스는 chdir()과 setsid()함수를 호출하여 위에서 이야기한 내용을 반영하고 있습니다.
 
int main(int argc, char *argv[])
{
        int pid;

        pid=fork();
        switch(pid){
        case -1:
                fprintf(stderr, "Fork Error\n");
                break;
        case 0:         // child
                break;
        default:        // parent
                return 0;
        }

        chdir("/");
        setsid();

        work();

        return 0;
}
 
 
자 여기까지 하면 뭐 데몬이 잘 만들어 진 것 입니다.
 
추가로 실제 데몬을 프로그래밍 하실 때 주의하실 점은 시그널에 대한 처리를 꼭 하시라는 것 입니다.
위의 샘플 소스는 뭐 특별하게 시그널 처리를 할께 딱히 없지만 예를 들어 웹서버와 같이 통신 프로그램인 경우 SIG_PIPE와 같은 시그널을 처리해야 합니다.
 
 
4. 오뚜기 같은 Daemon만들기
 
마지막으로 이야기 하는 것은 일반적인 데몬에 대한 이야기는 아니구요 팁같은 거라고 할까요?
데몬은 서비스 제공하는 프로세스이다 보니깐...
시스템이 시작되면 시작되서 끝날때 까지 살아 있어야 하는데...
우리가 짜는 프로그램이 문제가 없다고 장담할 수는 없겠죠? (저만 그런가?)
 
만약 데몬으로 띄운 프로그램이 죽으면 타격이 크죠.
뭐 그 타격은 타격이더라도 세션이나 트랜잭션이 중요한 서비스가 아닌 경우라면 빨리 다시 시작이라도 시켜서 정상화를 시키고 싶은 경우가 있습니다.
 
이런 경우에 다음과 같은 방법으로 할 수 있을 것입니다.
먼저 일반 데몬을 하나 만들기 위해 일반 fork하시구요.
그리고 다시 fork를 해서 자식프로세스는 서비스를 수행하구요 부모프로세스는 자식이 종료되기를 기다립니다.
그런데 자식프로세스는 무한루프일 거니깐...
부모프로세스 입장에서 자식이 종료되었다면 실제로는 뭔가 문제가 생긴 것이겠죠?
이때 다시 fork를 해서 위의 과정을 반복합니다.
 
소스는 다음과 같습니다.  
 
int main(int argc, char *argv[])
{
        int pid;
        int ret;

        if (( pid = fork()) < 0) {
                fprintf(stderr, "Main Fork Error\n");
                return 0;
        } else if(pid > 0) {
                return 0;
        }

        chdir("/");
        setsid();

        while(1) {
                if (( pid = fork()) < 0) {
                        fprintf(stderr, "Sub Fork Error\n");
                        return 0;
                } else if(pid == 0) {
                        break;
                } else if(pid > 0) {
                        wait(&ret);
                }
        }

        work();

        return 0;
}
 
 
만약 자식이 종료될때 상태를 알고자 한다면 wait()함수의 인자인 ret값을 조사하면 됩니다.
 
자 이렇게 해서 UNIX/LINUX에서 Daemon만들기에 대한 이야기를 마치도록 하겠습니다. 


출처 : http://honeybox.tistory.com/
HTML 코드를 작성할때 5배 속도 내는 코딩 비법, zen coding을 아시나요?

zen coding은 여러분들이 흔히 쓰고 계신 에디터 프로그램의 플러그인으로서 동작하며

CSS선택자 같은 입력방식으로 HTML코딩을 산출해냅니다.

백문이 불여일견!

일단 코드를 살펴보실까요?


div#page>div.logo+ul#navigation>li*5>a



마치 CSS선택자 같은 이 친숙한 코드 한줄이 zen coding을 만나면


<div id="page">
        <div class="logo"></div>
        <ul id="navigation">
                <li><a href=""></a></li>
                <li><a href=""></a></li>
                <li><a href=""></a></li>
                <li><a href=""></a></li>
                <li><a href=""></a></li>
        </ul>
</div>





이렇게 변합니다.

굉장히 다양한 입력방식이 있으니 더 자세히 보고 싶으신 분들은 동영상을 보세요.


Zen Coding v0.5 from Sergey Chikuyonok on Vimeo.



또 다른 동영상 보기



zen coding은 공식지원되는 에디터와 third party 개발자들에 의해 만들어진 플러그인까지 포함하여

  • Aptana/Eclipse
  • TextMate
  • Coda
  • Espresso
  • Komodo Edit/IDE
  • Notepad++
  • PSPad
  • Dreamweaver
  • Sublime Text
  • UltraEdit
  • TopStyle
  • GEdit
  • BBEdit/TextWrangler
  • Visual Studio
  • EmEditor
  • Sakura Editor



이만큼이나 되는 에디터 들을 지원합니다.

저는 주로 windows 환경에서 editplus를 사용하는데 아쉽게 editplus용은 없네요.


아무튼 너무나도 멋진 플러그인임에는 틀림이 없네요!!

출처 : http://v.daum.net/link/10578136


실전 HTML5 가이드

book

본 가이드는 웹 개발자들이 HTML5의 기본 개념을 배우고 모바일 웹에서 실질적으로 사용할 수 있도록 하기 위해 만들어졌습니다. 한국 웹 표준 커뮤니티가 주최한 HTML5 오픈 콘퍼런스의 다섯명의 발표자들이 각자 자원 봉사로 작성하였습니다.

총 5장으로 구성 되어 있으며, HTML5 소개 및 마크업, CSS3, HTML5 APIs 및 iPhone에서의 웹 앱 개발이 포함되어 있습니다.

A4크기로 총 170페이지이고, B5크기로 206페이지입니다. 우선 A4에서 출력가능한 PDF 파일을 배포합니다. 많은 이용 바랍니다.

실전 HTML5 가이드 다운로드(A4 인쇄용) | B5 인쇄용

별도의 고지가 없는 한, 본 사이트의 모든 콘텐츠는
Creative Commons Attribution Share-Alike License v3.0 라이센스 하에서 이용 가능합니다.

CC nc nd

동영상 강의 목록

저자들이 한 강의 동영상을 다운로드해서 보실 수 있습니다.

웹 표준의 전반적인 것에 대해서는 웹 표준화 프로젝트에서 2005년에 실전 웹 표준 가이드를 참고하세요.

History of this Guidebook

This guide was written for web developers to learn basic concepts of HTML5 and to adopt it into the mobile web application in direct. It was made by five speakers of the first HTML5 Open Conference in Korea organized by Web Standards Korea and several communities.

This book was published as a PDF format and except where otherwise noted, content on this site is licensed under the Creative Commons Attribution Share-Alike License v3.0 or any later version.

It has five sections of introduction, markup of HTML5, CSS3, HTML5 APIs and the mobile application development in iPhone.

This book has 170 pages (A4 format) or 206 pages (B5-Letter format). Now, you can download A4 printable size Download HTML5 Guidebook


출처 : http://webstandards.or.kr/html5

'Dev > RIA' 카테고리의 다른 글

Canvas2Image: Canvas 데이터를 이미지로 저장  (0) 2011.06.10
[HTML5] Canvas  (0) 2011.06.10

PHP로 웹 서비스를 구현하다 보면 반복적인 작업에다 비효율적으로 파일들을 만드는 경우가 많죠. 이를 깔끔하게 해결할 수 있도록 해 주는 DAO클래스를 자동으로 생성해조는 오픈소스가 있네요.
현업에 얼마나 사용하실지는 모르겠으나 잘 응용하면 좋은 프레임워크로 만들 수 있을 것 같아 자료를 공유합니다.

1. PHPDAO 다운로드
 - wget http://www.phpdao.com/phpdao-1.7.zip
 - mkdir phpdao-1.7
 - cd phpdao-1.7
 - mv ../phpdao-1.7.zip .
 - unzip phpdao-1.7.zip .

2. Mysql 커넥션 설정
 - vi templates/class/dao/sql/ConnectionProperty.class.php 에서 아래 항목 기입

   private static $host = 'localhost';
   private static $user = 'mimuluser';
   private static $password = 'mimuladmin';
   private static $database = 'mimuldb';

3. DAO 클래스 생성하기
 - php generate.php
 - 현재 디렉토리에 generated 디렉토리가 생겨서 위에서 설정한 데이터 베이스의 테이블 단위로 dto, dao, mysql 디렉토리가 생성되어 클래스가 자동 생성됨

4. PHPDAO 아키테처
phpdao


출처 : http://www.mimul.com/pebble/default/2009/09/01/1251803100000.html

Java Simple Daemon

뭐에 쓰는 물건인고?

Java Simple Daemon은 자바를 이용해서 데몬 프로그램을 작성하도록 도와주는 프레임워크이다(비록 프레임워크라고 부르기엔 너무도 작지만.. ^^).
가끔씩 특정 디렉토리에 들어오는 파일을 감시해서 파일이 들어오면 그 파일을 DB에 넣기만 하거나, 혹은 어떤 방식으로든 메시지를 받아서 받은 메시지를 가공해 다른 쪽에 메시지로 넘겨 주거나 하는 등의 역할을 하는 그런 프로그램을 자바로 짤 경우가 있다.
이러한 프로그램들은 일반적으로 그래픽 사용자 인터페이스(GUI)나 화면 출력이 필요 없이 파일로 로그만 남기고 자기 할 작업을 한다. 이러한 프로그램을 데몬(Daemon)이라 부른다.
자바로 이러한 데몬을 작성할 경우, 데몬을 실행시키는 것은 문제가 없다.

$ nohup java some.Daemon &
이러한 식으로 실행하면 된다. 헌데 문제는 종료이다. 프로그램과 의사 소통할 무슨 방법이 없기 때문에 Unix에서 ps -ef명령으로 "java" 프로세스를 찾아서 kill명령을 내리거나 윈도우의 경우에는 프로세스 종료를 시킬 수 밖에 없다. 이러한 문제를 해결하기 위해 만든 것이 Java Simple Daemon(이하 JSD)이다.

프로젝트 홈

다운로드

Java Simple Daemon 다운로드 목록

어찌 작동하는고?

원리는 단순하다. 데몬 역할을 하는 자바 클래스를 실행시켜주면서 쓰레드가 하나 떠서 사용자의 홈 디렉토리(Unix에서 $HOME 환경변수가 가리키는 디렉토리)에 특별한 파일이 존재하는지를 검사한다. 만약 그 특별한 파일이 존재한다면 JVM을 자동으로 종료시켜준다. JSD는 그 특별한 파일이 있는지 검사하는 역할과, 그 파일을 생성 시켜주는 역할을 한다.

덤으로, JSD는 데몬 클래스에 따라 락(Lock) 파일도 생성해준다. 홈 디렉토리에 락 파일이 존재할 경우, 데몬을 다시 띄우려고 하면 데몬 띄우기를 거부한다.

사용하기

  • 데몬 클래스는 net.kldp.jsd.SimpleDaemon 인터페이스를 구현해야 한다.
    • public void startDaemon() : 데몬작업을 수행하는 메소드. 여기에 실제 작업 구현이 들어간다.
    • public void shutdown() : 데몬이 종료하기 전에 수행할 작업을 기록한다.
  • net.kldp.jsd.SimpleDaemonManager의 객체를 생성하고, 객체에 데몬 클래스를 등록해 준다.
  • net.kldp.jsd.SimpleDaemonManager.start()를 실행하면 데몬이 시작된다.
  • net.kldp.jsd.SimpleDaemonManager.shutdownDaemon()를 실행하면 데몬을 종료시키는 파일이 $HOME 디렉토리에 생성되어, 데몬을 종료시키게 된다.

예제 보기

아래 예제는 대책없이 화면에 시간을 출력하는 데몬이다.

  • 데몬의 시작 : java net.kldp.jsd.sample.ShowTime
  • 데몬의 종료(다른 콘솔 창에서) : java net.kldp.jsd.sample.ShowTime -shutdown
여기서 데몬을 종료하기 전에 다시 한번 데몬을 시작해보면 락 파일이 존재하기 때문에 실행을 거부하는 것을 볼 수 있다.
/*
 * Created on 2004. 11. 6.

 */
package net.kldp.jsd.sample;

import java.io.IOException;
import java.util.Date;

import net.kldp.jsd.IllegalSimpleDaemonClassException;
import net.kldp.jsd.SimpleDaemon;
import net.kldp.jsd.SimpleDaemonManager;

/**
 * SampleDaemon 예제.
 * 
 * 현재 시간을 계속해서 보여주는 데몬이다.
 * 
 */
public class ShowTime implements SimpleDaemon {

  public static void main(String args[]) {
    // -shutdown 옵션이 있을 경우 데몬을 종료시킨다.
    if (args.length > 0 && args[0].equals("-shutdown")) {
      System.out.println("ShowTime 종료시작.");
      
      try {

        SimpleDaemonManager sdm = SimpleDaemonManager.getInstance(ShowTime.class);
        sdm.shutdownDaemon();
      } catch (IOException e1) {
        e1.printStackTrace();
      } catch (IllegalSimpleDaemonClassException e) {
        e.printStackTrace();
      }
      return; // 프로그램 종료.
    }

    SimpleDaemonManager sdm = null;
    
    try {
      sdm = SimpleDaemonManager.getInstance(ShowTime.class);
      sdm.start();
    }  catch (Exception e) {
      e.printStackTrace();
    }
  }
  
  /**
   * 데몬 작업수행 : 현재 시간을 계속 보여준다.
   */
  public void startDaemon() {
    while (true) {
      Date now = new Date();
      System.out.println(now.toString());
      try {
        Thread.sleep(5000);
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }
    
  }

  /**
   * ShowTime 종료시 실행할 내용들. 
   */
  public void shutdown() {
    System.out.println("ShowTime을 종료합니다.");
  }
}

[출처] Java Simple Daemon|작성자 승빈파파


'Dev > JAVA' 카테고리의 다른 글

Spring framework 설치  (0) 2012.11.25
자바 데몬(daemon) 만들기  (0) 2011.06.09
자바 이미지 사이즈 추출 예제  (0) 2011.04.23
자바 이미지 리사이즈(썸네일)  (0) 2011.04.06

+ Recent posts