Skip to content

[FEAT] 레포지토리 목록/상세 조회 및 삭제 API 구현#38

Merged
boogiewooki02 merged 11 commits into
SeCause:developfrom
boogiewooki02:feat/36-repository-management
Jun 14, 2026
Merged

[FEAT] 레포지토리 목록/상세 조회 및 삭제 API 구현#38
boogiewooki02 merged 11 commits into
SeCause:developfrom
boogiewooki02:feat/36-repository-management

Conversation

@boogiewooki02

@boogiewooki02 boogiewooki02 commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

‼️ 관련 이슈

close #36

🔎 개요

사용자가 등록한 GitHub 레포지토리의 목록·대시보드를 조회하고 삭제할 수 있는 API를 구현했습니다.


📝 작업 내용

  • 레포지토리 목록 조회 API 구현
  • 레포지토리 대시보드 조회 API 구현
  • 레포지토리 soft delete API 구현
  • 사용자 소유권 및 삭제 여부 검증
  • 언어 및 심각도별 이슈 통계 조회
  • Swagger 문서 및 단위 테스트 작성

👀 변경 사항

  • repositories 테이블에 owner, line_count 컬럼 추가가 필요합니다. (직접 쿼리 실행해서 추가해뒀습니다.)
  • 타인 소유 또는 존재하지 않는 레포지토리 접근은 동일하게 404를 반환합니다.
  • 삭제 시 실제 데이터를 제거하지 않고 is_deleted를 변경합니다. (소프트 삭제 진행)


✅ 체크리스트

  • label, milestone, assignees, reviewers 등을 지정했습니다.
  • 성능 개선/최적화 관련 내용이 있는지 확인했습니다.
  • 변경 사항에 대한 테스트를 했습니다.
  • 테스트 시 사용한 로그를 삭제했습니다.

Summary by CodeRabbit

출시 노트

  • 새로운 기능
    • 저장소 목록 조회 추가: 계정명과 검색어로 저장소를 필터링해 확인
    • 저장소 대시보드 추가: 코드 통계, 분석 상태/진행률, 요청·완료 시각, 실패 사유, 취약점 심각도 및 이슈 유형별 카운트 제공
    • 저장소 삭제 기능 추가
  • 개선 사항
    • GitHub 저장소 소유자 정보 관리 및 저장소 라인 수 기반 분석 지표 반영 강화

@boogiewooki02 boogiewooki02 requested a review from dldusgh318 June 12, 2026 14:31
@boogiewooki02 boogiewooki02 self-assigned this Jun 12, 2026
@boogiewooki02 boogiewooki02 added the ✨ FEAT New feature or request label Jun 12, 2026
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@boogiewooki02, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 54 minutes and 41 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4a72df30-3fdd-48de-9950-ed7d3cbdf8fd

📥 Commits

Reviewing files that changed from the base of the PR and between aea11ec and 4acfa84.

📒 Files selected for processing (1)
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/service/ProjectRepositoryService.java
📝 Walkthrough

Walkthrough

이 PR은 사용자가 등록한 GitHub 레포지토리를 조회, 대시보드 확인, 삭제할 수 있는 REST API를 구현합니다. 엔티티 스키마 확장, 9개의 응답 DTO, QueryDSL 기반 데이터 접근, 서비스 로직, 3개 HTTP 엔드포인트, 통합 테스트, 그리고 분석 요청 흐름과의 통합을 포함합니다.

Changes

Repository Management API Implementation

