반응형

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 대시보드 - 인프런 | 강의 (inflearn.com)

 

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의

웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있

www.inflearn.com

타임리프 스프링 통합으로 추가되는 기능들

  • 스프링의 SpringEL 문법 통합
  • ${@myBean.doSomething()} 처럼 스프링 빈 호출 지원
  • 편리한 폼 관리를 위한 추가 속성

        - th:object (기능 강화, 폼 커맨드 객체 선택)

        - th:field , th:errors , th:errorclass

  • 폼 컴포넌트 기능

        - checkbox, radio button, List 등을 편리하게 사용할 수 있는 기능 지원

  • 스프링의 메시지, 국제화 기능의 편리한 통합
  • 스프링의 검증, 오류 처리 통합
  • 스프링의 변환 서비스 통합(ConversionService)

입력 폼 처리

Controller에서 빈 item 객체를 생성해서 넘겨줌

th:field="${item.~~~~}" 사용하면 id = "~~~~", name = "~~~~", value = "~~~~" 자동으로 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<form action="item.html" th:object="${item}" th:action method="post">
    <div>
        <label for="id">상품 ID</label>
        <input type="text" id="id" name="id" class="form-control" value="1" th:value="${item.id}" readonly>
    </div>
    <div>
        <label for="itemName">상품명</label>
        <input type="text" id="itemName" name="itemName" class="form-control" value="상품A" th:value="${item.itemName}">
    </div>
    <div>
        <label for="price">가격</label>
        <input type="text" id="price" name="price" class="form-control" value="10000" th:value="${item.price}">
    </div>
    <div>
        <label for="quantity">수량</label>
        <input type="text" id="quantity" name="quantity" class="form-control" value="10" th:value="${item.quantity}">
    </div>
....
</form>
cs

form에서 th:object="${item}" 사용

th:field="*{itemName}" -> item.itemName

th:field="*{price}" -> item.price

th:field="*{quantity}" -> item.quantity

 

체크 박스 - 단일1

check 하면 on 넘어옴 -> true로 변환

check 안하면 check가 아예 사라짐-> null로 넘어감

 

수정 시 문제 발생 가능

체크 했다가 해제 -> null 넘어감 -> 아무 작업도 안 한 것으로 판단할 수 있음 -> 값 수정 안됨

 

Spring에서 체크 해제를 인식하기 위한 히든 필드 기능

<input type="checkbox" id="open" name="open" class="form-check-input">

<input type="hidden" name="_open" value="on"/>

 

실행 로그

item.open=true // 선택 O

item.open=false // 선택 X

※ 단점: 일일히 추가해야 함

 

체크 박스 - 단일2

th:field="*{open}": 히든 필드 생략 가능 (자동 생성됨)

<input type="checkbox" th:field="*{open}" class="form-check-input">

 

타임리프의 체크 확인

checked="checked"

체크 박스에서 판매 여부를 선택해서 저장하면, 조회시에 checked 속성이 추가된 것을 확인할 수 있다.

이런 부분을 개발자가 직접 처리하려면 상당히 번거롭다. 타임리프의 th:field 를 사용하면, 값이 true

인 경우 체크를 자동으로 처리해준다.

 

체크박스 - 등록, 수정

등록, 수정은 form에서 처리. form 태그에서 th:object="${item}" 추가했으므로 *{price}, *{quantity} 등 사용 가능

1
2
3
4
5
6
7
<div>판매 여부</div>
<div>
    <div class="form-check">
        <input type="checkbox" th:field="*{open}" class="form-check-input">
        <label for="open" class="form-check-label">판매 오픈</label>
    </div>
</div>
cs

 

체크박스 - 조회

조회는 form 태그 사용 안함. th:object="${item}" 없으므로 ${item.price}, ${item.quantity} 등으로 사용해야 함.

조회 -> 변경하면 안되므로 disabled 추가

1
2
3
4
5
6
7
<div>판매 여부</div>
<div>
    <div class="form-check">
        <input type="checkbox" th:field="${item.open}" class="form-check-input" disabled>
        <label for="open" class="form-check-label">판매 오픈</label>
    </div>
</div>
cs

 

체크박스 값 변경 후 수정 안되는 현상

