⌨️ Thymeleaf+Ajax 구현
이 부분은 나의 최대의 약점이었다.
이전에 과제에서도 SPA 형식의 웹 애플리케이션 구현을 받았는데, 미흡하게 제출을 하였었고, 그때의 기억을 기반하여 언젠가 극복하자고 생각했던 부분이 이번에 해결이 되어 기쁜 마음으로 남긴다.
*저는 바닐라 JS를 통해 Ajax를 구현 하였습니다. JQuery와 Axios 를 활용하지 않은 것은 백엔드 개발자로써 간단한 화면에 구성에 필요한 Ajax 통신만을 가벼운 사이즈로 구현하고 싶어서입니다.
1. Thymeleaf ?
Servlet, JSP를 지나 최근 많이 사용되는 SSR 방식의 템플릿 엔진이다.
1.1. Thymeleaf를 통해 비동기 처럼 보이게 ?
처음 공부를 시작했을 때 한 강의를 통해 배운 방법이다. 그리고 무지했던 나에게 잘못된 지식을 심어준 방법이기도 하다 (강의의 탓이 아닌 내가 제대로 몰랐기 때문이다.)
비동기 통신할 데이터를 Model에 같이 담아주는 방식이었다.
이러한 로직 때문에 나는 비동기가 많이 필요한 페이지에서 계속 모든 데이터를 다 모델에 담아야 하나? 하는 고민을 하게 되었다.
1.2. Controller
a). ArticleController.
@GetMapping("/{articleId}")
public String article(@PathVariable Long articleId, ModelMap map) {
ArticleWithCommentsResponse article = ArticleWithCommentsResponse.from(articleService.getArticleWithComments(articleId));
map.addAttribute("article", article);
map.addAttribute("articleComments", article.articleCommentsResponse());
map.addAttribute("totalCount", articleService.getArticleCount());
return "articles/detail";
}
b). ArticleCommentsController
@PostMapping ("/new")
public String postNewArticleComment(
@AuthenticationPrincipal BoardPrincipal boardPrincipal,
ArticleCommentRequest articleCommentRequest
) {
articleCommentService.saveArticleComment(articleCommentRequest.toDto(boardPrincipal.toDto()));
return "redirect:/articles/" + articleCommentRequest.articleId();
}
위 컨트롤러를 보면 댓글을 작성할 때 게시글 상세로 redirect를 보내준다. 결국 한 페이지에서 댓글이 비동기로 달리는 것 처럼 보이지만 실상은 페이지 렌더링이 다시 일어나는 것이다.
만약 또 다른 데이터가 필요하다면 Model 객체에 추가될 객체가 굉장히 많아질 것이다.
2. Ajax를 활용하여 진정한 비동기 구현
2.1. 템플릿 방식 이해하기
따로 검색을 하진 않았지만 꾸준히 웹개발을 공부해온 결과 스스로 문제의 해결법을 도출해냈다. 물론 best-fit은 아닐 수 있지만 바닥부터 온전히 스스로 해결했다는 것에 성취감을 느낄 수 있었다.
이야기가 잠시 다른 길로 빠졌지만, 결국 SSR의 방식 서버에서 페이지를 렌더링 해주는 것이다. 또한 타임리프의 템플릿 엔진은 직접 접근할 수 없고 "GET" 메서드를 통해 접근해야 한다.
2.2. 하나의 완성된 HTML 페이지를 삽입하면 되지 않을까?
내가 이 문제를 해결한 키워드는 바로 이것이다. 템플릿을 통해 렌더링 된 페이지를 자바스크립트를 통해 inner 해주는 것이다.
그렇다면 댓글의 데이터를 굳이 게시판까지 넘겨주지 않고 댓글 컨트롤러에서 온전히 해결할 수 있지 않을까?
이러한 부분에 쭉 고민하며 프론트에 자신이 없던 것을 이번 프로젝트를 통해 많이 개선 할 수 있었다.
2.3. Controller
@GetMapping("/comments/list/{walking-paths-id}")
public String list(Model model,
@PathVariable("walking-paths-id") int id) {
List<CommentsDTO> list = dao.getCommentById(id);
model.addAttribute("commentList", list);
return "comments :: #comments";
}
@PostMapping(value = "/comments/{walking-paths-id}",produces = "application/json; charset=utf-8")
public String writeComment(@RequestBody CommentsDTO dto,
@Login UsersDTO usersDto,
@PathVariable("walking-paths-id") int id,
Model model,
HttpServletRequest request) {
String ref = request.getHeader("Referer");
dto.setUsers_id(usersDto.getId());
dto.setWalkingPathsId(id);
dao.write(dto);
List<CommentsDTO> list = dao.getCommentById(id);
model.addAttribute("commentList", list);
return "comments :: #comments";
}
고민한 문제를 해결한 이번 프로젝트에서의 컨트롤러이다.
retrun 값으로 페이지 전체가 아닌 id=comments 인 영역을 리턴하여 이것이 필요한 곳에서 삽입해서 사용하는 방식이다.
-> 개선점
1. 컨트롤러의 의존성을 끊음으로 독자적으로 댓글을 여기 저기에 활용할 수 있게되었다.
-> 댓글영역이 하나의 component 느낌처럼 받아지기에 재사용성이 높아졌다.
2. 댓글에 대한 로직만 고민하면된다.
-> 댓글에 게시글 데이터를 추가한다든지 하는 것을 배제하고 오롯이 댓글 서비스에 집중 할 수 있다.
2.4. Script/HTML
<div class="comments_wrapper">
<div class="comments_content">
<!-- 여기에 댓글이 들어감-->
</div>
<form action="" method="post" id="comments_write" onsubmit="return false;">
<input type="text" id="comment_content" class="comments_input" name="content"
placeholder="댓글을 입력해주세요" required/>
<input type="button" class="btns btn_comments" th:if="${session.auth !=null}"
th:onclick="|writeComment(${walkingPaths.getId()})|" value="등록"/>
<input type="button" class="btns btn_comments" th:if="${session.auth == null}"
th:onclick="|location.href='@{/login?redirectURL=/walking-path/{id}(id=${walkingPaths.getId()})}'|"
value="등록">
</form>
</div>
댓글을 작성할 영역이다.
//댓글 가져오기
/*<![CDATA[*/
function getCommentList() {
var walkingPathId = [[${ walkingPaths.getId() }]]
fetch("/comments/list/" + walkingPathId, {
method: "GET"
})
.then((response) => response.text())
.then((data) => {
document.querySelector(".comments_content").innerHTML = `${data}`;
});
}
/*]]>*/
댓글을 가져오는 Script이다. CDATA 부분은 타임리프 데이터를 사용할 수 있게 해주는 문법이다.
키 포인트는 결국 댓글이 삽입된 HTML 문서에서 Script를 작성해주어야 한다는 것이다.
처음에 댓글 문서에서 등록 수정 등을 작성해서 넘겨주니 작동하지 않았다.
다시금 생각해보니 템플릿이 스크립트 영역까지 넘겨주는 것은 아니니 당연한 결과였다.
3. 피드백
- 맞는 방법인지는 모르나 스스로 문제를 해결 했다는 것에서 공부 방법을 깨달음
- 프론트 영역도 꾸준히 공부하여 퍼포먼스를 올려야겠다는 생각을 함
-> 프론트 퍼포먼스가 올라갈 수록 백의 부담이 적어질 수 있다는 생각이 있기 때문 - 글 작성이 초심자 혹은 Reference할 만한 구조는 아니다, 온전히 내 스스로 생각을 적기때문
-> 좀 더 보는 사람을 위한 글..? / 인사이트나 문제 해결법만 참고하면 방법은 스스로 찾아야 한다는 가치관 때문에 글도 추상적으로 되는 것 같다.. 이런 곳에 추상화를 적용하면 안되는데..