[Spring] 타임리프 - 스프링 통합과 폼

JJoSuk·2023년 6월 7일
0

본 프로젝트 자료는 김영한님의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술을 참고 제작됐음을 알립니다.

입력 폼 처리

타임리프가 제공하는 입력 폼 기능을 적용해서 기존 프로젝트의 폼 코드를 효율적으로 개선해보고자 한다.

1. 등록 폼

예제 코드

@GetMapping("/add")
    public String addForm(Model model) {
        model.addAttribute("item", new Item());
        return "form/addForm";
    }

HTML

  • th:object="${???}" : < form > 에서 ??? 사용할 객체를 지정한다.
  • th:field="*{???}"
    • $ -> * 로 변경
    • 기존 ${item.itemName} -> *{itemName} 변경
    • th:field 는 id , name , value 속성을 모두 자동으로 만들어준다.

수정 후 코드소스

참고

참고로 해당 예제에서 id 속성을 제거해도 th:field 가 자동으로 만들어준다.


수정 폼

예제 코드

@GetMapping("/{itemId}/edit")
    public String editForm(@PathVariable Long itemId, Model model) {
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item", item);
        return "form/editForm";
    }

HTML

수정 폼은 앞서 설명한 내용과 같다.

  • th:field 는 id , name , value 속성을 모두 자동으로 만들어준다.

수정 후 코드소스


요구사항(예시)

위에 배운 내용들로 체크박스, 라디오 버튼, 셀렉트 박스를 편리하게 사용하는 방법을 만들어 보고자 한다.

다음 요구사항

  • 판매 여부
    • 판매 오픈 여부
    • 체크 박스로 선택할 수 있다.
  • 등록 지역
    • 서울, 부산, 제주
    • 체크 박스로 다중 선택할 수 있다.
  • 상품 종류
    • 도서, 식품, 기타
    • 라디오 버튼으로 하나만 선택할 수 있다.
  • 배송 방식
    • 빠른 배송
    • 일반 배송
    • 느린 배송
    • 셀렉트 박스로 하나만 선택할 수 있다.

위 요구사항 관련 예시(사진)

예제 코드

public enum ItemType {

    BOOK("도서"), FOOD("음식"), ETC("기타");

    private final String description;

    ItemType(String description) {
        this.description = description;
    }
}

상품 종류는 ENUM 을 사용.

/**
 * FAST : 빠른 배송
 * NORMAL : 일반 배송
 * SLOW : 느린 배송
 */
@Data
@AllArgsConstructor
public class DeliveryCode {

    private String code;
    private String displayName;
}

배송 방식은 DeliveryCode 라는 클래스를 사용.
code 는 FAST 같은 시스템에서 전달하는 값이고, displayName 은 빠른 배송 같은 고객에게 보여주는 값이다.

@Data
public class Item {

    private Long id;
    private String itemName;
    private Integer price;
    private Integer quantity;

    private Boolean open; // 판매 여부
    private List<String> regions; // 등록 지역
    private ItemType itemType; // 상품 종류
    private String deliveryCode; // 배송 방식

    public Item() {
    }

    public Item(String itemName, Integer price, Integer quantity) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
    }
}

등록 준비


체크 박스 - 단일

판매 여부를 확인하기 위한 체크박스(단일) 를 만들고자 한다.

HTML

상품 등록이 제대로 넘어오는지 체크해보기 위해 로그를 사용해보자.

@Slf4j 애노테이션 추가 필수*

실행 결과

체크박스가 생긴 것을 확인 할 수 있고, 이후 체크하고 상품 등록을 누를 시

로그로 넘어왔다는 것을 확인 할 수 있다.

주의해야할 점

  • 타임리프를 사용하지 않고 HTML 체크박스를 등록할 경우

  • null 값을 받게 되어 정보가 넘어오지 않을 수 있다.
  • 만약 타임리프를 사용하지 않고 방법을 해결하고 싶으면 히든 필드를 사용하면 되는데

  • 체크 박스를 체크하지 않으면 스프링 MVC가 _open 만 있는 것을 확인하고, open 의 값이 체크되지 않았다고 인식한다.

  • 이 경우 서버에서 Boolean 타입을 찍어보면 결과가 null 이 아니라 false 인 것을 확인할 수 있다.

하지만 이대로 냅두면 상품 등록 후 나와도 박스를 체크 혹은 해제가 가능하다.
이 부분을 수정해야 한다.

우선 상품 상세 관련 클래스에 들어가 같은 코드를 넣어두자.

