'ejb'에 해당되는 글 2건

  1. 2008.12.05 CORBA, EJB, COM/DCOM
  2. 2008.08.05 ejb

CORBA, EJB, COM/DCOM

Algorithm 2008. 12. 5. 16:10
CORBA(Common Object Request Broker Architecture)
네 트웍에서 분산 프로그램 객체를 생성, 배포, 관리하기 위한 구조와 규격이며, 네트웍 상의 서로 다른 장소에 있고 여러 벤더들에 의해 개발된 프로그램들이 “인터페이스 브로커”를 통해 통신하도록 해준다. CORBA는 OMG라는 개발자 연합(현재 500여 회원사를 가지고 있다)에서 개발되었다. ISO와 X/Open 양측 모두 CORBA를 분산 객체(컴포넌트로도 알려진)를 위한 표준구조로서 인가하였으며, 현재 CORBA 2.0 이 최신 레벨이다. CORBA의 핵심개념은 ORB이다. 이기종 컴퓨터들의 클라이언트와 서버 네트웍에 대한 ORB 지원이란, 클라이언트 프로그램(객체 자신일 수 있다)이 분산 네트웍에서 서버가 어디 있는지, 또는 서버 프로그램이 어떤 인터페이스를 가질지 인식하지 않고서도, 서버 프로그램이나 객체로부터 서비스를 요구할 수 있다는 것을 의미한다. ORB들간의 요구와 응답을 성립시키기 위해 프로그램들은 GIOP(General Inter-ORB Protocol)를, 인터넷에서는 IIOP를 사용한다. IIOP는 GIOP의 요구와 응답을 각 컴퓨터의 인터넷 TCP 계층으로 대응시킨다. CORBA의 경쟁대상은 독자적인 분산 객체 구조를 가진 마이크로소프트의 DCOM이다. 그러나 CORBA와 마이크로소프트는 게이트웨이를 통해 COM 클라이언트 객체가 CORBA 서버와 통신할 수 있도록 하는데 합의하였다 (그 역방향의 통신도 마찬가지다). 객체지향 프로그래밍과 CORBA를 향한 분산 프로그래밍 구조의 선두주자였던 DCE는 현재 많은 회사에서 사용되고 있다. DCE는 아마 CORBA와 함께 계속 존재할 것이고 둘 사이를 연결시켜 주는 "다리"가 나타날 것이다.

EJB(Enterprise JavaBeans)
클 라이언트/서버 모델의 서버 부분에서 운영되는 자바 프로그램 컴포넌트들을 설정하기 위한 아키텍처이다. EJB는 네트웍 내의 클라이언트들에 분산되어 있는 프로그램 컴포넌트들을 위한 자바빈즈 기술 위에서 구현된다. EJB는 기업들에게, 새로운 프로그램 컴포넌트가 추가되거나 또는 변경될 때마다, 각 개별 컴퓨터를 갱신하지 않고서도 서버에서 변화를 통제할 수 있도록 하는 이점을 제공한다. EJB 컴포넌트들은 다중 응용프로그램들에서 재 사용되는 장점을 가지고 있다. EJB 빈이나 컴포넌트가 배치되기 위해서는 컨테이너라고 불리는 특정 응용프로그램의 일부가 되어야한다. 썬마이크로시스템즈에 의해 비롯된 EJB는, 개략적으로 마이크로소프트의 COM/DCOM 아키텍처에 필적하는 것이다. 그러나, 모든 자바 기반의 아키텍처와 같이, 프로그램들은 윈도우즈뿐만 아니라 모든 주요 운영체계에 걸쳐 배치될 수 있다. EJB의 프로그램 컴포넌트들은 대개 서블릿이라고 알려져 있다. 서블릿을 실행시키는 응용프로그램이나 컨테이너를 때로 애플리케이션 서버라고도 부르는 경우가 있다. 서블릿의 전형적인 용도는 CGI와 Perl 스크립트를 사용하는 웹프로그램을 대체하는 것이다. 또다른 일상적인 용도는 웹사용자와 레거시 메인프레임 응용프로그램과 데이터베이스 사이의 인터페이스를 제공하는 것이다. EJB 내에 두 가지 종류의 빈즈가 있는데, 하나는 세션 빈즈이고 또 하나는 엔터티 빈즈이다. 엔터티 빈즈는 세션 빈즈와 달리, 지속성을 가지고 있으며 원래의 습성이나 상태를 유지할 수 있다.

