스프링부트 공부기록(21) - 방명록 작성 :: 게시물 등록, 페이징 처리 구현하기
my code archive
article thumbnail
반응형

1. ✅구현 목표

기능 URL GET/POST 기능 Redirect URL
목록 /guestbook/list GET 목록/페이징/검색  
등록 /guestbook/register GET 입력 화면  
/guestbook/register POST 등록 처리 /guestbook/list
조회 /guestbook/read GET 조회 화면  
수정 /guestbook/modify GET 수정/삭제 기능 화면  
/guestbook/modify POST 수정 처리 /guestbook/read
삭제 /guestbook/remove POST 삭제 처리 /guestbook/list

2. 🤍자동으로 처리되는 날짜/시간 설정

 

BaseEntity 작성

  • 게시글 작성, 수정 시간 같이 자동으로 추가되고 변경되어야 하는 컬럼을 매번 처리하는 일은 번거롭기 때문에 자동으로 처리 가능한 어노테이션을 이용해서 설정한다.
  • @MapedSuperclass : 해당 어노테이션이 적용된 클래스는 테이블로 생성되지 않음.

 

Guestbook 작성

테이블 생성됨

GuestbookRepository 작성

<java />
public interface GuestbookRepository extends JpaRepository<Guestbook,Long>, QuerydslPredicateExecutor<Guestbook> {
}

 

build.gradle 파일에 Querydsl 설정

  • JPA의 쿼리 메서드, @Query를 통해서도 많은 기능을 구현할 수 있으나 Querydsl은 복잡한 검색조건, 조인, 서브쿼리 등의 기능까지 구현이 가능하다.
  • Querydsl를 사용하려면 'Q도메인'이라는 것을 이용해야함 -> build.gradl에 querydsl 관련 라이브러리 추가
<java />
//querydsl 추가
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
querydsl.extendsFrom compileClasspath
}

여기까지 설정하고 build 항목에서 compileQuerydsl을 실행하면 build 폴더 안에 Q로 시작하고 이후는 엔티티 클래스의 이름과 동일한 클래스가 생성됨.

3. 🤍300개의 테스트 데이터 먼저 넣기

<java />
@SpringBootTest
public class GuestbookRepositoryTests {
@Autowired
private GuestbookRepository guestbookRepository;
//300개의 데이터 먼저 삽입
@Test
public void insertDummies(){
IntStream.rangeClosed(1,300).forEach(i->{
Guestbook guestbook = Guestbook.builder().title("Title..."+i).content("Content..."+i).writer("user..."+(i%10)).build();
System.out.println(guestbookRepository.save(guestbook));
});
}
}

 

GuestbookDTO 클래스 작성


4. 🤍1. 목록 처리

  • 화면에서 필요한 목록에 대한 DTO 생성
  • DTO를 Pageable 타입으로 전환
  • Page<Entity>를 화면에서 사용하기 쉬운 DTO의 리스트 등으로 변환
  • 화면에 필요한 페이지 번호 처리

1. 페이지 처리를 위한 PageRequestDTO, PageResultDTO 클래스 작성

  • PageRequestDTO : 페이지 요청 처리
  • PageResultDTO : 페이지 결과 처리
<java />
@Builder
@AllArgsConstructor
@Data
public class PageRequestDTO { //JPA에서 사용하는 Pageable 타입의 객체를 생성하는 것이 목적임.
private int page;
private int size;
private String type;
private String keyword;
public PageRequestDTO(){
this.page=1;
this.size=10;
}
public Pageable getPageable(Sort sort){
return PageRequest.of(page-1,size,sort); //페이지 번호가 0부터 시작하기 때문에 1페이지의 경우 0이 될 수 있도록 page-1
}
}
<java />
@Data
public class PageResultDTO<DTO,EN> {
/*
JPA를 이용하는 Repository에서는 페이지 처리 결과를 Page<Entity> 타입으로 반환하기 때문에
서비스 계층에서 이를 처리하기 위해 별도의 클래스를 만들어야함.
1.Page 엔티티 객체들을 DTO 객체로 변환해서 자료구조로 담아주어야함.
2.화면 출력에 필요한 페이지 정보들을 구성해주어야함.
*/
//DTO 리스트
private List<DTO> dtoList;
//총 페이지 번호
private int totalPage;
//현재 페이지 번호
private int page;
//목록 사이즈
private int size;
//시작 페이지 번호,끝 페이지 번호
private int start, end;
//이전,다음
private boolean prev, next;
//페이지 번호 목록
private List<Integer> pageList;
}

