Skip to content

fix: Sheet API 요청자 접근 권한 검증 추가#491

Open
JanooGwan wants to merge 5 commits intodevelopfrom
fix/integrated-sheet-requester-access-check
Open

fix: Sheet API 요청자 접근 권한 검증 추가#491
JanooGwan wants to merge 5 commits intodevelopfrom
fix/integrated-sheet-requester-access-check

Conversation

@JanooGwan
Copy link
Copy Markdown
Contributor

@JanooGwan JanooGwan commented Apr 1, 2026

🔍 개요

  • integrated 시트 등록 API가 로그인한 사용자의 Google 계정이 실제로 시트 접근 권한을 가지고 있는지 검증하지 않아, 서비스 계정만 접근 가능해도 등록이 성공하던 문제를 수정했습니다.
  • 추가로 Drive OAuth가 연결되지 않은 상태에서 integrated 등록이 우회되지 않도록, Drive OAuth 미연결 시 즉시 재연결을 요구하도록 보완했습니다.

🚀 주요 변경 내용

  • GoogleSheetPermissionService에 요청자 Drive OAuth 기준 시트 접근 권한을 먼저 검증하는 로직을 추가했습니다.
  • integrated 등록 흐름에서 Drive OAuth가 연결되지 않은 경우 NOT_FOUND_GOOGLE_DRIVE_AUTH로 즉시 실패하도록 변경했습니다.
  • 서비스 계정 권한 부여가 실패한 경우, 서비스 계정의 실제 시트 접근 가능 여부를 재확인한 뒤 계속 진행 여부를 결정하도록 보완했습니다.
  • 공유 드라이브 스프레드시트도 처리할 수 있도록 Drive API 호출에 supportsAllDrives 설정을 추가했습니다.
  • 요청자 접근 불가, Drive OAuth 미연결, 서비스 계정 재검증, 권한 검증 후 후속 작업 중단 시나리오를 테스트로 보강했습니다.

💬 참고 사항

  • 현재 integrated 시트 등록은 요청자 본인의 Google Drive OAuth 연결이 선행되어야 합니다.
  • 기존 tryGrantServiceAccountWriterAccess() 메서드는 다른 호출 가능성을 고려해 유지하고 있으며, integrated 등록은 더 엄격한 validateRequesterAccessAndTryGrantServiceAccountWriterAccess() 흐름을 사용합니다.

✅ Checklist (완료 조건)

  • 코드 스타일 가이드 준수
  • 테스트 코드 포함됨
  • Reviewers / Assignees / Labels 지정 완료
  • 보안 및 민감 정보 검증 (API 키, 환경 변수, 개인정보 등)

@JanooGwan JanooGwan added the 버그 정상적으로 동작하지 않는 문제 상황 관련 이슈입니다. label Apr 1, 2026
@JanooGwan JanooGwan self-assigned this Apr 1, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

요청자 Google Drive 접근을 요청자 OAuth로 먼저 검증한 뒤 서비스 계정 쓰기 권한 부여를 시도하도록 권한 흐름을 변경하고, Drive 클라이언트 빌드·검증·재검증 로직을 헬퍼로 분리하며 관련 테스트를 보강했습니다. (기능 호출 위치 변경 외 기존 임포트 흐름은 유지)

Changes

Cohort / File(s) Summary
통합 서비스 호출 변경
src/main/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedService.java
analyzeAndImportPreMembers에서 tryGrantServiceAccountWriterAccess(...) 호출을 validateRequesterAccessAndTryGrantServiceAccountWriterAccess(...)로 교체. 그 외 분석·임포트 로직은 변경 없음.
권한 서비스 확장 및 리팩토링
src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java
validateRequesterAccessAndTryGrantServiceAccountWriterAccess(Integer,String) 공개 메서드 추가. 요청자 OAuth 확인, 사용자 범위 Drive 클라이언트 빌드, files.get(...).setFields("id")로 요청자 접근 검증 후 서비스 계정 권한 부여 시도 로직 도입. tryGrantServiceAccountWriterAccess는 refresh token 해결과 Drive 클라이언트 위임을 위해 헬퍼와 private overload로 리팩토링. 예외 매핑 및 서비스 계정 재검증 헬퍼 추가. Drive File 모델 의존성 추가.
Drive 권한 헬퍼 옵션 강화
src/main/java/gg/agit/konect/domain/club/service/GoogleDrivePermissionHelper.java
permissions.list/create/update 호출에 .setSupportsAllDrives(true) 추가하여 공유 드라이브/전체 드라이브 지원을 명시함.
통합 서비스 테스트 업데이트
src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java
권한 호출 모킹/검증을 새 공개 메서드로 교체. 권한 검증 실패(403/401/토큰 없음 등) 시 downstream(헤더 매핑·임포트) 호출이 발생하지 않음을 검증하는 테스트 추가/수정.
권한 서비스 테스트 확장
src/test/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionServiceTest.java
서비스를 명시적 초기화하고 googleDriveService 목 추가. requester/service-account 양쪽 Drive.Files 및 Drive.Files.Get 목 추가 및 .setSupportsAllDrives(true) 적용 검증. 403/401/토큰누락/성공/재검증 실패 등 다양한 시나리오에 대한 테스트 추가 및 기존 테스트 강화.

