[Spring Boot] Controller 설계
Controller
Controller 는 클라이언트의 요청을 비즈니스 로직과 연결시켜주는 다리 역할을 한다.
앞서 작성한 스프링 부트 동작 방식 중 핸들러에 해당한다.
@Controller & @RestController
@Controller 와 @RestController의 차이점을 알아보자.
먼저, @RestController 어노테이션 코드를 보면
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
// (...)
@AliasFor(annotation = Controller.class)
String value() default "";
}
@Controller 어노테이션과 @ResponseBody 어노테이션이 포함되어있는 것을 알 수 있다.
@ResponseBody 어노테이션은 응답을 JSON 형식으로 바꿔주는 역할을 한다.
즉, @RestController 는 응답으로 객체를 전달할 때 JSON 형식으로 변환하고 헤더의 Content-Type을 application/json으로 설정해 전해준다.
URI & HTTP 메서드 매핑
Controller 에 작성한 메서드는 URI 및 HTTP 메서드 매핑이 필요하다.
@RequestMapping
@RequestMapping 어노테이션을 사용하여 매핑하는 방법이다.
@RestController
public class MyController {
// http://localhost:8080/api/hello
@RequestMapping("/api/hello")
public String hello() {
return "Hello";
}
}
@RequestMapping 어노테이션에 URI를 설정해주면 해당 URI로 들어온 요청을 처리해준다.
@RequestMapping 어노테이션을 메서드가 아닌 클래스에 붙여줄 수도 있다.
@RestController
@RequestMapping("/api")
public class MyController {
// http://localhost:8080/api/hello
@RequestMapping("/hello")
public String hello() {
return "Hello";
}
}
이처럼 클래스에 @ReqeustMapping 어노테이션을 붙이면 해당 클래스 이하 메서드에 공통적으로 적용된다.
하지만, 위 코드는 HTTP 메서드에 대한 설정을 해주지 않아 모든 HTTP 메서드를 전부 처리하게 된다.
value 옵션에는 매핑할 URI를, method 옵션에는 매핑할 HTTP 메서드를 설정해주면 된다.
@RestController
@RequestMapping("/api")
public class MyController {
// http://localhost:8080/api/hello, only GET Method
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
return "Hello";
}
}
그러나, 스프링 4.3 부터는 @GetMapping, @PostMapping, @PutMapping, @DeleteMapping 어노테이션들로 HTTP 메서드 매핑을 한 번에 설정해 줄 수 있다.
@RequestMapping 활용
@GetMapping, @PostMapping, ... 이 있다고 @RequestMapping 이 불필요하진 않다.
만약, HTTP 메서드를 하나가 아닌 여러 개 설정해주고 싶을때 method 옵션에 메서드를 배열로 입력해주면 된다.
+ value 도 String[] 타입으로 입력해줄 수 있다.
@RequestMapping(value = "hello", method = {RequestMethod.GET, RequestMethod.POST})
매개변수
Get 메서드와 Delete 메서드의 경우 일반적으로 URL에 데이터를 담는다.
URL에 데이터를 담는 방법은 URL 경로 자체에 담는 방법과 쿼리 형식으로 담는 방법이 있다.
@PathVariable
먼저 URL 자체에 담겨온 데이터를 가져오는 방법이다.
@RestController
@RequestMapping("/api")
public class MyController {
// http://localhost:8080/api/hello/menuhwang
@GetMapping(value = "/hello/{name}")
public String hello(@PathVariable String name) {
return "Hello, " + name; // Hello, menuhwang
}
}
URI를 설정할 때 가져올 값의 위치에 중괄호로 감싼 변수명을 적어주고, 메서드 파라미터에 @PathVariable 어노테이션과 함께 작성해준다.
이때 파라미터의 변수명과 URI 안의 변수명이 일치해야 한다.
라고 했지만 꼭 일치해야만 동작하는 것은 아니다.
@RestController
@RequestMapping("/api")
public class MyController {
// http://localhost:8080/api/hello/menuhwang
@GetMapping(value = "/hello/{name}")
public String hello(@PathVariable("name") String userName) {
return "Hello, " + userName; // Hello, menuhwang
}
}
@PathVariable 에 변수명을 입력하면 URI 안에 해당 변수명을 가져와준다.
@RequestParam
두 번째 방법으로는 쿼리 형식으로 담겨온 데이터를 가져오는 방법이다.
쿼리형식은 URL 과 ? 뒤 Key 와 Value 형태로 데이터가 담겨오는 방식이다.
http://www.sample.com/test?key=value&mode=test
@RestController
@RequestMapping("/api")
public class MyController {
// http://localhost:8080/api/hello?name=menuhwang&age=5
@GetMapping(value = "/hello")
public String hello(@RequestParam("name") String name, @RequestParam("age") int age) {
return "Hello, " + name + ", " + age; // Hello, menuhwang, 5
}
}
@PathVariable 과 마찬가지로 변수명과 쿼리 키값이 같다면 @RequestParam 에 변수명을 지정해 주지 않아도 된다.
Map 활용
혹시 어떤 쿼리 값으로 요청받을지 알 수 없다면 Map 자료형을 사용하면 된다.
@RestController
@RequestMapping("/api")
public class MyController {
// http://localhost:8080/api/hello?name=menuhwang&weight=60
@GetMapping(value = "/hello")
public String hello(@RequestParam Map<String, Object> param) {
StringBuilder sb = new StringBuilder();
param.entrySet().forEach(map -> {
sb.append(map.getKey() + " : " + map.getValue() + "\n");
});
return sb.toString(); // name : menuhwang weight : 60
}
}
URL URI 차이 [결론 못냄]
URL과 URI의 차이 URL URL (Uniform Resource Locator) 네트워크 상에서 자원이 어디 있는지를 알려주기 위한 규약 *출처 : 위키백과 URI URI (Uniform Resource Identifier) 인터넷에 있는 자원을 나타내는 유일..
menuhwang.tistory.com
DTO
대체로 API 설계 시 정해진 규칙대로 요청을 받도록 설계한다. 정해진 입력값을 가져올 때 DTO를 활용할 수 있다.
DTO (Data Transfer Object) 는 다른 레이어 간의 데이터 교환에 활용된다. 따라서 DTO에는 별도의 로직을 포함시키지 않는다.
public class MyDTO {
private String name;
private Integer age;
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "MyDTO{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Lombok 의 Getter, Setter 어노테이션으로 getter, setter 메서드 생략 가능
DTO, VO 비교
DTO 와 VO 비교 DTO : Data Transfer Object VO : Value Object DTO Data Transfer Object Transfer : 옮기다, 이송하다 DTO는 레이어 간 데이터를 이송하기 위한 객체이다. 따라서 다른 로직이 포함되지 않고 데..
menuhwang.tistory.com
@RequestBody
Post 메서드와 Put 메서드는 Get과 Delete 메서드와 달리 URL에 값을 담아 보내지 않고 HTTP Body에 담아 보낸다.
HTTP Body에 담긴 값을 사용하기 위해 @RequestBody 어노테이션을 사용한다.
@RestController
@RequestMapping("/api")
public class MyController {
@PostMapping(value = "/user")
public MyDTO postUser(@RequestBody MyDTO myDTO) {
return myDTO;
}
}
앞서 @ResponseBody 어노테이션이 Content-Type 을 json으로 설정하고 보내준다고 설명했다.
하지만 메서드의 리턴 타입이 String 인 경우 Content-Type을 확인해 보면 text/plain 으로 응답하는 것을 볼 수 있다.
boolean, int, 객체 (DTO처럼 직접 생성한 객체를 포함한) 등은 json으로 잘 응답하지만 문자열의 경우 text/plain 으로 응답한다. (옵션을 설정해주면 String도 Content-Type이 json으로 응답할 수 있다.)
추가적으로 DTO와 같이 객체를 리턴해주는 경우 getter가 없다면 값에 접근하지 못한다.
* HttpMediaTypeNotAcceptableException 발생
ResponseEntity
스프링에는 헤더와 Body로 구성된 HTTP 요청과 응답을 구성하는 HttpEntity 라는 클래스가 있다.
ResponseEntity는 이 HttpEntity를 상속받아 구현됐다.
ResponseEntity를 활용하면 HttpStatus 코드와 헤더, Body를 쉽게 구성하여 응답할 수 있다.
@RestController
@RequestMapping("/api")
public class MyController {
@PostMapping(value = "/user")
public ResponseEntity<MyDTO> postUser(@RequestBody MyDTO myDTO) {
return ResponseEntity
.status(HttpStatus.ACCEPTED)
.body(myDTO);
}
}
ResponseEnity.status() 는 BodyBuilder를 리턴.
BodyBuilder는 HeaderBuilder를 상속받음.
HeaderBuilder에 header() 메서드가 있음.
body() 메서드는 BodyBuilder에 있음.
HTTP Status Code 정리
HTTP Status Code HTTP 응답 코드 1XX 100번 대 응답 코드는 잘 사용하지 않아 따로 정리하지 않음. 2XX 성공적으로 요청이 처리되었음을 의미. 200 [OK] : 처리 완료. 201 [Created] : 생성 완료. 204 [No Conten..
menuhwang.tistory.com