Refactor: 권한/기수 시스템 리팩토링 - #562
Conversation
- roles 5개 canonical(MASTER/CORE/ORGANIZER/DEEPER/GUEST) 고정
- member_roles.cohort_id 컬럼 도입 및 활성 기수 기반 판정 로직
- cohorts.is_active + PATCH /v1/cohorts/{id}/activate 신설
- member_authorities/authorities 구시스템 제거
- SQL 마이그레이션 스크립트 prod/pending/20260913_*.sql 추가
- application/build.gradle.kts: mainClass를 CoreApplicationKt에서 CoreApplication으로 원복 (develop 시점 값) - prod/pending/20260913_*.sql 3개를 20260914_*.sql로 리네임 및 파일 내부 날짜 참조 갱신
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…le-system # Conflicts: # application/src/main/resources/application.yml # persistence/src/main/kotlin/core/persistence/member/repository/MemberRepository.kt
86a0a47 to
aa7628e
Compare
… 등)" This reverts commit 9a478ee.
4a16a9f to
ab96200
Compare
hwistlezz
left a comment
There was a problem hiding this comment.
고생 많으셨습니다!!
아래의 다중 기수 케이스 하나 확인해 주시면 감사하겠습니다!
| private fun isAssignmentEffective( | ||
| assignment: MemberRoleAssignment, | ||
| context: CohortRoleContext, | ||
| ): Boolean { | ||
| val isActiveMember = context.activeCohortId != null && context.activeCohortId in context.memberCohortIds | ||
| return when (assignment.roleName) { | ||
| RoleType.Master.code, RoleType.Guest.code -> true | ||
| RoleType.Core.code, RoleType.Organizer.code, RoleType.Deeper.code -> { | ||
| if (!isActiveMember) return false | ||
| assignment.cohortId?.value?.let { it in context.memberCohortIds } ?: isActiveMember | ||
| } | ||
| else -> false | ||
| } | ||
| } |
There was a problem hiding this comment.
현재 활성(active) 기수가 18기일 때,
이전 기수인 17기에서 운영진이었던 사람이 18기에 디퍼로 참여한 경우를 생각해봣습니다
회원의 기수 참여 이력이 17기와 18기를 모두 포함한다면,
현재 조건 assignment.cohortId?.value?.let { it in context.memberCohortIds } ?: isActiveMember 에서는
17기 운영진의 cohortId도 memberCohortIds에 포함돼서, 이전 기수인 17기 운영진 권한을 유효하게 판정할 수 있을 것 같아요
위의 예시의 이해가 쉬우시도록 표로 정리해봤습니다
| 검사 항목 | 17기 운영진 | 18기 디퍼 |
|---|---|---|
| 역할에 연결된 기수 | 17기 | 18기 |
| 그 기수가 회원의 참여 이력에 포함되는가? | 예 | 예 |
| 현재 코드의 역할 판정 | 유효 | 유효 |
| 현재 활성 기수인 18기 역할만 인정한다면 | 제외되어야 함 | 인정 |
여기에 역할의 기수가 회원의 기수 참여 아력에 있는지보다, 활성 기수와 같은지를 비교하는 방법은 어떨까요??
There was a problem hiding this comment.
리뷰 감사합니다!
말씀해주신대로 현재 조건은 유저의 전체 참여 이력에 포함되기만 하면 유효로 판정하고 있어서, 포함 여부가 아니라 활성 기수와의 일치 여부로 비교하도록 수정했습니다. (in → ==)
@uykm 님 리뷰주신 사항들 반영해보았습니다.
|
…le-system # Conflicts: # application/src/main/kotlin/core/application/member/application/service/auth/EmailPasswordAuthService.kt


