MVC 동작순서에 따라 DB->MyBatis->DAO까지 만들었다면 Service를 만들어야함
@Service
@Component
로 대체가능하다표현 계층(Presentation layer. Front Controller)과 영속계층(Persistence layer. DAO) 사이를 연결해 두 계층이 직접적으로 통신하지 않게끔 결속력을 낮춘다.
서비스계층이 추가되면 코드복잡도가 증가함
트랜잭션(transaction)을 관리함
Service에서는 데이터조작을 CRUD(Create, Read, Update, Delete)라고 부름
구조는 DAO와 거의 유사하다
DB와 상관없이 DAO 여러개의 데이터를 처리할 수 있다. DAO처럼 DB와 1:1매칭이 필요없음.
@Override
public int create(BoardVO vo) {
LOGGER.info("create() 호출 : vo = "+vo.toString());
return dao.insert(vo);
}//end create()
@Override
public List<BoardVO> read(PageCriteria criteria) {
LOGGER.info("read() 호출 : PageCriteria = "+criteria);
return dao.select(criteria);
}//end read()
@Autowired
private BoardService boardService;
@GetMapping("/list")
public void list(Model model, Integer page, Integer numsPerPage) {
LOGGER.info("list() 호출");
LOGGER.info("page = "+page+", numsPerPage = "+numsPerPage);
//paging 처리. 기존데이터가 남아있으면 문제가 생길수있어서 new 사용
PageCriteria criteria = new PageCriteria();
if(page !=null) {
criteria.setPage(page);
}
if(numsPerPage!=null) {
criteria.setNumsPerPage(numsPerPage);
}
List<BoardVO> list=boardService.read(criteria);
model.addAttribute("list", list); //list 데이터 보내기
PageMaker pageMaker=new PageMaker();
pageMaker.setCriteria(criteria);
pageMaker.setTotalCount(boardService.getTotalNumsOfRecords());
pageMaker.setPageData();
model.addAttribute("pageMaker", pageMaker);
}//end list()
특정 게시글에서 목록으로 들어가면 해당 게시글이 존재하던 게시판 페이지번호가 남아있어야한다.
이게 없으면 15페이지에있는 게시글을 읽고 목록으로 갈경우 1페이지로 돌아가게되며 사용자는 다시 15페이지까지 찾아가야하는 불편함이 있다.
넘겨받은 pageMaker를 통해 링크에 게시판번호뿐만아니라 현재 page번호까지 넣어서 넘기기
<td><a href="detail?bno=${vo.bno }&page=${pageMaker.criteria.page}">${vo.title }</a></td>
ControllerTest는 front controller역할을 해야함.
@Autowired
private WebApplicationContext wac;
private MockMvc mock;
org.junit.Before
테스트할 때 경로와 method 방식이 필요
@Before
public void beforeTest() {
LOGGER.info("beforeTest() 호출");
LOGGER.info("wac : "+wac);
LOGGER.info("mock : "+mock);
//컨트롤러 메소드에게 요청을 보낼 수있는 mockup객체 생성
mock=MockMvcBuilders.webAppContextSetup(wac).build();
}//end beforeTest()
mock.perform()
: client와 controller의 데이터 이동이 있어서 데이터 유실될 위험이 있어 에러처리가 필요함get(uri) : get 요청에 대한 mock 객체 생성
post(uri) : post 요청에 대한 mock 객체 생성
put(uri) : 업데이트. put 요청에 대한 mock 객체 생성
delete(uri) : 삭제. delete 요청에 대한 mock 객체 생성
private void testList() throws Exception {
LOGGER.info("testList() 호출");
mock.perform(get("/board/list").param("page", "1"));
}
get방식의 param을 전송한다. 데이터가 문자열인 이유는 parameter는 String만 전송하기 때문.
MultiValueMap<String, String> params=new LinkedMultiValueMap<String, String>();
params.add("page", "1");
params.add("numsPerPage", "5");
mock.perform(get("/board/list").params(params));
JUnit 테스트 수행 후 호출되는 메소드
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:formatDate value="${vo.cdate }" pattern="yyyy-MM-dd HH:mm:ss" var="cdate" />
<td>${cdate }</td>
@PostMapping("/register")
public String registerPOST(BoardVO vo, RedirectAttributes reAttr) {
LOGGER.info("registerPOST() 호출");
LOGGER.info(vo.toString());
int result=boardService.create(vo);
LOGGER.info(result+"행 삽입");
if(result==1) {
LOGGER.info("result 1");
reAttr.addFlashAttribute("insert_result", "success"); //데이터 전송
return "redirect:/board/list"; // /board/list 경로로 이동시키는 get방식
}else {
reAttr.addFlashAttribute("insert_result", "fail");
return "redirect:/board/register"; //create 실패하면 다시 글작성페이지로 이동
}
}//end registerPOST()
controller에선 jsp관련 태그를 아예 안쓰는게 좋아서 클라이언트가 하도록 경로와 데이터만 전송함
<input type="hidden" id="insertAlert" value="${insert_result }">
<script type="text/javascript">
$(function() {
confirmInsertReslut();
function confirmInsertReslut() {
var result=$('#insertAlert').val();
if(result=='success'){
alert('새 글 작성 성공');
}
}
});//end document
</script>
뒤로가기 할때 이전에 이용했던 데이터가 남아있으면 안됨
url 이동 중간에 간섭해서 세션을 관리하는 인터셉션. 뒤로가기 방지
<interceptors>
<beans:bean id="webContentInterceptor" class="org.springframework.web.servlet.mvc.WebContentInterceptor">
<beans:property name="cacheSeconds" value="0"></beans:property>
<beans:property name="useExpiresHeader" value="true"></beans:property>
<beans:property name="useCacheControlHeader" value="true"></beans:property>
<beans:property name="useCacheControlNoStore" value="true"></beans:property>
</beans:bean>
</interceptors>
뒤로가기 설정을 안했을 땐 글작성 성공 후 뒤로가기 눌렀을 때 다시 글작성 성공했다는 alert가 뜸
뒤로가기 캐시 삭제를 설정하면 글작성 성공 후 뒤로가기 눌렀을 때 바로 글작성 페이지로 이동함.
tomcat timeout 오류
pc환경에서 노트북으로 파일을 옮겨서 진행했는데 갑자기 프로젝트가 무거운게 아닌데 계속 timeout이 떠서 1차 해결방법으로 서버의 start, stop시간을 조정해봤지만 무려 450초가 지나도 서버가 timeout되는 현상이 발생했음
그래서 tomcat을 재설치하려던 중 pc에 tomcat 설치폴더가 두개가 존재함을 발견. 혹시나해서 정상 경로에 있는 폴더 제외하고 다 지웠더니서버 start가 10초도 안걸렸음! 파일을 전부 인식한것같다... 이런경우가 다있네
일시적 효과였다.
반복적인 서버재시작을 하니깐 갑자기 target 폴더의 config.xml을 찾더니 또 timeout에 걸리고 또 잘됐다가 개복치마냥 다시 timeout..
심지어 port8080과 8005가 이미 사용중이라며 이제 서버가 시작조차 안됨. 최종진화같다.
수업도중에 손보기에 어려울것같아 일단은 임시방편으로 port번호를 8006, 9080으로 바꿔놓았음
문제해결ing