Spring Boot/STUDY

[Spring Boot] JPA 프로젝트 - 카테고리 추가

코맹 2024. 6. 25. 17:16

 

아래 순서대로 카테고리 추가해볼 예정이다

카테고리 추가
- /entity/Category.java 클래스 추가
- /repository/CategoryRepository.java 인터페이스 추가
- /service/CategoryService.java 추가
- /entity/Board.java category 속성 추가
- /service/BoardService.java 조회 조건에 카테고리 추가 수정
- 카테고리를 자유게시판, 질문응답게시판 분리
- /templates/layout.html. 사이드바 태그 추가 기입
- /controller/BoardController.java GetMapping 메서드에 카테고리를 추가

 

/entity/Category.java 클래스 추가
package com.eunji.backboard.entity;

import java.time.LocalDateTime;

import org.springframework.data.annotation.CreatedDate;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
public class Category {
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE)
  private Integer id;

  @Column(length = 30)
  private String title;

  @CreatedDate
  @Column(name = "createDate", updatable = false)   // updatable = false : 작성날짜 바꾸지 않겠다는 의미
  private LocalDateTime createDate;
  
}

 

  •  DBeaver에 Category 테이블 생성된 것 확인할 수 있음

 

/repository/CategoryRepository.java 인터페이스 추가
package com.eunji.backboard.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.eunji.backboard.entity.Category;

@Repository
public interface CategoryRepository extends JpaRepository<Category, Integer> {

  Optional<Category> findByTitle(String title);     // select * from Category where title = title;
  
}

 

 

/service/CategoryService.java 추가
package com.eunji.backboard.service;

import java.time.LocalDateTime;
import java.util.Optional;

import org.springframework.stereotype.Service;

import com.eunji.backboard.common.NotFoundException;
import com.eunji.backboard.entity.Category;
import com.eunji.backboard.repository.CategoryRepository;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class CategoryService {

  private final CategoryRepository categoryRepository;

  // 카테고리를 생성하는 메서드
  public Category setCategory(String title) {
    Category cate = new Category();
    cate.setTitle(title);
    cate.setCreateDate(LocalDateTime.now());

    Category category = this.categoryRepository.save(cate);
    return category;

  }

  // free, qna..
  // 생성한 카테고리를 가져오기 위한 메서드
  public Category getCategory(String title) {
    Optional<Category> cate = this.categoryRepository.findByTitle(title);

    if(cate.isEmpty()) {    // free나 qna라는 이름의 카테고리 데이터가 없으면
       cate = Optional.ofNullable(setCategory(title));    // db Category 테이블에 해당 카테고리 값을 생성(insert)

    }
    
    if(cate.isPresent()) {  // 카테고리가 있으면
      return cate.get();

    } else 
        throw new NotFoundException("Category NOT FOUND!!");    // 발생할 일이 없음
  }
  
}

 

 

/entity/Board.java category 속성 추가
  @ManyToOne
  private Category category;    // free, qna로 구분해서 글 작성 가능

 

  • Board테이블과 Category id와 연결된 것을 확인할 수 있음

 

/service/BoardService.java 조회 조건에 카테고리 추가 수정

1. getList() 메서드 추가

  public Page<Board> getList(int page, String keyword, Category category) {
    List<Sort.Order> sorts = new ArrayList<>();
    sorts.add(Sort.Order.desc("createDate"));
    Pageable pageable = PageRequest.of(page, 10, Sort.by(sorts));   // pageSize를 동적으로도 변경할 수 있음. 나중에...
    
    Specification<Board> spec = searchBoard(keyword, category.getId());
    return this.boardRepository.findAll(spec, pageable);   // Specification 인터페이스로 쿼리 생성로직 만들어서
    // return this.boardRepository.findAllByKeyword(keyword, pageable);

  }

 

 

2. searchBoard() 메서드 추가

  public Specification<Board> searchBoard(String keyword, Integer cateId) {
    return new Specification<Board>() {
      private static final long serialVersionUID = 1L;  // 필요한 값이라서 추가

      @Override
      @Nullable
      public Predicate toPredicate(Root<Board> b, CriteriaQuery<?> query, CriteriaBuilder cb) {
        // query를 JPA로 생성
        query.distinct(true); // 중복 제거
        Join<Board, Reply> r = b.join("replyList", JoinType.LEFT);

        return cb.and(cb.equal(b.get("category").get("id"), cateId),
                      cb.or(cb.like(b.get("title"), "%" + keyword + "%"),     // 게시글 제목에서 검색
                            cb.like(b.get("content"), "%" + keyword + "%"),   // 게시글 내용에서 검색
                            cb.like(r.get("content"), "%" + keyword + "%")    // 댓글 내용에서 검색
                            ));
      }
    };
  }

 

 

