프로젝트 작업 일지 23/10/20

song yuheon·2023년 10월 20일
0

Project

목록 보기
9/31
post-thumbnail

User List - ADMIN ONLY에 검색 기능 적용

백엔드

검색과 페이징 동시 적용을 위한 api를 작성해야한다.

검색은 queryDsl을 이용해서 동적으로 구현 할 것이다.

QueryDsl을 위해서는 우선 의존성을 gradle에서 설치하여야한다.

gradle

	// Query Dsl
	implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
	annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
	annotationProcessor "jakarta.annotation:jakarta.annotation-api"
	annotationProcessor "jakarta.persistence:jakarta.persistence-api"

그 후 querydsl을 통한 페이징을 처리하기 위한 컨트롤러와 서비스를 만든다.

Controller

Service

Reqository

레파지스터리에 QuerydslPredicateExecutor을 상속하여 백엔드 api를 마무리한다.

@Repository
public interface UserRepository extends JpaRepository<User, Long>, QuerydslPredicateExecutor<User> {
    Optional<User> findByUsername(String username);
    Page<User> findAll(Pageable pageable);
}

프론트

페이지 제작

검색을 위한 html요소를 작성한다.

<div class="search-form mb-4" style="max-width: 600px; margin: 0 auto;">
    <form th:action="@{/admin/users/v2}" method="get" class="row">
        <div class="col-4 mb-2">
            <input type="text" class="form-control" id="userNameSearch" name="userName"
                   placeholder="사용자 이름 검색">
        </div>
        <div class="col-4 mb-2">
            <select class="form-control" id="userRoleSearch" name="userRole">
                <option value="">사용자 역할 선택</option>
                <option value="ADMIN">ADMIN</option>
                <option value="USER">USER</option>
            </select>
        </div>
        <div class="col-4 mb-2">
            <button type="submit" class="btn btn-primary w-100">검색</button>
        </div>
    </form>
</div>

그리고 페이지 네비게이션을 클릭했을때 가져가는 파라미터에 검색 정보 역시 추가한다.

<div>
    <ul class="pagination">
        <!-- 현재 페이지 번호가 10 이상일 경우, 이전 페이지 그룹의 첫 번째 페이지로 이동 -->

        <li class="page-item" th:if="${(currentPage / 10 ) * 10 > 0}">
            <a class="page-link"
               th:href="@{/admin/users/v2(page=${(currentPage / 10 - 1) * 10}  , userName=${param.userName}, userRole=${param.userRole})}"
               aria-label="Go to the first page of the next group">
                <span aria-hidden="true">«</span>
            </a>
        </li>

        <!-- 현재 페이지 그룹의 페이지 번호들을 표시. 현재 페이지 번호 기준으로 10개 페이지를 표시 -->
        <li th:each="pageNumber : ${#numbers.sequence((currentPage / 10) * 10, (currentPage / 10) * 10 + 9 )}"
            th:if="${pageNumber < totalPages}"
            class="page-item"
            th:class="${pageNumber == currentPage ? 'active' : ''}">
            <a class="page-link"
               th:href="@{/admin/users/v2(page=${pageNumber} , userName=${param.userName}, userRole=${param.userRole})}"
               th:text="${pageNumber + 1}"></a>
        </li>

        <!-- 현재 페이지 그룹의 마지막 페이지가 전체 페이지 수보다 작을 경우, 다음 페이지 그룹의 첫 번째 페이지로 이동 -->
        <li class="page-item" th:if="${(currentPage / 10 + 1) * 10 < totalPages}">
            <a class="page-link"
               th:href="@{/admin/users/v2(page=${(currentPage / 10 + 1) * 10}  , userName=${param.userName}, userRole=${param.userRole})}"
               aria-label="Go to the first page of the next group">
                <span aria-hidden="true">»</span>
            </a>
        </li>
    </ul>
</div>

동작 확인

검색이 잘 되는 것을 확인할 수 있다.

이벤트 관리 페이지


이 페이지는 페이징이 2중으로 되어있다.
이벤트, 해당 이벤트의 도서들 이렇게 말이다.
따라서 하나씩 순차적으로 기능을 넣는것이 합리적이다.
우선 도서 검색 기능 부터 시작하겠다.

도서 검색 기능

백엔드

