오늘은 컬럼 간 순서이동과 컬럼 삭제 기능을 구현할 것이다.
// ColumnsServices.java
public ColumnsResponseDto deleteColumns(Long columnId) {
Columns columns = findById(columnId);
columnsRepository.delete(columns);
return createResponseDto("컬럼 삭제성공", HttpStatus.NO_CONTENT);
}
삭제기능은 간단히 구현했다.
순서를 바꾸는 api는 처음 구현해보는 거여서 방법을 구상하기 까지 오랫동안 헤메었다.
https://blog.naver.com/yangwonder/222315111294
[최신]게시물 순서바꾸기
https://sir.kr/g5_tip/8789 파일 구성 bbs/ ㄴforwardNum_update.php theme/basic/skin/board/sort/ ㄴli...
blog.naver.com
구글링하다가 한 블로그를 발견했고
안에도 출처가 남겨져 있어서 출처를 타고 들어가보니
리스트에서 게시글 순서(순번) 변경 > SIR
https://sir.kr/g4_tiptech/15990 <br/> <br/>이 팁자료를 보면서 view페이지나 write페이지에서 수정하기 불편해서 리스트페이지에서 수정할 수 있게 수정해 봤습니다. <br/> <br/> <br/> <br/>※ 본 첨부파일은 bas
sir.kr
여기서 한번 더 링크를 타고 들어가니
https://sir.kr/g4_tiptech/15990
게시물 순서 바꾸기 > SIR
갤러리 게시판에서 이미지를 이동할 필요가 있어서 만든 것인데 일반게시판에도 문제 없습니다 <br/> <br/>1. 첨부화일 (write_update.skin.php)을 스킨디렉토리에 복사 <br/> <br/>2. write.skin.php 35행 수정 <br
sir.kr
이런 글이 나왔다. 덕분에 어떻게 구현을 해야 할지 감을 잡을 수 있었다.
@Entity
@Table(name = "columns")
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Columns extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "board_id")
private Board board;
@Enumerated(value = EnumType.STRING)
private CategoryEnum category;
@Column(name = "ordernum")
private Long orderNum;
public void updateComment(ColumnsRequestDto requestDto) {
this.category = CategoryEnum.valueOf(requestDto.getCategory());
}
public void updateOrderNum(Long orderNum) {
this.orderNum = orderNum;
}
public void subtractOrderNum() {
this.orderNum--;
}
public void addOrderNum() {
this.orderNum++;
}
}
먼저 엔티티의 구조를 조금 변경했다. 이제 각 로우에는 정렬 순서를 나타내는 orderNum이라는 컬럼이 추가된다.
public interface ColumnsCustomRepository {
Optional<Long> findMaxOrderNum();
Optional<Long> findOrderNumById(Long id);
List<Columns> findByBetweenColumnIdAndInFrontOfId(Long orginalOrderNum, Long destinationOrderNum);
}
쿼리 메소드를 직접 정의하기 위해 커스텀 레포지토리 인터페이스를 생성했다.
@Repository
@RequiredArgsConstructor
public class ColumnsCustomRepositoryImpl implements ColumnsCustomRepository{
private final JPAQueryFactory jpaQueryFactory;
@Override
public Optional<Long> findMaxOrderNum() {
Long maxOrderNum = jpaQueryFactory.select(columns.orderNum.max())
.from(columns)
.fetchOne();
return Optional.ofNullable(maxOrderNum);
}
@Override
public Optional<Long> findOrderNumById(Long id) {
Long result = jpaQueryFactory.select(columns.orderNum)
.from(columns)
.where(columns.id.eq(id))
.fetch().get(0);
return Optional.ofNullable(result);
}
@Override
public List<Columns> findByBetweenColumnIdAndInFrontOfId(Long smallerOrderNum, Long biggerOrderNum) {
List<Columns> result = jpaQueryFactory.selectFrom(columns)
.where(columns.orderNum.between(smallerOrderNum, biggerOrderNum))
.fetch();
return result;
}
}
커스텀 레포지토리의 구현체이다. findMaxOrderNum()은 db에 존재하는 모든 로우들 중 orderNum컬럼의 최대값을 리턴하는 메소드이다. findByBetweenColumnIdAndInFrontOfId()는 두 orderNum 사이에 있는 모든 로우를 가져오는 메소드이다.
@Transactional
public ColumnsResponseDto changeOrderNum(Long columnId, Long inFrontofId) {
Columns columns = findById(columnId);
Long orginalOrderNum = columns.getOrderNum();
Long destinationOrderNum = columnsRepository.findOrderNumById(inFrontofId).orElseThrow(() -> new IllegalArgumentException("존재하지 않는 컬럼입니다."));
if(orginalOrderNum > destinationOrderNum) {
List<Columns> betweenColumns = columnsRepository.findByBetweenColumnIdAndInFrontOfId(destinationOrderNum, orginalOrderNum - 1);
betweenColumns.forEach(Columns::addOrderNum);
columns.updateOrderNum(destinationOrderNum);
} else if(orginalOrderNum < destinationOrderNum) {
List<Columns> betweenColumns = columnsRepository.findByBetweenColumnIdAndInFrontOfId(orginalOrderNum + 1, destinationOrderNum - 1);
betweenColumns.forEach(Columns::subtractOrderNum);
columns.updateOrderNum(destinationOrderNum - 1);
} else {
throw new IllegalArgumentException("서로 같은 컬럼을 선택했습니다.");
}
return createResponseDto("순서 변경성공", HttpStatus.OK);
}
changeOrderNum을 실행할 때는 파라미터로 위치를 바꾸기 원하는 엔티티의 id와 바꿀 위치에 해당하는 엔티티의 id가 필요하다. 참고로 위치를 바꾸면 바꿀 위치에 해당하는 엔티티 바로 앞에 오게 된다.
지금부터 컬럼 순서 바꾸기 코드의 로직을 예시를 들어 설명할 것이다.
a | b | c | d | e | f | g | h | i | j |
이렇게 엔티티 10개가 있다고 가정하고 orderNum에 의해 정렬돼었다고 가정해보자. a부터 j까지는 각각 1부터 10까지 orderNum이 할당되어 있다.
g(7)를 c(3)앞으로 위치를 옮기는 상황을 가정해서 설명을 하겠다.
우선 g를 제외하고 c와 g사이의 모든 엔티티(c, d, e, f)를 가져온다. 가져온 엔티티들의 orderNum을 한칸씩 뒤로 민다.(orderNum ++)
그러면 엔티티들의 리스트는 이런 모양이 될 것이다.
a | b | c | d | e | f | h | i | j |
마지막으로 g를 c 앞으로(orderNum = 3) 가져온다.
a | b | g | c | d | e | f | h | i | j |
이번엔 b를 h앞으로 옮겨보겠다.
우선 b(2)와 h(8)를 제외하고 b와 h사이의 모든 엔티티를 가져온다. 가져온 엔티티들의 orderNum을 한칸씩 앞으로 당긴다.(orderNum--)
그러면 엔티티들의 리스트는 이런 모양이 될 것이다.
a | c | d | e | f | g | b | h | i | j |
마지막으로 b를 h앞으로(orderNum = 7) 가져온다
컬럼 순서를 바꾸는 코드를 완성해서 테스트를 하던 도중 우연히 오류를 발견하게 된다.
삭제하고 다시 생성을 하면 ordernum이 5부터 시작하는 것이었다. ordernum에 빈자리가 생겨버렸다.
이대로 두면 언젠가 ordernum이 꼬일것 같아서 ordernum에 빈자리를 없애기로 했다.
엔티티를 삭제하면 순서상 뒤에있는 엔티티들을 앞으로 한 칸씩 당겨와야 한다.
public ColumnsResponseDto deleteColumns(Long columnId) {
Long maxOrderNum = columnsRepository.findMaxOrderNum().orElse(0L);
Columns columns = findById(columnId);
Long currentOrderNum = columns.getOrderNum();
List<Columns> betweenColumns = columnsRepository.findByBetweenColumnIdAndInFrontOfId(currentOrderNum - 1, maxOrderNum);
betweenColumns.forEach(Columns::subtractOrderNum);
columnsRepository.delete(columns);
return createResponseDto("컬럼 삭제성공", HttpStatus.NO_CONTENT);
}
그래서 delete메소드에 삭제한 ordernum보다 숫자가 큰 엔티티들을 ordernum의 값을 1씩 깎았다.
이제 필자가 구현할 api는 모두 구현해서 내일부터 프론트엔드를 구현할 것이다.
'내일배움캠프' 카테고리의 다른 글
최종프로젝트 1일차 (0) | 2024.07.18 |
---|---|
Trello프로젝트(심화 프로젝트) 3일차 (0) | 2024.07.16 |
Trello프로젝트(심화 프로젝트) 1일차 (0) | 2024.07.11 |
JPA 심화과정 (0) | 2024.07.10 |
querydsl로 동적 정렬 구현 (0) | 2024.07.05 |