disabled 를 사용해서 상품 상세에서는 체크 박스가 선택되지 않도록 작업해주면

체크박스를 수정으로 들어가지 않는 이상 막을 수 있다.

  • 주의: 만약 만든 클래스에 th:object 를 사용하지 않았다면 th:field 부분에 *{open} 가 아닌 ${item.open} 로 등록해줘야 한다.

체크 박스 - 멀티

체크 박스를 멀티로 사용해서, 하나 이상을 체크할 수 있도록 해보자.

등록 지역

  • 서울, 부산, 제주
  • 체크 박스로 다중 선택할 수 있다.

예제 코드

@ModelAttribute("regions")
    public Map<String, String> regions() {
        Map<String, String> regions = new LinkedHashMap<>();
        regions.put("SEOUL", "서울");
        regions.put("BUSAN", "부산");
        regions.put("JEJU", "제주");
        return regions;
    }

이것을 넣어주면 regions 으로 지역들을 간편하게 사용할 수 있다.

@ModelAttribute의 특별한 사용법

등록, 수정, 상세화면 폼에 모두 체크 박스를 반복해서 보여줘야 하는데, @ModelAttribute 를 사용하면 regions로 모든 지역을 넣어줄 수 있다.

th:for="${#ids.prev('regions')}" 를 사용하는 이유

  • name="" 은 같은 명이 들어가도 상관지만, id="" 는 같은 명이 들어갈 경우 문제가 생긴다.
  • 이를 방지하기 위해 th:for="${#ids.prev('...')}" 를 사용하게 되면 each루프 안에서 반복해서 만들 때 임의로 1,2,3 숫자를 뒤에 붙여준다.

나머지 폼에도 체크 박스 - 단일 와 같이 등록하면 된다.


라디오 버튼

라디오 버튼은 여러 선택지 중에 하나를 선택할 때 사용할 수 있다.

예제 코드

@ModelAttribute("itemTypes")
    public ItemType[] itemTypeps() {
        return ItemType.values();
    }

위와 마찬가지로 타임리프에서 지원하는 편의기능 @ModelAttribute 를 사용

@ModelAttribute("itemTypes")
    public ItemType[] itemTypeps() {
        return ItemType.values();
    }
  • ItemType.values() 를 사용하면 해당 ENUM의 모든 정보를 배열로 반환한다.
    • 예) [BOOK, FOOD, ETC]

HTML

상품 등록 폼에 기능을 추가

특이사항

라디오 버튼은 이미 선택이 되어 있다면, 수정시에도 항상 하나를 선택하도록 되어 있으므로 체크 박스와 달리 별도의 히든 필드를 사용할 필요가 없다.

나머지 폼들도 위와 같이 수정해서 넣어주면 된다.

주의: 타임리프에서 ENUM 을 직접 사용할 수 있다.

@ModelAttribute("itemTypes")
    public ItemType[] itemTypeps() {
        return ItemType.values();
    }

위 코드 없이

${T(hello.itemservice.domain.item.ItemType).values()}" 스프링EL 문법으로 ENUM을 직접 사용할 수 있지만, 나중에 수정할 사항이 생겼을 때 찾는데 복잡할 수 있어 사용하는거 추천하지 않는다.


셀렉트 박스

셀렉트 박스는 여러 선택지 중에 하나를 선택할 때 사용할 수 있다.

  • 배송 방식 빠른 배송
    • 일반 배송
    • 느린 배송
    • 셀렉트 박스
    • 하나만 선택할 수 있다.

예제 코드

@ModelAttribute("deliveryCodes")
    public List<DeliveryCode> deliveryCodes() {
        List<DeliveryCode> deliveryCodes = new ArrayList<>();
        deliveryCodes.add(new DeliveryCode("FAST", "빠른 배송"));
        deliveryCodes.add(new DeliveryCode("NORMAL", "일반 배송"));
        deliveryCodes.add(new DeliveryCode("SLOW", "느린 배송"));
        return deliveryCodes;
    }

DeliveryCode 를 등록 폼, 조회, 수정 폼에서 모두 사용하므로 위와 똑같이 @ModelAttribute 를 사용했다.

*참고: @ModelAttribute 가 있는 deliveryCodes() 메서드는 컨트롤러가 호출 될 때 마다 사용되므로 deliveryCodes 객체도 계속 생성된다. 이런 부분은 미리 생성해두고 재사용하는 것이 더 효율적이다.

HTML

셀럭트 박스는 select 를 사용해서 만들면 되고, 나머지는 위와 같은 방식이라 설명을 생략하겠다.

profile
안녕하세요

0개의 댓글