◎ 프레젠테이션 계층
▷ 비즈니스 계층의 구현까지 모든 테스트가 진행되었다면 남은 작업은 프레젠테이션 계층인 웹의 구현
1. Controller의 작성
▷ 스프링 MVC의 Controller는 하나의 클래스 내에서 여러 메소드를 작성하고 @RequestMapping 등을 이용해 URL을 분기하는 구조로 작성할 수 있음
▷ 하나의 클래스에서 필요한 만큼 메소드의 분기를 이용하는 구조로 작성
▷ WAS(Tomcat)을 실행하지 않고 Controller 테스트할 수 있는 방법 학습 필요
(1) BoardController 분석
▷ 작성 전 현재 원하는 기능 호출 방식에 대해 테이블로 정리한 후 코드를 작성하는 것이 좋음
From 항목은 해당 URL을 호출하기 위해서 별도의 입력화면이 필요하다는 것을 의미
2. BoardController의 작성
▷ org.codehows.controller 패키지에 선언하고 URL 분석된 내용들을 반영하는 메소드 설계
package org.codehows.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import lombok.extern.log4j.Log4j; @Controller @Log4j @RequestMapping("/board/*") public class BoardController{ }
1. @Controller 어노테이션 추가를 통해 스프링 빈으로 인식할 수 있게 함
2. @RequestMapping을 통해 '/board'로 시작하는 모든 처리를 BoardController가 하도록 지정
(1) 목록에 대한 처리와 테스트
▷ BoardController에서 전체 목록을 가져오는 처리를 먼저 작성
▷ BoardController는 BoardService 타입의 객체와 같이 연동해야 하므로 의존성 처리도 같이 진행
◎ src/main/java → org.codehows.controller → BoardController.java 클래스 내용 수정
package org.codehows.controller; import org.codehows.service.BoardService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import lombok.AllArgsConstructor; import lombok.extern.log4j.Log4j; @Controller @Log4j @RequestMapping("/board/*") @AllArgsConstructor public class BoardController{ private BoardService service; @GetMapping("/list") public void list(Model model) { log.info("list"); model.addAttribute("list", service.getList()); } }
▷ BoardController는 BoardService에 의존적이므로 @AllArgsConstructor를 이용해 생성자 만들고 자동 주입
▷ 생성자 만들지 않을 경우 @Setter (onMethod_ = {@Autowired}) 이용해 처리
▷ list()는 게시물 목록을 전달해야 하므로 Model을 파라미터로 지정하고 이를 통해 BoardServiceImpl 객체의 getList() 결과를 담아 전달
◎ src/test/java → org.codehows.controller → BoardControllerTests 클래스 생성
▷ Tomcat과 같은 WAS 실행하는 단계를 생략하기 위해 기존과 다르게 진행함
▷ 스프링의 테스트 기능을 이용
package org.codehows.controller; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import lombok.Setter; import lombok.extern.log4j.Log4j; @RunWith(SpringJUnit4ClassRunner.class) //Test for Controller @WebAppConfiguration @ContextConfiguration({ "file:src/main/webapp/WEB-INF/spring/root-context.xml", "file:src/main/webapp/WEB-INF/spring/appServlet-context.xml"}) @Log4j public class BoardControllerTests { @Setter(onMethod_ = {@Autowired }) private WebApplicationContext ctx; private MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build(); } @Test public void testList() throws Exception { log.info( mockMvc.perform(MockMvcRequestBuilders.get("/board/list")) .andReturn() .getModelAndView() .getModelMap()); } }
여기서 아래와 같은 에러가 나타나면 pom.xml로 넘어갑니다.
▷ java.lang.NoSuchMethodError: javax.servlet.http.HttpServletResponse.getStatus()I
◎ pom.xml 내용 수정...(생략)... <!-- Servlet --> <!-- 주석처리 <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> ...(생략)...
위와 같이 변경해줍니다.
근본적인 원인은 Spring Legacy Project에서 서블릿 버전을 기본적으로 2.5를 사용하는데, MockMvc는 서블릿 3.0이상을 사용해야 작동이 됩니다. 그치만 맨 위의 servlet-api가 적용되어 mockmvc가 작동되지 않았습니다.
코드를 변경해주면 아래와 같이 나타납니다.
(2) 등록 처리와 테스트
▷ BoardController에 POST 방식으로 처리되는 register()를 작성함
◎ src/main/java → org.codehows.controller → BoardController 클래스 수정
...(생략)... @PostMapping("/register") public String register(BoardVO board, RedirectAttributes rttr) { log.info("register: " + board); service.register(board); rttr.addFlashAttribute("result", board.getBno()); return "redirect:/board/list"; } }
▷ register() 메소드는 조금 다르게 String을 리턴 타입으로 지정하고, RedirectAttributes를 파라미터로 지정
▶ 등록 작업이 끝난 후 다시 목록 화면으로 이동하기 위함. 추가적으로 새롭게 등록된 게시물 번호 같이 전달
▷ 리턴 시 'redirect:' 접두어 사용
▶ 스프링 MVC가 내부적으로 response.sendRedirect()를 처리해주기 때문
◎ src/test/java → org.codehows.controller → BoardControllerTests 클래스 수정
...(생략)... @Test public void testRegister() throws Exception { String resultPage = mockMvc.perform(MockMvcRequestBuilders.post("/board/register") .param("title", "테스트 새글 제목") .param("content", "테스트 새글 내용") .param("writer", "user00") ).andReturn().getModelAndView().getViewName(); log.info(resultPage); } }
▷ MockMvcRequestBuilders의 post()를 이용하면 POST 방식으로 데이터를 전달할 수 있고, param()을 이용해 전달해야 하는 파라미터들을 지정할 수 있음(<input> 태그와 유사한 역할)
▷ 최초 작성시 일이 많다고 느껴지지만 매번 입력할 필요 없으므로 오류가 발생하거나 수정할 경우 반복적인 테스트가 수월해짐
로그 상단에 BoardVO 객체로 올바르게 바인딩된 결과를 볼 수 있고, 중간에는 SQL의 실행 결과가 나타남,
마지막에는 최종 반환 문자열이 확인됩니다.
비즈니스 계층의 구현까지 모든 테스트를 진행한 후 프레젠테이션 계층인 웹의 구현을 진행했습니다.
톰캣과 연동하지 않고 controller를 이용해서 테스트를 할 수 있네요!
이번에는 등록까지만 했고 다음 글에서 추가적인 내용 학습해볼게요!!
많은 분들의 피드백은 언제나 환영합니다! 많은 댓글 부탁드려요~~

'BackEnd > Spring' 카테고리의 다른 글
[코드로 배우는 스프링 웹 프로젝트] ch11 화면 처리 1 (0) | 2023.03.29 |
---|---|
[코드로 배우는 스프링 웹 프로젝트] ch10 프레젠테이션(웹) 계층의 CRUD 구현 2 (0) | 2023.03.28 |
[코드로 배우는 스프링 웹 프로젝트] ch09 비즈니스 계층 (0) | 2023.03.28 |
[코드로 배우는 스프링 웹 프로젝트] ch08 영속/비즈니스 계층의 CRUD 구현 2(create, read, update, delete) (0) | 2023.03.28 |
[코드로 배우는 스프링 웹 프로젝트] ch08 영속/비즈니스 계층의 CRUD 구현 1(select, 테이블 출력) (0) | 2023.03.28 |