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

✅구현 목표

기능 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

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

 

BaseEntity 작성

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

 

Guestbook 작성

테이블 생성됨

GuestbookRepository 작성

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

 

build.gradle 파일에 Querydsl 설정

  • JPA의 쿼리 메서드, @Query를 통해서도 많은 기능을 구현할 수 있으나 Querydsl은 복잡한 검색조건, 조인, 서브쿼리 등의 기능까지 구현이 가능하다.
  • Querydsl를 사용하려면 'Q도메인'이라는 것을 이용해야함 -> build.gradl에 querydsl 관련 라이브러리 추가
 //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로 시작하고 이후는 엔티티 클래스의 이름과 동일한 클래스가 생성됨.

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

@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 클래스 작성


🤍1. 목록 처리

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

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

  • PageRequestDTO : 페이지 요청 처리
  • PageResultDTO : 페이지 결과 처리
@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
    }
}
@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 클래스에 추가한다.

   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. 하단에 페이징 처리 코드 추가

    <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. 페이징 완료

 

🤍2. 등록 처리

 

1. 등록 컨트롤러 작성

 

2. 등록 화면 register.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

@얼레벌레 개발자👩‍💻

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

반응형