2010년 2월 26일 금요일

[소개] 소프트웨어, 누가 이렇게 개떡 같이 만든거야

저자 : 데이비드 S. 플랫 역자 : 윤성준
출판사 : 인사이트 ISBN : 978-89-91268-39-5
가격 : ₩14,000

[목차]

펼쳐두기..


[가을생각]
올해들어 읽은 책중에 가장 최악의 책이었다. 물론 읽는 사람을 흡입하는 작가의 말초적인 단어선택때문인지 하루밤을 꼬박새우게도 한 책이다. 1장, 2장을 넘기면서 강한 자극을 받고 있었는데 갑자기 다른 길로 새버렸다. 나도 한마디해야 겠다. "책, 누가 이렇게 개떡 같이 적은거야" 작가는 이걸로 인세를 받아먹겠지.. -,.-;;


2010년 2월 24일 수요일

[소개] 구글을 지탱하는 기술

저자 : 나시다 케이스케 역자 : 김성훈
출판사 : 멘토르 ISBN : 978-89-6305-004-1
가격 : ₩18,000

[목차]

펼쳐두기..


[가을생각]
본 책은 평소 검색엔진에 관심이 많은 나에게 가볍게 접근할 수 있는 책이었지만 방향성과 거시적 관점의 견해를 알려주는 좋은 책이었다. 다소 무거운 주제가 있기는 했지만 그렇다고 소스 코드 분석하는 수준은 아니라서 화장실 갈때나 지하철에서 조금 집중하면 쉽게 알 수 있는 내용이었다. 그래서 인지 2틀간 그냥 쉽게 읽혀진 책이었다.

이책의 의미를 굳이 말하라고 한다면 ... 구글은 개발자가 세운 개발자의 왕국이 아닐까...라는 생각을 해본다. 물론 상장된 기업이므로 그와 관련된 많은 일도 하고 있겠지만 개발자의, 개발자를 위한, 개발자에 의한 회사라는 생각이 든다. 국내에는 대주주의, 대주주를 위한, 대주주에 의한 회사가 많을 뿐이지만...

돈을 많이 벌고, 못벌고의 문제는 아니다. 기업의 냄새가 그렇다는 거지... 멋도 모르고 지껄인 하루다..

2010년 2월 23일 화요일

[03] 모든 객체에 공통적인 메소드

$8. equals 메소드를 오버라이딩 할 때는 보편전 계약을 따르자

인스턴스의 동일여부를 판정하는 equals 메소드의 오버라이딩은 간단한 것 같지만, 잘못되는 경우가 많아서 의도한 결과를 초래할 수 없는 경우가 많다. 이때에는 equals 메소드를 오버라이드 하지 말고 상속받은 그대로 사용하는 것이 좋다. 다음 조건 중 하나라도 만족하면 그렇게 하는 것이 좋다.

  • 클래스의 각 인스턴스가 본래부터 유일한 경우
          인스턴스가 갖는 값보다는 활동하는 개체임을 나타내는 것이 더 중요한 스레드와 같은 클래스
          가 여기에 해당한다.
  • 두 인스턴스가 논리적으로 같은지 검사하지 않아도 되는 클래스의 경우
          java,util.Random 클래스에서는 두 개의 Random 인스턴스가 같은 난수열을 만드는지 확인하기
         위해 equals 메소드를 오버라이딩 할 수 도 있겠지만 논리적으로 의미가 없다.
  • 수퍼 클래스에서 equals 메소드를 이미 오버라이딩 했고, 그 메소드를 그대로 사용해도 좋은 경우
          Set 인터페이스를 구현하는 대부분의 클래스들은 AbstractSet에 구현된 equals를 상속받아 사
          용하며, List의 경우는 AbstractList로 부터, Map의 경우는 AbstractMap에서 상속 받아 사용한           다.
  • private이나 패키지 전용 클래스라서 이 클래스의 equals 메소드가 절대 호출되지 않아야 할 경우
          우연히 호출될 수 있는 그런 상황에서는 다음과 같이 equals 메소드를 반드시 오버라이딩 해서
          호출되지 않도록 해야 한다.

          @Override public boolean equals(Object o) {
throw new AssertionError();
          }

그렇다면 Object.equals를 언제 오버라이드 해야 좋을까? 객체 참조만으로 인스턴스의 동일 여부를 판단하는 것이 아니라, 인스턴스가 갖는 값을 비교하여 논리적으로 같은지 판단할 필요가 있는 클래스로써, 자신의 수퍼클래스에서 equals 메소드를 오버라이드하지 않았을 때이다. 일반적으로 값 클래스가 여기에 해당된다. 하지만 위와 같은 경우이더라도 equals 메소드를 오버라이드 하지 않는 케이스가 있다. 바로 반드시 하나의 객체만 존재하도록 인스턴스 제어를 사용하는 클래스이다. 열거형이 이런 부류에 속하는데, 이런 클래스들의 경우는 논리적인 일치와 객체 참조 일치가 동일한 의미가 된다.

equals 메소드를 오버라이드 할 때는 이 메소드의 보편적 계약을 따라야 한다. 다음 내용은 계약의 내용이다.

equals 메소드는 동등 관계를 구현하며, 그것은
  • 재귀적이다
          : null이 아닌 모든 참조 값 x에 대해 x.equals(x)는 반드시 true를 반환해야 한다.
  • 대칭적이다
          : null이 아닌 모든 참조 값 x와 y에 대해, y.equals(x)가 true를 반환한다면 x.equals(y)도 반드             시 true를 반환해야 한다.
  • 이행적이다
          :  null이 아닌 모든 참조 값 x, y, z에 대해, 만일 x.equals(y)가 true를 반환하고 y.equals(z)가              true를 반환한다면 x.equals(z)도 반드시 true를 반환해야 한다.
  • 일관적이다
          : null이 아닌 모든 참조 값 x와 y에 대해, equals 메소드에서 객체 비교 시 사용하는 정보가 변               경되지 않는다면, x.equals(y)를 여러 번 호출하더라도 일관성 있게 true 또는 false를 반환해             야 한다.
  • null이 아닌 모든 참조 값 x에 대해, x.equals(null)은 반드시 false를 반환해야 한다.

상기와 같은 내용을 종합하여 양질의 equals 메소드 만드는 방법은 다음과 같다.

1. 객체의 값을 비교할 필요 없고 참조만으로 같은 객체인지 비교 가능하다면 == 연산자를 사용하자.

2. instanceof 연산자를 사용해서 전달된 인자가 올바른 타입인지 확인하자.

3. 인자 타입을 올바른 타입으로 변환한다.

4. 클래스의 "중요한(꼭 비교해야 하는)" 필드 각각에 대해서는 인자로 전달된 객체의 필드와 현재 객체(equals 메소드가 호출된)의 필드가 모두 같은지 빠뜨리지 말고 비교한다.
=> 이때 float, double 타입이 아닌 기본형 필드의 경우에는 == 연산자로 비교하며, float은
     float.compare메소드로 double은 double.compare 메소들를 사용한다.
     객체 참조 필드가 null 값을 가질 수 있는데, 이 경우 NullPointerException 예외의 발생을 막으려면
     (field == null ? o.field == null : field.equals(o.field)) 를 사용하거나
     (field == o.field || (field != null && field.equals(o.field))) 사용하자.  

5. 우리의 equals 메소드를 작성한 후에는 과연 그 메소드가 대칭적이며 이행적이고 일관성이 있는지 확인한다.

※유의
▶ equals 메소드를 오버라이드 할 때는 hashCode 메소드도 항상 오버라이드 한다.
▶ 너무 똑똑한 척 하지 마다. (너무 깊이 있게 들어가지 말라 !!!)
▶ equals 메소드의 인자 타입을 Object 대신 다른 타입으로 바꾸지 말자.

$9. equals 메소드를 오버라이드 할 때는 hashCode 메소드도 항상 같이 오버라이드 하자