2. 목록 처리 서비스 코드 작성

  • 인터페이스 GuestbookService

  • GuestServiceImpl

3. 페이징 처리

 

페이징 처리를 위해서는 4가지 요소를 생각해 보아야 한다.

  • 화면에서 시작 페이지 번호(start)
  • 화면에서 끝 페이지 번호(end)
  • 이전/다음 이동 링크 여부(prev, next)
  • 현재 페이지 번호(page)

아래 코드를 PageResultDTO 클래스에 추가한다.

<java />
public PageResultDTO(Page<EN> result, Function<EN,DTO> fn){
dtoList = result.stream().map(fn).collect(Collectors.toList());
totalPage = result.getTotalPages();
makePageList(result.getPageable());
}
private void makePageList(Pageable pageable){
this.page=pageable.getPageNumber()+1; //0부터 시작하므로
this.size=pageable.getPageSize();
//temp end page
int tempEnd = (int)(Math.ceil(page/10.0))*10;
start = tempEnd-9;
prev=start>1;
end=totalPage>tempEnd?tempEnd:totalPage;
next=totalPage>tempEnd;
pageList= IntStream.rangeClosed(start,end).boxed().collect(Collectors.toList());
}

 

4. 컨트롤러 코드 작성

 

5. 목록 화면 list.html 코드 작성

6. 하단에 페이징 처리 코드 추가

<java />
<ul class="pagination h-100 justify-content-center align-items-center">
<li class="page-item " th:if="${result.prev}">
<a class="page-link" th:href="@{/guestbook/list(page=${result.start -1}, type=${pageRequestDTO.type}, keyword=${pageRequestDTO.keyword})}" tabindex="-1">Previous</a>
</li>
<li th:class=" 'page-item '+${result.page == page?'active':''}" th:each="page:${result.pageList}">
<a class="page-link" th:href="@{/guestbook/list(page=${page}, type=${pageRequestDTO.type}, keyword=${pageRequestDTO.keyword})}">
[[${page}]]
</a>
</li>
<li class="page-item" th:if="${result.next}">
<a class="page-link" th:href="@{/guestbook/list(page=${result.end +1}, type=${pageRequestDTO.type}, keyword=${pageRequestDTO.keyword})}">Next</a>
</li>
</ul>

7. 페이징 완료

 

5. 🤍2. 등록 처리

 

1. 등록 컨트롤러 작성

 

2. 등록 화면 register.html 작성

<html />
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic::setContent(~{this::content})}">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- Bootstrap core JS-->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js"></script>
<!-- Core theme JS-->
<script th:src="@{/js/scripts.js}"></script>
</head>
<body>
<th:block th:fragment="content">
<h1 class="mt-4">GuestBook Register Page</h1>
<form th:action="@{/guestbook/register}" th:method="post">
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" name="title" placeholder="Enter Title">
</div>
<div class="form-group">
<label>Content</label>
<textarea class="form-control" rows="5" name="content"></textarea>
</div>
<div class="form-group">
<label>Writer</label>
<input type="text" class="form-control" name="writer" placeholder="Enter Writer">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</th:block>
</body>
</th:block>
</html>

 

3. 등록 구현 완료

4. 등록 완료 모달창 구현

반응형
profile

my code archive

@얼레벌레 개발자👩‍💻

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

반응형