1.  컨트롤러와 화면 처리

프로젝트 controller 패키지에 BoardController 구성

 

    🚀  가장 우선적으로 구현해야 하는 기능은 목록 기능이므로 list() 메서드를 추가하고 PageRequestDTO를 이용해서 페이징 처리와 검색에 이용

@Controller
@RequestMapping("/board")
@Log4j2
@RequiredArgsConstructor
public class BoardController {
    private final BoardService boardService;

    @GetMapping("/list")
    public void list(PageRequestDTO pageRequestDTO, Model model) { }
}

 


1)  화면 구성을 위한 준비

화면 구성은 Thymeleaf를 이용해서 레이아웃을 적용할 수 있도록 준비. build.gradle 파일에 Thymeleaf의 레이아웃 관련 라이브러리의 존재 여부를 먼저 확인

implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '3.1.0'

 


2)  템플릿 디자인 적용

Thymeleaf에는 레이아웃 기능을 적용할 수 있어서 본격적인 디자인을 적용. 부트스트랩의 여러 무료 디자인 템플릿 중 'Simple Sidebar'를 이용

https://startbootstrap.com/template/simple-sidebar

 

Start Bootstrap

 

startbootstrap.com

 

  • 무료로 이용할 수 있으므로 내려받은 후에 압축을 해제하고 모든 파일을 resources 의 static 폴더에 복사
  • 프로젝트를 실행하고 브라우저에 'http://localhost:8080/'를 실행했을 때 index.html 파일이 실행되는지 확인
  • 적용한 템플릿이 반응형이라 브라우저의 크기에 따라 다르게 보임
Thymeleaf 레이아웃으로 변경하기

 

    ✓  레이아웃을 적용하려면 layout 폴더에 basic.html을 추가하고 index.html 내용을 그대로 복사해서 추가

 

레이아웃 적용

 

    ✓  basic.html의 상단에는 Thymeleaf의 네임스페이스들을 추가

<html lang="en"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      xmlns:th="http://www.thymeleaf.org">

 

    ✓  <head> 태그에는 링크가 존재하므로 이를 Thymeleaf 스타일로 변경. 경로를 '@{/..}'와 같은 형태로 처리. ('/'로 경로가 시작하는 것을 주의)

    <link rel="icon" type="image/x-icon" th:href="@{/assets/favicon.ico}" />
    <!-- Core theme CSS (includes Bootstrap)-->
    <link th:href="@{/css/styles.css}" rel="stylesheet" />
</head>

 

    ✓  'Page content' 부분에 layout:fragment를 적용

<!-- Page content-->
<div class="container-fluid" layout:fragment="content">

 

    ✓  파일의 마지막 부분에 자바 스크립트를 위한 <th:block>을 추가하고 링크를 수정

<script th:src="@{/js/scripts.js}"></script>
    <th:block layout:fragment="script">
    
    </th:block>

 

컨트롤러를 통한 확인

 

    ✓  BoardController에는 list() 기능을 작성해 두었으므로 이를 활용해서 레이아웃까지 적용된 화면을 구성
    ✓  templates 폴더에 board 폴더를 생성하고 list.html을 추가

    ✓  list.html은 레이아웃 적용여부를 확인할 수 있도록 작성.

<html lang="en"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      xmlns:th="http://www.thymeleaf.org"
      layout:decorate="~{layout/basic.html}">

<div layout:fragment="content">
    <h1>Board List</h1>
</div>

<script layout:fragment="script" th:inline="javascript">
    console.log('script...');
</script>

   


2.  목록 화면 개발

1)  BoardController

  🚀  BoardController의 list()에 화면에 목록 데이터를 출력하는 코드 작성

  🚀  list()가 실행되면 PageRequestDTO와 PageResponseDTO 객체가 화면으로 전달

@GetMapping("/list")
public void list(PageRequestDTO pageRequestDTO, Model model) {
    PageResponseDTO<BoardDTO> responseDTO = boardService.list(pageRequestDTO);

    log.info(responseDTO);

    model.addAttribute("responseDTO", responseDTO);
}

 


2)  list.html 수정

  🚀  화면 상단에는 여러개의 <div>를 이용해서 화면 구성을 처리

  🚀  실제 내용물의 출력은 <tbody>를 이용하고 Thymeleaf의 반복문을 이용해서 처리

