1
0
Fork 0
Commit graph

27 commits

Author SHA1 Message Date
Yun Chan
4ca7352dc3 feat(ui): default to active queue, scope search toolbar, add double-bezel inspector, optimize queue columns, and achieve 100% pure Korean UI 2026-09-05 12:25:30 +09:00
Yun Chan
455c8b1324 feat(options): implement granular fold-out encoding options with industry standards
- Adopt Microsoft Fluent 2 Progressive Disclosure pattern with AdvancedOptionsExpander
- Add ISO 32000 / Ghostscript PDF optimization panel (Light/Strong/Max, 150/200/300 DPI)
- Add W3C / MozJPEG Image panel with Lossless WebP/PNG and Progressive toggles
- Add FFmpeg video/audio granular controls (codecs, CRF/ABR rate control, presets, Loudnorm)
- Mirror advanced options in QuickOptionsWindow context dialog
- Add DesignAudit AST and visual tests for foldout structure (249 tests passing in 2s)
2026-09-05 07:29:46 +09:00
Yun Chan
7cb75fd3aa fix(ui): fix SettingsWindow and dialog titlebar margins, vertical alignment, and button styling 2026-09-05 06:36:26 +09:00
Yun Chan
1f794e9e08 fix(ui): unify titlebar centerline vertical alignment and eliminate redundant brand container (v1.0.14) 2026-09-04 23:58:41 +09:00
Yun Chan
94a67f328c test(e2e): add complex multi-hop, storage savings and domain cross-boundary rejection scenarios 2026-09-03 22:13:02 +09:00
Yun Chan
4a4f2b78e4 feat(installer): add Inno Setup installer and fix Shell C++ DLL docx leakage
- Introduce Inno Setup standard EXE installer (Everything2Everything-Setup-x64.exe) for zero-friction end-user installation without certificate trust friction
- Fix hardcoded .docx recommendation in Everything2Everything.Shell dllmain.cpp
- Improve MSIX Install.cmd and Install.ps1 with robust privilege elevation and path parsing
- Integrate Inno Setup into tools/Publish-Release.ps1 pipeline and release upload
- Add automated regression tests for installer and packaging integrity
2026-09-03 20:57:26 +09:00
Yun Chan
28624c386e feat(negotiation): comprehensive media conversion filter negotiation across graph, UI, and shell
- Prevent cross-category graph leakage (png -> txt -> docx) in ConversionGraph.FindBestPath and ReachableOutputs
- Distinguish Video and Audio in MediaConversionNegotiator (block mp3->gif, allow mp4->png frame extraction)
- Handle empty common output intersection in MainWindow without forcing .jpg fallback
- Expand PopularOutputs in ContextMenuRegistrar to support mp4, mp3, and xlsx
- Add comprehensive TDD unit tests (213 passing tests)
2026-09-03 18:47:45 +09:00
Yun Chan
b67b3f1b61 feat(ui): implement 1-click presets, real-time search & filter bar, batch queue actions, and file inspector 2026-09-03 17:12:14 +09:00
Yun Chan
41c8a8a94b fix(ui): resolve pack URI assembly prefixes, crash-proof dynamic resource brush lookups, and add MainWindow visual tree test
Some checks failed
Forgejo Release / build-and-release (push) Has been cancelled
2026-09-03 12:30:21 +09:00
Yun Chan
4cd9c678c0 feat(tdd): implement SSOT canon, AGENTS.md, ultra-strict design audit, E2E scenarios, and Forgejo release pipeline
Some checks are pending
Forgejo Release / build-and-release (push) Waiting to run
2026-09-03 12:21:40 +09:00
Yun Chan
214789b656 feat(ffmpeg): 영상·오디오 상세 인코딩 옵션 (코덱·CRF·비트레이트·해상도·오디오 등)
HandBrake/Shutter Encoder급 인코더 옵션을 추가(웹 리서치 7-agent 종합 기반). 코어 + 테스트.

- ConvertOptions: VideoEncodeOptions/AudioEncodeOptions 불변 record + enum(VideoCodec/AudioCodec/
  RateControlMode/AudioRateMode/VideoSpeedPreset/RotateMode/DeinterlaceMode). 영상 24필드·오디오 14필드.
