[스프링 MVC - 1편] 스프링 MVC - 웹 페이지 만들기

지현·2021년 12월 17일
0

스프링

목록 보기
21/32

롬복의 @Data는 모든 메서드를 다 추가해주기 때문에 사용 권장하지 않음(사용할때 주의 해야함)
보통 @Getter, @Setter정도만 추가해서 사용

타임리프

  • 타임리프는 html모양을 그대로 살리면서 뷰 템플릿으로 렌더링 될 때만 조금씩 치환 > 화면을 크게 깨뜨리지 않음
  • 타임리프는 순수 HTML을 파일을 웹 브라우저에서 열어도 내용을 확인할 수 있고, 서버를 통해 뷰 템플릿을 거치면 동적으로 변경된 결과를 확인할 수 있음
  • 순수 HTML을 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을 네츄럴 템플릿(natural templates)이라 함

타임리프 사용 선언

<html xmlns:th="http://www.thymeleaf.org">

속성 변경 - th:href

  • 타임리프 뷰 템플릿을 거치게 되면 원래 값을 th:xxx 값으로 변경하고 만약 값이 없다면 새로 생성
  • HTML을 그대로 볼 때는 href 속성이 사용되고, 뷰 템플릿을 거치면 th:href 의 값이 href로 대체되면서 동적으로 변경
  • 대부분의 HTML 속성을 th:xxx로 변경할 수 있음

타임리프 핵심

  • th:xxx가 붙은 부분은 서버사이드에서 렌더링 되고, 기존 것을 치환
  • th:xxx 이 없으면 기존 html의 xxx 속성이 그대로 사용
  • HTML을 파일로 직접 열었을 때, th:xxx 가 있어도 웹 브라우저는 th: 속성을 알지 못하므로 무시 > 그냥 파일로 열어도되고 서버 사이드에서 렌더링된 채로 열어도 됨
  • HTML을 파일 보기를 유지하면서 템플릿 기능도 사용할 수 있음

리터럴 대체 - |...|

  • 타임리프에서 문자와 표현식 등은 분리되어 있기 때문에 더해서 사용해야 함
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
  • 리터럴 대체 문법을 사용하면, 더하기 없이 편리하게 사용 가능
<span th:text="|Welcome to our application, ${user.name}!|">

반복 출력 - th:each

<tr th:each="item : ${items}">
  • 반복은 th:each 를 사용
  • 모델에 포함된 items 컬렉션 데이터가 item 변수에 하나씩 포함되고, 반복문 안에서 item 변수를 사용 가능
  • 컬렉션의 수 만큼 <tr>..</tr> 이 하위 태그를 포함해서 생성

변수 표현식 - ${...}

<td th:text="${item.price}">10000</td>
  • 모델에 포함된 값이나, 타임리프 변수로 선언한 값을 조회
  • 프로퍼티 접근법을 사용 (item.getPrice())

내용 변경 - th:text

<td th:text="${item.price}">10000</td>
  • 내용의 값을 th:text 의 값으로 변경
  • 여기서는 10000을 ${item.price} 의 값으로 변경

URL 링크 표현식 - @{...},

th:href="@{/basic/items/{itemId}(itemId=${item.id})}"
  • 타임리프는 URL 링크를 사용하는 경우 @{...} 를 사용
  • URL 링크 표현식을 사용하면 서블릿 컨텍스트를 자동으로 포함 > 요즘에는 거의 사용 X
  • @{} 템플릿을 해놓고 () 변수를 선언하면 변수가 @{}에 있는 {}안으로 치환이됨 > 경로변수
  • 경로 변수({itemId}) 뿐만 아니라 쿼리 파라미터도 지원
    • 예) th:href="@{/basic/items/{itemId}(itemId=${item.id}, query='test')}"
    • 생성 링크 : http://localhost:8080/basic/items/1?query=test
  • URL 링크가 간단할때는 리터럴 대체 문법 사용 가능
    • th:href="@{|/basic/items/${item.id}|}"

속성 변경 - th:action

  • HTML form에서 action 에 값이 없으면 현재 URL에 데이터를 전송
  • th:action을 그냥 비워두면 같은 URL로 POST 방식으로 보내짐
    예) 상품 등록 폼의 URL과 실제 상품 등록을 처리하는 URL을 똑같이 맞추고 HTTP 메서드로 두 기능을 구분
    • 상품 등록 폼: GET /basic/items/add
    • 상품 등록 처리: POST /basic/items/add
    • GetMapping("/add")에서 회원 추가 폼으로 이동 > 회원 추가 폼에서 같은 경로인 /add로 Post 방식으로 보내려고 함 @PostMapping("/add") > th:action="/basic/items/add" 또는 th:action로 적어주면 됨

상품 등록 처리 - @ModelAttribute

