자바지기 샘플 애플리케이션의 목적
작은 샘플 애플리케이션을 만듦으로서 Spring 프레임워크를 전반적으로 이해하는데 도움을 주기 위함이다. 너무도 복잡한 비지니스 로직을 가지는 애플리케이션의 경우에는 Spring 프레임워크가 가지고 있는 기능보다는 비지니스 로직에 더 많은 시간과 노력을 투자해야한다. 이 같은 이유 때문에 비지니스 로직이 많이 포함되어 있지 않은 작은 애플리케이션을 만들어보면서 Spring 프레임워크의 기능들을 이해하고자한다. 이 샘플 애플리케이션은 사용자 관리 기능과 자료실 게시판 기능 두가지를 가지고 있다.
요구분석
기능 리스트
사용자 관리 - Admin 권한이 있는 사용자
- 새로운 사용자를 추가할 수 있다.
- 현재 시스템에서 관리할 수 있는 사용자 리스트를 볼 수 있다.
- 기존 사용자의 정보를 조회할 수 있다.
- 기존 사용자의 정보를 수정할 수 있다.
- 기존 사용자를 삭제할 수 있다.
- 로그인 할 수 있다.
- 로그아웃 할 수 있다.
사용자 관리 - Admin 권한이 없는 사용자
- 자바지기 샘플 애플리케이션에 회원가입할 수 있다.
- 회원가입이 된 사용자라면 로그인할 수 있다.
- 로그인한 상태에서 자신의 정보를 수정할 수 있다.
- 로그인한 상태라면 로그아웃할 수 있다.
게시판 관리
- 게시물 리스트를 볼 수 있다.
- 새로운 게시물을 추가할 수 있다. 게시물 추가시 파일 첨부가 가능하다.
- 기존의 게시물을 조회할 수 있다. 게시물 조회시 첨부 파일의 삭제가 가능하다.
- 기존의 게시물을 수정할 수 있다. 게시물 수정시 새로운 첨부파일의 추가가 가능하다.
- 게시물을 삭제할 수 있다. 게시물 삭제시 첨부파일까지 모두 삭제해야 한다.
Exception 처리방안
- 데이터베이스 다운과 같이 프로그램적으로는 해결하기 힘든 에러가 발생할 경우에는 시스템 관리자에게 메일을 발송한다.
- 치명적 에러 상황이 아니더라도 프로그램적인 버그로 인해서 발생하는 Runtime Exception과 같은 경우에도 메일을 전송한다.
- 메일 전송은 메일 서버에 Async로 메일 전송 이벤트를 발생시키는 방법으로 처리한다.
- Exception이 발생하는 상황에 따라 Checked Exception과 Unchecked Exception으로 구분하여 Exception을 처리한다. 너무나도 무분별한 Checked Exception의 사용을 피하도록 한다. 다음과 같은 Exception은 Unchecked Exception으로 처리한다.
- Checked Exception과 Runtime Exception 문서에서 가이드라인으로 제시하는 방법을 참고한다.
- 모든 SQLException은 Unchecked Exception으로 처리한다.
- EJB를 Lookup할 때 발생하는 Exception은 Unchecked Exception으로 처리한다.
- Remote EJB를 호출할 때 발생하는 RemoteException예외는 Unchecked Exception으로 처리한다.
- 비지니스 로직을 수행하는 중 발생하는 비지니스적인 Exception을 제외하고는 모든 Exception을 Unchecked Exception으로 처리한다. 예를 들어 돈을 인출할 때 인출할 돈보다 예금액이 부족할 경우 발생하는 Exception은 Checked Exception을 사용한다.
- 사실 이 같은 비지니스 로직 Exception을 제외하고 다른 Exception을 Catch한다고 해서 프로그램적으로 특별히 처리할 수 있는 부분이 없다.
- 이유를 알 수 없는 에러라 할지라도 클라이언트에게 직접적으로 500 에러 메세지를 표시하지 않는다. 500에러가 발생할 경우에는 특정 페이지로 이동하도록 하며, 특정 메세지를 출력하도록 한다.
- 404 에러가 발생했을 경우에는 해당 페이지가 존재하지 않는다는 메세지를 출력해주며, 메인 페이지로 이동할 수 있는 링크를 제공한다.
- Exception 발생시 사용하게될 메세지는 properties 파일을 사용하여 처리하도록 한다.
 | Checked Exception과 Unchecked Exception의 적절한 사용
|
 | 정보의 전달이 가능한 예외 만들기