- FfmpegArgBuilder(신규, 순수·테스트가능): 옵션→ffmpeg 출력 인자 매핑.
  * 레이트 컨트롤 5모드: CRF / 평균비트레이트(ABR) / 제약CRF(+maxrate) / CBR / 2패스.
  * 코덱 해소 + 컨테이너 호환(webm→vp9/av1 강제, Auto는 >4096px HEVC).
  * 프리셋 매핑: x264 문자열 / svtav1 숫자 / vp9 cpu-used / NVENC p1~p7.
  * HW 계층(EncoderTier): NVENC/QSV/AMF/CPU별 인자(CRF↔CQ/global_quality/qvbr).
  * -vf 필터 체인(crop→deinterlace→denoise→scale→transpose), 다운스케일 전용(min(W,iw)).
  * 오디오: 코덱별 비트레이트 vs VBR(-q:a), flac compression_level, wav pcm, 샘플레이트/채널/loudnorm/copy.
  * faststart(mp4/mov만), pix_fmt/profile/level/gop/bframes, RawArgs 탈출구.
- FfmpegProvider: RunAsync(bool)→다단계 폴백(NVENC→QSV→AMF→CPU, 계층별 인자 재생성) + 2패스(passlog 격리)
  + 트림(입력 Seek + WithDuration). 현행 자동선택/프레임추출/GPU폴백 동작 보존(Codec=Auto 경로).
- FfmpegArgBuilderTests 23개: 5모드·코덱강제·NVENC매핑·필터순서·오디오모드·faststart·GPU적용 판정.

83 → 106개 테스트 전부 그린, 빌드 0경고/0오류. UI 노출은 후속 슬라이스(GUI 스모크).
2026-06-02 20:12:16 +09:00
Yun Chan
e95ec967e6 refactor(P5b-2): 출력 형식 교집합 로직을 ProviderRegistry로 추출 (매트릭스 필터 테스트화)
MainWindow.RefreshAvailableOutputFormats에 박혀 있던 '큐 전체 공통 출력' 교집합 계산을
ProviderRegistry.AvailableOutputsForFiles 순수 쿼리로 추출 — 코드비하인드 18줄 → 2줄, 헤드리스 테스트 가능.

- ProviderRegistry.AvailableOutputsForFiles(sourcePaths): 비면 전체 출력, 아니면 OutputsForFile 교집합.
- MainWindow: 인라인 교집합 루프 제거 → 레지스트리 메서드 호출. UI(콤보 빌드)는 그대로.
- 테스트 2개: 빈 입력=전체, png+csv 혼합 시 jpg가 교집합에서 빠짐(매트릭스 필터 핵심 동작).

81 → 83개 테스트 전부 그린, 빌드 0경고/0오류.
2026-06-02 09:53:26 +09:00
Yun Chan
ba5738e705 refactor(P5b-1): CommunityToolkit.Mvvm 도입 + OptionsViewModel 추출 (옵션 로직 테스트화)
MVVM 첫 슬라이스 — 테스트 불가능했던 코드비하인드 BuildOptions의 순수 옵션 구성 로직을
헤드리스 단위테스트 가능한 OptionsViewModel.ToConvertOptions로 추출. XAML 바인딩 미변경(런타임 위험 0).

- CommunityToolkit.Mvvm 8.2.2(CPM) + App.csproj. App/ViewModels/OptionsViewModel.cs:
  ObservableObject + [ObservableProperty](Quality/CustomOutputDirectory/ConflictRule/AiTaskIndex/
  TargetLanguage/VideoPreferGpu) → 향후 옵션 패널 직접 바인딩 토대. ToConvertOptions()는 순수.
- MainWindow.BuildOptions: 컨트롤 값을 VM에 반영 후 ToConvertOptions() 위임(동작 동일).
- Tests → App ProjectReference 추가(App 뷰모델 헤드리스 테스트 가능화).
- OptionsViewModelTests 10케이스(품질매핑·AVIF clamp·커스텀폴더 trim·AI작업·대상언어·충돌/GPU).

71 → 81개 테스트 전부 그린, 빌드 0경고/0오류.
2026-06-02 09:51:49 +09:00
Yun Chan
729b9b699d refactor(P6): Independent 배치 병렬화 + Magick ResourceLimits (즉효 최적화)
순차 for-loop(멀티코어를 1코어만 사용)을 코어 수 병렬로 — 50장 배치가 코어 수만큼 가속.
ConvertOptions 불변(P4)이라 공유 읽기 스레드 안전. 시그니처 불변(내부 교체).

