Initial import

This commit is contained in:
how2ice
2026-04-21 03:33:23 +09:00
commit 9e4b70f1f1
495 changed files with 94680 additions and 0 deletions

126
docs/worklogs/2026-03-30.md Executable file
View File

@@ -0,0 +1,126 @@
# 2026-03-30 작업일지
## 오늘 작업
- Ant Design 기반 프론트엔드 프로젝트 초기 구조 생성
- `docs/worklogs`, `docs/features` 문서 폴더 구성
- 기본 대시보드 스타일의 시작 화면 작성
- React 19.2.4, Vite 8.0.3 최신 안정 버전으로 업데이트
- 컴포넌트 샘플 구조를 `plugins/`, `samples/`, `types/`, `xxxUI.tsx` 형태로 정리
- 공통 플러그인 제네릭과 `plugins<T>(props, pluginList)` 합성 유틸 추가
- `status-badge` 컴포넌트 샘플과 컴포넌트 문서 작성
- `InputUI.tsx` 단일 컴포넌트 구조와 validation plugin 기반 입력 샘플 추가
- 컴포넌트 샘플과 위젯 샘플을 별도 registry로 분리
- `WidgetShell`, `ApiSampleCardWidget`, 위젯 샘플 레이아웃 추가
- 컴포넌트 샘플 레이아웃을 좌측 내비게이션 + 우측 상세 카드 구조로 개편
- 입력 컴포넌트 구조를 `primitives / specialized / composite` 계층으로 재정리
- Git 저장소 초기화, `main` 브랜치 생성, 원격 `origin` 연결, 초기 커밋 생성
## 이슈 및 메모
- 초기 스택은 `React + Vite + TypeScript + Ant Design`으로 결정
- `antd` 포함 번들 특성상 빌드 시 청크 크기 경고가 발생함
- 원격 저장소 `push`는 HTTPS 인증 정보 부재로 실패
- 샘플 목록은 `samples/Sample.tsx` 기본형과 `samples/*.tsx` 확장형을 구분해 관리하기로 함
## 결정 사항
- 작업일지는 날짜별 Markdown 파일로 관리
- 기능 문서는 기능 단위 Markdown 파일로 관리
- 컴포넌트 문서는 `docs/components` 아래에서 관리
- 컴포넌트 샘플은 `samples/Sample.tsx`를 대표 샘플로 사용
- 입력 컴포넌트는 `InputUI.tsx` 하나만 두고 기능은 plugin으로 확장
- 입력 패키지는 `primitives`, `specialized`, `composite` 역할 기준으로 분리
- 플러그인은 기본적으로 공통 제네릭 타입을 사용하고, 필요 시 커링/팩토리 형태로 확장
- 여러 props 후처리 플러그인은 `plugins<T>(props, pluginList)` 형태로 합성
- 샘플 목록은 같은 컴포넌트 기준으로 묶고 `base -> plugin -> feature` 순서로 정렬
## 상세 작업 내역
- 프로젝트 부트스트랩 이후 문서 폴더와 샘플 레지스트리를 함께 정리해 이후 확장 기준을 먼저 마련
- `status-badge`, `InputUI`를 기준 컴포넌트로 삼아 plugin 합성 구조와 샘플 표시 구조를 동시에 검증
- 컴포넌트/위젯 샘플을 분리해 API 성격의 문서 화면으로 확장할 수 있는 기본 형태를 준비
- 원격 저장소 연결까지 완료했지만 인증 이슈로 `push`는 보류 상태로 남음
## 스크린샷
![input](../assets/worklogs/2026-03-30/input.png)
![status-badge](../assets/worklogs/2026-03-30/status-badge.png)
## 소스
- `src/components/status-badge/StatusBadgeUI.tsx`, `src/components/status-badge/plugins/status-badge.plugin.ts`: 상태 배지 기본 UI와 플러그인 합성 구조를 잡아 샘플 갤러리의 기준 컴포넌트로 삼았습니다.
- `src/components/inputs/input/InputUI.tsx`: validation plugin 기반 입력 컴포넌트 초안을 잡아 이후 `primitives / specialized / composite` 확장의 출발점으로 사용했습니다.
- `src/widgets/core/WidgetShell.tsx`: 위젯 본문을 단순 문단이 아니라 자유 레이아웃 컨테이너로 바꿔 카드형 샘플과 대시보드 위젯을 수용하게 했습니다.
- `docs/templates/worklog-template.md`: 작업일지 템플릿에 상세 내역, 스크린샷, 소스, 실행 커맨드, 변경 파일 섹션을 추가해 증적 기록 형식을 고정했습니다.
```diff
diff --git a/src/widgets/core/WidgetShell.tsx b/src/widgets/core/WidgetShell.tsx
-const { Paragraph, Title } = Typography;
+const { Title } = Typography;
...
- <Paragraph className="widget-shell__content">{children}</Paragraph>
+ <div className="widget-shell__content">{children}</div>
diff --git a/docs/templates/worklog-template.md b/docs/templates/worklog-template.md
+## 상세 작업 내역
+## 스크린샷
+## 소스
+
## 변경 파일 (전체, 중복 제거, KST 기준)
- A .gitignore
- A README.md
- A docs/README.md
- A docs/components/status-badge.md
- A docs/features/project-setup.md
- A docs/templates/feature-template.md
- A docs/templates/worklog-template.md
- A docs/worklogs/2026-03-30.md
- A index.html
- A package-lock.json
- A package.json
- A src/App.tsx
- A src/components/status-badge/StatusBadgeUI.tsx
- A src/components/status-badge/index.ts
- A src/components/status-badge/plugins/index.ts
- A src/components/status-badge/plugins/status-badge.plugin.ts
- A src/components/status-badge/samples/Sample.tsx
- A src/components/status-badge/types/index.ts
- A src/components/status-badge/types/status-badge.ts
- A src/main.tsx
- A src/styles.css
- A src/types/component-plugin.ts
- A tsconfig.app.json
- A tsconfig.json
- A tsconfig.node.json
- A vite.config.ts
## 실행 커맨드
+## 변경 파일
```
## 실행 커맨드
```bash
npm install
npm run build
git init
git checkout -b main
git add .
git commit -m "feat: initialize antd app and component plugin structure"
```
## 변경 파일
- `README.md`
- `docs/worklogs/2026-03-30.md`
- `src/App.tsx`
- `src/components/status-badge/StatusBadgeUI.tsx`
- `src/components/status-badge/plugins/status-badge.plugin.ts`
- `src/components/inputs/input/InputUI.tsx`
- `src/widgets/core/WidgetShell.tsx`
- `src/styles.css`
- `package.json`
- `vite.config.ts`

173
docs/worklogs/2026-03-31.md Executable file
View File

@@ -0,0 +1,173 @@
# 2026-03-31 작업일지
## 오늘 작업
- 입력 컴포넌트 구조를 `primitives / specialized / composite` 계층으로 재정리
- 기본 입력을 `src/components/inputs/primitives/input` 아래로 이동
- 이메일 입력을 `src/components/inputs/specialized/emailInput` 구조로 정리
- 3분할 입력을 `src/components/inputs/composite/multiInput` 구조로 정리
- 샘플 및 문서 경로를 새 입력 계층 구조에 맞게 갱신
- 프로젝트 종속 레이아웃을 `src/features/layout` 아래로 이동
- `basePath` 기반 공통 markdown preview 컴포넌트와 리스트 구성 추가
- `docs/**/*.md`까지 읽을 수 있도록 markdown registry 범위 확장
- `docs` 문서를 좌측 폴더/문서 트리 + 우측 markdown 카드 목록 구조로 구성
- 좌측 문서 바로가기를 공통 `FolderTreeNav` 기반 접기/펼치기 트리로 정리
- 구조 변경 내용을 커밋하고 원격 `main` 브랜치로 푸시
## 이슈 및 메모
- 입력 컴포넌트 이름과 패키지명 기준이 중간에 몇 차례 조정됨
- 샘플 레이아웃과 문서 경로는 구조 변경 시 함께 관리해야 함
- `src` 기준 glob만 사용하면 프로젝트 루트 `docs/`는 읽히지 않음
- 빌드는 정상 통과했지만 `antd` 포함 번들로 인해 청크 크기 경고는 계속 발생
## 결정 사항
- 입력 컴포넌트는 역할 기준으로 `primitives`, `specialized`, `composite`로 분리
- `InputUI`는 primitive로 관리
- `EmailInputUI`는 specialized 입력으로 관리
- `MultiInputUI`는 여러 입력을 조합한 composite 입력으로 관리
- 프로젝트 종속적인 레이아웃은 `src/features/layout` 아래에서 관리
- markdown preview는 공통 컴포넌트가 `basePath`를 받아 재사용 가능하게 구성
- 작업일지는 날짜별 파일로 계속 누적 기록
## 상세 작업 내역
- 입력 계층 재정리와 동시에 샘플/문서 경로도 같이 옮겨 구조 변경 시 누락 포인트를 줄임
- `docs` 트리와 markdown preview를 공통화하면서 프로젝트 루트 문서도 읽을 수 있게 범위를 확장
- 좌측 폴더 트리와 우측 카드 목록 구성을 도입해 문서 탐색 화면의 기본 틀을 완성
- 구조 개편 후 빌드와 원격 푸시까지 마쳐 문서 기반 화면을 기준선으로 고정
## 스크린샷
![email-input](../assets/worklogs/2026-03-31/email-input.png)
![multi-input](../assets/worklogs/2026-03-31/multi-input.png)
## 소스
- `src/components/inputs/primitives/input/InputUI.tsx`: 기본 입력을 primitive 계층으로 고정하고 commit plugin 검증 흐름을 공통 엔트리로 정리했습니다.
- `src/components/inputs/specialized/emailInput/EmailInputUI.tsx`: 이메일 전용 입력을 primitive 위에 얹어 specialized 계층 규칙을 검증했습니다.
- `src/components/inputs/composite/multiInput/MultiInputUI.tsx`: 전화번호형 3분할 입력을 추가해 composite 계층과 세그먼트 이동 UX를 구현했습니다.
- `src/components/markdownPreview/MarkdownPreviewCard.tsx`, `src/features/layout/docs-markdown-preview/DocsMarkdownPreviewLayout.tsx`: 루트 `docs/` 문서를 카드와 폴더 트리로 탐색하는 Docs 미리보기 레이아웃을 추가했습니다.
```diff
diff --git a/src/components/inputs/composite/multiInput/MultiInputUI.tsx b/src/components/inputs/composite/multiInput/MultiInputUI.tsx
+function splitValue(value?: string): MultiInputParts {
+ const digits = (value ?? '').replace(/\D/g, '').slice(0, 11);
+ return [digits.slice(0, 3), digits.slice(3, 7), digits.slice(7, 11)];
+}
...
+ if (event.target.value.length === 3) {
+ secondRef.current?.focus();
+ }
diff --git a/src/components/inputs/specialized/emailInput/EmailInputUI.tsx b/src/components/inputs/specialized/emailInput/EmailInputUI.tsx
+ inputMode={inputMode}
+ placeholder={placeholder}
+ autoComplete={autoComplete}
+ commitPlugins={[createEmailValidatorPlugin(), ...commitPlugins]}
```
## 변경 파일 (전체, 중복 제거, KST 기준)
- M README.md
- M docs/README.md
- M docs/features/project-setup.md
- A docs/worklogs/2026-03-31.md
- M src/App.tsx
- A src/components/markdownPreview/MarkdownPreviewCard.tsx
- A src/components/markdownPreview/MarkdownPreviewContent.tsx
- A src/components/markdownPreview/MarkdownPreviewList.tsx
- A src/components/markdownPreview/index.ts
- A src/components/markdownPreview/markdown-document.ts
- A src/components/markdownPreview/registry.ts
- A src/components/navigation/folder-tree-nav.tsx
- A src/components/navigation/index.ts
- A src/features/layout/README.md
- R src/layouts/component-sample-gallery/ComponentSamplesLayout.tsx -> src/features/layout/component-sample-gallery/ComponentSamplesLayout.tsx
- R src/layouts/component-sample-gallery/index.ts -> src/features/layout/component-sample-gallery/index.ts
- A src/features/layout/docs-markdown-preview/DocsMarkdownPreviewLayout.tsx
- A src/features/layout/docs-markdown-preview/index.ts
- A src/features/layout/feature-markdown-preview/FeatureMarkdownPreviewListLayout.tsx
- A src/features/layout/feature-markdown-preview/index.ts
- R src/layouts/widget-registry-gallery/WidgetRegistryLayout.tsx -> src/features/layout/widget-registry-gallery/WidgetRegistryLayout.tsx
- R src/layouts/widget-registry-gallery/index.ts -> src/features/layout/widget-registry-gallery/index.ts
- R src/layouts/widget-sample-gallery/SampleWidgetsLayout.tsx -> src/features/layout/widget-sample-gallery/SampleWidgetsLayout.tsx
- R src/layouts/widget-sample-gallery/index.ts -> src/features/layout/widget-sample-gallery/index.ts
- A src/features/markdownPreview/FeatureMarkdownPreviewCard.tsx
- A src/features/markdownPreview/index.ts
- A src/features/overview.md
- M src/styles.css
- M docs/components/input.md
- M docs/worklogs/2026-03-30.md
- A src/components/inputs/composite/multiInput/MultiInputUI.tsx
- A src/components/inputs/composite/multiInput/index.ts
- A src/components/inputs/composite/multiInput/plugins/index.ts
- A src/components/inputs/composite/multiInput/plugins/multi-input.plugin.ts
- A src/components/inputs/composite/multiInput/samples/Sample.tsx
- A src/components/inputs/composite/multiInput/types/index.ts
- A src/components/inputs/composite/multiInput/types/multi-input.ts
- R src/components/inputs/input/InputUI.tsx -> src/components/inputs/primitives/input/InputUI.tsx
- R src/components/inputs/input/index.ts -> src/components/inputs/primitives/input/index.ts
- R src/components/inputs/input/plugins/index.ts -> src/components/inputs/primitives/input/plugins/index.ts
- R src/components/inputs/input/plugins/input.plugin.ts -> src/components/inputs/primitives/input/plugins/input.plugin.ts
- R src/components/inputs/input/samples/Sample.tsx -> src/components/inputs/primitives/input/samples/Sample.tsx
- R src/components/inputs/input/samples/ValidInputSample.tsx -> src/components/inputs/primitives/input/samples/ValidInputSample.tsx
- R src/components/inputs/input/types/index.ts -> src/components/inputs/primitives/input/types/index.ts
- R src/components/inputs/input/types/input.ts -> src/components/inputs/primitives/input/types/input.ts
- A src/components/inputs/specialized/emailInput/EmailInputUI.tsx
- A src/components/inputs/specialized/emailInput/index.ts
- A src/components/inputs/specialized/emailInput/plugins/email-input.plugin.ts
- A src/components/inputs/specialized/emailInput/plugins/index.ts
- A src/components/inputs/specialized/emailInput/samples/Sample.tsx
- A src/components/inputs/specialized/emailInput/types/email-input.ts
- A src/components/inputs/specialized/emailInput/types/index.ts
- A docs/components/input.md
- A src/components/inputs/input/InputUI.tsx
- A src/components/inputs/input/index.ts
- A src/components/inputs/input/plugins/index.ts
- A src/components/inputs/input/plugins/input.plugin.ts
- A src/components/inputs/input/samples/Sample.tsx
- A src/components/inputs/input/samples/ValidInputSample.tsx
- A src/components/inputs/input/types/index.ts
- A src/components/inputs/input/types/input.ts
- M src/components/status-badge/samples/Sample.tsx
- A src/layouts/component-sample-gallery/ComponentSamplesLayout.tsx
- A src/layouts/component-sample-gallery/index.ts
- A src/layouts/widget-registry-gallery/WidgetRegistryLayout.tsx
- A src/layouts/widget-registry-gallery/index.ts
- A src/layouts/widget-sample-gallery/SampleWidgetsLayout.tsx
- A src/layouts/widget-sample-gallery/index.ts
- A src/samples/registry.ts
- A src/vite-env.d.ts
- A src/widgets/api-sample-card/ApiSampleCardWidget.tsx
- A src/widgets/api-sample-card/index.ts
- A src/widgets/api-sample-card/samples/Sample.tsx
- A src/widgets/core/WidgetShell.tsx
- A src/widgets/core/index.ts
- A src/widgets/core/registry/widget-features.ts
- A src/widgets/core/types/widget.ts
- A src/widgets/registry.ts
## 실행 커맨드
```bash
npm run build
git add .
git commit -m "입력 컴포넌트 구조 정리"
git commit -m "문서 미리보기와 레이아웃 정리"
git push origin main
```
## 변경 파일
- `docs/worklogs/2026-03-31.md`
- `src/components/inputs/primitives/input/InputUI.tsx`
- `src/components/inputs/specialized/emailInput/EmailInputUI.tsx`
- `src/components/inputs/composite/multiInput/MultiInputUI.tsx`
- `src/components/markdownPreview/MarkdownPreviewCard.tsx`
- `src/components/markdownPreview/MarkdownPreviewContent.tsx`
- `src/components/markdownPreview/MarkdownPreviewList.tsx`
- `src/components/navigation/folder-tree-nav.tsx`
- `src/features/layout/component-sample-gallery/ComponentSamplesLayout.tsx`
- `src/styles.css`

254
docs/worklogs/2026-04-01.md Executable file
View File

