1. 개요

웹 애플리케이션은 종종 여러 사용 사례를 충족하기 위해 사용자 입력에 의존합니다. 결과적으로 양식 제출은 이러한 앱에 대한 데이터를 수집하고 처리하는 데 많이 사용되는 메커니즘입니다.

이 예제에서는 Spring의 플래시 속성 이 양식 제출 워크 플로우를 안전하고 안정적으로 어떻게 도울 수 있는지 알아볼 것 입니다.

2. 플래시 속성 기초

플래시 속성을 편안하게 사용하기 전에 양식 제출 워크 플로와 몇 가지 주요 관련 개념에 대한 적절한 수준의 이해를 구축해야합니다.

2.1. 게시 / 리디렉션 / 패턴 가져 오기

웹 양식을 설계하는 순진한 방법 은 제출을 처리하고 응답을 통해 확인을 제공 하는 단일 HTTP POST 요청 을 사용하는 것 입니다. 그러나 이러한 디자인은 사용자가 페이지를 새로 고치는 경우 POST 요청의 중복 처리 위험을 노출합니다.

중복 처리 문제를 완화하기 위해 특정 순서 (예 : POST, REDIRECT 및 GET)의 상호 연결된 요청 시퀀스로 워크 플로를 만들 수 있습니다 . 간단히 말해, 양식 제출을위한 PRG (Post / Redirect / Get) 패턴이라고합니다.

POST 요청을 수신하면 서버는이를 처리 한 다음 제어를 전송하여 GET 요청을 작성합니다. 그 후 GET 요청의 응답에 따라 확인 페이지가 표시됩니다. 이상적으로는 마지막 GET 요청이 두 번 이상 시도 되더라도 부작용이 없어야합니다.

2.2. 플래시 속성의 수명주기

PRG 패턴을 사용하여 양식 제출을 완료하려면 리디렉션 후 초기 POST 요청에서 최종 GET 요청으로 정보를 전송해야합니다.

불행히도 우리는 RequestAttributesSessionAttributes를 사용할 수 없습니다 . 전자는 다른 컨트롤러를 통한 리디렉션에서 살아남지 못하며 후자는 양식 제출이 끝난 후에도 전체 세션 동안 지속되기 때문입니다.

그러나 Spring의 웹 프레임 워크는이 정확한 문제를 해결할 수있는 플래시 속성을 제공하므로 걱정할 필요가 없습니다.

프로젝트에서 플래시 속성을 사용하는 데 도움이 되는 RedirectAttributes 인터페이스 의 메서드를 살펴 보겠습니다 .

RedirectAttributes addFlashAttribute(String attributeName, @Nullable Object attributeValue);

RedirectAttributes addFlashAttribute(Object attributeValue);

Map<String, ?> getFlashAttributes();

플래시 속성은 수명이 짧습니다 . 따라서 리디렉션 직전에 일부 기본 저장소에 임시로 저장됩니다. 리디렉션 후 후속 요청에 사용할 수있는 상태로 유지 된 다음 사라집니다.

2.3. FlashMap 데이터 구조

Spring은 플래시 속성을 키-값 쌍으로 저장하기 위해 FlashMap 이라는 추상 데이터 구조를 제공합니다 .

FlashMap 클래스 의 정의를 살펴 보겠습니다 .

public final class FlashMap extends HashMap<String, Object> implements Comparable<FlashMap> {

    @Nullable
    private String targetRequestPath;

    private final MultiValueMap<String, String> targetRequestParams 
      = new LinkedMultiValueMap<>(4);

    private long expirationTime = -1;
}

FlashMap 클래스가 HashMap 클래스 에서 동작을 상속 한다는 것을 알 수 있습니다 . 따라서 FlashMap 인스턴스는 속성의 키-값 매핑을 저장할 수 있습니다 . 또한 특정 리디렉션 URL에서만 사용 하도록 FlashMap 인스턴스를 연결할 수 있습니다.

또한 모든 요청에는 PRG 패턴에서 중요한 역할을하는 두 개의 FlashMap 인스턴스, 즉 Input FlashMap 및 Output FlashMap 이 있습니다.

  • 출력 FlashMap 은 POST 요청에서 사용되어 플래시 속성을 일시적으로 저장하고 리디렉션 후 다음 GET 요청으로 보냅니다.
  • 입력 FlashMap 은 리디렉션 전에 이전 POST 요청에서 전송 된 읽기 전용 플래시 속성에 액세스하기 위해 최종 GET 요청에서 사용됩니다.

2.4. FlashMapManagerRequestContextUtils

이름에서 알 수 있듯이 FlashMapManager 를 사용하여 FlashMap 인스턴스 를 관리 할 수 있습니다 .

먼저이 전략 인터페이스의 정의를 살펴 보겠습니다.

public interface FlashMapManager {

    @Nullable
    FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);

    void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}

간단히 말해, FlashMapManager를 사용하면 일부 기본 저장소에서 FlashMap 인스턴스 를 읽고, 업데이트하고, 저장할 수 있습니다.

다음으로 RequestContextUtils 추상 유틸리티 클래스 에서 사용할 수 있는 몇 가지 정적 메서드에 익숙해 지겠습니다 .

이 예제의 범위 내에서 초점을 유지하기 위해 플래시 속성과 관련된 메서드로 범위를 제한합니다.

public static Map<String, ?> getInputFlashMap(HttpServletRequest request);

public static FlashMap getOutputFlashMap(HttpServletRequest request);

public static FlashMapManager getFlashMapManager(HttpServletRequest request);

