refactor: Google Sheet API 전화번호 포맷팅 및 마이그레이션 버그 수정#440
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📜 Recent 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)
🧰 Additional context used📓 Path-based instructions (2)src/main/java/**/*.java⚙️ CodeRabbit configuration file
Files:
**/*⚙️ CodeRabbit configuration file
Files:
🔇 Additional comments (2)
📝 WalkthroughWalkthroughDrive 복사 후 부모(folder) 메타데이터가 비어있을 때 추가로 부모를 재조회하고, 재조회 실패 또는 부모가 여전히 비어있으면 생성된 파일을 삭제하고 예외로 중단하도록 복사/이동 흐름을 강화했습니다. 전화번호 포맷팅은 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Service as SheetMigrationService
participant Drive as Google Drive API
participant RM as RollbackManager
Service->>Drive: files().copy(...).setFields("id, parents")
Drive-->>Service: { id: newFileId, parents: null/empty }
alt parents empty
Service->>Drive: files().get(newFileId).setFields("parents")
Drive-->>Service: { parents: [...] }
alt re-fetch success & parents present
Service->>Drive: files().update(addParents=targetFolder, removeParents=currentParents)
Drive-->>Service: updated file
else re-fetch fails (IOException) or parents still empty
Service->>Service: delete file(`newFileId`)
Service->>RM: throw CustomException(FAILED_SYNC_GOOGLE_SHEET)
end
else parents present
Service->>Drive: files().update(addParents=targetFolder, removeParents=parents)
Drive-->>Service: updated file
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
시
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/gg/agit/konect/domain/club/service/SheetMigrationService.java (1)
291-316:⚠️ Potential issue | 🟠 Major[LEVEL: medium] 재조회 실패 시 파일이 여러 폴더에 존재할 수 있음
재조회(
files().get)도 실패하면currentParents가 null/empty 상태로 유지되어removeParents가 빈 문자열이 됩니다. →setRemoveParents("")호출 시 원래 위치(루트)에서 파일이 제거되지 않아 파일이 두 폴더에 동시에 존재하게 됩니다. → 재조회 실패 시 경고 로그 대신 예외를 던지거나, 최소한 사용자에게 알려주는 방식을 고려하세요.🛠️ 수정 제안
} catch (IOException ex) { log.warn( "Failed to re-fetch parents for copied file. fileId={}, cause={}", newFileId, ex.getMessage() ); + // 부모 폴더를 확인할 수 없으면 이동을 건너뛰고 경고 + log.warn("Skipping folder move due to unknown current parents. fileId={}", newFileId); + return newFileId; }🤖 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/SheetMigrationService.java` around lines 291 - 316, The re-fetch path in SheetMigrationService leaves currentParents empty on files().get failure so removeParents becomes "" and setRemoveParents("") causes the file to remain in the original folder (duplicated); change the catch of IOException from merely logging to rethrowing (or throw a custom unchecked exception) so the method aborts instead of calling driveService.files().update with an empty removeParents. Specifically, in the block that calls driveService.files().get(newFileId) and assigns currentParents, replace the log.warn(...) in the catch with throwing the caught IOException (or wrap+throw a RuntimeException) so code after that (which uses removeParents and calls setRemoveParents) is not executed with invalid state.
🤖 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/global/util/PhoneNumberNormalizer.java`:
- Around line 66-71: Remove the unreachable branch handling 10-digit numbers
starting with "010" in PhoneNumberNormalizer (the if that checks digits.length()
== DIGITS_10 && digits.startsWith("010")), or if you want to keep it for
documentation, replace it with a clear comment explaining why it's unreachable
(010 numbers are always 11 digits) and why the branch is intentionally omitted;
update or delete related constants/paths (DIGITS_10, IDX_3, IDX_6) usage if they
become unused after removal to keep the class clean.
- Around line 54-57: The current recovery logic in PhoneNumberNormalizer that
prepends "0" when digits.length() == DIGITS_10 and digits.startsWith("1") is too
broad and converts service numbers (e.g., 1588-xxxx) incorrectly; narrow the
condition to only recover likely-missing leading-zero mobile prefixes by
checking digits.startsWith("10") (i.e., the 010 mobile prefix without the 0) or
otherwise match a stricter pattern for mobile numbers before setting digits =
"0" + digits; update the check around DIGITS_10 and the digits variable (in the
method handling normalization) accordingly.
---
Outside diff comments:
In `@src/main/java/gg/agit/konect/domain/club/service/SheetMigrationService.java`:
- Around line 291-316: The re-fetch path in SheetMigrationService leaves
currentParents empty on files().get failure so removeParents becomes "" and
setRemoveParents("") causes the file to remain in the original folder
(duplicated); change the catch of IOException from merely logging to rethrowing
(or throw a custom unchecked exception) so the method aborts instead of calling
driveService.files().update with an empty removeParents. Specifically, in the
block that calls driveService.files().get(newFileId) and assigns currentParents,
replace the log.warn(...) in the catch with throwing the caught IOException (or
wrap+throw a RuntimeException) so code after that (which uses removeParents and
calls setRemoveParents) is not executed with invalid state.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1361c910-bb32-47e2-a972-a8de01b6ae69
📒 Files selected for processing (3)
src/main/java/gg/agit/konect/domain/club/service/SheetMigrationService.javasrc/main/java/gg/agit/konect/domain/club/service/SheetSyncExecutor.javasrc/main/java/gg/agit/konect/global/util/PhoneNumberNormalizer.java
📜 Review details
🧰 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/SheetMigrationService.javasrc/main/java/gg/agit/konect/domain/club/service/SheetSyncExecutor.javasrc/main/java/gg/agit/konect/global/util/PhoneNumberNormalizer.java
**/*
⚙️ CodeRabbit configuration file
**/*: 공통 리뷰 톤 가이드:
- 모든 코멘트는 첫 줄에
[LEVEL: ...]태그를 포함한다.- 과장된 표현 없이 사실 기반으로 작성한다.
- 한 코멘트에는 하나의 이슈만 다룬다.
- 코드 예시가 필요하면 최소 수정 예시를 제시한다.
- 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.
Files:
src/main/java/gg/agit/konect/domain/club/service/SheetMigrationService.javasrc/main/java/gg/agit/konect/domain/club/service/SheetSyncExecutor.javasrc/main/java/gg/agit/konect/global/util/PhoneNumberNormalizer.java
🔇 Additional comments (4)
src/main/java/gg/agit/konect/domain/club/service/SheetSyncExecutor.java (3)
182-183: LGTM!
PhoneNumberNormalizer.format()호출로 전화번호 포맷팅이 통일되었고,putValue에서 null 처리가 적절히 이루어집니다.
201-201: LGTM!null 값을 빈 문자열로 변환하여 시트에 null이 기록되는 것을 방지합니다.
217-226: LGTM!전화번호 포맷팅과 null 처리가 적절합니다.
src/main/java/gg/agit/konect/domain/club/service/SheetMigrationService.java (1)
429-432: LGTM!
PhoneNumberNormalizer.format()호출로 전화번호 포맷팅이 통일되었습니다. (단,PhoneNumberNormalizer.format()의 앞자리 0 복구 로직 이슈는 해당 파일 리뷰에서 별도로 지적되었습니다.)
src/main/java/gg/agit/konect/global/util/PhoneNumberNormalizer.java
Outdated
Show resolved
Hide resolved
src/main/java/gg/agit/konect/global/util/PhoneNumberNormalizer.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Google Sheets/Drive 연동 기반의 동아리 명단 동기화·마이그레이션에서, 전화번호 표시 형식을 통일하고(0 잘림 복구 포함) 템플릿 복사 후 파일이 루트에 남는 문제를 완화하려는 PR입니다.
Changes:
PhoneNumberNormalizer.format()추가로 전화번호를 하이픈 포함 형태로 포맷팅(일부 케이스 0 잘림 복구)- 시트 동기화(
SheetSyncExecutor)에서 전화번호 입력 방식을format()기반으로 변경 및 null 값 안전 처리 - 템플릿 복사 후 폴더 이동 시
parents가 null로 오는 케이스를files().get()로 재조회하도록 보완
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/main/java/gg/agit/konect/global/util/PhoneNumberNormalizer.java | 전화번호 포맷팅 메서드 추가 및 관련 상수 도입 |
| src/main/java/gg/agit/konect/domain/club/service/SheetSyncExecutor.java | 시트 쓰기 시 전화번호 포맷 통일 및 null 값 처리 개선 |
| src/main/java/gg/agit/konect/domain/club/service/SheetMigrationService.java | 템플릿 복사 후 parents 재조회로 폴더 이동 안정성 개선, 마이그레이션 시 전화번호 포맷 적용 |
| * 전화번호를 010-XXXX-XXXX 형식으로 변환합니다. | ||
| * 구글 시트에서 앞자리 0이 잘린 경우(10자리, 1로 시작)도 복구합니다. | ||
| * 예) 1012345678 -> 010-1234-5678 | ||
| * 01012345678 -> 010-1234-5678 | ||
| * 010-1234-5678 -> 010-1234-5678 |
There was a problem hiding this comment.
format()의 Javadoc이 “010-XXXX-XXXX 형식으로 변환”이라고 되어 있지만, 실제 구현은 02 지역번호/기타 10자리/11자리도 처리하고(또는 조건에 안 맞으면 하이픈 없이 digits 그대로 반환) 문서와 동작이 불일치합니다. 문서를 구현에 맞게 수정하거나, 정말로 010-XXXX-XXXX로만 통일하려는 의도라면 그에 맞게 포맷 규칙/리턴값을 조정해 주세요.
| * 전화번호를 010-XXXX-XXXX 형식으로 변환합니다. | |
| * 구글 시트에서 앞자리 0이 잘린 경우(10자리, 1로 시작)도 복구합니다. | |
| * 예) 1012345678 -> 010-1234-5678 | |
| * 01012345678 -> 010-1234-5678 | |
| * 010-1234-5678 -> 010-1234-5678 | |
| * 전화번호에서 숫자만 추출한 뒤 길이와 패턴에 따라 하이픈(-)을 삽입하여 포맷팅합니다. | |
| * 구글 시트에서 앞자리 0이 잘린 경우(10자리, 1로 시작)도 복구합니다. | |
| * | |
| * <p>포맷 규칙은 다음과 같습니다. | |
| * <ul> | |
| * <li>구글 시트에서 앞자리 0이 잘린 10자리 번호(1로 시작): 앞에 0을 붙여 11자리로 복구</li> | |
| * <li>11자리: {@code XXX-XXXX-XXXX}</li> | |
| * <li>010으로 시작하는 10자리: {@code 010-XXX-XXXX}</li> | |
| * <li>서울 지역번호 02: | |
| * <ul> | |
| * <li>10자리: {@code 02-XXXX-XXXX}</li> | |
| * <li>9자리: {@code 02-XXX-XXXX}</li> | |
| * </ul> | |
| * </li> | |
| * <li>그 외 10자리: {@code XXX-XXX-XXXX}</li> | |
| * <li>위 조건에 모두 해당하지 않는 경우: 하이픈 없이 숫자만 그대로 반환</li> | |
| * </ul> | |
| * | |
| * <p>예) | |
| * <pre> | |
| * 1012345678 -> 010-1234-5678 (구글 시트에서 0이 잘린 경우 복구) | |
| * 01012345678 -> 010-1234-5678 | |
| * 010-1234-5678 -> 010-1234-5678 | |
| * 0212345678 -> 02-1234-5678 | |
| * 021234567 -> 02-123-4567 | |
| * 0311234567 -> 031-123-4567 | |
| * 1234567 -> 1234567 (길이가 맞지 않아 하이픈 없이 반환) | |
| * </pre> |
| private static final int DIGITS_9 = 9; | ||
| private static final int DIGITS_10 = 10; | ||
| private static final int DIGITS_11 = 11; |
There was a problem hiding this comment.
PHONE_NUMBER_MIN_DIGITS/MAX_DIGITS(9~11)와 별도로 DIGITS_9/10/11 상수를 추가해 같은 의미의 숫자를 중복 정의하고 있어 향후 변경 시 값 불일치가 생길 수 있습니다. 한쪽만 기준으로 쓰도록 정리(예: DIGITS_* 제거, 또는 MIN/MAX를 DIGITS_*로 대체)하는 편이 안전합니다.
| private static final int DIGITS_9 = 9; | |
| private static final int DIGITS_10 = 10; | |
| private static final int DIGITS_11 = 11; | |
| private static final int DIGITS_9 = PHONE_NUMBER_MIN_DIGITS; | |
| private static final int DIGITS_10 = PHONE_NUMBER_MIN_DIGITS + 1; | |
| private static final int DIGITS_11 = PHONE_NUMBER_MAX_DIGITS; |
| String removeParents = (currentParents != null && !currentParents.isEmpty()) | ||
| ? String.join(",", currentParents) | ||
| : ""; |
There was a problem hiding this comment.
parents 재조회 실패(또는 재조회 후에도 parents가 비어 있음)해도 경고 로그만 남기고 removeParents를 빈 문자열로 둔 채 update를 계속 진행하면, 파일이 루트(또는 기존 폴더)에 남아 ‘폴더 이동’이 보장되지 않습니다. 마이그레이션의 핵심 전제(지정 폴더로 이동)가 깨지는 경우이므로, parents 확보에 실패하면 예외로 처리해 롤백되도록 하거나(또는 update 호출 자체를 중단) 실패를 호출자에게 명확히 전달하는 방식이 더 안전합니다.
| String removeParents = (currentParents != null && !currentParents.isEmpty()) | |
| ? String.join(",", currentParents) | |
| : ""; | |
| // parents를 끝내 확보하지 못한 경우, 폴더 이동이 보장되지 않으므로 예외로 처리해 롤백되도록 한다. | |
| if (currentParents == null || currentParents.isEmpty()) { | |
| log.error("Failed to determine parents for copied file. fileId={}", newFileId); | |
| throw new IllegalStateException("Cannot determine parents for copied file: " + newFileId); | |
| } | |
| String removeParents = String.join(",", currentParents); |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/gg/agit/konect/domain/club/service/SheetMigrationService.java (1)
287-314:⚠️ Potential issue | 🟠 Major[LEVEL: medium] 예외 경로에서 복제 파일 보상 삭제가 누락됩니다.
Line 287-314에서
files().copy()이후parents재조회가 실패하면CustomException을 던지지만, 이미 생성된newFileId를 정리하지 않아 보상 처리가 비어 있습니다.
이 경우(예: copy 응답과 재조회 모두parents미확보) API는 실패로 끝나고 Drive에 고아 스프레드시트가 남아 누적될 수 있습니다.
newFileId를 try 바깥 변수로 보관해CustomException/IOException경로에서deleteFile(...)을 호출하거나 복사 직후 롤백 훅을 등록해 정리를 보장하세요; As per coding guidelines "보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다."최소 수정 예시
private String copyTemplate(Drive driveService, String templateId, String clubName, String targetFolderId) { + String newFileId = null; try { String title = NEW_SHEET_TITLE_PREFIX + clubName; File copyMetadata = new File().setName(title); File copied = driveService.files().copy(templateId, copyMetadata) .setFields("id, parents") .execute(); - String newFileId = copied.getId(); + newFileId = copied.getId(); if (targetFolderId != null) { List<String> currentParents = copied.getParents(); ... } log.info("Template copied by user. newId={}, folderId={}", newFileId, targetFolderId); return newFileId; + } catch (CustomException e) { + if (newFileId != null) { + deleteFile(driveService, newFileId); + } + throw e; } catch (IOException e) { + if (newFileId != null) { + deleteFile(driveService, newFileId); + } log.error("Failed to copy template. cause={}", e.getMessage(), e); throw CustomException.of(ApiResponseCode.FAILED_SYNC_GOOGLE_SHEET); } }🤖 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/SheetMigrationService.java` around lines 287 - 314, The copy error path can leave an orphaned Google Sheet because newFileId (from copied.getId()) is not deleted when re-fetching parents fails; update SheetMigrationService to ensure cleanup by storing newFileId in a variable visible to the exception handlers and invoking the existing deleteFile(...) (or equivalent) on newFileId in both the IOException catch and the subsequent parents-null check before throwing CustomException.of(ApiResponseCode.FAILED_SYNC_GOOGLE_SHEET); ensure any rollback is safe if deleteFile fails (log and proceed to throw) and reference the copied/newFileId, driveService.files().get(...) parents re-fetch logic, and CustomException path when implementing the fix.
🤖 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/main/java/gg/agit/konect/domain/club/service/SheetMigrationService.java`:
- Around line 287-314: The copy error path can leave an orphaned Google Sheet
because newFileId (from copied.getId()) is not deleted when re-fetching parents
fails; update SheetMigrationService to ensure cleanup by storing newFileId in a
variable visible to the exception handlers and invoking the existing
deleteFile(...) (or equivalent) on newFileId in both the IOException catch and
the subsequent parents-null check before throwing
CustomException.of(ApiResponseCode.FAILED_SYNC_GOOGLE_SHEET); ensure any
rollback is safe if deleteFile fails (log and proceed to throw) and reference
the copied/newFileId, driveService.files().get(...) parents re-fetch logic, and
CustomException path when implementing the fix.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: c7729d63-579e-45a9-b84f-e5d11e2e2fe3
📒 Files selected for processing (2)
src/main/java/gg/agit/konect/domain/club/service/SheetMigrationService.javasrc/main/java/gg/agit/konect/global/util/PhoneNumberNormalizer.java
📜 Review details
🧰 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/global/util/PhoneNumberNormalizer.javasrc/main/java/gg/agit/konect/domain/club/service/SheetMigrationService.java
**/*
⚙️ CodeRabbit configuration file
**/*: 공통 리뷰 톤 가이드:
- 모든 코멘트는 첫 줄에
[LEVEL: ...]태그를 포함한다.- 과장된 표현 없이 사실 기반으로 작성한다.
- 한 코멘트에는 하나의 이슈만 다룬다.
- 코드 예시가 필요하면 최소 수정 예시를 제시한다.
- 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.
Files:
src/main/java/gg/agit/konect/global/util/PhoneNumberNormalizer.javasrc/main/java/gg/agit/konect/domain/club/service/SheetMigrationService.java
| if (phone == null) { | ||
| return null; | ||
| } | ||
| String digits = phone.trim().replaceAll("[^0-9]", ""); |
There was a problem hiding this comment.
format()에서 숫자 추출/trim/빈문자 처리 로직이 기존 normalize()와 중복됩니다. 한쪽만 수정될 때 동작이 달라질 수 있으니 format() 내부에서 normalize(phone)를 재사용하도록 리팩터링하는 편이 유지보수에 안전합니다.
| if (phone == null) { | |
| return null; | |
| } | |
| String digits = phone.trim().replaceAll("[^0-9]", ""); | |
| String digits = normalize(phone); | |
| if (digits == null) { | |
| return null; | |
| } |
| "Failed to re-fetch parents for copied file. fileId={}, cause={}", | ||
| newFileId, ex.getMessage() |
There was a problem hiding this comment.
부모 폴더 재조회 실패 로그에서 ex.getMessage()만 기록되고 예외 객체가 전달되지 않아 스택트레이스가 누락됩니다. 운영 디버깅을 위해 log.error(..., ex) 형태로 예외를 함께 로깅해 주세요.
| "Failed to re-fetch parents for copied file. fileId={}, cause={}", | |
| newFileId, ex.getMessage() | |
| "Failed to re-fetch parents for copied file. fileId={}", | |
| newFileId, ex |
🔍 개요
Google Sheet API 테스트 결과 발견된 두 가지 버그를 수정합니다.
🚀 주요 변경 내용
1. 전화번호 포맷팅 통일 (010-XXXX-XXXX)
PhoneNumberNormalizer.format()메서드 추가0이 잘린 경우(1012345678) 자동 복구010-XXXX-XXXX형식으로 통일 출력SheetMigrationService.buildTargetRow():normalize()→format()교체SheetSyncExecutor.buildColumnData():"'" + phone방식 →format()교체SheetSyncExecutor.clearAndWriteAll(): 동일하게format()적용2. 마이그레이션 시 파일이 루트에 생성되는 버그 수정
SheetMigrationService.copyTemplate()내files().copy()응답에서getParents()가null로 반환되는 경우, 별도files().get()호출로 parents를 재조회하여removeParents누락 방지💬 참고 사항
files().copy()응답의parents필드는 간헐적으로null로 반환됨SheetSyncExecutor의 동기화(syncMembersToSheet)는 서비스 계정으로 직접 쓰기이므로 Drive OAuth 연동 여부와 무관하게 동작함SheetMigrationService에서는 Drive OAuth 미연동 시NOT_FOUND_GOOGLE_DRIVE_AUTH예외를 던져 정상 처리됨✅ Checklist (완료 조건)