@@ -0,0 +1,254 @@
# 2026-04-01 작업일지
## 오늘 작업
- 대시보드 카드 위젯과 샘플 구성을 보강
- WMS/TMS 대시보드 콘텐츠 배치와 차트 표현을 조정
- 대시보드 공통 표현 컴포넌트 `progress`, `multiProgress` 구조를 정리
- `src/data` 아래로 대시보드 프리셋 데이터를 분리
- 메인 화면을 좌측 메뉴 기반 API 스타일 구조로 정리
- `popup` 입력 컴포넌트를 추가하고 UI 스타일을 보강
- `select` 입력 컴포넌트를 추가
- `checkCombo` 입력 컴포넌트를 추가
- `package.json`에 Nexus `publishConfig` 추가
- 버전을 `0.1.0-alpha.0`로 조정하고 publish용 `private` 설정을 해제
- 메인 화면을 `src/views/main` 아래로 임시 분리
- Nexus 인증 방식을 `username / _password(base64)` 형식으로 재구성
- Nexus에 `ai-code-app@0.1.0-alpha.0` 배포 성공
- 메인 화면 구조를 `src/app/main` 기준으로 다시 정리하고 export 경로를 보강
- 콘텐츠 영역 바깥 여백을 줄여 카드 중심 레이아웃으로 정리
- 버튼 클릭 시에만 편집 가능한 `buttonEditableInput` 컴포넌트를 추가
- `buttonEditableInput`의 readonly 음영과 확인 버튼 편집 완료 동작을 보정
## 이슈 및 메모
- `npm publish`는 alpha 버전 배포 시 `--tag alpha`가 필요
- Nexus는 토큰 방식보다 `username / _password(base64)` 방식에서 인증이 정상 동작함
- popup 입력은 스타일 보강 과정에서 입력/버튼/readonly 영역의 경계와 톤을 반복 조정
- 대시보드 카드 높이와 콘텐츠 간격 조정은 과도한 고정값을 피하고 점진적으로 조정하는 편이 안정적
- 버튼 기반 편집 입력은 blur, Enter, 버튼 클릭 확정 타이밍을 함께 맞춰야 UX가 안정적
## 결정 사항
- 재사용 가능한 샘플/프리셋 데이터는 `src/data`에서 관리
- 대시보드 공통 표현은 `src/components/dashboard`, 카드 조합은 `src/widgets/dashboard-report-card`로 분리
- 신규 입력 컴포넌트도 `UI / plugins / samples / types` 구조를 동일하게 적용
- 메인 화면은 임시로 `views/main` 아래에 `header / sidebar / content` 단위로 분리
- 배포는 `npm publish --tag alpha` 기준으로 진행
- 메인 화면 엔트리는 `src/app/main` 기준으로 정리하고 패키지 루트 export를 함께 관리
- 버튼 편집 입력은 기본 readonly 상태를 유지하고 검증 실패 시 이전 값으로 복원
## 상세 작업 내역
- 대시보드 위젯, 입력 컴포넌트, 메인 화면 구조 변경이 동시에 진행되며 앱 성격이 샘플 갤러리 중심으로 이동
- Nexus 배포 설정과 인증 방식을 실사용 기준으로 정리해 alpha 배포 경로를 실제로 검증
- `popup`, `select`, `checkCombo`, `buttonEditableInput`을 추가하며 입력 UI 패키지 확장 패턴을 구체화
- 메인 화면을 `src/app/main`으로 재정리하면서 이후 헤더/사이드바/콘텐츠 분리 작업의 기반을 마련
## 스크린샷
![button-editable-input](../assets/worklogs/2026-04-01/button-editable-input.png)
![check-combo-input](../assets/worklogs/2026-04-01/check-combo-input.png)
![dashboard-multi-progress](../assets/worklogs/2026-04-01/dashboard-multi-progress.png)
![dashboard-progress](../assets/worklogs/2026-04-01/dashboard-progress.png)
![main-content-fullscreen-toggle](../assets/worklogs/2026-04-01/main-content-fullscreen-toggle.png)
![popup-input](../assets/worklogs/2026-04-01/popup-input.png)
![select-input](../assets/worklogs/2026-04-01/select-input.png)
## 소스
- `src/components/dashboard/progress/ProgressUI.tsx`, `src/components/dashboard/multiProgress/MultiProgressUI.tsx`: 대시보드 진행률 표현을 컴포넌트화해 카드/샘플/위젯에서 공통으로 쓰게 정리했습니다.
- `src/components/inputs/popup/PopupUI.tsx`, `src/components/inputs/select/SelectUI.tsx`, `src/components/inputs/checkCombo/CheckComboUI.tsx`: 신규 입력군을 같은 패키지 규칙으로 추가해 선택형 입력 범위를 넓혔습니다.
- `src/components/inputs/specialized/buttonEditableInput/ButtonEditableInputUI.tsx`: readonly 상태와 편집 확정 버튼을 가진 클릭 편집 입력을 도입했습니다.
- `src/app/main/MainContent.tsx`: 메인 콘텐츠를 `Docs / APIs` 기준 카드 레이아웃으로 나눠 현재 앱 성격에 맞는 메인 화면으로 재정리했습니다.
```diff
diff --git a/src/components/inputs/specialized/buttonEditableInput/ButtonEditableInputUI.tsx b/src/components/inputs/specialized/buttonEditableInput/ButtonEditableInputUI.tsx
+ const [isEditing, setIsEditing] = useState(false);
...
+ readOnly={!isEditing}
+ commitPlugins={mergedCommitPlugins}
...
+ {isEditing ? confirmButtonLabel : editButtonLabel}
diff --git a/src/app/main/MainContent.tsx b/src/app/main/MainContent.tsx
+ {activeTopMenu === 'docs' ? (
+ <div className="app-main-panel">
+ <Card title={`Docs / ${selectedDocsMenu}`} ...>
+ {selectedDocs.map((document) => (
+ <MarkdownPreviewCard document={document} />
+ ))}
```
## 변경 파일 (전체, 중복 제거, KST 기준)
- A app-dist/assets/2026-03-30-C4SD1FRx.js
- A app-dist/assets/2026-03-31-DwLJWvh2.js
- A app-dist/assets/2026-04-01-D5gI7Q4h.js
- A app-dist/assets/AntdIcon-Byo_R91X.js
- A app-dist/assets/CloseOutlined-B6nrJF3-.js
- A app-dist/assets/InputUI-DAmC5DJh.js
- A app-dist/assets/MultiProgressUI--uB5kqTr.js
- A app-dist/assets/ProgressUI-C91UL-oJ.js
- A app-dist/assets/README-CI9EVrw_.js
- A app-dist/assets/README-O9_O-4tf2.js
- A app-dist/assets/Sample-6Ml90fMj.js
- A app-dist/assets/Sample-BJxnglT1.js
- A app-dist/assets/Sample-BPCdH5hH.js
- A app-dist/assets/Sample-CLup9Uwo.js
- A app-dist/assets/Sample-CeT4nPqx.js
- A app-dist/assets/Sample-DKoCtyPX.js
- A app-dist/assets/Sample-DMEGMJwT.js
- A app-dist/assets/Sample-Dyso1eHr.js
- A app-dist/assets/Sample-E6V4D3Du.js
- A app-dist/assets/Sample-LB0lRdor.js
- A app-dist/assets/Sample-xgRr-oUd.js
- A app-dist/assets/SearchOutlined-Civ7xtmP.js
- A app-dist/assets/TmsDeliveryFlowSample-BHeS93-n.js
- A app-dist/assets/TmsDeliveryMetricsSample-BQV5az65.js
- A app-dist/assets/ValidInputSample-C9pl9si5.js
- A app-dist/assets/WidgetShell-DhXCYrC8.js
- A app-dist/assets/WmsInboundOutboundSample-BZCM3_0V.js
- A app-dist/assets/WmsInventoryTrendSample-DvxPBjgx.js
- A app-dist/assets/card-BpKFEf6A.js
- A app-dist/assets/check-combo-Bz7kGmN1.js
- A app-dist/assets/clsx-CzIxj0DI.js
- A app-dist/assets/component-plugin-BjxKibxS.js
- A app-dist/assets/dashboard-report-presets-Bh8duNGL.js
- A app-dist/assets/feature-template-D3D0o1kc.js
- A app-dist/assets/index-BQsYfbAI.js
- A app-dist/assets/index-CaXbpawn.css
- A app-dist/assets/input-B6oA1SZJ.js
- A app-dist/assets/input.plugin-ulF_zEvq.js
- A app-dist/assets/jsx-runtime-CNArSbpp.js
- A app-dist/assets/overview-DgYaz2rW.js
- A app-dist/assets/popup-BGFdvx2z.js
- A app-dist/assets/project-setup-jU8Nv-E8.js
- A app-dist/assets/select-DYfkmyn8.js
- A app-dist/assets/select-kIZVYgkF.js
- A app-dist/assets/status-badge-1fx0opaz.js
- A app-dist/assets/wave-DQjt-ubw.js
- A app-dist/assets/worklog-template-DE_f72dx.js
- A app-dist/index.html
- A docker-compose.yml
- M docs/worklogs/2026-04-01.md
- M package-lock.json
- M package.json
- M src/App.tsx
- A src/app/main/MainContent.tsx
- A src/app/main/MainHeader.tsx
- R src/views/main/MainSidebar.tsx -> src/app/main/MainSidebar.tsx
- R src/views/main/MainView.tsx -> src/app/main/MainView.tsx
- R src/views/main/index.ts -> src/app/main/index.ts
- R src/views/main/types.ts -> src/app/main/types.ts
- A src/app/manifests/docs.manifest.ts
- A src/app/manifests/samples.manifest.ts
- M src/components/inputs/primitives/input/index.ts
- A src/components/inputs/specialized/buttonEditableInput/ButtonEditableInputUI.css
- A src/components/inputs/specialized/buttonEditableInput/ButtonEditableInputUI.tsx
- A src/components/inputs/specialized/buttonEditableInput/index.ts
- A src/components/inputs/specialized/buttonEditableInput/samples/Sample.tsx
- M src/components/markdownPreview/MarkdownPreviewList.tsx
- M src/components/markdownPreview/index.ts
- M src/components/markdownPreview/registry.ts
- M src/components/status-badge/index.ts
- M src/features/layout/component-sample-gallery/ComponentSamplesLayout.tsx
- M src/features/layout/dashboard-report-gallery/DashboardReportGalleryLayout.tsx
- M src/features/layout/docs-markdown-preview/DocsMarkdownPreviewLayout.tsx
- M src/features/layout/feature-markdown-preview/FeatureMarkdownPreviewListLayout.tsx
- M src/features/layout/widget-sample-gallery/SampleWidgetsLayout.tsx
- A src/index.ts
- M src/samples/registry.ts
- M src/styles.css
- M src/views/main/MainContent.tsx
- D src/views/main/MainHeader.tsx
- A tsconfig.lib.json
- M README.md
- M docs/README.md
- M docs/features/project-setup.md
- A src/views/main/MainContent.tsx
- A src/views/main/MainHeader.tsx
- A src/views/main/MainSidebar.tsx
- A src/views/main/MainView.tsx
- A src/views/main/index.ts
- A src/views/main/types.ts
- A docs/components/check-combo.md
- A docs/components/popup.md
- A docs/components/select.md
- A docs/worklogs/2026-04-01.md
- A src/components/dashboard/multiProgress/MultiProgressUI.tsx
- A src/components/dashboard/multiProgress/index.ts
- A src/components/dashboard/multiProgress/plugins/index.ts
- A src/components/dashboard/multiProgress/plugins/multi-progress.plugin.ts
- A src/components/dashboard/multiProgress/samples/Sample.tsx
- A src/components/dashboard/multiProgress/types/index.ts
- A src/components/dashboard/multiProgress/types/multi-progress.ts
- A src/components/dashboard/progress/ProgressUI.tsx
- A src/components/dashboard/progress/index.ts
- A src/components/dashboard/progress/plugins/index.ts
- A src/components/dashboard/progress/plugins/progress.plugin.ts
- A src/components/dashboard/progress/samples/Sample.tsx
- A src/components/dashboard/progress/types/index.ts
- A src/components/dashboard/progress/types/progress.ts
- A src/components/inputs/checkCombo/CheckComboUI.tsx
- A src/components/inputs/checkCombo/index.ts
- A src/components/inputs/checkCombo/plugins/check-combo.plugin.ts
- A src/components/inputs/checkCombo/plugins/index.ts
- A src/components/inputs/checkCombo/samples/Sample.tsx
- A src/components/inputs/checkCombo/types/check-combo.ts
- A src/components/inputs/checkCombo/types/index.ts
- A src/components/inputs/popup/PopupUI.tsx
- A src/components/inputs/popup/index.ts
- A src/components/inputs/popup/plugins/index.ts
- A src/components/inputs/popup/plugins/popup.plugin.ts
- A src/components/inputs/popup/samples/Sample.tsx
- A src/components/inputs/popup/types/index.ts
- A src/components/inputs/popup/types/popup.ts
- A src/components/inputs/select/SelectUI.tsx
- A src/components/inputs/select/index.ts
- A src/components/inputs/select/plugins/index.ts
- A src/components/inputs/select/plugins/select.plugin.ts
- A src/components/inputs/select/samples/Sample.tsx
- A src/components/inputs/select/types/index.ts
- A src/components/inputs/select/types/select.ts
- A src/components/navigation/SectionMenuLayout.tsx
- M src/components/navigation/index.ts
- A src/data/dashboard-report-presets.ts
- A src/features/dashboard/TmsDashboardFeatureSamples.tsx
- A src/features/dashboard/WmsDashboardFeatureSamples.tsx
- A src/features/layout/dashboard-feature-gallery/DashboardFeatureGalleryLayout.tsx
- A src/features/layout/dashboard-feature-gallery/index.ts
- A src/features/layout/dashboard-report-gallery/DashboardReportGalleryLayout.tsx
- A src/features/layout/dashboard-report-gallery/index.ts
- M src/widgets/core/WidgetShell.tsx
- A src/widgets/dashboard-report-card/DashboardReportCardWidget.tsx
- A src/widgets/dashboard-report-card/index.ts
- A src/widgets/dashboard-report-card/samples/TmsDeliveryFlowSample.tsx
- A src/widgets/dashboard-report-card/samples/TmsDeliveryMetricsSample.tsx
- A src/widgets/dashboard-report-card/samples/WmsInboundOutboundSample.tsx
- A src/widgets/dashboard-report-card/samples/WmsInventoryTrendSample.tsx
- M src/widgets/registry.ts
## 실행 커맨드
```bash
npm run build
npm publish --tag alpha
npm run capture:component -- --date 2026-04-01 --name check-combo-input
npm run capture:component -- --date 2026-04-01 --name select-input
npm run capture:component -- --date 2026-04-01 --name popup-input
npm run capture:fullscreen -- --date 2026-04-01
```
## 변경 파일
- `docs/worklogs/2026-04-01.md`
- `package.json`
- `src/components/dashboard/progress/ProgressUI.tsx`
- `src/components/dashboard/multiProgress/MultiProgressUI.tsx`
- `src/components/inputs/popup/PopupUI.tsx`
- `src/components/inputs/select/SelectUI.tsx`
- `src/components/inputs/checkCombo/CheckComboUI.tsx`
- `src/components/inputs/specialized/buttonEditableInput/ButtonEditableInputUI.tsx`
- `src/app/main/MainContent.tsx`
- `src/styles.css`

396
docs/worklogs/2026-04-02.md Executable file
View File

