Spring

스프링 - Part 3 - 기본적인 웹 게시물 관리 04 (프레젠테이션(웹) 계층의 CRUD 구현)

록's 2023. 3. 22. 18:40
728x90
반응형

프레젠테이션(웹) 계층의 CRUD 구현

 

 

Controller의 작성

스프링 MVC의 Controller는 하나의 클래스 내에서 여러 메서드를 작성하고 @RequestMapping 등을 이용해서 URL을 분기하는 구조로 작성할 수 있기 때문에 하나의 클래스에서 필요한 만큼 메서드의 분기를 이용하는 구조로 작성한다.

 

 

BoardController의 분석

 

작성하기 전에 반드시 현재 원하는 기능을 호출하는 방식에 대해 테이블로 정리한 후 코드를 작성하는 것이 좋다.

테이블에서 From 항목은 해당 URL을 호출하기 위해서 별도의 입력화면이 필요하다는 것을 의미한다.

 

 

 

 

BoardController의 작성

 

BoardController는 org.codehows.controller 패키지에 선언하고 URL 분석된 내용들을 반영하는 메서드를 설계한다.

// BoardController.java

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 {

}

 

BoardController는 @Controller 어노테이션을 추가해서 스프링의 빈으로 인식할 수 있게 하고 @RequestMapping을 통해서 '/board'로 시작하는 모든 처리를 BoardController가 하도록 지정한다.

 

 

목록에 대한 처리와 테스트

 

BoardController에서 전체 목록을 가져오는 처리를 먼저 작성한다.

BoardController는 BoardService 타입의 객체와 같이 연동해야 하므로 의존성에 대한 처리도 같이 진행한다.

 

// 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.extern.log4j.Log4j;

@Controller
@Log4j
@RequestMapping("/board/*")
public class BoardController {

	private BoardService service;
	
	@GetMapping("/list")
	public void list(Model model) {
		
		log.info("list");
		model.addAttribute("list", service.getList());
	}
}

 

 

 

 

 

src/test/java에 org.codehows.controller 패키지에 BoardControllerTests 클래스를 선언한다.

 

BoardControllerTests 클래스

// BoardControllerTests.java

package org.codehows.controller;

import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.log;

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)
@WebAppConfiguration
@ContextConfiguration({
	"file:src/main/webapp/WEB-INF/spring/root-context.xml",
	"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
@Log4j
public class BoardControllerTests {
	
	@Setter(onMethod_ = {@Autowired})
	private WebApplicationContext ctx;
	
	private MockMvc mockMvc;
	
	@Before
	public void setup() {
		this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).bulid();
	}
	
	@Test
	public void testList() throws Exception {
		
		log.info(
				mockMvc.perform(MockMvcRequestBuilders.get("/board/list"))
				.andReturn()
				.getModelAndView()
				.getModelMap());
	}	
}

 

테스트 클래스의 선언부에는 @WebAppConfiguration 어노테이션을 적용한다.

@WebAppConfiguration 은 Servlet의 ServletContext를 이용하기 위해서인데, 스프링에서는 WebApplicationContext라는 존재를 이용하기 위해서이다. @Before가 적용된 메서드는 모든 테스트 전에 매번 실행되는 메서드가 된다.

 

 

등록 처리와 테스트

 

BoardController에 POST 방식으로 처리되는 register()를 작성하면 아래와 같다.

 

// BoardController.java

... 생략 ...

// 추가
		@PostMapping("/register")
	public String register(BoardVO board, RedirectAttributes rttr) {
		
		log.info("register: " + board);
		
		rttr.addFlashAttribute("result", board.getBno());
		
		return "redirect:/board/list";
	}
}

 

 

 

 

BoardControllerTests 클래스

// BoardControllerTests.java

... 생략 ...

// 추가
	@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);
	}
}

 

 

 

 

조회 처리와 테스트

 

등록 처리와 유사하게 조회 처리도 BoardController를 이용해서 처리할 수 있다. 특별한 경우가 아니라면 조회는 GET 방식으로 처리하므로, @GetMapping을 이용한다.

 

BoardController 클래스

// BoardController.java

... 생략 ...

// 추가 
	@GetMapping("/get")
	public void get(@RequestParam("bno") Long bno, Model model) {
		
		log.info("/get");
		model.addAttribute("board", service.get(bno));
	}

 

 

BoardControllerTests 클래스

// BoardControllerTests.java

... 생략 ...

	@Test
	public void testGet() throws Exception {
		
		log.info(mockMvc.perform(MockMvcRequestBuilders
				.get("/board/get")
				.param("bno", "2"))
				.andReturn()
				.getModelAndView().getModelMap());
	}
}

 

 

 

수정 처리와 테스트

 

수정 작업은 등록과 유사하다. 변경된 내용을 수집해서 BoardVO 파라미터로 처리하고, BoardService를 호출한다. 수정작업을 시작하는 화면의 경우에는 GET 방식으로 접근하지만 실제 작업은 POST 방식으로 동작하므로 @PostMapping을 이용해서 처리한다.

 

 

// BoardController.java

... 생략 ...

	@PostMapping("/modify")
	public String modify(BoardVO board, RedirectAttributes rttr) {
		log.info("modify: " + board);
		
		if(service.modify(board)) {
			rttr.addFlashAttribute("result", "success");
		}
		return "redirect:/board/list";
	}
}

 

BoardControllerTests 클래스

// BoardControllerTests.java

... 생략 ...

@Test
	public void testModify() throws Exception {
		
		String resultPage = mockMvc
				.perform(MockMvcRequestBuilders.post("/board/modify")
						.param("bno", "1")
						.param("title", "수정된 테스트 새글 제목")
						.param("content", "수정된 테스트 새글 내용")
						.param("writer", "user00"))
				.andReturn().getModelAndView().getViewName();
		
		log.info(resultPage);
	}

 

 

 

 

삭제 처리와 테스트

 

삭제 처리도 조회와 유사하게 BoardController와 테스트 코드를 작성한다. 삭제는 반드시 POST 방식으로만 처리한다.

 

BoardController 클래스

// BoardController.java

... 생략 ...

@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";
	}

 

 

BoardControllerTests 클래스

// BoardControllerTests.java

... 생략 ...

	@Test
	public void testRemove() throws Exception {
		// 삭제전 데이터베이스에 게시물 번호 확인 할 것
		String resultPage = mockMvc.perform(MockMvcRequestBuilders.post("/board/remove")
				.param("bno", "25")
				).andReturn().getModelAndView().getViewName();
		
		log.info(resultPage);
	}

 

 

728x90
반응형