수안이의 컴퓨터 연구실

  • Mainpage
  • About Me
  • Tags
  • Metapage
  • Notice
  • Location
  • Keywords
  • Guestbook
  • Admin
  • Write an Article
  • Total | 1621000
  • Today | 380
  • Yesterday | 482

38 Articles, Search for 'Programming/Java'

  1. 2008/03/21 Java DOM Tutorial
  2. 2007/10/13 Java Swing을 이용해서 제작한 마방진 (2)
  3. 2007/05/18 웹2.0(Ajax)과 아키텍처의 구현
  4. 2007/05/18 리팩토링을 이용한 자바 성능 최적화 기법
  5. 2007/05/18 유연한 참조로 메모리 누수 막기
  6. 2007/05/18 EJB 기반 프로젝트 수행 가이드 ④
  7. 2007/05/18 EJB 기반 프로젝트 수행 가이드 ③
  8. 2007/05/18 EJB 기반 프로젝트 수행 가이드 ②
  9. 2007/05/18 EJB 기반 프로젝트 수행 가이드 ①
  10. 2007/05/18 MIDI Sound 생성하기
«Prev  1 2 3 4  Next»
Programming/Java2008/03/21 00:11

Java DOM Tutorial

http://www.roseindia.net/xml/dom/index.shtml

Java DOM Tutorial

This tutorial is complete guide to DOM processing.

What is DOM?

Document Object Model: DOM is a platform- and language-neutral interface, that provides a standard model of how the objects in an XML object are put together, and a standard interface for accessing and manipulating these objects and their inter-relationships.

The DOM is an interface that exposes an XML document as a tree structure comprised of nodes. The DOM allows you to programmatically navigate the tree and add, change and delete any of its elements.

The DOM programming interface standards are defined by the World Wide Web Consortium (W3C). The W3C site provides a comprehensive reference of the XML DOM.

  1. Creating Blank DOM Document
    This section shows you how to create the blank DOM document.
           

  2. Adding Child Elements to the DOM tree
    This lesson shows you how to create root and child elements in the DOM tree.
       

  3. Getting The XML Root Element
    After reading this section, you will be able to retrieve a  root element from the XML document.  The JAXP (Java APIs for XML Processing) provides a common interface for creating and using xml files using the standard SAX, DOM and XSLTs.
         
  4. To Count XML Element
    In this section, you will learn to count the elements present in a XML file using DOM APIs.
           
  5. To Count The Elements in a XML File
    In this section, you will learn to count the element in XML document using DOM APIs defined in the org.apache.xerces.parsers.DOMParser  package.
           
  6. XML Well-Formed-ness
    In this section, you will learn to check the well-formed-ness  of a XML using the DOM interface. A  well-formed  XML  document must follow the xml syntax rules.

  7. Searching an Element in the given XML Document
    In this you will learn to search an element in the specified XML document using DOM APIs defined in the org.apache.xerces.parsers.DOMParser  package.
           
  8. Create - XML File (Document)
    In this section, you will  learn to create a XML document using the DOM APIs. This XML document uses  1.0 version and UTF-8 encoding.

  9. Regenerating XML  file
    In this section, you will learn to get the  elements and its value using DOM APIs.

  10. XML Error checker and locater (DOM)
    In this section, you will learn to check and locate (line and column number) an error in your XML document using the DOM APIs.  The XML document follows some rules to check its syntax.
       
  11. Getting all XML Elements
    In this section, you will learn to retrieve all elements of the XML file using the DOM APIs. This APIs provides some constructors and methods which helps us to parse the XML file and retrieve all elements.
       
  12. Adding DOCTYPE to a XML File
    In this section, you will learn to add a DOCTYPE to your XML file  using the DOM APIs.
       
  13. Getting Dom Tree Elements and their Corresponding XML Fragments
    In this section, you will learn to get the elements of a DOM tree and their corresponding XML fragments. Each element of dom tree has a node level starting with '0'. Here the DOM tree elements and their corresponding XML fragments are displayed on the console.
     
  14. Cloning a XML Element
    In this section, you will learn to create a clone of a  element in the DOM tree. In general, the cloning means to create a duplicate.
     
  15. Remove Element from XML Document
    In this section, you will learn to remove any element from a given  XML document. Whenever you remove the xml element from the xml document the data are also lost from the xml element.
         
  16. Getting Data from XML File (Document)
    In this section, you will learn to retrieve the data from a XML file. All xml files store the data. You can add and modify the data in the  xml document using the DOM APIs.
     
  17. Storing Data (Retrieved from a XML Document) to a File
    In this section, you will learn to store data (retrieved from the XML document) to a specified file (with  extension '.txt', '.doc', '.xls', '.shtml' etc.) in different formats (text, xml, html etc.).
         
  18. XML Validate DTD
    In this section, you will learn to validate a xml file against a  DTD (Document Type Definition)  using the DOM APIs. A DTD defines the document structure with a list of legal elements and attributes.

XML Tutorials Links
Links of Many XML Tutorials. Here you will find lots of XML Tutorials.


"Java" 카테고리의 다른 글
  • Java DOM Tutorial (0)2008/03/21
  • Java Swing을 이용해서 제작한 마방진 (3)2007/10/13
  • 웹2.0(Ajax)과 아키텍처의 구현 (0)2007/05/18
  • 리팩토링을 이용한 자바 성능 최적화 기법 (0)2007/05/18
  • 유연한 참조로 메모리 누수 막기 (0)2007/05/18
2008/03/21 00:11 2008/03/21 00:11
Posted by webdizen
Tags DOM, Java, XML, XMLParser
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3187

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Java2007/10/13 21:05

Java Swing을 이용해서 제작한 마방진

객체지향프로그래밍 수업을 들으면서 "마방진" 제작에 관한 Report가 있어서 제작해봤습니다.
Java Swing을 이용해서 제작하였습니다.

사용자 삽입 이미지
[그림 1. MagicSquare 실행 모습]

- 마방진 Algorithm

- 마방진 소스 다운로드
MagicSquare.java
MagicSquareControl.java

"Java" 카테고리의 다른 글
  • Java DOM Tutorial (0)2008/03/21
  • Java Swing을 이용해서 제작한 마방진 (3)2007/10/13
  • 웹2.0(Ajax)과 아키텍처의 구현 (0)2007/05/18
  • 리팩토링을 이용한 자바 성능 최적화 기법 (0)2007/05/18
  • 유연한 참조로 메모리 누수 막기 (0)2007/05/18
2007/10/13 21:05 2007/10/13 21:05
Posted by webdizen
Tags Java, Swing, 마방진, 컴퓨터과학과
1 Trackback 2 Comments

