티스토리 뷰
1. Controller의 리턴 타입
- String : jsp를 이용하는 경우에는 jsp파일의 경로와 파일 이름을 나타내기 위해 사용
- Void : 호출하는 상세 URI와 동일한 이름의 jsp를 리턴
- VO, DTO 타입 : 주로 JSON 타입의 데이터를 만들어서 반환하는 용도로 사용
- Response Entity 타입 : response할 때 Http 헤더 정보와 내용을 가공하는 용도로 사용
- Model, ModelAndView : Model로 데이터를 반환하거나 화면까지 지정하는 경우 사용
- HttpHeaders : 응답에 내용 없이 Http 헤더 메시지만 전달하는 용도로 사용
2. 자바 객체를 XML이나 JSON으로 변환하는 이유
- HTTP protocol에 정의된, 요청/응답 메시지의 body(==payload)에는 사이즈의 제약없이 어떤 데이터도 넣을 수가 있다.
- 즉, byte 기반의 데이터이든 문자 기반의 데이터이든 다 넣을 수 있다는 의미이다.
- 문제는 만약 자바객체 또는 영화 파일처럼, 바이트 기반의 데이터가 응답메시지의 body에 저장된 채 웹 브라우저로 갈 경우
- 과연, 웹 브라우저는 응답메시지의 body에 저장된 바이트 기반 데이터를 가지고 렌더링이 가능한가? -> NO! 406 에러가 발생한다.
- 그렇기에 @ResponseBody 어노테이션이 붙은 Controller handler method의 return 값이 자바 객체인 경우( 즉, 바이트 기반 데이터 )
- 그대로 응답메시지의 body에 넣지 못하기에, spring은 이런 자바객체를 XML이나 JSON으로 변환해줄 수 있는 라이브러리를 찾아, 이러한 라이브러리를 이용하여 JSON이나 XML로 변환해 준다.
- 그리고 변환결과인 JSON이나 XML 문자열을 응답메시지의 body에 저장하여 웹 브라우저에 보내주게 된다. ( 이때는 406 에러가 발생하지 않는다. )
- + 이렇게 요청에 대한 응답으로 XML이나 JSON같은 순수한 데이터를 전송하는 서비스를 "OpenAPI(개방형API)" 라고 불린다.
- + 이러한 개방형 API가 필요한 이유는 바로 클라이언트가 더이상 웹 브라우저가 아니라, 스마트폰의 어플과 같은 다른 종류의 클라이언트 프로그램이 등장하면서, 이들이 원하는 응답은 HTML문서가 아니라, XML/JSON 같은 순수한 데이터를 수신하기를 바라게 되었기 때문이다.
[ 1. Java 객체를 JSON으로 변환해 주는 라이브러리 / Java 객체를 XML로 변환해 주는 라이브러리 추가 - pom.xml ]
[ + 코드 보기 ]
<?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>chap06</artifactId>
<version>1.0.0-BUILD-SNAPSHOT</version>
<!-- 웹에 패키징되는 건 war -->
<packaging>war</packaging>
<name>chap06</name>
<url>http://chap06.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 ================= -->
<!-- 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>
<!-- ================ 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>
[ 2. 단순히 화면을 띄우고 싶을 때, Cotroller에서 Handler를 등록하지 않고 화면 띄우기 ] (*)
[ 2 - 1. Servlet-context.xml 파일에 태그 등록 ]
[ 2 - 2. path에 지정한 URI로 접속 ( Controller에 Handler를 등록하지 않아도 된다. ) ]
[ + 코드 보기 ]
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<!-- /resources/**의 의미는 resources폴더 아래에 있는 어떠한(모든) 자원을 의미한다. -->
<!-- < resources mapping location >의 경우 파일을 생성하여 매핑할 수 있으며, 같은 location이여도 mapping을 추가할 수 있다. -->
<!-- 설정파일을 수정하였으면, WAS를 내렸다가 올려야 한다. -->
<!-- 아래의 태그로 인해 URL mapping처럼 http://localhost:8080/resources/파일명으로 접근이 가능하다. -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<!-- View로 어떤 JSP가 사용되어야 하는지 알려준다. -->
<!-- ViewResolver는 MVC 패턴에서 Model 데이터를 이용하여 최종 응답화면을 생성할 View 역할을 수행할 대상을 찾아낸다. -->
<!-- [ 예전 버전 ] -->
<!-- + 예전 버전은 빈으로 등록하는 방법을 사용했다. -->
<!-- <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> -->
<!-- beans:property는 setPrefix와 setSuffix를 value값으로 해주는것이다. -->
<!-- <beans:property name="prefix" value="/WEB-INF/views/" /> -->
<!-- <beans:property name="suffix" value=".jsp" /> -->
<!-- </beans:bean> -->
<!-- [ 현재 버전 ] -->
<!-- View Resolver -->
<view-resolvers>
<jsp prefix="/WEB-INF/views/" suffix=".jsp" />
</view-resolvers>
<!-- base-package에 작성한 패키지 내에 있는 것을 빈으로 등록시킨다. -->
<context:component-scan base-package="org.zerock.myapp.controller" />
<!-- 단순 View 호출을 위한 설정 -->
<!-- path = 전체 URI 지정 -->
<!-- view-name = 보여줄 jsp 명 -->
<view-controller path="/sample2/getsample" view-name="sample" status-code="200" />
</beans:beans>
[ 3. Controller - 리턴 타입에 따른 Controller ] (***)
[ + Controller 생성 후, 반드시 servlet-context.xml 파일에서 Controller가 빈으로 등록되어 있는지 확인하기 ]
[ 3 - 1. Return Type : String ( View 이름 반환 ) ]
[ 3 - 2. Return Type : void ( View이름이 자동으로 지정 ) ]
[ 3 - 3. Return Type : String + "redirect:" ] (***)
[ 3 - 4. Return Type : String + "forward:" ] (***)
[ 3 - 5. Return Type : java 객체 + @ResponseBody ] (****)
[ + 반환된 xml ]
[ 3 - 6. Return Type : String + @ResponseBody ]
[ 3 - 7. Return Type : 컬렉션 데이터 + @ResponseBody ]
[ 3 - 8. Return Type : 사용자 정의 객체 + @ResponseBody ]
[ 3 - 9. Return Type : Object + ResponseEntity ] (*****)
[ + 코드 보기 ]
package org.zerock.myapp.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.zerock.myapp.domain.SampleDTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
@Controller
@RequestMapping("/return/*")
public class ReturnTypesController {
// ===================================================================
// Case 1. return type : String ( View의 이름 )
// ===================================================================
@GetMapping("/String")
public String returnString() {
log.info("returnString");
return "return/String"; // => /WEB-INF/views/ + return/String + .jsp
} // returnString
// ===================================================================
// Case 2. return type : void ( View이름 자동 지정 )
// ===================================================================
@GetMapping("/void")
public void returnVoid() {
log.info("returnVoid() invoked.");
// + 리턴 타입이 void면, 뷰의 이름은 자동으로 호출 URI가 된다. ( 기본 URI + 상세 URI )
// + void => /WEB-INF/views/ + return/void + .jsp
} // returnVoid
// ===================================================================
// Case 3. return type : String + using "redirect:" Keyword
// ===================================================================
@PostMapping("/redirect")
public String returnRedirect() {
log.info("returnRedirect() invoked.");
return "redirect:/return/void"; // OK!!
// return "redirect:void"; // OK!! ( 상세 URI가 void인 것을 찾는다. )
} // returnRedirect
// ===================================================================
// Case 4. return type : String + using "forward:" Keyword
// ===================================================================
@GetMapping("/forward")
public String returnForward() {
log.info("returnForward() invoked.");
// + 지정한 경로의 JSP로 Request Forwarding
return "forward:/WEB-INF/views/return/void.jsp"; // OK!!
// + 지정된 호출 URI와 GET 방식으로 매핑된 Controller의 Handler 메소드로 포워딩 수행
// return "forward:/return/void"; // OK!!
// + 기본 URI를 생략하면, 현재 컨트롤러에 지정된 Base URI를 자동으로 붙여서 포워딩을 수행
// + 그렇기에 외부 URL로 포워딩을 수행하려고 해도, 자동으로 Base URI(/return)이 붙어 오류가 발생한다.
// + ( redirect:는 자동으로 Base URI(/return)를 붙이지 않기에 외부 URL로도 이동이 가능하다. ) (***)
// return "forward:void";
} // returnForward
// ===================================================================
// Case 5. return type : java 객체 + using @ResponseBody (***)
// ===================================================================
// + @ResponseBody (****) :
// + 아래의 메소드가 리턴한 값을 응답 메세지의 Body에 넣어달라!!
// ===================================================================
// + 이 경우 pom.xml에 Java객체를 xml로 변환해주는 디펜더시를 추가하였기에
// + SampleDTO이라는 Java 객체를 xml 파일로 변환해서 반환해준다.
// ===================================================================
// ===================================================================
// 1 ) 자바 객체 -> XML 전송
// ===================================================================
@PostMapping("/ResponseBody")
public @ResponseBody SampleDTO returnResponseBody(
// + @NonNull : 이 매개변수의 값은 null이 되어서는 안된다는 의미이다.
// + ( final 키워드와 동일한 역할 수행 )
@NonNull @RequestParam("name") String NAME,
@NonNull @RequestParam("age") Integer AGE ) {
log.info("returnResponseBody(NAME, AGE) invoked.");
log.info("\t + NAME :{}, AGE : {}", NAME, AGE);
SampleDTO dto = new SampleDTO();
dto.setName(NAME);
dto.setAge(AGE);
log.info("\t + dto : {}", dto);
// + Java 객체를 반환하면, 응답 메세지의 바디에 넣기 전에 변환 라이브러리를 통해서
// + JSON이나 XML 문서로 변환해서 넣어준다.
// + 결과적으로, 응답 메세지의 바디에 Java객체가 JSON이나 XML 문서로 변환되어 들어간다.
// + JSON으로 갈 경우에는, JS의 연관배열과 같이 { "name" : "hoho", "age" : 777 }로 반환된다.
return dto;
} // returnResponseBody
// ===================================================================
// 2 ) String 전송
// ===================================================================
// + produces에는 무엇을 생성하는지 작성해줘야 한다.
@PostMapping(path="/ResponseBody2", produces = {"text/plain; charset=utf8"} )
@ResponseBody
public String returnResponseBody2(
@NonNull @RequestParam("name") String NAME,
@NonNull @RequestParam("age") Integer AGE ) {
log.info("returnResponseBody2(NAME, AGE) invoked.");
log.info("\t + NAME :{}, AGE : {}", NAME, AGE);
// + String 객체를 응답으로 전송
String string = "안녕하세요!";
// + 문자열 역시 JSON이나 XML 처럼, 순수한 데이터이기 때문에 변환없이 전송이 가능하다.
return string;
} // returnResponseBody2
// ===================================================================
// 3 ) 컬렉션 데이터 -> XML / JSON 전송
// ===================================================================
@PostMapping(path="/ResponseBody3")
@ResponseBody
public List<String> returnResponseBody3(
@NonNull @RequestParam("name") String NAME,
@NonNull @RequestParam("age") Integer AGE ) {
log.info("returnResponseBody3(NAME, AGE) invoked.");
log.info("\t + NAME :{}, AGE : {}", NAME, AGE);
// + ArrayList 객체를 응답으로 전송
List<String> list = new ArrayList<>();
list.add("NAME_1");
list.add("NAME_2");
list.add("NAME_3");
// + 컬렉션 데이터 역시 JSON이나 XML로 변환되어 전송이 가능하다.
return list;
} // returnResponseBody3
// ===================================================================
// 4 ) 사용자 정의 객체
// ===================================================================
@AllArgsConstructor
@Data
class Person {
private String name;
private int age;
} // Person
@PostMapping(path="/ResponseBody4")
@ResponseBody
public Person returnResponseBody4(
@NonNull @RequestParam("name") String NAME,
@NonNull @RequestParam("age") Integer AGE ) {
log.info("returnResponseBody4(NAME, AGE) invoked.");
log.info("\t + NAME :{}, AGE : {}", NAME, AGE);
// + 사용자 정의 객체 또한 XML이나 JSON으로 변환되어 전송이 가능하다.
return new Person("Yoyoyo", 29);
} // returnResponseBody4
// ===================================================================
// Case 6. return type : Object + using "ResponseEntity"
// ===================================================================
// + "ResponseEntity" : 응답 메세지의 응답행, 헤더, 바디 모든 부분을
// + 개발자 마음대로 만들어 낼 수 있게 해주는 스프링의 클래스
// ===================================================================
@PostMapping("/returnResponseEntity")
// public ResponseEntity<String> returnResponseEntity() {
public ResponseEntity<Person> returnResponseEntity() {
log.info("returnResponseEntity() invoked.");
// 1. JSON 문자열 준비 -> 응답 메세지의 바디에 넣을 데이터
// String json = "{ 'name' : 'YOyoyoyo', 'age' : 29 }"; // 하드 코딩
Person person = new Person("Yoyoyoyo", 29);
// 2. 응답 메세지의 헤더를 구성
HttpHeaders headers = new HttpHeaders();
// + 컨텐츠 타입 지정 (****) : json으로 타입을 지정하고 있다.
headers.add("Content-Type", "application/json; charset=utf8");
// 3. 응답 메세지의 (1) 상태 코드 (2) 헤더 (3) 바디 모두 설정하여, 생성 및 변환
// + 생성자 매개변수 선언부 : (String body, MultiValueMap<String, String> headers, HttpStatus status)
// return new ResponseEntity<>(json, headers, HttpStatus.OK);
return new ResponseEntity<>(person, headers, HttpStatus.OK);
} // returnResponseEntity
} // end class
'KH 정보교육원 [ Java ]' 카테고리의 다른 글
KH 114일차 - Spring ( Controller - 예외처리 / 게시판 만들기 ) (*****) (0) | 2022.08.09 |
---|---|
KH 113일차 - Spring MVC ( 파일 업로드 처리 ) (****) (0) | 2022.08.08 |
KH 111일차 - Spring ( Spring MVC - Controller ) (*****) (0) | 2022.08.04 |
KH 110일차 - Spring ( Spring MVC ) (****) (0) | 2022.08.03 |
KH 109일차 - Spring ( mybatis-spring : mapper ) (******) (0) | 2022.08.02 |