Skip to content

[FEAT]: 기수별 크루 목록 조회 API 추가 - #172

Merged
poketopa merged 1 commit into
developfrom
feat/171-crew-list
Jul 21, 2026
Merged

[FEAT]: 기수별 크루 목록 조회 API 추가#172
poketopa merged 1 commit into
developfrom
feat/171-crew-list

Conversation

@sun007021

Copy link
Copy Markdown
Member

🔗 관련 이슈

📝 작업 내용

조직 문서의 소속 정보를 기준으로 크루의 이름, 문서 UUID, 분야를 조회하는 API를 추가했습니다.

주요 변경사항

  • GET /document/crews?generation={generation} API 추가
  • 기수 소속 크루의 모든 조직 정보를 조회하는 projection 쿼리 구현
  • 조회 전용 query port 및 불변 read model 추가
  • 크루 문서 제목에서 이름을 추출하는 로직 구현
  • 조직 문서 제목을 BACKEND, FRONTEND, ANDROID 분야로 변환
  • 크루별 조회 결과를 일급 컬렉션으로 그룹화 및 캡슐화
  • 기수별 크루 조회 전용 컨트롤러 분리
  • 누락·타입 불일치 요청 파라미터의 VALIDATION_ERROR 처리 추가
  • 이름 오름차순 정렬 및 빈 조회 결과 반환 지원

검증 항목

  • 요청한 기수의 크루만 조회되는지 검증
  • 이름과 문서 UUID 응답 검증
  • 허용된 분야 매핑 검증
  • 분야 없음·알 수 없음·복수 분야일 때 null 반환 검증
  • 크루 제목 형식별 이름 추출 검증
  • 이름 오름차순 정렬 검증
  • 조회 결과가 없을 때 빈 배열 반환 검증
  • 기수 누락·문자열·0·음수 요청의 검증 실패 응답 확인

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 특정 기수에 속한 크루들의 이름, 문서 UUID, 분야 정보를 조회하는 기능을 추가합니다. 조직 문서의 소속 정보를 기반으로 데이터를 효율적으로 조회하고, 크루별로 정보를 그룹화하여 클라이언트가 필요로 하는 형태로 가공하여 제공합니다. 또한, 잘못된 요청 파라미터에 대한 검증 및 예외 처리를 강화하여 API의 안정성을 높였습니다.

Highlights

  • API 추가: 기수별 크루 목록을 조회할 수 있는 GET /document/crews?generation={generation} API를 추가했습니다.
  • 데이터 조회 및 가공: 기수 소속 크루 정보를 조회하기 위한 Projection 쿼리와 조회 전용 Query Port, Read Model을 구현했습니다.
  • 크루 프로필 추출 로직: 크루 문서 제목에서 이름을 추출하고, 조직 문서 제목을 기반으로 분야(BACKEND, FRONTEND, ANDROID)를 매핑하는 로직을 추가했습니다.
  • 예외 처리 강화: 요청 파라미터 누락이나 타입 불일치 시 VALIDATION_ERROR를 반환하도록 예외 처리기를 보강했습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new feature to query crew lists by generation, adding the /document/crews endpoint, corresponding service logic, and database queries. The review feedback highlights several critical improvement opportunities: optimizing database queries by replacing DTO projection with Fetch Join to avoid hardcoded package paths, preventing potential NullPointerExceptions in name extraction and list copying, and significantly improving test suite performance by replacing @DirtiesContext with @Transactional or manual database cleanup.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +34 to +52