equals 메소드를 오버라이드 하는 모든 클래스에는 반드시 hashCode 메소드도 오버라이드 해야 한다.






2010년 2월 22일 월요일

[소개] Head First Javascript


저자 : 마이클 모리슨 역자 : 홍형경

출판사 : 한빛미디어 ISBN : 978-89-7914-582-3
가격 : ₩28,000


[목차]

펼쳐두기..


2010년 2월 21일 일요일

[02] 객체의 생성과 소멸

$1. 생성자 대신 static 팩토리 메소드 사용을 고려하자.

클래스의 인스턴스를 생성하는 일반적인 방법은 public 생성자를 제공하는 것이지만, 보다 유용한 방법인 클래스에 public static 팩토리를 두는 것이다.

[java]
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}

상기와 같은 static 팩토리 메소드의 장단점은 다음과 같다.

1. static 팩토리 메소드의 한 가지 장점은, 생성자와 달리 자기 나름의 이름을 가질 수 있다.

자바 클래스는 생성자 이름이 클래스 이름과 같은 하나의 생성자만 가질 수 있다. 따라서 개발자들은 매개변수를 이용하여 여러 생성자를 만들곤 한다. 하지만 이는 좋은 방법이 아니다. 이와 같이 하나의 클래스에 동일한 시그너처를 갖는 여러 개의 생성자가 필요한 경우 생성자 대신 static 팩토리 메소드를 사용하되, 메소드 간의 차이점을 부각시키도록 신중하게 이름을 선정한다.

2. static 팩토리 메소드의 두 번째 장점은, 생성자와 달리 호출될 때마다 매번 새로운 객체를 생성할 필요가 없다.

동일한 객체가 자주 사용될 때, 특히 객체 생성시 자원이나 시간이 많이 든다면 이 방법은 프로그램 성능을 크게 향상시킬 수 있다.

static 팩토리 메소드는 여러 번 호출되더라도 이미 생성된 동일 객체를 반환할 수 있으므로, 클래스에서는 언제든지 인스턴스들의 존재를 직접 제어 할 수 있다. 인스턴스 제어 클래스라 불리는 이와 같은 클래스를 만드는 이유는 몇 가지가 있다.

인스턴스를 제어하면 싱글톤 또는 인스턴스 생성 불가 클래스로 만들 수 있으며, 또한 불변 클래스에서 두 개의 동일한 인스턴스가 생기지 않도록 해준다.

3. 자신의 클래스 인스턴스만 반환하는 생성자와 달리, static 팩토리 메소드는 자신이 반환하는 타입의 어떤 서브타입 객체도 반환할 수 있다.

접근 범위가 public이 아닌 클래스의 객체를 생성하고 반환할 수 있는 API가 있다. 이 기법은 static 팩토리 메소드의 반환 타입으로 인터페이스를 사용하는 인터페이스 기반 프레임워크에 적합하다.

1.5 버전에 추가된 java.util.EnumSet클래스에는 public 생성자는 없고, static 팩토리 메소드들만 있으며, 주어진 열거형 타입의 크기에 따라 둘 중 하나의 객체를 생성하고 반환한다. 즉 64개 이하의 요소수인 경우 RegularEnumSet 인스턴스를 반환하며, 65개 이상이면 JumboEnumSet 인스턴스를 반환한다.

이처럼 유연한 static 팩토리 메소드는 자바 데이터베이스 연결 API(JDBC)와 같은 서비스 제공자 프레임워크의 근간이 된다. 서비스 제공자 프레임워크(Service provider framework)는 세 가지 핵심 컨포넌트로 구성된다. 첫 번쨰는 제공자가 구현하는 서비스 인터페이스이며, 두 번째는 프레임워크 시스템에서 구현체를 등록하는데 사용하는 제공자 등록 API이며 끝으로, 서비스 인스턴스를 얻기 위해 클라이언트가 사용하는 서비스 접근 API가 있다. 또한 선택가능한 내 번쨰 요소는 서비스 제공자 인터페이스라는 것으로, 제공자가 자신의 서비스 구현체 인스턴스를 생성하기 위해 구현한다.

4. static 팩토리 메소드의 네 번째 장점은, 매개변수화 타입의 인스턴스를 생성하는 코드를 간결하게 해준다.

매개변수화 클래스의 생성자를 호출할 때는 타입 매개변수를 지정해야 한다.

[JAVA]
Map<String, List<String>> m = new HashMap<String, List<String>>();

이와 같이 코딩하면 타입 매개변수가 늘어나는 경우 타이핑할 분량이 많아지고 복잡해져서 매우 힘들어진다. 그러나 static 팩토리를 사용하면 컴파일러가 타입 매개변수를 해결하도록 할 수 있는데 이를 타입 추론이라고 한다.

만일 HashMap에서 다음의 static 팩토리 메소들를 제공한다고 가정해 보자

[JAVA]
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}

위에서 선언한 메소드를 다음과 같이 간결한 형태로 사용할 수 있다.

[JAVA]
Map<String, List<String>> m = HashMap.newInstance();


5. static 팩토리 메소드의 가장 큰 단점은, 인스턴스 생성을 위해 static 팩토리 메소드만 갖고 있으면서 public이나 protected 생성자가 없는 클래스의 경우는 서브 클래스를 가질 수 없다.

자바 표준 컬렉션 프레임워크에 구현된 클래스의 어느 것도 서브 클래스를 가질 수 없다.

6. static 팩토리 메소드의 두 번째 단점은, 다른 static 메소드와 쉽게 구별 할 수 없다는 것이다.

static 팩토리 메소드의 공통 명칭 사용 예
  • valueOf - 자신의 매개변수와 같은 값을 갖는 인스턴스를 반환한다.
  • of - valueOf를 줄인 형태의 이름이며, EnumSet에서 사용한다.
  • getInstance - 매개변수에 나타난 인스턴스를 반환하지만, 매개변수와 같은 값을 갖지 않을 수 도 있다. 싱글톤인 경우 getInstance는 매개변수가 없고, 오직하나의 인스턴스만 반환한다.
  • newInstance - getInstance와 유사하나 반환되는 각 인스턴스가 서로 다르다.
  • getType - getInstance와 유사하나 팩토리 메소드가 다른 클래스에 있을 떄 사용한다.
  • newType - newInstance와 유사하나 팩토리 메소드가 다른 클래스에 있을 때 사용한다.

$2. 생성자의 매개변수가 많을 때는 빌더를 고려하자

static 팩토리 메소드와 생성자는 공통적인 제약이 있다. 즉, 선택 가능한 매개변수가 많아질 경우 신축성있게 처리하지 못한다는 것이다.

이를 처리하는 방법중 하나는 텔리스코핑 생성자 패턴을 사용하는 것이다.  하지만 이 방법은 매개변수가 많을 때는 클라이언트 코드 작성이 힘들고 코드의 가독성도 떨어진다.

[JAVA] : 텔리스코핑
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;

public NutritionFacts (int servingSize, int servings) {
this(servingSize, servings, 0);
}

public NutritionFacts (int servingSize, int servings, int calories) {
this(servingSize, servings, calories, 0);
}

public NutritionFacts (int servingSize, int servings, int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}

public NutritionFacts (int servingSize, int servings, int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}

public NutritionFacts (int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
this. servingSize = servingSize;
this. servings = servings;
this. calories = calories;
this. fat = fat;
this. sodium = sodium;
this. carbohydrate = carbohydrate;
}
}


매개변수가 많은 생성자의 두 번째 대안으로 자바빈즈 패턴이 있다. 이 패턴에서는 매개변수가 없는 생성자를 호출해서 객체를 생성한 후 세터 메소드를 호출해서 각각의 필수 필드와 선택 필드 값을 지정한다,

