[Expert One-on-One J2EE Design and Development] J2EE 프로젝트를 위한 설계 기술과 코딩 표준 2

참조: Expert One-on-One J2EE Design and Development 4장

Template Method 디자인 패턴

알고리즘 단계와 그 순서를 알고 있지만, 그 각각의 단계가 구체적으로 어떻게 수행될지는 예측할 수 없을 때 사용할 수 있는 디자인 패턴이다. 템플릿 메서드 패턴은 어떻게 수행될지 모르는 부분을 추상 메서드로 캡슐화 하고, 알고리즘을 순서대로 지니고 있는 추상 클래스를 제공한다. 중요한 개념은 그 추상 클래스가 워크 플로우를 관리한다는 것이다. public 상위 클래스의 메서드는 보통 final이고, 추상 메서드는 protected다.

이렇게 워크 플로우 로직을 추상 상위 클래스로 모아둔 것은 IoC의 예에 해당한다. 할리우드 원칙(“Don’t call me I’ll call you”)이라고도 하는 이 원칙은 일반적인 클래스 라이브러리처럼, 사용자 코드가 라이브러리를 사용하는 것이 아니라, 프레임워크 코드가 사용자 코드를 호출한다. 바로 이런 IoC가 프레임워크의 기본이며, 프레임워크는 보통 템플릿 메서드 패턴을 아주 많이 활용한다.

템플리세 메서드 패턴은 훌륭한 SoC(Separation of concern)를 제공한다. 상위 클래스는 비즈니스 로직에 집중하고 하위 클래스는 세부 기능 구현에 집중한다.

추상 상위 클래스가 인터페이스를 구현하도록 하는 것이 좋다.

Stratege 디자인 패턴

전략 패턴은 행위를 인터페이스로 빼낸다. 따라서 알고리즘을 알고 있는 클래스는 더이상 추상 클래스가 아니며 구체 클래스가 해당 인터페이스를 구현한 헬퍼를 사용한다. 전략 패턴은 템플릿 메서드 패턴 보다 작업할 것이 많지만, 더 유연하다.

저자(로드 존슨)는 다음과 같은 경우 템플릿 메서드 패턴보다 전략 패턴을 선호한다.

– 모든 스탭이 변하는 경우
– 스탭을 구현하는 클래스가 독립적인 인터페이스 계층 구조를 필요로 할 때.
– 스탭 구현이 다른 클래스와 연관이 있을 경우.
– 스탭 구현체가 실행시에 바뀌어야 하는 경우
– 스탭 구현체가 매우 다양하며 계속 늘어날 여지가 있는 경우.

확장성을 위해 콜백 사용하기

또다른 “IoC”를 사용하여 단일 작업을 매개변수화 해보자. 엄밀히 말하자면, 이것은 일종의 Strategy 디자인 패턴이다.

이 패턴은 메서드에서 호출할 여러 콜백 메서드로 구성된다.

이 패턴은 특히 JDBC같은 저수준 API를 사용할 때 유용하다.

*예제코드는 JdbcTemplate.query() 에서 사용하는  RowCallbackHandler*

다음과 같은 장점과 단점이 있다.

장점
– 프레임워크 클래스가 에러 처리, 자원 가져오기/반납하기를 할 수 있다. 즉 JDBC를 사용할 때 필요한 복잡한 에러 처리를 한 번만 작성하고 그것을 호출하여 사용할 수 있다. 에러 처리와 자원 반납이 복잡할수록 이 방법이 돋보인다.
– 이것을 사용하는(호출하는) 코드는 저수준 API를 신경 쓸 필요가 없다.
– JDBC 같은 저수준 API를 사용할 때 코드 재사용성을 높여준다.

단점
– 실행 플로우 자체를 다루는 코드를 실행하는 것 보다 덜 직관적이다. 코드를 이해하기 어려울 수 있고 관리가 어려워질 수 있다.
– 콜백 핸들러 객체를 만들어야 한다.
– 드문 경우, 인터페이스를 통해 콜백을 호출하니까 성능이.. 문제가..  (이건 좀.. )

이 패턴은 콜백 인터페이스가 매우 가단한 경우에 최대의 가치를 지닌다.

Observer 디자이니 패턴

인터페이스를 사용하는 것 처럼, 옵져서 디자인 패턴도 변경 없이 확장을 가능케하며(Open Closed Principal) 컴포넌트 간의 의존성을 낮춘다. 또한 관심사 분리(Separation of concerns)에도 기여한다.