<div layout:fragment="content">
    <div clsss="col">
        <div class="card">
            <div class="card-header">
                Board List
            </div>
            <div class="card-body">
                <h5 class="card-title">Board List</h5>

                <table class="table">
                    <thead>
                    <tr>
                        <th scope="col">Bno</th>
                        <th scope="col">Title</th>
                        <th scope="col">Writer</th>
                        <th scope="col">RegDate</th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr th:each="dto:${responseDTO.list}">
                        <th scope="col">[[${dto.bno}]]</th>
                        <td>[[${dto.title}]]</td>
                        <td>[[${dto.writer}]]</td>
                        <td>[[${dto.regDate}]]</td>
                    </tr>
                    </tbody>
                </table>
            </div><!-- end card body -->
        </div><!-- end card -->
    </div><!-- end col -->
</div><!-- end row -->

 


3)  날짜 포멧팅 처리

  🚀  화면의 등록일 regDate 이 너무 길고 상세하게 나오는 것을 날짜만 나오도록 처리
  🚀  Thymeleaf의 #temporals라는 유틸리티 객체를 이용해서 처리

<td>[[${#temporals.format(dto.regDate, 'yyyy-MM-dd')}]]</td>


4)  페이지 목록의 출력

  🚀  <table> 태그가 끝나는 부분과 이어지게 <div>를 구성해서 페이지 번호들을 화면에 출력
  🚀  PageResponseDTO는 시작 번호 start와 끝 번호 end 만을 가지고 있으므로 특정 범위의 숫자를 만들기 위해서 Thymeleaf의 numbers를 이용

<div class="float-end">
    <ul class="pagination flex-wrap">
        <li class="page-item" th:if="${responseDTO.prev}">
            <a class="page-link" th:data-num="${responseDTO.start -1}">Previous</a>
        </li>
        <th:block th:each="i: ${#numbers.sequence(responseDTO.start,responseDTO.end)}">
            <li th:class="${responseDTO.page == i} ? 'page-item active' : 'page-item'">
                <a class="page-link" th:data-num="${i}">[[${i}]]</a>
            </li>
        </th:block>
        <li class="page-item" th:if="${responseDTO.next}">
            <a class="page-link" th:data-num="${responseDTO.end + 1}">Next</a>
        </li>
    </ul>
</div>

 

  📍  코드에서 중요한 부분은 #numbers.sequence()로 특정한 범위의 연속된 숫자를 만드는 부분과 <a> 태그에 'data-num'이라는 속성으로 페이지 처리하는 부분

  📍  브라우저에서 직접 URL을 변경하는 방법으로 '/board/list?page=2'와 같이 페이지 번호를 쿼리 스트링으로 추가해서 페이지 변경되는 것을 확인

 


5) 검색 화면 추가

  🚀  list.html 페이지에는 검색이 이루어질수 있도록 <table> 위에 별도의 card 영역을 만들어 검색 조건을 선택할 수 있도록 구성.

  🚀  검색 조건은 페이지 이동과 함께 처리될 수 있도록 <form> 태그로 감싸서 처리

<div class="row mt-3">
    <form action="/board/list" method="get">
        <div class="col">
            <input type="hidden" name="size" th:value="${pageRequestDTO.size}">
            <div class="input-group">
                <div class="input-group-prepend">
                    <select class="form-select" name="type">
                        <option value="">---</option>
                        <option value="t" th:selected="${pageRequestDTO.type =='t'}">제목</option>
                        <option value="c" th:selected="${pageRequestDTO.type =='c'}">내용</option>
                        <option value="w" th:selected="${pageRequestDTO.type =='w'}">작성자</option>
                        <option value="tc" th:selected="${pageRequestDTO.type =='tc'}">제목 내용</option>
                        <option value="tcw" th:selected="${pageRequestDTO.type =='tcw'}">제목 내용 작성자</option>
                    </select>
                </div>
                <input type="text" class="form-control" name="keyword" th:value="${pageRequestDTO.keyword}">
                <div class="input-group-append">
                    <button class="btn btn-outline-secondary searchBtn" type="submit">Search</button>
                    <button class="btn btn-outline-secondary clearBtn" type="button">Clear</button>
                </div>
            </div>
        </div>
    </form>
</div>

 

  📍  브라우저에 /board/list 뒤에 type과 keyword를 지정하면 화면에서 검색 항목과 키워드 부분으로 처리 되는것 확인

 


6)  이벤트 처리

  🚀  페이지 번호를 클릭하거나 검색/필터링 조건을 눌렀을 때 이벤트 처리를 추가

  • 페이지 번호를 클릭하면 검색 창에 있는 <form> 태그에 <input type='hidden'>으로 page를 추가한 후에 submit
  • Clear 버튼을 누르면 검색 조건 없이 /board/list 호출

  🚀  JSP에서 자바스크립트의 문자열을 템플릿으로 적용하고자 EL과 구분하기 위해 \${} 와 같은 방식을 사용했지만, Thymeleaf는 \ 없이 적용한다는 점을 제외하면 기존 코드를 그대로 사용

