1장. 사용자 관리 프로젝트로 알아본 스트럿츠 탄생 배경
Summary : 스트럿츠 프레임워크 워크북의 1장 원고를 통하여 스트럿츠 탄생 배경에 대하여 총 5번의 강좌를 통하여 알아본다. 이번 강좌에서는 사용자 관리 프로젝트의 모델 파트를 개발한다. 이번 강좌에서 개발되는 모델은 앞으로 진행할 3번의 강좌에서 공통적으로 사용한다.
2.모델(Model) 부분의 개발
지금까지 사용자 관리를 위한 요구분석을 진행하였다. 이 절에서는 앞절의 요구분석을 바탕으로 사용자 관리 프로젝트의 모델(Model)부분을 개발하도록 하겠다. 이 절에서 개발되는 모델은 계속해서 진행될 모델1 개발방식, 모델2 개발방식, 스트럿츠 프레임워크를 이용한 개발에 공통적으로 사용될 것이다.
이 절에서 개발되는 모델이 3가지 개발방식에 모두 사용되기 때문에 모델1 개발방식에는 적합하지 않을 수도 있다. 그렇지만, 모델1 개발방식도 이 절에서 살펴보는 방식으로 모델을 개발할 경우 유지보수가 편해질 수 있으며, JSP(Java Server Pages : 이하 JSP)의 복잡도를 다소나마 줄일 수 있을 것이다.
2.1 실행환경 설정
사용자 관리 모델의 직접적인 개발에 들어가기에 앞서 기본적인 실행환경을 설정하도록 하겠다. 이 절에서는 실행환경 설정에 대한 설명은 최대한 줄이도록 하겠다. 부족한 부분은 부록과 다양한 외부 문서를 링크하는 방식을 취하도록 하겠다.
이 책에서 다룰 예제들의 기본적인 실행환경은 다음과 같다.
JDK : 1.3이상
Servlet Server : Jakarta-Tomcat 4.1.24
관계형 데이터베이스 : MySQL 4.0.x
- 다운로드 : http://www.mysql.com
JDBC 드라이버 : MySQL JDBC 드라이버 2.0.14
Connection Pooling :
빌드 툴 : Apache Ant 1.5.3
- 다운로드 : http://ant.apache.org
스트러츠 프레임워크 : Jakarta-Struts 1.1
위에서 살펴본 항목들이 이 책의 예제들을 개발하고, 테스트해보기 위한 기본적인 환경이다. 컨넥션 풀링을 사용한 이유는 최근에 진행하는 대부분의 프로젝트에 컨넥션 풀링을 사용하고 있기 때문에 실전 프로젝트에서 직접 사용이 가능하도록 하기 위해서이다. MySQL JDBC 드라이버는 최신 버전인 3.0.8을 사용하지 않고 2.0.14로 사용한 이유는 최신 버전인 3.0.8는 데이터를 읽을 때 버그가 있기 때문이다.
이 책의 예제를 실행하기 위해서는 JDK, MySQL데이터베이스, Tomcat 애플리케이션 서버, ANT가 설치되어 있어야 한다. 나머지 패키지는 예제 소스에 포함되어 있기 때문에 신경쓰지 않아도 된다. 예제를 실행하는 환경을 위와 같이 선택한 이유는 예제 소스를 최대한 쉽게 테스트해볼 수 있도록 위함이다. 모든 툴이 설치가 간단하고 좋은 사양의 컴퓨터가 아니라도 테스트가 가능하다.
모든 예제소스는 ANT툴로 빌드하여 서버에 디플로이 시키기 때문에 예제를 실행하기 위하여 클래스패스를 추가할 필요가 없다. ANT툴에 대한 자세한 사용방법은 부록2를 참조하기 바란다.
JDK, MySQL데이터베이스, Tomcat 애플리케이션 서버, ANT중 설치하는 방법을 모르는 것이 있다면 이 책의 부록 1을 참고하여 예제를 실행해볼 수 있는 기본 환경을 세팅하기 바란다.
2.2 사용자 관리 테이블과 생성 쿼리
사용자 관리 테이블은 1절의 요구분석에서
정의했듯이 사용자 아이디, 비밀번호, 사용자 이름, 사용자 이메일주소만을 포함하도록 생성하였다. 가능한 작은 규모의 프로젝트를 만들기 위한 의도였다.
# DROP TABLE USERINFO; CREATE TABLE USERINFO ( userId varchar(12) NOT NULL, password varchar(12) NOT NULL, name varchar(20) NOT NULL, email varchar(50), PRIMARY KEY (userId), INDEX USERINFO_userId_idx (userId)); INSERT INTO USERINFO VALUES('admin', 'admin', '관리자', 'admin@javajigi.net');
2.3 데이터베이스 설정 및 테이블 생성
사용자 관리 모델을 만들기 위하여 먼저 데이터베이스 정보를 설정한 다음 테이블을 생성해야 한다. 데이터베이스 설정은 컨넥션 풀링에서 사용하고 있는 /chapter1/src/dbpool.properties의 값을 자신의 데이터베이스 환경에 맞도록 설정하면 된다. dbpool.properties의 내용은 이 장 초입부의 예제 실행 방법란을 참고하기 바란다.
사용자 관리용 테이블 생성은 ANT를 이용하였다. ANT를 이용한 이유는 독자들의 테스트를 쉽고 빠르게 하기 위해서이다. 많은 책들의 경우 너무도 많은 환경 설정과 클래스패스를 추가해주어야 하기 때문에 독자들이 테스트하는데 많은 시간을 소비해왔다. 따라서 필자는 테스트하는데 가능한 적은 시간을 소비하고, 책에서 다루고있는 내용에 독자들이 많은 시간을 투자하여 공부할 수 있게하기 위함이다. 그 일환으로 테이블의 생성 또한 ANT를 이용하였다.
ANT를 이용하여 테이블을 생성하는 자세한 방법은 이 장 초입부의 예제 실행 방법란을 참고하기 바란다.
2.4 모델 개발
많은 개발자들이 모델이라는 용어를 사용한다. 하지만, 대부분의 개발자들이 모델파트에서 담당해야하는 부분이 무엇인지에 대하여 모르고 있다. 필자 또한 MVC 아키텍처(모델, 뷰, 컨트롤러 계층으로 나뉘어 개발. 이하 MVC)에서 모델파트가 담당해야하는 부분이 무엇이라고 정확하게 경계선을 그어서 말하기는 힘들다. 각 계층의 경계선을 명확히 했다고 하지만 경우에 따라 경계가 무너지는 경우도 종종 본다.
하지만 간략하게나마 모델이 담당해야할 역할에 대해서 규명할 필요는 있다고 생각한다. 따라서 필자는 모델파트에서 담당해야하는 가장 큰 역할을 다음 두가지로 규명하겠다.
첫째, 애플리케이션의 비즈니스 로직적인 부분을 담당한다.
둘째,
애플리케이션이 데이터베이스, 레거시 시스템, 파일 시스템과의 데이터 조작등의 작업을 담당한다.
이와 같은 기본적인 개념하에 이 책에서 진행하게될 모델 예제들을 위한 기본적인 클래스 설계는 다음과 같이 진행하도록 하겠다.
첫째, 객체의 정보를 포함하고 있는 도메인 클래스이다. 도메인 클래스의 역할은 사용자가 입력한 정보를 모델에 전달하는 역할과 데이터베이스의 정보를 JSP에 전달하는 매개자 역할을 한다. 이 개념은 Value Object 패턴과 Data Transfer Object 패턴이라는 이름으로 많이 알려져 있다. 각각의 기능으로 나누어 개발할 수도 있지만 대부분의 Value Object와 Data Transfer Object는 같은 속성과 setter, getter를 가지고 있기 때문에 필자는 하나의 클래스로 이 두가지 기능을 모두 포함하도록 구현하기를 좋아한다.
참고문서
Value Object 패턴 :
- "Patterns of Enterprise Application Architecture", Martin Fowler, 2003, Addison Wesley 486Page
- "Core J2EE Patterns" Deepak Alur/John Crupi/Dan Malks, 2001, Prentice Hall 261Page
Data Transfer Object패턴 :
- "Patterns of Enterprise Application Architecture", Martin Fowler, 2003, Addison Wesley 401Page
둘째, 데이터베이스나 기존의 레거시 시스템과의 데이터작업을 전담하게될 데이터 접근 객체(Data Access Object:DAO)클래스이다. 모델 개발시 중요한 역할중의 하나는 데이터베이스나 기존의 레거시 시스템과의 데이터 교환작업이다. 비즈니스 로직과 별개로 이 같은 작업을 전담하는
클래스가 필요하게 되었다. 따라서 앞으로 진행되는 모든 프로젝트에서 데이터 접근 로직은 전담하는 이 같은 DAO클래스가 존재할 것이다. DAO클래스라고 해서 색다른 것이 아니다. 단지 독자들이 JSP와 여러 클래스에서 데이터베이스와의 데이터교환을 하던 부분을 하나의 클래스에서 전담하는 역할을 한다고 생각하면 된다.
참고문서
Data Acces Object 패턴 :
- "Core J2EE Patterns" Deepak Alur/John Crupi/Dan Malks, 2001, Prentice Hall 390Page
셋째, 비즈니스 로직을 전담하게될 비즈니스 객체(Business Object:BO)클래스이다. 단, 비즈니스 로직이 간단할 경우에 이 클래스는 생략할 수 있다. 모델을 개발할 때 또 하나의 중요한 부분은 비즈니스 로직을 담당하는 부분이다. 프로젝트의 성격에 따라 비즈니스 로직이 복잡한 경우에는 비즈니스 로직을 전담할 비즈니스 객체가 필요한 경우가 발생할 수 있다. 하지만, 간단한 경우에는 네번째로 살펴볼 Manager클래스에서 비즈니스 로직을 담당할 수도 있다.
넷째, 모델1 개발방식이라면 JSP, 모델2 개발 방식(MVC)이라면 컨트롤러(Controller)가 직접적으로 사용하게될 API를 전담하는 Manager클래스이다. Manager클래스를 만들 경우 비즈니스 로직이 간단할 경우에는 Manager클래스에서 비즈니스 로직을 수행할 수도 있으며, 모델을 접근할 때 Manager클래스만을 통해 접근하기 때문에 API를 사용하기 편하다는 장점이 있다. 사용자가 접근해야 하는 클래스가 여러 클래스에 분산되어 있다면, API사용에 익숙하지 않은 개발자들은 모델을 사용하는데 어려움을 느낄 수 있다. Manager클래스를 두는 방식을 Session Facade 패턴이라고 일컫는다.
참고문서
Session Facade 패턴 :
- "Core J2EE Patterns" Deepak Alur/John Crupi/Dan Malks, 2001, Prentice Hall 291Page
지금까지 살펴본 4개 클래스의 관계를 살펴보면 [그림 1-8]과 같다.

