Board
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Board extends TimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "board_id")
private Long board_id;
@Column(name = "title")
private String title;
@Column(name = "board_content")
private String content;
@Column(name = "writer")
private String writer;
@Enumerated(EnumType.STRING)
private BoardCategory category;
@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
@Enumerated
: enum
타입의 프로퍼티 또는 필드를 생성하는 애노테이션입니다.STRING
과 ORDINAL
이 존재하는데 ORDINAL
은 순서, STRING
은 글자 그대로이기 때문에 무조건 STRING을 사용하는 것이 좋습니다.User
- boards
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User extends TimeEntity {
//...
@JsonManagedReference
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Board> boards = new ArrayList<>();
//...
}
@JsonManagedReference
- @JsonBackReference
를 사용했습니다.@OneToMany
- @ManyToOne
/ @JoinColumn
을 사용했습니다. (💡 매우 중요하고 배울 것이 많은 개념이기 때문에 해당 글에서는 생략하겠습니다.)BoardCategory
public enum BoardCategory {
NORMAL, SPORTS, STYLE, FUN
}
BoardRepository
public interface BoardRepository extends JpaRepository<Board, Long> {
List<Board> findByCategory(BoardCategory category);
List<Board> findByUser(User user);
}
메소드 명
, 리턴 타입
, 파라미터 등
을 사용하여 메소드를 생성할 수 있습니다.findCategory()
: 선택한 카테고리에 맞는 게시판의 리스트를 조회할 수 있습니다.findByUser()
: 사용자가 작성한 게시글을 리스트로 조회할 수 있습니다.게시판의 작성, 조회, 수정, 삭제를 Spring Data Jpa의
CRUD
를 통해 구현합니다.
BoardFindService
@Service
@RequiredArgsConstructor
public class BoardFindService {
private final BoardRepository boardRepository;
private final UserFindService userFindService;
@Transactional(readOnly = true)
public Board findById(Long boardId) {
Board board = boardRepository.findById(boardId)
.orElseThrow(() -> new NotFoundBoardException(String.format("Board is not Found!")));
return board;
}
@Transactional(readOnly = true)
public List<Board> findAll() {
return boardRepository.findAll();
}
@Transactional(readOnly = true)
public List<Board> findByCategory(BoardCategory category) {
return boardRepository.findByCategory(category);
}
@Transactional(readOnly = true)
public List<Board> findByUser(Long userId) {
User user = userFindService.findById(userId);
return boardRepository.findByUser(user);
}
}
BoardRepository
와 UserFindService
를 생성자(@RequiredArgsConstructor
+ private final
)를 통해 참조하도록 합니다.UserFindService
를 참조하도록 했습니다.BoardWriteService
@Service
@RequiredArgsConstructor
public class BoardWriteService {
private final UserFindService userFindService;
private final BoardRepository boardRepository;
@Transactional
public Long writeBoard(Long userId, BoardWriteRequest boardWriteRequest) {
User user = userFindService.findById(userId);
Board board = Board.builder()
.title(boardWriteRequest.getTitle())
.writer(user.getName())
.content(boardWriteRequest.getContent())
.category(boardWriteRequest.getCategory())
.build();
Board savedBoard = boardRepository.save(board);
user.writeBoard(savedBoard);
return savedBoard.getBoard_id();
}
}
userId
와 BoardWriteRequest
입니다.@Getter
@NoArgsConstructor
public class BoardWriteRequest {
private String title;
private String content;
private BoardCategory category;
}
id
) + builder()
를 활용하여 새로운 게시글 생성하여 저장합니다.writeBoard()
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User extends TimeEntity {
//...
public void writeBoard(Board board) {
this.boards.add(board);
board.createdByUser(this);
}
//...
}
this.boards.add(board)
board.createdByUser(this)
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Board {
//...
public void createdByUser(User user) {
this.user = user; //like setter
}
//...
}
BoardUpdateService
@Service
@RequiredArgsConstructor
public class BoardUpdateService {
private final UserFindService userFindService;
private final BoardFindService boardFindService;
@Transactional
public Long updateBoard(Long userId, Long boardId, BoardUpdateRequest boardUpdateRequest) {
User user = userFindService.findById(userId);
Board board = boardFindService.findById(boardId);
checkBoardLoginUser(user, board);
Long updatedBoardId = board.update(
boardUpdateRequest.getTitle(),
boardUpdateRequest.getContent(),
boardUpdateRequest.getCategory()
);
return updatedBoardId;
}
private void checkBoardLoginUser(User user, Board board) {
if (!Objects.equals(board.getUser().getUser_id(), user.getUser_id())) {
throw new NotHavePermissionBoardException("해당 게시물을 수정할 권한이 없습니다.");
}
}
}
checkBoardLoginUser
)한 후 BoardUpdateRequest
를 통해 게시글을 수정할 수 있습니다.@Getter
@NoArgsConstructor
public class BoardUpdateRequest {
private String title;
private String content;
private BoardCategory category;
}
BoardDeleteService
@Service
@RequiredArgsConstructor
public class BoardDeleteService {
private final UserFindService userFindService;
private final BoardFindService boardFindService;
private final BoardRepository boardRepository;
@Transactional
public void deleteBoardById(Long userId, Long boardId) {
User user = userFindService.findById(userId);
Board board = boardFindService.findById(boardId);
checkBoardLoginUser(user, board);
boardRepository.deleteById(boardId);
}
private void checkBoardLoginUser(User user, Board board) {
if (!Objects.equals(board.getUser().getUser_id(), user.getUser_id())) {
throw new NotHavePermissionBoardException("해당 게시물을 삭제할 권한이 없습니다.");
}
}
}
BoardFindApi
@Slf4j
@RestController
@RequestMapping("/boards")
@RequiredArgsConstructor
public class BoardFindApi {
private final BoardFindService boardFindService;
@GetMapping()
public ApiResult<List<Board>> findAll() {
try {
return ApiResult.succeed(boardFindService.findAll());
} catch (Exception e) {
log.error(e.getMessage());
return ApiResult.failed(e.getMessage());
}
}
@GetMapping("/{boardId}")
public ApiResult<Board> findById(@PathVariable Long boardId) {
try {
return ApiResult.succeed(boardFindService.findById(boardId));
} catch (Exception e) {
log.error(e.getMessage());
return ApiResult.failed(e.getMessage());
}
}
@GetMapping("/category/{category}")
public ApiResult<List<Board>> findByCategory(@PathVariable BoardCategory category) {
try {
return ApiResult.succeed(boardFindService.findByCategory(category));
} catch (Exception e) {
log.error(e.getMessage());
return ApiResult.failed(e.getMessage());
}
}
@GetMapping("/user/{userId}")
public ApiResult<List<Board>> findByUserId(@PathVariable Long userId) {
try {
return ApiResult.succeed(boardFindService.findByUser(userId));
} catch (Exception e) {
log.error(e.getMessage());
return ApiResult.failed(e.getMessage());
}
}
}
@
(애노테이션) 관련 설명은 User편에서 확인하시길 바랍니다.ApiResult
설명 또한, 앞선 게시물에 있습니다!
findAll()
findById(Long boardId)
findByCategory(BoardCategory boardCategory)
findByUserId(Long userId)
BoardWriteApi
@Slf4j
@RestController
@RequestMapping("/boards")
@RequiredArgsConstructor
public class BoardWriteApi {
private final BoardWriteService boardWriteService;
@PostMapping("/{userId}/write")
public ApiResult<Long> writeBoard(@PathVariable Long userId,
@RequestBody BoardWriteRequest boardWriteRequest) {
try {
Long boardId = boardWriteService.writeBoard(userId, boardWriteRequest);
return ApiResult.succeed(boardId);
} catch (Exception e) {
log.error(e.getMessage());
return ApiResult.failed(e.getMessage());
}
}
}
BoardFindApi
와 마찬가지로 해당하는 비즈니스 로직의 서비스 클래스를 사용하여 필요한 정보를 반환하도록 합니다. 특히 try-catch
를 사용할 경우, 위처럼 에러를 확인만 하는 행위는 지양해야합니다.BoardUpdateApi
와 BoardDeleteApi
또한 필요한 매개변수와 서비스 클래스를 사용하여 비슷하게 구현했기 때문에 생략하겠습니다.