3. setBoard() 추가

public void setBoard(String title, String content, Member writer, Category category){
    // 빌더로 생성한 객체
    Board board = Board.builder().title(title).content(content)
                       .createDate(LocalDateTime.now()).build();

    board.setCategory(category);    // 카테고리 추가
    board.setWriter(writer);

    // 객체 저장
    this.boardRepository.save(board); // PK가 없으면 INSERT
  }

 

 

/templates/layout.html. 사이드바 태그 추가 기입
  <!-- 레이아웃에 적용될 하위 페이지 위치 -->
  <div class="container mt-3" style="height: auto !important;">
    <div class="row">
      <div class="col-sm-12 col-md-3 col-lg-2">
        <!-- 게시판 카테고리 영역 -->
        <nav class="border-top dorser-secondary">
          <div class="list-group">
            <a th:classappend="${category} == 'free' ? 'active'" th:href="@{/board/list/free}"
                class="category-group rounded-0 list-group-item list-group-item-action list-group-item-light p-2">자유게시판</a>
            <a th:classappend="${category} == 'free' ? 'active'" th:href="@{/board/list/qna}"
                class="category-group rounded-0 list-group-item list-group-item-action list-group-item-light p-2">질의응답</a>
          </div>
        </nav>
      </div>
      <div class="col-sm-12 col-md-9 col-lg-10">
        <!-- 기존 게시판 영역 -->
        <th:block layout:fragment="main-content">

        </th:block>
      </div>
    </div>
  </div>

 

 

/controller/BoardController.java GetMapping 메서드에 카테고리 매개변수 추가
private final CategoryService categoryService;  // 카테고리 사용


  @GetMapping("/list")
  public String list(Model model, @RequestParam(value="page", defaultValue = "0") int page,
                     @RequestParam(value = "kw", defaultValue = "") String keyword) {
    Page<Board> paging = this.boardService.getList(page, keyword);  // 검색 추가
    model.addAttribute("paging", paging);
    model.addAttribute("kw", keyword);

    return "board/list";  
  }

  // 24.06.25. 마지막 카테고리까지 추가
  @GetMapping("/list/{category}")
  public String list(Model model, 
                     @PathVariable(value="category") String category,
                     @RequestParam(value="page", defaultValue = "0") int page,
                     @RequestParam(value = "kw", defaultValue = "") String keyword) {

    Category cate = this.categoryService.getCategory(category);
    Page<Board> paging = this.boardService.getList(page, keyword, cate);  // 검색 및 카테고리 추가
    model.addAttribute("paging", paging);
    model.addAttribute("kw", keyword);
    model.addAttribute("category", category);

    return "board/list";  
  }
  •  CategoryService를 사용하기 위해 필드 추가
  • list() 메서드에서 좀전에 boardService에 새롭게 추가한 getList()사용

 

 

/templates/board/list.html 카테고리 변수 추가
  • 수정 전
<a th:href="@{/board/create}" class="btn btn-sm btn-primary my-2">게시글 등록</a>
<form th:action="@{/board/list}" method="get" id="searchForm">

 

  • 수정 후
<a th:href="@{|/board/create/${category}|}" class="btn btn-sm btn-primary my-2">게시글 등록</a>
<form th:action="@{|/board/list/${category}|}" method="get" id="searchForm">

 

 

 

/controller/BoardController.java create() GET, POST 메서드에 category 추가!

1. create() GET 메서드

@PreAuthorize("isAuthenticated()")  // 로그인 시만 작성 가능
  @GetMapping("/create/{category}")
  public String create(Model model, 
                      @PathVariable("category") String category, 
                       BoardForm boardForm){
    model.addAttribute("category", category);
    return "board/create";
  }

 

 

2. create() POST 메서드

  @PreAuthorize("isAuthenticated()")  // 로그인 시만 작성 가능
  @PostMapping("/create/{category}")
  public String create(Model model, 
                      @PathVariable("category") String category, 
                      @Valid BoardForm boardForm,
                       BindingResult bindingResult, 
                       Principal principal) {
    if(bindingResult.hasErrors()) {
      model.addAttribute("category", category);
      return "board/create";    // 현재 html에 그대로 머무르기
    }

    Member writer = this.memberService.getMember(principal.getName());  // 현재 로그인 사용자 아이디
    // this.boardService.setBoard(title, content);

    Category cate = this.categoryService.getCategory(category);
    this.boardService.setBoard(boardForm.getTitle(), boardForm.getContent(), writer, cate);
    
    return String.format("redirect:/board/list/%s", category);
  }

 

 

 

  • 질의응답 게시판 카테고리에서 생성한 게시글이 뜨는 것을 확인!