- "J2EE 설계와 개발" 175-177 페이지에서 다루고 있는 ErrorCoded 인터페이스를 사용하는 방법을 이용하여 예제 소스를 구현하는 것도 좋은 방법으로 생각된다.
|
Logging 처리 방안
- 비지니스 로직을 구현하고 있는 Service용 클래스와 Persistence Layer를 구성하고 있는 DAO클래스는 메써드의 시작하고 끝나는 시점에 Debugging을 위한 Logging을 한다.
- Unchecked Exception이 발생할 경우에는 Unchecked Exception에 대한 Stack Trace를 Logging으로 남긴다.
- Logging은 각 상황에 따라 INFO, DEBUG, WARN, ERROR 레벨로 나누어서 저장한다.
- Logging시에는 현재 Logging을 위한 Logging Level을 체크하는 구문을 반드시 추가한다. 예를 들어 Debug용 메세지를 Logging한다면 다음과 같이 작성해야한다.
if (logger.isDebugEnabled()) {
logger.debug("Disconnecting " + this);
}
 | 위와 같이 작성해야 하는 이유는 Log Level을 체크하는 부분이 존재하지 않는다면 Debug를 위한 메세지를 먼저 생성하게 된다. 대부분의 Logging 메세지는 String의 조합으로 작성되는 경우가 많다. Log Level을 체크하는 부분이 존재하지 않는다면 먼저 Logging을 위한 메세지를 생성하는 부분이 실행된 다음 Log Level을 체크하고 Logging해야될지 말아야 될지를 결정하게 된다. 이렇게 될 경우 Logging을 위한 메세지를 생성하면서 그 만큼의 부하가 발생하게 된다.
