달력

12022  이전 다음

  •  
  •  
  •  
  •  
  •  
  •  
  • 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
  •  
  •  
  •  
  •  
  •  
Core Java Technologies Technical Tips
 Core Java Technologies Tech Tip에 오신 여러분을 환영합니다
Core Java Technologies
TECHNICAL TIPS
2005년 10월 18일자
 이번 호에서는,

» CookieHandler를 이용한 쿠키 관리
» 테크팁 퀴즈 정답

을 다루게 됩니다.



CookieHandler를 이용한 쿠키 관리
 

자바 플랫폼의 경우, URL을 통한 오브젝트 액세스는 일련의 프로토콜 핸들러에 의해 관리된다. URL의 첫 부분은 사용되는 프로토콜을 알려주는데, 예를 들어 URL이 file:로 시작되면 로컬 파일 시스템 상에서 리소스를 액세스할 수 있다. 또, URL이 http:로 시작되면 인터넷을 통해 리소스 액세스가 이루어진다. 한편, J2SE 5.0은 시스템 내에 반드시 존재해야 하는 프로토콜 핸들러(http, https, file, ftp, jar 등)를 정의한다.

J2SE 5.0은 http 프로토콜 핸들러 구현의 일부로 CookieHandler를 추가하는데, 이 클래스는 쿠키를 통해 시스템 내에서 상태(state)가 어떻게 관리될 수 있는지를 보여준다. 쿠키는 브라우저의 캐시에 저장된 데이터의 단편이며, 한번 방문한 웹 사이트를 다시 방문할 경우 쿠키 데이터를 이용하여 재방문자임을 식별한다. 쿠키는 가령 온라인 쇼핑 카트 같은 상태 정보를 기억할 수 있게 해준다. 쿠키에는 브라우저를 종료할 때까지 단일 웹 세션 동안 데이터를 보유하는 단기 쿠키와 1주 또는 1년 동안 데이터를 보유하는 장기 쿠키가 있다.

J2SE 5.0에서 기본값으로 설치되는 핸들러는 없으나, 핸들러를 등록하여 애플리케이션이 쿠키를 기억했다가 http 접속 시에 이를 반송하도록 할 수는 있다.

CookieHandler 클래스는 두 쌍의 관련 메소드를 가지는 추상 클래스이다. 첫 번째 쌍의 메소드는 현재 설치된 핸들러를 찾아내고 각자의 핸들러를 설치할 수 있게 한다.

  • getDefault()
  • setDefault(CookieHandler)

보안 매니저가 설치된 애플리케이션의 경우, 핸들러를 얻고 이를 설정하려면 특별 허가를 받아야 한다. 현재의 핸들러를 제거하려면 핸들러로 null을 입력한다. 또한 앞서 얘기했듯이 기본값으로 설정되어 있는 핸들러는 없다.

두 번째 쌍의 메소드는 각자가 관리하는 쿠키 캐시로부터 쿠키를 얻고 이를 설정할 수 있게 한다.

  • get(URI uri, Map<String, List<String>> requestHeaders)
  • put(URI uri, Map<String, List<String>> responseHeaders)

get() 메소드는 캐시에서 저장된 쿠기를 검색하여 requestHeaders를 추가하고, put() 메소드는 응답 헤더에서 쿠키를 찾아내어 캐시에 저장한다.

여기서 보듯이 핸들러를 작성하는 일은 실제로는 간단하다. 그러나 캐시를 정의하는 데는 약간의 추가 작업이 더 필요하다. 일례로, 커스텀 CookieHandler, 쿠키 캐시, 테스트 프로그램을 사용해 보기로 하자. 테스트 프로그램은 아래와 같은 형태를 띠고 있다.

   import java.io.*;   import java.net.*;   import java.util.*;   public class Fetch {     public static void main(String args[]) throws Exception {       if (args.length == 0) {         System.err.println("URL missing");         System.exit(-1);       }       String urlString = args[0];       CookieHandler.setDefault(new ListCookieHandler());       URL url = new URL(urlString);       URLConnection connection = url.openConnection();       Object obj = connection.getContent();       url = new URL(urlString);       connection = url.openConnection();       obj = connection.getContent();     }   }

