์Šคํ”„๋ง ๋ถ€ํŠธ๋กœ ๊ฐ„๋‹จํ•œ ๊ฒŒ์‹œํŒ ๋งŒ๋“ค๊ธฐ (ํƒ€์ž„๋ฆฌํ”„ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ, Thymeleaf)

2022. 1. 19. 08:40ใ†Spring/[2022] Spring boot

728x90

 

๊ฒŒ์‹œํŒ์€ ์ง€๊ธˆ๊นŒ์ง€ ๋งŽ์ด ๋งŒ๋“ค์—ˆ์—ˆ๋Š”๋ฐ ์Šคํ”„๋ง ๋ถ€ํŠธ๋ฅผ ํ™œ์šฉํ•ด์„œ ๊ฒŒ์‹œํŒ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ๋„ ํ•œ๋ฒˆ ํ•ด๋ณด์ž ํ•ด์„œ 

๋˜ ๋งŒ๋“ค์–ด ๋ณธ๋‹ค! 

 

1๏ธโƒฃ ํ…Œ์ด๋ธ” ์ƒ์„ฑ 

ํ…Œ์ด๋ธ”์„ ์˜ˆ์ „๋ถ€ํ„ฐ ๊ณ„์† ์‚ฌ์šฉํ•ด์˜ค๋˜ ํ…Œ์ด๋ธ”์„ ์ด์šฉํ•ด์ค„ ์˜ˆ์ •์ด๋‹ค. 

 

2๏ธโƒฃ ์™„์„ฑ ํ™”๋ฉด 

๊ธฐ๋ณธ ํ™”๋ฉด์€ ์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค. 

 

 

์ด๋ฒˆ์—๋Š” ์ •๋ง ๊ฐ„๋‹จํ•˜๊ฒŒ CRUD๋งŒ ์ž‘์„ฑํ•  ์˜ˆ์ •์ด๊ณ  

์ด์ œ ๊ฒ€์ฆ, ๋กœ๊ทธ์ธ ๋“ฑ์„ ๊ตฌํ˜„ํ•ด์„œ ์ ์  ๋ฐœ์ „ ์‹œํ‚ฌ ์˜ˆ์ •์ด๋‹ค. 

(์ด๋ฒˆ์—” ์ง„์งœ..!!!)

 

๋ช‡๋ฒˆ์ด๋‚˜ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ๊ฒŒ์‹œํŒ์€ ๋งŒ๋“ค์–ด์™”๋Š”๋ฐ ๋‹ค๋ฅธ ์ ์ด๋ผ๋ฉด ํƒ€์ž„๋ฆฌํ”„๋ฅผ ํ™œ์šฉํ•ด JSP๋Š” ์ „ํ˜€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ด๋‹ค!

์‚ฌ์šฉํ•ด๋ณธ ๊ฒฐ๊ณผ HTML์— ๋‹ค๋ฅธ ์ฝ”๋“œ ๋“ฑ์ด ์„ž์ด์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ด ์ข‹๊ณ  ํƒ€์ž„๋ฆฌํ”„๋ž‘ ์Šคํ”„๋ง์˜ ์—ฐ๋™์„ฑ์ด ์ข‹์•„์„œ ํ™œ์šฉํ•˜๊ธฐ ๊ต‰์žฅํžˆ ์ข‹์•˜๋‹ค. 

 

3๏ธโƒฃ Board.class

์ด๋ฒˆ์—๋Š” ๋กฌ๋ณต๋„ ํ™œ์šฉํ•ด์„œ ์ข€ ๋” ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ•ด์คฌ๋‹ค.

๋„ˆ๋ฌด ์˜›๋‚  ์ž๋ฃŒ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ณต๋ถ€ํ•˜๋‹ค ๋ณด๋‹ˆ๊นŒ ์ด๊ฑธ ์ž˜ ๋ชฐ๋ž๋‹ค,,, ๋กฌ๋ณต,, 

@Data

: @Getter, @Setter, @ToString, @EqualsAndHashCode์™€ @RequiredArgsConstructor๋ฅผ ํ•ฉ์ณ๋†“์€ ๊ฒƒ์ด๋‹ค. 

