달력

5

« 2024/5 »

  • 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
2007. 8. 9. 18:26

객체의 hashcode에 대한 고찰 그거/Java2007. 8. 9. 18:26

# hashcode
  대부분 버전의 Java 에서 hashcode는 Heap에 있는 객체의 메모리 주소를 바탕으로 생성됩니다.
  따라서 서로 다른 객체가 같은 hashcode를 가질 수는 없습니다.

# 두 객체가 같다는 것의 의미는?
  1. reference 동치
    Heap에 있는 한 객체를 서로 다른 reference로 참조하는 경우
    두 reference에 대해서 hashCode() method를 호출하면 똑같은 결과가 나옵니다.
 

    String a = "test";
    String b = "test";

    System.out.println("# a.hashCode() : " + a.hashCode());
    System.out.println("# b.hashCode() : " + b.hashCode());

    # a.hashCode() : 3556498
    # b.hashCode() : 3556498


  2. 객체 동치
    Heap에 객체가 두 개 들어있고, 두 reference가 각 객체를 참조하지만 그 두 객체가 동치인 것으로 간주할 수 있는 경우
    Object class로부터 상속받은 hashCode() 와 equals() method를 모두 override 해야 합니다.
    hashCode() method를 override 하지 않으면 기본적으로 객체마다 유일한 hashcode 값을 반환하게 됩니다.

# hashCode() 와 equals() 에 관련된 규칙

  1. 두 객체가 같으면 반드시 같은 hashcode를 가져야 한다.
  2. 두 객체가 같으면 equals() method를 호출했을 때 true를 반환해야 한다.
     즉, a, b가 같으면 a.equals(b)와  b.equals(a) 둘 다 true 여야 한다.
  3. 두 객체의 hashcode 값이 같다고 해서 반드시 같은 것은 아니다.
     하지만 두 객체가 같으면 두 hashcode는 반드시 같아야 한다.
  4. equals()를 override 하면 반드시 hashCode()도 override 해야 한다.
  5. hashCode() 에서는 기본적으로 Heap에 있는 각 객체마다 서로 다른 값을 가지는 유일한 정수를 반환합니다.
     Class에서 hashCode() method를 override 하지 않으면 절대로 그 유형의 두 객체가 같은 것으로 간주될 수 없습니다.
  6. equals() method 에서는 기본적으로 == 연산자를 써서 객체를 비교합니다.
     즉, 두 reference가 Heap 에 있는 한 객체를 참조하는지를 확인하죠.
  따라서 equals()를 override 하지 않으면 절대 그 유형의 두 객체가 같은 것으로 간주될 수 없습니다.
  서로 다른 객체에 대한 reference에 들어있는 bit들이 같을 수가 없으니까요.
  7. a.equals(b)가 true 라면 a.hashCode() == b.hashCode() 도 성립합니다.
     하지만 a.hashCode() == b.hashCode() 가 성립해도 a.equals(b) 가 반드시 true 인 것은 아닙니다.

# hashCode() method 에서 사용하는 'Hashing Algorithm' 에서 서로 다른 객체들에 대해 같은 hashcode 값을 만들어낼 수 있기 때문에
  객체가 같지 않더라도 hashcode는 같을 수 있습니다.


 

'그거 > Java' 카테고리의 다른 글

JDK 5.0의 새로운 기능 Generics  (1) 2007.08.13
java Collection 들  (0) 2007.08.10
The Factory Method Pattern - Design Patterns in Java -  (1) 2007.05.30
MVC 기반 게시판 만들기  (2) 2007.04.27
Collection Framework  (0) 2007.04.23
:
Posted by 뽀기

   간단히 말하자면!!

   => 주어지는 값에 따라 다른 객체를 생성하게 하는 패턴

   이라고 말 할 수 있겠다.

   예를 들어 자판기가 있다고 하자. 이 자판기에는 금액에 따라 뽑을 수 있는 음료수가 다르다.

   100원을 넣으면 다방커피를, 200원을 넣으면 캔커피를, 300원을 넣으면 원두커피를 뽑을 수 있다.

   이 자판기에 어떤 금액을 넣느냐에 따라서 뽑아지는 커피가 다른 것이다.

   즉, 팩토리 패턴으로 설명을 하자면 커피라는 abstrac class 또는 interface가 있고 자판기라는 class가 있다.

   자판기라는 class에는 동전을 넣는 method(insertCoin())가 있고, 커피를 뽑는 method(popCoffee())가 있을 것이다.

   커피를 뽑는 method는 동전을 넣는 method에서 입력받은 동전을 계산하여 동전에 해당하는 커피를 뽑아주는 메소드이다.

   insertCoin() method를 통해서 동전을 넣으면 popCoffee() method를 통해서 금액에 해당하는 Coffee 객체를 생성하게 되는 것이다.

 

사용자 삽입 이미지

Factory Pattern


[소스코드]

  /**
   *
   * @(#) Coffee.java
   * created date : 2007. 05. 30
   *
   */
  package ybchoi.pattern.creational.factory;
 
  /**
   *
   * @author Youbok, Choi
   * @version 1.0
   *
   */
  public interface Coffee {
 
   public String getName();
  }
 
  /**
   *
   * @(#) Coffee.java
   * created date : 2007. 05. 30
   *
   */
  package ybchoi.pattern.creational.factory;
 
  /**
   *
   * @author Youbok, Choi
   * @version 1.0
   *
   */
  public class CanCoffee implements Coffee {
   String coffee_name = null;
 
   public CanCoffee(String name) {
    coffee_name = name;
   }
 
   public String getName() {
    return coffee_name;
   }
 
   public String toString() {
    return "Can Coffee : " + coffee_name;
   }
  }
 
  /**
   *
   * @(#) Coffee.java
   * created date : 2007. 05. 30
   *
   */
  package ybchoi.pattern.creational.factory;
 
  /**
   *
   * @author Youbok, Choi
   * @version 1.0
   *
   */
  public class DabangCoffee implements Coffee {
   String coffee_name = null;
 
   public DabangCoffee(String name) {
    coffee_name = name;
   }
 
   public String getName() {
    return coffee_name;
   }
 
   public String toString() {
    return "Dabang Coffee : " + coffee_name;
   }
  }
 
  /**
   *
   * @(#) Coffee.java
   * created date : 2007. 05. 30
   *
   */
  package ybchoi.pattern.creational.factory;
 
  /**
   *
   * @author Youbok, Choi
   * @version 1.0
   *
   */
  public class WondooCoffee implements Coffee {
   String coffee_name = null;
 
   public WondooCoffee(String name) {
    coffee_name = name;
   }
 
   public String getName() {
    return coffee_name;
   }
 
   public String toString() {
    return "Wondoo Coffee : " + coffee_name;
   }
  }
 
  /**
   *
   * @(#) Coffee.java
   * created date : 2007. 05. 30
   *
   */
  package ybchoi.pattern.creational.factory;
 
  /**
   *
   * @author Youbok, Choi
   * @version 1.0
   *
   */
  public class Machine {
   private int coin = 0;
 
   public void insertCoin(int coin) {
    this.coin = coin;
   }
 
   public void resetCoin() {
    this.coin = 0;
   }
 
   public Coffee popCoffee() {
    if( coin == 100 ) {
     return (new DabangCoffee("보람다방"));
    } else if( coin == 200 ) {
     return (new CanCoffee("레츠비"));
    } else if( coin == 300 ) {
     return (new WondooCoffee("블루마운틴"));
    } else {
     return null;
    }
   }
  }
 
  /**
   *
   * @(#) Coffee.java
   * created date : 2007. 05. 30
   *
   */
  package ybchoi.pattern.creational.factory;
 
  /**
   *
   * @author Youbok, Choi
   * @version 1.0
   *
   */
  public class MainClass {
      public static void main(String[] args) {
      Machine m = new Machine();
      m.insertCoin(100);
      System.out.println("# your coffee is : " + m.popCoffee());
      m.resetCoin();
      m.insertCoin(200);
      System.out.println("# your coffee is : " + m.popCoffee());
      m.resetCoin();
      m.insertCoin(300);
      System.out.println("# your coffee is : " + m.popCoffee());
    }
  }

[실행결과]
   # your coffee is : Dabang Coffee : 보람다방
   # your coffee is : Can Coffee : 레츠비
   # your coffee is : Wondoo Coffee : 블루마운틴

'그거 > Java' 카테고리의 다른 글

java Collection 들  (0) 2007.08.10
객체의 hashcode에 대한 고찰  (0) 2007.08.09
MVC 기반 게시판 만들기  (2) 2007.04.27
Collection Framework  (0) 2007.04.23
try catch finally 사용시 주의해야 할 점!  (1) 2007.04.23
:
Posted by 뽀기
2007. 4. 27. 16:54

MVC 기반 게시판 만들기 그거/Java2007. 4. 27. 16:54

3년인가 4년만에 다시 웹질하면서

만들어 본 게시판이닷!

푸하하하.

소스파일은... 안 올린다. 내 맘이니까~ 캬캬

:
Posted by 뽀기
2007. 4. 23. 14:11

Collection Framework 그거/Java2007. 4. 23. 14:11

Collection Framework

 

만든이: 송지훈
소속: JavaCafe 부시샵
email: johnleen@hanmail.net

 

이번 강좌를 통해 자바의 자료구조인 Collection Framework 에 대해 자세하게 알아보도록 하겠다. 가장 기본적이고 중요한 부분임에도 불구하고 프로그래밍을 공부하는 많은 사람들이 소홀히 생각하는 부분이다. 이 기회에 필자의 강좌를 통해 다시 한번 자바의 컬렉션 프레임워크에 대해 깊이 있는 이해를 할 수 있었으면 한다. 이 강좌에선 1.4에 추가된 새로운 자료구조, 정확하게 2개의 Map 계열 클래스와 1개의 Set 계열 클래스, 총 3가지를 포함해서 그 이전에 존재하는 자료구조 클래스들 모두를 설명한다.

 

1. 자바의 자료구조 Collection Framework 의 구조

다음 그림은 java.util 패키지 안의 컬렉션 프레임워크의 인터페이스를 관계를 나타내는 UML Class Diagram 을 보여주고 있다.
아래 그림에서 실선으로 그려진 화살표는 상속(extend)을 의미한다. 또한 "::" 을 기준으로 왼쪽은 패키지, 오른쪽은 이름을 나타낸다. 네모 안의 보라색 동그라미 안에 i 라고 써있는 것은 해당 객체가 인터페이스라는 것을 의미한다. 또 컬렉션 인터페이스를 구현한 클래스들을 설명할 때 나오겠지만 점선으로 그려진 화살표는 구현(implements)을 의미하고 클래스이기 때문에 녹색 동그라미안에 C 라는 글자가 쓰여있는 것을 보게 될 것이다. (참고로 필자는 이클립스 플러그인으로 제공되는 OMANDO 라는 UML 툴을 사용했다)

그림에서 볼 수 있듯이 자바의 컬렉션 프레임워크는 크게 두가지로 구분된다. 바로 Collection 과 Map 이다. Collection 은 다시 Set 과 List 로 구분된다.

또한 아래 Class Diagram 에서 볼 수 있듯이 Collection 인터페이스를 구현한 클래스들과 연계해서 편리하게 저장된 요소(Element)들을 다룰 수 있는 2가지 인터페이스가 있다.

  • Note :: 요소(Element) 는 객체로 생각해도 무방하다. 그 이유는 자바의 컬렉션 프레임워크의 구성 클래스들에 저장하거나 꺼내오는 요소의 타입이 객체의 최상위 타입인 Object 이기 때문이다. 따라서 int 등의 primary type 데이터는 랩퍼(Wrapper) 클래스로 감싸서 넣어야 한다. 예를 들어, int 의 경우엔 Integer 로 감싸서 클래스로 만들어 넣는 것이다. 아래 컬렉션 계열 인터페이스들을 보면 추가-삭제 메소드들의 파라미터와 리턴값이 모두 Object 인 것을 확인할 수 있을 것이다.

  • Issue :: J2SDK1.5 에선 primary type 도 랩퍼 클래스로 감싸지 않고 자동으로 컬렉션 클래스들에 넣어도 되는 auto-boxing 기능을 제공해 줄 예정이다.(내부에서 자동으로 적절한 타입으로 변환시켜 주게 됨) 참고로 C# 에선 이미 auto-boxing 기능이 제공되고 있다. auto-boxing 에 대한 자세한 내용은 이 글의 후반부에 다루도록 하겠다.

항상 숲과 나무를 같이 볼 수 있는 시야를 갖추어야 무엇이든 제대로, 깊이 있게 이해할 수 있다. 따라서 다시 한번 위에 있는 자바의 컬렉션 프레임워크의 전체 구조를 표현한 Class Diagram 을 살펴보도록 하자. 각 인터페이스들에 대한 자세한 설명은 아래 부분에서 하도록 하겠다.

Top

2. Collection Framework 인터페이스들과 클래스들

그럼 이제 자바의 자료구조인 Collection, Set, List, Map 등은 어떤 특징에 따라 구분한 것인지를 알아보겠다. 아래의 표에 각각의 특성을 정리해놨다. 아래 표를 주의 깊게 보도록 하자.

Package

Definition

 java.util.Collection

 순서 없는 단순한 요소들의 집합

 java.util.Set

 중복을 허용하지 않는 요소들의 집합

 java.util.List

 순차적 나열, 순서지정 가능한 요소들의 집합

 java.util.Map

 Key와 Key에 대응하는 값으로 이루어진 구조

 java.util.SortedSet

 값들이 정렬된 Set

 java.util.SortedMap

 key 가 정렬된 Map

그럼 먼저 Collection 인터페이스를 구현한 어떤 클래스들이 존재하는지 Set, List 로 나눠서 살펴보자. 아래의 표는 Collection 의 구성을 표현한 표다. 표에서 인터페이스와 해당 인터페이스를 구현한 실제 클래스들를 보여주고 있다.

Interface Implementation

Collection 

 Set HashSet LinkedHashSet TreeSet
 List ArrayList LinkedList Vector Stack

아래의 표는 Map 인터페이스를 구현한 클래스들이다. 역시 인터페이스와 그 구현 클래스들을 보여주고 있다.

Interface

Implementation

Map HashMap LinkedHashMap IdentityHashMap WeakHashMap Hashtable TreeMap

이제 해당 인터페이스와 그 구현 클래스들의 특징이 어떤 것이고 어떻게 사용되는를 자세히 살펴보도록 하겠다.

Top

2-1. Collection 인터페이스

우선 먼저 Collection 인터페이스를 살펴보도록 하자.

    package java.util;

    public interface Collection {
        // Query Operations
        int size();
        boolean isEmpty();
        boolean contains(Object o);
        Iterator iterator();
        Object[] toArray();
        Object[] toArray(Object a[]);

        // Modification Operations
        boolean add(Object o);
        boolean remove(Object o);

        // Bulk Operations
        boolean containsAll(Collection c);
        boolean addAll(Collection c);
        boolean removeAll(Collection c);
        boolean retainAll(Collection c);
        void clear();

        // Comparison and hashing
        boolean equals(Object o);
        int hashCode();
    }
 

위 코드에서 주석으로 설명된 부분에서 알 수 있듯이 기능에 따라 4가지 분류의 메소드들로 나눌 수 있다. 그럼 각 기능에 따른 메소드들을 자주 사용되는 것들 위주로 간단히 살펴보도록 하겠다.

첫번째로 쿼리(Query) 오퍼레이션들을 살펴보자. 쿼리 오퍼레이션은 컬렉션 안에 저장된 요소의 개수(size() 메소드)나 저장된 요소가 있는지(isEmpty() 메소드), 컬렉션 안에 해당 메소드 안에 파라미터로 전달한 Object 요소가 들어있는지(contains(Object o) 메소드) 등의 여부를 질의하는 메소드들의 분류다. 나중에 예제 소스에서도 살펴보겠지만 iterator() 메소드는 컬렉션 안에 저장된 요소들을 Iterator 에 순차적으로 저장한 후 그 Iterator 객체를 리턴해준다.

두번째는 변경(Modification) 오퍼레이션들이다. 메소드 이름만으로도 쉽게 알 수 있듯이 하나의 요소를 컬렉션에 추가(add(Object o)), 삭제(remove(Object o)) 하는 메소드들이다.

세번째는 대량으로 요소의 변경을 가하는 오퍼레이션들이다. 여기서 상당히 간편하게 사용될 수 있는 addAll(Collection c) 메소드가 있는데 이것은 파라미터로 들어온 컬렉션 객체가 갖고 있는 요소들 모두를 저장하는 메소드이고 removeAll(Collection c) 메소드는 반대로 파라미터로 들어온 컬렉션 객체가 갖고 있는 요소들 모두를 제거한다. 이외에 clear() 메소드는 해당 컬렉션 객체의 모든 요소를 전부 제거한다.

마지막으로 비교(Comparison) 및 해싱(Hashing)을 위한 오퍼레이션들을 정의하는 메소드다. 자주 쓰이지 않으므로 별도의 언급은 하지 않겠다.

Top

2-2. Set 인터페이스와 구현 클래스들

Set 인터페이스를 살펴보자. Collection 인터페이스를 상속하므로 큰 차이점은 없고 단지 "Set" 은 중복을 허용하지 않는 자료구조였다는 것을 다시 한번 기억하도록 하자.

    package java.util;

    public interface Set extends Collection {
        // Query Operations
        int size();
        boolean isEmpty();
        boolean contains(Object o);
        Iterator iterator();
        Object[] toArray();
        Object[] toArray(Object a[]);

        // Modification Operations
        boolean add(Object o);
        boolean remove(Object o);

        // Bulk Modification Operations
        boolean containsAll(Collection c);
        boolean addAll(Collection c);
        boolean removeAll(Collection c);
        boolean retainAll(Collection c);
        void clear();

        // Comparison and hashing
        boolean equals(Object o);
        int hashCode();
    }
 

위 코드를 보면 Collection 과 동일한 메소드만을 제공해주는 것을 볼 수 있다. 단지 구현 클래스 내부에 equals(Object o) 메소드를 이용해서 중복을 허용하지 않도록 체크하는 기능이 더해져 있다.

그럼 이제부터 Set 을 구현한 클래스들을 살펴보도록 하겠다.

2-2-1. HashSet

 

2-2-2. LinkedHashSet(1.4에서 추가)

 

2-2-3. TreeSet

 

Top

2-3. List 인터페이스와 구현 클래스들

List 인터페이스는 순서 붙일 수 있는 컬렉션이다. 이 인터페이스의 사용자는 List 내의 어디에 각 요소가 삽입될까를 정밀하게 제어 할 수 있다. 사용자는 정수값의 인덱스(List 내의 위치)에 의해 요소에 액세스(access) 하거나 List 내의 요소를 검색할 수가 있다. Set 과는 다르게, 보통 일반적으로 List는 중복하는 요소를 허가한다.

    package java.util;

    public interface List extends Collection {
        // Query Operations
        int size();
        boolean isEmpty();
        boolean contains(Object o);
        Iterator iterator();
        Object[] toArray();
        Object[] toArray(Object a[]);

        // Modification Operations
        boolean add(Object o);
        boolean remove(Object o);

        // Bulk Modification Operations
        boolean containsAll(Collection c);
        boolean addAll(Collection c);
        boolean addAll(int index, Collection c);
        boolean removeAll(Collection c);
        boolean retainAll(Collection c);
        void clear();

        // Comparison and hashing
        boolean equals(Object o);
        int hashCode();

        // Positional Access Operations
        Object get(int index);
       Object set(int index, Object element);
       void add(int index, Object element);
       Object remove(int index);
        // Search Operations
        int indexOf(Object o);
       int lastIndexOf(Object o);


        // List Iterators
        ListIterator listIterator();
       ListIterator listIterator(int index);


        // View
        List subList(int fromIndex, int toIndex);
    }
 