POST/ edit 할 때 변경된 item이 제대로 넘어오는 지 확인하기 위해  log 출력 -> 잘 넘어옴

itemRepository.update()에 setOpen() 추가 안되어있어서 그럼

 

체크 박스 - 멀티

HashMap(): 순서 보장 안됨

LinkedHashMap(): 순서 보장 됨

 

지역 추가하는 코드 등록, 수정, 조회 다 들어감

1
2
3
4
5
Map<StringString> regions = new LinkedHashMap<>();
regions.put("SEOUL""서울");
regions.put("BUSAN""부산");
regions.put("JEJU""제주");
model.addAttribute("regions", regions);
cs

@ModelAttribute(): 컨트롤러 호출 시 항상 자동으로 model.addAttribute() 실행

1
2
3
4
5
6
7
8
@ModelAttribute("regions")
public Map<StringString> regions() {
    Map<StringString> regions = new LinkedHashMap<>();
    regions.put("SEOUL""서울");
    regions.put("BUSAN""부산");
    regions.put("JEJU""제주");
    return regions;
}
cs

 

label 클릭해도 체크박스 활성화 -> 체크박스의 id를 알아야 함

th:field="*{regions}"에서 체크박스 id 자동 생성

th:for="${#ids.prev('open')}"

멀티 체크박스는 같은 이름의 여러 체크박스를 만들 수 있다. 그런데 문제는 이렇게 반복해서 HTML 태그를 생성할 때, 생성된 HTML 태그 속성에서 name 은 같아도 되지만, id 는 모두 달라야 한다. 따라서 타임리프는 체크박스를 each 루프 안에서 반복해서 만들 때 임의로 1 , 2 , 3 숫자를 뒤에 붙여준다.

 

each로 체크박스가 반복 생성된 결과 - id 뒤에 숫자가 추가됨

1
2
3
<input type="checkbox" value="SEOUL" class="form-check-input" id="regions1" name="regions">
<input type="checkbox" value="BUSAN" class="form-check-input" id="regions2" name="regions">
<input type="checkbox" value="JEJU" class="form-check-input" id="regions3" name="regions">
cs

 

HTML의 id 가 타임리프에 의해 동적으로 만들어지기 때문에 <label for="id 값"> 으로 label 의 대상이 되는 id 값을 임의로 지정하는 것은 곤란하다. 타임리프는 ids.prev(...) , ids.next(...) 을 제공해서 동적으로 생성되는 id 값을 사용할 수 있도록 한다.

 

th:value="${region.key}" 가 존재하면 checked, 없으면 체크안함

<input type="checkbox" th:field="${item.regions}" th:value="${region.key}" class="form-check-input" disabled>

 

라디오 버튼

라디오 박스: 히든 필드 안만들어도 됨 -> 한 번 선택하면 절대 null로 만들 수 없기 때문

 

메시지, 국제화

스프링 부트 메시지 소스 기본 값: spring.messages.basename=messages

 

MessageSource를 스프링 빈으로 등록하지 않고, 스프링 부트와 관련된 별도의 설정을 하지 않으면 messages 라는 이름으로 기본 등록된다. 따라서 messages_en.properties, messages_ko.properties, messages.properties 파일만 등록하면 자동으로 인식된다.

 

스프링 메시지 소스 사용

메시지가 없으면 오류 발생

1
2
3
4
5
@Test
void notFoundMessageCode() {
    assertThatThrownBy(() -> messageSource.getMessage("no_code"nullnull))
                            .isInstanceOf(NoSuchMessageException.class);
}
cs

 

default 메시지 설정 가능

1
2
3
4
5
@Test
void notFoundMessageCodeDefaultMessage() {
    String result = messageSource.getMessage("no_code"null"기본 메시지"null);
    assertThat(result).isEqualTo("기본 메시지");
}
cs

 

hello.name의 argument가 new Object[]에 넣은 값으로 치환됨

1
2
3
4
5
@Test
void argumentMessage() {
    String message = messageSource.getMessage("hello.name"new Object[]{"Spring"}, null);
    assertThat(message).isEqualTo("안녕 Spring");
}
cs
반응형

+ Recent posts