๊ทธ๋Ÿฐ๋ฐ ๋ณดํ†ต ์ƒ์„ฑ์ž๋Š” ๋ณด์•ˆ์ƒ?์ด๋ผ๊ณ  ํ–ˆ๋‚˜ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š”๊ฒŒ ๋” ์ข‹๋‹ค๊ณ ํ•ด์„œ ์ƒ์„ฑ์ž๋Š” ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์คฌ๋‹ค. 

 

4๏ธโƒฃ ๋ชฉ๋ก ์กฐํšŒ 

BoardController.class

์š”์ฒญ URL "/boards"๋กœ HTTP ์š”์ฒญ ๋ฉ”์„œ๋“œ๊ฐ€ GET์ด๋ฉด boards ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์ˆ˜ํ–‰๋œ๋‹ค. 

 

@RequiredArgsConstructor 

: ์ด ์–ด๋…ธํ…Œ์ด์…˜์€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์€ final ํ•„๋“œ๋‚˜ , @NonNull์ด ๋ถ™์€ ํ•„๋“œ์— ๋Œ€ํ•œ ์ƒ์„ฑ์ž๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๋Š”๋ฐ ์ฃผ๋กœ ์˜์กด์„ฑ ์ฃผ์ž…์— ๋Œ€ํ•œ ํŽธ์˜์„ฑ์„ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค. 

 

BoardService.class

 

BoardRepository.class

 

JdbcTemplateBoardRepository.class

์ด๋ฒˆ์—๋Š” jdbcTemplate๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ ๋‹ค์Œ์—” myBatis, JPA๋ฅผ ์‚ฌ์šฉํ•ด๋ณผ ์˜ˆ์ •์ด๋‹ค!

 

Boards.html 

<!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">
</head>
<body>

<div class="container" style="max-width: 1000px">
    <div class="py-5 text-center">
        <h2 th:text="#{page.boards}">๊ธ€ ๋ชฉ๋ก</h2>
    </div>

    <div class="row">
        <div class="col">
            <button class="btn btn-primary float-end"
                    onclick="location.href='addForm.html'"
                    th:onclick="|location.href='@{/boards/add}'|"
                    type="button" th:text="#{page.addBoard}">๊ธ€ ๋“ฑ๋ก</button>
        </div>
    </div>

    <hr class="my-4">
    <div>
        <table class="table">
            <thead>
            <tr>
                <th th:text="#{label.board.id}">ID</th>
                <th th:text="#{label.board.subject}">์ œ๋ชฉ</th>
                <th th:text="#{label.board.name}">์ž‘์„ฑ์ž</th>
                <th th:text="#{label.board.viewcnt}">์กฐํšŒ์ˆ˜</th>
                <th th:text="#{label.board.regdate}">๋“ฑ๋ก์ผ</th>

            </tr>
            </thead>
            <tbody>
            <tr th:each="board : ${boards}">
                <td><a href="item.html" th:href="@{/boards/{uid}(uid=${board.uid})}" th:text="${board.uid}">๊ธ€ ID</a></td>
                <td><a href="item.html" th:href="@{/boards/{uid}(uid=${board.uid})}" th:text="${board.subject}">์ œ๋ชฉ</a></td>
                <td th:text="${board.name}">์ž‘์„ฑ์ž</td>
                <td th:text="${board.viewcnt}">์กฐํšŒ์ˆ˜</td>
                <td th:text="${board.regdate}">๋“ฑ๋ก์ผ</td>
            </tr>
            </tbody>
        </table>
    </div>

</div> <!-- /container -->

</body>
</html>

ํƒ€์ž„ ๋ฆฌํ”„ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ 

th:text 

-> HTML ์ฝ˜ํ…์ธ ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ถœ๋ ฅํ•  ๋•Œ 

th:text="${...}"

-> ${...} ๋ณ€์ˆ˜ ํ‘œํ˜„์‹ 

th:href="@{...}"

-> @{...} URL ๋งํฌ 

th:each 

-> ๋ฐ˜๋ณต 

th:if, unless

-> ์กฐ๊ฑด (์ด๋•Œ ํƒ€์ž„ ๋ฆฌํ”„๋Š” ํ•ด๋‹น ์กฐ๊ฑด์ด ๋งž์ง€ ์•Š์œผ๋ฉด ์•„์˜ˆ ํƒœ๊ทธ ์ž์ฒด๋ฅผ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋Š”๋‹ค!)

th:object

-> ์ปค๋ฉ˜๋“œ ๊ฐ์ฒด๋ฅผ ์ง€์ •ํ•œ๋‹ค. 

