스프링에 대하여 공부하는데는 적당한 것 같다. 특히 REST API에 대한 설명이 잘되어 있는 것 같다.



Posted by 세상을 살아가는 사람
,

나는 webiopi라는 라즈베리파이 상에서 web으로 GPIO를 제어하는 프로그램을 참조하여

자바 스프링으로 유사한 기능을 제어하려고 하고 있다.

webiopi는 REST API를 사용하여 GPIO를 제어하고 있어서 자바 스프링에서 REST API를 어떻게 처리하는지 파악하고 있다.

이 글에서는 webiopi에서 제공하는 REST API 기능에 대하여 자바 스프링으로 처리하기 위한 전 단계로 REST API 처리를 위한 Controller, Method 및 입력 파라미터 파싱에 대하여 알아본다.

1. Get GPIO function

  - HTTP GET /GPIO/(gpioNumber)/function

package com.talanton.rest.controller;


import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RestController;


@RestController

@RequestMapping("/GPIO")

public class GpioController {

@RequestMapping(value="/{gpioNumber}/function", method=RequestMethod.GET)

public ResponseEntity<String> getFunction(@PathVariable("gpioNumber") Integer gpioNumber) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : in";

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}

}


2. Set GPIO function

  - HTTP POST /GPIO/(gpioNumber)/function/("in" or "out" or "pwm")

@RequestMapping(value="/{gpioNumber}/function/{value}", method=RequestMethod.POST)

public ResponseEntity<String> postFunction(@PathVariable("gpioNumber") Integer gpioNumber, @PathVariable("value") String value) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : " + value;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


3. Get GPIO value

  - HTTP GET /GPIO/(gpioNumber)/value

@RequestMapping(value="/{gpioNumber}/value", method=RequestMethod.GET)

public ResponseEntity<String> getValue(@PathVariable("gpioNumber") Integer gpioNumber) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : 0";

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


4. Set GPIO value

  - HTTP POST /GPIO/(gpioNumber)/value/(0 or 1)

@RequestMapping(value="/{gpioNumber}/value/{value}", method=RequestMethod.POST)

public ResponseEntity<String> postValue(@PathVariable("gpioNumber") Integer gpioNumber, @PathVariable("value") Integer value) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : " + value;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


5. Output a single pulse

  - HTTP POST /GPIO/(gpioNumber)/pulse/

@RequestMapping(value="/{gpioNumber}/pulse", method=RequestMethod.POST)

public ResponseEntity<String> postPulse(@PathVariable("gpioNumber") Integer gpioNumber) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : " + "pulse";

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


6. Output bit sequence

  - HTTP POST /GPIO/(gpioNumber)/sequence/(delay),(sequence)

@RequestMapping(value="/{gpioNumber}/sequence/{delay},{sequence}", method=RequestMethod.POST)

public ResponseEntity<String> postSequence(@PathVariable("gpioNumber") Integer gpioNumber,

@PathVariable("delay") Integer delay, @PathVariable("sequence") String sequence) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : " + "sequence : " + delay + " : " + sequence;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


7. Output PWM with a duty cycle ratio

  - HTTP POST /GPIO/(gpioNumber)/pulseRatio/(ratio)

// 소수점 데이터를 입력받기 위한 방법 {variable:.+}

// 참고 : http://stackoverflow.com/questions/16332092/spring-mvc-pathvariable-with-dot-is-getting-truncated

@RequestMapping(value="/{gpioNumber}/pulseRatio/{ratio:.+}", method=RequestMethod.POST)

public ResponseEntity<String> postPulseRatio(@PathVariable("gpioNumber") Integer gpioNumber,

@PathVariable("ratio") Float ratio) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : pulseRatio : " + ratio;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


8. Output PWM with an angle for servos

  - HTTP POST /GPIO/(gpioNumber)/pulseAngle/(angle)

@RequestMapping(value="/{gpioNumber}/pulseAngle/{angle}", method=RequestMethod.POST)

public ResponseEntity<String> postPulseAngle(@PathVariable("gpioNumber") Integer gpioNumber,

@PathVariable("angle") Integer angle) {

ResponseEntity<String> entity = null;

String result = gpioNumber + " : pulseAngle : " + angle;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}


9. Call a macro on the server

  - HTTP POST /macros/(macro)/(args)

왜냐하면 URL이 /macros로 시작하기 때문이다. 기존의 GpioController는 REST API URL이 /GPIO로 시작하였기 때문에 사용할 수 없고, 새로운 MacroController를 사용한다.

package com.talanton.rest.controller;


import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RestController;


@RestController

@RequestMapping("/macros")

public class MacroController {


@RequestMapping(value="/{macro}/{args}", method=RequestMethod.POST)

public ResponseEntity<String> macroFunction(@PathVariable("macro") String macro, @PathVariable("args") String args) {

ResponseEntity<String> entity = null;

String result = "macroFunction : " + macro + ", " + args;

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}

}


