4 분 소요

인프런 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술편을 학습하고 정리한 내용 입니다.

상품 상세

상품 상세 컨트롤러와 뷰를 개발하자.

hello.itemservice.web.basic.BasicItemController

1
2
3
4
5
6
@GetMapping("/{itemId}")  
public String item(Model model, @PathVariable("itemId") Long itemId) {  
    Item item = itemRepository.findById(itemId);  
    model.addAttribute("item", item);  
    return "basic/item";  
}

컨트롤러에 item 메서드를 추가했다.

PathVariable로 넘어온 상품 ID로 상품을 조회하고, 모델에 담아둔다. 그리고 뷰 템플릿을 호출한다.

상품 상세 뷰

정적 HTML을 뷰 템플릿(templates) 영역으로 복사하고 다음과 같이 수정하자.

/resources/static/item.html → 복사 → /resources/templates/basic/item.html

/resources/templates/basic/item.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!DOCTYPE HTML>  
<html xmlns:th="http://www.thymeleaf.org">  
<head>  
    <meta charset="utf-8">  
    <link th:href="@{/css/bootstrap.min.css}"  
            href="../css/bootstrap.min.css" rel="stylesheet">  
    <style>  
        .container {  
            max-width: 560px;  
        }  
    </style>  
</head>  
<body>  
<div class="container">  
    <div class="py-5 text-center">  
        <h2>상품 상세</h2>  
    </div>  
    <div>  
        <label for="itemId">상품 ID</label>  
        <input type="text" id="itemId" name="itemId" 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}" readonly>  
    </div>  
    <div>  
        <label for="price">가격</label>  
        <input type="text" id="price" name="price" class="form-control"  
               value="10000" th:value="${item.price}" readonly>  
    </div>  
    <div>  
        <label for="quantity">수량</label>  
        <input type="text" id="quantity" name="quantity" class="form-control"  
               value="10" th:value="${item.quantity}" readonly>  
    </div>  
    <hr class="my-4">  
    <div class="row">  
        <div class="col">  
            <button class="w-100 btn btn-primary btn-lg"  
                    onclick="location.href='editForm.html'"  
                    th:onclick="|location.href='@{/basic/items/{itemId}/edit(itemId=${item.id})}'|"  
                    type="button" >상품 수정</button>  
        </div>  
        <div class="col">  
            <button class="w-100 btn btn-secondary btn-lg"  
                    onclick="location.href='items.html'"  
                    th:onclick="|location.href='@{/basic/items}'|"  
                    type="button">목록으로</button>  
        </div>  
    </div>  
</div> <!-- /container -->  
</body>  
</html>

속성 변경 - th:value

th:value="${item.id}"

  • 모델에 있는 item 정보를 획득하고 프로퍼티 접근법으로 출력한다. (item.getId())
  • value 속성을 th:value 속성으로 변경한다.

상품수정 링크

  • th:onclick="|location.href='@{/basic/items/{itemId}/edit(itemId=${item.id})}'|"

아직 못 만들었지만, url을 보면 잘 동작한다.

목록으로 링크

  • th:onclick="|location.href='@{/basic/items}'|"

상품 등록 폼

BasicItemController에 추가

1
2
3
4
@GetMapping("/add")  
public String addForm() {  
    return "basic/addForm";  
}

상품 등록 폼은 단순히 뷰 템플릿만 호출한다.

상품 등록 폼 뷰

정적 HTML을 뷰 템플릿(templates) 영역으로 복사하고 다음과 같이 수정하자.

/resources/static/addForm.html → 복사 → /resources/templates/basic/addForm.html

/resources/templates/basic/addForm.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE HTML>  
<html xmlns:th="http://www.thymeleaf.org">  
<head>  
    <meta charset="utf-8">  
    <link th:href="@{/css/bootstrap.min.css}"  
            href="../css/bootstrap.min.css" rel="stylesheet">  
    <style>  
        .container {  
            max-width: 560px;  
        }  
    </style>  
</head>  
<body>  
<div class="container">  
    <div class="py-5 text-center">  
        <h2>상품 등록 폼</h2>  
    </div>  
    <h4 class="mb-3">상품 입력</h4>  
    <form action="item.html" th:action method="post" >  
        <div>  
            <label for="itemName">상품명</label>  
            <input type="text" id="itemName" name="itemName" class="form-control" placeholder="이름을 입력하세요">  
        </div>  
        <div>  
            <label for="price">가격</label>  
            <input type="text" id="price" name="price" class="form-control"  
                   placeholder="가격을 입력하세요">  
        </div>  
        <div>  
            <label for="quantity">수량</label>  
            <input type="text" id="quantity" name="quantity" class="form-control" placeholder="수량을 입력하세요">  
        </div>  
        <hr class="my-4">  
        <div class="row">  
            <div class="col">  
                <button class="w-100 btn btn-primary btn-lg" type="submit">상품 등  
                    록</button>  
            </div>  
            <div class="col">  
                <button class="w-100 btn btn-secondary btn-lg"  
                        onclick="location.href='items.html'"  
                        th:onclick="|location.href='@{/basic/items}'|"  
                        type="button">취소</button>  
            </div>  
        </div>  
    </form>  
