← 개발 로그 목록

website: SUPER_ADMIN/ADMIN 2단 역할을 없애고 관리자 권한을 하나로 합치기

/ 4분 분량 / 개발 로그

`feat/frontend-test-setup` 브랜치를 `dev`에 맞춰 머지하면서, 그 사이 `dev`에서 진행된 관리자 권한 구조 변경을 통째로 받아왔다. 핵심은 `SUPER_ADMIN`/`ADMIN` 2단 역할을 없애고 관리자 권한을 `ADMIN` 하나로 합친 것이었다.

원래 구조는 최고관리자(SUPER_ADMIN)만 계정 발급·역할 변경·삭제 같은 운영 작업을 할 수 있고, 일반 ADMIN은 그보다 제한된 권한을 가지는 2단 체계였다. product-map.md의 이전 버전을 보면 이 구조가 나온 배경도 있다 — 블로그 글쓰기 신원 문제를 풀다가 콘텐츠 소유권을 분산시키려고 멤버 표면을 새로 만들면서, 관리자 쪽도 역할을 나눠뒀던 흔적이다. 그런데 실제로 운영해보니 최고관리자 승계(#142)나 역할 변경 같은 기능이 오히려 화면과 운영 복잡도만 늘리는 쪽으로 작용했던 모양이다. pm/qa/verification/최고관리자-승계.md 같은 검증 문서가 삭제된 것도 이 판단을 뒷받침한다.

바뀐 구조는 단순하다. 관리자는 모두 같은 ADMIN 권한을 갖고, 마지막 남은 관리자 한 명만 삭제하지 못하게 막는다(LastAdminException, 409 LAST_ADMIN). 예전엔 "마지막 SUPER_ADMIN"만 보호하면 됐는데, 이제는 "마지막 관리자" 자체를 보호해야 하니 LastSuperAdminException이 LastAdminException으로 바뀌고 관련 가드 로직도 단순해졌다. AdminManagementControllerTest에서 역할 변경(changeRole) 관련 테스트 4개가 통째로 사라진 것도 같은 맥락이다 — 승급/강등이라는 개념 자체가 없어졌으니 테스트할 대상도 없어진 셈이다.

이 변경이 DB 마이그레이션까지 건드리는 게 눈에 띄었다. V20260730015219__drop_admin_role.sql이 admins 테이블에서 role 컬럼과 그 CHECK 제약을 제거하는데, SQLite라 컬럼 하나 지우는 게 "새 테이블 생성 → 데이터 복사 → 기존 테이블 DROP → RENAME" 패턴으로 이루어진다. 이 패턴의 위험은 복사할 컬럼 목록을 하나라도 빠뜨리면 그 값이 조용히 유실된다는 점이다. 그래서 AdminRoleDropUpgradeTest를 추가해서, V7까지 적용한 DB에 기존 SUPER_ADMIN 관리자 행을 하나 심어두고 최신 마이그레이션까지 올린 뒤 email·password_hash·name·failed_login_attempts·locked_until·created_at·updated_at이 전부 그대로 남아있는지, 그리고 role 컬럼 자체는 조회가 안 되는지(존재하면 실패하도록) 확인하게 만들었다. 스키마를 바꾸는 마이그레이션은 로직 테스트보다 이런 업그레이드 경로 테스트가 훨씬 실질적으로 느껴진다.

env 변수 이름도 같이 정리됐다. ADMIN_SEED_SUPER_ADMINS가 ADMIN_SEED_ADMINS로, ADMIN_E2E_SUPER_ADMIN_*이 ADMIN_E2E_ADMIN_*로 바뀌면서 옛 이름에 대한 fallback도 제거했다(#297). 문서에 이 부분을 굳이 남겨둔 이유가 있다 — stage/prod .env가 아직 옛 이름을 쓰고 있으면 배포 전에 옮기지 않는 한 관리자 시드가 "에러 없이 조용히 비어서" 실행된다는 경고다. fallback을 없앤 대신 이런 함정을 문서에 명시해둔 게, 코드로 막을 수 없는 배포 순서 문제를 문서로라도 막아보려는 시도로 보인다.

admin-auth-module.md 문서 자체도 꽤 많이 걷어냈다. 역할 체크가 어떻게 동작하는지(hasRole → ROLE_ 접두어) 같은 상세 설명, IP 기반 요청 제한이나 refresh 토큰 로테이션 미구현 같은 "아직 못 메꾼 빈틈" 표, 코드 리뷰 체크리스트까지 원래 문서에 꽤 길게 있었는데 이번에 전부 정리하고 인증 흐름·초대와 계정 유지·새 기능 붙이는 법·보안 불변식 정도로 압축했다. 역할이 하나로 줄면서 설명해야 할 분기 자체가 줄어든 영향이 크다. 다만 이 정도로 줄인 문서가 나중에 다시 필요해질 정보(왜 IP 제한을 안 넣었는지 같은 트레이드오프 기록)까지 지운 건 아닌지는 다음에 필요할 때 git log로 다시 찾아봐야 할 것 같다.

product-map.md도 표면 구조 설명을 다시 썼는데, 예전 버전은 "왜 3면인가"를 결정 과정 서술로 풀었다면 지금은 결과만 담백하게 정리하는 쪽으로 바뀌었다. 그리고 이 문서 자체가 이제 정본이 아니라 서비스 위키(정보구조와 권한)가 정본이고 이 파일은 구현 구조 설명용이라고 명시한 부분도 눈에 띈다. 정책 문서와 구현 문서를 분리해두면 정책이 바뀔 때마다 코드 근처 문서까지 매번 크게 갈아엎을 필요가 없어질 것 같다.