들어가기 전에
전 시간에는 REST API에 대해 알아보았습니다.
REST API 아키텍처 스타일을 완벽하게 구현하지 못할 경우 이를 Web API라고 부른다고 하였습니다.
이번 시간에는 Web API에 대해 알아보도록 하겠습니다.
학습 목표
- Web API가 무엇인지 이해합니다.
핵심 개념
- Web API
학습하기
우리는 앞에서 Spring MVC를 이용해 간단한 Web 어플리케이션을 작성했었습니다.
http://localhost:8080/mvcexam/plusform 이라는 URL주소를 브라우저의 주소창에 입력하면,
2개의 값을 입력받는 폼(Form)이 보여졌고 해당 폼에 값을 입력하고 버튼을 누르면
2개의 숫자의 합이 출력되는 프로그램 이었습니다.
mvcexam 프로젝트에 덧셈을 구하는 Web API를 추가해봄으로써 Web API가 무엇인지 간단히 알아보도록 하겠습니다.
mvcexam 프로젝트에 다음의 클래스를 추가합니다.
package kr.or.connect.webmvc.dto;
public class PlusResult {
private int value1;
private int value2;
private int result;
public int getValue1() {
return value1;
}
public void setValue1(int value1) {
this.value1 = value1;
}
public int getValue2() {
return value2;
}
public void setValue2(int value2) {
this.value2 = value2;
}
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
}
PlusResult클래스는 3개의 정수 값을 저장할 수 있는 객체입니다.
package kr.or.connect.webmvc.controller;
import kr.or.connect.webmvc.dto.PlusResult;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class PlusApiController {
@GetMapping("/api/plus")
@ResponseBody
public PlusResult plus(@RequestParam("value1") int value1, @RequestParam("value2") int value2){
PlusResult plusResult = new PlusResult();
plusResult.setValue1(value1);
plusResult.setValue2(value2);
plusResult.setResult(value1 + value2);
return plusResult;
}
}
우리는 앞에서 컨트롤러를 만들어 봤습니다. 컨트롤러가 가지는 메소드는 String을 반환했던것 기억하시나요? 컨트롤러 메소드가 반환하는 String값은 뷰(View)이름이나 리다이렉트되는 경로를 의미했었습니다.
그런데, 지금 작성하는 컨트롤러는 String이 아닌 PlusResult 객체를 리턴하고 있습니다.
그리고 @ResponseBody 라는 어노테이션이 붙어있네요? @ResponseBody 어노테이션이 붙게 되면 해당 메소드는 뷰이름을 리턴하는 것이 아니라, 리턴한 객체를 출력하라는 의미를 가집니다.
여기까지 작성을 하고 서버를 재시작 합니다.
http://localhost:8080/mvcexam/api/plus?value1=10&value2=20
이라고 브라우저에서 입력해보세요.
다음과 에러가 발생하네요.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentException: No converter found for return value of type: class kr.or.connect.webmvc.dto.PlusResult
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
근본 원인 (root cause)
java.lang.IllegalArgumentException: No converter found for return value of type: class kr.or.connect.webmvc.dto.PlusResult
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:187)
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:174)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:132)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
비고 근본 원인(root cause)의 풀 스택 트레이스를, 서버 로그들에서 확인할 수 있습니다.
에러메시지를 보면
‘No converter found for return value of type: class kr.or.connect.webmvc.dto.PlusResult’와 같은 내용이 보입니다.
PlusResult에 대한 컨버터가 없다는 것입니다.
DispathcerServlet은 컨트롤러 메소드를 실행하고 해당 메소드가 리턴한 객체를 변환시키려고 합니다.
변환을 시킬 때 메시지 컨버터(MessageConverter)를 사용하게 되는데 메시지 컨버터가
Bean으로 등록 되어 있지 않으면 위와 같은 오류를 출력하게 됩니다.
위와 같은 오류가 발생하지 않도록 하려면 MessageConverter를 Bean으로 등록해줘야 합니다.
보통 Web API는 JSON, XML 과 같은 데이터를 표현하기에 알맞은 형태로 결과를 출력합니다.
PlusResult를 JSON메시지로 변환하려면 pom.xml 파일에 다음의 라이브러리를 추가합니다.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
jackson 라이브러리는 객체를 JSON으로 또는 JSON을 객체로 변환시킬 때 주로 사용합니다.
2개의 라이브러리를 포함시킨 후 서버를 재시작 합니다.
http://localhost:8080/mvcexam/api/plus?value1=10&value2=20라고 다시 입력합니다.
이번엔 다음과 같이 JSON메시지가 브라우저에 출력되는 것을 확인할 수 있습니다. 에러가 발생하지 않았다는 것은 메시지 컨버터가 자동으로 등록되었다는 것을 의미합니다.
{"value1":10,"value2":20,"result":30}
HTML 결과물처럼 예쁘게 보여지지는 않습니다만, 2개의 값과 그 합한 결과가 무엇인지 알 수 있습니다.
지금까지 간단한 Web API를 만드는 실습을 해보았습니다.
2개의 정수값을 전달하여 결과를 구하는 Web API를 만들어 보았습니다.
다음 시간엔 이런 Web API를 만드는 방법을 좀 더 자세히 살펴보도록 하겠습니다.
생각해보기
아래는 공공기관 버스도착정보 Web API에 대한 문서입니다.
이 문서를 읽어보고 어떻게 활용하면 좋을까?하고 생각해보세요.
comment
라이브러리를 추가하셨으니 메이븐 업데이트 필수입니다!
http://localhost:8080/mvcexam/api/plus?value1=10&value2=20 입력시 크롬같은 브라우저에서는 정상출력되는데 이클립스에서는 plus.json파일을 열기/저장하라고 팝업창이 뜨는건 이클립스브라우저가 json파일을 지원하지 않기 때문인가요??