BoardDao class
<<Model>>에서 비즈니스 로직과 데이터 처리를 모두 담당
insert()
메서드로 게시글 입력과 첨부파일 입력을 모두 처리한다.
⇒ 만약 다른 프로젝트에서 첨부파일을 다루지 않은 경우에는 어떻게 하나?
⇒ DAO 클래스를 변경해야 한다.
업무 로직이 변경되면 DAO 코드를 변경해야한다. (고객사마다 업무처리 방식이 조금씩 다르다.)
⇒ 재사용성이 떨어진다.
DAO에서 업무로직을 분리한다.
BoardDao에서 관리하던 비즈니스 로직, 수행을 BoardAddController로 이동한다.
DAO를 조금 더 재사용하기 좋게 만든다.
Controller가 너무 많은 일들을 한다.
⇒ High Cohesion
⇒ 비즈니스 로직을 분리
⇒ 별도 클래스(서비스 컴포넌트)로 정의
@WebListener
public class ContextLoaderListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("공유 자원을 준비 중!!");
try {
Class.forName("org.mariadb.jdbc.Driver");
Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb","study","1111");
ServletContext ctx = sce.getServletContext();
BoardDao boardDao = new MariaDBBoardDao(con);
MemberDao memberDao = new MariaDBMemberDao(con);
ctx.setAttribute("boardService", new BoardService(boardDao));
ctx.setAttribute("memberService", new MemberService(memberDao));
} catch(Exception e ) {
e.printStackTrace();
}
}
}
// 비즈니스 로직을 수행하는 객체
// - 메서드 이름은 업무와 관련된 이름을 사용한다.
//
public class BoardService {
BoardDao boardDao;
public BoardService(BoardDao boardDao) {
this.boardDao = boardDao;
}
public void add(Board board) throws Exception {
// 1) 게시글 등록
if (boardDao.insert(board) == 0) {
throw new Exception("게시글 등록 실패!");
}
// 2) 첨부파일 등록
boardDao.insertFiles(board);
}
public boolean update(Board board) throws Exception {
// 1) 게시글 변경
if (boardDao.update(board) == 0) {
return false;
}
// 2) 첨부파일 추가
boardDao.insertFiles(board);
return true;
}
public Board get(int no) throws Exception {
// 이 메서드의 경우 하는 일이 없다.
// 그럼에도 불구하고 이렇게 하는 이유는 일관성을 위해서다.
// 즉 Controller는 Service 객체를 사용하고 Service 객체는 DAO를 사용하는 형식을
// 지키기 위함이다.
// 사용 규칙이 동일하면 프로그래밍을 이해하기 쉬워진다.
return boardDao.findByNo(no);
}
public boolean delete(int no) throws Exception {
// 1) 첨부파일 삭제
boardDao.deleteFiles(no);
// 2) 게시글 삭제
return boardDao.delete(no) > 0;
}
public List<Board> list() throws Exception {
return boardDao.findAll();
}
public AttachedFile getAttachedFile(int fileNo) throws Exception {
return boardDao.findFileByNo(fileNo);
}
public boolean deleteAttachedFile(int fileNo) throws Exception {
return boardDao.deleteFile(fileNo) > 0;
}
}
@MultipartConfig(maxFileSize = 1024 * 1024 * 10)
@WebServlet("/board/update")
public class BoardUpdateController extends HttpServlet {
private static final long serialVersionUID = 1L;
BoardService boardService;
@Override
public void init() {
boardService = (BoardService) this.getServletContext().getAttribute("boardService");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
request.setCharacterEncoding("UTF-8");
Board board = new Board();
board.setNo(Integer.parseInt(request.getParameter("no")));
board.setTitle(request.getParameter("title"));
board.setContent(request.getParameter("content"));
List<AttachedFile> attachedFiles = new ArrayList<>();
String dirPath = this.getServletContext().getRealPath("/board/files");
Collection<Part> parts = request.getParts();
for(Part part:parts) {
if(!part.getName().equals("files") || part.getSize() == 0) continue;
String filename = UUID.randomUUID().toString();
part.write(dirPath + "/" + filename);
attachedFiles.add(new AttachedFile(filename));
}
board.setAttachedFiles(attachedFiles);
// 게시글 작성자인지 검사한다.
Member loginMember = (Member) request.getSession().getAttribute("loginMember");
if (boardService.get(board.getNo()).getWriter().getNo() != loginMember.getNo()) {
throw new Exception("게시글 작성자가 아닙니다.");
}
if(!boardService.update(board)) {
throw new Exception("게시글을 변경할 수 없습니다!");
}
response.sendRedirect("list");
} catch (Exception e) {
request.setAttribute("exception", e);
request.getRequestDispatcher("/error.jsp").forward(request, response);
}
}
}
⇒ 유지보수를 어렵게 만든다.
⇒ Controller가 직접 class 이름을 언급하지 말고 인터페이스 규칙에 따라 사용하도록 변경하면 된다.
⇒ 유지보수가 쉽다!
// 비즈니스 로직을 수행하는 객체의 사용 규칙(호출 규칙)
//
public interface BoardService {
void add(Board board) throws Exception;
boolean update(Board board) throws Exception ;
Board get(int no) throws Exception;
boolean delete(int no) throws Exception;
List<Board> list() throws Exception;
AttachedFile getAttachedFile(int fileNo) throws Exception ;
boolean deleteAttachedFile(int fileNo) throws Exception ;
}
public class DefaultBoardService implements BoardService{
...
com.bitcamp.board.listener.ContextLoaderListener 클래스 변경
ContextLoaderListener class
@WebListener
public class ContextLoaderListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("공유 자원을 준비 중!!");
try {
Class.forName("org.mariadb.jdbc.Driver");
Connection con = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/studydb","study","1111");
ServletContext ctx = sce.getServletContext();
BoardDao boardDao = new MariaDBBoardDao(con);
MemberDao memberDao = new MariaDBMemberDao(con);
ctx.setAttribute("boardService", new DefaultBoardService(boardDao));
ctx.setAttribute("memberService", new DefaultMemberService(memberDao));
} catch(Exception e ) {
e.printStackTrace();
}
}
}