캠핑과 개발

디자인팀 전체가 뮤지컬 관람을 간다고 했서 예매했던 공연을 한사람의 개인 사정으로 인해 표하나가 남게 되었다. 김효정 대리님께서 표하나가 남으니 함께 가는게 어떻겠냐고 해서 매일 술만 마시고 집에서 티비만 보는것보다가는 이런 기회에 다른 문화생활을 접하게 될 좋은 기회가 될지도 모르고 대학 다닐때 두 번 가 본 뮤지컬이 나름대로 재미도 있었고 꼭 다음에도 가봐야 겠다고 생각했는데 마침 기회가 되어서 기분 좋게 함께 가자고 했다. 작가는 장유정 작가이고 제목은 "형제는 용감했다" 이다.

6시에 마치고 함께 나가기로 했는데 그날따라 여러가지 문제들로 인해 디자인팀은 먼저 출발하고 남아있는 문제들을 마저 처리하고 나 또한 대학로로 바로 가기로 했다. 간단하게 저녁을 먹고 대학로 자유극장에 뮤지컬을 감상한 기분은 기대 이상으로 나에게 큰 감동을 주고 재미를 줬다.

간단한 내용은 안동 종가집을 배경으로 한 아버지의 장례식을 위해 내려온 두 아들이 장례를 치루기 위한 이틀전날과 장례식을 치루기 까지의 날까지의 배경을 두고 어머니의 죽음으로 인해 아버지를 증오하는 두 아들들의 그림을 다루고 있다.  이 두 아들들은 처음에는 사이가 좋지 않았지만 공연 마지막에는 아버지의 진실한 마음을 알고 진실된 마음으로 아버지를 보내드리는 내용이다.

개인적으로 마음에 드는 배우가 한명 있었는데 오로라 역을 맞은 이주원이라는 배우이다.
얼굴도 이쁘시지만 개인적으로 정말 마음에 드는 부분은 목소리가 너무 좋아서 그분이 나오는 부분은 "정말 어떻게 목소리가 저렇게 차분하고 좋지" 하는 생각이 계속 머릿속에 맴돌 정도였다. 이 배우가 나오는 뮤지컬이 어떤거였는지 연극이 어떤거였는지도 집에 오자마자 인터넷을 뒤져 보고 했으니 큰 관심이 아닐 수가 없다.

0123456789101112








공 연 명 뮤지컬 형제는 용감했다
날     짜 2008년 3월 22일 (토) ~6월 8일 (일)
시     간 평일(화-금) 8시
토요일/일요일 4시, 8시 / 3시
장     소 대학로 자유극장
가     격 4만원 균일석
할인정보 PMC회원 상시 20%할인 *프리뷰 기간은 20,000원 *프리뷰 기간: 3/22~4/6
공연문의 02-738-8289

쿠키는 웹 어플리케이션에서 클라이언트의 정보를 임시로 저장하기 위해 많이 사용된다. 또한, 클라이언트의 상태를 유지할 때 사용되는 세션을 구현하기 위해 쿠키를 사용하기도 한다. 쿠키를 사용함으로써 좀더 쉽고 간결한 방법으로 웹 어플리케이션을 구현할 수 있게 되는 경우가 많은데 이를 좀더 편하게 관리하기 위해서는 쿠기를 사용할 수 있는 보조 클래스를 만들어서 사용하게 되면 편리하다.

CookieBox.java

/*
 * @(#)CookieBox.java
 * Copyright (c) 2000~ NowOnPlay.com inc., All rights reserved.
 * Total E-Business Group, http://www.nowonplay.com
 *
 * 최초작성일자 : April 15, 2008 (hmjkor@nowonplay.com)
 * 수정이력 :
 */
package kevin.common.utils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Cookie;

import java.util.HashMap;
import java.util.Map;
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.io.IOException;

/**
 * 쿠기를 보다 관리하기 쉽도록 만든 클래스로서
 * http://javacan.madvirus.net/에 있는 내용을 토대로 작성되었다.
 * @author diem
 *
 */
public class CookieBox {
   
    /* 쿠기를 담기위한 맵 */
    private Map cookieMap = new HashMap();
   
