feat(configuration): S3 및 files 테이블 어댑터와 유스케이스 구현 #26 - #57
Hidden character warning
Conversation
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>
명세 #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>
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one) (1)
🚫 Excluded labels (none allowed) (2)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 Walkthrough사용자는 파일 바이너리를 S3에 저장하고 메타데이터를 DB에서 관리할 수 있습니다. 다운로드 요청은 presigned URL을 반환합니다. 아키텍처 변경
위험 영역
마이그레이션 및 호환성
검증 체크리스트 및 롤아웃
WalkthroughS3 기반 파일 저장소와 presigned URL 발급을 추가했다. 파일 메타데이터의 JPA 영속화 계층을 추가했다. Changes파일 저장 및 문서 관리
Estimated code review effort: 3 (Moderate) | ~25 minutes Suggested labels: Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant UploadFileCommand
participant FileDocumentService
participant S3StorageAdapter
participant FileDocumentPersistenceAdapter
UploadFileCommand->>FileDocumentService: 업로드 명령 전달
FileDocumentService->>S3StorageAdapter: 파일 객체 업로드
S3StorageAdapter-->>FileDocumentService: StoredObject 반환
FileDocumentService->>FileDocumentPersistenceAdapter: 파일 메타데이터 저장
FileDocumentPersistenceAdapter-->>FileDocumentService: FileDocument 반환
🚥 Pre-merge checks | ✅ 8 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (8 passed)
✨ Finishing Touches 💡 1📝 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 |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
kotlin.MODULE.bazelis excluded by none and included by none
📒 Files selected for processing (8)
systems/configuration/configuration-adapter-out/deps.bzlsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/FileDocumentPersistenceAdapter.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/S3StorageAdapter.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/config/S3Config.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/entity/FileDocumentJpaEntity.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/repository/FileDocumentJpaRepository.ktsystems/configuration/configuration-application/src/main/kotlin/hs/kr/entrydsm/configuration/application/FileDocumentService.ktsystems/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
nameargument 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.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/config/S3Config.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/FileDocumentPersistenceAdapter.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/entity/FileDocumentJpaEntity.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/S3StorageAdapter.ktsystems/configuration/configuration-domain/src/main/kotlin/hs/kr/entrydsm/configuration/domain/document/command/UploadFileCommand.ktsystems/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
#123or a full tracker key like PROJ-123
Files:
systems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/repository/FileDocumentJpaRepository.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/config/S3Config.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/FileDocumentPersistenceAdapter.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/entity/FileDocumentJpaEntity.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/S3StorageAdapter.ktsystems/configuration/configuration-domain/src/main/kotlin/hs/kr/entrydsm/configuration/domain/document/command/UploadFileCommand.ktsystems/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.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/config/S3Config.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/FileDocumentPersistenceAdapter.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/entity/FileDocumentJpaEntity.ktsystems/configuration/configuration-adapter-out/src/main/kotlin/hs/kr/entrydsm/configuration/adapterout/S3StorageAdapter.ktsystems/configuration/configuration-domain/src/main/kotlin/hs/kr/entrydsm/configuration/domain/document/command/UploadFileCommand.ktsystems/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!
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
같은 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>
Summary
StoragePort를 AWS S3로,FileDocumentRepository를 JPA로 구현합니다.FileDocumentService유스케이스를 추가합니다.Related Issue
Scope
configuration-adapter-out,configuration-application,kotlin.MODULE.bazelImplementation
바이너리는 S3에, 메타데이터는 DB(
files)에 둡니다. 응답으로 바이너리를 스트리밍하지 않고 presigned URL을 반환해 서버 부하를 줄입니다.어댑터
S3StorageAdapter— 업로드 시 SHA-256 체크섬 수신, presigned URL 발급, 존재 확인, 삭제S3Config—S3Client/S3Presigner빈. 자격증명은 기본 provider chainFileDocumentJpaEntity/JpaRepository/PersistenceAdapter— ERDfiles스키마 대응,object_key에 유니크 제약SdkException을STORAGE_UPLOAD_FAILED/PRESIGN_FAILED도메인 예외로 변환해 AWS 타입이 어댑터 밖으로 새지 않게 합니다유스케이스 — 순서가 핵심입니다
S3 업로드는 DB 트랜잭션에 참여하지 않습니다. 순서를 잘못 잡으면 검증 실패인데 S3를 호출하거나, 메타데이터 없이 객체만 남습니다.
보상 삭제가 또 실패하면 로깅만 하고 원래 예외를 덮지 않습니다. 삭제 실패로 진짜 원인을 가리지 않기 위해서입니다.
Testing
컴파일과 기존 테스트 통과만 확인했습니다.
S3StorageAdapter는 실제 S3에 대해 검증되지 않았습니다.Deployment Notes
files테이블이 필요합니다.ddl-auto: validate이므로 없으면 기동 실패합니다. DDL은 PR 4에 포함되어 있습니다S3_BUCKET,AWS_REGION환경변수 주입 필요 (PR 4)Checklist
리뷰 시 봐주셨으면 하는 것
RequestBody.fromInputStream+checksumAlgorithm(SHA256)조합 — 스트리밍 체크섬이 실제로 동작하는지 목으로는 확인이 안 됩니다. 통합 테스트를 후속 이슈로 잡아두었습니다. 체크섬이 null이면 eTag로 폴백합니다.2.31.0— One-Version Rule에 따라kotlin.MODULE.bazel에만 기재했습니다. 팀에서 쓰는 버전이 따로 있으면 알려주세요.