게시판의 대략적인 페이지를 만들었으니, 게시글 페이지를 작성한다.
시작하기 전에 게시판 html에서 사용된 thymeleaf의 기능인 decoupled template logic을 사용한다. index에서 컴포넌트의 분리를 위해서 <header th:replace="header :: header"></header>
이런 식으로 구문을 작성했는데, 현재 index는 다른 css를 적용하지 않은 순수 마크업이지만, 디자인을 진행하다보면 여러가지 구문이 붙어서 index의 코드가 많이 뚱뚱해 질 수 있다.
따라서 thymeleaf 구문을 따로 분리시켜서 작성하고, index를 순수 마크업 상태로 유지시키는 방법이다.
위의 이미지 처럼 코드 내용이 순수 마크업과, thymeleaf 구문으로 따로 나뉘어진 모습이다.
스프링 부트 애플리케이션 프로퍼티에서 설정할수 있도록 제공되지 않았기 때문에 세팅을 따로 진행해줘야한다. githubgist에서 ThymeleafConfig를 검색하면 여러가지가 나오지만 그중 하나를 선택해서 작성했다. 혹시 몰라 링크를 작성했다.
깃헙 지스트 ThymeleafConfig
프로젝트에서 config 폴더안에 ThymeleafConfig.java
를 만들어서 내용을 붙여 넣는다.
하지만 여기서 @ConfigurationProperties
에 빨간줄이 나타날 수도 있는데, 이는 만약에 유저가 ConfigurationProperties를 직접 만든 경우에는 스캔을 진행해줘야 하기 때문에 생기는 것이다. 따라서 main 애플리케이션 파일에 @ConfigurationPropertiesScan
을 추가해주면 된다.
그리고 붙여넣은 내용중에 생성자 생성내용과 Getter 관련 내용이 있는데, 이것은 lombok으로 쉽게 생성이 가능하므로 @Getter
,@RequiredArgsConstructor
를 추가해주면
이렇게 코드가 짧아진다. 처음 이미지와 코드 내용은 같지만 강조를 위해 다시 이미지를 붙여넣었다.
전반적인 코드의 내용은 thymeleafTemplateResolver라는 Bean을 등록하는 것으로 return 타입은 SpringResourceTemplateResolver이다. bean을 추가하면 autoconfig이 잡힐테고, 그때 작동하는 defaultTemplateResolver에 기능을 추가한 것이다. 그것이 바로 setUseDecupledLogic메소드 이며, 이것은 이미 있는 기능이지만 외부 프로퍼티로 드러내지 않아서 보이지 않은 것 뿐이다. application.yaml
에 themleaf옵션의 내용을 보면 많은 옵션들이 있지만 이 decoupled 옵션이 나타나지 않는것이다.
간단하게 요약하면 "defaultTemplateResolver에 원래 가지고 있던 기능 추가해서 리턴할렵니다" 라는 것이다.
코드의 내용을 작성하고 저장했으면 application.yaml
에 가서 옵션을 추가해주면 된다.
tymeleaf에는 관련 정보를 표시해주지만 thymeleaf3의 경우는 작성한뒤에는 추천으로 나타나는 정보가 하나도 없다. thymeleaf3와 IDE를 더 매끄럽게 연동하는 방법은 build.gradle에서 디펜던시에 spring configuration processor를 추가하는 것이다.
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
사용자가 직접만든 프로퍼티들도 문서 세팅이 가능해지고, IDE의 지원을 받게된다.
이렇게 자동추천으로 정보가 나타나게 된다.
여기까지 설정을 했으면 이제 thymeleaf를 마크업에서 분리해보자. resources/templates/articles 에서 index.html파일이 있는 곳과 같은 경로에 있는 index.th.xml
파일을 생성한다.
그리고 위와같이 작성하고, index.html 에 있었던 thymeleaf 문법을 제거해준뒤 그냥 헤더와 푸터만 남겨두고 실행해도 문제없이 뷰가 나타난다.
이제 진짜 게시글 페이지를 만들어보자
ArticleController 에서 매핑을 위한 코드를 작성
@GetMapping("/{articleId}")
public String article(@PathVariable Long articleId, ModelMap map) {
map.addAttribute("article", null);
map.addAttribute("articleComments", List.of());
return "articles/detail";
}
테스트에서 경로설정은 articles/1 이라고 설정했기 때문에 articleId를 경로에 추가한다.
그리고 article과 articleComments의 어트리뷰트도 추가하고 리턴경로를 설정한다.
detail
뷰는 아직 안만들었으니 detail뷰를 생성한다. 물론 디커플링또한 똑같이 사용하기 위해 detail.html
과 detail.th.xml
을 생성한다. detail.th.xml
의 경우는 index.th.xml
과 동일하게 작성했다.
게시글에서 필요한 것들을 중점적으로 만들었다. 본문,작성자 정보, 댓글, 댓글목록, 이전글, 다음글 버튼.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>게시글 페이지</title>
</head>
<body>
<header id="header"></header>
<main>
<header>
<h1>첫번째 글</h1>
</header>
<aside>
<p>Jyc</p>
<p><a href="mailto:#">작성자 이메일 주소</a></p>
<p><time datetime="2022-01-01T00:00:00">2022-01-01</time></p>
<p>#java</p>
</aside>
<section>
<p>본문<br><br></p>
<div>
<form>
<label for="articleComment" hidden>댓글</label>
<textarea id="articleComment" placeholder="댓글 쓰기.." rows="3"></textarea>
<button type="submit">쓰기</button>
</form>
<ul>
<li>
<div>
<time><small>2022-01-01</small></time>
<strong>Jyc</strong>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br>
Lorem ipsum dolor sit amet
</p>
</div>
</li>
<li>
<div>
<time><small>2022-01-01</small></time>
<strong>Jyc</strong>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br>
Lorem ipsum dolor sit amet
</p>
</div>
</li>
</ul>
</div>
<nav>
<a href="#">이전 글</a>
<a href="#">다음 글</a>
</nav>
</section>
</main>
<footer id="footer"></footer>
</body>
</html>
main을 보면 header를 하나 더 사용했는데. 이렇게 되면 디커플링 로직 인식에 오류가 발생할 수 있으므로 header
와footer
에 id를 추가했다. 따라서 th.xml
파일에서 sel의 값을 id로 변경해주면 된다.
sel값에 #이 붙어있는 모습이다.
자 이제 저장하고 난 다음 매핑한 경로로 이동해보면
게시글의 대략적인 뼈대가 구현이 되었다. 작성자 정보를 aside
로 작성했지만 다른 스타일 설정이 들어가 있지 않아서 아무런 위치 변경 없이 그대로 출력된 모습이다.
이제 커밋으로 정리한다.