Skip to content

[ai-assisted] feat(thumbnail): 문서 썸네일 renderer 확장#374

Merged
donghyuck merged 1 commit into2.xfrom
codex/issue-371-document-thumbnail-renderers
Apr 29, 2026
Merged

[ai-assisted] feat(thumbnail): 문서 썸네일 renderer 확장#374
donghyuck merged 1 commit into2.xfrom
codex/issue-371-document-thumbnail-renderers

Conversation

@donghyuck
Copy link
Copy Markdown
Owner

@donghyuck donghyuck commented Apr 29, 2026

Why

  • 이슈 #371에서 요청한 PPTX/DOCX/HWP/HWPX 문서 썸네일 renderer를 독립 studio-platform-thumbnail 경로에 추가합니다.
  • LibreOffice 외부 프로세스 없이 구현하기 위해 PPTX는 Apache POI, DOCX/HWP/HWPX는 기존 textract 구조화 추출 결과 기반 preview 방식으로 정리합니다.
  • 저장된 썸네일이 없을 때 요청 스레드에서 바로 생성하지 않고 pending placeholder를 반환한 뒤 background executor에서 생성하도록 보완합니다.

What

  • PptxThumbnailRenderer를 Apache POI XMLSlideShow 기반 slide renderer로 구현했습니다.
  • PPTX와 DOCX는 POI 파싱 전에 ZIP entry 및 aggregate extracted byte budget으로 pre-scan합니다.
  • DOCX/HWP/HWPX는 studio-platform-thumbnail core renderer가 아니라 studio-platform-thumbnail-starter의 textract preview renderer로 등록합니다.
  • DOCX/HWP/HWPX marker interface를 추가해 사용자가 직접 등록한 format-specific renderer가 기본 preview renderer를 type 기준으로 대체할 수 있게 했습니다.
  • studio.thumbnail.libre-office.*studio.thumbnail.renderers.pptx.timeout 설정/metadata/docs를 제거하고, studio.thumbnail.renderers.<format>.enabled 기준으로 문서를 갱신했습니다.
  • HWP/HWPX parser에 per-entry 및 aggregate extraction budget을 적용했습니다.
  • attachment thumbnail cache miss 시 X-Thumbnail-Status: pending placeholder 이미지를 반환하고 background executor에서 생성/저장합니다.
  • queue reject 시 pending을 반환하지 않고 X-Thumbnail-Status: unavailable 204 경로로 빠지게 했습니다.
  • deterministic unsupported/failure 결과는 source 단위 bounded TTL 캐시에 memoize하고, 동일 source 동시 요청은 하나의 background job으로 합칩니다.
  • attachment 삭제와 queued generation 완료 사이 race에서 stale thumbnail이 다시 저장되지 않도록 deletion marker와 striped lock을 적용했습니다.
  • 테스트용 studio-one-api-server 설정에서 LibreOffice 설정을 제거하고 POI/textract preview 기준으로 맞췄습니다.

Related Issues

Validation

  • Command: ./gradlew :studio-platform-textract:test --tests 'studio.one.platform.textract.extractor.impl.DocxFileParserTest' :studio-application-modules:attachment-service:test --tests 'studio.one.application.attachment.thumbnail.ThumbnailServiceImplTest' --tests 'studio.one.application.web.controller.AttachmentControllerTest' :starter:studio-application-starter-attachment:test
  • Result: BUILD SUCCESSFUL
  • Command: ./gradlew :studio-platform-textract:test :studio-platform-thumbnail:test :starter:studio-platform-thumbnail-starter:test :studio-application-modules:attachment-service:test :starter:studio-application-starter-attachment:test
  • Result: BUILD SUCCESSFUL
  • Command: ./gradlew test
  • Result: BUILD SUCCESSFUL
  • Command: git diff --check
  • Result: passed
  • Command: /Users/donghyuck.son/git/studio-one/studio-one-api-server ./gradlew compileJava
  • Result: BUILD SUCCESSFUL
  • Command: /Users/donghyuck.son/git/studio-one/studio-one-api-server git diff --check
  • Result: passed

Review Follow-up

  • code-reviewer findings processed: format-specific custom renderer replacement, attachment starter optional dependency docs, queue reject pending contract, stale thumbnail race, DOCX ZIP budget documentation.
  • security-auditor findings processed: PPTX package pre-scan budget, HWP aggregate budget, bounded failure memoization, source-level running dedupe, DOCX ZIP budget.