[JAVA] : 자바빈즈 패턴
public class NutritionFacts {
private int servingSize = -1;
private int servings = -1;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;

public NutritionFacts() { }
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCalories(int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
}

자바빈즈 패턴은 심각한 단점을 갖고 있다. 여러 번의 메소드 호출로 나누어져 인스턴스가 생성되므로, 생성과정을 거치는 동안 자바빈 객체가 일관된 상태를 유지하지 못할 수 있다. 따라서 자바빈즈 패턴은 불변 클래스를 만들 수 있는 가능성을 배제하므로 스레드에서 안전성을 유지하려면 프로그래머의 추가적인 노력이 필요하다.

세번째 방법은 위의 두가지 방법을 결합한 빌더 패턴의 형태로써, 원하는 객체를 바로 생성하는 대신 클라이언트는 모든 필수 매개변수를 갖는 생성자를 호출하여 빌더 객체를 얻고, 빌더 객체의 세터 메소드를 호출하여 필요한 선택 매개변수들의 값을 설정한다. 끝으로 클라이언트는 매개변수가 없는 build 메소드를 호출하여 불변 객체를 생성한다.

[JAVA]
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;

public static class Builder {
private final int servingSize;
private final int servings;

private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;

public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}

public Builder calories(int val) { calories = val; return this; }
public Builder fat(int val) { fat = val; return this; }
public Builder carbohydrate(int val) { carbohydrate = val; return this; }
public Builder sodium(int val) { sodium = val; return this; }

public NutritionFacts (Builder builder) {
return new NutritionFacts(this);
}

private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
}

생성자 대비 빌더의 또 다른 장점은, 여러개의 가변인자 매개변수를 가질 수 있다는 것이다. 또한 빌더 패턴은 유연성이 좋다. 하나의 빌더는 여러개의 객체를 생성하는데 사용될 수 있으며, 이러한 과정 중에 빌더의 매개변수는 다양하게 조정될 수 있다. 빌더를 사용하면 일부 필드의 값을 자동으로 설정할 수 있으며, 매개변수 값이 설정된 빌더는 훌륭한 추상 팩토리를 만든다. 즉, 클라이언트 코드에서는 그런 빌더를 메소드로 전달하여 그 메소드에서 하나 이상의 객체를 생성하게 할 수 있다.

생성자나 static 팩토리 메소드에서 많은 매개변수를 갖게 될 클래스를 설계할 때는 빌더 패턴이 좋은 선택이다. 특히 선택 매겨변수가 대부분인 경우가 그렇다.

$3. private 생성자나 enum 타입을 사용해서 싱글톤의 특성을 유지하자

싱글톤은 정확히 하나의 인스턴스만 생성되는 클래스이다.

자바 1.5 버전 이전에는 싱글톤을 구현하는 방법이 두 가지 있었으며, 두 방법 모두 생성자를 private으로 하고, 유일한 인스턴스에 접근할 수 있도록 public static 멤버를 외부에 제공한다.

그중 한 가지 방법에서는 다음과 같이 멤버가 final 필드이다.

[JAVA]
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }

public void leaveTheBuilding() { ... }
}

private 생성자는 딱 한번만 호출되어 public static final 필드인 Elvis.INSTANCE를 초기화한다. 클라이언트는 이것을 변경할 수 있는 방법이 없다. 한 가지 예외가 있다면, 허가된 클라이언트가 AccessibleObject.setAccessible 메소드를 사용해서 private 생성자를 재귀적으로 호출할 수 있다는 것이다.

싱글톤을 구현하는 두 번쨰 방법은 static 팩토리 메소드를 public 멤버로 두는 것이다.

[JAVA]
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }

public static Elvis getInstance() { return INSTANCE; }

public void leaveTheBuilding() { ... }
}

몇 번이 호출되건 Elvis.getInstance에서는 항상 같은 객체 참조를 반환하며, 또 다른 Elvis인스턴스는 절대 생성되지 않는다.

이렇게 구현된 싱글톤 클래스를 직렬화 가능하도록 하려면 어떻게 해야 할까? 우선, 클래스 선언부에 implements Serializable을 추가해야 한다. 그리고 싱글톤을 보장하기 위해 모든 인스턴스 필드를 transient로 선언해야 하며, readResolve 메소드를 추가해야 한다. 그렇지 않으면 직렬화된 인스턴스가 역직렬화 될 때마다 새로운 인스턴스가 생성된다.

[JAVA]
private Object readResolve() {
// 하나의 진짜 Elvis를 반환하고 가비지 컬렉터가 가짜 Elvis를 처리하도록 한다.
return INSTANCE;
}

자바 1.5 이후 버전에서는 싱글톤을 구현하는 세 번쨰 방법이 있다.

[JAVA]
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}

이 방법은 public 필드 방법과 기능적으로 동일하지만 더 간단하다. 그리고 복잡한 직렬화나 리플렉션 상황에서도 직렬화가 자동으로 지원되고, 인스턴스가 여러 개 생기지 않도록 확실하게 보장해 준다. 이방법이 싱글톤을 구현하는 가장 좋은 방법이다.

$4. private 생성자를 사용해서 인스턴스 생성을 못하게 하자

static 메소드와 static 필드만을 모아 놓은 클래스를 만들 필요가 종종 있을 것이다. 예를 들면 java.lang.Math나 java.util.Arrays 클래스처럼 산술 연산에 필요한 기본형 값이나 배열에 관련된 메소드들을 모아 놓는데 사용될 수 도 있으며, java.util.Collections처럼 특정 인터페이스를 구현하는 객체들에 사용되는 static 메소드를 모아 놓는데 사용될 수 있다.

이러한 클래스들은 인스턴스를 생성하지 못하게 설계되었다. 하지만 명시적으로 지정한 생성자가 없을 떄는 컴파일러가 디폴트 생성자를 만들어 준다. 우리가 의도하는 것은 인스턴스를 생성할 수 없게 하는것이다. 이를 위해 간단한 이디엄이 있다. 즉, 디폴트 생성자는 우리가 명시적으로 지정한 생성자가 전혀 없을 때만 자동으로 만들어진다. 따라서 우리가 private 생성자를 정의하면 인스턴스 생성이 불가능한 클래스를 만들 수 있다.

[JAVA]
public class UtilityClass {
private UtilityClass() {
throw new AssertionError();
}
}

명시적으로 정의한 생성자가 private이므로 이 클래스 외부에서는 생성자 호출이 불가능하다.

위의 클래스의 단점은 서브 클래스를 만들 수 없다는 부작용이 있다. 서브 클래스 인스턴스를 생성할 때 생성자를 호출하면, 수퍼 클래스의 디폴트 생성자가 호출된다. 그러나 위 클래스의 서브 클래스를 만들면 수퍼 클래스의 생성자가 private이라 호출이 불가능해지므로 컴파일 시에 에러가 발생한다.

$5. 불필요한 객체의 생성을 피하자

기능적으로 동일한 객체를, 필요할 때마다 매번 새로 생성하기보다는 하나의 객체를 재사용하는 것이 좋을 때가 많다.

해서는 안될 극단적인 예로 다음 코드가 있다.

[JAVA]
String s = new String("stringette");

위의 코드는 String 인스턴스를 불필요하게 생성한다. 개선된 코드는 아래와 같다.

[JAVA]
String s = "stringette";

여기서는 실행될 때마다 새로운 인스턴스를 생성하지 않고 하나의 String 인스턴스를 사용하며, 같은 JVM에서 실행되는 어떤 코드에서도 동일한 문자열 리터럴을 갖도록 재사용 된다.

불변 클래스의 불필요한 객체 생성을 막으려면 생성자보다는 static 팩토리 매소드를 사용하는 것이 좋다.

[JAVA]
public class Person {
private final Date birthDate;

// 다른 부분은 생략
// 절대 이렇게 하지 말라
public boolean isBabyBoomer() {
// 불필요한 객체 생성
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
...
...
return ...
}
}

개선된 코드는 아래와 같다.

