TIL 56 [ 통합 테스트 + JPA 심화 플러스 과제 ( Lv 3-1 ) ]

2025. 5. 14. 22:39·[내일배움캠프-Sparta]/Spring 6기
반응형

16:00 ~ 17:00 통합 테스트 ( 스탠다드 )

2025.05.14 - [[내일배움캠프-Sparta]/Spring 6기 ( + TIL )] - 테스트 코드 (통합 테스트) ( 스탠다드 )

 

테스트 코드 (통합 테스트) ( 스탠다드 )

📅 통합 테스트전에 나왔던 단위 테스트의 3가지 요구사항 중 하나라도 충족하지 못하는 테스트가 통합 테스트에 해당됨. 작은 코드 조각 (단위)을 검증하고, 빠르게 수행되어야 하며, // 외부

dimenshun.tistory.com

🚧 JPA 심화 플러스 과제 ( 도전 Lv3-1 )

🔥 QueryDSL 을 사용하여 검색 기능 만들기

👉 일정을 검색하는 기능을 만들고 싶어요! 검색 기능의 성능 및 사용성을 높이기 위해 QueryDSL을 활용한 쿼리 최적화를 해보세요. ❗Projections를 활용해서 필요한 필드만 반환할 수 있도록 해주세요❗

  • 새로운 API로 만들어주세요.
  • 검색 조건은 다음과 같아요.
    • 검색 키워드로 일정의 제목을 검색할 수 있어요.
      • 제목은 부분적으로 일치해도 검색이 가능해요.
    • 일정의 생성일 범위로 검색할 수 있어요.
      • 일정을 생성일 최신순으로 정렬해주세요.
    • 담당자의 닉네임으로도 검색이 가능해요.
      • 닉네임은 부분적으로 일치해도 검색이 가능해요.
  • 다음의 내용을 포함해서 검색 결과를 반환해주세요.
    • 일정에 대한 모든 정보가 아닌, 제목만 넣어주세요.
    • 해당 일정의 담당자 수를 넣어주세요.
    • 해당 일정의 총 댓글 개수를 넣어주세요.
  • 검색 결과는 페이징 처리되어 반환되도록 합니다.

새로운 API를 만들어야 하므로, 기존에 있는 API를 쓰지 않아야 하므로 컨트롤러부터 시작해서 3-Layer구조로 만들어야 겠다는 생각이 들면서 접근을 시작하였다.  의존성은 이전에 QueryDSL을 적용시켰기 때문에 설명을 생략을 하고 진행하겠다.

✅ Controller

API의 만들기 시작은 역시 컨트롤러부터 이다. 적절한 URL경로를 설정해주고, 1-3의 요구사항과 같이 페이징 처리가 되어야하고, 일정의 생성일 범위로 검색할 수 있어야 한다.

추가적으로, 일정의 제목과 닉네임을 검색할수 있어야한다는 조건이 추가되었다. 컨트롤러에서는 요청/응답을 하는 곳이기 때문에 이를 설정해주고, 서비스로 넘어가도록 하자. 

LocalDate와 LocalDateTime의 차이점

LocalDate은 "yyyy-mm-dd" 타입으로 날짜만 나타나고, LocalDateTime은 "yyyy-mm-dd hh:rr:ss" 로 날짜/시간을 나타난다.

✅ TodoSearchResponse

 

요구 사항에서 검색 결과 반환은 제목이랑, 일정의 담당자수, 일정의 총 댓글 개수를 반환하도록 하라고 하였다.

그래서 새로 응답Dto를 만들어 주었다.

✅ Service

컨트롤러에서 요청해온 비즈니스 로직 처리는 서비스에서 해야한다. 그래서 서비스에서는 페이징 처리만하고 바로 Repository로 findBySearch로 구현체에 정보들을 담아 보내주었다.

Pageable로 페이징 처리를 해주고,  pageable 변수에 담아주었다. 

✅ QTodoRepository

todoRepository는 QTodoRepository를 상속 받고있다. 그래서 구현체를 문제없이 QTodoRepository 인터페이스에 담아서 구현체 클래스를 만들어서 구현하였다. 이전 1-3에서 한 코드들이 있는데, 그것을 활용해서 검색기능을 적절히 QueryDSL을 짜보았다.

@Override
public Page<TodoSearchResponse> findBySearch(String title, LocalDate startDate, LocalDate endDate, String nickname, Pageable pageable) {
    QTodo todo = QTodo.todo;
    QManager manager = QManager.manager;
    QComment comment = QComment.comment;
    QUser user = QUser.user;

    LocalDateTime startDateTime = startDate != null ? startDate.atStartOfDay() : LocalDateTime.MIN;
    LocalDateTime endDateTime = endDate != null ? endDate.atTime(LocalTime.MAX) : LocalDateTime.MAX;

    // 본 쿼리
    List<TodoSearchResponse> content = jpaQueryFactory
            .select(Projections.constructor(
                    TodoSearchResponse.class,
                    todo.title,
                    manager.id.countDistinct(),
                    JPAExpressions.select(comment.count())
                            .from(comment)
                            .where(comment.todo.id.eq(todo.id))
            ))
            .from(todo)
            .leftJoin(todo.managers, manager)
            .leftJoin(manager.user, user)
            .where(
                    titleContains(title),
                    todo.createdAt.between(startDateTime, endDateTime),
                    nicknameContains(nickname)
            )
            .groupBy(todo.id, todo.title)
            .orderBy(todo.createdAt.desc())
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetch();

    Long total = jpaQueryFactory
            .select(todo.countDistinct())
            .from(todo)
            .leftJoin(todo.managers, manager)
            .leftJoin(manager.user, user)
            .where(
                    titleContains(title),
                    todo.createdAt.between(startDateTime, endDateTime),
                    nicknameContains(nickname)
            )
            .fetchOne();

    return new PageImpl<>(content, pageable, total == null ? 0 : total);
}