먼저 이 프로그램은 간략하게 정의될 ListCookieHandler를 작성하고 설치한다. 그런 다음 URL(명령어 라인에서 입력)의 접속을 열어 내용을 읽는다. 이어서 프로그램은 또 다른 URL의 접속을 열고 동일한 내용을 읽는다. 첫 번째 내용을 읽을 때 응답에는 저장될 쿠키가, 두 번째 요청에는 앞서 저장된 쿠키가 포함된다.

이제 이것을 관리하는 방법에 대해 알아보기로 하자. 처음에는 URLConnection 클래스를 이용한다. 웹 상의 리소스는 URL을 통해 액세스할 수 있으며, URL 작성 후에는 URLConnection 클래스의 도움을 받아 사이트와의 통신을 위한 인풋 또는 아웃풋 스트림을 얻을 수 있다.

   String urlString = ...;   URL url = new URL(urlString);   URLConnection connection = url.openConnection();   InputStream is = connection.getInputStream();   // .. read content from stream

접속으로부터 이용 가능한 정보에는 일련의 헤더들이 포함될 수 있는데, 이는 사용중인 프로토콜에 의해 결정된다. 헤더를 찾으려면 URLConnection 클래스를 사용하면 된다. 한편, 클래스는 헤더 정보 검색을 위한 다양한 메소드를 가지는데, 여기에는 다음 사항들이 포함된다.

  • getHeaderFields() - 가용한 필드의 Map을 얻는다.
  • getHeaderField(String name) - 이름 별로 헤더 필드를 얻는다.
  • getHeaderFieldDate(String name, long default) - 날짜로 된 헤더 필드를 얻는다.
  • getHeaderFieldInt(String name, int default) - 숫자로 된 헤더 필드를 얻는다.
  • getHeaderFieldKey(int n) or getHeaderField(int n) - 위치 별로 헤더 필드를 얻는다.

일례로, 다음 프로그램은 주어진 URL의 모든 헤더를 열거한다

   import java.net.*;   import java.util.*;   public class ListHeaders {     public static void main(String args[]) throws Exception {       if (args.length == 0) {         System.err.println("URL missing");       }       String urlString = args[0];       URL url = new URL(urlString);       URLConnection connection = url.openConnection();       Map<String,List<String>> headerFields =          connection.getHeaderFields();       Set<String> set = headerFields.keySet();       Iterator<String> itor = set.iterator();       while (itor.hasNext()) {         String key = itor.next();         System.out.println("Key: " + key + " / " +            headerFields.get(key));       }     }   }

ListHeaders 프로그램은 가령 http://java.sun.com 같은 URL을 아규먼트로 취하고 사이트로부터 수신한 모든 헤더를 표시한다. 각 헤더는 아래의 형태로 표시된다.

   Key: <key> / [<value>]

따라서 다음을 입력하면,

  >> java ListHeaders http://java.sun.com

다음과 유사한 내용이 표시되어야 한다.

   Key: Set-Cookie / [SUN_ID=192.168.0.1:269421125489956;    EXPIRES=Wednesday, 31- Dec-2025 23:59:59 GMT;    DOMAIN=.sun.com; PATH=/]   Key: Set-cookie /    [JSESSIONID=688047FA45065E07D8792CF650B8F0EA;Path=/]   Key: null / [HTTP/1.1 200 OK]   Key: Transfer-encoding / [chunked]   Key: Date / [Wed, 31 Aug 2005 12:05:56 GMT]   Key: Server / [Sun-ONE-Web-Server/6.1]   Key: Content-type / [text/html;charset=ISO-8859-1]   

(위에 표시된 결과에서 긴 행은 수동으로 줄바꿈한 것임)

