Skip to content

feat(configuration): S3 및 files 테이블 어댑터와 유스케이스 구현 #26 - #57

Merged
tlgms merged 35 commits into
developfrom
feat(document)-파일-저장소-어댑터-#26
Aug 27, 2026

Hidden character warning

The head ref may contain hidden characters: "feat(document)-\ud30c\uc77c-\uc800\uc7a5\uc18c-\uc5b4\ub311\ud130-#26"
Merged

feat(configuration): S3 및 files 테이블 어댑터와 유스케이스 구현 #26#57
tlgms merged 35 commits into
developfrom
feat(document)-파일-저장소-어댑터-#26

Conversation

@tlgms

@tlgms tlgms commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

스택 PR 2/4 — base: feat(document)-파일-도메인-모델-#26
PR 1이 먼저 머지되어야 합니다. 머지 후 base가 develop으로 자동 전환됩니다.

Summary

  • StoragePort를 AWS S3로, FileDocumentRepository를 JPA로 구현합니다.
  • 두 포트를 조합하는 FileDocumentService 유스케이스를 추가합니다.

Related Issue

Scope

  • In scope: configuration-adapter-out, configuration-application, kotlin.MODULE.bazel
  • Out of scope: REST / gRPC 진입점, 버킷 생성·IAM 정책, 마이그레이션 도구 도입

Implementation

바이너리는 S3에, 메타데이터는 DB(files)에 둡니다. 응답으로 바이너리를 스트리밍하지 않고 presigned URL을 반환해 서버 부하를 줄입니다.

어댑터

  • S3StorageAdapter — 업로드 시 SHA-256 체크섬 수신, presigned URL 발급, 존재 확인, 삭제
  • S3ConfigS3Client / S3Presigner 빈. 자격증명은 기본 provider chain
  • FileDocumentJpaEntity / JpaRepository / PersistenceAdapter — ERD files 스키마 대응, object_key에 유니크 제약
  • SdkExceptionSTORAGE_UPLOAD_FAILED / PRESIGN_FAILED 도메인 예외로 변환해 AWS 타입이 어댑터 밖으로 새지 않게 합니다

유스케이스 — 순서가 핵심입니다

S3 업로드는 DB 트랜잭션에 참여하지 않습니다. 순서를 잘못 잡으면 검증 실패인데 S3를 호출하거나, 메타데이터 없이 객체만 남습니다.

1. 검증(형식·용량)  → 실패 시 S3 호출 없이 400/413
2. S3 업로드        → 실패 시 502, DB 미변경
3. 메타데이터 저장   → 실패 시 업로드된 객체를 보상 삭제

보상 삭제가 또 실패하면 로깅만 하고 원래 예외를 덮지 않습니다. 삭제 실패로 진짜 원인을 가리지 않기 위해서입니다.

Testing

  • Unit tests — 이 PR 범위에는 없습니다 (후속 이슈)
  • Integration tests — 후속 이슈
  • Manual verification
bazel build //systems/configuration/...
bazel test  //systems/configuration/...

컴파일과 기존 테스트 통과만 확인했습니다. S3StorageAdapter실제 S3에 대해 검증되지 않았습니다.

Deployment Notes

  • Feature flag: 없음
  • Migration required: files 테이블이 필요합니다. ddl-auto: validate이므로 없으면 기동 실패합니다. DDL은 PR 4에 포함되어 있습니다
  • Rollout considerations: S3_BUCKET, AWS_REGION 환경변수 주입 필요 (PR 4)

Checklist

  • Matches product/tech requirements
  • Backward compatibility considered
  • Docs updated if applicable

리뷰 시 봐주셨으면 하는 것

  1. RequestBody.fromInputStream + checksumAlgorithm(SHA256) 조합 — 스트리밍 체크섬이 실제로 동작하는지 목으로는 확인이 안 됩니다. 통합 테스트를 후속 이슈로 잡아두었습니다. 체크섬이 null이면 eTag로 폴백합니다.
  2. AWS SDK 버전 2.31.0 — One-Version Rule에 따라 kotlin.MODULE.bazel에만 기재했습니다. 팀에서 쓰는 버전이 따로 있으면 알려주세요.
  3. 보상 삭제까지 실패하면 고아 객체가 남습니다. 주기적 정리는 이 PR 범위 밖입니다.

