Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags more
Archives
Today
Total
관리 메뉴

요리사에서 IT개발자로

(Spring Boot) Sparta Kanban Board 프로젝트 기능 이슈 및 해결 본문

프로젝트

(Spring Boot) Sparta Kanban Board 프로젝트 기능 이슈 및 해결

H.S-Backend 2024. 7. 16. 12:04

1. 칸반 보드의 특정 컬럼에 새로운 카드를 생성 후에 해당 카드의 Sequence가 자동으로 증가하는 메소드

메소드 시그니처:

@Override
@Transactional
public CommonResponseDto<?> createCardAtKanbanColumn(long kanbanColumnId, CreateCardRequestDto requestDto, UserDetailsImpl userDetails)

  • kanbanColumnId: 카드를 생성할 칸반 컬럼의 ID
  • requestDto: 카드 생성 요청 DTO
  • userDetails: 사용자 정보

컬럼 조회:

KanbanColumn kanbanColumn = columnService.findById(kanbanColumnId);
ColumnResponseDto columnResponseDto = KanbanColumn.of(kanbanColumn);

  • kanbanColumnId를 이용해 컬럼을 조회하고, ColumnResponseDto로 변환합니다.

사용자 검증 및 조회:

User user;
if (Objects.equals(requestDto.getUsername(), userDetails.getUsername())) {
    user = userService.findByUserName(userDetails.getUsername());
} else {
    throw new IllegalArgumentException("사용자 아이디가 일치하지 않습니다.");
}

  • requestDto의 사용자명과 userDetails의 사용자명을 비교하여 일치하면 사용자를 조회합니다. 일치하지 않으면 예외를 발생시킵니다.

카드 생성:

Card card = Card.of(requestDto, user, kanbanColumn);

  • 요청 DTO, 사용자, 칸반 컬럼 정보를 이용해 새로운 카드를 생성합니다.

시퀀스 설정:

int newSequence = 1;
Card maxSequenceCard = cardRepository.findTopByKanbanColumnIdOrderBySequenceDesc(kanbanColumnId);
if (maxSequenceCard != null) {
    int currentSequence = maxSequenceCard.getSequence();
    newSequence = currentSequence + 1;
}
card.setSequence(newSequence);

  • 해당 컬럼에서 가장 큰 시퀀스 값을 가진 카드를 조회하여, 그 시퀀스 값보다 1 증가된 값을 새로운 카드의 시퀀스로 설정합니다. 해당 컬럼에 카드가 없으면 기본 시퀀스를 1로 설정합니다.

카드 저장:

cardRepository.save(card);

결과 반환:

CardResponseDto cardResponseDto = CardResponseDto.of(card);
KanbanColumnAndCardAndUserResponseDto kanbanColumnAndCardAndUserResponseDto = KanbanColumnAndCardAndUserResponseDto.builder()
    .column(columnResponseDto)
    .card(cardResponseDto)
    .build();

return CommonResponseDto.builder()
    .msg("카드 생성이 완료되었습니다.")
    .data(kanbanColumnAndCardAndUserResponseDto)
    .build();

  • 생성된 카드 정보를 CardResponseDto로 변환합니다.
  • 컬럼 정보와 카드 정보를 포함한 응답 객체를 생성하여 반환합니다.
  • 성공 메시지와 함께 반환합니다.

2. 선택한 카드가 현재 컬럼에서 다른 컬럼으로 이동할 때 Sequence를 조정하는 문제를 해결하기 위한 로직이 구현된 메소드

주어진 컬럼 ID, 카드 ID, 대상 컬럼 ID, 이동할 목표 순서를 입력받아 해당 카드를 이동시키고, 나머지 카드들의 순서도 조정합니다.

메소드 시그니처:

public CommonResponseDto<?> moveLocationByColumnId(long kanbanColumnId, long cardId, long targetColumnId, int moveSequence)

  • kanbanColumnId: 카드를 이동시킬 출발 컬럼의 ID
  • cardId: 이동할 카드의 ID
  • targetColumnId: 이동할 대상 컬럼의 ID
  • moveSequence: 이동할 목표 순서

컬럼 조회:

columnService.findById(kanbanColumnId);
KanbanColumn targetColumn = columnService.findById(targetColumnId);
  • 출발 컬럼과 대상 컬럼을 조회합니다.

카드 조회:

Card card = findById(cardId);
  • cardId를 이용해 이동할 카드를 조회합니다.

카드 목록 조회:

List<Card> sourceCardList = cardRepository.findAllByKanbanColumnIdOrderBySequenceAsc(kanbanColumnId);
List<Card> targetCardList = cardRepository.findAllByKanbanColumnIdOrderBySequenceAsc(targetColumnId);

  • 출발 컬럼과 대상 컬럼의 모든 카드를 시퀀스 순으로 조회합니다.