@@ -0,0 +1,396 @@
# 2026-04-02 작업일지
## 오늘 작업
- 루트 `README.md`를 현재 앱 구조와 스크립트 기준으로 최신화
- `docs/README.md`의 문서 운영 기준을 실제 디렉터리 구조와 Docs 수집 방식에 맞게 정리
- 오늘 작업일지에 문서 최신화 내역을 추가
- `window-ui` 공통 컴포넌트를 추가
- 윈도우 헤더 드래그, 리사이즈, 최소화, 최대화, 복원 동작을 구성
- 모바일 터치 기반 드래그와 버튼 터치 영역을 보정
- 멀티 윈도우 추천 배치, 가로 분할, 세로 분할, 그리드 배치를 추가
- 배치 이후 창 순서를 사용자가 조정할 수 있는 UI를 추가
- 활성화된 윈도우가 항상 최상단으로 오도록 z-index 정렬을 추가
- 전체화면 버튼을 빈 공간 우선 확장 후 부모 전체 확장 방식으로 보정
- `previewer-ui` 공통 컴포넌트를 추가
- `text`, `json`, `code`, `image`, `markdown`, `empty` preview 타입을 구성
- JSON preview에 영역별 컬러 토큰 표시를 추가
- Code preview에 언어 선택과 VSCode 스타일 표현을 추가
- Previewer 샘플에 TypeScript, JSON, HTML, CSS, Bash, SQL, YAML 예제를 추가
- 메인 앱을 PWA 오프라인 캐시 전략으로 보강
- 모바일 헤더/사이드바 동작을 손봐서 메뉴 선택 흐름을 단순화
- 헤더 액션 영역과 전체화면 토글 동작을 재정리
- `src/layer/gesture`를 추가해 모바일 우측 상단 제스처를 레이어로 관리
- `src/store/appStore`를 추가해 현재 페이지와 포커스 컴포넌트 상태를 관리
- `src/layer/search`를 추가해 통합검색 모달과 검색 옵션을 레이어 컨텍스트로 관리
- `SearchCommandModal` 통합 검색 컴포넌트를 추가
- 문서, API, 컴포넌트, 위젯 키워드 자동 추천과 빠른 이동을 연결
- 모바일 입력 확대 방지와 검색 모달 포커스 동작을 보정
- 빌드 산출물 추적 정리를 위해 `.gitignore`를 보강하고 `app-dist`, `dev-dist` 무시 준비를 진행
- `etc/db/work-db` 경로로 PostgreSQL 도커 구성을 분리하고 `.env` 기반 설정을 정리
- `etc/servers/work-server``Fastify + PostgreSQL` 기반 작업 API 서버를 추가
- Plan 게시판용 등록, 수정, 삭제, 테이블 생성 API를 추가
- Plan 게시판이 웹서버 프록시(`/api`)를 통해 `work-server`를 바라보도록 수정
- Plan 항목 등록 시 작업 ID, 요청 메모, 대상 브랜치/릴리즈 흐름을 함께 관리하도록 화면과 서버 모델을 정리
- Plan 항목을 주기적으로 읽어 `등록 -> 작업중(브랜치 준비)``개발완료 -> 완료(release merge)` 흐름을 자동화하는 worker를 추가
- `scripts/run-plan-codex-once.mjs`로 단건 Plan 항목을 읽어 Codex 실행 결과를 조치 이력에 반영하는 자동화 스크립트를 추가
- Plan 자동화 실패 시 상태를 `이슈`로 바꾸지 않고 해시태그/오류 이력으로 관리하도록 변경
- `etc` 내부에서 커밋되면 안 되는 `.env`, `node_modules`, `dist`, 로그 파일을 루트 `.gitignore`에서도 명시적으로 제외
- `작업시작` 이후에는 원본 요청을 수정할 수 없도록 잠금 규칙을 추가
- 작업 이후 변경 사항은 일반 조치 이력과 이슈 조치 이력으로 누적 기록되도록 확장
- 컴포넌트/화면 스크린샷을 생성한 뒤 작업일지 `## 화면 캡처` 섹션에 자동 링크하는 스크립트를 정리
- Plan 게시판 메모 상세의 모바일 높이를 절반 수준으로 줄이도록 조정
- 작업일지 캡처 스크립트를 공통 유틸로 정리하고 누락 방지용 모바일 Plan 상세 캡처를 추가
## 이슈 및 메모
- `vite-plugin-pwa``vite@8`과 peer 경고가 있어 도커 실행 시 `npm install --force`가 필요
- 작업일지 이미지 표시를 위해 markdown preview가 전체 블록을 렌더링하도록 수정
- 컴포넌트 캡처 자동화는 `Playwright` 기반으로 스크린샷을 생성하고 작업일지 링크를 함께 추가
- 캡처 스크립트는 날짜별 폴더를 만들고 동일 날짜 작업일지에 이미지 Markdown 링크를 자동 삽입함
- iOS에서는 제스처 후 키보드 오픈 보장이 제한적이라 강제 포커스 로직보다 레이어 분리와 단순 동작 유지에 우선순위를 둠
- Plan 자동화는 현재 저장소 worktree가 깨끗해야 브랜치 생성이 가능함
- 현재 저장소에는 `release` 브랜치가 아직 없어 실제 자동 merge를 쓰려면 브랜치 생성이 먼저 필요함
- 컨테이너 내부 git은 `safe.directory` 이슈가 있어 자동 처리 로직을 추가함
- Plan 이력 테이블 추가 이후 기존 실패 데이터에는 과거 이력이 없을 수 있어 재시도로 새 이력을 만드는 방식으로 정리함
- `run-plan-codex-once`는 최근 조치 이력/이슈 이력을 프롬프트에 포함해 재작업이나 보완 요청을 우선 반영하도록 구성함
## 결정 사항
- 공통 미리보기 컴포넌트는 `src/components/previewer` 아래에서 관리
- 공통 윈도우 컴포넌트는 `src/components/window` 아래에서 관리
- 작업일지 캡처 이미지는 `docs/assets/worklogs/YYYY-MM-DD/`에 저장
- 컴포넌트 캡처는 전체 화면이 아니라 샘플 컴포넌트 영역 중심으로 생성
- 앱 상태 저장은 `src/store`
- 화면 전역 UI는 `src/layer`
- 통합검색은 store가 아니라 layer context로 관리
- 프로젝트 내부 부가 서버/DB는 `etc/servers/<name>`, `etc/db/<name>` 구조로 관리
- `etc` 내부의 비밀값/생성물은 각 하위 `.gitignore`와 루트 `.gitignore`에서 이중으로 차단
- Plan 게시판은 상태 수기 변경보다 자동화 중심 흐름을 우선하고, 사용자는 상태별 허용 액션 버튼만 사용
- 원본 요청은 `작업시작` 이후 읽기 전용으로 전환하고 추가 조치사항은 이력으로만 남김
- 스크린샷 자산은 날짜별 작업일지와 1:1로 연결하고 캡처 직후 문서 링크까지 자동 반영
## 상세 작업 내역
- 초기 문서에 남아 있던 구형 `src/components/inputs`, `src/views/main` 기준 설명을 현재 `src/app/main`, `src/components/*`, `src/features/planBoard` 구조에 맞춰 교체
- 루트 안내문에서 실행 스크립트, 앱 섹션, 문서 위치를 한 번에 확인할 수 있도록 정리
- Docs 가이드에 실제 Markdown 수집 범위와 문서 최신화 기록 원칙을 반영
- `window-ui`, `previewer-ui` 추가 이후 앱 전반 구조가 컴포넌트 문서/샘플 허브 형태로 더 선명해짐
- PWA 오프라인 캐시 전략과 모바일 레이아웃 보정을 함께 진행해 실제 사용 환경 대응 범위를 넓힘
- `gesture`, `search`, `store/appStore`를 나눠 전역 UI와 상태 책임을 분리하는 기준을 세움
- 통합검색은 문서, API, 컴포넌트, 위젯을 한 흐름에서 찾도록 묶고 모달/제스처/포커스 추적을 연결
- 빌드 산출물 추적을 정리하며 `.gitignore`와 Git 인덱스 운영 기준도 함께 정비
- `work-server`는 Plan 게시판 API, DB 연결, worker 자동화 역할을 함께 가지도록 확장
- Plan 게시판은 단순 상태 보드가 아니라 등록 정보, 조치 이력, 이슈 이력, 원본 요청 잠금 규칙까지 함께 관리하는 작업 큐 성격으로 확장
- 등록 단계에서는 작업 ID와 요청 내용을 남기고, 자동화 단계에서는 브랜치 확보, Codex 실행, 커밋/푸시, release 반영 가능 여부를 순차적으로 판단하도록 흐름을 정리
- 자동화 worker는 현재 저장소에 직접 브랜치를 만들고 `release` 브랜치 머지까지 시도하되, 작업 디렉터리가 dirty면 이슈 태그와 오류 이력을 남기고 안전하게 멈춤
- Plan 화면에서는 브랜치명, 자동화 상태, 최근 오류, 이슈 태그, 조치 이력을 함께 확인할 수 있게 보강
- `작업시작` 이후 서버는 원본 요청 수정 API를 `409`로 차단하고, 화면에서도 메모/작업 ID를 읽기 전용으로 전환
- 스크린샷 처리 스크립트는 `docs/assets/worklogs/YYYY-MM-DD/` 저장, `docs/worklogs/YYYY-MM-DD.md` 링크 반영, 중복 링크 방지까지 한 번에 수행하도록 맞춤
- 검색 모달, 프리뷰어, 윈도우 UI 캡처는 각각 샘플 셀렉터를 기준으로 잘라 저장해 작업일지 증적 자료로 재사용 가능하게 구성
- Plan 게시판은 모바일에서 `새 메모` 상세를 바로 캡처하는 스크립트를 추가해 메모 높이 보정 결과를 작업일지에 바로 남길 수 있게 함
## 스크린샷
![plan-board-mobile-memo-detail](../assets/worklogs/2026-04-02/plan-board-mobile-memo-detail.png)
![previewer-ui](../assets/worklogs/2026-04-02/previewer-ui.png)
![search-command](../assets/worklogs/2026-04-02/search-command.png)
![window-ui](../assets/worklogs/2026-04-02/window-ui.png)
## 소스
- `src/components/window/WindowUI.tsx`: 드래그, 리사이즈, 분할 배치를 지원하는 공통 윈도우 UI를 추가했습니다.
- `src/components/previewer/PreviewerUI.tsx`: `text/json/code/image/markdown` 등 여러 증적 형식을 한 컴포넌트에서 미리보게 만들었습니다.
- `src/components/search/SearchCommandModal.tsx`: 문서, API, 컴포넌트, 위젯을 한 번에 찾는 통합 검색 모달을 추가했습니다.
- `src/features/planBoard/PlanBoardPage.tsx`: Plan 게시판 UI와 상세 흐름을 본격 추가했습니다.
- `etc/servers/work-server/src/workers/plan-worker.ts`, `scripts/run-plan-codex-once.mjs`: Plan 자동화 워커와 Codex 실행기를 도입해 브랜치 준비, 작업 실행, 반영 흐름을 자동화했습니다.
```diff
diff --git a/etc/servers/work-server/src/workers/plan-worker.ts b/etc/servers/work-server/src/workers/plan-worker.ts
+export class PlanWorker {
+ private readonly workerId: string;
+ private timer: NodeJS.Timeout | null = null;
+ private running = false;
...
+ await this.processRegisteredPlans();
+ await this.processExecutablePlans();
+ await this.processReleaseReadyPlans();
diff --git a/scripts/run-plan-codex-once.mjs b/scripts/run-plan-codex-once.mjs
+const planItemId = process.env.PLAN_ITEM_ID ? Number(process.env.PLAN_ITEM_ID) : null;
+const codexBin = process.env.PLAN_CODEX_BIN ?? 'codex';
...
+function requiresSourceChange(note) {
+ return /(?:\/|\\).+\.[a-z0-9]+/i.test(text) || /(생성|만들어|추가|수정|변경|삭제|파일)/.test(text);
+}
```
## 변경 파일 (전체, 중복 제거, KST 기준)
- M scripts/run-plan-codex-once.mjs
- M .gitignore
- M etc/servers/work-server/.env.example
- M etc/servers/work-server/docker-compose.yml
- M etc/servers/work-server/src/services/plan-service.ts
- M etc/servers/work-server/src/workers/plan-worker.ts
- M src/features/planBoard/PlanBoardPage.tsx
- M src/main.tsx
- M etc/servers/work-server/src/config/env.ts
- M etc/servers/work-server/src/routes/plan.ts
- M etc/servers/work-server/src/services/git-service.ts
- M src/features/planBoard/types.ts
- M package.json
- A scripts/run-plan-codex-once.mjs
- M docs/README.md
- M docs/worklogs/2026-04-02.md
- M src/features/planBoard/api.ts
- M docker-compose.yml
- A etc/db/work-db/.env.example
- A etc/db/work-db/.gitignore
- A etc/db/work-db/README.md
- A etc/db/work-db/docker-compose.yml
- A etc/servers/work-server/.env.example
- A etc/servers/work-server/.gitignore
- A etc/servers/work-server/README.md
- A etc/servers/work-server/docker-compose.yml
- A etc/servers/work-server/package-lock.json
- A etc/servers/work-server/package.json
- A etc/servers/work-server/src/app.ts
- A etc/servers/work-server/src/config/env.ts
- A etc/servers/work-server/src/db/client.ts
- A etc/servers/work-server/src/lib/identifier.ts
- A etc/servers/work-server/src/routes/crud.ts
- A etc/servers/work-server/src/routes/ddl.ts
- A etc/servers/work-server/src/routes/health.ts
- A etc/servers/work-server/src/routes/plan.ts
- A etc/servers/work-server/src/routes/schema.ts
- A etc/servers/work-server/src/server.ts
- A etc/servers/work-server/src/services/git-service.ts
- A etc/servers/work-server/src/services/plan-service.ts
- A etc/servers/work-server/src/workers/plan-worker.ts
- A etc/servers/work-server/tsconfig.json
- M src/app/main/MainContent.tsx
- M src/app/main/MainHeader.tsx
- M src/app/main/MainSidebar.tsx
- M src/app/main/MainView.tsx
- M src/app/main/types.ts
- A src/features/planBoard/PlanBoardPage.tsx
- A src/features/planBoard/api.ts
- A src/features/planBoard/index.ts
- A src/features/planBoard/types.ts
- M src/store/appStore/types/index.ts
- M src/styles.css
- M vite.config.ts
- A docs/assets/worklogs/2026-04-02/search-command.png
- M docs/templates/worklog-template.md
- M docs/worklogs/2026-03-30.md
- M docs/worklogs/2026-03-31.md
- M docs/worklogs/2026-04-01.md
- A scripts/capture-search-command-screenshot.mjs
- D app-dist/apple-touch-icon.svg
- D app-dist/assets/2026-03-30-C9zIPzPv.js
- D app-dist/assets/2026-03-31-DnTx-3Nm.js
- D app-dist/assets/2026-04-01-HPg12E04.js
- D app-dist/assets/AntdIcon-Byo_R91X.js
- D app-dist/assets/CloseOutlined-B6nrJF3-.js
- D app-dist/assets/InputUI-w7gS2eD_.js
- D app-dist/assets/MultiProgressUI-BrtiP5fC.js
- D app-dist/assets/ProgressUI-CcSfL1yk.js
- D app-dist/assets/README-C7FUDFuk.js
- D app-dist/assets/README-CCe9ioJ1.js
- D app-dist/assets/Sample-3CFWfRoz.js
- D app-dist/assets/Sample-AyuQ97sV.css
- D app-dist/assets/Sample-BVdBpN2O.js
- D app-dist/assets/Sample-BX522g-L.js
- D app-dist/assets/Sample-BcLA6P4T.js
- D app-dist/assets/Sample-BdE7za0g.js
- D app-dist/assets/Sample-CJuh1XyL.js
- D app-dist/assets/Sample-CXTqMdzC.js
- D app-dist/assets/Sample-CYEm5q8n.js
- D app-dist/assets/Sample-D8WPIGkO.js
- D app-dist/assets/Sample-DCMU_ANA.js
- D app-dist/assets/Sample-DucAjs70.css
- D app-dist/assets/Sample-H43X1Va0.js
- D app-dist/assets/Sample-H8g2LJhv.js
- D app-dist/assets/Sample-IHgSYgwU.css
- D app-dist/assets/Sample-iMOOVcMn.js
- D app-dist/assets/SearchOutlined-Civ7xtmP.js
- D app-dist/assets/TmsDeliveryFlowSample-BIsYLVLu.js
- D app-dist/assets/TmsDeliveryMetricsSample-Cij5-xON.js
- D app-dist/assets/ValidInputSample-DfYNB-Xp.js
- D app-dist/assets/WidgetShell-BuID0-5g.js
- D app-dist/assets/WmsInboundOutboundSample-D8v85F-k.js
- D app-dist/assets/WmsInventoryTrendSample-BaOj-o94.js
- D app-dist/assets/button-editable-input-TvwRLJmy.png
- D app-dist/assets/card-C6TZ9YUa.js
- D app-dist/assets/check-combo-Bz7kGmN1.js
- D app-dist/assets/clsx-CzIxj0DI.js
- D app-dist/assets/component-plugin-DrEfBYjG.js
- D app-dist/assets/dashboard-report-presets-BmenDHNL.js
- D app-dist/assets/feature-template-C3ggnfNS.js
- D app-dist/assets/index-CR5AbEsh.js
- D app-dist/assets/index-WZ9gt1kR.css
- D app-dist/assets/input-B6oA1SZJ.js
- D app-dist/assets/input.plugin-BigPQCa9.js
- D app-dist/assets/jsx-runtime-CNArSbpp.js
- D app-dist/assets/main-content-fullscreen-toggle-Cppu1H3t.png
- D app-dist/assets/overview-BtWbOP4n.js
- D app-dist/assets/popup-BGFdvx2z.js
- D app-dist/assets/previewer-ui-BheoU6aq.js
- D app-dist/assets/project-setup-B38Eco2m.js
- D app-dist/assets/select-BVXn7KWj.js
- D app-dist/assets/select-oi1Bjv-c.js
- D app-dist/assets/status-badge-C7aul6sS.js
- D app-dist/assets/wave-DQjt-ubw.js
- D app-dist/assets/window-ui-CXDuYQu1.js
- D app-dist/assets/workbox-window.prod.es5-B4qug_J_.js
- D app-dist/assets/worklog-template-Donys780.js
- D app-dist/favicon.svg
- D app-dist/index.html
- D app-dist/manifest.webmanifest
- D app-dist/pwa-192x192.svg
- D app-dist/pwa-512x512.svg
- D app-dist/sw.js
- D app-dist/workbox-8c29f6e4.js
- D dev-dist/sw.js
- D dev-dist/workbox-5a5d9309.js
- A docs/components/search-command.md
- A docs/features/search-layer.md
- M index.html
- A src/components/search/SearchCommandModal.tsx
- A src/components/search/index.ts
- A src/components/search/types.ts
- M src/features/layout/component-sample-gallery/ComponentSamplesLayout.tsx
- M src/features/layout/widget-sample-gallery/SampleWidgetsLayout.tsx
- M src/index.ts
- A src/layer/gesture/context/GestureContext.tsx
- A src/layer/gesture/hooks/useGestureLayer.ts
- A src/layer/gesture/index.ts
- A src/layer/gesture/types/index.ts
- A src/layer/index.ts
- A src/layer/search/context/SearchLayerContext.tsx
- A src/layer/search/hooks/useSearchLayer.ts
- A src/layer/search/index.ts
- A src/layer/search/types/index.ts
- A src/store/appStore/context/AppStoreContext.tsx
- A src/store/appStore/hooks/useAppStore.ts
- A src/store/appStore/index.ts
- A src/store/appStore/types/index.ts
- A src/store/index.ts
- A app-dist/apple-touch-icon.svg
- R app-dist/assets/2026-03-30-C4SD1FRx.js -> app-dist/assets/2026-03-30-C9zIPzPv.js
- R app-dist/assets/2026-03-31-DwLJWvh2.js -> app-dist/assets/2026-03-31-DnTx-3Nm.js
- D app-dist/assets/2026-04-01-D5gI7Q4h.js
- A app-dist/assets/2026-04-01-HPg12E04.js
- R app-dist/assets/InputUI-DAmC5DJh.js -> app-dist/assets/InputUI-w7gS2eD_.js
- R app-dist/assets/MultiProgressUI--uB5kqTr.js -> app-dist/assets/MultiProgressUI-BrtiP5fC.js
- R app-dist/assets/ProgressUI-C91UL-oJ.js -> app-dist/assets/ProgressUI-CcSfL1yk.js
- R app-dist/assets/README-CI9EVrw_.js -> app-dist/assets/README-C7FUDFuk.js
- R app-dist/assets/README-O9_O-4tf2.js -> app-dist/assets/README-CCe9ioJ1.js
- R app-dist/assets/Sample-CeT4nPqx.js -> app-dist/assets/Sample-3CFWfRoz.js
- A app-dist/assets/Sample-AyuQ97sV.css
- R app-dist/assets/Sample-Dyso1eHr.js -> app-dist/assets/Sample-BVdBpN2O.js
- A app-dist/assets/Sample-BX522g-L.js
- R app-dist/assets/Sample-DKoCtyPX.js -> app-dist/assets/Sample-BcLA6P4T.js
- R app-dist/assets/Sample-DMEGMJwT.js -> app-dist/assets/Sample-BdE7za0g.js
- R app-dist/assets/Sample-BJxnglT1.js -> app-dist/assets/Sample-CJuh1XyL.js
- R app-dist/assets/Sample-xgRr-oUd.js -> app-dist/assets/Sample-CXTqMdzC.js
- R app-dist/assets/Sample-BPCdH5hH.js -> app-dist/assets/Sample-CYEm5q8n.js
- R app-dist/assets/Sample-LB0lRdor.js -> app-dist/assets/Sample-D8WPIGkO.js
- R app-dist/assets/Sample-6Ml90fMj.js -> app-dist/assets/Sample-DCMU_ANA.js
- A app-dist/assets/Sample-DucAjs70.css
- D app-dist/assets/Sample-E6V4D3Du.js
- A app-dist/assets/Sample-H43X1Va0.js
- R app-dist/assets/Sample-CLup9Uwo.js -> app-dist/assets/Sample-H8g2LJhv.js
- A app-dist/assets/Sample-IHgSYgwU.css
- A app-dist/assets/Sample-iMOOVcMn.js
- R app-dist/assets/TmsDeliveryFlowSample-BHeS93-n.js -> app-dist/assets/TmsDeliveryFlowSample-BIsYLVLu.js
- R app-dist/assets/TmsDeliveryMetricsSample-BQV5az65.js -> app-dist/assets/TmsDeliveryMetricsSample-Cij5-xON.js
- R app-dist/assets/ValidInputSample-C9pl9si5.js -> app-dist/assets/ValidInputSample-DfYNB-Xp.js
- R app-dist/assets/WidgetShell-DhXCYrC8.js -> app-dist/assets/WidgetShell-BuID0-5g.js
- R app-dist/assets/WmsInboundOutboundSample-BZCM3_0V.js -> app-dist/assets/WmsInboundOutboundSample-D8v85F-k.js
- R app-dist/assets/WmsInventoryTrendSample-DvxPBjgx.js -> app-dist/assets/WmsInventoryTrendSample-BaOj-o94.js
- A app-dist/assets/button-editable-input-TvwRLJmy.png
- R app-dist/assets/card-BpKFEf6A.js -> app-dist/assets/card-C6TZ9YUa.js
- R app-dist/assets/component-plugin-BjxKibxS.js -> app-dist/assets/component-plugin-DrEfBYjG.js
- R app-dist/assets/dashboard-report-presets-Bh8duNGL.js -> app-dist/assets/dashboard-report-presets-BmenDHNL.js
- R app-dist/assets/feature-template-D3D0o1kc.js -> app-dist/assets/feature-template-C3ggnfNS.js
- R app-dist/assets/index-BQsYfbAI.js -> app-dist/assets/index-CR5AbEsh.js
- D app-dist/assets/index-CaXbpawn.css
- A app-dist/assets/index-WZ9gt1kR.css
- R app-dist/assets/input.plugin-ulF_zEvq.js -> app-dist/assets/input.plugin-BigPQCa9.js
- A app-dist/assets/main-content-fullscreen-toggle-Cppu1H3t.png
- R app-dist/assets/overview-DgYaz2rW.js -> app-dist/assets/overview-BtWbOP4n.js
- A app-dist/assets/previewer-ui-BheoU6aq.js
- R app-dist/assets/project-setup-jU8Nv-E8.js -> app-dist/assets/project-setup-B38Eco2m.js
- R app-dist/assets/select-DYfkmyn8.js -> app-dist/assets/select-BVXn7KWj.js
- R app-dist/assets/select-kIZVYgkF.js -> app-dist/assets/select-oi1Bjv-c.js
- R app-dist/assets/status-badge-1fx0opaz.js -> app-dist/assets/status-badge-C7aul6sS.js
- A app-dist/assets/window-ui-CXDuYQu1.js
- A app-dist/assets/workbox-window.prod.es5-B4qug_J_.js
- R app-dist/assets/worklog-template-DE_f72dx.js -> app-dist/assets/worklog-template-Donys780.js
- A app-dist/favicon.svg
- M app-dist/index.html
- A app-dist/manifest.webmanifest
- A app-dist/pwa-192x192.svg
- A app-dist/pwa-512x512.svg
- A app-dist/sw.js
- A app-dist/workbox-8c29f6e4.js
- A dev-dist/sw.js
- A dev-dist/workbox-5a5d9309.js
- A docs/assets/worklogs/2026-04-01/.gitkeep
- A docs/assets/worklogs/2026-04-01/button-editable-input.png
- A docs/assets/worklogs/2026-04-01/main-content-fullscreen-toggle.png
- A docs/assets/worklogs/2026-04-02/.gitkeep
- A docs/assets/worklogs/2026-04-02/previewer-ui.png
- A docs/assets/worklogs/2026-04-02/window-ui.png
- A docs/components/previewer-ui.md
- A docs/components/window-ui.md
- A docs/worklogs/2026-04-02.md
- M package-lock.json
- A public/apple-touch-icon.svg
- A public/favicon.svg
- A public/pwa-192x192.svg
- A public/pwa-512x512.svg
- A scripts/capture-component-screenshot.mjs
- A scripts/capture-fullscreen-toggle-screenshot.mjs
- M src/app/manifests/docs.manifest.ts
- M src/components/markdownPreview/MarkdownPreviewCard.tsx
- M src/components/markdownPreview/MarkdownPreviewContent.tsx
- A src/components/previewer/PreviewerUI.css
- A src/components/previewer/PreviewerUI.tsx
- A src/components/previewer/index.ts
- A src/components/previewer/samples/Sample.tsx
- A src/components/previewer/types/index.ts
- A src/components/previewer/types/previewer.ts
- A src/components/window/WindowUI.css
- A src/components/window/WindowUI.tsx
- A src/components/window/index.ts
- A src/components/window/samples/Sample.tsx
- A src/components/window/types/index.ts
- A src/components/window/types/window.ts
- M src/vite-env.d.ts
- M tsconfig.lib.json
## 실행 커맨드
```bash
npm run build
docker compose up -d
npm run plan:codex:once
node scripts/capture-search-command-screenshot.mjs --date 2026-04-02
npm run capture:plan-mobile -- --date 2026-04-02
```
## 변경 파일
- `docs/worklogs/2026-04-02.md`
- `src/components/window/WindowUI.tsx`
- `src/components/previewer/PreviewerUI.tsx`
- `src/components/search/SearchCommandModal.tsx`
- `src/features/planBoard/PlanBoardPage.tsx`
- `src/layer/search/context/SearchLayerContext.tsx`
- `src/store/appStore/context/AppStoreContext.tsx`
- `etc/servers/work-server/src/routes/plan.ts`
- `etc/servers/work-server/src/services/plan-service.ts`
- `scripts/run-plan-codex-once.mjs`

137
docs/worklogs/2026-04-03.md Executable file
View File

@@ -0,0 +1,137 @@
# 2026-04-03 작업일지
## 오늘 작업
- Plan 자동화 재처리 흐름과 main 반영 안정화
- release/main 브랜치 전략을 정리하고 서버 재시작 오류를 수정
- Plan 메모 검색 기능 추가
- Plan 상세에서 소스 작업 이력/미리보기 확인 흐름 보강
- 모바일 설정 아이콘 터치 불가 이슈를 제스처 오버레이 충돌 제거로 해결
- 작업 상세의 불필요한 `내용 조회` 버튼 제거
- Noti 설정의 서버 토큰 등록/삭제 연동 보강
- 작업 ID 기본값(`작업ID`) 처리 및 중복 제약 완화
- 완료/반영 단계에서도 소스 작업 이력이 누락되지 않도록 저장 로직 보강
- Web Push 등록/호환 처리 보강(미지원 API 404 노출 완화)
- 4월 3일 작업 증적용 스크린샷(앱 설정, 알림 설정) 보강
- Plan 상세 증적 탭 하단에 스크린샷/작업일지/preview/source 전체화면 미리보기 추가
## 이슈 및 메모
- release -> main 자동 머지 이후 브랜치 상태 꼬임이 반복되어 브랜치 전략 재정비 필요
- 모바일 터치 이슈는 기능 오류보다 레이어 겹침(오버레이) 영향이 컸음
- 알림 기능은 로컬 UI 상태와 서버 토큰 상태가 분리되면 재현 어려운 장애로 이어짐
- 작업/이슈 이력 누락은 재처리 판단 정확도에 직접 영향
## 결정 사항
- Plan 자동화 브랜치 기준은 `main` 중심으로 정리
- 실패 후 재실행은 명시적 재처리 요청 기반으로 진행
- 알림 설정은 UI 토글만이 아니라 서버 토큰 등록/삭제와 동기화
- 메모/소스 작업 이력은 완료 단계 이후까지 일관 저장
## 상세 작업 내역
- 자동화 워커의 재처리, 반영, 이력 저장 경로를 손봐 동일 이슈 반복 시 추적 가능성을 높임
- release/main 전개 과정에서 발생하던 복구성 작업을 줄이기 위해 브랜치 운영 규칙을 단순화
- Plan 보드 UX는 모바일 접근성(터치/조회/검색) 중심으로 우선 개선
- 알림 관련 API 미지원 상황에서도 사용자에게 과도한 오류를 직접 노출하지 않도록 완충 처리 추가
## 스크린샷
![settings-app](../assets/worklogs/2026-04-03/settings-app.png)
![settings-notification](../assets/worklogs/2026-04-03/settings-notification.png)
## 소스
- `etc/servers/work-server/src/services/git-service.ts`: 새 이슈 브랜치를 `release`가 아니라 `main` 기준으로 만들도록 바꿔 브랜치 꼬임을 줄였습니다.
- `etc/servers/work-server/src/services/plan-service.ts`: 기본 작업 ID 처리, 중복 제약 해제, 완료 시 소스 이력 저장, `main` 반영 요청 단건화 등 자동화 추적 로직을 보강했습니다.
- `src/features/planBoard/PlanBoardPage.tsx`: 소스 작업 이력과 증적 미리보기 흐름을 상세 화면에 반영했습니다.
- `src/app/main/MainHeader.tsx`, `src/app/main/notificationApi.ts`: 알림 설정을 서버 토큰 상태와 동기화하고 미지원 환경의 404를 사용자 오류로 과하게 노출하지 않도록 조정했습니다.
- `etc/servers/work-server/src/workers/plan-worker.ts`: 재처리와 반영 흐름을 `main` 중심으로 다시 맞췄습니다.
```diff
diff --git a/etc/servers/work-server/src/services/git-service.ts b/etc/servers/work-server/src/services/git-service.ts
- const baseBranch = releaseTarget || config.releaseBranch;
+ const baseBranch = config.mainBranch;
diff --git a/etc/servers/work-server/src/services/plan-service.ts b/etc/servers/work-server/src/services/plan-service.ts
- workId: z.string().trim().min(1),
+ workId: z.string().trim().optional().default('작업ID'),
...
+ await createPlanLifecycleSourceWorkHistory(
+ id,
+ '작업완료 처리로 release 반영 대기 상태로 전환했습니다.',
+ currentRow.assigned_branch ?? currentRow.release_target ?? 'release',
+ );
```
## 변경 파일 (전체, 중복 제거, KST 기준)
- M etc/servers/work-server/src/services/plan-service.ts
- M scripts/run-plan-codex-once.mjs
- M etc/servers/work-server/.env.example
- M etc/servers/work-server/README.md
- M etc/servers/work-server/src/config/env.ts
- M etc/servers/work-server/src/services/git-service.ts
- M etc/servers/work-server/src/workers/plan-worker.ts
- M src/app/main/notificationApi.ts
- M src/app/main/MainHeader.tsx
- M etc/servers/work-server/package-lock.json
- M etc/servers/work-server/package.json
- M etc/servers/work-server/src/routes/notification.ts
- M etc/servers/work-server/src/services/notification-service.ts
- A etc/servers/work-server/src/types/web-push.d.ts
- M src/main.tsx
- A src/sw.ts
- M vite.config.ts
- M src/features/planBoard/PlanBoardPage.tsx
- M etc/servers/work-server/src/routes/plan.ts
- M src/features/planBoard/api.ts
- M src/features/planBoard/types.ts
- A etc/servers/work-server/src/services/plan-notification-service.ts
- A src/app/main/notificationApi.ts
- M src/layer/gesture/context/GestureContext.tsx
- M src/styles.css
- M etc/servers/work-server/src/app.ts
- A etc/servers/work-server/src/routes/notification.ts
- M etc/servers/work-server/src/server.ts
- A etc/servers/work-server/src/services/notification-service.ts
- M README.md
- A docs/assets/worklogs/2026-04-02/plan-board-mobile-memo-detail.png
- M docs/worklogs/2026-04-02.md
- M package.json
- M scripts/capture-component-screenshot.mjs
- M scripts/capture-fullscreen-toggle-screenshot.mjs
- A scripts/capture-plan-board-mobile-screenshot.mjs
- M scripts/capture-search-command-screenshot.mjs
- A scripts/worklog-capture-utils.mjs
- M src/app/main/MainSidebar.tsx
- M docs/README.md
- M index.html
- M src/app/main/MainView.tsx
- M src/app/main/types.ts
- M docker-compose.yml
- M src/features/planBoard/index.ts
- A docs/test001.md
- M etc/servers/work-server/docker-compose.yml
## 실행 커맨드
```bash
npm run plan:codex:once
npm run build
npm run capture:settings -- --date 2026-04-03
```
## 변경 파일
- `docs/worklogs/2026-04-03.md`
- `src/features/planBoard/PlanBoardPage.tsx`
- `etc/servers/work-server/src/workers/plan-worker.ts`
- `etc/servers/work-server/src/services/plan-service.ts`
- `etc/servers/work-server/src/services/git-service.ts`
- `src/app/main/MainHeader.tsx`
- `src/app/main/notificationApi.ts`
- `src/layer/gesture/context/GestureContext.tsx`
- `scripts/run-plan-codex-once.mjs`
- `src/styles.css`

