Skip to content

업데이트 노트

Canvas 프로젝트의 날짜별 커밋·PR·파일 변경 이력입니다. 변경 범위, 설계 배경, 관련 파일 경로를 일자별로 확인할 수 있습니다.

프로젝트 개요 · GitHub 저장소 · 배포 문서


전체 요약

문서 범위

  • 데이터 출처: main 브랜치 git log 및 PR 머지 커밋
  • 기간: 2026-05-18 ~ 2026-06-01 (활동 8일, 커밋 22건)
  • PR: #15 ~ #51 (커밋 메시지에 기록된 머지 PR 17건)
  • 저장소: github.com/ToKyun02/canvas

누적 변경량 (일자별 합산)

날짜커밋추가 줄삭제 줄변경 파일(고유)주요 작업
05-18515,6253,06099모노레포 부트스트랩 · Docker · CI 레이블
05-1934003513TanStack Router · 협업 템플릿
05-2031,7453831Fabric 캔버스 · Command · 카메라
05-2121,6656427텍스트 노드 · 속성 패널
05-2251,45732734Store-first 리팩터 · 레이블 · persist · 휠 UX
05-2713,7761029VitePress 문서 · Firebase CI
05-28130622926dead code 제거 · 문서 동기화
06-0122,23459168Google OAuth · Prisma · lint-staged

개발 단계 개요

PR 맵 (번호 → 주제)

PR날짜제목Δ 줄 (대략)핵심 경로
#1505-18Docker Compose+1,823 / −2,041docker-compose*.yml, Dockerfile.dev
#1705-19pnpm 버전 고정+10 / −20apps/*/Dockerfile.dev
#1905-19PR 템플릿+13.github/PULL_REQUEST_TEMPLATE.md
#2105-19TanStack Router+377 / −15apps/web/src/routes/
#2205-20캔버스 초기화+1,138 / −21features/canvas/, /canvas 라우트
#2305-20Command & Shortcut+364 / −11stores/commands/
#2705-20카메라+243 / −6useCanvasCamera, zoomAtPoint
#2905-21텍스트 노드+1,657 / −64stores/nodes/, Sidebar
#3305-21Backspace 삭제+8stores/commands/index.ts
#3405-22노드 생성 버그+674 / −243useCanvasNodes, nodesSlice
#3905-22default fill+2 / −2stores/nodes/text/
#4005-22레이블 오버레이+482 / −25features/canvas/labels/
#4205-22상태 저장+135 / −17persistence/appStorage.ts
#4305-22휠 UX+164 / −40handleViewportWheel.ts
#4505-27문서·호스팅+3,776 / −10apps/docs/, hosting-docs.yml
#4705-28정리+306 / −229docs + web 정리
#4906-01OIDC+2,007 / −582apps/api/src/auth/, Prisma
#5106-01lint-staged+227 / −9.husky/, lint-staged.config.mjs

2026-06-01 — 인증 · 코드 품질 자동화

개발 초점: Google OAuth 기반 사용자 인증 도입, 커밋 시점 ESLint·Prettier 자동 적용.

지표
커밋2 · PR #49, #51
변경량+2,234 / −591 · 66개 경로

#49 OIDC / Google 로그인

변경 배경
캔버스 데이터는 클라이언트 localStorage에만 저장되고 있었습니다. 사용자 식별과 이후 서버 연동을 위해 인증 API 최소 구현(OAuth + JWT)이 추가되었습니다.

구현 요약

레이어추가·변경설명
DBprisma/schema.prisma, 2개 migrationUser 모델 — email, provider/providerId, avatarUrl
Authauth.module.ts, strategies, guardGoogle OAuth 2.0 → JWT 발급
APIauth.controller.ts, users.controller.ts/auth/google, callback, POST /auth/logout, GET /users/me
Webroutes/login/, routes/index.tsx로그인 진입·홈 연동
Infradocker-compose.dev.ymlapi 서비스에 GOOGLE_*, JWT_SECRET 주입
Docs아키텍처·env·docker 문서 15+ 페이지인증 반영

인증 플로우

핵심 코드 — 신규/기존 사용자 upsert 후 JWT