현재 시퀀스 값 저장:

int currentSequence = card.getSequence();

출발 칼럼의 시퀀스 조정:

for (Card c : sourceCardList) {
    if (c.getSequence() > currentSequence) {
        c.setSequence(c.getSequence() - 1);
    }
}

  • 출발 컬럼에서 이동할 카드보다 시퀀스가 큰 카드들의 시퀀스를 1 감소시킵니다.
  • 성공 메시지와 함께 반환합니다.

대상 칼럼의 시퀀스 조정:

for (Card c : targetCardList) {
    if (c.getSequence() >= moveSequence) {
        c.setSequence(c.getSequence() + 1);
    }
}

  • 대상 컬럼에서 목표 시퀀스보다 크거나 같은 카드들의 시퀀스를 1 증가시킵니다.

카드를 대상 칼럼으로 이동:

card.setKanbanColumn(targetColumn);
card.setSequence(moveSequence);

변경된 카드 목록 저장:

cardRepository.saveAll(sourceCardList);
cardRepository.saveAll(targetCardList);
cardRepository.save(card);

결과 반환:

CardResponseDto cardResponseDto = CardResponseDto.of(card);
KanbanColumnAndCardAndUserResponseDto kanbanColumnAndCardAndUserResponseDto = KanbanColumnAndCardAndUserResponseDto.builder()
    .column(KanbanColumn.of(targetColumn))
    .card(cardResponseDto)
    .build();

return CommonResponseDto.builder()
    .msg("카드가 칼럼 사이로 이동되었습니다.")
    .data(kanbanColumnAndCardAndUserResponseDto)
    .build();

  • 이동된 카드 정보를 CardResponseDto로 변환합니다.
  • 컬럼 정보와 카드 정보를 포함한 응답 객체를 생성하여 반환합니다.
  • 성공 메시지와 함께 반환합니다

3. 칸반 보드의 특정 컬럼에서 카드의 순서를 변경하는 문제를 해결하기 위한 로직이 구현된 메소드

메소드 시그니처:

public CommonResponseDto<?> moveCardByColumnId(
long kanbanColumnId, long cardId, int moveSequence)
  • kanbanColumnId: 카드를 이동시킬 컬럼의 ID
  • cardId: 이동할 카드의 ID
  • moveSequence: 이동할 목표 순서

컬럼 조회

KanbanColumn kanbanColumn = columnService.findById(kanbanColumnId);
ColumnResponseDto columnResponseDto = KanbanColumn.of(kanbanColumn);
  • kanbanColumnId를 이용해 컬럼을 조회하고, ColumnResponseDto로 변환합니다.

카드 조회

Card card = findById(cardId);
List<Card> cardList = cardRepository.findAllByKanbanColumnId(kanbanColumnId);
  • cardId를 이용해 이동할 카드를 조회하고, kanbanColumnId를 이용해 해당 컬럼의 모든 카드를 조회합니다.

현재 순서와 이동할 순서 차이 계산:

int currentSequence = card.getSequence();
int difference = moveSequence - currentSequence;
  • 현재 카드의 순서와 이동할 순서의 차이를 계산합니다.

순서 변경 로직:

if (difference != 0) {
    for (Card c : cardList) {
        int cSequence = c.getSequence();
        if (!c.getId().equals(cardId) &&
            (difference > 0 && cSequence > currentSequence && cSequence <= moveSequence) 
            ||
            (difference < 0 && cSequence < currentSequence && cSequence >= moveSequence)
        ) {
            c.setSequence(cSequence + (difference > 0 ? -1 : 1));
        }
    }
    card.setSequence(moveSequence);
    cardRepository.saveAll(cardList);
}

  • 순서 차이가 있을 때만 동작합니다.
  • 모든 카드를 순회하며, 이동할 카드가 아닌 경우 순서 이동 범위 내에 있는 카드의 순서를 조정합니다.
    • 이동 방향에 따라 해당 카드의 순서를 증가 또는 감소시킵니다.
  • 이동할 카드의 순서를 목표 순서로 설정합니다.
  • 변경된 카드 목록을 저장합니다.

결과 반환:

CardResponseDto cardResponseDto = CardResponseDto.of(card);
KanbanColumnAndCardAndUserResponseDto kanbanColumnAndCardAndUserResponseDto = KanbanColumnAndCardAndUserResponseDto.builder()
    .column(columnResponseDto)
    .card(cardResponseDto)
    .build();

return CommonResponseDto.builder()
    .msg("카드 순서 변경이 완료되었습니다.")
    .data(kanbanColumnAndCardAndUserResponseDto)
    .build();

  • 이동된 카드 정보를 CardResponseDto로 변환합니다.
  • 컬럼 정보와 카드 정보를 포함한 응답 객체를 생성하여 반환합니다.
  • 성공 메시지와 함께 반환합니다.