104
docs/worklogs/2026-04-04.md Executable file
View File

@@ -0,0 +1,104 @@
# 2026-04-04 작업일지
## 오늘 작업
- Plan 목록 조회 버튼을 자동조회 중심 UX로 개편(남은 시간 표시/토글)
- 자동조회 버튼 레이아웃 깨짐/문구 중복/선택 드래그 문제 보정
- 앱 업데이트 진행 UI에 단계 표시와 프로그레스 흐름 추가
- 서비스워커 교체 흐름(`SKIP_WAITING`, 등록 확인, 타임아웃) 안정화
- 업데이트 실패 시 상태 복구와 오류 가시성 개선
- `vite-plugin-pwa` 업데이트 경로를 공식 흐름 우선으로 정리
- 자동화 Codex 실행 타임아웃 도입 및 롤백 과정 반영
- Plan 삭제 응답/정리 로직 개선
- work-server 메인 프로젝트 경로/풀 동작 정리
## 이슈 및 메모
- 업데이트 이슈는 단일 원인보다 서비스워커 교체 타이밍/등록 상태/환경 설정이 복합적으로 영향
- 자동조회는 텍스트/배지/오버레이가 겹치면 UX가 빠르게 악화됨
- Codex 타임아웃은 안전장치가 되지만 과도하면 정상 작업도 중단시킬 수 있음
## 결정 사항
- 업데이트 상태는 단순 성공/실패가 아닌 단계 기반으로 노출
- 서비스워커 등록/교체는 실패 복구 경로까지 포함해 설계
- 자동조회 UI는 버튼 본문 최소화 + 보조 배지 방식 유지
- 워커 경로 설정은 환경별 명확한 기준 경로로 통일
## 상세 작업 내역
- Plan 보드에서는 자동조회 중심 운영을 위해 상단 액션을 재설계하고 모바일/데스크톱 표시 균형을 맞춤
- 앱 업데이트 영역은 사용자 체감 기준(지금 무엇을 하는지)으로 문구와 상태 전이를 재구성
- 서비스워커 관련 반복 장애를 줄이기 위해 등록 확인, 교체 메시지 처리, 타임아웃/롤백 로직을 함께 점검
- 서버 측 경로 및 삭제 처리 개선으로 운영 중 발생하던 잔여 데이터/응답 일관성 문제를 줄임
## 스크린샷
- 저장소 기준 연결된 스크린샷 없음
## 소스
- `src/app/main/appUpdate.ts`, `src/app/main/MainHeader.tsx`: 앱 업데이트를 단계형 상태와 프로그레스 UI로 노출하고 서비스워커 등록 대기/재시도 흐름을 추가했습니다.
- `src/features/planBoard/PlanBoardPage.tsx`: 자동조회 버튼 UX와 Plan 목록 상단 액션 구성을 손봤습니다.
- `scripts/run-plan-codex-once.mjs`: Codex 자식 프로세스 종료/에러를 중복 처리하지 않도록 `settled` 가드를 추가했습니다.
- `etc/servers/work-server/src/services/plan-service.ts`: Plan 삭제 시 연관 소스작업/조치/이슈 이력까지 함께 정리하도록 트랜잭션 삭제로 바꿨습니다.
- `etc/servers/work-server/src/workers/plan-worker.ts`: 타임아웃과 실패 처리 흐름을 보강했습니다.
```diff
diff --git a/etc/servers/work-server/src/services/plan-service.ts b/etc/servers/work-server/src/services/plan-service.ts
- await db(PLAN_TABLE).where({ id }).delete();
+ await db.transaction(async (trx) => {
+ await trx(PLAN_SOURCE_WORK_TABLE).where({ plan_item_id: id }).delete();
+ await trx(PLAN_ACTION_TABLE).where({ plan_item_id: id }).delete();
+ await trx(PLAN_ISSUE_TABLE).where({ plan_item_id: id }).delete();
+ await trx(PLAN_TABLE).where({ id }).delete();
+ });
diff --git a/src/app/main/MainHeader.tsx b/src/app/main/MainHeader.tsx
+import { applyAppUpdate, getAppUpdateSnapshot, subscribeAppUpdate, type AppUpdateStatus } from './appUpdate';
...
+ <Button
+ block
+ icon={appUpdateStatus === 'updating' ? <ReloadOutlined spin /> : <DownloadOutlined />}
+ >
```
## 변경 파일 (전체, 중복 제거, KST 기준)
- M src/app/main/appUpdate.ts
- M etc/servers/work-server/src/routes/plan.ts
- M etc/servers/work-server/src/services/plan-service.ts
- M src/features/planBoard/api.ts
- M etc/servers/work-server/src/config/env.ts
- M etc/servers/work-server/src/workers/plan-worker.ts
- M scripts/run-plan-codex-once.mjs
- M src/sw.ts
- M vite.config.ts
- M src/app/main/MainHeader.tsx
- M src/styles.css
- M etc/servers/work-server/.env.example
- M etc/servers/work-server/docker-compose.yml
- M src/features/planBoard/PlanBoardPage.tsx
- M docker-compose.yml
- A src/app/main/appUpdate.ts
- M src/main.tsx
## 실행 커맨드
```bash
npm run build
npm run plan:codex:once
```
## 변경 파일
- `docs/worklogs/2026-04-04.md`
- `src/app/main/appUpdate.ts`
- `src/app/main/MainHeader.tsx`
- `src/features/planBoard/PlanBoardPage.tsx`
- `src/sw.ts`
- `vite.config.ts`
- `etc/servers/work-server/src/config/env.ts`
- `etc/servers/work-server/src/workers/plan-worker.ts`
- `etc/servers/work-server/src/services/plan-service.ts`
- `scripts/run-plan-codex-once.mjs`

129
docs/worklogs/2026-04-05.md Executable file
View File

@@ -0,0 +1,129 @@
# 2026-04-05 작업일지
## 오늘 작업
- Plan 작업 등록 시 `메인까지 자동등록` 옵션 추가 및 자동 main 반영 흐름 연결
- 대기성 알림을 줄이고 실제 자동화 시작/핵심 단계 중심 알림으로 재정렬
- 기본 진입 화면을 Plans로 조정하고 알림 진입 시 plan 상세 연결 개선
- Plan 성과 차트를 보드 하단에서 별도 사이드 메뉴(차트) 화면으로 분리
- 작업 ID 기본값일 때 알림 제목을 `#채번번호` 기준으로 표기
- Codex Live 채팅 기능 추가 후 별도 Chat 메뉴/사이드바로 분리
- 채팅을 폴링 기반에서 `work-server /ws/chat` WebSocket 기반으로 전환
- Vite 개발 환경에 `/ws/chat` 프록시 추가 및 재연결 안정화
- 채팅 UI를 하단 입력 고정형으로 개편하고 진단/오프라인 폴백 개선
- 진행 알림(1분/3분) 메시지에 현재 처리 단계/요약 반영
- 재처리/재시작 알림 조건 정교화(실제 재실행 예약 시만 노출)
- Web Push 알림 태그 충돌(덮어쓰기) 방지 처리
- Noti 토큰 관리 UI 및 전송 조건(토큰 등록 시) 정리
- Docs 기본 선택을 `worklogs`로 조정
- Plan 목록에서 `정상처리 구분(상/중/하)` 즉시 변경 기능 추가
## 이슈 및 메모
- 채팅 연결 이슈는 프론트 코드보다 프록시/업그레이드 헤더/URL 계산의 영향이 큼
- 진행 알림은 내용 품질이 낮으면 오히려 운영 피로도를 높임
- Web Push는 `tag` 충돌 시 중요한 이벤트가 사라질 수 있어 이벤트 키 설계가 중요
- 재처리 알림은 "실행 예약"과 "단순 응답" 구분이 필요
## 결정 사항
- 채팅은 WebSocket 기본, 실패 시 진단 가능 메시지와 폴백 제공
- 진행 알림은 `1분 후`, 이후 `3분마다` 유지하되 현재 단계 중심 문구 사용
- 알림 제목은 사용자 입력값보다 채번 ID 우선 전략 유지
- 재시작 알림은 실제 재실행이 예약된 경우에만 발송
- Docs 진입 기본 탭은 작업일지 우선
## 상세 작업 내역
- Plan/Chat 네비게이션을 재구성해 상단 메뉴-사이드바-콘텐츠 관계를 명확히 분리
- 워커 알림 체계를 시작/경과/완료/재시작으로 세분화하고 노이즈 이벤트를 제거
- 채팅은 UI 레이아웃, 연결 안정성, 진단 문구, 오프라인 대체 응답까지 함께 다듬어 실사용 대응 범위를 확장
- 푸시 알림은 iOS/Web 공통 이벤트 일관성을 높이고 덮어쓰기 문제를 줄이도록 이벤트 키 전략을 조정
- Plan 보드 운영 기능(정상처리 구분, 상세 이동, 자동화 상태 반영)을 보강해 운영 화면으로서 활용도를 높임
## 스크린샷
![feature-chat-live](../assets/worklogs/2026-04-05/feature-chat-live.png)
![feature-plans-charts](../assets/worklogs/2026-04-05/feature-plans-charts.png)
## 소스
- `etc/servers/work-server/src/services/chat-service.ts`, `src/app/main/MainChatPanel.tsx`: Codex Live 채팅을 WebSocket 기반으로 연결하고, 현재 화면/Plan 상태를 반영한 응답 흐름을 추가했습니다.
- `src/features/planBoard/charts.tsx`, `src/features/planBoard/PlanBoardPage.tsx`: Plan 성과 차트를 별도 메뉴로 분리하고 보드 운영 기능을 확장했습니다.
- `src/features/planBoard/api.ts`: Plan 데이터 조회를 `no-store` 중심으로 바꿔 현재 상태가 바로 반영되게 조정했습니다.
- `etc/servers/work-server/src/services/plan-service.ts`, `etc/servers/work-server/src/workers/plan-worker.ts`: `메인까지 자동등록`, 정상처리 구분, 진행 알림/재시작 조건을 서버 흐름에 연결했습니다.
```diff
diff --git a/etc/servers/work-server/src/services/chat-service.ts b/etc/servers/work-server/src/services/chat-service.ts
+type ChatInboundMessage =
+ | { type: 'context:update'; payload: ChatContext }
+ | { type: 'message:send'; payload: { text: string } };
...
+function isWorklogRequest(input: string) {
+ return mentionsWorklog || (normalized.includes('log') && asksToWrite);
+}
...
+function buildWorklogReply(context: ChatContext | null, snapshot: PlanSnapshot | null) {
+ return [
+ `# ${today} 작업일지`,
+ '## 오늘 작업',
+ ].join('\\n');
```
## 변경 파일 (전체, 중복 제거, KST 기준)
- M etc/servers/work-server/src/services/chat-service.ts
- M etc/servers/work-server/src/routes/plan.ts
- M src/app/main/MainChatPanel.tsx
- M etc/servers/work-server/src/services/plan-notification-service.ts
- M src/app/main/MainHeader.tsx
- A src/app/main/tokenAccess.ts
- M src/features/planBoard/PlanBoardPage.tsx
- M etc/servers/work-server/src/config/env.ts
- M etc/servers/work-server/src/services/plan-service.ts
- M scripts/run-plan-codex-once.mjs
- M src/features/planBoard/types.ts
- M src/styles.css
- M etc/servers/work-server/src/workers/plan-worker.ts
- M src/app/main/MainContent.tsx
- M vite.config.ts
- M etc/servers/work-server/package-lock.json
- M etc/servers/work-server/package.json
- M etc/servers/work-server/src/server.ts
- A etc/servers/work-server/src/services/chat-service.ts
- M src/app/main/MainSidebar.tsx
- M src/app/main/MainView.tsx
- M src/app/main/types.ts
- M src/store/appStore/types/index.ts
- A src/app/main/MainChatPanel.tsx
- A src/features/planBoard/charts.tsx
- M src/features/planBoard/index.ts
- M src/store/appStore/context/AppStoreContext.tsx
- M src/sw.js
- M src/features/planBoard/api.ts
- M docker-compose.yml
- M scripts/serve-app-dist.mjs
- R src/sw.ts -> src/sw.js
- A scripts/serve-app-dist.mjs
- M src/app/main/appUpdate.ts
## 실행 커맨드
```bash
npm run build
npm run capture:feature -- --date 2026-04-05 --target chat-live
npm run capture:feature -- --date 2026-04-05 --target plans-charts
```
## 변경 파일
- `docs/worklogs/2026-04-05.md`
- `src/app/main/MainChatPanel.tsx`
- `src/features/planBoard/PlanBoardPage.tsx`
- `src/features/planBoard/charts.tsx`
- `src/features/planBoard/api.ts`
- `etc/servers/work-server/src/services/plan-service.ts`
- `etc/servers/work-server/src/services/plan-notification-service.ts`
- `etc/servers/work-server/src/services/plan-notification-policy.ts`
- `etc/servers/work-server/src/services/chat-service.ts`
- `etc/servers/work-server/src/workers/plan-worker.ts`

171
docs/worklogs/2026-04-06.md Executable file
View File

@@ -0,0 +1,171 @@
# 2026-04-06 작업일지
## 오늘 작업
- Plan API의 `Load Failed`, 완료건 정상처리, 삭제 요청 실패 같은 기본 동작 오류를 먼저 정리
- Plan 목록/상세에서 작업 ID 표기, 자동화 진행 상태, 로딩/오류 메시지, 복사 버튼 등 사용성 보강
- Chat 화면이 선택된 Plan 문맥을 더 직접적으로 답하도록 서버 응답과 헤더 UX를 계속 보완
- `Codex Live``앱로그 > 에러 로그` 구조를 나누고, 에러 로그 상세 화면을 탭/미리보기 중심으로 확장
- 앱 설정을 드롭다운 구조와 `app config` 저장 방식으로 재구성하고 DB 동기화, 스냅샷 캐시, 즉시 반영 흐름 정리
- Docs 작업일지를 산출물 브라우저와 섹션 탭 기반 미리보기 형태로 키우고, `## 소스`/`## 실행 커맨드`/`## 변경 파일` 포맷을 통일
- 작업일지 스크린샷 자동화, 중복 제거, 날짜별 연결 규칙을 정리하고 3월 30일~4월 6일 문서를 후속 보강
- 토큰 미등록 사용자의 Plan/설정 접근 범위를 readonly와 403 기준으로 제한
## 이슈 및 메모
- Fastify는 빈 바디인데 `Content-Type: application/json`만 있는 `DELETE` 요청을 `400`으로 거절해 프런트 공통 요청 유틸 수정이 필요했음
- 완료건 정상처리, 자동 재처리, 자동화 설정 반영 시점이 서로 어긋나면 사용자 입장에서는 저장 실패처럼 보여 서버/프런트 기준을 같이 맞춰야 했음
- 작업일지 본문만 보여주던 방식으로는 스크린샷, 로그, 코드 diff, 변경 파일을 따라가기 어려워 산출물 브라우저가 필요했음
- 에러 로그/채팅/작업일지 preview가 각각 다른 규칙으로 렌더링돼 fenced code, 커맨드 블록, 파일 목록 표시가 계속 들쭉날쭉했음
- 토큰 미등록 사용자에게는 Plan 상세, 메모, 소스 작업, 설정 일부를 그대로 노출하면 안 돼 화면과 API를 동시에 잠가야 했음
## 결정 사항
- 공통 API 유틸은 HTTP 메서드가 아니라 실제 바디 유무 기준으로 `Content-Type`을 설정
- 자동화 실패 후 메모 저장이나 이슈 조치만으로는 자동 재처리를 다시 켜지 않도록 `retry=false` 기본값을 유지
- 작업일지 preview는 Markdown 본문 외 산출물도 같은 카드 안에서 섹션 단위 탭으로 노출
- 작업일지 `## 소스`는 파일 나열보다 "요약 설명 + 대표 diff" 중심으로 고정
- 앱 설정은 클라이언트 임시 상태가 아니라 DB 저장값과 비교 가능한 `app config` 구조로 관리
- 토큰 미등록 사용자는 Plan 관련 쓰기 작업과 민감 상세 열람을 모두 제한
## 상세 작업 내역
- `src/features/planBoard/api.ts`에서 요청 타임아웃/재시도와 헤더 구성을 다듬어 `Load Failed`, 삭제 실패, 정상처리 갱신 문제를 함께 줄였음
- `src/features/planBoard/PlanBoardPage.tsx`에서 작업 ID 표기, 자동화 상태 설명, 오류 알림 복사, 진행중 우선 정렬, readonly 처리, 작업일지 산출물 브라우저를 순차 반영
- `etc/servers/work-server/src/services/chat-service.ts`는 Chat 화면이어도 `planId`가 있으면 상태/이력/이슈를 같이 답하게 보강했고, 응답 제한 이유도 더 직접적으로 바꿨음
- `src/app/main/MainChatPanel.tsx`와 서버 에러 로그 라우트/서비스를 연결해 `앱로그 > 에러 로그` 화면과 상세 탭/최대화/참조 preview를 정리했음
- `src/app/main/MainHeader.tsx`, `src/app/main/appConfig.ts`, `etc/servers/work-server/src/routes/app-config.ts` 계층에서 앱 설정 드롭다운, DB 저장, 동기화, 캐시, 즉시 반영 흐름을 묶었음
- `src/components/markdownPreview/MarkdownPreviewCard.tsx`, `src/components/markdownPreview/MarkdownPreviewContent.tsx`, `src/components/previewer/*`는 작업일지/산출물 preview를 공통 포맷 레이어로 렌더링하게 바꿨음
- `scripts/capture-menu-screenshot.mjs`, `scripts/capture-feature-screenshot.mjs`, `scripts/worklog-capture-utils.mjs`로 메뉴/기능 캡처와 작업일지 연결을 자동화했고, 불필요한 날짜 참조는 다시 정리했음
- `docs/worklogs/2026-03-30.md`부터 `docs/worklogs/2026-04-06.md`까지 문서 포맷과 스크린샷/소스/커맨드/변경 파일 섹션을 현재 규칙에 맞게 보강했음
## 스크린샷
- 저장소 기준 연결된 스크린샷 없음
## 소스
- Plan 기본 동작 안정화: 완료건 정상처리, 삭제, 재시도, 오류 표시에 걸친 프런트/서버 동작을 정리했습니다.
- `src/features/planBoard/api.ts`, `src/features/planBoard/PlanBoardPage.tsx`, `etc/servers/work-server/src/services/plan-service.ts`: 요청 헤더, 타임아웃, 목록 정렬, readonly, 완료 처리와 자동 재처리 기준을 함께 맞췄습니다.
- Chat/앱로그 확장: 선택된 Plan 문맥 응답과 에러 로그 상세 뷰를 계속 키웠습니다.
- `etc/servers/work-server/src/services/chat-service.ts`, `src/app/main/MainChatPanel.tsx`, `src/app/main/errorLogApi.ts`: Chat 답변 문맥, 앱로그 메뉴 구조, 에러 로그 조회/상세/preview 흐름을 연결했습니다.
- 작업일지/증적 preview 정리: 문서 본문, 코드, 커맨드, 변경 파일을 같은 시각 규칙으로 렌더링하게 통일했습니다.
- `src/components/markdownPreview/MarkdownPreviewContent.tsx`, `src/components/previewer/renderers.tsx`, `docs/templates/worklog-template.md`: 작업일지 포맷과 preview 렌더러를 섹션 중심으로 정리했습니다.
```diff
diff --git a/src/features/planBoard/api.ts b/src/features/planBoard/api.ts
+ const hasBody = init?.body !== undefined && init.body !== null;
...
- headers.set('Content-Type', 'application/json');
+ if (hasBody && !headers.has('Content-Type')) {
+ headers.set('Content-Type', 'application/json');
+ }
```
```diff
diff --git a/src/app/main/MainHeader.tsx b/src/app/main/MainHeader.tsx
- <button>앱 설정</button>
+ <Dropdown menu={{ items: appConfigMenus }}>
+ <button>앱 설정</button>
+ </Dropdown>
```
```diff
diff --git a/src/components/previewer/renderers.tsx b/src/components/previewer/renderers.tsx
+export function renderMarkdownSectionPreview(section, context) {
+ if (section.type === 'code') return <CodeBlockPreview {...context} />;
+ if (section.type === 'command') return <CommandLogPreview {...context} />;
+ return <DefaultSectionPreview {...context} />;
+}
```
## 변경 파일 (전체, 중복 제거, KST 기준)
- M src/app/main/MainChatPanel.tsx
- M src/styles.css
- M etc/servers/work-server/src/services/app-config-service.ts
- M etc/servers/work-server/src/workers/plan-worker.ts
- M src/app/main/MainHeader.tsx
- M src/components/window/WindowUI.tsx
- M src/app/main/MainSidebar.tsx
- M src/app/main/MainView.tsx
- M etc/servers/work-server/src/services/plan-service.ts
- M src/features/planBoard/PlanBoardPage.tsx
- M src/features/planBoard/api.ts
- M etc/servers/work-server/src/services/plan-policy.test.ts
- M etc/servers/work-server/src/app.ts
- M etc/servers/work-server/src/services/error-log-service.ts
- M src/app/main/errorLogApi.ts
- M src/app/main/MainContent.tsx
- M src/app/main/types.ts
- A etc/servers/work-server/src/routes/error-log.ts
- A etc/servers/work-server/src/services/error-log-service.ts
- M src/App.tsx
- A src/app/main/errorLogApi.ts
- M etc/servers/work-server/src/routes/app-config.ts
- M src/app/main/appConfig.ts
- A etc/servers/work-server/src/routes/app-config.ts
- A etc/servers/work-server/src/services/app-config-service.ts
- M etc/servers/work-server/src/config/env.ts
- M etc/servers/work-server/src/routes/plan.ts
- M etc/servers/work-server/src/services/git-service.ts
- M etc/servers/work-server/src/services/notification-service.ts
- A src/app/main/appConfig.ts
- M etc/servers/work-server/src/services/chat-service.ts
- M src/features/planBoard/charts.tsx
- M src/features/planBoard/types.ts
- A docs/worklogs/2026-04-03.md
- A docs/worklogs/2026-04-04.md
- A docs/worklogs/2026-04-05.md
- A docs/worklogs/2026-04-06.md
- M etc/servers/work-server/src/services/plan-notification-service.ts
- M etc/servers/work-server/package.json
- A etc/servers/work-server/src/services/plan-notification-policy.ts
- A etc/servers/work-server/src/services/plan-policy.test.ts
- A etc/servers/work-server/src/services/plan-retry-policy.ts
- M src/sw.js
## 실행 커맨드
```bash
npx tsc -b
npm run build:app
npm run capture:menu -- --date 2026-04-06
npm run capture:feature -- --date 2026-04-06
npm run plan:codex:once
```
## 변경 파일
- `docs/templates/worklog-template.md`
- `docs/worklogs/2026-03-30.md`
- `docs/worklogs/2026-03-31.md`
- `docs/worklogs/2026-04-01.md`
- `docs/worklogs/2026-04-02.md`
- `docs/worklogs/2026-04-03.md`
- `docs/worklogs/2026-04-04.md`
- `docs/worklogs/2026-04-05.md`
- `docs/worklogs/2026-04-06.md`
- `etc/servers/work-server/src/routes/app-config.ts`
- `etc/servers/work-server/src/routes/error-log.ts`
- `etc/servers/work-server/src/routes/plan.ts`
- `etc/servers/work-server/src/services/app-config-service.ts`
- `etc/servers/work-server/src/services/chat-service.ts`
- `etc/servers/work-server/src/services/error-log-service.ts`
- `etc/servers/work-server/src/services/plan-service.ts`
- `etc/servers/work-server/src/workers/plan-worker.ts`
- `scripts/capture-feature-screenshot.mjs`
- `scripts/capture-menu-screenshot.mjs`
- `scripts/worklog-capture-utils.mjs`
- `src/app/main/MainChatPanel.tsx`
- `src/app/main/MainHeader.tsx`
- `src/app/main/appConfig.ts`
- `src/app/main/errorLogApi.ts`
- `src/components/markdownPreview/MarkdownPreviewCard.tsx`
- `src/components/markdownPreview/MarkdownPreviewContent.tsx`
- `src/components/previewer/PreviewerUI.css`
- `src/components/previewer/PreviewerUI.tsx`
- `src/components/previewer/renderers.tsx`
- `src/features/planBoard/PlanBoardPage.tsx`
- `src/features/planBoard/api.ts`
- `src/styles.css`