주의할 것. 리스너는 작업을 빨리 마쳐야한다. 악당같은 리스너는 애플리케이션을 대기시킬 수 있다. 최대한 빨리 리턴하도록 하고 오래 걸리는 작업은 별도의 쓰레드로 분리한다. 리스너는 또한 동기화와 공유 객체 문제도 피해야하고 반드시 멀티 쓰레드에 안전해야 한다.

옵저버 디자인 패턴은 단일 서버보다 클러스터 배포시에 덜 유용하다. 오직 단일 서버에서만 이벤트를 발생시켜주기 떄문이다. 예를 들어 데이터 캐시를 갱신하는데 옵저버 패턴을 썼다면 그런 갱신은 오직 단일 서버에서만 발생할 것이다. 하지만, JMS를 사용한다면 클러스터 환경에서도 그러한 것이 가능하다. 하지만 API 복잡도와 성능 부담은 증가할 것이다.

저자의 경험상 옵저버 패턴은 EJB 단 보다는 웹 단에서 유용하다.

11장에서 옵저버 디자인 패턴을 애플리케이션 프레임워크에서 구현하는 방법을 살펴보겠다.

메서드 매개변수 뭉치기

public void setOptions(Font f, int lineSpacing, int linesPerPage,
                       int tabSize);

이런 것을

public void setOptions(Options options);

이렇게. (이건 리팩토링 기술에 있던것 같은데. 흠..)

주요 장점은 유연성이다. 더 많은 매개변수를 추가해도 메서드 시그너쳐를 변경하지 않아도 된다.

Command 디자인 패턴이 이런 접근 방법을 사용한다.

단점은 객체 생성이 많아질 수 있다는 것이다. 사용하는 메모리가 늘어나고 가비지 컬렉션을 필요로 하게 될 것이다. 객체는 힙 사이즈를 소비하지만, 기초타입은 그렇지 않는다. 이게 문제가 될지는 메서드가 얼마나 자주 호출되느냐에 달려있다.

 

[Expert One-on-One J2EE Design and Development] J2EE 프로젝트를 위한 설계 기술과 코딩 표준 1

참조: Expert One-on-One J2EE Design and Development 4장

좋은 코드란 무엇인가?

– 좋은 코드는 엄청난 변경이 필요없이도 확장이 용이하다.
– 좋은 코드는 읽기 쉽고 유지보수가 편하다.
– 좋은 코드는 문서화가 잘 되어있다.
– 좋은 코드는 나쁜 코드가 발생할 여지가 적다.
– 좋은 코드는 테스트하기 편하다.
– 좋은 코드는 디버깅하기 편하다.
– 좋은 코드는 중복 코드가 없다.
– 좋은 코드는 재사용된다.

J2EE 애플리케이션을 위한 객체지향 설계 권고사항

많은 개발자들이 J2EE API를 익히는데 시간을 많이 소비하지, 좋은 코딩 습관을 기르는데는 너무 시간을 투자하지 않는다. SUN의 J2EE 예제 애플리케이션만 봐도 그런 걸 알 수 있다.

저자(로드존슨)의 경험을 바탕으로 볼 때 좋은 객체지향 원칙을 따르느 것은 탁상공론에 그치는 것이 아니라 실용적인 가치를 전해준다.

객체지향 설계는 (J2EE나 심지어 자바 같은) 특정 구현 기술 보다도 더 중요하다. 좋은 프로그래밍 습관과 적절한 OO 설계는 좋은 J2EE 애플리케이션의 기반이다. 나쁜 자바 코드는 나쁜 J2EE 코드가 된다.

인터페이스를 통해 의존성 낮추기(Loose coupling)

“구현체가 아닌 인터페이스를 사용하여 프로그래밍하라.”
Progeam to an interface, not an implementation.

인터페이스 기반 접근방법의 장점

– 호출하는 쪽 코드를 변경하지 않고도 구현 내용을 변경할 수 있다.
– 인터페이스 구현의 자유. 오직 하나의 클래스 상속에 모든 걸 맡길 필요는 없다.
– 간단하게 테스트 구현체와 스텁 구현체를 만들어 제공할 수 있다.

딱딱한 상속 보다는 객체 조합을 선호하라(Composition)

“클래스 상속보다 객체 컴포지션을 선호하라”
Favor object composition over class inheritance