    /**
     * 생성자
     * request로 받은 쿠키값을 Key, value 값으로 매핑한다.
     * @param request
     */
    public CookieBox(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (int i = 0 ; i < cookies.length ; i++) {
                cookieMap.put(cookies[i].getName(), cookies[i]);
            }
        }
    }
   
    /**
     * 쿠키를 저장한다.
     * @param name 이름
     * @param value 값
     * @return
     * @throws IOException
     */
    public static Cookie createCookie(String name, String value)
    throws IOException {
        return new Cookie(name, URLEncoder.encode(value, "euc-kr"));
    }
   
    /**
     * 쿠키를 저장한다.
     * @param name 이름
     * @param value 값
     * @param path 경로
     * @param maxAge 유효시간
     * @return
     * @throws IOException
     */

    public static Cookie createCookie(
            String name, String value, String path, int maxAge)
    throws IOException {
        Cookie cookie = new Cookie(name,
                                URLEncoder.encode(value, "euc-kr"));
        cookie.setPath(path);
        cookie.setMaxAge(maxAge);
        return cookie;
    }
   
    /**
     * 쿠키를 저장한다.
     * @param name 이름
     * @param value 값
     * @param domain 도메인
     * @param path 경로
     * @param maxAge 유효시간
     * @return
     * @throws IOException
     */
    public static Cookie createCookie(
            String name, String value, 
            String domain, String path, int maxAge)
    throws IOException {
        Cookie cookie = new Cookie(name,
                  URLEncoder.encode(value, "euc-kr"));
        cookie.setDomain(domain);
        cookie.setPath(path);
        cookie.setMaxAge(maxAge);
        return cookie;
    }
   
    /**
     * 해당되는 쿠키를 가져온다.
     * @param name
     * @return 쿠키값
     */
    public Cookie getCookie(String name) {
        return (Cookie)cookieMap.get(name);
    }
   
    /**
     * 요청한 쿠키 이름의 값을 가져온다.
     * 가져온 값이 없을 경우 null을 리턴한다.
     * @param name 쿠키 이름
     * @return
     * @throws IOException
     */
    public String getValue(String name) throws IOException {
        Cookie cookie = (Cookie)cookieMap.get(name);
        if (cookie == null) return null;
        return URLDecoder.decode(cookie.getValue(), "euc-kr");
    }
   
    /**
     * 요청한 쿠기값이 있는지의 여부를 가져온다.
     * @param name 요청할 쿠키값
     * @return
     */
    public boolean exists(String name) {
        return cookieMap.get(name) != null;
    }
}

소스를 간단하게 설명을 하면 쿠기를 담는 맵을 생성 한 후  그 맵에 Key, Value 값으로 담아두고 Key에 해당하는 쿠기를 가져오는 클래스이다.

이를 사용하기 위해서는 다음과 같이 사용하면 된다.
// CookieBox 클래스의 생성자는 request로부터 쿠키 정보를 추출
CookieBox cookieBox = new CookieBox(request);

// 쿠키가 존재하지 않으면 null 리턴
Cookie idCookie = cookieBox.getCookie("id");

// 지정한 이름의 쿠키가 존재하는지의 여부
if (cookieBox.exists("name")) { ... }

//지정한 이름의 쿠키가 존재하지 않으면 값으로 null 리턴
 String value = cookieBox.getValue("key");


이글은 최범균님의 홈페이지에서 참고한것 입니다.

오늘 공부한 패턴은 Iterator 패턴이다. Iterator패턴이란 사전적 의미로는 반복자이다.
책에 쓰인 정의로는 컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 수 있게 해주는 방법을 제공해준다라고 되어 있다.

쉬운 예로 게시판을 예로 들어보자. 게시물의 들어가는 모든 내용(제목, 작성자, 내용, 등록을 등.)을 Board라는 객체를 만들어서 한번에 담아서 사용한다고 가장을 한다고 하면 대충 Board.java 안에는 이런 코드가 들어갈 것이다.

Board.java
package hmj.pattern.Iterator;

public class Board {
   
    private String name;
    private String subject;
    private String contents;
    private String registeDate;
   
    public Board(){}
    public Board(String name, String subject, String contents, String registeDate){
        this.name = name;
        this.subject = subject;
        this.contents = contents;
        this.registeDate = registeDate;
    }
   
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSubject() {
        return subject;
    }
    public void setSubject(String subject) {
        this.subject = subject;
    }
    public String getContents() {
        return contents;
    }
    public void setContents(String contents) {
        this.contents = contents;
    }
    public String getRegisteDate() {
        return registeDate;
    }
    public void setRegisteDate(String registeDate) {
        this.registeDate = registeDate;
    }   
}


일반적으로 게시판에 들어가는 내용은 저정도일리 없겠지만 Iterator 패턴을 설명하기에는 부족함이 없을꺼 같다. 다른 두사람이 각기 다른 게시판의 리스트를 불러오는 작업을 했다고 가정을 하자. 

A라는 사람은,
public ArrayList<Board> getList() throws Exception { /* 리스트를 불러오기 위한 로직 */ } 을 사용하고

또 다른 B라는 사람은,
public Board[] getList() throws Exception { /*  리스트를 불러오기 위한 로직 */ } 을 사용하였다.

이런식으로 넘겨 받은 객체들은 View페이지에서 각기 리턴형식이 다르기 때문에 서로 다른 형식으로 루프를 돌면서 객체를 꺼내와야 될것이다. 그안에 들어있는 객체는 똑같이 Board형식인 객체인데 말이다. 그리고 또 C가 나타나서 또 다른 방법으로 리스트 목록을 불러오는 메소드를 만든다면 또 다른 방법으로 그 객체를 가져와야 할것이다. 이런 방법은 정말 불편할것이다. 또한 코드 관리 및 확장도 어려울 것이다.