159
docs/worklogs/2026-04-07.md Executable file
View File

@@ -0,0 +1,159 @@
# 2026-04-07 작업일지
## 오늘 작업
- 누락된 `2026-04-07` 작업일지 파일을 복구
- Plan 재처리 요청 이력과 자동작업 실패 이슈를 기준으로 오늘 상태를 문서화
- `Docs > 작업일지` 화면 캡처를 생성해 오늘자 스크린샷 자산과 문서 링크를 연결
- `## 소스`, `## 실행 커맨드`, `## 변경/신규 파일` 섹션을 현재 정리 결과 기준으로 갱신
## 이슈 및 해결
- 오늘자 작업일지가 없어 Docs와 Plan 증적에서 `2026-04-07` 기록을 바로 확인할 수 없었음
- 별도 기능 수정 없이 누락된 일자 문서를 추가해 조회 공백을 해소
- 스크린샷 섹션이 비어 있어 오늘 작업 화면을 바로 검증하기 어려웠음
- `vite` 개발 서버와 `capture:feature` 스크립트로 `feature-docs-worklogs.png`를 생성해 문서와 연결
## 결정 사항
- 오늘 조치 범위는 작업일지 정리와 증적 보강으로 한정
- 2026-04-07 증적 대표 화면은 `Docs > 작업일지` 뷰로 통일
- 앱 번들 타입 오류가 남아 있어 이번 캡처는 `build:app` 대신 `vite` 개발 서버 기준으로 수행
## 상세 작업 내역
- `docs/worklogs/2026-04-07.md`를 생성해 오늘자 작업일지 기본 섹션을 채움
- 최근 조치 이력의 재처리 요청과 자동작업 실패 이력을 작업일지 맥락에 맞게 정리
- `docs/assets/worklogs/2026-04-07/feature-docs-worklogs.png`를 생성하고 `## 스크린샷`에 연결
- 실행한 확인/캡처 커맨드와 오늘 기준 변경 파일 증적을 문서 하단 섹션에 정리
## 스크린샷
![feature-docs-worklogs](../assets/worklogs/2026-04-07/feature-docs-worklogs.png)
## 소스
### 파일 1: `docs/worklogs/2026-04-07.md`
- 누락된 작업일지 복구 후 최신 증적까지 포함하도록 오늘자 문서를 다시 정리했습니다.
- 오늘 작업 요약, 스크린샷 링크, 실행 커맨드, 변경/신규 파일 목록을 최신 상태로 반영했습니다.
```diff
diff --git a/docs/worklogs/2026-04-07.md b/docs/worklogs/2026-04-07.md
+## 스크린샷
+![feature-docs-worklogs](../assets/worklogs/2026-04-07/feature-docs-worklogs.png)
+## 실행 커맨드
+npm run dev -- --host 127.0.0.1 --port 4173
```
### 파일 2: `docs/assets/worklogs/2026-04-07/feature-docs-worklogs.png`
- `Docs > 작업일지` 화면을 오늘자 자산 폴더에 저장해 문서 증적에서 바로 미리볼 수 있게 했습니다.
- 2026-04-07 작업일지의 스크린샷 섹션과 1:1로 대응하는 캡처 파일입니다.
```diff
Binary file added: docs/assets/worklogs/2026-04-07/feature-docs-worklogs.png
```
## 변경/신규 파일 (전체, 중복 제거, KST 기준)
- M src/app/main/MainHeader.tsx
- M scripts/run-plan-codex-once.mjs
- M docs/worklogs/2026-04-07.md
- M etc/servers/work-server/src/services/app-config-service.ts
- M etc/servers/work-server/src/workers/plan-worker.ts
- M src/app/main/appConfig.ts
- A .github/workflows/daily-docs-maintenance.yml
- M README.md
- M docs/README.md
- A docs/assets/worklogs/2026-04-07/.gitkeep
- A docs/assets/worklogs/2026-04-07/feature-docs-worklogs.png
- A docs/daily-maintenance.config.json
- M docs/worklogs/2026-03-30.md
- M docs/worklogs/2026-03-31.md
- M docs/worklogs/2026-04-01.md
- M docs/worklogs/2026-04-02.md
- M docs/worklogs/2026-04-03.md
- M docs/worklogs/2026-04-04.md
- M docs/worklogs/2026-04-05.md
- M docs/worklogs/2026-04-06.md
- M package.json
- A scripts/refresh-daily-docs.mjs
- M src/features/planBoard/PlanBoardPage.tsx
- A etc/servers/work-server/src/services/plan-service.test.ts
- M etc/servers/work-server/src/services/plan-service.ts
- A docs/worklogs/2026-04-07.md
- M docs/templates/worklog-template.md
- M etc/servers/work-server/src/services/chat-service.ts
- M src/components/markdownPreview/MarkdownPreviewCard.tsx
- M scripts/capture-component-screenshot.mjs
- M scripts/capture-feature-screenshot.mjs
- M scripts/capture-fullscreen-toggle-screenshot.mjs
- M scripts/capture-menu-screenshot.mjs
- M scripts/capture-plan-board-mobile-screenshot.mjs
- M scripts/capture-search-command-screenshot.mjs
- M scripts/capture-settings-screenshot.mjs
- M scripts/worklog-capture-utils.mjs
- M src/components/markdownPreview/MarkdownPreviewContent.tsx
- M src/components/previewer/PreviewerUI.css
- M src/components/previewer/PreviewerUI.tsx
- A src/components/previewer/renderers.tsx
- M src/components/previewer/types/index.ts
- M src/components/previewer/types/previewer.ts
- M etc/servers/work-server/src/routes/plan.ts
- M src/features/planBoard/types.ts
- M src/features/planBoard/api.ts
- M src/styles.css
- M src/app/main/MainContent.tsx
- M src/app/main/MainView.tsx
- R docs/assets/worklogs/2026-04-03/feature-chat-live.png -> docs/assets/worklogs/2026-04-05/feature-chat-live.png
- R docs/assets/worklogs/2026-04-03/feature-plans-charts.png -> docs/assets/worklogs/2026-04-05/feature-plans-charts.png
- A docs/assets/worklogs/2026-04-03/feature-chat-live.png
- A docs/assets/worklogs/2026-04-03/feature-plans-charts.png
- A docs/assets/worklogs/2026-04-03/settings-app.png
- A docs/assets/worklogs/2026-04-03/settings-notification.png
- A scripts/capture-settings-screenshot.mjs
- D docs/assets/worklogs/2026-04-06/docs-menu.png
- D docs/assets/worklogs/2026-04-06/feature-apis-components.png
- D docs/assets/worklogs/2026-04-06/feature-docs-worklogs.png
- D docs/assets/worklogs/2026-04-06/feature-plans-board.png
- D docs/assets/worklogs/2026-04-06/feature-plans-charts.png
- D docs/assets/worklogs/2026-04-06/plans-menu.png
- A docs/assets/worklogs/2026-04-06/docs-menu.png
- A docs/assets/worklogs/2026-04-06/feature-apis-components.png
- A docs/assets/worklogs/2026-04-06/feature-docs-worklogs.png
- A docs/assets/worklogs/2026-04-06/feature-plans-board.png
- A docs/assets/worklogs/2026-04-06/feature-plans-charts.png
- A docs/assets/worklogs/2026-04-06/plans-menu.png
- A scripts/capture-feature-screenshot.mjs
- A scripts/capture-menu-screenshot.mjs
- A docs/assets/worklogs/2026-03-30/input.png
- A docs/assets/worklogs/2026-03-30/status-badge.png
- A docs/assets/worklogs/2026-03-31/email-input.png
- A docs/assets/worklogs/2026-03-31/multi-input.png
- A docs/assets/worklogs/2026-04-01/check-combo-input.png
- A docs/assets/worklogs/2026-04-01/dashboard-multi-progress.png
- A docs/assets/worklogs/2026-04-01/dashboard-progress.png
- A docs/assets/worklogs/2026-04-01/popup-input.png
- A docs/assets/worklogs/2026-04-01/select-input.png
- M etc/servers/work-server/src/services/plan-notification-service.ts
- M etc/servers/work-server/src/services/plan-policy.test.ts
- M src/app/main/MainChatPanel.tsx
## 실행 커맨드
```bash
git -c safe.directory=/workspace/auto_codex/repo status --short --branch
sed -n '1,260p' docs/worklogs/2026-04-07.md
sed -n '1,260p' docs/templates/worklog-template.md
npm run dev -- --host 127.0.0.1 --port 4173
CAPTURE_BASE_URL=http://127.0.0.1:4173 npm run capture:feature -- docs-worklogs 2026-04-07
git -c safe.directory=/workspace/auto_codex/repo diff -- docs/worklogs/2026-04-07.md
```
## 자동 정리 상태
- 일일 정리 스크립트 실행일: `2026-04-07`
- 작업일지 파일 점검: `docs/worklogs/2026-04-07.md`
- 스크린샷 폴더 점검: `docs/assets/worklogs/2026-04-07/`
- README / Docs 가이드 자동 요약 갱신 완료

188
docs/worklogs/2026-04-08.md Executable file
View File

@@ -0,0 +1,188 @@
# 2026-04-08 작업일지
## 오늘 작업
- `Play` 상단 메뉴와 `Play > Test` 진입 경로를 추가했습니다.
- 헤더 최상단 메뉴를 `Docs / Plan / Play` 3개 기준으로 정리했습니다.
- `Play > layout` 화면을 만들고 상하/좌우 분할, 재귀 분할, 크기 단위, 최소 크기, 리사이즈 여부를 조절할 수 있게 확장했습니다.
- Plan 상세에서 긴 `issue`/`error` 본문을 기본 접힘으로 바꾸고 아이콘으로만 펼치도록 정리했습니다.
- `release` 환경에서 `main` 미반영 항목만 모아 보여주는 모달을 추가했습니다.
- 자동화 작업 브랜치 기준을 `release`가 아니라 `main`으로 바로잡고, worklog 자동화는 `hotfix/*` 브랜치를 쓰도록 보강했습니다.
- 누락됐던 `docs/worklogs/2026-04-08.md` 초안이 템플릿 상태로만 남아 있던 문제를 실제 작업 내역 기준으로 다시 정리했습니다.
- 누락됐던 `Play > layout` 스크린샷을 `docs/assets/worklogs/2026-04-08/feature-play-layout.png`로 다시 연결했습니다.
## 이슈 및 해결
- 오늘자 작업일지가 템플릿 문구만 남아 있어 `play`, 자동화 브랜치 정책, release 대기 모달 작업 이력을 문서에서 확인할 수 없었습니다.
- Git 기록 기준으로 2026-04-08 UTC/KST 커밋을 다시 확인해 실제 작업 항목과 변경 파일을 복원했습니다.
- 사용자가 언급한 `history` 기능은 Git 이력상 2026-04-07 13:44:06 UTC 커밋(`plan(history)`)에 생성됐고, 2026-04-08에는 `play`/plan 상세/자동화 보강 작업이 집중됐습니다.
- 스크린샷 섹션이 비어 있고 소스 뷰어의 diff 라벨도 일관되지 않아, 증적 확인 흐름이 한 번에 읽히지 않았습니다.
## 결정 사항
- 2026-04-08 문서는 placeholder 템플릿이 아니라 실제 커밋 증적 기준으로 유지합니다.
- `Play``Test``layout` 두 축으로 관리하고, 레이아웃 실험은 전용 playground에서 확장합니다.
- Plan 소스 뷰어 탭 명칭은 사용자가 바로 이해할 수 있게 `작업란 / 전체소스 / diff`로 정리합니다.
- 자동화 작업 브랜치 base는 `main`, 자동 worklog 정리는 `hotfix/*` 흐름을 따릅니다.
- 2026-04-08 대표 스크린샷은 `Play > layout` 화면으로 고정합니다.
## 상세 작업 내역
- `src/app/main/MainHeader.tsx`, `src/app/main/MainView.tsx`, `src/app/main/MainContent.tsx`, `src/app/main/types.ts`, `src/views/play/TestView.tsx`에서 `Play` 메뉴와 `Play > Test` 화면을 연결했습니다.
- `src/views/play/LayoutPlaygroundView.tsx``src/styles.css`에서 레이아웃 playground를 추가하고, 이후 같은 날 재귀 분할과 초기 빈 상태 진입까지 두 차례 확장했습니다.
- `src/features/planBoard/PlanBoardPage.tsx`에서 긴 이슈 본문 접힘 처리와 소스 작업 미리보기 경험을 보강했습니다.
- `src/App.tsx`, `src/app/main/ReleasePendingMainModal.tsx`, `src/styles.css`에서 `release` 접속 시 `main` 미반영 항목 안내 모달을 추가했습니다.
- `scripts/run-plan-codex-once.mjs`, `etc/servers/work-server/src/services/git-service.ts`, `etc/servers/work-server/src/workers/plan-worker.ts`, `etc/servers/work-server/src/services/plan-service.ts`에서 자동화 브랜치/정리 정책을 수정했습니다.
- 오늘 문서 누락 보완으로 `docs/worklogs/2026-04-08.md`를 실제 기록 기준으로 재작성했습니다.
- `scripts/capture-feature-screenshot.mjs``play-layout` 프리셋을 추가해 오늘자 대표 화면을 다시 캡처할 수 있게 했습니다.
- `src/components/previewer/CodexDiffPreviewer.tsx`에서 소스 뷰어 탭 라벨을 `전체소스 / diff`로 맞췄습니다.
## 스크린샷
![feature-play-layout](../assets/worklogs/2026-04-08/feature-play-layout.png)
## 소스
### 파일 1: `src/app/main/MainView.tsx`
- 상단 메뉴 체계를 `Docs / Plan / Play` 중심으로 재정리하고 `Play > Test`, `Play > layout` 탐색을 연결했습니다.
- 검색 커맨드와 초기 URL 파라미터도 `playSection` 기준으로 함께 맞췄습니다.
```diff
const PLAY_BASE_OPEN_KEYS = ['play-group', 'play-test-group', 'play-layout-group'] as const;
...
+ id: 'page:play:test',
+ keywords: ['play', 'test', '테스트', 'TEST메뉴'],
+ },
+ {
+ id: 'page:play:layout',
+ keywords: ['play', 'layout', 'flex', 'split', '분할', '레이아웃', 'preview'],
```
### 파일 2: `src/views/play/LayoutPlaygroundView.tsx`
- `play > layout` 화면을 추가하고, 같은 날 재귀 분할과 빈 상태 시작 플로우까지 확장했습니다.
- 상하/좌우 시작, `px/%` 단위, 최소 크기, 리사이즈 허용 여부를 화면에서 바로 조정할 수 있습니다.
```diff
diff --git a/src/views/play/LayoutPlaygroundView.tsx b/src/views/play/LayoutPlaygroundView.tsx
+const DEFAULT_LAYOUT_DIRECTION = 'row';
+const EMPTY_LAYOUT: LayoutNode | null = null;
...
+<Button onClick={() => handleCreateRoot('row')}>좌우로 시작</Button>
+<Button onClick={() => handleCreateRoot('column')}>상하로 시작</Button>
...
+<Button onClick={() => handleSplitNode(selectedNodeId, 'row')}>좌우 추가</Button>
+<Button onClick={() => handleSplitNode(selectedNodeId, 'column')}>상하 추가</Button>
```
### 파일 3: `src/app/main/ReleasePendingMainModal.tsx`
- `release` 환경에서 아직 `main`에 반영되지 않은 작업만 모아 보여주는 전용 모달을 추가했습니다.
- `릴리즈완료`, `main반영대기/중/실패` 상태를 기준으로 요약과 현황을 표시합니다.
```diff
diff --git a/src/app/main/ReleasePendingMainModal.tsx b/src/app/main/ReleasePendingMainModal.tsx
+export function ReleasePendingMainModal() {
+ // release 서버에서 main 미반영 항목만 조회
+}
```
### 파일 4: `scripts/run-plan-codex-once.mjs`
- `.auto_codex` 작업 브랜치의 base를 `release`가 아니라 `main`으로 맞췄습니다.
- 같은 날 자동 worklog 쪽은 `hotfix/*` 흐름을 따르도록 서버 로직도 함께 보강됐습니다.
```diff
diff --git a/scripts/run-plan-codex-once.mjs b/scripts/run-plan-codex-once.mjs
-const baseBranch = 'release';
+const baseBranch = 'main';
```
### 파일 5: `src/features/planBoard/PlanBoardPage.tsx`
-`issue`/`error` 본문은 기본 접힘으로 바꾸고, 이번 보완에서 소스 뷰어 탭 명칭도 `작업란 / 전체소스 / diff`로 정리했습니다.
```diff
diff --git a/src/features/planBoard/PlanBoardPage.tsx b/src/features/planBoard/PlanBoardPage.tsx
- label: '요약',
+ label: '작업란',
- label: '전체 소스',
+ label: '전체소스',
- label: 'Diff',
+ label: 'diff',
```
### 파일 6: `src/components/previewer/CodexDiffPreviewer.tsx`
- worklog 소스 탭과 Plan 소스 뷰어의 모드 라벨을 `전체소스 / diff`로 통일했습니다.
- 기존 `Raw Diff` 표기를 `diff`로 바꿔 사용자가 탭 목적을 바로 읽을 수 있게 맞췄습니다.
```diff
diff --git a/src/components/previewer/CodexDiffPreviewer.tsx b/src/components/previewer/CodexDiffPreviewer.tsx
- { label: '전체 소스', value: 'source' },
- { label: 'Raw Diff', value: 'diff' },
+ { label: '전체소스', value: 'source' },
+ { label: 'diff', value: 'diff' },
```
### 파일 7: `scripts/capture-feature-screenshot.mjs`
- `Play > layout` 화면도 기존 worklog 캡처 흐름으로 다시 만들 수 있게 프리셋을 추가했습니다.
- 오늘자 누락 스크린샷은 이 프리셋으로 `feature-play-layout.png`를 생성해 연결했습니다.
```diff
diff --git a/scripts/capture-feature-screenshot.mjs b/scripts/capture-feature-screenshot.mjs
+ 'play-layout': {
+ topMenu: 'play',
+ screenshotFileName: 'feature-play-layout.png',
+ targetSelector: '.app-main-card',
+ query: { playSection: 'layout' },
+ },
```
### 파일 8: `docs/assets/worklogs/2026-04-08/feature-play-layout.png`
- `Play > layout` 대표 화면을 다시 캡처해 오늘자 스크린샷 섹션과 바로 연결했습니다.
- 2026-04-08 작업일지에서 비어 있던 시각 증적을 채우는 실제 산출물 파일입니다.
```diff
Binary file added: docs/assets/worklogs/2026-04-08/feature-play-layout.png
```
## 실행 커맨드
```bash
git -c safe.directory=/workspace/auto_codex/repo log --since='2026-04-08 00:00' --until='2026-04-08 23:59:59' --stat --oneline --decorate
git -c safe.directory=/workspace/auto_codex/repo log --since='2026-04-08 00:00' --until='2026-04-08 23:59:59' --name-status --format='commit %H%nAuthor: %an%nDate: %ad%nSubject: %s' --date=iso
git -c safe.directory=/workspace/auto_codex/repo log --follow --name-status -- src/features/history/HistoryPage.tsx
npm run dev -- --host 127.0.0.1 --port 4173
CAPTURE_BASE_URL=http://127.0.0.1:4173 node scripts/capture-feature-screenshot.mjs play-layout 2026-04-08
```
## 변경/신규 파일 (전체, 중복 제거, Git 기록 기준)
- M src/app/main/MainHeader.tsx
- M src/app/main/MainContent.tsx
- M src/app/main/MainSidebar.tsx
- M src/app/main/MainView.tsx
- M src/app/main/types.ts
- M src/app/main/clientIdentity.ts
- M src/App.tsx
- A src/app/main/ReleasePendingMainModal.tsx
- A src/views/play/TestView.tsx
- A src/views/play/LayoutPlaygroundView.tsx
- M src/features/planBoard/PlanBoardPage.tsx
- M src/styles.css
- M src/store/appStore/types/index.ts
- M scripts/run-plan-codex-once.mjs
- M etc/servers/work-server/src/services/git-service.ts
- A etc/servers/work-server/src/services/git-service.test.ts
- M etc/servers/work-server/src/services/plan-service.ts
- M etc/servers/work-server/src/services/plan-service.test.ts
- M etc/servers/work-server/src/workers/plan-worker.ts
- M AGENTS.md
- A docs/worklogs/2026-04-08.md
- M src/components/previewer/CodexDiffPreviewer.tsx
- M scripts/capture-feature-screenshot.mjs
- A docs/assets/worklogs/2026-04-08/feature-play-layout.png

