본문 바로가기

Spring/Boost Course Web

4.4 Controller

1) RestController란?

Spring MVC에서 제공하는 RestController

 

@RestController

  • Spring 4 에서 Rest API 또는 Web API를 개발하기 위해 등장한 어노테이션 합니다.

  • 이전 버전의 @Controller와 @ResponseBody를 포함합니다.

 

MessageConvertor

  • 자바 객체와 HTTP 요청 / 응답 바디를 변환하는 역할

  • @ResponseBody, @RequestBody

  • @EnableWebMvc 로 인한 기본 설정

  • WebMvcConfigurationSupport 를 사용하여 Spring MVC 구현

  • Default MessageConvertor 를 제공

  • 링크 바로가기의 addDefaultHttpMessageConverters메소드 항목 참조

 

MessageConvertor 종류

 

JSON 응답하기

  • 컨트롤러의 메소드에서는 JSON으로 변환될 객체를 반환합니다.

  • jackson라이브러리를 추가할 경우 객체를 JSON으로 변환하는 메시지 컨버터가 사용되도록 @EnableWebMvc에서 기본으로 설정되어 있습니다.

  • jackson라이브러리를 추가하지 않으면 JSON메시지로 변환할 수 없어 500오류가 발생합니다.

  • 사용자가 임의의 메시지 컨버터(MessageConverter)를 사용하도록 하려면 WebMvcConfigurerAdapter의 configureMessageConverters메소드를 오버라이딩 하도록 합니다.

 


2) RestController를 이용하여 web api작성하기  

Rest Controller 사용하려면 반드시 jackson 라이브러리 추가. -> @RestCotroller

guestbook05의 controller패키지의 GuestbookApiController.java

package kr.or.connect.guestbook.controller;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import kr.or.connect.guestbook.dto.Guestbook;
import kr.or.connect.guestbook.service.GuestbookService;
// RestController를 활용하여 Wep Api 작성.
// Rest Api, 테스트하는 게 바로 확장 프로그램 (Talend API Tester)
@RestController
@RequestMapping(path="/guestbooks")
public class GuestbookApiController {
@Autowired // 서비스이용
GuestbookService guestbookService;
@GetMapping // get 방식으로 요청이 들어오면 이 메서드 실행. path는 위에 적어서 생략
// 결과값으로 Map 객체를 반환하는데 application/json 요청이기 때문에
// DipatcherServlet은 jsonMessageConvert를 내부적으로 사용해서 해당 Map 객체를 json으로 변환해서 전송을 하게 됩니다.
public Map<String, Object> list(@RequestParam(name="start", required=false, defaultValue="0") int start) {
List<Guestbook> list = guestbookService.getGuestbooks(start);
int count = guestbookService.getCount();
int pageCount = count / GuestbookService.LIMIT;
if(count % GuestbookService.LIMIT > 0)
pageCount++;
List<Integer> pageStartList = new ArrayList<>();
for(int i = 0; i < pageCount; i++) {
pageStartList.add(i * GuestbookService.LIMIT);
}
Map<String, Object> map = new HashMap<>();
map.put("list", list);
map.put("count", count);
map.put("pageStartList", pageStartList);
return map;
}
@PostMapping
// 클라이언트한테 응답이 갈 때는 json으로 바뀌어서 감.
public Guestbook write(@RequestBody Guestbook guestbook,
HttpServletRequest request) {
String clientIp = request.getRemoteAddr();
// id가 입력된 guestbook이 반환된다.
Guestbook resultGuestbook = guestbookService.addGuestbook(guestbook, clientIp);
return resultGuestbook;
}
@DeleteMapping("/{id}") //PathVariable인거 알고 계시죠?
public Map<String, String> delete(@PathVariable(name="id") Long id,
HttpServletRequest request) {
String clientIp = request.getRemoteAddr();
int deleteCount = guestbookService.deleteGuestbook(id, clientIp);
return Collections.singletonMap("success", deleteCount > 0 ? "true" : "false");
}
}

3) Web API 테스트 코드 작성하기

test 폴더에서. junit으로 run

