1. 소개
이 예제에서는 일반적인 Java 인터페이스를 사용하여 웹 요청을 지정할 수 있는 Spring MVC의 새로운 기능을 고려합니다.
2. 개요
일반적으로 Spring MVC에서 컨트롤러를 정의할 때 요청을 지정하는 다양한 어노테이션(엔드포인트의 URL, HTTP 요청 메서드, 경로 변수 등)으로 메서드를 장식합니다.
예를 들어, 일반 메서드에서 해당 어노테이션을 사용하여 /save/{id} 엔드포인트를 도입할 수 있습니다.
@PostMapping("/save/{id}")
@ResponseBody
public Book save(@RequestBody Book book, @PathVariable int id) {
// implementation
}
당연히 요청을 처리하는 컨트롤러가 하나만 있는 경우에는 전혀 문제가 되지 않습니다. 동일한 메서드 서명을 가진 다양한 컨트롤러가 있는 경우 상황이 약간 바뀝니다.
예를 들어 마이그레이션 또는 이와 유사한 이유로 동일한 메서드 서명을 가진 두 가지 다른 버전의 컨트롤러가 있을 수 있습니다. 이 경우 메소드 정의와 함께 상당한 양의 중복 어노테이션이 있습니다. 분명히, 그것은 DRY( 자신을 반복하지 마십시오 ) 원칙을 위반할 것입니다.
이 상황이 순수 Java 클래스에 대해 발생한다면 우리는 단순히 인터페이스를 정의하고 클래스가 이 인터페이스를 구현하도록 할 것입니다. 컨트롤러 에서 메서드에 대한 주요 부담은 메서드 서명 때문이 아니라 메서드 어노테이션 때문입니다.
그러나 Spring 5.1에는 다음과 같은 새로운 기능이 도입되었습니다.
컨트롤러 매개변수 어노테이션은 인터페이스에서도 감지됩니다. 컨트롤러 인터페이스에서 완전한 매핑 계약을 허용합니다.
이 기능을 어떻게 사용할 수 있는지 조사해 보겠습니다.
3. 컨트롤러의 인터페이스
3.1. 컨텍스트 설정
책을 관리하는 매우 간단한 REST 애플리케이션의 예를 사용하여 새 기능을 설명합니다. 책을 검색하고 수정할 수 있는 메서드가 있는 컨트롤러 하나만으로 구성됩니다.
예제에서는 기능과 관련된 문제에만 집중합니다. 애플리케이션의 모든 구현 문제는 GitHub 저장소 에서 찾을 수 있습니다 .
3.2. 상호 작용
메소드의 서명뿐만 아니라 처리해야 하는 웹 요청 유형도 정의하는 일반적인 Java 인터페이스를 정의해 보겠습니다.
@RequestMapping("/default")
public interface BookOperations {
@GetMapping("/")
List<Book> getAll();
@GetMapping("/{id}")
Optional<Book> getById(@PathVariable int id);
@PostMapping("/save/{id}")
public void save(@RequestBody Book book, @PathVariable int id);
}
클래스 수준 어노테이션과 메서드 수준 어노테이션이 있을 수 있습니다. 이제 이 인터페이스를 구현하는 컨트롤러를 만들 수 있습니다.
@RestController
@RequestMapping("/book")
public class BookController implements BookOperations {
@Override
public List<Book> getAll() {...}
@Override
public Optional<Book> getById(int id) {...}
@Override
public void save(Book book, int id) {...}
}
컨트롤러 에 클래스 수준 어노테이션 @RestController 또는 @Controller 를 추가해야 합니다. 이러한 방식으로 정의된 컨트롤러는 웹 요청 매핑과 관련된 모든 어노테이션을 상속합니다.
이제 컨트롤러가 예상대로 작동하는지 확인하기 위해 애플리케이션을 실행하고 해당 요청을 수행 하여 getAll() 메서드를 실행해 보겠습니다.
curl http://localhost:8081/book/
컨트롤러가 인터페이스를 구현하더라도 웹 요청 어노테이션을 추가하여 더 미세 조정할 수 있습니다. 인터페이스에 대해 했던 것처럼 클래스 수준에서든 메서드 수준에서든 그렇게 할 수 있습니다. 사실, 우리는 컨트롤러를 정의할 때 이 가능성을 사용했습니다:
@RequestMapping("/book")
public class BookController implements BookOperations {...}
컨트롤러에 웹 요청 어노테이션을 추가하면 인터페이스의 어노테이션보다 우선합니다. 즉, Spring은 Java가 상속을 처리하는 방식과 유사한 방식으로 컨트롤러 인터페이스를 해석합니다.
우리는 인터페이스에서 모든 공통 웹 요청 속성을 정의하지만 컨트롤러에서는 항상 미세 조정할 수 있습니다.
3.3. 주의사항
인터페이스와 이를 구현하는 다양한 컨트롤러가 있는 경우 웹 요청이 둘 이상의 방법으로 처리될 수 있는 상황이 발생할 수 있습니다. 당연히 Spring은 예외를 던질 것입니다:
Caused by: java.lang.IllegalStateException: Ambiguous mapping.
컨트롤러를 @RequestMapping 으로 장식하면 모호한 매핑의 위험을 줄일 수 있습니다.
4. 결론
이 예제에서는 Spring 5.1에 도입된 새로운 기능을 고려했습니다. 이제 Spring MVC 컨트롤러가 인터페이스를 구현할 때 표준 Java 방식으로 수행할 뿐만 아니라 인터페이스에 정의된 모든 웹 요청 관련 기능을 상속합니다.
항상 그렇듯이 GitHub 리포지토리 에서 해당 코드 조각을 찾을 수 있습니다 .