본문 바로가기

Advance I/Spring MVC

22.08.16

상품 등록 폼

속성 변경 - th:action

th:action HTML form에서 action 에 값이 없으면 현재 URL에 데이터를 전송한다.

상품 등록 폼을 보여주는 URL과 실제로 상품 등록을 처리하는 URL은 같음. HTTP 메서드로 컨트롤러 구분

상품 등록 폼: GET /basic/items/add

상품 등록 처리: POST /basic/items/add

상품 등록 처리

상품 등록 폼은 POST-HTML Form 방식으로 서버에 데이터를 전달한다.

메세지 바디에 쿼리 파라미터 형식으로 전달

=> @RequestParam(primitive) or @ModelAttribute(object) 사용

@ModelAttribute

요청 파라미터 처리

@ModelAttribute 는 객체를 생성하고, 요청 파라미터의 값을 프로퍼티 접근법(setXxx)으로 입력해준다.

 

Model 추가

모델(Model)에 @ModelAttribute 로 지정한 객체를 자동으로 넣어준다.

모델에 데이터를 담을 때 이름(key)은 @ModelAttribute 에 지정한 name(value) 속성을 사용한다.

 

만약 다음과 같이 @ModelAttribute 의 이름을 다르게 지정하면 다른 이름으로 모델에 포함된다.

@ModelAttribute("hello") Item item -> model.addAttribute("hello", item)

 

@ModelAttribute 의 이름을 생략할 수 있다.

모델에 저장될 때는 클래스명(첫글자 소문자로 변환)을 사용한다.

예) @ModelAttribute HelloData data -> model.addAttribute("helloData", data)

 

물론 @ModelAttribute 자체도 생략 가능하고, 대상 객체는 모델에 자동 등록된다. 그러나 불명확!

상품 수정

취소 버튼 -> 상품 상세 화면

th:onclick="|location.href='@{/basic/items/{itemId}(itemId=${item.id})}'|"

=> ex. http://localhost:8080/basic/items/1

 

*템플릿 포맷팅은 {...}, 모델이나 변수 사용은 ${...}, String과 코드(변수) 함께 사용시 |...|

*타임리프에서 " "는 스페이스를 하나의 토큰으로 인식하지 않아 오류가 난다. 공백 주의

*타임리프에서 문자열은 항상 ' ' 을 사용해야 한다. 스페이스를 토큰으로 인식하지만 대신 문자열끼리 합칠 때는 합연산을 해야 한다. -> 리터럴 대체 문자열(|...|)

ex. "'hello ' + '${data}'" -> "|hello ${data}|"

 

상품 수정은 상품 등록과 전체 프로세스가 유사하다.

GET /items/{itemId}/edit : 상품 수정 폼

POST /items/{itemId}/edit : 상품 수정 처리

리다이렉트

상품 수정은 마지막에 뷰 템플릿을 호출하는 대신에 상품 상세 화면으로 이동하도록 리다이렉트를 호출한다.

스프링은 redirect:/... 으로 편리하게 리다이렉트를 지원한다.

컨트롤러에 매핑된 @PathVariable 의 값은 redirect 에도 사용 할 수 있다.

ex. @PathVariable Long itemId

-> redirect:/basic/items/{itemId}

 

참고: HTML Form 전송은 PUT, PATCH를 지원하지 않는다. GET, POST만 사용할 수 있다. PUT, PATCH는 HTTP API 전송시에 사용. 스프링에서 HTTP POST로 Form 요청할 때 히든 필드를 통해서 PUT, PATCH 매핑을 사용하는 방법이 있지만, HTTP 요청상 POST 요청이다.

PRG Post/Redirect/Get

사실 지금까지 진행한 상품 등록 처리 컨트롤러는 심각한 문제가 있다. (addItemV1 ~ addItemV4)

상품 등록을 완료하고 웹 브라우저에서 새로고침을 하면 상품이 계속해서 중복 등록되고 있다.

 

1. 웹 브라우저가 상품 등록 폼을 요청하고, 서버가 상품 등록 폼 뷰를 내려준다.

2. 웹 브라우저가 POST로 상품 저장을 요청하고, 서버가 요청 처리 후 상품의 상세 화면 만 내려준다. ( URL 변화X )

즉, 웹 브라우저 입장에서 마지막 요청은 'POST /add'

웹 브라우저의 새로고침? 마지막에 서버에 전송한 데이터를 다시 전송하는 행위

POST, Redirect GET

상품 저장 후에 뷰 템플릿으로 이동하는 것이 아니라, 상품 상세 화면으로 리다이렉트를 호출 ( URL 변화 )

웹 브라우저는 리다이렉트의 영향으로 상품 저장 후에 실제 상품 상세 화면으로 다시 이동한다.

따라서 마지막에 호출한 내용이 상품 상세 화면인 GET /items/{id} 가 되는 것이다. 이후 새로고침을 해도 상품 상세 화면으로 이동하게 되므로 새로 고침 문제를 해결할 수 있다.

이런 문제 해결 방식을 PRG (Post/Redirect/Get) 라 한다.

 

주의! "redirect:/basic/items/" + item.getId()

redirect에서 +item.getId() 처럼 URL에 변수를 더해서 사용하는 것은 URL 인코딩이 안되기 때문에 위험하다. 다음에 설명하는 RedirectAttributes 를 사용하자.

RedirectAttributes

요구사항: 저장이 잘 되었으면 상품 상세 화면에 저장 되었다는 메시지 띄우기

 

RedirectAttributes: URL 인코딩, pathVarible, 쿼리 파라미터 처리

redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);

redirect:/basic/items/{itemId}

-pathVariable 바인딩: {itemId}

-나머지는 쿼리 파라미터로 처리: ?status=true

 

뷰 템플릿 메시지 추가

<h2 th:if="${param.status}" th:text="'저장 완료!'">

 

th:if="조건": 조건이 참(true)이면 렌더링

${param.xxx} : 타임리프에서 쿼리 파라미터를 편리하게 조회하는 기능 원래는 컨트롤러에서 모델에 직접 담고 값을 꺼내야 한다. 그런데 쿼리 파라미터는 자주 사용해서 타임리프에서 직접 지원한다.

 

'Advance I > Spring MVC' 카테고리의 다른 글

22.08.19  (0) 2022.08.19
22.08.17 :: 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술  (0) 2022.08.17
22.08.15  (0) 2022.08.15
22.08.13  (0) 2022.08.13
22.08.11  (0) 2022.08.11