Agile Java Lesson 8, 9, 10

기본

예외와 로깅

사실 작년에만 해도 예외나 로깅에 대해 별로 심각하게 생각해 본적이 없습니다. 예외야 던지면 그만이고 로깅이야 System.out.println() 로도 충분하다고 생각했었거든요. 그러다가 저번 방학 때 기선이 형 스프링 강의를 듣던 중 예외를 RuntimeException으로 처리하자는 얘기를 들었습니다. 이유인 즉슨, 대부분의 예외를 잡아도 할게 없는 경우가 많고 만약 잡아서 예외를 먹어(!)버릴 경우 클라이언트 입장에서 예외 스택 트레이스 일부만 보일 수 있기 때문입니다. RuntimeException을 사용할 경우 예외를 던지는 코드(throws Exceptoin)가 필요 없기 때문에 코드도 깔끔해 집니다. 물론 남용하면 안되겠지만 아직 기준을 모르겠네요. 지금 생각으로는 예외가 발생했을 경우 프레임워크에서 처리해 줄 수 있는 부분의 경우라면 굳이 예외를 잡을 필요가 없을 것 같고, 그 외의 경우라면 잡아서 처리해 주는게 좋지 않을까 싶습니다.

책에서 예외 코드를 작성할 때 몇 가지 지침이 나와 있습니다.

  • catch (SomethingException 예외가_발생하는_이유) { … }
  • 예외를 던지지 말아라. 그렇다고 빈 catch 블럭을 만들면 안된다. (확실히 처리할 거 아니면 Non-CheckedException으로 처리하라는 말.)
  • 모든 예외를 잡을 경우 UI에서 가장 가까운 상위 클래스에만 그렇게 하고, 에러를 기록한 후 사용자에게 메시지를 보여줘야 한다.
  • 애플리케이션에 적합한 형식의 예외를 생성하고 전달하라.

사용자 정의 예외 사용 시, 스택 트레이스를 일부 잃어버릴 수 있으니 생성자에 이전에 발생한 예외를 넘겨줘야 합니다.

catch (MalformedURLException e) {

throw new SessionException(e);

}

public SessionException(Throwable cause) {

super(cause);

}

과한 로깅은 없는 것 같습니다. 이게 시스템의 어떤 성능상의 문제를 야기한다면야 모르겠지만 말이죠. 만약 어떤 에러에 대한 로깅이없으면 왜 문제가 일어났는지 영영 못찾을지도 모릅니다. 그런데 sysout으로 로깅을 남길 경우 더이상 로깅을 사용하고 싶지 않을 경우 음.. 어떻게 해야 할까요. 그리고 보통 로깅은 남겨야 제맛(?)인데 매번 IO 코드를 써줄 수도 없고 그렇다고 로깅 클래스를 만들자니 이미 검증된 라이브러리가 있는데 뭐하러 만들까 하는거죠. 결론은 sysout 수준에서 더 나아가기 위해선 로깅을 쉽게할 방법이 필요합니다.

Java 1.4에 java.util.logging 패키지가 추가되면서 대부분의 로깅 기능이 지원되지만, 주로 Log4J나 다른 로깅 라이브러리를 쉽게 사용할 수 있게 해주는 SLF4J를 주로 사용하는 것 같습니다. 자바의 로깅 라이브러리를 포함한 대부분의 로깅 라이브러리는 다음의 특징을 가집니다.

  • 로그를 레벨 별로 관리한다.
  • 로그 출력을 제어한다. (콘솔, 파일 등)
  • 로그 포맷을 바꿀 수 있다.
  • 로그를 출력할 대상(특정 패키지나 클래스)을 지정할 수 있다.

그런데 모든 로그 메세지에 대해서 테스트를 해야 할까요? 로그는 대부분 사소한 것부터 중요한 것까지 광범위 하게 남기게 되는데, 모든 로그 메세지에 대해서 테스트를 하는건… 귀찮죠. 책에선 모든 로그 메시지에 대해 테스트를 남길 경우 다음과 같은 이점을 얻을 수 있다고 합니다.

  • 테스트를 작성하기 위해 문제가 되는 예외가 발생하는 상황을 생각해 볼 수 있게 한다. (빈 catch 문을 없애는 방법을 알 수 있음.)
  • 테스트를 작성해서 빈 catch 문의 잘못된 점을 더 잘 알게해준다.
  • 시스템으로 가는 모든 로그 메시지에 대해서 생각하게 만든다. (정말 필요한가?, 팀 정책에 맞는가?)
  • 다른 부분에 영향을 주는 로그를 발견할 수 있다.