Trackback URL : http://www.webdizen.net/blog/trackback/3129

  1. 마방진을 구하는 프로그램  Delete

    2007/10/29 17:39 Tracked fromOranzie's BLOG 3

    MagicSquare.zip객체지향 시간에 개인프로젝트로 작성한 마방진 프로그램아래는 마방진을 구해주는 알고리즘 : for (int counter = 1; counter <= (length * length); counter++) { square[row_ptr % length][col_ptr % length]..

Leave your greetings.

  1. Oranzie

    뭐야 사족을 더 붙였어?! ㅡㅡ^

    2007/10/16 12:43 [ Permalink : Modify/Delete : Reply ]
  2. 05성준

    ㅋㅋㅋㅋ구글 검색 하니까 이거 나오네요 ㅋㅋㅋㅋㅋ

    잘쓸게요.

    2009/10/11 23:16 [ Permalink : Modify/Delete : Reply ]
[로그인][오픈아이디란?]

Programming/Java2007/05/18 11:53

웹2.0(Ajax)과 아키텍처의 구현

웹2.0 도입을 위한 마이그레이션 프레임워크

웹2.0(Ajax)과 아키텍처의 구현

이동호, 윤현희, 손범성, 박정은

Ajax는 웹2.0의 기반이 되는 기술이다. 기존 웹을 재활용하는 측면에서 웹2.0과 Ajax, 그리고 웹2.0 환경으로 마이그레이션하는 방법을 함께 알아보자. 이번 시간은 웹2.0 및 Ajax의 개념과 전체적인 구조에 대한 기본 라이브러리를 어떻게 구현했는지 살펴본다.


구글 애드센스, 비트토런스, 넵스터 등 최근 언론의 주목을 끄는 사이트의 공통된 특징은 웹2.0 기반의 성공사례로 꼽힌다는 점이다. 전문가들은 웹2.0을 두 가지 측면으로 해석한다. 우선 콘텐츠의 질적, 양적 팽창이 사용자 중심으로 이뤄지는 구조로 흘려간다는 것이고 다른 하나는 마케팅 용어에 불과하다는 시각이다. 그런데 대다수 기업은 웹2.0의 의미보다는 자사 서비스가 웹2.0에 기반 한다는 사실을 사용자에게 더 알리고 싶어 한다. Time to Market(적기 시장 진입)이 기업 성공의 필수조건이니 만큼 트렌드를 따라가야겠다는 생각이다. 여기에는 웹2.0 기반의 개발 환경이 필연적이다.

이번 프로젝트의 결과물인 ‘Web2.0 Migration Framework’는 기존 웹의 재활용성을 높이고, 적기 시장 진입을 충족시켜줄 수 있는 환경을 제시한다. 기존 웹을 재활용하기 위해서는 범용성을 위한 언어 독립적 구현이 필요하고, 도입의 판단을 위해 기존 웹을 데이터적인 측면에서 바라보는 시각이 요구된다. 이번 호는 웹2.0 기반의 기술인 Ajax와 설계적인 측면에서 프로젝트 결과물을 살펴보고자 한다.




웹2.0과 Ajax

웹2.0은 X-인터넷과 더불어 현재 이슈화되고 있는 개념이다. 이 둘의 공통점은 웹 서비스의 애플리케이션화와 가벼운 리치 클라이언트 구현의 중심이라는 것이다. 이러한 웹 서비스의 애플리케이션화에 도움을 주는 것이 Ajax이다. Ajax는 Asynchronous JavaScript and XML의 줄임말로, 사용자 요청에 JavaScript와 XML을 이용, 비동기적으로 처리한다는 의미이다. 즉 Ajax는 특별한 기술을 요구하는 것이 아니라 실용성과 효율성을 바탕으로 현존하는 기술을 조합하고 웹 애플리케이션을 개발하려는 새로운 시도인 셈이다.

사용자 삽입 이미지

<그림1> 기존 동기 통신과 Ajax의 비동기 통신의 차이


<그림 1>에서 기존 동기 통신은 애플리케이션이 서버와의 인터랙션을 필요로 할 때 매번 브라우저가 사용자와의 인터랙션을 멈추고, 서버의 응답을 기다린다. 반면 비동기 통신은 사용자 동작과 상관없이 연속적인 작업 진행이 가능하다. Ajax의 구현과 확장에 대해서는 뒤에서 살펴보도록 하겠다.




필자 메모
Web2.0 Migration Framework라는 이번 프로젝트를 진행하면서 기존 시스템에서 스크랩한 정보, 즉 Html 파싱 부분이나 파싱한 정보에 Ajax 코드를 넣는 부분, 서비스에 필요한 설정과 여러 코드들의 생성, 이클립스 플러그인의 구현 등에 대한 부분에서 많은 어려움을 겪었다. Html 파싱의 경우, Html은 XML과 같은 Well-Formed 문서가 아닌 탓에 별도의 변환 과정을 거치고, 여러 사이트를 스크랩하면서 테스트를 수행해야 했다. 또한 서비스 구조에 대한 정확한 데이터 처리가 필요해 웹 서버에 대한 배경 지식도 요구되었다. 이클립스 플러그인이라는 생소한 부분도 직접 구현해야 했기에 이에 대한 조사는 물론 구현에 많은 시간을 할애할 수 밖에 없었다. Html 파싱은 NekoHtml이라는 라이브러리를 부분적으로 사용, 구현하였다. 파싱된 정보는 프로젝트 전반에 걸쳐 사용하기 때문에 Well-Formed 문서로 구성하는데 중점을 뒀다. 그리고 서비스 가운데 기존 시스템에서 스크랩한 부분은 페이지의 여러 부분에서 사용자 요청이 있을 수 있으니 이에 대한 정확한 처리를 위해 페이지 상태와 요청에 대한 부분을 별도로 관리했다. 아울러 Html 수정과 Ajax 코드를 끼워 넣을 때는 트리 형태로 파싱한 정보를 오브젝트에 바인딩 하여 관리하고, 서비스시 동적으로 Html 코드를 생성하여 사용자한테 보낼 수 있도록 했다.




전체 시스템의 설계

그럼 프로젝트의 전체 시스템 구조가 어떤 방향으로 설계되었는지 살펴보자.

우선 설계의 기본 개념을 열거하면 기존 웹의 부분적인 마이그레이션(MVC 패턴의 도입)과 웹2.0 기술인 Ajax의 손쉬운 도입, 그리고 기존 시스템의 구조적 수정 불가(데이터적 관점), 기존 시스템의 재활용, 언어 독립성 등이다.

사용자 삽입 이미지


<그림 2>에서 Scraper는 Html 소스를 스크랩하고 언어 독립적인 구현을 위해 존재한다. 최초 작업을 위해 Scraper에서 Html 소스를 스크랩한다. 이를 Html Parser에서 Html 소스를 well-formed 문서로 변환 후 파싱, ParseTree 형식으로 객체에 바인딩 한다. 이후 소스에 대한 수정과 Ajax 기능에 대한 추가 작업이 끝나면, 작업 히스토리에 대한 정보를 바탕으로 Web Service Configuration Module에서 Xml Parser와 Xml Generator로 웹 서비스를 위한 설정을 한다.

최종적으로 Struts Handle에서 web.xml, struts-config.xml, java, jsp 등의 파일을 생성한다. Web 2.0 Extension Module은 이 같은 부분을 바탕으로 웹2.0 기술인 Ajax에 대한 구현을 하며 javascript, java, html을 생성하는 역할을 한다. 사용자 인터페이스는 이클립스 플러그인 형태로 구현하였다. 이제 최종 작업이 완료된 후 서비스가 어떤 형태로 운영되는지 보자.


Struts MVC

웹 프로젝트에서 사용하는 MVC는 크게 Struts와 Spring로 나뉜다. 여기서는 대부분의 시스템에서 사용하는 Struts를 사용한다. Struts는 Java Servlet, Java Server Pages, Java Beans, Resource Bundle 그리고 Extended Markup Language(XML)처럼 표준 기술에 기반 한 오픈소스 프레임워크이다. Struts는 아파치 소프트웨어 재단이 지원하는 자카르타 프로젝트의 일부이며 Struts의 공식 홈페이지는 http://jakarta.apache.org/struts이다.


서비스 구조의 설계

먼저 Controller와 View, Model의 경우 MVC의 구조로 코드가 생성된 부분이고 Ajax Module은 Ajax 기능에 대해 코드가 생성된 부분이다. 최초 사용자의 페이지 요청이 있을 때 Cont roller에서 그 요청을 받아 Model 부분에 적합한 페이지를 요구한다.

Model 부분은 기존 페이지를 스크랩하여 View 부분으로 업데이트해 사용자 요청에 응답한다. 만약 비동기적인 요청인 경우엔 Ajax Module에서 처리하여 응답하는 서비스 구조를 갖는다. 이 방식은 웹2.0 도입이 적합하지 않다고 판단되면 <그림 3>의 ‘1’에 대한 부분만 빼내면 기존의 서비스를 다시 가동할 수 있다는 이점이 있다. 기존 시스템을 데이터적 관점에서 관찰하고 구조적인 수정을 하지 않는 형태의 웹2.0 프로젝트 콘셉트와 관련된다.

사용자 삽입 이미지
<그림 3> 서비스 구조



작업 파일의 구성

전체 시스템 구조에서 알 수 있듯이 사용자 인터페이스는 이클립스 플러그인으로 작성되었다. 이클립스의 에디트뷰, 즉 에디트할 수 있는 창은 파일 확장자와 연관 관계가 있는 것으로 파일 확장자에 대한 에디트 방법의 정의가 필요하다. 여기선 새로운 작업 파일에 대한 구성이 필요한 만큼 잠깐 살펴보도록 하자. 마이그레이션 작업을 위한 파일 포맷은 XML 형식을 따른다. 파일 포맷을 구성하는데 고려된 사항은 다음과 같다.

- 현재 작업 상태와 작업 편의성을 위한 작업 히스토리를 저장할 수 있어야 한다.
- 웹 서비스를 위한 각종 맵핑 정보와 스크랩을 위한 정보들을 보관해야 한다.

이를 바탕으로 구성한 스키마는 <리스트 1>과 같다.

사용자 삽입 이미지


<리스트 1>은 전체적인 작업 파일의 스키마 구성이다. 스키마는 크게 세 부분으로 분류할 수 있으며, 각 요소는 init, recently-html, histories 등이다.

사용자 삽입 이미지


<리스트 2>의 init 부분은 url-pattern, scrap-path, scrap-html 등으로 나눠진다. url-pattern은 서비스시 맵핑에 대한 정보를, scrap-path는 서비스시 스크랩할 주소, scrap-html은 작업을 위한 파일 생성시 스크랩된 html 정보를 포함한다. recen tly-html 부분은 연속적인 작업을 위한 최근 작업의 html 소스 부분을 담는다. histories 부분은 history라는 요소를 하부 항목으로 갖는다. 관련 스키마는 <리스트 3>과 같다.

사용자 삽입 이미지


<리스트 3>은 extension과 operations를 하부 항목으로 갖는다. extension의 경우 Ajax 기능, 즉 웹2.0 기술에 필요한 정보를 담고, operations는 html 소스의 수정에 대한 정보를 담는다. 작업 파일에 대한 파싱과 코드 생성은 스키마에 따라 XmlBeans를 사용하여 기본 라이브러리를 생성했고, 이를 토대로 작업 파일을 처리하는 부분을 작성하였다.


기본 라이브러리의 구현
Scraper(Screen Scraping의 구현)

> ● HttpClient 라이브러리의 사용

Scraper는 언어 독립적인(JSP, ASP, PHP 등에 독립적인) 구현을 위해 필요한 기술이다. Scraper는 또 웹 서비스시 사용자에 대한 요청을 기존 웹 서버로 보내 스크랩할 수 있는 형태로 구현돼야 한다. 그래서 선택한 것이 HttpClient 라이브러리이다. Http 프로토콜의 기본적인 메소드인 Post와 Get에 대한 서버측에 전송이 가능한 라이브러리이다.


HttpClient
HttpClient는 자카르타의 오픈소스 프로젝트로 진행되는 Http 프로토콜 메소드에 대한 부분을 자바로 구현한 것이다. GET, POST, PUSH 등 모든 Http 프로토콜에 대한 메소드가 정의되어 있어 Screen Scraping 기술 구현시 적합하다 (http://jakarta.apache.org/httpcomponents /index.html 참고).


● Scraper의 구현

사용자 삽입 이미지


<리스트 4>는 get 방식에 대한 Http 메소드의 구현 부분이다. 서비스 구조의 구현, 즉 서비스시 기존 웹에서 페이지를 스크랩할 때 쓰일 라이브러리 역시 동일한 방식으로 구현한다. 이에 대한 것은 Default Servlet의 구현 항목에서 자세히 알아보자.


Html Processing Handle의 구현

● ParseTree의 구현

ParseTree는 Html 파싱과 생성시 필요한 도큐먼트를 오브젝트에 트리구조로 바인딩하기 위한 부분이다. ParseTree는 크게 사용자에 보이는 인터페이스 구현 부분과 감춰진 내부 구현에 사용된다. 사용자 인터페이스는 사용자에게 직접적으로 영향을 미치므로 사용자 편의성이 우선시 된다. 소스 뷰, 트리 뷰, 브라우저 뷰 등 작업 환경 관련 노드에 대한 비주얼적인 어시스트/어프로치 구현은 물론 Ajax 기능 삽입시 Html 소스에 구조적 접근으로 요소요소를 핸들링하고 처리할 때 쓰인다.

사용자 삽입 이미지


<리스트 5>의 absolutePosition 메소드는 트리 인덱스를 인자로 노드에 한 번에 접근하기 위한 것으로 정확한 노드의 인덱스를 알고 있을 때 사용성이 증가한다. 즉 사용자의 노드 선택시 활용성이 크다. toList 메소드는 트리 구조를 순차적으로 액세스할 수 있다. 각 노드가 포함된 범위내의 하부 노드를 가져올 때 사용성이 증가한다. toList 메소드는 비지터 패턴으로 구현하였다. <리스트 5>의 visit 메소드가 비지터를 받아들이는 역할을 하며, 비지터는 각 노드가 된다.


● Html Parser의 구현

Html은 기본적으로 well-formed 형식의 문서가 아니다. 때문에 파싱시 well-formed 문서 형태로 변환하는 과정이 요구된다. SAX 파서를 기본으로 구현하였다. 또한, 각 웹 서비스를 위한 언어에서 동적으로 Front-End Html 소스가 변하는 부분에서의 필터링이 필요하다(<리스트 6> 참조).

사용자 삽입 이미지


● Html Generator의 구현

Html Generator는 Html Processing Handle에 포함되며, 기본적으로 ParseTree를 도큐먼트로 생성하는 기능과 Html 소스 변경에 대한 히스토리 처리 등의 기능을 한다.


Web Service Configuration Module

WebConfigParser와 WebConfigGenerator를 이용하여 web.xml에 각종 맵핑 정보(servlet-mapping/taglib 등)를 추가/삭제한다. WebConfigParser와 Generator는 web.xml의 스키마 문서를 XmlBeans를 사용하여 라이브러리를 생성, 오브젝트에 바인딩한다. XmlBeans는 내부적으로 SJSXP(StAX)를 사용하여 파싱을 한다.


Sun Java Streaming XML Parser(SJSXP : StAX)
XML 문서에 대한 파서는 크게 SAX, DOM, SJSXP 등 세 가지로 구분된다. SAX는 이벤트 기반의 API로 일반적으로 개발자가 파서와 몇 개의 리스너를 등록하고, XML 문서의 요소나 속성이 도착되면 리스너의 메소드가 호출된다. DOM은 반대로 전체 문서를 읽어 트리를 생성한다. 그러니 개발자는 트리 생성이 완료된 후 트리에 접근하여 처리할 수 있다. SAX나 DOM과 달리 SJSXP는 스트리밍 방식의 XML 문서의 처리가 가능하다. 개발자가 XML 문서의 요소나 속성에 능동적으로 처리가 가능함을 의미한다.


● WebConfigGenerator

웹 서버 톰캣의 구동에 대한 기본적 설정 파일인 web.xml 파일을 파싱하여 매핑된 정보의 getter와 setter를 구현하고 web.xml 파일에 정보를 추가/삭제/생성하는 기능을 구현한다. 또한 Request Filter와 Default Servlet에 대한 url-pattern 맵핑을 한다(<리스트 7> 참조).

사용자 삽입 이미지


StrutsHandle의 구현

Web Service Configuration Handle을 기반으로 기본적인 맵핑을 하며, struts-config.xml의 action-mapping과 data-source 설정, Servlet, JSP 등의 서비스가 가능한 파일 등을 생성하는 기능을 구현했다. Struts Handle의 경우 여러 파일을 생성하기 때문에 이에 대한 generate-map을 구성하여 각 파일에 대해 독립적으로 구현하였다. XmlBeans는 Xml 스키마 파일인 XSD 파일 기반으로 라이브러리를 생성한다. 그런데 Struts에 대한 설정 파일인 struts-config.xml의 스키마는 DTD 파일이다. 때문에 DTD 파일을 XSD 파일로 변환하는 작업이 필요하다. 스키마 문서를 변환할 수 있는 프로그램을 사용하여 변환하였다. 그리고 web.xml 경우 XSD 파일이 필요하다. 썬마이크로시스템즈 홈페이지에서 web-app_2_4.xsd, jsp_2_0.xsd, j2ee_1_4.xsd, j2ee_w eb_services_client_1_1.xsd 등의 여러 파일을 다운로드할 수 있다.

사용자 삽입 이미지


<리스트 8>은 generate-map에 대한 스키마이다. generate-map은 크게 Java 파일에 대한 부분과 JSP 파일 부분으로 나눠 정의하였다. 기본적인 파일 이름 및 확장자 부분과 import, path, JSP의 contentType의 정의를 볼 수 있다. Struts를 적용, 서비스하기 위해 다음의 세 가지 파일로 나눠 생성하였다.


● Action

애플리케이션의 모든 Action은 Struts의 org.apache.struts. action.Action을 확장한다. 이 Action 클래스는 애플리케이션의 Model 레이어 인터페이스를 제공하면서 비즈니스 로직 주변의 래퍼로 작동한다. Action 클래스는 각각의 구현을 perform 메소드에 정의한다. perform 메소드는 ActionForward의 값을 리턴하며, ActionForward의 값은 strust-config.xml 주소로 맵핑된다. 최초 사용자의 요청은 Action으로 들어오며, forwardedPage를 사용자에게 넘겨주게 된다.


● forwardedPage(JSP)

Action의 ActionForward의 값, 즉 맵핑된 주소에 해당하는 JSP 페이지 관련 부분이다. 사용자에게 직접적으로 보이는 뷰로 특히 이 부분에서 Scraper로 기존 시스템의 페이지를 스크랩하며, 스크랩한 정보의 수정 사항(줄 단위의 삽입/삭제에 대한 오퍼레이션)을 포함하여 처리한다. 수정 정보, 즉 코드의 삽입/삭제에 대한 부분은 작업시 HtmlHandle에서 생성하는데 다음 항목에서 살펴보도록 하자.


● FrontEndPage

FrontEndPage는 Struts와는 관련이 적다. Struts의 경우 주소에 일반적으로 ‘.do’로 web.xml에 맵핑하여 사용한다. 이러한 부분을 그대로 사용하면, 마이그레이션된 페이지의 링크가 바뀜으로 해서 기존 페이지를 수정해야 하는 번거로움이 있다. Front EndPage는 이를 해결하기 위한 파일이다. FrontEndPage를 생성함으로써 기존의 페이지 링크에 맞게 맵핑하고, 링크가 바뀌는 것을 막을 수 있다.



HtmlHandle의 구현

사용자 삽입 이미지

<리스트 9>는 HtmlHandle의 구현 부분이다. HtmlHandle은 크게 두 가지 역할을 하는데 한 가지는 수정 전, 후의 코드에 대해 삽입/삭제된 줄 정보를 알아내고 저장한다. 다른 하나는 이 정보를 이용하여 수정 전의 소스를 수정 후의 소스로 변경하는 역할이다. <리스트 9>는 수정된 부분에 대한 코드로 프로젝트 초기에는 이 부분의 설계에서 Html 소스의 골격이 서버에서 동적으로 변화하는 부분을 수용하려고 ParseTree의 노드를 비교, 노드 단위로 바뀐 부분을 잡아 연산하도록 했다. 하지만 이는 정확히 변경된 부분을 집어내기엔 다소 버겁다. 그래서 앞에서 설명한 줄 단위 비교로 바뀐 부분을 잡아내고, 이에 대한 소스 생성을 할 수 있도록 변경하였다.


Request Filter의 구현

웹 서비스시 사용자의 페이지 요청이 마이그레이션된 서버에 있는지, 기존 웹 서버에 있는지를 판단한다. 기존 웹의 정보라고 판단되면 Default Servlet으로 포워딩하여 처리하도록 했다.

사용자 삽입 이미지


<리스트 10>은 로컬상의 web.xml 파일에 맵핑된 페이지 정보와 로컬상의 페이지 이외의 이미지나 기타 파일이 존재하는지에 대한 검증 작업을 보여준다. <리스트 10>과 같이 작은 String 연산으로 맵핑 정보를 판단하는 코드로 구현한 이유는 웹 서버인 톰캣이 정확한 맵핑 정보를 얻을 수 없었기 때문이다. Request Filter는 전처리와 후처리를 가능하도록 Sevlet Filter로 구현하였다.


Default Servlet의 구현

web.xml에 맵핑되어 있는 페이지 이외의 페이지를 스크랩하여 사용자에게 보내주는 역할이다. Post, Get 등의 Http 메소드를 포함하는 Request를 구현하여 기존 웹 페이지가 사용자의 요청에 응할 수 있도록 하였다. web.xml에 url-pattern이 ‘/’ 로 맵핑된다.

<리스트 11>은 사용자 요청에서 Post 및 Get 메소드에 대한 판단을 하며, 응답 부분은 Scraper의 구현과 동일하다. 각 사이트별 사용 언어의 인코딩이 다르기 때문에 이에 대한 처리가 필요한데 모든 부분을 ‘utf-8’을 기준으로 변환하여 처리하였다. ‘utf-8’을 기준으로 변환한 것은 인코딩 문제가 Html에 대해 한정된 것이 아닌 Xml 파싱에서도 나타났기 때문이다. Xml 파싱의 경우 ‘utf-8’에 안정적으로 처리가 가능하다. 다음 시간에는 웹2.0 기술 확장과 Ajax 기본 기능의 구현, 이크립스 플러그인을 기반으로 사용자 편의성 향상에 대해 알아본다.


리스트 11> Default Servlet의 구현


참고 자료
1.Java 디자인 패턴 입문
2.프로그래머를 위한 서블릿 / JSP
3.http://kr.sun.com/developers/techtips/e2005_0222.html#2
4.http://struts.linuxstudy.pe.kr/
5.http://network.hanbitbook.co.kr/view.php?bi_id=1141 Web 2.0이란 무엇인가
6.http://network.hanbitbook.co.kr/view.php?bi_id=1148
7.http://network.hanbitbook.co.kr/view.php?bi_id=1152 8.http://www-12
8.ibm.com/developerworks/kr/library/j-sr2.html 필수 자바 라이브러리 - 출처 : IBM


제공 : DB포탈사이트 DBguide.net
"Java" 카테고리의 다른 글
  • Java DOM Tutorial (0)2008/03/21
  • Java Swing을 이용해서 제작한 마방진 (3)2007/10/13
  • 웹2.0(Ajax)과 아키텍처의 구현 (0)2007/05/18
  • 리팩토링을 이용한 자바 성능 최적화 기법 (0)2007/05/18
  • 유연한 참조로 메모리 누수 막기 (0)2007/05/18
2007/05/18 11:53 2007/05/18 11:53
Posted by webdizen
Tags Ajax, 마이그레이션, 아키텍처, 웹 2.0, 프레임 워크
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2999

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Java2007/05/18 11:46

리팩토링을 이용한 자바 성능 최적화 기법

분야별 특성에 맞춘 Programming Optimization


프로그래밍 최적화. 코드 몇 줄을 줄이고 실행 속도를 높이기 위해 머리를 쥐어짜던 시절이 있었다. 이미 추억 저편으로 멀어진 그 기억 속에서는 그런 것이 바로 프로그래밍 최적화였다. 그럼 하드웨어의 성능이 예전의 슈퍼컴퓨터와 맞먹을 정도로 높아진 지금, 프로그래밍 최적화가 대체 무슨 의미를 가질 수 있을까? 프로그램의 속도가 조금 빠르거나 느린 정도라면 이제 거의 체감할 수 없는 상황이 되지 않았는가. 성능이 아주 떨어지지만 않는다면 이제 약간의 실행 속도의 차이는 무의미해진 지 오래다. 이처럼 시대가 변했다고는 하지만 분명히 프로그래밍을 위해 갖춰야 할 최적화 항목들은 여전히 존재한다. 코드의 수를 줄이는 것이 아니더라도 OOP적 개발을 위한 기법들이 필요하고, 보다 개선된 프로그램을 위한 리팩토링도 필요하다. 심지어 예전처럼 속도를 따져야 하는 분야도 있다. 바로 임베디드 분야이다. 이번 특집에서는 각 개발 분야에서 중요하게 다뤄져야할 최적화 기법들에 대해 알아본다.


기획·정리
| 정희용 기자 flytgr@imaso.co.kr



리팩토링을 이용한 자바 성능 최적화 기법

허광남 | GS홈쇼핑 EC정보팀 과장


리팩토링, 복잡다단해지는 현대의 소프트웨어 개발에서 이 단어는 점점 중요한 위치를 차지해 가고 있다. 이제 리팩토링은 진정한 개발자의 덕목 중에 하나라고 단언할 수 있을 정도다. 리팩토링을 한다는 것은 개선에 대한 의지가 있음을 뜻하고, 좀 더 나은 코드, 구조, 프로세스를 지향한다는 의미가 된다. 리팩토링으로 소프트웨어의 성능을 직접적으로 높이지는 못 한다. 하지만 코드의 가독성을 증대시켜, 생각하는 프로그래머들의 머릿속 성능을 높여준다. 3부에서는 리팩토링 방법들에 대해 알아본다.



햄버거나 커피 등을 살 때, 또는 백화점이나 편의점에서 물건을 살 때, 우리는 1회용 물건을 쓰는 것에 대한 세금을 낸다. 1회용 물건을 쓰면 환경이 그만큼 빨리 피폐해지기 때문이란다. 그게 사실인지 아닌지 모르겠지만, 내 돈이 나가는 것은 용납이 안 된다. 1회용품의 편리함. 그 반대급부로 만들어지는 쓰레기 처리에 따른 비용을 지불한다고 하는데, 영 맘에 안 든다.

혹시 프로그램을 짤 때도 1회용 프로그램을 짠다는 생각을 해본 적이 있는가? 그런데 우리는 1회용 프로그램을 짜도 세금을 내지 않는다. 다행일까? 1회용 프로그램이 환경 자원을 소모시키지는 않는다. 다만 1회용 프로그램은 쓰레기를 양산한다. 그때 그때 필요한대로 찍어낸 프로그램은 수많은 중복코드를 양산해낸다. 재활용하지 않는 습관 탓에 시스템이라는 환경이 무거워지고 손이 많이 가도록 바뀌는 것이다.

재활용성은 객체지향 프로그램의 핵심원리 중의 하나이다. 재활용성을 높인다는 것은 찍어낼 때 사용하는 템플릿을 얘기하는 것이 아니다. 오히려 업무나 기능을 제어 가능한 곳에 집약시켜서 관리할 수 있도록 시스템 전체의 청결한 상태를 유지하는 것이다. 이리저리 산재된 중복 코드를 정리하는 것이 핵심이다. 이 때 필요한 기술이 리팩토링이다. 이쯤 얘기하면 리팩토링은 정리 정돈에 비견된다. 군대에서 총기수입을 하는 것과도 같고 집에서 설거지를 하는 것과도 같다.


리팩토링이란

Refactoring (Re + Factor + ing) 영어 단어를 요소별로 나눠보면 요소들을 재구성한다는 뉘앙스를 받을 수 있다. 이는 마틴 파울러의 책에서 비롯된 단어인데, 책에 있는 리팩토링의 정의를 보면 다음과 같다.

“리팩토링은 외부 동작을 바꾸지 않으면서 내부 구조를 개선하는 방법으로, 소프트웨어 시스템을 변경하는 프로세스이다.” 마틴 파울러, 리팩토링, P10. 대청출판사 책에 이어서 나오는 내용은 버그가 끼어들지 않도록 주의하면서 코드를 작성한 후에 더 나은 디자인으로 개선하는 방법이라고 한다. 디자인을 먼저 한 후 코드를 만드는 것이 아니라 일단 돌아가는 코드를 작성하고, 그 후에 그 코드가 더 좋은 구성을 갖도록 바꾼다는 것이다. 우리들의 코딩 관행을 돌아보면, 일단 돌아가는 프로그램을 짠다. 그리고? 끝이다. 그 다음으로 넘어간다. 정리? 남은 사람이 알아서 할 것이다. 남은 사람이 자기 밖에 없다면? 날 잡아서 정리하거나, 회사 옮긴다.


리팩토링을 하는 이유

야심찬 초급 개발자가 자주하는 것 중에 하나가 이전 소스에 대한 비평이다. “도대체 어떻게 이렇게 소스를 짤 수 있지. 발로 짜도 이것보다는 낫겠네. 왜 이렇게 if else가 많은 거야. 이 소스 이해할 시간 있으면 차라리 다시 짜고 만다.” 그래서, 다시 짠다. 그리고 오픈하면 이것 저것 버그 리포트와 요구사항이 들어온다. 이것 저것 예외 처리를 해주다 보면 내가 짠 코드지만 보기 싫어진다. 어느 정도 서비스가 안정적으로 돌아가도록 소스를 수정해 놓으니, 이런, 전에 내가 막 뭐라고 했던 이전 개발자의 소스와 별반 차이가 없다.

“제길, 다음 후임이 누가 될지는 몰라도 내 욕 무진장 하겠군.” 문서라도 잘 주면 모르겠지만, 처음 개발할 때 보고했던 문서 그대로다. 요구사항과 수정을 통해서 변경된 내용을 문서에 업데이트하질 못했다. “할 시간이 있어야지.” SM(System Maint enance)분야에서는 거의 이렇게 사는 것이 보통이다.

이전 사람이 만든 소스에서 버릴 것은 거의 없다. 정리가 안 되서 몇 달간 목욕 못한 모습일 뿐이지, 처리할 수 있는 모든 경우의 수는 그 안에 다 가지고 있다. 이런 코드를 새로 짠다는 것은 그 모든 경우의 수를 처음부터 다시 감수하겠다는 의미가 된다.
이전 소스를 씻기고 다듬는 것이 소스 수정을 위한 필수 과정이다. 정리하지 않고 계속해서 소스를 추가해 가는 일은 운동하지 않고 계속해서 먹어대는 것과 같이 시스템을 비만상태로 만들어간다. 움직임이 점차 둔해질 것이다. 정리 안 된 방처럼 발 디딜 팀이 없는 소스가 될 것이다.

무엇인가 소스의 변경이 필요할 때, 기능 추가나 삭제, 수정 작업이 일어날 때 소스의 리팩토링은 포장이사처럼 편하게 작업하도록 도와준다. 리팩토링은 소스의 중복된 부분을 모듈화 시켜준다. 모듈화는 입출력이 명확하기 때문에 이식성을 높여준다. 중복을 제거한다는 것은 시스템의 칼로리를 빼는 것과 같다. 시스템의 복잡도, 즉 코드를 읽는 사람의 머리가 열받는 정도를 낮춰준다. 물론 그렇다 해도 이사 자체는 귀찮은 일이다.


리팩토링을 위한 도구

리팩토링은 그로 인해 영향 받는 프로그램의 수가 적을 대에만 수작업으로 작업해야 한다. 사실 리팩토링을 수작업으로 한다는 것은 추천하지 않는다. 좋은 개발 환경이 있는데 사서 고생할 필요가 없는 탓이다. 리팩토링을 위한 좋은 툴이 많이 나왔다. 일단 통합개발환경(IDE, Integrated Development Environment)을 준비한다. 요즘의 자바 개발 시 많이 사용되는 IDE는 기본적으로 리팩토링을 지원한다.

리팩토링과 함께 진행되어야 할 JUnit 테스트케이스 자동 생성도 같이 지원되고 있다. 리팩토링 작업을 할 경우 여러 줄의 코드들이 수정된다. 이때 영향을 받는 프로그램들을 모두 불러내서 수작업으로 수정할 경우 리팩토링에 대한 공수가 많이 필요한 탓에 감히 리팩토링에 대한 엄두를 낼 수 없다. 하지만 요즘 통합 개발 환경을 지원하는 개발 도구들은 변경 받는 파일들의 목록과 변경 전 후의 코드 비교, 자동 변경 기능을 지원한다. 덕분에 리팩토링에 드는 수고가 전혀 수고로 생각되지 않을 정도다.


리팩토링 진행 방법

리팩토링하는 이유와 리팩토링 도구까지 알아보았으니 이제 리팩토링 방법에 대해 알아볼 차례다. 주저리 주저리 방법들을 늘어놓을 수 도 있겠지만 개발자는 코드로 얘기한다. 바로 이클립스에서 리팩토링을 사용하는 방법을 설명하도록 하자.


<리스트 1> 리팩토링 샘플


리팩토링에 대한 간단한 예를 들기 위해서 <리스트 1>을 보며 설명하겠다. 7~11번 줄의 코드가 13~17번 줄의 코드와 유사한 것을 알 수 있다. 중복이 계속되는 것은 일정한 패턴을 갖고 있는데 중복이 심해지면 패턴 변경에 따른 공수가 많이 필요하므로 소스의 유연성이 떨어지게 된다. 때문에 반복되는 패턴을 메소드화 시켜서 쉽게 코드를 읽을 수 있도록 한다.

사용자 삽입 이미지
<화면 1> 반복되는 부분, 메소드 추출의 대상


사용자 삽입 이미지
<화면 2> 메소드 추출(Extract Method)


이클립스에서 패턴부분을 선택하고, 오른쪽 버튼을 눌러 콘텍스트 메뉴를 열면 중간 위치에 [Refactor…]라는 메뉴가 보인다. 확장 메뉴에서 [Extract Method…]를 선택하면 <화면 2>와 같은 다이얼로그 창이 뜬다. ‘doQuery’라고 메소드명을 입력한 뒤에 파라미터들을 확인한다.

화면 아래쪽의 버튼 중 [Preview]를 클릭하면 <화면 3>과 같이 미리보기 창으로 바뀐다. 이때 화면에 표시되는 정보들이 기가 막힌다. 리팩토링을 통해서 변경되는 소스의 비교와 상단에는 이 리팩토링에 영향을 받는 소스들과 메소드명까지 친절하게 알려준다. 게다가 이클립스가 모두 다 자동으로 바꿔준다.

사용자 삽입 이미지
<화면 3> 리팩토링 결과 미리보기



<화면 4>에서는 다이얼로그에서 만든 doQuery() 메소드의 내용을 볼 수 있다. 소스 비교란의 맨 오른쪽에 있는 네모는 소스 전체에서 변경이 일어난 부분을 표시한 것이다.

사용자 삽입 이미지
<화면 4> 리팩토링으로만들어진 메소드


비교가 끝났다면 [OK] 버튼을 클릭해서 리팩토링을 실행한다. 소스 리팩토링을 마친 뒤에 doQuery() 메소드를 보면, <화면 5>처럼 파라미터가 Connection conn, int seq 두 개임을 알 수 있는데, 여기에 하나가 더 필요하다. 바로 쿼리 부분인데, 이것을 파라미터로 받아야 비로로 doQuery()가 공용으로 쓰일 수 있게 된다.

사용자 삽입 이미지
<화면 5> 리팩토링으로 만들어진 약간 아쉬운 메소드


QUERY_MOVE라는 상수를 파라미터로 대치한다. 이 상수에 마우스 오른쪽 버튼을 클릭한 뒤에 [Refactor]-[Introduce Para meter] 메뉴를 실행시키면 <화면 6>과 같은 다이얼로그 창을 볼 수 있다. 새로운 파라미터 이름을 ‘query’로 정하고 우측의 [up] 버튼을 클릭해서 파라미터의 위치를 조정한다. 파라미터의 변경은 메소드의 모습인 시그니처(signature)를 변경하는 것이다.

사용자 삽입 이미지


마찬가지로 [Preview] 버튼을 클릭하면 <화면 7>과 같이 리팩토링 전후의 소스를 비교할 수 있다.

<그림 7>에서 [OK] 버튼을 클릭해서 만들어진 doQuery() 메소드는 반복되는 쿼리 실행 부분을 메소드 추출(Extract Met hod)과 파라미터로 빼기(Introduce Parameter) 리팩토링을 이용해서 만든 것이다. <리스트 2>는 그것을 이용해서 바뀐 소스의 모습이다.

사용자 삽입 이미지
<그림 7> 파라미터로 만들기 적용하기 전 미리보기


<리스트 2> QUERY_DELETE 부분 리팩토링 과정


<리스트 2>는 아직 변경 중인 샘플코드이다. 앞서 만든 doQuery() 메소드를 이용해서 쿼리만 다른 것을 보내면 된다. 필자가 추가한 8번 줄은 10~14번 줄과 동일한 기능을 수행하게 된다. 코드를 정리하면 다음과 같이 된다.


<리스트 3> QUERY_DELETE 부분 리팩토링 후


하단의 구문이 지워지면서 이 deleteArticle() 메소드 내의 PreparedStatement pstmt 선언은 불필요하기 때문에 삭제했다. 처음 보았던 소스에서 많이 정리되었다. 정리를 하고 보니 deleteArticle() 메소드를 호출하는 곳에서 비슷한 기능을 하는 부분을 볼 수 있다.


<리스트 4> 리팩토링 적용 범위 확대


<리스트 4>의 16~20번 줄을 보면 앞서 추출한 메소드 doQuery()로 변경할 수 있을 것 같다. 그럼 코드는 <리스트 5>와 같이 수정될 것이다.


<리스트 5> QUERY_DEL_SEQ_FILE 부분 리팩토링 과정



이렇게 정리하고 난 후에 다시 전체적인 코드를 생각해보면 두 개의 메소드가 불필요하다 생각이 든다. 즉 <리스트 6>과 같이 deleteArticles()와 deleteFiles() 메소드를 지우고 바로 doQuery() 를 호출하도록 바꿀 수 있을 것이다. <리스트 6>은 리팩토링을 통해 최종적으로 정리된 소스이다.


<리스트 6> 리팩토링 적용으로 개선된 코드
[COE java]
// password 확인
if (confirmPassword.equals(MASTER_PASSWORD)
|| confirmPassword.equals(article.getPassword())) {
// db에서 삭제 - 삭제 테이블로 이동
doQuery(conn, QUERY_MOVE, seq);
doQuery(conn, QUERY_DELETE, seq);
// memo 삭제 생략
// file db에서 삭제 - sts 값 0 로 변경
doQuery(conn, QUERY_DEL_SEQ_FILE, seq);
// file 삭제 생략
} else {
resourceName = "/jsp/error.jsp";
throw new Exception(CommonUtil.k2a("잘못된 비밀번호"));
}
[/code]

앞에서 보았던 소스의 if else 구문과 비교해보면 doQuery() 라는 공통으로 사용할 수 있는 메소드와 5줄이 늘어났지만 deleteArticle(), deleteFiles() 두 개의 메소드가 사라졌다. 이전 소스와 비교해보면 메소드 구성은 <화면 8>과 같이 변경되는 것을 알 수 있다.

추가된 메소드는 +화살표, 제거된 메소드는 화살표로 표시되고 변경된 메소드는 그냥 검은 화살표로 표시된다. 화면 아래쪽에 표시되는 소스 비교하는 곳을 보면 더욱 명확하게 알 수 있다.

지금까지 샘플 소스의 구조를 개선하면서 두 가지 리팩토링 기법에 대해 알아보았다. 이 외에도 많은 기법들이 리팩토링 책에 소개되어있고, 이클립스에도 더 많은 리팩토링 기능이 지원된다.

사용자 삽입 이미지
<그림 8> 리팩토링 전 후 메소드 비교


리팩토링 경험담

필자는 이 글을 쓰고 있는 지금 큰 프로젝트를 진행하고 있다. 6년간 하나도 버려지지 않고 운영되면서 그때그때 패치된 페이지를 스프링 프레임워크에 맞춰서 바꾸는 작업이다. 그런데, 작업을 하는 동안 필자가 간과한 것이 있었다. 그렇게 복잡하게 얽히고설킨 페이지를 스프링 프레임워크의 새로운 바닥부터 하나씩 쌓아 올린 것이다. 기존에 운영하고 있는 소스에서 하나씩 뜯어서 새로운 토양으로 옮겨심기를 한 것이다. 이것은 재개발에 가까운 것이었고, 굉장히 많은 시간이 필요했다. 만약 옮겨야 할 소스를 기존의 토양 위에서 조금씩 리팩토링한 후에 옮겼다면 오히려 많은 시간을 절약할 수 있었을 것이다.

실수했다고 생각하는 부분은 다음과 같다. 우선적으로 모든 기능을 다 옮겨올 때까지 신규 페이지는 아직 미완성이다. 하지만 기존의 페이지 내에서 리팩토링을 한다고 하면 이미 모든 기능과 데이터를 다 갖고 있는 상태이다. 다른 파트에서 데이터가 필요하다고 할 때에도 현재 갖고 있는 데이터에서 데이터를 뽑아내서 보다 빨리 전달할 수 있을 것이다.

두 번째로 시간의 압박이다. 기존의 페이지는 언제든지 답이 나온다. 하지만 신규페이지는 모든 테스트를 마칠 때까지 계속 기다리라고 얘기해야만 한다. 바닥부터 모든 것들에 대해서 테스트를 만들어야 하는 탓에 더 많은 테스트 코드들이 필요하다. 여기에도 만만치 않은 시간이 투입된다.

세 번째는 애플리케이션에 대한 자신감이 떨어진다는데 있다. 맥가이버도 아닌데 시간에 쫓기면서 개발할 경우 만들어진 소스는 분명히 수많은 버그를 품고 있을 가능성이 높다. 그 값이 절대 정확하다고 이야기하기 힘들다. 하지만 리팩토링을 통해서 내부로부터 개혁해 나갈 경우 빠진 것 없이 소스를 재구성할 수 있기 때문에 안정된 기반에서 작업할 수 있을 것이다.

전산의 불문율 가운데 유명한 것이 하나 있다. ‘잘 돌아가는 것은 손대지 마라.’ 칼퇴근을 위해서 절대 절명으로 필요한 말이다. 이렇게 관리되는 소프트웨어의 품질은 논하기 힘들다. 그냥 먹고 살기 위한 프로그램과 그것을 관리하는 직장인이 되어버리게 된다. 반면에 리팩토링의 기본 사상은 개선을 위한 노력이다. 막무가내 개선이 아니라 현명한 개선을 위한 방법을 제시하고 있고, 친구격인 테스트 케이스가 그 안전장치가 되어 준다. 한 순간의 품질이 아닌 지속적인 소프트웨어의 건강을 생각한다면 꾸준히 리팩토링으로 손질할 필요가 있다. 그것이 끝없이 변하는 웹 애플리케이션과 같은 소프트웨어일 경우는 더욱 그렇다.
개선을 위한 작은 몸짓에 진정한 프로그래머가 되고 싶은 독자들을 초대한다.


참고 자료
1. 리팩토링, 마틴파울러, 윤성준,조재박 역, 대청, 2002년3월
2. 패턴을 활용한 리팩터링, 죠슈아 케리에브스키, 윤성준,조상민 역, 인사이트,
2006년7월


리팩토링 관련 사이트
1. http://www.refactoring.com/
Refactoring Home Page
2. http://xper.org/wiki/xp/ReFactoring
김창준 님의 Refactoring에 관한 정보
3. http://c2.com/cgi/wiki?CodeSmell
Code Smell
4. http://xper.org/wiki/xp/CodeSmell
Code Smell 번역
5. http://www.okjsp.pe.kr/lecture/ide/ecli ··· ing.html
Eclipse의 refactoring기능
6. http://www.okjsp.pe.kr/lecture/ide/ecli ··· all.html
Eclipse 시작하기


제공 : DB포탈사이트 DBguide.net
"Java" 카테고리의 다른 글
  • Java Swing을 이용해서 제작한 마방진 (3)2007/10/13
  • 웹2.0(Ajax)과 아키텍처의 구현 (0)2007/05/18
  • 리팩토링을 이용한 자바 성능 최적화 기법 (0)2007/05/18
  • 유연한 참조로 메모리 누수 막기 (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ④ (0)2007/05/18
2007/05/18 11:46 2007/05/18 11:46
Posted by webdizen
Tags Java, Programming Optimization, Refactoring, 리팩토링
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2998

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Java2007/05/18 11:32

유연한 참조로 메모리 누수 막기

난이도 : 중급

Brian Goetz, Principal Consultant, Quiotix


2006 년 1 월 24 일

지난 달에는 약한 참조(weak references)에 대해 설명했다. 이번 달에는 또 다른 형식의 참조 객체(Reference)인 유연한 참조(soft references)를 설명한다. 이것 역시 메모리 사용을 관리하고 잠재적인 메모리 누수를 없애는 보조 가비지 컬렉터이다.
가비지 컬렉션으로 자바 프로그램은 메모리 누수에 대해 면역성을 갖는다. 적어도 좁은 의미의 "메모리 누수 "의 경우가 그렇다는 것이다. 하지만 자바 프로그램의 객체 수명주기의 문제를 완전히 무시할 수 있다는 의미는 아니다. 자바 프로그램에서의 메모리 누수는 객체의 수명주기에 대한 주의가 부족할 때나 객체 수명 주기를 관리하는 표준 방식들을 파괴했을 때 발생한다. 예를 들어, 지난 시간에는 우리는 객체의 수명주기를 분명히 정하지 못하면, 메타데이터와 일시적인 객체를 제휴하려고 할 때 의도하지 않은 객체 보유를 야기하게 된다는 것을 배웠다. 객체 수명주기 관리를 무시하거나 메모리 누수로 진행될 수 있는 다른 이디엄들도 있다.

객체 로이터링(Object loitering)

메모리 누수의 한 형태인 객체 로이터링은 Listing 1의 LeakyChecksum 클래스가 설명하고 있다. 이것은 getFileChecksum() 메소드를 제공하여 파일 컨텐트의 체크섬을 계산한다. getFileChecksum() 메소드는 파일 내용들을 버퍼로 읽어 들여 체크섬을 계산한다. 보다 단순한 구현은 버퍼를 getFileChecksum() 내의 로컬 변수로 할당하는 것이다. 인스턴스 필드에 버퍼를 캐싱하여 메모리 혼합을 줄인다. 이러한 "최적화 " 로는 부족하다. 객체 할당이 더 낫다. (또한 버퍼를 로컬 변수에서 인스턴스 변수로 승격시키면 추가적인 동기화 없이 더 이상의 쓰레드 보안 없는 클래스로 만든다. 단순한 구현은 getFileChecksum()이 synchronized로 선언될 필요가 없고, 동시에 호출될 때 더 나은 확장성을 제공한다.)


Listing 1. "object loitering" 클래스



이 클래스는 많은 문제들을 갖고 있지만 메모리 누수에 집중해 보자. 버퍼를 캐싱하려는 결정은 이것이 프로그램 내에서 여러 번 호출될 수 있고 재할당 보다는 버퍼를 재사용하는 것이 더 효율적이라는 가정에서 나온 것이다. 하지만 결과적으로 버퍼는 결코 릴리스 되지 않는다. (LeakyChecksum 객체가 가비지 컬렉션이 되지 않는 한) 프로그램을 통해 언제나 접근할 수 있기 때문이다. 더욱 나쁜 것은 이것은 줄어들 수 없기 때문에 LeakyChecksum은 영구적으로 버퍼를 보유하게 된다. 이는 가비지 컬렉터에 많은 부담을 주고 더 많은 컬렉션을 요구한다. 앞으로의 체크섬을 계산할 목적으로 큰 버퍼를 사용한다면 가장 비효율적인 일이 아닐 수 없다.

LeakyChecksum에서 문제의 원인은 버퍼가 논리적으로 getFileChecksum() 연산에만 국한되기 때문이다. 하지만 수명 주기는 인공적으로 늘어나서 인스턴스 필드까지 진입한다. 결과적으로 JVM 대신에 이 클래스가 버퍼의 수명 주기를 관리해야 한다.


유연한 참조(soft reference)

이전 글에서 객체가 프로그램에 의해 사용되는 동안 객체에 접근하는 대안 방식을 약한 참조(weak references)가 제공하는 방법을 배웠다. 하지만 수명은 연장하지 않았다. Reference의 또 다른 하위 클래스인 유연한 참조(soft reference)는 다르면서도 관련된 목표를 수행한다. 약한 참조의 경우 애플리케이션이 가비지 컬렉션을 방해하지 않는 참조를 만들도록 한 반면 유연한 참조에서는 애플리케이션이 몇몇 객체를 "소모 가능한 것 "으로 위임하여 보조 가비지 컬렉터를 모으도록 하고 있다. 애플리케이션이 메모리를 사용하는지의 여부를 파악하는데 가비지 컬렉터가 제 기능을 하기 때문에 가용 메모리의 적절한 사용법을 결정하는 것은 애플리케이션에 달려있다. 애플리케이션이 객체 보유에 대해 그릇된 결정을 내린다면 퍼포먼스가 나빠진다. 왜냐하면 가비지 컬렉터는 애플리케이션이 메모리를 초과하여 실행되는 것을 막아야 하기 때문이다.

캐싱은 일반적인 퍼포먼스 최적화 방법이다. 애플리케이션이 이전 계산의 결과를 재사용 하는 것이 가능하다. 다시 계산을 할 필요가 없다. 캐싱은 CPU 활용과 메모리 사용은 서로 상쇄된다. 이상적인 균형은 얼마나 많은 메모리를 사용할 수 있는가에 달려있다. 너무 적은 캐싱으로는 원하는 퍼포먼스를 얻을 수 없다. 또한, 캐싱이 너무 많으면 퍼포먼스가 타격을 받는다. 너무 많은 메모리가 캐싱에 소비되기 때문에 다른 것에 쓰일 수 있는 것이 충분치 않기 때문이다. 메모리 수요를 결정할 때에는 애플리케이션 보다 가비지 컬렉터가 더 나은 위치에 있기 때문에 이러한 결정을 내릴 때에는 가비지 컬렉터의 도움을 받는 것이 합리적이다. 바로 이것이 유연한 참조가 하는 일이다.

객체에 유일하게 남아있는 참조가 약한 참조이거나 유연한 참조라면 그 객체는 유연하게 접근할 수 있다. 가비지 컬렉터는, 약하게 접근 할 수 있는 객체에 수행하는 것 처럼 이러한 객체들을 공격적으로 모으지 않는다. 대신 메모리가 정말 필요할 경우에만 유연하게 접근할 수 있는 객체를 모은다. 유연한 참조는 가비지 컬렉터에게 "메모리가 그렇게 적지 않다면 이 객체를 보유하고 싶다. 하지만 메모리가 부족하면 계속 진행하여 메모리를 모으면 처리하겠다. "라는 명령하는 방식이라고 할 수 있다. 가비지 컬렉터는 OutOfMemoryError를 던지기 전에 모든 유연한 참조를 제거해야 한다.

캐싱된 버퍼를 관리하는 유연한 참조를 사용하여 LeakyChecksum의 문제를 픽스할 수 있다.(Listing 2). 이제 메모리가 요구되지 않는 한 버퍼가 보유 되지만, 가비지 컬렉터에 의해 사용될 수 있다.


Listing 2. 유연한 참조로 LeakyChecksum 해결하기



캐시

CachingChecksum은 유연한 참조를 사용하여 한 개의 객체를 캐싱하고 JVM이 상세를 핸들하도록 하였다. 이와 비슷하게, GUI 애플리케이션에서 비트맵 그래픽을 캐싱하는데 유연한 참조가 사용된다. 유연한 참조가 사용될 수 있는지의 열쇠는 애플리케이션이 캐싱되는 데이터의 손실에서 회복될 수 있는지에 달려있다.

한 개 이상의 객체를 캐싱해야 한다면 Map을 사용하면 된다. 하지만 유연한 참조를 적용하는 방식에는 선택권이 있다. 캐싱을 Map> 또는 SoftReference>로서 관리할 수 있다. 대게는 후자 옵션이 사용된다. 컬렉터 작업이 수월하고 메모리 수요가 높을 때 전체 캐시를 수월하게 사용할 수 있기 때문이다. 가끔 캐시를 구현할 때 유연한 참조 대신 약한 참조가 사용되지만 이렇게 되면 캐싱 퍼포먼스는 나빠진다. 실제로, 약한 참조는 객체가 약하게 접근 가능한 것으로 된 후에 빠르게 제거될 것이다. 소소한 가비지 컬렉션들은 자주 실행되기 때문이다.

퍼포먼스를 위해 캐싱에 많이 의존하는 애플리케이션의 경우 유연한 참조는 너무 무디다. 유연한 종료, 복사, 트랜잭션 캐싱을 제공하는 보다 고급의 캐싱 프레임웍으로 대체해야 한다는 의미는 아니다. "값싸고 지저분한 " 캐싱 메커니즘으로서는 매력적인 장치이다.

약한 참조와 마찬가지로, 유연한 참조는 제휴 참조 큐를 사용하여 만들어지고, 가비지 컬렉터에 의해 생성될 때 이 참조가 인큐(enqueue)된다. 참조 큐는 약한 참조 만큼 유연한 참조에서는 유용하지 않다. 하지만 적은 메모리에서 애플리케이션이 시작할 때 관리 경고를 만드는데 사용될 수 있다.


가비지 컬렉터가 Reference를 핸들하는 방법

약한 참조와 유연한 참조 모두 (팬텀 참조(phantom references)처럼) 추상 Reference 클래스를 확장한다. 참조 객체들은 가비지 컬렉터에 의해 특별하게 취급된다. 가비지 컬렉터가 힙을 추적하는 과정에서 Reference를 만나면 대상 객체를 마킹 또는 추적하지 않고 대신 알려진 활성 Reference 객체 큐에 그 Reference를 둔다. 트레이싱 후에 컬렉터는 유연하게 참조할 수 있는 객체들을 규명한다. 이러한 객체에는 강한 참조가 존재하지 않지만 유연한 참조는 존재한다. 가비지 컬렉터는 현재 컬렉션에 의해 사용된 메모리 양과 다른 정책 사항에 기반하여 유연한 참조가 이 시점에서 제거되었는지의 여부를 판단한다. 상응하는 참조 큐가 있다면 정리될 유연한 참조가 인큐된다. 남아있는 객체들은 루트 세트로서 취급되고 힙 트레이스는 이러한 루트들을 사용하여 지속되어 유연한 참조를 통해 접근 가능한 객체들이 마킹될 수 있도록 한다.

유연한 참조를 처리한 후에 약하게 접근 할 수 있는 객체 세트가 정의된다. 이 객체에는 강한 참조나 유연한 참조가 존재하지 않는다. 이들은 정리 및 인큐된다. 모든 Reference 유형들은 인큐 되기 전에 정리되어서 사후 클린업을 핸들하는 쓰레드는 대상 객체에 액세스 할 수 없다. 이 같은 이유로 Reference가 참조 큐와 연합하여 사용될 때, WeakHashMap의 Map.Entry가 WeakReference를 확장하는 것 처럼) 적절한 참조 유형을 분류하고 디자인에서 직접 사용하거나 클린업을 필요로 하는 엔터티에 대한 참조를 저장한다.


참조 프로세싱의 퍼포먼스 비용

참조 객체들은 가비지 컬렉션 프로세스에 추가 비용을 부과한다. 각 가비지 컬렉션에서 활성 Reference 객체들의 리스트가 만들어져야 하고 각 참조는 적절하게 처리되어 Reference 당 오버헤드를 각 컬렉션에 추가한다. 이 때 해당 객체가 컬렉팅 되는지의 여부는 상관이 없다. Reference 객체들은 가비지 컬렉션에 속해있고 해당 객체 전에 컬렉팅 될 수 있다. 이 같은 경우는 인큐가 되지 않는 경우이다.


어레이 기반 컬렉션

스택이나 순환식 버퍼 같은 데이터 구조를 구현할 때 어레이가 사용될 때 또 다른 형태의 객체 로이터링이 발생한다. Listing 3의 LeakyStack 클래스는 어레이의 지원을 받는 스택의 구현 모습이다. pop() 메소드에서 상위 포인터가 감소된 후에도 elements는 스택에서 없어진 객체에 대한 참조를 여전히 유지하고 있다. 이 객체에 대한 참조는 프로그램에 의해서도 접근할 수 있다. 그 프로그램이 실제로 그 참조를 다시 사용하지 않더라도 말이다. 이 위치가 push()에 의해 재사용 될 때까지 객체의 가비지 컬렉션을 방지한다.


Listing 3. 어레이 기반 컬렉션에서의 객체 로이터링



이 경우 객체 로이터링에 대한 해결책은 스택에서 제거된 후에 참조를 무효로 하는 것이다. (Listing 3). 하지만 클래스가 자신의 메모리를 관리하는 경우는 매우 드문 상황이다. 더 이상 필요하지 않는 객체를 무효로 하는 것이 좋다. 대부분, 공격적으로 참조를 무효화 하는 것에는 퍼포먼스나 메모리 사용에 있어서 이득이 없다. 형편 없는 퍼포먼스나 NullPointerException을 발생시킬 뿐이다. 이러한 알고리즘의 연결된 구현은 문제가 없다. 연결된 구현에서 링크 노드의 수명(그리고 저장될 객체에 대한 참조)은 객체가 컬렉션에 저장되는 동안 자동으로 묶인다. 약한 참조는 이 문제를 해결하는데 사용된다. 강한 참조 대신에 약한 참조 어레이를 관리한다. 하지만 실제로 LeakyStack는 자신의 메모리를 관리하고 더 이상 필요 없는 객체에 대한 참조가 제거되도록 한다. 어레이를 사용하여 스택이나 버퍼를 구현하는 것은 할당을 줄이는 최적화이지만 구현자에게는 큰 짐이다. 이 어레이에 저장된 참조들의 수명을 관리해야 하기 때문이다.


요약

약한 참조와 마찬가지로 유연한 참조는 애플리케이션에서 객체 로이터링을 방지한다. 애플리케이션이 유연하게 참조된 객체의 손실을 감당할 수 있다면 유연한 참조가 알맞다.

기사의 원문보기

Java theory and practice: Plugging memory leaks with soft references
http://www.ibm.com/developerworks/java/ ··· 246.html



참고자료

교육

"Java theory and practice: 메모리 누수와 약한 참조"
http://www-128.ibm.com/developerworks/k ··· dex.html

"Tuning garbage collection in the HotSpot JVM"
http://www.ibm.com/developerworks/java/ ··· 06304%2F

Reference objects and garbage collection
http://java.sun.com/developer/technical ··· efobj%2F

Java theory and practice
http://www.ibm.com/developerworks/views ··· ctice%3A

The Java technology zone
http://www.ibm.com/developerworks/java/

제품 및 기술 얻기

JTune

http://www.hp.com/products1/unix/java/java2/hpjtune/
"Java" 카테고리의 다른 글
  • 웹2.0(Ajax)과 아키텍처의 구현 (0)2007/05/18
  • 리팩토링을 이용한 자바 성능 최적화 기법 (0)2007/05/18
  • 유연한 참조로 메모리 누수 막기 (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ④ (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ③ (0)2007/05/18
2007/05/18 11:32 2007/05/18 11:32
Posted by webdizen
Tags Java, 메모리 누수, 약한 참조
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2997

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Java2007/05/18 11:26

EJB 기반 프로젝트 수행 가이드 ④

김주현 (ERP 개발자)
2004/04/26

① 세션빈에서의 DB 접근전략 및 엔터티빈 사용시 주의사항
② 세션빈에서의 트랜잭션 관리
③ 비즈니스 프로세스 구현 최적화하기
④ 능률 높여주는 유틸리티를 사용하자

EJB를 사용하는 프로젝트에서 개발속도를 향상시킬 수 있는 유틸리티에 대해 알아보자. 이 유틸리티들은 개발자가 프로젝트를 시작할때 직접 만들어서 사용하는 것이다.

EJB 스펙을 지원하는 개발툴 중 비주얼 카페, 비주얼 에이지 등을 사용해본 개발자라면 이 툴들이 EJB 개발에 많은 도움을 주지만 소위 ‘무겁다’는 단점이 있음을 느꼈을 것이다.

PC 사양이 받쳐줘야 돌릴 수 있다는 것 외에, 이러한 툴들은 전체 개발을 놓고 볼 때 오히려 개발자의 집중에 저해요소로 작용하는 경우도 빈번하다.

따라서 개발자가 선택하게 되는 최종적인 툴은 바로 텍스트 에디터 프로그램이다. 실제로 개발자들을 보면 비주얼 카페나 비주얼 에이지를 기동시키고 코딩하는 것보다 "텍스트 에디터로 모든 것을 해결하기"를 선호하는 경우가 많다.

텍스트 에디터를 사용하면서 문제는 테이블 하나당 INSERT, DELETE, UPDATE, SELECT라는 DML 문을 DB 래퍼 클래스에 코딩할 때 남은 반복적인 작업을 어떻게 해결하느냐가 될 것이다. 이때 개발자 유틸리티가 필요하다.

DB 래퍼 클래스 자동 생성하기
DB 래퍼 클래스는 주로 테이블 하나당 하나씩 만들게 된다. EMP 테이블에 대한 INSERT, DELETE, UPDATE, SELECT 문을 실행시킬 EmpDb라는 클래스를 만든다고 생각해보자. 필요한 SQL문을 실행시키는 메소드가 나열될 것이다.

개발자는 다음 DEPT 테이블에 대해서도 똑같은 작업을 해야한다. 테이블 컬럼 명이 다르기 때문에 Copy & Paste 작업도 여의치 않다.

이쯤되면 해당 테이블의 컬럼명 정보 및 타입, PK 정보 등을 읽어서 자동으로 메소드들을 생성시켜 주는 유틸리티를 만들어야겠다는 생각이 들 것이다. PC에서 간단히 실행만 시키면 DB래퍼 자바 파일을 만들어주는 유틸리티가 있다면 작업 속도를 향상시키고 능률을 높일 수 있을 것이다.

이 클래스를 실행시키면 먼저 프로퍼티 파일을 읽어와야 한다. 클래스 안에 직접 테이블 명 혹은 DB URL 등을 타이핑해서 쓰는 것 보다, 프로퍼티 파일 안의 테이블 명, 접속 DB 정보 등을 읽어오도록 함으로써 보다 쉽게 활용할 수 있는 방향으로 만들어야 한다.

다음은 해당 DML 문이 적혀있는 파일을 생성시켜야 한다. 이 작업에는 파일을 생성시켜야 하므로 PrintWriter 클래스도 등장하고 프로퍼티 파일을 읽어올 때 Properties 클래스도 써야 할 것이다.

핵심적인 기능인 SELECT 및 DML문 생성 시에는 어떤 클래스가 사용될까.
DatabaseMetaData 인터페이스의 getColumns 메소드와 getPrimaryKeys 메소드를 이용하도록 하자.

아래는 컬럼 어레이를 리턴하는 메소드이다.

소스보기 1




이 메소드를 호출할 시에 catalog, schemaPattern, tableNamePattern, columnNamePattern을 넘기면(주로 catalog나 shema는 null로 넘겨진다) DatabaseMetaData는 받은 조건으로 검색한 후, 해당하는 컬럼이 있으면 그것의 모든 정보를 ResultSet 형태로 가지고 온다. 이를 컬럼 어레이로 받아오는 것이다.

DatabaseMetaData 인터페이스는 DB의 래퍼 클래스와 같은 역할을 하는 것이다. 이제 이 컬럼 정보로 만드는 INSERT문을 보자.

소스보기 2




실행하면 EmpDb.java 파일의 INSERT 문이 아래와 같이 생성될 것이다.

소스보기 3




INSERT 문을 예로 들었는데, 마찬가지 방법으로 SELECT, DELETE, UPDATE 문도 만들 수 있을 것이다.

다만 SELECT 문의 경우 테이블의 Primary Key를 인자로 받을 때와 받지 않을 때, 만들어지게 되는 쿼리가 다르므로 인자로 받는 Primary Key 갯수마다 각각 SELECT 문을 만들어 볼 수도 있다.

이때는 역시 DatabaseMetaData의 getPrimaryKeys 메소드를 이용해 해당 컬럼의 Primary Key 컬럼을 컬럼 어레이로 받아서 작업한다.

SQL문에 더블 쿼테이션을 붙이는 작업
실제 INSERT, DELETE , UPDATE 문은 위 유틸리티의 활용도가 상당히 높다. 하지만 SELECT 문은 역시 개발자의 손이 필요하다. 조회조건이 다양하고, 사용하게 될 표의 형식이 복잡하기 때문에 하나의 테이블에 대해 Primary Key로만 검색하는 SELECT문은 실제로 잘 쓰여지지 않으며 개발자가 쿼리를 새로 만드는 일이 빈번한 것이다.

개발자들은 주로 자신이 만든 쿼리를 SQL 네비게이터나 골든 뷰 등의 툴을 이용해 실행시켜 정상적으로 동작하고 데이터들이 올바로 표시되는 쿼리라는 것을 확인 한 후에 자바클래스의 메소드에 붙이게 된다.

쿼리를 문자열로 바꾸기 위해 더블 쿼테이션 마크를 붙여야 하는데 이 작업을 일일이 하고 있는 것도 체력 및 시간 낭비이므로 아래와 같은 간단한 유틸리티를 사용해 보자.

quotationSample.html

더블 쿼테이션 마크를 붙이거나 제거하는 이 유틸리티는 StringBuffer가 아닌 String을 바로 사용한다. 이를 보고 String은 Immutable이므로 String에 대해 가변적인 작업을 할 때는 StringBuffer를 쓰되, 최종적으로 StringBuffer.toString() 메소드를 쓰는 것이 자원활용 면에서 더 나은 것이 아닌가 하는 생각이 들 수도 있다.

즉 아래의 형태가 아니라,

예제(1)




아래와 같은 형태로 쓴다는 것인데

예제(2)




결론부터 말하자면 예제(1)의 형태를 추천한다. 코드 가독성 면에서 예제(1)이 훨씬 뛰어나고, StringBuffer와 String간의 퍼포먼스는 크리티컬한 정도로 차이가 나지 않는다. 쿼리 실행시 가장 신경 써야할 부분은 쿼리가 최적화된 경로로 실행돼야 한다는 점이다. 따라서 굳이 StringBuffer를 써서 개발 시 손이 더 많이 가고, 나중에 유지보수 할 때 쿼리 읽기가 힘들어지는 단점을 감수하지 않도록 하자.

마치며
2004년 5월이라는 이 시점에 EJB도 그다지 신기술은 아니다. 하지만 EJB 기술을 쓰는 개발자가 많음에도 실제 프로그램 개발 시의 최적화에 대한 가이드로써 공유된 부분은 많지 않다.

“하루에 1000라인 이상 개발할 수 있다”는 능력도 중요하지만, 짧다면 짧은 4회분의 이번 강의를 통해 필자가 전달하려 노력한 것은 실제 프로젝트를 진행하면서 “노동력을 낭비하지 않게 하는 방법들”이다. 작으나마 도움이 됐으면 하는 바램이다. @


http://www.zdnet.co.kr/techupdate/lectu ··· 2C00.htm
"Java" 카테고리의 다른 글
  • 리팩토링을 이용한 자바 성능 최적화 기법 (0)2007/05/18
  • 유연한 참조로 메모리 누수 막기 (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ④ (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ③ (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ② (0)2007/05/18
2007/05/18 11:26 2007/05/18 11:26
Posted by webdizen
Tags DB 래퍼 클래스, EJB, Java, 더블 쿼테이션
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2995

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Java2007/05/18 11:24

EJB 기반 프로젝트 수행 가이드 ③

비즈니스 프로세스 구현 최적화하기

김주현 (ERP 개발자)  
2004/04/09        

① 세션빈에서의 DB 접근전략 및 엔터티빈 사용시 주의사항
② 세션빈에서의 트랜잭션 관리
③ 비즈니스 프로세스 구현 최적화하기
④ 능률 높여주는 유틸리티를 사용하자

이번 강좌에서는 지난번에 언급한대로 Procedure에 대해 이야기해보겠다.

개발자가 SQL문에 대한 지식이 어느정도 있고 그 외 프로그래밍 언어에 대한 경력이 있다고 해서 언제나 강력한 프로그램을 개발하는 것은 아니며, 그 프로그램이 유지보수 및 재활용성이 뛰어난 프로그램이 될지 장담하지도 못한다. 또 EJB 스펙에 대한 지식이 EJB사용 프로젝트의 성패를 좌우하는 것도 아니다.

개발자의 역량과 함께 정확한 업무분석 및 요구사항 분석에 근거해 효율적으로 계산된 설계가 중요한 변수인 것은 굳이 강조하지 않아도 모두 알 것이다.

‘Stored Procedure’의 사용을 개발자 역량이라는 관점에서 이야기해 보자.

타 시스템에서 사용하기도 하는 공통된 기능, 비즈니스 프로세스의 변화 가능성이 작은 기능, 다수개의 테이블에 정보를 등록 및 변경하게 되는 배치 작업 등은 세션빈 메소드 안에서 쿼리 실행을 통해 구현하는 것을 지양하고 Procedure로 처리할 것을 권한다.

Procedure 사용시 얻을 수 있는 장점 중 첫째로 꼽을 수 있는 것은 성능 향상이다. 보통 조회성 프로그램은 1개의 SELECT문을 실행시키는 것으로 제 역할을 다 하겠지만 실제 업무 시스템에는 조회성 프로그램만 존재하는 것이 아니다.

구매처리 및 회계전표발행, 보험금 수납 등 실제 비즈니스 프로세스에서 내부 쿼리를 이용해 조회해와서 VALIDATION 체크를 하기도 하고, INSERT 또는 UPDATE를 여러 개의 테이블에 할 수도 있다.

단일 비즈니스 프로세스, 즉 하나의 트랜잭션에서 처리해야 하는 세분화된 작업이 다수개일 경우, EXECUTE 되는 쿼리도 여러개 일 수 밖에 없다. 이 메소드를 콜하는 REQUEST가 여러 개이면 그만큼 쿼리 실행도 늘어난다.

이에 비해 한번의 실행으로 세분화된 작업을 진행시키는 Procedure를 이용하면 상당한 성능향상 효과를 보게 될 것이다.

Procedure 사용의 두번째 장점은 유지보수가 손쉽다는 것이다.

비즈니스 프로세스 상의 변화나 오류 등으로 인해 Procedure를 고쳐야 할 일이 발생할 때, 해당 Procedure를 고치고 REPLACE 명령으로 DB에 다시 저장시키는 것 만으로 수정이 완료된다.

Procedure가 사용하는 인자의 갯수가 변경되지 않은 경우라면 이를 호출하는 자바클래스 내부 내용은 손 볼 것이 없다. 만약 Procedure로 처리하지 않고 세션빈 메소드 안에서 처리했다면 어떤 일이 뒤따를까.

자바 소스를 변경시키고, 다시 컴파일해 클래스파일을 서버에 업로드 해야한다. 세션빈 메소드 내에서 변경이 있었다면 세션빈클래스를 컴파일한 다음 JAR 파일을 다시 묶어준 후 업로드한다.

하지만 WAS서버의 클래스로더에 이미 적재되어있는 변경 전 클래스를 변경 후의 클래스로 참조하게 하려면 WAS 서버를 재기동시키는 수 밖에 없다. 그런데 시스템 WAS서버를 개발자 마음대로 재기동시킬 수 있을까? 현재 접속한 사용자들이 있으므로 여의치 않은 일이다. 재기동 가능 시점까지 기다릴 수 밖에 없다.

이렇게 자바소스를 변경하는 것과 비교해 보면 Procedure 유지보수가 얼마나 간편한지 알 수 있을 것이다.

세번째 장점은 보안성과 관련된 것이다. 다른 개발자나 유지보수 인력은 해당 비즈니스 프로세스가 Procedure를 콜하는 형태의 세션빈 메소드만 봐서는 메소드내에서 어떤 비즈니스 프로세스가 처리되는지 알 수 없다. DB에 접속해 Procedure 내용을 확인하고 나서야 알 수 있는 것이다. 비즈니스 프로세스가 DB쪽 TIER에 숨겨져 있는 모듈이 보안성이 더 높다고 말할 수 있다.

이제 직접 Procedure를 만들어 사용하도록 하자. 만약 Procedure에 전혀 개발 경험이 없는 사용자라면 이미 자바코딩으로 구현되어있는 모듈을 Procedure로 바꾸어 보는 작업을 해보는 것도 좋을 것이다.

Procedure도 명명규칙이 있다. 각종 펑션이나 Procedure 등 프로젝트의 담당 DBA가 정하는 명명규칙이 있으니, 그것을 참고해서 이름을 정한다.

필자의 경우에는 자바구문으로 데이터를 핸들링 해놓은 메소드를 Procedure로 모두 고치는 작업을 하기도 하고(이러한 경우는 FULL TEST를 반드시 거친후 자바클래스 실행시 결과물과 Procedure의 결과물이 같은지 확인한다), 개발하는 시점부터 Procedure를 도입하여 개발하기도 한다.

아래와 같은 간단한 샘플 Procedure를 보도록 하자.




위 Procedure는 자신이 받은 4가지 인자를 갖고 TEST_TABLE_01 테이블의 ROW를 검색 후, UPDATE를 치고 TEST_TABLE_02에 INSERT나 UPDATE를 한다.

보다시피 if then/else 구문을 활용할 수 있다는 것 뿐 아니라, 위의 예시에는 없지만 for 루프 구문도 사용할 수 있는 등 쿼리와 절차적 프로그래밍을 동시에 할 수 있다는 강점이 있다.

다음은 몇가지 유의할 점.

자바 코딩을 하다가 Procedure 코딩을 하게 되면 가끔 주석을 "//"로 표기하는 실수도 하게 되는데(아니라고 알고는 있지만 습관 때문에 손은 그렇게 타이핑을 하고 있다) Procedure 작성시 "//"라는 주석처리는 존재하지 않으며, "/* * /"로 처리해야 한다.

또한, 변수 명 앞에 "v_" 를 붙이는 명명규칙을 가지고 가는 것이 좋다. 자칫 오라클 예약어를 실수로 변수명으로 사용할 경우, 컴파일 및 실행시에 에러가 떨어지지는 않지만 실행결과가 개발자의 의도대로 산출되지 않게 되므로 변수명 앞에 이와 같은 문자를 달아주는 습관을 갖는게 좋다.

이제 위의 샘플 Procedure를 호출하게 되는 흐름이 어디서부터 시작되는지 세션빈 메소드부터 살펴보기로 하자.



2회 강좌를 본 사람을 알겠지만, 세션빈 메소드는 UsePcDB라는 이름의 DB래퍼 클래스의 메소드를 호출하는 것으로 임무를 마치고 있다. 그렇다면 UsePcDB 클래스의 usePcMethod 메소드는 어떤 내용일까? 아래 소스를 보자.




CallableStatement는 PreparedStatement를 상속받은 인터페이스로써, Procedure를 실행시킬때 사용된다. usePcMethod 메소드의 역할은 PC_TEST라는 이름의 Procedure를 콜하고 실행시키는 것으로 끝난다.

세션빈은 UsePcDB라는 클래스에게 일을 시키고, UsePcDB라는 클래스는 PC_TEST라는 Procedure에게 일을 시킨 셈이다. 실질적인 처리는 모두 PC_TEST Procedure가 하게 되는 것이다.

Procedure를 세션빈에서 사용하게 되는 패턴은 이것으로 대략 소개를 마친다. 한가지 덧붙일 말은 Procedure를 위와 같이 절차적 프로그래밍을 대신하기 위해서만 사용하는 것은 매우 제한적인 활용이라는 점이다. 성능, 보안, 유지보수의 간편함이라는 장점이 있지만, 자바코딩을 대신하는 듯한 절차적 활용에만 머물지 말고 대량의 데이터를 한번에 처리하는 작업에도 활용하는 것이 좋다.

DBMS는 데이터 I/O 시 블럭단위로 처리하고 있으므로, ROW 하나당 작업을 처리하는 Procedure를 1만번 콜하는 식으로 작업을 하는 것은 블럭단위 처리라는 효율적 도구가 있음에도 내버려두고 있는 셈인 것이다. 1만개의 ROW든 100만개의 ROW든 일괄 처리하게 하는 Procedure를 만들어서 활용하는 게 가장 최적화된 처리일 것이다.

감가상각계산등과 같은 비즈니스 프로세스를 예로 들어보자. 각 기업마다 취득중인 모든 자산 물품에 대해 연결산시 감가상각계산을 하게 되는데, 대부분 프로그램에서는 개별 자산번호마다 계산을 끝낸 후 감가상각계산 테이블에 INSERT 하도록 처리를 해놓고 있다.

그러나 감가상각계산이라는 비즈니스 프로세스가 자산번호마다 계산방법이 다른 것이 아니고 정률법 정액법 등 어떠한 계산방법을 취할 것인지, 해당자산이 그 해에 폐기됐는지와 같은 계산할 조건이 다른 것일 뿐, 모든 자산에 대해서 감가상각 계산방법은 동일하다.

각 기업마다 보유하고 있는 물품의 수가 적게는 1000개에서 많게는 만 단위가 넘어가는데, 1건 계산할때 0.5초가 걸린다 해도 자산이 1만건이면 5000초가 소요되는 것이다. 이런 경우에서는 계산의 주체가 세션빈인지, Procedure인지가 문제되는 것이 아니라, for loop 문을 이용하여 건별로 계산시킨다는 것이 문제다. Procedure를 하나 만들어서 감가상각계산할 범위만 인자로 던져준 후, 일괄 처리하도록 해야 한다.

이처럼 DB의 블럭단위 I/O를 일괄처리기법에 활용하는 것이 값비싼 DBMS를 쓰는 진정한 이유라 할 수 있다.

다음 강좌에서는 엔터티빈 없이 진행되는 EJB 프로젝트에서 유용하게 쓰이는 유틸을 소개하도록 하겠다. @


http://www.zdnet.co.kr/techupdate/lectu ··· 2C00.htm
"Java" 카테고리의 다른 글
  • 유연한 참조로 메모리 누수 막기 (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ④ (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ③ (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ② (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ① (0)2007/05/18
2007/05/18 11:24 2007/05/18 11:24
Posted by webdizen
Tags EJB, Java, Procedure
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2994

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Java2007/05/18 11:22

EJB 기반 프로젝트 수행 가이드 ②

세션빈에서의 트랜잭션 관리

김주현 (ERP 개발자)  
2004/03/30        

① 세션빈에서의 DB 접근전략 및 엔터티빈 사용시 주의사항
② 세션빈에서의 트랜잭션 관리
③ 비즈니스 프로세스 구현 최적화하기
④ 능률 높여주는 유틸리티를 사용하자

지난 강좌에서는 세션빈 내에서 DB를 핸들링하는 데에는 JDBC 드라이버를 이용한 Connection 객체를 사용하는 방법과, 엔터티빈을 이용하는 방법이 있다고 언급했다. 중요한 점은 두가지 방법을 혼용하는 것이 아니라 엔터티빈 사용시 발생하게 되는 문제점을 미리 알고 엔터티빈 사용을 지양하자는 것이다.

지난번 강좌에 이어 엔터티빈 사용을 지양할 수 밖에 없는 사례 한 가지를 더 예로 들겠다.

통상 SI 프로젝트에서 인사시스템과 같이 개인의 정보보안이 엄격히 다루어져야 하는 성격의 시스템인 경우, 업무 전반 모두를 웹기반으로 구현하지는 않는다. 물론 웹으로 구현하는 것과 보안과는 별개의 문제이지만, 아직도 공기업 등에서는 웹기반 구현을 꺼리는 경우가 많기 때문이다.

급여계산 및 업무평가점수계산 등 민감한 성격의 정보를 다루는 프로그램은
클라이언트/서버(C/S) 환경으로 구축돼 업무 담당자만 사용 할 수 있도록 하고, 웹으로는 일반사원들이 자신의 정보를 확인하는 조회성 프로그램만으로 구성되도록 요청하는 사례가 많다.

조회성 기능만 갖고 있는 웹기반 프로그램을 설계한다면, 설계자는 DB의 레코드를 SELECT하고 화면에 보여주는 기능만을 수행하므로 쿼리보다는 엔터티빈을 활용하겠다는 기준을 세우고, 실제 개발도 엔터티빈을 이용해 진행하게 된다.

C/S 환경 시스템과 웹프로그램은 서로 업무 범위 및 구축된 환경만 다를 뿐이지 물리적으로 같은 DB를 사용하고 있다. 이러한 환경에서 간혹 발생하는(주로 동시사용자수가 증가할 때 발생한다) EJB 컨테이너의 ‘NoSuchObjectException’ 장애물을 알아둬야 한다.

C/S 프로그램으로 업무를 하는 담당자와 일반 사용자가 같은 레코드를 조회한 상태에서, 담당자가 해당 레코드를 수정 혹은 삭제했고, 웹의 클라이언트 모듈이 해당 레코드를 표현하는 엔터티빈을 계속 참조하고 있는 상황이라면 NoSuchObjectException을 보게 될 것이다.

이 Exception을 던지는 일은 EJB 컨테이너의 기능 중 하나로 엔터티빈의 객체정보와 실제 DB의 정보가 일치하지 않을 경우 동작하게 된다. EJB 컨테이너는 실제 DB와 엔터티빈 객체를 동기화하고 있기 때문에 다른 세션이 자신이 참조하고 있는 DB의 레코드를 변경했을 경우 Exception을 던지는 것이다.

이 Exception이 발생하면 이를 해결하는 방법은 컨테이너가 참조하고 있는 엔터티빈을 핫디플로이 시켜서 객체를 다시 참조케 하거나, 핫디플로이 방법을 쓸 수 없다면 EJB 서버를 RESTART하는 수 밖에 없다.

컨테이너가 NosuchObjectException을 던질 경우 자동으로 엔터티빈을 Passivate시키고 Activate시키면 편리하겠지만 아쉽게도 그런 기능은 없다. 개발자가 향후에 NosuchObjectException이 생기지 않도록 프로그램마다 빈의 라이프싸이클을 제어하는 부분을 추가하는 방안도 생각해 볼 수 있겠지만 그 작업은 시간 소모가 크다.

해당 엔터티빈의 실행 중 정보를 갖고 있는 XML 데이터의 값 중에서 이러한 에러를 일으키지 않도록 해주는 속성이 무엇인지 안다면, XML 데이터의 값을 고치고 다시 디플로이 하는 방법도 있겠다.

하지만 개발자 입장에서는 본인의 판단 착오와는 전혀 관련이 없는 에러를 맞닥뜨리게 된 것이며, 이미 에러를 발견한 이후에 작업을 하는 것이므로 이미 사용자의 프로그램에 대한 신뢰도가 떨어져있는 상황이겠다. 또한 시스템 내의 모든 엔터티빈을 찾아내 작업을 하기도 꽤 번거로운 일일 것이다.

컨테이너의 동기화 기능이 유발시키는 NoSuchObjectException과 엔터티빈과 매핑되는 테이블이 컬럼이 추가되는 등의 변경 사항이 있을 시 엔터티빈의 소스를 고쳐 다시 디플로이 해야 한다는 문제점으로 인해, 항상 시간에 쫓기는 프로젝트 개발자 입장에서는 엔터티빈의 스펙때문에 자신의 보폭이 좁아지는 느낌을 받을 것이다
.
그리하여, 기존 시스템은 엔터티빈을 이용하되 추가 프로젝트에서는 엔터티빈을 전혀 사용하지 않고, DB핸들링은 100% SQL문으로 처리하고 있는 싸이트도 생기게 된다.

엔터티빈을 쓰지 않으면 DB의 테이블마다 SELECT, DELETE, INSERT, UPDATE 등의 쿼리를 일일이 개발자가 생산해야 하지 않느냐는 의견도 나올 수 있으나 프로젝트 프로토타이핑 단계시점 등에 테이블당 쿼리문을 자동으로 생성시켜주는 간단한 유틸 프로그램을 직접 만들어 사용한다면 생산성이 비약적으로 높아질 수 있다.(4회 강의에서 이 유틸을 소개할 예정이다.)

엔터티빈 사용시 발생하게 되는 문제점에 대한 이야기는 여기서 마무리하고, 초점을 세션빈으로 돌리자. 세션빈 내에서 비즈니스 처리를 할 때 유의해야 할 점이 무엇인지 알아본다.

비즈니스 프로세스와 트랜잭션
하나의 비즈니스 프로세스가 처리될 때 가장 중요하게 지켜져야 할 사항은
트랜잭션이 결함없이 처리되어야 한다는 점이다. 예를 들어 보험금 수납시에 보험담당자의 실적이 체크됨과 동시에, 회계장부에 보험금이 기록되어야 하는 [보험금수납]이란 이름의 업무정의가 있을 경우, 이 업무가 성공적으로 완료되었다고 판단할 수 있는 것은 단 2가지 외에는 없다.

실적도 체크되지 않고 장부에도 기록되지 않은 경우와, 실적 체크와 장부 기록 둘 다 성공한 경우다. 이 외 둘 중 하나만 성공한 경우는 트랜잭션이 실패한 것이다. 시스템 운영상 결코 발생해서는 안되는 치명적인 결함이다.

세션빈이 수행하는 메소드안에서 반드시 트랜잭션이 성공해야 함은 아무리 강조해도 지나치치 않을 것이다.


EJB 컨테이너와 트랜잭션
EJB 개발자들은 세션빈의 메소드 단위로 트랜잭션 레벨을 설정할 수 있다는 것을 알고 있다. 또한 컨테이너가 개발자가 설정한 트랜잭션의 레벨에 따라 메소드 내의 트랜잭션을 하나로 묶어준다는 것도 알고 있다.

EJB의 실행중 속성정보를 갖고 있는 XML 파일안을 조작함으로써 트랜잭션 관리가 필요한 메소드를 지정할 수 있고(기본값은 ALL이다), 레벨 또한 조작할 수 있다.
설정 레벨은 REQUIRED로 지정한다(트랜잭션을 관리할 필요가 없는 업무정의가 존재하는 경우는 없다고 봐도 좋다).

컨테이너는 이 두 가지 정보를 가지고 메소드 시작시점부터 완료시점까지 Exception이 발생하게 되면 레벨이 REQUIRED일 경우 Exception발생 전까지 수행했던 작업을 모두 롤백시킨다.

설정레벨을 하위로 조정하면 Exception을 던지되 롤백시키지는 않는 현상이 벌어진다. 그러므로 이 XML의 기본값을 고치는 경우는 없고, 고쳐서도 안된다. VisualCafe와 같이 자동으로 EJB의 XML을 생성시켜주는 툴에서부터 모두 이 옵션을 기본적으로 REQUIRED로 설정하고 있다.

이렇게 트랜잭션을 컨테이너가 메소드 단위로 관리하므로 개발자 입장에서는 명시적으로 트랜잭션을 ON/OFF 하는 기능을 구현할 필요 없이 일정부분 태스크가 줄어든 상태로 볼 수 있다.

하지만 메소드 내에서 Connection을 씀에 있어 언제 Connection을 얻어오고 반환해야 할 지, 또는 Connection 객체를 몇 개 선언해 생성시켜 쓸 것인지와 같은 고민들은 컨테이너의 기능과 별개로 전체 프로젝트에 일관성 있게 적용되어야 할 표준으로 정해져야 하며, 이는 설계자와 개발자의 몫이다.

Connection 객체 핸들링을 하는 기준
Conection 객체를 어느 클래스가 생성하며 반환할 것인가, DB 래퍼 클래스의 메소드와 통신할시 Connection 객체를 넘길 것인가 말 것인가, 넘긴다면 어떤 방법으로 넘기는가에 대한 기준을 세워야 한다.

세션빈 Connection을 선언하고 생성시켜 얻어오는 주체는 1회 강의에도 말했듯이 세션빈 클래스이다. 세션빈 클래스 안에서 Connection 객체는 전역변수로 선언하지 말아야 한다.

각각의 메소드마다 지역변수로 선언하여 사용하는 것이 효과적이다. 또한 메소드안의 프로세스가 진행 완료됐을 경우, finally 절에서 반드시 Connection을 반환해야 하는 것도 중요한 원칙 중 하나다. 커넥션을 얻어올 때, SQLException을 던져야 하므로 try, catch문을 사용하게 되는데, finally 절에서 반드시 커넥션을 반환하도록 하자.

생성된 Connection 객체는 DB와의 작업을 모두 한 이후 반드시 close 메소드를 통해 닫혀져야 하는데, Connection 객체가 반환되지 않는 경우 심각한 시스템 자원낭비를 유발하게 된다. 이는 곧 프로그램의 운용성능 저하로 이어진다.

Connection 객체생성은 언제나 close() 메소드와 쌍을 이루어야 한다는 식의 사고습관을 가져야 하겠다.

다음으로 언급할 것이 DB 래퍼 클래스와의 통신 패턴이다. DB 래퍼 클래스는 DB에 SELECT, INSERT, DELETE, UPDATE 쿼리를 전달하는 역할을 하는 메소드 집합체로써, DB와 가장 가까운 층이다.

세션빈 메소드는 복잡한 비즈니스 프로세스를 구현하게 되는데, 메소드 시작 부터 끝까지 Connection 객체를 통한 쿼리실행이 모두 그 안에 나열돼 있다면 유지보수 하는 사람 뿐 아니라 개발하는 당사자조차도 가독하기 힘들다.

따라서 DB 래퍼 클래스를 만들어서 쿼리실행에 필요한 인자(사용자가 입력한 조회조건 또는 사용자가 입력한 정보)를 세션빈이 전달하고, 실행 결과를 서로 주고 받는 패턴을 사용한다.

이처럼 DB래퍼 클래스를 통해 레코드를 조작하게 될 때, 세션빈이 생성한 Connection 객체를 DB 래퍼 클래스의 메소드에 콜할 때 인자로 함께 넘겨야한다.

세션빈이 Connection을 넘기지 않고 DB래퍼 클래스에게 Conenction을 핸들링하는 것을 맡길 경우, 5개의 DB래퍼 클래스 메소드를 호출하면 Connection 생성을 5번 하고, close() 명령을 통한 반환도 5번 일어나게 되는 셈이다. 다분히 비효율적이다.

이에 비해 DB래퍼가 자신을 호출한 클래스로부터 항상 Connection을 받아서 쓴다면 필요없는 자원낭비를 막을 수 있다.

이와 같은 원칙은 전체 시스템 구조에서 통일성 있게 적용돼야 할 것이다. 통일성 있게 적용되지 않으면 이런 원칙을 세우는 의미가 없다. 어떤 서브 패키지는 DB래퍼 클래스를 생성할 때 생성자에서 Connection을 받는다던가,
또는 아예 DB래퍼 클래스로 하여금 Connection을 관장하도록 한다던가 하면 전체 설계구조에 통일성이 없고 이를 바탕으로 한 추가 개발에 있어서도 비효율이 따르게 된다. 일관성 없는 구조의 클래스들을 재활용하는 신규 모듈도 역시 흐름에 일관성이 없을 것이다.

이제까지 언급했던 원칙을 패턴으로 사용하는 샘플소스를 보도록 하자.

아래 메소드는 사용자가 입력한 전표 내용으로 전표 등록을 하는 메소드이다. 이 세션빈 이름은 SlipManager이다.



전표를 등록하는 비즈니스 프로세스는 사용자가 입력한 전표정보를 받아서 마감여부를 체크한 후, 전표 번호 MAX 값을 알아내고 전표 TABLE에 INSERT 하는 작업으로 요약된다. 물론 실제 업무는 이보다 훨씬 더 복잡한 경우의 수를 가지고 있다. 법인카드사용정보가 있는 경우 법인카드 사용정보에도 등록을 시켜야 하고, 예산에서 지출되는 전표일 경우 예산 정보에도 그 내역을 기록해야 하는 등 다양한 업무 정의를 포함하고 있다.

소스를 보면 알겠지만 위에서 정한 원칙대로 Connectin 객체는 지역변수로 선언하여 사용하고 있으며, DB래퍼 클래스의 메소드에 Connection 객체를 넘기고 있다.

메소드 오버로딩(OVERLOADING)
이제 다른 시스템의 업무가 이 메소드를 사용해야 하는 업무 요구사항이 생길 경우에 대해 살펴보겠다. 인사시스템에 복리후생비 신청정보가 결재자의 승인을 받으면 자동으로 전표가 발행되어야 하는 요구사항이 나왔다 치자.

프로그램 설계자는 전표등록이라는 비즈니스 프로세스를 구현한 메소드가 SlipManager라는 세션빈에 이미 있다는 것을 알고 있으므로, 이 부분은 따로 전표 등록 메소드를 만들지 않고 기존 것을 재사용하기로 한다.

아래소스는 이처럼 위의 세션빈을 전표등록 메소드를 호출하는 복리후생비 신청정보 승인 프로세스를 보여주고 있다.

소스보기
 



위 소스를 보면 전표등록 메소드를 재사용 하고 있으나 한가지 우려되는 점이 있다. 바로 세션빈의 메소드를 콜 하는 것 까지는 문제가 없으나, 전표 등록 호출시 세션빈에게는 Connection 객체를 던지지 않으므로 호출되는 세션빈이 Connection을 따로 생성시켜 작업을 하고 있다.

위와 같은 업무에서는 세션빈의 메소드를 한번 호출하고 있지만 만약 사용자가 업무 복리후생비전표를 100장 생성시켜야 하는 경우라면 아래와 같은 for loop 문으로 콜 하게 될 것이다.

소스보기
 




결국 Connection은 100개가 생성되고, 100개가 반환되는 작업이 일어난다. 역시나 시스템 자원운용 면에서 보면 상당한 문제점을 야기시키는 부분일 것이다.

따라서 이와같이 타 세션빈에서 호출하는 메소드일 경우, DB 래퍼와 통신시에 Connection 객체를 넘겼듯 세션빈 메소드와 통신시에 Connection을 넘겨서 사용하는 패턴으로 가야 할 것이다.

위의 전표생성메소드를 Connection을 받아서 작업하는 메소드로 고쳐서 설계해보자. 물론 이 Connection을 생성시키고 닫아주는 주체는 전표생성 메소드를 콜한 세션빈이 하게 된다. 그렇다면 아래 소스처럼 regSlip 메소드를 오버로딩 한 메소드를 추가로 개발한다.

소스보기
 



이러한 메소드가 추가되면 먼저 만들어 두었던 regSlip 메소드는 아래와 같이 regSlip(Connection conn , SlipInfo[] sInfos)를 호출하자. 실제 비즈니스 프로세스는 regSlip(Connection conn , SlipInfo[] sInfos)안에 모두 있으므로, 이 메소드를 기존 그대로 놔두면 중복코드가 있게 된다.

소스보기
 




이와 같이 세션빈 메소드는 다른 세션빈 메소드가 업무상 호출할 수 있는 경우가 빈번하므로, 세션빈 메소드를 Connection 받는 것과 안 받는 것으로 오버로딩하는 패턴을 사용하게 된다.

세션빈 메소드는 다수 쿼리를 실행시키는 집합체
EJB 기반 프로젝트의 가장 큰 영역은 세션빈이다. 세션빈 내에서 모든 비즈니스 프로세스가 구현되므로, 세션빈 안에서 기타 클래스들을 어떠한 패턴으로 사용할 지 고민하며 설계하고 구현해야 재사용성이 높고 강력한 모듈을 만들 수 있다.

다음 강의에서는 실제 업무에서 일어나는 복잡한 프로세스를 구현함에 있어서 DBMS의 펑션이나 프로시져를 사용하는 것의 장점을 살펴보도록 하겠다.

2회까지는 세션빈 사용에 있어서의 클래스 패턴과, 시스템 성능 측면에서 어떤 점을 피해가야 하는지가 주된 논점이었다. 실제 프로젝트에서는 이 정도로 대부분의 이슈가 커버되겠지만, 막상 구현 결과물을 보면 세션빈 메소드가 수많은 쿼리를 실행시키는 집합체로 보인다.

그렇다면 이것을 단 한 번의 실행으로 끝내버리는 방법은 없을까. 다음 강의에서 그 방법에 대해 논의해보자. @  


http://www.zdnet.co.kr/techupdate/lectu ··· 2C00.htm
"Java" 카테고리의 다른 글
  • EJB 기반 프로젝트 수행 가이드 ④ (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ③ (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ② (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ① (0)2007/05/18
  • MIDI Sound 생성하기 (0)2007/05/18
2007/05/18 11:22 2007/05/18 11:22
Posted by webdizen
Tags EJB, Java, 메소드 오버로딩, 세션빈, 컨테이너, 트랜잭션
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2993

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Java2007/05/18 11:19

EJB 기반 프로젝트 수행 가이드 ①

세션빈에서의 DB 접근전략 및 엔터티빈 사용시 주의사항

김주현 (ERP 개발자)  
2004/03/16

① 세션빈에서의 DB 접근전략 및 엔터티빈 사용시 주의사항
② 세션빈에서의 트랜잭션 관리
③ 비즈니스 프로세스 구현 최적화하기
④ 능률 높여주는 유틸리티를 사용하자

EJB 아키텍처를 기반으로 웹애플리케이션을 구축하는 프로젝트들이 2000년 이후부터 증가해왔음은 모두가 아는 사실. 더불어 EJB 관련 서적들도 다양하게 출판됐고, EJB를 사용하는 개발자들도 늘어나고 있다.

그러나 실제 EJB를 사용하면서 개발자들이 저지르기 쉬운 실수, 함정 등은 아직 서적에서는 친절히 안내되어 있지 않은 듯 하다. 이번 강좌의 목적은 흔히 개발자들이 스스로 잘 안다고 생각하는 것 때문에 빠질 수 있는 함정들을 미리 짚어봄으로써 그 함정이 초래할 혼란을 최소화하는데 있다.

강좌 중간중간 퍼포먼스를 고려한 팁이 언급될 수도 있고, 클래스 설계와 관련된 첨언도 들어갈 수 있다. 이 강좌의 목적이나 성격이 하나의 개념에 대한 순차적인 설명 형식은 아니므로 기본개념 설명을 건너뛰거나, 동시에 초보 자바 개발자도 알고 있는 사항에 대해 새삼 지적하는 경우도 있을 것이다.

자신에게 닥칠 수도 있는 문제를 미리 파악해 보고, 어떠한 방법으로 예방하고 해결해야하는지 살펴보도록 하자. 이번 강좌에서 언급될 예시에서 쓰는 EJB의 스펙은 1.1버전이고, DBMS는 오라클 8i이다.

DB와의 연결은 누가, 언제, 어떻게 할까?
EJB 애플리케이션이 DB와의 연결자원을 이용하는 방법은 2가지다.

첫째, ConnectionPool 을 이용하는 방법이 있다. EJB 서버가 형성하게 되는 ‘ConnectionPool’이라는 데이터 소스를 이용하는 방법이다. 임의의 비즈니스 프로세스를 담당하는 세션빈의 메소드 내에서 DB의 레코드를 써야할 경우, ConnectionPool에서 java.sql.Connetion 객체를 받아 쓰는 경우이다.

웹로직(weblogic), 웹스피어(websphere), JBOSS 등 다양한 업체들의 EJB 서버마다 기동시 ConnetionPool을 생성시킬 수 있는 기능을 제공한다. 기동시 읽어들이는 프로퍼티 파일에 DB의 URL, SID, user name, 패스워드, ConnectionPool의 name까지 사용자가 지정해야 한다.

아래는 웹로직 서버(버전 5.1)의 weblogic.properties 파일 내의 ConnetionPool 설정정보 부분이다.



# testPool
weblogic.JBDC.connectionPool.testPool=\
url=JBDC:oracle:thin:@123.4.5.67:portNumber:sid명,\
driver=oracle.JBDC.driver.OracleDriver,\
initialCapacity=1,\
maxCapacity=10,\
CapacityIncrement=2,\
allowShrinking=true,\
shrinkPeriodMins=15,\
props=user=user명;password=패스워드




이렇게 EJB 서버 기동시 미리 ConnectionPool을 만들게 되면 애플리케이션 운영중 발생하게 되는 DBMS 와의 빈번한 요청이 있을 때마다 JBDC 드라이버로 DB와 일일이 연결할 필요없이, 만들어진 ConnetionPool에서 Connetion을 꺼내 쓰고 다시 반환하므로 시스템 퍼포먼스 측면에서 월등히 효율적이다.

둘째, 엔터티빈을 활용하는 방법이다. 엔터티빈 중에서도 컨테이너가 퍼시스턴스를 관리해주는 CMP 엔터티빈을 EJB 서버에 배치해 쓰면 개발자 입장에서는 많은 부담을 덜 수 있기 때문에 대부분의 프로젝트에서 BMP 엔터티빈 보다는 CMP 엔터티빈을 사용한다.

엔터티빈 객체 하나는 DB의 하나의 테이블을 대표하며, 엔터티빈 객체가 갖고 있는 필드들은 해당 테이블의 컬럼과 매핑된다. 엔터티빈의 인스턴스는 DB의 임의의 레코드를 객체화한 것이다. 그러므로 엔터티빈의 인스턴스를 호출하여, get, create, set, remove 등의 메소드를 쓴다면, 직접 SQL문을 DB로 날리지 않아도 동일한 결과를 얻을 수 있다. insert 문 대신 create 메소드를 쓰면 되므로 코딩이 간결해진다. 더불어 SQL문에 익숙하지 않은 개발자라도 쉽게 쓸 수 있다는 장점이 있다.

그러나 엔터티빈의 이러한, 소위 장점이라고 하는 것들에 섣불리 현혹되지 않는 것이 좋다. EJB 프로젝트에서 발생하는 문제의 원인은 대부분 엔터티빈에 있다.

DB 핸들링 주체는 세션빈
DB에 어떻게 접근하든 비즈니스 프로세스를 담당하는 주체는 세션빈이다. 그리고 분석된 업무 흐름을 애플리케이션으로 구현할 때의 최소단위는 세션빈의 메소드이다.

예를 들어 급여계산 프로그램이 있다면 세션빈 내의 calcuratorPay(String empNo, String yyyyMm)라는 메소드가 있을 것이고, 복리후생비 신청 프로그램은 submitWelfare(String empNo, String date, ...)라는 메소드를 콜한다.

개발자는 세션빈의 메소드 안에서 적절히 저 두가지 방법을 활용해 DB의 레코드를 핸들링하게 된다. 엔터티빈 인스턴스가 DB의 레코드를 표현한다고 해도, 엄청나게 다양한 조건 및 함수 등이 담겨있는 select 문의 효과를 엔터티빈 인스턴스를 사용해서 자바 코딩으로 얻을 수 있겠는가? 없다.

따라서 그러한 다양한 조회를 할 필요가 있는 경우에는 ConnectionPool 에서 connection 객체를 하나 얻어와서 쓰게 된다. 뿐만 아니라 update, delete, insert 등에서도 pk 값으로만 조회해서 그 값을 변경, 삭제, 추가하는 경우보다 추가적인 제약이 따르는 경우가 많다.

결국 전체 시스템의 모든 세션빈이 connetion 객체를 쓰는 경우가 훨씬 많기 때문에, 모든 세션빈이 콜 할 수 있는 공통 모듈로 connectionPool 에서 connection 객체를 하나 뽑아 리턴해주는 클래스의 개발 필요사항이 발생한다. 이 클래스에는 getConnetion() 및 closeConnection() 메소드가 위치하게 될 것이다.

아래 예시에서 등장하게 되는 ConnManager라는 클래스는 이러한 역할을 하는 클래스다.

예시 소스코드를 보자. 사번을 통해 사원명을 조회하는 매우 간단한 메소드이다.





위의 예시에서 보듯이 세션빈 메소드 내에서는 Connection 객체를 가지고 와서 마지막에 닫고, 자신을 호출한 클라이언트에게 사원명을 전달하는 일만 하고 있다. 직접적으로 DB에 갖다오는 일은 EmpDB라는 클래스에게 맡긴다. 다양한 업무를 반영하는 세션빈 메소드 내에서는 이와 같은 패턴이 보다 효과적이고 간결하다.

그렇다면 EmpDB라는 클래스의 getEmpName 메소드를 살펴보자.





사번 테이블에서 사원 번호로 사원명을 리턴하는 메소드이다. 이 메소드 중에서 가장 주의깊게 봐야 할 부분은 PreparedStatement를 Statement interface 대신 사용한 부분이다. 조회성 쿼리를 DB에 던지고자 할 경우 아무 생각없이 Statement interface를 사용하는 개발자가 많은데, 이렇게 만들어서는 안된다.

꼭 PreparedStatement를 사용하자
Statement 인터페이스 보다는 PreparedStatement가 낫다. 사실 ‘좋다’ 정도가 아니라 ‘Statement를 쓰지 말고 PreparedStatement를 써야한다’고 말해도 될 정도다.

getEmpName의 메소드에서 PreparedStatement 대신 Statement의 executeQuery(String sql) 메소드를 사용해서 개발하면 어떤 일이 생기는지 알아보자.

인사업무 담당자가 사원 한 명씩 상세정보를 조회하고자 한다고 가정하자. 조회버튼을 누르면 사원번호로 getEmpName 메소드안에서 쿼리가 스트링으로 완성돼 Statement의 executeQuery(String sql) 메소드안의 SQL 값으로 들어간다. DB의 옵티마이저가 쿼리를 파싱, 실행하고, resultSet 리턴한다.

여기서 다음 사원을 조회하면? 사원번호만 다른, 그러나 방금전 조회시의 쿼리와 모두 같은 스트링이 던져진다. DB의 옵티마이저가 쿼리를 파싱, 실행한다.

계속 다른 사원을 조회하면? DB 입장에서는 조회조건만 다른 쿼리에 대해 파싱을 반복적으로 하고 있는 셈이고, 매번 파싱을 하고 결과값을 리턴해야 하기에 조회시 결과값 출력도 그만큼 늦어진다.

만일 이 시스템 전체가 Statement 인터페이스로 이뤄져 있고, 향후 시스템 성능 테스트에서도 이 부분이 발견되지 않는다면 어느날 갑자기 원인도 모르게 CPU 사용도가 올라가고 시스템 전체가 느려지는 현상이 발생하게 된다. 서버 관리자가 서버를 내렸다 올리겠지만 원인을 알 수 없으니 속이 편하진 않을 터.

나중에 다시 스트레스 테스트를 하고, 결국 파싱과 실행 수가 비슷하다는 사실이 발견된다. 책임은 개발자들에게 돌아간다.

Statement로 이뤄진 프로그램을 PreparedStatement로 바꾸라는 지시가 떨어지겠지만 이는 간단치 않은 일이다. Statement로 되어있는 프로그램을 그동안 copy & paste 해서 만든 프로그램이 몇 개인지 파악도 안되고, 파악이 되다 해도 바꾸는 작업은 쉽지 않다.

PreparedStatement는 주어진 쿼리를 미리 파싱을 해두기 때문에, 조회조건만 달라진 다른 쿼리가 들어오면 바로 실행만 시키게 된다. 시스템 성능 면에서 훨씬 더 효율적이다.

엔터티빈으로 접근한 row는 또 다른 Connetion 객체로 접근하지 말 것
세션빈에서 한 레코드에 대해 변경을 하는 작업을 엔터티빈으로 한다고 하고, 이때 요청사항으로 추가되는 어떤 컬럼에 대해 변경하는 부분이 늘어난다고 하자. 기존 소스는 아래와 같다. 아래 소스는 세션빈 내에서 사원발령이 났을 경우 발령 시작일자와 발령 마감일자를 변경하는 비즈니스 프로세스를 갖고 있다.






여기에서 사원발령정보를 변경하기 위해 엔터티빈 리모트 인터페이스의 setStartDt, setEndDt 메소드를 사용했다. 여기서 만일 사원발령정보를 낼 때 변경 일자도 기록하라는 요청사항이 추가될 경우, 이를 기존 소스에 추가하는 작업에서 엔터티빈을 사용하지 않고 Connection 객체를 만들어서 직접 SQL문을 던지는 형식으로 레코드의 한 컬럼 값을 변경시킨다고 가정해보자.






위의 소스를 보면 일견 아무런 문제가 없어 보인다. 컴파일도 잘 되고 실행시에 아무런 에러도 나지 않는다. 하지만 결과를 보면,

기존





를 타서, 레코드에 startDt와 endDt 컬럼 부분은 모두 원하는 데로 변경이 되었으나,






부분이 실행이 되었음에도 변경 일자 컬럼에는 아무런 변화가 없음을 알 수 있다.

엔터티빈의 인스턴스를 가지고 온 후부터 DB 레코드와 엔터티빈간의 동기화가 형성되고, 세션빈 메소드가 끝났을 때 DB에 결과를 쓰게 된다. 즉, 최종적으로 기록이 남는 것은 엔터티빈으로 작업한 결과이다. 중간에 새로 생긴 업데이트 세션은 그 작업은 성공한다 해도 DB에는 전혀 반영이 되지 않는 것이다.

이 소스를 보고 ‘그렇다면 모두 엔터티빈 메소드로 실행시키면 되지 왜 굳이 Connection 객체로 접근하는가'라는 의문이 들 수도 있다. 하지만 시스템을 오픈하고 나서 테이블에 컬럼을 추가해야 하는 경우가 업무적으로 반드시 생기게 된다. 그때 추가된 컬럼에 대해서는 엔터티빈안에 추가된 컬럼의 필드와 상응하는 변수를 추가하고 엔터티빈을 서버에 다시 배치해야 한다. 하지만 이 작업이 번거로우므로 추가된 컬럼에 한해서 SQL 쿼리로 간단히 핸들링하고자 하는 계획을 세우게 된다. 그럴때 위와 같은 경우가 생기게 되고, 한 row에 대해서 엔터티빈을 콜 할 때 엔터티빈 인스턴스와 DB가 동기화 된다는 것을 염두에 두지 못한 채 Connetion 객체를 써서 업데이트하는 하는 일도 생기는 것이다.

위의 소스는 모두 Connection 객체를 써서 해당 row의 컬럼을 변경 시키든지, 아니면 모두 엔터티빈 메소드를 쓰도록 변경해야 한다.

엔터티빈 사용을 지양하자
주위에서 진행되는 프로젝트들을 살펴보면 엔터티빈을 몰라서 쓰지않는 것이 아니라, 엔터티빈의 성질을 잘 알기 때문에 안쓰는 경우를 종종 보게된다.

엔터티빈의 이점이 몇가지 있다는 것은 분명하지만 개발자 입장에서는 쿼리로 간단히 대처를 할 수 있다는 측면에서 엔터티빈을 기피하게 된다.

한편 엔터티빈 말고 Connection 객체로 DB를 핸들링한다고 해서 트랜잭션이 언제나 완결되는가 하면 그것은 또 아니다. 분명 엔터티빈보다는 훨씬 더 강력하지만 예외도 있다. 다음 이어지는 강좌에서는 그에 해당하는 예제를 들고, 이를 피하려면 어떤 전략을 취해야 하는지 소개하도록 하겠다. @

http://www.zdnet.co.kr/techupdate/lectu ··· 2C00.htm
"Java" 카테고리의 다른 글
  • EJB 기반 프로젝트 수행 가이드 ③ (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ② (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ① (0)2007/05/18
  • MIDI Sound 생성하기 (0)2007/05/18
  • Java 성능개선을 위한 Programming 기법 (0)2007/05/18
2007/05/18 11:19 2007/05/18 11:19
Posted by webdizen
Tags EJB, Java, 엔터티빈
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2992

Leave your greetings.

[로그인][오픈아이디란?]

Programming/Java2007/05/18 11:03

MIDI Sound 생성하기


자바 데스크탑 애플리케이션에 오디오 큐(audio cues)를 추가하면 세련된 느낌을 줄 뿐만 아니라 사용성을 극적으로 증가시킬 수가 있다. Musical Instrument Digital Interface (MIDI)는 장치간에 뮤지컬 이벤트를 주고 받을때 사용되는 커뮤니케이션 프로토콜이다. MIDI파일은 실제 오디오가 아닌 오디오 명령어를 포함한다. MIDI 가 사운드를 재현하기 위해 사운드 엔진에 명령어를 기술하는 반면, 오디오는 사운드의 디지탈 표현(digital representation)이다. 이 TechTip에서는 J2SE 1.3이후의 자바 플랫폼에서 지원하는 javax.sound.midi 패키지를 사용하여 MIDI사운드를 생성하는 3가지 방법을 소개한다.

첫번째 테크닉은, MidiChannel객체를 직접 호출해서 사운드를 생성하는 것이다. 이 객체는 MIDI전송을 위한 하나의 MIDI 채널을 나타낸다. 채널을 통해서 연주를 시작하기 위해서는 MidiChannel객체의 noteOn()메소드를 사용하고, 멈추기 위해서는 noteOff()메소드를 사용한다.

noteOn()메소드는 2개의 int값을 필요로 한다. 첫번째 int는 연주될 음을 지정하는데, 다음 SingleNoteChannel프로그램에서 noteOn()메소드에 넘겨진 int 60은 가운데 도(middle C)를 위한 표준 MIDI 음의 번호이다. 위아래의 정수는 반음에 해당하고 12 반음은 한 옥타브를 이룬다.

noteOn()메소드의 두번째 매개변수는 키가 눌려질 속도를 지정한다. 사실 이 매개변수는 흔히 속도를 나타내지만, 그것을 제어볼륨(volume control)으로 봐도 무방하다. SingleNoteChannel에서 noteOn()메소드의 두번째 매개변수는 70이다. 속도를 나타내는 70을 다른 숫자로 바꿔서 실험을 해보자.

noteOff()메소드는 두개의 signature를 갖는데, 하나는 noteOn()에서와 동일한 두개의 매개변수이고, 다른 하나는 연주될 음을 나타내는 번호이다.

음을 연주하기 전에, SingleNoteChannel생성자에 필요한 셋업을 해야한다. 사운드를 생성하기 위해서는 Synthesizer객체가 필요하다. 이 객체는 MIDI 1.0 명세(specification)에서 규정하는 각각의 16개 채널을 말하는 MidiChannel들의 컬랙션을 보여준다. SingleNoteChannel생성자는 MidiSystem클래스의 팩토리 메소드를 이용해서 Synthesizer에 대한 핸들을 받고, Synthesizer의 open()메소드를 호출한 후, getChannels()메소드를 호출함으로써 가능한 MidiChannels의 array를 얻어낸다. 그리고는 index 0자리에 첫번째 MidiChannel을 넣는다.


SingleNoteChannel프로그램의 playNote()메소드는 MidiChannel noteOn()메소드를 호출해서 음을 연주하기 시작한다. 연주는 쓰레드가 휴면하는 1초동안 지속되다가 MidiChannel noteOff()메소드가 호출됨으로 해서 끝나게 된다. 또한 버튼이나 키 혹은 on-screen event를 누르거나 떼서 특정 음들을 결합시킬 수도 있다.

MIDI사운드를 생성하기 위한 두번째 테크닉은, Synthesizer로 결합된 Receiver객체를 사용하는 것이다. 이 때에 특정 MidiChannel 대신 Receiver에 대한 핸들을 받아야 한다. 사용자는 ShortMessage타입의 MIDI메세지를 만들고 사용자설정을 한 후에 Receiver객체의 send()메소드를 호출해서 연주를 시작할 수 있다.

ShortMessage객체는 많아야 2데이터 바이트를 갖는 MIDI 메세지를 포함한다. MIDI 메세지의 매개변수를 설정하거나, 채널 메세지를 위한 메세지 매개변수를 설정하기 위해서 setMessage() 메소드를 사용할 수 있다. SingleNoteSynthesizer프로그램의 setMessage()메소드는 채널 메세지를 위한 메세지 매개변수를 설정하기 위해서 4개의 int값을 포함하는 signature를 갖는다. 첫번째 int는 NOTE_ON 와 NOTE_OFF중 보내질 코맨드를, 두번째 int는 타겟 채널을 지정한다. (앞의 예제와 일관되게, index 0을 조건으로 지정했다.) 마지막 두개의 int값은 음 식별자와 속도이다. SingleNoteSynthesizer에서 noteOn()과 noteOff()에 넘겨지는 int값은 SingleNoteChannel예제에서와 동일하다.

지금까지는 디폴트 악기에서 하나의 음을 연주하는 예만을 살펴보았다. 그렇다면 다른 악기에서의 연주는 어떨까? 세번째 테크닉에서 이 물음의 답을 보게 될 것이다. 실험을 통해 시작해보자. 연주 가능한 악기의 리스트를 생성하는 메소드가 있다.

위의 메소드를 호출하면 다음과 같은 아이템 리스트를 보게 된다.

0   Piano
1   Bright Piano
2   Electric Grand
3   Honky Tonk Piano
4   Electric Piano 1
5   Electric Piano 2
6   Harpsichord
7   Clavinet
8   Celesta
9   Glockenspiel
10   Music Box
11   Vibraphone
12   Marimba
13   Xylophone
14   Tubular Bell
15   Dulcimer
16   Hammond Organ
17   Perc Organ
18   Rock Organ
19   Church Organ
20   Reed Organ
21   Accordion
22   Harmonica
23   Tango Accordion
24   Nylon Str Guitar
25   Steel String Guitar
26   Jazz Electric Gtr
27   Clean Guitar
28   Muted Guitar
29   Overdrive Guitar
30   Distortion Guitar

SingleNoteSynthesizer2는 다른 악기들을 사용하여 하나 이상의 음을 연주하기 위해 SingleNoteSynthesizer를 보강한 프로그램이다. 하지만 이를 실행해보면, 시간을 맞추기 위해 Thread가 여전히 휴면하는 것을 볼 수 있다. 이는 음악을 만들고 연주하기 위한 좋은 방법이 아닐 뿐더러, AWT Event Dispatch thread에서 이를 실행했다면 GUI가 이벤트를 처리하거나 보수하는 것을 막게 될 것이다. 이 부분은 마지막 테크닉을 설명할 때 다루기로 하겠다.

SingleNoteSynthesizer2를 보면, 사용자가 만든 악기 리스트에서 사용하고 싶은 악기의 int값을 찾아 MidiChannel의 programChange()메소드에 넣고, 이를 호출해서 연주될 악기를 바꿀 수가 있다. 예에서 교회 오르간을 나타내는 int는 19이다.

startNote()와 stopNote()메소드가 각각 NOTE_ON명령어와 NOTE_OFF명령어를 이용해서 메세지를 생성할 수 있도록 코드를 추가한 것을 주목하자. playNote()메소드는 startNote()를 호출하고, duration에 지정된 밀리초(milliseconds)동안 휴면한 후, stopNote()를 호출한다.

주요 화음은 한개의 기본 음, 기본음으로부터 4개 반음 올린 음, 3개 반음 선간(spaces)을 올린 음으로 구성된다. SingleNoteSynthesizer2에서, playMajorChord()메소드는 먼저 하나씩 3개의 음를 연주한 뒤, 이들을 동시에 연주한다. 타이밍은 Threads를 이용해서 맞추는데, 타이밍이 중요할 경우에는 Sequencer를 이용하는 것이 좋다.

Sequencer객체는 MIDI 이벤트의 타이밍을 좀 더 효과적으로 컨트롤 할 수 있는 메소드를 포함한다. 전에는 MidiSystem의 팩토리 메소드를 사용했던 반면, 여기서는 Sequencer를 이용한다. Sequencer를 사용한 예는 다음 예제인 SequencerSound에서 볼 수 있다. 앞선 예와 비교해서 여기서는 코드에 몇몇 주요한 변경이 있음을 유의해야 한다. 먼저 startNote()와 stopNote()메소드는 생성되는 메세지가 일어나는 시점을 나타내는 int값을 갖는다. createTrack()메소드는 8분음표마다 4번의 똑딱 소리를 내서 하나의 Sequence를 생성한다. 예를 들면, 4/4박자는 4개의 8분음표 혹은 16번의 똑딱 소리로 나뉘어 질 수 있는 것처럼, 박자를 나눌 때 이 메소드를 사용할 수 있다.

setShortMessage()메소드는 setMessage()를 호출한후, ShortMessage에 기반하고, 똑딱 소리로 결합된 MidiEvent객체를 생성한다. 그리고는 마지막으로 트랙에 MidiEvent를 추가한다. 트랙을 재생할 때, startSequencer()메소드는 새롭게 생성된 Sequence를 기존 Sequence에 할당하고, start()메소드를 호출해서 재생시킬수가 있다. 또한 setTempoInBPM()메소드를 이용해서 템포를 분당 16 비트(BPM)으로 설정할 수가 있다.

이 글은 MIDI 파일을 생성하고 재생하는 방법을 설명했다. 개발자는 사운드뱅크가 J2SE SDK에 따라서 자동적으로 인스톨되기 때문에 음악을 들을 수가 있다. 사운드 뱅크는 악기 설정을 보여주고, 사운드를 종합하기 위해 필요하다. (사운드뱅크에 대해서는 후에 더 자세히 다루도록 하겠다.) 비 개발자들은 생성한 파일을 들으려면 사운드뱅크를 따로 설치해야 될 것이다. J2SE 1.4.1 혹은 이후버전에 JRE를 인스톨할 때, 사용자는 사운드뱅크를 포함시키도록 설정해야 한다.

MIDI파일의 저장과 재생관련 정보는 javax.sound.midi의 documentation를, 이 팁에 대한 자세한 내용은 Java Sound API home page를 참고한다.

출처 : http://kr.sun.com/developers/techtips/core_08_05.html#1
"Java" 카테고리의 다른 글
  • EJB 기반 프로젝트 수행 가이드 ② (0)2007/05/18
  • EJB 기반 프로젝트 수행 가이드 ① (0)2007/05/18
  • MIDI Sound 생성하기 (0)2007/05/18
  • Java 성능개선을 위한 Programming 기법 (0)2007/05/18
  • JMX와 JINI로 분산 환경에서 시스템 관리하기 - 2 (0)2005/12/27
2007/05/18 11:03 2007/05/18 11:03
Posted by webdizen
Tags Java, MIDI
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2989

Leave your greetings.

[로그인][오픈아이디란?]

«Prev  1 2 3 4  Next»

RSS HanRSS
Blog Image
webdizen
이 곳은 컴퓨터에 대해 연구하고, 공유하고, 소통하기 위한 연구실입니다. 개인적으로는 OLAP, Data Mining, Semantic Web, Data Modeling에 대해서 연구하고 있습니다.

Categories

전체 (2998)
Webdizen (134)
Life (6)
Diary (16)
Blog (9)
IDEA (1)
Travel (10)
Book (14)
Photo (7)
Movie (7)
Music (13)
Leisure Sports (10)
Funny (5)
Hardware (119)
Software (120)
Windows (5)
Unix & Linux (119)
Installation (4)
Kernel (10)
System (34)
Develop (22)
X-Window (0)
Applicaton (31)
Security (4)
Framework (2)
Hadoop (2)
Programming (805)
Algorithm & Data Structure (1)
Assembly (38)
UNIX/Linux C (95)
C++ (128)
STL (4)
Java (38)
Win32 API (92)
ATL/COM (44)
MFC (151)
.NET (26)
WCF/WPF (4)
C# (28)
Network Programming (17)
Database Programming (12)
OpenGL / DirectX (13)
Multimedia Programming (0)
Game Programming (21)
Parallel Distributed Progra... (0)
Reverse Engineering (0)
Debugging (9)
Python (1)
Ruby (1)
Ruby on Rails (1)
QT (4)
GTK (0)
JSP (0)
PHP (6)
ASP.NET (6)
ASP (3)
Development (28)
Useful Library (2)
Data Modeling (0)
Database (105)
Oracle (4)
MSSQL (41)
MySQL (2)
Data Warehouse (2)
Data Mining (3)
Network (66)
Web (78)
DHTML (4)
XHTML (1)
Javascript (1)
CSS (1)
AJAX (9)
XML (11)
Flex (1)
Silverlight (3)
Security (91)
DoS (1)
Kernel (10)
Scanning (3)
Sniffing (0)
Spoofing (4)
Overflow (28)
Web (11)
Shell (10)
Format String (14)
Window (2)
Embedded (70)
Multimedia (27)
Mobile (14)
Graphic (24)
Management (633)
Knowledge (581)
Hadoop (0)

Notice

  • 메타 블로그 사이트에 등록
  • 새해 맞이 블로그의 변화
  • 블로그 명칭 변경
  • 도메인(www.webdizen.net) 구...
  • TEXTCUBE 1.6.1로 업그레이드...

Tags

  • 샌프란시스코
  • Cheat Sheets
  • Data Mart
  • 돌체 비타 로소
  • 정치인
  • Port
  • 능력
  • 이수안
  • 저장 프로시저
  • 태백관
  • 집현관
  • COM+
  • CAB
  • 세마포어
  • 형식라이브러리
  • PE
  • 웹
  • 여자
  • IPC
  • Kprobes

Recent Articles

  • ASCII Code의 CRLF 제거 방법.
  • Hadoop 에서 c++ API 이용시....
  • Ubuntu Linux에서 Hadoop 구....
  • 내 심장을 한껏 뛰게한 "국가....
  • 스타 스키마 데이터베이스 설....

Recent Comments

  • ■ 온라인카지노 ▶ http://L....
    asdf 11/21
  • 그리고 혹시 해외여행자보험....
    kim 11/05
  • ★★실제 바다게임장과 똑같....
    asdf 11/04
  • sbsyama.co.to← 짱5000만당....
    asdf 11/04
  • ♡KicaZ??o(???) 바카라사....
    fdsf3fass 11/03

Recent Trackbacks

  • 파일 열기/저장하기 CFileDialog.
    은마군의 나태블록 02/11
  • World IT Show 2008.
    상우 :: Oranzie's BLOG 2008
  • cvs서버 설치하기.
    3인3색 2008
  • 속속 공개되는 Google Chart....
    PHP와 Web 2.0 2007
  • 마방진을 구하는 프로그램.
    Oranzie's BLOG 3 2007

Archive

  • 2009/09 (3)
  • 2009/08 (1)
  • 2009/03 (1)
  • 2009/02 (9)
  • 2009/01 (13)

Calendar

«   2009/11   »
일 월 화 수 목 금 토
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          

Bookmarks

    • Administration
      • IIS.NET
      • NTFAQ
      • OS의 모든 것
      • 리눅스포털
    • Database
      • SQL Server Central
      • SQL Team
    • Development
      • .NET Heaven
      • ASP Alliance
      • ASP.NET 2.0
      • Bullog.net
      • C# Corner
      • C++ (C PlusPlus.com)
      • C++ Reference
      • CodeGuru
      • CodePlex
      • DebugLab
      • Dev Articles
      • Devpia
      • DotNet Junkies
      • DotNet Zone
      • Driver Online
      • GOSU.NET
      • HOONS 닷넷
      • Joinc 팀블로그
      • KOSR
      • MSDN Home Page
      • OSR Online
      • Sky.ph - 개발자 커뮤니...
      • TAEYO.NET
      • The Code Project
      • WindowsClient.net
      • 김상욱의 개발자 Side
      • 조인시 위키
    • Human Networks
      • belief21c's e-space
      • I think I can
      • Invisible Rover's Blog :D
      • Rodman®
      • ■ Feel So Good~! ■
      • 까만 나비
      • 나를 가꾸는 시간.
      • 나만의 즐거움~~!
      • 단녕
      • 상우 :: Oranzie's BLOG
    • Information Technology
      • Microsoft TechNet
      • 지디넷코리아 - 글로벌...
    • Security
      • FoundStone
      • milw0rm
      • NewOrder
      • OpenRCE
      • Phrack.org
      • Reverse Engineering b1...
      • Reverse Engineering Team
      • RootKit
      • SecurityFocus
      • SecurityXploded by Nag...
      • Wow Hacker
      • Zone-H
Textcube
Louice Studio Inc.
Powered by Textcube. Original designed by Tistory.