Collection 인터페이스에서 제공해주던 메소드들에 List 인터페이스의 특징인 특정 위치의 요소를 찾거나 특정 위치에 요소를 추가하는 등의 메소드들이 추가되었다. 메소드 이름이 워낙 일관되고 명확하게 잘 지어져 있기 때문에(필자가 자바를 좋아하는 이유 중 하나) 메소드 이름만으로도 대강 어떤 역할을 하는지 짐작할 수 있을 것이다. List 인터페이스에서 추가된 메소드들은 Bold 를 주어 표현해놨다.

2-3-1. ArrayList

 

2-3-2. LinkedList

 

2-3-3. Vector

 

2-3-4. Stack

 

Top

2-4. Map 인터페이스와 구현 클래스들

Map 인터페이스는 키(key)를 값(value)에 매핑(mapping) 한다. 또한 Map은 동일한 키를 복수 등록할 수 없고 각 키는 1 개의 값밖에 매핑 할 수 없다. 즉, 하나의 키 값에 대응하는 하나의 값을 갖는 자료구조다.

    package java.util;

    public interface Map {
         // Query Operations
        int size();
        boolean isEmpty();
         boolean containsKey(Object key);
        boolean containsValue(Object value);
        Object get(Object key);

         // Modification Operations
        Object put(Object key, Object value);
        Object remove(Object key);

        // Bulk Modification Operations
        void putAll(Map t);
         void clear();

         // Views
        Set keySet();
        Collection values();
        Set entrySet();
        interface Entry {
            Object getKey();
            Object getValue();
            Object setValue(Object value);
            boolean equals(Object o);
             int hashCode();
        }

         // Comparison and hashing
        boolean equals(Object o);
        int hashCode();
    }
 

// 설명

2-4-1. HashMap

 

2-4-2. LinkedHashMap(1.4에서 추가)

 

2-4-3. IdentityHashMap(1.4에서 추가)

 

2-4-4. WeakHashMap

 

2-4-5. Hashtable

 

2-4-6. TreeMap

 

Top

2-5. Enumeration 와 Iterator 인터페이스

Collection Framework 에는 Enumeration 와 Iterator 라는 인터페이스가 있다. 사전적인 의미로는 반복, 순환이라는 뜻을 지니고 있다. 어떤 객체들의 모임이 있을 때(Collection 계열 구현 클래스들, Collection 인터페이스에 iterator() 메소드가 있었음을 기억해라) 이 객체들을 어떤 순서에 의해서 하나씩 꺼내 쓰기 위한 인터페이스라고 할 수 있다. 원래 Java 2 이전는 Enumeration 이라는 인터페이스가 많이 사용되었지만 최근에는 Iterator 인터페이스가 더 많이 사용된다. 그 이유는 각 인터페이스를 살펴보며 알아보기로 하겠다.

2-5-1. Enumeration

아래의 Enumeration 인터페이스의 코드를 보자.

    package java.util;

    public interface Enumeration {
         boolean hasMoreElements();
        Object nextElement();
    }
 

이 인터페이스는 단지 두개의 메소드만을 제공한다. 이 인터페이스의 사용은 상당히 간단하다. hasMoreElements() 메소드로 인터페이스 안에 다음 요소가 있는지를 질의한다. 만약 true 가 리턴되었다면(다음 인덱스에 요소가 있다는 의미) nextElement() 메소드로 다음 요소를 꺼내서 사용하면 되는 것이다.

java.util.StringTokenizer 클래스가 Enumeration 인터페이스를 구현하고 있다. 따라서 StringTokenizer 클래스가 제공하는 메소드들 중에서 Enumeration 에서 정의한 2개의 메소드가 제공되는 것을 볼 수 있을 것이다.

2-5-2. Iterator

아래의 코드는 Iterator 인터페이스다.

    package java.util;

    public interface Iterator {
         boolean hasNext();
        Object next();
        void remove();
    }
 

Enumeration 과의 차이점은 단지 remove() 메소드가 추가된 것 뿐이다. hasNext() 와 next() 메소드는 이름만 약간 다를 뿐 Enumeration 인터페이스의 hasMoreElements() 와 nextElement() 와 정확히 일치하는 기능을 한다.

그럼 왜 Enumeration 대신 Iterator 를 Java 2에서 추가해서 사용할까? 그것은 Enumeration 인터페이스는 집합 내에서 요소를 제거할 방법이 없기 때문이다. 그것을 보완하기 위해 나온 것이 Iterator 인터페이스다.

Top

3. J2SDK1.5 에서 추가될 auto-boxing 과 generic

 

Top

4. 자주 사용되는 컬렉션 객체들의 퍼포먼스 표

4-1. Set 객체

  동기화 설명
HashSet no 가장 빠른 집합. HashMap 보다 느리지만 Set 인터페이스를 구현하고 있다. HashMap 은 Set 이 아니라 Map 임.
TreeSet no HashSet보다 느리다. 차례대로 키를 사용할 수 있다. (키가 정렬됨)

4-2. Map 객체

  동기화 설명
HashMap no 가장 빠른 매핑.
Hashtable yes HashMap 보다 느리지만 동기화한 HashMap 보다 빠르다.
TreeMap no Hashtable 과 HashMap 보다 느리다. 차례대로 키를 사용할 수 있다. (키가 정렬됨)

4-3. List 객체

  동기화 설명
ArrayList no 가장 빠른 리스트.
LinkedList no 다른 리스트보다 느리지만 큐로 이용했을 경우 더 빠를 수도 있다. 느린 이유는 ArrayList 나 Vector, Stack 과 달리 array 계열이 아니기 때문.
Vector yes ArrayList 보다 느리지만 동기화한 ArrayList 보다 빠르다.
Stack yes Vector 와 동일한 속도. LIFO 큐 기능을 제공한다.

필자생각 :: HashMap, ArrayList 에 동기화를 걸어 사용하는것 보다 동기화된 Hashtable, Vector 를 사용하는 것이 더 빠른것으로 미루어 짐작컨데 Hashtable, Vector 경우에는 동기화가 되어 있는 내부 메소드들이 JIT 컴파일러에 의해 최적화 되는음.

Top

5. 효율적인 컬렉션 객체들의 사용

*** Vector-Hashtable vs ArrayList-HashMap ***
보통 일반적으로 Vector 와 Hashtable 을 주로 사용하고 있을 것이다.
컬렉션 객체들은 모든 메소드가 synchronized 되어 있기 때문에 동시에 여러 스레드가 접근 할 수 없.

반명 동일한 기능을 하는데도 불구하고 ArrayList 와 HashMap 은 메소드가 synchronized 로 되어있지 않아서 스레드들이
해당 객체에 동시접근이 가능다.

은행에서 현금 입출금에 관련된것처럼 반드시 미션크리티컬한 로직이 필요한 곳에선
VectorHashtable을 사용하는게 바람직하고 당연하지만 필자는 초보 분들이 프로그래밍한 코드에서 멀티스레드 접근을 해도 무방한데도 불구하고 모두 Vector니면 Hashtable을 사용하는 것을 많이 봐왔다. 이건 특히나 jsp 처럼 시간을 다투는 프로그램에선 치명타. 동기화가 필요한지 아닌지를 잘 판단해서 정확히 필요한 곳에만 Vector나 Hashtable을 사용하고 그 이외의 부분에선 ArrayList HashMap 을 사용해야 할 것이다.

이미 다 아는 얘기라고 하실지도 모르겠지만 모르시는 분들이 너무 많아서 다시 한번 언급해봤다.

필자의 경우에는 효율을 좀 더 높이기 위해 동기화가 필요한 부분도 ArrayList 나 HashMap 에다가 락을 걸어서 멀티스레드의 폐해를 피해가는 방식을 사용하고 있다. 모든 경우에 이렇게 한다는 것은 아니고 예를 들어 데이터를 넣는 부분은 멀티스레드 접근이 허용되지만 데이터를 꺼낸 후 삭제해야 하는 부분은 동기화가 필요하다고 가정했을 때 동기화가 필요한 "데이터를 꺼낸 후 삭제" 하는부분에만 락을 걸어서 동기화 블럭을 최소화시켜서 좀 더 효율을 가져간다는 것이다.

데이터를
컬렉션 객체에 넣(put) 가져오고(get) 삭제하는(remove) 등의 모든 부분에 동기화가 필요하다면 당연히 그냥 이미 그런 용도로 만들어진 VectorHashtable 을 사용하는 것이 편하 또 이렇게 사용 하는 것이 ArrayList HashMap 의 모든 메소드에 락을 걸어 사용하는 것보다 더 빠르다.

Top

6. 아쉬움을 남긴채 강좌를 마무리하며

자바의 컬렉션 프레임워크는 매년 자바의 가장 훌륭한 라이브러리로 선정되는 파트다. 그만큼 설계적인 측면에서나 구현적인 측면에서 배울 것이 많은 부분이다. 필자는 지금까지 바로 이 자바의 컬렉션 프레임워크에 대해 설명을 했다. 하지만 아쉬움이 남는다. 그 이유는 필자가 "물고기 잡는 법"을 가르쳐 준 것이 아니라 물고기를 잡아서 준 것이기 때문이다. 즉, 어떻게 이런 자료구조(구현 클래스들)를 만들지에 대한 강좌가 아니라 단순히 만들어진 자료구조를 어떻게 이용하는지에 초점을 맞춰서 설명했다는 것이다.

필자가 독자분들에게 한가지 당부를 한다면 이미 만들어져 있는 api 를 단순히 이용하기 보다는 직접 만들어서 사용할 수 있는 능력을 키우라는 것이다. 그게 진정 창조적인 그리고 프로페셔널한 개발자가 되기 위한 길이라고 필자는 생각한다. 따라서 우선 독자분들은 컬렉션 프레임워크의 구현 클래스들이 어떻게 만들어졌는지를 직접 J2SDK 폴더 안의 src.zip 파일의 압축을 풀어서 분석해봤으면 한다. 그리고 나름대로 직접 그런 자료구조를 구현하기 위한 방법들도 생각해보고 가능하다면 직접 구현해보았으면 한다. 그럼 이제 결코 짧지 않았던 컬렉션 프레임워크 강좌를 마무리하겠다.

Top

 

 

:
Posted by 뽀기
2007. 4. 23. 10:43

try catch finally 사용시 주의해야 할 점! 그거/Java2007. 4. 23. 10:43

public class ThrowsTest {
 public static void main(String[] args){
  ThrowsTest tt = new ThrowsTest();
  tt.check();
  System.out.println("11");
 }

 String check() {
  try {
   Integer.parseInt("abc");
  } catch(Exception e) {
   throw e;
  } finally {
   return "";
  }
 }
}


위 소스를 보고 이상한점이 없는가?

check() 라는 method를 보면 catch block에서 exception을 throw 하는데

check method 선언부에서는 exception을 throws 하지 않고 있다.

즉, 컴파일 error가 발생해야 한다.

하지만!!!!!!

위 소스를 컴파일해보라... 놀랄것이다.

에러가 나지 않는다. 허허.. 이런 -_-;

check method를 잘 보면,

finally block에 return 문이 있다.

바로 이 finally block의 return 문이 catch block의 throw를 먹어버린것이다!

finally block의 return 문을 finally 밖으로 빼고 다시 컴파일 해보라..

컴파일 에러가 날것이다..

흔히들 try catch finally block을 함께 사용한다.

그리고, finally block은 무조건 실행되기 때문에 finally block에 return 문을 위치하는 경우가 종종있다.

이는 위험 천만한 발상인 것이다!

위 예에서처럼 try block에서 exception이 발생해서 catch block이 실행되어야 하는데

finally block에 return 문이 있으면 catch block의 exception을 먹어버려서

exception 발생을 감지하지 못하게 되는 것이다!!

무슨 일이 있어도..

finally block 안에 return 문은 넣지 말지어다!!!!!!!!

:
Posted by 뽀기
2007. 3. 14. 10:29

Recommended Documents for Beginners 그거/Java2007. 3. 14. 10:29


    서블렛 + JDBC 연동시 코딩 고려사항 -제1탄-
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=968185187

    서블렛 + JDBC 연동시 코딩 고려사항 -제2탄-
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=968522077

    Re: DB Connection Pool: Orphan and Idle Timeout
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=1005294960

    Connection/Statement 최대 동시 Open 수
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=jdbc&c=r_p&n=972287002
    '최대열기 커서수를 초과했습니다' 오류 튜닝법
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=976511779

    Connection Pool & PreparedStatement Cache Size
    http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=was&c=r_p&n=995572195

    Understanding JavaServer Pages Model 2 architecture
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=944700949

    Re: 서블릿에서의 Global 변수
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=959175733

    Re: [팁] EUC-KR, KSC5601, euc-kr , etc.. 20010321  손님(guest)
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=985157298

    "/servlet/test?name=이원영&address=서울 도곡동"
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=960356845

    JSP에서 DB연결의 문제
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=954339077

    Re: DB Connection Pool & 그 외의 것들
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=960704622

    Oracle OSDK DB Connection Pool 사용 방법
    http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=oas&c=r_p&n=965358223

    소스 Review 2 - ConnectionPool Q&A -
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=971498129

    73   Hans Bergsten Connectionpool 을 Thread간통신에 붙여보았습니다
    http://www.javaservice.net/~java/bbs/read.cgi?m=dbms&b=jdbc&c=r_p&n=1011919113

    AIX Java App. SIGSEGV 진단 방법에 관한 예
    http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=was&c=r_p&n=1000371692

    (웹로직)엔티티 빈 자동 생성 프로그램입니다.
http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=weblogic&c=r_p&n=1009036410

    Re: WebLogic JSP와 Servlet에서 한글 깨지는 문제
http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=weblogic&c=r_p&n=957179071

    jspSmartUpload 패키지 사용을 금합니다
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=995622233

    파일 Download에 관한 참고
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=992449785

    JDBC 코딩시 Tips - 대량데이타 Select -
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=982581488

    소스 Review 7 - Servlet/JDBC 클래스 디자인
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=983500745

    Re: 특수한 기호를 HTML TAG로 변환하는 방법
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=www&c=r_p&n=934284565

    [자바의 숫자표현] 어디까지 가능할까 !!!
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=920434490

    Object Pool Contorl 기법
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=974750350

    PPC(Peek Point Control) 기법
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=996642473

    Re: 각 환경별 Servlet & JNI 정리
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=956659322
    JNI - Native Method Pooling 기법
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=975913198
    41 JNI 2탄입니다. 
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=963972664
    Java에서 signal 처리 - JNI -
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=978373413

    26   JavaStackTrace(javacore/ThreadDump) 분석방법
    http://www.javaservice.net/~java/bbs/read.cgi?m=etc&b=jdk&c=r_p&n=1039372188

    TCP/IP Socket 프로그램 구현시 고려사항
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=1009171849
    Java Socket Utilities API : SocketUtil.java
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=976117970
    18   parse int and long type from byte[]
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=953537896
    99     Re: C <--> Java 임의의 데이타 타입 주고 받기
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=discussion&c=r_p&n=1029190704
    Re: [답변]자바와 C 간의 소켓 통신상 한글 문제
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=956208406
    109     Re: TCP Status & JNI & Socket Communication
    http://www.javaservice.net/~java/bbs/read.cgi?m=unix&b=unix&c=r_p&n=1022636033
    92   Re: TCP/IP ESTABLISHED & Connection Timeout
     http://www.javaservice.net/~java/bbs/read.cgi?m=unix&b=unix&c=r_p&n=1005195947
    Re: DB Connection Pool: Orphan and Idle Timeout
     http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=servlet&c=r_p&n=1005294960

    89 AIX 4.3.3 FIN_WAIT_1, FIN_WAIT_2, TIME_WAIT
     http://www.javaservice.net/~java/bbs/read.cgi?m=unix&b=unix&c=r_p&n=1002732057
    88 AIX4.3 "netstat -n" shows too many FIN_WAIT_2
     http://www.javaservice.net/~java/bbs/read.cgi?m=unix&b=unix&c=r_p&n=1002693727
    87 AIX 4.3 tcp keepalives & tcp_
     http://www.javaservice.net/~java/bbs/read.cgi?m=unix&b=unix&c=r_p&n=1002689463
    85 Solaris 2.x - Tuning Your TCP/IP Stack and 
     http://www.javaservice.net/~java/bbs/read.cgi?m=unix&b=unix&c=r_p&n=1002605172
    82 TCP/IP Normal Close Sequence
     http://www.javaservice.net/~java/bbs/read.cgi?m=unix&b=unix&c=r_p&n=994674560

    30   RMI 및 Object Serialization 세미나 자료 20011203  絶對高手(juldaegosu)
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=rmi&c=r_p&n=1007358212

    Orion Sever 에서 Apache SOAP 사용하기
    http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=orion&c=r_p&n=985270192
    Orion Server에서 Apache SOAP Server와 Visual Basic SOAP Client 연동하기
    http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=orion&c=r_p&n=985572196

    [세미나자료] 김덕태님의 "XML과 자바"
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=websvcs&c=r_p&n=955159322

    JSP에서 동적으로 생성된 xml보여주기
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=websvcs&c=r_p&n=985450162

    [필수]객체지향 개발자가 꼭 읽어야 할 책 4권 
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=988257203

    173   자바 프로그래밍 언어 정복(Garbage Collection 등 추가)  -출판초고,이덕하-
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=1012451983

    [필수]Design Pattern - javacompanion
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=977108336

    UML modeling 도구입니다.
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=977368627

    UML 개론 프리젠테이션 자료
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=979041227

    139   BROS - JavaBeans 의 변형입니다.
    http://www.javaservice.com/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=999099977

    [온라인책] "Mastering EJB"- Ed Roman -
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=ejb&c=r_p&n=960552403
    41 EJB책한권 올립니다. (Mastering EJB)
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=ejb&c=r_p&n=1010784800

    EJB 사용을 위한 결정 요소
    http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=ejb&c=r_p&n=973909127

    352  To EJB, or not to EJB
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=1010454437

    [TIP]EJB 1.1 트랜잭션 속성에 관한 강좌
http://www.javaservice.com/~java/bbs/read.cgi?m=appserver&b=powertier&c=r_p&n=1002186458

    Usage & Limitations of Txn Control in WebLogic V5.1
http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=weblogic&c=r_p&n=1000771631

    15 응용 S/W 아키텍춰 및 모델링
    http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=was&c=r_p&n=930570297

    WEB개발 및 Application Server동향
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=936757537

    Re: [답변] 자바 프로젝트 개발 방향
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=955519088

    Re: 자바 프로젝트 개발 방향 - 답변2
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=956198284

    Re: 자바 기술 Set 구성과 관련된 질문
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=956676463

    RE: Applet에서 WAS의 Connection Pool 이용 ?
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=1007007020

    대표적인 S/W 아키텍쳐 및 기술셋 예제
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=957877865

    웹서비스(WebService) 관련 기술정보/예제 및 기사
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=999508397

    Re: 세션클러스트링
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=959681361

    어플리케이션서버와 데이타서버 분리의 장단점
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=962379253

    Re: 어플리케이션서버와 데이타서버 분리의 장단점
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=962585278

    Re: ◈ 대량의 동시사용자를 위한 조언
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=974615872
   
    81   Re: [쇼핑몰] 대량의 고객정보 관리
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=966762191

    [답변]쇼핑몰 퍼포먼스 관련
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=980794006

    Oracle 8i Performance & Tuning
    http://www.javaservice.com/~java/bbs/read.cgi?m=dbms&b=dbms&c=r_p&n=1002277016

    응답시간 느린 HTTP 요청 두번이상 자동호출될 가능성
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=982251775

    장애진단(Problem Determination) 절차 
    http://www.javaservice.net/~java/bbs/read.cgi?m=resource&b=consult&c=r_p&n=1023729776

    255   성능저하시, 모니터링 방법
    http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=weblogic&c=r_p&n=1012075146

    106   RE: netstat & lsof
    http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=engine&c=r_p&n=1007110331

    대량데이타 조회시 고려사항
    http://www.javaservice.net/~java/bbs/read.cgi?m=qna&b=consult&c=r_p&n=1000186035


