Skip to content

Refactor/file delete - #180

Merged
hwangtae123 merged 7 commits into
developfrom
refactor/file-delete
Jun 1, 2026
Merged

Refactor/file delete#180
hwangtae123 merged 7 commits into
developfrom
refactor/file-delete

Conversation

@hwangtae123

@hwangtae123 hwangtae123 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Tasks

  • 스케줄 추가로 무한 pedding 문제 해결
  • pedding 시 5분 지나면 fastAPI에 재처리 요청
  • pedding 상태로 30분 지나면 자동 failed 처리

To Reviewer

Screenshot

Summary by CodeRabbit

주요 변경사항

  • 새로운 기능
    • 미처리 상태로 남아있는 문서에 대한 자동 재처리 메커니즘이 추가되었습니다
    • 설정된 시간을 초과한 문서는 자동으로 처리 실패 상태로 업데이트됩니다
    • 문서 상태를 주기적으로 모니터링하는 스케줄링 기능이 추가되었습니다

hwangtae123 and others added 6 commits May 25, 2026 22:31
fix : docker-compose.prod.yml admin-bff에 FASTAPI_SERVICE_URL 추가(#175)
- DocumentRepository: findByStatusAndCreatedAtBefore 쿼리 추가
- DocumentService: getPendingDocumentsOlderThan(int minutes) 메서드 추가
- GuideonCoreApplication: @EnableScheduling 추가
- DocumentGlobalInternalController: GET /internal/v1/documents/pending 엔드포인트 추가
- DocumentTimeoutScheduler: PENDING 30분 초과 문서 FAILED 자동 전환 (5분 주기)
- GuideonBackendApplication: @EnableScheduling 추가
- CoreDocumentClient: getPendingDocuments Feign 메서드 추가
- DocumentRetryScheduler: PENDING 5분 초과 문서 FastAPI 자동 재처리 요청 (5분 주기)
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@hwangtae123, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2d375151-5d70-43e3-a537-a84bad812efa

📥 Commits

Reviewing files that changed from the base of the PR and between 4479c8c and 5fc7b24.

📒 Files selected for processing (4)
  • guideon-admin-bff/src/main/resources/application.yml
  • guideon-core/src/main/java/com/guideon/core/api/DocumentGlobalInternalController.java
  • guideon-core/src/main/java/com/guideon/core/domain/document/entity/Document.java
  • guideon-core/src/main/java/com/guideon/core/domain/document/scheduler/DocumentTimeoutScheduler.java

개요

이 PR은 대기 중인 문서에 대한 자동 재처리 및 타임아웃 스케줄링 기능을 추가합니다. Core 서비스는 대기 문서 조회 API와 타임아웃 스케줄러를 제공하며, Admin-BFF는 이 API를 호출하는 재시도 스케줄러를 구현합니다.

변경사항

문서 재시도 및 타임아웃 스케줄링 프레임워크

계층 / 파일 요약
Spring 스케줄링 활성화
guideon-admin-bff/src/main/java/com/guideon/guideonbackend/GuideonBackendApplication.java, guideon-core/src/main/java/com/guideon/core/GuideonCoreApplication.java
두 애플리케이션 모두에서 @EnableScheduling 어노테이션 및 임포트를 추가하여 Spring 스케줄링 기능을 활성화합니다.
Core 대기 문서 조회 API
guideon-core/src/main/java/com/guideon/core/domain/document/repository/DocumentRepository.java, guideon-core/src/main/java/com/guideon/core/service/DocumentService.java, guideon-core/src/main/java/com/guideon/core/api/DocumentGlobalInternalController.java
DocumentRepository에 상태와 생성 시간 기반 쿼리 메서드 추가; DocumentService에 지정된 시간보다 오래된 대기 문서 조회 메서드 추가; DocumentGlobalInternalController에서 /internal/v1/documents/pending 엔드포인트 노출하여 대기 문서 반환
Core 문서 타임아웃 스케줄러
guideon-core/src/main/java/com/guideon/core/domain/document/scheduler/DocumentTimeoutScheduler.java
주기적으로 설정된 타임아웃 임계값을 초과한 PENDING 상태 문서를 조회하고 FAILED로 상태 전환하며, 전환 이유 및 경고 로그를 기록합니다.
Admin-BFF 재시도 클라이언트 및 스케줄러
guideon-admin-bff/src/main/java/com/guideon/guideonbackend/client/CoreDocumentClient.java, guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java
CoreDocumentClient에 Core 서비스 /internal/v1/documents/pending 엔드포인트 호출 메서드 추가; DocumentRetryScheduler는 주기적으로 대기 문서를 조회하고 각 문서별 재처리 요청을 수행하며, 개별 오류를 처리하여 나머지 문서 처리를 계속합니다.
환경 설정 및 코드 포맷팅
docker-compose.yml, guideon-core/src/main/java/com/guideon/core/api/DocumentInternalController.java
docker-compose.yml의 admin-bff 서비스에 FILE_BASE_URL 환경 변수 추가(http://admin-bff:8081로 설정); DocumentInternalController에 공백 라인 추가

예상 코드 리뷰 난이도

🎯 3 (중간 난이도) | ⏱️ ~25분

관련 이슈

  • #179: 이 PR은 DocumentRetryScheduler, DocumentTimeoutScheduler, CoreDocumentClient.getPendingDocuments, DocumentRepository.findByStatusAndCreatedAtBefore, DocumentService.getPendingDocumentsOlderThan, 내부 GET /internal/v1/documents/pending 엔드포인트, 그리고 양쪽 애플리케이션의 @EnableScheduling 등 동일한 스케줄된 재시도 및 타임아웃 기능을 구현하므로 직접 관련이 있습니다.

축하 시

🐰 문서들이 이제 스스로를 챙겨봐요,
타임아웃에 안녕을 고하고,
재시도로 다시 일어나니,
스케줄러의 마법으로 모든 것이 흘러가요,
자동으로, 주기적으로, 완벽하게! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning 제목 'Refactor/file delete'는 무한 pending 문제 해결을 위한 스케줄러 추가라는 실제 변경사항과 관련이 없으며, PR의 주요 변경사항을 정확히 반영하지 못합니다. 제목을 'Add document retry and timeout schedulers for pending document handling' 또는 '무한 pending 문제 해결을 위한 스케줄러 추가'로 변경하여 실제 변경사항을 정확히 반영하도록 개선하십시오.
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed PR 설명이 필수 섹션(Summary, Tasks, To Reviewer)을 기본 구조는 따르고 있으나, 변경사항의 기술적 세부사항이 부족합니다.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/file-delete

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.

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

Caution

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

⚠️ Outside diff range comments (1)
guideon-admin-bff/src/main/java/com/guideon/guideonbackend/GuideonBackendApplication.java (1)

14-18: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

@EnableScheduling + com.guideon.core.domain 스캔으로 core 스케줄러가 여기서도 실행됩니다.

scanBasePackagescom.guideon.core.domain이 포함되어 있어, 해당 패키지 하위의 DocumentTimeoutScheduler(com.guideon.core.domain.document.scheduler)가 admin-bff 컨텍스트에도 빈으로 등록됩니다. 여기에 @EnableScheduling까지 더해지면 타임아웃 스케줄러가 core와 admin-bff 양쪽에서 중복 실행됩니다. 자세한 내용은 DocumentTimeoutScheduler 코멘트를 참고하세요.

🤖 Prompt for 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.

In
`@guideon-admin-bff/src/main/java/com/guideon/guideonbackend/GuideonBackendApplication.java`
around lines 14 - 18, The application scans com.guideon.core.domain while also
enabling scheduling, which causes DocumentTimeoutScheduler
(com.guideon.core.domain.document.scheduler.DocumentTimeoutScheduler) to be
registered and run twice; to fix, stop scanning that package or explicitly
exclude the scheduler: in GuideonBackendApplication remove
"com.guideon.core.domain" from the scanBasePackages array or add a ComponentScan
excludeFilters entry (or use `@ComponentScan`(basePackages = {...}, excludeFilters
= `@Filter`(type = FilterType.ASSIGNABLE_TYPE, classes =
DocumentTimeoutScheduler.class))) so the DocumentTimeoutScheduler is not created
in this context while keeping `@EnableScheduling` for the admin-bff only.
🧹 Nitpick comments (1)
guideon-core/src/main/java/com/guideon/core/service/DocumentService.java (1)

115-121: ⚡ Quick win

DocumentDto.from에서 Site LAZY 로딩으로 N+1 쿼리 가능성이 있습니다.

  • DocumentDto.fromdoc.getSite().getSiteId()를 호출하고, Document.site@ManyToOne(fetch = FetchType.LAZY)라서 findByStatusAndCreatedAtBefore로 가져온 문서 개수만큼 추가 SELECT가 발생할 수 있습니다. JOIN FETCH/@EntityGraphSite를 함께 가져오는 쪽으로 개선 권장.
  • /internal/v1/documents/pendingolderThanMinutesdefaultValue="5"만 있고 음수 입력 검증이 없어(예: @Min 없음) 음수 전달 시 cutoff이 미래로 계산되어 조회 범위가 과도해질 수 있습니다.
🤖 Prompt for 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.

In `@guideon-core/src/main/java/com/guideon/core/service/DocumentService.java`
around lines 115 - 121, The current getPendingDocumentsOlderThan uses
documentRepository.findByStatusAndCreatedAtBefore and then DocumentDto.from
which calls doc.getSite().getSiteId(), causing N+1 due to Document.site being
LAZY; change the repository call to fetch Site eagerly (e.g., add a repository
method annotated with `@EntityGraph`(attributePaths = "site") or a JPQL query with
JOIN FETCH, e.g., findWithSiteByStatusAndCreatedAtBefore) and call that from
DocumentService.getPendingDocumentsOlderThan so DocumentDto.from won't trigger
extra selects; additionally validate the olderThanMinutes input on the
/internal/v1/documents/pending endpoint (add `@Min`(0) or explicit negative check
on the controller method parameter) to prevent a negative value producing a
future cutoff.
🤖 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 `@docker-compose.yml`:
- Line 79: The docker-compose sets FILE_BASE_URL to an internal container host
(http://admin-bff:8081) which LocalFileStorageService uses as the public file
URL (via file.base-url in application.yml), causing external clients to receive
unreachable links; update the local docker-compose.yml to set FILE_BASE_URL to
an externally reachable address (e.g., http://localhost:8081) or add an env
branching mechanism so application.yml’s file.base-url resolves to a public host
for local runs, ensuring FileDownloadController-served paths remain accessible
and SecurityConfig’s permitAll endpoints produce usable URLs.

In
`@guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java`:
- Around line 28-50: The scheduler currently fetches PENDING docs and calls
reprocessDocument but because Document.reprocess resets fields without changing
createdAt, the same doc is repeatedly picked up; modify retryPendingDocuments to
atomically mark a document as PROCESSING (or RETRYING) before calling
coreDocumentClient.reprocessDocument (e.g., via a new
coreDocumentClient.markProcessing(siteId, docId) or a single
coreDocumentClient.reserveAndReprocess(siteId, docId) endpoint) and ensure the
core side updates createdAt/lastRetryAt or attemptCount so
getPendingDocuments(retryAfterMinutes) excludes recently retried items;
alternatively add/consume lastRetryAt/attemptCount fields in getPendingDocuments
filtering to throttle retries. Ensure changes touch
DocumentRetryScheduler.retryPendingDocuments, coreDocumentClient.* (new
mark/reserve API or reprocess semantics), and server-side Document.reprocess /
FastApiDocumentService.processDocument to perform atomic state transition to
PROCESSING/RETRYING.

In
`@guideon-core/src/main/java/com/guideon/core/api/DocumentGlobalInternalController.java`:
- Around line 22-26: Add parameter validation to prevent non-positive minutes:
annotate the controller class DocumentGlobalInternalController with `@Validated`
and add a javax.validation constraint on the getPendingDocuments method
parameter (e.g., `@Min`(1) or `@Positive` on the olderThanMinutes `@RequestParam`) so
invalid values are rejected before calling
documentService.getPendingDocumentsOlderThan; add the required import for the
chosen constraint and ensure javax validation is on the class via `@Validated`.

In
`@guideon-core/src/main/java/com/guideon/core/domain/document/scheduler/DocumentTimeoutScheduler.java`:
- Around line 1-44: The DocumentTimeoutScheduler is being registered in both
core and admin-bff causing duplicate scheduling; restrict its activation by
annotating the DocumentTimeoutScheduler class with a conditional (e.g., Spring's
`@ConditionalOnProperty` or `@Profile`) so it only runs when a property like
document.enable-timeout-scheduler=true is set (choose matchIfMissing=true or
false per desired default), and update the deployments: enable it in
GuideonCoreApplication and disable it in GuideonBackendApplication (or set the
appropriate profile) so only one app executes failTimedOutDocuments(); refer to
the class DocumentTimeoutScheduler, its failTimedOutDocuments() method, and the
new property document.enable-timeout-scheduler when applying the change and
documenting the required configuration.

---

Outside diff comments:
In
`@guideon-admin-bff/src/main/java/com/guideon/guideonbackend/GuideonBackendApplication.java`:
- Around line 14-18: The application scans com.guideon.core.domain while also
enabling scheduling, which causes DocumentTimeoutScheduler
(com.guideon.core.domain.document.scheduler.DocumentTimeoutScheduler) to be
registered and run twice; to fix, stop scanning that package or explicitly
exclude the scheduler: in GuideonBackendApplication remove
"com.guideon.core.domain" from the scanBasePackages array or add a ComponentScan
excludeFilters entry (or use `@ComponentScan`(basePackages = {...}, excludeFilters
= `@Filter`(type = FilterType.ASSIGNABLE_TYPE, classes =
DocumentTimeoutScheduler.class))) so the DocumentTimeoutScheduler is not created
in this context while keeping `@EnableScheduling` for the admin-bff only.

---

Nitpick comments:
In `@guideon-core/src/main/java/com/guideon/core/service/DocumentService.java`:
- Around line 115-121: The current getPendingDocumentsOlderThan uses
documentRepository.findByStatusAndCreatedAtBefore and then DocumentDto.from
which calls doc.getSite().getSiteId(), causing N+1 due to Document.site being
LAZY; change the repository call to fetch Site eagerly (e.g., add a repository
method annotated with `@EntityGraph`(attributePaths = "site") or a JPQL query with
JOIN FETCH, e.g., findWithSiteByStatusAndCreatedAtBefore) and call that from
DocumentService.getPendingDocumentsOlderThan so DocumentDto.from won't trigger
extra selects; additionally validate the olderThanMinutes input on the
/internal/v1/documents/pending endpoint (add `@Min`(0) or explicit negative check
on the controller method parameter) to prevent a negative value producing a
future cutoff.
🪄 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: CHILL

Plan: Pro

Run ID: 4e9199d8-baf4-49e2-be4e-5ed2f755e3bf

📥 Commits

Reviewing files that changed from the base of the PR and between 4b0321d and 4479c8c.

📒 Files selected for processing (10)
  • docker-compose.yml
  • guideon-admin-bff/src/main/java/com/guideon/guideonbackend/GuideonBackendApplication.java
  • guideon-admin-bff/src/main/java/com/guideon/guideonbackend/client/CoreDocumentClient.java
  • guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/scheduler/DocumentRetryScheduler.java
  • guideon-core/src/main/java/com/guideon/core/GuideonCoreApplication.java
  • guideon-core/src/main/java/com/guideon/core/api/DocumentGlobalInternalController.java
  • guideon-core/src/main/java/com/guideon/core/api/DocumentInternalController.java
  • guideon-core/src/main/java/com/guideon/core/domain/document/repository/DocumentRepository.java
  • guideon-core/src/main/java/com/guideon/core/domain/document/scheduler/DocumentTimeoutScheduler.java
  • guideon-core/src/main/java/com/guideon/core/service/DocumentService.java

Comment thread docker-compose.yml
SPRING_DATA_REDIS_PORT: 6379
CORE_SERVICE_URL: http://core:8080
FASTAPI_SERVICE_URL: http://fastapi:8000
FILE_BASE_URL: http://admin-bff:8081

@coderabbitai coderabbitai Bot Jun 1, 2026

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 | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: FILE_BASE_URL 환경변수 사용처 확인

# admin-bff 코드베이스에서 FILE_BASE_URL 참조 검색
rg -n --type=java -C3 'FILE_BASE_URL' guideon-admin-bff/

# application.yml 또는 properties 파일에서 설정 확인
fd -e yml -e yaml -e properties . guideon-admin-bff/ --exec rg -n 'file.*base.*url' {} \;

Repository: Project-Guideon/guideon-backend

Length of output: 57


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show docker-compose.yml around the referenced line
echo "== docker-compose.yml: around line 70-90 =="
nl -ba docker-compose.yml | sed -n '70,95p'

# 2) Repo-wide search for exact key
echo
echo "== rg: FILE_BASE_URL (repo-wide) =="
rg -n --hidden --no-ignore -S 'FILE_BASE_URL' . || true

# 3) Repo-wide search for likely config/property names (case-insensitive)
echo
echo "== rg: file.*base.*url (repo-wide, case-insensitive) =="
rg -n --hidden --no-ignore -i -S 'file.*base.*url' . || true

echo
echo "== rg: file-base-url (repo-wide, case-insensitive) =="
rg -n --hidden --no-ignore -i -S 'file[-_]?base[-_]?url' . || true

echo
echo "== rg: fileBaseUrl (repo-wide) =="
rg -n --hidden --no-ignore -S 'fileBaseUrl' . || true

Repository: Project-Guideon/guideon-backend

Length of output: 180


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docker-compose.yml: around line 70-90 =="
# show with line numbers using awk (avoid nl)
awk 'NR>=70 && NR<=95 {printf "%4d:%s\n", NR, $0}' docker-compose.yml

echo
echo "== rg: FILE_BASE_URL (repo-wide, exact key) =="
rg -n --hidden --no-ignore -S 'FILE_BASE_URL' . || true

echo
echo "== rg: file.*base.*url (repo-wide, case-insensitive) =="
rg -n --hidden --no-ignore -i -S 'file.*base.*url' . || true

echo
echo "== rg: file-base-url (repo-wide, case-insensitive) =="
rg -n --hidden --no-ignore -i -S 'file[-_]?base[-_]?url' . || true

echo
echo "== rg: fileBaseUrl (repo-wide, exact) =="
rg -n --hidden --no-ignore -S 'fileBaseUrl' . || true

Repository: Project-Guideon/guideon-backend

Length of output: 4639


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== application.yml (guideon-admin-bff) around line 80-120 =="
sed -n '80,120p' guideon-admin-bff/src/main/resources/application.yml

echo
echo "== LocalFileStorageService.java around lines 1-120 =="
sed -n '1,120p' guideon-admin-bff/src/main/java/com/guideon/guideonbackend/global/storage/LocalFileStorageService.java

Repository: Project-Guideon/guideon-backend

Length of output: 6012


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n --hidden --no-ignore -S 'internal/files' guideon-admin-bff/src/main/java || true
rg -n --hidden --no-ignore -S '"/internal/files' guideon-admin-bff/src/main/java || true
rg -n --hidden --no-ignore -S 'internalFiles' guideon-admin-bff/src/main/java || true

Repository: Project-Guideon/guideon-backend

Length of output: 1918


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== FileDownloadController.java (around mapping) =="
sed -n '1,140p' guideon-admin-bff/src/main/java/com/guideon/guideonbackend/global/storage/FileDownloadController.java

echo
echo "== SecurityConfig.java (around /internal/files/** allowlist) =="
sed -n '1,120p' guideon-admin-bff/src/main/java/com/guideon/guideonbackend/global/config/SecurityConfig.java

echo
echo "== deploy/docker-compose.prod.yml (around FILE_BASE_URL) =="
sed -n '60,110p' deploy/docker-compose.prod.yml

Repository: Project-Guideon/guideon-backend

Length of output: 11625


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== rg: internal/files/ (repo-wide) =="
rg -n --hidden --no-ignore -S 'internal/files/' . || true

echo
echo "== rg: storageUrl (repo-wide) =="
rg -n --hidden --no-ignore -S 'storageUrl' . || true

Repository: Project-Guideon/guideon-backend

Length of output: 6769


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== DocumentService.java (around where it sets storageUrl) =="
sed -n '1,220p' guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/document/service/DocumentService.java

echo
echo "== MascotService.java (around where it sets imageUrl/storageUrl) =="
sed -n '1,180p' guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/mascot/service/MascotService.java

echo
echo "== PlaceService.java (around where it sets imageUrl/storageUrl) =="
sed -n '110,220p' guideon-admin-bff/src/main/java/com/guideon/guideonbackend/domain/place/service/PlaceService.java

echo
echo "== rg FASTAPI_SERVICE_URL usage (admin-bff) =="
rg -n --hidden --no-ignore -S 'FASTAPI_SERVICE_URL' guideon-admin-bff/src/main/java || true

echo
echo "== rg calls to FastAPI base URL or /pdf /generate (admin-bff) =="
rg -n --hidden --no-ignore -S 'fastapi' guideon-admin-bff/src/main/java || true

Repository: Project-Guideon/guideon-backend

Length of output: 20994


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docker-compose.yml: admin-bff service (ports + environment) =="
# show admin-bff block (best-effort by printing a range around the first occurrence)
rg -n --hidden --no-ignore -S '^  admin-bff:' docker-compose.yml
START=$(rg -n --hidden --no-ignore -S '^  admin-bff:' docker-compose.yml | head -n1 | cut -d: -f1)
# show 80 lines starting from admin-bff start
sed -n "$((START)),$((START+120))p" docker-compose.yml

echo
echo "== deploy/nginx.conf.template (location /internal/files) =="
sed -n '1,120p' deploy/nginx.conf.template
sed -n '35,80p' deploy/nginx.conf.template

Repository: Project-Guideon/guideon-backend

Length of output: 7014


FILE_BASE_URL가 실제 파일 다운로드 URL 생성에 사용되므로 로컬 값(admin-bff)이 외부 클라이언트에선 깨질 수 있습니다.

  • guideon-admin-bff/src/main/resources/application.ymlfile.base-url: ${FILE_BASE_URL:...} 값이 LocalFileStorageService에서 /internal/files/{siteId}/{filename} URL 호스트로 그대로 사용됩니다.
  • /internal/files/**FileDownloadController로 제공되고 SecurityConfig에서 permitAll이라 생성된 URL이 그대로 노출/소비됩니다.
  • 프로덕션은 deploy/docker-compose.prod.yml에서 FILE_BASE_URL=https://${DOMAIN}로 외부 접근 가능한 값이라 문제 소지가 낮습니다.
  • 하지만 로컬 docker-compose.yml(79행) FILE_BASE_URL: http://admin-bff:8081는 컨테이너 내부 DNS라 브라우저/외부 클라이언트에서 해석되지 않으면 이미지/다운로드가 실패할 수 있습니다.

로컬 실행 시 FILE_BASE_URLhttp://localhost:8081처럼 외부에서 접근 가능한 주소로 변경(또는 env 분기) 권장합니다.

🤖 Prompt for 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.

In `@docker-compose.yml` at line 79, The docker-compose sets FILE_BASE_URL to an
internal container host (http://admin-bff:8081) which LocalFileStorageService
uses as the public file URL (via file.base-url in application.yml), causing
external clients to receive unreachable links; update the local
docker-compose.yml to set FILE_BASE_URL to an externally reachable address
(e.g., http://localhost:8081) or add an env branching mechanism so
application.yml’s file.base-url resolves to a public host for local runs,
ensuring FileDownloadController-served paths remain accessible and
SecurityConfig’s permitAll endpoints produce usable URLs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

현재 docker-compose.yml은 이미 https://${DOMAIN} 으로 되어 있어 pull 후 해결된 상태. application.yml 기본값 http://localhost:8081은 로컬 개발용이라 문제없을 것 같습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@hwangtae123, 설명 감사합니다. 정리하면:

  • 프로덕션 (deploy/docker-compose.prod.yml): FILE_BASE_URL=https://${DOMAIN} — 외부 접근 가능, 문제없음 ✅
  • application.yml 기본값 (http://localhost:8081): 순수 로컬 개발(IDE에서 직접 실행)용으로 적절 ✅

한 가지만 참고로 남기면, 로컬 Docker Compose 환경 (현재 docker-compose.ymlhttp://admin-bff:8081)에서는 컨테이너 내부 DNS를 사용하므로, 브라우저/외부 클라이언트에서 생성된 파일 URL이 깨질 수 있습니다. 로컬 Compose 실행 시에는 http://localhost:8081로 변경하거나 .env로 분기하는 것을 추후 고려해 보시면 좋을 것 같습니다. 현재 요구사항 범위에서는 프로덕션이 이미 올바르게 설정되어 있으므로 큰 문제는 없어 보입니다.

(∪・ω・∪)

- reprocess() 상태를 PENDING → PROCESSING으로 변경하여 재처리 스케줄러의 중복 요청 방지
- DocumentTimeoutScheduler에 @ConditionalOnProperty 추가로 admin-bff 중복 실행 방지
- admin-bff application.yml에 timeout-scheduler 비활성화 설정 추가
- DocumentGlobalInternalController olderThanMinutes 파라미터에 @positive 검증 추가
@hwangtae123
hwangtae123 merged commit 33a8056 into develop Jun 1, 2026
2 checks passed
@hwangtae123 hwangtae123 mentioned this pull request Jun 1, 2026
1 task
@hwangtae123
hwangtae123 deleted the refactor/file-delete branch June 3, 2026 13:33
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