*{...}

-> ์„ ํƒ ๋ณ€์ˆ˜ ์‹์œผ๋กœ th:object์—์„œ ์„ ํƒํ•œ ๊ฐ์ฒด์— ์ ‘๊ทผํ•œ๋‹ค. 

th:field

-> HTML ํƒœ๊ทธ์˜ id, value, name ๋“ฑ ์†์„ฑ์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.(๋งŽ์€ ๊ธฐ๋Šฅ ์ˆ˜ํ–‰ํ•จ) โญโญ

ex) th:field="*{itemName}" = ${item.itemName} 

 

 

 

๋ชฉ๋ก ํ™”๋ฉด&nbsp;

 

 

5๏ธโƒฃ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ 

BoardController.class

๊ฒŒ์‹œ๊ธ€ ์กฐํšŒํ•  ๋•Œ๋Š” ์šฐ์„  ์กฐํšŒ์ˆ˜๋„ ์ฆ๊ฐ€ํ•ด์•ผํ•˜๊ณ , ๊ธ€ ๋ชฉ๋ก๋„ ์กฐํšŒ๋˜์–ด์•ผ ํ•œ๋‹ค.

์ด๋•Œ๋Š” /boards/{uid}๋กœ ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์˜ uid ๋กœ ์กฐํšŒํ•œ๋‹ค. 

 

BoardService.class

BoardRepository.class

JdbcTemplateBoardRepository.class

jdbcTemplate query()

: SELECT ์ฟผ๋ฆฌ ์‹คํ–‰์„ ์œ„ํ•œ ๋ฉ”์„œ๋“œ 

 

jdbcTemplate update()

: INSERT, UPDATE, DELETE ์ฟผ๋ฆฌ๋Š” update() ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค. 

 

Board.html 

<!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 th:text="#{page.board}">์ƒํ’ˆ ์ƒ์„ธ</h2>
    </div>

    <!-- ์ถ”๊ฐ€ -->
    <h2 th:if="${param.status}" th:text="'์ €์žฅ ์™„๋ฃŒ'"></h2>

    <div>
        <label for="boardId" th:text="#{label.board.id}">ID</label>
        <input type="text" id="boardId" name="boardId" class="form-control" value="1" th:value="${board.uid}" readonly>
    </div>
    <div>
        <label for="boardSubject" th:text="#{label.board.subject}">์ œ๋ชฉ</label>
        <input type="text" id="boardSubject" name="boardSubject" class="form-control" value="์ƒํ’ˆA" th:value="${board.subject}" readonly>
    </div>
    <div>
        <label for="name" th:text="#{label.board.name}">์ž‘์„ฑ์ž</label>
        <input type="text" id="name" name="name" class="form-control" value="์ž‘์„ฑ์ž" th:value="${board.name}" readonly>
    </div>
    <div>
        <label for="viewcnt" th:text="#{label.board.viewcnt}">์กฐํšŒ์ˆ˜</label>
        <input type="text" id="viewcnt" name="viewcnt" class="form-control" value="10" th:value="${board.viewcnt}" readonly>
    </div>

    <div>
        <label for="regdate" th:text="#{label.board.regdate}">๋“ฑ๋ก์ผ</label>
        <input type="text" id="regdate" name="regdate" class="form-control" value="10" th:value="${board.regdate}" readonly>
    </div>

    <div>
        <label for="contents" th:text="#{label.board.contents}">๊ธ€ ๋‚ด์šฉ</label>
        <input type="text" id="contents" name="contents" class="form-control" value="10" th:value="${board.contents}" 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='@{/boards/{uid}/edit(uid=${board.uid})}'|"
                    type="button" th:text="#{page.updateBoard}">์ƒํ’ˆ ์ˆ˜์ •</button>
        </div>
        <div class="col">
            <button class="w-100 btn btn-secondary btn-lg"
                    onclick="location.href='items.html'"
                    th:onclick="|location.href='@{/boards}'|"
                    type="button" th:text="#{button.list}">๋ชฉ๋ก์œผ๋กœ</button>
        </div>

        <div class="col">
            <button class="w-100 btn btn-secondary btn-lg"
                    onclick="location.href='items.html'"
                    th:onclick="|location.href='@{/boards/{uid}/delete(uid=${board.uid})}'|"
                    type="button" th:text="#{button.delete}">์‚ญ์ œ</button>
        </div>
    </div>