이는 해당 URL에 대한 헤더들만을 표시하며, 그곳에 위치한 HTML 페이지는 표시하지 않는다. 표시되는 정보에는 사이트에서 사용하는 웹 서버와 로컬 시스템의 날짜 및 시간이 포함되는 사실에 유의할 것. 아울러 2개의 ‘Set-Cookie’ 행에도 유의해야 한다. 이들은 쿠키와 관련된 헤더들이며, 쿠키는 헤더로부터 저장된 뒤 다음의 요청과 함께 전송될 수 있다.

이제 CookieHandler를 작성해 보자. 이를 위해서는 두 추상 메소드 CookieHandler: get() 과ㅓ put()을 구현해야 한다.

  •   public void put(    URI uri,    Map<String, List<String>> responseHeaders)      throws IOException
  •   public Map<String, List<String>> get(    URI uri,    Map<String, List<String>> requestHeaders)      throws IOException

우선 put() 메소드로 시작한다. 이 경우 응답 헤더에 포함된 모든 쿠키가 캐시에 저장된다.put()을 구현하기 위해서는 먼저 ‘Set-Cookie’ 헤더의 List를 얻어야한다. 이는 Set-cookieSet-Cookie2 같은 다른 해당 헤더로 확장될 수 있다.

   List<String> setCookieList =     responseHeaders.get("Set-Cookie");