4 .  칸반 보드의 특정 컬럼에서 카드를 삭제하면 나머지 카드들의 Sequence가 감소되는 메소드

입력된 컬럼 ID, 카드 ID, 사용자 정보를 받아 해당 카드를 삭제하고, 나머지 카드들의 시퀀스를 조정

주어진 정보를 기반으로 특정 카드를 삭제하고, 해당 카드의 시퀀스보다 큰 시퀀스를 가진 카드들의 시퀀스를 1씩 감소시켜 저장한 후, 결과를 사용자에게 반환합니다.

메소드 시그니처:

@Override
@Transactional
public CommonResponseDto<?> deleteFindByKanbanColumnIdAndCard(long kanbanColumnId, long cardId, UserDetailsImpl userDetails)

  • kanbanColumnId: 카드를 삭제할 칸반 컬럼의 ID
  • cardId: 삭제할 카드의 ID
  • userDetails: 사용자 정보

카드 조회:

Card cardToDelete = findById(cardId);

사용자 검증:

if (!Objects.equals(cardToDelete.getUsername(), userDetails.getUsername())) {
    throw new IllegalArgumentException("작성자가 일치하지 않습니다.");
}

  • cardToDelete의 작성자와 userDetails의 사용자명이 일치하지 않으면 예외를 발생시킵니다.

삭제할 카드의 시퀀스 값 저장:

int deletedCardSequence = cardToDelete.getSequence();

시퀀스 조정할 카드 목록 조회:

List<Card> cardsToUpdateSequence = cardRepository.findAllByKanbanColumnIdAndSequenceGreaterThan(kanbanColumnId, deletedCardSequence);

  • 삭제할 카드의 시퀀스 값보다 큰 시퀀스를 가진 카드들을 조회합니다.

카드들의 시퀀스 값 조정:

for (Card card : cardsToUpdateSequence) {
    int currentSequence = card.getSequence();
    card.setSequence(currentSequence - 1);
}

카드 목록과 삭제할 카드 저장:

cardRepository.saveAll(cardsToUpdateSequence);
cardRepository.delete(cardToDelete);

결과 반환:

return CommonResponseDto.builder()
    .msg("카드 삭제가 완료되었습니다.")
    .data("Deleted Card ID: " + cardToDelete.getId())
    .build();

  • 성공 메시지와 함께 삭제된 카드의 ID를 반환합니다.

 

칸반 보드 기능 메소드 구현 정리

1. 칸반 보드의 특정 컬럼에 새로운 카드를 생성 후에 해당 카드의 Sequence가 자동으로 증가하는 메소드

최종 코드:

@Override
@Transactional
public CommonResponseDto<?> createCardAtKanbanColumn(long kanbanColumnId,
    CreateCardRequestDto requestDto, UserDetailsImpl userDetails
) {
    KanbanColumn kanbanColumn = columnService.findById(kanbanColumnId);
    ColumnResponseDto columnResponseDto = KanbanColumn.of(kanbanColumn);

    User user;
    if (Objects.equals(requestDto.getUsername(), userDetails.getUsername())) {
       user = userService.findByUserName(userDetails.getUsername());
    } else {
       throw new IllegalArgumentException("사용자 아이디가 일치하지 않습니다.");
    }

    Card card = Card.of(requestDto, user, kanbanColumn);

    int newSequence = 1;
    Card maxSequenceCard = cardRepository.findTopByKanbanColumnIdOrderBySequenceDesc(
       kanbanColumnId);
    if (maxSequenceCard != null) {
       int currentSequence = maxSequenceCard.getSequence();
       newSequence = currentSequence + 1;
    }

    card.setSequence(newSequence);
    cardRepository.save(card);

    CardResponseDto cardResponseDto = CardResponseDto.of(card);
    KanbanColumnAndCardAndUserResponseDto kanbanColumnAndCardAndUserResponseDto = KanbanColumnAndCardAndUserResponseDto.builder()
       .column(columnResponseDto)
       .card(cardResponseDto)
       .build();

    return CommonResponseDto.builder()
       .msg("카드 생성이 완료되었습니다.")
       .data(kanbanColumnAndCardAndUserResponseDto)
       .build();
}

2. 선택한 카드가 현재 컬럼에서 다른 컬럼으로 이동할 때 Sequence를 조정하는 메소드

최종 코드:

