๐œŽ Spring Boot ์˜ˆ์ œ2 (๊ฒŒ์‹œ๊ธ€ ๊ด€๋ฆฌ) (ใฃใƒปโˆ€ใƒป๏ผ‰ใฃ

@Autowiredยท2022๋…„ 1์›” 14์ผ
3

Spring Boot

๋ชฉ๋ก ๋ณด๊ธฐ
10/11

Spring Boot์—์„œ ๊ฒŒ์‹œ๊ธ€ ๊ด€๋ฆฌ ํ•ด๋ณด๊ธฐ! ใƒฝ(โœฟ๏พŸโ–ฝ๏พŸ)ใƒŽ

  • ์‚ฌ์šฉ IDE : IntelliJ IDEA Ultimate
  • ์‚ฌ์šฉ DB : MySQL
  • ์‚ฌ์šฉ ์–ธ์–ด & SDK : Java & Amazon correto 11
  • ์ •๋ฆฌ๋ณธ์ž…๋‹ˆ๋‹ค ์ฐธ๊ณ ์šฉ์œผ๋กœ๋งŒ ๋ด์ฃผ์„ธ์š” :D
  • ์ €๋ฒˆ์‹œ๊ฐ„์—” ๊ฒŒ์‹œ๊ธ€ ์ €์žฅ, ๋ชฉ๋ก์ถœ๋ ฅ, ์ƒ์„ธ์กฐํšŒ๊นŒ์ง€ ํ–ˆ์œผ๋‹ˆ ์ด๋ฒˆ์—๋Š”
    ๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ • & ์‚ญ์ œ & ํŽ˜์ด์ง•์ฒ˜๋ฆฌ๊นŒ์ง€ ์ง„ํ–‰ํ•ด๋ด…์‹œ๋‹ค (ใฃใƒปโˆ€ใƒป๏ผ‰ใฃ


    ์ด์ „๊ธ€
    ๐œŒ Spring Boot ์˜ˆ์ œ1 (๊ฒŒ์‹œ๊ธ€ ๊ด€๋ฆฌ) ใƒฝ(โœฟ๏พŸโ–ฝ๏พŸ)ใƒŽ

Update & Delete

  • ์ด๋ฒˆ์—๋Š” ์ˆ˜์ •์„ ํ•  ๋•Œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ๋ฐ›์•„ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ผ์น˜ ์‹œ ์ˆ˜์ •์ด ๋˜๊ฒŒ๋” ์ง„ํ–‰ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
    ์„ค๋ช…์€ DTO๋ถ€ํ„ฐ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค:D

  • ์‚ญ์ œ๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—…๋ฐ์ดํŠธ ํŽ˜์ด์ง€์—์„œ ๊ฐ™์ด ์ง„ํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค ๊ทธ๋Ÿผ ๋ฐ”๋กœ ๊ฐ€์‹œ์ฃ !! (เน‘โ•นโˆ€โ•นเน‘)

UpdateDTO

@Data
@NoArgsConstructor
@AllArgsConstructor
public class BoardUpdateDTO {

    private Long boardId;
    private String boardWriter;
    private String boardPassword;
    private String boardTitle;
    private String boardContents;

Update์— ๊ด€ํ•œ DTO ๋‚ด์šฉ์€ ์œ„์™€๊ฐ™์ด ์„ค์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
์ˆ˜์ •์— ํ•„์š”ํ•œ ๋‚ด์šฉ๋งŒ ๋‹ด๋Š”๊ฑฐ๋ผ ํŠน๋ณ„ํ•œ ๋‚ด์šฉ์ด ์—†์œผ๋ฏ€๋กœ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค :D


html

findById.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>findById.html</h2>
<table>
    <thead>
        <tr>
            <th>๋ฒˆํ˜ธ</th>
            <th>์ž‘์„ฑ์ž</th>
            <th>์ œ๋ชฉ</th>
            <th>๋‚ด์šฉ</th>
            <th>์ž‘์„ฑ์‹œ๊ฐ„</th>
            <th>์ˆ˜์ •์‹œ๊ฐ„</th>
            <th>์ˆ˜์ •</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th th:text="${boardDetailDTO.boardId}"></th>
            <th th:text="${boardDetailDTO.boardWriter}"></th>
            <th th:text="${boardDetailDTO.boardTitle}"></th>
            <th th:text="${boardDetailDTO.boardContents}"></th>
            <th th:text="${boardDetailDTO.createTime}"></th>
            <th th:text="${boardDetailDTO.updateTime}"></th>
            <!-- findById์— update ํผ์œผ๋กœ ๊ฐ€๊ธฐ ์œ„ํ•œ ๋งํฌ ์ถ”๊ฐ€ -->
            <th><a th:href="@{|/board/update/${boardDetailDTO.boardId}|}">์ˆ˜์ •ํŽ˜์ด์ง€</a></th>
        </tr>
    </tbody>
</table>
</body>
</html>

์—…๋ฐ์ดํŠธ ํŽ˜์ด์ง€๋ฅผ ๋„์šฐ๊ธฐ ์œ„ํ•ด์„œ๋Š” boardId๋ฅผ ๊ฐ™์ด ๋ณด๋‚ด์ค˜์•ผํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ ํƒ€์ž„๋ฆฌํ”„๋ฌธ๋ฒ• ์ค‘ ํ•˜๋‚˜์ธ @{||}๋ฅผ ์ด์šฉํ•ด boardId๋ฅผ ๊ฐ™์ด ๋ณด๋‚ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค :D

update.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
    <script>
	// ์•„๋ž˜ ์ฝ”๋“œ๋Š” ์ผ๋ฐ˜ Post ๋ฐฉ์‹์œผ๋กœ ์ˆ˜์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•, ์„ค๋ช… ์ƒ๋žต
        // const update = () => {
        //     const inputPassword = document.getElementById("boardPassword").value
        //     const boardPassword = "[[${boardDetailDTO.boardPassword}]]"
        //     if (inputPassword==boardPassword) {
        //         updateForm.submit();
        //     } else {
        //         alert("๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ‹€๋ ธ์Šต๋‹ˆ๋‹ค.")
        //     }
        // }

        const boardUpdate = () => {
            const id = document.getElementById("boardId").value
            const writer = document.getElementById("boardWriter").value
            const title = document.getElementById("boardTitle").value
            const contents = document.getElementById("boardContents").value
            const inputPassword = $("#boardPassword").val(); // ajax๋กœ value๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ
            const password = "[[${boardDetailDTO.boardPassword}]]"
            // const boardDate = document.getElementById("boardDate").value
            const updateData = JSON.stringify({
                boardId: id,
                boardWriter: writer,
                boardTitle: title,
                boardContents: contents,
                boardPassword: password
                // boardPassword: pw,
                // boardDate: boardDate
            })
            console.log(updateData)
            const reqUrl = "/board/"+id
            if (inputPassword == password) { // ๋น„๋ฐ€๋ฒˆํ˜ธ ์ผ์น˜์—ฌ๋ถ€ ์ฒดํฌ
                $.ajax({
                    type: "put", // method๊ฐ€ put์ธ ๋ฉ”์„œ๋“œ๋กœ ๋ณด๋‚ธ๋‹ค๋Š” ๋œป
                    url: reqUrl, // /board/{boardId}
                    contentType: "application/json", // json์„ ๋ณด๋‚ธ๋‹ค๋ฉด ๋ฐ˜๋“œ์‹œ ์ž‘์„ฑ
                    data: updateData, // ์œ„์—์„œ ์„ค์ •ํ•œ updateData๋ฅผ ๋ณด๋ƒ„ (jsonํƒ€์ž…)
                    success: function () {
                        location.href = "/board/"+id;
                    },
                    error: function () {
                        alert("ajax ์‹คํŒจ")
                    }
                })
            } else {
                alert("๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ‹€๋ ธ์Šต๋‹ˆ๋‹ค")
            }
        }
      
      const boardDelet = () => {
      	const id = document.getElementById("boardId).value
      	reqUrl = "/board/"+id
      	$.ajax({
      	  type: "delete",
      	  url: reqURL,
      	  success: function () {
          	location.href = "/board/"
          },
          error: function () {
          	alert("ajax ์‹คํŒจ")
          }
      	})
      }
    </script>
</head>
<body>
<h2>update.html</h2>
<form action="/board/update" method="post" name="updateForm">
    <input type="text" id="boardId" name="boardId" th:value="${boardDetailDTO.boardId}">
    <input type="text" id="boardWriter" name="boardWriter" th:value="${boardDetailDTO.boardWriter}" placeholder="์ž‘์„ฑ์ž" readonly>
    <input type="text" id="boardPassword" name="boardPassword" placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ">
    <input type="text" id="boardTitle" name="boardTitle" th:value="${boardDetailDTO.boardTitle}" placeholder="์ œ๋ชฉ">
    <input type="text" id="boardContents" name="boardContents" th:value="${boardDetailDTO.boardContents}" placeholder="๋‚ด์šฉ">
    <!-- <input type="button" th:onclick="update()" value="์ˆ˜์ •"> -->
    <!-- ์œ„ Post๋ฐฉ์‹์œผ๋กœ ์ˆ˜์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. -->
    <input type="button" th:onclick="boardUpdate()" value="์ˆ˜์ •(put)">
    <input type="button" th:onclick="boardDelete()" value="์‚ญ์ œ(delete)"
</form>
</body>
</html>

์ด๋ ‡๊ฒŒ update.html ๋‚ด์šฉ์„ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
Post๋ฐฉ์‹์œผ๋กœ ์ˆ˜์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ Spring Framework ๋ถ€ํ„ฐ ๋งŽ์ด ์จ๋ดค๊ธฐ ๋•Œ๋ฌธ์— ์ƒ๋žตํ•˜์˜€์Šต๋‹ˆ๋‹ค.
๋‚ด์šฉ์€ ์ฃผ์„์œผ๋กœ ์ฒ˜๋ฆฌํ–ˆ์œผ๋‹ˆ ํ•„์š”ํ•˜์‹  ๋ถ„๋“ค์€ ์ฃผ์„์„ ํ•ด์ œํ•˜๊ณ  ์‚ฌ์šฉํ•ด๋ณด์‹œ๋ฉด ๋ ๊ฒ๋‹ˆ๋‹ค!

๋น„๋ฐ€๋ฒˆํ˜ธ ์ผ์น˜์—ฌ๋ถ€ ์ฒดํฌ๋Š” DTO์—์„œ ๊ฐ€์ ธ์˜จ ๊ฐ’ํ•˜๊ณ  input์— ์ž…๋ ฅํ•œ ๊ฐ’์„ ์„œ๋กœ ๋น„๊ตํ•ด,
์ผ์น˜ํ•˜๋ฉด form์˜ ๋‚ด์šฉ์ด jsonํƒ€์ž…์œผ๋กœ put๋ฉ”์„œ๋“œ์— ๊ฐ€๊ฒŒ๋” ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
Controller์—์„œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•„ ์ˆ˜์ •์ด ์ •์ƒ์ ์œผ๋กœ ์ˆ˜ํ–‰๋˜์—ˆ๋‹ค๋ฉด success์—
location.href="/board/"+id ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์ƒ์„ธ์กฐํšŒ ํŽ˜์ด์ง€๋กœ ๊ฐ€๊ฒŒ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿผ html์„ ์ „๋ถ€ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ Controller๋กœ ๊ฐ€๋ณด์‹ค๊นŒ์š”?? (เน‘หƒฬตแด—ห‚ฬต)ูˆ


Controller

// ์—…๋ฐ์ดํŠธ ํŽ˜์ด์ง€ ์š”์ฒญ
@GetMapping("/update/{boardId}")
public String updateForm(@PathVariable Long boardId, Model model) {
	BoardDetailDTO boardDetailDTO = bs.findById(boardId);
	model.addAttribute("boardDetailDTO",boardDetailDTO);
	return "board/update";
}

// ์ˆ˜์ • ๋‚ด์šฉ ์ €์žฅ
@PutMapping("/{boardId}")
	public ResponseEntity update2(@RequestBody BoardUpdateDTO boardUpdateDTO) {
	bs.update(boardUpdateDTO);
	return new ResponseEntity(HttpStatus.OK);
}

// ๊ฒŒ์‹œ๊ธ€ ์‚ญ์ œ
@DeleteMapping("/{boardId}")
public ResponseEntity deleteById2(@PathVariable Long boardId) {
	bs.deleteById(boardId);
	return  new ResponseEntity(HttpStatus.OK);
}

์—…๋ฐ์ดํŠธ ํŽ˜์ด์ง€ ์š”์ฒญ & ์—…๋ฐ์ดํŠธ ๋‚ด์šฉ ์ €์žฅ์— ๊ด€ํ•œ ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
์—…๋ฐ์ดํŠธ ํŽ˜์ด์ง€์— ๊ฐ€๊ธฐ ์ „ findById๋ฅผ ํ†ตํ•ด ์—…๋ฐ์ดํŠธ ํ•  ๋‚ด์šฉ์„ ๊ฐ€์ ธ๊ฐ€๊ณ ,
ajax์—์„œ Put ๋ฐฉ์‹์œผ๋กœ ์ˆ˜์ • ๋‚ด์šฉ์„ ์ „๋‹ฌ๋ฐ›์•„ ์ˆ˜์ • ์™„๋ฃŒ ์‹œ ์ƒํƒœ์ฝ”๋“œ 200์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค!!
ajax์—์„œ 200์ด๋ผ๋Š” ์ƒํƒœ์ฝ”๋“œ๋ฅผ ๋ฐ›๊ฒŒ ๋˜๋ฉด ์ž๋™์œผ๋กœ success์— ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
๋˜ํ•œ ajax์—์„œ json์œผ๋กœ ๋ณด๋‚ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ธฐ ์œ„ํ•ด์„  @RequestBody๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋Ÿผ Service๋กœ ๋„˜์–ด๊ฐ€์‹œ์ฃ  :D


Service

// ๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ •
@Override
public Long update(BoardUpdateDTO boardUpdateDTO) {
	BoardEntity boardEntity = BoardEntity.updateBoard(boardUpdateDTO);
	return br.save(boardEntity).getId();
}

// ๊ฒŒ์‹œ๊ธ€ ์‚ญ์ œ
@Override
public void deleteById(Long memberId) {
	br.deleteById(boardId);
}

ํšŒ์›๊ด€๋ฆฌ ์˜ˆ์ œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์—ฌ๊ธฐ์„œ๋„ Jpa์˜ save ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
์ƒˆ๋กœ์šด ๊ฐ’์ด ๋“ค์–ด์˜จ๋‹ค๋ฉด ์ž๋™์œผ๋กœ insert,
pk๊ฐ€ ์žˆ๋Š” ๊ฐ’์ด ๋“ค์–ด์˜จ๋‹ค๋ฉด ์ž๋™์œผ๋กœ update๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
ํŠน๋ณ„ํ•œ ๋‚ด์šฉ์€ ์—†์œผ๋ฏ€๋กœ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค :D


Paging

ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ์ž…๋‹ˆ๋‹ค.
์ด๋ฒˆ์—๋Š” Jpa๊ฐ€ ์ œ๊ณตํ•˜๋Š” Page ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•  ์˜ˆ์ •์ด๋ฉฐ,
๋งŽ์ด ์–ด๋ ค์šฐ๋‹ˆ ์ž˜ ๋”ฐ๋ผ์™€์ฃผ์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค :D
๊ทธ๋Ÿผ Page์šฉ DTO๋ถ€ํ„ฐ ์ƒ์„ฑํ•˜๋Ÿฌ ๊ฐ‘์‹œ๋‹ค ูฉ( แ› )ูˆ

Paging์šฉ DTO

@Data
@NoArgsConstructor
@AllArgsConstructor
public class BoardPagingDTO {

    private Long boardId;
    private String boardWriter;
    private String boardTitle;

}

PagingDTO์—์„œ๋Š” ๋ณ„ ๋‹ค๋ฅธ ๋‚ด์šฉ์ด ์—†์œผ๋ฏ€๋กœ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ณ  ๋„˜์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค.


PagingConst

  • common์ด๋ผ๋Š” ํŒจํ‚ค์ง€ ์ƒ์„ฑ, ํ›„ PagingConst Java ํด๋ž˜์Šค ์ƒ์„ฑ
public class PagingConst {

    public static final int PAGE_LIMIT = 5; // ํ•œ ํŽ˜์ด์ง€์— ๋ณด์—ฌ์ค„ ๊ธ€ ๊ฐฏ์ˆ˜
    public static final int BLOCK_LIMIT = 3; // ํ•œ ํ™”๋ฉด์— ํŽ˜์ด์ง€ ๊ฐฏ์ˆ˜

}

ํŽ˜์ด์ง•์ฒ˜๋ฆฌ๋ฅผ ๋„์™€์ฃผ๊ธฐ ์œ„ํ•ด PagingConst๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
๋ˆˆ์น˜๊ฐ€ ๋น ๋ฅด์‹  ๋ถ„๋“ค์ด๋ฉด ๊ฐ ํ•„๋“œ๊ฐ€ ๋ฌด์Šจ ์—ญํ• ์„ ํ•˜๋Š”์ง€ ์•„์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
Jpa์—์„œ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ๋ฅผ ๋„์™€์ฃผ์ง€๋งŒ ํ•œ ํŽ˜์ด์ง€์— ๋ณด์—ฌ์ค„ ๊ธ€ ๊ฐฏ์ˆ˜์™€ ํŽ˜์ด์ง€ ๊ฐฏ์ˆ˜๋Š” ์ง์ ‘ ์„ค์ •ํ•ด์ค˜์•ผ ํ•ด์„œ
์œ„์™€ ๊ฐ™์€ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์–ด๋ ค์šด ๋‚ด์šฉ ์•„๋‹ˆ๋‹ˆ ์ž˜ ๋”ฐ๋ผ์™€ ์ฃผ์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค :D
์ €๋Š” ํ•œ ํŽ˜์ด์ง€๋‹น ๊ธ€ ๊ฐฏ์ˆ˜ 5๊ฐœ, ํŽ˜์ด์ง€ ๊ฐฏ์ˆ˜๋Š” 3๊ฐœ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.


Controller

Controller์—์„œ๋ถ€ํ„ฐ ๋ณธ๊ฒฉ์ ์ธ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ ์ž‘์—…์ด ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.
์˜คํƒ€ ์•ˆ๋‚˜๊ฒŒ ์ž˜ ๋”ฐ๋ผ์™€์ฃผ์„ธ์š” :D
๊ทธ ์ „์— ์‚ผํ•ญ ์—ฐ์‚ฐ์ž์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ง€๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์‚ผํ•ญ ์—ฐ์‚ฐ์ž๋ž€?

int num = 10, num2 = 0;

if(num == 10) {
	num2 =5;
} else {
	num2 = 100;
}

// ์œ„ if ์™€ ์•„๋ž˜ ์ฝ”๋“œ๋Š” ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅ
num2 = (num==10)? 5: 100;

// ๊ธฐ๋ณธ์ ์ธ ๊ตฌ์กฐ
// ๋ณ€์ˆ˜ = (์กฐ๊ฑด์‹) ? true์ผ ๋•Œ ๊ฐ’ : false์ผ ๋•Œ ๊ฐ’
// ์กฐ๊ฑด์ด ๋งž์œผ๋ฉด true์ผ ๋•Œ ๊ฐ’์ด ๋ณ€์ˆ˜์— ๋‹ด๊น€, false์ผ ๋•Œ๋Š” ๋ฐ˜๋Œ€

// ๋‹จ์ˆœํ•œ if else์ผ ๋•Œ ์œ„์™€ ๊ฐ™์€ ์‚ผํ•ญ ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ ๊ฐ€๋Šฅ
// if else์— ๊ฐ™์€ ๋ณ€์ˆ˜๋กœ ๊ฐ’์— ์ฐจ์ด๋ฅผ ๋‘˜ ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉ

์ด๋ ‡๊ฒŒ ์‚ผํ•ญ ์—ฐ์‚ฐ์ž์— ๋Œ€ํ•ด ๊ฐ„๋žตํ•˜๊ฒŒ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.
if else์™€ ๋ณ„ ๋‹ค๋ฅธ ์ฐจ์ด์ ์€ ์—†์œผ๋‹ˆ ๋‹นํ™ฉํ•˜์ง€ ๋งˆ์‹œ๊ณ  ์ด๋Ÿฐ ์–‘์‹์˜ ์ฝ”๋“œ๊ฐ€ ๋‚˜์˜ค๋ฉด
์‚ผํ•ญ ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ–ˆ๊ตฌ๋‚˜ ๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
๊ทธ๋Ÿผ Controller๋กœ ๋‹ค์‹œ ๋„˜์–ด๊ฐ€๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

// ํŽ˜์ด์ง•์ฒ˜๋ฆฌ(board?page=5)
// 5๋ฒˆ๊ธ€(/board/5)
// @PageableDefault(page=1) : ๋ณ„๋‹ค๋ฅธ ํŽ˜์ด์ง• ์š”์ฒญ์ด ์—†์„ ๋•Œ (์ฒ˜์Œ ์š”์ฒญ) 1ํŽ˜์ด์ง€๋ฅผ ๋‚ด๋ณด๋‚ด๊ฒ ๋‹ค.
@GetMapping
public String paging(@PageableDefault(page = 1)Pageable pageable ,Model model) {
	Page<BoardPagingDTO> boardList = bs.paging(pageable);
	model.addAttribute("boardList", boardList);

	// startPage, endPage ๊ณ„์‚ฐ
	// ํ˜„์žฌ 2ํŽ˜์ด์ง€ ์ผ ๋•Œ ์‹œ์ž‘ ํŽ˜์ด์ง€ 1, ๋ ํŽ˜์ด์ง€ 3
	// ํ˜„์žฌ 7ํŽ˜์ด์ง€ ์ผ ๋•Œ ์‹œ์ž‘ ํŽ˜์ด์ง€ 6, ๋ ํŽ˜์ด์ง€ 8
	int startPage = (((int)(Math.ceil((double)pageable.getPageNumber()/PagingConst.BLOCK_LIMIT)))-1)*PagingConst.BLOCK_LIMIT+1;
	int endPage = (startPage+PagingConst.BLOCK_LIMIT-1)<boardList.getTotalPages())?startPage+PagingConst.BLOCK_LIMIT-1 : boardList.getTotalPages();

	model.addAttribute("startPage", startPage);
	model.addAttribute("endPage", endPage);

	return "board/paging";
}

๊ฐ‘์ž๊ธฐ ์–ด๋ ค์šด ๋‚ด์šฉ์ด ๋‚˜์™”์Šต๋‹ˆ๋‹ค๋งŒ ์ผ๋‹จ Service๊นŒ์ง€ ์„ค๋ช… ํ•˜๊ณ ,
Test ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์ž์„ธํžˆ ์•Œ๋ ค๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.
startPage, endPage๋Š” Spring Framework์—์„œ๋„ ์ผ๋˜ ๋‚ด์šฉ์ด๋ฏ€๋กœ ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


Service

@Override
public Page<BoardPagingDTO> paging(Pageable pageable) {
	int page = pageable.getPageNumber(); // pageable ๊ฐ์ฒด์—์„œ .get์„ ํ†ตํ•ด page ์ •๋ณด๋ฅผ ๊บผ๋‚ผ ์ˆ˜ ์žˆ์Œ.
	// JPA๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ํŽ˜์ด์ง€ ๊ฐ์ฒด๋Š” 0๋ฒˆ๋ถ€ํ„ฐ ๊ด€๋ฆฌ
	// ๊ทธ๋ž˜์„œ ์‚ผํ•ญ ์—ฐ์‚ฐ์ž๊ฐ€ ํ•„์š”

	// ์š”์ฒญํ•œ ํŽ˜์ด์ง€๊ฐ€ 1์ด๋ฉด ํŽ˜์ด์ง€ ๊ฐ’์„ 0์œผ๋กœ ํ•˜๊ณ , 1์ด ์•„๋‹ˆ๋ฉด ์š”์ฒญ ํŽ˜์ด์ง€์—์„œ 1์„ ๋บ€๋‹ค.
	// page = page-1; // ์ด๋Ÿฐ์‹์œผ๋กœ ์จ๋„ ๋˜์ง€๋งŒ ์‚ผํ•ญ์—ฐ์‚ฐ์ž๋ฅผ ์ด์šฉํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
	page = (page==1)? 0:(page-1);
	// Pageable ํƒ€์ž…์˜ pagealbe ๊ฐ์ฒด๋ฅผ ๋„˜๊ฒจ์ฃผ๋Š” findAll() ํ˜ธ์ถœ
	Page<BoardEntity> boardEntities = br.findAll(PageRequest.of(page, PagingConst.PAGE_LIMIT, Sort.by(Sort.Direction.DESC, "id")));
	// findAll(PageRequest.of(์–ด๋–ค ํŽ˜์ด์ง€๋ฅผ, ์–ด๋–ค ๊ฐฏ์ˆ˜๋กœ), ์ •๋ ฌ (์–ด๋–ค ์ •๋ ฌ ,"๊ธฐ์ค€์œผ๋กœ ์‚ผ์„ Entity ํด๋ž˜์Šค์˜ ํ•„๋“œ์ด๋ฆ„"))
	// ๊ธฐ์ค€์œผ๋กœ ์‚ผ์„ Entity ํด๋ž˜์Šค์˜ ํ•„๋“œ์ด๋ฆ„์— ์–ธ๋”๋ฐ”( _ ) ๊ฐ€ ํฌํ•จ๋˜์–ด์žˆ์œผ๋ฉด JPA ๊ฐ€ ์ธ์‹์„ ๋ชปํ•ด ์˜ค๋ฅ˜ ๋ฐœ์ƒ
	// findAll์˜ ๋ฆฌํ„ด๊ฐ’์€ Page<Entityํƒ€์ž…>๋กœ ๋„˜์–ด์˜ด. ๋”ฐ๋ผ์„œ DTO๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ์ž‘์—…์ด ํ•„์š”ํ•จ.
	// Page<BoardEntity> -> Page<BoardPagingDTO>
	// ๊ธฐ์กด DTO์—์„œ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ์ž‘์—…์„ ํ•  ํ•„์š” ์—†์ด Page๊ฐ์ฒด๊ฐ€ ์ œ๊ณตํ•ด์ฃผ๋Š ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋ณ€ํ™˜์‹œ์ผœ์คŒ
	// ์•„๋ž˜์™€ ๊ฐ™์€ ๋ณ€ํ™˜์šฉ ์ฝ”๋“œ๋กœ ์‚ฌ์šฉ
	Page<BoardPagingDTO> boardList = boardEntities.map(
		board -> new BoardPagingDTO(board.getId(),
					    board.getBoardWriter(),
					    board.getBoardTitle())
					    );
	return boardList;
}

jpa๋Š” ํŽ˜์ด์ง€๋ฅผ 0๋ถ€ํ„ฐ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— 1ํŽ˜์ด์ง€๋ฅผ ์š”์ฒญํ•˜๋ ค๋ฉด 0์„, 4ํŽ˜์ด์ง€๋ฅผ ์š”์ฒญํ•˜๋ ค๋ฉด 3์„ ์š”์ฒญํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
๋˜ํ•œ Page<Entity> ๊ฐ์ฒด๋กœ ๋„˜์–ด์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ์กด ๋ฐฉ์‹์œผ๋กœ DTO๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด Jpa์˜ Page ๊ด€๋ จ ๋ฉ”์„œ๋“œ๋“ค์„ ์‚ฌ์šฉ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์ด .map()์œผ๋กœ ๋ณ€ํ™˜ ์‹œ์ผœ์ฃผ๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์ž์„ธํ•œ ๋‚ด์šฉ์€ Test์ฝ”๋“œ์—์„œ ํ™•์ธํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


Test

@Test
@Transactional
@DisplayName("ํŽ˜์ด์ง• ํ…Œ์ŠคํŠธ")
public void pagingTest() {

	// ๊ธ€๊ฐฏ์ˆ˜ 30๊ฐœ ๊ธฐ์ค€ ํ•œํŽ˜์ด์ง€ 5๊ฐœ ๊ธฐ์ค€ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ
	// int page = 0; // JPA ๊ธฐ์ค€ 1ํŽ˜์ด์ง€ (์ฒซ๋ฒˆ์งธ ํŽ˜์ด์ง€)
	int page = 5; // JPA ๊ธฐ์ค€ 6ํŽ˜์ด์ง€ (๋งˆ์ง€๋ง‰ ํŽ˜์ด์ง€)
	Page<BoardEntity> boardEntities = br.findAll(PageRequest.of(page, PagingConst.PAGE_LIMIT, Sort.by(Sort.Direction.DESC, "id")));

	// Page ๊ฐ์ฒด๊ฐ€ ์ œ๊ณตํ•ด์ฃผ๋Š” ๋งค์„œ๋“œ ํ™•์ธ
	System.out.println("boardEntities.getContent() = " + boardEntities.getContent()); // ์š”์ฒญ ํŽ˜์ด์ง€์— ๋“ค์–ด์žˆ๋Š” ๋ฐ์ดํ„ฐ, toString์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์†Œ๊ฐ’์ด ์ถœ๋ ฅ
	System.out.println("boardEntities.getTotalElements() = " + boardEntities.getTotalElements()); // ์ „์ฒด ๊ธ€ ๊ฐฏ์ˆ˜
	System.out.println("boardEntities.getNumber() = " + boardEntities.getNumber()); // JPA ๊ธฐ์ค€ ์š”์ฒญ ํŽ˜์ด์ง€
	System.out.println("boardEntities.getTotalPages() = " + boardEntities.getTotalPages()); // ์ „์ฒด ํŽ˜์ด์ง€ ๊ฐฏ์ˆ˜
	System.out.println("boardEntities.getSize() = " + boardEntities.getSize()); // ํ•œ ํŽ˜์ด์ง€์— ๋ณด์—ฌ์ง€๋Š” ๊ธ€ ๊ฐฏ์ˆ˜
	System.out.println("boardEntities.hasPrevious() = " + boardEntities.hasPrevious()); // ์ด์ „ ํŽ˜์ด์ง€ ์กด์žฌ ์—ฌ๋ถ€
	System.out.println("boardEntities.isFirst() = " + boardEntities.isFirst()); // ์ฒซ ํŽ˜์ด์ง€์ธ์ง€ ์—ฌ๋ถ€
	System.out.println("boardEntities.isLast() = " + boardEntities.isLast()); // ๋งˆ์ง€๋ง‰ ํŽ˜์ด์ง€์ธ์ง€ ์—ฌ๋ถ€

	// Page<BoardEntity> -> Page<BoardPagingDTO>
	// ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด ํŽ˜์ด์ง•๊ฐ์ฒด๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉ ๋ชปํ•จ.
	// .map() : Entity ๊ฐ€ ๋‹ด๊ธด ํŽ˜์ด์ง€ ๊ฐ์ฒด๋ฅผ dto ๊ฐ€ ๋‹ด๊ธด ํŽ˜์ด์ง€ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ์—ญํ• 
	// board : ๋ฐ˜๋ณต์šฉ ๋ณ€์ˆ˜๋กœ ๋งŒ๋“  ๋ณ€์ˆ˜, foreach ๋ฌธ์˜ ๋ฐ˜๋ณต ๋ณ€์ˆ˜์™€ ๋™์ผํ•œ ๊ฐœ๋…
	// ์—ฌ๊ธฐ์„œ์˜ ๋ฐ˜๋ณต๋ณ€์ˆ˜(board)๋Š” Entity ๊ฐ์ฒด์ž„. Entity์— ๋‹ด๊ธด ์ •๋ณด๋ฅผ BoardPagingDTO์— ๋‹ด์Œ.
	Page<BoardPagingDTO> boardList = boardEntities.map(
		board -> new BoardPagingDTO(board.getId(),
					    board.getBoardWriter(),
					    board.getBoardTitle())
					    );

	// ์ œ๋Œ€๋กœ ๋ณ€ํ™˜์ด ๋˜์—ˆ๋‹ค๋ฉด Page<BoardPagingDTO>์—์„œ๋„ Jpa์˜ Page ๊ด€๋ จ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
	System.out.println("boardList.getContent() = " + boardList.getContent());
	System.out.println("boardList.getTotalElements() = " + boardList.getTotalElements());
	System.out.println("boardList.getNumber() = " + boardList.getNumber());
	System.out.println("boardList.getTotalPages() = " + boardList.getTotalPages());
	System.out.println("boardList.getSize() = " + boardList.getSize());
	System.out.println("boardList.hasPrevious() = " + boardList.hasPrevious());
	System.out.println("boardList.isFirst() = " + boardList.isFirst());
	System.out.println("boardList.isLast() = " + boardList.isLast());
}

์‹ค์ œ๋กœ ์‹คํ–‰ํ•ด๋ณด์‹œ๋ฉด (๋‹จ, DB์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์žˆ์„ ๋•Œ)
๊ฐ’์ด ์ „๋ถ€ ์ž˜ ๋‚˜์˜ค๋Š”๊ฑฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด ๊ฐ์ข… ๋ฉ”์„œ๋“œ๋“ค์€ ํƒ€์ž„๋ฆฌํ”„๋ฅผ ํ†ตํ•ด paging.html์—์„œ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ด๋ฏ€๋กœ ๊ฐ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฌด์Šจ ์—ญํ• ์„ ํ•˜๋Š”์ง€
์ž˜ ์•Œ์•„๋‘์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ html์„ ๋งŒ๋“ค๋Ÿฌ ๊ฐ€๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!! :D


paging.html

  • paging.html์„ ๋งŒ๋“  ๋’ค ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•
    ํ˜„์žฌ๋Š” ๋”ฑํžˆ ๋งํฌ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ์ฃผ์†Œ์ฐฝ localhost๋’ค์— /board ๋ฅผ ๋ถ™์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค.
    ex) localhost:8097/board
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<body class="container">

<h2>ํšŒ์›์ œ ๊ฒŒ์‹œํŒ</h2>

<table class="table table-hover">
  <thead>
  <tr>
    <th scope="col">๊ธ€ ๋ฒˆํ˜ธ</th>
    <th scope="col">๊ธ€์“ด์ด</th>
    <th scope="col">๊ธ€ ์ œ๋ชฉ</th>
  </tr>
  </thead>
  <tbody>
  <tr th:each="board: ${boardList}">
    <td th:text="${board.boardId}"></td>
    <td th:text="${board.boardWriter}"></td>
    <td><a th:href="@{|/board/${board.boardId}|}" th:text="${board.boardTitle}">์ œ๋ชฉ</a></td>
  </tr>
  </tbody>
</table>

<!--
  /board
  ๋ธŒ๋ผ์šฐ์ € ์ฃผ์†Œ์ฐฝ์— ๋ณด์ด๋Š” ์ฃผ์†Œ๊ฐ’ : /board?page=1
  html์—์„œ ํƒ€์ž„๋ฆฌํ”„๋กœ ์ž‘์„ฑํ•˜๋Š” ์ฃผ์†Œ๊ฐ’ : /board(page=1)

  /board/*
  /board/?page=1
  /board/(page=1)
-->

<div class="container">
  <ul class="pagination">
    <li class="page-item">
      <!-- ์ฒซํŽ˜์ด์ง€๋กœ ๊ฐ€๋Š” ๋งํฌ -->
      <a class="page-link" th:href="@{/board(page=1)}">
        <span>First</span>
      </a>
    </li>

    <li th:class="${boardList.first} ? 'disabled'" class="page-item">
      <!-- ์œ„ ์ฝ”๋“œ ํ•ด์„ : ์ฒซ๋ฒˆ์งธ ํŽ˜์ด์ง€๋ผ๋ฉด 'disabled'๋ผ๋Š” ์†์„ฑ ๋ถ€์—ฌ -->

      <!-- boardList.first : isFirst() ์ฒซ๋ฒˆ์งธ ํŽ˜์ด์ง€์ธ์ง€ ์กฐ๊ฑด์„ ๊ฒ€ -->
      <!-- # : ์•„๋ฌด ์š”์ฒญ์„ ์•ˆ๋ณด๋ƒ„ -->
      <!-- boardList.number() : getNumber ์ด์ „ ํŽ˜์ด์ง€ ์š”์ฒญ -->
      <a class="page-link" th:href="${boardList.first} ? '#' : @{/board(page=${boardList.number})}">  <!-- Page๊ฐ์ฒด๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ด๋ฆ„์ด ์‚ด์ง ๋ณ€๊ฒฝ๋จ. -->
        <!-- ํŽ˜์ด์ง€๊ฐ€ ์ฒซ๋ฒˆ์งธ ํŽ˜์ด์ง€๋ผ๋ฉด < ์„ ๋ˆŒ๋ €์„ ๋•Œ ์ปจํŠธ๋กค๋Ÿฌ์— ์š”์ฒญ์„ ์•ˆํ•จ -->
        <!-- ์ฒซํŽ˜์ด์ง€๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ์ด์ „ ํŽ˜์ด์ง€๋ฅผ ์š”์ฒญ -->
        <span>&lt;</span> <!-- <๋ฅผ ๊ทธ๋ƒฅ ์“ฐ๋ฉด html์—์„œ ์ฒ˜๋ฆฌ๋ฅผ ๋ชปํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— html ํŠน์ˆ˜๊ธฐํ˜ธ ํ‘œ์— ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉ -->
      </a>
    </li>

    <!-- startPage ~ endPage ๊นŒ์ง€ ์ˆซ์ž๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์—ญํ•  -->
    <!--
      th:each="page: ${#numbers.sequence(startPage, endPage)}
      th:each -> ๋ฐ˜๋ณต
      page -> ๋ฐ˜๋ณต ๋ณ€์ˆ˜
      ${#number.sequence(์‹œ์ž‘๊ฐ’, ๋๊ฐ’)} -> ์–ผ๋งˆ๋‚˜ ๋ฐ˜๋ณต

    -->
    <li th:each="page: ${#numbers.sequence(startPage, endPage)}"
        th:class="${page == boardList.number + 1} ? 'page-item active'">
      <a class="page-link" th:text="${page}" th:href="@{/board(page=${page})}"></a>
      <!-- ํ•ด๋‹น ์ˆซ์ž๊ฐ’์œผ๋กœ ์ปจํŠธ๋กค๋Ÿฌ์— ์š”์ฒญ -->
    </li>

    <!--
      ๋‹ค์Œ ํŽ˜์ด์ง€ ์š”์ฒญ
      ํ˜„์žฌ 3ํŽ˜์ด์ง€๋ฅผ ๋ณด๊ณ  ์žˆ๋‹ค๋ฉด ๋‹ค์Œ ํŽ˜์ด์ง€๋Š” 4ํŽ˜์ด์ง€์ž„.
      getNumber()๊ฐ’์€ 2์ž„.
      ๋”ฐ๋ผ์„œ 4ํŽ˜์ด์ง€๋ฅผ ๋ณด๊ณ ์‹ถ๋‹ค๋ฉด getNumber()+2๋ฅผ ํ•ด์•ผ ์ปจํŠธ๋กค๋Ÿฌ์— 4๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค.
     -->
    <li th:class="${boardList.last} ? 'disabled'">
      <a class="page-link" th:href="${boardList.last} ? '#' : @{/board(page=${boardList.number + 2})}">
        <span>&gt;</span>
      </a>
    </li>

    <li class="page-item">
      <a class="page-link" th:href="@{/board(page=${boardList.totalPages})}">
        <span>Last</span>
      </a>
    </li>
  </ul>
</div>

</body>
</html>

ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ ์ฝ”๋“œ๊ฐ€ ๋งŽ์ด ๋ณต์žกํ•˜๊ธด ํ•˜์ง€๋งŒ ์–ด์ฉ” ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ลฬฅฬฅฬฅฬฅืลฬฅฬฅฬฅฬฅ
์„ค๋ช…์ด ํ•„์š”ํ•˜๋‹ค ์ƒ๊ฐ๋˜๋Š”๊ฒƒ์€ ์ฃผ์„์œผ๋กœ ๋‹ฌ์•„๋†“์•˜์œผ๋‹ˆ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”!
์ž˜ ๋”ฐ๋ผ์˜ค์…จ๋‹ค๋ฉด ํŽ˜์ด์ง•์ฒ˜๋ฆฌ๊นŒ์ง€ ๋ฌธ์ œ์—†์ด ์ž˜ ๋˜์…จ์„๊ฒ๋‹ˆ๋‹ค :D


๋งˆ์น˜๋ฉฐ

์ด๋ ‡๊ฒŒ ๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ • & ์‚ญ์ œ & ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ๊นŒ์ง€ ์ „๋ถ€ ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.
ํšŒ์›๊ด€๋ฆฌ ์—์ œ์™€ ๋น„์Šทํ•œ ์ ์ด ๋งŽ์•„ ์„ค๋ช…์„ ๋งŽ์ด ์ƒ๋žตํ•˜๊ธด ํ–ˆ์œผ๋‚˜ ๋‹ค๋“ค ์ž˜ ๋”ฐ๋ผ์˜ค์…จ์„๊ฑฐ๋ผ ๋ฏฟ์Šต๋‹ˆ๋‹ค :D
๋‹ค์Œ์— ๋” ์ถ”๊ฐ€๋˜๋Š” ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ๊ทธ๋•Œ ๋‹ค์‹œ ๋งŒ๋‚˜๋Š”๊ฑธ๋กœ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!!
๋‹ค๋“ค ๋‹ค์Œ์— ๊ฑด๊ฐ•ํ•œ ๋ชจ์Šต์œผ๋กœ ๋งŒ๋‚˜์š”~ ( แƒฆ'แด—'แƒฆ )

profile
์ฆ๊ฒ๋‹ค!

0๊ฐœ์˜ ๋Œ“๊ธ€