---
title: "작업보고 — 프로필 공개 대시보드/기록 스냅샷 기능 + 타인 프로필 폭 수정"
category: "workreport"
document_type: "작업보고"
source_status: "generated"
knowledge_group: "03_history"
priority: "High"
purpose: "프로필에 대시보드/기록을 저장시점 스냅샷(불변)으로 선택 공개하는 기능(PR#32) + 타인 프로필 컨테이너 폭 회귀 수정(PR#33) 전모. profile_publications 테이블·RLS·스냅샷 빌드·읽기전용 렌더·2단 레이아웃."
read_when: ["기능파악","DB·RLS·보안","최신상태복구"]
updated: "2026-06-04"
work_timestamp: "20260604_195220"
context: "달록본레포CC (D:\\dallog\\dallog_git)"
source_of_truth: "https://dallog-tools.hansbridge.co.kr/knowledge/"
---

# 작업보고 — 프로필 공개 대시보드/기록 스냅샷 기능 + 타인 프로필 폭 수정

> 이 Claude Code 컨텍스트(달록본레포CC)에서 수행한 작업 전모. 작업 발생: **기능 구현 2026-06-02**, **폭 회귀 수정 2026-06-03~04**. 두 작업은 같은 기능 영역이라 본 작업보고 1건으로 통합 기록한다(폭 수정은 §6 별항).

## 0. 요약
프로필 페이지를 **피드 / 프로필 기록** 2개 영역으로 분리하고, 사용자가 자신의 대시보드·기록을 **저장 시점 데이터 스냅샷(불변)** 으로 선택 공개하는 기능을 신설했다. 실시간 공개가 아니라 "동결본"이 핵심 — 원본 기록을 수정해도 공개본은 자동 변경되지 않고, 사용자가 직접 "업데이트"할 때만 갱신된다. 데스크탑은 좌(프로필 기록)·우(피드) 2단 병렬, 모바일은 전환형. 기존 대시보드 섹션 컴포넌트를 `readOnly` 옵션으로 재사용해 그래프·카드 충실도를 확보했고, 기존 UI는 무손상이다. 배포 직후 타인 프로필(`/u/:handle`)에서 2단 컨테이너가 찌그러지는 회귀가 발견되어 원인(Layout wide-route 정확매칭 누락)을 규명·수정했다.

## 1. 작업 날짜 / 목적 / 요청 배경
- **날짜**: 2026-06-02(기능), 2026-06-03~04(폭 수정)
- **목적**: 피드 중심이던 프로필에 "내 운동 성과(대시보드/기록)를 선택적으로 남에게 보여주는" 공개 기능 추가.
- **요청 배경**: 사장님 퇴근 직전 지시 — "논스톱으로 완료→검증→커밋→푸시→머지까지, 범위 임의 축소 금지, 기존 UI 구현 절대 훼손 금지". 참고 스크린샷(데스크탑 기준) 제공.
- **핵심 제약**: 공개는 **저장 당시 스냅샷**(실시간 아님). 기존 피드·팔로우·프로필편집·설정·대시보드·기록·UI편집기 구조 보존.

## 2. 구현 내용

### A. DB — 공개 스냅샷 저장소
- 신규 테이블 `profile_publications` (마이그레이션 `migrations/2026-06-02_profile_publications.sql`).
  - 컬럼: `id · user_id · kind(dashboard|history) · section(body|running|strength) · range_type(all|week|month|project|keyword|current|custom) · range_label · range_meta(jsonb) · view_mode · title · snapshot(jsonb) · is_public · created_at · updated_at`.
  - **RLS**: SELECT = `is_public OR 본인`(공개본은 비로그인 포함 누구나, 비공개본은 본인만) / INSERT·UPDATE·DELETE = 본인만. 마스터 우회 없음(`project_master_rls_policy` 일관).
  - `set_updated_at` 트리거 재사용(소셜 스키마 함수).
- **분류**: 🔒 필수보존(DDL). Supabase 사이드바 권장명 `260602_프로필공개스냅샷_필수보존`. 검증 SELECT 2줄만 1회성.

### B. 데이터 레이어 — `src/lib/publications.ts` (신규)
- 타입·상수(RANGE_LABEL·SECTION_LABEL), 범위 필터(`filterByRange`), 스냅샷 빌더(note 등 비공개 필드 제거), CRUD(list·create·updateSnapshot·setPublic·delete·count).
- **공용 단일 경로**: `loadPublishSource()`(본인 현재 데이터 로드) + `buildSnapshotFor(kind, section, source, rangeType, meta)`(범위 필터→스냅샷 동결). 대시보드·기록·프로필관리(업데이트·범위수정)가 모두 이 한 경로를 사용 → 빌드 로직 중복 제거.
- `rebuildPublication(pub)` — 기존 공개본을 현재 데이터로 재동결(업데이트).

### C. 대시보드 섹션 재사용 (`readOnly` 옵션 — 가법적)
- `BodySection`·`StrengthSection`에 `readOnly?: boolean`(기본 false) 추가.
  - true일 때만: 내부 `app_settings` fetch 생략(타 사용자 권한 없음), 프로젝트 필터 pill 숨김(Body), 브리프 캐시 fetch 생략(Strength).
  - 기본 false라 **기존 대시보드 동작 100% 불변**.
- `RunningSection`은 순수(props-only)라 미변경.

### D. 신규 컴포넌트 (`src/components/profile/`)
- `PublishModal` — 공개·업데이트·범위수정 공용. 범위(전체·1주일·1개월·프로젝트·키워드·현재화면·기간)·보기형식·제목 선택, 미리보기 건수, 부모가 `build` 콜백 제공.
- `PublicDashboardCard` — 스냅샷을 readOnly 섹션에 주입해 요약카드·그래프 충실 재현(ChartJS 등록 포함).
- `PublicRecords` — 기록 스냅샷을 보기형식(달력/큰썸네일/주간보기/한줄)으로 읽기전용 렌더. History 마크업·`hist-*` CSS 재사용(선택·편집·디테일 없음).
- `ProfileRecords` — 공개본 목록 + 관리(공개·비공개·범위수정·업데이트·삭제). 본인은 전체+관리, 타인은 RLS가 공개본만 반환 → 읽기전용.

### E. 프로필 페이지 재구성 — `SocialProfilePage.tsx`
- "공개한 기록" 통계 추가. **데스크탑**: 좌(프로필 기록)·우(피드) 2단 병렬, 각 독립 스크롤. **모바일**: 전환형(기본 프로필 기록, F-1 레이아웃 — 1행 프사·닉·소개+설정·편집 / 2행 통계+피드·기록 전환 / 그 아래 활성 보기).
- 기존 설정 인라인 패널 모드는 종전 단일컬럼 레이아웃을 그대로 보존(설정 UX 무손상).

### F. 공개 진입점
- `Dashboard.tsx`·`History.tsx` 우상단에 "프로필에 공개" 버튼 → `loadPublishSource()` 후 `PublishModal`. History는 현재 필터(기간·검색)·보기형식을 `currentMeta`로 캡처해 "현재 화면 기준" 공개 재현 가능.

## 3. 수정/생성 파일
- **신규**: `migrations/2026-06-02_profile_publications.sql` · `src/lib/publications.ts` · `src/components/profile/{PublishModal,PublicDashboardCard,PublicRecords,ProfileRecords}.tsx`
- **수정**: `src/components/dashboard/BodySection.tsx` · `src/components/dashboard/StrengthSection.tsx` · `src/pages/SocialProfilePage.tsx` · `src/pages/Dashboard.tsx` · `src/pages/History.tsx` · `src/index.css`
- **폭 수정(별도 PR)**: `src/components/Layout.tsx`

## 4. 주요 의사결정 / 트레이드오프
- **스냅샷 = 데이터 JSONB 동결** (이미지/HTML 아님). 불변성·보안(공개 범위 외 노출 방지)·재현성 동시 충족. 원본 수정과 분리.
- **기존 섹션 재사용(readOnly 가법 옵션)** vs 전용 렌더 신규 작성 → **재사용 채택**. 그래프/카드 시각 충실도 + 코드 최소화 + 기존 UI 무손상(기본값 보존). "기존 구현 보호 원칙" 부합.
- **빌드 로직 중앙화**(`loadPublishSource`+`buildSnapshotFor`) — 대시보드·기록·프로필관리 단일 경로. DRY + 업데이트/범위수정 자기완결.
- **note 등 비공개 필드 스냅샷 제외** — 프라이버시(요구 G). 보기 렌더도 디테일 모달 미제공이라 메모 비노출.
- **RLS 본인쓰기·공개읽기** — 마스터 우회 없음(메모리 일관).

## 5. 검증 / 결과
- `npm run build`(tsc + vite) **성공**. 머지 후 main 재빌드도 성공.
- git: 수정 파일만 명시 add(`git add .`/`-A` 미사용), 무관 변경(wrangler.toml·.claude/settings.json) 제외.
- **PR #32** → main 머지(`8847da1` → 머지 `4532bc7`).
- ⚠️ 배포 전 **Supabase에 마이그레이션 수동 적용 필요**. 미적용 시 공개 기능만 무동작(기존 기능 영향 없음). → 사장님이 적용·검증 완료(테이블 1건 + RLS 4정책 확인).
- 라이브 확인: 전체공개 프로필에서 공개 카드·바로가기·관리(비공개/업데이트/범위수정/삭제) 정상. 타인 접근 경로 `/u/:handle`(예 `/u/hans`) 확인.

## 6. 폭 회귀 — 문제 / 원인 / 해결 (별항, PR #33)
- **문제**: 배포 후 타인 프로필(`/u/hans`)에서 "프로필 기록+피드" 2단 컨테이너가 본인(`/profile`)보다 훨씬 좁게(~542px) 찌그러짐.
- **원인 규명**(추측 금지 — Playwright로 양 도메인 실측): 스냅샷·CSS·컴포넌트는 본인/타인 동일, 차이는 바깥 `<main>` 폭. `Layout.tsx`의 `WIDE_ROUTES`가 **정확 경로 매칭**(`includes(pathname)`)이라 동적 경로 `/u/:handle`이 wide 목록에 못 들어감 → 좁은 main에 1180px 2단이 압축.
- **해결**: `isWide`에 `|| location.pathname.startsWith('/u/')` 1줄 추가 → 타인 프로필도 wide main. 본인 프로필과 동일 폭. 기존 라우트·UI 무영향.
- **결과**: `npm run build` 성공, **PR #33** → main 머지(`4bebb6c` → 머지 `4325ada`).

## 7. 미해결 / 후속 작업
- (운영) 다른 OAuth 계정·시크릿창에서 공개/비공개 가시성 최종 교차검증 권장.
- (배포) CF Pages 자동배포 webhook 누락 시 Deploy Hook 강제 재배포(연속 머지 주의).
- (UX 후보) "공개한 기록" 통계 클릭 시 프로필 기록 영역으로 스크롤/포커스(현재 데스크탑 no-op).
- (문서) 본 기능 UI시각화 설명서는 MCP 스크린샷 기반으로 추후 별도 발행 가능(현재 기능설명서까지 발행).
