본문 바로가기

개발 공부/Spring

코드로 배우는 스프링 웹 프로젝트-10.프레젠테이션(웹)계층의 CRUD 구현

코드로 배우는 스프링 웹 프로젝트-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 파라미터는 문자열로만 처리해야 한다.