Sequence Diagram(s)

sequenceDiagram
    participant Integrator as ClubSheetIntegratedService
    participant PermSvc as GoogleSheetPermissionService
    participant DriveUser as User Drive API
    participant DriveSA as ServiceAccount Drive API

    Integrator->>PermSvc: validateRequesterAccessAndTryGrantServiceAccountWriterAccess(requesterId, spreadsheetId)
    PermSvc->>DriveUser: files.get(spreadsheetId).setFields("id").setSupportsAllDrives(true).execute()
    alt 요청자 접근 허용
        PermSvc->>DriveUser: permissions.list(spreadsheetId).setSupportsAllDrives(true).execute()
        alt 서비스계정에 권한 존재
            DriveUser-->>PermSvc: permissions list -> includes service account
            PermSvc-->>Integrator: 검증 완료 (권한 확인)
        else 권한 없음
            PermSvc->>DriveSA: permissions.create/update(...).setSupportsAllDrives(true).execute()
            DriveSA-->>PermSvc: 권한 생성/업데이트 응답
            PermSvc->>DriveSA: files.get(spreadsheetId).setSupportsAllDrives(true).execute() (재검증)
            alt 서비스계정 접근 확인
                DriveSA-->>PermSvc: 파일 존재 응답
                PermSvc-->>Integrator: 권한 부여 완료 (재검증 성공)
            else 재검증 실패
                DriveSA-->>PermSvc: 예외/접근없음
                PermSvc-->>Integrator: 예외 던짐 (권한 부여 실패)
            end
        end
    else 요청자 인증/권한 실패
        DriveUser-->>PermSvc: 401/403/NOT_FOUND 등 오류
        PermSvc-->>Integrator: 예외 던짐 (요청자 인증/권한 오류)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
시트 문을 킁킁 맡아본 뒤 먼저 문을 열까요,
토큰 냄새 맡고 접근을 재고한 다음에,
서비스 계정에 살며시 펜을 쥐어줘요,
당근처럼 달콤한 임포트가 시작되면 웃음! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 PR의 주요 변경 사항인 '요청자 접근 권한 검증 추가'를 명확하게 반영하고 있으며, 제공된 코드 변경 사항과 일치합니다.
Description check ✅ Passed PR 설명은 변경 사항과 충분히 관련이 있으며, 문제 상황, 주요 변경 내용, 참고 사항을 포함하고 있습니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/integrated-sheet-requester-access-check

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.

@JanooGwan JanooGwan changed the title fix: integrated 시트 요청자 접근 권한 검증 추가 fix: Sheet API 요청자 접근 권한 검증 추가 Apr 1, 2026
@JanooGwan JanooGwan requested a review from Copilot April 1, 2026 12:43
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Integrated 시트 등록 흐름에서 요청자(사용자) Google Drive OAuth 기준으로 실제 스프레드시트 접근 가능 여부를 선검증하여, 서비스 계정만 접근 가능해도 등록이 성공하던 문제를 방지하려는 PR입니다.

Changes:

  • GoogleSheetPermissionService에 요청자 Drive 접근 검증(files().get) + 이후 서비스 계정 권한 부여 시도를 묶은 신규 메서드 추가
  • ClubSheetIntegratedService의 integrated 시작 지점에서 신규 검증 메서드를 호출하도록 변경
  • 요청자 접근 불가 시 FORBIDDEN_GOOGLE_SHEET_ACCESS로 실패하는 테스트 및 성공 시나리오 테스트 보강

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java 요청자 계정의 시트 접근 검증 후 서비스 계정 writer 권한 부여를 시도하는 흐름 추가
src/main/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedService.java integrated 등록 시작 시점에 요청자 접근 검증 로직을 적용
src/test/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionServiceTest.java 요청자 접근 불가(403) 및 접근 가능 시나리오 테스트 추가/보강
src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java 신규 메서드 호출로 테스트 갱신 및 접근 불가 시 후속 작업 중단 테스트 추가

