Compare commits

...

2 Commits

Author SHA1 Message Date
joungmin
a4de9ba87b feat(admin): #356 후속 — verifyAllAsync로 즉시 응답
- 1244 링크 백필 동기 호출 시 HTTP 25~30분 hang (ingress 5분 timeout 초과)
- @Async verifyAllAsync 추가, controller는 started/pending 즉시 응답
- 진행은 /api/admin/video-relevance/pending 폴링으로 확인

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 15:15:48 +09:00
joungmin
cf37e496d4 docs(design): #363 실 운영 fix 기록 (1~2단계 + v0.1.57 안정화)
- 배포 흐름 v0.1.51~v0.1.57 정리
- 운영 진단 확인사항 (NCLOUD 도메인 형식, Search/Maps 별개 시스템)
- NaverMapView 안정화 핵심 결정사항 (divRef 항상 마운트, ResizeObserver, try/catch)
- 후속 분리 항목 명시

Refs: #363

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 10:56:51 +09:00
3 changed files with 43 additions and 3 deletions

View File

@@ -40,9 +40,11 @@ public class AdminVideoRelevanceController {
@PostMapping("/all") @PostMapping("/all")
public Map<String, Object> verifyAll(@RequestParam(defaultValue = "10") int batchSize) { public Map<String, Object> verifyAll(@RequestParam(defaultValue = "10") int batchSize) {
var admin = AuthUtil.requireAdmin(); var admin = AuthUtil.requireAdmin();
log.info("[ADMIN] {} triggered video-relevance verifyAll(batchSize={})", admin.getSubject(), batchSize); int pending = restaurantService.countUnevaluatedLinks();
int processed = relevanceService.verifyAll(batchSize); log.info("[ADMIN] {} triggered video-relevance verifyAllAsync (batchSize={}, pending={})", admin.getSubject(), batchSize, pending);
return Map.of("processed", processed); // 비동기 트리거 — HTTP request는 즉시 응답. 진행은 /pending 폴링으로 확인.
relevanceService.verifyAllAsync(batchSize);
return Map.of("started", true, "pending", pending);
} }
@PostMapping("/{linkId}/evaluate") @PostMapping("/{linkId}/evaluate")

View File

@@ -63,6 +63,16 @@ public class VideoRelevanceService {
restaurantService.updateLinkRelevance(linkId, result.relevance(), truncate(result.reason(), 120)); restaurantService.updateLinkRelevance(linkId, result.relevance(), truncate(result.reason(), 120));
} }
@Async
public void verifyAllAsync(int batchSize) {
try {
int n = verifyAll(batchSize);
log.info("[VideoRelevance] backfill done: {} processed", n);
} catch (Exception e) {
log.warn("verifyAllAsync failed: {}", e.getMessage());
}
}
public int verifyAll(int batchSize) { public int verifyAll(int batchSize) {
int total = 0; int total = 0;
List<Map<String, Object>> batch; List<Map<String, Object>> batch;

View File

@@ -82,3 +82,31 @@ MapView (dispatcher)
- 한 화면 mixed(국가 경계 근처) 동시 마커 — 후속. - 한 화면 mixed(국가 경계 근처) 동시 마커 — 후속.
- 사용자 토글 UI — 후속. - 사용자 토글 UI — 후속.
- 모바일 nearby 동일 분기 — 1차 적용 후 결정. - 모바일 nearby 동일 분기 — 1차 적용 후 결정.
## 11. 실제 구현 기록 (2026-06-16)
### 배포 흐름
| 버전 | 내용 |
|---|---|
| v0.1.51 | **1단계** — 식당 상세 외부 링크 좌표 기반 분기 (`RestaurantDetail.tsx`) |
| v0.1.52 | **2단계** — MapView dispatcher + NaverMapView/GoogleMapView 분리 + Dockerfile/deploy.sh build-arg |
| v0.1.53 | **fix**: 인증 파라미터 `ncpClientId``ncpKeyId` (NCLOUD 신 정책, 옛 NAVER Developers와 다름) |
| v0.1.5556 | 임시 fallback (운영 일시 GoogleMap, 디버그) |
| v0.1.57 | **안정화 + 재활성** — divRef 첫 렌더 누락 fix, ResizeObserver/rAF, try/catch |
### 운영 진단에서 확인된 사항
- NCLOUD Maps Application의 Web 서비스 URL은 **스킴 포함**(`https://...`).
- 옛 NAVER Developers와 NCLOUD는 다른 시스템 — Search Application과 Maps Application은 도메인 중복 충돌 없음.
- NCLOUD 콘솔의 신규 경로: `Services > Application Services > Maps > Application`.
### NaverMapView 안정화 핵심 결정사항
- **`divRef` 항상 마운트** (early return 제거) — `ready=false` 동안에도 div를 두고 로딩 메시지는 overlay로 표시.
- **명시적 `width:100%; height:100%`** + 회색 배경 — 컨테이너 영역이 시각적으로 확인 가능.
- **ResizeObserver + requestAnimationFrame**으로 컨테이너 0×0 → 정상 크기 변경 시 `m.refresh(true)`.
- **try/catch + `initError` state** — init 실패 시 화면 가시화.
### 후속 (별도 PR)
- 사용자 토글 (네이버/구글 강제 선택) UI.
- mixed 화면(국경 근처) 동시 마커.
- 모바일 nearby 탭 동일 분기 검토.
- 채널 색상/InfoWindow 등 GoogleMapView 수준의 디테일을 NaverMapView에 도입.