COM/DCOM(Distributed Component Object Model)
네트웍 상에서 클라이언트 프로그램 객체가 다른 컴퓨터에 있는 서버 프로그램 객체에 서비스를 요청할 수 있도록 해주는 마이크로소프트의 개념이자 프로그램 인터페이스들이다. COM은 같은 컴퓨터(윈도우95나 NT 시스템) 내에서 사용될 수 있도록 클라이언트와 서버에 인터페이스 집합을 제공한다. 예를 들어, 어떤 웹 사이트에 자신의 웹서버가 아닌 다른, 즉 네트웍 상의 보다 특정한 서버에서만 수행되는 스크립트나 프로그램을 가지도록 페이지를 만들 수 있을 것이다. 그 웹사이트의 프로그램(마치 클라이언트 객체처럼 동작하는)은 DCOM 인터페이스를 이용해, 필요한 절차를 수행하고 결과를 웹 서버 사이트에 돌려 주는 특정한 서버 객체에 RPC를 전달할 수 있으며, DCOM은 그 결과를 웹 페이지 뷰어에 넘긴다. DCOM은 또한 대규모 네트웍이나 인터넷과 같은 네트웍 환경에서도 작동할 수 있다. DCOM은 TCP/IP와 HTTP를 사용하며, NT 4.0의 일부가 되었고, 윈도우 95에서 무료로 업그레이드할 수 있다. DCOM은 곧 대부분의 유닉스 플랫폼이나 IBM과 같은 대규모 서버에서도 사용이 가능해질 것이다. DCOM은 OLE Remote Automation을 대체한다. DCOM은 여러가지 분산 서비스들을 제공한다는 차원에서 CORBA와 대등하다. DCOM은 네트웍 환경에서 프로그램과 자료 객체에 대한 마이크로소프트의 접근방식이며, CORBA는 OMG의 후원자들인 그외 나머지 정보통신업계의 지원을 받고있다.

 

'Algorithm' 카테고리의 다른 글

ACID  (0) 2008.09.16
Sort.. algorithm  (0) 2008.09.09
HDLC 프로토콜  (0) 2008.08.26
셀정렬  (1) 2008.08.23
삽입정렬  (0) 2008.08.23
Posted by 으랏차
,

ejb

dev 2008. 8. 5. 17:03

출처 : 월간 마이크로 소프트웨어


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라는 클래스는 이러한 역할을 하는 클래스다.

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


public 클래스 EmpManagerEJB implements javax.ejb.세션빈{

    public EmpManagerEJB()
    {
    }

    public String getEmpName(String empNo) throws Exception
    {
      Connection con = null;
      String eName = null;

      try {
         con = ConnManager.getConnection();
         EmpDB DB = new EmpDB();
         eName = DB.getEmpName(con,empNo);

      } catch(Exception e) {
         throw new Exception(e.toString());
      } finally {
         try {
            ConnManager.closeConnection(con);
         } catch(Exception e) {
            throw new Exception(e);
         }
      }
      return eName;
    }

    // ---------------------------------------------------------------
    // 세션빈 interface implementation
    public void ejbActivate()
    {
      // to do: code goes here.

    }
.... 이하 생략,

}

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

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


    public String getEmpName(Connetion con , String empNo)
    throws Exception {

      String eName = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;

      /*
         SELECT
            EMP_NAME
         FROM EMP
         WHERE EMP_NO = '000001'
      */

      String sql =
         "SELECT " +
         " EMP_NAME " +
         "FROM EMP " +
         "WHERE EMP_NO = ? ";

      try {
         pstmt = con.prepareStatement(sql);
         pstmt.setString(1, empNo);
         rs = pstmt.executeQuery();

         if(rs.next()) {

            eName = rs.getString("EMP_NAME"));

         }
      } finally {
         ConnManager.closeResultSet(rs);
         ConnManager.closeStatement(pstmt);
      }
      return eName;
    }

사번 테이블에서 사원 번호로 사원명을 리턴하는 메소드이다. 이 메소드 중에서 가장 주의깊게 봐야 할 부분은 PreparedStatement를 Statement interface 대신 사용한 부분이다. 조회성 쿼리를 DB에 던지고자 할 경우 아무 생각없이 Statement interface를 사용하는 개발자가 많은데, 이렇게 만들어서는 안된다.
① 세션빈에서의 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라는 클래스는 이러한 역할을 하는 클래스다.

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


