ResponseEntity

호돌·2021년 7월 19일
1

BackEnd

목록 보기
15/25

Controller 와 RestController의 차이점

Spring MVC Controller와 Restful Web Service Controller의 가장 큰 차이점은 HTTP Response Body가 생성되는 방식입니다.

기존 MVC Controller는 View를 활용하여 주로 view(화면)을 return 합니다.

하지만 Restful 웹 서비스 Controller는 객체를 반환하기만 하면 객체 데이터는 JSON/XML 형식의 HTTP 응답을 직접 작성하게 됩니다.

Spring MVC의 전통적인 Work Flow

아래는 대표적인 Controller의 예시이다.

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
 
@Controller
@RequestMapping("/hi/*")
public class Controller {
    @RequestMapping("/hello")
    public String Hello(){
        return "hello";
    }
}

아래는 Controller를 통한 view (hello.jsp)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
    hello world
</body>
</html>

간략한 Spring MVC의 흐름은 다음과 같습니다.
1. Client는 URI 형식으로 web service에 요청(request)를 보냅니다.
2. 요청(request)는 Handler Mapping과 그 type을 찾는 DispatcherServlet에 의해 인터셉트 됩니다.
3. 요청(request)는 Controller에 의해 처리되고 응답은 DispatcherServlet으로 리턴된 후 DispatcherServlet은 View로 디스패치 됩니다.

Spring MVC WorkFlow는 모델앤 뷰(ModelAndView) 객체가 Controller에서 Client로 전달되는 것을 알 수 있습니다.

단,@ResponseBody 어노테이션을 사용하면 View를 return하지 않고 Controller에서 직접 Data를 return할 수 있습니다.

(Spring 4.0 version 부터는 @RestController 어노테이션을 통해 더 쉽게 사용할 수 있습니다.)

Spring 4.x REstful Web Serivce Work Flow

RestController

아래는 간략한 RestController 이다.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import com.example.spring02.model.board.dto.BoardVO;
 
@RestController
@RequestMapping("/basic/*")
public class ControllerRest {
    
    // json객체 리턴
    @RequestMapping("/sendVO2")
    public BoardVO sendVO2(){
        BoardVO vo = new BoardVO();
        vo.setBno(1);
        vo.setWriter("DoubleS");
        vo.setContent("게시글 내용입니다");
        vo.setRecnt(1);
        vo.setTitle("게시글 1");
        vo.setUserName("DoubleS");
        return vo;
    }
    
    // json 객체 배열 리턴
    @RequestMapping("/sendList")
    public List<BoardVO> sendList(){
        // ArrayList 객체 생성
        List<BoardVO> items = new ArrayList<>();
        for(int i=1; i <=10; i++){
            BoardVO vo = new BoardVO(); //vo 객체 생성
            vo.setBno(i);
            vo.setWriter("DoubleS"+i);
            vo.setContent("게시글 내용입니다"+i);
            vo.setRecnt(i);
            vo.setTitle("게시글"+i);
            vo.setUserName("DoubleS"+i);
            items.add(vo); // 리스트에 vo추가
        }
        return items; // 리스트를 리턴함
    }
    
    // json객체를 map에 저장하여 
    @RequestMapping("/sendMap")
    public Map<Integer, BoardVO> sendMap(){
        // Map<Key자료형, Value자료형>
        Map<Integer, BoardVO> map = new HashMap<Integer, BoardVO>();
        for(int i=1; i <=10; i++){
            BoardVO vo = new BoardVO(); //vo 객체 생성
            vo.setBno(i);
            vo.setWriter("DoubleS"+i);
            vo.setContent("게시글 내용입니다"+i);
            vo.setRecnt(i);
            vo.setTitle("게시글"+i);
            vo.setUserName("DoubleS"+i);
            map.put(i, vo); // 맵에 vo추가
        }
        return map;
    }
}
 

Spring4.0에서는 @RestController 어노테이션을 선언해주면

컨트롤러 클래스의 각 메서드마다 @ResponseBody을 추가할 필요가 없어졌고, 모든 메서드는 @ResponseBody 애노테이션이 기본으로 작동이 됩니다.

ResponseEntity

RestController는 별도의 View를 제공하지 않는 형태로 서비스를 실행하기 때문에, 때로는 결과데이터가 예외적인 상황에서 문제가 발생할 수 있습니다.

ResponseEntity는 개발자가 직접 결과 데이터와 HTTP 상태 코드를 직접 제어할 수 있는 클래스로 개발자는 404나 500같은 HTTP 상태 코드를 전송하고 싶은 데이터와 함께 전송할수 있기 때문에 좀더 세밀한 제어가 필요한 경우 사용할 수 있습니다.

아래는 ResponseEntity의 예시입니다.

@GetMapping("/hello")
ResponseEntity<String> hello() {
    return new ResponseEntity<>("Hello World!", HttpStatus.OK);
}
@GetMapping("/age")
ResponseEntity<String> age(
  @RequestParam("yearOfBirth") int yearOfBirth) {
 
    if (isInFuture(yearOfBirth)) {
        return new ResponseEntity<>(
          "Year of birth cannot be in the future", 
          HttpStatus.BAD_REQUEST);
    }

    return new ResponseEntity<>(
      "Your age is " + calculateAge(yearOfBirth), 
      HttpStatus.OK);
}

또한 사용자 설정의 HTTP 헤더를 설정할 수 있습니다.

@GetMapping("/customHeader")
ResponseEntity<String> customHeader() {
    HttpHeaders headers = new HttpHeaders();
    headers.add("Custom-Header", "foo");
        
    return new ResponseEntity<>(
      "Custom header set", headers, HttpStatus.OK);
}

또한 BodyBuilder 상태(HttpStatus 상태) 및 BodyBuilder 상태(int 상태) 메서드를 사용하여 HTTP 상태를 설정할 수 있습니다.

마지막으로 ResponseEntity BodyBuilder.body(T body) 를 사용하여 HTTP 응답 본문을 설정할 수 있습니다.

@GetMapping("/age")
ResponseEntity<String> age(@RequestParam("yearOfBirth") int yearOfBirth) {
    if (isInFuture(yearOfBirth)) {
        return ResponseEntity.badRequest()
            .body("Year of birth cannot be in the future");
    }

    return ResponseEntity.status(HttpStatus.OK)
        .body("Your age is " + calculateAge(yearOfBirth));
}
@GetMapping("/customHeader")
ResponseEntity<String> customHeader() {
    return ResponseEntity.ok()
        .header("Custom-Header", "foo")
        .body("Custom header set");
}

엔티티와 DTO

기존 엔티티를 파라미터로 받아서 사용하기엔 많은 부담이 있을 수 있다. 적합한 form의 DTO를 만들어서 사용하는것이 올바른 방법일것이다.

또한 엔티티가 화면 종속적인 코드들로 증가되서는 안된다. 최대한 엔티티가 순수하게 유지될 수 있게, 핵심 비지니스 로직에만 종속되게 만들어야 유지보수성을 높일 수 있다. 또한 API 환경에서 API 스펙에 엔티티가 추가되면 많은 문제를 초래할 수 있기에 각 화면에 맞는 DTO를 생성하여 개발하는것을 권장한다.

📝참고

https://2ham-s.tistory.com/279
https://www.baeldung.com/spring-response-entity

profile
저도 모르는데요?, 내가 몰라서 적는 글

0개의 댓글