1. 소개
MVC(Model View Controller)는 웹 애플리케이션을 구축하는 데 널리 사용되는 디자인 패턴입니다. 몇 년 동안 현대 웹 기반 애플리케이션을 구축하기 위한 사실상의 설계 원칙이었습니다.
이 사용방법(예제)에서는 웹 페이지 및 REST API와 함께 Jakarta EE MVC 2.0을 사용하여 웹 애플리케이션을 빌드하는 방법에 대해 알아봅니다.
2. JSR-371
Jakarta MVC 2.0(이전의 JSR 371 MVC 1.0)은 Jakarta RESTful 웹 서비스 또는 JAX-RS(이전의 RESTful 웹 서비스용 Java API ) 에 구축된 작업 기반 웹 프레임워크입니다 . JSR-371은 웹 애플리케이션 구축을 보다 편리하게 만드는 추가 어노테이션으로 JAX-RS를 보완합니다.
JSR 371 또는 Jakarta MVC는 Java로 웹 애플리케이션을 개발하는 방법을 표준화합니다. 또한 주요 목표는 기존 CDI(Contexts and Dependency Injections) 및 Bean Validation을 활용하고 JSP 및 Facelet을 뷰 기술로 지원하는 것 입니다.
현재 Jakarta MVC 2.1 사양 작업이 진행 중이며 아마도 Jakarta EE 10 릴리스와 함께 출시될 것입니다.
3. JSR-371 어노테이션
JSR-371은 JAX-RS 어노테이션 외에 몇 가지 추가 어노테이션을 정의합니다. 이러한 모든 어노테이션은 jakarta.mvc.* 패키지 의 일부입니다 .
3.1. 자카르타.mvc.컨트롤러
@Controller 어노테이션은 리소스 를 MVC 컨트롤러로 표시합니다. 클래스에 사용될 때 클래스의 모든 리소스 메서드는 컨트롤러가 됩니다. 마찬가지로 리소스 메서드에서 이 어노테이션을 사용하면 해당 메서드가 컨트롤러가 됩니다. 일반적으로 메서드에서 @Controller를 정의하는 것은 동일한 클래스에서 MVC 컨트롤러와 REST API를 정의하려는 경우에 유용합니다.
예를 들어 컨트롤러를 정의해 보겠습니다.
@Path("user")
public class UserController {
@GET
@Produces("text/html")
@Controller
public String showUserForm(){
return "user.jsp";
}
@GET
@Produces("application/json")
public String getUserDetails(){
return getUserDetails();
}
}
이 클래스에는 사용자 양식( showUserForm )을 렌더링하는 @Controller 와 사용자 세부 정보 JSON( getUserDetails ) 을 반환하는 REST API가 있습니다 .
3.2. jakarta.mvc.보기
@Controller 와 마찬가지로 리소스 클래스 또는 리소스 메서드를 @View 어노테이션으로 표시할 수 있습니다. 일반적으로 void를 반환하는 리소스 메서드에는 @View가 있어야 합니다 . @View 가 있는 클래스는 void 유형 이 있는 클래스의 컨트롤러에 대한 기본 보기를 나타냅니다 .
예를 들어 @View를 사용하여 컨트롤러를 정의해 보겠습니다 .
@Controller
@Path("user")
@View("defaultModal.jsp")
public class UserController {
@GET
@Path("void")
@View("userForm.jsp")
@Produces("text/html")
public void showForm() {
getInitFormData();
}
@GET
@Path("string")
@Produces("text/html")
public void showModal() {
getModalData();
}
}
여기서 리소스 클래스와 리소스 메서드에는 모두 @View 어노테이션이 있습니다. 컨트롤러 showForm은 보기 userForm.jsp를 렌더링합니다. 마찬가지로 showModal 컨트롤러는 리소스 클래스에 정의된 defaultModal.jsp를 렌더링합니다 .
3.3. 자카르타.mvc.binding.MvcBinding
Jakarta RESTful Webservices는 바인딩 및 유효성 검사 오류가 있는 요청을 거부합니다. 유사한 설정은 웹 페이지와 상호 작용하는 사용자에게 적합하지 않을 수 있습니다. 다행스럽게도 Jakarta MVC는 바인딩 및 유효성 검사 오류가 발생하더라도 컨트롤러를 호출합니다. 일반적으로 사용자는 데이터 바인딩 오류에 대해 잘 알고 있어야 합니다.
컨트롤러는 사람이 읽을 수 있는 유효성 검사 및 바인딩 오류 메시지를 사용자에게 표시하기 위해 BindingResult를 주입합니다. 예를 들어 @MvcBinding을 사용하여 컨트롤러를 정의해 보겠습니다 .
@Controller
@Path("user")
public class UserController {
@MvcBinding
@FormParam("age")
@Min(18)
private int age;
@Inject
private BindingResult bindingResult;
@Inject
private Models models;
@POST
public String processForm() {
if (bindingResult.isFailed()) {
models.put("errors", bindingResult.getAllMessages());
return "user.jsp";
}
}
}
여기에서 사용자가 18세 미만의 연령을 입력하면 사용자는 바인딩 오류가 있는 동일한 페이지로 다시 보내집니다. EL(Expression Language)을 사용하는 user.jsp 페이지 는 요청 속성 오류를 검색하여 페이지에 표시할 수 있습니다.
3.4. jakarta.mvc.RedirectScoped
사용자가 데이터를 채우고 제출하는 양식(HTTP POST)을 고려하십시오. 서버는 데이터를 처리하고 사용자를 성공 페이지(HTTP GET)로 리디렉션합니다. 이 패턴은 PRG(Post-Redirect-Get) 패턴 으로 널리 알려져 있습니다 . POST와 GET 사이에 데이터를 보유하고 싶은 몇 가지 시나리오가 있습니다. 이러한 시나리오에서 모델/beans의 범위는 단일 요청을 넘어섭니다.
빈이 @RedirectScoped 로 어노테이션되면 빈의 상태는 단일 요청을 넘어선다. 그럼에도 불구하고 POST, 리디렉션 및 Get이 완료되면 상태가 소멸됩니다. @RedirectScoped 로 구분된 빈은 POST, Redirect 및 GET이 완료된 후 소멸됩니다.
예를 들어 빈 사용자에 @RedirectScoped 어노테이션이 있다고 가정합니다 .
@RedirectScoped
public class User
{
private String id;
private String name;
// getters and setters
}
다음으로 이 bean을 컨트롤러에 삽입합니다.
@Controller
@Path("user")
public class UserController {
@Inject
private User user;
@POST
public String post() {
user.setName("John Doe");
return "redirect:/submit";
}
@GET
public String get() {
return "success.jsp";
}
}
여기에서 bean User는 POST 및 후속 리디렉션 및 GET에 사용할 수 있습니다. 따라서 success.jsp는 EL을 사용하여 bean의 이름 속성에 액세스할 수 있습니다.
3.5. 자카르타.mvc.UriRef
리소스 메서드에만 @UriRef 어노테이션을 사용할 수 있습니다 . @UriRef를 사용하면 리소스 메서드에 이름을 제공할 수 있습니다. 컨트롤러 경로 URI 대신 이러한 이름을 사용하여 뷰에서 컨트롤러를 호출할 수 있습니다.
href 가 있는 사용자 양식이 있다고 가정합니다 .
<a href="/app/user">Clich Here</a>
Click Here를 클릭하면 GET /app/user 에 매핑된 컨트롤러가 호출됩니다 .
@GET
@UriRef("user-details")
public String getUserDetails(String userId) {
userService.getUserDetails(userId);
}
여기에서 컨트롤러 이름을 user-details 로 지정했습니다 . 이제 뷰에서 URI 대신 이 이름을 참조할 수 있습니다.
<a href="${mvc.uri('user-details')}">Click Here</a>
3.6. 자카르타.mvc.security.CsrfProtected
이 어노테이션은 자원 메소드를 호출하기 위해 CSRF 유효성 검증이 필요함을 지시합니다. CSRF 토큰이 잘못된 경우 클라이언트는 ForbiddenException (HTTP 403) 예외를 수신합니다. 리소스 메서드만 이 어노테이션을 가질 수 있습니다.
컨트롤러를 고려하십시오.
@POST
@Path("user")
@CsrfProtected
public String saveUser(User user) {
service.saveUser(user);
}
컨트롤러에 @CsrfProtected 어노테이션이 있는 경우 유효한 CSRF 토큰이 포함된 경우에만 요청이 컨트롤러에 도달합니다.
4. MVC 애플리케이션 구축
다음으로 REST API와 컨트롤러를 사용하여 웹 애플리케이션을 빌드해 보겠습니다. 마지막으로 최신 버전의 Eclipse Glassfish 에 웹 애플리케이션을 배포해 보겠습니다 .
4.1. 프로젝트 생성
먼저 Maven archetype:generate를 사용하여 Jakarta MVC 2.0 프로젝트를 생성해 보겠습니다.
mvn archetype:generate
-DarchetypeGroupId=org.eclipse.krazo
-DarchetypeArtifactId=krazo-jakartaee9-archetype
-DarchetypeVersion=2.0.0 -DgroupId=com.baeldung
-DartifactId=krazo -DkrazoImpl=jersey
위의 원형은 다음과 같이 필요한 아티팩트가 있는 maven 프로젝트를 생성합니다.
또한 생성된 pom.xml 에는 jakarta.platform , jakarta.mvc 및 org.eclipse.krazo 의존성이 포함되어 있습니다 .
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>9.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.mvc</groupId>
<artifactId>jakarta.mvc-api</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.krazo</groupId>
<artifactId>krazo-jersey</artifactId>
<version>2.0.0</version>
</dependency>
4.2. 컨트롤러
다음으로 양식을 표시하고 사용자 세부 정보를 저장하기 위한 컨트롤러와 사용자 세부 정보를 가져오기 위한 API를 정의해 보겠습니다. 그러나 먼저 애플리케이션 경로를 정의해 보겠습니다.
@ApplicationPath("/app")
public class UserApplication extends Application {
}
애플리케이션 경로는 /app 로 정의됩니다 . 다음으로 사용자를 사용자 세부 정보 양식으로 전달하는 컨트롤러를 정의해 보겠습니다.
@Path("users")
public class UserController {
@GET
@Controller
public String showForm() {
return "user.jsp";
}
}
다음으로 WEB-INF/views 에서 user.jsp 보기를 만들고 애플리케이션을 빌드 및 배포할 수 있습니다.
mvn clean install glassfish:deploy
이 Glassfish Maven 플러그인은 포트 8080에서 빌드, 배포 및 실행됩니다. 성공적으로 배포한 후 브라우저를 열고 URL을 누를 수 있습니다.
http://localhost:8080/mvc-2.0/app/users :
다음으로 양식 제출 작업을 처리하는 HTTP POST를 정의해 보겠습니다.
@POST
@Controller
public String saveUser(@Valid @BeanParam User user) {
return "redirect:users/success";
}
이제 사용자가 만들기 버튼을 클릭하면 컨트롤러가 POST 요청을 처리하고 사용자를 성공 페이지로 리디렉션합니다.
Jakarta Validations, CDI 및 @MvcBinding을 사용하여 양식 유효성 검사를 제공합니다.
@Named("user")
public class User implements Serializable {
@MvcBinding
@Null
private String id;
@MvcBinding
@NotNull
@Size(min = 1, message = "Name cannot be blank")
@FormParam("name")
private String name;
// other validations with getters and setters
}
양식 유효성 검사가 완료되면 바인딩 오류를 확인하겠습니다. 바인딩 오류가 있는 경우 유효성 검사 메시지를 사용자에게 표시해야 합니다. 이를 위해 BindingResult를 주입하여 잘못된 양식 매개변수를 처리해 보겠습니다. saveUser 메서드를 업데이트해 보겠습니다 .
@Inject
private BindingResult bindingResult;
public String saveUser(@Valid @BeanParam User user) {
if (bindingResult.isFailed()) {
models.put("errors", bindingResult.getAllErrors());
return "user.jsp";
}
return "redirect:users/success";
}
검증이 완료된 상태에서 사용자가 필수 매개변수 없이 양식을 제출하면 검증 오류가 표시됩니다.
다음으로 @CsrfProtected 를 사용하여 CSRF 공격으로부터 POST 메서드를 보호해 보겠습니다 . saveUser 메서드에 @CsrfProtected를 추가합니다 .
@POST
@Controller
@CsrfProtected
public String saveUser(@Valid @BeanParam User user) {
}
다음으로 만들기 버튼을 클릭해 보겠습니다 .
컨트롤러가 CSRF 공격으로부터 보호되면 클라이언트는 항상 CSRF 토큰을 전달해야 합니다. 따라서 모든 요청에 CSRF 토큰을 추가하는 숨겨진 필드를 user.jsp 에 추가해 보겠습니다 .
<input type="hidden" name="${mvc.csrf.name}" value="${mvc.csrf.token}"/>
마찬가지로 지금 REST API를 개발해 보겠습니다 .
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<User> getUsers() {
return users;
}
이 HTTP GET API는 사용자 List을 반환합니다.
5. 결론
이 기사에서는 Jakarta MVC 2.0에 대해 알아보고 Eclipse Krazo를 사용하여 웹 애플리케이션 및 REST API를 개발하는 방법에 대해 알아보았습니다. 우리는 MVC 2.0이 Java에서 MVC 기반 웹 애플리케이션을 구축하는 방식을 어떻게 표준화하는지 살펴보았습니다.
항상 그렇듯이 전체 소스 코드는 GitHub에서 사용할 수 있습니다 .