package kr.or.connect.guestbook.controller;
import kr.or.connect.guestbook.config.ApplicationConfig;
import kr.or.connect.guestbook.config.WebMvcContextConfiguration;
import kr.or.connect.guestbook.dto.Guestbook;
import kr.or.connect.guestbook.service.GuestbookService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
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.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {WebMvcContextConfiguration.class, ApplicationConfig.class })
public class GestubookApiControllerTest {
@InjectMocks
public GuestbookApiController guestbookApiController;
// @InjectMocks어노테이션이 붙여서 선언된 guestbookApiController는 목객체인 GuestbookService를 사용하게 됩니다.
// 스프링에 위해 주입된 객체를 사용하는 것이 아닌 Mockito 프레임워크에 위해 생성된 목객체가 주입되어 객체가 생성됩니다.
@Mock //@Mock어노테이션을 붙여서 선언된 guestbookService는 mockito에 위해 목객체로 생성됩니다. 말그대로 가짜 객체가 됩니다.
GuestbookService guestbookService;
private MockMvc mockMvc;
@Before //테스트 메소드가 실행되기 전에 @Before어노테이션이 붙은 메소드가 실행됩니다.
public void createController() {
MockitoAnnotations.initMocks(this); //현재 객체에서 @Mock이 붙은 필드를 목객체로 초기화시킵니다.
mockMvc = MockMvcBuilders.standaloneSetup(guestbookApiController).build();
// MockMVC타입의 변수 mockMvc를 초기화 합니다. guestbookApiController를 테스트 하기 위한
// MockMvc객체를 생성합니다.
}
@Test
public void getGuestbooks() throws Exception {
Guestbook guestbook1 = new Guestbook();
guestbook1.setId(1L);
guestbook1.setRegdate(new Date());
guestbook1.setContent("hello");
guestbook1.setName("kim");
List<Guestbook> list = Arrays.asList(guestbook1);
when(guestbookService.getGuestbooks(0)).thenReturn(list);
// List<Guestbook>타입의 변수 list를 초기화하고 해당 list에 방명록 한 건을 저장합니다.
// when(guestbookService.getGuestbooks(0)).thenReturn(list);
// 위의 문장은 아래와 같이 동작합니다.
// when( 목객체.목객체메소드호출() ).threnReturn(목객체 메소드가 리턴 할 값)
// guestbookService.getGuestbook(0) 이 호출되면 위에서 선언된 list객체가 리턴 되도록 설정합니다.
RequestBuilder reqBuilder = MockMvcRequestBuilders.get("/guestbooks").contentType(MediaType.APPLICATION_JSON);
// MockMvcRequestBuilders를 이용해 MockMvc에게 호출할 URL을 생성합니다.
// get(“/guestbooks”)
// GET 방식으로 /guestbooks 경로를 호출하라는 의미입니다.
// contentType(MediaType.APPLICATION_JSON);
// application/json 형식으로 api를 호출합니다.
// 즉 2가지가 합치면 application/json형식으로 /guestbooks를 GET방식으로 호출한다는 것을 뜻합니다.
// 이러한 URL정보를 가진 reqBuilder를 생성합니다
mockMvc.perform(reqBuilder).andExpect(status().isOk()).andDo(print());
// mockMvc.perform(reqBuilder) 는 reqBuilder에 해당하는 URL에 대한 요청을 보냈다는 것을 의미합니다.
// andExpect(status().isOk()) 는 mockMvc에 위해 URL이 실행되고 상태코드값이 200이 나와야 한다는 것을 의미합니다.
// andDo(print())는 처리 내용을 출력하게 됩니다.
// 여기까지 실행되면 화면에 다음과 같은 결과가 출력되면서 테스트가 성공하게 됩니다.
verify(guestbookService).getGuestbooks(0);
// Guestbook 목객체의 getGuestbooks(0)메소드가 호출했다면 검증은 성공하게 됩니다.
}
// 아래의 코드는 방명록을 삭제하는 web api를 테스트하고 있습니다.
@Test
public void deleteGuestbook() throws Exception {
Long id = 1L;
when(guestbookService.deleteGuestbook(id, "127.0.0.1")).thenReturn(1);
RequestBuilder reqBuilder = MockMvcRequestBuilders.delete("/guestbooks/" + id).contentType(MediaType.APPLICATION_JSON);
// “/guestbooks/” + id 경로를 DELETE방식으로 호출하기 위한 경로 정보를 가지고 있는 reqBuilder객체를 생성합니다.
mockMvc.perform(reqBuilder).andExpect(status().isOk()).andDo(print());
// reqBuilder에 해당하는 URL을 호출한 후, 상태 코드가 200일 경우 성공합니다. 그리고 결과를 출력하게 됩니다.
verify(guestbookService).deleteGuestbook(id, "127.0.0.1");
// guestbookService 목객체의 deleteGuestbook(id, “127.0.0.1”)메소드가 Web API가 동작하면서 호출되었다면 성공하게 됩니다.
}
}

 

 

'Spring > Boost Course Web' 카테고리의 다른 글

5.1 상태유지기술 - Cookie & Session  (0) 2020.11.19
4.5 Swagger  (0) 2020.11.19
4.3 WEB API  (0) 2020.11.19
4.2 Layered Architecture  (0) 2020.11.19
4.1 Spring MVC  (0) 2020.11.19
3.3 Spring JDBC  (0) 2020.11.19