typescript
// apps/api/src/auth/auth.service.ts
let user = await this.prisma.user.findUnique({
  where: { providerId: googleUser.providerId },
});
if (!user) {
  user = await this.prisma.user.create({
    data: {
      /* email, name, avatarUrl, provider */
    },
  });
}
return {
  accessToken: this.jwtService.sign({ sub: user.id, email: user.email }),
  user,
};

쿠키·CORS

  • main.ts: cookieParser(), enableCors({ origin: ['http://localhost:3001'], credentials: true })
  • Callback에서 res.cookie('access_token', …, { httpOnly: true }) 후 web으로 redirect

스키마 (Prisma)

prisma
model User {
  id         Int      @id @default(autoincrement())
  email      String   @unique
  name       String?
  avatarUrl  String?
  provider   String
  providerId String   @unique
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt
}

함께 반영된 변경

  • shadcn UI 컴포넌트 포맷 정리 (button, sheet, sidebar 등)
  • apps/docs 내 OAuth·DB 관련 문서 갱신

#51 pre-commit lint & format

변경 배경
web / api / packages가 패키지별 ESLint 설정을 사용하므로, 변경된 경로에 해당하는 패키지만 검사하도록 lint-staged를 구성했습니다.

파일역할
.husky/pre-commitpnpm exec lint-staged 실행
lint-staged.config.mjs경로 prefix별 eslint --fix + 전역 prettier --write