- ConversionEngine.ConvertManyAsync: Independent 분기를 Parallel.ForEachAsync(MaxDOP=BatchParallelism)로.
  결과는 인덱스 고정 ConvertResult[]로 순서 보존. 진행률은 완료 건수(Interlocked.Increment)로 보고
  (파일별 분수 진행률은 동시 실행에서 의미가 흐려짐). DOP<=1이면 기존 순차 경로(세부 진행률 보존).
  CombineToSingle은 단일 연산이라 병렬 대상 아님(기존대로).
- ConvertOptions.BatchParallelism(기본 = 논리 코어 수) 추가 — 미디어 위주 배치는 낮춰 튜닝 가능.
- MagickProvider 정적 생성자: ResourceLimits.LimitMemory(60%) — 병렬 시 동시 MagickImage OOM 방지
  (초과분 디스크 스필, 출력 바이트 무영향).
- 신규 테스트: 8장 병렬 배치의 순서 보존(results[i]↔sources[i]) + 전수 변환 검증.

71개 테스트 전부 그린(골든마스터 동일성 유지), 빌드 0경고/0오류.
⚠ 진행률 UX(완료 건수 모델) 변경 — GUI 스모크 권장.
2026-06-02 09:43:20 +09:00
Yun Chan
379a4a738d refactor(P4): ConvertOptions God Object → 불변 record (init-only)
mutable sealed class(13 sub-record 가변)을 불변 record로 전환 — 배치 병렬 변환(P6)에서
공유해도 안전한 스레드 안전 전제 확보 + 값 의미론. 시그니처(ConvertAsync(ConvertOptions))는 비침습 유지.

- ConvertOptions + 13개 옵션 타입(Jpeg/Png/Webp/Avif/Tiff/Bmp/Gif/PdfRender/PdfBuild/HtmlRender/
  Ocr/PdfCompress/Ai) → sealed record, 전 프로퍼티 get;init;. 8개 Provider의 READ는 무영향.
- 변이 5곳을 with 식/객체 초기화로 교정(WRITE만 깨지므로 국소적):
  MainWindow.BuildOptions(한 식으로 불변 구성), App.RunQuickAsync(with), OcrProvider(PdfRender 초기화),
  특성화 테스트 2곳, ImageOptimProviderTests.
- 카테고리 분해(RoutingOptions/OutputOptions)는 reader 파급이 커서 후속으로 보류(비침습 우선).

70개 테스트 전부 그린(골든마스터 동일성 = 동작 불변), 빌드 0경고/0오류.
2026-06-02 09:40:07 +09:00
Yun Chan
dad1c0bca3 refactor(P3): combine을 IMultiInputConverter로 추출 — 엔진 ImageMagick 누수 봉합
ConversionEngine이 더 이상 ImageMagick에 의존하지 않는다(추상화 누수 확정 봉합).
결합(N→1)을 변환 그래프의 1입력 엣지로 표현 못 하던 한계를 1급 인터페이스로 분리(Branch by Abstraction).

- Core/Providers/IMultiInputConverter.cs 신규 — CanCombineTo + CombineAsync(N→1).
- MagickProvider가 IMultiInputConverter 구현 — 엔진의 CombineAsync/LoadImageForCombine/
  ApplyCombineEncoding 코드를 동작 그대로 이주(CombinableOutputs도 이관).
- ConversionEngine: using ImageMagick 제거(코드 참조 0건, grep 검증 — 주석만 잔존),
  CombineAsync는 _registry.All.OfType<IMultiInputConverter>()로 결합기를 찾아 위임하는 얇은 코드로 축소.
  출력 디렉터리 결정만 엔진이 수행, 파일명·인코딩·쓰기는 결합기가 담당.
- UI 무변경: 정적 CanCombine/CanCombineInput(순수 메타데이터, ImageMagick 무관)은 엔진에 그대로 유지.
- 신규 테스트: MagickProvider가 IMultiInputConverter로 발견되는 seam 잠금.

