[Spring] REST 방식
도움이 되셨다면 광고 한 번 클릭 부탁드립니다.
한 번의 클릭이 제게 큰 힘이 됩니다! :)
REST는 'Representational State Transfer'의 약어로 하나의 URI는 하나의 고유한 리소스(Resource)를 대표하도록 설계된다는 개념에 전송방식을 결합해서 원하는 작업을 지정합니다.
예를 들어 '/boards/123'은 게시물 중에서 123번이라는 고유한 의미를 가지도록 설계하고, 이에 대한 처리는 GET, POST 방식과 같이 추가적인 정보를 통해서 결정합니다. 따라서 REST 방식은 다음과 같이 구성된다고 생각할 수 있습니다.
URI + GET/POST/PUT/DELETE/...
스프링은 @RequestMapping이나 @ResponseBody와 같이 REST 방식의 데이터 처리를 위한 여러 종류의 어노테이션과 기능이 있습니다.
REST와 관련해서 알아 두어야 하는 어노테이션
@RestController : Controller가 REST방식을 처리하기 위한 것임을 명시합니다.
@ResponseBody : 일반적인 JSP와 같은 뷰로 전달되는 게 아니라 데이터 자체를 전달하기 위한 용도
@PathVariable : URL 경로에 있는 값을 파라미터로 추출하려고 할 때 사용
@CrossOrigin : Ajax의 크로스 도메인 문제를 해결해주는 어노테이션
@RequestBody : JSON 데이터를 원하는 타입으로 바인딩 처리
@RestController
REST 방식에서 가장 먼저 기억해야 하는 점은 서버에서 전송하는 것이 순수한 데이터라는 점입니다.
(기존의 Controller에서 Model에 데이터를 담아 JSP 등과 같은 뷰로 전달하는 방식 X)
스프링 4부터는 @Controller 외에 @RestController라는 어노테이션을 추가해서 Controller의 모든 메서드의 리턴 타입을 기존과 다르게 처리한다는 것을 명시합니다. @RestController 이전에는 @Controller와 메서드 선언부에 @ResponseBody를 이용해서 동일한 결과를 만들 수 있습니다. @RestController는 메서드의 리턴 타입으로 사용자가 정의한 클래스 타입을 사용할 수 있고, 이를 JSON이나 XML로 자동으로 처리할 수 있습니다.
JSON 데이터를 처리하기 위해 jackson-databind 라이브러리를 pom.xml에 추가합니다. 이 라이브러리는 브라우저에 객체를 JSON이라는 포맷의 문자열로 변환시켜 전송할 때 필요합니다.
XML의 처리는 jackson-dataformat-xml 라이브러리를 이용합니다.
<!--JSON-->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</dependency>
<!--XML-->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-xml -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.12.4</version>
</dependency>
테스트할 때는 직접 Java 인스턴스를 JSON 타입의 문자열로 변환해야 하는 일들도 있으므로 gson 라이브러리도 추가합니다.
<!-- GSON -->
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.7</version>
</dependency>
@RestController의 반환 타입
1. SampleController 클래스를 생성
단순 문자열 반환
@RestController는 JSP와 달리 순수한 데이터를 반환하는 형태이므로 다양한 포맷의 데이터를 전송할 수 있습니다.
: 문자열, JSON, XML 등을 사용
기존 @Controller는 문자열을 반환하는 경우에는 JSP 파일의 이름으로 처리하지만, @RestController의 경우에는 순수한 데이터가 됩니다. @GetMapping에 사용된 produces 속성은 해당 메서드가 생산하는 MIME 타입을 의미합니다. 예제와 같이 문자열로 직접 지정할 수도 있고, 메서드 내의 MediaType이라는 클래스를 이용할 수도 있습니다.
브라우저에서 '/sample/getText' 호출
개발자 도구를 이용해 브라우저에 전송된 실제 데이터를 확인합니다.
결과를 보면 produces의 속성 값으로 지정된 'text/plain' 결과가 나오는 것을 확인할 수 있습니다.
객체의 반환
객체를 반환하는 작업은 JSON이나 XML을 이용합니다. SampleVO 클래스를 작성합니다.
SampleVO 클래스
SampleVO 클래스는 비어 있는 생성자를 만들기 위한 @NoArgsConstructor와 모든 속성을 사용하는 생성자를 위한 @AllArgsConstructor 어노테이션을 이용했습니다. 어노테이션을 통해 생성된 결과를 보면 생성자가 여러 개 생성되는 것을 볼 수 있습니다.
SampleController에서는 SampleVO를 리턴하는 메서드를 다음과 같이 설계합니다.
SampleController 클래스
getSample( )은 XML과 JSON 방식의 데이터를 생성할 수 있도록 작성되었습니다.
(APPLICATION_JSON_UTF8_VALUE는 스프링 5.2 버전부터는 Deprecated 되고, APPLICATION_JSON_VALUE로 사용합니다.)
브라우저에서 '/sample/getSample'을 호출해봅니다. (xml 데이터 get)
브라우저가 받은 데이터가 XML로 왔기에 보이는 화면이고, 개발자 도구를 통해 xml 데이터로 들어 온 것을 확인할 수 있습니다.
동일한 메서드를 '/sample/getSample.json'을 호출하게 되면 기존과 달리 JSON 타입의 데이터가 전달되는 것을 확인할 수 있습니다.
@GetMapping이나 @RequestMapping의 produces 속성은 반드시 지정해야 하는 것은 아니므로 생략도 가능합니다.
컬렉션 타입의 객체 반환
경우에 따라서는 여러 데이터를 한 번에 전송하기 위해서 배열이나 리스트, 맵 타입의 객체들을 전송하는 경우도 발생합니다.
SampleController의 일부
브라우저에서 '/sample/getList'를 호출하면 기본 XML 데이터를 전송하고, 확장자 'json'으로 처리하면 '[ ]'로 싸인 JSON 형태의 배열 데이터를 볼 수 있습니다.
맵의 경우에는 '키'와 '값'을 가지는 하나의 객체로 간주됩니다.
브라우저에서 '/sample/getMap' 호출 ( + .json 도 호출)
ResponseEntity 타입
REST 방식으로 호출하는 경우는 화면 자체가 아니라 데이터 자체를 전송하는 방식으로 처리되기 때문에 데이터를 요청한 쪽에서는 정상적인 데이터인지 비정상적인 데이터인지를 구분할 수 있는 확실한 방법을 제공해야만 합니다.
ResponseEntity는 데이터와 함께 HTTP 헤더의 상태 메시지 등을 같이 전달하는 용도로 사용합니다. HTTP의 상태 코드와 에러 메시지 등을 함께 데이터를 전달할 수 있기 때문에 받는 입장에서는 확실하게 결과를 알 수 있습니다.
check( )는 반드시 'height'와 'weight'를 파라미터로 전달받습니다. 이때 만일 'height'값이 150보다 작다면 502(bad gateway) 상태 코드와 데이터를 전송하고, 그렇지 않다면 200(ok) 코드와 데이터를 전송합니다.
'sample/check.json?height=140&weight=60'과 같이 JSON 타입의 데이터를 요구하고, height 값을 150보다 작게 하는 경우에는 502메시지와 데이터가 전달됩니다.
@RestController에서 파라미터
@RestController는 기존의 @Controller에서 사용하던 일반적인 타입이나 사용자가 정의한 타입(클래스)을 사용합니다. 여기에 추가로 몇 가지 어노테이션을 이용하는 경우가 있습니다.
- @PathVariable : 일반 컨트롤러에서도 사용이 가능하지만 REST 방식에서 자주 사용됩니다. URL 경로의 일부를 파라미터로 사용할 때 이용
- @RequestBody : JSON 데이터를 원하는 타입의 객체로 변환해야 하는 경우에 주로 사용
@PathVariable
REST 방식에서는 URL 내에 최대한 많은 정보를 담으려고 노력합니다. 예전에는 '?' 뒤에 추가되는 쿼리 스트링(query string)이라는 형태로 파라미터를 이용해서 전달되던 데이터들이 REST 방식에서는 경로의 일부로 차용되는 경우가 많습니다.
스프링 MVC에서는 @PathVariable 어노테이션을 이용해서 URL 상에 경로의 일부를 파라미터로 사용할 수 있습니다.
http://localhost:8080/sample/{sno}
http://localhost:8080/sample/{sno}/page/{pno}
위 URL에서 '{ }'로 처리된 부분은 컨트롤러의 메서드에서 변수로 처리가 가능합니다.
@PathVariable은 '{ }'의 이름을 처리할 때 사용합니다.
REST 방식에서는 URL 자체에 데이터를 식별할 수 있는 정보들을 표현하는 경우가 많으므로 다양한 방식으로 @PathVariable이 사용됩니다.
SampleController의 일부
@PathVariable을 적용하고 싶은 경우에는 '{ }'를 이용해서 변수명을 지정하고, @PathVariable을 이용해서 지정된 이름의 변숫값을 얻을 수 있습니다. 값을 받을 때에는 int, double과 같은 기본 자료형은 사용할 수 없습니다.
브라우저에서 '/sample/product/bags/1234'로 호출하면 cat과 pid 변수의 값으로 처리되는 것을 확인할 수 있습니다.
@RequestBody
@RequestBody는 전달된 요청(request)의 내용(body)을 이용해서 해당 파라미터의 타입으로 변환을 요구합니다. 내부적으로 HttpMessageConverter 타입의 객체들을 이용해서 다양한 포맷의 입력 데이터를 변환할 수 있습니다. 대부분의 경우에는 JSON 데이터를 서버에 보내서 원하는 타입의 객체로 변환하는 용도로 사용되지만, 경우에 따라서는 원하는 포맷의 데이터를 보내고, 이를 해석해서 원하는 타입으로 사용하기도 합니다.
변환을 위한 예제를 위해 'com.project.domain' 패키지에 Ticket 클래스를 정의합니다.
SampleController 클래스에 @RequestBody 어노테이션을 사용하여 메서드를 정의합니다.
SampleController의 다른 메서드와 달리 @PostMapping이 적용된 것을 볼 수 있는데, 이것은 @RequestBody가 말 그대로 요청(request)한 내용(body)을 처리하기 때문에 일반적인 파라미터 전달방식을 사용할 수 없기 때문입니다.
@REST 방식의 테스트
POST 등의 방식으로 지정되어 있으면서 JSON 형태의 데이터를 처리하는 것을 브라우저에서 개발하려면 많은 시간과 노력이 들어갑니다. JUnit과 spring-test를 이용해서 테스트하는 방식을 고려할 수 있습니다.
JUnit 기반의 테스트
1. src/test/java 폴더 아래 SampleControllerTests 클래스 작성
package com.project.controller;
import static org.springframework.test.web.servlet.request.
MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.
MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
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.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.google.gson.Gson;
import com.project.domain.Ticket;
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/servlet-context.xml"})
*/
// Java Config
@ContextConfiguration(classes = {
com.project.config.RootConfig.class,
com.project.config.ServletConfig.class
})
@Log4j
public class SampleControllerTests {
@Setter(onMethod_ = { @Autowired })
private WebApplicationContext ctx;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
}
@Test
public void testConvert() throws Exception {
Ticket ticket = new Ticket();
ticket.setTno(123);
ticket.setOwner("Admin");
ticket.setGrade("AAA");
String jsonStr = new Gson().toJson(ticket);
log.info(jsonStr);
mockMvc.perform(
post("/sample/ticket")
.contentType(MediaType.APPLICATION_JSON)
.content(jsonStr))
.andExpect(status().is(200));
}
}
testConvert( )는 SampleController에 작성해 둔 convert( ) 메서드를 테스트하기 위해서 작성하였습니다. SampleController의 convert( )는 JSON으로 전달되는 데이터를 받아서 Ticket 타입으로 변환합니다. 이를 위해서는 해당 데이터가 JSON이라는 것을 명시해 줄 필요가 있습니다. MockMvc는 contentType( )을 이용해서 전달하는 데이터가 무엇인지를 알려줄 수 있습니다. 코드 내의 Gson 라이브러리는 Java의 객체를 JSON 문자열로 변환하기 위해서 사용합니다.
위 코드를 실행하면 JSON 문자열이 Ticket 타입의 객체로 변환된 것을 볼 수 있습니다.
JUnit을 이용하는 방식의 테스트 장점은 역시 Tomcat을 구동하지 않고 컨트롤러를 구동해 볼 수 있다는 점입니다.
다양한 전송방식 (REST)
REST방식의 데이터 교환에서 가장 특이한 점은 기존의 GET/POST 외에 다양한 방식으로 데이터를 전달한다는 점입니다. HTTP의 전송방식은 아래와 같은 형태로 사용됩니다.
작업 | 전송방식 |
Create | POST |
Read | GET |
Update | PUT |
Delete | DELETE |
REST 방식은 URI와 같이 결합하므로 회원(member)이라는 자원을 대상으로 전송방식을 결합하면 다음과 같은 형태가 됩니다.
작업 | 전송방식 | URI |
등록 | POST | /members/new |
조회 | GET | /members/{id} |
수정 | PUT | /members/{id} + body (json 데이터 등) |
삭제 | DELETE | /members/{id} |
POST 방식도 그렇지만 PUT, DELETE 방식은 브라우저에서 테스트하기가 쉽지 않기 때문에 개발 시 JUnit이나 'Restlet Client' 등과 같은 도구를 이용해서 테스트하고 개발해야 합니다.
'코드로 배우는 스프링' 책의 내용을 공부하며 작성했습니다.
오늘은 스프링에서 REST 방식에 대해서 알아보았습니다.
도움이 되셨다면 광고 한 번 클릭 부탁드립니다.
한 번의 클릭이 제게 큰 힘이 됩니다! :)
'java, spring' 카테고리의 다른 글
[Spring] 댓글 처리, 서비스 영역과 Controller 처리 (feat. @RestController) (0) | 2021.08.03 |
---|---|
[Spring] 댓글 처리를 위한 영속 속성 (0) | 2021.08.01 |
[Spring] UriComponentsBuilder를 이용하는 링크 생성 (0) | 2021.07.25 |
[Spring] 화면에서 검색 조건 처리 (0) | 2021.07.25 |
[Spring] 페이징 처리에 검색 기능 SQL 적용하기 (feat. Mybatis 동적 SQL) (0) | 2021.07.24 |