How to receive data from a post request in reactjs

2024, Dec 30    

Problem

  • 전달 받을 파라미터가 많다.
  • 파라미터 값에는 한글이 포함된 데이터도 있다. (get일 경우 인코딩 및 암호화 적용이 필수)
  • post로 호출가능한 URL제공을 요청받았다..
  • reactjs로는 post요청을 받을 수 없다.
  • 프로젝트는 reactjs로 frontend를 담당하며 spring boot를 통해 서비스하고 있다.

Try 1. jar → jsp & war

방법

  • post요청을 받기 위한 path mapping 선언
  • post요청을 받은 후 인코딩 및 암호화 등 필요한 전처리 작업 진행한다.
  • request 내용 그대로 jsp로 redirect한다.
  • jsp에서 브라우저 session storage를 통해 파라미터를 저장하고 동일 path에 대해 get으로 호출한다.
  • jar로 배포하던 것을 war로 변경한다.

작업내용

  • pom.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://maven.apache.org/POM/4.0.0"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <packaging>war</packaging>
        
          <dependencies>
             .....skip.....
              <dependency>
                  <groupId>org.apache.tomcat.embed</groupId>
                  <artifactId>tomcat-embed-jasper</artifactId>
              </dependency>
              <dependency>
                  <groupId>javax.servlet</groupId>
                  <artifactId>jstl</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-devtools</artifactId>
              </dependency>
          </dependencies>
        
          <build>
              <finalName>${project.artifactId}-boot-${project.version}</finalName>
              <plugins>
                  <plugin>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-maven-plugin</artifactId>
                       <configuration>
                          <mainClass>com.test.Application</mainClass>
                          <layout>WAR</layout>
                      </configuration>
                  </plugin>
              </plugins>
          </build>
      </project>
        
    
  • Controller.java

      @PostMapping("/**/{path:[^\\.]*}")
      public void postForward(
            @PathVariable String path,
            HttpServletRequest request,
            HttpServletResponse response,
            HttpEntity<String> httpEntity
      ) {
        String forwardUrl = UriMaker.makeUriWithQuery(path, request, httpEntity);
        response.setHeader("Location", forwardUrl);
        response.setStatus(302);
      }
    
  • WEB-INF/post.jsp

      <%@ page import="java.util.Enumeration" %>
      <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
        
      <%
          request.setCharacterEncoding("UTF-8");
        
          Enumeration<String> params = request.getParameterNames();
          StringBuilder paramString = new StringBuilder();
        
          while (params.hasMoreElements()) {
              String name = params.nextElement();
              paramString.append(name);
              paramString.append(request.getParameter(name));
        
              if (params.hasMoreElements()) {
                  paramString.append("_");
              }
          }
      %>
      <!DOCTYPE html>
      <html lang="ko">
      <body>
      <script type="text/javascript">
          const setStorage = (key, value) => {
              if (typeof value === 'object') {
                  sessionStorage.setItem(key, JSON.stringify(value));
              } else {
                  sessionStorage.setItem(key, value);
              }
          };
      </script>
      <script type="text/javascript">
          const CONTEXT_PATH = location.pathname;
          const params = '<%= paramString %>'
          const jsonParams = {};
        
          params.split(PARAM_SEPARATOR).forEach(param => {
              const [key, value] = param.split("_");
              jsonParams[key] = value;
          });
          setStorage("keys", jsonParams);
        
          const url = jsonParams.url ? jsonParams.url: location.origin + CONTEXT_PATH;
          location.href = url
      </script>
      </body>
      </html>
    

Try 2. Thymeleaf

방향

  • html페이지에서 필요한 처리를 진행가능
  • jar로 배포
  • post로 호출되는 것은 모두 post.html로 포워딩한다.

작업내용

  • pom.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://maven.apache.org/POM/4.0.0"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        
          <dependencies>
             .....skip.....
               
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-thymeleaf</artifactId>
              </dependency>
          </dependencies>
        
          <build>
              <finalName>${project.artifactId}-boot-${project.version}</finalName>
              <plugins>
                  <plugin>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-maven-plugin</artifactId>
                  </plugin>
              </plugins>
          </build>
      </project>
        
    
  • Controller.java

      @PostMapping("/**/{path:[^\\.]*}")
      public String handlePost(HttpServletRequest request, Model model) {
          model.addAttribute("paramString", UriMaker.makeUriWithQuery(request));
          return "post";
      }
    
  • resources/templates 폴더 생성
  • src/main/resources/templates/post.html
      <!DOCTYPE html>
      <html lang="ko" xmlns:th="http://www.thymeleaf.org">
      <head>
          <title>CS Talk - Guest</title>
          <script type="text/javascript">
              const setStorage = (key, value) => {
                  if (typeof value === 'object') {
                      sessionStorage.setItem(key, JSON.stringify(value));
                  } else {
                      sessionStorage.setItem(key, value);
                  }
              };
          </script>
      </head>
      <body>
      <script th:inline="javascript">
          /*<![CDATA[*/
          const CONTEXT_PATH = location.pathname;
          const params = /*[[${paramString}]]*/ '';
          const jsonParams = {};
        
          try {
              params.split(PARAM_SEPARATOR).forEach(param => {
                  const [key, value] = param.split("_");
                  jsonParams[key] = value;
              });
          } catch (e) {
              console.log(e);
          }
        
          setStorage("keys", jsonParams);
        
          const url = jsonParams.url ? jsonParams.url : location.origin + CONTEXT_PATH;
          window.location.href = url;
          /*]]>*/
      </script>
      </body>
      </html>
        
    
  • application.yml
    • 필수 아님
      spring.mvc.view.prefix=/WEB-INF/views/
      spring.mvc.view.suffix=.html
    
    • ThymeleafViewResolver는 뷰 이름을 접두사와 접미사로 둘러싸 리소스를 찾는다.
    • 접두어와 접미어의 기본값은 각각 ‘classpath:/templates/’와 ‘.html’ 이다. 변경이 필요한 경우 application.yml에 위의 설정을 추가
    • ThymeleafViewResolver동일한 이름의 bean을 제공하여 재정의할 수 있다.

결론

  • jsp&war를 적용했다가, 동료 리뷰를 듣고 다시 타임리프로 변경 및 적용했다.
  • 추가적으로 에러페이지 핸들링도 추가했다.
  • 이유는❓
    • war로 배포 argocd 및 개발자 로컬 환경 셋팅을 다시 해야하는 문제가 있었다. 😭
    • 유지보수 측면에서도 타임리프가 편할 것이라고 판단했다.(배경을 몰라도 프론트엔드 유지보수 작업에 영향이 적은 편이 중요도 측면에서 우선했다.)

Reference