</div> <!-- /container -->
</body>
</html>

 

6๏ธโƒฃ ๊ฒŒ์‹œ๊ธ€ ๋“ฑ๋ก

BoardController.class

๋จผ์ € @Getmapping("/add")์—์„œ๋Š” ๋“ฑ๋ก ํผ์„ ๋ณด์—ฌ์ฃผ๊ณ , ๋“ฑ๋ก ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด @Postmaping์œผ๋กœ ๊ฐ™์€ url๋กœ ํ•ด๋‹น ๊ธ€์„ ๋“ฑ๋กํ•ด์ค€๋‹ค. 

๊ทธ๋ฆฌ๊ณ  ์ƒํ’ˆ ๋“ฑ๋กํ›„์—๋Š” redirectํ•ด์„œ ๋ชฉ๋ก ์กฐํšŒํ•˜๋ฉด์œผ๋กœ ์ด๋™ํ•œ๋‹ค. 

 

BoardService.class

 

BoardRepository.class

JdbcTemplateBoardRepository.class

 

addForm.html 

<!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;
        }
        .field-error {
            border-color: #dc3545;
            color: #dc3545;
        }
    </style>
</head>
<body>

<div class="container">

    <div class="py-5 text-center">
        <h2 th:text="#{page.addBoard}">์ƒํ’ˆ ๋“ฑ๋ก</h2>
    </div>

    <form action="item.html" th:action th:object="${board}" method="post">

        <div>
            <input type="hidden" id="name" class="form-control"  th:field="*{name}" value="testUser" placeholder="์ œ๋ชฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”.">

        </div>

        <div>
            <label for="subject" th:text="#{label.board.subject}">์ œ๋ชฉ</label>
            <input type="text" id="subject" th:field="*{subject}" class="form-control" placeholder="์ œ๋ชฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”.">

        </div>
        <div>
            <label for="contents" th:text="#{label.board.contents}">๋‚ด์šฉ</label>
            <textarea cols="150" rows="10" id="contents" th:field="*{contents}" class="form-control" placeholder="๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”.">

            </textarea>

        </div>

        <hr class="my-4">

        <div class="row">
            <div class="col">
                <button class="w-100 btn btn-primary btn-lg" type="submit" th:text="#{button.save}">์ €์žฅ</button>
            </div>
            <div class="col">
                <button class="w-100 btn btn-secondary btn-lg"
                        onclick="location.href='items.html'"
                        th:onclick="|location.href='@{/boards}'|"
                        type="button" th:text="#{button.cancel}">์ทจ์†Œ</button>
            </div>
        </div>

    </form>

</div> <!-- /container -->
</body>
</html>

 

7๏ธโƒฃ ๊ฒŒ์‹œ๊ธ€ ์‚ญ์ œ 

BoardController.class

์—ฌ๊ธฐ์„œ๋Š” ๋”ฐ๋กœ ํ™”๋ฉด ๊ตฌํ˜„ ์—†์ด ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ ํ™”๋ฉด์—์„œ ์‚ญ์ œ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์‚ญ์ œ๊ฐ€ ๋˜๋ฉด์„œ ๋ชฉ๋ก์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•œ๋‹ค. 

 

BoardService.class

BoardRepository.class

JdbcTemplateBoardRepository.class

 

8๏ธโƒฃ ๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ • 

BoardController.class

@GetMapping("/{uid}/edit") ์„ ํ†ตํ•ด ํ•ด๋‹น uid์˜ ๊ธ€์„ ๊ฐ€์ ธ์˜ค๊ณ , ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•œ๋‹ค. 

๊ทธ๋ฆฌ๊ณ  ์ˆ˜์ • ํŽ˜์ด์ง€์—์„œ ์ˆ˜์ • ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด @PostMapping("/{uid}/edit")์œผ๋กœ ํ•ด๋‹น ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋™์ž‘ํ•˜๊ณ  ํŽ˜์ด์ง€์˜ ์ œ๋ชฉ๊ณผ ๋‚ด์šฉ์ด ์ˆ˜์ •๋œ๋‹ค.

 

BoardService.class

BoardRepository.class

JdbcTemplateBoardRepository.class

 

 

์ˆ˜์ •ํ•œ ๋‹ค์Œ์—๋Š” ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•ด์„œ ์ˆ˜์ •ํ•œ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90