◆  Web Application Servers

    어플리케이션서버
    http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=appserver

    ** 이하 ABC 순입니다.

    ATG(Art Technology Group) Dynamo
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=atg
    BEA/WebLogic Application Server
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=weblogic
    Borland AppServer(Inprise Application Server)
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=inprise
    Evermind Orion Server
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=orion
    GemStone's GemStone/J
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=gemstone
    HP Netaction Application Server
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=eserver
    IBM WebSphere Application Server
     http://www.javaservice.net/~java/bbs/read.cgi?m=appserver&b=was&c=r_p&n=1010952419
    IONA iPortal Application Server (iPAS)
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=ipas
    Marc Fleury's JBoss Application Server
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=jboss
    Oracle 9iAS/OAS/OSDK
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=oas
    Persistence PowerTier
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=powertier
    Sun/Netscape iPlanet
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=iplanet
    Sybase EAServer
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=sybase
    TmaxSoft JEUS
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=jeus
    서블렛엔진(JServ,Tomcat,JRun,..)
     http://www.javaservice.net/~java/bbs/index.cgi?m=appserver&b=engine


◆  Java Development Kit
    http://www.javaservice.net/~java/docs/jdk1.1.8/
    http://www.javaservice.net/~java/docs/jdk1.2.2/

    http://www.javaservice.net/~java/docs/j2sdk_1.3.1/
    http://www.javaservice.net/~java/docs/j2sdk_1.3.1/src/

◆  JAVA Specifications

   [Java 관련 Specifications]

   Java Language Specification -Second Edition-
      http://java.sun.com/docs/books/jls/second_edition/html/jTOC.doc.html

   Java Virtual Machine Specification
      http://www.java.sun.com/docs/books/vmspec/
      http://www.java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html
      http://www.java.sun.com/docs/books/vmspec/html/VMSpecTOC.doc.html

   Servlet Specification 2.3 ( Public Draft )
      ftp://ftp.javaservice.net/pub/java/spec/servlet23_PublicDraft1.pdf
      (1,475,499 bytes)

   Servlet Specification 2.2
      ftp://ftp.javaservice.net/pub/java/spec/servlet2_2-spec.pdf

   Servlet Specification 2.1
      ftp://ftp.javaservice.net/pub/java/spec/servlet-2.1.pdf

   JSP (Java Server Page) Specification 1.2 ( Public Draft )
      ftp://ftp.javaservice.net/pub/java/spec/jsp12_PublicDraft1.pdf

   JSP (Java Server Page) Specification 1.1
      ftp://ftp.javaservice.net/pub/java/spec/jsp1_1-spec.pdf

   JSP (Java Server Page) Specification 1.0
      ftp://ftp.javaservice.net/pub/java/spec/jsp1_0-spec.pdf
  
   JSP 0.91 programming reference : The IBM implementation version
http://www-4.ibm.com/software/webservers/appserv/doc/v30/ae/web/doc/whatis/icjspref.html

   OMG CORBA/IIOP 2.2 Specification : The Complete formal/98-07-01
      ftp://ftp.javaservice.net/pub/corba/spec/98-07-01.pdf (5,968,346 bytes)

   EJB(Enterprise JavaBeans) Specification Version 1.0
      ftp://ftp.javaservice.net/pub/java/spec/ejb_10.pdf  (330,603 bytes)

   Enterprise JavaBeans 1.0 CORBA Mapping
      ftp://ftp.javaservice.net/pub/java/spec/ejb-corba_10.pdf (35,377 bytes)

   JavaBeans Specification 1.01
      ftp://ftp.javaservice.net/pub/java/spec/beans_101.pdf (257,046 bytes)

   JDBC 2.0 Core API Specification
      ftp://ftp.javaservice.net/pub/java/spec/jdbc20.pdf (141,137 bytes)

   JDBC 2.0 Standard Extension API Specification (old)
      ftp://ftp.javaservice.net/pub/java/spec/jdbc20_stdext.pdf (154,667 bytes)

   JDBC 2.1 Core API Specification
      ftp://ftp.javaservice.net/pub/java/spec/jdbc2_1-spec.pdf (269,312 bytes)

   JDBC 2.0 Optional Package API specification: (new)
       NOTE: The javax.sql package is also called the JDBC 2.0 Optional Package API
             (formerly known as the JDBC 2.0 Standard Extension API).
      ftp://ftp.javaservice.net/pub/java/spec/jdbc20.stdext.pdf (327,005 bytes)


   Java Remote Method Invocation Specification (JDK 1.1, JDK 1.2)
     http://www.java.sun.com:80/products/jdk/1.1/docs/guide/rmi/spec/rmiTOC.doc.html
     http://www.java.sun.com:80/products/jdk/1.2/docs/guide/rmi/spec/rmiTOC.doc.html

   RMI Architecture and Functional Specification (JDK 1.2)
      ftp://ftp.javaservice.net/pub/java/spec/rmi-spec-JDK1_2.pdf
                                                    (495,423 bytes)

   Java Transaction API specification
      ftp://ftp.javaservice.net/pub/java/spec/jta-090.pdf

   Java IDL Documentation
      http://www.java.sun.com:80/products/jdk/1.2/docs/guide/idl/index.html

출처 : JavaServiceNet

:
Posted by 뽀기
2007. 3. 13. 15:04

객체 직렬화(Object Serialization) 그거/Java2007. 3. 13. 15:04

객체 직렬화(Object Serialization):

송 재승(angel1011@hanmail.net)
 
자바에서는 자바에서 제공하는 기본적인 데이터 유형 이외에도 여러 객체들을 스트림에 쓰고, 읽을 수 있는 기능을 제공하는데 이것이 바로 객체 직렬화를 통해서 가능하다.
이러한 객체 직렬화 기법은 원격 메소드 호출, 보안 및 암호화 등 실제 프로그래밍 시에 유용하게 사용되어 질 수 있다.

 객체 직렬화의 비밀

그러면 먼저 객체 직렬화가 어떠한 과정을 거쳐서 이루어 지는지 비밀을 벗겨보도록 하자.
먼저 객체들은 ObjectOutputStream에 의해서 직렬화가 되며, ObjectInputStream에 의해서 직렬화가 해제된다는 사실을 기억하도록 하자. 이러한 일련의 과정을 그림으로 나타내면 다음과 같다.
[그림 객체 직렬화]

사용자 삽입 이미지
여기에 사용되는 ObjectOutputStream과 ObjectInputStream은 모두 java.io 패키지의 일부분이고, 각각 DataOutput과 DataInput클래스를 implement한 ObjectOutput과 ObjectInput을 extends 해서 만들어 진것이다. 그리고 이 두 클래스에는 비원시 객체와 배열등을 스트림에 읽고 쓰기 위한 메쏘드들이 추가되어져 있다.
[그림 클래스 구조도]

객체 직렬화의 과정

지금부터는 보다 자세히 객체의 직렬화 과정에 대해 알아보도록 하자.
객체는 ObjectOutputStream의 writeObject() 메쏘드에 자신을 넘김으로써 직렬화 된다.
WirteObject()메쏘드는 private 필드와 super 클래스로부터 상속받은 필드를 포함, 객체의 모든 것을 기록하게된다.
직렬화 해제는 직렬화와 반대의 과정을 거치게 되는데 ObjectInputStream의 readObject()메쏘드를 호출함으로써 스트림으로부터 읽어 들이고 이를 직렬화가 되기전의 객체로 다시 만들게 된다.
다음은 객체가 직렬화 되고 해제되어 원래의 객체로 복원되는 과정에 대한 간단한 예제이다.
눈여겨 볼 부분은 ObjectInputStream을 생성해서 writeObject()를 사용해서 객체를 직렬화 하는것과 ObjectInputStream을 생성해서 readObject()를 통해서 객체를 복원하는 부분이다. 또한 SerializableClass가 Serializable을 implements한 것을 주의해서 보길 바란다.
 
import java.io.*;
public class ObjectSerializeTest {
    public static void main(String[] args) throws Exception {
/* 파일을 열어서 그곳에 객체를 직렬화 시켜서 저장한다. */
// 파일 출력 스트림을 연다.

    FileOutputStream fos = new FileOutputStream("_imsi.txt");
// 객체 스트림을 열고, 객체스트림을 통해 객체를 파일에 저장
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(new SerializableClass("Serialize Test Program", 1004));
// 스트림을 닫는다.
    oos.close();
/* 직렬화 된 객체가 저장된 파일로 부터 객체를 해제시켜 원래의 객체를 복원*/
// 파일 입력 스트림으로부터 객체 입력 스트림을 연다.

    FileInputStream fis = new FileInputStream("_imsi.txt");
    ObjectInputStream ois = new ObjectInputStream(fis);
// 객체 입력 스트림으로부터 객체를 읽어온다.
    SerializableClass sc = (SerializableClass)ois.readObject();
// 스트림을 닫는다.
    ois.close();
/* 스트림으로부터 읽어들인 객체의 내용을 출력 원래 생성되었던 객체와 같은 값을 갖는다는 것을 알수가 있다. */
    System.out.println("String : " + sc.mString);
    System.out.println("Integer : " + sc.mInt);
   }
}
/* 하나의 문자열과 정수를 저장하고있는 클래스
Serializable을 implements 함으로써 스트림을 통해 직렬화되고 해제되어질수 있다. */
class SerializableClass implements Serializable {
    public String mString;
    public int mInt;
// 생성자
    public SerializableClass(String s, int i) {
        this.mString = s;
        this.mInt = I;
    }
}
  • 실횅방법
    javac.exe ObjectSerializeTest.java
    java.exe ObjectSerializeTest
  • 실행결과
String : Serialize Test Program
Integer : 1004
 
 

커스텀 직렬화

  • Serializable
스트림을 통해서 직렬화 또는 해제 될수 있는 객체는 Serialiazable을 implements한 객체만이 가능하다.자바 2에서는 java.awt, javax.swing, etc등의 클래스들이 Serializable을 implements하고 있으며, 이러한 상위 클래스들이 implements하므로 하위 클래스들 또한 같이 직렬화/해제의 범주에 들 수가 있다.
다음은 Serializable을 implements하고있어서 객체의 직렬화가 가능한 객체들의 리스트이다.
[직렬화 가능 객체 리스트]
 
패키지
Serializable
java.awt
BorderLayout, CardLayout, CheckboxGroup, Color, Component, ComponentOrientation, Cursor, Dimension, Event, FlowLayout, FocusManager, Font, FontMetrics, GraphicsConfigTemplate, GridBagConstraints, GridBagLayout, GridBagLayoutInfo, GridLayout, ImageMediaEntry, Insets, LightweightDispatcher, MediaTracker, MenuComponent, MenuShortcut, Point, Polygon, Rectangle, ScrollPaneAdjustable, SystemColor
java.awt.dnd
DropTarget
java.awt.font
TransformAttribute
java.awt.geom
AffineTransform
java.awt.image.renderable
ParameterBlock
java.beans
PropertyChangesSupport, VetoableChangeSupport, BeanContext, BeanContextChildSupport, BeanContextSupport
java.io
ExternalizableFile, FilePermission, FilePermissionCollection, ObjectStreamClass
java.net
InetAddress, SocketPermission, SocketPermissionCollection, URL
java.rmi
MarshalledObject
java.rmi.activation
ActivationDesc, ActivationGroupDesc, ActivationGroupID, ActivationID
java.rmi.dgc
Lease, VMID
java.rmi.server
ObjID, RemoteObject, UID
java.security
AllPermissionCollection, BasicPermission, BasicPermissionCollection, CodeSource, GuardedObject, Identity, Key, KeyPair, Permission, PermissionCollection, Permissions, PermissionsHash, SecureRandomSpi, SignedObject, UnresolvedPermission, UnresolvedPermissionCollection
java.text
BreakIterator, Collector, DateFormatSymbols, DecimalFormatSymbols, Format, SpecialMapping, TextBoundaryData, UnicodeClassMapping, WordBreakTable
java.util
ArrayList, BitSet, Calendar, Date, EventObject, HashMap, HashSet, Hashtable, LinkedList, Locale, PropertyPermissionCollection, Random, TimeZone, TreeMap, TreeSet, Vector
javas.swing.table
AbstractTableModel, DefaultTableCellRenderer, DefaultTableColumnModel, DefaultTableModel, TableColumn
javax.swing.text
AbstractDocument, EditorKit, GapContext, SimpleAttributeSet, StringContent, StyleContext, TabSet, TabStop
javax.swing.tree
DefaultMutableTreeNode, DefaultTreeModel, DefaultTreeSelectionModel, TreePath
 
Serializable은 Cloneable과 같은 마커 인터페이스로서, 어떠한 메쏘드들을 정의해놓은 것이 아닌, serizlVersionUDI라는 변수 하나만을 가지며, 이 객체가 직렬화가 가능하다는 것을 알려주는 역할만을 하는 인터페이스일 뿐이다.
  • transient
스트림을 이용해서 직렬화 하는데 있어서, 커다란 프로그램 전체가 직렬화된다면, 여러가지 면에서 많은 낭비일 것이다. 예를 들어, 한 객체가 마우스가 눌려진 위치를 알기 위해서 마우스 클릭시에 위치를 저장하는 필드를 가지고 있다고 가정하자, 이 경우 마우스의 위치값은 프로그램이 돌아가는 상태에서 마우스가 눌려졌을 당시에만 유효한 값으로, 객체가 직렬화 되었다가 해제 되었을 경우에는 쓸모없는 값이 되어버린다.
이런 객체 직렬화에 쓸모없는 값들은 transient로 선언해 줌으로써 객체 직렬화에서 제외되어질수 있다.
Private transient int x;
이러한 선언은 플랫폼에 따라 다른 값을 가지는 필드나, 현재의 환경에서만 유효한 필드등을 객체 직렬화에서 제외하는데 유용하게 쓰일 수가 있다.
transient선언이 적용된 예이다.
 
import java.io.*;
public class ObjectSerializeTest1 extends TestClass implements Serializable {
// 변수 선언, x는 객체직렬화에서 제외되도록 transient로 선언
    private int i;
    private transient int x;
// 생성자 Serializable을 implements한 i와 x는 받아온 인자를 그대로 대입하고, TestClass를 extends한 j는
// i값에 5배를 한 값을 대입했다.

    public ObjectSerializeTest1(int i, int x) {
        this.i = i;
        this.x = x;
        j = i*5;
    }
/* 객체직렬화를 하기전의 객체의 값들을 알아본뒤에, 해당 객체를 스트림에 직렬화 시킨다.*/
    private void writeObject(ObjectOutputStream out) throws IOException {
        System.out.println("writeObject");
        System.out.println("write ==> i = "+this.i+", j = "+j+", x = "+this.x);
        out.defaultWriteObject();
// 객체를 직렬화 한다.
        out.writeInt(j);
// 임시파일에 기록
    }
/* 스트림으로 부터 객체를 해제시킨다. */
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        System.out.println("readObject");
        in.defaultReadObject();
// 객체 직렬화 해제 작업
        j=in.readInt();
// 임시파일에서 읽어옴
    }
