티스토리 뷰
1. Restful 방식의 서비스 개발
- 1 ) 최종 목적
- : MVC 패턴에서 View는 수행하지 않고, 순수한 데이터( JSON, XML )를 응답으로 제공
- 2 ) @RestController 어노테이션을 사용하여 컨트롤을 개발한다.
- 3 ) HTTP method
- : GET, POST 외의 여러가지의 추가적인 전송방식을 활용한다.
- 4 ) Request Mapping
- : C ( Create ) -> PUT
- : R ( Read ) -> GET
- : U ( Update ) -> POST
- : D ( Delete ) -> DELETE
- : URI ( URI 자체가 비즈니스 적인 의미를 가진다. )
- Ex. 게시물 삭제요청 [ DELETE, /board/bno/77 ]
- Ex. 게시물 등록요청 [ PUT, /board/new ]
[ 1. 자바 객체를 JSON으로 변환하기 ] (****)
[ + 데이터 바인딩 라이브러리 추가 ]
[ + 코드 보기 ]
<?xml version="1.0" encoding="UTF-8"?>
<!-- 1~2칸 띄우기 -->
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.zerock</groupId>
<artifactId>chap14</artifactId>
<version>1.0.0-BUILD-SNAPSHOT</version>
<!-- 웹에 패키징되는 건 war -->
<packaging>war</packaging>
<name>chap14</name>
<url>http://chap14.example.com</url>
<description>Spring MVC project - maven</description>
<!-- 프로젝트 어디서든 가져다 쓸수 있도록 정의 -->
<properties>
<!-- 자바 버전 -->
<java-version>11</java-version>
<!-- <java-home>${env.JAVA_HOME}</java-home> -->
<!-- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target> -->
<!-- 스프링 프레임 워크 버전 -->
<org.springframework-version>5.3.21</org.springframework-version>
<org.aspectj-version>1.9.9.1</org.aspectj-version>
<org.apache.logging.log4j-version>2.17.2</org.apache.logging.log4j-version>
</properties>
<dependencies>
<!-- =============== Logging =============== -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${org.apache.logging.log4j-version}</version>
</dependency>
<!-- For Spring framework, HikariCP, DriverSpy logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${org.apache.logging.log4j-version}</version>
</dependency>
<!-- ============= Servlet/JSP ============= -->
<!-- 이에 따라서 선택해야 하는 톰켓 버전이 달라질 수 있다. -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- =============== AspectJ (스프링할 때 rt, weaver 꼭)=============== -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId> <!-- rt = runtime -->
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- ================ Spring (밑의 2개는 최소한 꼭) =============== -->
<!-- spring bean container -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- spring mvc 구동 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- spring test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
<scope>test</scope>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.21</version>
</dependency>
<!-- spring-tx manager -->
<!-- X/Open XA 규약대로 모든 TX Manager가 구현되어 있다. -->
<!-- 1. WAS 2. Spring MVC 3. JDBC Driver 4. Database Instance 안에 구현체가 있다. -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.21</version>
</dependency>
<!-- =============== JDBC =============== -->
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4</artifactId>
<version>1.16</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8-production</artifactId>
<version>21.5.0.0</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>com.oracle.database.ha</groupId>
<artifactId>simplefan</artifactId>
</exclusion>
<exclusion>
<groupId>com.oracle.database.ha</groupId>
<artifactId>ons</artifactId>
</exclusion>
<exclusion>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>rsi</artifactId>
</exclusion>
<exclusion>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ucp</artifactId>
</exclusion>
<exclusion>
<groupId>com.oracle.database.xml</groupId>
<artifactId>xdb</artifactId>
</exclusion>
<exclusion>
<groupId>com.oracle.database.xml</groupId>
<artifactId>xmlparserv2</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- =============== Testing (버전 4,5 둘다 쓸줄알아야함)=============== -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<!-- ================ DataSource ================= -->
<!-- HikariCP -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
<!-- 이 부분만 뺀다 -->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- ================ MyBatis ================= -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!-- 마이바티스와 스프링 연동 라이브러리 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!-- ================ Data-Binding ================= -->
<!-- 변환 라이브러리 -->
<!-- (1) 자바객체를 직렬화(Serialize)하여 JSON / XML 로 변환 -->
<!-- (2) JSON / XML를 역직렬화(De-serialize)하여 자바객체로 변환 -->
<!-- Java 객체를 JSON으로 변환시켜 준다. -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
<!-- Java 객체를 XML로 변환해 준다. -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.13.3</version>
</dependency>
<!-- Java 객체를 JSON으로 변환 -->
<!-- 위의 2개는 스프링 프레임워크가 사용하고, gson은 우리가 사용한다. -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.1</version>
</dependency>
<!-- ================ File - upload ================= -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- ================ Misc ================= -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- 롬복 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>/</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.10</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>false</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<release>${java-version}</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
[ + 자바 객체 Person 객체를 JSON으로 변환 ]
[ + 코드 보기 ]
package org.zerock.myapp.gson;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
import com.google.gson.Gson;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
public class GsonTests {
@Test
public void testGson() {
log.trace("testGson() invoked.");
// + JSON 문자열로 변환할 자바객체 Person객체 생성
// + 변환할 객체의 필드를 모두 사용할 필요는 없기에, 필요 부분만 넣어도 된다.
Person person = Person.builder().
age(29).
name("YOYOYO").
height(180.2).
weight(60.3).
isMale(true).
hobbies( new String [] { "Movie", "Music", "Sleeping" }).
build();
assertNotNull(person);
// + 자바 객체 -> JSON으로 serialize(직렬화) 수행
Gson gson = new Gson();
String json = gson.toJson(person);
log.info("\t + json : {}", json);
// + json : {"name":"YOYOYO","age":29,"height":180.2,"weight":60.3,"isMale":true,"hobbies":["Movie","Music","Sleeping"]}
// + JSON은 위와 같이 필드의 이름을 모두 문자열로 표시한다.
// + 또한 배열이 {}에서 []로 바뀐다.
} // testGson
} // end class
@Builder
class Person {
private String name;
private int age;
private double height;
private double weight;
private boolean isMale;
private String[] hobbies;
} // Person
[ 2. 자바 객체를 JSON으로 변환하기 2 - list ]
[ 3. @RestController + produces - View가 아닌 순수한 데이터를 반환 ] (****)
[ 3 - 1. 순수한 데이터로 문자열을 반환 ]
[ + html 태그 적용 버전 ]
[ 3 - 2. 순수한 데이터로 XML을 반환 ]
[ 3 - 3. 순수한 데이터로 JSON을 반환 ]
[ 3 - 4. 순수한 데이터로 XML Or JSON 반환 ]
[ + 코드 보기 ]
package org.zerock.myapp.controller;
import java.util.ArrayList;
import java.util.List;
import org.apache.catalina.tribes.util.Arrays;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.zerock.myapp.domain.SampleVO;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
@RestController
@RequestMapping("/sample/")
//+ String / XML / JSON 형식으로 돌려주는 컨트롤러
public class StringXMLJSONController {
// ===================================================================
// 1. 순수한 데이터로 문자열( String )을 반환하는 헨들러 메소드 작성
// ===================================================================
// 1 ) 일반 text
@GetMapping(path="/sendText", produces = { "text/plain; charset=utf8" })
// + produces는 헨들러가 발생시키는 미디어 타입( Content type )을 지정한다.
// + text/plain; charset=utf8는 utf8로 구성된 평범한 text라는 의미이다.
public String sendText() {
log.info("sendText() invoked.");
return "안녕하세요!! 반가워요!";
// + 이제는 더 이상 View의 이름을 반환하지 않고, 순수한 데이터를 반환한다. (***)
// + http://localhost:8080/sample/sendText로 접속하면 반환된 순수 데이터가 나타난다.
} // sendText
// ===================================================================
// 2 ) html
@GetMapping(path="/sendText2", produces = { "text/html; charset=utf8" })
// + text/html; charset=utf8로 하면, html 파일로 인식하여 태그를 적용시켜 준다. (**)
public String sendText2() {
log.info("sendText2() invoked.");
return "<h1>안녕하세요!! 반가워요!</h1>";
// + 이제는 더 이상 View의 이름을 반환하지 않고, 순수한 데이터를 반환한다. (***)
// + http://localhost:8080/sample/sendText로 접속하면 반환된 순수 데이터가 나타난다.
} // sendText2
// ===================================================================
// 3 ) MediaType. 적용 ver
@GetMapping(path="/sendText3", produces = { MediaType.TEXT_HTML_VALUE + "; charset=utf8" })
// + MediaType을 통해 ContentType을 지정해 줄 수는 있으나, charset이 적용되지 않기에 따로 적용해 줘야 하며
// + MediaType 사용 시에는 반드시 뒤에 VALUE가 있는 버전으로 사용해야 한다.
public String sendText3() {
log.info("sendText3() invoked.");
return "<h1>안녕하세요!! 반가워요!</h1>";
// + 이제는 더 이상 View의 이름을 반환하지 않고, 순수한 데이터를 반환한다. (***)
// + http://localhost:8080/sample/sendText로 접속하면 반환된 순수 데이터가 나타난다.
} // sendText3
// ===================================================================
// 2. 순수한 데이터로 XML 문서를 반환하는 헨들러 메소드 작성
// ===================================================================
@GetMapping(path="/sendXML", produces = MediaType.TEXT_XML_VALUE + "; charset=utf8" )
public SampleVO sendXML() {
log.info("sendXML() invoked.");
SampleVO vo = new SampleVO("JOJOJOJO", 30);
log.info("\t + vo : {}", vo);
return vo;
// + URI로 접속하면 아래와 같이 출력된다.
// <SampleVO>
// <name>JOJOJOJO</name>
// <age>30</age>
// </SampleVO>
} // sendXML
// ===================================================================
// + 배열 버전
@GetMapping(path="/sendXML2", produces = MediaType.TEXT_XML_VALUE + "; charset=utf8")
public SampleVO[] sendXML2() {
log.info("sendXML2() invoked.");
SampleVO[] vo = {
new SampleVO("AAAAAAAA", 10),
new SampleVO("BBBBBBBB", 20),
new SampleVO("CCCCCCCC", 30) };
log.info("\t + vo : {}",Arrays.toString(vo));
return vo;
// + URI로 접속하면 아래와 같이 출력된다.
// <SampleVOs>
// <item>
// <name>AAAAAAAA</name>
// <age>10</age>
// </item>
// <item>
// <name>BBBBBBBB</name>
// <age>20</age>
// </item>
// <item>
// <name>CCCCCCCC</name>
// <age>30</age>
// </item>
// </SampleVOs>
} // sendXML2
// ===================================================================
// + list 버전
@GetMapping(path="/sendXML3", produces = MediaType.TEXT_XML_VALUE + "; charset=utf8")
public List<SampleVO> sendXML3() {
log.info("sendXML3() invoked.");
List <SampleVO> vo = new ArrayList<>();
vo.add(new SampleVO("AAAAAAAA", 50));
vo.add(new SampleVO("BBBBBBBB", 60));
vo.add(new SampleVO("CCCCCCCC", 70));
log.info("\t + vo : {}", vo);
return vo;
// + URI로 접속하면 아래와 같이 출력된다.
// <List>
// <item>
// <name>AAAAAAAA</name>
// <age>50</age>
// </item>
// <item>
// <name>BBBBBBBB</name>
// <age>60</age>
// </item>
// <item>
// <name>CCCCCCCC</name>
// <age>70</age>
// </item>
// </List>
} // sendXML3
// ===================================================================
// 3. 순수한 데이터로 JSOM 문서를 반환하는 헨들러 메소드
// ===================================================================
@GetMapping(path="/sendJSON", produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf8")
public SampleVO sendJSON() {
log.info("sendJSON() invoked.");
SampleVO vo = new SampleVO("JOJOJOJO", 30);
log.info("\t + vo : {}", vo);
return vo;
// + URI로 접속하면 아래와 같이 출력된다.
// {
// "name": "JOJOJOJO",
// "age": 30
// }
} // sendJSON
// ===================================================================
// 4. 순수한 데이터로 XML or JSON 문서를 반환하는 헨들러 메소드
// ===================================================================
// + XML보다는 JSON이 더 가볍고 다루기 편하기에, JSON을 주로 사용한다.
// + 2개를 동시에 하는 것을 불가능하다.
// ===================================================================
@GetMapping(
path="/sendXMLOrJSON",
produces = {
MediaType.APPLICATION_JSON_VALUE + "; charset=utf8",
MediaType.APPLICATION_XML_VALUE + "; charset=utf8" })
public SampleVO sendXMLOrJSON() {
log.info("sendXMLOrJSON() invoked.");
SampleVO vo = new SampleVO("JOJOJOJO", 30);
log.info("\t + vo : {}", vo);
return vo;
// + URI로 접속하면 아래와 같이 출력된다.
} // sendXMLOrJSON
// ===================================================================
} // end class
[ 4. Restful 방식의 컨트롤 헨들러 ] (*****)
[ + 코드 보기 ]
package org.zerock.myapp.controller;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
@CrossOrigin
// + @CrossOrigin을 붙여 놓으면 동일출처 원칙을 위반해도 데이터를 보내준다.
// + 비동기 호출로도 가능하게 만들어 준다.
@RestController
@RequestMapping("/product/")
public class PathVariableController {
// + Restful 방식의 컨트롤러 헨들러 메소드의 Request Mapping은 아래의 2가지를 잘 조합해야 한다.
// + (1) Http Method : PUT(C), GET(R), POST(U), DELETE(D) ( 4가지 )
// + (2) Mapping URI : CRUD 대상 자원을 지정하는 형태로 매핑
// + 아래의 예와 같이, 요청 비지니스 로직을 표현할 수 있는 형태로 전송방식(HTTP method)과
// + 요청URI(Request URI)를 조합해서 매핑을 표현하는 방식이 바로 "Restful" 방식이다.
// ===================================================================
// + Request URI 안에 지정된 경로 변수( Path Variable, 기호 : {경로변수명} ) 값 얻는 법
// + : 스프링에서는 @PathVariable이란 어노테이션을 제공하는데, 이를 통해서 한다.
// ===================================================================
// ===================================================================
// Case 1. 신규상품 등록( CREATE ) -> PUT
// ===================================================================
// + {}는 경로 구분자이다.
// + {} 안에는 가변적인 경로가 들어오게 된다.
// ===================================================================
@PutMapping(path="/{category}/new", produces = MediaType.TEXT_PLAIN_VALUE + "; charset=utf8")
public String createNewProduct(@PathVariable("category") String productCategory) {
// ===================================================================
// + Request URI로부터, 경로 변수의 값 얻어내기
// + : 얻어낸 경로변수 값을 매개변수의 타입에 맞게 형변환까지 해서 넣어준다.
// + : 주의. 매개변수의 이름은 경로변수명과 직접적인 연관성은 없기에, 자유롭게 작성이 가능하다.
// ===================================================================
log.trace("createNewProduct() invoked.");
log.trace("\t + productCategory : {}",productCategory);
return "SUCCESS";
} // createNewProduct
// ===================================================================
// Case 2. 기존 상품 삭제( DELETE ) -> DELETE
// ===================================================================
@DeleteMapping(path="/{category}/{id}", produces = MediaType.TEXT_PLAIN_VALUE + "; charset=utf8")
public String removeProduct(
@PathVariable("category") String productCategory,
@PathVariable("id") Integer productId ) {
log.trace("removeProduct() invoked.");
log.trace("\t + productCategory : {}", productCategory );
log.trace("\t + productId : {}", productId );
return "SUCCESS";
} // removeProduct
// ===================================================================
// Case 3. 기존 상품 수정( UPDATE ) -> POST
// ===================================================================
@PostMapping(path="/{category}/{id}", produces = MediaType.TEXT_PLAIN_VALUE + "; charset=utf8")
public String updateProduct(@PathVariable("category") String productCategory, @PathVariable("id") Integer productId) {
log.trace("updateProduct() invoked.");
log.trace("\t + productCategory : {}", productCategory );
log.trace("\t + productId : {}", productId );
return "SUCCESS";
} // updateProduct
// ===================================================================
// Case 4. 기존 상품 조회( READ ) -> GET
// ===================================================================
@GetMapping(path="/{category}/{id}", produces = MediaType.TEXT_PLAIN_VALUE + "; charset=utf8")
public String readProduct(@PathVariable("category") String productCategory, @PathVariable("id") Integer productId) {
log.trace("readProduct() invoked.");
log.trace("\t + productCategory : {}", productCategory );
log.trace("\t + productId : {}", productId );
return "SUCCESS";
} // readProduct
} // end class
'KH 정보교육원 [ Java ]' 카테고리의 다른 글
KH 123일차 - Spring ( 인터셉터 ) (***) (0) | 2022.08.24 |
---|---|
KH 122일차 - Spring ( Restful 방식 2 / 인터셉터 ) (****) (0) | 2022.08.22 |
KH 120일차 - Spring ( 페이징 처리 ) (******) (0) | 2022.08.18 |
KH 119일차 - Spring ( Oracle 힌트 / 게시판 만들기 / 페이징 처리 ) (*******) (0) | 2022.08.17 |
KH 118일차 - Spring ( MockMVC ) (*****) (0) | 2022.08.16 |