C++과 달리 자바의 클래스 상속은 한개의 클래스만 상속할 수 있다. 클래스 계층 구조는 매우 엄격하다. 클래스의 구현체 일부만 바꾸는 것이 불가능하다.(상위 클래스를 바꾸면 나머지도 다 바뀐다는 뜻인듯.) 하지만, 인터페이스로 그 부분을 (Strategy 패턴과 위임을 사용하여)캡슐화하면 문제는 해결된다.

객체 컴포지션은 클래스 상속보다 훨씬 유연하다. 자바 인터페이스는 위임을 자연스럽게 만들어준다. 객체 컴포지션은 -호출하는 쪽에서 행위를 표현하고 있는 인터페이스의 구현체를 주입하여- 객체의 행위를 실행중(런타임)에 변경할 수 있게 해준다. Strategy와 State 패턴이 이런 접근방법을 사용한다.

클래스 상속이 잘못 사용되는 예

– 간단한 인터페이스 구현이 필요한 상황인데도, 사용자가 추상 또는 구체 클래스를 상속받아 쓰도록 강제한다.  이렇게 하면 사용자 코드가 자신만의 상속 구조를 만들 수 있는 권한을 빼앗는 것이다.

– 하위 클래스가 사용할 헬퍼 메서드들을 상위 클래스에 놓고 클래스 상속을 사용한다. 만약 그 클래스 계층구조 밖에서 헬퍼 메서드를 호출할 필요가 있다면 어떻게 되는가? 객체 컴포지션이 낫다.

– 인터페이스 대신 추상 클래스를 사용하는 경우. Template Method 패턴처럼 추상 클래스가 매우 유용할 떄도 있다. 하지만 추상 클래스는 인터페이스의 대체제가 아니다. 인터페이스를 구현하는 유용한 과정일 뿐이다. 타입을 정의하고자 추상 클래스를 쓰지는 말아라.  다중 상속이 막혀있는 자바에게는 문제가 될 수 있다.

인터페이스는 간단하게 유지할수록 그 가치가 극대화 된다. 인터페이스가 복잡해지면 많은 양의 코드 구현을 해야하기 때문에 추상 클래스 또는 구체 클래스 상속을 강요하게 될 것이다. 따라서 그 가치가 떨어진다. 인터페이스의 적덜한 세밀도가 중요한 경우이다.
 
인터페이스 상속(클래스로 부터 기능을 상속받는 것이 아니라 인터페이스를 구현하는 것)은 클래스 상속보다 훨씬 유연하다.

그럼 클래스 상속이 나쁘다는 걸까? 전혀 아니다. 클래스 상속은 객체 지향 언어에서 코드를 재사용하는 강력한 방법을 제공한다. 하지만 높은 수준의 설계 접근 방법 보다는 구현 방식으로 생각해야 한다. 애플리케이션의 전반적인 설계로 강요하기 보다는 그 사용 여부를 우리가 선택할 수 있어야한다.

[Expert One-on-One J2EE Design and Development] 실용적인 데이터 접근 2

참고: Expert One-on-One J2EE Design and Development 9장

일반적인 JDBC 추상 프레임워크

JDBC API와 그 이슈를 이해하는 것으로 충분하지 않다.

JDBC API를 사용할 때는 항상 도무이 클래스를 사용하여 애플리케이션 코드를 간편화하라. 하지만, O/R 맵핑 계층을 손수 작성하지는 말자. O/R 맵핑이 필요하다면 기존의 솔루션을 사용하라.

동기

저수준 JDBC 코딩을 하는 것은 고통스럽다. 문제는 SQL이 아니라, 코드량이다. JDBC API는 일부 엘레강트하지 않으며 애플리케이션 코드에서 사용하기에는 너무 저수준이다.

SQL 자체는 간단하지만 이 쿼리를 조회하는데 30줄의 JDBC 코드가 필요하다. 거기에 중첩 try-catch까지 보니까 프레임워크 클래스로 리팩토링하고 싶은 강한 욕구를 느낀다.

*고수준 추상화 API*

간단한 추상화 프레임워크를 만들어서 JDBC API 사용을 훨씬 간편하게 할 수 있다.

AvailabilityQuery  availabilityQuery  =  new AvailabilityQuery (ds); 
List 1= availabilityQuery.execute(1, 1) ;

이렇게 할 수 있다. 이 쿼리 객체를 재사용할 수 있으며, execute() 메서드는 런타임 예외를 던진다. 즉 복구가 가능할 경우 필요에 따라 예외를 잡아서 처리할 수도 있다. JDO의 Query 인터페이스와 비슷하다.