446
docs/worklogs/2026-04-09.md Executable file
View File

@@ -0,0 +1,446 @@
# 2026-04-09 작업일지
## 오늘 작업
- `Play > Layout`을 컴포넌트/위젯 선택형 레이아웃 편집기로 확장하고, 저장 레이아웃 목록/상세 흐름을 여러 차례 다듬었습니다.
- 레이아웃 저장소를 브라우저 `IndexedDB`에서 `work-db` API 기반 서버 저장 방식으로 전환했습니다.
- `Window UI` 검색 중복 열림, 모바일 리사이즈 hit area, 더블탭/더블클릭 확장, 스크롤 처리 문제를 순차적으로 정리했습니다.
- `EmbeddedMapUI`, `GPS Sample Widget`, 서버 경유 푸시 알림 흐름을 추가해 GPS 거점/반경/알림 시나리오를 앱 안에서 직접 다룰 수 있게 만들었습니다.
- `Text Memo Widget`을 신규 추가하고, 이어서 iOS 메모 느낌의 시트형 UI로 재구성했습니다.
- `Plan Board`에 로컬 페이지네이션, `release 반영 상태`/`자동화 실패` 바로가기, `main` 일괄 반영 흐름을 추가했습니다.
- 앱 초기 1.9초 로딩 오버레이, 제스처 단축키 설정, `release 반영 상태` 메뉴/모달, Git flow 안전 규칙 문서 보강까지 반영했습니다.
- 오늘자 작업 증적을 위해 전체 화면 1장과 위젯 단위 부분 캡처 1장을 `docs/assets/worklogs/2026-04-09/`에 저장하고 연결했습니다.
## 이슈 및 해결
- `Play > Layout` 저장본을 클라이언트 로컬 저장소만으로 관리하던 상태라 작업 환경이 바뀌면 레이아웃이 끊겼습니다.
- `src/views/play/layoutStorage.ts``work-db` API 호출 구조로 전환하고, 인증 토큰/클라이언트 ID 헤더를 함께 보내도록 바꿔 저장본을 서버 기준으로 유지했습니다.
- `Window UI` 검색/열기 흐름에서 같은 항목이 짧은 시간 안에 2개씩 열리거나, 전체 부모 기준으로 과도하게 확장되는 재발 이슈가 있었습니다.
- `SearchLayerContext`, `SearchCommandModal`, `WindowUI`를 함께 수정해 선택 잠금, 500ms 중복 차단, 보이는 viewport 기준 확장 로직으로 안정화했습니다.
- 지도 반경 오버레이는 초기에 좌표 기반 렌더가 아니라 CSS 고정 오버레이 성격이 강해 지도를 움직일 때 의미가 깨졌습니다.
- `EmbeddedMapUI`를 실제 좌표/경계 계산 기반 OSM iframe 오버레이로 바꾸고, GPS 위젯에서 선택 거점과 현재 위치를 함께 표시하도록 맞췄습니다.
- 자동화 쪽은 `main` 반영이 단건 기준으로만 움직여 `release`에 쌓인 항목을 한 번에 넘기기 어려웠습니다.
- `plan-service.ts`, `plan-worker.ts`에서 `release_target` 기준 `main` 일괄 반영/재시도 흐름으로 보강했습니다.
## 결정 사항
- `Play > Layout`의 대표 명칭은 `Layout Editor`로 통일하고, 저장된 레이아웃은 별도 메뉴 엔트리로 노출합니다.
- 레이아웃/플레이그라운드 저장은 브라우저 로컬 상태보다 `work-db` 서버 저장을 우선 기준으로 사용합니다.
- `Plan` 상단/설정 영역의 `release 반영 상태`, `자동화 실패`는 별도 빠른 진입점으로 유지합니다.
- `hotfix``feature` 후속 작업은 항상 새 브랜치에서 다시 시작한다는 Git 안전 규칙을 문서에 명시합니다.
- 오늘 대표 스크린샷은 `Play > Layout`, 부분 스크린샷은 실제 위젯 단위로 확인 가능한 `GPS Sample Widget` 카드로 남깁니다.
## 상세 작업 내역
- `src/views/play/LayoutPlaygroundView.tsx`에서 섹션별 `showHideAction`, 컴포넌트/위젯 검색 모달, preview-only 렌더, 저장 레이아웃 목록/상세, 전체보기/빈 상태 플로우를 누적 확장했습니다.
- `src/views/play/layoutStorage.ts`에서 `play_layouts` 테이블을 다루는 서버 API 저장소로 전환하고, fallback URL과 timeout 처리까지 추가했습니다.
- `src/components/search/SearchCommandModal.tsx`, `src/layer/search/context/SearchLayerContext.tsx`, `src/components/window/WindowUI.tsx`, `src/components/window/WindowUI.css`에서 검색 선택 중복, 모바일 hit area, 보이는 화면 기준 확장, 닫기 액션을 정리했습니다.
- `src/components/embeddedMap/*`, `src/layer/gps/*`, `src/widgets/gps-sample-card/*`, `etc/servers/work-server/src/routes/notification.ts`, `src/app/main/notificationApi.ts`에서 지도 내장 UI, GPS 레이어, 웹푸시 알림 경로를 구축했습니다.
- `src/widgets/text-memo-widget/*`, `src/widgets/core/WidgetShell.tsx`, `src/widgets/registry.ts`, `src/index.ts`에서 메모 위젯을 추가하고 위젯 카드 래퍼 옵션과 샘플 연결을 마쳤습니다.
- `src/features/planBoard/PlanBoardPage.tsx`, `src/features/planBoard/quickFilters.ts`, `src/app/main/MainHeader.tsx`, `src/app/main/MainView.tsx`, `src/app/main/MainContent.tsx`에서 plan 빠른 필터/로컬 패이징/윈도우 열기/메뉴 정리를 반영했습니다.
- `etc/servers/work-server/src/services/plan-service.ts`, `etc/servers/work-server/src/workers/plan-worker.ts`에서 `main` 일괄 반영 요청과 배치 처리 로직을 추가했습니다.
- `src/App.tsx`, `src/styles.css`에 앱 시작 로딩 오버레이와 로그 스트림 애니메이션을 넣었습니다.
- `AGENTS.md``release` 반영 직후 같은 브랜치에서 작업을 이어가지 않도록 하는 안전 체크리스트와 꼬임 위험 신호를 추가했습니다.
- 오늘 업무일지 문서와 스크린샷 자산은 현재 날짜 기준 Git 이력과 실제 캡처 결과로 다시 정리했습니다.
## 스크린샷
![feature-play-layout](../assets/worklogs/2026-04-09/feature-play-layout.png)
- `Play > Layout` 전체 화면입니다. 저장 레이아웃/편집/분할/섹션 선택 흐름을 한 번에 확인할 수 있습니다.
![widget-gps-sample](../assets/worklogs/2026-04-09/widget-gps-sample.png)
- `GPS Sample Widget` 부분 캡처입니다. 오늘 추가된 지도/거점/반경 위젯 단위 증적입니다.
## 소스
### 파일 1: `src/views/play/LayoutPlaygroundView.tsx`
- `Layout Editor`의 핵심 화면으로, 컴포넌트/위젯 바인딩과 저장 레이아웃 상세 플로우를 대부분 이 파일에서 확장했습니다.
```diff
+import { SearchCommandModal, type SearchKeywordOption } from '../../components/search';
+import { componentSampleEntries, widgetSampleEntries } from '../../app/manifests/samples.manifest';
+type LayoutComponentBinding = {
+ optionId: string;
+ label: string;
+ description?: string;
+ keywords: string[];
+};
+type LayoutPlaygroundViewProps = {
+ savedLayoutViewId?: string | null;
+ showSavedLayoutsOnly?: boolean;
+ onSavedLayoutsChange?: (layouts: SavedLayoutRecord[]) => void;
+};
- title: string;
- description: string;
+ showHideAction: boolean;
+ componentBinding: LayoutComponentBinding | null;
```
### 파일 2: `src/views/play/layoutStorage.ts`
- 레이아웃 저장소를 `IndexedDB`에서 `work-db` API 기반 서버 저장 방식으로 전환했습니다.
```diff
+import { appendClientIdHeader } from '../../app/main/clientIdentity';
+import { getRegisteredAccessToken } from '../../app/main/tokenAccess';
-const DATABASE_NAME = 'play-layout-db';
-const STORE_NAME = 'saved-layouts';
-const DATABASE_VERSION = 1;
+const WORK_SERVER_TIMEOUT_MS = 8000;
+const PLAY_LAYOUTS_TABLE = 'play_layouts';
+class LayoutStorageError extends Error {
+ status: number;
+}
```
### 파일 3: `src/components/search/SearchCommandModal.tsx`
- 검색 모달 제목/설명 커스터마이징, 모바일 축약 문구, 선택 잠금 로직을 추가했습니다.
```diff
+ title?: string;
+ description?: string;
+ placeholder?: string;
+ submitHint?: string;
+ const selectionLockRef = useRef(false);
+ const [isMobileViewport, setIsMobileViewport] = useState(() => {
+ return window.innerWidth <= 768;
+ });
+ const submitOption = (option: SearchKeywordOption | undefined) => {
+ if (!option || selectionLockRef.current) {
+ return;
+ }
+ selectionLockRef.current = true;
+ onSelectOption(option);
+ onClose();
+ };
```
### 파일 4: `src/components/window/WindowUI.tsx`
- `Window UI`는 닫기 액션, 모바일 리사이즈 개선, 보이는 viewport 기준 더블탭/더블클릭 확장 로직을 받았습니다.
```diff
-import { FullscreenExitOutlined, FullscreenOutlined, MinusOutlined } from '@ant-design/icons';
+import { CloseOutlined, FullscreenExitOutlined, FullscreenOutlined, MinusOutlined } from '@ant-design/icons';
+type ResizeTapRecord = {
+ direction: ResizeDirection;
+ at: number;
+ x: number;
+ y: number;
+};
+const DOUBLE_TAP_DELAY = 320;
+const DOUBLE_TAP_MOVE_TOLERANCE = 18;
+const RESIZE_MOVE_THRESHOLD = 4;
+function getVisibleParentBounds(element: HTMLDivElement | null) {
+ const viewportWidth = window.visualViewport?.width ?? window.innerWidth;
+}
```
### 파일 5: `src/components/embeddedMap/EmbeddedMapUI.tsx`
- 새 내장 지도 UI를 추가하고, 좌표/반경/보조 마커를 OSM iframe 위에 오버레이로 표시하도록 구성했습니다.
```diff
+export type EmbeddedMapUIProps = {
+ latitude: number;
+ longitude: number;
+ radiusMeters?: number;
+ lockViewport?: boolean;
+ secondaryMarker?: {
+ latitude: number;
+ longitude: number;
+ label?: string;
+ } | null;
+ overlay?: ReactNode;
+};
+function createEmbedUrl(bounds: Bounds, latitude: number, longitude: number) {
+ return `https://www.openstreetmap.org/export/embed.html?...`;
+}
```
### 파일 6: `src/widgets/gps-sample-card/GpsSampleWidget.tsx`
- GPS on/off, 현재 좌표, 거점 저장, 반경, In/Out 푸시 알림, 지도 표시를 한 위젯으로 묶었습니다.
```diff
+import {
+ AimOutlined,
+ BellOutlined,
+ EnvironmentOutlined,
+ DeleteOutlined,
+ RadarChartOutlined,
+} from '@ant-design/icons';
+import { EmbeddedMapUI } from '../../components/embeddedMap';
+import { useGpsLayer } from '../../layer';
+const selectedAnchor = anchors.find((anchor) => anchor.id === selectedAnchorId) ?? null;
+const distanceToSelectedAnchor =
+ selectedAnchor && currentPosition
+ ? calculateDistanceMeters(...)
+ : null;
```
### 파일 7: `src/widgets/text-memo-widget/TextMemoWidget.tsx`
- `Text Memo Widget`을 신규 추가하고 최근 6개 메모 저장/불러오기/삭제와 시트형 편집 UI를 구성했습니다.
```diff
+const STORAGE_KEY = 'ai-code-app:text-memo-widget';
+const MAX_SAVED_NOTES = 6;
+type SavedNote = {
+ id: string;
+ body: string;
+ createdAt: string;
+};
+export const TextMemoWidget = forwardRef<WidgetHandle, TextMemoWidgetProps>(function TextMemoWidget(
+ { cardWrapper },
+ ref,
+) {
```
### 파일 8: `src/features/planBoard/PlanBoardPage.tsx`
- plan 목록 로컬 패이징, 빠른 필터, 최대화 뒤 아이콘-only 돌아가기, 긴 본문 접힘 흐름을 오늘 기준으로 누적 반영했습니다.
```diff
+ const [currentPage, setCurrentPage] = useState(1);
+ const pageSize = 10;
+ const pagedItems = filteredItems.slice((currentPage - 1) * pageSize, currentPage * pageSize);
+ useEffect(() => {
+ setCurrentPage(1);
+ }, [filter, searchQuery]);
- label: '요약',
+ label: '작업란',
```
### 파일 9: `src/app/main/MainHeader.tsx`
- 설정 메뉴에 `release 상태 작업`, `자동화 실패` 바로가기를 추가하고 현재 건수를 같이 보여주도록 확장했습니다.
```diff
+import { isAutomationFailedItem, isReleasePendingMainItem } from '../../features/planBoard/quickFilters';
+const [planShortcutCounts, setPlanShortcutCounts] = useState({
+ releasePendingMain: 0,
+ automationFailed: 0,
+});
+void fetchPlanItems('all')
+ .then((items) => {
+ setPlanShortcutCounts({
+ releasePendingMain: items.filter(isReleasePendingMainItem).length,
+ automationFailed: items.filter(isAutomationFailedItem).length,
+ });
+ })
```
### 파일 10: `src/app/main/MainView.tsx`
- 상단/사이드 메뉴 구조를 `release 반영 상태`, `APIs / Widgets`, `Layout Editor`, 저장 레이아웃 메뉴까지 포괄하도록 재정리했습니다.
```diff
- 'in-progress': '작업중',
+ 'in-progress': '자동화 대기 / 작업 중',
- error: '오류',
+ error: '오류 (작업 완료 전)',
+ release: 'release 반영 상태',
+const PLAY_LAYOUT_RECORD_PREFIX = 'layout-record:' as const;
+function resolvePlanQuickFilterMenu(filter: PlanQuickFilter): Extract<PlanSidebarKey, 'release' | 'error'> {
+ return filter === 'release-pending-main' ? 'release' : 'error';
+}
```
### 파일 11: `src/app/main/MainContent.tsx`
- 검색 결과를 `Window UI` 창으로 여는 흐름, 샘플 숨김 목록, `Plan`/저장 레이아웃 렌더 분기를 추가했습니다.
```diff
+import { useMemo, useState, type ReactNode } from 'react';
+import { WindowUI, type WindowFrame } from '../../components/window';
+import { useSearchLayer } from '../../layer';
+const HIDDEN_COMPONENT_IDS = ['search-command-modal', 'window-ui'];
+const { windowSelections, clearWindowSelection } = useSearchLayer();
+const [windowFrames, setWindowFrames] = useState<Record<string, WindowFrame>>({});
+const [windowZIndexes, setWindowZIndexes] = useState<Record<string, number>>({});
```
### 파일 12: `src/App.tsx`
- 앱 시작 직후 1.9초 동안 보여주는 풀스크린 로딩 오버레이와 로그 스트림 UI를 추가했습니다.
```diff
+const INITIAL_LOADING_LOGS = [
+ 'BOOT SEQUENCE :: app shell warmup',
+ 'CONFIG SYNC :: workspace profile applied',
+ 'SESSION LINK :: reconnecting realtime channel',
+ 'MODULE CHECK :: dashboard widgets online',
+ 'READY SIGNAL :: rendering main viewport',
+];
+const [showInitialLoading, setShowInitialLoading] = useState(true);
+window.setTimeout(() => {
+ setShowInitialLoading(false);
+}, 1900);
```
### 파일 13: `etc/servers/work-server/src/services/plan-service.ts`
- `main` 반영 요청/클레임/완료 처리 모두를 `release_target` 기준 배치 처리로 바꿨습니다.
```diff
- isMainRetry ? 'main 반영 재시도를 요청했습니다.' : 'release 반영 재시도를 요청했습니다.',
+ isMainRetry ? 'main 일괄 반영 재시도를 요청했습니다.' : 'release 반영 재시도를 요청했습니다.',
+ const pendingRows = await db(PLAN_TABLE)
+ .select('id')
+ .where({ status: '릴리즈완료', release_target: releaseTarget });
- .where({ id })
+ .whereIn('id', targetIds.length > 0 ? targetIds : [id])
+ message: `${releaseTarget} 브랜치 기준으로 ${Math.max(targetIds.length, 1)}건 main 일괄 반영을 요청했습니다.`,
```
### 파일 14: `AGENTS.md`
- Git 안전 규칙을 보강해 `release` 반영 뒤 같은 브랜치에서 추가 작업을 이어가는 패턴을 금지하고 점검 절차를 명시했습니다.
```diff
+* `release` 반영 상태에서 현재 브랜치 그대로 추가 작업 진행
+* `release` 반영이 끝난 뒤 추가 수정 요청이 들어오면, 반드시 새 브랜치에서 다시 시작한다
+## 안전 점검 체크리스트
+1. 지금 반영할 변경이 `release`에 먼저 들어갔는지 확인
+2. 추가 요청이라면 새 `feature/*` 또는 `hotfix/*` 브랜치에서 작업 중인지 확인
+## 꼬임 위험 신호
+* `release` 반영 직후 추가 요청을 현재 브랜치에서 바로 이어서 처리하는 경우
```
### 파일 15: `docs/assets/worklogs/2026-04-09/feature-play-layout.png`
- 오늘 대표 전체 화면 스크린샷입니다.
```diff
Binary file added: docs/assets/worklogs/2026-04-09/feature-play-layout.png
```
### 파일 16: `docs/assets/worklogs/2026-04-09/widget-gps-sample.png`
- 오늘 위젯 단위 부분 스크린샷입니다.
```diff
Binary file added: docs/assets/worklogs/2026-04-09/widget-gps-sample.png
```
## 실행 커맨드
```bash
git -c safe.directory=/workspace/auto_codex/repo log --since='2026-04-09 00:00' --until='2026-04-09 23:59:59' --stat --oneline --decorate
git -c safe.directory=/workspace/auto_codex/repo log --since='2026-04-09 00:00' --until='2026-04-09 23:59:59' --name-status --format='commit %H%nAuthor: %an%nDate: %ad%nSubject: %s%n' --date=iso
git -c safe.directory=/workspace/auto_codex/repo diff --stat b70ce0c2549542703967b73aedf8d249050a40f7..HEAD
npm run dev -- --host 127.0.0.1 --port 4173
CAPTURE_BASE_URL=http://127.0.0.1:4173 node scripts/capture-feature-screenshot.mjs play-layout 2026-04-09
node --input-type=module # GPS Sample Widget 부분 캡처
npm run build:app
```
## 변경/신규 파일 (전체, 중복 제거, Git 기록 기준 + 오늘 업무일지 산출물)
- A src/components/dashboard/multiProgress/samples/BaseSample.tsx
- A src/components/dashboard/progress/samples/BaseSample.tsx
- A src/components/embeddedMap/EmbeddedMapUI.css
- A src/components/embeddedMap/EmbeddedMapUI.tsx
- A src/components/embeddedMap/index.ts
- A src/components/embeddedMap/samples/BaseSample.tsx
- A src/components/embeddedMap/samples/Sample.tsx
- A src/components/inputs/checkCombo/samples/BaseSample.tsx
- A src/components/inputs/composite/multiInput/samples/BaseSample.tsx
- A src/components/inputs/popup/samples/BaseSample.tsx
- A src/components/inputs/primitives/input/samples/BaseSample.tsx
- A src/components/inputs/select/samples/BaseSample.tsx
- A src/components/inputs/specialized/buttonEditableInput/samples/BaseSample.tsx
- A src/components/inputs/specialized/emailInput/samples/BaseSample.tsx
- A src/components/markdownPreview/samples/MarkdownPreviewCardBaseSample.tsx
- A src/components/markdownPreview/samples/MarkdownPreviewContentBaseSample.tsx
- A src/components/markdownPreview/samples/MarkdownPreviewListBaseSample.tsx
- A src/components/navigation/samples/FolderTreeNavBaseSample.tsx
- A src/components/navigation/samples/SectionMenuLayoutBaseSample.tsx
- A src/components/previewer/samples/BaseSample.tsx
- A src/components/previewer/samples/CodexDiffBaseSample.tsx
- A src/components/search/samples/BaseSample.tsx
- A src/components/status-badge/samples/BaseSample.tsx
- A src/components/window/samples/BaseSample.tsx
- A src/features/planBoard/quickFilters.ts
- A src/layer/gps/context/GpsLayerContext.tsx
- A src/layer/gps/hooks/useGpsLayer.ts
- A src/layer/gps/index.ts
- A src/layer/gps/types/index.ts
- A src/widgets/gps-sample-card/GpsSampleWidget.css
- A src/widgets/gps-sample-card/GpsSampleWidget.tsx
- A src/widgets/gps-sample-card/index.ts
- A src/widgets/gps-sample-card/samples/Sample.tsx
- A src/widgets/text-memo-widget/TextMemoWidget.css
- A src/widgets/text-memo-widget/TextMemoWidget.tsx
- A src/widgets/text-memo-widget/index.ts
- A src/widgets/text-memo-widget/samples/Sample.tsx
- M AGENTS.md
- M docs/components/window-ui.md
- M etc/servers/work-server/src/routes/notification.ts
- M etc/servers/work-server/src/services/plan-service.ts
- M etc/servers/work-server/src/workers/plan-worker.ts
- M package-lock.json
- M package.json
- M src/App.tsx
- M src/app/main/MainContent.tsx
- M src/app/main/MainHeader.tsx
- M src/app/main/MainView.tsx
- M src/app/main/ReleasePendingMainModal.tsx
- M src/app/main/appConfig.ts
- M src/app/main/notificationApi.ts
- M src/app/main/types.ts
- M src/components/dashboard/multiProgress/samples/Sample.tsx
- M src/components/dashboard/progress/samples/Sample.tsx
- M src/components/embeddedMap/EmbeddedMapUI.css
- M src/components/embeddedMap/EmbeddedMapUI.tsx
- M src/components/embeddedMap/samples/Sample.tsx
- M src/components/inputs/checkCombo/samples/Sample.tsx
- M src/components/inputs/composite/multiInput/samples/Sample.tsx
- M src/components/inputs/popup/samples/Sample.tsx
- M src/components/inputs/primitives/input/samples/Sample.tsx
- M src/components/inputs/select/samples/Sample.tsx
- M src/components/inputs/specialized/buttonEditableInput/samples/Sample.tsx
- M src/components/inputs/specialized/emailInput/samples/Sample.tsx
- M src/components/previewer/samples/CodexDiffSample.tsx
- M src/components/previewer/samples/Sample.tsx
- M src/components/search/SearchCommandModal.tsx
- M src/components/status-badge/samples/Sample.tsx
- M src/components/window/WindowUI.css
- M src/components/window/WindowUI.tsx
- M src/components/window/samples/Sample.tsx
- M src/components/window/types/window.ts
- M src/features/layout/component-sample-gallery/ComponentSamplesLayout.tsx
- M src/features/planBoard/PlanBoardPage.tsx
- M src/features/planBoard/index.ts
- M src/index.ts
- M src/layer/gps/context/GpsLayerContext.tsx
- M src/layer/index.ts
- M src/layer/search/context/SearchLayerContext.tsx
- M src/layer/search/types/index.ts
- M src/main.tsx
- M src/styles.css
- M src/views/play/LayoutPlaygroundView.tsx
- M src/views/play/layoutStorage.ts
- M src/widgets/api-sample-card/ApiSampleCardWidget.tsx
- M src/widgets/api-sample-card/samples/Sample.tsx
- M src/widgets/core/WidgetShell.tsx
- M src/widgets/core/index.ts
- M src/widgets/core/types/widget.ts
- M src/widgets/dashboard-report-card/DashboardReportCardWidget.tsx
- M src/widgets/dashboard-report-card/samples/TmsDeliveryFlowSample.tsx
- M src/widgets/dashboard-report-card/samples/TmsDeliveryMetricsSample.tsx
- M src/widgets/dashboard-report-card/samples/WmsInboundOutboundSample.tsx
- M src/widgets/dashboard-report-card/samples/WmsInventoryTrendSample.tsx
- M src/widgets/gps-sample-card/GpsSampleWidget.tsx
- M src/widgets/gps-sample-card/samples/Sample.tsx
- M src/widgets/registry.ts
- M src/widgets/text-memo-widget/TextMemoWidget.css
- M src/widgets/text-memo-widget/TextMemoWidget.tsx
- A docs/worklogs/2026-04-09.md
- A docs/assets/worklogs/2026-04-09/feature-play-layout.png
- A docs/assets/worklogs/2026-04-09/widget-gps-sample.png

357
docs/worklogs/2026-04-10.md Executable file
View File

@@ -0,0 +1,357 @@
# 2026-04-10 작업일지
## 오늘 작업
- `react-router-dom` 기반 앱 셸로 메인 화면을 재구성하고, 상단 메뉴와 사이드바 선택이 URL 경로와 직접 연결되도록 정리했습니다.
- `AppShell`, `MainLayout`, `routes.tsx`를 추가해 `Docs / APIs / Plans / Chat / Play`를 페이지 단위로 분리했습니다.
- `MainContent`는 공통 창 레이어와 콘텐츠 컨테이너 역할만 남기고, 실제 화면 렌더링은 각 페이지 컴포넌트로 이동시켰습니다.
- 저장 레이아웃 상세 화면은 `savedLayoutViewId` 전용 fit 모드로 보정해, 상단 바를 포함한 전체 구성을 한 화면 안에 맞춰 보이도록 수정했습니다.
- `Text Memo Widget`의 삭제 버튼이 실제로 동작하도록 `Modal.useModal()` 기반 확인 플로우를 연결했습니다.
- 오늘 증적용으로 `APIs / Widgets` 전체 화면 1장과 `Text Memo Widget` 부분 화면 1장을 `docs/assets/worklogs/2026-04-10/`에 저장했습니다.
- 오늘자 작업일지 문서를 새로 작성하고, 소스 탭에서 `전체소스 / diff` 전환에 대응할 수 있도록 파일별 raw diff 근거와 전체 변경 파일 목록을 정리했습니다.
## 이슈 및 해결
- 기존 `MainView` 한 파일에 메뉴 상태, 검색 옵션, 문서/플랜/채팅/플레이 렌더링이 모두 얽혀 있어 경로 기반 탐색과 확장이 어려웠습니다.
- `AppShell + MainLayout + pages/* + routes.tsx` 구조로 나누고, 라우팅 파생 상태는 `parseRoute()``MainLayoutContext`에서만 계산하도록 정리했습니다.
- 저장 레이아웃 전용 화면은 내부 콘텐츠 높이와 스크롤 상태에 따라 미리보기가 잘리거나 빈 여백이 생겼습니다.
- `useLayoutEffect`, `ResizeObserver`, `MutationObserver`로 실제 렌더 크기를 다시 재고 `savedLayoutFitScale`을 계산해 fit 렌더를 안정화했습니다.
- 메모 위젯 삭제 버튼은 정적 모달 호출과 컨텍스트 미연결 상태 때문에 클릭해도 확인창이 뜨지 않는 문제가 있었습니다.
- `Modal.useModal()`을 도입하고 `modalContextHolder`를 위젯 루트에 넣어 저장 메모와 작성 중 초안 모두 삭제 가능하게 바꿨습니다.
- 이 작업 환경에서는 Git이 `dubious ownership`으로 차단되고 글로벌 `safe.directory` 등록도 실패했습니다.
- 글로벌 설정 대신 모든 Git 조회에 `git -c safe.directory=/workspace/auto_codex/repo ...`를 사용해 증적 수집을 계속 진행했습니다.
## 결정 사항
- 메인 앱 진입은 상태 기반 조건 분기보다 URL 라우팅을 우선 기준으로 유지합니다.
- 공통 레이아웃 상태는 `MainLayoutContext`로 전달하고, 페이지별 렌더링 책임은 `pages/*`로 분리합니다.
- 저장 레이아웃 상세 뷰는 스크롤보다 fit 우선 미리보기 규칙을 사용합니다.
- 메모 삭제는 즉시 삭제하지 않고 항상 확인 모달을 거치게 유지합니다.
- 오늘 대표 스크린샷은 `APIs / Widgets` 전체 화면, 부분 스크린샷은 `Text Memo Widget`으로 고정합니다.
## 상세 작업 내역
- 오늘 Git 이력은 `WindowUI` 최소화/헤더 정리, 메모 삭제 플로우 복구, 저장 레이아웃 fit 모드 보정, 앱 셸 라우팅 분리 순서로 누적됐고 최종적으로 `release -> main` 동기화까지 진행됐습니다.
- 라우팅 개편은 `MainView`에 모여 있던 메뉴/문서/플랜/채팅/플레이 조건 분기를 걷어내고, `AppShell` 아래에서 `MainLayout`이 공통 상태를 제공한 뒤 각 페이지가 자기 화면만 렌더링하도록 재조정한 작업입니다.
- 검색 옵션 빌드도 같은 라우팅 구조를 따라가도록 재작성해서, 검색 결과가 단순 상태 변경이 아니라 실제 경로 전환과 포커스 이동을 함께 수행하도록 맞췄습니다.
- 저장 레이아웃 보정은 단순 CSS 스케일링이 아니라 실제 콘텐츠 크기를 측정해 viewport 안에 맞추는 방식으로 바꿨고, 관련 overflow 규칙도 같이 조정했습니다.
- 메모 위젯은 삭제 버튼 활성화와 확인 모달 흐름을 분리해, 저장된 메모와 작성 중 초안을 같은 UX 안에서 처리하도록 정리했습니다.
- 오늘 작업일지 작성 단계에서는 Git 로그, diff, 빌드 결과, 실제 Playwright 캡처를 함께 확인해서 문서와 스크린샷 링크를 최신 상태로 맞췄습니다.
## 스크린샷
![feature-apis-widgets-full](../assets/worklogs/2026-04-10/feature-apis-widgets-full.png)
- `APIs / Widgets` 전체 화면 캡처입니다. 라우팅 기반 앱 셸 아래에서 위젯 갤러리 전체 내역이 한 번에 보이도록 full-page로 저장했습니다.
![widget-text-memo](../assets/worklogs/2026-04-10/widget-text-memo.png)
- `Text Memo Widget` 부분 캡처입니다. 오늘 복구한 메모 입력/삭제 흐름을 위젯 단위로 바로 확인할 수 있도록 별도로 남겼습니다.
## 소스
### 파일 1: `src/app/main/AppShell.tsx`
- 라우터 엔트리와 페이지 분리의 기준점입니다.
```diff
+import { Navigate, Route, Routes } from 'react-router-dom';
+import { MainLayout } from './layout/MainLayout';
+import { ApisPage } from './pages/ApisPage';
+import { ChatPage } from './pages/ChatPage';
+import { DocsPage } from './pages/DocsPage';
+import { PlansPage } from './pages/PlansPage';
+import { PlayPage } from './pages/PlayPage';
+<Route path="/" element={<MainLayout />}>
+ <Route path="docs/:folder" element={<DocsPage />} />
+ <Route path="apis/:section" element={<ApisPage />} />
+ <Route path="plans/:section" element={<PlansPage />} />
+ <Route path="chat/:section" element={<ChatPage />} />
+ <Route path="play/layout-record/:layoutId" element={<PlayPage />} />
+</Route>
```
### 파일 2: `src/app/main/layout/MainLayout.tsx`
- 기존 `MainView`의 거대한 상태 조합을 라우팅 기반 공통 레이아웃으로 옮겼습니다.
```diff
+import { Outlet, useLocation, useNavigate, useSearchParams } from 'react-router-dom';
+function parseRoute(pathname: string) {
+ if (top === 'docs') { ... }
+ if (top === 'apis' && (first === 'components' || first === 'widgets')) { ... }
+ if (top === 'play' && first === 'layout-record' && second) { ... }
+}
+const routeState = useMemo(() => parseRoute(location.pathname), [location.pathname]);
+const layoutData = useMainLayoutData();
+<MainLayoutContextProvider value={...}>
+ <MainHeader ... />
+ <MainSidebar ... />
+ <MainContent contentExpanded={contentExpanded} onToggleContentExpanded={...}>
+ <Outlet />
+ </MainContent>
+</MainLayoutContextProvider>
```
### 파일 3: `src/app/main/routes.tsx`
- 메뉴 라벨, 경로 빌더, 사이드바 공개 타입을 한곳에 모았습니다.
```diff
+export type TopMenuKey = 'docs' | 'apis' | 'plans' | 'chat' | 'play';
+export type PlanSectionKey = PlanFilterStatus | 'release' | 'charts' | 'history';
+export const DOCS_DEFAULT_FOLDER = 'worklogs';
+export function buildDocsPath(folder = DOCS_DEFAULT_FOLDER) {
+ return `/docs/${folder}`;
+}
+export function buildPlansPath(section: PlanSectionKey = 'all') {
+ return `/plans/${section}`;
+}
+export function buildPlayMenuItems(savedLayouts: Array<{ id: string; name: string }>): MenuProps['items'] {
+ return [{ key: 'play-group', children: [...] }];
+}
```
### 파일 4: `src/app/main/MainContent.tsx`
- 공통 창 레이어만 남기고 화면별 조건 분기를 페이지 컴포넌트로 밀어냈습니다.
```diff
-import { Button, Card, Layout, Modal, Space, Typography } from 'antd';
+import { Button, Layout, Modal, Space, Typography } from 'antd';
-import { MarkdownPreviewCard } from '../../components/markdownPreview';
+import { useMainLayoutContext } from './layout/MainLayoutContext';
-export function MainContent({ activeTopMenu, selectedApiMenu, selectedDocsMenu, ... }: MainContentProps) {
+export function MainContent({ contentExpanded, onToggleContentExpanded, children }: MainContentProps) {
+ const { componentSampleEntries, widgetSampleEntries, componentSamples, widgetSamples, initialSelectedPlanId, initialSelectedWorkId } =
+ useMainLayoutContext();
- <div className={activeTopMenu === 'chat' ? 'app-main-layout app-main-layout--single' : 'app-main-layout'}>
- {activeTopMenu === 'docs' ? ... : activeTopMenu === 'plans' ? ... : ...}
- </div>
+ <div className="app-main-layout">{children}</div>
```
### 파일 5: `src/app/main/MainSidebar.tsx`
- 소개 영역과 API 메뉴 선택 타입을 공통 라우팅 구조에 맞게 정리했습니다.
```diff
+ introColor,
+ introTag,
+ introDescription,
- const activeTagColor = isDocsGroup ? 'gold' : activeTopMenu === 'play' ? 'cyan' : 'green';
- <Tag color={activeTagColor}>...</Tag>
+ <Tag color={introColor}>{introTag}</Tag>
+ <Text type="secondary">{introDescription}</Text>
- onSelectApiMenu(key);
+ onSelectApiMenu(key as MainSidebarProps['selectedApiMenu']);
```
### 파일 6: `src/app/main/layout/MainLayoutContext.ts`
- 페이지와 공통 레이어가 같은 상태를 공유하도록 컨텍스트를 신설했습니다.
```diff
+export type MainLayoutContextValue = {
+ topMenu: TopMenuKey;
+ selectedDocsMenu: string;
+ selectedApiMenu: ApiSectionKey;
+ selectedPlanMenu: PlanSectionKey;
+ selectedChatMenu: ChatSectionKey;
+ selectedPlayMenu: PlaySidebarKey;
+ searchOptions: SearchKeywordOption[];
+};
+const MainLayoutContext = createContext<MainLayoutContextValue | null>(null);
+export function useMainLayoutContext() { ... }
```
### 파일 7: `src/app/main/layout/buildSearchOptions.ts`
- 검색 결과가 상태 변경이 아니라 실제 라우팅 전환을 수행하도록 재작성했습니다.
```diff
+import { buildApisPath, buildChatPath, buildDocsPath, buildPlansPath, buildPlayPath } from '../routes';
+onSelect: () => {
+ requestPlanQuickFilter(null);
+ navigateTo(buildApisPath('widgets'));
+ setFocusedComponentId(null);
+},
+onSelect: () => {
+ requestPlanQuickFilter('release-pending-main');
+ navigateTo(buildPlansPath('release'));
+ setFocusedComponentId(null);
+},
+onSelect: () => {
+ navigateTo(buildDocsPath(document.folder));
+ setFocusedComponentId(`doc:${document.id}`);
+ scrollToElement(`document-preview-${document.id}`);
+},
```
### 파일 8: `src/app/main/pages/ApisPage.tsx`
- APIs 화면 렌더링 책임을 페이지 단위로 분리했습니다.
```diff
+export function ApisPage() {
+ const { selectedApiMenu, componentSampleEntries, widgetSampleEntries } = useMainLayoutContext();
+ return (
+ <div className="app-main-panel">
+ <Card title={selectedApiMenu === 'components' ? 'APIs / Components' : 'APIs / Widgets'} className="app-main-card" bordered={false}>
+ {selectedApiMenu === 'components' ? (
+ <ComponentSamplesLayout entries={componentSampleEntries} excludeComponentIds={HIDDEN_COMPONENT_IDS} />
+ ) : (
+ <SampleWidgetsLayout entries={widgetSampleEntries} />
+ )}
+ </Card>
+ </div>
+ );
+}
```
### 파일 9: `src/views/play/LayoutPlaygroundView.tsx`
- 저장 레이아웃 전용 fit 모드와 실제 크기 측정 로직을 추가했습니다.
```diff
-import { useEffect, useMemo, useRef, useState, type CSSProperties } from 'react';
+import { useEffect, useLayoutEffect, useMemo, useRef, useState, type CSSProperties } from 'react';
+const isSavedLayoutFitMode = Boolean(savedLayoutViewId);
+const [savedLayoutFitScale, setSavedLayoutFitScale] = useState(1);
+const [savedLayoutFitSize, setSavedLayoutFitSize] = useState<{ width: number; height: number } | null>(null);
+const savedLayoutFitViewportRef = useRef<HTMLDivElement | null>(null);
+const savedLayoutFitContentRef = useRef<HTMLDivElement | null>(null);
+useLayoutEffect(() => {
+ const nextScale = Math.min(1, availableWidth / width, availableHeight / height);
+ setSavedLayoutFitScale(...);
+}, [savedLayoutViewId, selectedSavedLayoutRecord, sampleEntries.length]);
+style={{ transform: `scale(${savedLayoutFitScale})`, width: `${savedLayoutFitSize.width}px` }}
+previewSurface: isSavedLayoutFitMode,
```
### 파일 10: `src/styles.css`
- fit 모드에서 overflow와 스크롤바를 억제하는 전용 스타일을 추가했습니다.
```diff
+.layout-playground__fullscreen-shell--saved-fit {
+ min-height: 0;
+ overflow: hidden;
+}
+.layout-playground__saved-fit-viewport {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+}
+.layout-playground__saved-detail--fit * {
+ scrollbar-width: none;
+}
+.layout-playground__saved-detail--fit *::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+}
```
### 파일 11: `src/widgets/text-memo-widget/TextMemoWidget.tsx`
- 삭제 버튼 비활성 상태를 해제하고, 확인 모달을 거쳐 실제 삭제되도록 바꿨습니다.
```diff
-import { Button, Empty, Input, message } from 'antd';
+import { Button, Empty, Input, Modal, message } from 'antd';
+const [modalApi, modalContextHolder] = Modal.useModal();
-const handleDelete = () => {
- void messageApi.info('삭제 기능은 현재 비활성화되어 있습니다.');
-};
+const handleDelete = () => {
+ if (!selectedNote && !hasDraft) {
+ return;
+ }
+ void modalApi.confirm({
+ title: isDraftOnly ? '작성 중인 메모를 삭제할까요?' : '선택한 메모를 삭제할까요?',
+ onOk: () => { ... },
+ });
+};
+{modalContextHolder}
-disabled
+disabled={!selectedNote && !hasDraft}
```
### 파일 12: `src/App.tsx`, `src/main.tsx`, `src/app/main/index.ts`
- 앱 진입점을 라우터 기반 셸로 연결했습니다.
```diff
-import { MainView } from './app/main';
+import { BrowserRouter } from 'react-router-dom';
+import { MainView } from './app/main';
+<BrowserRouter>
+ <MainView />
+</BrowserRouter>
-export { MainView } from './MainView';
+export { AppShell } from './AppShell';
+export { MainView } from './MainView';
-export function MainView() { ...legacy layout... }
+export function MainView() {
+ return <AppShell />;
+}
```
## 실행 커맨드
```bash
find docs/worklogs docs/assets/worklogs -type f | sort
grep -RIn "작업일지\|스크린샷\|소스 탭\|실행 커맨드\|상세형" docs/worklogs --include='*.md'
git -c safe.directory=/workspace/auto_codex/repo status --short --branch
sed -n '1,260p' docs/worklogs/2026-04-09.md
sed -n '1,260p' docs/templates/worklog-template.md
find scripts -maxdepth 1 -type f | sort
git -c safe.directory=/workspace/auto_codex/repo log --since='2026-04-10 00:00:00' --until='2026-04-10 23:59:59' --stat --oneline
git -c safe.directory=/workspace/auto_codex/repo log --since='2026-04-10 00:00:00' --until='2026-04-10 23:59:59' --name-only --pretty=format: | sed '/^$/d' | sort | uniq
git -c safe.directory=/workspace/auto_codex/repo diff --stat 0b105f8..HEAD -- src/app/main/MainView.tsx src/app/main/layout/MainLayout.tsx src/app/main/routes.tsx src/app/main/MainContent.tsx src/widgets/text-memo-widget/TextMemoWidget.tsx src/components/window/WindowUI.tsx src/styles.css src/views/play/LayoutPlaygroundView.tsx
git -c safe.directory=/workspace/auto_codex/repo diff 0b105f8..HEAD -- src/app/main/MainView.tsx
git -c safe.directory=/workspace/auto_codex/repo diff 0b105f8..HEAD -- src/app/main/layout/MainLayout.tsx
git -c safe.directory=/workspace/auto_codex/repo diff 0b105f8..HEAD -- src/widgets/text-memo-widget/TextMemoWidget.tsx
git -c safe.directory=/workspace/auto_codex/repo diff 0b105f8..HEAD -- src/app/main/routes.tsx
git -c safe.directory=/workspace/auto_codex/repo diff 0b105f8..HEAD -- src/app/main/MainContent.tsx
git -c safe.directory=/workspace/auto_codex/repo diff 0b105f8..HEAD -- src/views/play/LayoutPlaygroundView.tsx
git -c safe.directory=/workspace/auto_codex/repo diff 0b105f8..HEAD -- src/app/main/AppShell.tsx src/app/main/MainSidebar.tsx src/app/main/layout/MainLayoutContext.ts src/app/main/layout/buildSearchOptions.ts src/app/main/layout/useMainLayoutData.ts src/app/main/pages/ApisPage.tsx src/app/main/pages/ChatPage.tsx src/app/main/pages/DocsPage.tsx src/app/main/pages/PlansPage.tsx src/main.tsx
git -c safe.directory=/workspace/auto_codex/repo diff 0b105f8..HEAD -- src/components/window/WindowUI.tsx src/components/window/WindowUI.css src/widgets/text-memo-widget/TextMemoWidget.css src/styles.css
git -c safe.directory=/workspace/auto_codex/repo diff --name-status 0b105f8..HEAD
npm run build:app
PORT=4173 node scripts/serve-app-dist.mjs
node --input-type=module <<'EOF'
import { chromium } from 'playwright';
// APIs / Widgets full-page screenshot and text memo widget crop capture
EOF
```
## 변경/신규 파일
- A docs/worklogs/2026-04-10.md
- A docs/assets/worklogs/2026-04-10/feature-apis-widgets-full.png
- A docs/assets/worklogs/2026-04-10/widget-text-memo.png
- M package-lock.json
- M package.json
- M src/App.tsx
- A src/app/main/AppShell.tsx
- M src/app/main/MainContent.tsx
- M src/app/main/MainSidebar.tsx
- M src/app/main/MainView.tsx
- M src/app/main/index.ts
- A src/app/main/layout/MainLayout.tsx
- A src/app/main/layout/MainLayoutContext.ts
- A src/app/main/layout/buildSearchOptions.ts
- A src/app/main/layout/useMainLayoutData.ts
- A src/app/main/pages/ApisPage.tsx
- A src/app/main/pages/ChatPage.tsx
- A src/app/main/pages/DocsPage.tsx
- A src/app/main/pages/PlansPage.tsx
- A src/app/main/pages/PlayPage.tsx
- A src/app/main/routes.tsx
- M src/app/main/types.ts
- M src/main.tsx
- M src/styles.css
- M src/views/play/LayoutPlaygroundView.tsx
- M src/widgets/text-memo-widget/TextMemoWidget.tsx

296
docs/worklogs/2026-04-11.md Executable file
View File

@@ -0,0 +1,296 @@
# 2026-04-11 작업일지
## 오늘 작업
- 게시판 글을 자동화 항목으로 접수하는 흐름을 열고, 중복 접수 상태와 권한 검사를 같이 묶어 게시판에서 바로 후속 작업으로 넘길 수 있게 정리했습니다.
- 게시판 기반 Markdown 자동 등록 자동화는 설정, 워커 스케줄, 대상 폴더 규칙을 한 흐름으로 묶고 주기 계산을 분 단위 기준으로 다시 맞췄습니다.
- Plan 반복 요청은 기존 메모 내부 옵션에서 분리해 전용 스케줄 기능으로 옮기고, 스케줄 화면과 API, 서버 저장 구조를 새로 연결했습니다.
- Plan 화면은 목록 필터, 체크리스트, 릴리즈 요약, 증적 탭, 게시판 연결, 이슈/조치 기록, 본문 최대화, 읽기 전용 제어를 순차 보강했습니다.
- Plans 진입 구조는 불필요한 중간 화면을 걷어내고 `Plan / 게시판 / 차트 / 스케줄 / 히스토리`가 직접 연결되도록 다시 정리했습니다.
- 공통 컴포넌트 묶음으로 `FormField`, `StateKit`, `DataListTable`을 추가해 이후 화면 확장에 재사용할 수 있는 기반을 확보했습니다.
- 오늘 증적용으로 `Plan/자동화` 전체 화면 1장과 상단 개요 영역 1장을 `docs/assets/worklogs/2026-04-11/`에 저장했습니다.
## 이슈 및 해결
- 게시판 초기화가 동시에 들어오면 테이블과 컬럼 생성 경합으로 첫 진입이 실패할 수 있었습니다.
- 생성 전 존재 여부를 다시 확인하는 방식으로 setup 경합을 흡수해 초기 로드 실패를 줄였습니다.
- 게시판 자동 등록은 대기 글이 없을 때 다음 주기까지 완전히 멈춰 추천 문서 Plan이 더 이상 생기지 않는 구간이 있었습니다.
- 워커가 빈 큐에서도 다음 스케줄을 유지하도록 바꾸고, 분/초 단위 혼용 계산도 함께 보정했습니다.
- Plan 반복 요청 옵션을 메모 등록 폼 안에 유지하니 일반 메모 편집과 스케줄성 작업이 한 화면에서 섞여 판단이 어려웠습니다.
- 반복 요청 UI를 걷어내고 전용 스케줄 화면과 서버 테이블로 분리해 책임 경계를 명확히 했습니다.
- Plan 상세는 자동화 접수 항목인데도 일부 상태값이나 이력 입력이 우회 수정될 수 있었습니다.
- 잠금 판정을 공통 함수로 모으고, 권한 토큰이 없는 사용자는 조치 입력과 상태 액션이 모두 읽기 전용으로만 보이게 막았습니다.
- 증적/미리보기 전체화면은 내부 스크롤 대신 부모 스크롤이 따라 움직여 읽기 흐름이 깨졌습니다.
- 전체화면 진입 시 `body/html` 스크롤을 고정하고, 모달 내부 스크롤만 유지하도록 바꿔 사용 흐름을 안정화했습니다.
## 결정 사항
- 게시판에서 만들어진 자동화 접수 건은 기본적으로 `release` 기준으로 처리하고 `main` 자동 반영은 별도 정책이 없는 한 꺼 둡니다.
- 반복성 작업은 일반 메모 옵션이 아니라 전용 스케줄 목록에서 관리합니다.
- Plan 상세에서 자동화로 접수된 원본 요청은 수정 대신 조치 기록과 증적 확인 중심으로 다룹니다.
- Plans 화면은 중간 안내 카드보다 실제 작업 화면을 바로 여는 구성이 우선입니다.
- 오늘 대표 증적 화면은 `Plan/자동화` 전체 화면, 부분 증적은 상단 개요 카드로 통일합니다.
## 상세 작업 내역
- 오전 초반에는 게시판 setup 경합과 자동화 접수 진입점을 먼저 정리해, 게시판 글이 실패 없이 열리고 필요한 경우 바로 자동화 요청으로 이어지게 만들었습니다.
- 이어서 게시판 글을 Markdown 생성 작업으로 돌리는 자동화 흐름을 붙였고, 추천 문서 생성 규칙과 스케줄 조건을 계속 수정하면서 빈 큐에서도 다음 주기를 유지하도록 다듬었습니다.
- 공통 컴포넌트 추가는 이후 화면 정리에 필요한 기반 작업으로 진행했고, 새 입력/상태/목록 컴포넌트를 샘플과 함께 공개해 재사용 가능한 토대를 마련했습니다.
- 중반 작업에서는 반복 요청 옵션을 걷어내고 전용 스케줄 기능으로 치환하는 쪽으로 방향을 바꿨습니다. 이 과정에서 저장 구조, API, 워커, 화면을 함께 움직여 반복 작업 관리의 중심을 스케줄 화면으로 옮겼습니다.
- 이후 Plans 네비게이션은 실제 사용 흐름에 맞게 다시 접었고, 게시판 복구, 차트/스케줄/히스토리 재연결, 용어 정리, 검색 라벨 통일을 거쳐 지금 구조로 수렴시켰습니다.
- 오후 후반에는 Plan 목록과 상세 화면 품질을 집중 보강했습니다. 조합 필터, 체크리스트, 릴리즈 요약, 증적 탭, 게시판 연결, 이슈 조치 기록이 순차적으로 붙었고, 본문 최대화와 전체화면 스크롤 제어도 여기서 함께 정리됐습니다.
- 마감 단계에서는 자동화 접수 건의 잠금 규칙을 다시 점검해 우회 수정 가능성을 줄였고, 비토큰 사용자 읽기 전용 제어와 알림 링크 연결까지 보완한 뒤 오늘 작업일지와 증적 캡처를 최신 상태로 정리했습니다.
## 스크린샷
![feature-plans-automation-full](../assets/worklogs/2026-04-11/feature-plans-automation-full.png)
- `Plan/자동화` 전체 화면 캡처입니다. 오늘 정리한 목록, 상세 진입 흐름, 상단 개요 구성이 한 화면에서 보이도록 전체 페이지 기준으로 저장했습니다.
![plan-board-overview](../assets/worklogs/2026-04-11/plan-board-overview.png)
- 상단 개요 카드 부분 캡처입니다. 자동 새로고침 상태, 작업 개수 요약, 안내 문구처럼 오늘 보강한 운영 문맥을 빠르게 확인할 수 있도록 따로 남겼습니다.
## 소스
### 파일 1: `etc/servers/work-server/src/routes/board.ts`, `etc/servers/work-server/src/services/board-service.ts`, `src/features/board/BoardPage.tsx`, `src/features/board/api.ts`, `src/features/board/types.ts`
- 게시판 글을 즉시 자동화 Plan으로 접수하고, 접수 중복과 권한을 함께 처리하는 흐름입니다.
```diff
+app.post('/api/board/posts/:id/actions/automation-receive', async (request, reply) => {
+ if (!requireBoardAutomationAccess(request, reply)) {
+ return;
+ }
+ const result = await receiveBoardPostAutomation(id);
+ return { ok: true, item: result.item, planItemId: result.planItemId, alreadyReceived: result.alreadyReceived };
+});
+if (currentRow.automation_received_at || currentRow.automation_plan_item_id) {
+ return { item: mapBoardPostRow(currentRow), planItemId: ..., alreadyReceived: true };
+}
+const workId = `board-post-${id}`;
+note: [`게시판 제목: ${title}`, '', content].join('\n'),
+auto_deploy_to_main: false,
+const [automationReceivingId, setAutomationReceivingId] = useState<number | null>(null);
+const [automationReceiveError, setAutomationReceiveError] = useState<string | null>(null);
+icon={<PlayCircleOutlined />}
+Tag color={automationStatus.color}
```
### 파일 2: `etc/servers/work-server/src/services/board-service.ts`, `etc/servers/work-server/src/workers/plan-worker.ts`, `etc/servers/work-server/src/services/app-config-service.ts`, `src/app/main/MainHeader.tsx`, `src/app/main/appConfig.ts`
- 게시판 Markdown 자동 등록 자동화를 설정과 워커 주기 계산까지 포함해 운영 가능한 상태로 묶었습니다.
```diff
+markdownPlanItemId: number | null;
+markdownExportedAt: string | null;
+function buildBoardPostMarkdownPlanNote(row: Record<string, unknown>, targetFolder: string) {
+ return [
+ `게시판 추천 글 #${id}을 Markdown 문서로 등록해 주세요.`,
+ `대상 파일: ${targetPath}`,
+ ].join('\n');
+}
+export async function createNextBoardPostMarkdownAutomationPlan(targetFolder: string, releaseTarget = 'release') {
+ work_id: `board-md-${id}`,
+ auto_deploy_to_main: true,
+}
+private evaluateBoardMarkdownAutomationSchedule(config, now) {
+ const scheduleType = config?.scheduleType ?? 'interval';
+ return { due, nextEligibleAt, scheduleLabel };
+}
+await this.processBoardMarkdownAutomation(appConfig);
+intervalMinutes: z.coerce.number().int().min(1).default(5)
```
### 파일 3: `src/components/formField/FormField.tsx`, `src/components/stateKit/StateKit.tsx`, `src/components/dataListTable/DataListTable.tsx`, `src/index.ts`
- 입력/상태/목록 UI를 공통 컴포넌트로 분리해 이후 Plan, Board, History 화면이 같은 토대 위에서 확장되도록 만들었습니다.
```diff
+export type DataListTableProps<T extends object> = {
+ data: T[];
+ searchFields?: ReadonlyArray<keyof T | ((item: T) => string)>;
+ filters?: ReadonlyArray<DataListFilterOption<T>>;
+ mobileCardRender?: (item: T) => ReactNode;
+};
+export function DataListTable<T extends object>({ ... }: DataListTableProps<T>) {
+ const filteredData = useMemo(() => { ... }, [...]);
+ return <Table<T> ... />;
+}
+export type FormFieldProps = Omit<FormItemProps, 'children' | 'help' | 'required'> & {
+ error?: ReactNode;
+ children: ReactNode | ((state: FormFieldRenderState) => ReactNode);
+};
+export { FormField } from './components/formField';
+export { StateKit } from './components/stateKit';
+export { DataListTable } from './components/dataListTable';
```
### 파일 4: `etc/servers/work-server/src/services/plan-service.ts`, `etc/servers/work-server/src/services/plan-schedule-service.ts`, `etc/servers/work-server/src/routes/plan.ts`, `etc/servers/work-server/src/workers/plan-worker.ts`, `src/features/planBoard/PlanSchedulePage.tsx`, `src/features/planBoard/api.ts`
- 반복 요청을 메모 옵션에서 분리하고 전용 스케줄 저장소와 화면으로 재편했습니다.
```diff
+export const PLAN_SCHEDULED_TASK_TABLE = 'plan_scheduled_tasks';
+export const createPlanScheduledTaskSchema = z.object({
+ workId: z.string().trim().optional().default('반복작업'),
+ repeatIntervalMinutes: z.coerce.number().int().min(1).max(1440).default(60),
+});
+app.get('/api/plan/scheduled-tasks', async (request, reply) => { ... });
+app.post('/api/plan/scheduled-tasks', async (request, reply) => { ... });
+app.patch('/api/plan/scheduled-tasks/:id', async (request, reply) => { ... });
+app.delete('/api/plan/scheduled-tasks/:id', async (request, reply) => { ... });
+await ensurePlanScheduledTaskTable();
+return fetchWithFallback('/api/plan/scheduled-tasks', '/api/plans/scheduled-tasks');
+'/plan/schedule', '/plan/schedules', '/plans/schedule', '/plans/schedules'
```
### 파일 5: `src/features/planBoard/PlanBoardPage.tsx`, `src/features/history/HistoryPage.tsx`, `src/features/board/BoardPage.tsx`, `src/styles.css`
- Plan 목록과 상세는 필터, 요약, 증적, 본문 최대화, 잠금 규칙, 읽기 전용 제어를 중심으로 계속 보강했습니다.
```diff
+function isPlanItemRequestLocked(item: Pick<PlanItem, 'startedAt'> | null | undefined) {
+ return Boolean(item?.startedAt);
+}
+const [noteExpanded, setNoteExpanded] = useState(false);
+const canAppendActionHistory = hasAccess && Boolean(selectedItem) && isPlanItemRequestLocked(selectedItem);
+if (!hasAccess) {
+ messageApi.error('설정 > 토큰 관리에서 권한 토큰을 등록한 사용자만 조치 이력을 추가할 수 있습니다.');
+}
+if (isPlanItemRequestLocked(currentItem)) {
+ messageApi.warning('자동화 접수된 항목은 기능동작확인을 수정할 수 없습니다.');
+}
+className={`plan-board-page__notepad-frame${noteExpanded ? ' plan-board-page__notepad-frame--expanded' : ''}`}
+className={`board-page__editor-frame${contentExpanded ? ' board-page__editor-frame--expanded' : ''}`}
+options={FUNCTION_CHECK_OPTIONS}
+disabled={!hasAccess || jangsingProcessingSavingId === item.id || isPlanItemRequestLocked(item)}
```
### 파일 6: `src/app/main/MainContent.tsx`, `src/app/main/layout/MainLayout.tsx`, `src/app/main/layout/buildSearchOptions.ts`, `src/app/main/mainView/constants.tsx`, `src/app/main/mainView/navigation.ts`, `src/app/main/mainView/searchOptions.ts`, `src/app/main/pages/PlansPage.tsx`, `src/app/main/routes.tsx`, `src/app/main/MainHeader.tsx`
- Plans 진입 구조를 단순화하고, 게시판/차트/스케줄/히스토리 복구와 용어 정리를 같은 축에서 마무리했습니다.
```diff
+type PlanSectionKey = 'all' | 'board' | 'charts' | 'schedule' | 'history' | 'release';
+navigateTo(buildPlansPath('board'));
+navigateTo(buildPlansPath('schedule'));
+navigateTo(buildPlansPath('history'));
+label: '자동화'
+label: 'Plan'
+title: '자동화'
+return <PlanBoardPage />;
+release target link -> '*.sm-home.cloud'
+search label: 'Plan'
```
### 파일 7: `etc/servers/work-server/src/services/plan-notification-service.ts`, `src/sw.js`, `src/components/previewer/PreviewerUI.tsx`, `src/components/previewer/CodexDiffPreviewer.tsx`
- 알림 클릭 이동과 전체화면 미리보기는 마지막 품질 보완으로 정리했습니다.
```diff
+function buildPlanNotificationTargetUrl(planId: number, workId: string | null | undefined, eventType: string) {
+ const baseUrl = eventType.startsWith('release-') ? 'https://rel.sm-home.cloud/' : 'https://sm-home.cloud/';
+ targetUrl.searchParams.set('planId', String(planId));
+}
+targetUrl = notificationData.targetUrl ? new URL(String(notificationData.targetUrl)) : new URL('/', self.location.origin);
+const scrollY = window.scrollY;
+document.body.style.position = 'fixed';
+document.body.style.top = `-${scrollY}px`;
+document.documentElement.style.overflow = 'hidden';
+window.scrollTo(0, scrollY);
```
### 파일 8: `docs/components/component-addition-suggestions.md`
- 게시판 논의 결과를 문서 증적으로 옮겨 오늘 제안 정리 흐름도 문서 세트 안에서 바로 확인할 수 있게 했습니다.
```diff
+## 이번 반영 요약
+- FormField: 폼 레이블, 도움말, 오류 메시지를 같은 포맷으로 묶는 공통 입력 레이어
+- StateKit: 로딩, 비어 있음, 오류 상태를 화면마다 반복 작성하지 않도록 정리한 상태 표현 묶음
+- DataListTable: 검색, 필터, 모바일 카드 대체 렌더를 함께 제공하는 공통 목록 레이어
```
## 실행 커맨드
```bash
find docs/worklogs -maxdepth 2 -type f -name '*.md' | sort
grep -RInE '작업일지|실행 커맨드|소스 탭|상세 작업 내역' docs/worklogs --include='*.md'
git -c safe.directory=/workspace/auto_codex/repo branch --show-current
git -c safe.directory=/workspace/auto_codex/repo status --short --branch
git -c safe.directory=/workspace/auto_codex/repo log --since='2026-04-11 00:00' --pretty=format:'%h %ad %d %s' --date=iso-local --reverse --all
git -c safe.directory=/workspace/auto_codex/repo log --since='2026-04-11 00:00' --name-status --pretty=format:'commit %h' --all
git -c safe.directory=/workspace/auto_codex/repo diff 6f7a9aa..a6a3c92 -- src/features/planBoard/PlanBoardPage.tsx
git -c safe.directory=/workspace/auto_codex/repo show 818cc2f -- src/features/board/BoardPage.tsx src/features/board/api.ts src/features/board/types.ts etc/servers/work-server/src/routes/board.ts etc/servers/work-server/src/services/board-service.ts
git -c safe.directory=/workspace/auto_codex/repo show d9d124b -- src/features/planBoard/PlanSchedulePage.tsx src/features/planBoard/api.ts etc/servers/work-server/src/routes/plan.ts etc/servers/work-server/src/services/plan-schedule-service.ts etc/servers/work-server/src/workers/plan-worker.ts
git -c safe.directory=/workspace/auto_codex/repo show dd2e975 -- src/components/formField/FormField.tsx src/components/stateKit/StateKit.tsx src/components/dataListTable/DataListTable.tsx src/index.ts
git -c safe.directory=/workspace/auto_codex/repo show 4e48813 -- src/app/main/MainHeader.tsx src/app/main/appConfig.ts etc/servers/work-server/src/workers/plan-worker.ts etc/servers/work-server/src/services/board-service.ts
npm run build:app
PORT=4173 node scripts/serve-app-dist.mjs
curl -I http://127.0.0.1:4173/plans/all
node --input-type=module <<'EOF'
// Playwright로 /plans/all 진입 후 전체 화면과 개요 카드 캡처 저장
EOF
find docs/assets/worklogs/2026-04-11 -maxdepth 1 -type f | sort
```
## 변경/신규 파일
- A docs/worklogs/2026-04-11.md
- A docs/assets/worklogs/2026-04-11/feature-plans-automation-full.png
- A docs/assets/worklogs/2026-04-11/plan-board-overview.png
- M docs/components/component-addition-suggestions.md
- M etc/db/work-db/sql/board-posts.sql
- M etc/servers/work-server/src/routes/board.ts
- M etc/servers/work-server/src/routes/plan.ts
- M etc/servers/work-server/src/routes/visitor-history.ts
- M etc/servers/work-server/src/services/app-config-service.ts
- M etc/servers/work-server/src/services/board-service.ts
- M etc/servers/work-server/src/services/plan-notification-service.ts
- A etc/servers/work-server/src/services/plan-schedule-service.ts
- M etc/servers/work-server/src/services/plan-service.ts
- M etc/servers/work-server/src/services/visitor-history-service.ts
- M etc/servers/work-server/src/services/worklog-automation-service.ts
- M etc/servers/work-server/src/services/worklog-automation-utils.ts
- A etc/servers/work-server/src/workers/plan-worker.test.ts
- M etc/servers/work-server/src/workers/plan-worker.ts
- M src/app/main/MainContent.tsx
- M src/app/main/MainHeader.tsx
- M src/app/main/appConfig.ts
- M src/app/main/clientIdentity.ts
- M src/app/main/layout/MainLayout.tsx
- M src/app/main/layout/buildSearchOptions.ts
- M src/app/main/mainView/constants.tsx
- M src/app/main/mainView/navigation.ts
- M src/app/main/mainView/searchOptions.ts
- M src/app/main/pages/PlansPage.tsx
- M src/app/main/routes.tsx
- A src/components/dataListTable/DataListTable.css
- A src/components/dataListTable/DataListTable.tsx
- A src/components/dataListTable/index.ts
- A src/components/dataListTable/samples/BaseSample.tsx
- A src/components/formField/FormField.css
- A src/components/formField/FormField.tsx
- A src/components/formField/index.ts
- A src/components/formField/samples/BaseSample.tsx
- M src/components/previewer/CodexDiffPreviewer.tsx
- M src/components/previewer/PreviewerUI.tsx
- A src/components/stateKit/StateKit.css
- A src/components/stateKit/StateKit.tsx
- A src/components/stateKit/index.ts
- A src/components/stateKit/samples/BaseSample.tsx
- M src/features/board/BoardPage.tsx
- M src/features/board/api.ts
- M src/features/board/types.ts
- M src/features/history/HistoryPage.tsx
- M src/features/history/api.ts
- M src/features/planBoard/PlanBoardPage.tsx
- A src/features/planBoard/PlanSchedulePage.tsx
- M src/features/planBoard/api.ts
- M src/features/planBoard/index.ts
- M src/features/planBoard/types.ts
- M src/index.ts
- M src/store/appStore/context/AppStoreContext.tsx
- M src/styles.css
- M src/sw.js