[JAVA]
public class Person {
private final Date birthDate;

// 다른 부분은 생략
static {
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
}

public boolean isBabyBoomer() {
return ...
}
}


$6. 쓸모 없는 객체 참조를 제거하자

자바와 같이 가비지 컬렉션을 자동으로 해주는 언어로 프로그램을 개발하면 객체들이 사용하던 메모리가 자동으로 회수된다고 생각하여 메모리 관리를 전혀 고려하지 않아도 된다는 생각을 가질 수 있다. 그러나 이러한 생각은 잘못된 것이다.

[JAVA]
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;

public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}

public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}

public Object pop() {
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
// 배열에 요소를 저장하는데 필요한 공간을 확인하고 배열이 커질 필요가 있을 때는
        // 그 크기를 2배로 늘린다.
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}

상기 코드는 메모리 누출이 생긴다. 만일 스택이 커졌다가 줄어들면 스택에서 꺼냈던 객체들은 가비지 컬렉션되지 않을 것이다.  이렇듯 가비지 컬렉션이 지원되는 프로그래밍 언어에서 생기는 메모리 누출(의도하지 않은 객체 유지)은 우리가 모르는 사이에 진행된다.

이와 같은 문제에 대한 해결책은 매우 간단하다. 즉, 쓸모 없는 참조를 null로 만드는 것이다. 앞의 Stack 클래스에서는 스택에서 요소를 꺼내는 즉시로 그 요소의 참조가 쓸모 없게 된다.

[JAVA]
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;  // 쓸모없는 참조를 제거한다.
return result;
}

쓸모없는 참조를 null 값으로 바꾸면 부수적인 장점이 생긴다. 이런 참조들이 나중에 실수로 사용된다면 NullPointerException 예외가 생긴다.

하지만 이와 같이 처리하는 것이 반드시 바람직하지 않다. 꼭 필요할 때만 예외적으로 행해야 한다. 이를 판단하는 기준으로는 자신의 메모리를 스스로 관리하는 부분에서만 사용한다. 즉 자신의 메모리를 자기가 관리한다면, 프로그래머는 항상 메모리 누출에 주의해야 한다.

메모리 누출이 흔히 생기는 또 다른 근원은 캐시(clone)이다. 객체 참조를 케시에 저장하면 저장했다는 것을 잊어 버리고 객체가 더 이상 필요 없을 때까지 캐시에 내버려 두기 쉽다. 이런일은 백그라운드 스레드(Timer 또는 ScheduledThreadPoolExecutor)로 처리하거나, 또는 새 EldestEntry 메소드를 사용하자.

메모리 누출의 세 번째 근원은 리스너와 콜백이다. 만일 클라이언트가 콜백을 등록하되 말소는 하지 않는 API를 구현한다면 콜백은 계속 누적될 것이다. 콜백이 신속하게 가비지 콜렉션 되도록 하는 가장 좋은 방법은 약한 참조만을 저장 유지하는 것이다. 예들 들어 WeakHashMap의 키로만 콜백을 저장한다.

메모리 누출은 잘 드러나지 않으므로 코드를 철저하게 검사하거나 힙 프로파일러와 같은 디버깅 도구의 도움을 받아야만 원인을 찾을 수 있다. 따라서 정답은 코드를 잘짜는 것이다.

$7. 파이널라이저의 사용을 피하자

파이널라이저는 예측 불가에다가 위험하기도 하고 일반적으로는 불필요하다. 하지만 몇가지 적합한 용도가 있다.

파이널라이저는 신속하게 실행된다는 보장이 없기 때문에 실행시간이 매우 중요한 작업을 절대 하지 말아야 한다. 예를 들면 파이널라이저에서 파일을 닫는 것은 매우 심각한 실수이다.

파이널라이저가 얼마나 빨리 실행되는가는 주로 가비지 컬렉션 알고리즘에 달려있으며, 그 알고리즘은 JVM 종류에 따라 다양하다. 또한 클래스에 파이널라이저를 사용하면 간혹 인스턴스들의 메모리 회수와 재활용이 지연될 수 있다.

이를 해결하기 위한 System.gc와 System.runFinalization 메소드들을 사용하고픈 유혼에 빠지지 말자. 만일 파이널라이즈를 하는 동안 catch 되지 않은 예외가 발생하면 그 예외는 무시되고 그 객체의 파이널라이즈는 종결된다.

파이널라이즈를 사용하면 엄청난 성능 저하가 발생한다. 그러면 파일이나 스레드처럼 종결 작업이 필요한 자원을 갖는 객체들의 클래스에서는 파이널라이저 대신 무엇을 써야 할까? 작업이나 자원을 정상적으로 종료하는 메소드만 별도로 추가하면 된다. 그리고 더 이상 필요 없는 각 인스턴스에 대해서 그 클래스의 클라이언트가 종료 메소드를 호출하도록 하면 된다. 이 때 주의 할것은 각 인스턴스에서 자신의 종료 여부를 유지 관리해야 한다. 즉, 종료 메소드에서는 해당 객체가 더 이상 유효하지 않다는 것을 private 필드에 기록해야 하며, 다른 메소드에서 이 필드 값을 확인하여 객체 종료후 호출되었다면 IllegalStateException 예외를 발생시켜야 한다.

가급적 종료 메소드는 try-finally와 함께 사용하여 확실하게 실행되도록 하자.

그렇다면, 파이널라이저는 어떤 경우에 사용하면 좋을까? 첫번째는 생성된 객체를 갖고 있는 코드에서 그 객체의 종료 메소드 호출을 빠뜨렸을 경우에 "안전망"의 역할을 하는 것이다. 즉, 클라이언트가 종료 메소드 호출에 실패하는 그런 경우를 대비해서 파이널라이저를 사용한다. 두번째는 네이티브 피어와 관련이 있다. 네이티브 피어는 네이티브 메소드를 통해 일반 자바 객체가 자신의 일을 위임하는 네이티브 객체이다. 네이티브 피어는 일반 자바 객체가 아니므로, 그것과 연관된 자바 피어 객체가 소멸되면 가비지 컬렉터가 알지 못하며 재활용 할 수도 없다. 따라서 이런 경우 파이널라이저가 적합할 수 있다.







[01] 개요

이펙티브 자바는 총 78개의 항목으로 구성되며, 자바의 기본 라이브러리인 java.lang, java.util, 그리고 java.util.concurrent와 java.io의 효과적인 사용에 대해서 설명한다.

대다수의 규칙은 다음 몇 가지 기본 원리를 근간으로 한다.

  • 명확함과 단순함이 가장 중요하다.
  • 모듈은 정해진 기능대로 수행되어야 하며, 가급적 크기를 작게 만들되 너무 작아지지 않게 한다.
  • 소스 코드를 복사 후 수정하여 다시 사용하는 형태가 아니고 기존 코드를 그대로 재사용해야 하며, 모듈간의 종속성은 최소화해야 한다.
  • 에러는 가능한 발생된 즉시 검출되어야 하며, 이상적으로는 컴파일 시점이 좋다.

이러한 규칙을 무작정 따라 하기보다는 타당한 이유를 갖고 어길 줄도 알아야 한다. 대부분의 다른 분야가 그렇듯, 프로그래밍 기술의 습득 또한 규칙을 먼저 배운 다음 그 규칙을 언제 깰 것인지 아는 것이 중요하다.

이책의 웹사이트인 http://java.sun.com/docs/books/effective에 방문하면 각 예제의 소스 코드를 받아서 컴파일과 실행을 해 볼 수 있다.




2010년 2월 8일 월요일

[02] 주요 개념

$1. 전체구조

 

안드로이드 오픈 소스 소프트웨어 스택을 구성하는 주요 계층과 구성요소를 포함한 전반적 시스템 아키텍처를 살펴보자.

 

 

각 층은 그 바로 아래 계층에서 제공하는 서비스를 이용한다.

 

1) 리눅스 커널

 