P1 combine 골든마스터(프레임수·NoCompression·치수·평균색 + PDF 헤더)가 추출 후에도 그린 = 동작 동일성 입증.
70개 테스트 전부 그린, 빌드 0경고/0오류.
2026-06-01 23:39:02 +09:00
Yun Chan
8419bf30ff refactor(P2): DI 컴포지션 루트 + Scrutor 어셈블리 스캔 자동등록
컴파일타임 하드코딩(Bootstrap 14개 new + 수동 생성자 와이어링)을 제거 — 새 Provider는 클래스만
추가하면 자동 등록되고 그래프가 변환 엣지를 자동 흡수(확장성 토대). Strangler Fig 파사드로 호출부 무변경.

- Core/ServiceCollectionExtensions.AddEverything2Everything: Scrutor FromAssembliesOf →
  AssignableTo<IConverterProvider> → AsSelfWithInterfaces(단일 싱글턴 공유) → Singleton.
  Heic(Magick)/Docx·Ocr·Hwpx(Pdf)/Llm(Settings) 생성자 의존 자동 해소. ProviderRegistry/Engine 등록.
- Bootstrap.CreateDefault: 내부를 DI(BuildServiceProvider→GetRequiredService)로 교체하되
  시그니처·동작 불변 — App/CLI/테스트 호출부 전부 무파손.
- 패키지: Microsoft.Extensions.DependencyInjection 9.0.0, Scrutor 4.2.2 (CPM 경유).
- DependencyInjectionTests: 14개 전수 해소 + 생성자 의존 자동 와이어링 + AsSelf 동일 인스턴스
  + 공유 Settings + 파사드 하위호환 검증(적대적 리뷰의 'AsSelf 누락 silent 실패' 방어).

App.xaml.cs Generic Host 전환·서비스로케이터 제거는 GUI 스모크가 필요해 P5(MVVM 생성자 주입)로 통합.
69개 테스트 전부 그린(65+4), 빌드 0경고/0오류, 골든마스터 녹색 유지(동작 불변).
2026-06-01 23:30:06 +09:00
Yun Chan
6b524662a4 refactor(P1): 엔진 실행경로 특성화(골든마스터) 안전망 구축
코드 변경 전 회귀 머지 게이트 확보 — 프로덕션 코드 0줄 변경.
적대적 리뷰 지적("엔진 e2e 테스트 0개", "메타-only는 alpha/압축 회귀에 눈멈") 반영.

- EngineCharacterizationTests: ConvertOneAsync/ConvertManyAsync/ExecuteChainAsync/CombineAsync
  실파일 e2e 10 시나리오 — png→jpg(1홉)/svg→jpg(멀티홉)/PDF→페이지별/중간 다중산출물 거부
  (line200-205)/이미지N장→TIFF·PDF 결합/멀티홉·단일홉 Skip 존중/동일포맷 Skip/png 재압축
- 단순 파일존재 체크 금지 → 구조메타(치수·HasAlpha·포맷·프레임수·압축) + 내용지문(평균색 우세) 박제
- RoutingMatrixCharacterizationTests: FindBestPath/ReachableOutputs 불변 라우팅(13 경로 홉수 +
  self-edge + AvoidLossy 래스터 회피 + UI 추천출력 세트) 박제
- docs/ssot/REFACTOR_SMOKE_CHECKLIST.md: 진입점 수동 스모크(DI·MVVM 전환 직후 강제)

발견된 잠재 부채: combine→TIFF가 Tiff.Compression "lzw" define에도 NoCompression으로 저장(미적용).
동작 보존 원칙에 따라 현재 동작을 박제하고 P3 combine 추출 시 정책 통일로 다룸.

38 → 65개 테스트 전부 그린, 빌드 0경고/0오류.
2026-06-01 23:20:53 +09:00
Yun Chan
f36ee299f4 refactor(P0): CPM 도입 + Magick.NET 14.13.1 통일
- src/Directory.Packages.props 신규 — 모든 PackageReference 버전의 단일 출처(SSOT)
- ManagePackageVersionsCentrally=true, 3개 csproj에서 Version 속성 제거
- Magick.NET-Q16-AnyCPU Core 14.13.0 → 14.13.1로 통일(Tests와 정합)
  테스트와 프로덕션이 동일 네이티브 바이너리로 동작 → 골든마스터 신뢰 기반 확보

