본문 바로가기

Java/Spring

[Spring] 게시판 만들기 #9

What to do?

 

정렬 기능

  • 제목, 글쓴이, 해쉬태그, 작성일자

 

검색기능

  • 제목, 본문 : 부분검색
  • 해쉬태그, 작성일자 : Exact Match
  • 작성자 : 인증기능을 완성한 후 추가 수정(TODO)

구현내용

 

  • 정렬기능
    • 테이블 헤더에서 Title을 클릭하면, Title 기준으로 정렬 → a 로 시작하는 게시물들부터 출력

 

  • 검색기능
    • zoo라는 Title을 가진 게시물들 검색


SearchType

 

  • 검색유형을 enum으로 정의
    • 제목(title), 본문(content), 작성자(author), 해쉬태그
  • description 필드는 드롭박스에 표시할 내용
@Getter
@AllArgsConstructor
public enum SearchType {
    TITLE("Title"), CONTENT("Content"), USERNAME("Author"), HASHTAG("Hashtag");
    private String description;
}

Service

 

ArticleService.java 

@Transactional(readOnly = true)
public Page<ArticleDto> searchArticleDtoPage(SearchType searchType, String keyword, Pageable pageable){
    if (keyword == null || keyword.isBlank()){
        return articleRepository.findAll(pageable).map(ArticleDto::from);
    }
    return switch (searchType){
        case TITLE->articleRepository.findByTitleContaining(keyword, pageable).map(ArticleDto::from);
        case USERNAME->articleRepository.findByUserAccount_UsernameContaining(keyword, pageable).map(ArticleDto::from);
        case HASHTAG->articleRepository.findByHashtags(keyword, pageable).map(ArticleDto::from);
        case CONTENT->articleRepository.findByContentContaining(keyword, pageable).map(ArticleDto::from);
        default -> articleRepository.findAll(pageable).map(ArticleDto::from);
    };
}

이전에 작성했던 Service의 코드 일부분을 발췌했다.

작성자로 검색했을 경우, UserAccount 필드에서 username을 추출하도록 했는데, 인증기능이 완성되지 않아서 이 부분은 추후 수정될 수 있다.


Controller

 

ArticleController.java

@GetMapping
public String articles(
        @RequestParam(required = false) SearchType searchType,
        @RequestParam(required = false) String keyword,
        @PageableDefault(size=20, sort = "createdAt", direction = Sort.Direction.DESC)Pageable pageable,
        ModelMap map){
    Page<ArticleDto> articleDtoPage = articleService.searchArticleDtoPage(searchType, keyword, pageable);
    List<Integer> pagination = articleService.getPaginationBarNumbers(pageable.getPageNumber(), articleDtoPage.getTotalPages());
    map.addAttribute("articles", articleDtoPage.map(ArticlesResponse::from));
    map.addAttribute("pagination", pagination);
    map.addAttribute("searchTypes", SearchType.values());
    return "article/index";
}

기존 코드에서 map.addAttribute("searchTypes", SearchType.values()) 한줄 추가했다. 


HTML

resources/articles/index.html

 

  • 검색창
<form id="search-form" name="searchForm">
    <div class="input-group justify-content-between" id="search-bar" name="searchBar">
        <!--Select Bar-->
        <div class="w-20">
            <select class="form-select" id="search-type" name="searchType">
                <option value="title" selected>Title</option>
                <option value="author">Author</option>
                <option value="hashtag">Hashtag</option>
            </select>
        </div>
        <!--Search Input-->
        <div class="flex-fill">
            <input type="search" class="form-control" placeholder="keyword..." name="keyword" id="keyword"/>
        </div>
        <!--Submit Button-->
        <div class="w-20">
            <button id="search-button" type="submit" class="btn btn-primary">
                <span>Search</span>
            </button>
        </div>
    </div>
</form>

 

 

  • 테이블 헤더
<tr id="table-header" name="table-header">
    <th scope="col" class="col-6 title"><a>Title</a></th>
    <th scope="col" class="hashtags"><a>Hashtag</a></th>
    <th scope="col" class="author"><a>Author</a></th>
    <th scope="col" class="created-at"><a>Written At</a></th>
</tr>

 


Thymeleaf Template

resources/articles/index.th.xml

 

  • 검색창
    • param - 현재 url에 있는 parameter를 가져옴
<!--Search Bar-->
<attr sel="#search-form">
    <attr sel="#keyword" th:value="${param.keyword}"/>
    <attr sel="#search-type" th:remove="all-but-first">
        <attr sel="option[0]" th:each="searchType:${searchTypes}"
              th:value="${searchType.name}"
              th:text="${searchType.description}"
              th:selected="${param.searchType != null && (param.searchType.toString == searchType.name)}"/>
    </attr>
</attr>

 

  • 테이블 헤더
    • sort - Pageable을 사용했기 때문에, url parameter에 sort 기준을 전달하면 정렬해서 Page를 넘겨줌
<!--Table Header-->
<attr sel="#table-header" th:object="${articles}">
    <attr sel=".title/a" th:text="Title"
          th:href="@{/articles(page=${articles.number},
          sort='title' + (*{sort.getOrderFor('title')} != null ? (*{sort.getOrderFor('title').direction.name} != 'DESC' ? ',desc' : '') : ''),
          searchType=${param.searchType},
          searchValue=${param.searchValue})}"/>
    <attr sel=".hashtags/a" th:text="Hashtag"
          th:href="@{/articles(page=${articles.number},
          sort='hashtags' + (*{sort.getOrderFor('hashtags')} != null ? (*{sort.getOrderFor('hashtags').direction.name} != 'DESC' ? ',desc' : '') : ''),
          searchType=${param.searchType},
          searchValue=${param.searchValue})}"/>
    <attr sel=".author/a" th:text="Author"
          th:href="@{/articles(page=${articles.number},
          sort='createdBy' + (*{sort.getOrderFor('createdBy')} != null ? (*{sort.getOrderFor('createdBy').direction.name} != 'DESC' ? ',desc' : '') : ''),
          searchType=${param.searchType},
          searchValue=${param.searchValue})}"/>
    <attr sel=".created-at/a" th:text="'Written At'"
          th:href="@{/articles(page=${articles.number},
          sort='createdAt' + (*{sort.getOrderFor('createdAt')} != null ? (*{sort.getOrderFor('createdAt').direction.name} != 'DESC' ? ',desc' : '') : ''),
          searchType=${param.searchType},
          searchValue=${param.searchValue})}"/>
</attr>

'Java > Spring' 카테고리의 다른 글

[Spring] 게시판 만들기 #11  (0) 2022.11.24
[Spring] 게시판 만들기 #10  (0) 2022.11.23
[Spring] 게시판 만들기 #8  (0) 2022.11.23
[Spring] 게시판 만들기 #7  (0) 2022.11.22
[Spring] 게시판 만들기 #6  (0) 2022.11.21