목표

파레토 원칙을 기억하는가(80:20) 최선의 결과는 JDBC 추상화 계층으로부터 얻을 수 있다. 추상화 계층은 다음 요소들에 초점을 맞춘다.

– 너무 많은 코드
– 에러 발생시 깔끔한 클린업 – 대부분이 깨진 try-catch를 사용하는데 이를 방지한다.
– SQLException 다루리 – 검증형 예외일 필요가 없다.
– 사용자 코드를 작성하기 쉽게한다.

놀랍게도 이 문제를 다루는 프레임워크와 라이브러리가 많지 않다. 따라서 내가(로드 존슨) 예제 애플리케이션에서 사용할 용도로 프레임워클 개발했다.

이게 유일한 방법은 아니지만, 간단하고 매우 효율적이다.

예외 처리

JDBC API는 예외를 어떻게 사용하지 말하야하는지에 대한 교훈이다.

JDBC는 단일 예외 클래스를 사용한다. “뭔가 잘못됐다” 라는 것 빼고는 알 수가 없다. 이미 살펴봤다시피, 벤더 관련 코드와 SQL 예외를 구분할 수 있다.

다음은 모든 RDBMS에서 의미가 있는 에러들이다.
– 문법 에러
– 데이터 무결성 제약 위반
– SQL의 값을 부적절한 타입의 변수에 바인딩 시도

이런 문제는 java.lang.SQLException의 하위 클래스로 만들어져야 한다. 우리가 만들 추상화 프레임워크에서 풍부한 에러 계층 구조를 제공할 것이다.

JDBC 에러 처리를 할 때 다음의 이슈들도 다룬다.

– 우리가 만들 추상화 계층 코드가 JDBC에 묶이지 않기를 원한다. DAO 패턴 구현체에서 사용할 수 있도록 의도하고 있다. 만약 DAO를 사용하는 코드가 SQLException 같은 특정 리소스에 국한된 것을 처리해야 한다면 비즈니스 로직과 (DAO 패턴을 구현한)데이터 접근 구현체 간의 디커플링(decoupling)을 할 수가 없다. (메서드 시그너처에 들어가기 떄문에..) 따라서 JDBC API는 JDBC 관련 예외를 사용하더라도 우리가 만들 예외 계층 구조는 JDBC에 묶이지 않게 만들겠다.

– 4자에서의 검증형 예외와 비검증형 예외 논의에 따라 모든 예외를 런타임 예외로 만들겠다. JDO가 이런 접근 방법을 통해 좋은 결고를 냈다. JDBC 예외는 대부분 복구가 불가능하기 때문에 이런 방법이 적절하다.

– EJB를 쓴다면 블라 블라. 생략.

일반적인 데이터 접근 예외 계층구조

일반적인 데이터 접근 예외 계층구조를 만들어서 위와 같은 요구사항을 만족시킬 수 있다. JDBC 사용에만 국한 되지 않고 데이터베이스를 사용하는 DAO에서 모두 사용할 수 있다.

계층 구조의 최상위는 DataAccessException 클래스다. NestedRunitmeException을 상속받았다. 이 상위 클래스는 감쌓아야 할 예외를 스택 트레이스에 유지할 수 있게 해준다. 자세한건 4장에..

DataAccessException은 구체적인 데이터 접근 문제를 나타내는 하위 클래스를 가지고 있다.
– DataAccessResourceFailureException
자원을 가져오지 못한 경우에 발생. JDBC용 구현에서는 데이터베이스에서 connectino을 가져오지 못했을 때 발생한다.
– CleanupFailureDataAccessException
작업 수행을 잘 마치고 깨끗히 정리를 못했을 때 발생. JDBC용 구현에서는 Connection을 닫지 못했을 때 발생.
– Datalntegr ityViolationExcept ion
– InvalidDataAccessApiUsageException
– InvalidDataAccessResourceUsageException
부적절한 SQL을 날릴 때 발생
– OptimisticLockingViolationException
– DeadlockLoserDataAccessException
– UncategorizedDataAccessException
차마 분류하지 못한 것들…

JDBC 예외를 일반적인 예외로 변환하기

지금까지는 SQL 예외를 일반적인 예외로 변환하는 방법을 생각하지 않았다. 그렇게 하려면 SQLState 코드와 벤더 코드를 분석해야 한다. 하지만 SQLState로는 모든 문제를 분석하기에 적절치 않다. RDBMS 구현체 특화된 옵션들을 분석해야 한다.

