Skip to content

feat: 내 워크스페이스 목록·생성 Supabase 백엔드 연동 (#34) - #36

Merged
wjswlgh96 merged 2 commits into
developfrom
feat/#34/workspace-backend
Jul 9, 2026
Merged

feat: 내 워크스페이스 목록·생성 Supabase 백엔드 연동 (#34)#36
wjswlgh96 merged 2 commits into
developfrom
feat/#34/workspace-backend

Conversation

@wjswlgh96

@wjswlgh96 wjswlgh96 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Pull Request

작업 내용

  • 내 워크스페이스 목록·생성을 mock → Supabase 실데이터로 전환 (지호님 담당 도메인 첫 백엔드 연동, 다른 도메인이 따라갈 기준 구현)
  • get_my_workspaces / create_workspace RPC 함수 생성 (마이그레이션)
  • 데이터 페칭 컨벤션 확립: 조회(GET) = tanstack-query, 쓰기 = server action — 문서화 포함

작업 결과

  • /workspaces — DB 시드 3개 워크스페이스를 실데이터로 렌더 (멤버 수·업무 수·진행률 전부 DB 집계값)
  • /workspaces/new — 템플릿 선택 → Dialog 입력 → DB에 실제 생성 (owner 멤버십 + purpose별 기본 모듈 + invite_code까지 트랜잭션 생성) → 목록에 즉시 반영
  • 로딩/에러(재시도 버튼) 상태 처리 추가
  • E2E 검증 완료: 생성한 워크스페이스가 DB에 정확히 기록되는 것 확인 (owner 1행 · side_project 모듈 7개 · invite_code 16자)

설계 포인트

성능 (N+1 방지)

  • get_my_workspaces는 lateral 조인으로 멤버 수·업무 집계를 워크스페이스 수와 무관하게 쿼리 1회로 반환

동시성

  • create_workspace는 plpgsql 단일 트랜잭션 (workspaces + owner 멤버십 + 모듈 생성이 전부 성공하거나 전부 롤백)
  • invite_code(unique) 충돌 시 재시도 루프 — 랜덤 hex 16자 재생성

검증 (이중 방어)

  • 클라이언트: react-hook-form + zod (기존)
  • 서버액션: 같은 zod 스키마로 재검증 + purpose enum 검증 — 스키마는 entities/workspace/model이 소유하고 feature가 공유

임시 유저 규칙

  • auth 연동 전이므로 shared/config/dev-user.tsDEV_USER_ID(시드 테스트유저1) 단일 상수만 사용 — auth 연동 시 이 파일만 교체

변경 사항

Added

  • supabase/migrations/20260709054147_create_workspace_rpcs.sql — RPC 2종 (원격 적용 완료, 버전 동기화)
  • supabase/migrations/20260709065317_create_workspace_purpose_guard.sql — purpose 모듈 매핑 누락 시 즉시 실패 가드 (리뷰 반영)
  • shared/ui/sonner.tsx — shadcn(sonner) 토스트, 루트 레이아웃에 <Toaster /> 마운트 (리뷰 반영)
  • shared/api/supabase/ — browser/server 클라이언트 (@supabase/ssr) + env 검증. 배럴로 묶지 않음 (server 클라이언트의 next/headers가 클라이언트 번들에 섞이면 빌드가 깨짐 — 컨벤션 문서에 명시)
  • shared/config/dev-user.tsDEV_USER_ID 상수
  • app/providers.tsx — tanstack-query QueryClientProvider (루트 레이아웃에 연결)
  • entities/workspaceuse-my-workspaces 쿼리 훅, create-workspace.schema(zod), purpose.mapper(DB snake ↔ 프론트 hyphen, snake 통일 리팩터링 시 제거 예정), RPC 타입(MyWorkspaceRpcRow 등)
  • .env.example — 환경 변수 양식 (.env.local은 팀 Discord 참고)

Changed

  • entities/workspace/api/get-my-workspaces.ts — mock → RPC 호출
  • entities/workspace/api/create-workspace.ts — mock → server action ('use server') + 서버 재검증 + 에러 응답 일반화(상세는 서버 로그에만)
  • views/workspaces/WorkspacesPage — RSC → 클라이언트 컴포넌트 + useQuery (로딩/에러 상태 포함)
  • features/create-workspace — 생성 성공 시 목록 쿼리 캐시 무효화, 실패 시 sonner 토스트 안내(리뷰 반영), 폼 스키마는 entity 재노출로 전환
  • docs/conventions/supabase-convention.md — "5. 데이터 페칭 규칙" 섹션 추가
  • shared/model/database.types.ts — RPC 타입 + 복합 FK 관계 반영 (gen:types 재생성)

Fixed

  • (없음)

실행화면

  • 내 워크스페이스 목록 (DB 실데이터)
workspace-backend-list
  • 생성 → 목록 반영
workspace-backend-create

테스트

  • 로컬 실행 확인 (목록 조회 + 생성 E2E — 브라우저에서 실데이터 확인)
  • 주요 시나리오 확인 (시드 3개 렌더 / 생성 → DB 기록 → 목록 반영 / 이름 미입력 검증 / 에러 시 재시도 UI)
  • 영향 범위 확인 (workspace 도메인 한정 — 다른 도메인 mock은 그대로 동작, lint/typecheck 통과)

리뷰 체크리스트

  • PR base branch가 올바릅니다. (feature/* -> develop, 배포 시 develop 또는 release/* -> main)
  • 브랜치명이 Type/#issue-number/description 형식을 따릅니다.
  • 커밋 메시지가 컨벤션을 따릅니다.
  • 불필요한 console.log, 주석, 임시 코드를 제거했습니다.
  • 타입 에러와 린트 에러를 확인했습니다.
  • CodeRabbit 1차 리뷰를 확인했습니다.
  • CodeRabbit 리뷰 반영 후 Discord에 공유했습니다.
  • 최소 1명 이상의 approve 후 merge합니다.

리뷰 요청사항

  • 다른 도메인 연동 시 이 PR을 기준 구현으로 참고해주세요: 조회는 use-my-workspaces.ts, 쓰기는 create-workspace.ts(서버액션) 패턴. 규칙 전체는 docs/conventions/supabase-convention.md 5절
  • 로컬 실행에는 .env.local 필요합니다 (.env.example 양식, 값은 Discord 공유)
  • purpose.mapper(snake↔hyphen)는 임시입니다 — 프론트 purpose 표기 snake_case 통일 리팩터링 때 제거 예정
  • RPC의 p_user_id 파라미터는 auth 연동 시 auth.uid()로 일괄 교체 예정

관련 이슈

Closes #34

Summary by CodeRabbit

Summary by CodeRabbit

  • 새 기능

    • 워크스페이스 목록을 실제 데이터 기반으로 불러오도록 전환했으며, 로딩/에러 상태와 재시도 버튼을 추가했습니다.
    • 워크스페이스 생성 후 목록을 즉시 갱신하고 생성 완료 시 해당 흐름으로 이동합니다.
    • 테마에 맞춘 토스트 알림 UI를 적용했습니다.
  • 버그 수정

    • 워크스페이스 생성/조회가 실제 Supabase RPC와 연동되도록 개선했습니다.
  • 문서

    • Supabase 연동 규칙 및 데이터 페칭/서버액션 기준을 정리했습니다.
  • Chores

    • Supabase 환경 변수 템플릿/가이드 및 로깅 제외 규칙을 보완했습니다.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Supabase RPC와 환경/타입 유틸이 추가되고, 워크스페이스 목록 조회는 React Query 훅으로, 생성은 server action으로 전환됩니다. 관련 UI는 클라이언트 컴포넌트로 바뀌고, 앱 전역 Provider/Toaster와 문서 규칙이 함께 갱신됩니다.

Changes

워크스페이스 백엔드 연동

Layer / File(s) Summary
Supabase 환경/클라이언트 설정
.env.example, .gitignore, src/shared/api/supabase/env.ts, src/shared/api/supabase/client.ts, src/shared/api/supabase/server.ts, src/shared/model/supabase.types.ts, src/shared/config/dev-user.ts
Supabase env 변수 검증, 브라우저/서버 클라이언트 생성 유틸, Database 타입 재-export, 개발용 임시 유저 ID를 추가.
RPC 마이그레이션 및 DB 타입
supabase/migrations/20260709054147_create_workspace_rpcs.sql, supabase/migrations/20260709065317_create_workspace_purpose_guard.sql, src/shared/model/database.types.ts
get_my_workspaces, create_workspace SQL 함수를 정의하고 관련 함수 시그니처/외래키 관계를 DB 타입에 반영.
스키마/매퍼/타입 export
src/entities/workspace/model/create-workspace.schema.ts, src/entities/workspace/model/purpose.mapper.ts, src/entities/workspace/model/workspace.db.types.ts, src/entities/workspace/index.ts, src/features/create-workspace/model/schema.ts
createWorkspace 입력 Zod 스키마, DB↔UI purpose 매퍼, RPC 인자/반환 타입을 정의하고 entities/workspace에서 재-export.
createWorkspace 서버액션 전환
src/entities/workspace/api/create-workspace.ts
mock id 생성 로직을 서버액션 기반 create_workspace RPC 호출로 교체하고 입력을 서버에서 재검증.
목록 조회 RPC 및 훅
src/entities/workspace/api/get-my-workspaces.ts, src/entities/workspace/api/use-my-workspaces.ts
getMyWorkspaces를 get_my_workspaces RPC 호출로 전환하고 useMyWorkspaces React Query 훅을 추가.
앱 셸과 워크스페이스 UI 연결
package.json, src/app/providers.tsx, src/app/layout.tsx, src/shared/ui/sonner.tsx, src/views/workspaces/ui/WorkspacesPage.tsx, src/features/create-workspace/ui/CreateWorkspaceDialog.tsx
QueryClient/Toaster 셸을 추가하고, 목록 페이지를 클라이언트 기반 상태 렌더링으로 바꾸며, 생성 성공 시 캐시를 무효화한다.
데이터 페칭 컨벤션 문서화
docs/conventions/supabase-convention.md
GET/쓰기 경로 규칙을 명시하는 데이터 페칭 규칙 섹션을 추가하고 이후 섹션 번호를 조정.

Estimated code review effort: 4 (Complex) | ~55 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WorkspacesPage as WorkspacesPage
  participant useMyWorkspaces as useMyWorkspaces
  participant SupabaseBrowserClient as SupabaseBrowserClient
  participant get_my_workspaces as get_my_workspaces

  WorkspacesPage->>useMyWorkspaces: 렌더
  useMyWorkspaces->>SupabaseBrowserClient: rpc('get_my_workspaces', p_user_id)
  SupabaseBrowserClient->>get_my_workspaces: 실행
  get_my_workspaces-->>SupabaseBrowserClient: 목록 반환
  SupabaseBrowserClient-->>useMyWorkspaces: data/error
  useMyWorkspaces-->>WorkspacesPage: workspaces / 상태
Loading
sequenceDiagram
  participant CreateWorkspaceDialog as CreateWorkspaceDialog
  participant createWorkspace as createWorkspace
  participant SupabaseServerClient as SupabaseServerClient
  participant create_workspace as create_workspace
  participant queryClient as queryClient

  CreateWorkspaceDialog->>createWorkspace: submit(name, purpose, description)
  createWorkspace->>createWorkspace: safeParse 입력 검증
  createWorkspace->>SupabaseServerClient: rpc('create_workspace', args)
  SupabaseServerClient->>create_workspace: 실행
  create_workspace-->>SupabaseServerClient: workspace id
  SupabaseServerClient-->>createWorkspace: { id }
  createWorkspace-->>CreateWorkspaceDialog: 성공
  CreateWorkspaceDialog->>queryClient: invalidateQueries(['workspaces'])
  CreateWorkspaceDialog->>CreateWorkspaceDialog: /workspaces 이동
Loading

Possibly related PRs

  • TeampleRun/syncly#10: mock getMyWorkspaces 구현과 /workspaces 페이지 UI 연동을 대체하는 동일 도메인 변경.
  • TeampleRun/syncly#16: workspace purpose 타입과 매핑 흐름을 공유하는 코드 레벨 변경.
  • TeampleRun/syncly#20: 동일 create-workspace.ts의 mock 생성 흐름을 다루는 선행 변경.

Suggested reviewers: Kwon812, 0011810, JiWoongE

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed RPC 추가, useQuery 전환, server action 전환, 문서화, Supabase 클라이언트/타입 정비가 모두 목적과 일치합니다.
Out of Scope Changes check ✅ Passed 워크스페이스 연동과 이를 뒷받침하는 UI·문서·인프라 변경만 보여서 명확한 범위 밖 변경은 보이지 않습니다.
Title check ✅ Passed 제목이 핵심 변경인 워크스페이스 목록/생성의 Supabase 백엔드 연동을 간결하게 요약합니다.
Description check ✅ Passed 필수 섹션인 작업 내용, 작업 결과, 변경 사항, 테스트, 리뷰 체크리스트, 관련 이슈를 대부분 충족합니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#34/workspace-backend

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/entities/workspace/api/create-workspace.ts`:
- Around line 28-30: The createWorkspace flow is exposing raw Supabase error
details to the client via the thrown Error message. Update the error handling in
create-workspace.ts so the internal `error.message` is not included in the
thrown response; instead, log the detailed Supabase error server-side in the
workspace creation action and throw a generic user-facing message from
`createWorkspace` to keep internal constraint or column info out of the browser
console.

In `@src/entities/workspace/api/get-my-workspaces.ts`:
- Around line 7-9: `getMyWorkspaces` is passing `DEV_USER_ID` into the
`supabase.rpc('get_my_workspaces', ...)` call, which lets the browser control
the user identity; remove the client-supplied `p_user_id` from this flow. Update
`getMyWorkspaces` to use the authenticated user context only, either by moving
the call to a server-only path or by changing the `get_my_workspaces` RPC to
derive the user from `auth.uid()` in the database, so no client input can
influence which workspaces are returned.

In `@src/features/create-workspace/ui/CreateWorkspaceDialog.tsx`:
- Around line 63-67: The CreateWorkspaceDialog submission failure path only logs
to console and leaves users without feedback. Update the catch block in
CreateWorkspaceDialog to surface an in-UI error notification, such as a toast or
inline alert, instead of relying on console.error alone. Use the existing submit
flow around setIsSubmitting and the try/catch in CreateWorkspaceDialog to show a
clear failure message and keep the UI consistent when workspace creation fails.

In `@supabase/migrations/20260709054147_create_workspace_rpcs.sql`:
- Around line 92-99: The CASE expression in the workspace module generation
logic has no fallback, so an added workspace_purpose value could silently
produce no modules. Update the p_purpose handling in the RPC that builds the
module list to include an ELSE branch that explicitly raises an exception with a
clear message, so unexpected enum values fail fast instead of flowing into
unnest(null).
- Around line 1-105: The RPCs are currently trust-basing identity on the
client-supplied p_user_id, which allows privilege escalation through public
access. Update public.get_my_workspaces and public.create_workspace to derive
the user from auth.uid() (or otherwise restrict them to a server-only path) so
callers cannot read or create data for arbitrary users, and keep the
membership/workspace inserts bound to the authenticated identity rather than the
input parameter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c0fb5634-595f-48b4-b3ff-e34e8557662b

📥 Commits

Reviewing files that changed from the base of the PR and between b3d43aa and ed08b29.

📒 Files selected for processing (22)
  • .env.example
  • .gitignore
  • docs/conventions/supabase-convention.md
  • src/app/layout.tsx
  • src/app/providers.tsx
  • src/entities/workspace/api/create-workspace.ts
  • src/entities/workspace/api/get-my-workspaces.ts
  • src/entities/workspace/api/use-my-workspaces.ts
  • src/entities/workspace/index.ts
  • src/entities/workspace/model/create-workspace.schema.ts
  • src/entities/workspace/model/purpose.mapper.ts
  • src/entities/workspace/model/workspace.db.types.ts
  • src/features/create-workspace/model/schema.ts
  • src/features/create-workspace/ui/CreateWorkspaceDialog.tsx
  • src/shared/api/supabase/client.ts
  • src/shared/api/supabase/env.ts
  • src/shared/api/supabase/server.ts
  • src/shared/config/dev-user.ts
  • src/shared/model/database.types.ts
  • src/shared/model/supabase.types.ts
  • src/views/workspaces/ui/WorkspacesPage.tsx
  • supabase/migrations/20260709054147_create_workspace_rpcs.sql

Comment thread src/entities/workspace/api/create-workspace.ts
Comment thread src/entities/workspace/api/get-my-workspaces.ts
Comment thread src/features/create-workspace/ui/CreateWorkspaceDialog.tsx
Comment thread supabase/migrations/20260709054147_create_workspace_rpcs.sql
Comment thread supabase/migrations/20260709054147_create_workspace_rpcs.sql

@seongjinss555 seongjinss555 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다~

@wjswlgh96
wjswlgh96 merged commit 40dd70b into develop Jul 9, 2026
1 check passed
@wjswlgh96
wjswlgh96 deleted the feat/#34/workspace-backend branch July 9, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 내 워크스페이스 목록·생성 백엔드 연동 (RPC + tanstack-query + server action)

2 participants