tlgms and others added 13 commits July 27, 2026 20:46
One-Version Rule에 따라 kotlin.MODULE.bazel에 버전을 고정하고
configuration-adapter-out에서만 S3를 참조한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ERD의 files 스키마에 대응하는 JPA 엔티티와 FileDocumentRepository
아웃바운드 포트 구현을 추가한다. object_key에 유니크 제약을 둔다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
StoragePort를 AWS SDK v2로 구현한다. 업로드 시 SHA-256 체크섬을 받아
files 메타데이터에 남기고, 다운로드는 presigned URL로 발급한다.

SdkException을 STORAGE_UPLOAD_FAILED / PRESIGN_FAILED 도메인 예외로
변환해 어댑터 밖으로 AWS 타입이 새지 않게 한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FileDocumentService가 UploadFileUseCase, IssueDownloadUrlUseCase,
ReadFileUseCase를 구현한다.

검증(형식/용량) → S3 업로드 → 메타데이터 저장 순서를 지키고,
메타데이터 저장이 실패하면 업로드된 객체를 보상 삭제한다.
보상 삭제 실패는 로깅만 하고 원래 예외를 덮지 않는다.

파일명은 API마다 결정 방식이 달라 커맨드의 fileName을 필수로 바꾸고
컨트롤러가 결정하도록 한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
명세의 성공/실패 응답 형식에 맞춘 ApiResponse envelope과
파일 관련 ErrorCode를 정의한다. 성공 응답에도 error 키를 항상 포함하고
timestamp는 실패 시에만 직렬화한다.

도메인 예외와 MaxUploadSizeExceededException을 명세의 HTTP 상태로
변환한다. 후자를 잡지 않으면 413이 아니라 500이 나간다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
명세의 응답 스키마에 대응하는 DTO와, 참조 ID(attachment_1 형태) 변환,
MultipartFile→커맨드 변환 헬퍼를 추가한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
명세 #1 원서 저장, #2 원서 조회, #3 원서 다운을 구현한다.

조회는 파일이 없을 때 404 대신 200 + exists=false를 반환한다.
'다운로드가 아닌 조회 목적'이라는 명세 설명상 부재는 예외가 아니라
정상 결과다. 저장 확장자가 pdf/hwp 둘 다 가능하므로 순서대로 탐색한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
명세 #4 수험표 저장, #5 수험표 다운을 구현한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
명세 #6, #7을 구현한다. fileName 미지정 시 applicants_yyyyMMdd.xlsx로
자동 생성하고, 지정 시 확장자가 .xlsx인지 검증한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
명세 #8을 구현한다.

url 필드는 평문 S3 URL이 아니라 presigned URL로 발급한다. 증명사진은
개인정보이고, 버킷을 공개로 두면 키를 아는 누구나 열람할 수 있다.
응답 스키마는 명세 그대로 유지한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
명세 #9, #10을 구현한다.

object_key에는 랜덤 토큰을 쓰고 attachmentId는 files의 PK로 발급한다.
키가 추측 불가능해지고, 업로드 전에 ID를 알아야 하는 순서 문제도 없다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
명세 #11을 구현한다. ID 기반 조회라 요강 파일이 어느 prefix로
저장되었든 동작한다. 요강 업로드 API는 명세에 없어 미구현이다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
멀티파트 한도를 21MB로 두어 카테고리별 도메인 규칙(최대 20MB)이
먼저 판정하도록 하고, 서블릿 컨테이너 한도는 백스톱으로 남긴다.

ddl-auto가 validate이므로 files 테이블 DDL을 함께 추가한다.
마이그레이션 도구 도입 전까지 수기 적용이 필요하다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • ready-for-review
🚫 Excluded labels (none allowed) (2)
  • wip
  • do-not-review

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 03bcfa81-47f0-4442-95b5-edfd814708e2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

사용자는 파일 바이너리를 S3에 저장하고 메타데이터를 DB에서 관리할 수 있습니다. 다운로드 요청은 presigned URL을 반환합니다.