list.html의 마지막 부분에 <script> 영역을 작업
<script layout:fragment="script" th:inline="javascript">
    document.querySelector(".pagination").addEventListener("click", function(e) {
        e.preventDefault(); // a 태그일 경우 기본 이벤트 제거
        e.stopPropagation();

        const target = e.target;

        if(target.tagName !== 'A') { // a 태그가 아니면 종료
            return;
        }

        const num = target.getAttribute("data-num"); // 클릭한 a 태그의 data-num 속성(이동해야할 페이지번호)를 가져옴.

        const formObj = document.querySelector("form");

        formObj.innerHTML += `<input type='hidden' name='page' value='${num}'>`; // form 태그에 page 태그 추가

        formObj.submit();

    }, false)

    document.querySelector('.clearBtn').addEventListener("click", function(e){
        e.preventDefault();
        e.stopPropagation();

        self.location = '/board/list';

    }, false)
</script>

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  Thymeleaf

스프링과 마찬가지로 스프링 부트도 다양한 뷰 view 관련 기술을 적용할 수 있음. 스프링은 대부분 JSP를 위주로 개발하는 경우가 많지만 스프링 부트는 Thymeleaf라는 템플릿 엔진을 주로 이용.

Thymeleaf는 '템플릿'이기 때문에 JSP 처럼 직접 데이터를 생성하지 않고, 만들어진 결과에 데이터를 맞춰서 보여주는 방식으로 구현.
JSP와 마찬가지로 서버에서 동작하기는 하지만 Thymleaf는 HTML을 기반으로 화면을 구성하기 때문에 HTML에 조금 더 가까운 방식으로 작성


2.   Thymeleaf 기초 문법

Thymeleaf는 JSP를 대신하는 목적으로 작성된 라이브러리이므로, JSP에서 필요한 기능들을 Thymeleaf로 구성


1) 인텔리제이 설정

앞에서 작성한 hello.html을 열기. Thymeleaf를 이용하기 위해서 가장 중요한 설정은 네임스페이스 xmlns에 Thymeleaf를 지정. 네임스페이스를 지정하면 'th:'와 같은 Thymeleaf의 모든 기능을 사용할 수 있음.

작성된 hello.html은 다음과 같이 'th:'로 시작하는 기능을 사용할 수 있지만 Model에 담긴 데이터를 사용할 때는 '해당 변수를 찾을 수 없다'는 방식으로 에러가 날 수 있음.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 th:text="${msg}"></h1>
</body>
</html>


  📍  만일 에러가 발생하는 경우에는 인텔리제이의 설정을 조금 변경해서 에러 없는 화면을 보는 것이 더 좋기 때문에 Setting 메뉴에서 Thymeleaf를 검색하고 Unresolved references .. 를 체크 해제 해 주도록 함

    ➡️  설정을 변경하고 기존에 열려있는 hello.html 에디터를 종료한 후에 다시 에디터로 보면 변수에 대한 검사를 하지 않는 것을 확인


2)  Thymleaf 출력

Model로 전달된 데이터를 출력하기 위해서 HTML 태그 내에 'th:'로 시작하는 속성을 이용하거나 inlining을 이용
SampleController에 ex1()을 추가해서 '/ex/ex1'이라는 경로를 호출할 때 동작하도록 구성

 @GetMapping("/ex/ex1")
    public void ex1(Model model) {
        List<String> list = Arrays.asList("AAA", "BBB", "CCC", "DDD");

        model.addAttribute("list", list);
    }

 

ex1()의 결과 화면은 templates 내에 ex 디렉토리를 생성하고 ex1.html을 추가
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!-- inlining 사용 -->
<h4>[[${list}]]</h4>
<hr>
<!-- 'th:' 속성 사용 -->
<h4 th:text="${list}"></h4>
</body>
</html>


 

3)  Thymeleaf 주석 처리