변환 기능을 제공하는 인터페이스를 만든다. (보통 인터페이스를 사용하는 설계가 이식성을 얻을 수 있는 최선책이다.) 이 책에서는 SQLExceptionTranslater 인터페이스를 구현할 것이다.

public interface SQLExceptionTranslater {
    DataAccessException translate(String task, String sql, SQLException  sqlex);
}

*SQLState 코드를 사용하는 SQLExceptionTranslater 기본 구현체 코드*

정적인 데이터 구조(HasSet)를 만들어서 if/else 문을 줄였다.(묶을 수 있는 에러덩어리들을 하나의 맵에 넣어두고 map.contains()를 사용하여 if문 사용을 줄였군요.. 오호…)

*오라클에 특화된 구현체 OracleSQLExceptionTranslater*

벤더 코드가 SQLState 코드 보다 훨씬 많다.

2 단계 추상화 계층

강력하면서도 데이터베이스에 종속적이지 않은 예외 처리 계층 구조를 가지게 되었다. JDBC 사용을 간편하게하는 추상화 프레임워크를 구현해보자.

JDBC 프레임워크를 2 단계 추상화 계층으로 나눌 수 있다.

낮은 수준의 추상화는 com.interface21.jdbc.core 패키지에 있다. JDBC 워크프롤우와 예외 처리를 담당한다. 콜백 접근 방법을 취하였으며 애플리케이션 코드가 콜백 인터페이스를 구현토록 한다.

높은 수준의 추상화는 com.interface21.jdbc.core.object 패키지에 있다. JDO와 비슷하게 보다 객체 지향적인 방법을 제공한다. RDBMS 작업을 자바 객체로 표현할 수 있다.

JDBC 워크프롤우와 에러 처리를 담당하는 프레임워크

낮은 수준의 추상화는 JDBC 쿼리를 보내고 SQLException을 SQLExceptionTranslater를 사용하여 일반적인 예외 계층 구조로 변환한다.

다시 보는 “Inversion of Control”

일반적인 클래스 라이브러리 있는것처럼 애플리케이션 코드가 인프라 코드를 호출하는 것이 아니라.. 인프라 코드가 애플리케이션 코드를 사용하도록(이 부분이 “IoC”라 불리는 접근방법) 복잡한 예외 처리 코드를 그(인프라 코드) 안으로 넣어서 해결하는 방법을 보았다. 이런 접근 방법을 사용한 패키지를 “프레임워크”라 하고 그렇지 않은 것을 라이브러리라 한다.

com.interface21.jdbc.core 패키지

이 패키지에서 가장 중요한 클래스는 JdbcTemplate이다(오오.. 드디어.. 등장이다.) 핵심 워크 프롤우와 애플리케이션 코드 호출을 담당한다. jdbcTemplate에 있는 코드는 PreparedStatements 생성을 위임하여 쿼리를 실행하고 그 결과를 JDBC ResultSet에서 뽑아내는 콜백을 사용한다. 그 두 인터페이스가 바로 PreparedStatementCreator와 RowCallbackHandler 인터페이스다. 애플리케이션 개발자는 이 두 인터페이스 구현체만 만들면 된다. JDBC statement를 직접 실행하거나 예외를 처리할 필요가 없다.

preparedStatementCreator 인터페이스와 관련 클래스

preparedStatementCreator 인터페이스는 java.sql.PreparedStatement를 만들 애플리케이션 클래스가 반드시 구현해야 한다. 즉 SQL과 매개변수만 바인딩 시키면 jdbcTemplate이 실행해줄 것이다. 이것을 구현할 때 Connection을 가져오는 것이나 SQLException 처리는 신경쓰지 않아도 된다.

public interface PreparedStatementCreator {
PreparedStatement createPreparedStatement(Connection conn)
throws   SQLException; }

PreparedStatementCreatorFactory는 동일한 SQL에 매번 다른 매개변수로 PreparedStatementCreator 객체를 만들 때 도와주는 클래스다. 이 클래스는 높은 수준 추상화 프레임워크에서 빈번하게 사용한다.

RowCallbackHandler 인터페이스와 관련 클래스

RowCallbackHandler는 쿼리가 반환하는 ResultSet의 행에서 컬럼 값을 축출하는 클래스가 구현해야하는 인터페이스다. JdbcTemplate이 모든 ResultSet을 순회할 때 사용한다. 이 인터페이스는 SQLException을 그대로 나뒀다. JdbcTemplate이 처리한다.

