IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
한국 developerWorks   >   Open developerWorks  > developerworks

그 동안 몰랐던 서버 사이드 자바스크립트 이야기, Part 4: 헬마로 할 일 애플리케이션 만들기



김영후김영후 zerohoo.kim@gmail.com

웹 개발 기술과 매킨토시에 관심이 많은 스물여섯 살 개발자다. 능력과 시간을 전부 창조하는 작업에만 쓸 수 없을까 궁리 중이며 현재 병역 특례 업체에서 플렉스를 주로 다루고 있다.


난이도 : 중급
2008년 12월 16일


연재순서
1회(2008년9월): 다시 보는 자바스크립트의 역사
2회(2008년10월): 자바로 만든 자바스크립트 엔진, 리노
3회(2008년11월): 리노 기반 웹 프레임워크, 헬마
4회(2008년12월): 헬마로 할 일 애플리케이션 만들기


[오픈 developerWorks]는 여러분이 직접 필자로 참가하는 코너입니다.

드디어 마지막 Part 4다. 본격적으로 헬마(Helma)의 깊은 세계에 빠지기 전에 자바스크립트와 관련된 소식을 소개하고 싶다. 우선 지난 Part 3에 언급했던 앱젯(AppJet)과 관련된 뉴스다. 앱젯팀은 최근에 웹에서 실시간 동시 편집기를 이용할 수 있는 EtherPad를(맥을 사용하는 사람이라면 실시간 공동 편집을 할 수 있는 맥 편집기 SubEthaEdit을 생각하면 될 것이다) 런칭하여 큰 주목을 받았는데 자바스크립트로 구현한 이 편집기의 기능도 흥미롭지만 이 웹 애플리케이션이 운영되는 백그라운드 환경도 주목할 만하다. Appjet.com에서 호스팅되는 앱젯 애플리케이션은 대부분 심각한 용도로 사용하긴 어려운 것들이다. 이에 비해 많은 주목을 받고, 많은 사용자가 동시에 이용할 EtherPad는 많은 부하를 견뎌낼 수 있도록 만든 "자체 수정된 앱젯"에서 운영된다고 하며 이 "자체 수정된 앱젯" 역시 곧 일반 앱젯 사용자들이 쓸 수 있게끔 공개될 것이다. 즉 앱젯 환경이 구글의 앱엔진(AppEngine)과 경쟁할 만한 서비스가 되도록 하겠다는 것이다. 앱엔진과 다른 점은 웹에서 개발이 가능하면서 동시에 자체 호스팅을 할 수 있다는 점이고, 언어가 파이썬이 아닌 자바스크립트라는 정도다(http://blog.appjet.com/2008/11/18/whats-next-for-appjet/).

이제 본 주제로 돌아와 다시 헬마를 다뤄보자. 지난 Part 3에서는 헬마의 액션과 스킨, 매크로 등 주로 HTTP 요청을 핸들링하고 처리 결과를 사용자에게 보여주는 방법을 주로 다루었다. 여타 웹 프레임워크와 비교하면 스킨은 뷰(View), 액션은 컨트롤러에 해당하는 역할이고 매크로는 뷰 처리를 담당하는 컨트롤러가 될 수 있겠다. 여기에 빠진 것이 있으니 바로 애플리케이션의 실제 데이터 역할을 하는 모델 부분이다. 루비 온 레일즈나 장고(Django) 같은 최근의 웹 프레임워크는 모델을 ORM 기술과 결합하여 데이터베이스 테이블을 객체로 이용할 수 있게 해준다. 헬마에서도 이러한 ORM이 가능하며 상황에 따라 XML 기반 객체지향 데이터베이스도 이용할 수 있다. 이를 위해 헬마의 기본 객체라고 부를 수 있는 HopObject에 대해 알아보자.


HopObject

HopObject는 표준 자바스크립트 객체를 헬마에 맞게 확장한 것이다. 모든 헬마 애플리케이션은 HopObject를 가지고 있으며 우리가 작성한 Root 프로토타입의 객체도 HopObject다. 모든 HopObject는 여러 HopObject를 포함하는 컬렉션(Collection)을 가질 수 있으며 모든 HTTP요청은 URL에 따라 이 컬렉션의 특정 HopObject로 대응될 수 있다. 다시 정리하면 지난 Part 3에서 우리가 작성한 Root 디렉터리 안의 함수와 스킨은 Root 프로토타입에 속하는 것이며 Root는 그 자체로 HopObject다. 사용자의 요청은 HopObject에 바로 대응되며 Part 3의 hello 애플리케이션을 http://127.0.0.1:8080/hello로 접속할 경우 Root 프로토타입의 기본 객체이며 HopObject인 root가 실행된다.

이러한 HopObject는 기본적으로 XML로 저장될 수 있으며 설정을 통해 ORM을 이용하여 데이터베이스에 저장될 수도 있다. 당장은 헬마의 ORM에 대해 아는 것보다 HopObject의 개념과 이용법을 아는 것이 더 중요하기 때문에 일단은 RDBMS에 대해 신경쓰지 않고 HopObject를 이용한 할 일(To Do) 애플리케이션을 만들어 볼 것이다.



위로


할 일 애플리케이션

헬마 디렉터리의 apps.properties 파일을 열면 다음과 같은 내용이 있을 것이다.


welcome 
welcome.mountpoint = / 
welcome.repository.0 = apps/welcome/code/ 
welcome.repository.1 = modules/helmaTools.zip 
welcome.static = apps/welcome/static 
welcome.staticMountpoint = /static 
welcome.staticHome = index.html,default.html 
welcome.staticIndex = true 
welcome.uploadLimit = 2048


지난 Part 3 예제를 따라 했다면 가장 마지막 줄에 hello라는 내용이 있을 것이다. 그것과 상관없이 마지막 줄에 새로운 애플리케이션을 위해 todo라는 줄을 추가하고 저장하자. hello와 마찬가지로 헬마의 apps 디렉터리에 가면 todo 디렉터리가 자동으로 생성될 것이다. 우선 Root 프로토타입을 위해 todo 디렉터리 밑에 Root 디렉터리를 생성하고 액션을 처리하는 handler.js 파일을 생성하자.

Listing 1. todo/Root/handler.js

function main_action() { 
  res.write('To Do Application'); 
} 


시험을 위해 main 액션을 임시로 구현했다. 이제 http://127.0.0.1:8080/todo에서 To Do Application이 보이는 것을 확인했으면 기본 레이아웃을 위한 Global 스킨을 만들자.

Listing 2. todo/Root/Global/html.skin

<html> 
<head> 
   <title><% response.title %></title> 
</head> 
<body> 
  <h2> <% response.title %> </h2> 
  <div id="content"> 
    <% response.body %> 
  </div> 
</body> 
</html>  


이 스킨을 이용하도록 액션을 수정하고 http://127.0.0.1:8080/todo에서 결과를 확인하자.

Listing 3. todo/Root/handler.js

function main_action() { 
  res.data.title = "To Do Application"; 
  res.data.body = "Body" 
  renderSkin("html"); 
}  




위로


할 일 목록

지금까진 루트라는 하나의 HopObject만을 이용했지만 본격적으로 할 일을 관리하는 HopObject들을 만들 것이다. 우선 개발을 쉽게 하기 위해 기본으로 할 일 HopObject를 몇 개 저장해 놓자.

Listing 4. todo/Root/handler.js

function init_action() { 
  if (root.size() == 0) { 
    var task = new Task(); 
    task.name = 'Learn Helma'; 
    root.add(task); 
    task = new Task(); 
    task.name = 'Learn JavaScript'; 
    root.add(task); 
    res.write("initilzed"); 
  } else { 
    res.write("Already initilzed"); 
  } 
} 


루트에 init이라는 새 액션을 추가하였다. 이 액션은 Task 두 개를 루트에 추가해주는 일을 수행한다. 이미 추가되어 있을 경우 중복해 만들지 않는다. http://127.0.0.1:8080/todo/init으로 접속하여 데이터를 초기화하고, 다시 한번 접속해 보자.

아직 우리의 할 일 애플리케이션에선 볼 수 없지만 init을 통해 Task 객체 두 개를 생성하였다. 이 객체는 어디에 저장될까? 특별히 RDBMS와의 연결을 위한 ORM 설정을 하지 않았기 때문에 XML 형태로 파일 시스템에 저장된다. helma 디렉터리의 db 디렉터리를 보면 우리가 만든 todo 애플리케이션 폴더를 볼 수 있다. 이곳에 HopObject가 저장된다. 지금 현재 XML 파일이 여섯 개 있을 것이다. helma.xsl, idgen.xml 파일과 함께 0.xml, 1.xml 같이 숫자로 파일명을 가지고 있는 XML 파일이 있다. 이것은 해당 HopObject의 ID다. 0.xml을 열어보면 다음과 같은 내용을 볼 수 있을 것이다.


<?xml version="1.0" encoding="ISO-8859-1"?> 
<?xml-stylesheet type="text/xsl" href="helma.xsl"?> 
<xmlroot xmlns:hop="http://www.helma.org/docs/guide/features/database"> 
  <hopobject id="0" name="root" prototype="Root" created="1228873443296" lastModified="1228882490781"> 
    <name>root</name> 
    <hop:child idref="3" prototyperef="Task"/> 
    <hop:child idref="4" prototyperef="Task"/> 
  </hopobject> 
</xmlroot> 


이 0.xml 파일은 우리가 줄곧 이용해왔던 루트 HopObject의 정보를 저장하는 파일이다. 루트의 자식으로 init 액션에서 생성한 Task 두 개를 지정하고 있다. 1.xml은 사용하지 않았지만 기본적인 사용자 정보를 저장할 수 있는 사용자 HopObject다. 번호가 다를 수도 있겠지만 나머지 3.xml, 4.xml 파일을 열어보면 우리가 init 액션에서 지정한 내용이 들어가 있음을 확인할 수 있을 것이다. 그럼 HopObject가 XML로 관리되는 것을 알았으니 실제 기능을 구현해보자.

Listing 5. todo/Root/handler.js

function main_action() { 
  res.data.title = "To Do Application"; 
  var str = ""; 
  for (var i=0; i< root.size(); i++) { 
    var task= root.get(i); 
    str += task.renderSkinAsString("link"); 
  } 
  res.data.body = str; 
  renderSkin("html"); 
} 


Listing 6. todo/Task/link.skin

<a href="/todo/<% this.name %>"><% this.name %></a><br /> 


수정된 main 액션은 init에서 저장한 루트 객체의 자식 HopObject, 즉 Task를 하나씩 얻어와서 Task 프로토타입 자체의 link 스킨을 이용하여 Task를 표현한다. http://127.0.0.1:8080/todo로 다시 접속해 보면 Learn Helma와 Learn JavaScript 두 Task 객체가 표현되는 것을 볼 수 있을 것이다. 한 가지 문제는 링크를 클릭해보면 다음과 같은 에러를 본다는 것이다.

그림 1

이는 헬마에서 요청한 URL을 대응하는 적절한 HopObject를 찾지 못했음을 의미한다. http://127.0.0.1:8080/task/Learn%20Helma로 접속해도 Learn Helma 이름을 가진 Task HopObject와 연결되지 않은 것이다. 이것을 연결하기 위해 Root 프로토타입 폴더에 type.properties 파일을 만들자.

Listing 7. todo/Root/type.properties

_children = collection(Task) 
_children.accessname = name 


모든 HopObject 프로토타입은 type.properties 파일이 있을 수 있으며 이 설정 파일은 다른 HopObject와의 관계를 정의할 수 있다. 이 파일에선 루트 HopObject는 Task 컬렉션을 자식으로 가질 수 있으며 name 프로퍼티를 이용해 각 Task 자식에 접근할 수 있다고 정의했다. 이제 다시 http://127.0.0.1:8080/task/Learn%20Helma로 접속해보면 이전과 다른 에러를 볼 수 있을 것이다.

그림 2

이 에러는 해당 요청에 대한 HopObject, 즉 Task는 찾았지만 요청을 처리할 액션이 없다는 것이다. 즉 연결이 성공되었다. 이제 액션을 정의하자.

Listing 8. todo/Task/task_handler.js

function main_action() { 
  res.data.title = this.name; 
  renderSkin("html"); 
} 


Task 프로토타입에 대한 main 액션을 정의하였다. Part 3에서 말했듯이 파일명은 어느 것이 되든 상관없으며 액션을 처리하는 main.hac 파일을 만들고 main_action 함수의 내용을 써도 정확히 똑같이 동작한다. HopObject의 기능을 알아보는 본 기사에선 우선 이름만을 표시했지만 할 일 애플리케이션에서 "할 일"의 메인 화면은 다양한 기능을 포함할 수 있을 것이다. 독자들은 추후에 수정이나 생성, 혹은 태그 기능을 구현하고 이를 위한 스킨과 매크로를 구현해 보는 것도 좋을 것이다.



위로


할 일 추가

이제 저장된 Task를 보여주는 기능을 구현했으니 생성 기능을 구현해보자.

Listing 9. todo/Root/add.skin

<form method="post" action="add"> 
    <p>Name: <input type="text" name="name"> 
    <input type="submit" name="add" value="Add"></p> 
</form> 


Listing 10. todo/Root/hander.js

function main_action() { 
  res.data.title = "To Do Application"; 
  var str = ""; 
  for (var i=0; i< root.size(); i++) { 
    var task = root.get(i); 
    str += task.renderSkinAsString("link"); 
  } 
  str += this.renderSkinAsString("add"); 
  res.data.body = str; 
  renderSkin("html"); 
} 

function add_action() { 
  if (req.data.add && req.data.name) { 
    var task = new Task(); 
    task.name = req.data.name; 
    this.add(task); 
    res.redirect(task.href()); 
  } 
} 


Task 생성을 위한 폼 스킨을 Root 프로토타입에 정의하고 main 액션에서 이 폼이 기본적으로 보이도록 수정하였다. 그리고 이 폼을 처리할 add_action을 구현하였다. add 액션의 조건문은 폼의 액션이 add이며 name 매개변수가 있는 POST만을 처리하기 위한 것이다. http://127.0.0.1:8080/todo/add로 접속하면 아무것도 볼 수 없다. Task 추가 폼을 메인 화면이 아닌 별로 페이지에서 보여주길 원하면 이 액션에서 renderSkin을 이용하면 될 것이다. 연습해 보는것도 좋다.

현재 add 액션을 수행하면 입력한 이름대로 Task를 추가할 수 있지만 경로가 '/todo/숫자'로 리다이렉션되는 에러가 있다. 이는 기본적으로 HopObject의 href 함수가 해당 HopObject의 id를 이용하기 때문인데 우리는 Root의 type.properties에서 _children.accessname을 name으로 설정했기 때문에 id로는 Task 객체를 찾지 못하는 것이다. Task가 href에 자신의 id가 아닌 name 프로퍼티를 이용할 수 있도록 Task에도 type.properties를 정의하자.

Listing 11. todo/Task/type.properteis

name 


이제 폼을 수행해보면 정상으로 추가되며 추가한 Task로 리다이렉션되는 것을 볼 수 있을 것이다. 마지막으로 삭제 기능을 구현해보자.



위로


할 일 삭제

Task의 link 스킨에 삭제를 위한 링크를 추가했으며 이 링크에 대응하기 위해 Task 프로토타입에 delete 액션을 구현하였다. delete 액션은 자신을 삭제하고 부모 객체에서도 자신의 레퍼런스를 제거한 후 루트의 main 액션으로 리다이렉션한다. 테스트를 위해 delete 링크를 누르면 화면에서 해당 Task가 삭제되는 것을 확인할 수 있다. 실제 헬마 디렉터리의 db/todo 디렉터리에서도 삭제된 HopObject에 대응하는 XML 파일이 제거된 것을 볼 수 있다.

Listing 12. todo/Task/link.skin

<a href="/todo/<% this.name %>"><% this.name %></a> <a href="/todo/<% this.name %>/delete">delete</a><br>


Listing 13. todo/Task/task_handler.js

function delete_action() { 
  this.remove(); 
  root.removeChild(this); 
  res.redirect(root.href()); 
} 




위로


마치며

지금까지 헬마의 HopObject 기능을 알아보았다. 모든 헬마 객체의 기본이 되는 HopObject는 단순하지만 강력한 개념으로 모델 기능과 URL에 컬렉션 객체를 대응하는 개념, 이에 따른 자연스러운 모듈화는 타 웹 프레임워크보다 상당히 유연한 특징이라고 볼 수 있다. 요청 URL을 파싱하여 ID나 유일한 키 값을 얻어내고, 그 키를 이용하여 객체에서 가져오는 코드가 전혀 없음을 볼 수 있고 이는 HopObject를 RDBMS로 연동해도 똑같이 작동한다(실제로 컬렉션에서 특정 HopObject의 접근을 캐싱하는 기능까지 포함되어 있다). 이번 회의 최초 목표는 ORM 기능을 포함하는 것이었지만 HopObject 자체 동작을 이해하는 것이 더 중요했기 때문에 테이블 정의나 관계 정의에 상당한 지면을 할애해야 하는 데이터베이스 부분은 과감히 포기하였다. 헬마의 ORM 예제를 시도하고 싶다면 튜토리얼을 참고할 수 있을 것이다.


참고자료

서버 사이드 자바스크립트에 관심이 있다면 헬마와 리노 외에 다음 자료들도 살펴보면 유용할 것이다.

  • appjet.com: 여러 번 언급한 앱젯은 가장 유망한 서버 사이드 자바스크립트 기술 중 하나다. 앱젯 사이트의 클라우드 환경에서도 운영될 수 있고, 앱젯이 공개한 자바 기반의 클라우드 시스템을 자체적으로 호스팅할 수도 있다.
  • Phobos: 썬 마이크로시스템즈에서 지원하는 자바스크립트 플랫폼 기술이다.
  • Jaxer: Aptana의 Jaxer는 존 레식이 말하는 가장 흥미로운 자바스크립트 기술 중 하나다. 브라우저에서 서버 사이드 코드를 동작시키는 방식으로, 서버 사이드 스크립트에서 Prototype이나 jQuery 같은 클라이언트 라이브러리를 이용할 수 있다.

그 외 자바스크립트를 배우려는 독자들에겐 존 레식의 http://ejohn.org/apps/learn/ 같은 프로그램도 참고할 만 하다. 브라우저에서 바로 코드를 작성, 수정해보고 실행할 수 있는 이 튜토리얼은 자바스크립트의 특성을 최대한 살려서 장을 구성하였다.



이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us

이제 전문가의 글을 단순히 ‘보는 것’에서, 직접 여러분이 developerWorks의 필자가 될 수 있습니다. IBM developerWorks를 통해 공유하고 싶은 지식이 있으신 분들은 원고 기획안을 접수해주세요. 채택되신 분께는 소정의 원고료를 드립니다.



[지난 Open dW 보기]
사이트 여행

dW 커뮤니티
포럼 | 블로그 | Spaces
dW Student Community

로컬 컨텐츠

행사 및 세미나

기획 기사

개발자 입문

튜토리얼 및 교육

TOP 10 인기자료

SW 다운로드

RSS 피드

뉴스레터
 
  
자바스크립트가 작동이 중지되었습니다. 이 기능을 수행하시려면 브라우저에서 자바스크립스트를 작동시켜 주시거나 이곳을 클릭해주세요.

Special offers
Screencast
IBM SOA Sandbox 시험판
dW Student Community
로보코드
코드 트레이닝


    IBM 소개 개인정보 보호정책 문의