Thymeleaf가 작성하는 단계에서는 단순해 보이지만 디버깅할 때는 상황이 다름. 에러가 발생하게 되면 에러의 원인을 찾아내기 힘들다.
에러가 난 부분을 찾기 위해서는 주석 처리를 해야할 때는 '<!--/* ... */-->'를 이용하는 것이 좋음. 주석은 Thymeleaf가 파싱 처리할 때 삭제되어 처리되기 때문에 1) 잘못된 문법 에 대한 체크도 건너 뛸 수 있고, 삭제된 상태에서 처리되므로 2)브라우저에서는 아예 해당 부분은 결과 자체가 없음

 

hello.html 수정

 


 

4)  th:with를 이용한 변수 선언

Thymeleaf를 이용하는 과정에서 임시로 변수를 선언해야 하는 상황에서는 'th:with'를 이용해서 간단히 처리 가능
'th:with'로 만드는 변수를 '변수명 = 값'의 형태로 ', '를 이용해서 여러 개를 선언 할 수도 있음

    <div th:with="num1 = ${10}, num2 = ${20}">
        <h4 th:text="${num1 + num2}"></h4>
    </div>

 


3.  반복문과 제어문 처리

화면 구성에서 가장 많이 사용되는 반복문과 제어문 처리.
SampleController의 ex1()에서는 Model을 이용해 'List<String>'을 담고 ex1.html을 이용해서 출력하도록 구성.

 @GetMapping("/ex/ex1")
    public void ex1(Model model) {
        List<String> list = Arrays.asList("AAA", "BBB", "CCC", "DDD");

        model.addAttribute("list", list);
    }

 

반복문 처리는 크게 2가지 방법을 이용


    -  반복이 필요한 태그에 'th:each'를 적용하는 방법
    -  <th:block>이라는 별도의 태그를 이용하는 방법


    💡  'th:each' 속성을 이용할 때는 기존의 HTML을 그대로 둔 상태에서 반복 처리를 할 수 있다는 장점이 있지만 JSTL 과는 조금 이질적인 형태이고, <th:block>을 이용할 때는 추가로 태그가 들어가는 단점이 있음.

 

ex1.html의 내용을 변경
<ul>
    <li th:each="str: ${list}" th:text="${str}"></li>
</ul>

<ul>
    <th:block th:each="str: ${list}">
        <li>[[${str}]]</li>
    </th:block>
</ul>


 

1)  반복문의 status 변수

Thymeleaf는 th:each를 처리할 때 현재 반복문의 내부 상태에 변수를 추가해서 사용할 수 있음.
일명 status 변수라고 하는데 index / count / size / first / last / odd / even 등을 이용해서 자주 사용하는 값들을 출력할 수 있음

<ul>
    <li th:each="str, status: ${list}">
        [[${status.index}]] -- [[${str}]]
    </li>
</ul>

 

  ✓  status 변수명은 사용자가 지정할 수 있고, index는 0부터 시작하는 번호를 의미. count는 1부터 시작


 

2)  th:if / th:unless / th:switch

Thymeleaf는 제어문의 형태로 th:if / th:unless / th:switch를 이용할 수 있음

th:if / th:unless는 별도의 속성으로 사용할 수 있으므로 if ~ else 와는 다르게 사용
예를 들어 반복문의 홀수 / 짝수를 구분해서 처리하고 싶다면 다음과 작성

<ul>
    <li th:each="str, status: ${list}">
        <span th:if="${status.odd}">ODD -- [[${str}]]</span>
        <span th:unless="${status.odd}">EVEN -- [[${str}]]</span>
    </li>
</ul>


 

📍  ?를 이용하면 앞선 방식보다는 좀 더 편하게 이항 혹은 삼항 처리가 가능
      예를 들어 반복 중에 홀수 번째만 무언가를 보여주고 싶다면 다음과 같이 ? 뒤에 하나만 표현식을 사용할 수 있음

<ul>
    <li th:each="str, status: ${list}">
        <span th:text="${status.odd} ? 'ODD ---' + ${str}"></span>
    </li>
</ul>


 

📍  ?를 삼항연산자 그대로 사용할 수도 있음

<ul>
    <li th:each="str, status: ${list}">
        <span th:text="${status.odd} ? 'ODD ---' + ${str} : 'EVEN ---' + ${str}"></span>
    </li>
</ul>


 

