블로그 이미지
좋은느낌/원철
이것저것 필요한 것을 모아보렵니다.. 방문해 주셔서 감사합니다..

calendar

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 31

Notice

    2008. 7. 1. 15:44 개발/Java

    J2EE를 마스터하는 것은 어려운 일이다. 기술과 신조어들이 나날이 늘어가기 때문이기도 하다. Java Naming and Directory Interface (JNDI)는 처음부터 Java 2 Platform, Enterprise Edition (J2EE)의 핵심에 있었지만 풋내기 J2EE 개발자들은 이를 충분히 활용하지 못한다. 이 글에서 J2EE 애플리케이션에서의 JNDI의 역할을 규명하고 애플리케이션을 전개로부터 분리하는 방법을 설명하겠다.

    J2EE 플랫폼이 엔터프라이즈 개발자의 삶을 향상시킨 것 만큼 J2EE의 많은 스팩과 기술을 배워야 하는 대가를 치러야 했다. Dolly Developer는 엔터프라이즈 애플리케이션의 전개에 수반되는 부담을 경감시키는 한 가지 기능인 JNDI를 발견하지 못한 많은 개발자들 중 하나이다. JNDI를 잘 활용했을 때 상황이 어떻게 향상되었는지를 살펴보자.



    너무나 익숙한 여행

    Dolly 개발자는 JDBC 데이터 소스를 사용하는 웹 애플리케이션을 코딩중이다. 그녀는 MySQL을 사용하고 있다는 것을 알기 때문에 MySQL JDBC 드라이버 클래스에 대한 레퍼런스를 인코딩하고 JDBC URL을 사용하여 웹 애플리케이션의 데이터베이스에 연결한다. 데이터베이스 커넥션 풀링은 중요하기 때문에 커넥션 풀링 패키지를 포함하고 이를 설정하여 단 64 커넥션만 사용해야 한다; 그녀는 데이터베이스 서버가 128 클라이언트 커넥션을 할당하도록 설정되었다는 것을 알고 있다.

    재앙으로 향하는 Dolly

    개발 단계 동안은 모든 것이 순조롭게 진행된다. 하지만 전개할 때 상황은 달라진다. 네트워크 관리자는 데스크탑에서 제품 또는 스테이징 서버로 액세스하지 못한다는 것을 지시했기 때문에 그녀는 각 전개 단계마다 다른 버전의 코드를 만들어야 한다. 상황이 이렇기 때문에 그녀에게는 테스트, 스테이징, 제품 마다 전개 가능한 새로운 JDBC URL이 필요하다. (설정 관리에 익숙한 사람들은 개별 구현을 각 환경에 전개한다는 개념 때문에 망설이지만 이는 매우 일반적인 상황이기 때문에 너무 그럴 필요는 없다.)

    Dolly가 그녀가 전개 가능한 다른 URL 들로 설정 문제를 “해결”했다라고 생각했을 때, 그녀는 데이터베이스 관리자가 제품에서 MySQL 인스턴스를 실행하는 것을 원하지 않는다는 것을 발견한다. 이것은 개발에는 알맞지만 중요한 미션을 위한 데이터용 비즈니스 표준은 DB2®이다. 이제 그녀의 구현은 데이터 URL을 달리 해야 할 뿐만 아니라 다른 드라이버를 갖추어야 한다.

    상황은 더 나빠진다. 그녀의 애플리케이션은 너무 유용해서 너무 중요해진다. 애플리케이션 서버에서 페일오버 기능을 얻고 4개의 서버 클러스터로 복제된다. 하지만 데이터베이스 관리자들은 빨간 깃발을 들어올리지만 그녀의 애플리케이션의 모든 인스턴스는 64 커넥션을 사용한다. 데이터베이스 서버는 전체적으로 200개의 사용 가능한 커넥션을 갖고 있다. 모두가 Dolly의 애플리케이션에 의해 연결되고 있다. 더욱이 DBA는 Dolly의 애플리케이션이 단 32개의 커넥션만을 필요하다는 것을 결정했고, 이것도 하루에 단 한 시간 동안이다. 그녀의 애플리케이션이 올라갈 때 애플리케이션은 데이터베이스 레이어에서의 경쟁 문제로 끝나서 그녀의 유일한 옵션은 클러스터링 된 커넥션의 수를 변경하여 클러스터가 늘어나면 다시 이와 같은 것을 준비하거나 애플리케이션이 다른 클러스터에서 복제되도록 한다. 애플리케이션 설정에 대해 내린 결정이 시스템과 데이터베이스 관리자들에겐 최상인 것 같았다.

    J2EE 역할

    Dolly는 J2EE 역할에 대한 지식을 기반으로 애플리케이션을 개발했다면 이런 딜레마는 피할 수 있었을 것이다. J2EE 스팩은 다양한 개발 역할들에 책임을 위임한다: 컴포넌트 공급자, 애플리케이션 어셈블러, 전개자, 시스템 관리자. (많은 조직들에서 컴포넌트 공급자와 어셈블러 역할은 통합되었다. 전개자와 시스템 관리자 역할 또한 통합되어 있다.) J2EE에서의 JNDI의 역할을 이해하기 전에 J2EE의 역할을 이해하는 것도 중요하다.

    컴포넌트 공급자
    이 역할은 J2EE 컴포넌트를 만드는 책임이 있다. 웹 애플리케이션, Enterprise JavaBean (EJB) 컴포넌트, 애플리케이션 컴포넌트(Swing 기반의 GUI 클라이언트 애플리케이션) 등이 될 수 있다. 컴포넌트 공급자에는 HTML 콘텐트 디자이너, 문서 프로그래머, 기타 개발자 역할들이 포함된다. 대부분의 J2EE 개발자들은 컴포넌트 공급자 역할을 수행하고 있다.

    애플리케이션 어셈블러
    이 역할은 다중의 J2EE 모듈을 인접해 있는 전개 가능한 모든 것에 묶는 것이다: enterprise archive (EAR) 파일. 애플리케이션 어셈블러는 컴포넌트를 선택하고, 이들이 인터랙팅 하는 방법을 정의하고, 보안과 트랜잭션 애트리뷰트를 설정하고, 애플리케이션을 EAR 파일로 패키징한다. WebSphere® Studio, IDEA, JBuilder, WebLogic Workshop 같은 많은 IDE 들은 EAR 파일의 인터랙티브 설정을 갖춘 애플리케이션 어셈블러를 지원하는 기능을 갖고 있다.

    전개자
    이 역할은 전개를 담당하고 있다. EAR을 J2EE 컨테이너(애플리케이션 서버)에 설치하면서 데이터베이스 커넥션 풀 같은 리소스들을 설정하고, 애플리케이션에 필요한 리소스들을 애플리케이션 서버의 특정 리소스들로 바인딩하고 애플리케이션을 시작한다.

    시스템 관리자
    이 역할은 컨테이너가 필요로 하는 리소스들이 해당 컨테이너에서 사용할 수 있는 것인지를 확인한다.

    역할 수행

    비즈니스 로직과 영속성을 위해 하나의 웹 애플리케이션과 하나의 EJB 컴포넌트를 포함하고 있는 엔터프라이즈 애플리케이션을 상상해 보자. 이 애플리케이션을 개발하는 데는 많은 컴포넌트 공급자들이 개입된다. 많은 경우 같은 사람이 이 모든 일들을 수행하곤 한다. 이 컴포넌트들은 데이터 전송 객체(JAR 파일), EJB 인터페이스(또 다른 JAR 파일), EJB 구현(이것 역시 또 다른 JAR 파일), 사용자 인터페이스 컴포넌트-서블릿, JSP, HTML 페이지, 정적 웹 콘텐트 들을 포함시킬 수 있다. 사용자 인터페이스 컴포넌트들은 웹 애플리케이션으로 패키징되고 여기에는 서블릿 클래스, JSP 파일, 정적 콘텐트, EJB 인터페이스를 비롯하여 다른 필요한 컴포넌트를 포함하고 있는 JAR 등이 포함된다.

    일반적인 웹 애플리케이션을 구현하는데 얼마나 많은 JAR 파일들이 사용되는지를 고려할 때 준비해야 할 많은 컴포넌트들이 있는 것 처럼 들린다. 종속성(dependency)은 여기에서 조심스럽게 다뤄져야 한다. 인터페이스들과 전송 객체들은 웹 애플리케이션과 EJB 구현에 대한 합당한 종속관계이다. 하지만 이러한 계열의 종속관계는 이 같은 방향으로 모두 실행되어야 한다: 주기적인 종속성은 피해야 한다. WAR 파일과 EJB JAR 파일 같은 J2EE 컴포넌트들은 전개 단위 밖에 있는 리소스에 대한 종속성을 선언해야 한다.

    애플리케이션 어셈블러는 웹 애플리케이션에서의 종속성을 추가하고 모든 것을 하나의 엔터프라이즈 애플리케이션으로 패키징 해야한다. 툴이 많은 도움이 된다. IDE는 모듈과 JAR의 종속성을 반영하는 프로젝트 구조를 만드는데 도움이 되고 모듈의 추가 또는 배제를 지정할 수 있도록 한다.

    전개자는 컴포넌트가 요구하는 리소스들이 전개 환경에 존재하는지를 확인하고 이들을 플랫폼의 사용 가능한 리소스들로 바인딩하는 책임이 있다. 예를 들어, 웹 애플리케이션의 외부 EJB 레퍼런스(전개 디스크립터의 ejb-ref) 이 지점에서 실제로 전개된 EJB 컴포넌트에 묶인다.

    외부 리소스로의 늦은 바인딩

    중요한 J2EE 애플리케이션은 작동하기로 되어있는 환경을 설명하고 있는 정보에 액세스해야 한다. 다시 말해서 컴포넌트를 개발하고 테스트 하는 데에는 개발자가 일종의 전개 의무를 지는 것이 필요하다는 것을 의미한다. 코드를 테스트하는 임시적인 목적일 경우에도 그렇다. 이렇게 함으로서 개발자 도메인 밖으로 벗어났다는 것을 이해하는 것이 중요하다. 그렇지 않으면 의도되지 않았던, 때로는 재앙 같은 함축을 지닌 JDBC 드라이버, URL, JMS 큐 이름, 기타 머신 리소스들에 의존하려는 유혹에 빠진다.

    구원자 JNDI

    Dolly의 문제에 대한 솔루션은 데이터 스토어에 대한 모든 직접적인 레퍼런스들을 애플리케이션 코드에서 제거하는 것이다. JDBC 드라이버에 대한 레퍼런스, 서버 이름, 사용자 이름 또는 패스워드, 심지어 데이터베이스 풀링 또는 커넥션 관리 도 없다.Dolly는 그녀의 코드를 작성하여 액세스하려고 하는 외부 리소스들이 무엇인지 무시해야 한다. 다른 사람들이 그러한 외부 리소스들을 활용해야 하는 링크를 제공할 것이라는 것을 이해해야 한다. 이는 전개자가 데이터베이스 커넥션을 그녀의 애플리케이션에 할당 할 수 있도록 한다. Dolly가 개입될 필요가 없다. (데이터 보안은 물론 Sarbanes-Oxley 호환성 까지 다양한 이유가 때문이다.)

    많은 개발자들은 코드와 외부 리소스들 간 강결합(tight coupling)이 잠재적으로 문제가 된다는 것을 알고 있다. 하지만 실제로는 역할의 분리를 종종 무시한다. (팀 크기 또는 전개의 관점에서)작은 개발 노력으로, 역할 분리를 무시하는 것은 충분히 성공적일 수 있다. (결국, 개인적인 애플리케이션일 경우, PostgreSQL 인스턴스로 애플리케이션을 잠그는 것이 좋고, 여기에 의존할 생각은 말아야 한다.)

    J2EE 스팩은 모든 J2EE 컨테이너들이 JNDI 스팩의 구현을 제공해야 한다는 것을 요구한다. J2EE에서의 JNDI의 역할은 “스위치보드(switchboard)” 이다—런타임 시 J2EE 컴포넌트가 다른 컴포넌트, 리소스, 서비스를 찾는 일반적인 메커니즘이다. 대부분의 경우 컨테이너가 제공되는 JNDI 공급자는 제한된 데이터 스토어로서 작용하여 관리자들이 하나의 애플리케이션에서 실행 속성을 설정할 수 있고 다른 애플리케이션들이 이들을 레퍼런스 할 수 있게 한다. (Java Management Extensions (JMX)도 이 목적으로 사용될 수 있다.) J2EE 애플리케이션에서의 JNDI의 주 역할은 인다이렉션 레이어를 제공하여 컴포넌트들이 필요한 리소스들을 인다이렉션을 의식하지 않고 찾을 수 있도록 하는 것이다.

    문제 해결

    다시 Dolly의 상황으로 돌아가보자. 그녀의 간단한 웹 애플리케이션에서 그녀는 그녀의 애플리케이션 코드에서 직접 JDBC 커넥션을 사용했다. Listing 1을 보면, JDBC 드라이버, 데이터베이스 URL, 서블릿의 사용자 이름과 패스워드의 이름을 분명히 코딩했음을 알 수 있다:


    Listing 1. 전형적인 (하지만 좋지 않은) JDBC 사용

    
    
    Connection conn=null;
    try {
      Class.forName("com.mysql.jdbc.Driver",
                    true, Thread.currentThread().getContextClassLoader());
      conn=DriverManager.getConnection("jdbc:mysql://dbserver?user=dolly&password=dagger");
      /* use the connection here */
      conn.close();
    } 
    catch(Exception e) {
      e.printStackTrace();
    } 
    finally {
      if(conn!=null) {
        try {
          conn.close();
        } catch(SQLException e) {}
      }
    }
    

    이러한 방식으로 설정 정보를 지정하는 대신 Dolly(와 그녀의 동료들)는 JNDI를 사용하여 JDBC DataSource를 찾았다면 더 좋았을 것이다. (Listing 2):


    Listing 2. JNDI를 사용하여 데이터 소스 얻기

    
    
    Connection conn=null;
    try {
      Context ctx=new InitialContext();
      Object datasourceRef=ctx.lookup("java:comp/env/jdbc/mydatasource");
      DataSource ds=(Datasource)datasourceRef;
      conn=ds.getConnection();
      /* use the connection */
      conn.close();
    } 
    catch(Exception e) {
      e.printStackTrace();
    } 
    finally {
      if(conn!=null) {
        try {
          conn.close();
        } catch(SQLException e) { }
      }
    }
    

    JDBC 커넥션을 얻을 수 있으려면 소소한 전개 설정을 하여 로컬 컴포넌트의 JNDI 콘텍스트의 JDBC DataSource를 검색해야 한다. 조잡할 것 같지만 배우기 쉽다. 불행히도, 컴포넌트를 테스트하기 위해서 개발자는 전개자의 경지를 건너서 애플리케이션 서버를 설정하는 것을 준비해야 한다.

    JNDI 레퍼런스 설정하기

    JNDI가 java:comp/env/jdbc/mydatasource 레퍼런스를 해결하려면 전개자는 <resource-ref>태그를 web.xml 파일(웹 애플리케이션용 전개 디스크립터)에 삽입해야 한다. <resource-ref>태그는 “이 컴포넌트는 외부 리소스에 대한 의존성을 갖고 있다.”는 것을 의미한다. (Listing 3):


    Listing 3. resource-ref 엔트리

    
    
    <resource-ref>
      <description>Dollys DataSource</description>
      <res-ref-name>jdbc/mydatasource</res-ref-name>
      <res-ref-type>javax.sql.DataSource</res-ref-type>
      <res-auth>Container</res-auth>
    </resource-ref>
    

    <resource-ref> 엔트리는 jdbc/mydatasource라고 하는 컴포넌트 네이밍 콘텍스트의 리소스가 전개자에 의해 설정될 것이라는 것을 알려준다. 컴포넌트 네이밍 콘텍스트는 접두사 java:comp/env/로 표시되어 완전한 자격의 로컬 리소스 이름은 java:comp/env/jdbc/mydatasource이다.

    이것은 단지 외부 리소스에 대한 로컬 레퍼런스를 정의하고 이 레퍼런스가 가르키게 될 실제 리소스는 만들지 않는다. (자바와 비슷한 언어들은 <resource-ref>Object foo같은 레퍼런스를 선언하지만, Object를 실제로 참조하기 위해 foo를 설정하지 않는다.)

    전개자의 역할은 DataSource를 만드는 것이다. (또는, 우리의 자바 예제에서 처럼 foo가 지목할 Object를 만든다.) 각 컨테이너에는 데이터 소스를 설정하는 고유의 메커니즘이 있다. JBoss 에서 데이터 소스는 서비스와 함께 정의되고($JBOSS/server/default/deploy/hsqldb-ds.xml 참조), 이것이 DataSource를 위한 글로벌 JNDI 이름이라는 것을 지정한다. (기본적으로, DefaultDS). 일단 리소스가 만들어지면 여전히 중요한 제 3 단계가 남아있다: 리소스를 애플리케이션 컴포넌트가 사용하는 로컬 이름으로 연결 또는 바인딩하는 것. 이 웹 애플리케이션의 경우 벤더 스팩의 전개 디스크립터 확장은 바인딩을 지정하는데 사용된다. (Listing 4). (JBoss는 벤더 스팩의 웹 애플리케이션 전개 디스크립터에 jboss-web.xml이라고 하는 파일을 사용한다.)


    Listing 4. 벤더 스팩의 전개 디스크립터에서 리소스를 JNDI 이름으로 바인딩하기

    
    
    <resource-ref>
       <res-ref-name>jdbc/mydatasource</res-ref-name>
       <jndi-name>java:DefaultDS</jndi-name>
    </resource-ref>
    

    로컬 리소스 ref 이름 (jdbc/mydatasource)은 java:DefaultDS라는 글로벌 리소스로 매핑되어야 한다는 것을 나타내고 있다. 글로벌 리소스 이름이 변경되면 애플리케이션 코드는 변하지 않는다. 단 이 매핑만 그렇다. 여기에는 두 가지 레벨의 인다이렉션이 있다. 하나는 리소스를 정의하고 이름을 짓는 것이고 (java:DefaultDS), 다른 하나는 로컬 컴포넌트 스팩의 이름 (jdbc/mydatasource) 을 이름이 지어진 리소스에 바인딩하는 것이다. (사실, EAR 레벨에서 리소스들을 매핑할 수 있듯이 인다이렉션의 3 번째 레벨에도 가능하다.)

    과거의 DataSource 옮기기

    물론 J2EE의 리소스들은 JDBC 데이터 소스에 제한되지 않는다. 여러 유형의 레퍼런스들이 있다. 리소스 레퍼런스, 환경 엔트리, EJB 레퍼런스 등이 그것이다. 특히 EJB 레퍼런스들은 J2EE의 JNDI에 대한 또 다른 중요한 역할이 있다. 다른 애플리케이션 컴포넌트 찾기이다.

    한 회사가 전개 가능한 EJB 컴포넌트를 구입하여 Order Ontology Processing Services (OOPS) 에서 고객 주문들을 처리할 때 어떤 일이 발생하는지 생각해보자. 이 예제에서는 ProcessOrders V1.0이라고 칭하겠다. ProcessOrders 1.0은 두 조각들로 나타난다: 인터페이스와 지원 클래스 세트(홈 인터페이스 및 원격 인터페이스와 전속 객체 지원), 그리고 실제 EJB 컴포넌트들. OOPS는 이 분야의 전문이기 때문에 선택되었다.

    이 회사는 J2EE 스팩을 따라 EJB 레퍼런스를 사용하는 웹 애플리케이션을 작성한다. 전개자는 ProcessOrders 1.0을 JNDI 트리로 ejb/ProcessOrders/1.0로서 바인딩하고 웹 애플리케이션의 리소스 이름이 글로벌 JNDI 이름을 가르키도록 한다. 여기까지는 EJB 컴포넌트의 일반적인 사용법이다. 하지만 이 회사의 개발 사이클과 공급자의 개발 사이클 사이의 인터랙션을 고려해보면 상황은 더 복잡해진다. 여기에서도 JNDI가 도움이 될 수 있다.

    OOPS가 새 버전인 ProcessOrders V1.1을 발표했다고 가정해보자. 이 새 버전에는 이 회사 내부의 새로운 애플리케이션이 필요로 하는 몇 가지 기능을 갖추었고 EJB 컴포넌트를 위해 비즈니스 인터페이스를 확장한다.

    이 회사에게 몇 가지 선택권이 있다. 이 모든 애플리케이션들을 업데이트하여 새로운 버전을 선택하거나, 고유의 애플리케이션을 작성하거나, JNDI의 레퍼런스를 사용하여 다른 애플리케이션에 영향을 주지 않고 각 애플리케이션이 EJB 컴포넌트의 고유 버전을 사용하도록 할 수 있다. 모든 애플리케이션들을 한번에 업데이트하는 것은 관리가 매우 힘들고, 모든 컴포넌트의 전체 회귀 테스트가 필요하며 함수 테스트가 실패했을 때 또 다른 디버깅이 기다리고 있다.

    인하우스(in-house) 컴포넌트를 작성하는 것은 종종 불필요한 중복 작업이다. 컴포넌트가 해당 비즈니스 영역에서 전문 기술을 닦은 기업에 의해 작성된다면 주어진 IT shop은 비즈니스 기능을 마스터하기 위해 관리할 것 같지 않다. 특성화된 컴포넌트 공급자도 마찬가지다.

    이미 알고 있겠지만 최상의 솔루션은 JNDI를 사용하는 것이다. EJB JNDI 레퍼런스는 JDBC 리소스 레퍼런스들과 매우 비슷하다.
    각 레퍼런스의 경우 전개자는 새로운 컴포넌트를 특정 이름 (ejb/ProcessOrders/1.1) 에 있는 글로벌 트리로 바인딩 한다. 그리고 EJB 컴포넌트를 필요로 하는 다른 모든 컴포넌트를 위해 그 컴포넌트용 전개 디스크립터에서 EJB 레퍼런스를 결정한다. 더 오래된 애플리케이션들(1.0V 기반)은 변경 및 재테스트를 할 필요가 없고 구현 시간, 비용, 복잡성도 줄어든다. 서비스가 버저닝되는 환경에서 이것은 강력한 방식이다. 이러한 종류의 설정 관리는 애플리케이션 아키텍쳐의 모든 획득 가능한 컴포넌트들, EJB 컴포넌트에서 JMS 큐 까지 수행된다. 간단한 설정 스트링 또는 다른 객체 들까지도 수행된다. 시간이 지나면서 서비스가 변경할 때 관리 비용도 낮아지고 전개 및 통합의 수고로움도 줄어든다.

    결론

    모든 프로그래밍 문제는 단 한 개 이상의 추상화(또는 인다이렉션) 레이어로 해결될 수 있다는 오래된 컴퓨터과학 분야의 농담이 있다. J2EE에서, JNDI는 J2EE 애플리케이션들을 하나로 묶는 풀의 역할을 한다. 이것이 엔터프라이즈를 통해 확장성있고 기능이 많으면서 유연한 애플리케이션의 제공을 가능하게 하는 JNDI가 제공하는 인다이렉션이다. 이것이 J2EE가 약속하는 것이고 계획 수립과 앞선 생각이 이 모든 것을 실현할 수 있다. 사실 사람들이 생각하는 것 보다 쉽다.


    출처 : http://www.ibm.com/developerworks/kr/library/j-jndi/

    posted by 좋은느낌/원철