Risk / Rollback

  • Risk: DOCX/HWP/HWPX preview thumbnail은 실제 문서 레이아웃 rasterize가 아니라 textract 결과 기반 대표 preview입니다.
  • Risk: queue가 포화되면 /thumbnail은 pending 대신 204/unavailable을 반환합니다. 클라이언트는 이후 재시도할 수 있습니다.
  • Risk: HWP/HWPX/DOCX parser는 추출/압축 해제 크기 상한을 추가했지만, 운영에서는 studio.textract.max-extract-sizestudio.thumbnail.max-source-size를 보수적으로 유지해야 합니다.
  • Rollback: 이 PR을 revert하거나 studio.thumbnail.renderers.<format>.enabled=false로 문서 renderer를 비활성화하면 기존 image/PDF 중심 동작으로 되돌릴 수 있습니다.

AI / Subagent Usage

  • AI-assisted: Yes
  • Subagent used: Yes
  • Delegated scope: code-reviewer와 security-auditor가 PR diff를 재검토했습니다.
  • Main author validation: 리뷰 결과를 반영한 뒤 targeted test, related module test, 전체 ./gradlew test, git diff --check, 외부 app compile을 재실행했습니다.

Checklist

  • commit message follows policy
  • issue template used or exception recorded
  • AI-Assisted value is correct
  • validation recorded
  • subagent usage recorded when used
  • CI / repository verification passed
  • human review completed before merge
  • no unrelated changes included

@donghyuck donghyuck force-pushed the codex/issue-371-document-thumbnail-renderers branch 3 times, most recently from da74c3a to 486e95f Compare April 29, 2026 07:40
Issue: #371
AI-Assisted: Yes

PPTX 썸네일은 LibreOffice 외부 프로세스 대신 Apache POI slide renderer를 사용하도록 정리했다.
DOCX/HWP/HWPX는 새 별도 renderer를 core에 두지 않고 thumbnail starter에서 textract `FileContentExtractionService` 기반 preview renderer로 등록한다.
`studio.thumbnail.libre-office.*`와 `pptx.timeout` 설정/metadata/docs를 제거하고, 앱 예시는 POI/textract preview 기준으로 갱신했다.

Async thumbnail follow-up:
- 저장된 썸네일이 없으면 `/thumbnail`은 `X-Thumbnail-Status: pending` placeholder 이미지를 즉시 반환하고 starter background executor에서 생성한다.
- queue reject 시 실제 작업이 없는데 pending을 반환하지 않고 unavailable 경로로 빠지도록 했다.
- deterministic failure/unsupported 결과는 source 단위 bounded TTL 캐시에 memoize하고, 동일 source 동시 요청은 하나의 background job으로 합친다.
- attachment 삭제와 queued generation 완료 사이 race에서 stale thumbnail이 다시 저장되지 않도록 deletion marker와 striped lock을 적용했다.
- 204 응답에는 `X-Thumbnail-Status: unavailable`과 `Cache-Control: no-store`를 내려준다.

Review follow-up:
- format marker interface를 추가해 사용자가 직접 등록한 DOCX/HWP/HWPX renderer가 기본 preview renderer를 type 기준으로 대체할 수 있게 했다.
- PPTX package를 POI 파싱 전에 ZIP entry/aggregate byte budget으로 pre-scan한다.
- HWP/HWPX parser에 entry 및 aggregate extraction budget을 적용했다.
- DOCX parser도 POI 파싱 전 ZIP entry/aggregate byte budget으로 pre-scan한다.
- attachment starter README의 optional dependency 안내를 PDF/PPTX/DOCX-HWP-HWPX 기준으로 분리했다.

Subagent usage:
- code-reviewer: renderer replacement, dependency docs, async queue reject, stale thumbnail race findings.
- security-auditor: PPTX package bound, HWP aggregate budget, deterministic failure memoization findings.

Validation:
- ./gradlew :studio-platform-textract:test --tests 'studio.one.platform.textract.extractor.impl.DocxFileParserTest' :studio-application-modules:attachment-service:test --tests 'studio.one.application.attachment.thumbnail.ThumbnailServiceImplTest' --tests 'studio.one.application.web.controller.AttachmentControllerTest' :starter:studio-application-starter-attachment:test => BUILD SUCCESSFUL
- ./gradlew :studio-platform-textract:test :studio-platform-thumbnail:test :starter:studio-platform-thumbnail-starter:test :studio-application-modules:attachment-service:test :starter:studio-application-starter-attachment:test => BUILD SUCCESSFUL
- ./gradlew test => BUILD SUCCESSFUL
- git diff --check => passed
- /Users/donghyuck.son/git/studio-one/studio-one-api-server ./gradlew compileJava => BUILD SUCCESSFUL
- /Users/donghyuck.son/git/studio-one/studio-one-api-server git diff --check => passed
@donghyuck donghyuck force-pushed the codex/issue-371-document-thumbnail-renderers branch from 486e95f to 094aba9 Compare April 29, 2026 07:59
@donghyuck donghyuck merged commit cf28777 into 2.x Apr 29, 2026
2 checks passed
@donghyuck donghyuck deleted the codex/issue-371-document-thumbnail-renderers branch April 29, 2026 08:38
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.

1 participant