모델 API를 사용하기 위하여 Manager클래스만을 접근하는 것을 볼 수 있다. Manager클래스는 도메인 클래스에 담긴 데이터를 비지니스 객체와 데이터 접근 객체에 전달하는 역할을 하며, 작업후 결과 반환되는 도메인 클래스를 JSP, Servlet, Action에 전달하는 역할을 한다.
사용자 관리는 1절의 요구사항에서도 볼 수 있듯이 복잡한 비즈니스 로직을 포함하고 있지 않다. 따라서 비즈니스 객체를 따로 두지않고 Manager클래스에서 비즈니스 로직을 수행하도록 구현하였다. 사용자 관리 모델파트의 클래스 다이어그램은 다음과 같다.[그림1-9]

먼저 사용자의 정보를 담고 있는 도메인 클래스를 살펴보자. 사용자 관리의 도메인 클래스는 USERINFO테이블의 각 칼럼과 일치하는 속성들을 가지고 있으며, 각각의 속성에 대한 setter와 getter 메소드를 가진다.
package net.javajigi.user; /** * 사용자 관리를 위하여 필요한 도메인 클래스. * USERINFO 테이블의 각 칼럼에 해당하는 setter와 getter를 가진다. */ public class User { private String userId = null; private String password = null; private String name = null; private String email = null; public String getEmail() { return email; } public String getName() { return name; } public String getPassword() { return password; } public String getUserId() { return userId; } public void setEmail(String string) { email = string; } public void setName(String string) { name = string; } public void setPassword(String string) { password = string; } public void setUserId(String string) { userId = string; } /** * 비밀번호가 일치하는지 여부를 결정하는 메소드. */ public boolean isMatchPassword(String inputPassword) { if (getPassword().equals(inputPassword)) { return true; } else { return false; } /* This is the better. return getPassword().equals(inputPassword); */ } }
[예제 1-1]의 User 도메인 클래스에도 비밀번호가 일치하는지의 여부를 체크하는 간단한 메소드를 가진다.
UserDAO는 사용자 관리 테이블인 USERINFO와의 데이터 작업을 전담하는 클래스이다.
package net.javajigi.user; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import net.javajigi.db.ConnectionManager; /** * 사용자 관리에서 데이터베이스와의 작업을 전담하는 클래스. * UserInfo 테이블에 사용자를 추가, 수정, 삭제, 검색등의 작업을 한다. */ public class UserDAO { /** * 사용자 관리 테이블에 새로운 사용자 생성. */ public int create(User user) throws SQLException { Connection con = null; PreparedStatement pstmt = null; try { StringBuffer insertQuery = new StringBuffer(); insertQuery.append("INSERT INTO USERINFO VALUES "); insertQuery.append("(?, ?, ?, ?)"); con = ConnectionManager.getConnection(); pstmt = con.prepareStatement(insertQuery.toString()); pstmt.setString(1, user.getUserId()); pstmt.setString(2, user.getPassword()); pstmt.setString(3, user.getName()); pstmt.setString(4, user.getEmail()); int result = pstmt.executeUpdate(); pstmt.close(); con.close(); return result; } finally { if (pstmt != null) { pstmt.close(); } if (con != null) { con.close(); } } } /** * * 기존의 사용자 사용자 정보를 수정. */ public int update(User user) throws SQLException { //구체적인 구현 부분은 첨부되는 CD의 예제소스 참조. } /** * 사용자 아이디에 해당하는 사용자를 삭제. */ public int remove(String userId) throws SQLException { //구체적인 구현 부분은 첨부되는 CD의 예제소스 참조. } /** * 사용자 아이디 정보를 데이터베이스에서 찾아 User 도메인 클래스에 * 저장하여 반환. */ public User findUser(String userId) throws SQLException { //구체적인 구현 부분은 첨부되는 CD의 예제소스 참조. } /** * 사용자 리스트를 만들기 위한 부분으로 현재 페이지와 * 페이지당 카운트수를 이용하여 해당부분의 사용자만을 List콜렉션에 * 저장하여 반환. */ public List findUserList(int currentPage, int countPerPage) { //구체적인 구현 부분은 첨부되는 CD의 예제소스 참조. } /** * 인자로 전달되는 아이디를 가지는 사용자가 존재하는지의 * 유무를 판별. */ public boolean existedUser(String userId) throws SQLException { //구체적인 구현 부분은 첨부되는 CD의 예제소스 참조. } }
[예제 1-2]를 보면 데이터베이스와의 데이터 교환 작업 이외에 다른 작업은 존재하지 않음을 확인할 수 있다.
package net.javajigi.user; import java.sql.SQLException; import java.util.List; /** * 사용자 관리 API를 사용하는 개발자들이 직접 접근하게 되는 클래스. * UserDAO를 이용하여 데이터베이스에 데이터 조작 작업이 가능하도록 하며, * 데이터베이스의 데이터들을 이용하여 비지니스 로직을 수행하는 역할을 한다. * 비지니스 로직이 복잡한 경우에는 비지니스 로직만을 전담하는 클래스를 * 별도로 둘 수 있다. */ public class UserManager { private UserManager() { } public static UserManager instance() { return (new UserManager()); } public int create(User user) throws SQLException, ExistedUserException { if (getUserDAO().existedUser(user.getUserId())) { throw new ExistedUserException(user.getUserId() + "는 존재하는 아이디입니다."); } return getUserDAO().create(user); } public int update(User user) throws SQLException { return getUserDAO().update(user); } public int remove(String userId) throws SQLException { return getUserDAO().remove(userId); } public User findUser(String userId) throws SQLException, UserNotFoundException { User user = getUserDAO().findUser(userId); if (user == null) { throw new UserNotFoundException(userId + "는 존재하지 않는 아이디입니다."); } return user; } public List findUserList(int currentPage, int countPerPage) throws SQLException { return getUserDAO().findUserList(currentPage, countPerPage); } public boolean login(String userId, String password) throws SQLException, UserNotFoundException, PasswordMismatchException { User user = findUser(userId); if (!user.isMatchPassword(password)) { throw new PasswordMismatchException("비밀번호가 일치하지 않습니다."); } return true; } private UserDAO getUserDAO() { return new UserDAO(); } }
[예제 1-3]은 사용자 관리 API를 사용하는 개발자들이 직접 접근하게 되는 클래스로서UserDAO를 이용하여 데이터베이스에 데이터 조작 작업이 가능하도록 하며, 데이터베이스의 데이터들을 이용하여 비지니스 로직을 수행하는 역할을 한다.
이상으로 사용자 관리를 위한 3개의 클래스를 모두 살펴보았다. 사용자 관리 프로젝트의 규모가 작기 때문에 위 3개의 클래스만으로 모델을 구현하는 것이 가능했다.
강좌에 대하여
작성자 : 박재성
작성일 : 2005년 2월 20일
문서이력 :
- 2005년 2월 20일 박재성 문서 최초 생성
참고 자료
- 강좌에서 사용한 예제소스. : http://www.javajigi.net/resources/src/strutsworkbook/chapter1.zip
- Struts 프레임워크 사이트 : http://jakarta.apache.org/struts/index.html
- 스트럿츠 프레임워크 워크북 : http://www.hanbitbook.co.kr/look.php?isbn=89-7914-254-4