안드로이드는 리눅스 커널이라는 견고하고 검증된 토대 위에 구축되며, 이는 내부의 메모리관리, 프로세스 관리, 네트워킹, 운영체제 시스템 서비스 등에 이용된다.

 

2) 내장 라이브러리

 

커널 바로 위에 내장 라이브러리가 있다. 이 공유 라이브러리는 모두 C 또는 C++로 쓰여 있고 핸드폰에 사용되는 특정 하드웨어를 위해 컴파일되어 핸드폰 공급업체에 의해 폰에 미리 설치된다.

 

  • surface manager
    안드로이드는 매우 간단한 복합 윈도 매니저를 사용한다.
  • 2D와 3D 그래픽
    안드로이드에서는 2차원요소와 3차원 요소가 하나의 사용자 인터페이스에서 결합될 수 있다.
  • 미디어 코덱
    안드로이드는 동영상을 재생할 수 있고, 오디오 녹음과 재생이 가능한데 AAC, AVC, H.263, MP3, MPEG-4등의 다양한 포맷을 지원한다.
  • SQL 데이터베이스
    안드로이드는 경량의 SQLite 데이터베이스 엔진을 갖추가 있다. 이는 파이어폭스와 애플사의 아이폰에도 사용되는 데이터베이스다.
  • 브라우저 엔진
    HTML 콘텐츠를 신속하게 디스플레이하기 위해 안드로이드는 Webkit라이브러리를 사용한다. 이는 구글 크롬 브라우저, 애플의 사파리, 아이폰, 노키아 S60등의 플랫폼에 동일하게 사용되는 엔진이다.

3) 안드로이드 런타임

 

커널위에는 안드로이드 런타임인 달빅(Dalvik) VM과 코어 자바 라이브러리 등이 포함된다. 달빅 VM은 모바일 다바이스용으로 최적화해 구현된 구글의 자바 격이다.

 

달빅은 구글의 댄 본스타인이 만든 VM이다. 달릭이란 결국 낮은 메모리용으로 최적화된 자바 VM이다. 여러개의 VM 인스턴스가 한 번에 실행 가능하며, 내부 운영체제의 장점으로 보안과 프로세스의 격리가 가능하다. 달빅이라는 이름은 본스타인 조상이 살던 아이슬란드의 한 물고기 잡는 마을의 이름을 딴 것이다.


달빅과 전형적인 자바와의 차이점은 다음과 같다.

 

  • 달빅은 .dex파일을 실행시키는데 이 파일은 .class와 .jar파일이 컴파일 시점에 변환된 것이다. .dex파일은 클래스 파일보다 콤팩트하고 효율성이 높기 때문에 안드로이드가 사용될 적은 메모리와 베터리로 작동하는 환경에 적합하다.
  • 안드로이드에 들어 있는 코어 자바 라이브러리는 자바 스탠더드 에디션 라이브러리와 자바 모바일 에디션 라이브러리와는 다르지만 중복되는 부분이 상당히 있다.

4) 애플리케이션 프레임워크

 

어플리케이션을 만들 때 사용될 높은 수준의 빌딩 블록이 여기에서 제공된다. 필요시 개발자가 이를 확장시킬 수 있으며, 프레임워크의 가장 중요한 부분은 아래와 같다.

 

  • activity manager
    애플리케이션의 생명주기를 제어하고 사용자 내비게이션을 위해 'back-stack'을 유지한다.
  • content manager
    주소록과 애플리케이션 사이에서 공유되는 정보를 요약한다.
  • resource manager
    리소스란 프로그램의 코드 외 모든 부분인데 이를 관리하낟.
  • location manager
    안드로이드 폰은 항상 자신의 위치를 파악하고 있다.
  • notification manager
    도착 메시지, 약속, 근접성 알림 등의 사건을 사용자에게 방해되지 않는 방식으로 전달한다.
안드로이드의 강력한 특징 중 하나는 모든 애플리케이션이 동일한 수준으로 실행된다는 것이다. 이말은 시스템 애플리케이션이 개발자가 사용하는 것과 동일한 공개 API를 거쳐야 한다는 뜻인데, 원한다면 안드로이브가 기본 시스템 애플리케이션 대신 개발자가 만든 애플리케이션을 사용하게 만들 수 있다.


5) 애플리케이션

 

안드로이드 아키텍쳐의 최상위 계층이며, 최종사용자는 오직 이 어플리케이션만을 보게 된다.

 

$2. 살아있네!

 

안드로이드에는 foregraund 애플리케이션이 하나며, 상태 줄을 제외한 전체 스크린을 차지한다. 사용자가 애플리케이션을 실행하면, 안드로이드는 그 프로그램을 시작한 후, 포어그라운드에 놓는다. 거기에서 사용자는 다른 애플리케이션을 호출하거나 같은 애플리케이션 내의 다른 화면을 호출한다. 이 모든 프로그램과 화면은 시스템의 액티비티 매니저에 의해 애플리케이션 스택에 기록한다.

 

1) 프로세스는 애플리케이션과 다른다.

 

내부적으로 개별 사용자 인터페이스 화면은 Activity 클래스에 의해 표현된다. 액티비티는 생명주기를 갖고 있으며, 어플리케이션은 한 개 이상의 액티비티와 이것을 담을 리눅스 프로세스를 합친 것이다.

 

주의할것은 안드로이드상에서, 애플리케이션은 해당 프로세스가 죽여졌을 때조차도 살아 있을 수 이따. 즉, 액티비티의 생명주기가가 프로세스의 생명주기와 긴밀하게 결합되어 있지 않다. 프로세스란 액티비티를 자유롭게 담을 수 있는 그릇, 즉 컨테이너이다.

 

2) 액티비티 생명주기

 

안드로이드의 액티비티는 아래 그림의 여러 상태중 하나에 놓여 있다. 이는 개발자가 조정할 수 없으며 시스템에 의해 관리된다. 하지만 개발자는 상태가 변경될 때 onXX() 메서드가 호출되는 방식으로 알 수 있다.

 

 

Activity 클래스에서 해당 메소드를 적당하게 오버라이드하면, 안드로이드는 이를 적당한 시기에 호출할 것이다.

 

$3. 구성요소

 

1) 액티비티

 

액티비티란 사용자 인터페이스 화면을 일컫는다. 애플리케이션은 프로그램의 여러 단계를 다루기 위해 한 개 이상의 액티비티를 정의한다.

 

2) 인텐트

 

인텐트란 구체적인 행동을 설명하는 메커니즘이다. 안드로이드에서 거의 모든 것이 인텐트를 거치므로 구성요소를 대체하거나 재사용할 수 있게 해 준다.

 

3) 서비스

 

서비스란 유닉스 데몬처럼 사용자와의 직접적인 상호작용 없이 배경에서 실해되는 작업이다. 안드로이드에는 많은 내장 서비스와 그 서비스에 쉽게 액세스하게 해 주는 간편한 API가 제공된다.

 

4) 콘텐츠 제공자

 

콘텐츠 제공자란 데이터의 읽기와 쓰기를 위해 데이터가 사용자 API로 포장된 것이다.

 

$4. 리소스 사용하기

 

리소스란 프로그램에 필요한 지역화된 텍스트와 비트맵 등 코드 외의 작은 정보다. 안드로이드 리소스 컴파일러는 파일이 어느 하위 디렉토리에 있는지와 파일의 형식에 따라 처리한다. 예를 들어 PNG와 JPG형식의 비트맵은 res/drawable 디렉토리에 놓여야 하며 화면 레이어를 설명하는 XML 파일은 res/layout 디렉토리에 놓여야 한다.

 

$5. 안전과 보안

 

하드웨어는 한 프로세스가 다른 프로세스의 메로리에 액세스하는 것을 막고, 각 애플리케이션은 사용자 ID를 할당받아 애플리케이션이 만든 파일을 다른 애플리케이션이 읽거나 쓸 수 없다.

 

