녕의 학습 기록
[Spring MVC 1] 스프링 MVC 웹 페이지 만들기 본문
♪ 학습 내용
Spring MVC 기반 웹 페이지 만들기 (완)
HTML/CSS/JS 프레임워크인 부트스트랩 사용하기 위해 부트스트램 파일 설치.
spring 프로젝트 생성 시 뷰 템플릿인 타임리프 / 롬복 포함시키기.
* 전체적인 흐름
* 시작
상품 도메인 모델 Item ( ID / 이름 / 가격 / 수량)
@Repository 애노테이션 내부에는 @Component 가 포함되어 있어 컴포넌트 스캔 대상이 된다.
상품 저장소 로직 테스트 완료
롬복--> @RequiredArgsContructor 애노테이션 --> final 붙은 멤버변수를 사용해 생성자 생성
생성자 하나 있을 때 의존관계 생성자 주입은 @AutoWired 생략가능
따라서 컨트롤러는 ItemRepository를 생성자 주입 받고 있다. 싱글톤으로 관리.
<html xmlns:th="http://www.thymeleaf.org">
타임리프를 사용하기 위한 선언이다. th.
타임리프를 사용하면 순수 HTML 을 유지하면서 뷰 템플릿을 사용할 수 있다.
HTML 을 유지하기 때문에 그냥 HTML 파일 열어도 문제 없는 것을 확인가능!
전체적으로 HTML 요소를 타임리프 문법을 이용해서 동적으로 변경. 치환하는 느낌
th:가 붙으면 서버사이드에서 렌더링되며 기존 HTML 요소를 대체.
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css"
기존 href 속성 값을 th:href의 값으로 변경한다.
실제로 서버 띄우고 소스 확인하면 상대경로가 절대경로로 변경된 것을 확인 가능
<button class="btn btn-primary float-end"
onclick="location.href='addForm.html'"
th:onclick="|location.href='@{/basic/items/add}'|"
type="button">
기존 onclick 속성 값을 th:onclick 값이 대체한다. 이제 슬슬 익숙해지며 타임리프의 장점이 느껴지는 거 같다
@{/basic/items/add} - 타임리프 URL 링크 표현식
타임리프에서 URL 링크를 표현할 때는 @{ }로 감싸 표현한다.
리터럴 대체 | .... |
원래 타임리프에서는 문자와 표현식은 분리해야한다. 마치 자바처럼..!
하지만 리더털 대체 문법인 | |를 사용한다면( 둘러싼다면 ) 더하기 없이 사용 가능하다.
편리하므로 자주 사용!
<tr th:each="item : ${items}">
타임리프에서 반복 사용하려면 th:each 사용.
여기선 모델의 items 에서 값을 꺼내 item 변수에 담아. 루프.
<a href="item.html" th:href="@{/basic/items/{itemId}(itemId=${item.id})}" th:text="${item.id}">
기존 text 자리의 값이 th:text 의 값으로 치환된다.
스프링의 @PathVariable 처럼 url 링크 템플릿화한 것도 확인 가능
필요에 따라 쿼리파라미터도 생성 가능!
ex (itemId=${item.id}, query = 'test')
${...} - 타임리프 변수 표현식
모델의 값이나 타임리프 변수의 값 사용 가능
프로퍼티 접근법 사용! car.color
컨트롤러에서 모델에 특정 상품을 담았고 th:value를 통해 해당 특정 상품의 id를
기존 value값인 1 자리를 치환하게 했다.
간단히 생각해서 그냥 value 값을 넣어주었다.
상품 등록 폼과 상품 등록 처리는 URL을 같게하고 HTTP 메서드로 구분했다.
- 상품 등록 폼 : GET 방식의 /basic/items/add
- 상품 등록 처리 : POST 방식의 /basic/items/add
사실 더 이전 버전에서는 Model model을 파라미터로 받아서, model에 addAttribute를 해주었었다.
하지만 @ModelAttribute의 또 다른 기능을 학습하고 생략할 수 있었다.
앞에서 @ModelAttirubte는 HTTP 요청 쿼리파라미터 값을 이용해 객체를 생성해준다는 것을 학습.
추가로 Model에 지정한 객체를 자동으로 넣어주는 기능이 있다.
그렇기 때문에 model을 파라미터로 받을 필요도, addAttribute를 직접 해줄 필요도 x.
Model에 객체를 넣을 때는 지정한( "item") 이름으로 저장한다.
만약 이름 지정을 생략하면 객체의 클래스명( 맨 앞글자 소문자 )을 이름으로 사용하여 저장.
그리고 @ModelAttribute 애노테이션은 생략 가능하다고도 학습했기 때문에 생략했다.
<form action="item.html" th:action method="post">
위처럼 th:action 속성의 값을 따로 안주게 된다면 현재의 URL과 같다는 것이다.
상품 수정 폼이 렌더링 됐다는 것은 상품 수정 폼 컨트롤러의 매핑 URL인 /basic/items/{itemID}/edit 라는 것이고
th: 값을 안주었기 때문에 해당 form 을 전송하는 경로도 /basic/items/{itemID}/edit 라는 것이다.
한가지 다른 점이 있다면 HTTP 메서드가 POST 라는 점.
같은 경로에 POST 메서드이기 때문에 위의 상품 수정 컨트롤러에 매핑.
상품 수정 폼 컨트롤러와는 다른 것임을 짚고 가자.
상품 수정 폼 컨트롤러는 단지 뷰를 보여주기만 할뿐이고, 실제로 상품 수정 로직을 하는 것이
이 상품 수정 컨트롤러이다.
return "redirect:/basic/items/{itemId}";
Spring은 redirect: 를 통해 리다이렉트를 편리하게 지원!
리다이렉트를 하게 되면 상품 수정을 마치고 뷰 템플릿 호출하는 대신 상품 상세 화면으로 이동하게끔
상품 상세 컨트롤러로 리다이렉트.
리다이렉트이므로 URL 변경 된 것도 확인 가능.
아까 상품 등록에서도 Redirect를 적용했어야 했다.
왜냐하면 상품 등록을 마치면 뷰는 상품 상세 화면을 보이지만 URL은 여전히 POST의 /add 로 남아있기 때문이다.
따라서 새로고침을 하게 되면 마지막에 전송한 POST /add와 상품 데이터를 중복 등록하게 되는 현상이 발생한다.
이를 해결하기 위해 PRG( Post / Redirect / Get ) 방식 적용
prg를 적용하게 되면 상품 저장을 하고 나서 뷰 템플릿으로 이동하는 게 아닌,
상품 상세 화면으로 리다이렉트. 리다이렉트를 했기 때문에 상품 상세화면으로 GET !!
이렇게 하면 다시 새로고침을 해도 상품 상세 화면만 GET 해오기만 하고 중복 등록은 되지 않는다.
PRG를 적용하기 위해 URL에 item.getId() 변수를 더해주었지만,
이처럼 URL에 변수를 사용하는 것은 위험한 사항이라고 한다.(인코딩 문제)
RedirectAttributes를 이용하면 pathVariable 과 쿼리 파라미터 처리가 가능해진다.
결국 savedItem.getId() 의 값이 itemId로 치환되는 것.
그리고 URL에 ?status = true 쿼리 파라미터를 둠으로써 저장이 성공했는지 실패했는지를
제품 상세 폼에서 구분할 수 있게 끔 해주었다.
th:if
만약 해당 조건이 참이면 실행된다.
제품 등록을 성공하면 쿼리 파라미터로 ?status=true 가 들어오기 때문에 true --> 참.
제품 상세 페이지에 저장 완료라는 텍스트가 나타나게 된다.
물론 상품 저장 과정을 거치지 않고 바로 URL에 직접 status 쿼리 파라미터를 쳐서 해당 메시지를 확인할 수 도 있다.
하지만 딱히 보안상 문제가 되는 일도 아니기에 이렇게 처리.
스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의
웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -
www.inflearn.com
'Dev > Spring' 카테고리의 다른 글
.properties 파일 인코딩 문제 해결 (0) | 2023.07.28 |
---|---|
[Spring] Spring 프로젝트 생성 및 설정 (0) | 2023.03.05 |
[Spring MVC 1] 스프링 MVC-기능 (4) (0) | 2023.02.08 |
[Spring MVC 1] 스프링 MVC-기능 (3) (0) | 2023.02.07 |
[Spring MVC 1] 스프링 MVC-기능 (2) (0) | 2023.02.02 |