Summary
1. 개요
역할을
MASTER/CORE/ORGANIZER/DEEPER/GUEST다섯 가지로 고정하고,member_roles.cohort_id와cohorts.is_active로 역할과 기수를 분리합니다. 활성 기수는application.yml하드코드가 아닌 DB와 전환 API만으로 관리합니다. 구시스템 테이블(member_authorities,authorities)을 제거하며,PATCH /v1/roles/members/{memberId}요청 body가 Breaking change입니다. 로컬 스모크 테스트 16개 케이스를 통과하였습니다.2. 배경
기존 시스템에서 다음 문제가 확인되었습니다.
roles테이블에 row가 추가됩니다("17기 운영진","18기 디퍼"등).role_permissions매핑이 실질적인 구분 없이 모든 role에 36개 권한 전체를 부여합니다.member_authorities,member_roles,member_permissions세 테이블에 분산되어 있습니다.member_cohorts에 유니크 제약이 없어 실제 중복 데이터가 존재합니다.application.yml(하드코드)과getLatestCohort()(DB 조회) 두 가지로 혼재되어 있습니다.이번 리팩토링은 표준 RBAC 구조를 유지한 상태에서 역할을 다섯 가지로 고정하고, 기수 정보를 별도 컬럼으로 분리하여 위 문제들을 해소합니다.
3. 주요 변경 사항
3-1. 도메인 및 엔티티
RoleType에Master추가, alias map에"master","마스터"등록RoleType.ktMemberRoleaggregate에cohortId: CohortId?추가MemberRole.ktMemberRoleAssignmentVO 신설MemberRoleAssignment.kt(roleName, cohortId)튜플 기반 판정 로직을 순수 함수화Cohortaggregate에isActive,activatedAt추가Cohort.ktCohortEntity/MemberRoleEntity매핑 반영MemberPermissionEntity에 "READ 되지 않음" 안전장치 주석MemberPermissionEntity.ktMemberAuthorityPersistencePort삭제3-2. 서비스 계층
CurrentCohortRoleResolver.isAssignmentEffective()에 새 판정 규칙 구현CurrentCohortRoleResolver.ktROLE_PRIORITY최상위에Master배치CohortRoleService.createLatestCohortRoles()비활성화CohortRoleService.ktCohortCommandService.activateCohort()단일 active 보장 트랜잭션CohortCommandService.ktCohortRepository.activate()에서deactivateAll후activateCohortRepository.ktCohortQueryService.getActiveCohort()를 DB 조회로 통일CohortQueryService.ktapplication.yml의cohort.value하드코드 제거MemberAuthorityService,CohortProperties삭제AfterPartyCommandService의when(roleType)에Master케이스 추가AfterPartyCommandService.ktwhen유지판정 규칙 요약
MASTER: 기수 무관 항상 유효CORE/ORGANIZER/DEEPER: 활성 기수 참여자만 유효 (미참여 시 필터링·자동 회수)GUEST: 로그인만 가능, 부여 권한 0개활성 기수 업데이트 경로는
PATCH /v1/cohorts/{cohortId}/activateAPI 하나뿐입니다.application.yml하드코드는 완전히 제거되었습니다.3-3. 컨트롤러 및 API
GET /v1/cohorts/active,PATCH /v1/cohorts/{cohortId}/activateCohortAdminController.kt,CohortAdminApi.ktUpdateMemberRoleRequestbody를{cohort, isAdmin}에서{roleType, cohortId}로 변경UpdateMemberRoleRequest.ktroleType정규식에 MASTER 포함ConvertDeeperToOrganizerRequest삭제/v1/members/authority/organizer정리3-4. 인프라 및 설정
application.yml,application-local.yml에서cohort.value하드코드 제거prod/pending/20260913_*.sql4. API 변경 요약
4-1. Breaking change (요청 body 형식)
PATCH /v1/roles/members/{memberId}요청 body 형식이 변경되었습니다.{ "cohort": "17", "isAdmin": true }{ "roleType": "MASTER | CORE | ORGANIZER | DEEPER | GUEST", "cohortId": 17 }roleType유효값은MASTER|CORE|ORGANIZER|DEEPER|GUEST입니다.roleType: 필수 입력값입니다)이 반환됩니다.ORGANIZER,DEEPER에 대해서만 실제 부여를 허용합니다.MASTER/CORE부여는 별도 슈퍼어드민 API로 관리할 예정입니다.4-2. 삭제된 endpoint
PATCH /v1/members/authority/organizerGLOBAL-404-01)대체:
PATCH /v1/roles/members/{memberId}에{ "roleType": "ORGANIZER", "cohortId": <기수 ID> }로 호출합니다.4-3. 신규 endpoint
GET /v1/cohorts/activePATCH /v1/cohorts/{cohortId}/activateupdate:cohort활성 기수 전환은 트랜잭션 내에서 기존 활성 기수 해제와 새 활성 기수 지정을 원자적으로 수행합니다.
5. DB 마이그레이션
5-1. 파일 구성
prod/pending/20260913_backup_member_roles.sqlmember_roles에legacy_role_id,legacy_role_name추가 및 원본 값 복사prod/pending/20260913_role_system_seed.sqlcohorts.is_active/activated_at,cohort_id=0특수 슬롯; roles 다섯 개 시드와role_permissions매트릭스(MASTER 23, CORE 23, ORGANIZER 17, DEEPER 7, GUEST 0);member_roles.role_id재매핑;member_cohorts중복 제거 및 UNIQUE;member_authorities/authorities아카이빙 후 DROPprod/pending/20260913_schema_alignment.sqlmember_roles.cohort_id및 인덱스;legacy_role_name파싱으로cohort_id채우기; canonical 이외 roles 아카이빙 후 삭제;member_cohorts.cohort_valueDROP각 파일 하단에 읽기 전용 VERIFY 섹션과 역순 복구용 ROLLBACK 주석이 포함되어 있습니다.
5-2 롤백 절차
각 SQL 파일 하단의 ROLLBACK 주석에 포함된 쿼리를 역순으로 실행합니다.
20260913_schema_alignment.sql롤백 —_archive_roles_20260913테이블에 의존합니다.20260913_role_system_seed.sql롤백 —_archive_member_authorities_20260913,_archive_authorities_20260913와legacy_role_id를 이용해 원본 역할을 복원합니다.20260913_backup_member_roles.sql롤백 — legacy 컬럼을 DROP합니다.주의:
role_permissions원본 매트릭스는 별도 스냅샷이 없으면 완전 복원이 불가합니다.6. FE 대응 필요 사항
PATCH /v1/roles/members/{memberId}요청 body를 신 형식(roleType,cohortId)으로 교체