[ REST (REpresentational State Transfer) ]
: "분산 시스템"을 위한 HTTP 기반 소프트웨어 아키텍쳐
* 즉, 웹 어플리케이션, 다양한 언어, 모바일 어플리케이션, 다른 서버 (*다 HTTP 기반) 등 끼리 서로 통신할 수 있도록,
통역 역할을 해주는 API
참고) 분산시스템 : 하나의 시스템으로 보이는 독립된 컴퓨터들의 집합 -> 이를 위해 네트워크를 통한 컴퓨터 간의 통신이 필요
REST 구성 3가지 : 자원 , 행위 , 메시지
- 자원(resource) : 접근할 대상
- 메서드 : HTTP Method - GET(조회) , POST(생성) , PUT(수정), DELELTE(삭제)
* 일반적으로는 GET, POST 방식을 사용하나, REST에서는 PUT, DELETE도 사용한다. - 메시지
ex) " 상품명이 진라면인 상품을 생성한다” 라는 호출이 있을 때,
“상품”은 생성되는 자원 (resource)
“생성한다”라는 행위는 메서드 (post)
"상품명이 진라면인 상품"은 메시지 ({"name":"진라면","price":"1000"})
- 이를 REST 형태로 표현하면 다음과 같다.
HTTP POST , http://localhost/products/ {
"products": {
"name":"진라면",
"price":"1000"
}
}
REST 특징
- HTTP 프로토콜 기반 → 웹표준을 최대한 활용 → Stateless ( 클라이언트의 상태를 저장 x )
- 웹에 존재하는 모든 자원에 고유한 URI(통합자원식별자:Uniform Resource Identifier)를 부여해 활용
(uri 는 url 의 상위개념 )
- HTTP GET(조회) , POST(생성) , PUT(수정), DELELTE(삭제) - "다양한 클라이언트"에게 서비스를 제공하고 클라이언트와 서버 역할의 명확한 분리가 가능.
모바일 , 태블릿 등과 같은 다양한 디바이스 및 이기종(다른 기술언어로 구축)된 시스템에게 HTTP기반 서비스를 제공하기 위해 사용됨. 클라이언트는 REST API를 통해 서버와 정보를 주고 받는다 - "서비스별 분리 , 통합"에 대한 표준화된 방법을 제공
어플리케이션의 복잡도가 증가 -> 서비스별로 독립적으로 구축, 운영하는 것에 대한 필요성 대두
ex) 고객정보서비스 | 예약서비스 | 항공편서비스
* 관련 기술 : SOAP -> REST
[ REST API ]
: REST 기반 서비스 API
어플리케이션 간의 데이터 통신을 위한 어플리케이션 프로그래밍 인터페이스
> RESTful : REST API 제공하는 웹서비스 시스템을 지칭 , "A 서비스 시스템은 'RESTful' 하다"
> RestTemplate : Spring에서 제공하는 REST API Server와의 HTTP 통신을 위한 객체
서버와 서버간의 연동을 위해 사용된다
> Http Method : get , post , put , delete 을 지원하는 메서드를 제공한다
- getForEntity()
- postForObject()
- put()
- delete()
[ REST - 실제 코드 적용 예제 1 ]
SpringBoot 실행 방법
1. springboot4-REST(프로젝트 명) 의 Springboot4RestApplication.java을 실행
(* run on server가 아니라 java application으로 실행)
2. springboot4-REST(프로젝트명) 의 Springboot4RestApplication.java을 실행한 후
브라우저에서 application.properties에 지정한 port 8888 로 실행시킨다 localhost:7777
/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.kosta</groupId>
<artifactId>springboot4-REST</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot4-REST</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
SpringBoot 설정
/application.properties
# port setting
server.port=9999
# dbcp setting
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@127.0.0.1:1521:xe
spring.datasource.username=scott
spring.datasource.password=tiger
#view resolver
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
#devtools : reloading
spring.devtools.livereload.enabled=true
#log level setting
logging.level.root=ERROR
# mybatis setting
mybatis.type-aliases-package=org.kosta.myapp.model
mybatis.configuration.map-underscore-to-camel-case=true
/src/main/webapp/WEB-INF/views
View
/index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>REST</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
//ajaxSetup(): 실행될 AJAX 요청에 대한 기본 속성을 정의해 재사용
$.ajaxSetup({
success:function(result){
alert(result);
},
error: function (jqXHR) {
alert("jqXHR status code:"+jqXHR.status+" message:"+jqXHR.responseText);
}
});//ajaxSetup
$("#testGetListBtn").click(function(){
$.ajax({
type:"get",
url:"products",
success:function(productList){
$("#listView").empty();
$.each(productList,function(i,product){
$("#listView").append(product.id+" "+product.name+" "+product.maker+" "+product.price+"<br>").css("background","pink");
});
}
});//ajax
});//click
$("#testGetBtn").click(function(){
$.ajax({
type:"get",
url:"products/"+$("#pid").val(),
success:function(product){
alert(product.id+" "+product.name+" "+product.maker+" "+product.price);
}
});//ajax
});//click
$("#testCreateBtn").click(function(){
$.ajax({
type:"post",
url:"products",
data:$("#createProductForm").serialize()
}).done(function(){ // done - success 와 동일
$("#createProductForm")[0].reset();
});//ajax
});//click
$("#testPutBtn").click(function(){
$.ajax({
type:"put",
url:"products?"+$("#updateProductForm").serialize()
}).always(function(){ // always 요청에 대한 처리가 success or fail 상관없이 항상 실행
$("#updateProductForm")[0].reset();
});
});//click
$("#testDeleteBtn").click(function(){
$.ajax({
type:"delete",
url:"products/"+$("#deleteId").val()
});//ajax
});//click
});//ready
</script>
<style type="text/css">
.restImg{
width: 600px;
height: 400px
}
</style>
</head>
<body>
<%--
REST(REpresentational State Transfer) : 분산 시스템을 위한 HTTP 기반 소프트웨어 아키텍쳐
특징
0. HTTP 프로토콜 기반 -> 웹표준을 최대한 활용 -> Stateless ( 클라이언트의 상태를 저장 x )
1. 웹에 존재하는 모든 자원에 고유한 URI(통합자원식별자:Uniform Resource Identifier)를 부여해 활용
HTTP GET(조회) , POST(생성) , PUT(수정), DELELTE(삭제)
2. "다양한 클라이언트"에게 서비스를 제공하고 클라이언트와 서버 역할의 명확한 분리가 가능.
-> 모바일 , 태블릿 등과 같은 다양한 디바이스 및 이기종(다른 기술언어로 구축)된 시스템에게
HTTP기반 서비스를 제공하기 위해 사용되고 클라이언트는 REST API를 통해
서버와 정보를 주고 받는다
3. "서비스별 분리 , 통합"에 대한 표준화된 방법을 제공
어플리케이션의 복잡도가 증가 -> 서비스별로
독립적으로 구축, 운영하는 것에 대한 필요성 대두
ex) 고객정보서비스 | 예약서비스 | 항공편서비스
관련 기술 : SOAP -> REST
REST API :
REST 기반 서비스 API
어플리케이션 간의 데이터 통신을 위한 어플리케이션 프로그래밍 인터페이스
RESTful : REST API 제공하는 웹서비스 시스템을 지칭 , "A 서비스 시스템은 'RESTful' 하다"
--%>
<h3>REST 적용 웹어플리케이션 구현 예제 </h3>
<ul>
<li>
HTTP Request Method <br><br>
GET : 리스트 조회 <input type="button" value="testGetListBtn" id="testGetListBtn"><br>
<div id="listView"></div> <br> <br>
GET : 조회 <input type="button" value="testGetBtn" id="testGetBtn"><input type="text" id="pid"><br><br>
POST :생성 <input type="button" value="testCreateBtn" id="testCreateBtn"><br>
<form id="createProductForm">
상품명 <input type="text" name="name" size="5"> 제조사 <input type="text" name="maker" size="5"> 가격 <input type="number" name="price">
</form>
<br><br>
PUT : 수정 <input type="button" value="testPutBtn" id="testPutBtn"><br>
<form id="updateProductForm">
상품번호 <input type="text" name="id" size="3" id="updateId">
상품명 <input type="text" name="name" size="5"> 제조사 <input type="text" name="maker" size="5"> 가격 <input type="number" name="price">
</form>
<br>
DELETE : 삭제 <input type="button" value="testDeleteBtn" id="testDeleteBtn">
<input type="text" id="deleteId">
</li>
</ul>
<%-- src/main/resources/static 아래에 images/rest-api.png 가 있다 --%>
<img class="restImg" src="images/rest-api.png">
</body>
</html>
/src/main/webapp/resources/css
/board2.css
/* Remove the navbar's default margin-bottom and rounded borders */
.navbar {
margin-bottom: 0;
border-radius: 0;
}
/* Set height of the grid so .sidenav can be 100% (adjust as needed) */
.row.content {
height: 450px;
}
#main{
padding: 20px;
}
/* Set gray background color and 100% height */
.sidenav {
padding-top: 20px;
background-color: #f1f1f1;
height: 100%;
}
/* Set black background color, white text and some padding */
footer {
background-color: #555;
color: white;
padding: 15px;
}
/* On small screens, set height to 'auto' for sidenav and grid */
@media screen and (max-width: 767px) {
.sidenav {
height: auto;
padding: 15px;
}
.row.content {height:auto;}
}
MyBatis 설정 (Proxy)
/src/main/resource/org.kosta.myapp.model.mapper
/ProductMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- Sql Mapper -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.kosta.myapp.model.mapper.ProductMapper">
<sql id="selectProduct">
select id,name,maker,price from rest_product
</sql>
<select id="getAllProductList" resultType="productVO">
<include refid="selectProduct"></include>
order by id desc
</select>
<select id="findProductById" resultType="productVO" >
<include refid="selectProduct"></include>
where id=#{value}
</select>
<insert id="registerProduct" parameterType="productVO">
<selectKey keyProperty="id" resultType="string" order="BEFORE">
select rest_product_seq.nextval from dual
</selectKey>
insert into rest_product(id,name,maker,price)
values(#{id},#{name},#{maker},#{price})
</insert>
<delete id="deleteProduct" parameterType="string">
delete from rest_product where id=#{value}
</delete>
<update id="updateProduct" parameterType="productVO">
update rest_product set name=#{name},maker=#{maker},price=#{price}
where id=#{id}
</update>
</mapper>
src/main/java/org.kosta.myapp.model.mapper
/ProductMapper.java<<interface>>
package org.kosta.myapp.model.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.kosta.myapp.model.vo.ProductVO;
@Mapper
public interface ProductMapper {
public List<ProductVO> getAllProductList();
public ProductVO findProductById(String id);
public void registerProduct(ProductVO productVO);
public int updateProduct(ProductVO productVO);
public int deleteProduct(String id);
}
SpringBoot Application
/src/main/java/org.kosta.myapp
/Springboot4RestApplication.java -- 이 객체를 실행한다! (Run on server X - Java Application O)
package org.kosta.myapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Springboot4RestApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot4RestApplication.class, args);
}
}
Model
/src/main/java/org.kosta.myapp.model.vo
/ProductVO.java
package org.kosta.myapp.model.vo;
public class ProductVO {
private String id;
private String name;
private String maker;
private int price;
public ProductVO() {
super();
}
public ProductVO(String id, String name, String maker, int price) {
super();
this.id = id;
this.name = name;
this.maker = maker;
this.price = price;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMaker() {
return maker;
}
public void setMaker(String maker) {
this.maker = maker;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "ProductVO [id=" + id + ", name=" + name + ", maker=" + maker + ", price=" + price + "]";
}
}
Controller
/src/main/java/org.kosta.myapp.controller
/HomeController.java
package org.kosta.myapp.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HomeController {
@RequestMapping("/")
public String home() {
return "index";
}
}
@RestController annotaion 적용 - REST 적용
/ProductController.java
package org.kosta.myapp.controller;
import java.util.List;
import javax.annotation.Resource;
import org.kosta.myapp.model.mapper.ProductMapper;
import org.kosta.myapp.model.vo.ProductVO;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.PutMapping;
import org.springframework.web.bind.annotation.RestController;
@SuppressWarnings({ "unchecked", "rawtypes" })
@RestController // @Controller + @ResponseBody
public class ProductController {
@Resource
private ProductMapper productMapper;
@GetMapping("/products")
public List getAllProductList() {
System.out.println("Request Method : GET");
return productMapper.getAllProductList();
}
/*
* @PathVariable : url 정보를 변수로 할당받기 위한 어노테이션
* {id} 자리에 해당 어노테이션이 선언된 변수로 데이터가 할당된다
*/
@GetMapping("/products/{id}")
public ResponseEntity findProductById(@PathVariable("id") String id) {
System.out.println("Request Method : GET");
ProductVO product=productMapper.findProductById(id);
if (product == null) {
return new ResponseEntity("상품이 존재하지 않습니다", HttpStatus.NOT_FOUND);
}
return new ResponseEntity(product, HttpStatus.OK);
}
@PostMapping(value = "/products")
public ResponseEntity registerProduct( ProductVO productVO) {
System.out.println("Request Method : POST");
productMapper.registerProduct(productVO);
return new ResponseEntity(productVO.getId()+" "+productVO.getName()+" 상품등록완료", HttpStatus.OK);
}
@DeleteMapping("/products/{id}")
public ResponseEntity deleteProduct(@PathVariable String id) {
System.out.println("Request Method : DELETE");
if (productMapper.deleteProduct(id)==0) {
return new ResponseEntity(id+"상품 아이디에 대한 상품이 없어 삭제 불가", HttpStatus.NOT_FOUND);
}
return new ResponseEntity(id+" id 상품정보삭제완료", HttpStatus.OK);
}
@PutMapping("/products")
public ResponseEntity updateProduct(ProductVO productVO) {
System.out.println("Request Method : PUT "+productVO);
int result= productMapper.updateProduct(productVO);
if (result==0) {
return new ResponseEntity(productVO.getId()+"번 상품 아이디에 대한 상품이 없어 수정불가", HttpStatus.NOT_FOUND);
}
return new ResponseEntity(productVO.getId()+" id 상품정보수정완료", HttpStatus.OK);
}
}
[ Browser 결과 화면 ]
1. home 화면
2. 맨 위의 GET : 리스트 조회 옆 'testGetListBtn' 버튼 클릭 시, DB 정보 뜬다
3_1. 두 번째 줄의 GET: 조회에서 상품번호 입력 후, 'testGetBtn' 버튼 클릭 시,
DB에 있는 정보인 경우 'testGetListBtn' 버튼 클릭 시, 다음과 같이 alert이 뜬다.
3_2. DB에 없는 정보인 경우 'testGetListBtn' 버튼 클릭 시, 다음과 같이 alert이 뜬다. (에러메시지)
4_1. POST 생성에서, 새로운 product를 입력하여, DB에 추가
4_2. GET:리스트 조회 시, 방금 추가한 product가 추가되어 검색된다.
5_1. PUT:수정을 통해 방금 입력한 product의 정보를 수정 후, 'testPutBtn' 버튼을 누른다.
5_2. GET:리스트 조회 시, 방금 수정한 product가 수정되어 검색된다.
6_1. DELETE:삭제에 입력한 상품 번호를 입력하고, 'testDeleteBtn' 버튼을 누른다.
6_2. GET:리스트 조회 시, 방금 삭제한 product가 삭제되어 검색된다.
[ REST API - 실제 코드 적용 예제 2 ]
server 7777 - 8888 port 연동 test
> REST API 실행 방법
1. REST API Server 실행
springboot6-RestAPI-Server의 Springboot6RestAPIServerApplication을 실행
(run on server가 아니라 java application으로 실행)
2. springboot5 서버 실행
springboot5-RestTemplate의 Springboot5RestTemplateApplication을 실행한 후
브라우저에서 application.properties에 지정한 port 8888 로 실행시킨다 localhost:7777
/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.kosta</groupId>
<artifactId>springboot5-RestTemplate</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot4-REST-1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
SpringBoot 설정
/application.properties
# port setting
server.port=8888
# dbcp setting
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@127.0.0.1:1521:xe
spring.datasource.username=scott
spring.datasource.password=tiger
#view resolver
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
#devtools : reloading
spring.devtools.livereload.enabled=true
#log level setting
logging.level.root=ERROR
# mybatis setting
mybatis.type-aliases-package=org.kosta.myapp.model
mybatis.configuration.map-underscore-to-camel-case=true
/src/main/webapp/WEB-INF/views
View
/index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>REST</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
//ajaxSetup(): 실행될 AJAX 요청에 대한 기본 속성을 정의해 재사용
$.ajaxSetup({
success:function(result){
alert(result);
},
error: function (jqXHR) {
alert("jqXHR status code:"+jqXHR.status+" message:"+jqXHR.responseText);
}
});//ajaxSetup
$("#testGetListBtn").click(function(){
$.ajax({
type:"get",
url:"items",
success:function(itemList){
$("#listView").empty();
$.each(itemList,function(i,item){
$("#listView").append(item.id+" "+item.name+" "+item.maker+" "+item.price+"<br>").css("background","pink");
});
}
});//ajax
});//click
$("#testGetBtn").click(function(){
$.ajax({
type:"get",
url:"items/"+$("#pid").val(),
success:function(item){
alert(item.id+" "+item.name+" "+item.maker+" "+item.price);
}
});//ajax
});//click
$("#testCreateBtn").click(function(){
$.ajax({
type:"post",
url:"items",
data:$("#createItemForm").serialize()
}).done(function(){ // done - success 와 동일
$("#createItemForm")[0].reset();
});//ajax
});//click
$("#testPutBtn").click(function(){
$.ajax({
type:"put",
url:"items?"+$("#updateItemForm").serialize()
}).always(function(){ // always 요청에 대한 처리가 success or fail 상관없이 항상 실행
$("#updateItemForm")[0].reset();
});
});//click
$("#testDeleteBtn").click(function(){
$.ajax({
type:"delete",
url:"items/"+$("#deleteId").val()
});//ajax
});//click
});//ready
</script>
<style type="text/css">
.restImg{
width: 600px;
height: 400px
}
</style>
</head>
<body>
<%--
REST(REpresentational State Transfer) : 자원의 표현 상태(정보) 전송
분산 시스템을 위한 소프트웨어 아키텍쳐
특징
0. HTTP 프로토콜 기반 -> 웹표준을 최대한 활용
1. 자원에 고유한 URI(주소)를 부여해 활용
HTTP GET(조회) , POST(생성) , PUT(수정), DELELTE(삭제)
2. "다양한 클라이언트"에게 서비스를 제공
-> 모바일 , 태블릿 등과 같은 다양한 디바이스 및 이기종(다른 기술언어로 구축)된 시스템에게
HTTP기반 서비스를 제공하기 위해 사용된다
3. "서비스별 분리 , 통합"에 대한 표준화된 방법을 제공
어플리케이션의 복잡도가 증가 -> 서비스별로
독립적으로 구축, 운영하는 것에 대한 필요성 대두
ex) 고객정보서비스 | 예약서비스 | 항공편서비스
관련 기술 : RPC/CORBA/RMI -> SOAP -> REST
REST API :
REST 기반 서비스 API
어플리케이션 간의 데이터 통신을 위한 어플리케이션 프로그래밍 인터페이스
RESTful : REST API 제공하는 웹서비스를 지칭 , "A 서비스 시스템은 'RESTful' 하다"
--%>
<h3>REST 적용 웹어플리케이션 구현 예제 </h3>
<ul>
<li>REST(Representational State Transfer) </li>
<li>다양한 클라이언트(웹 or 앱) 가 표준화된 서비스를 받기 위한 HTTP 기반 아키텍쳐 스타일</li>
<li>
HTTP Request Method <br><br>
GET : 리스트 조회 <input type="button" value="testGetListBtn" id="testGetListBtn"><br>
<div id="listView"></div> <br> <br>
GET : 조회 <input type="button" value="testGetBtn" id="testGetBtn"><input type="text" id="pid"><br><br>
POST :생성 <input type="button" value="testCreateBtn" id="testCreateBtn"><br>
<form id="createItemForm">
상품명 <input type="text" name="name" size="5"> 제조사 <input type="text" name="maker" size="5"> 가격 <input type="number" name="price">
</form>
<br><br>
PUT : 수정 <input type="button" value="testPutBtn" id="testPutBtn"><br>
<form id="updateItemForm">
상품번호 <input type="text" name="id" size="3" id="updateId">
상품명 <input type="text" name="name" size="5"> 제조사 <input type="text" name="maker" size="5"> 가격 <input type="number" name="price">
</form>
<br>
DELETE : 삭제 <input type="button" value="testDeleteBtn" id="testDeleteBtn">
<input type="text" id="deleteId">
</li>
</ul>
<%-- src/main/resources/static 아래에 images/rest-api.png 가 있다 --%>
<img class="restImg" src="images/rest-api.png">
</body>
</html>
MyBatis 설정 (Proxy)
/src/main/resource/org.kosta.myapp.model.mapper
/ItemMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- Sql Mapper -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.kosta.myapp.model.mapper.ItemMapper">
<sql id="selectItem">
select id,name,maker,price from rest_item
</sql>
<select id="getAllItemList" resultType="ItemVO">
<include refid="selectItem"></include>
order by id desc
</select>
<select id="findItemById" resultType="ItemVO" >
<include refid="selectItem"></include>
where id=#{value}
</select>
<insert id="registerItem" parameterType="ItemVO">
<selectKey keyProperty="id" resultType="string" order="BEFORE">
select rest_item_seq.nextval from dual
</selectKey>
insert into rest_item(id,name,maker,price)
values(#{id},#{name},#{maker},#{price})
</insert>
<delete id="deleteItem" parameterType="string">
delete from rest_item where id=#{value}
</delete>
<update id="updateItem" parameterType="ItemVO">
update rest_item set name=#{name},maker=#{maker},price=#{price}
where id=#{id}
</update>
</mapper>
src/main/java/org.kosta.myapp.model.mapper
/ItemMapper.java<<interface>>
package org.kosta.myapp.model.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.kosta.myapp.model.vo.ItemVO;
@Mapper
public interface ItemMapper {
public List<ItemVO> getAllItemList();
public ItemVO findItemById(String id);
public void registerItem(ItemVO itemVO);
public int updateItem(ItemVO itemVO);
public int deleteItem(String id);
}
SpringBoot 설정
/src/main/java/org.kosta.myapp
/Springboot6RestAPIServerApplication.java -- 이 객체를 실행한다! (Run on server X - Java Application O)
package org.kosta.myapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Springboot6RestAPIServerApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot6RestAPIServerApplication.class, args);
}
}
REST API 설정 (Config)
/src/main/java/org.kosta.myapp.config
/RestTemplateConfig.java
package org.kosta.myapp.config;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.Charset;
import java.time.Duration;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
.requestFactory(() -> new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()))
.setConnectTimeout(Duration.ofMillis(5000)) // connection-timeout
.setReadTimeout(Duration.ofMillis(5000)) // read-timeout
.additionalMessageConverters(new StringHttpMessageConverter(Charset.forName("UTF-8")))
.build();
}
}
Model
/src/main/java/org.kosta.myapp.model.vo
/ItemVO.java
package org.kosta.myapp.model.vo;
public class ItemVO {
private String id;
private String name;
private String maker;
private int price;
public ItemVO() {
super();
}
public ItemVO(String name, String maker, int price) {
super();
this.name = name;
this.maker = maker;
this.price = price;
}
public ItemVO(String id, String name, String maker, int price) {
super();
this.id = id;
this.name = name;
this.maker = maker;
this.price = price;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMaker() {
return maker;
}
public void setMaker(String maker) {
this.maker = maker;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "ProductVO [id=" + id + ", name=" + name + ", maker=" + maker + ", price=" + price + "]";
}
}
/src/main/java/org.kosta.myapp.model.service
/ItemService.java <<interface>>
package org.kosta.myapp.service;
import java.util.List;
import org.kosta.myapp.model.vo.ItemVO;
public interface ItemService {
public List<ItemVO> getAllItemList();
public ItemVO findItemById(String id);
public void registerItem(ItemVO itemVO);
public int updateItem(ItemVO itemVO);
public int deleteItem(String id);
}
/ItemServiceImpl.java
package org.kosta.myapp.service;
import java.util.List;
import javax.annotation.Resource;
import org.kosta.myapp.model.mapper.ItemMapper;
import org.kosta.myapp.model.vo.ItemVO;
import org.springframework.stereotype.Service;
@Service
public class ItemServiceImpl implements ItemService {
@Resource
private ItemMapper itemMapper;
@Override
public List<ItemVO> getAllItemList() {
return itemMapper.getAllItemList();
}
@Override
public ItemVO findItemById(String id) {
return itemMapper.findItemById(id);
}
@Override
public void registerItem(ItemVO ItemVO) {
itemMapper.registerItem(ItemVO);
}
@Override
public int updateItem(ItemVO itemVO) {
return itemMapper.updateItem(itemVO);
}
@Override
public int deleteItem(String id) {
return itemMapper.deleteItem(id);
}
}
Controller
/src/main/java/org.kosta.myapp.controller
/HomeController.java
package org.kosta.myapp.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HomeController {
@RequestMapping("/")
public String home() {
return "index";
}
}
/ItemController.java
package org.kosta.myapp.controller;
import java.util.List;
import javax.annotation.Resource;
import org.kosta.myapp.model.vo.ItemVO;
import org.kosta.myapp.service.ItemService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@SuppressWarnings({ "unchecked", "rawtypes" })
@RestController // @Controller + @ResponseBody
public class ItemController {
@Resource
private ItemService itemService;
@GetMapping("/items")
public List getAllitemList() {
System.out.println("Rest API Server Request Method : GET");
return itemService.getAllItemList();
}
/*
* @PathVariable : url 정보를 변수로 할당받기 위한 어노테이션
* {id} 자리에 해당 어노테이션이 선언된 변수로 데이터가 할당된다
*/
@GetMapping("/items/{id}")
public ResponseEntity findItemById(@PathVariable("id") String id) {
System.out.println("Rest API Server Request Method : GET");
ItemVO item=itemService.findItemById(id);
if (item == null) {
return new ResponseEntity("아이템이 존재하지 않습니다", HttpStatus.NOT_FOUND);
}
return new ResponseEntity(item, HttpStatus.OK);
}
@PostMapping(value = "/items")
public ResponseEntity registerItem(@RequestBody ItemVO itemVO) {
System.out.println("Rest API Server Request Method : POST ");
itemService.registerItem(itemVO);
return new ResponseEntity(itemVO.getId()+" "+itemVO.getName()+" 아이템등록완료", HttpStatus.OK);
}
@DeleteMapping("/items/{id}")
public ResponseEntity deleteItem(@PathVariable String id) {
System.out.println("Rest API Server Request Method : DELETE");
if (itemService.deleteItem(id)==0) {
return new ResponseEntity(id+"아이템 아이디에 대한 아이템이 없어 삭제 불가", HttpStatus.NOT_FOUND);
}
return new ResponseEntity(id+" id 아이템정보삭제완료", HttpStatus.OK);
}
@PutMapping("/items")
public ResponseEntity updateItem(@RequestBody ItemVO itemVO) {
System.out.println("Rest API Server Request Method : PUT "+itemVO);
int result= itemService.updateItem(itemVO);
if (result==0) {
return new ResponseEntity(itemVO.getId()+"번 아이템 아이디에 대한 아이템이 없어 수정불가", HttpStatus.NOT_FOUND);
}
return new ResponseEntity(itemVO.getId()+" id 아이템정보수정완료", HttpStatus.OK);
}
}
[ Browser 결과 화면 ]
1. home 화면 - 현재 server port가 7777, 8888 둘 다 켜져있어 연동을 위한 테스트이다.
(위 예제 코드는 8888 port이며, 연동 테스트는 7777 port 이다.)
2. 하단의 'REST API 서버 테스트(서버간 통신테스트)' 클릭 시, 7777 포트로 이동