애플리케이션이 설치되면 패키지 매니저가 인증에 따라 권한을 허거하거나 제한하는데, 필요 시에는 사용자가 확인할 수 있다.

 

안드로이드는 AndroidManifest.xml 파일을 설정하여 시스템 전 영역의 액세스를 제한 할 수 있다.

 

 

 

 

 

 

 

[01] 시작하기

구글 및 오픈 핸드셋 얼라이언스의 후원을 받은 모바일 플래폼이며, 쉬운 개발과 오픈소스의 장점을 가지고 있다.

 

$1. 도구 설치

 

1) 자바 5.0 이상

 

JRE 만으로는 충분하지 않으며, 반드시 JDK5.0 이상이 필요하다.

 

2) 이클립스

 

최소버젼은 3.3.1 이상의 버전을 필요로 하며, 자바 개발자를 위한 이클립스 IDE를 다운로드 받는다.

 

3) 안드로이드

 

구글의 안드로이드를 http://developer.android.com/sdk/download.html에서 개발 OS에 맞게 다운로드 받는다.

 

4) 이클립스 플러그인

 

구글에서 배포하는 ADT(Android Development Toolkit) 플러그인을 설치한다.

 

안드로이드 업데이트 주소
http://dl-ssl.google.com/android/eclipse/


플러그인 설치후 이클립스를 재시작하면 안드로이드 SDK가 설치된 디렉토리를 Windows > Preferences > Android에서 입력한다.

 

 

 

[소개]헬로, 안드로이드

 

저자 : 에드 버넷 역자 : 한정민

출판사 : ITC ISBN : 978-89-6351-001-9
가격 : ₩20,000

[목차]

펼쳐두기..


 

2010년 2월 4일 목요일

[03]이벤트 - 트리거 사용하는 방법

$1. 페이지 로드 시점에 작업 수행하기

1) 코드 실행 시간

[window.onload 이벤트]
관련된 모든 도큐먼트가 브라우저로 다운로드된 후에 발생한다. 다시 말해, 요소들이 로드되는 순서를 고려할 필요가 없게 해 주는 매우 좋은 기능이다. (관련파일 => 이미지 등)

[$(document).ready()]
DOM이 로드되어 사용할 준비가 끝나는 시점에 바로 호출된다. 이것 역시 스크립트에서 모든 요소들에 접근할 수 있지만 모든 파일들이 다운로드된 후를 의미하는 것은 아니다.

[가을생각]
일반적으로 $(document).ready()를 사용하는 것이 장점이 많지만 때때로 관련파일에 대한 핸들링이 필요한 경우가 있다. 이런 경우에는 당연히 window.onload (jQuery의 .load())를 사용해야 한다.

2) 하나의 페이지에 다중 스크립트 사용하기

onload속성은 오로지 하나의 함수만 저장할 수 있기 때문에(이부분은 사실이 아닌 것 같다...) 여러 함수를 할당할 수 없다. 하지만 $(document).ready()는 추가된 함수들을 내부의 큐에 모두 로드되기 때문에, 페이지가 로드될 때 등록된 모든 함수들이 순서대로 실행된다.

3) 간결하게 단축된 코드

$(document).ready()는 매우 빈번하게 사용되기 떄문에 이를 간결하게 사용할 수 있는 $()함수를 제공한다. 또한 이 팩터리 함수는 함수를 인자로 받을 수 있으며 함수를 인자로 받게 되면 묵시적으로 ready() 함수를 호출하여 수행한다.

$(document).ready(function() { }); 표현은 $(function() { });으로 간결하게 사용할 수 있다.

[가을생각]
간결한 코드의 장점은 많다. 하지만 간결하고 축약된 코드가 항상 좋은 것은 아니다. 실 개발을 할때에는 코드의 가독성이 때때로 간결한 코드유지 보다 중요할 때가 있다.

$2. 기본 이벤트들

1) .bind() 메소드

.bind() 메소드는 자바스크립트가 제고하는 이벤트에, 행위자를 할당하는 역할을 담당하고 있다.
예를 들면 $('#switcher-large').bind('click', function() { ... }); 는 switcher-large아이디를 가진 요소에서 click 이벤트가 발생하면 정의된 함수가 실행된다.

2) this 키워드

이벤트 핸들러가 실행되는 동안에 사용할 수 있는 this는 핸들어가 추가된 DOM요소를 가르킨다.
$(this)와 같이 사용하여 해당 요소를 제어할 수 있다.

3) 보다 간소화된 이벤트

jQuery는 핸들러를 이벤트에 연결하는 것은 매우 흔한 작업이므로 보다 명료화된 방식의 이벤트 메소드를 제공하고 있다. 이것들은 .bind()와 동일한 기능을 하면서도 간략하게 기술할 수 있어 편하다.

$('#switcher-large').bind('click', function() { ... });는 다음과 같이 기술할 수 있다.
$('#switcher-large').click(function() {...});

$3. 합성 이벤트

.toggle(), .hover() 메소드는 사용자 행위 조합을 가로채어, 하나 이상의 함수에 대해 응답하는 기능을 제공하고 있다.

1) .toggle 메소드

도규먼트 요소에 접고 펼치는 기능을 제공하는 .toggle메소드는 jQuery에서 정의한 메소드이다.

[JAVASCRIPT]
$(document).ready(function() {
    $('#switcher h3').toggle(function() {
        $('switcher .button').addClass('hidden');
    }, function() {
$('#switcher .button').removeClass('hidden');
    });
});

.toggle() 함수는 두 개의 인자를 함수형태로 가진다, 첫 번째 인자는 처음 클릭하면 실행되고, 두번쨰 인자는 두 번쨰 클릭하면 실행되도록 정의되어 있다.

jQuery는 클래스를 추가하거나 제거하기 전에 클래스의 존재 유무를 자동으로 검사해야 할 경우에는 .toggleClass() 메소드를 활용할 수있다.

상기예제는 아래처럼 멋지게 간소화 시킬수 있다.

[JAVASCRIPT]
$(document).ready(function() {
    $('#switcher h3').click(function() {
$('#switcher .button').toggleClass('hidden');
    });
});

2) .hover() 메소드

사용자의 마우스 커서가 요소의 영역안에 들어왔을 때, 해당 요소의 스타일을 변경하거나 할 수 있지만 IE6에서는 이러한 기능을 link 요소에만 적용가능하다. 이처럼 jQuery는 이를 위해서 독자적인 방식을 제공하고 있다. 즉 .hover()메소드이다.

[JAVASCRIPT]
$(document).ready(function() {
    $('#switcher .button').hover(function() {
$(this).addClass('hover');
    }, function() {
$(this).removeClass('hover');
    });
});

.hover()메소드의 첫번째 인자는 마우스 커서가 들어올때 실행되는 함수이며, 두번째 인자는 커서가 떠날때 실행되는 함수이다.

또한 .hover()메서드를 사용하면 자바스크립트에서 이벤트전파 때문에 발생하는 골치 아픈 문제들도 피할 수 있다.

3) 이벤트의 여행

페이지에서 이벤트가 발생하면, DOM 전체 계층에 있는 모든 요소들이 이벤트를 처리할 수 있는 기회를 갖게 된다.


상기 그림에서 <a>가 클릭되면 <div>, <span>, <a>는 모두 클릭 이벤트에 응답할 수 있는 기회를 가진다. 이유는 세 개가 동일한 마우스 좌표상에 있기 때문이다. 여러 요소가 한 번의 클릭에 모두 반응하도록 하는 전략을 이벤트 캡처링이라고 한다. 즉 이벤트 캡처링에서, 모든 것을 감싸고 있는 최상위의 요소에게 이벤트가 처음으로 주어지고, 그 후에 연속해서 하위 요소로 전달된다.


이와는 반대로 이벤트가 전파되는 것을 이벤트 버블링이라고 한다. 이벤트는 가장 낮은 자식 요소에 처음으로 전달되고, 해당요소가 반응한 후에, 그 이벤트는 그 부모 요서로 전파된다.


