conteroller의 작성
스프링 MVC의 Controller는 하나의 클래스 내에서 여러 메서드를 작성하고, @RequestMapping등을 이용해 URL을 분기하는 구조로 작성한다.
과거 이 단계에서 Tomcat(Was)를 실행하고, 웹화면을 만들어 결과를 확인하는 코드를 확인했지만, 시간도 오래걸리고 테스트 자동화하기에도 어려움이 있다.
Boardcontroller의 분석
원하는 기능을 호출하는 방식은 테이블로 정리하는 게 좋다.
From 항목은 해당 URL을 호출하기 위해 별도의 입력화면이 필요하다는 것을 의미(화면구성)
Task | URL | Method | Parameter | From | URL 이동 |
전체 목록 | /board/list | GET | |||
등록 처리 | /board/register | POST | 모든 항목 | 입력화면 필요 | 이동 |
조회 | /board/read | GET | bno=123 | ||
삭제 | /board/remove | POST | bno | 입력화면 필요 | 이동 |
수정 | /board/modify | POST | 모든 항목 | 입력화면 필요 | 이동 |
BoardController의 작성
org.zerock.controller /BoardContorller.java
1. 목록에 대한 처리와 테스트
list()
- GET
- @AllArgsConstructor
- model
testList()
- MockMvc
- 가짜로 URL과 파라미터 등을 브라우저에서 사용하는 것처럼 만들어서 Controller를 실행 해 볼 수 있다.
- MockMvcRequestBuilders를 이용해 GET방식을 호출한다.
- controller에서 반환한 결과를 이용해 Model에 어던 데이터들이 담겨 있는지 확인한다.
setup()
- WebApplicationContext
- 환경설정 초기화
// servlet-context.xml <context:component-scan base-package="org.zerock.controller" />
@Controller //빈등록(root-context.xml)
@Log4j
@RequestMapping("/board/*")
@AllArgsConstructor //생성자 자동주입 @Autowired을 대체함
public class BoardController {
private BoardService service;
@GetMapping("/list")
public void list(Model model) {
log.info("list");
//BoardServiceImpl 객체의 getList()결과를 담아 전달 addAttribute
model.addAttribute("list", service.getList());
}
}
//was를 실행하지 않는 테스트(Test for Controller)
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration //servlet-context.xml
@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;
// MockMvc는 가짜MVC로 가짜URL과 파라미터 등을 브라우저에서 사용하는 것처럼 만들어 Controller를 실행하기 위함
private MockMvc mockMvc;
//@Before는 해당메서드는 모든 메서드 실행 전에 매번 실행되는 초기화 메서드가 됨
@Before //import org.junit.Before;
public void setup() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(ctx) // 웹 어플리케이션 환경설정(ApplicationContext 또는 WebApplicationContext를 인자로 받음)
.build();
}
// 가짜URL: MockMvcRequestBuilders를 이용해 GET방식 호출하고,
// BoardContorller의 @RequestMapping("/board/*")@GetMapping("/list")list()
// list()메서드의 service.getList()에서 반환된 결과를 이용해 Model에 어떤 데이터가 담긴지 확인함
@Test
public void testList() throws Exception {
log.info(
mockMvc.perform(MockMvcRequestBuilders.get("/board/list"))
.andReturn() //요청결과를 반환
.getModelAndView() // 반환된 결과에서`ModelAndView`객체를 가져옴
.getModelMap()); // `ModelAndView`에서 모델 정보를 가져옴
}//INFO : org.zerock.controller.BoardControllerTests - {list=[BoardVO(bno=1, title=수정된 제목, content=수정된 내용, writer=user00, regdate=Wed Nov 22 03:20:42 KST 2023, updateDate=Mon Nov 27 19:26:19 KST 2023), BoardVO(bno=49, title=테스트 제목, content=테스트 내용, writer=user00, regdate=Mon Nov 27 18:30:16 KST 2023, updateDate=Mon Nov 27 18:30:16 KST 2023), BoardVO(bno=50, title=테스트 제목, content=테스트 내용, writer=user00, regdate=Mon Nov 27 18:30:17 KST 2023, updateDate=Mon Nov 27 18:30:17 KST 2023), BoardVO(bno=52, title=테스트 제목, content=테스트 내용, writer=user00, regdate=Mon Nov 27 18:42:56 KST 2023, updateDate=Mon Nov 27 18:42:56 KST 2023), BoardVO(bno=53, title=제목 수정합니다., content=새로 작성하는 내용, writer=newbie, regdate=Wed Nov 29 15:42:22 KST 2023, updateDate=Wed Nov 29 19:14:48 KST 2023), BoardVO(bno=54, title=새로 작성하는 글, content=새로 작성하는 내용, writer=newbie, regdate=Wed Nov 29 15:48:45 KST 2023, updateDate=Wed Nov 29 15:48:45 KST 2023)]}
}
2. 등록 처리와 테스트
register()
- POST
- @RedirectAttributes
- servlet의 response.sendRedirect()와 같은 용도
1) addFlashAttribute(이름, 값)메서드는 화면에 한번만 사용되는 데이터를 전달하기 위해 사용 데이터가 내부적으로 전달 되어 URL에 노출되지 않음
2) addAttribute()리다이렉트할 주소 뒤에 쿼리스트링으로 데이터를 전달해준다.(`targetPage?key=value`)
testRegister()
- param()을 이용해 파라미터를 전달
- MockMvc
BoardController
// 다른 페이지로 이동하기 위해 redirect를 사용
// RedirectAttributes는 리다이렉트 시 속성을 전달하기 위해 사용
// flash속성
// 전달할 데이터를 flash속성으로 전달하면 URL 파라미터가 아닌 내부적으로 전달(일회성 데이터전달시)
// URL 파라미터로 전달되지 않아 다른사용자에게 노출되지 않음
// 예를 들어, 한 페이지에서 다른 페이지로 리다이렉트될 때,
// 이동 전에 Flash 속성에 저장된 데이터는 다음 페이지로 전달
// 스프링 4.3이전에 사용
//@RequestMapping(value = "/register", method = RequestMethod.POST)
@PostMapping("/register") //// 스프링 4.3이후
public String register(BoardVO boardVo, RedirectAttributes rttr) {
log.info("register: " + boardVo);
service.register(boardVo);
rttr.addFlashAttribute("result", boardVo.getBno());
return "redirect:/board/list";
//redirect를 사용하면 스프링에서 내부적으로 response.sendRedirect()를 처리함
//`board/list?result=value` 형태로 전달
}
BoardControllerTests
@Test
public void testRegister()throws Exception{
String resultPage = mockMvc.perform(MockMvcRequestBuilders.post("/board/register")
.param("title", "테스트 새글 제목")
.param("content", "테스트 새글 내용")
.param("writer", "user")
).andReturn().getModelAndView().getViewName();
log.info(resultPage);
}
3. 조회 처리와 테스트
- GET
4. 수정 처리와 테스트
- POST
* select문을 사용했을때 ResultSet객체에서 1을 반환하고, update문을 사용했을때는 getUpdateCount()에서 1을 반환하네.. JDBC의 객체를 공부하면 로그를 좀 더 쉽게 알아 볼 수 있을 거같다.
Statement(stmt)
- SQL 문장 전달 및 실행을 담당하는 기본 객체
- statement의 exexute메서드를 사용해 쿼리를 내보내고, 그 결과값을 받는다.
- 정적인 SQL 문장 실행
- boolean execute( String sql ):
- SQL 문장 실행시키고 결과를 리턴
- 리턴값이 ResultSet일 경우 true, 아니면 false를 출력 - ResultSet executeQuery( String sql )
- 주로 조회문( SELECT , SHOW)에 사용 - int executeUpdate( String sql ):
- UPDATE/INSERT/DELETE일때 실행 후 영향 받은 행의 수를 반환하고, SELECT문에서는 사용하지 않고, executeQuery로 실행
- DDL(CREATE, DROP, ALTER)일때 영향 받은 행의 수를 반환 하지 않아 0을 반환(DDL문은 사용되지않)
- String sql은 쿼리로 일반적으로 미리 변수로 작성
5. 삭제 처리와 테스트
- POST