맵과 동일성

테스트 작성 시, 어떤 객체가 동일한지 비교해야 하는 경우가 자주 있습니다. 그런데 이 때 두 객체가 진짜 같은지(같은 메모리를 참조하는지)를 알기 원한다기 보단 두 객체의 필드가 같은지를 알아야 하는 경우가 더 많은 것 같습니다. 이런 경우, equals() 메소드를 오버라이드 하지 않으면 두 객체는 같을 수 없습니다.

@Override
public boolean equals(Object obj) {
	if (obj == null)
		return false;
	if (this.getClass() != obj.getClass())
		return false;
	Course that = (Course) obj;
	return this.department.equals(that.department) &&
		this.number.equals(that.number);
}


그런데 equals() 메소드를 오버라이드 했는데도 불구하고 두 객체를 다르다고 하는 경우가 있습니다. 바로 해쉬테이블을 사용할 경우죠.

@Test
public void map() throws Exception {
	Course courseA = new Course("AAAA", "001");
	Course courseB = new Course("BBBB", "001");
	Map map = new HashMap();
	map.put(courseA, "");
	assertTrue(map.containsKey(courseB)); //! AssertionError
}


해쉬 테이블은 보통 어떤 연산으로 일련의 해쉬코드를 구한 후 그 해쉬코드를 이용해 엘리먼트 위치를 결정합니다.

해쉬 코드 % 테이블 크기 = 엘리먼트 위치

해쉬 테이블에서 중요한 요소는 바로 엘리먼트 위치에 하나의(또는 적은) 엘리먼트만 들어있는 것입니다. 그래야 요소를 빠르게 가져오죠. 만약 같은 위치에 엘리먼트가 2개 이상 있는 경우엔 다시 리스트 형태로 관리합니다. 같은 필드를 가졌을 때 같은 해쉬 코드를 반환하게 한다면 이 경우에도 동일성을 보장할 수 있게 됩니다.

@Override
public int hashCode() {
	final int hashMultiplier = 41;
	int result = 7;
	result = result * hashMultiplier + department.hashCode();
	result = result * hashMultiplier + number.hashCode();
	return result;
}


중요한건 equals() 메소드만 오버라이드 한다고 해서 동일성을 보장 받을 수 없다는 겁니다. 따라서 equals() 메소드를 오버라이드 해야할 경우 hashCode() 메소드도 꼭 오버라이드 해야 합니다.

수학 계산

수학 관련 계산은 입문서 초반에 나오는 단골 메뉴인데 볼때마다 아리송 합니다 (…) 특히 비트 관련 연산자는 뭐에 쓰나 싶기도 하죠. 제가 본 입문서(C, C++, Java, Haskell, etc..)를 통틀어서 비트 연산자로 유용한 예제를 보여준 건 Agile Java가 최초인듯 싶네요. 예제의 내용은 이렇습니다.

어떤 학생에 대한 정보(캠퍼스 안에 거주, 세금 면제, 미성년, 문제 학생)를 기록해야 하는데 200,000명의 대한 정보를 핸들링 하기엔 메모리가 부족합니다.

일반적인 접근법은 boolean 필드를 4개 만든 후, is…() 메소드를 4개 추가하겠죠. 그런데 이럴 수 없는 경우 어떻게 해야 하는가?

책에선 int 필드를 하나 만든 후, 저장해야 할 플래그들을 각각 한 비트씩 담당(0, 1)하게끔 합니다. 그리고 bit 연산자를 이용해 비트를 조작해 이 플래그 들을 관리합니다.

이제서야 드는 생각이지만 HTML 5 컨퍼런스에서 JavaScript로 고화질 이미지를 처리하는데 비트 연산을 한 이유를 알 것도 같습니다.

Advertisements

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중