/* 객체의 값을 알기 위해서 오버라이드 */
    public String toString() {
        return "i = "+i+", j = "+j+", x = "+x;
    }
    public static void main(String[] args) throws Exception {
// 객체직렬화에 사용될 화일을 열고 스트림을 얻는다.
        FileOutputStream fos = new FileOutputStream("temp.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
// 객체를 생성시킨뒤, 스트림에 객체를 쓴다.(10은 transient로 선언된 필드의 값임을 유의)
        oos.writeObject(new ObjectSerializeTest1(1, 10));
        oos.close();
// 정보를 저장한 화일을 열고 스트림을 통해 읽은뒤 객체직렬화를 해제시킨다.
        FileInputStream fis = new FileInputStream("temp.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ObjectSerializeTest1 ser = (ObjectSerializeTest1)ois.readObject();
        ois.close();
// print문 수행시 형 변환이 이루어져서 이미 오버라이드된 toString()메쏘드가 수행된다.
        System.out.println("read ==> "+ser);
    }
}
/* Serialize되지 않은 클래스 객체 직렬화 작업으로부터는 제외, 만약 사용자가 이 클래스를 객체 직렬화에 사용하고 싶다면 Serializable을 implements하면 된다. 만약 그렇게 할수 없을 경우에는 위와 같이 하도록 한다. */
class TestClass {
    int j;
}
  • 실횅방법
    javac.exe ObjectSerializeTest1.java
    java.exe ObjectSerializeTest1
  • 실행결과
writeObject
write ==> i = 1, j = 5, x = 10
readObject
read ==> i = 1, j = 5, x = 0
 
  • externalizable
객체직렬화의 또 다른 방법으로는 Externalizable 인터페이스를 들수 있는데, 이 방법은 Serializable보다 더 강력하게 객체 직렬화를 제어하고 싶을 경우에 사용되어진다. 어떤 클래스가 이 인터페이스를 구현하게 되면 readExternal()과 writeExternal()메쏘드를 사용하여 객체를 스트림으로부터 읽고 쓸수있다. Externalizable객체들은 자신의 상태를 직렬화 및 해제하는 과정에서 강력한 제어를 할수 있으며, Serializable 객체에서의 기본 데이터 포멧을 사용하고싶지 않을때에 사용한다.
다음은 externalizable을 사용한 객체 직렬화의 예이다.
 
import java.io.*;
public class ObjectExternalizeTest
   
 implements Externalizable {
    int i;
    String s;
/* 인자없는 생성자(직렬화된 데이터를 읽어들여서 객체를 만들기 위해 필요) */
    public ObjectExternalizeTest() { }
/* 생성자 */
    public ObjectExternalizeTest(String s, int i) {
        this.s = s;
        this.i = i;
    }
/* readObject()메쏘드로부터 호출이 되며, 직렬화된 객체를 해제시킨다. */
    public void
readExternal(ObjectInput oi) throws IOException, ClassNotFoundException {
        System.out.println("Execute readExternal()");
        s = (String)oi.readObject();
        i = oi.readInt();
    }
/* writeObject()메쏘드로부터 호출이 되며, 변수들을 직렬화한다.*/
    public void
writeExternal(ObjectOutput oo) throws IOException {
        System.out.println("Execute writeExternal()");
        oo.writeObject(s);
        oo.writeInt(i);
    }
/* 변수들의 값을 보여준다.*/
    public String toString() {
        return Integer.toString(i)+", "+s;
    }
    public static void main(String[] args) throws Exception {
        ObjectExternalizeTest oet1 = new ObjectExternalizeTest("Externalizable Test", 1000);

// 객체를 직렬화시켜 저장할 화일로부터 쓰기 스트림을 얻어온다.
        FileOutputStream out = new FileOutputStream("temp.txt");
        ObjectOutputStream oos = new ObjectOutputStream(out);

/* 객체를 직렬화 시킨다. 이경우 writeObject는 oet1의 writeExternal을호출하게되고writeExternal() 메쏘드의 코드들이 확장되어 실행이 된다. */
       oos.writeObject(oet1);
// 객체가 직렬화되어 저장된 파일로부터 스트림을 얻어온다.
        FileInputStream in = new FileInputStream("temp.txt");
        ObjectInputStream ois = new ObjectInputStream(in);
        ObjectExternalizeTest oet2 = (ObjectExternalizeTest)ois.readObject();
// 객체 직렬화 이전의 객체와 직렬화를 거친 뒤 다시 해제된 객체의 값을 출력한다.
        System.out.println(oet1);
        System.out.println(oet2);
// 만약 모든 과정이 제대로 이루어졌다면 oet1과 oet2객체는 같은 값을 가지고있을 것이다.
    }
}
  • 실횅방법
    javac.exe ObjectExternalizeTest.java
    java.exe ObjectExternalizeTest
  • 실행결과
Execute writeExternal()
Execute readExternal()
1000, Externalizable Test
1000, Externalizable Test
 
 

애플릿 직렬화

객체직렬화는 애플릿을 직렬화하는데에도 사용이 되어진다. 이러한 경우 <Applet>태그는 클래스 파일 대신에 직렬화 된 객체 파일을 지정하게 되고, 브라우저는 이러한 태그를 만나면 직렬화된 애플릿을 해제하여 화면에 보여주게 된다. 즉, 그래픽 유저 인터페이스를 생성하기 위한 코드를 전혀 가지고 있지 않으면서도 완벽한 그래픽 유저 인터페이스를 가지게 되는것이다.
일반적으로 직렬화된 애플릿은 *.ser의 확장자를 가진다.
직렬화된 애플릿을 호출하는 html 파일은 다음과 같은 내용을 가진다.
<Applet Object="SerializableApplet.ser" Width=300 Height=300></Applet>

소켓을 통한 객체 직렬화

소켓을 생성한 뒤 스트림을 얻어서 ObjectInputStream과 ObjectOutputStream을 얻어서 사용한다면, 파일이 아닌 네트웍 너머의 다른 컴퓨터와도 객체를 직접 주고 받을수 있다.
소켓을 통해 직렬화 되어 쓰고 읽혀질 객체는 역시 Serializable을 구현하고 있어야 하며, 사용법은 파일에 쓰고 읽는것과 동일하다.
다음은 서버에서 이미지를 포함하고 있는 Serializable을 구현한 객체를 생성하여 소켓을 이용하여 클라이언트 애플릿에게 직렬화된 객체를 보내고, 애플릿에서는 이를 해제하여 화면에 나타내는 예제이다.
여기서 주의깊게 살펴보아야 할 점은 이미지를 직렬화하기위한 방법이다.
이미지는 Serializable을 구현하고 있지 않기 때문에 직접적으로 객체 직렬화에 사용되어질 수는 없다. 하지만, 자바 2에서는 배열을 직렬화에 사용할 수가 있고, 이미지는 각각의 픽셀들이 배열에 저장되어질수 있으므로 직접적으로 이미지를 직렬화 할 수는 없지만 배열을 사용하여 간접적으로나마 이미지를 객체 직렬화에 사용할 수가 있다.
  • 서버측 프로그램
 
import java.awt.*;
import java.awt.image.*;
import java.net.*;
import java.io.*;
import java.util.*;
public class ImageSerializeServer {
// 서버는 3434 포트로 임의로 설정
    public static final int DEFAULT_PORT = 3434;
    protected int port;
    protected ServerSocket server;
// ImageSerializeServer를 실행한다.
    public static void main (String args[]) {
        new ImageSerializeServer ();
    }
/* 생성자. 서버의 소켓을 생성하고 클라이언트의 연결을 대기 */
    public ImageSerializeServer() {
// 포트를 지정(3434)
        port = DEFAULT_PORT;
        this.port = port;
// 지정된 포트로 서버의 소켓을 생성한다.
        try {
            server = new ServerSocket (port);
        } catch (IOException e) {
            System.err.println ("Error creating server");
            System.exit (1);
        }
// 클라이언트의 연결을 대기
        connectClient();
    }
/* 클라이언트로 부터의 연결을 기다리면서 무한루프를 돈다. */
    public void connectClient() {
        System.out.println ("Server Running");
        try {
// 클라이언트로 부터의 연결이 요청되면 클라이언트의 소켓을 생성 하고 이미지 픽셀을 클라이언트로 보내는 쓰레드를 생성한다.
            while (true) {
                Socket client = server.accept();
                System.out.println ("Connection from: " + client.getInetAddress().getHostName());
                appletConnection c = new appletConnection (client);
// 쓰레드 생성
            }
        } catch (IOException e) {
            System.err.println ("Exception listening");
            System.exit (1);
        }
        System.exit (0);
    }
}
/* 간단한 이미지를 이루는 픽셀을 만들어 이를 네트웍너머의 클라이언트로 보내는 클래스 */
class appletConnection extends Thread {
// 변수 선언
    private final static int b = Color.blue.getRGB();
    private final static int g = Color.green.getRGB();
    private final static int r = Color.red.getRGB();
    protected ObjectOutputStream oos;
// 간단한 이미지 픽셀
    protected int ipTemp[] = { b, b, b, b, b, b, b, b, b, b,
                                         b, b, b, b, b, b, b, b, b, b,
                                         b, b, g, g, g, g, g, g, b, b,
                                         b, b, g, g, g, g, g, g, b, b,
                                         b, b, g, g, r, r, g, g, b, b,
                                         b, b, g, g, r, r, g, g, b, b,
                                         b, b, g, g, g, g, g, g, b, b,
                                         b, b, g, g, g, g, g, g, b, b,
                                         b, b, b, b, b, b, b, b, b, b,
                                         b, b, b, b, b, b, b, b, b, b };
    public appletConnection (Socket client) {
// 객체 직렬화를 위한 출력 스트림을 생성
        try {
            oos = new ObjectOutputStream(client.getOutputStream());
        } catch (IOException e) {
            try {
                client.close();
            } catch (IOException e2) { }
            System.err.println ("Unable to connect.");
            return;
        }
        start();
    }
    public void run () {
/* Serializable을 구현한 imagePixel객체를 생성해서 Vector에 집어넣는다. 따라서 이미지가 하나가 아닌 여러개 일지라도 객체 직렬화를 통해 전달하는것이 가능하다.*/
        Vector vt = new Vector();
        ImagePixel ip = new imagePixel(ipTemp);
        vt.addElement(ip);
// 이미지 픽셀들을 포함하고 있는 벡터를 객체 직렬화 시켜 스트림에 쓴다.
        try {
         
   oos.writeObject(vt);
        } catch (Exception e) {
            System.out.println("Object write exception : " + e);
        }
    }
}
/* 객체의 직렬화를 위해 Serializable을 구현한 imagePixel 클래스 */
class imagePixel implements Serializable {
    protected int ip[];
// 생성자
    public imagePixel(int[] ip) {
        this.ip = ip;
    }
}
  •  실행방법
    javac.exe ImageSerializeServer.java
    java.exe ImageSerializeServer
    Server Running.....(서버가 대기하고 있는 상태)
 
  • 클라이언트측 프로그램
 
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import java.util.*;
public class ImageSerializeClient extends Applet {
// 기본 포트는 서버와 같이 3434로 하고 host는 localhost로 설정한다.
    private int defaultPort = 3434;
    private String host = "localhost";
    private Socket s;
    private ObjectInputStream oin;
// 변서 선언
    Vector vt;
    Image myImage;
    public void init() {
/* 서버에 접속하여 소켓을 구한뒤에 객체 직렬화를 해제하기 위한 스트림을 얻는다. 이것은 마치 파일로부터 스트림을 얻는 과정과 비슷하다. */
        try{
            s = new Socket(host, defaultPort);
            oin = new ObjectInputStream(s.getInputStream());
        } catch(IOException e) { }
    }
    public void start() {
// 객체를 스트림으로 부터 읽어들여 직렬화를 해제한다.
        try {
         
   vt = (Vector)oin.readObject();
        } catch(Exception e) {}
// 직렬화가 해제된 객체는 Vector에 들어가게 되고 Vector의 요소인 imagePixel을 구한다.
        imagePixel myPixel = (imagePixel)vt.elementAt(0);
// 이렇게 구해진 imagePixel의 이미지 픽셀을 가지고 실제 이미지를 생성
        myImage = createImage(new MemoryImageSource(10, 10, myPixel.ip, 0, 10));
    }
// 생성된 이미지로 그림을 그린다.
    public void paint(Graphics g) {
        g.drawImage(myImage, 0, 0, 100, 100, this);
    }
}
  •   실행방법
    javac.exe ImageSerializeClient.java
    java.exe ImageSerializeClient
     
  • 애플릿 포함 Html 내용
 
<HTML>
<TITLE>객체 직렬화</TITLE>
<BODY>
<APPLET CODE = "ImageSerializeClient.class" WIDTH = 100 HEIGHT = 100 ></APPLET>
</BODY>
</HTML>
 
  • 최종 결과 화면
사용자 삽입 이미지
 프로그램을 실행하기 위해서는 다음의 과정으로 실행하면 된다.
  1. 서버측 프로그램인 ImageSerializeServer.java를 컴파일하고, 서버 쪽에서 실행시면, "Server Running..."이라는 메시지가 나타나면서 서버측 프로그램이 제대로 작동을 하는 것이다.
  2. 다음으로는 클라이언트 애플릿 프로그램인 ImagSerializeClient.java를 컴파일한다.
  3. 브라우져를 실행시킨뒤에 클라이언트 애플릿 클래스를 실행시키기 위한 html파일이 있는 URL주소를 주소 입력란에 넣는다.
  4. 이때 서버프로그램이 돌아가는 곳과 애플릿을 위한 코드 및 html파일이 위치한 곳은 같은 Machine상에 있어야 한다.
참고로 이번 예제에서는 이미지 픽셀을 직접 그 값을 대입해서 이미지를 대신했는데, 실제 이미지를 이미지 픽셀로 바꾸는 기능이 자바에서 지원되고 있으므로 이를 간단히 소개토록 하겠다.
이미지는 여러개의 색깔을 표현하고 있는 픽셀들의 집합으로 볼 수 있고, 보다 정교하게 이미지를 다루는 작업을 하고 싶다면, 이미지의 픽셀들을 얻어서 조작을 해야한다. 그렇다면 어떻게 이미지의 픽셀들을 얻을 수 있을까? 그것은 PixelGrabber를 통해서 가능하다.
PixelGrabber는 이미지와 해당 이미지의 정보들을 기반으로 이미지 픽셀을 구해준다.
다음은 실제로 이러한 것을 보여주는 코드이다.
 
int imagePixel[];                         // 이미지의 픽셀들을 저장할 배열
int imageWidth, imageHeight;      
// 이미지의 폭과 높이를 가지게 될 변수들
Image imageObject;                  
// 이미지 객체
/* imageObject로부터 픽셀들을 얻어서 imagePixel배열에 저장한다. */
private void getImagePixel() {
    imagePixel = new int[imageWidth * imageHeight];    // imagePixel을 초기화 한다.
    PixelGrabber grabber = new PixelGrabber(imageObject, 0, 0, imageWidth, imageHeight, imagePixel, 0,                                                           imageWidth);
// grabber를 생성해서 픽셀들을 얻는다.
    try {
        grabber.grabPixels();
    } catch(Exception e) {
        return; }
    if((grabber.status() & ImageObserber.ABORT) != 0)
        return;
}

 
참고 문헌
  • Java Examples In a Nutshell (O'RELLY)
  • Java I/O (O'RELLY)
:
Posted by 뽀기

자바 프레임웍에서 Ajax 애플리케이션 구현을 위한 Google Web Toolkit





난이도 : 중급

Noel Rappin, Senior Software Engineer, Motorola, Inc.

2007 년 2 월 06 일

Google Web Toolkit (GWT)은 동적 Java™Script의 생성에 혁신을 가져왔습니다. GWT를 사용하면, 개발자들은 익숙한 자바 기술을 사용하여 사용자 인터페이스(UI)와 이벤트 모델을 디자인하고 대다수의 브라우저에 익숙한 코드를 만드는 일을 하게 됩니다. 이 글을 통해, GWT의 기초를 설명하고, GWT에서 Asynchronous JavaScript + XML (Ajax) 애플리케이션을 만드는 방법과, 자바 언어로 코드를 작성하는 방법을 설명합니다. 또한 온라인에서 피자를 판매하는 Slicr라고 하는 Web 2.0 비즈니스 샘플을 가지고, GWT 애플리케이션을 생성 및 실행하는 방법을 설명합니다.

GWT를 사용하여, 전통적인 자바 GUI 인터페이스를 구현하는 것 보다 훨씬 더 쉽게, 더욱 풍부한 Ajax 브라우저 클라이언트 인터페이스를 구현할 수 있다. GWT가 매우 놀랍기는 하지만, 전체 웹 애플리케이션을 이것 하나로 만들 수는 없다. 서버에 데이터 스토어가 있어야 하고, 데이터를 자바 객체로 변환하여, GWT가 서버에서 클라이언트로 전달할 수 있도록 하는 프레임웍이 있어야 한다. 본 기술자료 시리즈에서, 100% 순수 자바 데이터베이스인 Apache Derby를 사용하는 방법을 설명한다.

이 글에서는 GWT에 대한 중점적인 설명과 더불어, GWT를 설정하는 방법과 사용자 액션에 반응하는 간단한 클라이언트 인터페이스를 구현하는 방법을 설명한다. 후속 기술자료에서는 Derby 데이터베이스를 설치하고, GWT 프론트엔드를 Derby 기반 백엔드에 연결하는 방법을 설명한다. 마지막으로, 개발 환경 밖에서 시스템을 전개하는 방법도 배운다.

Ajax 리소스 센터에서는 Ajax 프로그래밍 모델과 관련한 기술자료, 튜토리얼, 포럼, 블로그, wiki, 이벤트, 뉴스 등이 제공된다.

Google Web Toolkit이란 무엇인가?

GWT를 사용하여, 자바 프로그래밍 언어로 Ajax 애플리케이션을 개발할 수 있다. Ajax 애플리케이션의 장점은 전통적인 UI 애플리케이션과 연결되는 풍부한 인터랙티브 환경일 것이다. 그림 1은 샘플 GWT 인터페이스로서 데스크탑 이메일 애플리케이션이다. 이 데모는 GWT 웹 사이트에서 볼 수 있다.


그림 1. GWT 이메일 데모
A GWT e-mail demonstration

GWT의 가장 고유한 기능은 Ajax 애플리케이션을 만들 수 있고, 자바 언어로 코드를 작성할 수 있다는 점이다. 선호하는 자바 통합 개발 환경(IDE)을 사용하여, 클라이언트를 디버깅 할 수도 있다. 자바 객체를 사용하여 클라이언트와 서버간 통신도 가능하고, 모든 것이 자바 애플릿 보다 가볍다.

GWT는 기본적으로 컴파일러이다. GWT는 자바 코드를 HTML 페이지에 삽입되어 실행될 수 있는 JavaScript 코드로 변환하여 클라이언트 측 애플리케이션을 실행하는데 사용된다. JavaScript 코드는 거의 모든 브라우저에서 지원되므로 여러분은 프로그램의 인터페이스와 인터랙션에 집중할 수 있다.

물론, GWT가 컴파일러일 뿐이라면, 거론하지 않았을 것이다. 다행히도, 그 이상의 기능을 한다. GWT는 컴파일러 메커니즘 뿐 만 아니라, 아래와 같은 부가적인 특징이 있다.

  • 표준 UI 위젯은 유연하고, 거의 모든 주요 브라우저에서 실행된다. (Safari와 Opera 포함)
  • 클라이언트 측의 이벤트를 포착하고 응답하는 이벤트 메커니즘.
  • 웹 애플리케이션과 서버 간 비동기식 호출을 관리하는 프레임웍.
  • Ajax 애플리케이션이 예상 Back 버튼 작동에 관여하지 않도록 하는, STATEFUL 브라우저 히스토리를 만드는 메커니즘.
  • JUnit을 사용하여 클라이언트 애플리케이션용 단위 테스트를 작성하는 테스트 프레임웍.

본 시리즈에서는 이러한 기능들을 설명할 것이다. 하지만 먼저, GWT를 다운로드 및 설치해야 한다.

GWT 설치하기

이 글을 쓰고 있는 현재, GWT 최신 버전은 1.2이다. (참고자료) GWT는 Microsoft® Windows® XP, Windows 2000, Linux® 그리고, Mac OS X에서 실행되는 GTK+ 2.2.1 또는 그 이후 버전에서 완벽히 지원된다.

다운로드 한 압축 파일을 원하는 디렉토리에 압축을 푼다. 버전들마다 약간 다르지만, 기본 엘리먼트는 다음과 같다.

  • 세 개의 .jar 파일: gwt-user.jar 에는 프로젝트 classpath에 필요한 사용자 클래스들이 포함되어 있다. gwt-dev-windows.jar 또는 gwt-dev-linux.jar에는 컴파일러 코드가 포함되어 있다. 세 번째 파일, gwt-servlet.jar는 전개(deploy)에 사용된다.
  • 세 개의 명령행 유틸리티: applicationCreator, junitCreator, projectCreator. (Windows에서는, .cmd가 붙는다.)
  • 샘플 코드 디렉토리

GWT를 사용할 때, 일부 파일들은 임시 파일들을 관리하기 위해 GWT 홈 디렉토리에 놓인다.

프로젝트 만들기

다운로드를 마치면, 가장 먼저 프로젝트를 만들어야 한다. 온라인에서 피자를 파는, Slicr라고 하는 새로운 Web 2.0 비즈니스의 온라인 사이트를 구축할 것이다. GWT 프로젝트를 설정하는 방법들은 IDE를 사용할 것인지의 여부에 따라 다르다. 여기에서는 GWT 명령행 유틸리티의 지원도 받는 무료 Eclipse를 사용하도록 하자.

명령행 유틸리티를 사용하여 Eclipse 프로젝트를 만든다.

  1. 하드 드라이브의 적당한 곳에 slicr라고 하는 새로운 디렉토리를 만든다.
  2. 새로운 slicr 디렉토리에 명령어 프롬프트를 연다.
  3. 다음 명령어를 입력한다. (슬래시와 그 외 것들을 각자 운영 체계에 맞게 변환한다.)

<GWT_HOME>/projectCreator -eclipse slicr

이 명령어로는 GWT 프로젝트에 필요한 최소한의 것만 만든다. 새로운 src 하위 디렉토리와 새로운 .project와 .classpath 파일이 생겼다.

지금 바로 프로젝트로 시작할 수 있지만, GWT는 그 이상의 구조를 기대한다. 다음 명령어를 사용하여 설정할 수 있다.


<GWT_HOME>/applicationCreator -eclipse slicr com.ibm.examples.client.Slicr

-eclipse slicr 인자는 Eclipse 프로젝트의 이름이고, projectCreator에서 사용했던 것과 같아야 한다. (그렇지 않으면, Eclipse 내에서 GWT 애플리케이션을 시작하는데 문제가 생긴다.) 마지막 인자는 애플리케이션의 메인 클래스가 될 클래스 풀네임(fullname)이다. 이때, 패키지명의 마지막은 의미상 client로 정하기로 하고, client 이전의 부모 패키지 이름은 여러분이 알아서 작성하도록 한다. (일단, 'com.ibm.examples'로 부모 패키지 이름을 정하였다.)

이 명령어는 몇 가지 파일을 생성한다. .java 파일은 메인 클래스이고, 해당 클래스에 동반된 패키지 명에 부합되게 부모 디렉토리 들이 생성된다. 여러분은 최하위 디렉토리에는 clientpublic이 생성되어 있는 것을 확인 할 수 있다. 앞에서 거론한 public 디렉토리에는 Slicr.html와 Slicr.gwt.xml을 확인 할 수 있다. 또한, GWT는 eclipse가 사용하게 될 Slicr.launch파일과 두 개의 셀 스크립트 파일을 생성 한다.




위로


Eclipse로 옮기기

이제 프로젝트를 Eclipse로 옮긴다.

  1. Eclipse를 열고, File > Import를 클릭한다.
  2. 새로운 창에서, General 트리를 확장하여 Existing Projects into Workspace를 선택한다.
  3. Next를 클릭한 다음 Browse를 클릭하여 slicr 루트 디렉토리를 선택한다.
  4. 프로젝트를 선택하고, Copy projects into workspace 옵션이 설정되지 않도록 한다. (프로젝트를 옮기지 않을 것이기 때문이다.)

이 프로세스를 통해, 코드를 Eclipse로 옮겨서 볼 수 있다. GWT는 세 개의 중요한 파일들을 만들었다. 첫 번째가 com.ibm.examples 패키지에 있는 slicr.gwt.xml 파일이다. Listing 1은 XML 설정 파일이다.


Listing 1. slicr.gwt.xml
				
<module>
    <inherits name='com.google.gwt.user.User'/>
    <entry-point class='com.ibm.examples.client.Slicr'/>
</module>

여기에서 GWT 애플리케이션에 대한 모듈을 정의할 수 있다. 모듈(module)은 GWT 코드의 기본 단위이고, 클라이언트가 사용하는 HTML 페이지가 참조한다.

두 번째 파일은 퍼블릭 디렉토리에서 만들어진 Slicr.html 파일이다. 이것은 웹 애플리케이션의 프론트 페이지로서 클라이언트로 보내지는 .html 파일이다. 실제로 필요하지 않는 많은 코멘트들이 들어있다. Listing 2는 파일의 핵심 부분이다.


Listing 2. Slicr.html
				
<html>
    <head>
        <title>Wrapper HTML for Slicr</title>
        <meta name='gwt:module' 
            content='com.ibm.examples.Slicr'>
    </head>
    <body>
        <script language="javascript" 
            src="gwt.js"></script>
        <iframe id="__gwt_historyFrame" 
            style="width:0;height:0;border:0"></iframe>
        <h1>Slicr</h1>
        <p>
            This is an example of a host page for 
            the Slicr application. 
        </p>
        <table align=center>
            <tr>
                <td id="slot1"></td>
                <td id="slot2"></td>
            </tr>
        </table>
    </body>
</html>

가장 중요한 것은 바로 위의 .html 파일이다. 원하는 어떤 HTML이라도 추가할 수 있다. 가장 중요한 것은 HTML 파일에 원하는 어떤 내용이라도 추가 및 변형 할 수 있다는 것이다. 단지, GWT는 아래 네 가지 엘리먼트에 의해서 실행된다.

  • meta 태그: name 애트리뷰트와 content 애트리뷰트는 모듈의 완전한 형식을 갖춘 논리적 이름이다. (XML 모듈 파일과, 확장자 없는 XML의 파일 이름을 포함하고 있는 패키지이다.) 이 태그는 HTML 페이지를 특정 모듈로 연결시킨다. 페이지를 호출하면 모듈이 시작된다. (특히, 모듈에 있는 모든 엔트리 포인트들이 초기화 된다.)
  • script 태그: 이 태그는, GWT 자바 코드를 JavaScript 코드로 변환할 때 생성된 파일 중 하나인 gwt.js라고 하는 파일을 로딩한다. 이 파일은 모든 클라이언트 코드의 로딩을 제어하기 때문에, 프로그램을 실행하는 것 보다 이를 포함시키는 것이 더 중요하다.
  • iframe 태그: 이 태그를 쓰여진 대로 정확히 사용하면 웹 애플리케이션은 히스토리와 상태를 기억할 수 있다. GWT 애플리케이션에서 Back 버튼을 실행 불가로 만들 필요가 없다.
  • td 태그: 특정 .html 파일에 있는 이 태그에는 JavaScript 식별자들이 포함된다. 특별한 것은 없지만, GWT는 이 식별자를 엘리먼트를 배치할 장소로서 사용한다.

자바 시작 클래스

GWT는 재사용이 가능한(boilerplate) 자바 시작 클래스도 Listing 3과 같이 만든다.


Listing 3. 자바 시작 클래스
				
public class Slicr implements EntryPoint {

  public void onModuleLoad() {
    final Button button = new Button("Click me");
    final Label label = new Label();
    button.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        if (label.getText().equals(""))
          label.setText("Hello World!");
        else
          label.setText("");
      }
    });
    RootPanel.get("slot1").add(button);
    RootPanel.get("slot2").add(label);
  }
}

