😺

【初心者向け/ITスクール 69日】個人プロジェクトスタート3日目

2023/10/25に公開

はじめに

今日は、Spring BootのWEB MVCの基礎勉強が終わり、簡単なWEBアプリケーションを練習しました。
こちらを元にして、プロジェクトを設計し、作っていきたいと思います。

Domain(not DTO)

DTOはただのDataをViewに転送するためのオブジェクトらしいですが、
今まではDAOを一つに設計したので、その違いが分かりませんでした!
Domainは商品のようにDBに直接入力するためのオブジェクトです!

本来なら、Domainには@Dataをつけば、問題が起こる可能性があるようですが、練習の際には@Dataを付けます。
Domainということを表示するため、@Entityを付けるケースも多いらいしいです。

package hello.itemservice.domain.item;

import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class Item {

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

    public Item() {
    }

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

BootStrap

時間を節約するための最適のCSS,JSのライブラリ

https://getbootstrap.com/

Model(DBの値を出したり、入れたり)


@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
public class BasicItemController {

    private final ItemRepository itemRepository;

    @GetMapping
    public String items(Model model){
        List<Item> items = itemRepository.findAll();
        model.addAttribute("items",items);
        return "basic/items";
    }
          <tr th:each="item :${items}">
               <td><a href="item.html" th:href="@{/basic/items/{itemId}(itemId=${item.id})}"  th:text="${item.id}">회원id</a></td>
               <td><a href="item.html" th:href="@{/basic/items/${item.id}}" th:text="${item.itemName}">상품명</a></td>
               <td th:text="${item.price}">10000</td>
               <td th:text="${item.quantity}">10</td>

PathVariable

作成したitemのURI(=商品のid, primary key)をマッピングするため、
@PathVariableを活用します。

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

model attribute

情報を追加する時、以前はrequest, responseオブジェクトを活用したのですが、
DTO、Domainなどでデーターを転送する際には@Model attributeを利用することが効果的です。

//@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";
    }

    //@PostMapping("/add")
    public String addItemV2(@ModelAttribute("item") Item item, Model model){

        itemRepository.save(item);
        //model.addAttribute("item", item); //パラメータのModel省略可能
        return "basic/item";
    }

    //@PostMapping("/add")
    public String addItemV3(@ModelAttribute Item item){

        //Item -> item
        itemRepository.save(item);
        //model.addAttribute("item", item); //パラメータのModel省略可能
        return "basic/item";
    }

    //@PostMapping("/add")
    public String addItemV4(Item item){

        //Item -> item
        itemRepository.save(item);
        //model.addAttribute("item", item); //パラメータのModel省略可能
        return "basic/item";
    }

    @PostMapping("/add")
    public String addItemV5(@ModelAttribute Item item){

        //Item -> item
        itemRepository.save(item);
        //model.addAttribute("item", item); //パラメータのModel省略可能
        return "redirect:/basic/items/" + item.getId();
    }

redirect attribute

redirect attributeは先ほどの問題を簡単に解決することができます。
URLをエンコーディングする機能とredirect時にrequestにsetattributeをする効果があります。

@PostMapping("/add")
    public String addItemV6(@ModelAttribute Item item, RedirectAttributes redirectAttributes){
        Item savedItem = itemRepository.save(item);
        redirectAttributes.addAttribute("itemId", savedItem.getId());
        redirectAttributes.addAttribute("status", true);
        return "redirect:/basic/items/{itemId}";
    }

<h2 th:if="${param.status}" th:text="저장 완료"></h2>

PRG patterns

POST要請の際に使う、パターンで、必修です!

  @PostMapping("/{itemId}/edit")
    public String edit(@PathVariable Long itemId,@ModelAttribute Item item){
        itemRepository.update(itemId, item);
        return "redirect:/basic/items/{itemId}";
    }

message

htmlのテキストは基本的にhard codingになっております。
例えば、100個のタグの中に「商品名」というテキストあると想像します。
その際に、いきなりすべての「商品名」を「商品」に変更することになる場合、
我々はいちいちtemplate engine内の全ての「商品名」を変更しなければならない状況に向かいます。
その際に変数のようにtextを保管する機能がSpring messageで、これによって、色々な国を支援することが可能です。

SpringBootはMessege機能が登録されているので、@beanで何かを登録する必要がありません。

application.properties
spring.messages.basename=messages

独自の設定を行わない場合、デフォルトで "messages" という名前で登録されます。resourcesフォルダーにmessages_en.properties、messages_ko.properties、messages.properties ファイルのみを登録すれば、自動的に認識されます。

messages.properties
hello=안녕
hello.name=안녕 {0}
messages_en.properties
hello=hello
hello.name=hello {0}


@SpringBootTest
public class MessageSourceTest {

    @Autowired
    MessageSource ms;

    @Test
    void helloMessage(){
        String result = ms.getMessage("hello", null, null);
        assertThat(result).isEqualTo("안녕");
    }

    @Test
    void notFoundMessageCode(){
        assertThatThrownBy(() -> ms.getMessage("no-code", null, null))
                .isInstanceOf(NoSuchMessageException.class);
    }

    @Test
    void notFoundMessageCodeDefaultMessage(){
        String result = ms.getMessage("no-code", null, "기본 메시지", null);
        assertThat(result).isEqualTo("기본 메시지");
    }

    @Test
    void argumentMessage(){
        String message = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
        assertThat(message).isEqualTo("안녕 Spring");
    }

    @Test
    void defaultLang(){
        assertThat(ms.getMessage("hello", null, null)).isEqualTo("안녕");
        assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕");
    }

    @Test
    void enLang(){
        assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
    }
}

messageの適用と国際言語支援

messageの適用

messages.properties
label.item.id=상품 ID
label.item.itemName=상품명
label.item.price=가격
label.item.quantity=수량
<th th:text="#{label.item.id}">ID</th>
<th th:text="#{label.item.itemName}">상품명</th>
<th th:text="#{label.item.price}">가격</th>
<th th:text="#{label.item.quantity}">수량</th>

th:text="#{}" でマッピングすることが可能です。

国際化

messages_en.properties
label.item.id=Item ID
label.item.itemName=Item Name
label.item.price=price
label.item.quantity=quantity

messages_en.propertiesに追加し、Chromeの設定から言語の優先順位を変更してみますと

Discussion