이번 포스팅에서는 게시글 상세 화면에서 이전글 또는 다음글로의 즉각적인 이동을 가능케 하는 기능을 구현해 보고자 한다.
이를 위해 게시글을 클릭할 때 이전글과 다음글의 정보를 함께 조회하기 위해 union all을 사용한 쿼리를 구현했다.
JPQL이 union을 지원하지 않기 때문에 NativeQuery를 활용했다. NativeQuery는 JPQL이 아닌 SQL을 직접 정의하여 사용하는 방식이다. 이를 통해 속도가 빠르고, 복잡한 쿼리 작성 시 직관적이고 이해하기 쉬운 장점이 있지만, 특정 DB에 종속적이며 DB가 변경되면 쿼리를 다시 작성해야 하는 단점이 있다.
/* NoticeRepository */
@Query(value = "select n.notice_id as noticeId, n.title, n.content, n.noticeyn, n.account_id as accountId, n.reg_time as regTime from notice n where n.notice_id = :noticeId" +
" union all (select n.notice_id as noticeId, n.title, n.content, n.noticeyn, n.account_id as accountId, n.reg_time as regTime from Notice n where n.notice_id < :noticeId order by n.notice_id desc limit 1)" +
" union all (select n.notice_id as noticeId, n.title, n.content, n.noticeyn, n.account_id as accountId, n.reg_time as regTime from Notice n where n.notice_id > :noticeId order by n.notice_id asc limit 1)", nativeQuery = true)
List<NoticeDtlDtoInterface> getNoticeDtl(@Param("noticeId") Long noticeId);
NativeQuery를 사용할 때는 DTO 클래스 대신 Getter만 존재하는 인터페이스를 정의해야 한다. 이 때 인터페이스의 이름은 SQL의 리턴되는 칼럼명과 일치해야 한다. 이후에 Service에서 DTO로 변환한 후 Controller에 반환한다.
/* NoticeDtlDtoInterface.java */
public interface NoticeDtlDtoInterface {
Long getNoticeId();
String getTitle();
String getContent();
boolean getNoticeYN();
Long getAccountId();
LocalDateTime getRegTime();
}
/* NoticeDtlDto.java */
@Getter @Setter
@AllArgsConstructor
@NoArgsConstructor
public class NoticeDtlDto {
public Long noticeId;
public String title;
public String content;
public boolean noticeYN;
public String createdBy;
public LocalDateTime regTime;
public NoticeDtlDto(NoticeDtlDtoInterface noticeDtlDtoInterface, String createdBy) {
this.noticeId = noticeDtlDtoInterface.getNoticeId();
this.title = noticeDtlDtoInterface.getTitle();
this.content = noticeDtlDtoInterface.getContent();
this.noticeYN = noticeDtlDtoInterface.getNoticeYN();
this.createdBy = createdBy;
this.regTime = noticeDtlDtoInterface.getRegTime();
}
}
NoticeService에서는 getNoticeDtl 메소드를 통해 해당 공지사항의 세부 정보를 가져온 후, DTO로 변환하여 반환한다.
/* NoticeService */
public List<NoticeDtlDto> getNoticeDtl(Long noticeId) {
return noticeRepository.getNoticeDtl(noticeId)
.stream()
.map(n -> new NoticeDtlDto(n, accountRepository.findById(n.getAccountId()).orElseThrow(EntityNotFoundException::new).getName()))
.collect(Collectors.toList());
}