Skip to content

[#709] 업무·하위 업무 담당자 자격을 등록·수정에서 검사하고, 후보 라벨에 운영진 역할을 싣는다 - #710

Merged
bell-person-ii merged 1 commit into
developfrom
fix/#709
Nov 11, 2026
Merged

bell-person-ii merged 1 commit into
developfrom
fix/#709

Conversation

@bell-person-ii

Copy link
Copy Markdown
Contributor

🍀 이슈 번호

📎 근거

  • 근거: ssccops#628

🎯 목적

업무·하위 업무 담당자로 그 일을 처리할 수 없는 회원을 지정할 수 있었다.

  • 담당자 후보 목록(GET /v1/members/assignable?authority=)만 권한으로 걸렀다([FIX] 운영관리 접근 권한 수정 #101). 등록·수정은 MemberService.findAssignableMember(탈퇴·제명만)로만 봤다. 그래서 업무도 API·MCP로는 아무나 담당자가 됐다
  • 하위 업무 후보는 웹이 authority를 주지 않아 활동 회원 전원이었다(web#831). 역할이 없는 회원과 리더 역할만 가진 회원은 WORK_READ가 없어서, 지정돼도 자기 건을 열지 못한다(403)
  • 업무 후보 7명은 dev 실측으로 모두 운영진이었다. 그런데 라벨이 «대표 역할, 없으면 등급»이라 «정회원»·«트랙장»으로 찍혀 역할 없는 회원처럼 보였다

📝 변경 사항

  • OperationOwnerPolicy — «누구를 담당자로 지정할 수 있는가»의 유일한 구현. 활동 회원이면서 업무는 WORK_MANAGE, 하위 업무는 WORK_READ를 오늘 행사할 수 있어야 한다. 재료는 후보 목록과 같은 두 규칙(findAssignableMember · AuthorityPolicy.hasAuthority)이라 «목록에는 있는데 등록하면 거절»이 생기지 않는다
  • WorkServiceImpl·SubWorkServiceImpl이 MemberService 대신 이 정책을 받는다. 둘 다 MemberService를 담당자 판정에만 썼으므로 생성자 인자 수는 그대로다. 회의 안건 승격(/promote·/promote-sub-work)은 등록 서비스를 부르므로 따라온다
  • 수정은 담당자가 바뀔 때만 판정한다. 같은 담당자를 다시 보내면 탈퇴·제명·임기 종료여도 그대로 둔다. 임기가 끝난 국장이 맡았던 완료 업무의 총평을 쓸 때 «누가 맡았나»를 지우지 않게 하려는 것이다. 웹 수정 화면도 «현재 담당자는 후보에 없어도 그대로 저장»을 전제한다(isAssignablePick). 이전에는 탈퇴한 현재 담당자를 그대로 보내면 400이었다
  • 거절 코드 WORK_OWNER_NOT_ELIGIBLE·SUB_WORK_OWNER_NOT_ELIGIBLE(400 VALIDATION_FAILED). «활동 회원 아님»(OWNER_NOT_ACTIVE_MEMBER)과 고칠 길이 달라 enum만 나눴다. 코드 문자열은 정의서에 없는 값을 만들지 않으려고 그대로다
  • AssignableMemberResponse.displayRoleName — 목록 한 줄에 적을 역할명. 운영진 역할(LeaderRole이 아닌 역할)을 먼저 싣는다. 순서는 ① 대표가 운영진 역할이면 그것 ② 운영진 역할 중 표시 순번이 가장 앞선 것 ③ 대표 역할 ④ null. representativeRoleName은 lms 팀원 추가 시트가 «대표 역할»로 읽으므로 뜻을 바꾸지 않았다. 응답에 필드를 더하는 것이라 계약을 깨지 않는다
  • domain/operation/AGENTS.md·domain/member/AGENTS.md에 규칙과 시험 함정을 적었다

🚫 검토했으나 택하지 않은 길

후보 왜 안 되는가
역할 이름으로 가른다(리더 셋을 뺀 역할이 하나라도 있으면 통과 — 요청 원문에 가까운 안) WORK_READ가 없는 역할의 회원이 담당자가 되면 자기 건을 열지 못한다. 판정이 권한과 역할 이름 두 벌이 된다(ADR-0067). 기본 시드에서는 WORK_READ 보유자가 곧 리더 셋을 뺀 운영진이라 결과는 같다. 사용자가 권한 기준을 골랐다
WorkServiceImpl·SubWorkServiceImpl에 AuthorityPolicy를 직접 주입한다 같은 판정이 두 서비스에 복사된다. 요구 권한·거절 코드·«그대로면 판정 안 함»이 한 곳에 있어야 회의 책임자를 넣을 때도 한 줄로 끝난다
MemberService.findAssignableMember(id, authority) 단건판(Optional) 빈 값으로는 «활동 회원 아님»과 «권한 없음»을 가를 수 없다. 오류를 나누려면 두 번 불러야 한다
수정도 매번 자격을 본다 임기가 끝나면 그가 맡았던 건은 담당자를 바꾸기 전까지 고칠 수 없다. 완료 업무의 기록이 바뀐다
representativeRoleName에 운영진 역할을 싣는다 이름과 값이 어긋나고 lms의 읽기가 바뀐다
업무 담당자도 WORK_READ로 넓힌다 #101 «업무 담당자는 국장 이상»을 뒤집는다. 사용자가 유지를 골랐다

💬 리뷰어에게

  • 시험 22개 파일이 바뀐 것은 픽스처 때문이다. 업무·하위 업무를 만드는 시험의 담당자가 역할 없는 회원이었다. 담당자에게 시드된 국장을 붙였는데, 국장은 투표(APPROVAL_VOTE)와 국장 결재까지 준다. 그래서 승인·투표 자격을 세는 두 시험(ApprovalServiceImplTest·SubWorkApprovalVoteServiceTest)만 WORK_MANAGE만 가진 MemberRoleFixture.WORK_OWNER를 쓴다. 생성자 호출 13곳은 memberService → new OperationOwnerPolicy(memberService, authorityPolicy) 기계적 치환이다
  • 동작이 바뀌는 자리 하나: 탈퇴·제명된 현재 담당자를 그대로 두는 수정이 400 → 200이 된다. 위 «수정은 담당자가 바뀔 때만» 항목의 의도된 결과다
  • 회의 책임자(MEETING_MANAGE)도 «목록만 거르고 서버는 안 본다»는 같은 구조다. ssccops#628 범위 밖이라 두었다(OperationOwnerPolicy.Kind에 한 줄 + MeetingServiceImpl)

📋 확인

  • base 브랜치가 develop인가 (main은 릴리스 전용이다)
  • 이미 적용된 마이그레이션 파일을 고치지 않았다 — 스키마 변경 없음
  • 되돌리기 어려운 결정이 섞여 있다면 ADR로 남겼다 — 되돌리기 쉬운 판정 변경이라 ADR 없이 AGENTS.md·ssccops#628에 남겼다

로컬 확인: spotlessApply · 관련 패키지 시험(operation · member · notification · share · mcp · audit · security 93개 클래스) 통과 · checkstyleMain checkstyleTest spotlessCheck · 전체 test 223개 클래스 중 Testcontainers 두 클래스(FlywayMigrationValidateTest·ProposalFormTrackOptionMigrationTest)만 «Could not find a valid Docker environment»로 실패 — 이 기기에 Docker가 없다. 스키마 변경이 없으므로 CI에 맡긴다

📌 참고 사항

ssccops#628. 담당자 후보 목록(GET /v1/members/assignable?authority=)만 권한으로 걸렀고, 등록·수정은
MemberService.findAssignableMember(탈퇴·제명)만 봤다. 그래서 API·MCP로는 누구든 업무 담당자가
됐고, 하위 업무 후보는 웹이 authority를 주지 않아 활동 회원 전원이었다.

- OperationOwnerPolicy(«누구를 담당자로 지정할 수 있는가»의 유일한 구현): 활동 회원이면서 업무는
  WORK_MANAGE, 하위 업무는 WORK_READ를 오늘 행사할 수 있어야 한다. 재료는 목록과 같은 두 규칙
  (findAssignableMember · AuthorityPolicy.hasAuthority). WorkServiceImpl·SubWorkServiceImpl은
  MemberService 대신 이 정책을 받는다(둘 다 MemberService를 담당자 판정에만 썼다). 안건 승격은
  등록 서비스를 부르므로 따라온다.
- 수정은 담당자가 바뀔 때만 판정한다. 같은 담당자를 다시 보내면 탈퇴·제명·임기 종료여도 그대로
  둔다 — 완료 업무의 총평을 쓰려고 «누가 맡았나»를 지우지 않게.
- 거절 코드 WORK_OWNER_NOT_ELIGIBLE·SUB_WORK_OWNER_NOT_ELIGIBLE(400 VALIDATION_FAILED). 활동 회원이
  아님(OWNER_NOT_ACTIVE_MEMBER)과 고칠 길이 달라 enum을 나눴다.
- AssignableMemberResponse.displayRoleName: 운영진 역할(LeaderRole이 아닌 역할)을 먼저 싣는 표시용
  역할명. 대표 표시만 보면 운영진이 «정회원»·«트랙장»으로 찍혀 «역할 없는 회원이 후보에 있다»로
  읽혔다(dev 실측). representativeRoleName은 lms가 «대표 역할»로 읽어 그대로 둔다.
- 시험: 새 규칙(역할 없음·국원·스터디장 거절, 국원은 하위 업무 담당 가능, 담당자 교체는 판정,
  그대로면 통과), displayRoleName 우선순위. 업무를 만드는 기존 시험의 담당자에게 역할을 붙였다
  (승인·투표 시험은 WORK_MANAGE만 가진 MemberRoleFixture.WORK_OWNER — 국장은 투표권까지 준다).

기각: 역할 이름(리더 셋 제외)으로 가르기 — WORK_READ 없는 역할의 담당자는 자기 건을 못 열고
판정이 두 벌이 된다(ADR-0067). 회의 책임자는 같은 구조지만 범위 밖이다.
@github-actions github-actions Bot added size/XL PR size: XL fix 버그 수정 labels Nov 11, 2026
@bell-person-ii bell-person-ii self-assigned this Nov 11, 2026
@bell-person-ii
bell-person-ii merged commit 493380f into develop Nov 11, 2026
8 checks passed
@bell-person-ii
bell-person-ii deleted the fix/#709 branch November 11, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix 버그 수정 size/XL PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FIX] 업무·하위 업무 담당자 자격을 등록·수정에서 검사하고, 후보 라벨에 운영진 역할을 싣는다

1 participant