@Override
@Query("""
SELECT new com.wooteco.wiki.document.repository.GenerationCrewOrganizationReadModel(
crewDocument.title,
crewDocument.uuid,
organizationDocument.title
)
FROM DocumentOrganizationLink documentOrganizationLink
JOIN documentOrganizationLink.crewDocument crewDocument
JOIN documentOrganizationLink.organizationDocument organizationDocument
WHERE crewDocument IN (
SELECT generationLink.crewDocument
FROM DocumentOrganizationLink generationLink
WHERE generationLink.organizationDocument.title = :generationTitle
)
""")
List<GenerationCrewOrganizationReadModel> findAllByGenerationTitle(
@Param("generationTitle") String generationTitle
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

JPQL 쿼리 내에서 DTO 프로젝션(new com.wooteco.wiki.document.repository.GenerationCrewOrganizationReadModel(...))을 사용하고 있습니다. 이는 DTO의 패키지 경로를 하드코딩하게 만들어 리팩터링을 어렵게 하고 유지보수성을 떨어뜨립니다. N+1 문제를 해결하고 타입 안정성을 확보하기 위해, DTO 프로젝션 대신 Fetch Join을 사용하여 DocumentOrganizationLink 엔티티와 연관된 crewDocument, organizationDocument를 함께 조회한 후 애플리케이션 레이어에서 변환하는 방식을 권장합니다.

    @Query("""
            SELECT dol
            FROM DocumentOrganizationLink dol
            JOIN FETCH dol.crewDocument crewDocument
            JOIN FETCH dol.organizationDocument organizationDocument
            WHERE crewDocument IN (
                SELECT generationLink.crewDocument
                FROM DocumentOrganizationLink generationLink
                WHERE generationLink.organizationDocument.title = :generationTitle
            )
            """)
    List<DocumentOrganizationLink> findAllByGenerationTitle(
            @Param("generationTitle") String generationTitle
    );
References
  1. To resolve N+1 query problems, prefer using a fetch join over DTO projection. DTO projection can introduce maintenance issues by hardcoding DTO package paths in JPQL queries, making refactoring difficult. Fetch join provides a type-safe alternative without this drawback.

Comment on lines +21 to +22
public Optional<String> extractName(String title) {
int delimiterIndex = title.indexOf(NAME_DELIMITER);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

title 매개변수가 null일 경우 title.indexOf() 호출 시 NullPointerException이 발생할 수 있습니다. 방어적 프로그래밍 관점에서 null 체크를 추가하여 안전하게 처리하는 것이 좋습니다.

    public Optional<String> extractName(String title) {
        if (title == null) {
            return Optional.empty();
        }
        int delimiterIndex = title.indexOf(NAME_DELIMITER);

Comment on lines +76 to +78
private GenerationCrewCandidate {
organizationTitles = List.copyOf(organizationTitles);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

List.copyOf()는 리스트 내에 null 요소가 포함되어 있을 경우 NullPointerException을 발생시킵니다. 만약 organizationTitlesnull이 포함될 가능성이 있다면 안전하게 null 요소를 필터링한 후 불변 리스트로 변환하는 것이 안전합니다.

        private GenerationCrewCandidate {
            organizationTitles = organizationTitles.stream()
                    .filter(java.util.Objects::nonNull)
                    .toList();
        }

Comment on lines +27 to +28
import org.springframework.test.annotation.DirtiesContext;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

webEnvironment = SpringBootTest.WebEnvironment.NONE 환경에서 실행되는 통합 테스트이므로, 매 테스트 메서드마다 스프링 컨텍스트를 새로 띄우는 @DirtiesContext 대신 @Transactional을 사용하는 것이 좋습니다. 이를 통해 테스트 실행 속도를 획기적으로 개선할 수 있습니다.

Suggested change
import org.springframework.test.annotation.DirtiesContext;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@org.springframework.transaction.annotation.Transactional

import org.springframework.test.annotation.DirtiesContext;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)는 매 테스트 메서드가 끝날 때마다 스프링 컨텍스트를 재시작하므로 테스트 수행 시간을 매우 느리게 만듭니다. RANDOM_PORT 환경이므로 @Transactional 롤백은 적용되지 않지만, @AfterEach 메서드를 추가하여 레포지토리를 직접 비워주는(manual cleanup) 방식으로 변경하면 컨텍스트 재시작 없이 훨씬 빠르게 테스트를 수행할 수 있습니다.

@poketopa
poketopa merged commit 3f9ea52 into develop Jul 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants