자바 웹 프로그래밍 Next Step - 7장 DB를 활용해 데이터를 영구적으로 저장하기.

 

들어가며

 

많은 장을 뛰어넘어 7장입니다. 4에서 5장의 경우 3장에서 요구사항을 보고 열심히 노력했다면 그다지 할 것이 많은 파트는 아닙니다. 해당 3장에서 정답을 알려주는 파트에 가깝기 때문입니다. 6장의 경우엔 이미 김영한님의 Spring MVC1을 수강하셨다면 익숙한 부분이라 따로 정리하지 않았습니다.

 

3장 ~ 6장에서 만들었던 서버는 클라이언트에게 http request를 받아 그에 따른 response를 내려주는 것에 집중했습니다. 하지만 대부분의 서버는 해당 요청에 따라 Database에 여러 데이터를 저장하고 관리합니다. (6장까지의 서버는 간단한 구현을 위해 HashMap을 통해 user 테이블을 관리하고 있습니다.)

 

7장에서는 회원 데이터를 DB에 저장하는 실습을 하게 됩니다. 이때 3장에서 프레임워크 없이 웹 서버를 만들면서 서블릿구현체와 유사한 코드를 만들어갔던 것처럼 7장에서는 DB에 sql 쿼리를 날리는 코드를 짜면서 JDBC 구현체에 가까운 코드를 짜게 되는 경험을 하게 됩니다. 즉 3장에서 7장에 걸쳐 java의 웹 기술을 직접 구현해보는 경험을 할 수 있습니다 :)

 

요구사항

1. DB 테이블 초기화

 

src/main/resources 디렉토리 아래 jwp.sql 파일에 초기 테이블 생성 코드가 있습니다. 

 

이 jwp.sql 파일은 톰캣 서버가 시작할 때 초기화하도록 ContextLoaderListener 클래스에 구현되어 있습니다. 초기화 작업을 위해 스프링 프레임워크 기능을 사용했는데 톰캣 서버가 실행 시 contextInitialized() 메소드를 호출하면서 초기화 작업을 진행합니다. 해당 정보를 통해 USERS 테이블을 알 수 있습니다.

 

 

2. 리팩토링

 

저희가 리팩토링할 코드는 UserDao입니다. 리팩토링의 메서드 중 하나인 findByUserId입니다. 이름처럼 user의 id 값에 따라 row 정보를 불러오는 코드입니다.

 

해당 코드만 보면 문제를 느끼지 못할 수도 있지만 DB에 SQL 쿼리를 넘기는 코드를 계속 짜야되는 상황이라고 생각해보면 문제를 느낄 수 있습니다. 데이터베이스에 쿼리 하나 날리기 위함인데 개발자가 구현해야할 코드는 너무나 많으며 대부분의 구현은 반복될 것입니다. 이와 같이 많은 중복이 있고 반복적인 부분은 공통 라이브러리화하여 관리를 해야합니다.

3강 설명에서 Request가 오고 response를 내보내는 부분에서 공통되는 부분을 라이브러리화하여 따로 관리했던 것을 떠올리면 됩니다 :)

 

그럼 공통된 부분과 DB 쿼리 요청에 따라 달라지는 부분을 구분해볼까요?

 

 

즉 개발자가 구현할 부분은 SQL 쿼리, 쿼리에 전달할 인자, SELECT 구문의 결과 조회된 데이터를 추출하는 3가지만 구현하면 되고 나머지는 라이브러리화해서 역할을 맡기는게 낫겠네요. 하나씩 하나씩 해보도록 하겠습니다 :)

 

메소드 분리 및 클래스 분리

 

개발자가 구현해야하는 부분만 따로 메소드 분리를 한 뒤 공통 부분은 클래스로 따로 분리했습니다. 요구사항을 듣고 가장 직관적으로 할 일이라고 많은 분들이 느끼셨을 것 같습니다 :) 이제 뼈대는 갖춘 셈입니다!

 

UserDao와의 의존성 분리

 

하지만 지금 만든 코드는 라이브러리라기엔 민망한 것 같습니다. UserDao에 강한 의존성을 가져 해당 클래스에 밖에 쓸 수 없기 때문입니다. 어떻게 하면 UserDao와의 의존성 분리를 할 수 있을까요?

 

템플릿 메소드 패턴을 사용하면 좋을 것 같습니다. 우선 insertJdbcTemplate를 추상 클래스로 선언해줍니다. 그리고 개발자가 구현해야할 부분, 즉 쿼리마다 달라지는 부분은 추상 메소드로 선언한 뒤, insertJdbcTemplate 익명 객체를 선언하는 쪽에서 해당 method를 구현하도록 하는 것입니다. 

 

 

 

이렇게 되면 UserDao와의 의존성도 분리했고 User table을 쓰는 쿼리라면 해당 라이브러리를 사용할 수 있게 되었습니다. 무엇보다 처음에 '개발자가 구현해야하는 부분만 구현하자'의 취지에도 잘 맞는 것 같습니다.

 

User 의존성 분리

 

 

저희의 update 문은 user에 의존성을 가지고 있습니다. 하지만 코드를 보면 굳이 그럴 이유가 없습니다. DB에는 User 테이블 외에도 많은 테이블이 쓰일 수 있습니다. 그런 테이블도 지원하기 위해 User와의 의존성을 분리해주도록 합시다!

 

콜백 인터페이스 

 

이제 라이브러리다운 형태를 갖춘 것 같습니다. 하지만 아직 남아있는 것이 있습니다. 저희의 라이브러리는 아직 SELECT 쿼리는 지원하지 않습니다. SELECT 쿼리에는 어떤 특징이 있을까요? SELECT는 DB에서 정보를 조회해오는 쿼리인만큼 조회해온 쿼리 정보를 라이브러리 사용자들에게 알려줘야 합니다.

하지만 어떤 쿼리 정보를 받아올 지 라이브러리 입장에서 모두 지원해주는 것은 쉽지 않습니다. 즉 해당 mapping 영역은 개발자가 구현해줘야할 영역입니다. 그렇다면 위처럼 템플릿 메소드 패턴을 적용 하면 되지 않을까요?

 

하지만 그렇게 구현하면 문제가 있습니다. 해당 추상 클래스를 사용하기 위해서 update 쿼리에서도 mapping 메소드를 dummy로라도 정의해줘야 합니다. 이는 사용하는 입장에서 직관적이지 않습니다. 쿼리문마다 개발자가 구현해야할 부분이 달라졌으니 그에 맞게 구현하도록 하는 것이 좋을 것 같습니다. 이를 위해 콜백 인터페이스를 사용하도록 하면 됩니다.

 

 

좋습니다. 이제 정말 라이브러리다운 코드가 된 것 같습니다!

 

사용자 편의성 증진 - 제네릭 사용

 

아직 사용하는데 불편함이 있습니다. Object를 반환하므로 매번 타입 변환을 해서 써야 된다는 불편함이 있습니다. 이는 java에서 제공하는 제네릭을 통해 간단하게 해결할 수 있습니다.

 

이제 정말 끝일까요? SQLException이 compileTImeException이므로 매번 처리해야된다는 점, 쿼리에 콜백 인터페이스 대신 가변 인자를 지원하는 점, 구현하는 입장에서 람다 활용해보기 등 더 많은 방법을 책에서는 제시해주고 있습니다. 남은 부분은 직접 해보시는 걸 추천드립니다 :)