public  interface RowCallbackHandler  {
void processRow(ResultSet  rs)   throws  SQLException; }

구현체는 컬럼의 수와 데이터 타입을 알고 있어야 한다.

RowCountCallbackHandler 클래스는 이 인터페이스의 구현체로 컬럼 이름과 타입 그리고 ResultSet의 행 갯수에 대한 정보를 가지고 있다. 비록 구체적인 클래스이지만, 애플리케이션 클래스가 이것을 상속받아서 사용한다.

ResultReader 인터페이스는 RowCallbackHandler를 확장하여 가져온 결과를 java.util.List에 저장하는 인터페이스다.

그밖의 클래스

JdbcTemplate 클래스는 SQLExceptionTranslator 객체 하나를 사용하여 SQLException을 일반화된 예외 계층구조로 변환한다. 중요한 건 JdbcTemplate의 동작을 매개변수화 했다는 것이다.

DataSourceUtils 클래는 javax.sql.DataSrouce 에서 Connection을 가져오는 static 메서드, SQLException을 일반화된 계층 구조로 변환하는 메서드, Connection을 닫는 메서드, JNDI에서 DataSource를 가져오는 메서드를 담고 있다.

JdbcTemplate은 DataSourceUtils 클래스를 사용한다.

JdbcTemplate 클래스의 핵심 워크 플로우

이 API가 전반적으로 java.sql.Connection 객체가 아니라 DataSource를 사용하는 이유는 다음과 같다.
– 그렇게 하지 않으면 connection을 어디에선가 얻어와야 하는데 그럼 애플리케이션 코드가 복잡해지고 DatsSrource.getConnection() 메서드를 사용한다면, 애플리케이션 코드에서 SQLException을 처리해야 할 것이다.

– JdbcTemplate이 사용한 connection을 닫는 것이 중요한데 connection을 닫을 때 예외가 발생할 수 있기 때문에 모든 JDBC 예외를 처리를 우리 프레임워크에 맡기고 싶다. 그러나 connection을 사요하면 connection을 외부에서 가져오고 그것을 JdbcTemplate에서 닫는 건 이상하고 이미 닫혀버린 connectino을 사용할 수 있는 여지를 만들게 된다.

JdbcTemplate 클래스 사용하기

조회하기

*JdbcTemplate 사용하여 조회(query)하는 예제 코드*

익명 내부 클래스르 사용하여 RowCallbackHandler와 PreparedStatementCreator 인터페이스를 구현했다.

가장 중요한 것은 JdbcTemplate을 사용함으로써 주요 에러 발생을 제거했다는 것이다. Connection이 닫히지 않을 위험이 없어졌다.

갱신하기

*JdbcTemplate 사용하여 갱신(update)하는 예제 코드*

PreparedStatementCreator 구현체를 만들고 그 객체를 jdbcTemplate.update()에 넘겨주면 끝. 단 한줄이다.

[Expert One-on-One J2EE Design and Development] 실용적인 데이터 접근 1

참고: Expert One-on-One J2EE Design and Development 9장

데이터 접근 기술 선택

J2EE 애플리케이션에서 사용할 수 있는 데이터 접근 기술 들을 두 개의 카테고리로 분류할 수 있다. SQL 기반과 O/R 맵핑 기반이다.

SQL 기반 기술

JDBC

JDBC는 SQL을 기반으로 한다. 저장 프로시저, 커스텀 쿼리, RDBMS에 특화된 기능들을 사용할 때 적절하다.


요한 것은 우리가 JDBC를 어떻게 사용하느냐이다. 나이브한(안일한) 접근 방법은 애플리케이션 코드에서 JDBC 코드와
SQL문을 사용하는 것인데 이것은 큰 재앙을 가져올 것이다. 전체 애플리케이션을 특정 영속화 전략에 묶어버려서, 데이터 스키마가
바뀔 때 문제가 생길 것이다. 하지만 다음의 가이드라인만 지킨다면 애플리케이션 코드에서 JDBC를 효율적으로 사용할 수 있다.

– JDBC 접근 코드를 비즈니스 로직에서 최대한 분리하라. JDBC 코드는 오직 DAO에만 있어야 한다.