그러나 위처럼 Log Level을 체크하는 부분을 가진다면 큰 부하없이 Logging 기능을 추가하는 것이 가능할 것이다. |
Logging 명명 규칙
- 비지니스 로직을 구현하고 있는 Service 클래스에서 각각의 메써드
- 메써드의 실행이 시작될 때 "MethodName 시작", 메써드가 끝나는 곳에 "MethodName 종료"로 Logging을 진행한다.
- 메써드가 시작될 때 해당 메써드에 전달된 인자를 Logging한다.
자바지기 샘플 애플리케이션 화면
자바지기 샘플 애플리케이션 화면 : 자바지기 샘플 애플리케이션을 만들기 위하여 작성해야할 화면이다.
데이터베이스 스키마
샘플 애플리케이션의 ERD는 다음과 같다.
자바지기 샘플 애플리케이션에서 사용할 테이블은 다음과 같다.
BOARD테이블의 컬럼 및 내용
| 컬럼 이름 |
SQL 타입 |
내용 |
기타 |
| BOARDNO |
INT |
게시물 번호 |
Primary Key |
| TITLE |
VARCAHR |
게시물 제목 |
| NAME |
VARCAHR |
작성자 이름 |
| EMAIL |
VARCAHR |
작성자 이메일 주소 |
| PASSWORD |
VARCAHR |
작성자 비밀번호 |
| CREATEDATE |
VARCAHR |
생성일자 |
| CONTENT |
TEXT |
게시물 내용 |
| HITCOUNT |
INT |
게시물 조회수 |
BOARDSEQUENCE테이블의 컬럼 및 내용
| 컬럼 이름 |
SQL 타입 |
내용 |
기타 |
| VALUE |
INT |
Sequence 값 |
BOARDFILE테이블의 컬럼 및 내용
| 컬럼 이름 |
SQL 타입 |
내용 |
기타 |
| FILENO |
INT |
파일 번호 |
Primary Key |
| BOARDNO |
INT |
게시물 번호 |
| FILESIZE |
INT |
첨부된 파일 크기 |
| FILENAME |
VARCAHR |
파일 이름 |
| CONTENTTYPE |
VARCHAR |
첨부파일의 컨텐트 유형 |
| TEMPFILENAME |
VARCAHR |
임시 파일 이름 |
BOARDFILESEQUENCE테이블의 컬럼 및 내용
| 컬럼 이름 |
SQL 타입 |
내용 |
기타 |
| VALUE |
INT |
Sequence 값 |
USERINFO테이블의 컬럼 및 내용
| 컬럼 이름 |
SQL 타입 |
내용 |
기타 |
| USERID |
VARCHAR |
사용자 아이디 |
Primary Key |
| PASSWORD |
VARCHAR |
사용자 비밀번호 |
| NAME |
VARCHAR |
사용자 이름 |
| EMAIL |
VARCHAR |
사용자 이메일 주소 |
| ADMINYN |
TINYINT |
관리자 유무 |
위 테이블을 생성하기 위한 쿼리는 다음과 같다.
DROP TABLE BOARD;
CREATE TABLE BOARD (
BOARDNO int(11) NOT NULL,
TITLE varchar(80) NOT NULL,
NAME varchar(10) NOT NULL,
EMAIL varchar(30),
PASSWORD varchar(10) NOT NULL,
CREATEDATE varchar(14) NOT NULL,
CONTENT text NOT NULL,
HITCOUNT int(11) DEFAULT '0' NOT NULL,
PRIMARY KEY (BOARDNO),
INDEX BOARD_no_idx (BOARDNO)
);
DROP TABLE BOARDFILE;
CREATE TABLE BOARDFILE (
FILENO int(11) NOT NULL,
BOARDNO int(11) NOT NULL,
FILESIZE int(11) NOT NULL,
FILENAME varchar(200) NOT NULL,
CONTENTTYPE varchar(200),
TEMPFILENAME varchar(100) NOT NULL,
PRIMARY KEY (FILENO)
);
DROP TABLE BOARDSEQUENCE;
CREATE TABLE BOARDSEQUENCE (
VALUE int(11) NOT NULL
);
INSERT INTO BOARDSEQUENCE VALUES(0);
DROP TABLE BOARDFILESEQUENCE;
CREATE TABLE BOARDFILESEQUENCE (
VALUE int(11) NOT NULL
);
INSERT INTO BOARDFILESEQUENCE VALUES(0);
DROP TABLE USERINFO;
CREATE TABLE USERINFO (
USERID varchar(12) NOT NULL,
PASSWORD varchar(12) NOT NULL,
NAME varchar(20) NOT NULL,
EMAIL varchar(50),
ADMINYN tinyint(1) NOT NULL,
PRIMARY KEY (userId),
INDEX USERINFO_userId_idx (userId)
);
INSERT INTO USERINFO VALUES('admin', 'admin', '관리자', 'admin@javajigi.net', 1);
INSERT INTO USERINFO VALUES('javajigi', 'javajigi', '자바지기', 'javajigi@javajigi.net', 0);
설계 문서
클래스 다이어그램
아키텍처
샘플 애플리케이션의 아키텍처는 가능한 단순한 형태로 가져가는 방향으로 설계하였다. 그렇지만 Spring 프레임워크의 활용가치를 충분히 느낄 수 있도록 하기 위하여 노력하였다. 특히 Spring AOP의 사용과 비동기 처리를 위하여 Spring 기반하에서 POJO MDB를 이용하여 구현하도록 설계하였다. 자바지기 샘플 애플리케이션의 기본적인 소프트웨어 아키텍처를 보면 다음과 같다.
메일 애플리케이션의 기본적인 아키텍처는 Spring Container 기반하에서 세개의 계층으로 나뉜다. 세개의 계층은 Persistence Layer, Business Layer, Presentation Layer로 나누게 된다. 아키텍처를 계층화 함으로서 각 계층별로 독립성을 유지하기 위하여 노력하였다. 각 계층별 사용 기술을 보면 다음과 같다.
- Persistence Layer : Spring JDBC
- Business Layer :
- 비지니스 로직 : Spring Container에 의하여 관리되는 POJO Bean
- Transaction, Logging : Spring AOP
- Presentation Layer :
- UI : JSP 또는 Velocity
- MVC : Spring MVC
- Layout : Sitemesh
- 분산환경 지원 : EJB
- 메일 처리 : 메일은 별도의 서버를 두어서 처리하며, Spring의 POJO MDB 기능을 활용하여 비동기 방식으로 메일 이벤트를 처리하도록한다. 비동기시에 사용할 MQ 서버는 ActiveMQ를 사용하도록 한다.
위 기술들은 각 계층에서 독립적으로 사용되고 있다. 따라서 추후 해당 기술을 적용하다 문제점이 발생하는 부분이 있다면 작은 노력으로 변경하는 것이 가능하다. 그러나 위 아키텍처의 중심이라고 할 수 있는 Spring Container 기능은 변경없이 사용하는 것으로 한다.
애플리케이션 서버에 대한 개발 전략
애플리케이션 서버는 기본적으로 Tomcat 서블릿 컨테이너를 사용하도록 한다. Tomcat 서버로 개발을 진행하고 추후에 애플리케이션 서버가 확정되면 해당 서버로 Deploy하는 것으로 한다. 대부분의 애플리케이션 서버들이 서블릿 스펙에 대해서는 표준을 잘 따르고 있기 때문에 큰 변경없이 개발한 애플리케이션을 배포하는 것이 문제가 되지 않을 것으로 생각한다.
만약 분산환경을 지원해야 되는 상황이 발생한다면 기본 애플리케이션 서버는 JBoss로 진행하는 것으로 하며, 다른 애플리케이션 서버가 선정되었을 경우에는 해당 애플리케이션 서버를 위한 Descriptor파일을 만듦으로서 지원하도록 한다. 분산환경을 지원할 경우 EJB는 단지 인터페이스를 위한 Facade로서만 동작하기 때문에 기존의 개발된 애플리케이션에는 영향이 없을 것이다. 따라서 이 부분에 대해서는 프로젝트 초기에 고민할 필요없이 개발을 진행해도 무관할 것으로 생각한다.
Add Comment