코드로 배우는 스프링 웹 프로젝트-10.프레젠테이션(웹)계층의 CRUD 구현
코드로 배우는 스프링 웹 프로젝트 - 개정판
2019년 7월 10일 인쇄판
Part3. 기본적인 웹 게시물 관리
Chapter10. 프레젠테이션(웹) 계층의 CRUD 구현
10.1 Controller의 작성
Controller는 하나의 클래스 내에서 여러 메서드를 작성하고 @RequestMapping을 이용해 URL을 분기하는 구조로 작성할 수 있기 때문에 하나의 클래스에서 필요한 만큼 분기를 이용하는 구조로 작성한다.
10.1.1 BoardController의 분석
Task | URL | Method | Parameter | From | URL 이동 |
전체 목록 | /board/list | GET | |||
등록 처리 | /board/register | POST | 모든 항목 | 입력화면 필요 | 이동 |
조회 | /board/read | GET | bno=123 | ||
삭제 | /board/remove | POST | bno | 입력화면 필요 | 이동 |
수정 | /board/modify | POST | 모든 항목 | 입력화면 필요 | 이동 |
From 항목은 해당 URL을 호출하기 위해 별도의 입력화면이 필요하다는 것을 의미
10.2 BoardController의 작성
org.zerock.controller 패키지 > BoardController 클래스
package org.zerock.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 {
}
JAVA 설정의 경우 @ComponentScan 이용
10.2.1 목록에 대한 처리와 테스트
BoardCotroller는 BoardService 객체와 같이 연동해야 하므로 의존성 처리도 진행됨.
package org.zerock.controller;
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 org.zerock.service.BoardService;
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 결과를 담아 전달한다.
테스트 코드는 기존과 좀 다르게 진행된다. 웹을 개발할 때 매번 URL 테스트를 위해 WAS를 실행하는 단계를 생략하기 위함이다. 스프링의 테스트 기능으로 WAS를 실행하지 않고도 테스트가 가능하다.
package org.zerock.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 org.zerock.domain.BoardVO;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration //Test for Controller
@ContextConfiguration({
"file:src/main/webapp/WEB-INF/spring/root-context.xml",
"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"
})
//Java Config
//@ContextConfiguration(classes={org.zerock.config.RootConfig.class, org.zerock.config.ServletConfig.class})
@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());
}
}
@WebAppConfiguration djshxpdltusdms Servlet의 ServletContext를 이용하기 위해서인데 스프링에선 WebApplicationContext라는 존재를 이용하기 위해서다. @Before 어노테이션이 적용된 setUp에선 import할 때 JUnit을 이용해야 한다. @Before 적용된 메서드는 모든 테스트 전에 매번 실행되는 메서드가 된다.
MockMv는 "가짜 mvc"로 가짜로 URL과 파라미터를 브라우저에서 사용하는 것처럼 만들어 Controller를 실행할 수 있다.
testList는 MockMvcRequestBuilders로 GET 방식의 호출을 한다.
이후 BoardController의 getList 반환값을 이용해 Model에 어떤 데이터들이 담겨 있는지 확인한다.
10..2.2 등록 처리와 테스트
POST 방식으로 처리되는 register()
@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()를 처리해 준다.
test)
@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);
}
10.2.3 조회 처리와 테스트
@GetMapping("/get")
public void get(@RequestParam("bno") Long bno, Model model)
{
log.info("/get");
model.addAttribute("board", service.get(bno));
}
BoardController의 get 메서드는 bno 값을 좀 더 명시적으로 처리하는 @RequestParam을 이용해 지정. 파라미터 이름과 변수 이름을 기준으로 동작하므로 생략해도 무방하다.
또한 화면 쪽으로 해당 번호의 게시물을 전달해야 하므로 Model을 파라미터로 지정.
@Test
public void testGet() throws Exception {
log.info(mockMvc.perform(MockMvcRequestBuilders
.get("/board/get")
.param("bno", "2"))
.andReturn()
.getModelAndView()
.getModelMap());
}
10.2.4 수정 처리와 테스트
변경된 내용을 수집해 BoardVO 파라미터로 처리하고 BoardService 호출
수정 작업 시작 화면은 GET으로 접근하지만 실제론 POST 방식으로 동작한다.
@PostMapping("/modify")
public String modify(BoardVO board, RedirectAttributes rttr) {
log.info("modify: " + board);
if(service.modify(board)) {
rttr.addFlashAttribute("result", "sucess");
}
return "redirect:/board/list";
}
@Test
public void testModify() throws Exception {
String resultPage = mockMvc
.perform(MockMvcRequestBuilders
.post("/board/modify")
.param("bno", "10")
.param("title", "수정된 테스트 글 제목")
.param("content", "수정된 테스트 글 내용")
.param("writer", "user00"))
.andReturn()
.getModelAndView()
.getViewName();
log.info(resultPage);
}
10.2.5 삭제 처리와 테스트
@PostMapping("/remove")
public String remove(@RequestParam("bno") Long bno, RedirectAttributes rttr) {
log.info("remove..." + bno);;
if(service.remove(bno))
{
rttr.addFlashAttribute("result", "success");
}
return "redirect:/board/list";
}
삭제는 반드시 POST로만. BoardController의 remove는 삭제 후 페이지 이동이 필요하므로 RedirectAttributes를 파라미터로 등록하고 redirect로 다시 목록 페이지로 이동.
@Test
public void testRemove() throws Exception {
String resultPage = mockMvc
.perform(MockMvcRequestBuilders
.post("/board/remove")
.param("bno", "5"))
.andReturn()
.getModelAndView()
.getViewName();
log.info(resultPage);
}
MockMvc 파라미터는 문자열로만 처리해야 한다.
'개발 공부 > Spring' 카테고리의 다른 글
코드로 배우는 스프링 웹 프로젝트-12.오라클 데이터베이스 페이징 처리 (0) | 2020.07.12 |
---|---|
코드로 배우는 스프링 웹 프로젝트-11.화면처리 (0) | 2020.07.12 |
코드로 배우는 스프링 웹 프로젝트 P3: Chap09.비즈니스 계층 (0) | 2020.07.12 |
코드로 배우는 스프링 웹 프로젝트 P3: Chap08. 영속/비즈니스 계층의 CRUD 구현 (0) | 2020.07.11 |
코드로 배우는 스프링 웹 프로젝트 - Part3: Chap07. 스프링 MVC 프로젝트의 기본 구성 (0) | 2020.07.11 |