@Override
@Transactional
public CommonResponseDto<?> moveLocationByColumnId(long kanbanColumnId, long cardId,
    long targetColumnId, int moveSequence) {
    columnService.findById(kanbanColumnId);
    KanbanColumn targetColumn = columnService.findById(targetColumnId);

    Card card = findById(cardId);

    // 출발 칼럼의 카드 목록 가져오기
    List<Card> sourceCardList = cardRepository.findAllByKanbanColumnIdOrderBySequenceAsc(
       kanbanColumnId);

    // 대상 칼럼의 카드 목록 가져오기
    List<Card> targetCardList = cardRepository.findAllByKanbanColumnIdOrderBySequenceAsc(
       targetColumnId);

    int currentSequence = card.getSequence();

    // 출발 칼럼의 시퀀스 조정
    for (Card c : sourceCardList) {
       if (c.getSequence() > currentSequence) {
          c.setSequence(c.getSequence() - 1);
       }
    }

    // 대상 칼럼의 시퀀스 조정
    for (Card c : targetCardList) {
       if (c.getSequence() >= moveSequence) {
          c.setSequence(c.getSequence() + 1);
       }
    }

    // 카드를 대상 칼럼으로 이동하고 새로운 시퀀스 설정
    card.setKanbanColumn(targetColumn);
    card.setSequence(moveSequence);

    cardRepository.saveAll(sourceCardList);
    cardRepository.saveAll(targetCardList);
    cardRepository.save(card);

    CardResponseDto cardResponseDto = CardResponseDto.of(card);
    KanbanColumnAndCardAndUserResponseDto kanbanColumnAndCardAndUserResponseDto = KanbanColumnAndCardAndUserResponseDto.builder()
       .column(KanbanColumn.of(targetColumn))
       .card(cardResponseDto)
       .build();

    return CommonResponseDto.builder()
       .msg("카드가 칼럼 사이로 이동되었습니다.")
       .data(kanbanColumnAndCardAndUserResponseDto)
       .build();
}

3. 칸반 보드의 특정 컬럼에서 카드의 순서를 변경하는 메소드

최종 코드:

 
@Override
@Transactional
public CommonResponseDto<?> moveCardByColumnId(long kanbanColumnId, long cardId,
    int moveSequence
) {
    KanbanColumn kanbanColumn = columnService.findById(kanbanColumnId);
    ColumnResponseDto columnResponseDto = KanbanColumn.of(kanbanColumn);

    Card card = findById(cardId);
    List<Card> cardList = cardRepository.findAllByKanbanColumnId(kanbanColumnId);

    int currentSequence = card.getSequence();
    int difference = moveSequence - currentSequence;

    if (difference != 0) {
       for (Card c : cardList) {
          int cSequence = c.getSequence();
          if (!c.getId().equals(cardId) &&
             (difference > 0 && cSequence > currentSequence && cSequence <= moveSequence)
             ||
             (difference < 0 && cSequence < currentSequence && cSequence >= moveSequence)
          ) {
             c.setSequence(cSequence + (difference > 0 ? -1 : 1));
          }
       }

       card.setSequence(moveSequence);

       cardRepository.saveAll(cardList);
    }

    CardResponseDto cardResponseDto = CardResponseDto.of(card);

    KanbanColumnAndCardAndUserResponseDto kanbanColumnAndCardAndUserResponseDto = KanbanColumnAndCardAndUserResponseDto.builder()
       .column(columnResponseDto)
       .card(cardResponseDto)
       .build();

    return CommonResponseDto.builder()
       .msg("카드 순서 변경이 완료되었습니다.")
       .data(kanbanColumnAndCardAndUserResponseDto)
       .build();
}

4. 칸반 보드의 특정 컬럼에서 카드를 삭제하면 나머지 카드들의 Sequence가 감소되는 메소드

최종 코드:

@Override
@Transactional
public CommonResponseDto<?> deleteFindByKanbanColumnIdAndCard(long kanbanColumnId,
    long cardId, UserDetailsImpl userDetails
) {
    Card cardToDelete = findById(cardId);

    if (!Objects.equals(cardToDelete.getUsername(), userDetails.getUsername())) {
       throw new IllegalArgumentException("작성자가 일치하지 않습니다.");
    }

    int deletedCardSequence = cardToDelete.getSequence();

    List<Card> cardsToUpdateSequence = cardRepository.findAllByKanbanColumnIdAndSequenceGreaterThan(
       kanbanColumnId, deletedCardSequence);

    for (Card card : cardsToUpdateSequence) {
       int currentSequence = card.getSequence();
       card.setSequence(currentSequence - 1);
    }

    cardRepository.saveAll(cardsToUpdateSequence);
    cardRepository.delete(cardToDelete);

    return CommonResponseDto.builder()
       .msg("카드 삭제가 완료되었습니다.")
       .data("Deleted Card ID: " + cardToDelete.getId())
       .build();
}

 

 

 

반응형