public 클래스 EmpManagerEJB implements javax.ejb.세션빈{

    public EmpManagerEJB()
    {
    }

    public String getEmpName(String empNo) throws Exception
    {
      Connection con = null;
      String eName = null;

      try {
         con = ConnManager.getConnection();
         EmpDB DB = new EmpDB();
         eName = DB.getEmpName(con,empNo);

      } catch(Exception e) {
         throw new Exception(e.toString());
      } finally {
         try {
            ConnManager.closeConnection(con);
         } catch(Exception e) {
            throw new Exception(e);
         }
      }
      return eName;
    }

    // ---------------------------------------------------------------
    // 세션빈 interface implementation
    public void ejbActivate()
    {
      // to do: code goes here.

    }
.... 이하 생략,

}

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

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


    public String getEmpName(Connetion con , String empNo)
    throws Exception {

      String eName = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;

      /*
         SELECT
            EMP_NAME
         FROM EMP
         WHERE EMP_NO = '000001'
      */

      String sql =
         "SELECT " +
         " EMP_NAME " +
         "FROM EMP " +
         "WHERE EMP_NO = ? ";

      try {
         pstmt = con.prepareStatement(sql);
         pstmt.setString(1, empNo);
         rs = pstmt.executeQuery();

         if(rs.next()) {

            eName = rs.getString("EMP_NAME"));

         }
      } finally {
         ConnManager.closeResultSet(rs);
         ConnManager.closeStatement(pstmt);
      }
      return eName;
    }

사번 테이블에서 사원 번호로 사원명을 리턴하는 메소드이다. 이 메소드 중에서 가장 주의깊게 봐야 할 부분은 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 객체로 접근하지 말 것
세션빈에서 한 레코드에 대해 변경을 하는 작업을 엔터티빈으로 한다고 하고, 이때 요청사항으로 추가되는 어떤 컬럼에 대해 변경하는 부분이 늘어난다고 하자. 기존 소스는 아래와 같다. 아래 소스는 세션빈 내에서 사원발령이 났을 경우 발령 시작일자와 발령 마감일자를 변경하는 비즈니스 프로세스를 갖고 있다.


  public boolean setMoveEmp(String empNo,String startDt, String endDt ) throws Exception
  {

    boolean doneYn = false;
    EmpHome empHome = null;
    Emp emp = null;

    try {
      EmpPk pk = new EmpPk();

      /* HomeFactory : JNDI 이름으로 Entitybean의 HOME 객체를 리턴한다 */
      empHome = (EmpHome)HomeFactory.getHome(EmpHome.클래스);

      pk.empNo = empNo;
      emp = empHome.finDByPrimaryKey(pk);
      emp.setStartDt( startDt );
      emp.setEndDt( endDt );

    } catch(Exception e) {
      throw new Exception(e.toString());
    } finally {
          ConnManager.closeConnection(con);
      } catch(Exception e) {
          throw new Exception(e);
      }
    }
    return doneYn;
  }


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


  public boolean setMoveEmp(String empNo,String startDt, String endDt ) throws Exception
  {

    /* Connection 선언 추가한다. */
    Connection con = null;
    boolean doneYn = false;
    EmpHome empHome = null;
    Emp emp = null;

    try {

      /* Connection instance 받아온다. */
      con = ConnManager.getConnection();
      EmpDB DB = new EmpDB(); /* DB 래퍼 선언 */



      EmpPk pk = new EmpPk();

      /* HomeFactory : JNDI 이름으로 Entitybean의 HOME 객체를 리턴한다 */
      empHome = (EmpHome)HomeFactory.getHome(EmpHome.클래스);

      pk.empNo = empNo;
      emp = empHome.finDByPrimaryKey(pk);

      emp.setStartDt( startDt );


      /* 변경 일자를 기록하는 부분이 추가된다 */
      DB.setUpdateDate(con,empNo);


      emp.setEndDt( endDt );



    } catch(Exception e) {
      throw new Exception(e.toString());
    } finally {
      try {
        ConnManager.closeConnection(con);
      } catch(Exception e) {
        throw new Exception(e);
      }
    }
    return doneYn;
  }


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

기존

emp.setStartDt( startDt );
emp.setEndDt( endDt );


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


/* 변경 일자를 기록하는 부분이 추가된다 */
DB.setUpdateDate(con,empNo);


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

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

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

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

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

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

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

'dev' 카테고리의 다른 글

locatin 속성  (0) 2008.09.12
EUC-kr, Unicode, UTF-8  (1) 2008.08.20
jms  (0) 2008.08.04
expression pattern  (0) 2008.08.01
script class  (0) 2008.08.01
Posted by 으랏차
,