그룹미팅 시스템¶
문서 역할¶
- 역할:
설명 - 문서 종류:
architecture - 충돌 시 우선 문서: 권한은 보안/접근통제 정책, 결제는 결제 운영 정책, 푸시는 푸시알림 운영 정책, 데이터는 데이터 거버넌스 정책, 사용자 노출명과 신규 N:N 식별자 명명은 서비스 용어 정책
- 기준 성격:
as-is
이 문서는 운영 중인 신규 N:N 그룹미팅의 논리 모델과 도메인 불변조건을 설명한다. 기존 2:2 미팅은 UI 패턴만
참고하며 데이터, 상태, API, 알림 타입을 재사용하지 않는다. private 물리 스키마의 단일 기준은
coupler-api의 migration, schema lock, DB native COMMENT다.
범위와 모집 마감 규칙¶
1차 범위는 행사 생성 -> 공개 시작 -> 공개 활성·모집 마감 운영 -> 신청·Admin 승인/확정 취소 -> 그룹 채팅 -> 종료 -> 후기다.
- 구현 계약 범위: API, DB, Admin, Mobile, Docs
- 운영 반영 기준점과 exact artifact는 2.3.0 릴리스 실행 기록에 보존한다.
- 제외: 현장 체크인, 좌석/회전 라운드, 호감 선택, 전역 회원 패널티, 외부 결제/정산
- 호스트는 참가 정원에서 제외한다.
- Group Meeting의 공통 이용 자격은
GENERAL_MEMBER이상의 이용 가능한 모바일 회원이다. 호스트는 이 자격과 유효한 호스트 연결을 모두 충족한다. - Admin은 DRAFT에서 행사 정보 수정 API의 모든 필드를 변경할 수 있다. OPEN·CONFIRMED에서는 참가비· 사진 공개를 제외한 필드와 행사·신청 마감 일시를 변경할 수 있고, OPEN에서 신청 접수 중일 때만 참가비·사진 공개도 변경할 수 있다. FINISHED·CANCELED·DELETED에서는 수정할 수 없다. Mobile 호스트는 DRAFT에서만 수정할 수 있으며 상태 변경은 두 주체 모두 별도 전이 명령으로만 수행한다.
- 일반 클럽매니저는 신청이 없는 자신 소유 DRAFT만 정상 삭제할 수 있다. Super Admin은 필수 사유와 version을
받아
DELETED가 아닌 행사를DELETED로 강제 전이할 수 있다. 두 삭제 모두 행과 종속 이력을 보존한다. - 남녀 정원은 각각 2~20명이고 합계는 최대 40명이다.
10+10,20+20,7+5를 허용하고 어느 한쪽이라도 21명 이상이면 거절한다. - 모집 마감에는 승인 인원 조건이 없다. 신청 수는 신청 접수가 가능한 동안 제한하지 않고 Admin 승인 시에만 성별 정원을 검사한다.
- 신청 자체에는 Key를 차감하지 않는다. 외부 입금 확인은 Admin의 참여 승인으로 표현한다.
- 채팅 구성원 요약의 익명 별칭과 현재 미니프로필 우선(미등록 시 관리자 지정 대표 프로필) 아바타는 종료·취소
읽기 전용 이력에서도 대화 문맥으로 유지한다. 별도 전체 참가자 프로필 조회는 채팅이 초기화된 실효
OPEN·CONFIRMED행사에서만 무료이며,FINISHED·CANCELED와 행사 시작 +24시간 경계를 지난 방에서는 허용하지 않는다. 신규 drawer의 매칭 ProfilePreview와 카드 동작은 실제 채팅 개방 경계 이후이면서photo_public=2인 경우로 더 좁다. 공개 프로필 카드는 관리자 지정 대표 프로필과 행사 사진 공개 범위의 승인 프로필 이미지를 사용하며, Key를 차감하거나 유료 열람 이력을 새로 만들지 않는다. 소개글은 소개글 심사가 승인된 회원에게만 포함하며, 승인 전에는null을 반환한다. - 서버 설정 16/18과 기존 프로필 열람 원장은 과거 유료 이력과 향후 정책 검토를 위해 보존한다. 향후 과금은 설정값만 바꿔 현재 조회에 숨겨 적용하지 않고 가격·사용자 확인·멱등성이 있는 별도 동작 명령과 계약으로 도입한다.
- Mobile 호스트와 Admin의 운영 목적 신청자 프로필 조회도 무료이며 Admin 운영 조회는 감사 이력을 남긴다.
- 종료 후기 최초 보상은 서버 설정 25를 사용한다. 클라이언트가 Key 금액을 결정하지 않는다.
- 종료한 N:N 그룹미팅에서 여전히 APPROVED이고 후기를 작성하지 않은 참가자는 다른 N:N 행사에 새로 신청할 수 없다. CANCELED·LEFT는 후기 대상과 이 제한에서 즉시 제외한다. 목록·종료 채팅·후기 상태 조회와 후기 작성은 제한하지 않아 미작성 상태를 해소할 수 있게 한다.
- 운영 CMS는
application_close_at = event_at으로 저장한다. 해당 시각이 지나면 행사 상태를 바꾸지 않고application_is_open = false로 신청만 차단한다. 기한이 지난 OPEN에서 Admin이 신청 접수 재개를 명시하면 같은 마감 시각 뒤에도 신규 신청을 다시 받을 수 있다. event_at은 KST 기준 행사 시작 시각이다. 최초 모집 마감으로 채팅을 초기화한 활성 행사는 정확히event_at + 24시간부터 API의 조회·권한· 쓰기 판정에서 FINISHED로 취급하며 후기 작성, 미작성 후기 신청 제한, 채팅 비활성화와 Admin 변경 제한은 cron 실행 여부에 의존하지 않는다. 별도 실제 종료 시각(end_at)은 현재 모델에 없다.- 최초 모집 마감은 승인 인원과 무관하게 채팅 principal·현재 승인 구성원·안내 메시지를 한 번만 초기화한다. 이후 OPEN·CONFIRMED 왕복은 채팅 구성원·메시지를 삭제하거나 다시 만들지 않는다.
- 초기화된 채팅은 KST 기준 최신
event_at의 달력상 전날 오후 1시부터 열린다. 개방 여부는 API가 매 요청에서 최신 행사 일시, 활성 상태와 현재 참여 자격으로 계산하므로 행사 일시 변경 시 개방·종료 경계를 다시 계산한다. 개방 전에는 대기 카드를 행사 상세로 연결하고 채팅방 상세·메시지 목록·읽음 갱신·전송을 거부한다. - 서버 job은 유효하게 종료된 행사의 저장 상태를 FINISHED로 따라잡고 같은 transaction에서 감사 로그와 종료 시스템 메시지를 기록한 뒤 후기 알림을 발송한다. Admin 수동 종료 API는 두지 않는다.
논리 데이터 모델¶
- 도메인 ID:
group-meeting
먼저 보는 그림¶
이 그림은 데이터가 어디에 속하고 무엇을 참고하는지 먼저 보여준다. 정확한 이름과 조건은 아래 상세 표를 따른다.
flowchart LR
entity_club_dash_manager_dot_manager["클럽매니저 · 다른 영역<br/>club-manager.manager"]
entity_conversation_dot_thread["대화방 · 다른 영역<br/>conversation.thread"]
entity_group_dash_meeting_dot_action_dash_history["그룹미팅 행위 이력<br/>group-meeting.action-history"]
entity_group_dash_meeting_dot_application["그룹미팅 신청<br/>group-meeting.application"]
entity_group_dash_meeting_dot_detail_dash_slice["행사 상세 이미지 조각<br/>group-meeting.detail-slice"]
entity_group_dash_meeting_dot_detail_dash_version["행사 상세 이미지 버전<br/>group-meeting.detail-version"]
entity_group_dash_meeting_dot_event["그룹미팅 행사<br/>group-meeting.event"]
entity_group_dash_meeting_dot_host["그룹미팅 호스트<br/>group-meeting.host"]
entity_group_dash_meeting_dot_participant["그룹미팅 참여자<br/>group-meeting.participant"]
entity_group_dash_meeting_dot_review["그룹미팅 후기<br/>group-meeting.review"]
entity_key_dash_wallet_dot_profile_dash_access["프로필 열람 거래 · 다른 영역<br/>key-wallet.profile-access"]
entity_member_dot_member["회원 계정 · 다른 영역<br/>member.member"]
entity_moderation_dot_member_dash_report["회원 신고 · 다른 영역<br/>moderation.member-report"]
entity_group_dash_meeting_dot_host -->|"참고"| entity_club_dash_manager_dot_manager
entity_group_dash_meeting_dot_host -->|"참고"| entity_member_dot_member
entity_group_dash_meeting_dot_event -->|"참고"| entity_group_dash_meeting_dot_host
entity_group_dash_meeting_dot_event -->|"같이 관리"| entity_group_dash_meeting_dot_detail_dash_version
entity_group_dash_meeting_dot_detail_dash_version -->|"같이 관리"| entity_group_dash_meeting_dot_detail_dash_slice
entity_group_dash_meeting_dot_event -->|"같이 관리"| entity_group_dash_meeting_dot_application
entity_group_dash_meeting_dot_application -->|"참고"| entity_member_dot_member
entity_group_dash_meeting_dot_event -->|"같이 관리"| entity_group_dash_meeting_dot_participant
entity_group_dash_meeting_dot_participant -->|"참고"| entity_member_dot_member
entity_group_dash_meeting_dot_participant -->|"연결"| entity_conversation_dot_thread
entity_group_dash_meeting_dot_event -->|"같이 관리"| entity_group_dash_meeting_dot_review
entity_group_dash_meeting_dot_review -->|"참고"| entity_member_dot_member
entity_group_dash_meeting_dot_event -->|"같이 관리"| entity_group_dash_meeting_dot_action_dash_history
entity_group_dash_meeting_dot_application -->|"연결"| entity_key_dash_wallet_dot_profile_dash_access
entity_group_dash_meeting_dot_application -->|"연결"| entity_moderation_dot_member_dash_report
꼭 지킬 규칙:
- 행사와 신청·참여·메시지·후기·신고는 같은 행사 문맥에 속한다
- 한 회원은 같은 행사에 신청을 하나만 가진다
- 남녀 정원은 각각 2~20명, 합계 최대 40명이고 승인 인원은 해당 성별 정원을 넘지 않는다
- 호스트 또는 승인 신청 중 정확히 하나의 자격으로 참여한다
- 종료 행사에서 현재 APPROVED인 회원만 최초 후기를 작성할 수 있고 후기 작성은 신청 상태를 바꾸지 않는다
- 상태 변경과 감사 이력은 같은 요청·transaction의 결론을 가진다
- 매니저와 Group Meeting 이용 자격을 충족한 모바일 회원은 각각 최대 하나의 호스트 연결만 가지며 행사 생성 전에 연결이 유효해야 한다
- Admin은 DRAFT에서 모든 필드를, OPEN·CONFIRMED에서는 참가비·사진 공개를 제외한 필드와 행사·신청 마감 일시를 변경할 수 있다. OPEN에서 신청 접수 중일 때만 참가비·사진 공개도 변경할 수 있고 FINISHED·CANCELED·DELETED에서는 수정할 수 없다. Mobile 호스트는 DRAFT에서만 수정할 수 있다
- 종료한 N:N 행사에서 현재 APPROVED인 신청의 미작성 후기가 있으면 다른 N:N 행사에 신규 신청할 수 없다
- OPEN·CONFIRMED는 서로 전환할 수 있고 최초 모집 마감 뒤 채팅 principal·구성원·메시지는 이 운영 상태와 독립적으로 보존한다
- 채팅 초기화 뒤 승인된 참가자는 승인 안내 메시지부터 이력을 볼 수 있고 승인 전 메시지는 볼 수 없다
- 방별 새 USER 메시지 알림 선택은 82의 FCM·알림함만 통제하고 메시지·WebSocket·unread/read와 다른 그룹미팅 알림은 바꾸지 않는다
- 최초 후기 보상은 서버 설정 금액을 사용하고 후기·Key 잔액·Key 원장을 같은 transaction에서 한 번만 반영한다
정확한 값과 조건 보기
논리 엔티티¶
| 논리 ID | 표시명 | 생명주기 역할 | 엔티티 형태 | 기록 역할 | 책임 | 최고 데이터 분류 | 생명주기 |
|---|---|---|---|---|---|---|---|
group-meeting.host |
그룹미팅 호스트 | root | association | state | 클럽매니저 계정과 모바일 호스트 회원의 명시적 연결 | 민감 | 활성 행사가 없을 때 원천 계정 연결 해제 가능, 행사 이력은 보존 |
group-meeting.event |
그룹미팅 행사 | root | entity | state | 공개·신청 접수·모집 마감·종료와 공개 행사 정보 | 민감 | 삭제·취소·종료 상태로 보존, 공개 이미지는 정책에 따라 정리 |
group-meeting.detail-version |
행사 상세 이미지 버전 | child | entity | snapshot | 긴 상세 이미지 원본과 변환 상태 | 내부 | 현재 버전 유지, 실패·교체 버전 정리 가능 |
group-meeting.detail-slice |
행사 상세 이미지 조각 | child | entity | snapshot | 상세 이미지 버전의 표시용 조각 | 내부 | 상위 버전 정리 시 파일과 함께 정리 |
group-meeting.application |
그룹미팅 신청 | child | association | state | 신청·승인·확정 취소·채팅 자격 종료 | 민감 | 행사 종료 뒤 신청 당시 별칭과 상태를 비식별 이력으로 보존 가능 |
group-meeting.participant |
그룹미팅 참여자 | child | association | state | 승인된 채팅 참여 자격, 읽음 경계와 방별 USER 메시지 알림 선택 | 내부 | 자격 종료 뒤에도 메시지 문맥을 위해 보존 가능 |
group-meeting.review |
그룹미팅 후기 | child | entity | history | 종료 행사 후기와 보상 연결 | 민감 | 개인정보 정리 시 자유문 비식별화, 보상 이력 보존 |
group-meeting.action-history |
그룹미팅 행위 이력 | child | entity | history | 상태 변경과 중요 운영 행위의 행위자·사유 | 내부 | append-only 감사 이력으로 보존 |
관계¶
| 출발 논리 ID | 관계 역할 | 관계 유형 | 도착 논리 ID | 카디널리티 | 소유·삭제 규칙 |
|---|---|---|---|---|---|
group-meeting.host |
manager |
references | club-manager.manager |
1:1 | 매니저 계정 회수 뒤에도 과거 운영 이력 보존 |
group-meeting.host |
member |
references | member.member |
1:1 | 회원 개인정보 정리 뒤 비식별 표시 사용 |
group-meeting.event |
host |
references | group-meeting.host |
N:1 | 활성 행사가 있으면 호스트 연결 삭제 금지 |
group-meeting.event |
detail-versions |
owns | group-meeting.detail-version |
1:N | 현재 활성 버전은 행사와 함께 유지 |
group-meeting.detail-version |
slices |
owns | group-meeting.detail-slice |
1:N | 버전 정리 시 조각과 파일 함께 정리 |
group-meeting.event |
applications |
owns | group-meeting.application |
1:N | 신청 이력은 행사와 함께 보존 |
group-meeting.application |
applicant |
references | member.member |
N:1 | 신청 당시 자격과 표시 별칭 snapshot 보존 |
group-meeting.event |
participants |
owns | group-meeting.participant |
1:N | 승인 자격과 대화 참여 자격을 분리해 판정 |
group-meeting.participant |
member |
references | member.member |
N:1 | 승인 신청자 또는 호스트만 참여 자격 보유 |
group-meeting.participant |
thread |
associates | conversation.thread |
N:1 | 유효한 참여자만 그룹 채팅 읽기·쓰기 가능 |
group-meeting.event |
reviews |
owns | group-meeting.review |
1:N | 신청 회원당 최초 후기 하나만 허용 |
group-meeting.review |
author |
references | member.member |
N:1 | 종료 행사에 유효하게 참여한 회원만 작성 가능 |
group-meeting.event |
action-history |
owns | group-meeting.action-history |
1:N | 상태 변경과 같은 transaction에서 기록 |
group-meeting.application |
profile-access |
associates | key-wallet.profile-access |
N:M | 과거 유료 열람 거래만 보존하며 현재 무료 공개 프로필 조회는 거래를 만들지 않음 |
group-meeting.application |
member-report |
associates | moderation.member-report |
N:M | 같은 행사 참여 문맥에서만 회원 신고 허용 |
불변조건¶
| 규칙 ID | 관련 논리 ID | 불변조건 | 기준 문서 |
|---|---|---|---|
GROUP-MEETING-INV-001 |
group-meeting.event |
행사와 신청·참여·메시지·후기·신고는 같은 행사 문맥에 속한다 | 엔지니어링 가드레일 |
GROUP-MEETING-INV-002 |
group-meeting.application |
한 회원은 같은 행사에 신청을 하나만 가진다 | 이 문서 |
GROUP-MEETING-INV-003 |
group-meeting.event |
남녀 정원은 각각 2~20명, 합계 최대 40명이고 승인 인원은 해당 성별 정원을 넘지 않는다 | 이 문서 |
GROUP-MEETING-INV-004 |
group-meeting.participant |
호스트 또는 승인 신청 중 정확히 하나의 자격으로 참여한다 | 이 문서 |
GROUP-MEETING-INV-005 |
group-meeting.review |
종료 행사에서 현재 APPROVED인 회원만 최초 후기를 작성할 수 있고 후기 작성은 신청 상태를 바꾸지 않는다 | 이 문서 |
GROUP-MEETING-INV-006 |
group-meeting.action-history |
상태 변경과 감사 이력은 같은 요청·transaction의 결론을 가진다 | 엔지니어링 가드레일 |
GROUP-MEETING-INV-007 |
group-meeting.host |
매니저와 Group Meeting 이용 자격을 충족한 모바일 회원은 각각 최대 하나의 호스트 연결만 가지며 행사 생성 전에 연결이 유효해야 한다 | 이 문서 |
GROUP-MEETING-INV-008 |
group-meeting.event |
Admin은 DRAFT에서 모든 필드를, OPEN·CONFIRMED에서는 참가비·사진 공개를 제외한 필드와 행사·신청 마감 일시를 변경할 수 있다. OPEN에서 신청 접수 중일 때만 참가비·사진 공개도 변경할 수 있고 FINISHED·CANCELED·DELETED에서는 수정할 수 없다. Mobile 호스트는 DRAFT에서만 수정할 수 있다 | 이 문서 |
GROUP-MEETING-INV-009 |
group-meeting.application |
종료한 N:N 행사에서 현재 APPROVED인 신청의 미작성 후기가 있으면 다른 N:N 행사에 신규 신청할 수 없다 | 이 문서 |
GROUP-MEETING-INV-010 |
group-meeting.event |
OPEN·CONFIRMED는 서로 전환할 수 있고 최초 모집 마감 뒤 채팅 principal·구성원·메시지는 이 운영 상태와 독립적으로 보존한다 | 이 문서 |
GROUP-MEETING-INV-011 |
group-meeting.participant |
채팅 초기화 뒤 승인된 참가자는 승인 안내 메시지부터 이력을 볼 수 있고 승인 전 메시지는 볼 수 없다 | 이 문서 |
GROUP-MEETING-INV-013 |
group-meeting.participant |
방별 새 USER 메시지 알림 선택은 82의 FCM·알림함만 통제하고 메시지·WebSocket·unread/read와 다른 그룹미팅 알림은 바꾸지 않는다 | 푸시알림 운영 정책 |
GROUP-MEETING-INV-012 |
group-meeting.review |
최초 후기 보상은 서버 설정 금액을 사용하고 후기·Key 잔액·Key 원장을 같은 transaction에서 한 번만 반영한다 | 이 문서 |
상태 모델¶
행사¶
| 값 | 상태 | 의미 |
|---|---|---|
| 0 | DRAFT | 행사 정보 작성 중 |
| 1 | OPEN | 행사가 공개된 활성 운영 상태, 신청 접수 여부와 독립 |
| 3 | CONFIRMED | 모집 마감 표시, 최초 진입 시 채팅 초기화 |
| 4 | FINISHED | 행사 종료, 후기 가능 |
| -1 | CANCELED | 행사 취소 |
| -2 | DELETED | 작성 중 정상 삭제 또는 Super Admin 강제삭제, Mobile 목록 제외 |
Admin에서 POST /admin/group-meetings/{event_id}/confirm을 실행하는 버튼과 CONFIRMED(3) 상태·감사 이력의
한국어 노출명은 모두 모집 마감이다. CONFIRMED, EVENT_CONFIRMED, /confirm은 배포된 기술 식별자로
유지하며 별도의 한국어 제품명으로 사용하지 않는다.
최초 공개는 DRAFT -> OPEN이며 ready 상세 이미지가 필요하다. 이후 활성 운영 상태는
OPEN <-> CONFIRMED로 전환할 수 있다. DRAFT는 DELETED, OPEN·CONFIRMED는 CANCELED로 종료할 수 있고,
채팅이 초기화된 활성 상태는 행사 시작 +24시간에
FINISHED가 된다. CONFIRMED 전 승인 인원 조건은 없다.
일반 클럽매니저의 삭제는 신청이 없는 자신 소유 DRAFT에서만 EVENT_DELETED를 기록한다. Super Admin의
강제삭제는 DELETED가 아닌 모든 상태에서 EVENT_FORCE_DELETED를 기록하며 hard delete하지 않는다.
OPEN은 행사의 공개 활성 생명주기만 뜻하며 그 자체로 신청 접수 중임을 뜻하지 않는다. 실제 신청 접수 가능
여부인 application_is_open은 OPEN 여부, application_close_at, 명시적 재개 marker를 함께 사용해 계산한다.
기한이 지난 OPEN에 /open 명령을 실행하면 상태 값은 1로 유지하면서 APPLICATION_REOPENED 감사 이력과
marker를 한 번 기록한다. CONFIRMED를 OPEN으로 되돌리는 명령은 기존 EVENT_OPENED 이력을 사용하고 초기화된
채팅을 보존한다.
N:N 기능의 운영 최초 도입 전 데이터가 없었으므로 기존 상태 2 행사·감사 이력의 데이터 전환은 개발계에서만
일회성으로 수행했다. 운영에는 데이터 backfill이나 호환 상태를 추가하지 않고 새 상태 제약만 동일하게
적용했다.
Admin 행사 정보 수정 API는 DRAFT에서 모든 필드를 허용한다. OPEN과 CONFIRMED에서는 제목, 행사·신청 마감
일시, 장소, 정원, 썸네일, 상세 이미지·문구, 해시태그를 수정할 수 있다. 참가비와 사진 공개는 OPEN에서 요청
시작 시점의 application_is_open이 true일 때만 변경할 수 있고 CONFIRMED 또는 신청 접수가 닫힌 OPEN에서는
현재 값으로 고정한다. 마감 시각 변경과 잠긴 신청 접수 조건 변경을 한 요청으로 함께 보내도 요청 시작 시점 판정을
우회할 수 없다. 운영 CMS는 행사 일시와 신청 마감 일시를 같은 값으로 저장한다. FINISHED, CANCELED,
DELETED에서는 행사 정보와 상세 이미지를 수정할 수 없다. 남녀 정원은 현재 승인 인원보다 작게 낮출 수 없고,
신청 마감은 행사 일시 이후로 둘 수 없으며 권한·optimistic version
불변조건을 계속 적용한다. Mobile 호스트의 행사 정보 수정은 DRAFT에만 허용한다. 호스트 연결은 생성 뒤 행사
정보 수정으로 바꾸지 않고, 행사 상태는 위 허용 전이를 수행하는 별도 명령으로만 변경한다. 행사 정보와 상세
이미지의 각 수정은 해당 요청 transaction에서 행위 이력과 함께 기록한다.
클라이언트가 수정 화면을 노출하거나 숨기더라도 서버가 주체별 상태와 불변조건을 최종 판정한다.
신청¶
| 값 | 상태 | 의미 |
|---|---|---|
| 0 | APPLIED | 신청 접수, 입금 확인 전 |
| 1 | APPROVED | 참여 승인 |
| -1 | CANCELED | 승인 후 Admin 확정 취소, 외부 환불 필요 |
| -2 | LEFT | 참가자가 채팅방을 명시적으로 나감 |
참여 승인과 확정 취소는 행사 OPEN·CONFIRMED에서만 허용한다. 채팅 초기화 뒤 승인하면 chat member와 안내 메시지를 만들고 해당 안내 메시지를 개인별 이력 노출 하한으로 저장한다. 개방 경계가 지났다면 즉시 기존 채팅방에 진입하지만 승인 전 메시지는 보지 못한다. 확정 취소하면 CANCELED로, 참가자가 명시적으로 나가면 LEFT로 바꾸고 읽기·쓰기·프로필 열람·정원 점유·후기 자격을 즉시 제거하되 기존 메시지와 참여 이력은 보존한다. FINISHED 행사에서 현재 APPROVED 참가자가 최초 후기를 완료해도 신청 상태는 APPROVED로 유지하고 후기 행으로 완료 여부를 판정한다.
다른 N:N 행사 신규 신청은 FINISHED 행사에서 신청 상태가 APPROVED이고 후기가 없는지 서버가
판정한다. 같은 행사에서 Admin이 확정 취소한 CANCELED 신청은 신청 접수가 열려 있고 미작성 후기가 없을 때
기존 신청 행을 APPLIED로 되돌려 재신청할 수 있다. 이때 현재 성별·별칭 snapshot을 갱신하고 승인 시각을
비우며 신청 시각과 version을 갱신한다. 기존 채팅 principal은 보존했다가 재승인 시 재사용하고 새 합류 안내
메시지부터 이력을 다시 노출한다. APPLIED, APPROVED, LEFT의 같은 행사 신청 재시도는 기존 신청을 반환한다.
미작성 후기 해소에 필요한 목록·종료 채팅·후기 조회·작성은 계속 허용한다. 기존 1:1·2:2 기능에는 이 제한을
적용하지 않는다.
거래와 동시성¶
- 행사, 신청, 참여 자격, Key 원장은 server transaction이 단독 판정한다.
- 행사·신청 변경은 optimistic version을 사용하고 승인 시 행사와 신청을 잠근 뒤 성별 승인 수를 다시 집계한다.
- 생성, 메시지, 신고의 idempotency key는 행위 주체 범위에서 유일하다. 같은 key와 같은 canonical payload의 재요청은 부수효과 없이 기존 성공을 반환하고, 다른 payload에 재사용하면 실패한다.
- 현재 무료 공개 프로필 조회는 read-only이며 회원 Key·Key 원장·프로필 유료 열람 이력을 쓰지 않는다. 향후 유료 열람을 도입하면 회원 Key, 기존 Key 원장, 그룹미팅 연결을 별도 명시적 명령의 한 transaction에서 반영하고 설정 누락이나 잘못된 부호를 fallback하지 않는다.
- N:N 그룹미팅 출처 매칭카드는 사전 검사와 별도로 실제 생성 transaction에서 행사, 발송자·대상 신청과 채팅
principal, 요청한 대상 식별자를 잠그고 채팅 개방·실효 활성·현재 자격을 다시 확인한다. 실패하면 Key·매칭·
최초 로그를 모두 쓰지 않고, 수신 푸시는 transaction commit 뒤에만 시도한다.
free_send=true의 0원 전달도 Mobile의 명시적 확인 뒤 호출하며 Key 잔액·원장은 변경하지 않는다. 상태·Key 규범은 매칭 운영 정책을 따른다. - 후기 보상은 회원 Key, 기존 Key 원장, 그룹미팅 연결을 한 transaction에서 반영하며 설정 누락이나 잘못된 부호는 fallback하지 않고 전체 transaction을 실패시킨다.
- 알림은 원천 transaction commit 뒤 기존
sendFCMPush()한 경로에서만 발송·저장한다. 그룹미팅 코드가t_alarm을 직접 추가하지 않는다. - 방별 새 USER 메시지 알림 선택은 현재 호스트 또는 APPROVED 참가자가 자기 채팅 principal에 멱등 갱신한다.
기본값은 수신이며, 전역
alarm_chat과 방별 선택이 모두 켜진 수신자만 82의 FCM·t_alarm대상이 된다. - 행사당 채팅은 하나이며 최초 모집 마감 시각으로 초기화 여부만 기록한다. OPEN·CONFIRMED 왕복은 채팅 principal·구성원·메시지를 변경하지 않는다. 송신 가능 여부는 유효 행사 상태와 KST 기준 최신 행사 일시의 전날 오후 1시 개방 경계, 참가자 자격은 신청 상태에서 매 요청 판정한다. FINISHED·CANCELED 채팅 이력은 읽기 전용으로 유지한다.
- 읽기 전용 채팅은 메시지와 미작성 후기 문맥만 보존한다. 구성원 요약 아바타는 과거 메시지 식별을 위해
남기지만 다른 구성원의 전체 프로필 열람과 그룹미팅 출처 매칭카드 사전 검사·전달은 허용하지 않는다.
기존 앱의 참가자 프로필 호환 경로는 채팅 초기화 뒤 실제 개방 전 조회 동작을 유지하되 실효
OPEN·CONFIRMED가 아니면 차단한다. - 채팅 메시지는
USER와SYSTEM의 tagged union이다.USER만 채팅 구성원과 client idempotency key를 가지며 Mobile 전송 API로 생성한다.SYSTEM은 sender 없이 서버 상태 전이와 연결된 action log를 원천으로 같은 transaction에서 한 번만 생성한다. EVENT_CONFIRMED는 최초 모집 마감에서만 채팅 구성원과 개방 전 대기 카드를 생성하고 전날 오후 1시 개방 시각을 안내한다.PARTICIPANT_JOINED는 채팅 초기화 뒤 승인 참가자의 이력 노출 하한과 합류 안내,PARTICIPANT_CANCELED는 Admin의 승인 확정 취소,PARTICIPANT_LEFT는 참가자의 명시적 퇴장,EVENT_FINISHED는 행사 종료,EVENT_CANCELED는 초기화된 채팅이 있는 활성 행사 취소와 함께 기록한다. 후기 완료는 신청 상태나 시스템 메시지를 변경하지 않고, 채팅 생성 전 행사 취소에는 시스템 메시지를 만들지 않는다.- 시스템 메시지는 메시지 목록과 채팅 목록의
last_message에 같은 DTO로 노출하고 다른 구성원에게 unread로 계산한다. 모집 마감·행사 취소 FCM과 채팅 이력은 역할이 다르므로 시스템 메시지 생성만으로 별도 채팅 FCM이나t_alarm을 중복 생성하지 않는다. transaction commit 뒤 현재 구성원에게 payload 없는chat:unread:invalidated를 best-effort로 보내 Mobile 전역 unread snapshot 재조회를 유도한다.
입력과 파생 값¶
- 해시태그는
#단어를 ASCII 공백 한 칸으로 구분한 canonical 문자열만 허용한다. trim, 중복 제거, 공백 정규화로 잘못된 요청을 보정하지 않는다. - 신청·승인 성별 인원수, 역할, unread, 접근 권한과 채팅 개방 여부는 원천 상태와 최신
event_at에서 계산한다. 같은 의미의 상태나 합계를 별도 필드에 중복 저장하지 않는다. - 신청 당시 성별과 별칭은 시간축 snapshot이며 현재 회원 프로필 SoT로 사용하지 않는다.
- 현재 무료 공개 프로필 조회는 Key 변동을 만들지 않는다. 후기 보상과 향후 별도 유료 열람 명령의 실제 Key 변동량·잔액만 기존 Key 원장에 한 번 기록한다.
- raw DB row, 내부 감사 연결, Key 원장 연결은 프론트 응답에 노출하지 않는다.
삭제와 보관¶
- 행사, 신청, 참여, 후기, 신고, 행위 이력은 hard delete하지 않는다. 실패·교체된 이미지 버전과 조각만 media cleanup 정책에 따라 정리할 수 있다.
- Super Admin 강제삭제도 행사와 신청·채팅·후기·신고를 그대로 보존하고 같은 transaction에 행위자·대상·
이전/이후 상태·필수 사유를 기록한다.
DELETED행사는 감사와 운영 확인을 위해 CMS 목록·상세에는 남기고, Mobile의 전체·주최·참여 행사 목록과 전체 채팅 목록에서는 제외한다. - 그룹 채팅은 게시글 댓글 도메인이 아니다. Admin은
USER메시지만 삭제할 수 있고 row를 지우지 않고 상태를ADMIN_DELETED로 바꾸며 조회 DTO는 content를삭제된 메시지입니다.로 반환한다. 삭제 actor와 reason은 같은 transaction의 행위 이력에 남긴다.SYSTEM메시지는 상태 전이 이력이므로 삭제하지 않는다. 따라서 스퀘어 게시글·댓글 삭제 정책과 충돌하지 않고conversation.message의 감사·대화 문맥 보존 원칙을 따른다. - 회원 개인정보 정리 시 신청 별칭과 작성 자유문을 비식별화하고 nullable 원천 회원/Admin 연결만 해제한다.
별칭을 포함한 참가자 입장·확정 취소·퇴장 시스템 메시지도 action log의 신청 대상을 기준으로 같은
transaction에서
탈퇴한 참가자문구로 비식별화한다. 행사 상태, 비식별 신고, Key 원장 연결, 행위 이력은 운영 감사 기록으로 보존한다. - action reason에는 연락처, 프로필 원문, 인증정보를 넣지 않는다.
알림¶
그룹미팅 알림 타입 77~85의 의미와 구조는 푸시알림 시스템, 사용자 설정과 발송 조건은
푸시알림 운영 정책을 따른다. 모든 target은 행사 ID이고 원천 write가
실제로 한 번 commit된 경우에만 발송한다. 기존 2:2 신고 알림이나 라우트를 N:N에 재사용하지 않는다.
Mobile은 신청·승인·확정 취소·행사 취소 알림 77~79·81과 신청 완료 84를 행사 상세로 연결하고, 새 메시지·후기·
채팅 개방 알림 82·83·85를 채팅 이력으로 연결한다. 호환용 GROUP_MEETING_EVENT_CONFIRMED(80)은 기존 알림
재진입만 지원하고 신규 모집 마감 시 발송하지 않는다. GROUP_MEETING_CHAT_OPENED(85)는 행사 전날 KST 13시에
호스트와 현재 APPROVED 참가자에게 경계당 한 번 발송하며 행사 일시 변경으로 경계가 바뀌면 새 경계에서 다시
계산한다. 채팅이 열린 뒤 Admin이 참가자를 새로 승인하면 해당 참가자에게 승인 알림 78과 채팅 개방 알림 85를
함께 발송한다. 이 보충 알림은 새 승인자만 대상으로 하며 같은 경계의 기존 구성원에게 다시 발송하지 않는다.
Admin 승인과 cron은 채팅 구성원별로 처리한 개방 경계를 공동 중복 방지 기준으로 사용하므로, 즉발 처리된 새
승인자는 다음 cron 대상에서 제외된다. 행사 단위 개방 marker는 cron batch 요약이며 수신자 판정 기준이 아니다.
API·Admin·Mobile과 운영 DB의 반영 기준점은 2.3.0 릴리스 실행 기록에 보존한다.
활성 채팅 화면의 새 USER 메시지는 기존 회원 WebSocket의 group_meeting:message로 동기화한다. 이벤트는
canonical GroupMeetingChatUserMessageItem을 재사용하며 새 저장에만 현재 호스트·APPROVED 참가자와 발신자에게
전달한다. FCM 82는 사용자 알림과 단절 시 상태 갱신 보조 수단이고, 재연결·focus 복귀의 누락 복구 원천은 기존
채팅방·메시지 HTTP snapshot이다. USER 발신자의 read watermark가 새 메시지까지 전진하므로 전역 unread
invalidation에는 발신자도 포함한다. 상세 전송·병합 계약은 채팅 시스템의 N:N 절과 전역
unread 절을 따른다.
방별 새 메시지 알림을 끈 구성원도 메시지·WebSocket·전역 unread 대상에는 계속 포함하며, 82의 FCM과
t_alarm만 생략한다. 77~81·83~85는 행사 알림 설정과 각 발송 조건을 그대로 따른다.
API와 DTO 계약¶
- Swagger/OpenAPI가 21개 Mobile operation과 27개 Admin operation의 path/query/body 요청 DTO와 성공
dataDTO의 단일 SoT다. - contracts package는 기존 operation metadata, operation input type, 성공 data map, envelope type과 named
request/read DTO를 공개한다. 정원 2/20/40은 행사 생성·수정 operation의 generated
requestConstraints.groupMeetingCapacitymetadata에서 제공한다. - 재사용 body와 read model은
GroupMeetingCreateRequest,GroupMeetingVersionRequest,AdminGroupMeetingEventDetail처럼 package public entrypoint에서 직접 export한다. - Admin은 operation key와 generated DTO를 직접 소비한다. local wire DTO,
RequireExact, URI fallback, 응답 cast, normalize, 호환 adapter를 두지 않는다. - Admin request boundary는 operation metadata에 따라 path parameter를 인코딩하고 multipart body를
FormData로 직렬화한 뒤 strict envelope의ok를 분기한다. 이는 전송 책임이며 DTO 변환 계층이 아니다. POST /admin/group-meetings/{event_id}/delete는 같은 reason·version 요청과 상세 응답 계약 안에서 역할별 명령을 판정한다. 일반 클럽매니저는 신청이 없는 자신 소유 DRAFT를 정상 삭제하고, Super Admin은DELETED가 아닌 모든 상태를 강제삭제한다. 서버는 두 행위를 서로 다른 감사 action으로 기록한다.- Admin이 사용하는 호스트 식별자는 매니저 관리의 로그인 ID인
manager_user_id다. 내부 연결·Admin·회원의 숫자 PK는 요청 입력이나 운영자용 호스트 식별자로 노출하지 않는다. - Super Admin은
manager_user_id와 모바일 회원 이메일을 정확히 조회해 최초 호스트 연결을 만든다. 행사 생성은manager_user_id만 받고 API가 내부 연결을 해석하며, 연결 또는 공통 이용 자격을 충족하는 모바일 계정이 없으면 계약된 실패를 반환한다. Admin 화면에서 일반 클럽매니저는 로그인한 자신의manager_user_id를 자동으로 사용하고 입력란을 노출하지 않으며, Super Admin은 기존 호스트 연결 목록에서 주최자를 선택한다. group-meeting.host연결과t_member_manager_assignment의 클럽 배정은 책임이 다르다. 전자는 Admin 매니저와 모바일 호스트 회원을 연결해 작성 행사와 로그인 호스트를 식별하고, 후자는 일반 회원의 전담CHARGE·공유SHARE클럽을 기록해 목록·상세 노출 범위를 판정한다.- Mobile 전체 목록은 호출 회원의
CHARGE또는SHARE매니저가 주최한 행사만 반환한다. 화면에 내려주는 현재 상태 기준으로OPEN모집 중,CONFIRMED모집 마감,FINISHED·CANCELED순서로 묶는다.OPEN·CONFIRMED묶음은 가까운 행사 일시(event_at ASC) 순으로,FINISHED·CANCELED묶음은 최근 행사 일시(event_at DESC) 순으로 정렬한다. 같은 행사 일시 안에서는 최근 생성글(created_at DESC,id DESC)을 먼저 노출한다.DELETED는 전체·주최·참여 행사 목록과 전체 채팅 목록에서 반환하지 않는다. 직접 URL 상세도 호스트·신청자·같은 클럽 회원이 아니면 노출하지 않는다. - 채팅방 진입은
GET /group-meetings/{event_id}/chat한 건으로 행사, 호출자self, 승인 구성원members와 익명 공개 프로필, 최초 메시지 page, 종료 후기 상태, 읽기 전용 여부를 구조화해 반환한다.chat_member_id는 내 메시지 판별과 신고 대상, 참가자의application_id는 기존 앱의 무료 프로필 조회 호환 경로에만 사용한다. 실제member_id해석과 동일 행사 소속 검증은 API 내부 책임이며 Mobile DTO에 노출하지 않는다. 과거 메시지 추가 page, 메시지 전송·읽음·신고·나가기는 증분 조회 또는 동작 명령으로 분리한다. GET /group-meetings/{event_id}/chat/members/{chat_member_id}/profile과 그룹미팅 출처 카드 사전 검사·전달은 실제 채팅 개방 중에만 허용한다. 기존 앱 호환용 참가자 프로필 operation은 채팅 초기화 뒤 개방 전 조회를 유지하지만 실효OPEN·CONFIRMED에서만 허용한다. 두 프로필 operation 모두 종료·취소 이력 접근 권한을 전체 프로필 권한으로 확대하지 않는다.- 채팅방 응답의
self.message_notification_enabled가 현재 방 82 수신 선택을 제공한다.PUT /group-meetings/{event_id}/chat/message-notification은 strict boolean 하나로 현재 principal의 같은 값을 교체하고 적용된 값을 반환한다. 별도 조회 API·audit table·optimistic version은 두지 않는다. - 새 사용자 메시지의 WebSocket payload는 별도 public DTO나 계약 package version을 만들지 않고 기존
GroupMeetingChatUserMessageItem을 그대로 사용한다. 전송·읽음 write와 복구 read의 HTTP shape도 바꾸지 않는다. - 전체 채팅 첫 화면
GET /chat/chatList는 기존 매칭·2:2 미팅과 N:N 그룹미팅 채팅 첫 page를 한 응답에 집계한다. 그룹미팅 section 실패를 빈 목록으로 바꾸지 않는다. - Mobile 신청자 DTO와 Admin 운영 DTO는 분리한다.
GroupMeetingApplicantItem은 신청 문맥만 노출하고,AdminGroupMeetingApplicantItem만 운영에 필요한 회원 ID·이메일·탈퇴/취소 시각을 추가한다. POST /group-meetings/{event_id}/applications는 종료한 다른 N:N 행사에서 현재 APPROVED인 신청의 미작성 후기가 있으면MEETING_REVIEW_REQUIRED로 실패한다. 이 제한은 신규 신청에만 적용하고 후기 작성에 필요한 조회·동작에는 적용하지 않는다.- 성공 DTO generic은 compile-time 계약이다. runtime에서 검증하지 않은 성공 data를 별도 decoder가 보장하는 것처럼 단정하지 않는다.
- API·Admin·Mobile은 published latest stable contracts package를 exact pin하고 동일 DTO를 직접 소비한다. 현재 version 정렬은 세 레포 package manifest·lockfile로 판정하고 concrete version과 기준 ref는 릴리스 기록에 둔다. 이 문서에는 별도 normalize, ID fallback, local wire DTO를 추가하지 않는다.
대표 read model은 다음과 같다.
| DTO | 책임 |
|---|---|
GroupMeetingEventListItem |
행사, 호스트 요약, 신청 집계, 호출자 신청 상태 |
AdminGroupMeetingEventDetail |
행사 상세, ready 이미지, 신청 집계, 전체·미처리 신고 수, Admin 운영 정보 |
GroupMeetingApplicantItem |
신청 상태와 신청 당시 표시 정보 |
AdminGroupMeetingApplicantItem |
Admin 신청 운영용 회원 ID·이메일·탈퇴/취소 시각 |
GroupMeetingChatRoom |
행사별 호출자와 방별 새 메시지 알림 선택, 승인 구성원의 익명 공개 프로필, 최초 메시지, 종료 후기 상태와 채팅·신청 ID 경계 |
GroupMeetingChatMessageItem |
메시지, 송신자 역할, 운영 삭제 상태 |
GroupMeetingReviewState |
후기 작성 가능 여부, 기존 후기, 서버 보상값 |
Admin 운영 화면¶
- 역할별 최대 범위와 직접 URL/API 허용 기준은 보안/접근통제 정책을 단일 기준으로 사용한다. 아래는 그 정책을 적용한 현재 Admin 화면 구조다.
- 기존 2:2 운영 화면은
그룹미팅 관리아래의미팅 내역,채팅 내역,후기 내역,신고 내역,패널티 내역메뉴와/meeting/list,/meeting/chat,/meeting/review,/meeting/blame,/meeting/penalty라우트를 그대로 유지하되, 해당 상위 메뉴는 Super Admin에게만 노출한다. 일반 클럽매니저 사이드바에서는 기존 2:2 메뉴 전체를 숨긴다. - 신규 N:N은 기존 메뉴의 형제 위치에 별도
클럽 Host 단체미팅 관리상위 메뉴를 두고, 그 아래미팅 내역하나만 노출한다. 진입 라우트는/group-meeting/list이며 행사 상세에서 신청, 채팅, 후기, 신고, 프로필 열람 이력을 종속시켜 본다. 이 메뉴는 Super Admin과 일반 클럽매니저 모두에게 노출한다. - 행사 목록은 ID·호스트·제목·일시·장소·사진 공개·상태·미처리 신고 수·생성일 헤더에서 전체 필터 결과의
서버 ASC/DESC 정렬을 제공한다. 썸네일과 승인 수/정원이 함께 표시되는 복합 열은 정렬하지 않는다.
정렬을 선택하지 않으면 기존 활성 행사 우선·생성 역순을 유지하고, 컬럼이나 방향을 바꾸면 첫 page부터 다시
조회한다. 현재 page 배열만 클라이언트에서 재정렬하지 않는다. Super Admin 목록에는 별도
관리열을 두고DELETED가 아닌 각 행사 행에서 필수 사유를 받는강제삭제를 제공한다. groupMeetingChatUserReport알림은 미처리 신고가 있는 N:N 행사만 표시하는/group-meeting/list?pending_reports=1로 이동한다. 행사 목록은pending_report_count를 표시하고 해당 수를 누르면 행사 상세의 신고 탭을 바로 연다. 기존 2:2 신고 화면으로 연결하지 않는다.- 신고 탭은 신고자·피신고자의 현재 상태와 프로필을 표시하고 회원 상세 조회를 제공한다. Super Admin은 신고 처리·기각과 별도로 회원 차단 및 피신고자 미팅 패널티를 적용할 수 있고, 일반 클럽매니저는 자신이 담당하는 행사의 신고 내역만 읽는다.
- 동일 신고자의 같은
idempotency_key재전송은 원래 결과를 반환한다. 서로 다른 key는 반복 위반을 별도 사건으로 보존하며 각 신고를 독립적으로 처리·기각한다. - 일반 클럽매니저 화면은 자신이 소유한 행사만 조회·변경하고, Super Admin 화면은 최초 호스트 연결과 전체 운영 기능을 제공한다. 서버의 실제 허용 범위는 보안/접근통제 정책과 operation 인가 결과를 따른다.
- 일반 클럽매니저 상세에는 신청이 없는 DRAFT의
삭제만 노출한다. Super Admin은 상세가 아니라 목록의관리열에서DELETED가 아닌 행사에 필수 사유를 받는강제삭제를 실행한다. 완료 뒤에도 삭제 상태의 목록·상세와 감사 이력을 운영 확인용으로 유지한다. - 모임 만들기와 기존 모임 수정은 별도 중첩 모달 없이 같은
미팅정보폼을 사용한다. 일반 클럽매니저의 목록·상세·생성 화면에서는 내부 모임 ID와 매니저 ID를 숨기고 로그인 계정을 주최자로 자동 사용한다. Super Admin은 목록·상세에서 두 ID를 보고 검색할 수 있으며, 생성할 때 유효한 기존 호스트 연결 목록에서 주최자를 선택한다. 연결된 공통 이용 자격 충족 모바일 계정이 없으면 호스트 연결 관리가 필요하다는 안내를 표시한다. - 호스트 연결 관리는 매니저 ID와 모바일 회원 이메일을 사용한다. 내부 숫자 ID를 운영자가 찾아 입력하는 흐름은 제공하지 않으며 Super Admin에게만 노출한다.
미팅정보는 상태와 주최자를 각각 독립된 행으로 두고 제목, 인원수(남성·여성 정원), 참가비, 참여확정자 사진 공개, 장소, 태그, 행사 일시(신청 마감 동일), 내용, 이미지, 신고 수, 생성일 순으로 표시한다. 생성 전에는 존재하지 않는 상태·신고 수·생성일을 숨기고 모임 ID는 위 역할 기준을 적용한다. 태그 UI는 Space로#단어칩을 만들되 API에는 ASCII 공백 한 칸의 canonical 문자열을 그대로 보낸다.- 썸네일과 긴 상세 이미지는 인증된 upload operation만 사용하며 비이미지 파일을 성공으로 처리하지 않는다.
화면에서는 긴 상세 이미지를
소개글 이미지로 부르고 썸네일과 같은 하단 행의 2열에 표시하며 좁은 화면에서는 세로로 쌓는다. 상세의 신고 수는전체 N건 | 미처리 M건으로 표시하고 누르면 같은 상세의 신고 탭을 연다.
DB 계약 경계와 운영 기준¶
이 문서는 논리 상태, 관계, 불변조건만 설명한다. append-only migration source·checksum·schema lock·DB native
COMMENT와 환경별 기존 적용 이력은 coupler-api의 private 물리 계약이 단일 기준이다. 날짜별 적용 현황이나
물리 객체 수를 이 문서에 복제하지 않는다.
그룹미팅 runtime을 활성화하기 전에는 대상 환경의 pending migration 적용, 최종 schema 검증과 runtime smoke를 모두 통과해야 한다. group-meeting 논리 ID는 운영 API·Admin·Mobile과 대상 DB에 반영된 현행 모델이다.