📍 th:switch는 th:case와 같이 사용해서 Switch 문을 처리할 때 사용할 수 있음

<ul>
    <li th:each="str, status: ${list}">
        <th:block th:switch="${status.index % 3}">
            <span th:case="0">0</span>
            <span th:case="1">1</span>
            <span th:case="2">2</span>
        </th:block>
    </li>
</ul>


4.  Thymeleaf 링크 처리

  -  Thymeleaf는 '@'로 링크를 작성하기만 하면 됨

<a th:href="@{/hello}">Go to /hello </a>

 

 

1) 링크의 쿼리 스트링 처리

 

링크를 'key=value'의 형태로 필요한 파라미터를 처리해야 할 때 상당히 편리.

쿼리 스트링은 '()'를 이용해서 파라미터의 이름과 값을 지정.

<a th:href="@{/hello(name="AAA", age=16)}">Go to /hello </a>


 

📍  GET 방식으로 처리되는 링크에서 한글이나 공백 문자는 항상 주의해야 하는데 Thymeleaf를 이용하면 이에 대한 URL 인코딩 처리가 자동으로 이루어짐

<a th:href="@{/hello(name="한글처리", age=16)}">Go to /hello</a>


 

📍  만일 링크를 만드는 값이 배열과 같이 여러 개일 때는 자동으로 같은 이름의 파라미터를 처리

<a th:href="@{/hello(types=${{'AAA', 'BBB', 'CCC'}}, age=16}">Go to /hello</a>


5.  Thymeleaf의 특별한 기능들

1)  인라인 처리

Thymeleaf는 여러 편리한 점이 있지만 상황에 따라 동일한 데이터를 다르게 출력해 주는 인라인 기능은 자바 스크립트를 사용할 때 편리한 기능.

 

SampleController에 다양한 종류의 데이터를 Model에 담아서 전달하는 메서드를 추가

 

  • 추가되는 코드는 내부 클래스인 SampleDTO와 ex2()
  • SampleDTO를 정의할 때는 반드시 getter들을 만들어 줌
class SampleDTO {
        private String p1, p2, p3;

        public String getP1() {
            return p1;
        }

        public String getP2() {
            return p2;
        }

        public String getP3() {
            return p3;
        }

    }

 

@GetMapping("ex/ex2")
    public void ex2(Model model) {

        log.info("ex/ex2..........");

        List<String> strList = IntStream.range(1,10)
                .mapToObj(i -> "Data"+i)
                .collect(Collectors.toList());

        model.addAttribute("list", strList);

        Map<String, String> map = new HashMap<>();
        map.put("A", "AAAA");
        map.put("B", "BBBB");

        model.addAttribute("map", map);

        SampleDTO sampleDTO = new SampleDTO();
        sampleDTO.p1 = "Value -- p1";
        sampleDTO.p2 = "Value -- p2";
        sampleDTO.p3 = "Value -- p3";

        model.addAttribute("dto", sampleDTO);

    }

 

화면 구성을 위해 ex2.html을 추가
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div th:text="${list}"></div>
    <div th:text="${map}"></div>
    <div th:text="${dto}"></div>

    <script th:inline="javascript">

        const list = [[${list}]]

        const map = [[${map}]]

        const dto = [[${dto}]]

        console.log(list)
        console.log(map)
        console.log(dto)

    </script>
</body>
</html>

 

📍 HTML 코드를 이용하거나 자바 스크립트 코드를 이용할 때 같은 객체를 사용. 다만 차이점은 <script th:inline="javascript">가 지정된 점

📍 프로젝트를 실행해서 만들어진 결과를 보면 HTML은 기존처럼 출력되고, <script> 부분은 자바 스크립트에 맞는 문법으로 만들어진 것을 확인

 


2)  Thymeleaf의 레이아웃 기능

Thymeleaf의 <th:block>을 이용하면 레이아웃을 만들고 특정한 페이지에서는 필요한 부분만을 작성하는 방식으로 개발이 가능

 

레이아웃 기능을 위해서 별도의 라이브러리가 필요하므로 build.gradle에 추가
// 레이아웃 기능
implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '3.1.0'

 

templates에 layout 폴더를 생성하고 레이아웃을 위한 layout1.html을 작성
<!DOCTYPE html>
<html lang="en"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Layout page</title>
</head>
<body>

    <div>
        <h3>Sample Layout Header</h3>
    </div>

    <div layout:fragment="content">
        <p>Page content goes here</p>
    </div>

    <div>
        <h3>Sample Layout Footer</h3>
    </div>

    <th:block layout:fragment="script">

    </th:block>