위의 자바 클래스는 client 패키지에 있고, 이로 인하여 GWT는 해당 코드를 JavaScript로 컴파일 할 것이다. 이것은 파일에 해당 특정 제한이 있는 것 같지만, 코드 내부를 보면 기본적인 Java 1.4 코드일 뿐이다.EntryPoint 인터페이스는 onModuleLoad()만을 정의하였으며, GWT는 이 모듈을 참조하는 HTML 페이지가 로딩되면 이 메소드를 자동으로 호출하게 되는 것이다.

위의 코드는 단순하다고 할 수 있다. 처음 두 라인에 버튼과 레이블을 정의한다. 그 다음 두 라인에서는, RootPanel.get() 메소드를 사용하여, 이 위젯들을 HTML 페이지의 특정 엘리먼트로 연결시킨다. 이 메소드에 대한 인자는 HTML 페이지에 정의된 엘리먼트의 JavaScript ID이다.

위의 코드 라인들 사이에 Swing에서와 같이 이벤트를 묶는데 사용하는 것과 비슷한 방식을 사용하여 이벤트 리스너(eventListner)를 정의한다. 위의 경우, 버튼을 클릭하면 ClickListener가 호출된다.




위로


GWT 프로그램 실행하기

GWT가 만든 샘플 프로그램을 실행해 보자. GWT 프로그램을 실행하는 두 가지 방법이 있다. 바로 웹 모드(Web mode)와 호스트 모드(hosted mode)이다. 웹 모드는 완전한 전개 모드로서, GWT 프로그램을 JavaScript 코드로 컴파일 한 후에 실행한다.

호스트 모드는 개발하는 동안 사용된다. 호스트 모드란, 클라이언트와 서버 코드를 한번에 시뮬레이트 하면서, 개발하는 동안 전개를 단순화 시키는 일종의 에뮬레이터이다. (호스트 모드는 Mac OS X에서는 불가능하다.) 디버거와 함께 IDE를 사용한다면, 호스트 모드에서 GWT 프로그램을 실행할 수 있으며, JavaScript로 컴파일 될 코드 부분에서 중단점(breakpoint)을 지정하고 그의 변수(variable)의 변경사항을 알아낼 수 있다. 이러한 부분은 개발 시 매우 유용하며, GWT로 작업하게 되면, 대부분 호스트 모드를 사용하게 된다.

또한, 여러 가지 방법들로 호스트 모드를 실행할 수 있다. 이전에 실행했던 applicationCreator 스크립트는 명령행에서 호출할 수 있는 Slicr-shell 스크립트를 만들어서 호스트 모드를 실행할 수 있도록 하였다. 또한, 이 글 초반에, GWT 프로젝트를 Eclipse로 반입하는 방법을 설명했다. Eclipse 프로젝트에서, Run 메뉴 또는 툴바에서 Debug 또는 Run을 선택할 수 있다. 생성된 창을 통해서는 Java Application을 클릭하여 Slicr 옵션을 볼 수 있다. 이 옵션은 GWT가 만들었고, 나머지 프로젝트와 함께 Eclipse로 반입했던 Slicr.launch 파일에서 사용할 수 있다.


그림 2. 호스트 모드 호출하기
Invoking hosted mode

호스트 모드에서 실행할 때, 두 개의 창이 나타난다. (처음 실행할 경우, 호스트 모드 설정이 초기화되는 동안 1분여의 시간이 걸린다.) 그림 3에서 보이는 첫 번째 창의 이름은 Google Web Toolkit Development Shell / Port 8888.이다. 여기에는 GWT의 에러와 로그 메시지들이 들어있다. 툴바를 사용하여, 새로운 호스트 브라우저를 열 수 있고, 스크린상의 로그를 확장, 축소, 삭제할 수 있다.


그림 3. 호스트 모드 쉘 윈도우
Hosted mode shell window

그림 4에 보이는 두 번째 창은 브라우저 모습이다. 보다시피, slicr.html 페이지에서 생성된 정적 HTML과 Slicr.java EntryPoint 클래스에서 생성된 버튼 위젯이 있다. 버튼을 클릭하면 레이블이 선택된다. 설정 단계에서 오류가 있었다면, 이 창을 볼 수 없고 대신 쉘에 에러 메시지가 나타난다. 모든 이름들이 정확한지를 확인하라. (특히, .launch 파일에서, 정확한 프로젝트 디렉토리가 지정되었는지를 확인한다.)


그림 4. 호스트 모드 브라우저
Hosted mode simulated browser

클라이언트 설정하기

이 글에서는 스크린상에서 위젯을 실행시키는 것에 초점을 맞추겠다. 그림 5에 보이는 스크린은 매우 단순해 보이지만, 꽤나 기능적이다.


그림 5. Slicr
Slicr

페이지가 로딩될 때 이러한 위젯들을 생성시키려면, EntryPoint 클래스의 onModuleLoad() 메소드에 코드를 넣어야 한다. Listing 4는 두개의 데이터를 정의하고, 각 패널을 구현하기 위해 헬퍼를 호출하는 메소드를 정의하고 있다. 이 코드가 실행되려면, slicr ID로 HTML 페이지에 엘리먼트를 삽입해야 한다. 이렇게 하면 리스팅의 RootPanel.get() 명령어가 그 페이지의 엘리먼트를 찾을 수 있다. 가장 쉬운 방법은 이전 HTML 리스팅의 테이블을 >div id="slicr" /<로 대체하는 것이다.


Listing 4. 모듈 로드 이벤트 핸들러
				
private DockPanel panel;
private List clearables;

public void onModuleLoad() {
    clearables = new ArrayList();
    initDockPanel();
    panel.add(buildActionPanel(), DockPanel.SOUTH);
    panel.add(buildPizzaTypePanel(), DockPanel.WEST);
    panel.add(buildToppingPanel(), DockPanel.EAST);
    RootPanel.get("slicr").add(panel);
}

위젯 설정하기

DockPanel에 모든 위젯을 설정하도록 한다. GWT에서의 DockPanel이란, Swing에서 BorderLayout을 사용하는 Panel에 해당한다. Swing에서는 한 개의 panel 클래스와 여러 개의 레이아웃 매니저가 있는 반면, GWT에는 여러 Panel 서브클래스가 있고, 각각 자식 위젯을 전개하는 고유의 알고리즘이 있다. 다른 패널 클래스로는 SimplePanel, HTMLTable, FlowPanel, StackPanel 등이 있다. DockPanel을 만드는 것은 어렵지 않다. 아래 Listing 5에서와 같이 세터(setter)가 이 일을 대신한다.


Listing 5. 메인 패널 초기화 하기
				
private void initDockPanel() {
    panel = new DockPanel();
    panel.setBorderWidth(1);
    panel.setSpacing(5);
}

SOUTH 패널(button) 생성하기

DockPanel의 선착순 원리를 따르기 때문에, 먼저 하단부 패널을 정의한다. 이런 방식으로, SOUTH 위젯은 전체 패널에서 실행한다. 아래 Listing 6과 같이 HorizontalPanel을 액션 패널로서 정의한다. (GWT의 HorizontalPanel이란, Swing에서의 box와 비슷하다고 보면 된다.)


Listing 6. SOUTH (buttons) 패널
				
public HorizontalPanel buildActionPanel() {
    HorizontalPanel actions = new HorizontalPanel();
    actions.setSpacing(10);
    Button clear = new Button("Clear");
    clear.addClickListener(new ClearClickListener());
    Button newPizza = new Button("Another Pizza");
    Button submitOrder = new Button("Submit");
    actions.add(clear);
    actions.add(newPizza);
    actions.add(submitOrder);
    return actions;
}

GWT Button 위젯을 사용하여 세 개의 버튼을 만든 다음, 패널에 추가한다. 또한, 나중에 정의하게 될 Clear 버튼에 ClickListener를 만든다. GWT는 이벤트 리스너들을 Swing과는 다르게 나눈다. ClickListener는 마우스 클릭만 리스닝한다. (종종, 인라인 클래스로서 정의된 리스너를 보곤 하는데, 이 스타일은 읽고 테스트 하기 까다롭기 때문에 네임드 클래스를 만들었다.)

WEST 패널(pizza type) 생성하기

pizza type 의 패널은 Listing 7과 같이 복잡하지 않다. GWT RadioButton 위젯을 사용하면 된다.


Listing 7. WEST (pizza types) 패널
				
public static final String[] PIZZA_TYPES = new String[] {
    "Thin Crust Medium", "Thin Crust Large", 
    "Thin Crust X-Large", "Thick Crust Medium", 
    "Thick Crust Large"
};

private VerticalPanel buildPizzaTypePanel() {
    VerticalPanel pizzaTypes = new VerticalPanel();
    HTML label = new HTML("<h2>Pizza</h2>");
    pizzaTypes.add(label);
    for (int i = 0; i < PIZZA_TYPES.length; i++) {
        RadioButton radio = new RadioButton("pizzaGroup", 
            PIZZA_TYPES[i]);
        clearables.add(radio);
        pizzaTypes.add(radio);
    }
    return pizzaTypes;
}

HTML을 실행하는 레이블인 HTML 위젯을 사용할 것이다. HTML 위젯은 결국 HTML상의 <span> 태그 주위의 래퍼(Wrapper)로서 역할을 한다. RadioButton 컨스트럭터(constructor)는 두 개의 인자들을 취한다. 첫 번째는 라디오 버튼용 스트링 레이블이고, 두 번째는 텍스트 레이블이다. 각 버튼을 패널과 인스턴스 리스트에 추가하면 이 리스너들 중 한 곳에서 사용하게 될 것이다.


EAST 패널 (toppings) 만들기

topping 패널은 Listing 8처럼 좀더 복잡하다. 사용자가 다양한 토핑을 가진 피자를 만들 수 있도록 해야 한다. 토핑 버튼을 클릭하면 두 쪽 모두 체크되지만, 한 쪽만 체크되거나, 개별적으로 삭제될 수 있다. 모든 것의 줄을 맞춰야 하기 때문에 Grid를 사용한다.


Listing 8. 토핑 그리드
				
public static final String[] TOPPINGS = new String[] {
    "Anchovy", "Gardineria", "Garlic", 
    "Green Pepper", "Mushrooms", "Olives", 
    "Onions", "Pepperoni", "Pineapple", 
    "Sausage", "Spinach"
};

private VerticalPanel buildToppingPanel() {
    VerticalPanel toppings = new VerticalPanel();
    toppings.add(new HTML("<h2>Toppings</h2>"));
    Grid topGrid = new Grid(TOPPINGS.length + 1, 3);
    topGrid.setText(0, 0, "Topping");
    topGrid.setText(0, 1, "Left");
    topGrid.setText(0, 2, "Right");
    for (int i = 0; i < TOPPINGS.length; i++) {
        Button button = new Button(TOPPINGS[i]);
        CheckBox leftCheckBox = new CheckBox();
        CheckBox rightCheckBox = new CheckBox();
        clearables.add(leftCheckBox);
        clearables.add(rightCheckBox);
        button.addClickListener(new ToppingButtonListener(
                leftCheckBox, rightCheckBox));
        topGrid.setWidget(i + 1, 0, button);	
        topGrid.setWidget(i + 1, 1, leftCheckBox);
        topGrid.setWidget(i + 1, 2, rightCheckBox);
    }
    toppings.add(topGrid);
    return toppings;
}

VerticalPanelHTML 위젯을 사용한다. GWT Grid에 모든 것을 놓기 때문에, 그리드의 크기를 설정해야 한다. 그리드의 각 셀에는 플레인 텍스트나, 또 다른 GWT 위젯을 포함시킬 수 있다. 각 행(row)에, 버튼과 두 개의 체크 박스를 만들고, 이들을 셀에 맞춰 정렬한다. 버튼용 리스너를 추가하고, 체크 박스를 clearable 리스트에 놓는다.

리스너 정의

위젯을 설정했다면, 두 개의 정의된 리스너를 보자. 두 개 중 더 단순한 것이 Clear 버튼에 대한 것이다. Listing 9와 같이 이 버튼은 clearable 리스트에서 실행되고, 모든 것을 지운다.


Listing 9. Clear 버튼에 정의된 리스너
				
private class ClearClickListener implements ClickListener {
    public void onClick(Widget sender) {
    for (Iterator iter = clearables.iterator(); iter.hasNext();) {
            CheckBox cb = (CheckBox) iter.next();
            cb.setChecked(false);
        }
    }
}

주: GWT에서, RadioButton은 실제로 CheckBox의 서브클래스이다. 따라서 위 코드는 class cast exception을 트리거(trigger)하지 않는다.

토핑 버튼용 리스너는 더 복잡하다. 제휴 체크 박스들 중 어떤 것도 선택되지 않으면, 이 리스너는 두 개의 체크 박스 모두를 선택한다. 다른 상황에서는, 두 가지 모두를 지운다. Listing 10은 그 예이다.


Listing 10. 버튼에 정의된 리스너
				
private class ToppingButtonListener implements ClickListener {

    private CheckBox cb1;
    private CheckBox cb2;

    public ToppingButtonListener(CheckBox cb1, CheckBox cb2) {
        this.cb1 = cb1;
        this.cb2 = cb2;
    }

    public void onClick(Widget sender) {
        boolean unchecked = !cb1.isChecked() && !cb2.isChecked();
        cb1.setChecked(unchecked);
        cb2.setChecked(unchecked);
    }
}

공유 사이트...

digg Digg
del.icio.us del.icio.us
Slashdot Slashdot

예고

이번 글에서는 slicr 클라이언트를 구현해 보았다. 다음 글에서는 Derby 데이터베이스를 사용하여 서버 측에서 데이터 레이어를 구현하고, 데이터베이스에서 온 데이터를 GWT 클라이언트로 보내질 수 있는 자바 객체들로 변환하는 방법을 설명하겠다. 서버와 클라이언트를 연결하는 원격 프로시저 아키텍처도 설명한다.

서버 측이 개별적으로 실행되어야 한다면, 개발 및 실행 환경에 이를 전개하는 방법을 고려해야 한다. 또한, 인터페이스를 보기 좋은 모양으로 만드는 방법도 배울 것이다. 그 전에 GWT 다운로드 사이트에 가서 직접 실행해 보기 바란다.

기사의 원문보기



참고자료

교육

제품 및 기술 얻기

토론


필자소개

Noel Rappin 박사는 Georgia Institute of Technology의 Graphics, Visualization, and Usability Center 소속이며, Motorola의 소프트웨어 엔지니어이다. wxPython in Action and Jython Essentials를 공동 집필했다.

출처 : IBM developerworks
:
Posted by 뽀기

애플리케이션에 맞게 데이터를 인코딩(encode) 하는 가장 올바른 방법


JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.



난이도 : 중급

Dethe Elza, Senior Software Developer, Uniserve Communications Corporation
David Mertz, Ph.D, Author, Gnosis Software, Inc.

2007 년 2 월 06 일

Ajax(Asynchronous JavaScript and XML)는 신뢰성을 기반으로, 서버에 채널을 개방하여, 웹 애플리케이션에서 사용되는 데이터를 교환하는 신개념 웹 브라우저입니다. 이것은 표준 웹 기술과는 반대되는 개념이고, Ajax를 기반으로 개발 할 때, 전통적인 웹 페이지들과는 다른 디자인 정책이 필요합니다. 백(back) 버튼을 관리하는 방법, 업데이트 된 데이터를 디스플레이 하는 방법, 업데이트를 보내는 빈도수 등도 그 예가 될 수 있습니다. 이 글에서는 데이터 교환에 어떤 포맷을 사용해야 할 것인지를 중점적으로 설명합니다.

Ajax라는 단어에서 X는 XML을 뜻하지만, XML은 언어가 아니라 언어를 구현하는 툴킷이다. 따라서, 가장 먼저, 기존 언어를 사용할 것인가, 아니면 자신의 언어로 재 구축할 것인지를 결정해야 한다. 결정하고 나서는 부가적인 문제들이 생기기 시작한다. 기존의 언어들은 자신의 필요에 맞는가, 자신에 필요에 맞게 설계할 수 있는가? 그 언어를 처리할 툴은 있는가, 툴을 구현해야 하는가? 다른 사람들과 커뮤니케이션을 해야 한다면, 그 언어는 잘 알려진 언어인가? 다른 애플리케이션들도 쉽게 여러분의 데이터에 액세스 하여, 사용할 수 있는가?