빌드 0경고/0오류, 38개 테스트 전부 그린.
2026-06-01 23:02:16 +09:00
Yun Chan
47600d2eb5 fix: mp4 변환 멈춤 해결 — 8K HEVC 코덱 + 동일포맷 재인코딩 + 종료상태
증상: 8K(7680x4320) HEVC 11분 6.76GB mp4 변환이 멈춤.

근본 원인 2가지:
1. mp4→mp4(같은 형식)이 그래프 self-edge 부재로 Skip 처리되고,
   QuickProgressWindow가 '전부 건너뜀' 종료 상태를 처리 안 해 멈춘 듯 보임.
2. 강제하던 h264_nvenc가 4096px 초과(8K)를 인코딩 못 해 즉시 실패 →
   CPU libx264 8K 폴백이 사실상 무한정 소요(진짜 멈춤).

수정:
- FfmpegProvider: 해상도별 코덱 — videoWidth>4096(8K 등)은 hevc_nvenc(GPU)
  →libx265(CPU)로, 이하는 h264_nvenc→libx264. 코덱 강제는 mp4/mkv/mov에만
  적용하고 webm/avi/gif는 컨테이너 기본 코덱(vp9 등) 유지.
- FfmpegProvider: 비디오/오디오 동일포맷 self-edge 추가(mp4→mp4 등 재압축).
  gif는 제외 — 이미지로도 다뤄져 ffmpeg 미설치 시 Skip→하드실패 회귀 방지.
- QuickProgressWindow.Finish: 성공·실패 없이 전부 건너뜀인 종료 상태를 명확히 안내.

검증: 실제 8K HEVC 클립으로 mp4→mp4 변환 → hevc_nvenc로 정상 인코딩,
28.3MB→2.1MB(93% 압축). 전체 38개 단위테스트 통과(적대적 리뷰 2건 반영).
Package 1.0.3.0.
2026-06-01 22:25:12 +09:00
Yun Chan
2a3d220db4 feat(ui): AI 설정창 + Codex CLI OAuth 백엔드 + ⚙ 진입점
사용자 원래 요구였던 Codex non-interactive OAuth를 실제 동작까지 검증해 통합. ChatGPT 구독으로 API 키 없이 AI 변환.

- CodexChatClient: codex exec --skip-git-repo-check --ephemeral -o <file> - (stdin 프롬프트). 실제 한글→영어 번역 검증 완료
- ExternalProcessRunner: stdin 지원 + UTF-8 인코딩(한글 깨짐 해결)
- ExternalToolDetector.IsCodexAvailable() + public 승격
- LlmProvider: codex 백엔드 + auto 폴백(키 없으면 Codex)
- SettingsWindow: OpenAI/Anthropic 키(2단계 Verify) + Codex(OAuth) + 외부도구 상태/다운로드 CTA + 모델 선택
- App.Settings를 LlmProvider와 공유(키 저장 즉시 반영)
- MainWindow 네비바 ⚙ 설정 진입점
- 디자인: Cursor/Zed/Raycast 설정 UX + Radix 상태 토큰 영감
2026-06-01 14:47:07 +09:00
Yun Chan
47a4a961e2 feat: 멀티홉 변환 UI 자동 노출 (OutputsForInput → 그래프 reachability)
ProviderRegistry.OutputsForInput을 그래프 transitive closure로 전환. UI 코드 변경 0으로 멀티홉 변환(json→xlsx, svg→jpg 등)이 사이드바·카스케이드 메뉴에 자동 노출된다. 동일 포맷 self-edge는 제외.

- DirectOutputsForInput(1홉)도 별도 보존
- 카스케이드 메뉴는 PopularOutputs 교집합이라 폭발 없이 풍부해짐
- 테스트 1개 추가 (멀티홉 노출 검증) — 총 38개 통과
- SSOT P6 in_progress
2026-06-01 13:44:46 +09:00
Yun Chan
7235fc87f5 feat: 영상·오디오 Provider (FfmpegProvider) — FFmpeg 설치 시 활성화
FFMpegCore로 영상/오디오 변환·트랜스코딩·오디오 추출. ffmpeg/ffprobe가 없으면 NotReady로 비활성, PATH 또는 앱 폴더에 두면 자동 활성화(LGPL 분리 호출, 본체 미번들).

