본문 바로가기
Sparta 내일배움캠프 Java 5기/[13주차] Spring 심화 Team Project ✓ 2024. 7. 11.

(24.07.11) 칸반보드의 Column 관리 기능 개발

3 Layer Architecture(Controller - Service - Repository) 중 Service 기준

Column 생성, 삭제 기능

 
// 컬럼 생성
public ColumnInfoResponseDto createColumn(Long boardId,
                                              @Valid ColumnInfoRequestDto columnInfoResponseDto,
                                              User user) {

       ...

        ColumnInfo columnInfo = new ColumnInfo(columnInfoResponseDto, foundBoard);

        columnInfo.updatePosition(foundColumnStatuses.size() + 1);

        columnsRepository.save(columnInfo);

        return new ColumnInfoResponseDto(columnInfo);

    }
    
    
    // 컬럼 삭제
    public void deleteColumn(Long boardId, Long columnInfoId, User user) {

  ...

        validateBoardOwnership(boardId, foundUser);

        ColumnInfo foundColumn = columnsRepository.findById(columnInfoId).orElseThrow(
                () -> new BadRequestException("해당 컬럼은 존재하지 않습니다.")
        );
...

        columnsRepository.delete(foundColumn);

    }

 

  • Column 은 MySQL DB에 저장을 할 때, SQL 쿼리문에 사용되기 때문에 ColumnInfo로 협의하여 엔터티로 지정
  • colmnsRepository는 Service 단에서 형태로  생성자 주입을 받아 느슨한 연결로 구성
@Service
@RequiredArgsConstructor
public class ColumnInfoService {

    private final ColumnsRepository columnsRepository;
  • columnRepository는 interface 로 JpaRepository를 상속 받아,  save delete를 @Transactional을 사용하지 않고 자동으로 트랜잭션을 관리할 수 있음
@Repository
public interface ColumnsRepository extends JpaRepository<ColumnInfo, Long> {

 

 

Column 순서 변경 기능

Controller

 @PostMapping("/manager/boards/{boardId}/columns/{columnsId}/move")
    public ResponseEntity<MessageResponse> moveColumn(@PathVariable(name = "boardId") Long boardId,
                                                      @PathVariable(name = "columnsId") Long columnInfoId,
                                                      @RequestParam(name = "position") Integer newPosition,
                                                      @AuthenticationPrincipal UserDetailsImpl userDetails) {

...
  • Controller에서 무조건 바꿀 위치 position 을 RequestParam 형식으로 받아, PRD 에서 요구했듯이 유동적으로 작동 될 수 있도록 함

Service

@Transactional
    public void moveColumn(Long boardId, Long columnInfoId, Integer newPosition, User user) {

        User foundUser = userRepository.findByUsername(user.getUsername()).orElseThrow(
                () -> new BadRequestException("해당 사용자는 존재하지 않습니다.")
        );
        

        ...
        
        
        List<ColumnInfo> columnList = columnsRepository.findByBoardIdOrderByPosition(boardId);

        newPosition = newPosition > columnList.size() ? columnList.size() : newPosition;

        foundColumn.updatePosition(newPosition);

        Integer position = 1;
        for (ColumnInfo column : columnList) {
            if (!column.getId().equals(foundColumn.getId())) {
                if (position == newPosition) {
                    position++;
                }

                column.updatePosition(position);

                position++;
            }
        }

    }
  • 최대한 성능을 위해 for문을 최소화 할 수 있도록  for 문 하나로 로직을 구성하려고 고민
    • 단, 컬럼이 각 보드마다 과도하게 많은 양으로 저장되지 않을 것으로 가정
  • 가장 이상적인 알고리즘은 아래와 같으나, for문이 두개를 구성을 해야해서 하나로 만들기 위해 위와 같이 조건으로 옮겨질 Column과 Position을 뛰어넘을 수 있도록 바꿈  
    1. RequestParam으로 받은 Column 이 옮겨질 position을 CurrentPosition으로 지정
    2. position이 CurrentPosition 이전의 것들을 for문을 돌면서 1부터 부여
    3. position이 CurrentPosition 이후의 것들을 for문을 돌면서 CurrentPosition 다음부터 부여
  • 중간에 하나의 Column이라도 Position이 update 되지 않을 경우 Save 를 모두  Rollback 하기 위해서 @Transactional을 메서드에 사용
    • save 도  JpaRepository로 인해서 자동으로 Commit 되지만, 그 상위의 메서드 @Transactional이 되어야 Commit 되기 때문