Controller
bookService.findBookByNameAndRoleAndDonationIdWithPagination(
        bookName,author,publish,status,
        bookDonationEventPageResponseDtoV3.getBookDonationEventResponseDtoV3().get(i).getDonationId(),
        bookPageRequest
Service
@Service
@RequiredArgsConstructor
public class BookService {
    private final BookRepository bookRepository;

    public Page<Book> findBookByNameAndRoleAndDonationIdWithPagination(String bookName, String author, String publish, String status,
                                                                       Long donationId, Pageable pageable) {
        QBook qBook = QBook.book;
        BooleanBuilder builder = new BooleanBuilder();

        builder.and(qBook.bookDonationEvent.donationId.eq(donationId));
        if(!bookName.isEmpty())
            builder.and(qBook.bookName.contains(bookName));
        if(!author.isEmpty())
            builder.and(qBook.bookAuthor.contains(author));
        if(!publish.isEmpty())
            builder.and(qBook.bookPublish.contains(publish));
        if(!status.isEmpty())
            builder.and(qBook.bookStatus.eq(BookStatusEnum.valueOf(status)));

        return bookRepository.findAll(builder,pageable);
    }
}
Repository
public interface BookRepository extends JpaRepository <Book,Long>, QuerydslPredicateExecutor<Book> {

프론트

html

우선 책과 이벤트 검색을 위한 검색 요소들을 만든다.

<div class="search-form mb-4" style="max-width: 1200px; margin: 0 auto;">
    <form th:action="@{/admin/donation/v4}" method="get" class="row">
        <div class="col-2 mb-2">
            <input type="text" class="form-control" id="eventIdSearch" name="eventId"
                   placeholder="Event Id">
        </div>
        <div class="col-2 mb-2">
            <input type="text" class="form-control" id="bookNameSearch" name="bookName"
                   placeholder="book Name">
        </div>
        <div class="col-2 mb-2">
            <input type="text" class="form-control" id="authorSearch" name="author"
                   placeholder="Author">
        </div>
        <div class="col-2 mb-2">
            <input type="text" class="form-control" id="publishSearch" name="publish"
                   placeholder="Publisher">
        </div>
        <div class="col-2 mb-2">
            <select class="form-control" id="status" name="status">
                <option value="">Status</option>
                <option value="SOLD_OUT">SOLD_OUT</option>
                <option value="DONATION">DONATION</option>
            </select>
        </div>
        <div class="col-2 mb-2">
            <button type="submit" class="btn btn-primary w-100">검색</button>
        </div>
    </form>
</div>

검색하거나 페이징을 하였을때 검색 정보들을 다 가져가도록 한다.

<div>
    <ul class="pagination">
        <!-- 현재 페이지 번호가 10 이상일 경우, 이전 페이지 그룹의 첫 번째 페이지로 이동 -->
        <li class="page-item"
            th:if="${(currentBookPage[eventStatus.index] / 10 ) * 10 > 0}">
            <a class="page-link"
               th:onclick="|modifyArrayAndRedirect(${eventStatus.index}, ${(currentBookPage[eventStatus.index] / 10 - 1) * 10},
                ${currentEventPage}, '${eventId}', '${bookName}', '${author}', '${publish}', '${status}')|"
               aria-label="Go to the first page of the next group">
                <span aria-hidden="true">«</span>
            </a>
        </li>

        <li th:each="pageNumber : ${#numbers.sequence((currentBookPage[eventStatus.index] / 10) * 10, (currentBookPage[eventStatus.index] / 10) * 10 + 9)}"
            th:if="${pageNumber < totalPages}"
            class="page-item"
            th:class="${pageNumber == currentBookPage[eventStatus.index] ? 'active' : ''}">
            <a class="page-link"
               th:onclick="|modifyArrayAndRedirect(${eventStatus.index}, ${pageNumber}, ${currentEventPage}, '${eventId}', '${bookName}', '${author}', '${publish}', '${status}')|"
               th:text="${pageNumber + 1}"></a>
        </li>

        <!-- 현재 페이지 그룹의 마지막 페이지가 전체 페이지 수보다 작을 경우, 다음 페이지 그룹의 첫 번째 페이지로 이동 -->
        <li class="page-item"
            th:if="${(currentBookPage[eventStatus.index] / 10 + 1) * 10 < totalPages}">
            <a class="page-link"
               th:onclick="|modifyArrayAndRedirect(${eventStatus.index}, ${(currentBookPage[eventStatus.index] / 10 + 1) * 10}, ${currentEventPage}, '${eventId}', '${bookName}', '${author}', '${publish}', '${status}')|"
               aria-label="Go to the first page of the next group">
                <span aria-hidden="true">»</span>
            </a>
        </li>
    </ul>
</div>
    function modifyArrayAndRedirect(eventIndex, newValue, currentEventPage, eventId, bookName, author, publish, status) {
        currentBookPage[eventIndex] = newValue;
        var updatedBookPageString = currentBookPage.join(',');

        var newUrl = '/admin/donation/v4?bookPage=' + updatedBookPageString + '&eventPage=' + currentEventPage + '&eventId=' + eventId + '&bookName=' + bookName + '&author=' + author + '&publish=' + publish + '&status=' + status;
        window.location.href = newUrl;
    }

트러블 슈팅

업로드중..

검색 버튼을 눌렀을때 검색 필드의 값이 초기화 되어 이후 다른 페이지를 누르면 null값으로 전송되는 문제 발생

profile
backend_Devloper

0개의 댓글