private BooleanExpression titleContains(String title) {
    return (title == null || title.isBlank()) ? null : QTodo.todo.title.containsIgnoreCase(title);
}

private BooleanExpression nicknameContains(String nickname) {
    return (nickname == null || nickname.isBlank()) ? null: QUser.user.nickname.containsIgnoreCase(nickname);
}

QueryDSL을 제대로 활용한 느낌이었고, 많은 시행착오가 있었다. 도대체 어떻게 진행해야하고, 요구사항에 Projection이라는 것은 뭔지 블로그와 GPT를 활용해서 깨우쳐 나갔다. 

LocalDate를 LocalDateTime으로 변환해주지 않아서, API를 요청하는데 오류가 발생하였다. 그래서 각각 startDate와 endDate를 LocalDateTime으로 변환해주고, GPT와 구글링을 통해 해결하였다.

 

private으로 만든 두 메소드들은 편의상으로 만들어주었는데, 제목이 포함되어있으면 제목의 경우는 무시하고 넘어가며, 닉네임 또한 똑같이 구현되어 있다. 

 

https://sas-study.tistory.com/393

 

[QueryDSL] BooleanExpression을 통한 복잡한 동적쿼리 표현(BooleanBuilder 제거)

안녕하세요. 오늘은 BooleanExpression을 통해서 QueryDSL Repository의 표현을 좀더 직관적으로 볼 수 있도록 리팩토링하는 과정을 소개해보고자 합니다. 사실 이부분에 대해서는 그동안 BooleanBuilder라는

sas-study.tistory.com

 

 

반응형

'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글

TIL 58 [ Spring 실전 프로젝트 ( Day 1 ) - 설계 ( ERD, API, 와이어프레임 ) ]  (0) 2025.05.16
TIL 57 [ AWS( EC2, ELB, RDS ) ]  (0) 2025.05.15
테스트 코드 (통합 테스트) ( 스탠다드 )  (0) 2025.05.14
TIL 55 [ 커리어 데이 ( 커리어여정, 협업 가이드, 면접 팁, 취업 ) ]  (0) 2025.05.13
TIL 54 [ Redis 공부 + 커리어데이( 이력서, 개발 성장기 ,AI 시대 신입 개발자 ) ]  (0) 2025.05.12
'[내일배움캠프-Sparta]/Spring 6기' 카테고리의 다른 글
  • TIL 58 [ Spring 실전 프로젝트 ( Day 1 ) - 설계 ( ERD, API, 와이어프레임 ) ]
  • TIL 57 [ AWS( EC2, ELB, RDS ) ]
  • 테스트 코드 (통합 테스트) ( 스탠다드 )
  • TIL 55 [ 커리어 데이 ( 커리어여정, 협업 가이드, 면접 팁, 취업 ) ]
dimenshun
dimenshun
한 소년의 개발 일기
    반응형
  • dimenshun
    Dev Life Notes
    dimenshun
  • 전체
    오늘
    어제
    • 분류 전체보기 (268)
      • CS (23)
        • 자료구조 (0)
        • 알고리즘 (0)
        • 컴퓨터 구조 (8)
        • 네트워크 (6)
        • 운영체제 (3)
        • DB ( + SQLD ) (5)
        • SW공학 (1)
      • 프로그래밍 (3)
        • Java (0)
        • Spring (0)
        • HTML,CSS (3)
        • JavaScript (0)
      • 개발 툴 (7)
        • Git(버전관리) (1)
        • Docker (3)
        • AWS (2)
        • JSP (1)
      • 코딩테스트(Algorithm) (125)
        • 백준 (6)
        • 프로그래머스 (119)
      • [내일배움캠프-Sparta] (110)
        • Spring 6기 (106)
        • KPT 회고 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    web
    코딩테스트
    개발자
    network
    It
    docker
    백엔드
    OS
    KPT
    Testcode
    SQL
    알고리즘
    CPU
    Til
    컴퓨터구조
    cs
    SQLD
    메모리
    db
    내일배움캠프
    네트워크
    spring
    세션
    배포
    Python
    Java
    트랜잭션
    운영체제
    AWS
    웹
  • hELLO· Designed By정상우.v4.10.3
dimenshun
TIL 56 [ 통합 테스트 + JPA 심화 플러스 과제 ( Lv 3-1 ) ]
상단으로

티스토리툴바