- 영상 mp4/mkv/webm/mov/avi/gif, 오디오 mp3/aac/m4a/opus/ogg/flac/wav N×M + 영상→오디오 추출
- ExternalToolDetector.TryFindFfmpeg (앱 폴더 + PATH 탐지)
- 진행률 best-effort (ffprobe duration 기반), 취소 전파
- 테스트 3개 + SSOT status 갱신 — 총 37개 통과
2026-06-01 13:24:50 +09:00
Yun Chan
b76d51f3c1 feat: AI 텍스트 Provider (LlmProvider) — OpenAI/Anthropic 요약·번역·교정
AI를 '키 없으면 비활성되는 부가 엣지'로 통합. 기존 변환에 무영향, 일반 변환과 충돌 없는 txt↔txt/md↔md self-edge만 노출.

- IChatClient + OpenAiChatClient/AnthropicChatClient (HttpClient 직접, SDK 의존 0)
- LlmProvider: summarize/translate/proofread/custom, ISettingsStore 키 + 환경변수(OPENAI_API_KEY/ANTHROPIC_API_KEY) 폴백
- CheckAvailabilityAsync 게이트: 키 없으면 NotReady → 그래프에서 실행 비활성
- ConvertOptions.Ai (백엔드/모델/작업/대상언어)
- 테스트 5개 (키 게이트·프롬프트 구성·그래프 엣지) — 총 34개 통과
2026-06-01 13:21:14 +09:00
Yun Chan
937d9e7878 feat: 외부도구 활성화 인프라 — ISettingsStore(DPAPI) + ExternalProcessRunner
AI 키·외부 도구 경로를 안전 저장하고 외부 CLI를 일원화 실행하는 토대. 둘 다 순수 .NET, 실동작 테스트 검증.

- DpapiSettingsStore: DPAPI(CurrentUser) 암호화로 %LOCALAPPDATA%에 저장, 평문 노출 없음 검증
- ExternalProcessRunner: stdout/stderr 수집 + 타임아웃 + 프로세스 트리 Kill (LibreOffice/FFmpeg/Ghostscript 공용)
- 테스트 6개 추가 (암호화 round-trip, echo/비정상종료/타임아웃) — 총 29개 통과
2026-06-01 13:16:56 +09:00
Yun Chan
d49ee6365f feat: 순수 .NET Provider 3종 (Data/Vector/ImageOptim) + Loss 분류 정확화
검증 가능한 순수 .NET 변환기를 우선 추가. 외부 도구 불필요, 실제 동작까지 테스트로 검증.

- DataProvider: csv<->json, csv<->xlsx 원자 엣지 (json<->xlsx는 그래프가 csv 경유 자동 합성). CsvHelper/ClosedXML
- VectorProvider: svg->png/pdf (svg->기타이미지는 png 경유 합성). Svg.Skia
- ImageOptimProvider: 동일포맷 재압축 self-edge (jpg/png/webp/tiff/bmp)
- Loss 분류 정확화: OCR·문서/HTML->이미지=Rasterize, ->pdf=Recode (멀티홉 이상경로 svg->pdf->docx->jpg 차단)
- 그래프 합성/실동작 테스트 9개 추가 (총 23개 통과)
- DocumentFormat.OpenXml 3.1.0->최신 (ClosedXML 호환)
2026-06-01 13:09:27 +09:00
Yun Chan
e3ef59e009 feat(P1): 변환 그래프 엔진 + 멀티홉 경로탐색 + PDF 압축
ProviderRegistry를 ConversionGraph로 승격하고 ConversionEngine이 자체 Dijkstra로 멀티홉 경로를 자동 합성하도록 전환. 손실을 LossClass 가중치로 SSOT화.

- ConversionGraph: (node,hop) 라벨링 Dijkstra로 홉 제약 최단경로 (일반 Dijkstra의 hops 오염 false negative 회피)
- ConversionEngine.ExecuteChainAsync: 멀티홉 체이닝, 중간 다중출력 거부, 마지막 홉 Status 존중
- PdfToolProvider: pdf->pdf 구조 압축 (PDFsharp, Save 원자화)
- PairsFromMatrix 자기쌍 제외 (png->png 재인코딩 회귀 방지), PDF->이미지 Rasterize 표시
- xUnit 테스트 프로젝트 신설 + 그래프/회귀 14개 통과
- 적대적 병렬 리뷰(27 agents)로 확정된 13건 중 정확성 핵심 수정

SSOT: P1 done
2026-06-01 12:48:58 +09:00