본문 바로가기

개발/JSP

chap13-2.7 서비스 클래스의 구현

각 DBMS별 MessageDao 구현 클래스와 DBMS에 알맞은 MessageDaoProvider 클래스를 구현했으므로 이제 이들 클래스를 이용해서 방명록 기능을 제공하는 서비스 클래스를 구현해 보자. 주요 기능별로 구현할 클래스는 다음과 같다.

  • 방명록에 등록된 메시지 목록 제공 : GetMessageListService
  • 신규 메시지 등록 기능 : WriteMessageService
  • 메시지 삭제 기능 : DeleteMessageService

방명록 예제에서 사용할 서비스 클래스들은 DAO를 실행하는 도중에 SQLException이 발생할 경우 다음과 같이 ServiceException을 발생시키도록 하였다. 
try{
	...
} catch(SQLException ex){
	...
	throw new ServiceException("메시지 목록 구하기 실패", ex);
}
ServiceException 클래스는 간단한 예외 클래스로서 다음과 같이 작성하였다.

chap13\WEB-INF\src\kame\chap13\service\ServiceException.java
package kame.chap13.service;

public class ServiceException extends Exception{

	public ServiceException(String message, Exception cause){
		super(message, cause);
	}

	public ServiceException(String message){
		super(message);
	}
}
(1) GetMessageListService 클래스의 구현

GetMessageListService는 요청한 페이지 번호에 해당하는 메시지 목록을 제공하는 기능을 제공하며, 다음과 같이 getMessageList() 메서드를 통해서 기능을 구현하고 있다.
public class GetMessageListService {
	...
	public MessageListView getMessageList(int pageNumber)
		throws ServiceException{
		...
	}
}
MessageListView 클래스는 요청한 페이지 번호, 요청한 페이지의 메시지 목록, 전체 메시지 개수, 페이지 개수, 페이지 당 보여줄 메시지 개수 등의 정보를 담는 클래스로 다음과 같다.

chap13\WEB-INF\src\kame\chap13\model\MessageListView.java
package kame.chap13.model;

import java.util.List;

public class MessageListView{

	private int messageTotalCount;
	private int currentPageNumber;
	private List messageList;
	private int pageTotalCount;
	private int messageCountPerPage;
	private int firstRow;
	private int endRow;

	public MessageListView(List messageList, int messageTotalCount, int currentPageNumber, int messageCountPerPage, int startRow, int endRow){
		this.messageList = messageList;
		this.messageTotalCount = messageTotalCount;
		this.currentPageNumber = currentPageNumber;
		this.messageCountPerPage = messageCountPerPage;
		this.firstRow = startRow;
		this.endRow = endRow;

		calcountPageTotalCount();
	}

	private void calcountPageTotalCount() {
		if(messageTotalCount == 0){
			pageTotalCount = 0;
		} else {
			pageTotalCount = messageTotalCount / messageCountPerPage;
			if(messageTotalCount % messageCountPerPage > 0){
				pageTotalCount++;
			}
		}
	}

	public int getMessageTotalCount(){
		return messageTotalCount;
	}

	public int getCurrentPageNumber(){
		return currentPageNumber;
	}

	public List getMessageList(){
		return messageList;
	}

	public int getPageTotalCount(){
		return pageTotalCount;
	}

	public int getMessageCountPerPage{
		return messageCountPerPage;
	}

	public int getFirstRow(){
		return firstRow;
	}

	public int getEndRow(){
		return endRow;
	}

	public boolean isEmpty(){
		return messageTotalCount == 0;
	}
}
GetMessageListService는 다음의 순서로 필요한 작업을 실행한다.

  • 전체 메시지 개수를 구한다 : MessageDao.selectCount() 호출
  • 요청한 페이지 번호에 해당하는 메시지의 시작 행과 끝 행을 구한다.
  • 시작 행과 끝 행에 포함된 메시지 목록을 구한다 : MessageDao.selectList() 실행
  • MessageView List 객체를 리턴한다.

실제 GetMessageListService 클래스의 구현 코드는 다음과 같다.

chap13\WEB-INF\src\kame\chap13\service\GetMessageListService.java 
package kame.chap13.service;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;

import kame.chap13.dao.MessageDao;
import kame.chap13.dao.MessageDaoProvider;
import kame.chap13.model.Message;
import kame.chap13.model.MessageListView;
import kame.jdbc.JdbcUtil;
import kame.jdbc.connection.ConnectionProvider;

public class GetMessageListService {
	private static GetMessageListService instance =
			new GetMessageListService();

	public static GetMessageListService getInstance() {
		return instance;
	}

	private GetMessageListService() {
	}

	private static final int MESSAGE_COUNT_PER_PAGE = 3;

	public MessageListView getMessageList(int pageNumber)
			throws ServiceException {
		Connection conn = null;
		int currentPageNumber = pageNumber;
		try {
			conn = ConnectionProvider.getConnection();
			MessageDao messageDao =
					MessageDaoProvider.getInstnace().getMessageDao();

			int messageTotalCount = messageDao.selectCount(conn);

			List messageList = null;
			int firstRow = 0;
			int endRow = 0;
			if (messageTotalCount > 0) {
				firstRow =
						(pageNumber - 1) * MESSAGE_COUNT_PER_PAGE + 1;
				endRow = firstRow + MESSAGE_COUNT_PER_PAGE - 1;
				messageList =
						messageDao.selectList(conn, firstRow, endRow);
			} else {
				currentPageNumber = 0;
				messageList = Collections.emptyList();
			}
			return new MessageListView(messageList,
					messageTotalCount, currentPageNumber,
					MESSAGE_COUNT_PER_PAGE, firstRow, endRow);
		} catch (SQLException e) {
			throw new ServiceException("메시지 목록 구하기 실패: "
					+ e.getMessage(), e);
		} finally {
			JdbcUtil.close(conn);
		}
	}
}