JDBC API를 직접 사용하는 저수준(쌩짜) JDBC 코드를 기피하라. JDBC 에러 처리는 생산성을 심각하게 저하할 정도로
난잡하다. 에러 처리같은 저수준의 구체적인 내용들은 도우미 클래스로 빼버리고 고수준 API를 사용하라. SQL 제어권에 지장없이
그렇게 하는 것이 가능하다.

JDBC를 쓰는 것에 대해서는 아무 문제가 없다 다면, 세션 EJB같은 비즈니스 객체나 DAO에서 조차 JDBC를 직접 사용하지는 말자. 비즈니스 컴포넌트를 저수준 JDBC에서 분리할 수 있는 추상화 계층을 사용하라.

SQLJ

생략

O/R 맵핑 기술

O/R 맵핑 기술은 JDBC나 SQLJ 같은 API와는 전혀 다른 프로그래밍 모델이다. 이것을 사용해서도 J2EE 애플리케이션에서 DAO를 구현할 수 있다.

기업 수준에서 사용할만한 O/R 맵핑 솔루션을 제공하는 오픈 소스 제품이 없다.(로드 존슨이 이 책을 쓸 당시 하이버네이트가 없었거나.. 그리 알려지지 않았었나 봅니다.), 

상용 제품

생략

TopLink

생략

CoCoBase

생략

JDO

JDO는 J2EE에 중요한 기술이지만, 미션-크리티컬한 엔터프라이즈 애플리케이션에서 사용하기에는 아직 성숙도가 증명되지 않았다.(역시 책이 쓰여진 시점을 신경쓰며 읽으셔야겠죠)

샘플 애플리케이션에서 사용할 데이터 접근 기술 선택하기

(어찌저찌해서 JDBC 선택! 캐싱할 껀덕지도 별로 없고 저장 프로시저도 효율적으로 사용해야 하기 때문이라고 하는군요.)

JDBC 자세히 보기

그간의 경험을 통해 심각한 문제를 야기할 수 있는 J2EE 애플리케이션에서 sloppy(지져분한, 더러운, 진흙물이 튄것 같은) JDBC 코드를 보았다.

*에러 처리를 제대로 못한 예제 코드*
(대부분의 JDBC 코드와 con.close(); 까지 하나의 try-catch 문으로 묶여있습니다.)

이렇게 코딩을 하면 전체 애플리케이션이 깨지거나 데이터베이스가 뻗을 수 있다. con.close()까지 가기 전에 SQLException이 발생하면, 커넥션을 반환하지 못할 것이기 때문이다.

*에러 처리를 제대로 한 예제 코드*
(con.close();를 finally 블럭에서 null 체크를 한 다음에 수행하면서 try-catch로 묶어줬습니다.)

이렇게 하는것이 좀 더 견고하지만 너무 장황하다. 가장 간단한 JDBC 기능을 하나 수행하는데 39줄의 코드가 필요하다. 자.. 이제 JDBC API를 직접 쓰는것 말고 더 좋은 어떤 방법이 필요하다는 것을 분명히 알 수 있다.

SQLException에서 정보 축출하기

JDBC 예외 처리에서 또 다른 중요한 것은 SQLExcpetion에서 최대한 정보를 뽑아내는 것이다. 불행히도 JDBC는 이 걸 매우 복잡하게 만든다.

java.sql.SQLException은 getNextException() 메서드로 다음 SQLException을 받아올 수
있다. JDBC는 모든 예외 상황에 대해 오직 한 개의 예외 클래스만 사용하고 있다. 또한 벤더 코드와 SQLState 코드를
포함하고 있다.

벤더코드는 getErrorCode() 메서드가 반환하는 int 값이다. 그 이름이 암시하듯이 벤더 코드는 벤더마다 다를 수 있다. 따라서 이 코드에 의존하는 것은 이식성을 저해한다.

getSQLState() 메서드는 5자리 문자열을 반환한다. 이론적으로는 이식이 가능한 에러 코드다. 처음 두 자리는 고수준의 정보를 담고 있고 다음 세자리는 보다 상세한 정보를 담고 있다.

불행히도 SQLState 코드는 모든 데이터베이스에서 지원하지 않으며 문제에 대해 충분한 정보를 제공해주지도 못한다. 때에따라
벤더 코드가 구체적인 정보를 얻을 수 있는 유일한 방법이 된다. 대부분의 데이터베이스는 SQLState 코드보다 훨씬 많은 벤더
코드를 제공하며 더 잘 정리된 문서를 제공하고 있다.