</body>
</html>

 

📍 코드 위쪽에는 http://www.ultraq.net.nz/thymeleaf/layout을 이용해서 Thymeleaf의 Layout을 적용하기 위한 네임스페이스를 지정
📍 코드 중간에는 layout:fragment 속성을 이용해서 해당 영역은 나중에 다른 파일에서 해당 부분만을 개발할 수 있음
     ➡️  layout1.html에는 content와 script 부분을 fragment로 지정

 

SampleController에 레이아웃 예제를 위한 ex3()을 추가
  @GetMapping("/ex/ex3")
    public void ex3(Model model) {
        model.addAttribute("arr", new String[] {"AAA", "BBB", "CCC"});
    }

 

templates의 ex폴더에 ex3.html을 생성. 가장 중요한 부분은 <html>에서 사용된 레이아웃 관련 설정
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/layout1.html}">

<div layout:fragment="content">

    <h1>ex3.html</h1>

</div>

 

fragment에 content 부분만 작성한 것을 확인

 

 

layout1.html에는 content와 script 영역을 따로 구성했으므로 이를 이용해서 자바스크립트를 처리하고 싶다면 별도의 영역을 지정하고 fragment를 지정
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/layout1.html}">

<div layout:fragment="content">

    <h1>ex3.html</h1>

</div>

<script layout:fragment="script" th:inline="javascript">

    const arr = [[${arr}]]

</script>

 


1.  스프링 부트란 ?

🐰  스프링 부트는 엄밀하게 말하면 '스프링 프레임워크 개발 도구'. 즉, 엔터프라이즈급 에플리케이션을 개발 하기 위해서 필요한 기능을 제공하는 개발 도구
🐰  스프링의 중요한 특징자동 설정 Auto Configuration (라이브러리만으로 설정을 인식하려는 특성) 을 내세울 수 있음
     예를 들어 스프링 부트는 데이터베이스와 관련된 모듈을 추가하면 자동으로 데이터베이스 관련 설정을 찾아서 실행
      ✓  스프링만을 이용하는 경우와 비교해보면 추가한 모듈 설정이 전혀 필요하지 않고, 설정 자체도 단순
🐰  다른 특징으로는 '내장 톰캣'과 단독 실행 가능한 도구라는 점. 스프링 부트는 별도의 서버 설정 없이도 개발이 가능하고, 실행도 가능

 

 

1) 기존 개발과 차이점


✓ 설정과 관련해서는 직접 필요한 라이브러리를 기존 build.gradle 파일에 추가하는 설정이 상당히 단순하기도 하지만 자동으로 처리

✓ 특히 톰캣이 내장된 상태로 프로젝트가 생성되기 때문에 WAS의 추가 설정이 필요하지 않다는 점도 편리
✓ 빈 설정은 XML을 대신해서 자바 설정을 이용하는 것으로 약간의 변경이 있음

스프링 MVC에서는 JSP를 이용할 수 있지만 기본 설정이 아니라서 라이브러리를 추가해야 되고, 스프링 부트는 Thymeleaf라는 템플릿 엔진을 활용하는 경우가 많음
스프링 부트에서도 MyBatis를 이용할 수 있지만, JPA를 이용하면 객체지향으로 구성된 객체들을 데이터베이스에 반영할 수 있는데 이를 자동으로 처리할 수 있으므로 별도의 SQL의 개발 없이도 개발이 가능

 