Comment on lines +153 to +154
if (GoogleSheetApiExceptionHelper.isAccessDenied(e)
|| GoogleSheetApiExceptionHelper.isAuthFailure(e)
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

validateRequesterSpreadsheetAccess()에서 GoogleSheetApiExceptionHelper.isAuthFailure(e) (401 Unauthorized)까지 accessDenied(FORBIDDEN_GOOGLE_SHEET_ACCESS)로 변환하고 있습니다. 인증 실패(토큰 만료/무효)는 권한 부족과 의미가 달라 클라이언트에 오해를 줄 수 있으니, isAuthFailure는 INVALID_GOOGLE_DRIVE_AUTH 또는 FAILED_INIT_GOOGLE_DRIVE 등 별도 에러로 매핑하도록 분기하는 것이 안전합니다.

Suggested change
if (GoogleSheetApiExceptionHelper.isAccessDenied(e)
|| GoogleSheetApiExceptionHelper.isAuthFailure(e)
if (GoogleSheetApiExceptionHelper.isAuthFailure(e)) {
log.warn(
"Google Drive OAuth auth failure while validating spreadsheet access. requesterId={}, "
+ "spreadsheetId={}, cause={}",
requesterId,
spreadsheetId,
GoogleSheetApiExceptionHelper.extractDetail(e)
);
throw GoogleSheetApiExceptionHelper.invalidGoogleDriveAuth(e);
}
if (GoogleSheetApiExceptionHelper.isAccessDenied(e)

Copilot uses AI. Check for mistakes.
Comment on lines +101 to +102
@DisplayName("Drive OAuth가 미연결된 경우 검증을 건너뛰고 기존 흐름으로 가져오기를 계속 시도한다")
void analyzeAndImportPreMembersContinuesWhenDriveOAuthIsNotConnected() {
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

이 테스트는 googleSheetPermissionService가 Mock이라 실제로 “Drive OAuth 미연결” 시나리오를 구성/검증하지 못하고, 단지 permissionService가 예외 없이 반환하면 흐름이 계속된다는 것만 확인합니다. DisplayName/메서드명을 더 일반적인 표현으로 바꾸거나, OAuth 미연결 케이스는 GoogleSheetPermissionService 단위 테스트에서 검증하는 편이 테스트 의도를 명확히 합니다.

Suggested change
@DisplayName("Drive OAuth가 미연결된 경우 검증을 건너뛰고 기존 흐름으로 가져오기를 계속 시도한다")
void analyzeAndImportPreMembersContinuesWhenDriveOAuthIsNotConnected() {
@DisplayName("구글 시트 권한 검증 후에도 기존 흐름대로 사전 회원 가져오기를 수행한다")
void analyzeAndImportPreMembersContinuesAfterPermissionValidation() {

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java`:
- Around line 135-137: Add .setSupportsAllDrives(true) to all Drive API calls
that may operate on shared drives to ensure shared-drive spreadsheets are
handled: update userDriveService.files().get(spreadsheetId) call,
driveService.permissions().list(fileId) call,
userDriveService.permissions().create(fileId, permission) call, and
userDriveService.permissions().update(fileId, existingPermission.getId(),
updatedPermission) so each request includes .setSupportsAllDrives(true) before
.execute() (in GoogleSheetPermissionService where these symbols are used).

In
`@src/test/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionServiceTest.java`:
- Around line 257-302: Add two unit tests in GoogleSheetPermissionServiceTest
covering the unconnected-OAuth branches of
validateRequesterAccessAndTryGrantServiceAccountWriterAccess: one where
userOAuthAccountRepository.findByUserIdAndProvider(...) returns
Optional.empty(), and one where it returns a userOAuthAccount whose
getGoogleDriveRefreshToken() returns blank; in both tests call
validateRequesterAccessAndTryGrantServiceAccountWriterAccess(REQUESTER_ID,
FILE_ID) and assert it does not throw and that google drive interactions are
skipped by verifying files() and permissions.* methods are never invoked
(verify(files, never()).get(...); verify(permissions, never()).create(...),
etc.); if your service uses
googleSheetsConfig.buildUserDriveService(REFRESH_TOKEN) ensure you stub or avoid
calling it in the blank-token case so the behavior matches the contract.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 06215671-dcae-4db6-a8d5-2f3de6fb6fcb

📥 Commits

Reviewing files that changed from the base of the PR and between 6782851 and be8dd04.

📒 Files selected for processing (4)
  • src/main/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedService.java
  • src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java
  • src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java
  • src/test/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionServiceTest.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.

  • 코멘트는 반드시 한국어로 작성한다.
  • 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
  • 각 코멘트 첫 줄에 심각도를 [LEVEL: high|medium|low] 형식으로 반드시 표기한다.
  • 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
  • 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
  • 가능하면 재현 조건 및 실패 시나리오도 포함한다.
  • 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
  • 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
  • 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
  • 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
  • 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.

Files:

  • src/main/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedService.java
  • src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java
**/*

⚙️ CodeRabbit configuration file

**/*: 공통 리뷰 톤 가이드:

  • 모든 코멘트는 첫 줄에 [LEVEL: ...] 태그를 포함한다.
  • 과장된 표현 없이 사실 기반으로 작성한다.
  • 한 코멘트에는 하나의 이슈만 다룬다.
  • 코드 예시가 필요하면 최소 수정 예시를 제시한다.
  • 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.

Files:

  • src/main/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedService.java
  • src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java
  • src/test/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionServiceTest.java
  • src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java

Comment on lines +135 to +137
File file = userDriveService.files().get(spreadsheetId)
.setFields("id")
.execute();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

service_file="$(fd -i 'GoogleSheetPermissionService.java' src | head -n1)"
helper_file="$(fd -i 'GoogleDrivePermissionHelper.java' src | head -n1)"

# 신규 접근 검증 경로에서 supportsAllDrives 사용 여부 확인
rg -n -C2 'files\(\)\.get|setSupportsAllDrives' "$service_file"

# 권한 조회/부여 helper 경로에서도 동일 플래그 사용 여부 확인
if [[ -n "${helper_file:-}" ]]; then
  rg -n -C2 'permissions\(\)\.(list|create|update)|setSupportsAllDrives' "$helper_file"
fi

Repository: BCSDLab/KONECT_BACK_END

Length of output: 909


공유 드라이브 스프레드시트 접근 검증에 setSupportsAllDrives(true) 누락

[LEVEL: high] Line 135-137의 files().get(spreadsheetId) 호출과 이후 권한 조회/부여 API들이 Google Drive API의 공유 드라이브 지원 플래그를 설정하지 않아, 공유 드라이브에 있는 스프레드시트는 요청자가 실제 접근 권한을 가져도 검증이 실패할 수 있습니다. 이로 인해 동아리 문서가 공유 드라이브에 위치할 경우 정상적인 등록 요청도 차단되는 장애가 발생할 수 있습니다. Line 135-137의 files().get(), 96의 permissions().list(), 143의 permissions().create(), 167의 permissions().update() 모두에 .setSupportsAllDrives(true)를 추가하여 shared drive 접근성을 보장하세요.

관련 코드
Line 135-137:
File file = userDriveService.files().get(spreadsheetId)
    .setFields("id")
    .execute();

Line 96:
Drive.Permissions.List request = driveService.permissions().list(fileId)
    .setFields(PERMISSION_FIELDS);

Line 143-145:
userDriveService.permissions().create(fileId, permission)
    .setSendNotificationEmail(false)
    .execute();

Line 167-168:
userDriveService.permissions().update(fileId, existingPermission.getId(), updatedPermission)
    .execute();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java`
around lines 135 - 137, Add .setSupportsAllDrives(true) to all Drive API calls
that may operate on shared drives to ensure shared-drive spreadsheets are
handled: update userDriveService.files().get(spreadsheetId) call,
driveService.permissions().list(fileId) call,
userDriveService.permissions().create(fileId, permission) call, and
userDriveService.permissions().update(fileId, existingPermission.getId(),
updatedPermission) so each request includes .setSupportsAllDrives(true) before
.execute() (in GoogleSheetPermissionService where these symbols are used).

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java`:
- Around line 49-51: 현재 GoogleSheetPermissionService에서 buildUserDriveService ->
validateRequesterSpreadsheetAccess -> tryGrantServiceAccountWriterAccess 순으로
호출하지만 tryGrantServiceAccountWriterAccess의 실패(false)를 무시해 권한 부여 실패가 정상 흐름으로
처리됩니다; fix: 변경할 수 있는 두 가지 중 하나를 선택하세요 — (1) tryGrantServiceAccountWriterAccess를
boolean에서 예외를 던지도록 변경하거나 (2) boolean 반환을 유지하되 호출부(해당 코드 블록)에서 반환값을 검사해 false이면
즉시 처리 중단(명시적 예외 throw)하도록 수정하고, 추가로 예외경로에서 서비스 계정 접근 재검증 로직을 넣어 불일치 원인을 명확히 하세요;
관련 식별자: tryGrantServiceAccountWriterAccess(...),
validateRequesterSpreadsheetAccess(...), buildUserDriveService(...), 그리고 이 실패가
downstream에 영향을 주는 ClubSheetIntegratedService.analyzeAndImportPreMembers()를 고려해
호출자가 중단 여부를 결정하도록 구현하세요.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 53bf3f7a-e338-46c4-b866-68caec463617

📥 Commits

Reviewing files that changed from the base of the PR and between be8dd04 and 4fa9e02.

📒 Files selected for processing (4)
  • src/main/java/gg/agit/konect/domain/club/service/GoogleDrivePermissionHelper.java
  • src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java
  • src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java
  • src/test/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionServiceTest.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.

  • 코멘트는 반드시 한국어로 작성한다.
  • 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
  • 각 코멘트 첫 줄에 심각도를 [LEVEL: high|medium|low] 형식으로 반드시 표기한다.
  • 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
  • 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
  • 가능하면 재현 조건 및 실패 시나리오도 포함한다.
  • 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
  • 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
  • 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
  • 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
  • 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.

Files:

  • src/main/java/gg/agit/konect/domain/club/service/GoogleDrivePermissionHelper.java
  • src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java
**/*

⚙️ CodeRabbit configuration file

**/*: 공통 리뷰 톤 가이드:

  • 모든 코멘트는 첫 줄에 [LEVEL: ...] 태그를 포함한다.
  • 과장된 표현 없이 사실 기반으로 작성한다.
  • 한 코멘트에는 하나의 이슈만 다룬다.
  • 코드 예시가 필요하면 최소 수정 예시를 제시한다.
  • 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.

Files:

  • src/main/java/gg/agit/konect/domain/club/service/GoogleDrivePermissionHelper.java
  • src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java
  • src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java
  • src/test/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionServiceTest.java

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java:63

  • 통합 흐름이 validateRequesterAccessAndTryGrantServiceAccountWriterAccess로 이동하면서 tryGrantServiceAccountWriterAccess(requesterId, spreadsheetId)는 현재 main 코드에서 호출처가 없어 보입니다. 외부 사용 의도가 없다면 접근자를 package-private로 낮추거나(테스트는 동일 패키지) 메서드 역할을 정리해 혼동을 줄이는 편이 유지보수에 유리합니다.
    public boolean tryGrantServiceAccountWriterAccess(Integer requesterId, String spreadsheetId) {
        String refreshToken = userOAuthAccountRepository
            .findByUserIdAndProvider(requesterId, Provider.GOOGLE)
            .map(account -> account.getGoogleDriveRefreshToken())
            .filter(StringUtils::hasText)
            .orElse(null);

Comment on lines +35 to +39
String refreshToken = userOAuthAccountRepository
.findByUserIdAndProvider(requesterId, Provider.GOOGLE)
.map(account -> account.getGoogleDriveRefreshToken())
.filter(StringUtils::hasText)
.orElse(null);
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

validateRequesterAccessAndTryGrantServiceAccountWriterAccess()tryGrantServiceAccountWriterAccess()에서 refresh token 조회/필터링 로직이 동일하게 중복되어 있습니다. 한쪽만 수정되면 동작이 쉽게 불일치할 수 있으니, refresh token 해석 로직을 별도 private 메서드로 추출해 재사용하도록 정리하는 편이 안전합니다.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java`:
- Around line 41-47: The current logic in GoogleSheetPermissionService that
returns early when refreshToken == null (skipping Drive access validation)
allows integrated sheet registration without verifying requester permissions;
change this to enforce failure or an alternate verification: in the method
containing the refreshToken check (GoogleSheetPermissionService), replace the
early return with either (A) throw a validation/authorization exception when
attempting an integrated sheet registration and no user OAuth refreshToken is
present, or (B) implement an alternative validation path that, when refreshToken
is null, uses the service account to explicitly call Drive API to confirm the
requesterId is an editor/owner of the sheet (or requires the requester to prove
ownership) before accepting the sheet; ensure the change references the
refreshToken branch and the requesterId check so registrations cannot bypass
access validation.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a0453844-01c4-45aa-b513-5c0fe99e550b

📥 Commits

Reviewing files that changed from the base of the PR and between 4fa9e02 and ec75d64.

📒 Files selected for processing (2)
  • src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java
  • src/test/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionServiceTest.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.

  • 코멘트는 반드시 한국어로 작성한다.
  • 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
  • 각 코멘트 첫 줄에 심각도를 [LEVEL: high|medium|low] 형식으로 반드시 표기한다.
  • 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
  • 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
  • 가능하면 재현 조건 및 실패 시나리오도 포함한다.
  • 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
  • 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
  • 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
  • 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
  • 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.

Files:

  • src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java
**/*

⚙️ CodeRabbit configuration file

**/*: 공통 리뷰 톤 가이드:

  • 모든 코멘트는 첫 줄에 [LEVEL: ...] 태그를 포함한다.
  • 과장된 표현 없이 사실 기반으로 작성한다.
  • 한 코멘트에는 하나의 이슈만 다룬다.
  • 코드 예시가 필요하면 최소 수정 예시를 제시한다.
  • 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.

Files:

  • src/main/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionService.java
  • src/test/java/gg/agit/konect/domain/club/service/GoogleSheetPermissionServiceTest.java

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Comment on lines +28 to +31
googleSheetPermissionService.validateRequesterAccessAndTryGrantServiceAccountWriterAccess(
requesterId,
spreadsheetId
);
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

ClubSheetIntegratedService의 주석(“OAuth 미연결이면 … 건너뛴다”) 및 PR 설명(Drive OAuth 미연결 사용자 흐름 유지)과 달리, 현재는 validateRequesterAccessAndTryGrantServiceAccountWriterAccess()를 무조건 호출해서 OAuth 계정/refresh token이 없으면 NOT_FOUND_GOOGLE_DRIVE_AUTH 예외로 흐름이 중단됩니다(기존에는 tryGrant...가 false 반환 후 계속 진행). OAuth 미연결 시에는 기존처럼 검증/권한부여를 스킵하고 다음 단계로 진행하도록 분기(예: refresh token 존재 시에만 validate 메서드 호출, 또는 validate 메서드 내부에서 token 없으면 no-op) 중 하나로 정합성을 맞춰주세요.

Suggested change
googleSheetPermissionService.validateRequesterAccessAndTryGrantServiceAccountWriterAccess(
requesterId,
spreadsheetId
);
try {
googleSheetPermissionService.validateRequesterAccessAndTryGrantServiceAccountWriterAccess(
requesterId,
spreadsheetId
);
} catch (RuntimeException e) {
// OAuth 미연결(예: NOT_FOUND_GOOGLE_DRIVE_AUTH)인 경우에는 검증/권한부여를 스킵하고
// 기존 동작대로 이후 흐름(시트 분석 및 임포트)을 계속 진행한다.
String message = e.getMessage();
if (message == null || !message.contains("NOT_FOUND_GOOGLE_DRIVE_AUTH")) {
// 다른 예외는 기존과 동일하게 전파한다.
throw e;
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +41
public void validateRequesterAccessAndTryGrantServiceAccountWriterAccess(
Integer requesterId,
String spreadsheetId
) {
String refreshToken = requireRefreshToken(requesterId);
Drive userDriveService = buildUserDriveService(refreshToken, requesterId);
validateRequesterSpreadsheetAccess(userDriveService, requesterId, spreadsheetId);
boolean granted = tryGrantServiceAccountWriterAccess(userDriveService, requesterId, spreadsheetId);
if (!granted) {
requireServiceAccountSpreadsheetAccess(spreadsheetId, requesterId);
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

validateRequesterAccessAndTryGrantServiceAccountWriterAccess()가 requireRefreshToken()을 사용해 OAuth 미연결/blank token을 곧바로 NOT_FOUND_GOOGLE_DRIVE_AUTH로 예외 처리하고 있습니다. 그런데 동일 PR에서 “OAuth 미연결이면 기존 동작 유지(스킵)”를 명시했고, ClubSheetIntegratedService 주석도 스킵을 기대하고 있어 실제 런타임 동작이 설명과 불일치합니다. 이 메서드를 (1) refresh token이 없으면 조용히 return 하도록 변경하거나, (2) ‘반드시 OAuth 연결이 필요’한 API에서만 호출되도록 호출부를 조정하는 등 의도에 맞게 계약을 명확히 해주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +400 to +441
@Test
@DisplayName("Drive OAuth 계정이 없으면 요청자 접근 검증을 거부한다")
void validateRequesterAccessAndTryGrantServiceAccountWriterAccessThrowsWhenOAuthAccountIsMissing()
throws IOException, GeneralSecurityException {
given(userOAuthAccountRepository.findByUserIdAndProvider(REQUESTER_ID, Provider.GOOGLE))
.willReturn(Optional.empty());

assertThatThrownBy(() ->
googleSheetPermissionService.validateRequesterAccessAndTryGrantServiceAccountWriterAccess(
REQUESTER_ID,
FILE_ID
)
)
.isInstanceOf(CustomException.class)
.extracting("errorCode")
.isEqualTo(ApiResponseCode.NOT_FOUND_GOOGLE_DRIVE_AUTH);
verify(userDriveService, never()).files();
verify(userDriveService, never()).permissions();
verify(googleSheetsConfig, never()).buildUserDriveService(any());
}

@Test
@DisplayName("Drive OAuth refresh token이 비어 있으면 요청자 접근 검증을 거부한다")
void validateRequesterAccessAndTryGrantServiceAccountWriterAccessThrowsWhenRefreshTokenIsBlank()
throws IOException, GeneralSecurityException {
given(userOAuthAccountRepository.findByUserIdAndProvider(REQUESTER_ID, Provider.GOOGLE))
.willReturn(Optional.of(userOAuthAccount));
given(userOAuthAccount.getGoogleDriveRefreshToken()).willReturn(" ");

assertThatThrownBy(() ->
googleSheetPermissionService.validateRequesterAccessAndTryGrantServiceAccountWriterAccess(
REQUESTER_ID,
FILE_ID
)
)
.isInstanceOf(CustomException.class)
.extracting("errorCode")
.isEqualTo(ApiResponseCode.NOT_FOUND_GOOGLE_DRIVE_AUTH);
verify(userDriveService, never()).files();
verify(userDriveService, never()).permissions();
verify(googleSheetsConfig, never()).buildUserDriveService(any());
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

신규 테스트가 OAuth 계정/refresh token이 없을 때 validateRequesterAccessAndTryGrantServiceAccountWriterAccess()가 NOT_FOUND_GOOGLE_DRIVE_AUTH를 던지는 것을 요구하고 있는데, PR 설명에는 “Drive OAuth 미연결 사용자의 기존 흐름은 유지”라고 되어 있어 의도와 상충됩니다. 실제 제품 요구가 ‘미연결이면 스킵’이라면, 해당 테스트는 예외를 기대하기보다 no-op(후속 로직 진행) 또는 ClubSheetIntegratedService에서 validate 호출 자체를 생략하는 시나리오를 검증하도록 수정하는 편이 정합적입니다.

Copilot uses AI. Check for mistakes.
Comment on lines 73 to 84
// then
InOrder inOrder = inOrder(
clubPermissionValidator,
googleSheetPermissionService,
sheetHeaderMapper,
clubMemberSheetService,
sheetImportService
);
inOrder.verify(clubPermissionValidator).validateManagerAccess(clubId, requesterId);
inOrder.verify(googleSheetPermissionService)
.tryGrantServiceAccountWriterAccess(requesterId, spreadsheetId);
.validateRequesterAccessAndTryGrantServiceAccountWriterAccess(requesterId, spreadsheetId);
inOrder.verify(sheetHeaderMapper).analyzeAllSheets(spreadsheetId);
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

ClubSheetIntegratedService에서 권한 검증 메서드 호출로 흐름이 바뀌었는데, ClubSheetIntegratedServiceTest에는 ‘Drive OAuth 미연결 사용자도 기존처럼 분석/가져오기가 계속 진행되는지’에 대한 케이스가 없습니다. OAuth 미연결(또는 refresh token blank) 시 validate 메서드가 no-op으로 설계된다면, 이 동작이 유지되는지 테스트를 추가해 회귀를 방지하는 것이 좋습니다.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment on lines +400 to +418
@Test
@DisplayName("Drive OAuth 계정이 없으면 요청자 접근 검증을 거부한다")
void validateRequesterAccessAndTryGrantServiceAccountWriterAccessThrowsWhenOAuthAccountIsMissing()
throws IOException, GeneralSecurityException {
given(userOAuthAccountRepository.findByUserIdAndProvider(REQUESTER_ID, Provider.GOOGLE))
.willReturn(Optional.empty());

assertThatThrownBy(() ->
googleSheetPermissionService.validateRequesterAccessAndTryGrantServiceAccountWriterAccess(
REQUESTER_ID,
FILE_ID
)
)
.isInstanceOf(CustomException.class)
.extracting("errorCode")
.isEqualTo(ApiResponseCode.NOT_FOUND_GOOGLE_DRIVE_AUTH);
verify(userDriveService, never()).files();
verify(userDriveService, never()).permissions();
verify(googleSheetsConfig, never()).buildUserDriveService(any());
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

테스트가 validateRequesterAccessAndTryGrantServiceAccountWriterAccess()의 OAuth 미연결 시나리오를 NOT_FOUND_GOOGLE_DRIVE_AUTH 예외로 ‘거부’하는 동작으로 고정하고 있습니다. PR 설명에는 OAuth 미연결 사용자는 기존 흐름을 유지한다고 되어 있어, 의도가 스킵인지/거부인지가 불명확합니다. 의도가 스킵이라면 해당 테스트 기대값과 서비스 로직이 함께 조정되어야 합니다.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java (1)

100-154: 🧹 Nitpick | 🔵 Trivial

[LEVEL: minor]

중복 테스트 케이스 검토 필요

analyzeAndImportPreMembersContinuesAfterPermissionValidation 테스트가 analyzeAndImportPreMembersSuccess 테스트와 거의 동일합니다.

기존에는 tryGrantServiceAccountWriterAccess가 boolean을 반환하여 true(성공)/false(continue) 경로를 구분했을 것으로 보이지만, 현재 validateRequesterAccessAndTryGrantServiceAccountWriterAccess는 void 메서드로 변경되어:

  • 예외 없이 정상 반환 → 후속 작업 진행 (두 테스트 모두 이 경로)
  • 예외 발생 → 중단 (별도 예외 테스트에서 커버)

두 테스트 간 유의미한 차이가 없어 하나로 통합하거나, 의도된 차이점이 있다면 테스트 설정에 반영이 필요합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java`
around lines 100 - 154, The two tests
analyzeAndImportPreMembersContinuesAfterPermissionValidation and
analyzeAndImportPreMembersSuccess are effectively identical because
googleSheetPermissionService.validateRequesterAccessAndTryGrantServiceAccountWriterAccess
is now void; either merge them into a single test that verifies the normal
(no-exception) flow, or make the second test exercise a distinct behavior (e.g.,
simulate the permission method throwing an exception and assert exception
handling) by configuring the mock for
validateRequesterAccessAndTryGrantServiceAccountWriterAccess accordingly; update
or remove the redundant test so only one verifies the successful path (keeping
the inOrder verifications and assertions around analyzeAndImportPreMembers).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java`:
- Around line 100-154: The two tests
analyzeAndImportPreMembersContinuesAfterPermissionValidation and
analyzeAndImportPreMembersSuccess are effectively identical because
googleSheetPermissionService.validateRequesterAccessAndTryGrantServiceAccountWriterAccess
is now void; either merge them into a single test that verifies the normal
(no-exception) flow, or make the second test exercise a distinct behavior (e.g.,
simulate the permission method throwing an exception and assert exception
handling) by configuring the mock for
validateRequesterAccessAndTryGrantServiceAccountWriterAccess accordingly; update
or remove the redundant test so only one verifies the successful path (keeping
the inOrder verifications and assertions around analyzeAndImportPreMembers).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7ec665a9-dc5d-49ef-aeb1-3286cc0a1507

📥 Commits

Reviewing files that changed from the base of the PR and between 8baec45 and c3b614c.

📒 Files selected for processing (1)
  • src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🧰 Additional context used
📓 Path-based instructions (1)
**/*

⚙️ CodeRabbit configuration file

**/*: 공통 리뷰 톤 가이드:

  • 모든 코멘트는 첫 줄에 [LEVEL: ...] 태그를 포함한다.
  • 과장된 표현 없이 사실 기반으로 작성한다.
  • 한 코멘트에는 하나의 이슈만 다룬다.
  • 코드 예시가 필요하면 최소 수정 예시를 제시한다.
  • 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.

Files:

  • src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java
🔇 Additional comments (5)
src/test/java/gg/agit/konect/domain/club/service/ClubSheetIntegratedServiceTest.java (5)

6-6: [LEVEL: approve]

void 메서드 스터빙을 위한 willThrow import 추가가 적절합니다.

BDDMockito에서 void 메서드에 예외를 설정할 때 willThrow(...).given(...) 패턴이 필요하며, 이를 위한 import입니다.


82-83: [LEVEL: approve]

새로운 메서드명으로 검증 업데이트가 프로덕션 코드 변경과 일치합니다.

tryGrantServiceAccountWriterAccessvalidateRequesterAccessAndTryGrantServiceAccountWriterAccess 변경이 ClubSheetIntegratedService의 실제 호출과 동기화되었습니다.


167-177: [LEVEL: approve]

void 메서드 스터빙 패턴과 예외 검증이 적절합니다.

  • willThrow(...).given(...) 패턴: void 메서드에 대한 BDDMockito 표준 방식
  • .isSameAs(expected): 동일한 예외 인스턴스 검증으로 정확한 예외 전파 확인
  • verifyNoInteractions(...): 예외 발생 시 후속 서비스가 호출되지 않음을 보장

180-202: [LEVEL: approve]

요청자 시트 접근 권한 검증 실패 시나리오 테스트 추가

PR 목표인 "요청자 계정에 시트 접근 권한이 없을 경우 FORBIDDEN_GOOGLE_SHEET_ACCESS로 실패" 시나리오가 적절히 커버되었습니다. verifyNoInteractions로 후속 작업이 진행되지 않음을 검증합니다.


204-226: [LEVEL: approve]

Drive OAuth 미연결 시나리오 테스트가 적절하게 검증됩니다. OAuth 미연결 시 NOT_FOUND_GOOGLE_DRIVE_AUTH 예외가 발생하고 후속 작업(sheetHeaderMapper, clubMemberSheetService, sheetImportService)이 진행되지 않음을 확인합니다.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment on lines +26 to +31
// OAuth 미연결이면 기존 동작대로 검증을 건너뛴다.
// 다만 Drive OAuth가 연결된 경우에는 요청자 계정의 실제 시트 접근 권한을 먼저 검증한다.
googleSheetPermissionService.validateRequesterAccessAndTryGrantServiceAccountWriterAccess(
requesterId,
spreadsheetId
);
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

ClubSheetIntegratedService의 주석이 현재 동작과 불일치합니다. 이제 analyzeAndImportPreMembers()는 OAuth 미연결 시 검증을 ‘건너뛰는’ 것이 아니라 validateRequesterAccess...에서 NOT_FOUND_GOOGLE_DRIVE_AUTH 예외로 즉시 실패합니다. 주석을 실제 정책(미연결 시 실패/재연결 요구)로 수정하거나, 정말로 우회 허용이 목적이라면 호출 로직을 변경해야 합니다.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

버그 정상적으로 동작하지 않는 문제 상황 관련 이슈입니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants