구독 플랜별 팀 멤버 상한선 강제 적용과 경계 케이스 처리
목차
팀 허브에서 구독 플랜별 멤버 상한선을 강제하는 로직을 추가했다. 기본값 5명 기준으로 시작했는데, 이 작업이 단순해 보이지만 구독 시스템과 팀 관리의 경계에서 꽤 중요한 결정들이 섞여 있었다.
플랜 기반 제약을 왜 이제야?
사실 서비스 초기엔 "팀에 몇 명까지 추가할 수 있다"는 개념 자체가 명확하지 않았다. 모든 사용자가 자유롭게 멤버를 초대할 수 있었고, 그게 문제가 되지 않는 줄 알았다. 하지만 시간이 지나면서 두 가지가 명확해졌다. 하나는 비즈니스 관점—구독 플랜마다 다른 가격대와 가치 제안이 있는데, 그걸 기술적으로 강제하지 않으면 결국 모든 사용자가 최고 가치를 경험하게 되고, 다른 하나는 운영 관점—"몇 명까지 초대할 수 있냐"는 고객 문의가 계속 들어오고 있었다. 정책이 불명확하면 사용자도 헷갈리고 팀은 일관성 있게 대답할 수 없다.
엔타이틀먼트 레이어에서 처리
변경 파일 세 개를 봐야 한다. src/domain/entitlements.ts는 비즈니스 규칙의 중심부다. 여기서 "특정 플랜은 최대 5명"이라는 제약이 정의되고 검증된다. 일반적으로 이런 제약은 데이터베이스 제약만으로는 부족한데, 왜냐하면 사용자 초대 흐름이 여러 단계(초대 생성 → 승인 → 멤버 추가)를 거치기 때문이다. 중간 어느 지점에서라도 "이 플랜은 더 이상 초대할 수 없다"고 판단할 수 있어야 한다.
src/interface/server/server.ts는 API 계층인데, 여기서 초대나 멤버 추가 요청이 올 때 제약을 먼저 확인한다. 사용자가 버튼을 클릭했을 때 "죄송합니다, 이 플랜은 최대 5명까지만 추가할 수 있습니다"라고 즉시 응답할 수 있게 하려면, HTTP 400 레벨에서 걸러내는 게 맞다. 데이터베이스까지 갔다가 유니크 제약으로 실패하는 것보다 훨씬 낫다.
// 의사코드: 플랜별 제약 확인 패턴
if (currentMemberCount >= plan.maxSeats) {
throw new Error('Seat limit exceeded for this plan');
}
테스트로 경계 케이스를 다 잡아야 함
src/interface/server/team-usage.test.ts가 있다는 건 단순히 "5명까지 되는지 확인"하는 테스트가 아니라, 그 경계에서 벌어질 수 있는 여러 시나리오를 테스트했다는 뜻이다.
- 4명 상태에서 1명 초대: 성공
- 5명 상태에서 1명 초대: 실패
- 한 명이 탈퇴한 직후 새 초대: 성공해야 함 (이게 놓치기 쉬운 부분)
- 초대 승인 대기 중인 멤버도 상한선에 포함되는가? (정책에 따라 다름)
마지막 케이스가 중요한데, 팀에 따라 "초대만 했지만 아직 들어오지 않은 사람"을 상한선 계산에 포함할지 말지가 다르다. 우리의 경우 포함하기로 결정했는데 (초대 승인이 시간 문제니까), 이건 API 응답 설계와도 연결된다.
// 클라이언트에서 "사용 중인 자리"를 표시하려면
{
maxSeats: 5,
currentMembers: 3,
pendingInvites: 2,
availableSeats: 0 // = maxSeats - currentMembers - pendingInvites
}
플랜 변경 시나리오
이번에 제약을 강제했으니, 이제 다른 문제가 생긴다. 만약 팀에 5명이 있는데, 사용자가 더 저렴한 플랜으로 다운그레이드하면 어떻게 할 것인가? 또는 그 반대로 업그레이드했을 때는? 이건 이번 커밋에는 직접 담기지 않았을 테지만, 앞으로 처리해야 할 엣지 케이스다.
실제로는:
- 다운그레이드 시: 현재 멤버가 상한선을 초과하면 "이 플랜으로 변경하려면 먼저 멤버를 제거해야 함"이라고 알려줘야 한다.
- 업그레이드 시: 즉시 적용.
이건 UX 관점에서도 중요한데, 고객이 "어라, 왜 초대가 안 되지?"라고 당황하지 않으려면, 팀 관리 페이지에서 "현재 사용 중인 자리 3/5"라고 명확하게 표시해야 한다.
회고: 작은 제약이 큰 시스템
"팀 최대 5명" 같은 작은 결정이 실은 여러 계층을 타고 흐른다. 도메인 규칙 → 엔티티 검증 → API 응답 형식 → 클라이언트 UI. 하나라도 일관성 있게 처리하지 않으면 사용자 입장에서 혼란스럽다. 그래서 테스트를 충분히 작성해야 하고, 특히 경계 케이스(정확히 5명일 때, 6번째 초대 시도)에서 동작을 명확히 해야 한다.
팀 리딩 관점에서도, 이런 비즈니스 제약은 개발자 혼자 판단하기보다 PO/비즈니스와 함께 "초대 대기 중인 사람을 몇 명까지 예약할 수 있나", "플랜 변경 시 어떻게 transition할 건가" 같은 걸 먼저 정의해야 한다. 나중에 "아, 그 시나리오 안 했네"라고 돌아오는 게 가장 비효율적이니까.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.