Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public enum ResultCode {
BOARD_NOT_EXIST("B004", "존재하지 않는 게시물"),
BOARD_MEMO_UPDATE_SUCCESS("B005", "게시물 메모 수정 성공"),
BOARD_DELETE_SUCCESS("B006", "게시물 삭제 성공"),
BOARD_ALREADY_EXIST("B007", "존재하는 게시물"),

// SNS
SNS_INFO_SUCCESS("N001", "소셜 조회 성공"),
Expand Down
21 changes: 8 additions & 13 deletions src/main/java/com/Alchive/backend/controller/BoardController.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@ public class BoardController {
@PostMapping("/saved")
public ResponseEntity<ResultResponse> isBoardSaved(@AuthenticationPrincipal User user, @RequestBody @Valid ProblemNumberRequest problemNumberRequest) {
BoardDetailResponseDTO board = boardService.isBoardSaved(user, problemNumberRequest);
if (board != null) {
return ResponseEntity.ok(ResultResponse.of(BOARD_INFO_SUCCESS, board));
} else {
return ResponseEntity.ok(ResultResponse.of(BOARD_NOT_EXIST, null));
}
return ResponseEntity.ok(ResultResponse.of(boardService.boardSavedStatus(board), board));
}

@Operation(summary = "게시물 목록 조회", description = "게시물 목록을 조회하는 메서드입니다. ")
Expand All @@ -55,7 +51,6 @@ public ResponseEntity<ResultResponse> getBoardList(@RequestParam(value = "offset
@PostMapping("")
public ResponseEntity<ResultResponse> createBoard(@AuthenticationPrincipal User user, @RequestBody @Valid BoardCreateRequest boardCreateRequest) {
BoardResponseDTO board = boardService.createBoard(user, boardCreateRequest);
// slackService.sendMessageCreateBoard(boardCreateRequest, board);
return ResponseEntity.ok(ResultResponse.of(BOARD_CREATE_SUCCESS, board));
}

Expand All @@ -73,17 +68,17 @@ public ResponseEntity<ResultResponse> updateBoard(@AuthenticationPrincipal User
return ResponseEntity.ok(ResultResponse.of(BOARD_MEMO_UPDATE_SUCCESS, board));
}

@Operation(summary = "게시물 삭제", description = "게시물을 삭제하는 메서드입니다. ")
@DeleteMapping("/{boardId}")
public ResponseEntity<ResultResponse> deleteBoard(@AuthenticationPrincipal User user, @PathVariable Long boardId) {
boardService.deleteBoard(user, boardId);
return ResponseEntity.ok(ResultResponse.of(BOARD_DELETE_SUCCESS));
}

@Operation(summary = "게시물 메모 업데이트", description = "게시물 메모를 수정하는 메서드입니다. ")
@PatchMapping("/memo/{boardId}")
public ResponseEntity<ResultResponse> updateBoardMemo(@AuthenticationPrincipal User user, @PathVariable Long boardId, @RequestBody BoardMemoUpdateRequest updateRequest) {
BoardResponseDTO board = boardService.updateBoardMemo(user, boardId, updateRequest);
return ResponseEntity.ok(ResultResponse.of(BOARD_MEMO_UPDATE_SUCCESS, board));
}

@Operation(summary = "게시물 삭제", description = "게시물을 삭제하는 메서드입니다. ")
@DeleteMapping("/{boardId}")
public ResponseEntity<ResultResponse> deleteBoard(@AuthenticationPrincipal User user, @PathVariable Long boardId) {
boardService.deleteBoard(user, boardId);
return ResponseEntity.ok(ResultResponse.of(BOARD_DELETE_SUCCESS));
}
}
123 changes: 63 additions & 60 deletions src/main/java/com/Alchive/backend/service/BoardService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.Alchive.backend.config.error.exception.board.NotFoundBoardException;
import com.Alchive.backend.config.error.exception.problem.NotFoundProblemException;
import com.Alchive.backend.config.result.ResultCode;
import com.Alchive.backend.config.result.ResultResponse;
import com.Alchive.backend.domain.algorithm.Algorithm;
import com.Alchive.backend.domain.algorithmProblem.AlgorithmProblem;
import com.Alchive.backend.domain.board.Board;
Expand Down Expand Up @@ -37,62 +39,44 @@ public class BoardService {
private final SolutionRepository solutionRepository;
private final UserService userService;

private BoardDetailResponseDTO toBoardDetailResponseDTO(Board board) {
BoardResponseDTO boardResponseDTO = new BoardResponseDTO(board);

// 문제 정보
Long problemId = board.getProblem().getId();
Problem problem = problemRepository.findById(problemId)
.orElseThrow(NotFoundProblemException::new);
ProblemResponseDTO problemResponseDTO = new ProblemResponseDTO(problem, getProblemAlgorithms(problemId));

// 풀이 정보
List<SolutionResponseDTO> solutions = getSolutions(board.getId());

// DTO로 묶어서 반환
return new BoardDetailResponseDTO(boardResponseDTO, problemResponseDTO, solutions);
}

// Board 저장 여부 구현
public BoardDetailResponseDTO isBoardSaved(User user, ProblemNumberRequest problemNumberRequest) {
Optional<Board> board = boardRepository.findByProblem_PlatformAndProblem_NumberAndUser_Id(problemNumberRequest.getPlatform(), problemNumberRequest.getProblemNumber(), user.getId());
return board.map(this::toBoardDetailResponseDTO).orElse(null);
}

public ResultCode boardSavedStatus(BoardDetailResponseDTO board) {
if (board != null) {
return ResultCode.BOARD_ALREADY_EXIST;
}
return ResultCode.BOARD_NOT_EXIST;
}

public Page<List<BoardDetailResponseDTO>> getBoardList(int offset, int limit) {
Pageable pageable = PageRequest.of(offset, limit);
Page<Board> boardPage = boardRepository.findAll(pageable);

// Board를 BoardDetailResponseDTO로 변환
List<BoardDetailResponseDTO> boardList = boardPage.getContent().stream()
.map(this::toBoardDetailResponseDTO)
.toList();

// 변환된 리스트를 새로운 Page 객체로 감싸서 반환
return new PageImpl<>(List.of(boardList), pageable, boardPage.getTotalElements());
}
Comment on lines 54 to 63
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Page<List<BoardDetailResponseDTO>> → 중첩 리스트 반환 구조 문제

현재 구현은
boardList : List<BoardDetailResponseDTO>
new PageImpl<>(List.of(boardList), …) 로 한 번 더 래핑하여 List<List<BoardDetailResponseDTO>> 형태로 페이지네이션됩니다.
컨트롤러/프론트단이 예상하는 스키마와 달라질 위험이 크며 직렬화 시 JSON 구조도 두 단계로 중첩됩니다.

아래와 같이 제너릭 타입을 단일 DTO 로 변경하고 불필요한 List.of 래핑을 제거해 주세요.

-    public Page<List<BoardDetailResponseDTO>> getBoardList(int offset, int limit) {
+    public Page<BoardDetailResponseDTO> getBoardList(int offset, int limit) {
        ...
-        return new PageImpl<>(List.of(boardList), pageable, boardPage.getTotalElements());
+        return new PageImpl<>(boardList, pageable, boardPage.getTotalElements());
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public Page<List<BoardDetailResponseDTO>> getBoardList(int offset, int limit) {
Pageable pageable = PageRequest.of(offset, limit);
Page<Board> boardPage = boardRepository.findAll(pageable);
// Board를 BoardDetailResponseDTO로 변환
List<BoardDetailResponseDTO> boardList = boardPage.getContent().stream()
.map(this::toBoardDetailResponseDTO)
.toList();
// 변환된 리스트를 새로운 Page 객체로 감싸서 반환
return new PageImpl<>(List.of(boardList), pageable, boardPage.getTotalElements());
}
public Page<BoardDetailResponseDTO> getBoardList(int offset, int limit) {
Pageable pageable = PageRequest.of(offset, limit);
Page<Board> boardPage = boardRepository.findAll(pageable);
List<BoardDetailResponseDTO> boardList = boardPage.getContent().stream()
.map(this::toBoardDetailResponseDTO)
.toList();
return new PageImpl<>(boardList, pageable, boardPage.getTotalElements());
}
🤖 Prompt for AI Agents
In src/main/java/com/Alchive/backend/service/BoardService.java around lines 53
to 62, the method getBoardList currently returns a Page of List of
BoardDetailResponseDTO, causing an unintended nested list structure. To fix
this, change the return type to Page<BoardDetailResponseDTO> and remove the
List.of wrapping around boardList when creating the PageImpl, so that the
pagination directly wraps the list of DTOs without extra nesting.


// 게시물 메서드
public BoardDetailResponseDTO getBoardDetail(Long boardId) {
Board board = boardRepository.findById(boardId)
.orElseThrow(NotFoundBoardException::new);
return toBoardDetailResponseDTO(board);
}

@Transactional
public BoardResponseDTO createBoard(User user, BoardCreateRequest boardCreateRequest) {
ProblemCreateRequest problemCreateRequest = boardCreateRequest.getProblemCreateRequest();
// 문제 정보 저장 여부 확인
if (!problemRepository.existsByNumberAndPlatform(problemCreateRequest.getNumber(), problemCreateRequest.getPlatform())) {
// 문제가 저장되지 않은 경우
createProblem(problemCreateRequest);
}
createProblem(problemCreateRequest);
Problem problem = problemRepository.findByNumberAndPlatform(problemCreateRequest.getNumber(), problemCreateRequest.getPlatform());
Board board = Board.of(problem, user, boardCreateRequest);
return new BoardResponseDTO(boardRepository.save(board));
}

public BoardDetailResponseDTO getBoardDetail(Long boardId) {
// 게시물 정보
Board board = boardRepository.findById(boardId)
.orElseThrow(NotFoundBoardException::new);
return toBoardDetailResponseDTO(board);
}

@Transactional
public BoardResponseDTO updateBoard(User user, Long boardId, BoardUpdateRequest updateRequest) {
Board board = boardRepository.findById(boardId)
Expand All @@ -102,55 +86,74 @@ public BoardResponseDTO updateBoard(User user, Long boardId, BoardUpdateRequest
}

@Transactional
public void deleteBoard(User user, Long boardId) {
public BoardResponseDTO updateBoardMemo(User user, Long boardId, BoardMemoUpdateRequest updateRequest) {
Board board = boardRepository.findById(boardId)
.orElseThrow(NotFoundBoardException::new);
userService.validateUser(user.getId(), board.getUser().getId());
boardRepository.delete(board);
return new BoardResponseDTO(board.updateMemo(updateRequest.getMemo()));
}

@Transactional
public BoardResponseDTO updateBoardMemo(User user, Long boardId, BoardMemoUpdateRequest updateRequest) {
public void deleteBoard(User user, Long boardId) {
Board board = boardRepository.findById(boardId)
.orElseThrow(NotFoundBoardException::new);
userService.validateUser(user.getId(), board.getUser().getId());
return new BoardResponseDTO(board.updateMemo(updateRequest.getMemo()));
boardRepository.delete(board);
}

// 문제 메서드
@Transactional
public void createProblem(ProblemCreateRequest problemCreateRequest) {
Problem problem = problemRepository.save(Problem.of(problemCreateRequest));
List<String> algorithms = problemCreateRequest.getAlgorithms();
private BoardDetailResponseDTO toBoardDetailResponseDTO(Board board) {
BoardResponseDTO boardResponseDTO = new BoardResponseDTO(board);
ProblemResponseDTO problemResponseDTO = getBoardProblem(board);
List<SolutionResponseDTO> solutions = getBoardSolutions(board.getId());

for (String algorithmName : algorithms) {
// 알고리즘 저장 여부 확인
if (!algorithmRepository.existsByName(algorithmName)) {
// 알고리즘이 저장되지 않은 경우
createAlgorithm(algorithmName);
}
Algorithm algorithm = algorithmRepository.findByName(algorithmName);
// Algorithm - Problem 연결
AlgorithmProblem algorithmProblem = AlgorithmProblem.of(algorithm, problem);
algorithmProblemRepository.save(algorithmProblem);
}
return new BoardDetailResponseDTO(boardResponseDTO, problemResponseDTO, solutions);
}

private ProblemResponseDTO getBoardProblem(Board board) {
Long problemId = board.getProblem().getId();
Problem problem = problemRepository.findById(problemId)
.orElseThrow(NotFoundProblemException::new);
return new ProblemResponseDTO(problem, getProblemAlgorithms(problemId));
}

public List<SolutionResponseDTO> getBoardSolutions(Long boardId) {
List<Solution> solutions = solutionRepository.findAllByBoard_Id(boardId);
return solutions.stream()
.map(SolutionResponseDTO::new)
.toList();
}

public List<String> getProblemAlgorithms(Long problemId) {
return algorithmProblemRepository.findAlgorithmNamesByProblemId(problemId);
}

// 알고리즘 메서드
@Transactional
public void createAlgorithm(String name) {
Algorithm newAlgorithm = Algorithm.of(name);
public void createProblem(ProblemCreateRequest problemCreateRequest) {
if (problemRepository.existsByNumberAndPlatform(problemCreateRequest.getNumber(), problemCreateRequest.getPlatform())) {
return;
}
Problem problem = problemRepository.save(Problem.of(problemCreateRequest));
connectProblemAlgorithm(problem, problemCreateRequest.getAlgorithms());
}

private void connectProblemAlgorithm(Problem problem, List<String> algorithms) {
for (String algorithmName : algorithms) {
createAlgorithm(algorithmName);
createAlgorithmProblem(algorithmName, problem);
}
}

private void createAlgorithm(String algorithmName) {
if (algorithmRepository.existsByName(algorithmName)) {
return;
}
Algorithm newAlgorithm = Algorithm.of(algorithmName);
algorithmRepository.save(newAlgorithm);
}

public List<SolutionResponseDTO> getSolutions(Long boardId) {
List<Solution> solutions = solutionRepository.findAllByBoard_Id(boardId);
return solutions.stream()
.map(SolutionResponseDTO::new)
.toList();
private void createAlgorithmProblem(String algorithmName, Problem problem) {
Algorithm algorithm = algorithmRepository.findByName(algorithmName);
AlgorithmProblem algorithmProblem = AlgorithmProblem.of(algorithm, problem);
algorithmProblemRepository.save(algorithmProblem);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.Alchive.backend.controller;

import com.Alchive.backend.config.result.ResultResponse;
import com.Alchive.backend.domain.board.Board;
import com.Alchive.backend.domain.board.BoardStatus;
import com.Alchive.backend.domain.problem.Problem;
import com.Alchive.backend.domain.problem.ProblemDifficulty;
import com.Alchive.backend.domain.problem.ProblemPlatform;
import com.Alchive.backend.domain.user.User;
import com.Alchive.backend.dto.request.ProblemNumberRequest;
import com.Alchive.backend.dto.response.BoardDetailResponseDTO;
import com.Alchive.backend.dto.response.BoardResponseDTO;
import com.Alchive.backend.dto.response.ProblemResponseDTO;
import com.Alchive.backend.service.BoardService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.ResponseEntity;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.Alchive.backend.config.result.ResultCode.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class BoardControllerTest {
@InjectMocks
private BoardController sut;
@Mock
private BoardService boardService;
private Board board;
private Problem problem;
private User user;
private List<String> algorithms = Arrays.asList("BFS", "DFS");

private long problemId = 1L;
private int problemNumber = 1;
private String problemTitle = "testProblem";
private String problemContent = "this is test problem's content";
private String problemUrl = "https://test/problem/1";
private ProblemDifficulty problemDifficulty = ProblemDifficulty.LEVEL0;
private ProblemPlatform problemPlatform = ProblemPlatform.BAEKJOON;
private long userId = 1L;
private String userEmail = "[email protected]";
private String userName = "testUsername";
private long boardId = 1L;
private String boardMemo = "test problem's memo";
private BoardStatus boardStatus = BoardStatus.CORRECT;
private String boardDescription = "test problem's description";
@BeforeEach

public void setUp() {
problem = new Problem(problemId, problemNumber, problemTitle, problemContent, problemUrl, problemDifficulty, problemPlatform);
user = new User(userId, userEmail, userName);
board = new Board(boardId, problem, user, boardMemo, boardStatus, boardDescription);
}

@DisplayName("게시물 저장 여부 조회 - 이미 존재하는 문제")
@Test
public void existBoard() {
BoardDetailResponseDTO response = new BoardDetailResponseDTO(new BoardResponseDTO(board), new ProblemResponseDTO(problem, algorithms), new ArrayList<>());
when(boardService.isBoardSaved(any(User.class), any(ProblemNumberRequest.class))).thenReturn(response);
when(boardService.boardSavedStatus(response)).thenReturn(BOARD_ALREADY_EXIST);

ResponseEntity<ResultResponse> result = sut.isBoardSaved(user, new ProblemNumberRequest(problemPlatform, problemNumber));

Assertions.assertEquals(BOARD_ALREADY_EXIST.getMessage(), result.getBody().getMessage());
}

@DisplayName("게시물 저장 여부 조회 - 존재하지 않는 문제")
@Test
public void newBoard() {
when(boardService.isBoardSaved(any(User.class), any(ProblemNumberRequest.class))).thenReturn(null);
when(boardService.boardSavedStatus(nullable(BoardDetailResponseDTO.class))).thenReturn(BOARD_NOT_EXIST);

ResponseEntity<ResultResponse> result = sut.isBoardSaved(user, new ProblemNumberRequest(problemPlatform, problemNumber));

Assertions.assertEquals(BOARD_NOT_EXIST.getMessage(), result.getBody().getMessage());
}
}
Loading
Loading