//    @PostMapping("/add")
    public String addItemV1(@RequestParam String itemName,
                       @RequestParam int price,
                       @RequestParam Integer quantity,
                       Model model
                       ){

        Item item=new Item();
        item.setItemName((itemName));
        item.setPrice(price);
        item.setQuantity(quantity);

        itemRepository.save(item);

        model.addAttribute("item",item);

        return "basic/item";
    }
  • @RequestParam으로 파라미터 데이터를 변수에 받고싶을 때, 넘어오는 파라미터의 이름은 html폼에 name의 이름에서 확인
  • @RequestParam String itemName에서 itemName은 html 폼에서 데이터의 name
    @PostMapping("/add")
    public String addItemV2(@ModelAttribute("item") Item item){
        itemRepository.save(item);

        //ModelAttribute("네임속성")을 사용해서 자동으로 모델에 넣어주는 기능이 있음
        //네임속성에 지정해둔 이름을 가지고 넣어줌
        //model.addAttribute("item",item); //자동추가, 생략 가능

        return "basic/item";
    }
  • @ModelAttribute - 요청 파라미터 처리
    • @ModelAttribute 는 Item 객체를 생성하고, 요청 파라미터의 값을 프로퍼티 접근법(setXxx)으로 입력
    • 아래 코드를 대신 해줌
          Item item=new Item();
          item.setItemName((itemName));
          item.setPrice(price);
          item.setQuantity(quantity);
  • @ModelAttribute - Model 추가
    • @ModelAttribute는 모델에 @ModelAttribute로 지정한 객체를 자동으로 넣어주는 기능도 있음 > 네임속성을 사용
    • @ModelAttribute에 name(value) 속성을 사용하여 모델에 담을 이름을 지정
      • @ModelAttribute("hello") Item item > 이름을 hello 로 지정
      • model.addAttribute("hello", item); > 모델에 hello 이름으로 저장
    @PostMapping("/add")
    public String addItemV3(@ModelAttribute Item item){
        //ModelAttribute의 네임속성을 지우면 클래스명(Item)을 첫글자만 소문자로 바꿔서(item) Model에 담기게 됨

        itemRepository.save(item);
        return "basic/item";
    }
  • @ModelAttribute의 이름은 생략 가능
  • 이름을 생략하면 모델에 저장될 때 클래스 명의 첫글자만 소문자로 변경해서 등록
  • 모델에 자동 추가되는 이름
    • Item이면 > item
    • HelloWorld이면 > helloWorld
@PostMapping("/add")
    public String addItemV4(Item item){
        itemRepository.save(item);
        return "basic/item";
    }
  • ModelAttribute 자체도 생략가능하고 대상 객체는 모델에 자동 등록됨
  • 나머지 사항은 기존과 동일
  • 단순 타입이면 @RequestParam이 자동 적용되고 그 밖의 경우(임의의 객체)에는@ModelAttribute가 자동 적용됨

상품 수정

리다이렉트

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

    @PostMapping("/{itemId}/edit")
    public String edit(@PathVariable Long itemId, @ModelAttribute Item item){
        itemRepository.update(itemId, item);
        return "redirect:/basic/items/{itemId}"; //{itemId} > PathVariable에 있는 것 사용
    }
  • redirect:/... 으로 리다이렉트 사용 가능 redirect:/basic/items/{itemId}
  • 컨트롤러에 매핑된 @PathVariable 의 값은 redirect 에도 사용 가능

참고
HTML Form 전송은 PUT, PATCH를 지원하지 않고 GET, POST만 사용할 수 있음
PUT, PATCH는 HTTP API 전송시에 사용


PRG Post/Redirect/Get

POST 등록 후 새로 고침

  • 웹 브라우저의 새로고침은 마지막에 한 것을 다시 요청, 마지막에 서버에 전송한 데이터를 다시 전송
  1. 상품 등록 폼에서 데이터를 입력하고 저장을 선택하면 POST /add + 상품 데이터를 서버로 전송
  2. 이 상태에서 새로 고침을 또 선택하면 마지막에 전송한 POST /add + 상품 데이터를 서버로 다시 전송
  3. 내용은 같고, ID만 다른 상품 데이터가 계속 쌓임

POST, Redirect GET

  • 브라우저의 새로 고침은 마지막에 서버에 전송한 데이터를 다시 전송
  • 새로 고침 문제를 해결하려면 상품 저장 후에 뷰 템플릿으로 이동하는 것이 아니라, 상품 상세 화면으로 리다이렉트를 호출해주면 됨
  1. Redirect 응답을 주면 웹브라우저가 상품 저장 후에 URL이 바뀌면서 상품상세로 다시 요청 GET /items/{id}
  2. 이상태에서 새로고침을 하면 GET으로 상품상세를 다시 요청하게 되므로 새로고침으로 인한 데이터 중복 저장 문제를 해결
    @PostMapping("/add")
    public String addItemV5(Item item){
        itemRepository.save(item);
        return "redirect:/basic/items/"+item.getId();
    }

RedirectAttributes

    @PostMapping("/add")
    public String addItemV6(Item item, RedirectAttributes redirectAttributes){
        Item savedItem = itemRepository.save(item);
        //redirect 관련된 속성들을 넣음
        redirectAttributes.addAttribute("itemId",savedItem.getId());
        redirectAttributes.addAttribute("status",true);

        return "redirect:/basic/items/{itemId}";
        //redirectAttributes에 넣은 itemId 값이 치환이 됨
        //나머지는 쿼리 파라미터 형식으로 들어감 (?status=true)
    }
  • 치환, URL 인코딩 자동으로 해줌
  • 치환이 된 것을 제외한 나머지는 쿼리 파라미터로 들어감 ?status=true
  • 위에서 한 것처럼 item.getId()로 바로 넣는것보다는 RedirectAttributes를 사용하여 치환하는것이 더 좋음

item.html

<!-- 추가 -->
 <h2 th:if="${param.status}" th:text="'저장 완료!'"></h2>
  • th:if : 해당 조건이 참이면 실행
  • ${param.status} : 타임리프에서 쿼리 파라미터를 편리하게 조회하는 기능
    원래는 컨트롤러에서 모델에 직접 담고 값을 꺼내야 하지만 쿼리 파라미터는 자주 사용해서 타임리프에서 기능을 제공


출처
[인프런] 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

0개의 댓글