본문 바로가기
Java Web Programming/7. REST API

[REST API] REST / REST API 개념과 적용 + 코드 예제 (SpringBoot 기반)

by 파프리카_ 2020. 12. 22.
728x90
반응형

[ REST (REpresentational State Transfer) ]

: "분산 시스템"을 위한 HTTP 기반 소프트웨어 아키텍쳐

 

* 즉, 웹 어플리케이션, 다양한 언어, 모바일 어플리케이션, 다른 서버  (*다 HTTP 기반) 등 끼리 서로 통신할 수 있도록,

통역 역할을 해주는 API

 

참고) 분산시스템 : 하나의 시스템으로 보이는 독립된 컴퓨터들의 집합 -> 이를 위해 네트워크를 통한 컴퓨터 간의 통신이 필요


REST 구성 3가지 : 자원 , 행위 , 메시지

  1. 자원(resource) : 접근할 대상
  2. 메서드 : HTTP Method - GET(조회) , POST(생성) , PUT(수정), DELELTE(삭제)
    * 일반적으로는 GET, POST 방식을 사용하나, REST에서는 PUT, DELETE도 사용한다.
  3. 메시지

ex) " 상품명이  진라면인 상품생성한다” 라는 호출이 있을 때,

상품”은 생성되는 자원 (resource)

생성한다”라는 행위는 메서드 (post)

"상품명이 진라면인 상품"은 메시지   ({"name":"진라면","price":"1000"})

 

- 이를 REST 형태로 표현하면 다음과 같다.

HTTP POST , http://localhost/products/ {
	"products": {
    		"name":"진라면",
		"price":"1000"
	}
}

REST 특징

 

  1. HTTP 프로토콜 기반 → 웹표준을 최대한 활용 →  Stateless ( 클라이언트의 상태를 저장 x )
  2. 웹에 존재하는 모든 자원에 고유한 URI(통합자원식별자:Uniform Resource Identifier)를 부여해 활용
    (uri 는 url 의 상위개념 )
    - HTTP GET(조회) , POST(생성) , PUT(수정), DELELTE(삭제)
  3. "다양한 클라이언트"에게 서비스를 제공하고 클라이언트와 서버 역할의 명확한 분리가 가능.
    모바일 , 태블릿 등과 같은 다양한 디바이스 및 이기종(다른 기술언어로 구축)된 시스템에게 HTTP기반 서비스를 제공하기 위해 사용됨. 클라이언트는 REST API를 통해 서버와 정보를 주고 받는다
  4. "서비스별 분리 , 통합"에 대한 표준화된 방법을 제공
    어플리케이션의 복잡도가 증가 -> 서비스별로 독립적으로 구축, 운영하는 것에 대한 필요성 대두

    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 포트로 이동 

728x90
반응형