</div> <!-- /container -->  
</body>  
</html>

속성 변경 - th:action

  • th:action
  • HTML form에서 action에 값이 없으면 현재 URL에 데이터를 전송한다.
  • 상품 등록 폼의 URL과 실제 상품 등록을 처리하는 URL을 똑같이 맞추고, HTTP 메서드로 두 기능을 구분한다.
    • 상품 등록 폼 : GET /basic/items/add
    • 상품 등록 처리 : POST /basic/items/add
  • 이렇게 하면 하나의 URL로 등록 폼과, 등록 처리를 깔끔하게 처리할 수 있다.

지금 컨트롤러가 없어서 안되지만, 데이터는 잘 넘어갔다.

취소

  • 취소시 상품 목록으로 이동한다.
  • th:onclick="|location.href='@{/basic/items}'|"

상품 등록 처리 - @ModelAttribute

이제 상품 등록 폼에서 전달된 데이터로 실제 상품을 등록 처리해보자.

상품 등록 폼은 다음 방식으로 서버에 데이터를 전달한다.

  • POST - HTML Form
    • content-type: application/x-www-form-urlencoded
    • 메시지 바디에 쿼리 파리미터 형식으로 전달 itemName=itemA&price=10000&quantity=10
    • 예) 회원 가입, 상품 주문, HTML Form 사용

요청 파라미터 형식을 처리해야 하므로 @RequestParam을 사용하자.

상품 등록 처리 V1 - @RequestParam

addItemV1 - BasicItemController에 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@PostMapping("/add")  
public String save(@RequestParam("itemName") String itemName,  
                   @RequestParam("price") int price,  
                   @RequestParam("quantity") 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("itemName") String itemName : itemName 요청 파라미터 데이터를 해당 변수에 받는다.
  • Item 객체를 생성하고 itemRepository 를 통해서 저장한다.
  • 저장된 item을 모델에 담아서 뷰에 전달한다.

중요: 여기서는 상품 상세에서 사용한 item.html 뷰 템플릿을 그대로 재활용한다.

실행해서 상품이 잘 저장되는지 확인하자.

리다이렉트 처리를 해줘야 할꺼 같은데 아무튼 잘 됐다.

상품 등록 처리 V2 - @ModelAttribute

@RequestParam으로 변수를 하나하나 받아서 Item 을 생성하는 과정은 불편했다.

이번에는 @ModelAttribute를 사용해서 한번에 처리해보자.

addItemV2 - 상품 등록 처리 - ModelAttribute

이전 코드는 주석 처리하고 새로 만들자.

1
2
3
4
5
6
@PostMapping("/add")  
public String addItemV2(@ModelAttribute("item") Item item, Model model) {  
    itemRepository.save(item);  
    // model.addAttribute("item", item); // 이거 자동 추가임. 생략 가능  
    return "basic/item";  
}

@ModelAttribute - 요청 파라미터 처리

@ModelAttributeItem객체를 생성하고, 요청 파라미터의 값을 프로퍼티 접근법(setXxx)으로 입력해준다.

@ModelAttribute - Model 추가

@ModelAttribute는 중요한 한 가지 기능이 더 있는데, 바로 모델(Model)에 @ModelAttribute로 지정한 객체를 자동으로 넣어준다.

지금 코드를 보면 model.addAttribute("item", item); 을 주석 처리 해버렸는데, 잘 동작하는 걸 볼 수 있다.

모델에 데이터를 담을 때는 이름이 필요하다. 이름은 @ModelAttribute에 지정한 name(value)속성을 사용한다. 만약 다음과 같이 @ModelAttribute의 이름을 다르게 지정하면 다른 이름으로 모델에 포함된다.

@ModelAttribute("hello") Item item → 이름을 hello로 지정 model.addAttribute("hello", item); → 모델에 hello 이름으로 저장

상품 등록 처리 V3 - ModelAttribute 이름 생략

1
2
3
4
5
@PostMapping("/add")  
public String addItemV3(@ModelAttribute Item item) {  
    itemRepository.save(item);  
    return "basic/item";  
}

이렇게 @ModelAttribute 생략이 가능하다.

@ModelAttribute의 이름을 생략하면 모델에 저장될 때 클래스명을 사용한다.

이때 클래스의 첫글자만 소문자로 변경해서 등록한다.

  • 예) @ModelAttribute 클래스명 → 모델에 자동 추가되는 이름
    • Itemitem
    • HelloWorldhelloWorld

상품 등록 처리 V4 - ModelAttribute 전체 생략

자 이제 극한으로 줄여보자.

1
2
3
4
5
@PostMapping("/add")  
public String addItemV4(Item item) {  
    itemRepository.save(item);  
    return "basic/item";  
}

@ModelAttribute자체도 생략 가능하다. 대상 객체는 모델에 자동 등록된다.

나머지 사항은 기존과 동일하다.

댓글남기기