public static void saveOutputFlashMap(String location, 
  HttpServletRequest request, HttpServletResponse response);

이러한 메소드를 사용하여 입력 / 출력 FlashMap 인스턴스 를 검색하고 , 요청에 대한 FlashMapManager가져 오고 , FlashMap 인스턴스를 저장할 수 있습니다.

3. 양식 제출 사용 사례

지금까지 우리는 플래시 속성과 관련된 다양한 개념에 대한 기본적인 이해를 구축했습니다. 자, 더 나아가시 대회 웹 애플리케이션에서 사용합시다.

우리의시 콘테스트 앱에는 양식 제출을 통해 다른 시인의시 항목을 수락하는 간단한 사용 사례가 있습니다. 또한 콘테스트 응모작에는 제목, 본문, 저자 이름 등시와 관련된 필수 정보가 포함됩니다.

3.1. Thymeleaf 구성

우리가 사용하게 될 Thymeleaf 동적 웹 페이지 작성을위한 자바 템플릿 엔진입니다, 간단한 HTML 템플릿을 통해입니다.

먼저 프로젝트의 pom.xml에 spring-boot-starter-thymeleaf 의존성을 추가해야합니다 .

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>

다음으로, 우리는 고 우리의의의 Thymeleaf 고유의 속성 중 일부 정의 할 수 있습니다 pplication.properties 에 위치 정보 파일 SRC / 메인 / 자원  디렉토리를 :

spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true 
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

이러한 속성을 정의 했으므로 이제 / src / main / resources / templates 디렉터리 아래에 모든보기를 만들 수 있습니다 . 차례로 Spring은 컨트롤러 내부의 모든 뷰에 .html 접미사를 추가합니다 .

3.2. 도메인 모델

다음 으로 Poem 클래스 에서 도메인 모델정의하겠습니다 .

public class Poem {
    private String title;
    private String author;
    private String body;
}

또한 Poem 클래스 isValidPoem () 정적 메서드를 추가 하여 필드가 빈 문자열을 허용하지 않는지 확인할 수 있습니다.

public static boolean isValidPoem(Poem poem) {
    return poem != null && Strings.isNotBlank(poem.getAuthor()) 
      && Strings.isNotBlank(poem.getBody())
      && Strings.isNotBlank(poem.getTitle());
}

3.3. 양식 작성

이제 제출 양식을 만들 준비가되었습니다. 이를 위해 사용자에게 양식표시하기 위해 GET 요청을 제공 할 엔드 포인트 / poem / submit필요합니다 .

@GetMapping("/poem/submit")
public String submitGet(Model model) {
    model.addAttribute("poem", new Poem());
    return "submit";
}

여기에서는 모델을 컨테이너로 사용하여 사용자가 제공 한 시별 데이터를 보관했습니다. 또한 submitGet  메서드는 제출 뷰에서 제공하는 뷰를 반환합니다 .

또한 POST 양식을 모델 속성 poem 으로 바인딩하려고합니다 .

<form action="#" method="post" th:action="@{/poem/submit}" th:object="${poem}">
    <!-- form fields for poem title, body, and author -->
</form>

3.4. 게시 / 리디렉션 / 제출 흐름 가져 오기

이제 양식에 대한 POST 작업을 활성화하겠습니다. 이를 위해 PoemSubmission 컨트롤러 / poem / submit 엔드 포인트를 만들어 POST 요청을 처리합니다 .

@PostMapping("/poem/submit")
public RedirectView submitPost(
    HttpServletRequest request, 
    @ModelAttribute Poem poem, 
    RedirectAttributes redirectAttributes) {
    if (Poem.isValidPoem(poem)) {
        redirectAttributes.addFlashAttribute("poem", poem);
        return new RedirectView("/poem/success", true);
    } else {
        return new RedirectView("/poem/submit", true);
    }
}

제출이 성공하면 제어가 / poem / success 엔드 포인트 로 전송 된다는 것을 알 수 있습니다 . 또한 리디렉션을 시작하기 전에시 데이터를 플래시 속성으로 추가했습니다.

이제 사용자에게 확인 페이지를 표시해야하므로 GET 요청을 제공 할 / poem / success 엔드 포인트에 대한 기능을 구현해 보겠습니다 .

@GetMapping("/poem/success")
public String getSuccess(HttpServletRequest request) {
    Map<String, ?> inputFlashMap = RequestContextUtils.getInputFlashMap(request);
    if (inputFlashMap != null) {
        Poem poem = (Poem) inputFlashMap.get("poem");
        return "success";
    } else {
        return "redirect:/poem/submit";
    }
}

성공 페이지로 리디렉션하기 전에 FlashMap의 유효성을 검사해야 한다는 점에 유의 해야 합니다 .

마지막으로 성공 페이지에 있는 flash 속성 poem을 사용 하여 사용자가 제출 한시의 제목을 표시해  보겠습니다 .

<h1 th:if="${poem}">
    <p th:text="${'You have successfully submitted poem titled - '+ poem?.title}"/>
    Click <a th:href="@{/poem/submit}"> here</a> to submit more.
</h1>

4. 결론

이 사용방법(예제)에서는 Post / Redirect / Get 패턴 및 플래시 속성에 대한 몇 가지 개념을 배웠습니다. 또한 Spring Boot 웹 애플리케이션에서 간단한 양식 제출로 작동중인 플래시 속성도 보았습니다.

항상 그렇듯이 예제의 전체 소스 코드는 GitHub에서 사용할 수 있습니다 .