브라우저 개발자들은 브라우저를 개발하면서 서로 다른 이벤트 전파 모델을 구현하면서 결국 DOM 표준은 이 두가지 전략을 모두 사용되어야 한다고 지정하게 되었다.

jQuery는 브라우저 호환의 일관성을 제공하기 위해 버블링 모델을 지원하는 이벤트 핸들러들을 항상 사용하고 있다. 따라서 이벤트에 첫 번째 응답하는 것이 향상 선택된 최하위 특정 자식 요소라는 것을 보장받을 수 있다.

4) 이벤트 버블링의 부작용

이벤트 버블링은 기대하지 않은 결과를 낳을 수 있는데, 특히 마우스 관련 이벤트에서 자주 발생한다. 따라서 마우스 관련 이벤트 처리에서는 버블링의 문제를 고려하고 있는 .hover()메소드를 사용하는 것이 매우 안정적이다.

$4. 이벤트 제한하고 마무리하기

때에 따라서 이벤트를 공간적으로 제한(다른 요소로 전달되는 것을 막는 것)하거나 혹은 시간적으로 제한(특정 시점에서 이벤트를 막는것)해야 할 경우도 생길 수 있다.

1) 이벤트 버블링 막기




2010년 2월 3일 수요일

[02]선택자 - 원하는 노드를 얻는 방법

jQuery는 문서객체모델의 요소나 혹은 요소들의 집합에 빠르고 쉽게 접근하는 캐스케이딩 스타일시트 선택자 기능과 Basic XPath 플러그 인을 제공한다. 또한 HTML 요소를 검색한느 데 많은 융통성을 제공하는 jQuery의 DOM 순회메소드도 제공한다.

$1. DOM

DOM은 HTML 도큐먼트를 정의하고 객체 모델이며, 계층적인 구조를 가지고 있다. jQuery를 시작하기 전에 알아야 할 중요한 점 중에 하나는 여러가지의 선택자들과 메소드로부터 얻는 결과 집합이 jQuery객체라는 사실이다.

$2. $()팩터리 함수

jQuery에서는 어떤경우든 시작할 때는 언제나 $()처럼 달러 기호와 괄호를 사용하여 시작한다. 어떤 것이든 일단 괄호 안에 넣기만 하면 자동으로 반복 순회하여 jQuery객체로 만들어 낸다.

괄호안에 들어가는 요소에 대한 예를 들면
  • 태그이름
  • ID
  • 클래스
[가을생각]
jQuery에서 $는 jQuery를 줄여서 표현한 것이다. 하지만 다른 자바스크립트에서도 $()함수는 널리사용됨으로 충돌을 피하기 위해서 jQuery 코드내에서 사용하는 모든 $ 인스턴스를 다른 문자로 사용할 수도 있다.

$3. CSS 선택자들

jQuery에서는 W3C에서 제시하는 CSS1 ~ 3까지에 포함되어 있는 대부분의 선택자들을 지원한다. 이는 브라우저가 자바스크립트를 지원하기만 한다면, 다양한 브라우저에서 새로운 선택자들이 항상 지원될 수 있다는 것을 보장하므로, 개발자들에게 많은 편의성을 제공한다.

1) 콤비네이터 (>) 사용

[JAVASCRIPT]
#1 $(document).ready(function() {
#2 $('#selected-plays > li').addClass('horizontal');
#3 $('#selected-plays li:not(.horizontal)').addClass('sub-level');
#4 });

상기 예제에서 #selected-plays > li의 의미는 도규먼트에서 ID값을 selected-plays를 가지는 요소의 자식(>)중에서 li항목을 찾는다는 의미이다.

2) 부정의사 클래스 사용

상기 예제에서 #selected-plays li:not(.horizontal)의 의미는 도규먼트에서 ID값을 selected-plays를 가지는 요소의 자식인 li요소의 클래스가 horizontal값이 아닌요소를 찾는다는 의미이다.

$4. XPath 선택자

일반적으로 jQuery는 속성 선택자들을 표현할 떄, 대괄호 안에 @표시를 사용하여 속성임을 표시하는 XPath 표기를 사용한다.

[가을생각]
jQuery에 1.1 이후 버전에서 XPath를 사용하기 위해서는 플러그인을 설치해야 한다.
http://plugins.jquery.com/project/xpath 에서 다운로드 가능하다.

속성 셀렉터들은 문자열의 시작부분(^)또는 끝부분($)을 가르키는 정규 표현식 패턴을 지원한다. 또한 문자열 내의 임의의 위치를 가르키기 위해서 애스터리스트(*) 문자를 가질 수도 있다.

[JAVASCRIPT]
$(document).ready(function() {
$('a[href^="mailto:"]').addClass('mailto');
$('a[href$=".pdf"]').addClass('pdflink');
$('a[href*="klri.re.kr"]').addClass('mysite');
});

1) ^ 사용하기

$('a[href^="mailto:"]').addClass('mailto');는 a요소에서 href속성값이 mailto:로 시작하는 요소를 찾아서 mailto 클래스를 추가하라는 의미이다.

2) $ 사용하기

$('a[href$=".pdf"]').addClass('pdflink');는 a요소에서 href속성값이 .pdf로 끝나는 요소를 찾아서 pdflink 클래스를 추가하라는 의미이다.

3) * 사용하기

$('a[href*="klri.re.kr"]').addClass('mysite');는 a요소에서 href속성값에 klri.re.kr이라는 문자열이 존재하는 요소를 찾아서 mysite 클래스를 추가하라는 의미이다.

$5. 사용자정의 선택자들

jQuery는 고유한 사용자정의 선택자들을 추가로 가지고 있다. 사용자정의 선택자들의 대부분은 배열에서 임의의 요소를 추출하는 데 사용하게 된다.

1) :odd, :even 사용하기

[JAVASCRIPT]
$(document).ready(function() {
$('tr:odd').addClass('odd');
$('tr:even').addClass('even');
$('td:contains("Herry")').addClass('highlight');
});

상기예제에서 처럼 tr 홀수 행에는 odd 클래스를 추가하고 tr 짝수 행에는 even 클래스를 추가하라는 의미이다.

[가을생각]
상기 예제에서 주의할 것은 tr의 집합에서 첫번째 요소는 항상 0부터 시작한다. 따라서 0은 짝수로취급됨으로 첫번째 행에는 even 클래스가 적용된다.

2) :contains() 사용하기

$('td:contains("Herry")').addClass('highlight');는 td요소에서 Herry라는 텍스트를 포함한 요소를 찾아서 highlight클래스를 추가하라는 의미이다.

$6. DOM 순회 메소드들

개발을 하다보면 종종 부모나 조상요소를 얻어야 할 필요가 많이 생긴다. jQuery의 DOM 순회 메소드들을 이용하면 위로 혹은 아래로 그리고 모든 DOM 트리 전체를 쉽게 검색할 수 있다.

[JAVASCRIPT]
...
$('tr:not(:has(th)):even').addClass('even');
$('tr:not(:has(th)):odd').addClass('odd');
$('td:contains("Herry")').siblings().addClass('highlight');
...

상기 예제에서 $('tr:not(:has(th)):even').addClass('even');는 tr요소의 자식요소가 th를 가지지 않는 짝수 요소에 even클래스를 추가하라는 의미이다.

$('td:contains("Herry")').siblings().addClass('highlight');는 td요소의 값이 Herry라은 문자열을 가지는 요소의 바로 옆 요소들에게 highlight클래스를 추가하라는 의미이다.

형제 요소들 모두가 아닌 바로 다음요소만을 가져올때는 next() 메소들를 사용하면 된다. 즉,
$('td:contains("Herry")').next().addClass('highlight');

부모 요소를 가져올려면 parent() 메소드를 사용한다. 즉, $('td:contains("Herry")').parent().addClass('highlight');