java.sql.SQLWarning 예외도 SQLException 만큼이나 잘 이해할 필요가 있다. SQLException처럼
검증형 예외(checked exception)지만, JDBC API가 실제로 던지지는 않는다. 치명적이지 않은 SQL 에러로
여기고 그 내용을 ResultSet, Statement, Connection에 덭 붙여준다. 이런 JDBC API에
getWarnings() 메서드를 호출해서 가져올 수 있다.

이런 Warning이 JDBC를 직접사용하면 좋치않은 이유 중 하나다. JDBC를 직접사용하는 코드에서 Warging을 확인하는 과정을 추가하면 111줄의 코드가 더 추가된다. 실제로 하기엔 너무 많다.

PreparedStatement 질문

java.sql.PreparedStatement 인터페이스와 java.sql.Statement 인터페이스의 차이를 이해하는 것이
중요하다. prepared statent는 변수들을 바인딩할 위치와 각각의 위치에 매개변수를 설정할 수 있는 기능을 제공한다.

SQL에 적합한 문자 형태로 바꿔주는 역할을 해준다.

문자열 보다 더 효율적이다. 처음 prepared statement를 실행하면 파싱되고 데이터베이스에 의해 컴파일 되며
statement 캐시에 저장될 수 있다. 반면, 일반적인 SQL 문자열은 매번 변수 값이 달라질 경우 이렇게 캐시를 할 수가
없다.

JDBC 2.0은 prepared statement 캐싱을 필수로 하지 않았지만, 3.0에서는 표준으로 정했다.

[Expert One-on-One J2EE Design and Development] J2EE 아키텍처 3

Web Ties Design

이전에 보았던 각기 다른 네 가지 아키텍처에 모두 적용가능하다.

웹 단을 미들 단의 비즈니스 인터페이스에 의존하는 별도의 계층으로 구분하는 것이 중요하다. 그렇게 해야 비즈니스 객체를 바꾸지 않고도 웹 단을 수정할 수 있으며 웹 단을 참조하지 않고도 비즈니스 객체를 테스트할 수 있다.

MVC 아키텍처 패턴

이 패턴은 스몰토크 유저 인터페이스를 위해 처음으로 문서화 되었으며 가장 성공적인 OO 아키텍처 패턴중 하나이다.

중략~

웹 단에서 MVC 아키텍처 패턴을 사용하라. 스트럿츠같이 작성해야 할 애플리케이션 코드의 양을 줄여주며, 기본적인 MVC 패턴 구현체를 사용하라.

Designing Applications for Portability

특정 플랫폼에 종속적인 것은 추상화 계층을 사용하여 구현해야 한다. 인터페이스 자체는 플랫폼에 독립적이기 때문이다.

구현 이식성과 디자인 이식성의 차이를 알아야 한다. 구현 이식성이랑 어떤 서버에 배포하든 코드를 고칠 필요가 없는 것이고, 디자인 이식성은 분명한 인터페이스 약간을 다시 구현하기만 하면 어떤 서버에든 배포할 수 있는 것이다. 완전한 구현 이식성을 달성하기는 매우 많은 노력이 필요할 수 있지만, 디자인 이식성은 달성할만하며 충분한 비즈니스 가치를 가져다 준다. 이식성이 비즈니스 요구사항이 아니더라도, 디자인 이식성은 좋은 OO 디자인 실천으로 자연스래 달성할 수 있다.

Summary

– 분산 아키텍처의 단점: 보다 복잡하고, 구현, 테스트, 유지보수 하기 힘들다. 주의깊게 설계하지 않으면 성능 문제가 발생한다.
– 분산 아키텍처의 장점” 특정 경우에 따라, 보다 견고하고 확장성이 좋을 수 있다. 특정 비즈니스 요구사항에 따라 분산 아키텍처가 필요할 수도 있다.
=> 진짜 장점을 주지 못한다면 분산 아키텍처를 피하는 것이 최선이다.

– EJB 2.0에 웹 서비스가 포함되었다. 따라서 RMI에 종속적이지 않게 되었다. 로컬 인터페이스를 통해 EJB가 실행되고 있는 동일한 JVM에 접근할 수 있다. 따라서 분산 아키텍처에 종속적이지 않게 되었다.

– 언제 EJB를 사용할 것인가. EJB는 특정 문제를 매우 잘 해결할 수 있는 복잡하고, 강력한 기술이다. 하지만 대부분의 애플리케이션에 적절하지 않다.

– 나머지 dao, tiered architecture, four J2EE architecture, web-tire design issue 생략~