스프링 부트의 프로젝트 생성 방식


  👾  스프링 부트를 위한 프로젝트의 생성 방법은 크게 2가지
      -  Spring Initializr를 이용한 자동 생성 ( 대부분 많이 사용. 프로젝트의 기본 템플릿 구조를 만들어 주기 때문 )

            ➡️  웹 사이트(https://start.spring.io/) 에서 프로젝트를 생성하거나, 이클립스나 인텔리제이, VS Code 등에서도 Spring Initializr를 지원하기 때문에 호환성 면에서도 유리
      -  Maven이나 Gradle을 이용한 직접 생성

  👾  스프링 부트는 스프링을 쉽게 사용하기 위한 도구이므로 프로젝트를 생성하고 필요한 라이브러리들을 추가하는 형태의 개발도 가능


 

2.  프로젝트의 실행

🐰  스프링 부트의 프로젝트는 이미 서버를 내장한 상태에서 만들어지기 때문에 스프링만을 이용할 때와 달리 별 의 WAS (Web Application Server) 설정이 필요하지 않고 main() 메서드의 실행을 통해서 프로젝트를 실행

 

main()을 실행하면 자동으로 내장된 톰캣이 실행되는 것을 로그를 통해서 확인

  ✓  실행 결과는 에러가 발생
  ✓  스프링 부트가 자동 설정을 통해서 인식한 Spring Data JPA를 실행했을 때 DB와 관련된 설정을 찾을 수 없어서 발생한 에러

  📍  에러가 발생하긴 했지만, 아무런 설정이 없는 상태인데 자동으로 데이터베이스 관련 설정을 이용을 함
        ➡️  이와 같이 라이브러리만으로 설정을 인식하려는 특성을 '자동 설정 auto configuration'이라고 함

  📍  스프링 부트 설정은 프로젝트 생성 시에 만들어진 application.properties 파일을 이용하거나 application.yml (YAML 이라고 함) 파일을 이용할 수 있음
        ➡️  만일 파일 설정을 피하고 싶으면 @Configuration이 있는 클래스 파일을 만들어서 필요한 설정을 추가할 수 있음

  📍  대부분의 스프링을 지원하는 개발 도구 IDE에서는 application.properties 파일에 들어갈수 있는 내용을 쉽게 완성해 주는 기능을 제공

 

application.properties 파일에 데이터베이스 설정을 추가
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/sample
spring.datasource.username="아이디"
spring.datasource.password="비밀번호"

 

  💡  만일8080 port가 다른 프로젝트에서 실행되고 있다면 포트번호를 application.properties에서 server.port를 지정해서 변경할 수 있음

server.port=8082

 

서블릿이나 스프링처럼 프로젝트를 실행하면 브라우저가 자동으로 실행되지 않음. 부트의 경우 서버를 내장하고 있어서 브라우저가 자동 실행이 안되어서, 직접 브라우저를 열어서 주소창에 주소를 입력해야 함


 

3.  편의성을 높이는 몇 가지 설정

DataSource를 이용하는 것만으로도 스프링 부트가 편리하다는 사실을 알수 있지만, 추가적인 설정 몇 가지만 더 한다면 개발 시간을 더욱 단축할 수 있음

자동 리로딩 설정

 

  • 웹 개발 시에 코드를 수정하고 다시 deploy를 하는 과정을 자동으로 설정. Edit Configuration 메뉴를 실행
  • Bulid and run 메뉴에 있는 Modify options를 선택
  • On 'Update' action / On frame deactivation 의 옵션값을 Update classes and resources 로 지정

 

 

Lombok을 테스트 환경에서도 사용하기


스프링 부트는 체크박스를 선택하는 것만으로 Lombok 라이브러리를 추가하지만 테스트 환경에서는 설정이 빠져 있음
build.gradle 파일 내 dependencies 항목에 test 관련 설정을 조정

// lombok을 테스트 환경에서도 사용.
testCompileOnly('org.projectlombok:lombok')
testAnnotationProcessor('org.projectlombok:lombok')



로그 레벨의 설정


스프링 부트는 기본적으로 Log4j2가 추가되어 있기 때문에 라이브러리를 추가하지 않아도 됨.
applocation.properties 파일을 이용해서 간단하게 로그 설정을 추가할 수 있음.

logging.level.org.springframework=info
logging.level.kr.nomadlab=debug

 

 

인텔리제이의 DataSource 설정


인텔리제이 ultimate의 경우 JPA 관련 플러그인이 이미 설치되어 있기 때문에 DataSource를 설정해두면 나중에 엔티티 클래스의 생성이나 기타 클래스의 생성과 설정 시에 도움이 됨

 

 

테스트 환경과 의존성 주입 테스트


스프링에는 'spring-test-xxx' 라이브러리를 추가해야 하고 JUnit 등도 직접 추가 해야만 하지만, 스프링 부트는 프로젝트 생성할 때 이미 테스트 관련 설정이 완료되고 테스트 코드가 하나 생성되어 있음

 

  ✓  테스트 코드의 실행을 점검하기 위해서 DataSourceTest를 작성해서 HikariCP의 테스트와 Lombok을 확인

  ✓  DataSource는 application.properties에 설정된 DataSource 관련 설정을 통해서 생성된 빈 Bean이고, 이에 대한 별도의 설정 없이 스프링에서 바로 사용이 가능

package com.example.springboot;

import lombok.Cleanup;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@SpringBootTest
@Log4j2
public class DataSourceTest {
    @Autowired
    private DataSource dataSource;

    @Test
    public void connection() throws SQLException {
        @Cleanup Connection connection = dataSource.getConnection();

        log.info(connection);
        Assertions.assertNotNull(connection);
    }
}

 

 

Spring Data JPA을 위한 설정


DataSource 설정까지 모든 테스트가 완료되었다면 Spring Data JPA를 이용할 때 필요한 설정을 추가
application.properties에 다음과 같은 내용을 추가

spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.show-sql=true

 

  ✓  spring.jpa.hibernate.ddl-auto 속성은 프로젝트 실행 시 DDL 문을 처리 할 것인지를 명시
  ✓  DDL : Data Definition Language  ➡️  테이블이나 관계의 구조를 생성하는데 사용하며 CREATE, ALTER, DROP, TRUNCATE 문 등이 있음

  ✓  spring.jpa.properties.hibernate.format_sql 속성은 실제로 실행되는 SQL을 포맷팅해서 알아보기 쉽게 출력
  ✓  spring.jpa.show-sql은 JPA가 실행하는 SQL을 같이 출력

 

속성값 의미
none DDL을 하지 않음
create-drop 실행할 때 DDL을 실행하고 종료시에 만들어진 테이블 등을 모두 삭제
create 실행할 때마다 새롭게 테이블을 생성
update 기존과 다르게 변경돤 부분이 있을 때는 새로 생성
validate 변경된 부분만 알려주고 종료

 

📍  update 속성 값의 경우 테이블이 없을 때는 자동으로 생성하고 변경이 필요할 때는 alter table이 실행. 테이블뿐만 아니라 인덱스나 외래키 등도 자동으로 처리

 


 

4.  스프링 부트에서 웹 개발

🐰  스프링 부트를 이용해서 웹을 개발하는 일은 컨트롤러나 화면을 개발하는 것은 유사하지만, web.xml이나 server-context.xml과 같은 웹 관련 설정 파일들이 없기 때문에 이를 대신하는 클래스를 작성해 준다는 점이 다름
   ✓  xml을 통한 설정은 오류가 발생했을 때 찾기 힘듬. 마크업 언어라서 문법을 체크하는데 한계가 있음. 인텔리제이같은 통합 개발 환경에서도 자동완성을 사용하기 힘듬

 

1) 컨트롤러와 Thymeleaf 만들기

