본문 바로가기

BackEnd/Spring

[코드로 배우는 스프링 웹 프로젝트] ch10 프레젠테이션(웹) 계층의 CRUD 구현 1

728x90
반응형

◎ 프레젠테이션 계층

   ▷ 비즈니스 계층의 구현까지 모든 테스트가 진행되었다면 남은 작업은 프레젠테이션 계층인 웹의 구현

 

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를 이용해서 테스트를 할 수 있네요!

 

이번에는 등록까지만 했고 다음 글에서 추가적인 내용 학습해볼게요!!

 

많은 분들의 피드백은 언제나 환영합니다!  많은 댓글 부탁드려요~~

 

728x90
반응형