lint-staged 규칙

  • *.{ts,tsx,md} → Prettier
  • apps/web/**pnpm --filter web exec eslint --fix
  • apps/api/**pnpm --filter api exec eslint --fix
  • packages/api/**pnpm --filter @repo/api exec eslint --fix

2026-06-01 (이후) — 워크스페이스 · 노드 서버 저장

개발 초점: 인증(#49) 이후 캔버스 문서를 PostgreSQL에 영속화하고, URL·접근 제어를 워크스페이스 단위로 정리.

INFO

이 섹션은 main에 아직 머지되지 않은 작업 기준입니다.

배경

#42에서 nodes/nodeOrder를 localStorage document 그룹에 저장했으나, OAuth(#49) 이후 사용자·워크스페이스 단위 서버 저장으로 이전했습니다. localStorage에는 UI 설정(isPropertiesSidebarOpen, isVisibleNodeLabels)만 남깁니다.

DB · API

모델 / 엔드포인트설명
Workspace, WorkspaceMember, NodePrisma 스키마 + migration
GET/POST /workspaces목록·생성
GET /workspaces/:id상세 (멤버십 확인)
GET/PUT /workspaces/:id/nodes문서 스냅샷 load/save (Node.data JSON)

WorkspacesService.syncNodes: transaction으로 deleteMany + upsert. 비멤버 → 403, 미존재 → 404.

Web

경로역할
features/workspace/API 클라이언트, WorkspaceHub, useWorkspaceDocument
routes/canvas/index.tsx워크스페이스 목록·생성 (/canvas)
routes/canvas/$workspaceId.tsx에디터 + beforeLoad 인증·권한 검사
useCanvasHydration.ts서버 로드 후 Fabric 복원 (nodesLoaded + workspaceId)
appStorage.tsdocument 그룹 제거 — UI 설정만 persist

저장 흐름

Dev tooling

  • package.json: docker:prisma:studio — Prisma 7 호환 (--browser none, --hostname 제거)
  • apps/docs: 아키텍처·가이드·온보딩 문서 전반 갱신

2026-05-28 — 리팩터링 · 문서 동기화

개발 초점: 뷰포트·영속성 관련 미사용 코드 제거, 기술 문서를 현재 코드 동작과 일치시킴.

지표
커밋1 · PR #47
변경량+306 / −229 · 26개 경로

제거·단순화 (web)

경로내용
handleViewportWheel.ts중복 분기 56줄 삭제 — 단일 책임 유지
useCanvasViewportWheel.ts훅 내부 보일러플레이트 축소
appStorage.ts미사용 persist 키·헬퍼 제거
assignRef.ts미사용 유틸 파일 삭제
hydrateCanvas.ts, useFabricCanvas.ts불필요 의존·dead import 정리
routes/canvas/index.tsx사용하지 않는 UI/상태 연결 제거

문서 갱신 (docs)

문서갱신 포인트
architecture/node-system.md+133줄 — NodeDefinition·Fabric 동기화 상세
architecture/frontend.md라우트·feature 폴더 구조
guide/canvas-usage.md, adding-nodes.md실제 단축키·도구 흐름
reference/commands.md, env.md커맨드 ID·환경 변수 목록

참고

이 PR은 기능 코드 정리와 함께 아키텍처·가이드 문서를 갱신하여, 문서만으로도 구현 상태를 추적할 수 있도록 맞춘 변경입니다.


2026-05-27 — 기술 문서 사이트 · CI 배포

개발 초점: VitePress 기반 문서 사이트 신설, main 브랜치 푸시 시 Firebase Hosting 자동 배포.

지표
커밋1 · PR #45
변경량+3,776 / −10 · 29개 경로

신규 apps/docs 구조

apps/docs/
├── .vitepress/config.ts      # nav, sidebar, mermaid, lastUpdated
├── index.md                  # 홈 Hero
├── onboarding/   (4페이지)   # welcome, setup, first-week, codebase-tour
├── guide/        (5페이지)   # getting-started, docker, canvas-usage, …
├── architecture/ (6페이지)   # overview, monorepo, frontend, node-system, …
└── reference/    (3페이지)   # commands, env, scripts

CI — hosting-docs.yml

트리거동작
pushmain + apps/docs/** 변경VitePress build → Firebase Hosting deploy
workflow_dispatch수동 재배포

2026-05-22 — 에디터 안정화 · UX 확장 (5 PR)

개발 초점: 텍스트 노드 생성 경로 재설계, DOM 레이블·localStorage 영속성·뷰포트 휠 처리 개선.


#34 텍스트 노드 생성 버그 수정 · 아키텍처 전환

| 지표 | +674 / −243 · 22 files |

이전 동작
placement.ts에 배치·Fabric 생성·선택 로직이 혼재하여, 텍스트 노드 생성 시 store와 캔버스 상태가 어긋나거나 중복 객체가 남는 문제가 있었습니다.

변경 후

신규/강화책임
useCanvasNodes.ts (129줄 신규)store nodes ↔ Fabric 객체 생성·갱신·삭제
nodesSlice.tsaddNode, updateNode, removeNode — 데이터 진실 공급원
canvasSync.ts편집 중(isCanvasInteracting) 동기화 스킵
textboxScaling.ts (144줄)Fabric Textbox 리사이즈 시 font/width 보정
stores/nodes/fabric.tsapplyBaseNodeFields, readBaseNodeFields 공통화
placement.ts223줄 → 대폭 축소 — 배치만 담당

적용된 설계: Fabric 객체는 View, Zustand nodes는 Model로 분리. 이후 레이블·persist 구현의 기준이 됨.

노드 시스템 · 상태 관리


#39 text node default fill

  • stores/nodes/text/index.ts — 기본 fill/color 2줄 조정
  • 다크/라이트 테마에서 텍스트 가독성 개선

#40 노드 레이블 오버레이

| 지표 | +482 / −25 · 10 files |

설계 선택
Fabric 기본 텍스트만으로는 줌·편집 모드와 레이블 UI가 충돌하기 쉽습니다. HTML 오버레이를 사용해 노드 이름 표시·드래그 이동을 DOM 레이어에서 처리합니다.

파일역할
NodeLabelsOverlay.tsxnodeOrder 순회, zoom/pan에 맞춰 screen 좌표 계산
NodeLabel.tsx (174줄)인라인 편집, 선택 하이라이트
coords.tsscene ↔ screen 변환
moveNodeOnCanvas.ts레이블 드래그 → store position 갱신

동작

  • canvas.on('object:moving') 등 Fabric 이벤트로 레이블 위치 재계산
  • isVisibleNodeLabels 플래그로 표시 on/off (이후 persist 대상)

#42 캔버스 상태 저장

| 지표 | +135 / −17 · 10 files |

persist 설계 (당시 #42 기준)

typescript
// apps/web/src/stores/persistence/appStorage.ts (2026-05-22 머지 시점)
export const PERSIST_GROUPS = {
  document: ['nodes', 'nodeOrder'] as const,
  preferences: ['isPropertiesSidebarOpen', 'isVisibleNodeLabels'] as const,
};
export const APP_STORAGE_KEY = 'canvas-app-v1';
export const APP_STORAGE_VERSION = 2;

이후 변경

2026-06-01 이후 워크스페이스 작업에서 document 그룹(nodes, nodeOrder)은 제거되었고, 노드는 PostgreSQL Node 테이블에 저장됩니다. 현재 persist 대상은 preferences만입니다. → 워크스페이스 · 노드 서버 저장

구성요소역할 (#42 시점)
zustand/middleware persistpartializeAppState로 필요 필드만 저장
useCanvasHydration.tsFabric 캔버스 준비 후 hydrateCanvas 1회 실행
hydrateCanvas.tsstore 노드 → Fabric 객체 재생성

저장 범위에서 제외: zoom, position — 세션 시작 시 뷰포트는 초기화됨 (향후 사용자 설정으로 확장 가능).


#43 zoom·pan vs 브라우저 제스처

| 지표 | +164 / −40 · useCanvasCamera 39줄 삭제 후 책임 분리 |

이전 동작
캔버스 영역에서 휠 입력 시 브라우저 페이지 스크롤·시스템 제스처가 함께 동작하고, 입력 필드 포커스 중에도 줌이 적용되는 문제.

변경 후

파일내용
useCanvasViewportWheel.tsnon-passive wheel 리스너, preventDefault
handleViewportWheel.tsmod+wheelzoomAtPoint, shift+wheel → 수평 pan, 그 외 수직 pan
hasEditableFocus()input/textarea/contenteditable 포커스 시 줌·pan 무시
isCanvasInteracting()Fabric 텍스트 편집 중에는 뷰포트 변경 차단
typescript
// handleViewportWheel.ts (요지)
if (options?.canvas && isCanvasInteracting(options.canvas)) return;
if (e.metaKey || e.ctrlKey) {
  setViewport(zoomAtPoint(zoom, position, anchor, factor));
} else if (e.shiftKey) {
  panBy({ x: -e.deltaY, y: 0 });
} else {
  panBy({ x: 0, y: -e.deltaY });
}

2026-05-21 — 텍스트 노드 · 속성 패널

| 지표 | 2 커밋 · +1,665 / −64 |

#29 텍스트 노드 · Properties Sidebar

| 지표 | +1,657 / −64 · 27 files |

NodeDefinition 패턴 도입

typescript
// stores/nodes/text/definition.ts (요지)
export const textNodeDefinition = {
  type: 'text',
  tool: 'text',
  shortcut: 't',
  createState,
  createFabricObject,
  stateFromFabricObject,
  applyStateToFabricObject,
};
영역주요 추가
stores/nodes/registry.ts, base.ts, types.ts, text index.ts
drawing/placement.ts클릭·드래그 배치 (260줄)
hooks/useDrawingTools, useCanvasSelection (104줄)
UIpropertiesSidebar/, shadcn sidebar.tsx (640줄), sheet, tooltip
StoreeditorSlice, selection·command 연동

UX

  • 단축키 T → 텍스트 도구 → 캔버스 클릭 배치
  • 우측 Properties Sidebar에서 fontSize, color, fill 편집

노드 추가하기


#33 Backspace 삭제

  • stores/commands/index.tsselection.deleteSelection command 8줄
  • shortcut: delete / backspace — 선택된 selectedIds 일괄 removeNode

2026-05-20 — 캔버스 에디터 핵심 기능

| 지표 | 3 PR · +1,745 / −38 |

#22 캔버스 초기화

| 지표 | +1,138 / −21 · 17 files |

추가설명
fabric 의존성Canvas 2D 렌더링 엔진
routes/canvas//canvas 전용 레이아웃·페이지
components/canvas/index.tsxFabric 래퍼 + ref
useFabricCanvas.tsmount/dispose, resize observer
themeProvider.tsx, theme-mode-togglelight/dark/system
dev/registry.ts, global-injection.ts개발 중 store/canvas 디버그 노출
shadcn dropdown-menu향후 메뉴·도구 UI 기반

#23 Command & Shortcut

| 지표 | +364 / −11 · 14 files |

Command 스토어 구조

stores/commands/
├── index.ts          # COMMANDS[], executeCommand, useAppStore
├── types.ts          # Command, AppState
└── slices/
    ├── canvasSlice.ts
    ├── historySlice.ts   # undo/redo 스택 (도구 변경 추적)
    └── selectionSlice.ts
Command 그룹예시 ID단축키
toolstool.move, tool.textv, t
historyhistory.undo, history.redomod+z, mod+shift+z
selectionselection.selectAll, selection.deleteSelectionmod+a, delete
  • useGlobalShortcuts.tskeyCombo.tsmod/shift 정규화
  • 확장 포인트: NODE_DEFINITIONS를 map해 도구 command 자동 등록

단축키 & 커맨드


#27 카메라 (줌·팬)

| 지표 | +243 / −6 |

파일역할
zoomAtPoint.ts커서 앵커 기준 줌 — Figma/Excalidraw 패턴
useCanvasCamera.tsFabric viewport ↔ store zoom/position
zoomLevel.tsx, position.tsxHUD UI
canvasSlice.tssetViewport, panBy, resetViewport

2026-05-19 — 앱 라우팅 · 협업 규칙

| 지표 | 3 PR · +400 / −35 |

#21 TanStack Router

  • Next.js 잔재 제거 후 Vite SPA + 파일 기반 라우팅
  • routes/__root.tsx, routes/index.tsx, routeTree.gen.ts (codegen)
  • tsr.config.json, Vite 플러그인 연동

#19 PR 템플릿

  • 이슈 번호 close #N, Description, as-is / to-be 테스트 증적 섹션

#17 Docker 안정화

  • Dockerfile.dev에 pnpm 버전 핀
  • README 중복 제거, 실행 명령 단일화

2026-05-18 — 프로젝트 초기 설정 · 개발 환경

| 지표 | 5 커밋 · +15,625 / −3,060 (pnpm-lock·스캐폴딩 포함) |

wip + init — Turborepo 모노레포 골격

커밋Δ내용
434c294 wip+9,490 · 84 filesturbo, eslint-config, jest-config, packages/api DTO, Nest links 샘플, Next.js web
0df241f init+4,244 / −1,019 · 43 filesNext → Vite 전환, packages/ui 제거, shadcn button, vite.json tsconfig

최종 앱 구조

  • apps/api — NestJS 11
  • apps/web — React + Vite (포트 3001)
  • packages/api — 공유 DTO (@repo/api)

레이블 워크플로 (커밋 9e1e928)

  • .github/workflows/label.ymlworkflow_dispatch로 레이블 일괄 생성
  • Priority High/Medium/Low, Type: Feature/Bug/Doc/Refactor/UX 등 9종

#15 Docker Compose

yaml
# docker-compose.dev.yml (요지)
services:
  web: # :3001, hot reload volumes
  api: # :3000, DATABASE_URL, OAuth env
  db: # PostgreSQL 17, healthcheck
산출물설명
docker-compose.yml / .dev.ymlprod vs dev 오버레이
apps/*/Dockerfile.devmonorepo context build
package.json scriptsdocker:dev, docker:down

Favicon (f813152)

  • apps/web/public/Favicon.svg — 브랜드 아이덴티티

알려진 제한 사항 · 향후 작업

항목현재 상태메모
캔버스 ↔ API 동기화구현됨문서 전체 PUT debounce. 노드 단위 PATCH·실시간 협업은 향후
워크스페이스구현됨목록·생성·/canvas/:id, 멤버십 기반 접근 제어
undo/redohistory 스택 존재노드 편집 히스토리는 제한적
뷰포트 persist미저장#42에서 의도적 제외
E2E 테스트Jest 단위 위주캔버스 시나리오 E2E는 확장 여지

권장 읽기 순서

  1. 프로젝트 개요 — 목적·기술 스택
  2. 2026-05-20 ~ 05-22 — 에디터 관련 PR (#22–#43)
  3. 2026-05-27 — 문서 사이트·CI (#45)
  4. 2026-06-01 — 인증·품질 도구 (#49, #51)
  5. 2026-06-01 (이후)워크스페이스 · 노드 서버 저장
  6. 아키텍처 개요 — 설계 원칙

관련 링크

Canvas — 시각적 프로토타입 에디터