프로젝트에 우선 controller라는 패키지를 생성하고 SampleController 클래스를 생성
SampleController 자체의 개발은 기존의 스프링 MVC를 그대로 이용

@Controller
@Log4j2
public class SampleController {

    @GetMapping("/hello")
    public void hello(Model model) {

        log.info("hello...");
        model.addAttribute("msg", "Hello World");

    }
}

 

  📍 화면은 Thymeleaf를 이용하는데 위치를 주의해서 작성. 프로젝트 생성 시에 만들어져 있는 resources/templates 폴더에 hello.html을 작성
  📍 중요한 부분은 Thymeleaf의 네임스페이스 namespace를 추가

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 th:text="${msg}"></h1>
</body>
</html>

 

 

 

Thymeleaf는 JSP와 동일하게 서버에서 결과물을 생성해서 보내는 방식이지만
좀더 HTML에 가깝게 작성할 수 있고 다양한 기능을 가지고 있음

 

 

 

 


 

2)  JSON 데이터 만들기

스프링을 사용할 때는 jackon-databind 라는 별도의 라이브러리를 출력한 후에 개발할 수 있지만, 스프링 부트는 'web' 항목을 추가할 때 자동으로 포함되므로 별도의 설정 없이 바로 개발할 수 있음

 

controller 패키지에 SampleJSONController라는 클래스를 작성
import lombok.extern.log4j.Log4j2;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Log4j2
@RestController
public class SampleJSONController {

    @GetMapping("/helloArr")
    public String[] helloArr() {
        log.info("HelloArr...");

        return new String[] {"AAA", "BBB", "CCC"};
    }

}

 

브라우저에 'helloArr' 경로를 호출하면 배열이 그대로 출력. 중요한 점은 서버에서 해당 데이터는 Content_Type을 'application/json' 방식 으로 전송

 

+ Recent posts