본문 바로가기

Spring 3.0 - 4.3

1탄 - 스프링을 이용해 Rest API 만들어보기

전통적인 웹 애플리케이션은 주로 서버사이드에서 화면에 필요한 모든 데이터를 만들어서 브라우저에 전송해주고, 브라우저는 단순 뷰어 역할에 그치는 형태였습니다. 

 

요즘의 서버는 브라우저나 모바일에서 필요한 순수한 데이터만을 전달하는 API 서버의 형태로 변화하고 있습니다.

예컨대, 검색 API 서버는 검색의 결과를 XML이나 JSON의 형태로 전달하고, 브라우저나 모바일에서는 이를 가공해서 사용자에게 보여주는 방식입니다.

 

즉, 브라우저에서 서버에 기대하는 것은 완성된 HTML이 아니라 그저 자신에게 필요한 순수한 데이터만을 요구합니다. 

 

REST = Representational State Transfer 의 약어로 하나의 URI는 하나의 고유한 리소스를 대표하도록 설계된다는 개념에 전송방식을 결합해서 원하는 작업을 지정하는 것입니다.

 

예를 들어 '/boards/123'은 게시물 중에서 123번이라는 고유한 의미를 가지도록 설계하고, 이에 대한 처리는 GET/POST 방식과 같이 추가적인 정보를 통해서 결정합니다.

 

따라서 REST 방식은 다음과 같이 구성됩니다.

URI + GET/POST/PUT/DELETE... 

 

  • 스프링의 REST 관련 어노테이션
@RestController Controller가 REST 방식을 처리하기 위한 것임을 명시
@ResponseBody 일반적인 JSP와 같은 뷰로 전달되는 게 아니라 데이터 자체를 전달하기 위한 용도
@PathVariable URL 경로에 있는 값을 파라미터로 추출하려고 할 때 사용
@CrossOrigin Ajax의 크로스 도메인 문제를 해결해주는 어노테이션
@RequestBody JSON 데이터를 원하는 타입으로 바인딩 처리

 

  • @PathVariable

예전에는 '?' 뒤에 추가되는 쿼리스트링 형태로 파라미터를 이용해서 전달되던 데이터들이 REST 방식에서는 경로의 일부로 사용되는 경우가 많았습니다. 

 

이제는 PathVariable 을 이용하여 

 

http://localhost:8080/sample/{sno}/page/{pno}

 

- {} 로 처리된 부분이 컨트롤러의 메서드에서 변수로 처리가 가능합니다.

- 값을 얻을 때에는 int, double과 같은 기본 자료형은 사용할 수 없습니다.

 

 

  • @RequsetBody

말 그대로 전달된 요청(request)의 내용(body)을 처리하기 때문에 일반적인 파라미터 전달방식을 사용할 수 없습니다. 그래서 다른 메서드들과 달리 @PostMapping이 메서드 위에 적용됩니다.

 

 


기존 Controller와 RestController 동작방식 비교

 

  • 기존 Controller 

Controller에서 Model에 데이터를 담아서 JSP 등과 같은 뷰로 전달하는 방식

 

  • RestController 

Spring 4에서부터는 @Controller 외에 @RestController라는 어노테이션을 추가해서 해당 Controller의 모든 메서드의 리턴 타입을 기존과 다르게 처리한다는 것을 명시합니다.

 

@RestController 이전에는 @Controller + @ResponseBody를 이용해서 동일한 결과를 만들 수 있었습니다. (Spring 3)

 

@RestController에서는 메서드의 리턴 타입으로 사용자가 정의한 클래스 타입을 사용할 수 있고, 이를 JSON이나 XML로 자동으로 처리할 수 있습니다. 


이제 실제로 만들어 봅시다 :)

 

1. pom.xml 작성하기

jackson-databind 라이브러리를 추가해줍니다. 이 라이브러리는 나중에 브라우저에 객체를 JSON 포맷의 문자열로 변환시켜 전송할 때 필요합니다. XML 처리를 위해서 jackson-dataformat-xml 도 추가해줍니다.