Layer / File(s) Summary
엔티티 및 데이터베이스 스키마 확장
src/main/java/SeCause/SeCause_be/domain/projectRepository/entity/ProjectRepository.java, src/main/resources/schema.sql, src/main/java/SeCause/SeCause_be/domain/analysis/service/AnalysisRequestPersistenceService.java
ProjectRepository 엔티티에 ownerlineCount 필드를 추가하고, 생성자/팩토리 메서드를 업데이트합니다. 스키마 마이그레이션에서 기존 데이터의 ownergithub_link에서 파생시키고, lineCount를 0으로 초기화합니다. AnalysisRequestPersistenceService는 ProjectRepository 생성 시 GitHub 저장소 소유자 정보를 함께 전달합니다.
저장소 접근 계약 및 인터페이스
src/main/java/SeCause/SeCause_be/domain/projectRepository/repository/ProjectRepositoryRepository.java, src/main/java/SeCause/SeCause_be/domain/projectRepository/repository/ProjectRepositoryRepositoryCustom.java
ProjectRepositoryRepository는 ProjectRepositoryRepositoryCustom을 상속하여 커스텀 조회 메서드를 지원합니다. 소유자 검증용 findByRepositoryIdAndUserUserIdAndDeletedFalse 메서드를 추가하여 사용자 권한을 확인합니다.
응답 DTO 정의
src/main/java/SeCause/SeCause_be/domain/projectRepository/dto/*Response.java
API 응답 형식을 위해 9개의 record DTO(RepositoryAnalysisResponse, RepositoryCodeDetailsResponse, RepositoryDashboardResponse, RepositoryDashboardSummaryResponse, RepositoryIssueTypeCountResponse, RepositoryListResponse, RepositorySeverityBreakdownResponse, RepositorySeverityCountResponse, RepositorySummaryResponse, RepositoryDashboardQueryResult)를 정의하여 응답 데이터 구조를 표준화합니다. RepositorySummaryResponseowner/name이 null/공백인 경우를 처리하는 createFullName 헬퍼를 포함합니다.
QueryDSL 기반 데이터 접근 구현
src/main/java/SeCause/SeCause_be/domain/projectRepository/repository/ProjectRepositoryRepositoryImpl.java
목록 조회와 대시보드 조회를 위해 JOIN, GROUP BY, 조건절을 조합하여 저장소 메타데이터, 언어 목록, 이슈 카운트(유형별/심각도별)를 효율적으로 조회합니다. 레포지토리별 언어, 이슈 카운트를 별도 쿼리로 조회하여 사전 형태로 누적한 후 DTO로 매핑합니다.
서비스 비즈니스 로직
src/main/java/SeCause/SeCause_be/domain/projectRepository/service/ProjectRepositoryService.java
저장소 목록/대시보드 조회와 삭제를 수행합니다. getRepositories는 요약 목록을 반환하고, getRepositoryDashboard는 심각도별 집계와 비율 계산을 포함하여 대시보드 응답을 조합합니다. deleteRepository는 소유자 검증 후 soft delete 처리를 수행하고, 접근 불가 시 PROJECT_REPOSITORY_NOT_FOUND 예외를 발생시킵니다.
API 계약 및 컨트롤러
src/main/java/SeCause/SeCause_be/domain/projectRepository/controller/ProjectRepositoryApi.java, src/main/java/SeCause/SeCause_be/domain/projectRepository/controller/ProjectRepositoryController.java
ProjectRepositoryApi 인터페이스로 3개 엔드포인트(GET /api/repositories, GET /api/repositories/{id}, DELETE /api/repositories/{id})의 스펙과 200/401/404 응답 및 Swagger 예시를 정의합니다. ProjectRepositoryController는 이를 구현하여 인증된 사용자 컨텍스트를 기반으로 서비스 메서드를 호출합니다.
컨트롤러 및 서비스 테스트
src/test/java/SeCause/SeCause_be/domain/projectRepository/controller/ProjectRepositoryControllerTest.java, src/test/java/SeCause/SeCause_be/domain/projectRepository/service/ProjectRepositoryServiceTest.java
엔드포인트 응답 성공/실패, 대시보드 조합, fullName 생성(null/공백 처리), 심각도별 비율 계산, 삭제 처리, 소유자 검증 예외를 검증하는 8개의 테스트 케이스를 작성합니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50분

Possibly related PRs

  • SeCause/SeCause-BE#35: 메인 PR의 AnalysisRequestPersistenceService.saveAnalysisRequest(...)에서 ProjectRepository.create(...) 호출 인자로 GitHub owner 로그인 값을 추가한 변경이 검색된 PR #35의 분석 요청 생성/퍼시스팅 흐름과 직접적으로 맞물립니다.
  • SeCause/SeCause-BE#34: 메인 PR의 AnalysisRequestPersistenceService 변경(owner 전달)이 검색된 PR #34에서 제공한 GithubRepositoryResponse/GithubAccountResponse의 owner.login 필드와 직접 연결됩니다.

Suggested reviewers

  • dldusgh318

Poem

🐰 레포지토리 목록 조회하고, 대시보드 살펴보고~
QueryDSL로 쿼리 펼치고, 심각도별 집계하고~
사용자 권한 검증해서 안전하게 삭제하고~
응답 DTO로 감싸서 클라이언트에 전달하네!
8개 테스트로 든든하게 검증하고 ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 레포지토리 목록/상세 조회 및 삭제 API 구현이라는 주요 변경 사항을 명확하고 간결하게 요약하고 있습니다.
Linked Issues check ✅ Passed PR 구현이 Issue #36의 모든 코딩 요구사항을 충족합니다: GET /repositories, GET /repositories/{repositoryId}, DELETE /repositories/{repositoryId} 엔드포인트 구현, 권한 검증, 예외 처리, 응답 DTO 작성 완료.
Out of Scope Changes check ✅ Passed 모든 변경사항이 Issue #36의 범위 내에 있습니다. 레포지토리 관리 API 구현, 데이터베이스 스키마 확장(owner, line_count 컬럼), 엔티티 및 서비스 로직, 테스트 코드가 모두 연관된 기능 구현입니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/test/java/SeCause/SeCause_be/domain/projectRepository/service/ProjectRepositoryServiceTest.java (1)

129-129: ⚡ Quick win

부동소수점 비교는 isCloseTo()를 사용하는 것이 더 안전합니다.

isEqualTo(3.23)는 부동소수점 정밀도 문제로 인해 예상치 못한 테스트 실패를 유발할 수 있습니다. AssertJ의 isCloseTo()를 사용하면 허용 오차 범위 내에서 비교할 수 있습니다.

♻️ 제안하는 수정안
-        assertThat(response.severityBreakdown().getFirst().percentage()).isEqualTo(3.23);
+        assertThat(response.severityBreakdown().getFirst().percentage()).isCloseTo(3.23, within(0.01));

import static org.assertj.core.data.Percentage.withPercentage; 또는 import static org.assertj.core.data.Offset.offset;를 추가해 within() 메서드를 사용할 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/SeCause/SeCause_be/domain/projectRepository/service/ProjectRepositoryServiceTest.java`
at line 129, In ProjectRepositoryServiceTest replace the exact floating-point
equality assertion on response.severityBreakdown().getFirst().percentage()
(currently assertThat(...).isEqualTo(3.23)) with a tolerant comparison such as
AssertJ's isCloseTo(..., offset(...)) or isCloseTo(..., withPercentage(...));
add the corresponding static import (import static
org.assertj.core.data.Offset.offset; or import static
org.assertj.core.data.Percentage.withPercentage;) and choose a small
offset/percentage (e.g. offset(0.01) or withPercentage(1.0)) to avoid
floating-point precision failures.
src/main/java/SeCause/SeCause_be/domain/projectRepository/controller/ProjectRepositoryController.java (1)

24-62: ⚖️ Poor tradeoff

서비스 메서드 호출 시 매개변수 순서가 일관되지 않습니다.

  • Line 31-35: getRepositories(userId, accountName, keyword) - userId가 첫 번째
  • Line 46-49: getRepositoryDashboard(repositoryId, userId) - repositoryId가 첫 번째
  • Line 60: deleteRepository(repositoryId, userId) - repositoryId가 첫 번째

목록 조회는 사용자 중심, 대시보드/삭제는 리소스 중심이라는 설계 의도는 이해되지만, 향후 유지보수 시 혼란을 줄 수 있습니다. 가능하다면 서비스 계층에서 매개변수 순서를 통일하는 것을 고려해보세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/SeCause/SeCause_be/domain/projectRepository/controller/ProjectRepositoryController.java`
around lines 24 - 62, Controller calls use inconsistent parameter ordering
across getRepositories, getRepositoryDashboard, and deleteRepository;
standardize the service API so all methods use the same parameter order (pick
one convention, e.g., userId first). Update the service interface and its
implementations for getRepositoryDashboard(...) and deleteRepository(...) to
accept (userId, repositoryId) to match getRepositories(userId, accountName,
keyword), then change the controller calls in ProjectRepositoryController to
pass userPrincipal.userId() first for getRepositoryDashboard and
deleteRepository; also update any other call sites and tests to the new
signature.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/SeCause/SeCause_be/domain/projectRepository/dto/RepositorySummaryResponse.java`:
- Around line 24-53: In RepositorySummaryResponse.of(...) (the static factory
method) protect construction of fullName (currently owner + "/" + name) from
null owner or name by building it null-safely: check owner and name and join
only non-null/ non-empty parts with "/" (or use empty string when both null),
then pass that computed fullName into the RepositorySummaryResponse constructor;
update the method signature usage for fullName so it never becomes "null/..." or
".../null" when owner or name are null.

In
`@src/test/java/SeCause/SeCause_be/domain/projectRepository/controller/ProjectRepositoryControllerTest.java`:
- Around line 33-94: Add a unit test for the missing getRepositories endpoint:
create a UserPrincipal, stub
projectRepositoryService.getRepositories(userPrincipal.userId(), accountName,
keyword) to return a RepositoryListResponse (e.g., empty list), call
projectRepositoryController.getRepositories(userPrincipal, accountName,
keyword), assert response.isSuccess() is true, response.message() equals "레포지토리
목록 조회가 완료됐습니다.", response.result() isSameAs the stubbed RepositoryListResponse,
and verify projectRepositoryService.getRepositories(...) was invoked with the
correct args; place the test alongside the existing
getRepositoryDashboardReturnsSuccessResponse and
deleteRepositoryReturnsSuccessResponse tests.

---

Nitpick comments:
In
`@src/main/java/SeCause/SeCause_be/domain/projectRepository/controller/ProjectRepositoryController.java`:
- Around line 24-62: Controller calls use inconsistent parameter ordering across
getRepositories, getRepositoryDashboard, and deleteRepository; standardize the
service API so all methods use the same parameter order (pick one convention,
e.g., userId first). Update the service interface and its implementations for
getRepositoryDashboard(...) and deleteRepository(...) to accept (userId,
repositoryId) to match getRepositories(userId, accountName, keyword), then
change the controller calls in ProjectRepositoryController to pass
userPrincipal.userId() first for getRepositoryDashboard and deleteRepository;
also update any other call sites and tests to the new signature.

In
`@src/test/java/SeCause/SeCause_be/domain/projectRepository/service/ProjectRepositoryServiceTest.java`:
- Line 129: In ProjectRepositoryServiceTest replace the exact floating-point
equality assertion on response.severityBreakdown().getFirst().percentage()
(currently assertThat(...).isEqualTo(3.23)) with a tolerant comparison such as
AssertJ's isCloseTo(..., offset(...)) or isCloseTo(..., withPercentage(...));
add the corresponding static import (import static
org.assertj.core.data.Offset.offset; or import static
org.assertj.core.data.Percentage.withPercentage;) and choose a small
offset/percentage (e.g. offset(0.01) or withPercentage(1.0)) to avoid
floating-point precision failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bdce35e7-3dac-4341-aaf7-82bb1985afab

📥 Commits

Reviewing files that changed from the base of the PR and between 36a3dbc and 6548eda.

📒 Files selected for processing (20)
  • src/main/java/SeCause/SeCause_be/domain/analysis/service/AnalysisRequestPersistenceService.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/controller/ProjectRepositoryApi.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/controller/ProjectRepositoryController.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/dto/RepositoryAnalysisResponse.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/dto/RepositoryCodeDetailsResponse.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/dto/RepositoryDashboardResponse.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/dto/RepositoryDashboardSummaryResponse.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/dto/RepositoryIssueTypeCountResponse.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/dto/RepositoryListResponse.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/dto/RepositorySeverityBreakdownResponse.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/dto/RepositorySeverityCountResponse.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/dto/RepositorySummaryResponse.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/entity/ProjectRepository.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/repository/ProjectRepositoryRepository.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/repository/ProjectRepositoryRepositoryCustom.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/repository/ProjectRepositoryRepositoryImpl.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/repository/RepositoryDashboardQueryResult.java
  • src/main/java/SeCause/SeCause_be/domain/projectRepository/service/ProjectRepositoryService.java
  • src/test/java/SeCause/SeCause_be/domain/projectRepository/controller/ProjectRepositoryControllerTest.java
  • src/test/java/SeCause/SeCause_be/domain/projectRepository/service/ProjectRepositoryServiceTest.java

@dldusgh318 dldusgh318 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

확인했습니다. pr에 대해 1가지 코멘트 전달드립니다

  1. 말씀하신대로 owner, line_count 컬럼이 엔티티에 추가됐는데, 현재 변경사항에는 DB 스키마 변경을 재현할 수 있는 migration/schema 반영이 없어 보입니다.
    dev 환경은 ddl-auto: validate라서 직접 쿼리를 실행하지 않은 환경에서는 애플리케이션 부팅 시 validation 에러가 날 수 있을 것 같아요. schema.sql 또는 마이그레이션 파일로 컬럼 추가를 코드에 포함하는 게 좋을 거 같습니다!

다른 부분들은 리뷰 코멘트 확인 부탁드립니다 수고하셨습니다~!!

public record RepositoryDashboardSummaryResponse(
long totalIssues,
long criticalIssues,
long highIssues,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

해당 부분의 high, medium, low 이슈 수는 대시보드 와이어프레임 뷰의 어느 부분에 해당하는걸까요? 찾지못해서 코멘트 남겨둡니다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

대시보드 상에서는 집계 정보는 사용되지 않는 것 같네요. 수정하도록 하겠습니다!

result.repositoryId(),
result.owner(),
result.name(),
result.owner() + "/" + result.name(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

코드래빗이 달아둔 리뷰가 해당 부분에도 똑같이 있네요! 반영할 때 참고하시면 될 거 같습니다

projectRepositoryValidator.validateRepositoryOwner(repositoryId, userId);

RepositoryDashboardQueryResult result = projectRepositoryRepository
.findRepositoryDashboard(repositoryId, userId)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

findRepositoryDashboard()에서 소유자검증도 하고있는 걸로 알고있습니다.
validateRepositoryOwner()랑 겹치는 부분이라, validate 로직은 생략해도 괜찮을 거 같습니다

.mapToLong(Long::longValue)
.sum();

return new RepositoryDashboardResponse(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

응답 DTO를 조립하는 return 구문이 길어서 각 필드의 의미를 한눈에 파악하기 조금 어려운 것 같습니다!
codeDetails, analysis, summary, issuesByType, severityBreakdown를 private 메서드로 분리하면 getRepositoryDashboard()에서는 전체 응답 흐름만 보이고, 세부 조립 로직은 각 메서드에서 확인할 수 있어 가독성이 좋아질 것 같습니다! 제안이니 가볍게 봐주세용

@boogiewooki02 boogiewooki02 merged commit a2622cd into SeCause:develop Jun 14, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ FEAT New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 레포지토리 목록/상세 조회 및 삭제 API 구현

2 participants