그래픽 데이터일 경우에는 (X)HTML, SVG 또는 X3D, 데이터 조각에는 Atom, 단순한 아웃라인에는 OPML, 그래프에는 RDF 등 용도가 확실한 것도 있다. 완벽한 기능을 갖추고 있고, 장황한 DocBook, DITA, OpenOffice 포맷으로 된 데이터를 보낼 수도 있다. (참고자료)

한편, Ajax라는 단어에서 X가 무엇을 뜻하는지에 상관없이, 여러분이 원하는 다양한 종류의 데이터를 보낼 수 있다. 바이너리 데이터, 이미지, 무비, PDF 파일도 가능하지만, JavaScript로 되어있는 브라우저에서는 다루기 어렵다. XML 보다 단순한 텍스트 포맷으로 보낼 수도 있다. Tab 또는 콤마 분리 리스트(tab-, comma-delimited list), Markdown, YAML, JSON 등을 XML 대안으로 사용할 수 있다. 이외에도, XML을 사용하지 않는 수 십 개의 옵션들이 있다. (참고자료 - XML Alternatives 웹 사이트 참조) 물론, 장황함(verbose)과 풍부함(rich)의 차이는 있다. 심지어, 이 리스트에 있는 한 개의 아이템에서도 여러 가지의 데이터 인코딩 방식이 있을 수 있다.

  • OpenOffice Spreadsheet
  • DITA (Darwin Information Typing Architecture)
  • DocBook
  • RDF-XML
  • XHTML
  • Microformat
  • Atom
  • OPML (Outline Processor Markup Language)
  • Custom XML
  • Markdown / Textile / reStructured Text
  • YAML (YAML Ain't Markup Language)
  • JSON (JavaScript Object Notation)
  • Comma- (or Tab-) delimited text

무엇을 선택하여야 하는가?

이러한 많은 옵션들 중에서, 어떻게 하면 현명한 결정을 내릴 수 있을까? 위에 제시한 리스트는 엄밀히 말해서 복잡한 순서대로 나열한 것은 아니다.

  • 우선, 가장 단순한 방식 또는 가장 복잡한 방식으로 포맷을 사용할 수 있다.
  • 생성, 읽기, 파싱, 프로세스 중에서 어떤 것이 가장 복잡한 것인지를 파악해야 한다. 예를 들어, XML은 그 자체로 복잡한 편이지만, 파서가 브라우저에 이미 있기 때문에 파싱은 오히려 단순하다.
  • 복잡성은 데이터의 유형에 상당히 많이 의존한다. 스프레드시트나 데이터베이스처럼, 고정된 구조를 갖고 있는가? 워드 프로세싱 문서나 블로그 포스트처럼 유연한(loosely) 구조를 갖고 있는가? 항공기 매뉴얼 같은 대형 문서인가, 응답 코드 같은 작은 데이터 조각인가? 데이터 세트가 크다면, 이것을 한 번에 보낼 것인가, 아니면 필요할 때마다 작은 조각 단위로 보낼 것인가? 큰 데이터를 작은 조각 단위로 나누는 것은 얼마나 어려우며, 받는 쪽에서, 작은 조각들을 올바르게 조합하려면 얼마나 많은 정보가 필요한가?

즉, 무엇이 가장 중요한 요소인가?

  • 장황함/대역폭(특별한 경우를 제외하고는, 이러한 요소는 여러분이 생각하는 것만큼 중요하지 않다.)
  • 가독성(작성 가능성, 관리성)
  • 파싱 난이도(클라이언트 측과 서버 측)
  • 파싱 속도(네이티브 대 스크립팅)
  • 정보 손실(ordered pairs -> dictionary -> sets)
  • 유연성(가끔은, 덜 유연한 것이 더 나을 때도 있다.)
  • 언어 이식성(얼마나 많은 언어들을 사용할 수 있는가?)
  • 정확성(포맷에 어느 정도 순응하는가?)
  • 라운드 트리핑 (Round-Tripping) (Markdown -> XHTML -> Markdown, 얼마나 많은 데이터 손실이 있는가?)

속도, 대역폭 활용, 효율성을 위해서가 아니라, 사용자를 만족시키기 위해 최적화 하는 경우가 대부분이다. 지연되는 부분은 숨기고, 즉각적인 업데이트가 가능하다면, 미미한 네트워크 지연은 문제가 되지 않는다. 바로 이것이 Ajax의 가장 큰 장점일 것이다.




위로


제 1 원칙

이 글에서, 몇 가지 선택 원칙을 제시하고, 이를 뒷받침 할 몇 가지 예제도 소개하겠다. 이모든 것들이 여러 가지 옵션들을 선택하는데 있어서 충실한 가이드라인이 될 것이다.

  • 데이터용 JSON: 데이터가 구축되었다면 JSON이 제격이다. 적어도 세 개의 파서들이 브라우저에 포함되어 있고(HTML, XML, JavaScript), 이중 가장 빠른 것은 JavaScript 이다. 또한, 데이터가 이미 JavaScript로 되어 있을 경우, JavaScript를 사용하여 데이터를 조작하는 것이 DOM 보다는 낫다. 데이터를 바로 디스플레이 하지 않거나, 데이터가 먼저 수정되어야 한다거나, 또는 데이터가 웹 페이지의 다른 부분에서, 또는 다른 포맷으로 디스플레이 된다면, JSON이 잘 맞는다. 대부분의 언어들에는 JSON용 라이브러리가 있기 때문에, 더 이상 JavaScript만을 위한 것은 아니다.
  • 혼합 콘텐트(문서)를 위한 XML: URL과 같은 메타데이터를 사용하거나, 워드 프로세싱 문서와 블로그 포스트 같은 마크업과 텍스트를 혼합해야 한다면 XML을 사용하라. 데이터가 한 곳에서 직접 디스플레이 된다면, 서버에서 이를 정형화(format)하고, Ajax를 사용하여 검색 및 삽입한다.(이 기법은 'client-side includes'라고 알려졌다.) David가 "MochiKit" (참고자료)에서 언급하였듯이, XML을 브라우저로 전달하고 CSS로 포맷하거나, 또는 HTML을 통해서 이를 스타일링 할 것인지를 선택할 수 있다. 당신이 XML을 사용해야 한다면, 필자는 XHTML, SVG, X3D등과 같은 표준 포맷을 사용하라는 것 외에 많은 가이드라인을 줄 수는 없다. 이러한 포맷들의 서브셋을 사용하여, 시스템들 간 상호 운용성이 뛰어난 데이터를 만들 수 있고, 다른 프로그래머들도 익숙하게 사용할 수 있도록 한다. 가끔은 자신만의 XML 포맷을 만들 수도 있지만, 상호 운용성 부분에서는 더욱 많은 신경을 써야 한다. 그것도 의심스럽다면, HTML을 계속 사용하라.
  • 동기화(Syndication)를 위한 Atom: 여기에서 나는 동기화에 대해 매우 포괄적으로 정의를 내리고자 한다. 데이터를 주기적으로 업데이트 할 것이라면, 데이터에 타임스탬프가 필요하다면, Atom을 사용하는 것이 좋다. 또한, 데이터 흐름에 대한 표준 인벨롭(envelope)으로서 Atom 포맷을 사용하는 것이 좋다. 이렇게 하면, 애그리게이터(aggregator), 뉴스 리더기, 스크립팅 라이브러리를 통해서, 데이터를 추적하고 재사용할 수 있는 많은 툴들을 사용할 수 있을 것이다. 또한, 웹 페이지에 데이터를 삽입만 하면, 크게 노력하지 않아도 동기화 피드가 될 수 있는 효과도 누릴 수 있다.



위로


선택의 파라독스

이제, 다른 포맷들과, 각 포맷의 사용 방법에 대해서 알아보자. 이전 두 개의 칼럼에서는(참고자료) 메커니즘에 초점을 맞추느라 간단한 예제를 사용했지만, 이번에는 실제 예제를 사용해보겠다. 나의 아내인 Daniela는 시인이다. 그녀는 잡지, 콘테스트, 이벤트에 제출한 작품들을 관리해야 했다. 제출한 시가 어떤 단계에 와 있으며, 어떤 것이 채택 또는 탈락 되었는지, 각 작품당 가격은 얼마인지, 이벤트 비용은 얼마인지를 알아야 한다. Listing 1은 데이터 내용이다.


Listing 1
				

Room of One's Own
Submitted May 10, '05
* Hyacinth Blue
* Fabrication
* Thanksgiving
* Spilling the Peas
Accepted Hyacinth Blue
Accepted Sept 2005
Published Oct 2006
Paid Sept 2006
Paid $50 + 2 copies
Postage $1.12
Submission Fee: 0
Journal submission

Surrey International Writer's Contest
Contest submission
Submitted Aug. 31 2006
* 13th Child
Fee: $15
Postage: 1.05
Honorable Mention
Prize: $150 + copy of anthology
Accepted Sept. 26 2006
Publication Date Oct. 20 2006

Word on the Street
Public Reading
Invited speaker
Reading time: 10 minutes
Paid: T-shirt and lunch
Date: Sept 24 2006

Paideusis: The Journal of the Canadian Philosophy of \
    Education Society
Submitted Oct. 13th 2006
* To carry over: metaphor invents us (seven poems)
Email submission
Referreed Journal
Accepted Oct. 16th 2006
Published (Pending) Nov. 2006


어느 정도 까지는 이러한 포맷도 유효했지만, 출품작이 많아지면서, 트래킹(tracking)은 더욱 어려워졌다. 연간 수입과 지출을 가늠하기가 더 어려워졌고, 평균 응답 시간(제출과 채택 또는 탈락까지 걸리는 시간)같은 정보도 받아보고 싶었다. 그래서, 나는 아내에게 "Fame Not Fortune"이라고 하는, Ajax 웹 애플리케이션을 구현 할 것을 제안했다.

물론, 나의 게으름 탓에, 새로운 애플리케이션을 구현하지 않고, 원래 있었던 애플리케이션을 사용하게 되었다. 아내가 시를 편집할 때 Open Office (워드 프로세싱) (참고자료)를 사용하기 때문에, 그림1처럼 트래킹에도 Open Office (spreadsheet)를 사용할 수 있었다.


그림 1. Open Office Spreadsheet 예제
Open Office Spreadsheet example

벌써, 몇 가지 문제가 드러나고 있다. 특히, 스프레드시트는 트래킹에 맞는 인터페이스가 아니다. 대상 데이터는 매우 유연하기 때문에, 많은 칼럼들이 채워지지 않았고, 아내도 스프레드시트를 사용하는 것에 익숙하지 않았다. 웹 애플리케이션을 구현해야 한다는 나의 생각이 옳았다. 물론, Open Office는 문서를 XML로 저장하기 때문에, 복잡한 데이터 마이닝(data-mining) 태스크에는 Open Office를 사용하고, 데이터 입력에는 웹을 사용하는 상황이다. 이러한 경우에는 한 가지 포맷을 결정하는 것이 더 낫다.

Open Office에 저장된 포맷은, 실상은 데이터와 기타 리소스들(삽입된 이미지, 스크립트, 스타일링 정보)을 포함하고 있는 .zip 파일인, .odf 파일이다. 이 문서를 보면서, 다음과 같은 사실들을 발견했다.

  • META-INF는 .odf 문서의 콘텐트 리스트인 manifest.xml을 포함하고 있는 폴더이다.
  • Configurations2는 상태 바, 메뉴 같은 UI를 포함하고 있는 폴더이다. 현재까지는 무시해도 좋다.
  • Thumbnails은 작은 .png 이미지를 포함하고 있는 폴더이다.
  • content.xml은 내가 입력했던 실제 데이터 파일이다.
  • meta.xml은 문서에 대한 정보를 포함하고 있는 파일이다. 생성자, 수정일, 기타 상세 정보를 포함하고 있다.
  • mimetype은 "application/vnd.oasis.opendocument.spreadsheet" 스트링을 포함하고 있는 파일이다.
  • settings.xml은 Open Office 설정 내용들을 포함하고 있는 파일이다.
  • styles.xml은 스프레드시트에 대한 포맷팅 정보를 포함하고 있는 파일이다.

따라서, 웹 애플리케이션과 Open Office간 데이터를 교환해야 한다면, 전체 .odf 파일이 아닌 내부 content.xml 파일에 주목해야 한다. 이 파일에는 무엇이 있을까? 17,629개의 문자와, 23개의 XML 네임스페이스, 63 줄의 스타일링 정보가 포함되어 있고, 모든 셀에는 스타일 정보가 들어있다. 이것은 데스크탑 스프레드시트 애플리케이션에는 합리적인 대안이지만, 네트워크를 통해서까지 이렇게 불필요한 정보를 처리하느라 시간을 허비하고 싶지는 않다. Listing 2는 실제 데이터의 한 행(row)이다.


Listing 2
				<table:table-row table:style-name="ro3">
  <table:table-cell office:value-type="string">
    <text:p>Room of One's Own</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="Default"
  office:value-type="string">
    <text:p>Journal</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce2"
  office:value-type="string">
    <text:p>Hyacinth Blue; Fabrication; Thanksgiving;
    Spilling the Peas</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce5"
  office:value-type="date" office:date-value="2005-05-10">
    <text:p>10 May 2005</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce5"
  office:value-type="date" office:date-value="2005-09-01">
    <text:p>1 Sep 2005</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce5"
  office:value-type="date" office:date-value="2006-10-01">
    <text:p>1 Oct 2006</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce5"
  office:value-type="date" office:date-value="2006-09-01">
    <text:p>1 Sep 2006</text:p>
  </table:table-cell>
  <table:table-cell office:value-type="string">
    <text:p>Hyacinth Blue</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce6"
  office:value-type="currency" office:currency="CAD"
  office:value="50">
    <text:p>50.00 CAD</text:p>
  </table:table-cell>
  <table:table-cell office:value-type="string">
    <text:p>2 Copies of Publication Issue</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce7"
  office:value-type="currency" office:currency="CAD"
  office:value="1.12">
    <text:p>1.12 CAD</text:p>
  </table:table-cell>
  <table:table-cell table:style-name="ce7" />
  <table:table-cell table:number-columns-repeated="244" />
</table:table-row>

데이터를 XHTML에 삽입하는 것은 어떨까? 이 같은 경우, 파싱이나 포맷팅을 할 필요 없이, Listing 3처럼, 직접 디스플레이 할 수 있다.


Listing 3
				<html><body><ul>
    <li><dl>
        <dt>publisher</dt><dd>Room of One's Own</dd>
        <dt>type</dt><dd>Journal</dd>
        <dt>titles</dt><dd><ul>
            <li>Hyacinth Blue</li>
            <li>Fabrication</li>
            <li>Thanksgiving</li>
            <li>Spilling the Peas</li>
        </ul></dd>
        <dt>submitted</dt><dd>2005-05-10</dd>
        <dt>accepted</dt><dd>2005-09-01</dd>
        <dt>published</dt><dd>2006-10-01</dd>
        <dt>payment received</dt><dd>2006-09-01</dd>
        <dt>titles accepted</dt><dd><ul>
            <li>Hyacinth Blue</li>
        </ul></dd>
        <dt>expenses</dt><dd><dl>
            <dt>postage</dt><dd>CAD 1.12</dd>
        </dl></dd>
        <dt>payment</dt><dd><ul>
            <li>CAD 50.00</li>
            <li>2 Copies of Publication Issue</li>
        </ul></dd>
    </dl></li>
    <li><dl>
        <dt>publisher</dt>
            <dd>Surrey International Writers' Competition</dd>
        <dt>type</dt><dd>Contest</dd>
        <dt>titles</dt><dd><ul>
            <li>The Thirteenth Child</li>
        </ul></dd>
        <dt>submitted</dt><dd>2006-08-31</dd>
        <dt>accepted</dt><dd>2006-09-26</dd>
        <dt>published</dt><dd>2006-10-20</dd>
        <dt>payment received</dt><dd>2006-10-20</dd>
        <dt>titles accepted</dt><dd><ul>
            <li>The Thirteenth Child</li>
        </ul></dd>
        <dt>expenses</dt><dd><dl>
            <dt>postage</dt><dd>CAD 1.05</dd>
            <dt>entry fee</dt><dd>CAD 15.00</dd>
        </dl></dd>
        <dt>payment</dt><dd><ul>
            <li>CAD 150.00</li>
            <li>Honorable Mention</li>
            <li>Copy of Anthology</li>
        </ul></dd>
    </dl></li>
    <li><dl>
        <dt>publisher</dt><dd>Word on the Street</dd>
        <dt>type</dt><dd>Invited Reader</dd>
        <dt>event</dt><dd>10 Minutes of readings</dd>
        <dt>event date</dt><dd>2006-09-24</dd>
        <dt>payment</dt><dd><ul>
            <li>T-Shirt</li>
            <li>Lunch</li>
        </ul></dd>
    </dl></li>
    <li><dl>
        <dt>publisher</dt><dd>Paideusis: The Journal of the
            Canadian Philosophy of Education Society</dd>
        <dt>type</dt><dd>Refereed Journal</dd>
        <dt>titles</dt><dd><ul>
            <li>To Carry Over: Metaphor Invents Us (seven poems)</li>
        </ul></dd>
        <dt>submitted</dt><dd>2006-10-13</dd>
        <dt>accepted</dt><dd>2006-10-16</dd>
        <dt>published</dt><dd>Pending</dd>
        <dt>titles accepted</dt><dd>All</dd>
    </dl></li>
</ul></body></html>

이것은 데이터를 HTML로 단순히 매핑한 것이다. 기본 스타일링이 충분하지는 않지만, 크로스 플랫폼(cross-platform) 방식으로, CSS를 사용하면 스타일링 하기가 매우 쉽고, DOM을 사용하여 데이터를 쉽게 조작할 수 있다. 추가된 HTML 코드 때문에 약간 부풀려 보이지만, 그렇게 심한 것은 아니다. 이 예제는 XOXO outline Microformat(참고자료)와 매우 비슷하고, class="outline"을 첫 번째 <ul /> 엘리먼트에 붙인다면, XOXO 아웃라인이 될 것이다. 여기에 커스텀 XML을 사용하여 콘텐트를 추가할 수 있고, 디스크립션 리스트를 <submission/> 엘리먼트로 대체했다. 하지만 이 예제의 경우, 간결함이나 가독성 측면에서는 별로 얻은 것이 없다. 간결함을 원한다면, JavaScript Object Notation (JSON)을 고려해 볼 수 있다.


Listing 4
				

    {   "publisher": "Room of One's Own",
        "type": "Journal",
        "titles": ["Hyacinth Blue", "Fabrication", "Thanksgiving",
            "Spilling the Peas"],
        "titles accepted": ["Hyacinth Blue"],
        "submitted": "2005-05-10",
        "accepted": "2005-09-01",
        "published": "2006-10-01",
        "payment received": "2006-09-01",
        "expenses": [{"postage": "CAD 1.12"}],
        "payment": ["CAD 50.00", "2 Copies of Publication Issue"]},
    {   "publisher": "Surrey International Writers' Competition",
        "type": "Contest",
        "titles": ["The Thirteenth Child"],
        "titles accepted": ["The Thirteenth Child"],
        "submitted": "2006-08-31",
        "accepted": "2006-09-26",
        "published": "2006-10-20",
        "payment received": "2006-10-20",
        "expenses": [{"postage": "CAD 1.05"},
            {"postage": "CAD 15.00"}],
        "payment": ["CAD 150.00", "Honorable Mention",
            "Copy of Anthology"]},
    {   "publisher": "Word on the Street",
        "type": "Invited Reader",
        "event": "10 Minutes of readings",
        "event date": "2006-09-24",
        "payment": ["T-Shirt", "Lunch"]},
    {   "publisher": "Paideusis: The Journal of the Canadian\
             Philosophy of Education Society",
        "type": "Refereed Journal",
        "titles": ["To Carry Over: Metaphor Invents Us \
            (seven poems)"],
        "titles accepted": "All",
        "submitted": "2006-10-13",
        "accepted": "2006-10-16",
        "published": "Pending"}
]

Listing 4를 보면, HTML 인코딩과 모두 같은 내용이지만, 이것은 JavaScript의 서브셋(subset)이기 때문에, JavaScript 객체, 리스트(list), 스트링(string)으로서 직접 데이터에 액세스 할 수 있다. 이 포맷은 단순하고 간결하며, 이전 포맷에 있는 모든 정보를 포함하고 있다. 게다가, 구조도 잘 보존하면서 Semi-structured 데이터에도 잘 맞는다. 이것으로 간결성에 대한 해결책은 얻었지만, 이것보다 더 단순해 질 수 있다. 첫 번째 예제로 다시 돌아가서, 스프레드시트에 데이터를 저장해 보자. 한 스프레드시트에서 다른 스프레드시트로 데이터를 옮기는 방식 중에는 Comma-Separated Value (CSV)가 있다. Listing 5를 보자.


Listing 5
				

"Publisher", "Type", "Titles", "Submitted", "Accepted/Rejected", \
"Published", "Payment Received", "Titles Accepted", "Payment", \
"In Kind", "Postage", "Fees"
"Room of One's Own", "Journal", "Hyacinth Blue; Fabrication; \
Thanksgiving; Spilling the Peas", 10 May 2005, 1 Sep 2005, \
1 Oct 2006, 1 Sep 2006, "Hyacinth Blue", 50.00 CAD, \
"2 Copies of Publication Issue", 1.12 CAD,
"Surrey International Writer's Competition", "Contest", \
"The Thirteenth Child", 31 Aug 2006, 26 Sep 2006, 20 Oct 2006, \
20 Oct 2006, "The Thirteenth Child", 150.00 CAD, "Honorable Mention, \
Copy of Anthology", 1.05 CAD, 15.00 CAD
"Word on the Street, Vancouver", "Invited Speaker", \
"10 Minutes of Readings", , , 24 Sep 2006, , , , "T-Shirt, Lunch", ,
"Paideusis: The Journal of the Canadian Philosophy of Education \
Society", "Refereed Journal", "To Carry Over: Metaphor Invents Us \
(seven poems)", 13 Oct 2006, 16 Oct 2006, "(Pending) Nov 2006", , \
"All", , , "Email",


Listing 5는 어느 정도까지 간결해 질 수 있는지를 보여주는 것 같다. JSON과 CSV의 중대한 차이도 보인다. CSV는 일반적인 기술이고, 결코 표준화 되어 있지 않으며, 오직 ASCII 텍스트만을 취하는 반면, JSON은 명확하게 표준화 되었으며, Unicode의 UTF-8 인코딩 사용에 대해서도 정의를 내린다. 따라서, CSV 예제의 경우 ASCII 범위 이외의 어떤 텍스트도 사용하지 않지만, JSON(그리고 XML)은 어떤 텍스트와도 잘 작동한다.




위로


Atom 연결

Atom Syndication Format은 필자, 발행일 등, Fame not Fortune의 정보와 몇 가지 중복되는 사항들이 있다. 이 모든 데이터를 하나의 피드(feed)에 놓고, XML로 포맷하고, 이와 동시에 표준 애그리게이터를 사용하여 이 내용들이 검색되도록 할 수 있다. 바로 이것이 나의 애플리케이션과 다른 툴들을 구별하는 요소가 된다. Listing 6은 Atom 피드의 모습이다. (간략하게 도입 부문만 소개하고자 한다.)


Listing 6
				

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Fame not Fortune</title>
  <subtitle>Recent submissions</subtitle>
  <link href="http://example.org/famenotfortune"/>
  <updated>2006-12-03T20:37:16Z</updated>
  <author>
    <name>Daniela Elza</name>
  </author>
  <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
  <entry>
    <title>Hyacinth Blue</title>
    <link href="http://example.org/famenotfortune/hyacinthblue"/>
    <id>urn:uuid:68C0BAAB-C955-45F9-BDD6-21A22FC809AF</id>
    <updated>2006-12-01T20:37:16Z</updated>
    <published>2006-10-01T12:12:12Z</published>
    <category term="Journal"/>
    <content type="xhtml">
        <div xmlns="http://www.w3.org/1999/xhtml">
            <dl>
                <dt>publisher</dt><dd>Room of One's Own</dd>
                <dt>titles</dt><dd><ul>
                    <li>Hyacinth Blue</li>
                    <li>Fabrication</li>
                    <li>Thanksgiving</li>
                    <li>Spilling the Peas</li>
                </ul></dd>
                <dt>submitted</dt><dd>2005-05-10</dd>
                <dt>accepted</dt><dd>2005-09-01</dd>
                <dt>payment received</dt><dd>2006-09-01</dd>
                <dt>expenses</dt><dd><dl>
                    <dt>postage</dt><dd>CAD 1.12</dd>
                </dl></dd>
                <dt>payment</dt><dd><ul>
                    <li>CAD 50.00</li>
                    <li>2 Copies of Publication Issue</li>
                </ul></dd>
            </dl>
        </div>
    </content>
  </entry>
</feed>


Listing 6이 낯설지가 않다면, Atom Entry 엘리먼트에 데이터에 필요한 모든 필드들이 없고, 대부분의 엔트리가 <content/> 태그로 래핑된 XHTML 예제이기 때문일 것이다. published 필드와 author를 약간 변경할 수도 있었지만, published 필드의 남용일 뿐이다. 고유의 네임스페이스를 만들어서, 추가 필드로 Atom Entry를 확장할 수 있지만, 어떤 기존 애그리게이터나 피드 리더기도 그 정보를 사용할 수 없기 때문에 의미가 없다. 단순히 Atom 인벨롭으로 XHTML 예제를 래핑하여 애플리케이션에 동기화 지원을 할 수 있지만, 순수하게 데이터 지향 관점에서 본다면 Atom은 어떤 장점도 없다. 애플리케이션의 데이터를 이동할 때, Atom Entry로 래핑하고, Atom Publishing Protocol (참고자료)을 사용하면 도움이 될 것이다. Atom을 사용하고 싶다면, 데이터 포맷 선택에 제한이 생긴다. Atom <content/> 태그에는 세 가지 데이터 유형, 텍스트(JSON 포맷), HTML, XHTML이 포함된다. Atom을 먼저 확장하지 않고는, Atom에 임의의 XML 콘텐트를 삽입할 수 없다. 따라서, Atom 통합을 할 경우 다른 데이터 포맷 결정에도 영향을 줄 수 있기 때문에, 데이터에 동기화를 지원할 것인지의 여부를 먼저 결정해야 한다.




위로


예제

Ajax 웹 사이트나 웹 애플리케이션을 구현할 때 결정해야 할 것들이 많다. 기본적으로, 어떤 데이터 포맷을 사용할 것인지를 결정해야 한다. 나는 이 글을 통해서, 그러한 결정들을 내리는데 참조할 수 있는 몇 가지 제안을 했었다. 앞서 언급한 내용을 다시 반복하자면,

  • 데이터에는 JSON
  • 문서에는 XML (XHTML)
  • 신디케이션에는 Atom (Atom Publishing Protocol 지원)
공유 사이트...

digg Digg
del.icio.us del.icio.us
Slashdot Slashdot

여러 가지 대안들과 선택 원칙들을 설명했다. 물론, 모든 사안들 마다 특성을 갖고 있고, 예외는 어디에나 존재하기 때문에 어떤 것도 단정지을 수는 없다. 이 가이드라인이 나에게는 잘 맞았던 것처럼, 여러분에게도 도움이 되길 바란다. 의견이나 제안 사항이 있는 경우, 언제라도 환영한다.

기사의 원문보기



참고자료

교육

제품 및 기술 얻기

토론


필자소개

Photo of Dethe Elza

Dethe Elza는 http://livingcode.org/에서 적극적으로 활동하고 있다. 제안이나 권고 사항은 delza@livingcode.org로 보내기 바란다.


Photo of David Mertz

http://gnosis.cx/publish/를 방문하면 David Mertz(mertz@gnosis.cx)의 열정의 일면을 볼 수 있다. 어떤 제안이나 권고도 환영한다. http//gnosis.cx/TPiP/에서 그의 저서 "Text Processing in Python"을 만나볼 수 있다.



출처 : IBM developerworks
:
Posted by 뽀기
2007. 1. 29. 15:43

Java 5의 새로운 기능 그거/Java2007. 1. 29. 15:43

관련글
http://kwon37xi.egloos.com/2510988
http://java.sun.com/j2se/1.5.0/docs/relnotes/features.html

by Jess Garms and Tim Hanson
2005/12/06

개요

Java 5.0은 이미 나와 있고, 많은 사용자들이 이번 릴리스의 JDK에 추가된 새로운 기능을 사용하기 시작할 것입니다. 이제 향상된 for loop 기능에서부터 generics와 같은 좀 더 복잡한 기능에 이르는 모든 기능이 코드 작성에 사용될 것입니다. 우리는 대규모의 Java 5.0 기반 작업을 방금 완료했으며, 이 문서에서는 이러한 여러 기능을 사용한 경험에 대해 이야기하고자 합니다. 이 문서에서는 단순한 소개를 넘어 기능에 대해 좀 더 깊이 있게 살펴보고 이 기능들이 실제 프로젝트에 미칠 영향과 이러한 기능들을 효과적으로 사용하기 위한 몇 가지 팁에 대해 알아보도록 하겠습니다.

소개

JDK 1.5의 베타 시험 기간 동안 우리는 BEA의 Java IDE용 Java 5 컴파일러를 사용해 보았습니다. 우리가 다양한 새 기능을 구현했던 것처럼 다른 사람들도 이 기능들을 새로운 방식으로 이용하기 시작했습니다. 일부는 훌륭하게 사용하였고 일부는 그렇지 못 했습니다. 컴파일러 자체가 새로운 기능을 사용하므로 우리는 이러한 기능을 사용하여 코드를 유지 관리하는 직접적인 경험을 얻을 수 있었습니다. 이 문서에서 이러한 여러 가지 기능과 이에 관련된 경험을 살펴보겠습니다.

이미 새로운 기능에 익숙하리라 생각되므로 각 기능에 대한 포괄적인 소개 대신, 흥미롭고 명확하게 표현되지 않은 함축적 내용 및 사용에 대해 살펴보겠습니다. 이러한 팁은 실행해 본 것 중에서 임의로 선택하여 언어 기능별로 대략적으로 분류한 것입니다.

가장 단순한 기능부터 시작해서 가장 고급 기능의 순서로 다룰 계획이며 Generics는 특히 이야기거리가 많은 주제이므로 이 문서의 반 정도를 할애할 것입니다.


Enhanced for Loop

향상된 새로운 for 루프는 컬렉션 및 배열에서 반복적으로 사용하기 위한 간단하면서도 일관된 구문을 제공합니다. 몇 가지 항목을 소개하면 다음과 같습니다.

Init 표현식

초기화 표현식은 루프 내에서 한 번만 값을 구하게 됩니다. 이것은 변수 선언을 삭제할 수 있음을 의미합니다. 이 예제에서 우리는 각 메서드가 루프를 통과할 때 해당 메서드의 값을 다시 구하지 않도록 하기 위해 Integer 배열을 만들어 computeNumbers()의 결과를 유지하도록 했습니다. 아래쪽 코드가 위쪽 코드에 비해 좀 더 명확하고 numbers 변수의 손실이 없습니다.

향상된 For가 없는 경우:

int sum = 0;
Integer[] numbers = computeNumbers();
for (int i=0; i < numbers.length ; i++)
    sum += numbers[i];

있는 경우:

int sum = 0;
for ( int number: computeNumbers() )
    sum += number;

제한

반복하는 동안 반복자 또는 인덱스에 액세스해야 하는 경우가 있습니다. 언뜻 보기에는 향상된 for 루프에서 이것을 허용되는 것처럼 보이지만 사실은 그렇지 않습니다. 다음 예제를 보겠습니다.

for (int i=0; i < numbers.length ; i++) {
    if (i != 0) System.out.print(",");
    System.out.print(numbers[i]);
}

배열에서 쉼표로 구분된 값 리스트을 출력하려고 합니다. 쉼표를 인쇄해야 할지 알아보려면 현재 첫 번째 항목에 있는지 여부를 알아야 합니다. 그러나 향상된 for를 사용하여 이런 정보를 얻을 방법이 없습니다. 인덱스를 유지하거나 첫 번째 항목을 이미 지나쳤는지 알려주는 boolean을 유지해야 합니다.

다음은 다른 예제입니다.

for (Iterator<integer> it = n.iterator() ; it.hasNext() ; )
    if (it.next() < 0)
        it.remove();

이 경우에는 Integers 컬렉션에서 음수 항목을 삭제하려고 합니다. 그렇게 하려면 반복자에서 메서드를 호출해야 하지만 향상된 for 루프를 사용할 경우 이러한 반복자는 숨겨집니다. 그러므로 Java 5 이전의 반복 메서드를 사용해야 합니다.

어쨌든 주의할 점은 Iterator는 Generic이므로 선언은 Iterator<Integer>이라는 것입니다. 많은 사람들은 이 사실을 놓치고 Iterator를 원시 형태로 사용합니다.


주석

주석 처리는 광범위한 주제입니다. 이 문서는 핵심적인 언어 기능에 대해 제한적으로 다루기 때문에 주석의 모든 가능성과 위험 요인에 대해서는 설명하지 않습니다.

그러나 내장된 주석(SuppressWarnings, Deprecated 및 Override) 및 일반적인 주석 처리의 제한 사항에 대해서 설명하도록 하겠습니다.

Suppress Warnings

이 주석은 클래스 또는 메서드 수준에서 컴파일러 경고 표시를 끕니다. 코드에서 더 이상 사용하지 않는(Deprecated) 메서드를 사용해야 하거나 정적으로는 typesafe인지 확인할 수 없지만 사실은 typesafe인 액션을 수행해야 한다는 것을 사용자가 컴파일러 보다 더 잘 알 수도 있습니다.

@SuppressWarnings("deprecation")
public static void selfDestruct() {
    Thread.currentThread().stop();
}

이것은 아마 가장 유용한 내장된 주석일 것입니다. 그러나 불행히도 javac는 1.5.0_04에서 이것을 지원하지 않습니다. 하지만 1.6에서는 지원되며, Sun은 이것을 1.5로 백포팅하는 중입니다.

이 주석은 Eclipse 3.1 및 다른 IDE에서도 지원됩니다. 이 주석을 사용하여 코드에 대한 경고가 발생하지 않도록 할 수 있습니다. 컴파일 시 경고가 표시된 경우, 이미 이 코드를 추가했으므로 다른 잘못된 코드가 있음을 알 수 있습니다. Generics를 추가하면 훨씬 더 좋습니다.

Deprecated

Deprecated는 아쉽게도 그다지 유용하지는 않습니다. 이것은 원래 @deprecated javadoc 태그를 대체하기 위한 것이지만 필드를 가지고 있지 않기 때문에 deprecated 클래스 또는 메서드 사용자에게 대안으로 사용하라고 알려줄 방법이 없습니다. 대부분 사용자는 javadoc 태그와 이 주석을 모두 필요로 합니다.

Override

Override는 이 주석이 달린 메서드가 슈퍼클래스에서 동일한 서명을 가진 메서드를 무시한다는 것을 나타냅니다.

@Override
public int hashCode() {
    ...
}

위의 경우를 예로 들면, hashCode에서 "C" 를 대문자로 쓰지 않은 경우 컴파일 시에는 오류가 표시되지 않지만 런타임 시에는 메서드가 예상했던 대로 호출되지 않습니다. Override 태그를 추가하면 실제로 override가 수행되지 않을 경우 컴파일러가 문제를 표시합니다.

또한 슈퍼클래스를 변경하는 경우 이것이 도움이 됩니다. 말하자면 이 메서드에 새 파라미터를 추가하고 메서드 자체의 이름을 바꾸면 하위 클래스가 슈퍼 클래스에서 더 이상 아무것도 무시하지 않기 때문에 컴파일에 실패하게 됩니다.

주석의 기타 정보

주석은 다른 상황에서 매우 유용할 수 있습니다. 주석은 동작을 직접 수정하지 않고 강화시킨 경우, 특히 보일러플레이트 코드를 추가하는 경우 EJB 및 웹 서비스 같은 프레임워크에서 가장 잘 작동합니다.

주석은 전처리기로 사용할 수 없습니다. 특히 Sun의 설계는 주석 때문에 클래스의 바이트 코드를 직접 수정하지 못하도록 했습니다. 이로써 언어의 결과를 제대로 이해할 수 있고 IDE같은 툴이 코드 심층 분석 및 refactoring 같은 기능을 수행할 수 있습니다.

주석은 완전한 해결책이 아닙니다. 처음 주석이 나타나면 사람들은 온갖 종류의 기술을 시도해 보려고 합니다. 다음을 살펴보겠습니다.

public class Foo {

    @Property
    private int bar;

}

여기서는 전용 필드 bar에 대해 getter 및 setter 메서드를 자동으로 생성하려고 합니다. 하지만 불행히도 이 생각은 다음과 같은 두 가지 이유에서 좋지 못합니다. 1) 이것은 작동하지 않으며, 2) 이 코드를 읽고 처리하기가 더욱 어려워집니다.

앞서 말한 바와 같이 Sun이 특별히 주석이 표시되는 클래스를 수정하지 못하도록 했기 때문에 이것은 실현될 수 없습니다.

가능하더라도 이 코드를 이해하기 더욱 어렵게 만들기 때문에 좋은 생각이 아닙니다. 이 코드를 처음 본 사람은 이 주석이 메서드를 생성한다는 생각을 하지 못할 것입니다. 또한 향후 이러한 메서드 중 하나에서 무언가를 해야 한다면 이 주석은 쓸모가 없습니다.

요약하자면 정규 코드로 할 수 있는 것에 주석을 사용하려고 하지 말라는 것입니다.


Enumerations

Enum은 수 년간 enum 값으로 사용되어온 public static final int 선언과 많이 유사합니다. int에서 가장 크고 확실하게 개선된 점은 type safe입니다. int와는 달리 enum 타입 중 한 가지를 다른 타입의 위치에 사용할 수 없습니다. 왜냐하면 컴파일러에게는 모든 것이 똑같아 보이기 때문입니다. 아주 드물게 예외가 있긴 하지만 이 경우에도 enum과 유사한 모든 int 구성을 enum 인스턴스로 교체해야 합니다.

Enum은 여러 가지 추가 기능을 제공합니다. 유틸리티 클래스인 EnumMap 및 EnumSet은 특히 enum에 최적화된 표준 컬렉션 구현입니다. 컬렉션에 enum만 포함되는 것을 알고 있다면 HashMap 또는 HashSet 대신 이러한 특정 컬렉션을 사용해야 합니다.

대부분의 경우 코드에서 모든 public static final ints를 enums으로 교체할 수 있습니다. enum은 비슷(Comparable)하고, 내부 클래스(또는 내부 enum)일지라도 이에 대한 참조가 똑같아 보이므로 정적으로 가져올 수 있습니다. enum을 비교할 때 선언되는 순서가 서수 값을 나타낸다는 사실에 유의하십시오.

"Hidden" 정적 메서드

작성한 모든 enum 선언에는 두 가지 정적 메서드가 표시됩니다. 이 두 가지 메서드는 Enum 자체가 아니라 enum 하위 클래스에 대한 정적 메서드이기 때문에 java.lang.Enum에 대한 javadoc에는 표시되지 않습니다.

첫 번째 values()는 enum에 가능한 모든 값의 배열을 리턴합니다.