<!-- 브라우저에 객체를 JSON 포맷의 문자열로 변환시켜 전송할 때 필요 -->
		<dependency>
		    <groupId>com.fasterxml.jackson.core</groupId>
		    <artifactId>jackson-databind</artifactId>
		    <version>2.9.8</version>
		</dependency>

		<dependency>
		    <groupId>com.fasterxml.jackson.dataformat</groupId>
		    <artifactId>jackson-dataformat-xml</artifactId>
		    <version>2.9.8</version>
		</dependency>

 

테스트할 때는 직접 Java 인스턴스를 JSON 타입의 문자열로 변환해야 하는 일들도 있으므로 gson 라이브러리도 추가합니다.

<!-- java 인스턴스를 json타입의 문자열로 변환할 때 필요 -->
		<dependency>
		    <groupId>com.google.code.gson</groupId>
		    <artifactId>gson</artifactId>
		    <version>2.8.5</version>
		</dependency>		

 

@RestController는 JSP와 달리 순수한 데이터를 반환하는 형태이므로 다양한 포맷의 데이터를 전송할 수 있습니다.

주로 많이 사용하는 형태는 일반 문자열이나 JSON, XML을 사용합니다.

 

기존의 @Controller는 문자열을 반환하는 경우에 JSP 파일의 이름으로 처리했습니다. (경로를 return 했죠)

@GetMapping에 사용된 produces 속성은 해당 메서드가 생산하는 MIME 타입을 의미합니다.

 

 

2. VO객체 생성하기 (반환할 객체 작성)

package com.exe.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class SongVO {
	
	private String song_key;
	private String song_title;
	private String rel_Date;
	private String youtube_link;
	private String album_key;
	
}

 

비어 있는 생성자를 만들기 위한 @NoArgsConstructor와 모든 속성을 사용하는 생성자를 위한 @AllArgsConstructor 어노테이션을 이용했습니다. 어노테이션을 통해서 생성된 결과를 보면 생성자가 여러 개 생성된든 것을 볼 수 있습니다.

 

 

 

3. Controller 작성하기

저는 예제로 HomeController 라는 이름을 이용하겠습니다.

HomeController는 SongService 타입의 객체인 SongServiceImpl 객체를 주입받도록 설계합니다.

 

@Setter 주입을 이용하거나 @Autowired를 이용하거나 @AllArgsConstructor를 이용하여 SongService 타입의 객체를 필요로 하는 생성자를 만들어서 사용합니다.

 

스프링의 테스트 기능을 활용하면 개발 당시에 Tomcat(WAS)을 실행하지 않고도 스프링과 웹 URL 테스트를 할 수 있기 때문에 테스트로 진행하겠습니다. src/test/java에 HomeControllerTests 클래스를 선언합니다. (Tomcat 실행 X)

 

HomeController에서는 SongVO를 리턴하는 메서드를 다음과 같이 작성합니다.

 @GetMapping(value = "/song", 
    		produces = { MediaType.APPLICATION_JSON_UTF8_VALUE,	// JSON이나 XML 객체를 반환하겠다
    				MediaType.APPLICATION_XML_VALUE })
	public SongVO test() {
    	song = songService.getSong(song);
    	log.info(song); // Lombok이 만들어주는 toString()을 이용해서 출력
    	
		return song;
	}

@GetMapping이나 @RequestMapping의 produces 속성은 생략이 가능합니다.

 

 

 

● 컬렉션 타입의 객체를 반환하고 싶은 경우 (List, Map..)

여러 데이터를 한 번에 전송하기 위해서 배열이나 리스트, 맵 타입의 객체를 전송하려면?

List를 반환하려 한다면, 그대로 List를 return 해주면 됩니다.

 @GetMapping(value = "/getTodayChart", 
    		produces = { MediaType.APPLICATION_JSON_UTF8_VALUE,
    				MediaType.APPLICATION_XML_VALUE })	 
	public List<Chart> getTodayChart() {     	
    	SearchChartDTO dto = new SearchChartDTO();
    	dto.setDate("20200616");
    	return songService.getTodayChart(dto);
	}

 

주소창에 url 입력

 

다음과 같이 XML 형태로 된 데이터가 반환됩니다. 

JSON 형태로 데이터를 받아보고 싶다면 localhost:8080/..../getTodayChart.json 을 해주면 됩니다.