Files
comrelay/docs/project-refactor-plan.md
T
ttipo 6ba148314c refactor: 백엔드를 프로젝트 우선 구조로 이전 (업무·멤버·활동·저장소)
저장소=프로젝트 1:1 구조를 프로젝트 우선 + 프로젝트 1:N 저장소로 이전(2단계).

- Activity: repo FK → project FK, listForProject, /projects/:projectId/activity
- Repo: project FK 추가, RepoMember 폐지(멤버십은 프로젝트), /projects/:projectId/repos 로 이동
- Task: project 종속(seq=프로젝트 내 순번 @Unique[project,seq]), 권한=ProjectMember, /projects/:projectId/tasks
- RepoService: 프로젝트 종속(멤버·업무카운트·캐시 제거), 권한은 ProjectService 위임
- RepoSyncService: Gitea import → "미분류" 시스템 프로젝트로 연결, import-admin 로직 제거
- AiService: 컨텍스트·추천을 프로젝트 기준으로
- 삭제: repo-member 엔티티/멤버 서비스·컨트롤러/repo-cache
- 알림 payload repoId/repoName → projectId/projectName

검증: build/lint 0, 14 tests. 잔여(후속): ProjectResponse 카운트 집계·멤버 활동/알림 배선.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:03:09 +09:00

5.3 KiB

프로젝트 우선 구조 리팩터 계획 (Project-First)

작성 2026-06-20. 현재 저장소(Repo)=프로젝트 1:1 구조를 프로젝트 우선 + 프로젝트 1:N 저장소 로 전환한다. 이 문서가 리팩터의 단일 진실 공급원(SSOT). 단계별로 갱신한다.

1. 목표 구조

Project (이름/설명/공개범위)                ← 작업의 기본 단위(우선)
  ├─ ProjectMember (admin/member, isOwner)  ← 멤버십을 프로젝트로 이동(RepoMember 폐지)
  ├─ Task (project, seq[프로젝트 내 순번], …) ← 업무는 프로젝트 소속
  │     └─ checklist / comment / attachment (기존 그대로, Task 종속)
  ├─ Activity (project, taskSeq?, …)         ← 활동도 프로젝트 단위
  └─ Repo[] (project FK, gitea 필드…)         ← 0~N개 저장소 연동(선택, 코드 연동 전용)
  • 일반 프로젝트 = 연동 저장소 0개. 업무·멤버·활동 정상 사용.
  • 저장소 연동 프로젝트 = 저장소 1~N개 링크. 추가로 Gitea/코드참조.

2. 확정된 설계 결정 (사용자 합의 2026-06-20)

  1. 업무·멤버·활동 = 프로젝트 단위. Task→Repo 종속을 Task→Project 로 이동. RepoMember 폐지 → ProjectMember.
  2. Gitea 자동 가져오기(역방향 동기화) → import 저장소는 "미분류" 기본 프로젝트에 연결. 관리자가 나중에 적절한 프로젝트로 이동.
  3. dev DB 초기화(volume 재생성). 마이그레이션 스크립트 불필요.

3. 추가 설계 결정 (구현자 판단 — 변경 가능)

  • 라우팅: 업무는 프로젝트 직속, 저장소는 형제 부속.
    • /projects · /projects/:projectId (상세=업무목록) · /projects/:projectId/{members,activity,settings}
    • /projects/:projectId/tasks/:seq (업무 상세) · /projects/:projectId/tasks/new
    • /projects/:projectId/repos (연동 저장소 목록·링크) · /projects/:projectId/repos/:repoSlug (저장소 상세/설정)
    • /tasks (내 업무, 프로젝트 횡단 — 경로 유지)
  • 저장소 식별자: slugName 은 Gitea 조직 내 유일이므로 전역 유일 유지(프로젝트 내 유일로 바꾸지 않음). Repo 에 project_id FK만 추가.
  • Task.seq: @Unique(['repo','seq'])@Unique(['project','seq']) (프로젝트 내 순번 #N).
  • 권한: assertMember/assertAdmin(repo.id, …)assertProjectMember/assertProjectAdmin(project.id, …). 저장소 단위 세분 권한은 두지 않음(프로젝트 멤버면 연동 저장소 전체 접근). 향후 옵션.
  • 저장소 자체엔 멤버 없음. 저장소는 순수 Gitea 연동 메타. 멤버십/권한은 소유 프로젝트가 결정.
  • "미분류" 기본 프로젝트: 시스템 예약 프로젝트 1개(부팅 시 보장 생성). Gitea import 대상.

4. 단계별 실행 계획

Phase 1 — 백엔드 데이터 모델 + Project 모듈 (핵심)

  • Project·ProjectMember 엔티티 + ProjectModule/ProjectService/ProjectController(CRUD + 멤버 초대/역할/제거 + 권한 가드)
  • "미분류" 기본 프로젝트 부팅 보장(OnApplicationBootstrap)
  • Repoproject FK 추가. RepoMember 폐지. Repo 생성/수정/삭제가 프로젝트 권한으로 게이트.
  • Taskproject 종속으로 변경(@Unique(['project','seq'])), 권한 체인 프로젝트로 교체, 담당자=프로젝트 멤버.
  • Activityproject 종속으로. (taskSeq 유지)
  • RepoSyncService: import → "미분류" 프로젝트 연결.
  • AiService.suggestChecklist: 프로젝트/저장소 컨텍스트 정합.
  • 컨트롤러 라우트 재배치(/projects/...). 기존 /repos/... 제거 또는 이전.
  • 검증: build/lint/test.

Phase 2 — 프론트 프로젝트 중심 재구성

  • project.store·useProject·types/project.ts
  • 신규 페이지: ProjectListPage(진입점)·ProjectDetailPage(업무목록)·ProjectSettingsPage·ProjectMembersPage·ProjectActivityPage
  • 저장소: 프로젝트 내 "연동 저장소" 섹션 + RepoLinkPage(기존 RepoCreate 재구성)
  • task/member/activity 스토어를 projectId 키로 재설계
  • 라우팅 전면 교체, AppShell 네비(프로젝트/내 업무)
  • 검증: type-check/lint/build.

Phase 3 — 마무리

  • 문서(api-contract.md)·메모리 갱신, 잔여 UI 정리, 런타임 점검.

5. 진행 로그

  • Phase 0: 계획 수립
  • Phase 1-a: Project/ProjectMember 모듈 기반(엔티티·서비스·컨트롤러·"미분류" 부팅) (커밋 2f66cdd)
  • Phase 1-b: 백엔드 이전 — Activity(project FK·listForProject)·Repo(project FK, RepoMember 폐지)·Task(project 종속, seq 프로젝트 내 순번, 권한=ProjectMember) 전환. RepoService 프로젝트 종속(/projects/:projectId/repos, 멤버·업무카운트·캐시 제거). Task/Activity 컨트롤러 /projects/.... RepoSync→"미분류". AiService 프로젝트 컨텍스트. member.service/controller·repo-member·repo-cache 삭제. 검증 build/lint 0·14 tests.
  • Phase 1 잔여(소소, 후속): ProjectResponse 카운트(repoCount/doneCount/totalCount 현재 0 stub) 집계 배선, ProjectMemberService 활동·알림 배선(TODO), TaskService.assigneeCounts→ProjectMemberService taskCount 연결.
  • 다음 = Phase 2 프론트.