두 번째 valueOf()는 제공된 문자열에 대한 enum을 리턴하는데, 원본 코드 선언과 똑같이 일치해야 합니다.

메서드

enums의 좋은 점 중의 하나는 메서드를 가질 수 있다는 것입니다. 과거에는 데이터베이스 타입을 JDBC URL로 번역하기 위해 public static final int에서 스위치를 수행한 코드가 필요했을 수 있습니다. 이제는 enum 자체에 직접 코드를 정리할 수 있는 메서드를 가질 수 있습니다. 다음은 DatabaseType enum에서 추상 메서드와 각 enum 인스턴스에 제공된 구현을 사용하여 이것을 어떻게 수행하는 지 보여주는 예제입니다.

public enum DatabaseType {
    ORACLE {
        public String getJdbcUrl() {...}
    },
    MYSQL {
        public String getJdbcUrl() {...}
    };
    public abstract String getJdbcUrl();
}

이제 enum에서 유틸리티 메서드를 직접 제공할 수 있습니다. 예를 들면 다음과 같습니다.

DatabaseType dbType = ...;
String jdbcURL = dbType.getJdbcUrl();

이전에는 URL을 얻기 위해 유틸리티 메서드가 있는 장소를 알려주어야 했습니다.


Varargs

varargs를 올바로 사용하면 정말로 거추장스러운 코드를 일부 정리할 수 있습니다. 다음과 같은 기본 예제는 String 인수를 다양하게 가진 로그 메서드입니다.

Log.log(String code)
Log.log(String code, String arg)
Log.log(String code, String arg1, String arg2)
Log.log(String code, String[] args)

varargs에 관한 설명에서 흥미로운 사실은 처음 4개의 예제를 새로운 vararged 예제로 교체하는 경우 얻어지는 호환성입니다.

Log.log(String code, String... args)

모든 varargs는 소스 호환적이어서 log() 메서드의 모든 호출자를 재컴파일하는 경우 4개의 모든 메서드를 바로 교체할 수 있습니다. 그러나 이전 버전과의 이진 호환성이 필요한 경우 처음 세 개는 그대로 두어야 합니다. 마지막 메서드에서만 Strings 배열을 가져오면 같은 값이 되므로 vararged 버전을 교체할 수 있습니다.

Casting

항목은 String이고 두 번째는 Exception일 것으로 예상할 수 있습니다.

Log.log(Object... objects) {
    String message = (String)objects[0];
    if (objects.length > 1) {
        Exception e = (Exception)objects[1];
        // Do something with the exception
    }
}

그 대신 메서드 서명은 vararg 파라미터에서 별도로 선언된 String 및 Exception 을 사용하여 다음과 같아야 합니다.

Log.log(String message, Exception e, Object... objects) {...}

지나치게 사용하려고 하지 마십시오. varargs를 사용하여 타입 시스템을 망가뜨릴 수 있습니다. 강력한 타입 지정(strong typing) 이 필요한 경우 사용하십시오. PrintStream.printf()은 이러한 규칙에 대한 예외입니다. 이것은 타입 정보를 나중에도 수용할 수 있도록 첫 번째 인수로 제공합니다.


Covariant Returns

공변(covariant) 리턴은 주로 구현의 리턴 타입이 API보다 일반적이지 않은 경우 캐스트를 방지하기 위해 사용됩니다. 다음 예제에서는 Animal 객체를 리턴하는 Zoo 인터페이스를 사용합니다. 이 구현은 AnimalImpl 객체를 리턴합니다. 그러나 JDK 1.5 이전에서는 Animal 객체를 리턴하도록 선언해야 했습니다.

public interface Zoo {
    public Animal getAnimal();
}

public class ZooImpl implements Zoo {
    public Animal getAnimal(){
        return new AnimalImpl();
    }
}

공변 리턴을 사용하면 다음 세 가지 anti-pattern을 바꿀 수 있습니다.

  1. 직접 필드에 액세스합니다. API 제한을 피하기 위해 일부 구현은 하위 클래스를 직접 필드로 제공합니다.
    ZooImpl._animal
  2. 추가 양식이 호출자에서 다운캐스트를 수행하므로 이 구현이 실제로 이러한 특정 하위 클래스임을 알 수 있습니다.
    ((AnimalImpl)ZooImpl.getAnimal()).implMethod();
  3. 마지막 양식은 완전히 다른 서명을 제안함으로써 이러한 문제를 피해가는 특수 메서드입니다.
    ZooImpl._getAnimal();

이러한 패턴은 모두 문제점과 제한이 있습니다. 보기에 안 좋거나 아니면 필요하지도 않은 구현 세부 정보를 제공합니다.

With covariance

공변(covariant) 리턴 패턴은 유지 관리가 더욱 간편하고 안전하고 편리합니다. 캐스트 또는 특수 메서드나 필드가 필요하지 않습니다.

public AnimalImpl getAnimal(){
    return new AnimalImpl();
}

결과 사용:

ZooImpl.getAnimal().implMethod();

Generics 사용

Generics 사용 및 Generics 구성이라는 두 가지 각도에서 Generics를 살펴보겠습니다. List, Set 및 Map의 사용에 관해서는 이야기하지 않겠습니다. 지금은 Generic 컬렉션에 대해서만 언급하도록 하겠습니다.

Generic 메서드 사용 및 컴파일러가 타입을 추론하는 방법에 대해 다룰 것입니다. 일반적으로는 이 정도의 내용만으로도 충분할 것입니다. 그러나 오류 메시지의 뜻을 이해할 수 있도록 문제의 해결 방법도 알아야 합니다.

Generic 메서드

generic 타입 이외에 Java 5에서는 generic 메서드를 도입했습니다. java.util.Collections의 예제에서는 싱글톤(singleton) 리스트을 구성합니다. 새로운 List의 엘리먼트 타입은 메서드에 전달된 객체의 타입에 기반하여 추론됩니다.

static <T> List<T> Collections.singletonList(T o)

이용 예제:

public List<Integer> getListOfOne() {
    return Collections.singletonList(1);
}

위 이용 예제에 int를 전달하면 메서드의 리턴 타입이 List가 됩니다. 컴파일러는 T에 대한 Integer를 추론합니다. 일반적으로 인수 타입를 명시적으로 지정해야 할 필요가 없기 때문에 이것은 Generic 타입과 다릅니다.

또한 이것은 autoboxing과 generics의 상호 작용도 보여줍니다. 인수 타입은 레퍼런스 타입이어야 합니다. 그것이 바로 List<Integer>가 아니라 List<int>를 얻게 되는 이유입니다.

파라미터가 없는 Generic 메서드

emptyList() 메서드는 java.util.Collections의 EMPTY_LIST 필드에 대한 type safe 대안으로서 Generics과 함께 도입되었습니다.

static <T> List<T> Collections.emptyList()

이용 예제:

public List<Integer> getNoIntegers() {
    return Collections.emptyList();
}

이전 예제와 달리 이것은 파라미터가 없으므로 T에 대한 타입을 컴파일러에서 어떻게 추론해야 할까요? 기본적으로 파라미터를 사용하여 한 번만 시도합니다. 실행된 것이 없을 경우 리턴 또는 할당 타입을 사용하여 다시 시도합니다. 이 경우 List<Integer>를 리턴하므로 T는 Integer로 추론됩니다.

리턴 문 또는 할당 문 이외의 위치에서 generic 메서드를 호출하면 어떻게 될까요? 그러면 컴파일러는 타입 추론의 두 번째 단계를 수행할 수 없습니다. 다음 예제에서 emptyList()가 조건부 연산자 내에서 호출됩니다.

public List<Integer> getNoIntegers() {
    return x ? Collections.emptyList() : null;
}

컴파일러는 리턴 컨텍스트를 볼 수 없고 T를 추론할 수 없으므로 포기하고 Object로 가정합니다. "cannot convert List<Object> to List<Integer>." 같은 오류 메시지가 표시됩니다.

이것을 수정하려면 메서드 호출에 인수 타입를 명시적으로 전달해야 합니다. 그러면 컴파일러는 인수 타입를 추론하려고 시도하지 않으므로 올바른 결과를 얻을 수 있습니다.

return x ? Collections.<Integer>emptyList() : null;

이런 상황이 자주 발생하는 또 다른 위치는 메서드 호출입니다. 메서드에 List을 사용하고 해당 파라미터에 대한 emptyList()를 전달하여 이것을 호출하려고 시도하는 경우에도 이 구문을 사용해야 합니다.

그 밖의 컬렉션

다음은 컬렉션이 아니라 새로운 방식으로 generics를 사용하는 세 가지 Generic 타입 예제입니다. 이 예제는 모두 표준 Java 라이브러리에 있는 것입니다.

  • Class<T>
    Class는 클래스의 타입에 파라미터화됩니다. 그러면 캐스팅 없이 newInstance를 구성할 수 있게 됩니다.

  • Comparable<T>
    Comparable 은 실제 비교 타입별로 파라미터화됩니다. 이것은 compareTo() 호출에 더욱 강력한 타입 지정을 제공합니다. 예를 들어, String은 Comparable<String>을 구현합니다. String 이외에서 compareTo()를 호출하면 컴파일 시 실패합니다.

  • Enum은 enum 타입별로 파라미터화됩니다. Color라는 enum은 Enum<Color>를 확장합니다. getDeclaringClass() 메서드는 enum 타입에 대한 클래스 객체를 리턴하는데, 이 경우에는 Color가 리턴됩니다. 이것은 익명의 클래스를 리턴할 수 있는 getClass()와는 다릅니다.

와일드카드

generics에서 가장 난해한 부분은 와일드카드를 이해하는 것입니다. 여기서는 세 가지 와일드카드의 타입 및 사용 목적에 대해 설명합니다.

먼저 배열 사용 방법을 살펴보겠습니다. Integer[]에서 Number[]를 할당할 수 있습니다. Float를 Number[]에 쓰려고 시도하면 컴파일은 되지만 런타임 시 ArrayStoreException에서 실패하게 됩니다.

Integer[] ia = new Integer[5];
Number[] na = ia;
na[0] = 0.5; // compiles, but fails at runtime

이 예제를 직접 generics으로 번역하려고 시도하면 할당이 허용되지 않기 때문에 컴파일 시 실패합니다.

List<Integer> iList = new ArrayList<Integer>();
List<Number> nList = iList; // not allowed
nList.add(0.5);

Generics을 사용하는 경우 경고 없이 컴파일하는 코드가 있으면 런타임 ClassCastException을 절대로 얻을 수 없습니다.

상한선 와일드카드

배열와 달리, 여기서 필요한 것은 정확한 엘리먼트 타입이 알려지지 않은 리스트입니다.

List<Number>는 엘리먼트 타입이 명확한 Number인 리스트입니다.

List<? extends Number>는 정확한 엘리먼트 타입이 알려지지 않은 리스트입니다. 이것은 Number 또는 하위 타입입니다.

상한선

원래 예제를 업데이트하고 List<? extends Number>에 할당하면 이 할당은 성공합니다.

List<Integer> iList = new ArrayList<Integer>();
List<? extends Number> nList = iList;
Number n = nList.get(0);
nList.add(0.5); // Not allowed

리스트의 정확한 엘리먼트 타입(Float, Integer 또는 Number)에 상관없이 Number를 할당할 수 있기 때문에 이 리스트에서 Number를 얻을 수 있습니다.

floats를 리스트에 삽입할 수는 없습니다. 그럴 경우 안전하다는 것을 입증할 수 없기 때문에 컴파일 시 실패합니다. 리스트에 float를 추가하면 Integer만 저장하는 iList의 원래 type safety를 위반하게 됩니다.

와일드카드는 배열을 사용하는 것보다 강한 표현력을 제공합니다.

와일드카드 사용 이유

다음 예제에서 와일드카드는 API의 사용자에게 타입 정보를 숨기는 데 사용됩니다. 내부적으로 Set은 CustomerImpl로 저장됩니다. API 사용자가 알고있는 전부는 Customers를 읽을 수 있는 Set을 얻게 될 것이라는 사실입니다.

여기서는 Set<CustomerImpl>에서 Set<Customer>로 할당할 수 없기 때문에 와일드카드가 필요합니다.

public class CustomerFactory {

    private Set<CustomerImpl> _customers;

    public Set<? extends Customer> getCustomers() {
        return _customers;
    }

}

와일드카드 및 공변(covariant) 리턴

와일드카드를 일상적으로 사용하는 또 다른 경우는 공변(covariant) 리턴입니다. 동일한 규칙이 할당되어 공변 리턴에 적용됩니다. overridden 메서드에서 더 특정한 generic 타입을 리턴하도록 하려면 선언하는 메서드에 반드시 와일드카드를 사용해야 합니다.

public interface NumberGenerator {
    public List<? extends Number> generate();
}

public class FibonacciGenerator extends NumberGenerator {
    public List<Integer> generate() {
        ...
    }
}

여기서 배열을 사용하면 인터페이스는 Number[]를 리턴하고 구현은 Integer[]를 리턴할 수 있습니다.

하한선

상한선 와일드카드에 대해 이미 설명했습니다. 하한선 와일드카드도 있습니다. List<? super Number>는 정확한 "엘리먼트 타입"이 알려지지 않은 리스트이지만 이것은 MNumber이거나 또는 Number의 수퍼 타입입니다. 그러므로 이것은 List<Number> 또는 List<Object>일 수 있습니다.

하한선 와일드카드는 상한선 와일드카드 만큼 일반적이지 않지만 필요한 경우 반드시 사용되어야 합니다.

하한선과 상한선 비교

List<? extends Number> readList = new ArrayList<Integer>();
Number n = readList.get(0);

List<? super Number> writeList = new ArrayList<Object>();
writeList.add(new Integer(5));

첫 번째 리스트은 숫자를 읽을 수 있는 리스트입니다.

두 번째 리스트은 숫자를 쓸 수 있는 리스트입니다.

제한 없는 와일드카드

마지막으로, List<?>는 모든 것의 리스트이며 List와 거의 동일합니다. 항상 Objects를 읽을 수 있지만 리스트에 쓸 수는 없습니다.

공용 API의 와일드카드

요약하자면 와일드카드는 몇 단원 전에 살펴본 바와 같이 호출자에게 구현 세부 정보를 숨기는 데는 아주 유용합니다. 그러나 읽기 전용 액세스를 제공하기 위해 하한선 와일드카드가 표시되더라도 remove(int position)같은 비-generic 메서드로 인해 그렇게 하지 못합니다. 정말로 불변하는 컬렉션을 원한다면 unmodifiableList()처럼 java.util.Collections에서 이 메서드를 사용하십시오.

API를 작성할 경우 와일드카드를 알아야 합니다. 일반적으로 generic 타입을 전달할 때 와일드카드를 사용해야 합니다. 그러면 API가 더 광범위한 호출자에게 액세스할 수 있습니다.

다음 예제는 List<Number> 대신 List<? extends Number>를 사용하여 아래의 메서드가 여러 가지 다양한 타입의 Lists를 사용하여 호출되도록 합니다.

void removeNegatives(List<? extends Number> list);

Generic 타입 구성

이제 generic 타입 구성에 대해 살펴보겠습니다. generic 타입 구현 시 발생하는 일반적인 문제 뿐만 아니라 generic을 사용하여 type safety를 향상시킬 수 있는 예제를 보여줄 것입니다.

컬렉션과 유사한 함수

generic 클래스의 첫 번째 예제는 컬렉션과 유사한 예제입니다. Pair는 두 개의 파라미터를 가지며 필드는 타입의 인스턴스입니다.

public final class Pair<A,B> {
    public final A first;
    public final B second;

    public Pair(A first, B second) {
        this.first = first;
        this.second = second;
    }
}

이것은 두 가지 타입으로 된 각각의 콤보에 대해 특수 목적의 클래스를 작성하지 않고 메서드에서 두 개의 항목을 리턴하도록 합니다. Object[]를 리턴하는 것도 할 수 있지만 그것은 타입이 안전하지 않거나 깔끔하지 않습니다.

아래 이용 예제는 메서드에서 File 및 Boolean을 리턴합니다. 메서드의 클라이언트는 캐스팅하지 않고 직접 이 필드를 사용할 수 있습니다.

public Pair<File,Boolean> getFileAndWriteStatus(String path){
    // create file and status
    return new Pair<File,Boolean>(file, status);
}

Pair<File,Boolean> result = getFileAndWriteStatus("...");
File f = result.first;
boolean writeable = result.second;

그 밖의 컬렉션

다음 예제에서 generic은 추가 컴파일 시 안전을 위해 사용되었습니다. 생성되는 Peer 타입별로 DBFactory 클래스를 파라미터화하면 Factory 하위 클래스가 Peer의 특정 하위 타입을 리턴하도록 할 수 있습니다.

public abstract class DBFactory<T extends DBPeer> {
    protected abstract T createEmptyPeer();

    public List<T> get(String constraint) {
        List<T> peers = new ArrayList<T>();
        // database magic
        return peers;
    }
}

DBFactory<Customer>를 구현하면 CustomerFactory는 createEmptyPeer()에서 Customer를 리턴하게 됩니다.

public class CustomerFactory extends DBFactory<Customer>{

    public Customer createEmptyPeer() {
        return new Customer();
    }
}

Generic 메서드

파라미터 간에 또는 파라미터와 리턴 타입 간의 generic 타입에 대한 제한을 두기 위해 generic 메서드를 사용할 수 있습니다.

예를 들어, 제자리에서 역행하는 리버스 함수를 작성하면 generic 메서드가 필요 없습니다. 그러나 리버스 함수가 새로운 List를 리턴하도록 하려면 새로운 List 의 엘리먼트 타입이 전달된 List와 동일해야 합니다. 그럴 경우 generic 메서드가 필요합니다.

<T> List<T> reverse(List<T> list)

Reification

generic 클래스를 구현할 때 배열 T[]를 구성할 수 없습니다. generics은 데이터 삭제에 의해 구현되기 때문에 배열 T[]를 구성할 수 없습니다.

또한 Object[]를 T[]로 캐스트하려고 시도하지 마십시오. 이것은 안전하지 않습니다.

Reification 솔루션

generics 자습서에 따르면, 이 솔루션은 "Type 토큰"을 사용합니다. 생성자에 Class<T> 파라미터를 추가하면 클라이언트가 클래스의 타입 파라미터에 대해 올바른 클래스 객체를 제공하도록 할 수 있습니다.

public class ArrayExample<T> {
    private Class<T> clazz;

    public ArrayExample(Class<T> clazz) {
        this.clazz = clazz;
    }

    public T[] getArray(int size) {
        return (T[])Array.newInstance(clazz, size);
    }
}

ArrayExample<String>을 구성하려면, String.class의 타입이 Class<String>이기 때문에 클라이언트는 String.class를 생성자에게 전달해야 합니다.

클래스 객체가 있으면 올바른 엘리먼트 타입을 사용하는 배열을 구성할 수 있습니다.

결론

한마디로 요약하면 이 새로운 기능은 Java에 근본적 변화를 가져왔습니다. 이러한 기능을 언제 어떻게 사용하는지를 이해하면 더 나은 코드를 작성할 수 있을 것입니다.

추가자료

Jess Garms은 BEA Systems의 Javelin 컴파일러 팀의 리더입니다. 그 이전에 Jess는 BEA의 Java IDE, WebLogic Workshop에 관여했습니다. 또한 그는 암호화 관련 경험이 상당히 풍부하며 Wrox Press에서 출판한 "Professional Java Security"를 공동 저술하기도 했습니다.

Tim Hanson은 BEA Systems의 Javelin 컴파일러 설계자입니다. Tim은 BEA의 Java 컴파일러(가장 초기1.5 호환 구현 중 하나)를 상당 부분 개발했습니다. 이외에도 그는 CORBA/IDL 컴파일러(IBM 재직 시) 및 XQuery 컴파일러를 비롯한 수많은 컴파일러를 작성했습니다.


Return to dev2dev.

:
Posted by 뽀기