[Spring] 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 섹션7 스프링 MVC - 웹 페이지 만들기
◼️ 요구사항 분석
상품 도메인 모델 | 상품 관리 기능 |
상품 ID 상품명 가격 수량 |
상품 목록 상품 상세 상품 등록 |
🟢 부트스트랩
HTML 화면 개발은 부트스트랩을 사용했다.
부트스트랩을 다운로드 받고 압축을 풀자.
이동: https://getbootstrap.com/docs/5.0/getting-started/download/
Compiled CSS and JS 항목을 다운로드, 압축을 출고 bootstrap.min.css 를 복사해서 다음 폴더에 추가해준다.
resources/static/css/bootstrap.min.css
🪄참고
부트스트랩(Bootstrap)은 웹사이트를 쉽게 만들 수 있게 도와주는 HTML, CSS, JS 프레임워크이다.
하나의 CSS로 휴대폰, 태블릿, 데스크탑까지 다양한 기기에서 작동한다.
다양한 기능을 제공하여 사용자가 쉽게 웹사이트를 제작, 유지, 보수할 수 있도록 도와준다.
🪄참고
리소스가 공개되는 /resources/static 폴더에 HTML을 두면 실 서비스에서도 공개된다. 따라서 공개 필요가 없는 HTML을 두는것을 주의해야한다.
🟢 @RequiredArgsConstructor
final이 붙은 멤버변수만 사용해서 생성자를 자동으로 만들어준다.
◼️ 타임리프 문법 정리
🟢 타임리프
타임리프 사용 선언
<html xmlns:th="http://www.thymeleaf.org">
속성 변경 - th:href
- 원래 값을 th:xxx 값으로 변경한다. 만약 값이 없다면 새로 생성한다.
- 핵심은 th:xxx 가 붙은 부분은 서버사이드에서 렌더링 된다는 것이다. 따라서 HTML 파일 보기를 유지하면서 템플릿 기능도 할 수 있다.
URL 링크 표현식 - @{...}
URL 링크 표현식을 사용하면 서블릿 컨텍스트를 자동으로 포함한다.
th:href="@{/css/bootstrap.min.css}"
리터럴 대체 - |...|
<span th:text="|Welcome to our application, ${user.name}!|">
속성 변경 - th:onclick
onclick="location.href='addForm.html'"
th:onclick="|location.href='@{/basic/items/add}'|"
반복 출력 - th:each
<tr th:each="item : ${items}">
변수 표현식 - ${...} / 내용 변경 - th:text
<td th:text="${item.price}">10000</td>
URL 링크 표현식2 - @{...},
- 경로 변수 뿐만 아니라 쿼리 파라미터도 생성한다.
- 두번째 예시 ▷ 생성 링크: http://localhost:8080/basic/items/1?query=test
th:href="@{/basic/items/{itemId}(itemId=${item.id})}"
th:href="@{/basic/items/{itemId}(itemId=${item.id}, query='test')}"
속성 변경 - th:value
th:value="${item.id}"
속성 변경 - th:action
HTML form에서 action 에 값이 없으면 현재 URL에 데이터를 전송한다.
🪄참고 - 순수 HTML을 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을 네츄럴 템플릿(natural templates)이라 한다.
☀️ 주의! 스프링 부트 3.2 파라미터 이름 인식 문제 스프링 부트 3.2부터 자바 컴파일러에 -parameters 옵션을 넣어주어야 애노테이션의 이름을 생략할 수 있다.
해결방안1: 애노테이션에 이름을 생략하지않고 적어주기
@RequestParam("username") String username
@PathVariable("userId") String userId
@Qualifier("memberRepository") MemberRepository memberRepository
해결방안2: 컴파일 시점에 -parameters 옵션 적용
해결방안3: Gradle을 사용하여 빌드, 실행 (컴파일 시점에 -parameters 옵션 자동적용)
◼️ 상품 등록 처리 - @ModelAttribute, Redirect
🟢 @ModelAttribute
- 객체를 생성하고, 요청 파라미터의 값을 프로퍼티 접근법(setXxx)으로 입력해준다.
- 모델(Model)에 @ModelAttribute 지정 객체를 자동으로 넣어준다.
- @ModelAttribute(name) 에서 이름을 생략하면 모델에 저장될 때 클래스명을 사용한다. 이때 클래스의 첫글자만 소문자로 변경해서 등록한다.
/**
* @ModelAttribute("item") Item item
* model.addAttribute("item", item); 자동 추가
*/
@PostMapping("/add")
public String addItemV2(@ModelAttribute("item") Item item, Model model) {
itemRepository.save(item);
//model.addAttribute("item", item); //자동 추가, 생략 가능
return "basic/item";
}
/**
* @ModelAttribute name 생략 가능
* model.addAttribute(item); 자동 추가, 생략 가능
* 생략시 model에 저장되는 name은 클래스명 첫글자만 소문자로 등록 Item -> item
*/
@PostMapping("/add")
public String addItemV3(@ModelAttribute Item item) {
itemRepository.save(item);
return "basic/item";
}
/**
* @ModelAttribute 자체 생략 가능
* model.addAttribute(item) 자동 추가
*/
@PostMapping("/add")
public String addItemV4(Item item) {
itemRepository.save(item);
return "basic/item";
}
🟢 리다이렉트
컨트롤러에 매핑된 @PathVariable 값은 redirect에도 사용할 수 있다.
@PostMapping("/{itemId}/edit")
public String edit(@PathVariable Long itemId, @ModelAttribute Item item) {
itemRepository.update(itemId, item);
return "redirect:/basic/items/{itemId}";
}
◼️ PRG Post/Redirect/Get
웹 브라우저 새로고침은 마지막에 서버에 전송한 데이터를 다시 전송한다.
따라서 상품 저장 후 새로고침을 하면 POST /add가 계속 전송되어 상품이 중복 전송된다.
해결법은 상품 저장 후 뷰 템플릿으로 이동하지 않고, 상품 상세 화면으로 리다이렉트를 호출하면 된다. (웹 브라우저는 실제 상품 상세 화면으로 이동) 따라서 마지막에 호출한 내용이 상품 상세 화면인 GET /items/{id} 가 되는 것이다.
이후 새로고침을 해도 상품 상세 화면으로 이동하게 되므로 새로 고침 문제를 해결할 수 있다.
이런 문제 해결 방식을 PRG Post/Redirect/Get 라 한다.
/**
* PRG - Post/Redirect/Get
*/
@PostMapping("/add")
public String addItemV5(Item item) {
itemRepository.save(item);
return "redirect:/basic/items/" + item.getId();
}
◼️ RedirectAttributes
고객에게 잘 저장되었다는 메시지를 보내기 위해 status 를 추가한다.
/**
* RedirectAttributes
*/
@PostMapping("/add")
public String addItemV6(Item item, RedirectAttributes redirectAttributes) {
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/basic/items/{itemId}";
}
http://localhost:8080/basic/items/3?status=true // 리다이렉트 결과
🟢RedirectAttributes
- RedirectAttributes 를 사용하면 URL 인코딩도 해주고, pathVariable , 쿼리 파라미터까지 처리해준다.
- pathVariable 바인딩: {itemId}
- 나머지는 쿼리 파라미터로 처리: ?status=true
아래와 같이 메시지를 작성해주면, 저장 완료라는 메시지가 화면에 나온다.
<div class="container">
<div class="py-5 text-center">
<h2>상품 상세</h2>
</div>
<!-- 추가 -->
<h2 th:if="${param.status}" th:text="'저장 완료!'"></h2>
- th:if : 해당 조건이 참이면 실행
- ${param.status} : 타임리프에서 쿼리 파라미터를 편리하게 조회하는 기능.
- (원래는 컨트롤러에서 모델에 직접 담고 값을 꺼내야 한다. 그런데 쿼리 파라미터는 자주 사용해서 타임리프 에서 직접 지원한다.)
'Spring' 카테고리의 다른 글
[Spring] 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 섹션4 검증1 - Validation (1) | 2024.05.06 |
---|---|
[Spring] 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 섹션3 메시지, 국제화 (0) | 2024.05.06 |
[Spring] 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 섹션6 스프링 MVC - 기본 기능 (1) | 2024.04.21 |
[Spring] 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 섹션5 스프링 MVC - 구조 이해 (0) | 2024.04.17 |
[Spring] 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 섹션4 MVC 프레임워크 만들기 (0) | 2024.04.14 |