디자인 원칙중에는 "바뀌는 부분을 캡술화 하라" 라는 디자인 원칙이 있다.
위에 리스트 목록을 불러오기 위해서 각기 서로 다른 방법으로 Board 객체를 불러와서 리스트 페이지에 뿌려준다. 이렇게 서로 다른 방식으로 객체를 가져오는 부분을 캡술화를 할수 있을까?

Iterator iterator = NoticeBoardFacade.createIterator();
while(iterator.hasNext()){
    Board board = (Board)iterator.next();
}

이런식으로 Iterator라는 객체를 만들어서 사용하면 어떨까? 리스트 목록을 Iterator라는 객체에 담아서 Iterator 내부에서 ArrayList이건 Board[] 배열이건 캡술화 해서 가져오게 하면 되지 않을까? 된다.. 이게 바로 Iterator 패턴이다..

모르겠으면 일단 한번 해보면 될것 아닌가? 열번 듣는것보다 한번 소스를 보는게 낫고 직접 타이핑을 해보면 더욱 좋다. 내가 그런게 아니고 다들 그러더라.. ㅡㅡ;
소스 코드를 보기 전에 가장 먼저 알아야 할것은 Iterator라는 인터페이스에 의존한다는 점이다.

Iterator.java
/*
 * @(#)Iterator.java
 * Copyright (c) 2000~ NowOnPlay.com inc., All rights reserved.
 * Total E-Business Group, http://www.nowonplay.com
 *
 * 최초작성일자 : April 14, 2008 (hmjkor@nowonplay.com)
 * 수정이력 :
 */
package hmj.pattern.Iterator.Iterator;

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

일단 이렇게 인터페이를 만들어 두면 그 어떤 종류의 객체 컬렉션도 알맞게 구현하여 반복자를 구현 할 수가 있다.

그럼 Iterator 인터페이스를 구현한 클래스 또한 봐야 이해가 갈것이다. 그럼 보자

NoticeBoardIterator.java
/*
 * @(#)NoticeBoardIterator.java
 * Copyright (c) 2000~ NowOnPlay.com inc., All rights reserved.
 * Total E-Business Group, http://www.nowonplay.com
 *
 * 최초작성일자 : April 14, 2008 (hmjkor@nowonplay.com)
 * 수정이력 :
 */
package hmj.pattern.Iterator.board;

import hmj.pattern.Iterator.Board;
import hmj.pattern.Iterator.Iterator.Iterator;

public class NoticeBoardIterator implements Iterator {
    
    private Board[] boards;
    private int position = 0;
    
    /**
     * 생성자
     * @param boards
     */
    public NoticeBoardIterator(Board[] boards) {
        // TODO Auto-generated constructor stub
        this.boards = boards;
    }
    
    /**
     * 해당 배열에 다음 객체가 있는지 여부를 리턴한다.
     */
    public boolean hasNext() {
        // TODO Auto-generated method stub
        if(position >= boards.length || boards[position] == null){
            return false;
        }else{
            return true;
        }        
    }
    
    /**
     * 해당 포지션에 대한 객체를 가져오고 해당 위치에 해당하는 포지션을 다음 포지션으로
     * 이동 시키기 위해  1 더해준다.
     */
    public Object next() {
        // TODO Auto-generated method stub
        Board board = boards[position];
        position = position + 1;
        return board;
    }
}

코드가 그리 어렵지 않아서 자세하진 않지만 간단한 주석만 봐도 알수 있을것이다.
Iterator 인터페이를 구현하여 hasnext(), next() 메소드를 정의해줬다. hasnext()메소드는 Board[]배열에 현재 위치값에서 다음 값이 있는지를 나타내는 메소드이고, next() 메소드는 현재 포지션의 객체를 리턴하고 다음 객체를 얻기 위해 포지션을 1 증가 시키는 메소드이다.

그럼 NoticeBoardFacade 에서는  public Board[] getList() {  ..코드 ..} 이런식으로 넘겨줬던 코드를 Iterator에 맞게 다시 수정을 해야 할것이다. 다음과 같이 말이다.

public Iterator createIterator() {
    /* boards 객체를 가져오기 위한 코드 */
    return new NoticeBoardIterator(boards);
}

이런식으로 넘겨 받은 객체를 View 페이지에서는 간단하게

Iterator iterator = NoticeBoardFacade.createIterator();
while(iterator.hasNext()){
    Board board = (Board)iterator.next();
    board.getName();
    board.getSubject();
    .
    .
}

위와 같은 식으로 사용하면 된다. 이렇게 되면 사용자들은 내부에서는 어떻게 돌아가는지 알지 못해도 간단하게 사용 할 수가 있다. 이게 바로 Iterator 패턴인다.
내용은 그리 어렵지 않은데 설명은 무척이나 길게 한듯하다..ㅠㅠ
오늘도 패턴 하나를 알았다. 패턴 정말 재미있어진다.