Session Hierarchy
작성일: 2026-06-03 · 개정: 2026-09-04 (이슈 #1215 다섯 층 모델)
데이터 모델 정본은
session-data-model.md이다. 이 문서는 위계·상태·테이블을 화면과 파일 소유권 관점에서 요약한다.
데이터 위계
운동 기록의 기준 위계는 아래와 같다(이슈 #1215, 2026-09-04부터 다섯 층).
하루
세션 N개 -- session
종목 N개 -- session_exercise (복합 종목이면 동작들을 묶는 층)
세부 종목 1~5개 -- session_exercise_part (동작 하나; 단일 종목은 정확히 1개)
세트 N개 -- exercise_set (세트 한 번)
세부 세트 N개 -- exercise_set_part (세부 종목 하나의 값: 횟수·무게 …)하루는 기록의 조회 단위일 뿐, 저장 단위가 아니다. 저장 단위는 세션이다.
세트 수 규칙(오너 결정 D1~D3): 세션·종목의 총 세트 수는 exercise_set 행 수, 세부 종목(동작)별 세트 수는 그 세부 종목을 가리키는 exercise_set_part 행 수, 횟수·볼륨은 세부 세트 단위로 더한다. 서버 헬퍼는 session_set_totals_v1(session_id).
세션 상태
세션은 세 가지 상태로 표시한다.
completed: 완료한 과거 기록. 파란색으로 표시한다.missed: 과거에 기록해두었지만 완료하지 못한 기록. 회색으로 표시한다.planned: 미래의 기록. 회색으로 표시한다.
완료 기록·계획·그룹 운동 계획(보드)은 모두 session 테이블의 행이며 session.status로 구분한다.
- 완료 기록:
status = completed - 계획:
status = planned— 날짜가 오늘보다 과거로 지나가면missed - 그룹 운동 계획(보드):
status = planned+group_id(그룹 리더 소유)
계획을 수행해 완료하면 같은 행이 planned → completed로 바뀐다(새 행도, 연결 테이블도 없다). 하지 않은 세트는 지워진다. 보드에서 시작한 완료 세션은 origin_kind = 'group-board', origin_ref = '<group_id>:<date>'로 출처를 남긴다.
Supabase 구조
완료 기록·계획·그룹 운동 계획이 한 구조를 쓴다.
session
session_exercise
session_exercise_part
exercise_set
exercise_set_partsession_exercise_part.session_exercise_id와exercise_set_part.exercise_set_id는 NOT NULL — 소속 없는 세부 행은 존재할 수 없다.- 옛
planned_sessions·planned_sets·group_boards·group_board_sets·group_board_likes·group_board_comments·planned_session_adoptions테이블과composite_meta컬럼은 이슈 #1215(2026-09-04)로 삭제됐다. 보드 좋아요·댓글은session_likes·session_comments로 합쳐졌다. 계획 반영(팔로우한 사람의 계획을 내 달력에 링크로 가져오기)은 기능 자체가 폐기됐다. - 읽기 표면: 통계·달력·피드 함수는
completed_session_v1·planned_session_v1뷰를 읽는다. 복합 종목의 묶음은 별도 메모 없이 층 필드로 읽는다(이슈 #1244): 읽기 함수(세션 상세·달력 하루 요약·피드 카드·계획 상세)가 세부 종목 행마다session_exercise_id(소속 종목 id)·session_exercise_name(종목 이름, 복합이 아니면'')·session_exercise_position(종목 순서)·movement_position(종목 안 동작 순서)·details(수행 상세)를 싣고, 계획 상세는exercise_set_position(세트 순서)도 싣는다. 같은 소속 종목에 동작이 2개 이상이면 앱이 한 종목(복합)으로 묶는다. 옛composite_meta합성 함수(session_part_composite_meta_v1·bounded_home_composite_meta)는 삭제됐다. - 쓰기 계약:
save_session_v5(p_payload, p_client_mutation_id, p_request_hash)(완료·계획·그룹 계획의 생성·수정·계획→완료 전이) +delete_session_v5(p_session_id, p_expected_revision, p_client_mutation_id, p_request_hash)한 쌍. 상세는workout-write-path.md.
주요 테이블
session — 세션 한 행(완료 기록·계획·그룹 운동 계획)
id,user_id,date,status(completed|planned|missed)group_id(그룹 운동 계획이면 그룹 id, 개인은 null),origin_kind·origin_ref(보드에서 시작한 완료 세션의 출처)title,note,condition,pre_note,post_condition,start_time,end_time,duration_label,scheduled_time,body_weight_kg,session_intentsource,source_ref,brid,server_revision(모든 종류의 낙관적 동시성 — 계획이 쓰던expected_updated_at대체),kudos,created_at,updated_at
session_exercise — 종목 한 행(복합 종목이면 동작들을 묶는 층)
id,session_id,position,name,import_group_key(WodUp 인입 복합 묶음 키, 앱 저장은 null),created_at
session_exercise_part — 세부 종목(동작) 한 행; 단일 종목은 정확히 1개
id,session_exercise_id(NOT NULL),session_id,exercise_id,synonym_id,positionentry_kind,entry_title,note,entry_review,recording_fields,bodyweight_factor,load_multipliersource,source_ref,provider_exercise_id,raw_payload(선택한 수행 디테일details포함),created_at
exercise_set — 세트 한 행
id,session_exercise_id,position,set_type,created_at
exercise_set_part — 세부 세트 한 행(세부 종목 하나의 값)
id,exercise_set_id(NOT NULL),session_exercise_part_id(세부 종목 id, #1245),position,set_typereps,target_reps,set_result,load,load_unit,load_lb,effective_load,assist_kg,duration_seconds,distance_meters,caloriesperceived_rpe(RPE 1.0~10.0 — 이슈 #1237로 5단계 번호·옛 난이도 컬럼 삭제),rest_seconds,free_rest,notesource,source_ref,raw_payload,created_at
화면 표현
모바일 세션 페이지 달력:
- 완료 세션이 있는 날짜: 파란색 표시
- 미완료/예정 세션이 있는 날짜: 회색 표시
- 하루에 세션이 여러 개면 날짜 셀에 개수를 작게 표시
선택 날짜 상세:
선택한 날짜
세션 카드
상태 배지
종목 수 / 세트 수 / 볼륨 또는 계획
종목 목록
세트 목록파일 소유권
- 현재 React 앱 surface:
src/react/ - 세션/홈/운동 입력 UI:
src/react/ui/screens/SessionScreen.jsx,src/react/ui/screens/HomeScreen.jsx,src/react/ui/screens/WorkoutFlow.jsx,src/react/ui/styles/styles.css - 화면 UI:
src/react/ui/screens/*의 순수 프레젠테이션 컴포넌트가 담당한다. root-level 보조 화면 파일은 로드하지 않는다. - Supabase 공개 facade:
src/react/services/barbelicApi.js - Supabase 읽기/쓰기:
src/react/services/barbelicRepository.js - 데이터 변환/요약 계산:
src/react/services/barbelicMappers.js - 디자인/기능 접합 마커 계약:
src/react/contracts/designContract.js - Supabase 전체 schema:
supabase/schema.sql
개인 상태 저장
- 날짜별 컨디션:
daily_conditions— 기능 폐기(2026-08-24, #668)- 키:
user_id + date - 테이블·과거 데이터는 보존하지만, 앱은 더 이상 읽지도 쓰지도 않는다(파이프라인 제거).
- 키:
- 주 운동 스타일:
profiles.primary_training_style- 온보딩의 6종 선택값을 안정적인 식별자로 저장한다.
- 신체 변화 이력은
body_metrics, 초기 1RM은user_manual_pr_records가 정본이다.
- DB migration baseline:
supabase/migrations/20260821000000_baseline_v2.sql - Legacy patch reference:
supabase/legacy/exercise_sets_patch.sql(do not apply)