상기와 같은 표현식이 가능한 것은 jQuery가 체인 능력을 완벽히 가지고 있음을 보여준다. 또한 상기표현을 한줄 표현할 수 도 있지만 다음과 같이 표현할 수도 있다.

$('td:contains("Herry")') // 주석입력가능
.parent() // 주석입력가능
.addClass('highlight'); // 주석입력가능

이와 같이 작성하면 코드 가독성이 증가할 수 있다.

$7. DOM 요소 접근하기

때때로 개발을 하다보면 DOM 요소를 직접 접근해야 할 경우가 있는데 이런 경우 jQuery에서는 .get() 메소드를 제공하고 있다.

예를 들어 my-element를 아이디로 갖는 요소의 태그 이름을 알고 싶다면

$('#my-element').get(0).tagName;또는 $('#my-element')[0].tagName; 과 같이 사용할 수 있다.

[01] jQuery 시작하기

많은 개발자들이, 일반적인 작업들을 자동화하고 복잡한 작업을 단순화 하기 위해 jQuery와 같은 자바스크립트 라이브러리를 사용하고 있다.

그럼 jQuery가 왜 필요한지 살펴보자.

$1. jQuery란 무엇인가

jQuery 라이브러리는 웹 스크립팅에 폭넓게 사용될 수 있는 추상 계층을 제공하므로, 스크립팅에 필요로 하는 거의 모든 상황에 유용하게 활용할 수 있다.

그럼 jQuery의 핵심기능에 대해 알아 보자.

  • 페이지 일부분에 접근
  • 페이지가 보여지는 모습 변경하기
  • 페이지의 콘텐트 변경하기
  • 페이지와 사용자 간 상호작용 처리
  • 페이지에 애니메이션 추가하기
  • 페이지를 새로고침하지 않고 서버로부터 정보를 가져오기
  • 일반적인 자바스크립트 작업을 단순화

[가을생각]
현재 jQuery는 2010년 1월 14일 1.4 버젼이 릴리즈된 상태이다. 2006년 8월 26일 1.0버젼이 릴리즈된 이후 지속적인 발전을 거듭하고 있다. 일관성과 균형있는 설계 철학과 경량의 라이브러리를 제공하고 있어 매우 효과적이고 강력한 라이브러리이다.

$2. jQuery, 왜 좋은가

동적인 HTML에 대한 관심에 증대로 현재 수많은 자바스크립트 라이브러리들이 등장하였다. 이들 라이브러리들은 장단점이 존재하며 특정기능에 있어 jQuery 보다 효율성이 좋을 수도 있다.

jQuery의 개발 전략을 검토하면서 특장점에 대해 알아보자.

  • CSS에 대한 지식을 최대한 활용
  • 확장 지원
  • 브라우저의 차이로부터 해방
  • 언제나 집합을 가지고 작업
  • 여러 동작을 한 줄에 쓴다.
상기와 같은 전략으로 jQuery 패키지는 매우 경량의 사이즈를 유지하고 있다.

지속적인 피드백과 열려있는 커뮤니티를 통해 성장하고 있는 jQuery는 모든 사람들에게 무료로 제공되고 있다. jQuery의 라이션스 정책은 공식 사이트인 http://jquery.org에 다음과 같이 나와 있다.

You may use any jQuery project under the terms of either the MIT License or the GNU General Public License (GPL) Version 2.

The MIT License is recommended for most projects. It is simple and easy to understand and it places almost no restrictions on what you can do with a jQuery project.

If the GPL suits your project better you are also free to use a jQuery project under that license.

You don’t have to do anything special to choose one license or the other and you don’t have to notify anyone which license you are using. You are free to use a jQuery project in commercial projects as long as the copyright header is left intact.


$3. 첫 번쨰 jQuery 도큐먼트

이제 부터는 이 jQuery 라이브러리를 어떻게 사용하는지 간단한 예제를 살펴보자

1) jQuery 다운로드

가장 먼저 해야 할 일은 jQuery를 다운로드 하는 것이다. jQuery를 사용하기 위해서는 별도의 설치없이 단지 접근 가능한 위치에 파일을 올려 놓기만 하면 된다.

현재 가장 최신의 버젼은 1.4.1 버젼이다 다음 경로를 통해 다운로드 하면 된다.

[가을생각]
웹 어플리케이션 개발에 있어 클라이언트 사이드 개발을 위해서는 이에 적합한 툴을 사용함을 적극고려해 볼 필요가 있다. 많이 툴이 있지만 다음과 같은 툴의 사용을 추천한다.

이클립스 플러그인 사용 : Aptana plug-in
독립적인 어플리케이션 사용 : Aptana Studio 2.0

2) HTML 도큐먼트 설정

예제 HTML 파일을 생성한다.

[HTML]
...
<link rel="stylesheet" href="css/jquery.css" type="text/css" media="screen" />
<script src="lib/jquery/jquery-1.4.1.js" type="text/javascript"></script>
...

예제 파일에서 주의 할 것은 반드시 jquery-1.4.1.js 파일인 jQuery 라이브러리 파일을 먼저 로드해야 한다. 그렇지 않으면 다른 스크립트에서 jQuery 라이브러리를 참조할 수 없다.

3) jQuery 코드 작성

[javascript]
$(document).ready(function() {
$('.poem-stanza').addClass('emphasized');
});

jQuery의 가장 기본적인 조작은 도큐먼트의 일부를 선택하는 것이다. $() 생성자를 이용하여 선택을 수행한다. $() 함수는 우리가 작업하는 대상 객체를 만들어 내는, jQuery의 객체 생성 공장 같은 역할을 한다. 상기 예제에서는 텍스트의 클래스를 바꿈으로써 페이지 모습의 일부를 변경한 것이다.

.addClass() 메소드는 선택한 페이지의 일부분에 클래스를 적용하는 기능을 수행하여 이와 반대의 기능을 하는 .removeClass() 메소드도 존재한다.

상기 예제에서 눈여겨 봐야할 부분은 클래스를 추가하기 위해 반복문을 사용하지 않는다는 것이다. 단 한번의 함수 호출로, 도큐먼트에서 선택된 모든 부분들을 변경할 수 있다.

만일 상기 코드가 HTML 도큐먼트 헤더 부분에 추가되어 있다면, 텍스트에 아무런 영향을 주지 못한다. (자바스크립트 코드는 브라우저가 읽어들이는 시점에서 수행되기 때문에) 수행시점을 제어하는 가장 일반적인 방법은 자바스크립트 코드를 이벤트 핸들러에서 호출하는 것이다. 하지만 이와 같은 방법에도 단점이 있다. 즉, 구조와 코드를 강하게 묶어버리기 때문에, 새로운 행위를 추가하려면 모든 페이지에 걸쳐 코드와 HTML 둘다 변경해야 한다. 이런 단점을 보완하기 위해 jQuery는 $(document).ready()메소드를 제공하는데 이 메소드는 DOM이 로드되고 이미지가 로드되기 직전에 특정한 작업을 수행할 수 있다. 이떄 익명함수 호출(종종 람다함수라고도 한다)기능을 이용하여 더욱 유연성 있게 코드를 작성할 수 있다. 즉 $(document).ready(function() 와 같이 function 키워드만 사용하여 한정된 영역에서만 코드가 사용되도록 함수를 작성하여 전역 선언을 배제할 수 있다.


[소개]프로 자바스크립트 테크닉


저자 : 존 레식 역자 : 송인철, 이동기, 이유원, 황인석

출판사 : 인사이트 ISBN : 978-89-91268-43-2
가격 : ₩23,000


[목차]

펼쳐두기..


2010년 2월 2일 화요일

[소개]jQuery 작고 강력한 자바스크립트 라이브러리


저자 : 조나단 채퍼, 칼 스웨드버그 역자 : 이승준, 박용우

출판사 : 위키북스 ISBN : 978-89-92939-18-8
가격 : ₩20,000

[목차]

펼쳐두기..