쿠키의 리스트를 확보한 후 각 쿠키를 반복(loop)하고 저장한다. 쿠키가 이미 존재할 경우에는 기존의 것을 교체하도록 한다.

    if (setCookieList != null) {      for (String item : setCookieList) {        Cookie cookie = new Cookie(uri, item);        // Remove cookie if it already exists in cache        // New one will replace it        for (Cookie existingCookie : cache) {          ...        }        System.out.println("Adding to cache: " + cookie);        cache.add(cookie);      }    }

여기서 ‘캐시’는 데이터베이스에서 Collections Framework에서 List에 이르기까지 어떤 것이든 될 수 있다. Cookie 클래스는 나중에 정의되는데, 이는 사전 정의되는 클래스에 속하지 않는다.

본질적으로, 그것이 put() 메소드에 대해 주어진 전부이며, 응답 헤더 내의 각 쿠키에 대해 메소드는 쿠키를 캐시에 저장한다.

get() 메소드는 정반대로 작동한다. URI에 해당되는 캐시 내의 각 쿠키에 대해, get() 메소드는 이를 요청 헤더에 추가한다. 복수의 쿠키에 대해서는 콤마로 구분된(comma-delimited) 리스트를 작성한다. get() 메소드는 맵을 반환하며, 따라서 메소드는 기존의 헤더 세트로 Map 아규먼트를 취하게 된다. 그 아규먼트에 캐시 내의 해당 쿠키를 추가해야 하지만 아규먼트는 불변의 맵이며, 또 다른 불변의 맵을 반환해야만 한다. 따라서 기존의 맵을 유효한 카피에 복사한 다음 추가를 마친 후 불변의 맵을 반환해야 한다.

get() 메소드를 구현하기 위해서는 먼저 캐시를 살펴보고 일치하는 쿠키를 얻은 다음 만료된 쿠키를 모두 제거하도록 한다.

    // Retrieve all the cookies for matching URI    // Put in comma-separated list    StringBuilder cookies = new StringBuilder();    for (Cookie cookie : cache) {      // Remove cookies that have expired      if (cookie.hasExpired()) {        cache.remove(cookie);      } else if (cookie.matches(uri)) {        if (cookies.length() > 0) {          cookies.append(", ");        }        cookies.append(cookie.toString());      }    }

이 경우에도 Cookie 클래스는 간략하게 정의되는데, 여기에는 hasExpired()matches() 등 2개의 요청된 메소드가 표시되어 있다. hasExpired() 메소드는 특정 쿠키의 만료 여부를 보고하고, matches() 메소드는 쿠키가 메소드에 패스된 URI에 적합한지 여부를 보고한다.

get() 메소드의 다음 부분은 작성된 StringBuilder 오브젝트를 취하고 그 스트링필드 버전을 수정 불가능한 Map에 put한다(이 경우에는 해당 키 ‘Cookie’를 이용).

    // Map to return    Map<String, List<String>> cookieMap =      new HashMap<String, List<String>>(requestHeaders);    // Convert StringBuilder to List, store in map    if (cookies.length() > 0) {      List<String> list =        Collections.singletonList(cookies.toString());      cookieMap.put("Cookie", list);    }    return Collections.unmodifiableMap(cookieMap);

다음은 런타임의 정보 표시를 위해 println이 일부 추가되어 완성된 CookieHandler 정의이다.

   import java.io.*;   import java.net.*;   import java.util.*;   public class ListCookieHandler extends CookieHandler {     // "Long" term storage for cookies, not serialized so only     // for current JVM instance     private List<Cookie> cache = new LinkedList<Cookie>();     /**      * Saves all applicable cookies present in the response       * headers into cache.      * @param uri URI source of cookies      * @param responseHeaders Immutable map from field names to       * lists of field      *   values representing the response header fields returned      */     public void put(         URI uri,         Map<String, List<String>> responseHeaders)           throws IOException {       System.out.println("Cache: " + cache);       List<String> setCookieList =          responseHeaders.get("Set-Cookie");       if (setCookieList != null) {         for (String item : setCookieList) {           Cookie cookie = new Cookie(uri, item);           // Remove cookie if it already exists           // New one will replace           for (Cookie existingCookie : cache) {             if((cookie.getURI().equals(               existingCookie.getURI())) &&                (cookie.getName().equals(                  existingCookie.getName()))) {              cache.remove(existingCookie);              break;            }          }          System.out.println("Adding to cache: " + cookie);          cache.add(cookie);        }      }    }    /**     * Gets all the applicable cookies from a cookie cache for      * the specified uri in the request header.     *     * @param uri URI to send cookies to in a request     * @param requestHeaders Map from request header field names      * to lists of field values representing the current request      * headers     * @return Immutable map, with field name "Cookie" to a list      * of cookies     */    public Map<String, List<String>> get(        URI uri,        Map<String, List<String>> requestHeaders)          throws IOException {      // Retrieve all the cookies for matching URI      // Put in comma-separated list      StringBuilder cookies = new StringBuilder();      for (Cookie cookie : cache) {        // Remove cookies that have expired        if (cookie.hasExpired()) {          cache.remove(cookie);        } else if (cookie.matches(uri)) {          if (cookies.length() > 0) {            cookies.append(", ");          }          cookies.append(cookie.toString());        }      }      // Map to return      Map<String, List<String>> cookieMap =        new HashMap<String, List<String>>(requestHeaders);      // Convert StringBuilder to List, store in map      if (cookies.length() > 0) {        List<String> list =          Collections.singletonList(cookies.toString());        cookieMap.put("Cookie", list);      }        System.out.println("Cookies: " + cookieMap);    return Collections.unmodifiableMap(cookieMap);    }  }

퍼즐의 마지막 조각은 Cookie 클래스 그 자체이며, 대부분의 정보는 생성자(constructor) 내에 존재한다. 생성자 내의 정보 조각(비트)들을 uri 및 헤더 필드로부터 파싱해야 한다. 만료일에는 하나의 포맷이 사용되어야 하지만 인기 있는 웹 사이트에서는 복수의 포맷이 사용되는 경우를 볼 수 있다. 여기서는 그다지 까다로운 점은 없고, 쿠키 경로, 만료일, 도메인 등과 같은 다양한 정보 조각을 저장하기만 하면 된다.

   public Cookie(URI uri, String header) {     String attributes[] = header.split(";");     String nameValue = attributes[0].trim();     this.uri = uri;     this.name = nameValue.substring(0, nameValue.indexOf('='));     this.value = nameValue.substring(nameValue.indexOf('=')+1);     this.path = "/";     this.domain = uri.getHost();     for (int i=1; i < attributes.length; i++) {       nameValue = attributes[i].trim();       int equals = nameValue.indexOf('=');       if (equals == -1) {         continue;       }       String name = nameValue.substring(0, equals);       String value = nameValue.substring(equals+1);       if (name.equalsIgnoreCase("domain")) {         String uriDomain = uri.getHost();         if (uriDomain.equals(value)) {           this.domain = value;         } else {           if (!value.startsWith(".")) {             value = "." + value;           }           uriDomain =              uriDomain.substring(uriDomain.indexOf('.'));           if (!uriDomain.equals(value)) {             throw new IllegalArgumentException(               "Trying to set foreign cookie");           }           this.domain = value;         }       } else if (name.equalsIgnoreCase("path")) {         this.path = value;       } else if (name.equalsIgnoreCase("expires")) {         try {           this.expires = expiresFormat1.parse(value);         } catch (ParseException e) {           try {             this.expires = expiresFormat2.parse(value);           } catch (ParseException e2) {             throw new IllegalArgumentException(               "Bad date format in header: " + value);           }         }       }     }  }

클래스 내의 다른 메소드들은 단지 저장된 데이터를 반환하거나 만료 여부를 확인한다.

   public boolean hasExpired() {     if (expires == null) {       return false;     }     Date now = new Date();     return now.after(expires);   }   public String toString() {     StringBuilder result = new StringBuilder(name);     result.append("=");     result.append(value);     return result.toString();   }

쿠키가 만료된 경우에는 ‘match’가 표시되면 안 된다.

   public boolean matches(URI uri) {     if (hasExpired()) {       return false;     }     String path = uri.getPath();     if (path == null) {       path = "/";     }      return path.startsWith(this.path);   }

Cookie 스펙이 도메인과 경로 양쪽에 대해 매치를 수행할 것을 요구한다는 점에 유의해야 한다. 단순성을 위해 여기서는 경로 매치만을 확인한다.

아래는 전체 Cookie 클래스의 정의이다.

   import java.net.*;   import java.text.*;   import java.util.*;   public class Cookie {     String name;     String value;     URI uri;     String domain;     Date expires;     String path;     private static DateFormat expiresFormat1         = new SimpleDateFormat("E, dd MMM yyyy k:m:s 'GMT'", Locale.US);     private static DateFormat expiresFormat2        = new SimpleDateFormat("E, dd-MMM-yyyy k:m:s 'GMT'", Local.US);		     /**      * Construct a cookie from the URI and header fields      *      * @param uri URI for cookie      * @param header Set of attributes in header      */     public Cookie(URI uri, String header) {       String attributes[] = header.split(";");       String nameValue = attributes[0].trim();       this.uri = uri;       this.name =          nameValue.substring(0, nameValue.indexOf('='));       this.value =          nameValue.substring(nameValue.indexOf('=')+1);       this.path = "/";       this.domain = uri.getHost();       for (int i=1; i < attributes.length; i++) {         nameValue = attributes[i].trim();         int equals = nameValue.indexOf('=');         if (equals == -1) {           continue;         }         String name = nameValue.substring(0, equals);         String value = nameValue.substring(equals+1);         if (name.equalsIgnoreCase("domain")) {           String uriDomain = uri.getHost();           if (uriDomain.equals(value)) {             this.domain = value;           } else {             if (!value.startsWith(".")) {               value = "." + value;             }             uriDomain = uriDomain.substring(               uriDomain.indexOf('.'));             if (!uriDomain.equals(value)) {               throw new IllegalArgumentException(                 "Trying to set foreign cookie");             }             this.domain = value;           }         } else if (name.equalsIgnoreCase("path")) {           this.path = value;         } else if (name.equalsIgnoreCase("expires")) {           try {             this.expires = expiresFormat1.parse(value);           } catch (ParseException e) {             try {               this.expires = expiresFormat2.parse(value);             } catch (ParseException e2) {               throw new IllegalArgumentException(                 "Bad date format in header: " + value);             }           }         }       }     }     public boolean hasExpired() {       if (expires == null) {         return false;       }       Date now = new Date();       return now.after(expires);     }     public String getName() {       return name;     }     public URI getURI() {       return uri;     }     /**      * Check if cookie isn't expired and if URI matches,      * should cookie be included in response.      *      * @param uri URI to check against      * @return true if match, false otherwise      */     public boolean matches(URI uri) {       if (hasExpired()) {         return false;       }      String path = uri.getPath();       if (path == null) {         path = "/";       }       return path.startsWith(this.path);     }     public String toString() {       StringBuilder result = new StringBuilder(name);       result.append("=");       result.append(value);       return result.toString();     }   }

이제 조각들이 모두 확보되었으므로 앞의 Fetch 예제를 실행할 수 있다.

   >> java Fetch http://java.sun.com   Cookies: {Connection=[keep-alive], Host=[java.sun.com],     User-Agent=[Java/1.5.0_04], GET / HTTP/1.1=[null],     Content-type=[application/x-www-form-urlencoded],     Accept=[text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2]}   Cache: []   Adding to cache: SUN_ID=192.168.0.1:235411125667328   Cookies: {Connection=[keep-alive], Host=[java.sun.com],     User-Agent=[Java/1.5.0_04], GET / HTTP/1.1=[null],     Cookie=[SUN_ID=192.168.0.1:235411125667328],     Content-type=[application/x-www-form-urlencoded],     Accept=[text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2]}   Cache: [SUN_ID=192.168.0.1:235411125667328]

(위에 표시된 결과에서 긴 행은 수동으로 줄바꿈한 것임)

‘Cache’로 시작되는 행은 저장된 캐시를 나타낸다. 저장된 쿠키가 즉시 반환되지 않도록 put() 메소드 전에 get() 메소드가 어떻게 호출되는지에 대해 유의하도록 할 것.

쿠키와 URL 접속을 이용한 작업에 관해 자세히 알고 싶으면 자바 튜토리얼의 Custom Networking trail(영문)을 참조할 것. 이는 J2SE 1.4에 기반을 두고 있으므로 튜토리얼에는 아직 여기서 설명한 CookieHandler에 관한 정보가 실려 있지 않다. Java SE 6 ("Mustang")(영문) 릴리즈에서도 기본 CookieHandler 구현에 관한 내용을 찾아볼 수 있다.

Back to Top

테크팁 퀴즈 정답
 
지난 주 뉴스레터로 발송되었던 테크팁 퀴즈의 정답을 알려드립니다.
참여해주신 모든 분께 감사드리며, 당첨자는 10월 26일 SKDN 홈페이지에서 발표합니다.
  1. 다음 중 "pull"파서는?
    1. Xerces
    2. SSAX
    3. Sun Java Streaming XML Parser (SJSXP)
    4. 모두 아님

    정답 c. SJSXP(Sun Java Streaming XML Parser)
    SJSXP는 StAX(Streaming API for XML) 파서의 고속 구현으로서 일종의 pull 파서이다(이와 대조적으로, Xerces와 SSAX는 SAX 파서임). SJSXP는 파서가 문서 내에서 현재 스캔되는 장소에 sorts 포인터?이를 흔히 커서라고 한다?를 유지하는 pull 메소드를 구현한다. 우리는 SJSXP가 제공하는 2개의 API, 커서와 반복자(iterator) 중 하나를 이용하여 커서가 현재 가리키고 있는 노드를 파서에 대해 요구하면 된다. 커서 API는 XML 정보를 문자열로 반환하는 반면 반복자 API는 별개의 이벤트 오브젝트를 파서에서 읽히는 순서대로 반환한다. SJSXP에 관한 자세한 내용은 2005년 4월 29일자 J2EE 테크팁 Sun Java Streaming XML Parser 소개 참조

  2. 다음의 WSDL 바인딩 style/use 조합 중에서 WS-I 호환이 되는 것은?
    1. RPC/encoded
    2. RPC/literal
    3. Document/encoded
    4. Document/literal

    정답 b와 d
    RPC/literal과 Document/literal은 WS-I 호환이 된다. style/use 조합에 관한 자세한 내용은 2005년 8월 23일자 J2EE 테크팁 JAX-RPC 바인딩 스타일과 사용 속성 참조

  3. 다음 중 JSF(JavaServer Faces) 기술에서 유효한 바인딩 식은?
    1. #{invoice.customerName}
    2. ${invoice.date}
    3. #[customer.status == 'VIP']
    4. 모두 아님

    정답 a. #{invoice.customerName}
    바인딩 식의 신택스는 그 자체가 JavaScript의 오브젝트 액세스 신택스에 기초한 JSP(JavaServer Pages) 2.0 Expression Language에 기반을 두고 있다. JSP에서는 식들이 "${}"로 둘러싸이지만 JSF에서는 "#{}"이 사용된다. 자세한 내용은 2004년 10월 15일자 J2EE 테크팁 JSF의 값 바인딩 표현과 메서드 바인딩 표현의 내용을 참조할 것. Expression Language를 JSP 2.1로 통일하기 위해 JSF 1.2에 대해서 업데이트가 이루어졌다는 점에 유의한다. 새로이 통일된 Expression Language에 관한 자세한 내용을 보려면 테크니컬 아티클 Unified Expression Language(영문)참조.

  4. message-driven 빈에 관한 설명 중 맞는 것은?
    1. message-driven 빈의 인스턴스는 특정 클라이언트의 데이터 또는 대화 상태를 유지하지 않는다.
    2. message-driven 빈의 모든 인스턴스는 서로 동등하므로 컨테이너는 어떠한 message-driven 빈 인스턴스에도 메시지를 할당할 수 있다.
    3. 하나의 message-driven 빈이 복수 클라이언트의 메시지를 처리할 수 있다.
    4. 모두 맞음
    5. 모두 틀림

    정답 d. 모두 맞음
    모두 맞음. MDBs에 관한 자세한 내용은 2005년 5월 18일자 J2EE 테크팁 EJB 2.1로 메시지 구동 빈 이용하기의 내용 참조

  5. 스트링 리스트를 정렬하는 방법은?
    1. TreeList 클래스를 사용한다
    2. TreeMap 생성자에 List를 패스하고 맵을 반복한다
    3. Arrayssort 메소드를 호출한다
    4. Collectionssort 메소드를 호출한다

    정답 d. Collections의 정렬 메소드를 호출한다
    자세한 관련 정보는 2004년 7월 16일자 J2SE 테크팁 리스트를 분류하고 섞기 위한 COLLECTIONS의 메소드 사용하기를 참조한다. Collections Framework에 관한 부분은 이미 테크 팁을 통해 여러 차례에 걸쳐 다룬 바 있다.

  6. 다음 중 Math 클래스로 할 수 없는 연산은?
    1. 특정 수의 기수 10 로그를 계산한다
    2. 특정 String에 대한 해시코드를 생성한다
    3. 유니코드 문자의 세제곱근을 계산한다
    4. 두 점간의 유클리드 거리를 계산한다

    정답 b. 특정 String에 대한 해시코드를 생성한다.
    String의 해시코드 계산은 String 클래스에 의해 이루어지며, 다른 세 연산은 JDK 5.0에 추가된 Math 클래스 메소드를 통해 수행할 수 있다. 유니코드 문자의 세제곱근을 계산하려면 계산에 앞서 문자를 char에서 int로 변환한다. 자세한 내용은 2004년 11월 11일자 J2SE 테크팁 MATH 클래스에서의 새로운 점참조
Back to Top

본 메일은 수신을 동의한 회원님에게만 발송됩니다.
본 메일의 수신을 거부하거나 수신주소를 변경하려면 SKDN@Sun.com으로 문의 주시기 바랍니다.

SKDN(Sun Korea Developers Network)에서 J2EE/J2SE 테크팁 등 다양한 아티클들을 참고하세요.

Copyright 2003-2005 Sun Korea, Ltd. All rights reserved.


Posted by tornado

댓글을 달아 주세요