## 70. Controller에서 비즈니스 로직 분리하기: 서비스 컴포넌트 도입
- Controller에서 비즈니스 로직을 분리하는 이유
- 서비스 컴포넌트의 역할 이해
=>
ㄴ service 패키지에 MemberService 라는 이름으로 새로운 인터페이스 추가
=>
service/MemberService.java
ㄴ Member 관련 인터페이스 작성
=>
ㄴ service 패키지에 DefaultMemberService 라는 이름의 새로운 클래스 파일 추가
=>
service/DefaultMemberService.java
ㄴ MemberService 구현하기
ㄴ 구현 필요한 메서드 구현하기
=>
ㄴ [OK] 선택하여 필요한 메서드 모두 오버라이딩 하기
=>
service/DefaultMemberService.java
ㄴ MemberDao 생성자 추가
=>
=>
service/DefaultMemberService.java
=>
app-common/.../dao/MemberDao.java
ㄴ insert 메서드의 반환 값을 int 로 변경해주기
=>
app-common/.../dao/MemberDao.java
ㄴ BoardDao 도 MemberDao 와 마찬가지로 insert 메서드의 반환 값을 int 로 변경해주기
=>
service/DefaultMemberService.java
ㄴ MemberService 메서드 구현하기
=>
service/DefaultMemberService.java
ㄴ PlatformTransactionManager 추가
=>
ㄴ MemberAddController.java 에 있는 해당 코드 복사
=>
service/DefaultMemberService.java
ㄴ 복사한 코드 붙여넣기
ㄴ DefaultTransactionDefinition def를 사용하여 트랜잭션 속성을 정의하고, 이를 FlatformTransactionManager txManager를 통해 트랜잭션을 시작하고 status에 상태를 저장하도록 함
ㄴ 트랜잭션 동안 예외가 발생하면 txManager.rollback(status)를 호출하여 롤백하고, 예외가 발생하지 않으면 txManager.commit(status)를 호출하여 트랜잭션을 커밋하도록 함
=>
service/DefaultMemberService.java
ㄴ 트랜잭션을 처리하도록 해당 try~catch 문 추가
=>
service/DefaultMemberService.java
ㄴ 클래스의 인스턴스를 자동으로 생성하고 Bean으로 관리하도록 @Component 애노테이션 추가
=>
service/DefaultMemberService.java
ㄴ 보다 정확한 표현을 위해 @Service 라는 애노테이션으로 변경해줌
ㄴ @Service는 주로 비즈니스 로직을 처리하는 서비스 클래스를 나타냄
service/DefaultMemberService.java
ㄴ add 메서드의 코드 전체 복사하여 update 메서드에 복사한 코드 붙여넣기
=>
service/DefaultMemberService.java
ㄴ insert -> update 로 변경하기
=>
service/DefaultMemberService.java
ㄴ update 메서드의 코드 전체 복사하여 delete 메서드에 복사한 코드 붙여넣기
ㄴ update -> delete 로 변경
ㄴ member -> memberNo 로 변경
controller/MemberAddController.java
ㄴ MemberDao -> MemberService 로 변경
=>
controller/MemberAddController.java
ㄴ 이제 트랜잭션 처리는 DefaultMemberService 에서 하므로 해당 코드 제거
=>
controller/MemberAddController.java
ㄴ 생성자도 새로 추가해주기
=>
controller/MemberAddController.java
ㄴ 이제 트랜잭션 처리는 DefaultMemberService 에서 하므로 해당 코드 제거
=>
controller/MemberAddController.java
ㄴ memberDao.insert -> memberService.add 로 변경
ㄴ 이제 트랜잭션 처리는 DefaultMemberService 에서 하므로 txManager.commit(status) 코드 제거
=>
controller/MemberAddController.java
ㄴ 이제 트랜잭션 처리는 DefaultMemberService 에서 하므로 해당 코드 제거
=>
controller/MemberAddController.java
ㄴ Spring MVC에서 웹 요청을 처리하는 데 사용되는 컨트롤러 클래스를 나타내도록 @Controller 애노테이션으로 변경해줌
ㄴ 보다 정확한 표현을 위해 @Controller 라는 애노테이션으로 변경해줌
나머지 MemberXxxController 도 동일한 방법으로 처리해주도록함
=>
controller/MemberDeleteController.java
package bitcamp.report.controller;
import bitcamp.report.service.MemberService;
import org.springframework.stereotype.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller("/member/delete")
public class MemberDeleteController implements PageController {
MemberService memberService;
public MemberDeleteController(MemberService memberService) {
this.memberService = memberService;
}
@Override
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
if (memberService.delete(Integer.parseInt(request.getParameter("no"))) == 0) {
throw new Exception("해당 번호의 회원이 없습니다!");
} else {
return "redirect:list";
}
} catch (Exception e) {
request.setAttribute("refresh", "2;url=list");
throw e;
}
}
}
controller/MemberDetailController.java
package bitcamp.report.controller;
import bitcamp.report.service.MemberService;
import org.springframework.stereotype.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller("/member/detail")
public class MemberDetailController implements PageController {
MemberService memberService;
public MemberDetailController(MemberService memberService) {
this.memberService = memberService;
}
@Override
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setAttribute("member", memberService.get(Integer.parseInt(request.getParameter("no"))));
return "/WEB-INF/jsp/member/detail.jsp";
}
}
controller/MemberListController.java
package bitcamp.report.controller;
import bitcamp.report.service.MemberService;
import org.springframework.stereotype.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller("/member/list")
public class MemberListController implements PageController {
MemberService memberService;
public MemberListController(MemberService memberService) {
this.memberService = memberService;
}
@Override
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setAttribute("list", memberService.list());
return "/WEB-INF/jsp/member/list.jsp";
}
}
controller/MemberUpdateController.java
package bitcamp.report.controller;
import bitcamp.report.service.MemberService;
import bitcamp.report.service.NcpObjectStorageService;
import bitcamp.report.vo.Member;
import org.springframework.stereotype.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
@Controller("/member/update")
public class MemberUpdateController implements PageController {
MemberService memberService;
NcpObjectStorageService ncpObjectStorageService;
public MemberUpdateController(MemberService memberService, NcpObjectStorageService ncpObjectStorageService) {
this.memberService = memberService;
this.ncpObjectStorageService = ncpObjectStorageService;
}
@Override
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
Member member = new Member();
member.setNo(Integer.parseInt(request.getParameter("no")));
member.setName(request.getParameter("name"));
member.setPhone(request.getParameter("phone"));
member.setPassword(request.getParameter("password"));
member.setPosition(request.getParameter("position").charAt(0));
Part photoPart = request.getPart("photo");
if (photoPart.getSize() > 0) {
String uploadFileUrl = ncpObjectStorageService
.uploadFile("bitcamp-nc7-bucket-25", "member/", photoPart);
member.setPhoto(uploadFileUrl);
}
if (memberService.update(member) == 0) {
throw new Exception("회원이 없습니다.");
} else {
return "redirect:list";
}
} catch (Exception e) {
request.setAttribute("refresh", "2;url=list");
throw e;
}
}
}
=>
ㄴ service 패키지에 BoardService 라는 이름으로 새로운 인터페이스 추가
=>
service/BoardService.java
ㄴ Board 관련 인터페이스 작성
=>
service/BoardService.java
ㄴ list 제외한 나머지 메서드의 category 제거하기
=>
service/BoardService.java
ㄴ delete 메서드의 리턴값 int 로 변경
=>
ㄴ service 패키지에 DefaultBoardService 라는 이름의 새로운 클래스 파일 추가
=>
service/DefaultBoardService.java
ㄴ BoardService 구현하기
ㄴ 구현 필요한 메서드 구현하기
=>
ㄴ [OK] 선택하여 필요한 메서드 모두 오버라이딩 하기
=>
service/DefaultBoardService.java
ㄴ @Service 애노테이션 추가해주기
=>
service/DefaultBoardService.java
ㄴ BoardService, PlatformTransactionManager 추가 후 생성자 추가
=>
controller/BoardAddController.java
ㄴ 해당 코드 복사하기
=>
service/DefaultBoardService.java
ㄴ 복사한 코드 붙여넣기
=>
controller/BoardAddController.java
ㄴ 해당 코드 복사
=>
service/DefaultBoardService.java
ㄴ 복사한 코드 붙여넣기
=>
service/DefaultBoardService.java
=>
controller/BoardAddController.java
ㄴ BoardDao -> BoardService 로 변경
=>
controller/BoardAddController.java
ㄴ 트랜잭션 처리는 이제 DefaultBoardService 에서 해주므로 해당 코드 제거
=>
controller/BoardAddController.java
ㄴ 생성자 새로 추가해주기
=>
controller/BoardAddController.java
ㄴ 생성자를 직접 추가하는 대신 @Autowired 애노테이션을 이용하기
=>
controller/BoardAddController.java
ㄴ Spring MVC에서 웹 요청을 처리하는 데 사용되는 컨트롤러 클래스를 나타내도록 @Controller 애노테이션으로 변경해줌
ㄴ 보다 정확한 표현을 위해 @Controller 라는 애노테이션으로 변경해줌
=>
controller/BoardAddController.java
ㄴ 트랜잭션 처리는 이제 DefaultBoardService 에서 해주므로 해당 코드 제거
=>
controller/BoardAddController.java
ㄴ 트랜잭션 처리는 이제 DefaultBoardService 에서 해주므로 해당 코드 제거
=>
controller/BoardAddController.java
ㄴ boardDao.insert -> boardService.add 로 변경
=>
controller/BoardAddController.java
ㄴ 트랜잭션 처리는 이제 DefaultBoardService 에서 해주므로 해당 코드 제거
나머지 BoardXxxController 도 동일한 방식으로 수정해주도록 함
=>
그 전에 MemberXxxController 도 생성자를 자동으로 생성해주는 @Autowired 방식으로 변경
=>
controller/MemberAddController.java
controller/MemberDeleteController.java
controller/MemberDetailController.java
controller/MemberListController.java
controller/MemberUpdateController.java
DefaultBoardService, BoardXxxController 추가로 수정하기
=>
service/DefaultBoardService.java
ㄴ add 메서드의 전체 코드 복사하기
=>
service/DefaultBoardService.java
ㄴ 복사한 코드를 delete 메서드에 붙여넣기
=>
controller/BoardDeleteController.java
ㄴ 해당 게시글의 첨부파일을 삭제하는 코드 복사
=>
service/DefaultBoardService.java
ㄴ 복사한 코드 붙여넣은 후 b.getNo() 대신 boardNo 이용하기
=>
app-common/.../dao/BoardDao.java
ㄴ Board board 대신 int no 이용하기
=>
app-common/.../dao/BoardDao.java
ㄴ Board board 대신 int no 이용하기
=>
app-common/.../dao/BoardDao.java
ㄴ category 를 받지 않도록 하고, category 를 받지 않으면 받은 파라미터가 no 밖에 없으므로 @Param 제거
=>
resources/.../BoardDao.xml
ㄴ parameterType 를 int 로 설정하여 추가
=>
resources/.../BoardDao.xml
ㄴ category를 받지 않으므로 해당 코드 제거
=>
resources/.../BoardDao.xml
ㄴ parameterType 를 board 대신 int 로 변경
=>
resources/.../BoardDao.xml
ㄴ parameterType 를 board 대신 int 로 변경
=>
resources/.../BoardDao.xml
ㄴ #{viewCount} 대신 view_count + 1 를 이용하여 조회수 증가 시키기
=>
resources/.../BoardDao.xml
ㄴ category를 받지 않으므로 해당 코드 제거
=>
resources/.../BoardDao.xml
ㄴ parameterType 를 board 대신 int 로 변경
=>
resources/.../BoardDao.xml
ㄴ category를 받지 않으므로 해당 코드 제거
=>
resources/.../BoardDao.xml
ㄴ 작성자도 로그인한 사람인 loginUser 를 이용하므로 해당 코드 제거
service/DefaultBoardService.java
ㄴ 해당 코드는 필요 없으므로 제거
=>
service/DefaultBoardService.java
ㄴ insert(board) -> delete(boardNo) 로 변경
=>
controller/BoardDeleteController.java
=>
controller/BoardDeleteController.java
ㄴ HTTP 요청 파라미터를 통해 게시물 번호(no)를 전달받아 boardService를 사용하여 해당 게시물을 검색하도록 함
=>
service/DefaultBoardService.java
ㄴ boardNo를 사용하여 boardDao의 findBy 메서드를 호출하여 해당 게시물을 검색하고 반환하도록 함
=>
service/BoardService.java
ㄴ 조회수 증가 관련 인터페이스 추가
=>
service/DefaultBoardService.java
ㄴ 구현되지 않은 메서드 구현하도록 함
=>
=>
service/DefaultBoardService.java
ㄴboardDao 객체를 사용하여 updateCount 메서드를 호출하도록 함
=>
service/DefaultBoardService.java
ㄴ delete 메서드의 해당 코드 복사
=>
service/DefaultBoardService.java
ㄴ 복사한 코드 붙여넣기
=>
controller/BoardDetailController.java
ㄴ 해당 코드 복사
=>
controller/BoardDetailController.java
ㄴ 복사한 코드 붙여넣기
=>
controller/BoardDetailController.java
ㄴ 코드 수정
controller/BoardDeleteController.java
ㄴ 작성자는 여기서 다루지 않으므로 제거
ㄴ category 는 받지 않으므로 제거
=>
controller/BoardDeleteController.java
ㄴ 가져온 게시글 b가 null인 경우 또는 해당 게시글을 작성한 사용자의 번호가 현재 로그인한 사용자의 번호와 다른 경우, 예외를 발생시키도록 함
=>
controller/BoardDeleteController.java
ㄴ 이제 트랜잭션은 여기서 다루지 않으므로 txManager.commit(status) 코드 제거
ㄴ boardService.delete(b.getNo()) 를 이용하여 게시글을 삭제하도록 함
=>
controller/BoardDeleteController.java
ㄴ 이제 트랜잭션은 여기서 다루지 않으므로 해당 코드 제거
=>
controller/BoardDeleteController.java
ㄴ @Component -> @Controller 로 애노테이션 변경
controller/BoardDetailController.java
ㄴ @Component -> @Controller 로 애노테이션 변경
=>
controller/BoardDetailController.java
ㄴ category 받지 않으므로 해당 코드 제거
=>
controller/BoardDetailController.java
ㄴ boardDao.findBy(category, no) 대신 boardService.get(no) 이용하기
=>
controller/BoardDetailController.java
ㄴ board.setViewCount(board.getViewCount() + 1) 대신 increaseViewCount 를 이용하여 조회수 증가시키도록 하기
=>
controller/BoardDetailController.java
ㄴ 해당 코드 필요 없으므로 제거
=>
controller/BoardDetailController.java
ㄴ 트랜잭션 처리는 여기서 하지 않으므로 해당 코드 제거
BoardFileDeleteController.java
ㄴ @Component -> @Controller 로 변경하기
=>
BoardFileDeleteController.java
ㄴ category 받지 않으므로 해당 코드 제거
=>
service/BoardService.java
ㄴ getAttachedFile 메서드 규칙 추가
=>
service/DefaultBoardService.java
ㄴ 구현되지 않은 메서드 추가
=>
=>
service/DefaultBoardService.java
ㄴ 파일 번호 fileNo를 기반으로 해당 파일 정보를 검색하는 메서드 추가
=>
controller/BoardFileDeleteController.java
ㄴ boardDao.findFileBy 대신 boardService.getAttachedFile 이용하기
=>
controller/BoardFileDeleteController.java
ㄴ BoardDao.findBy 대신 boardService.get 메서드 이용
=>
controller/BoardFileDeleteController.java
ㄴ BoardDao.deleteFile 대신 boardService.deleteAttachedFile 메서드 이용
=>
controller/BoardFileDeleteController.java
ㄴ 예외 문구 변경
=>
controller/BoardFileDeleteController.java
ㄴ 트랜잭션은 여기서 처리하지 않으므로 txManager.commit(status); 코드 제거
ㄴ category 는 받지 않으므로 category=" + category + "& 코드 제거
=>
controller/BoardFileDeleteController.java
ㄴ 트랜잭션은 여기서 처리하지 않으므로 txManager.rollback(status); 코드 제거
ㄴ category 는 받지 않으므로 category=" + request.getParameter("category") + "& 코드 제거
controller/BoardListController.java
package bitcamp.report.controller;
import bitcamp.report.service.BoardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller("/board/list")
public class BoardListController implements PageController {
@Autowired
BoardService boardService;
@Override
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
request.setAttribute("list", boardService.list(Integer.parseInt(request.getParameter("category"))));
return "/WEB-INF/jsp/board/list.jsp";
} catch (Exception e) {
request.setAttribute("refresh", "1;url=/");
throw e;
}
}
}
=>
service/DefaultBoardService.java
ㄴ 목록을 출력하는 메서드 추가
service/DefaultBoardService.java
ㄴ add 메서드의 전체 코드를 복사
=>
service/DefaultBoardService.java
ㄴ update 메서드에 복사한 코드 붙여넣기
=>
service/DefaultBoardService.java
ㄴ insert -> update 로 변경
=>
service/DefaultBoardService.java
ㄴ count > 0 조건을 추가
=>
controller/BoardUpdateController.java
ㄴ boardDao -> boardService 로 변경
=>
resources/.../BoardDao.xml
ㄴ category 를 사용하지 않으므로 해당 코드 제거
ㄴ 작성자는 loginUser 로 관리하므로 해당 코드 제거
=>
controller/BoardUpdateContorller.java
ㄴ 작성자는 여기서 셋팅할 필요가 없으므로 해당 코드 제거
=>
controller/BoardUpdateContorller.java
ㄴ boardService.get 메서드를 통해 board 객체를 가져오도록 함
=>
controller/BoardUpdateContorller.java
ㄴ 작성자와 로그인한 사람의 번호가 다를 경우 "게시글이 존재하지 않거나 변경 권한이 없습니다." 문구를 출력하도록 함
=>
controller/BoardUpdateContorller.java
ㄴ 해당 코드 제거
=>
controller/BoardUpdateContorller.java
ㄴ category 는 이용하지 않으므로 해당 코드 제거
=>
controller/BoardUpdateContorller.java
ㄴ if 문 안에 있던 boardService.update(board) 코드를 if 문 밖으로 빼주기
=>
controller/BoardUpdateContorller.java
ㄴ if 문 제거
=>
controller/BoardUpdateContorller.java
ㄴ 트랜잭션은 여기서 처리하지 않아도 되므로 해당 코드 제거
=>
controller/BoardUpdateContorller.java
ㄴ 트랜잭션은 여기서 처리하지 않아도 되므로 해당 코드 제거
=>
controller/BoardUpdateContorller.java
ㄴ category 는 사용하지 않으므로 해당 코드 제거
controller/HomeController.java
ㄴ @Component -> @Controller 로 변경
LoginController.java
ㄴ @Component -> @Controller 로 변경
=>
LoginController.java
ㄴ 생성자 직접 생성하는 대신 @Autowired 이용하기
ㄴ MemberDao -> MemberService 로 변경
=>
LoginController.java
ㄴ memberDao.findByPhoneAndPassword 대신 memberService.get 이용하기
=>
LogoutController.java
=>
App 실행
=>
=>
ㄴ 로그인 선택
=>
ㄴ 로그인 정보 입력 후 [로그인] 선택
=>
ㄴ 로그인 정상적으로 실행됨을 확인
=>
ㄴ 회원 선택 시 회원 목록 정상적으로 출력됨을 확인
=>
ㄴ 게시글 선택 시 게시글 목록 정상적으로 출력됨을 확인
=>
ㄴ 새 글 선택 후 내용 입력 및 첨부파일 등록 후 [등록] 선택
=>
=>
ㄴ 정상적으로 등록됨을 확인
=>
ㄴ 게시글 내용 변경 후 [변경] 선택
=>
ㄴ 오류 발생
=>
WEB-INF/jsp/board/list.jsp
ㄴ category 제거해주기
=>
WEB-INF/jsp/board/detail.jsp
ㄴ category 제거해주기
service/DefaultBoardService.java
ㄴ deleteAttachedFile 메서드 구현해주기
=>
App 재실행
=>
ㄴ 첨부파일 [삭제] 선택
=>
ㄴ 첨부파일 정상적으로 삭제됨을 확인
=>
게시글 삭제 오류
=>
WEB-INF/jsp/board/detail.jsp
ㄴ category=${board.category}& 을 다시 추가해주고 param -> board 로 변경해주기
=>
App 재실행
=>
=>
ㄴ 게시글 [삭제] 선택
=>
ㄴ 게시글이 정상적으로 삭제됨을 확인
=>
=>
ㄴ 게시글 변경 후 [변경] 선택
=>
ㄴ 오류 발생
=>
controller/BoardUpdateController.java
ㄴ request.getParameter("category") 대신 board.getCategory() 이용하기
=>
resources/.../BoardDao.xml
ㄴ parameterType 을 int 가 아닌 board 로 변경
=>
App 재실행
=>
=>
ㄴ 로그인 후 게시글 내용 변경 후 [변경] 선택
=>
ㄴ 게시글이 정상적으로 변경됨을 확인
WEB-INF/jsp/board/detail.jsp
ㄴ 날짜 데이터를 출력하기 위해 ${simpleDateFormatter.format(board.createdDate)} 대신 fmt 태그 라이브러리 이용하기
=>
ㄴ 날짜 데이터가 정상적으로 출력됨을 확인