# PLANS.md — 디자인·기능 전격 개편 > 목표: `design-refs/AI DEV 포털.dc.html` 의 디자인/기능을 **거의 그대로** 살려서 > 현재 포털을 개편한다. 단, 제약은 유지 — **빌드 없음 · React 없음 · 서버 렌더링(EJS) + > 단일 CSS(`public/style.css`) + 가벼운 바닐라 JS(`public/app.js`)**. > > 기준: 디자인이 기획(PRD)보다 앞섰으므로 **디자인을 정답으로** 본다. PRD에 빠진 항목은 > PRD에 보강한다(Phase 6). ## 확정된 설계 결정 (사용자 합의) - **반응 2종 유지**: ❤️ 좋아요(heart) + ⭐ 즐겨찾기(star) 둘 다. 인기순 정렬·즐겨찾기 필터 포함. - **부가 요소 전부 유지**: 다크모드 토글, 토스트, 헤더 검색, 이스터에그(로고 흔들기·👍🚀✨·LGTM 스탬프). - **공개/비공개(visibility)** 도입: 내 프로젝트는 공개/비공개 전환 가능, 비공개는 피드 미노출. - **관리자 판별은 실제 Keycloak role** 로. (디자인의 "관리자 보기" 데모 토글은 제거하고 role 기반으로 대체) - **상세는 모달** + 비-JS 폴백용 `/projects/:id` 단독 페이지도 유지. - **반응/댓글은 점진적 향상(progressive enhancement)**: JS 없으면 폼 POST→리다이렉트, JS 있으면 fetch 로 즉시 반영. - 폐쇄망: 폰트/아이콘은 외부 CDN 의존 최소화(시스템 폰트 폴백 + 인라인 SVG/이모지). --- ## Phase 0 — DB 스키마 & db.js ✅ (db:init 실행만 남음) - [x] `schema.sql`: `projects` 에 `is_public boolean NOT NULL DEFAULT true`, `lang text DEFAULT ''` 컬럼 추가(멱등 `ADD COLUMN IF NOT EXISTS`). - [x] `schema.sql`: `comments` 에 `parent_id bigint REFERENCES comments(id) ON DELETE CASCADE` 추가(대댓글 1뎁스). - [x] `schema.sql`: ❤️ 좋아요용 `hearts(project_id, sabun, created_at, PK)` 테이블 추가. (기존 `stars` = ⭐ 즐겨찾기 유지) - [x] `db.js`: `listProjects(viewer)` — heart_count·star_count·comment_count·owner_name·is_public·lang + viewer hearted/starred. 공개 + 본인 비공개만 반환(남의 비공개 미노출). - [x] `db.js`: `getProject(id, viewer)` — 단일 조회 + 카운트 + viewer 반응 여부. - [x] `db.js`: `listComments(projectId)` — parent_id 포함, 작성자 이름. - [x] `db.js`: `addProject({owner,title,description,repoUrl,appUrl,tags,isPublic,lang})`. - [x] `db.js`: `addComment({projectId,author,body,parentId})` — 부모가 같은 프로젝트의 최상위일 때만(1뎁스 강제), 새 행 반환. - [x] `db.js`: `deleteComment({id, sabun})` — 본인만, 자식 댓글까지 cascade. - [x] `db.js`: `toggleHeart`(❤️) / `toggleStar`(⭐) — `{on,count}` 반환(AJAX 즉시 반영용). - [x] `db.js`: `setVisibility({id, sabun, isPublic})` — 본인 프로젝트만. - [x] `db.js`: `deleteProject({id, sabun})` — 본인만. - [ ] **`npm run db:init` 재실행해 적용** — 로컬에 `.project-env`(DATABASE_URL)가 없어 미실행. 실제 DB 환경에서 사용자가 실행 필요. ## Phase 1 — 설정 · 권한 · 사이트 카탈로그 ✅ - [x] `auth.js`: 콜백 claims 에서 role 추출(`realm_access.roles` + 클라이언트 role), `roles` + `isAdmin` 반환 → 세션 저장. - [x] `config.js`: 도구 URL 8종 환경변수화(기본값 유지). 관리자 role 이름(`ADMIN_ROLE`, 기본 `admin`) 추가. - [x] `sites.js`: 디자인 도구 목록으로 재구성 — 개발(Coder·Gitea·Kubero·Harbor) + 인프라(OpenEverest·MinIO·Keycloak·LiteLLM, `adminOnly:true`). SSO 플래그·이모지 아이콘·`toolsForUser(isAdmin)` 헬퍼. - [x] `.project-env.example`: `ADMIN_ROLE` + 도구 URL 항목 추가. - 참고: `server.js` 는 아직 `siteGroups`(구 export)를 import → **Phase 4 에서 재작성 전까지 부팅 불가** (의도된 중간 상태). ## Phase 2 — 공통 레이아웃 · CSS · 클라이언트 JS ✅ - [x] `public/style.css`: CSS 변수 시스템(라이트+다크 `html[data-aidev-theme="dark"]`) 전면 도입. 헤더/도구 그리드/내 프로젝트 패널/피드 그리드/카드/배지/버튼/모달/공유모달/댓글 스레드/토스트/플라이어/푸터/로그인/단독페이지/키프레임/반응형 — **단일 파일**. 반응 아이콘 채움↔외곽선 토글 규칙 포함. - [x] `views/_header.ejs`: 스티키 다크 헤더(로고+이스터에그 훅, 검색창, 테마 토글, 관리자 배지, 사용자 메뉴 드롭다운+로그아웃). 데모 관리자 토글 제거→실제 role. FOUC 방지 테마 초기화 인라인 스크립트. - [x] 아이콘: `views/_icons.ejs` 대신 **`src/helpers.js`** 에 `icon(name,{size,fill})` 헬퍼로 구현(Octicon 22종). 더불어 `identicon(seed,size)`(서버 SVG 아바타)·`langColor`·`langFromTags` 도 제공 → Phase 4 에서 res.locals 로 노출. - [x] `public/app.js`(바닐라): 테마 토글·localStorage, 사용자 메뉴, 상세 모달 열기/닫기(Esc·오버레이), 검색·정렬(최신/인기)·즐겨찾기 필터(피드, 클라이언트), 내 프로젝트 필터(전체/공개/비공개), 댓글 답글 타깃·Enter 등록·삭제 확인, 공유 모달·공개범위 토글, 반응 fetch 즉시 반영, 토스트, 이스터에그(로고 흔들기·👍🚀✨·콘솔 아트), body 데이터속성 시작 신호(`data-toast`/`data-egg`/`data-open-modal`). - 참고: 대시보드/모달 마크업은 app.js 의 **DOM 계약(data-* 속성)** 에 맞춰 Phase 3/5 에서 렌더. ## Phase 3 — 대시보드(`/`) ✅ - [x] `views/dashboard.ejs` ① 개발 도구 섹션: role 필터된 카드 그리드(a target=_blank), SSO 배지, 관리자 전용 카드 스타일(보라) + "관리자 도구 표시 중" 배지. - [x] ② 내 프로젝트 섹션: 좌측 액센트 바 패널, 전체/공개/비공개 필터 탭+카운트, "새 프로젝트" 버튼, 카드(가시성 배지·언어 점·❤️/⭐/💬 카운트·공개↔비공개 폼 토글), 새 프로젝트 추가 카드, 빈 상태. - [x] ③ 공유 피드 섹션: 2열 카드 그리드, 인라인 ❤️/⭐ 반응 버튼(채움/외곽선 토글), 작성자 아바타·날짜, 바로가기 링크, 최신/인기 탭 + 즐겨찾기 필터, 검색 빈 상태 요소. - [x] 검색/정렬/필터용 data-* 속성(`data-search`/`data-created`/`data-pop`/`data-starred`/`data-vis`) 부여 → `app.js` 가 클라이언트 처리. - [x] 모의 데이터로 EJS 렌더 검증 통과(헤더 include 포함). 모달/공유모달/상세저장소는 Phase 5. ## Phase 4 — 라우트(`server.js`) ✅ - [x] 공통: res.locals 에 `icon`/`identicon`/`langColor` 노출, `isAjax`/`safeBack` 헬퍼. - [x] `GET /` — `listProjects(me)` → 내 프로젝트/피드 분리, 도구 role 필터. `?open`/`?toast`/`?egg` → body 신호. - [x] `POST /projects` — 태그 정리·lang 추론·visibility 반영 후 toast 리다이렉트. - [x] `POST /projects/:id/heart` · `/star` — 토글. AJAX 면 JSON `{on,count}`, 아니면 Referer 복귀. - [x] `POST /projects/:id/visibility` — 공개/비공개 전환(본인). - [x] `POST /projects/:id/delete` — 본인 프로젝트 삭제. - [x] `POST /projects/:id/comments` — 댓글/대댓글(`parent_id`), LGTM/🚀 egg 신호, `return_to` 복귀. - [x] `POST /comments/:id/delete` — 본인 댓글 삭제. - [x] `GET /projects/:id` — 단독 페이지(비공개는 본인만). `_header` body 신호용 typeof 가드 추가. - [x] 크로스 모듈 import/export 해석 검증 통과(서버 부팅 가능 상태 복구). ## Phase 5 — 상세 모달 · 공유 모달 · 단독 페이지 ✅ - [x] `views/_project_detail.ejs` 신설 — 헤더(제목·가시성·작성자·날짜·언어)·태그·설명·❤️/⭐/바로가기/저장소·소유자 삭제·댓글 스레드(대댓글 들여쓰기·LGTM 스탬프·답글/삭제)·댓글 입력(Enter 등록). 모달/단독 페이지 공용(`modal` 플래그). - [x] 대시보드: `#detailModal` 오버레이 + `#detailStore`(프로젝트별 `