(2) WriteMessageListService 클래스의 구현

chap13\WEB-INF\src\kame\chap13\service\WriteMessageListService.java
package kame.chap13.service;

import java.sql.Connection;
import java.sql.SQLException;

import kame.chap13.dao.MessageDao;
import kame.chap13.dao.MessageDaoProvider;
import kame.chap13.model.Message;
import kame.jdbc.JdbcUtil;
import kame.jdbc.connection.ConnectionProvider;

public class WriteMessageService {
	private static WriteMessageService instance =
			new WriteMessageService();

	public static WriteMessageService getInstance() {
		return instance;
	}

	private WriteMessageService() {
	}

	public void write(Message message) throws ServiceException {
		Connection conn = null;
		try {
			conn = ConnectionProvider.getConnection();
			MessageDao messageDao =
					MessageDaoProvider.getInstnace().getMessageDao();
			messageDao.insert(conn, message);
		} catch (SQLException e) {
			throw new ServiceException(
					"메시지 등록 실패: " + e.getMessage(), e);
		} finally {
			JdbcUtil.close(conn);
		}
	}

}
WriteMessageService 클래스의 경우 별도 로직을 수행하지 않고 MessageDao의 insert() 메서드를 실행하고 있다. 필요한 경우 파라미터로 전달받은 Message 객체에 저장된 값이 올바른지의 여부를 검사하는 로직을 아래 코드와 같이 추가할 수도 있다.
public void writeMessage(Message message) throws ServiceException{
	if(message.getGuestName() == null || message.getGuestName.isEmpty()){
		throw new IllegalArgumentException("invalid guest name", message);
	}
	...
}
(3) DeleteMessageService 클래스의 구현

메시지 삭제 기능을 제공하는 DeleteMessageService 클래스는 다음과 같은 순서로 기능을 실행한다.

지정한 번호에 해당하는 메시지를 검색한다. - MessageDao.select()
메시지가 존재하지 않을 경우 예외 발생
메시지에 암호가 지정되어 있지 않을 경우 예외 발생
메시지의 메시지와 파라미터로 전달받은 암호가 다를 경우 예외 발생
메시지를 삭제 - MessageDao.delete()

메시지 삭제 기능은 다소 복잡한데, 그 이유는 존재하지 않는 메시지를 삭제하려고 시도하는 경우와 잘못된 암호를 입력하는 경우를 처리하기 때문이다. 실제 DeleteMessageService 캘르스의 소스 코드는 다음과 같다.

chap13\WEB-INF\src\kame\chap13\service\DeleteMessageListService.java
package kame.chap13.service;

import java.sql.Connection;
import java.sql.SQLException;

import kame.chap13.dao.MessageDao;
import kame.chap13.dao.MessageDaoProvider;
import kame.chap13.model.Message;
import kame.jdbc.JdbcUtil;
import kame.jdbc.connection.ConnectionProvider;

public class DeleteMessageService {

	private static DeleteMessageService instance =
			new DeleteMessageService();

	public static DeleteMessageService getInstance() {
		return instance;
	}

	private DeleteMessageService() {
	}

	public void deleteMessage(int messageId, String password)
			throws ServiceException, InvalidMessagePassowrdException,
			MessageNotFoundException {
		Connection conn = null;
		try {
			conn = ConnectionProvider.getConnection();
			conn.setAutoCommit(false);

			MessageDao messageDao =
					MessageDaoProvider.getInstnace().getMessageDao();
			Message message = messageDao.select(conn, messageId);
			if (message == null) {
				throw new MessageNotFoundException("메시지가 없습니다:"
						+ messageId);
			}
			if (!message.hasPassword()) {
				throw new InvalidMessagePassowrdException();
			}
			if (!message.getPassword().equals(password)) {
				throw new InvalidMessagePassowrdException();
			}
			messageDao.delete(conn, messageId);

			conn.commit();
		} catch (SQLException ex) {
			JdbcUtil.rollback(conn);
			throw new ServiceException("삭제 처리 중 에러가 발생했습니다:"
					+ ex.getMessage(), ex);
		} catch (InvalidMessagePassowrdException ex) {
			JdbcUtil.rollback(conn);
			throw ex;
		} catch (MessageNotFoundException ex) {
			JdbcUtil.rollback(conn);
			throw ex;
		} finally {
			if (conn != null) {
				try {
					conn.setAutoCommit(false);
				} catch (SQLException e) {
				}
				JdbcUtil.close(conn);
			}
		}
	}
}
* DeleteMessageService 클래스는 ServiceException 외에 MessageNotFoundException,클래스와 InvalidMessagePasswordException 클래스를 필요로 한다. 이 두 예외 클래스의 소스코드는 ServiceException 클래스와 유사하다.