아키텍처 변경

  • S3StorageAdapter를 추가했습니다.
    • 파일 업로드, 삭제, 존재 여부 확인을 처리합니다.
    • 다운로드용 presigned URL을 발급합니다.
    • 업로드 시 SHA-256 체크섬을 설정합니다.
    • AWS SDK 예외를 도메인 예외로 변환합니다.
  • FileDocumentJpaEntityFileDocumentJpaRepository를 추가했습니다.
    • files 테이블의 파일 메타데이터를 관리합니다.
    • objectKey 기반 조회와 삭제를 지원합니다.
  • FileDocumentPersistenceAdapter를 추가했습니다.
    • 도메인 저장소와 JPA 저장소를 연결합니다.
  • FileDocumentService를 추가했습니다.
    • 파일 형식, 카테고리, 파일명, 용량을 검증합니다.
    • S3 업로드 후 메타데이터를 저장합니다.
    • 메타데이터 저장에 실패하면 S3 객체를 보상 삭제합니다.
  • AWS S3 SDK 의존성과 S3 Client, Presigner 설정을 추가했습니다.
  • UploadFileCommand.fileName을 필수 String으로 변경했습니다.

위험 영역

  • 실제 S3 환경 검증과 신규 테스트는 포함하지 않았습니다.
  • 메타데이터 저장 실패 시 보상 삭제가 실패하면 고아 S3 객체가 남을 수 있습니다.
  • S3와 DB 저장은 단일 트랜잭션이 아니므로 상태 불일치 가능성이 있습니다.
  • presigned URL 만료 시간과 버킷 접근 정책을 운영 환경에서 확인해야 합니다.
  • AWS 자격 증명과 권한 설정이 올바르지 않으면 업로드와 URL 발급이 실패합니다.

마이그레이션 및 호환성

  • files 테이블 마이그레이션은 포함하지 않았습니다.
  • S3 버킷, IAM 정책, 환경변수 설정은 후속 작업이 필요합니다.
  • UploadFileCommand 호출부는 필수 fileName 인자에 맞게 수정해야 합니다.
  • REST 및 gRPC 진입점은 포함하지 않았습니다.

검증 체크리스트 및 롤아웃

  • 컴파일 확인
  • 기존 테스트 확인
  • 실제 S3 업로드 및 삭제 검증
  • presigned URL 다운로드 검증
  • AWS 예외 변환 검증
  • 메타데이터 저장 실패 시 보상 삭제 검증
  • 신규 단위 테스트와 통합 테스트 추가
  • files 테이블 마이그레이션 적용
  • 버킷, IAM, 환경변수 설정 적용
  • 운영 배포 전 S3와 DB 상태 불일치 모니터링 준비

Walkthrough

S3 기반 파일 저장소와 presigned URL 발급을 추가했다. 파일 메타데이터의 JPA 영속화 계층을 추가했다. FileDocumentService가 파일 검증, 객체 저장, 메타데이터 저장, 조회 흐름을 조정한다.

Changes

파일 저장 및 문서 관리

Layer / File(s) Summary
파일 문서 영속화
systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/entity/FileDocumentJpaEntity.kt, systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/repository/FileDocumentJpaRepository.kt, systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/FileDocumentPersistenceAdapter.kt
FileDocumentJpaEntityfiles 테이블과 파일 메타데이터를 매핑한다. JPA 저장소가 ID와 objectKey 기반 작업을 제공한다. persistence adapter가 도메인 객체 변환과 저장소 호출을 위임한다.
S3 저장소 연결
systems/configuration/configuration-adapter-out/deps.bzl, systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/config/S3Config.kt, systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapter-out/S3StorageAdapter.kt
AWS SDK S3 의존성을 추가했다. S3Config가 리전 기반 S3ClientS3Presigner를 생성한다. S3StorageAdapter가 업로드, 체크섬 처리, presigned URL 발급, 존재 확인, 삭제를 구현한다.
파일 문서 서비스 흐름
systems/configuration/configuration-domain/src/main/kotlin/hs/kr/entrydsm/configuration/domain/document/command/UploadFileCommand.kt, systems/configuration/configuration-application/src/main/kotlin/hs/kr/entrydsm/configuration/application/FileDocumentService.kt
UploadFileCommand의 파일명을 필수 값으로 변경했다. 서비스가 확장자·크기·파일명을 검증하고, S3 객체와 메타데이터를 저장한다. 메타데이터 저장 실패 시 고아 객체를 삭제한다. 다운로드 URL 발급과 파일 조회도 제공한다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested labels: feature, kotlin, bazel

Suggested reviewers: wlyoon921, kusuri12-09

Sequence Diagram(s)