10. Get full GPIO state/configuration

  - HTTP GET /*

    /* 라는 URL을 처리하기 위하여 새로운 RestController StateController를 정의한다.

package com.talanton.rest.controller;


import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RestController;


@RestController

@RequestMapping("")

public class StateController {


@RequestMapping(value="*", method=RequestMethod.GET)

public ResponseEntity<String> gpioState() {

ResponseEntity<String> entity = null;

String result = "getState";

entity = new ResponseEntity<String>(result, HttpStatus.OK);

return entity;

}

}


이제는 Webiopi에서 제공하는 REST API에 대한 처리 함수와 입력 파라미터 파싱을 완료하였다. 이후 실제 라즈베리파이에 적용하여 기능을 개발하는 것이 필요하다.

Posted by 세상을 살아가는 사람
,

나는 안드로이드를 2010년에 배우면서 Twitter Open API를 사용하는 것을 익히게 되었다. 트위터에서는 트윗을 보낼 때 Restful API를 사용하여 안드로이드 앱에서 서버로 트윗을 전송하고, 또 서버에 있는 트윗들을 앱으로 다운로드 한다. 이와 같이 트위터에서 자신들의 서버에 REST API를 제공하여 많은 어플리케이션을 개발할 수 있도록 하고 있다.

우리도 서버를 개발하면서 이와 같이 서버의 기능을 REST API를 통해 Open API로 제공하므로써 서비스의 이용을 촉진하고 수익의 증대를 가져올 수 있다. 따라서 REST API를 웹에서 제공하는 방법을 알아본다. 여기서는 jsp 방식의 스프링을 기반으로 기술한다.

REST는 'REpresentational State Transfer'의 약어로 하나의 URI는 하나의 고유한 리소스(Resource)를 대표하도록 설계한다는 개념이다.

REST API는 외부에서 특정 URI를 통해서 사용자가 원하는 정보를 제공하는 방식이다. 최근에 Open API에서 많이 사용되면서 REST 방식으로 제공되는 외부 연결 URI를 REST API라고 하고, REST 방식의 서비스 제공이 가능한 것을 'Restful'하다고 표현한다.

스프링은

- 버전 3부터 @ResponseBody annotation을 지원하면서 본격적으로 REST 방식의 처리를 지원

- 그 이전부터 스프링은 Content Negotiation 등을 이용해서 처리할 수 있다.

- 스프링 버전 4에 들어와서 @RestController가 본격적으로 소개됨


1. @RestController의 소개

  - 스프링 4에서 REST 방식의 데이터 처리를 위해서 Controller 차원의 annotation을 지원

  - 스프링 3까지는 컨트롤러에서 @Controller annotation을 사용해서 처리하고, 화면 처리를 담당하는 JSP가 아닌 데이터 자체를 서비스하려면 해당 메소드나 리턴 타입에 @ResponseBody annotation을 추가하는 형태로 작성


2. Sample project 연습

우선 @RestController를 사용하여 jsp view 형태가 아니라 문자열의 형태로 서버로부터 응답을 받는 방법을 기술한다. 반환되는 형태는 단순 문자열 또는 JSON, XML 형태를 가진다.

2.1 테스트용 컨트롤러 생성하기

  - 새로운 스프링 MVC 프로젝트를 생성

    + 이클립스에서 File>New>Spring Legacy Project 선택

    + Project name을 입력하고, Templates로 Spring MVC Project 선택하고 Next 버튼 클릭

    + 적절한 package name을 입력하고, Finish 버튼을 클릭

  - pom.xml 수정

    + Properties에서 java-version : 1.6 -> 1.8

    + Properties에서 org-springframework-version : 3.1.1.RELEASE -> 4.1.7.RELEASE

  - Project Facets 수정

    + 이클립스에서 프로젝트를 선택하고 우클릭하고 Properties 선택

    + Project Facets 선택하고 Java Version을 1.8로 변경하고 Runtimes에서 Apache Tomcat v8.5 선택하고 OK 버튼 클릭

  - @RestController로 SampleController 생성

    + java source에서 새로운 package 생성 : com.example.restful.controller

    + 기존 HomeController를 controller package 밑으로 이동하고 package 정보를 맞추어 준다.

    + 새로운 SampleController 생성

package com.example.restful.controller;


import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;


@RestController                    // REST API 기능을 처리하기 위해 @RestController annotation을 사용

@RequestMapping("/sample")  // path를 /sample로 지정

public class SampleController {


}


2.2 단순 문자열의 경우

  - @RestController에서 문자열 데이터는 기본적으로 브라우저에는 'text/html' 타입으로 처리된다.

  - 단순 문자열 처리를 위한 REST API를 만든다. (/sample/hello)

@RestController // REST API 기능을 처리하기 위해 @RestController annotation을 사용

@RequestMapping("/sample") // path(URI)를 /sample로 지정

public class SampleController {


@RequestMapping("/hello")    // path(URI)를 /sample/hello로 지정

public String sayHello() {        // method는 class내에서 path와 유사하게, 유닉하게 작명

return "Hello World!";    // 문자열(text/html) "Hello World!" 형태를 응답

}

}


  - contextPath 변경

    + 보통 REST API는 URI "/sample/hello"와 같이 contextPath가 "/"로 정의된다. 따라서 이클립스에서 default로 생성되는 contextPath를 변경해 주어야 한다.

    + Servers 창에서 Tomcat v8.5 Server at localhost를 double click하면 "Tomcat v8.5 Server at localhost" 창이 편집창에 생긴다. 여기서 Modules로 변경한 후, Web Modules에서 생성한 프로젝트를 선택 후 "Edit" 버튼을 클릭한다.

    + Path를 "/"로 수정하고 저장한다.

  - 다시 프로젝트를 선택하여 Tomcat을 구동한다. 그러면 "localhost:8080/"으로 구동됨을 확인할 수 있다.

  - 브라우저에서 "localhost:8080/sample/hello"를 입력하면 아래 그림과 같이 "Hello World!"가 출력됨을 확인할 수 있다.


2.3 객체를 JSON으로 반환하는 경우

  - 반환하는 형태가 문자열의 형태가 아니라 JSON 형태로 반환하는 방법을 기술한다.

  - 예를 들기 위하여 간단한 객체를 생성 (SampleVO)

package com.example.restful.domain;


public class SampleVO {

  private Integer mno;

  private String firstName;

  private String lastName;

  public Integer getMno() {

return mno;

  }

  public void setMno(Integer mno) {

this.mno = mno;

  }

  public String getFirstName() {

return firstName;

  }

  public void setFirstName(String firstName) {

this.firstName = firstName;

  }

  public String getLastName() {

return lastName;

  }

  public void setLastName(String lastName) {

this.lastName = lastName;

  }

  @Override

  public String toString() {

return "SampleVO [mno=" + mno + ", firstName=" + firstName + ", lastName=" + lastName + "]";

  }

}

  - 객체를 반환하는 REST API를 생성

  @RequestMapping("/sendVO")    // JSON 형식으로 객체 SampleVO를 반환하는 "/sample/sendVO" 작성

  public SampleVO sendVO() {       // SampleVO를 반환하는 sendVO method 정의

SampleVO vo = new SampleVO();     // SampleVO를 생성

vo.setMno(123);                            // 데이터를 삽입

vo.setFirstName("길동");

vo.setLastName("홍");

return vo;                                    // SampleVO 객체를 반환

  }


  - JSON 형태로 객체를 반환하기 위해서는 jackson-databind 라이브러리를 pom.xml에 추가로 정의해야 함

  <dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-databind</artifactId>

<version>2.8.4</version>

  </dependency>


  - 브라우저에서 접속 결과

   위 그림과 같이 JSON 형태의 SampleVO가 반환됨을 알 수 있다. 또한 아래 그림을 보면, Response Headers의 Content-Type으로 application/json 형태임을 확인할 수 있다.


2.4 컬렉션 타입의 객체를 반환하는 경우

  - 결과 데이터로 List 형태가 제공되는 경우에 대한 예를 들어 본다.

  @RequestMapping("/sendList")     // List 형태의 데이터를 반환하는 예 (/sample/sendList)

  public List<SampleVO> sendList() {    // List<SampleVO> 형태의 리스트를 반환하는 method sendList

List<SampleVO> list = new ArrayList<SampleVO>(); // List 생성

for(int i = 0;i < 10;i++) {    // 예로 10개의 SampleVO를 담는다.

SampleVO vo = new SampleVO(); // SampleVO를 생성

vo.setMno(i); // 데이터를 담는다.

vo.setFirstName("길동" + i);

vo.setLastName("홍");

list.add(vo); // list에 객체를 추가한다.

}

return list; // List 객체를 반환

  }

  - 아래 그림과 같이 JSON 형태로 List<SampleVO> list가 반환된다.


  - 결과 데이터로 Map 형태가 제공되는 경우에 대한 예를 들어 본다.

  @RequestMapping("/sendMap")

  public Map<Integer, SampleVO> sendMap() { // Map 형태의 데이터 반환하는 예 (/sample/sendMap)

Map<Integer, SampleVO> map = new HashMap<Integer, SampleVO>(); // Map 생성

for(int i = 0;i < 10;i++) { // 예로 10개의 SampleVO를 map에 추가

SampleVO vo = new SampleVO(); // SampleVO 생성

vo.setMno(i); // 데이터를 담는다.

vo.setFirstName("길동" + i);

vo.setLastName("홍");

map.put(i, vo); // Map에 SampleVO를 추가

}

return map; // Map 데이터를 반환

  }

  - 아래 그림은 Map 형태의 데이터를 JSON 형식으로 반환하는 결과이다.


Posted by 세상을 살아가는 사람
,