sequenceDiagram
  participant UploadFileCommand
  participant FileDocumentService
  participant S3StorageAdapter
  participant FileDocumentPersistenceAdapter
  UploadFileCommand->>FileDocumentService: 업로드 명령 전달
  FileDocumentService->>S3StorageAdapter: 파일 객체 업로드
  S3StorageAdapter-->>FileDocumentService: StoredObject 반환
  FileDocumentService->>FileDocumentPersistenceAdapter: 파일 메타데이터 저장
  FileDocumentPersistenceAdapter-->>FileDocumentService: FileDocument 반환
Loading
🚥 Pre-merge checks | ✅ 8 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 85.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Behavior Change Needs Tests ⚠️ Warning 변경된 Kotlin 로직은 application과 adapter-out에 추가됐지만 해당 모듈에는 moduleLoads 테스트만 있습니다. 설명은 테스트를 후속 이슈로 미뤘을 뿐 불필요 사유를 제시하지 않습니다. configuration-application과 configuration-adapter-out에 FileDocumentService, S3StorageAdapter, JPA 위임 로직의 정상·실패 경로를 검증하는 Kotlin 테스트를 추가하세요.
✅ Passed checks (8 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Kotlin Layer Boundary ✅ Passed 변경된 domain 파일에는 adapter/bootstrap import가 없습니다. 새 application 서비스의 Spring 의존성은 기존 EnvironmentVariableService와 사전 존재한 Spring 의존성 선언을 따릅니다.
Go Error Context ✅ Passed 확인한 PR 범위의 변경 파일은 Kotlin(.kt), Bzl(.bzl), Bazel(.bazel)뿐이며 Go(.go) 변경이 없습니다. 따라서 이 검사는 적용되지 않습니다.
Bazel Formatting ✅ Passed PR의 deps.bzl와 BUILD.bazel 변경은 기존 4칸 들여쓰기와 후행 쉼표 형식을 유지하며, 타깃 이름도 고정된 main·test·document_test 문자열을 사용합니다.
Todo Must Reference Issue ✅ Passed 기준 커밋과 HEAD에서 TODO/FIXME는 변경되지 않은 .coderabbit.yaml 설정 문구만 존재하며, PR의 추가 라인에는 해당 마커가 없습니다.
Title check ✅ Passed 제목이 feat(configuration): <subject> 형식을 따릅니다. S3 어댑터, files 테이블 어댑터, 유스케이스 구현이라는 주요 변경 내용을 설명하며 이슈 번호 #26도 포함합니다.
Description check ✅ Passed 설명이 S3 스토리지 어댑터, JPA 영속성 어댑터, 파일 문서 유스케이스, 처리 순서, 테스트 범위와 배포 조건을 구체적으로 설명합니다. 변경 내용과 직접 관련됩니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat(document)-파일-저장소-어댑터-#26
  • 🛠️ cleanup stale imports
  • 🛠️ harden error messages
  • 🛠️ test clarity pass

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.

@tlgms
tlgms requested review from kusuri12-09 and wlyoon921 July 27, 2026 13:58
@tlgms

tlgms commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@EntryDSM EntryDSM deleted a comment from coderabbitai Bot Aug 12, 2026

@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: 7

🤖 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
`@systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/entity/FileDocumentJpaEntity.kt`:
- Around line 12-38: FileDocumentJpaEntity and FileDocumentPersistenceAdapter
require the files table schema to exist in the same deployment. Add a migration
defining all FileDocumentJpaEntity columns, the unique constraint on object_key,
and the required indexes, or prevent the upload functionality from being exposed
until that migration is applied.

In
`@systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/repository/FileDocumentJpaRepository.kt`:
- Around line 6-9: Update FileDocumentJpaRepository.deleteByObjectKey or the
corresponding FileDocumentService deletion method to explicitly declare a
write-enabled `@Transactional` boundary. Ensure the annotation overrides
FileDocumentService’s class-level readOnly = true setting so the derived delete
query executes within a transaction.

In
`@systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/S3StorageAdapter.kt`:
- Around line 74-94: Update S3StorageAdapter.exists and delete to translate
storage failures into the adapter’s domain exception, preserving the original
SdkException as the cause. In exists, return false for both NoSuchKeyException
and S3Exception responses with status code 404, while translating other
SdkException failures; translate deleteObject SdkException failures as well. Add
adapter-module tests covering success, 404, and other exception paths for both
methods.

In
`@systems/configuration/configuration-application/src/main/kotlin/hs/kr/entrydsm/configuration/application/FileDocumentService.kt`:
- Around line 18-29: Remove the direct Spring annotations and `@Value`
configuration binding from FileDocumentService, keeping it as a plain Kotlin
application service. Move bean registration, presignExpirySeconds configuration,
and the read-only transaction boundary to the bootstrap or adapter composition
layer, then inject the resolved expiry value through the constructor while
preserving existing behavior.
- Around line 36-105: Add comprehensive tests for the changed file-storage flow:
in
systems/configuration/configuration-application/src/main/kotlin/hs/kr/entrydsm/configuration/application/FileDocumentService.kt:36-105,
test validation, successful upload, metadata-save compensation, and download URL
flows; in
systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/S3StorageAdapter.kt:30-94,
mock upload, presign, existence, deletion, and SDK exception translation; in
systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/FileDocumentPersistenceAdapter.kt:14-29,
test mapping and persistence contracts; in
systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/entity/FileDocumentJpaEntity.kt:12-61,
verify schema constraints and Instant mapping; in
systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/config/S3Config.kt:15-25,
verify the configured region reaches both AWS clients; and in
systems/configuration/configuration-domain/src/main/kotlin/hs/kr/entrydsm/configuration/domain/document/command/UploadFileCommand.kt:5-10,
test uploads using the required fileName contract.
- Around line 43-58: Update the upload flow around FileDocumentService and
storagePort.upload so an existing objectKey is never overwritten or deleted on
metadata-save failure. Generate a unique object key or use conditional upload
semantics with explicit collision handling, and ensure orphan cleanup only
removes an object created by the current request; also cover object-key
collisions and metadata persistence failures with deterministic tests.
- Around line 46-60: FileDocumentService의 save 처리에서 JpaRepository.save의
flush·commit 실패까지 감지하도록 트랜잭션 완료 콜백을 등록하고, 완료 상태가 STATUS_COMMITTED가 아니면 S3 객체를
삭제하십시오. deleteOrphan 실패는 재시도 가능한 영속 보상 작업으로 기록하도록 보상 흐름을 확장하고, flush 실패·commit
실패·보상 실패를 검증하는 통합 테스트를 추가하십시오.
🪄 Autofix

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 Plus

Run ID: bbaff4be-a60c-49db-9790-fd8528e6c20d

📥 Commits

Reviewing files that changed from the base of the PR and between eeddabd and 4b9254c.

⛔ Files ignored due to path filters (1)
  • kotlin.MODULE.bazel is excluded by none and included by none
📒 Files selected for processing (8)
  • systems/configuration/configuration-adapter-out/deps.bzl
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/FileDocumentPersistenceAdapter.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/S3StorageAdapter.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/config/S3Config.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/entity/FileDocumentJpaEntity.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/repository/FileDocumentJpaRepository.kt
  • systems/configuration/configuration-application/src/main/kotlin/hs/kr/entrydsm/configuration/application/FileDocumentService.kt
  • systems/configuration/configuration-domain/src/main/kotlin/hs/kr/entrydsm/configuration/domain/document/command/UploadFileCommand.kt
📜 Review details
🧰 Additional context used
📓 Path-based instructions (7)
**/{BUILD.bazel,*.bzl}

📄 CodeRabbit inference engine (Custom checks)

In BUILD.bazel and .bzl files, require buildifier-compatible formatting and stable target naming

Files:

  • systems/configuration/configuration-adapter-out/deps.bzl
**/*.bzl

⚙️ CodeRabbit configuration file

**/*.bzl: Apply Bazel Starlark (.bzl) style guidance.

Readability and docs:

  • Keep file/module docstrings and docstrings for public functions/macros.
  • Use descriptive parameter names and document attribute intent.

API design:

  • Macros should take a name argument and derive generated target names from it.
  • Prefer keyword arguments when calling macros for clarity and stability.
  • Keep macro side effects predictable and visible.

Encapsulation:

  • Use private visibility for helper targets created by macros unless explicitly public.
  • Avoid exposing internal implementation targets unintentionally.

Tooling:

  • Enforce buildifier formatting and lint compliance.

Files:

  • systems/configuration/configuration-adapter-out/deps.bzl
**/*.{kt,go}

📄 CodeRabbit inference engine (Custom checks)

If production logic is changed in Kotlin or Go files, require corresponding test updates in the same subsystem unless the PR description explicitly justifies why tests are unnecessary

Files:

  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/repository/FileDocumentJpaRepository.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/config/S3Config.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/FileDocumentPersistenceAdapter.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/entity/FileDocumentJpaEntity.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/S3StorageAdapter.kt
  • systems/configuration/configuration-domain/src/main/kotlin/hs/kr/entrydsm/configuration/domain/document/command/UploadFileCommand.kt
  • systems/configuration/configuration-application/src/main/kotlin/hs/kr/entrydsm/configuration/application/FileDocumentService.kt
**/*.{java,kt,scala,groovy,go,js,ts,tsx,jsx,py,rb,rs,cpp,c,h,hpp,cs}

📄 CodeRabbit inference engine (Custom checks)

Flag TODO/FIXME comments introduced by this PR that do not include an issue reference in the form #123 or a full tracker key like PROJ-123

Files:

  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/repository/FileDocumentJpaRepository.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/config/S3Config.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/FileDocumentPersistenceAdapter.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/entity/FileDocumentJpaEntity.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/S3StorageAdapter.kt
  • systems/configuration/configuration-domain/src/main/kotlin/hs/kr/entrydsm/configuration/domain/document/command/UploadFileCommand.kt
  • systems/configuration/configuration-application/src/main/kotlin/hs/kr/entrydsm/configuration/application/FileDocumentService.kt
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: Apply Kotlin Official Coding Conventions.

Formatting and structure:

  • Use 4 spaces for indentation; no tabs.
  • Keep files focused and readable; avoid horizontal alignment for spacing.
  • Place related declarations together and keep overloads adjacent.
  • Keep implementation member order stable and logical for readability.

Naming:

  • Package names are lowercase and do not use underscores.
  • Class/object names use UpperCamelCase.
  • Functions/properties/local variables use lowerCamelCase.
  • Constants use UPPER_SNAKE_CASE only for true constants.

API and null-safety:

  • Avoid platform type leakage in public APIs.
  • Use explicit types in public APIs when inference obscures meaning.
  • Prefer immutable values (val) over mutable values (var) unless mutation is required.
  • Flag nullable flows that can be replaced with safer modeling.

Imports and idioms:

  • Avoid wildcard imports unless justified by language/tooling conventions.
  • Prefer expression bodies for short, clear functions.
  • Prefer standard library idioms over custom utility wrappers when equivalent.

Architecture and tests:

  • Respect module boundaries (domain/application/adapter/bootstrap layering).
  • Highlight behavior-changing code that lacks corresponding unit/integration tests.
  • Ask for deterministic tests and meaningful assertions, not only happy-path checks.

Files:

  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/repository/FileDocumentJpaRepository.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/config/S3Config.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/FileDocumentPersistenceAdapter.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/entity/FileDocumentJpaEntity.kt
  • systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/S3StorageAdapter.kt
  • systems/configuration/configuration-domain/src/main/kotlin/hs/kr/entrydsm/configuration/domain/document/command/UploadFileCommand.kt
  • systems/configuration/configuration-application/src/main/kotlin/hs/kr/entrydsm/configuration/application/FileDocumentService.kt
**/*-domain/**/*.{java,kt,scala,groovy}

📄 CodeRabbit inference engine (Custom checks)

For files under *-domain modules, fail if imports reference adapter or bootstrap packages

Files:

  • systems/configuration/configuration-domain/src/main/kotlin/hs/kr/entrydsm/configuration/domain/document/command/UploadFileCommand.kt
**/*-application/**/*.{java,kt,scala,groovy}

📄 CodeRabbit inference engine (Custom checks)

For files under *-application modules, flag direct dependency on infrastructure-specific framework classes unless justified

Files:

  • systems/configuration/configuration-application/src/main/kotlin/hs/kr/entrydsm/configuration/application/FileDocumentService.kt
🪛 detekt (1.23.8)
systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/S3StorageAdapter.kt

[warning] 83-83: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

systems/configuration/configuration-application/src/main/kotlin/hs/kr/entrydsm/configuration/application/FileDocumentService.kt

[warning] 57-57: The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled.

(detekt.exceptions.TooGenericExceptionCaught)

🔇 Additional comments (1)
systems/configuration/configuration-adapter-out/deps.bzl (1)

4-4: LGTM!

tlgms and others added 10 commits August 19, 2026 22:53
FileReferenceId.parse 가 removePrefix 만 사용해 접두사 없는 값도 통과시켰다.
접두사가 있는 값만 허용하고, 실패는 전용 InvalidFileReferenceIdException 으로 알린다.
예외 핸들러는 IllegalArgumentException 을 통째로 400 으로 묶던 것을 멈추고
요청 바인딩 예외만 INVALID_REQUEST_PARAM 으로 매핑한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ApiResponse, ErrorCode, DocumentExceptionHandler 의 상태 코드와 응답 본문,
FileReferenceId 파싱, multipart 확장자 검증, 응답 DTO 변환을 고정한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
같은 수험번호에 여러 형식이 있으면 formats 선언 순서상 먼저 걸린 파일을 돌려줘
새로 올린 파일 대신 오래된 형식의 metadata 가 나갈 수 있었다. 최근 업로드본을 고른다.
requireExtension 이 카테고리 허용 목록을 보지 않아 어댑터에서 형식 검증이 통과했다.
요청 형식 오류는 IllegalArgumentException 대신 InvalidFileFormatException 으로 던진다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MockMvc standalone 으로 multipart 바인딩, 카테고리별 파일명 규칙,
참조 ID 파싱, 다운로드 URL 발급, 오류 응답 상태를 검증한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
HEAD 응답에는 본문이 없어 객체 없음이 NoSuchKeyException 대신 404 S3Exception 으로
올라올 수 있었고, 403 이나 네트워크 오류까지 조용히 "없음"으로 처리될 위험이 있었다.
404 만 false 로 보고 나머지 SDK 오류는 StorageUnavailableException 으로 올린다.
deleteObject 의 SDK 오류도 같은 도메인 예외로 변환한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FileDocumentService 의 @service, @transactional, @value 를 걷어내고 빈 등록과
설정 주입을 bootstrap 으로 옮겼다. 트랜잭션 경계는 영속성 어댑터로 내려서
파생 쿼리인 deleteByObjectKey 가 쓰기 트랜잭션 없이 호출되는 문제도 함께 없앤다.
저장이 upload 바깥의 커밋 시점이 아니라 try 안에서 끝나므로 보상 삭제가 실제로 동작한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tlgms and others added 9 commits August 20, 2026 00:55
같은 objectKey 로 업로드하면 기존 객체를 덮어쓰는데, 이후 메타데이터 저장이
실패하면 보상 삭제가 남아 있던 파일까지 지웠다. 새로 만든 객체만 지운다.
ddl-auto 가 validate 라 files 테이블 없이는 업로드 경로가 기동 즉시 실패하므로
configuration_db 스키마를 같은 배포 단위에 넣는다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
형식·용량·파일명 검증, 저장 성공, 메타데이터 실패 보상, 덮어쓰기 시 보상 생략,
다운로드 URL 발급을 fake 포트로 고정한다.
S3 어댑터는 404/403/네트워크 오류와 삭제 실패 경로를 검증한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
S3StorageAdapter 가 읽는 aws.s3.bucket 이 어디에도 정의돼 있지 않아
컨텍스트 초기화 단계에서 기동이 실패했다. 버킷은 기본값 없이 환경 변수로 받고,
리전과 presign 만료 시간에만 기본값을 둔다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
S3 존재 확인·삭제가 올리는 StorageUnavailableException 이 매핑되지 않아
502 대신 500 으로 나갔다. STORAGE_UNAVAILABLE 로 묶어 응답 규약에 넣는다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
S3Presigner 의 presign 메서드 7개가 추상이라 빈 구현으로는 컴파일되지 않았다.
이 테스트는 presign 을 쓰지 않으므로 프록시로 대신한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kusuri12-09
kusuri12-09 previously approved these changes Aug 27, 2026
Base automatically changed from feat(document)-파일-도메인-모델-#26 to develop August 27, 2026 06:42
@tlgms
tlgms dismissed kusuri12-09’s stale review August 27, 2026 06:42

The base branch was changed.

@tlgms
tlgms removed the request for review from wlyoon921 August 27, 2026 06:47
tlgms and others added 3 commits August 27, 2026 15:57
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(configuration): 파일 API 공통 응답 규약 및 예외 처리 #26
feat(configuration): 파일 REST API 11종 구현 #26
@tlgms
tlgms merged commit ea53691 into develop Aug 27, 2026
2 checks passed
@tlgms
tlgms deleted the feat(document)-파일-저장소-어댑터-#26 branch August 27, 2026 07:04
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