RETROSPECTIVE · 회고
하드코딩된 역할 체크를 '행위 단위 권한키'로 바꾸기
Backend
관리자 기능 곳곳에 if (isAdmin(user) || isCsManager(user)) 같은 조건이 서비스, 컨트롤러, JSP에 흩어져 있었다. 특정 담당자에게 기능 하나만 열어주려 해도 코드를 고치고 배포해야 했고, 어떤 사람이 무엇을 할 수 있는지 한눈에 볼 방법도 없었다.
역할이 아니라 행위에 이름을 붙인다
"누가(Role)"를 코드에 박는 대신 "무엇을(Action)"에 키를 붙이고, 누가 그 키를 갖는지는 데이터로 관리하도록 바꿨다.
coupon.master.write: 쿠폰 등록delivery.comment.edit: 배송 비고 수정member.account.edit: 회원 계좌 정보 수정
코드는 "이 행위를 할 수 있는가"만 묻는다.
if (!authService.hasPermission(memberId, "coupon.master.write")) {
throw new AccessDeniedException("coupon.master.write");
}
<auth:has key="delivery.comment.edit">
<button id="btnEditComment">비고 수정</button>
</auth:has>
데이터 구조
- 권한키 테이블: 키, 설명, 사용 여부
- 매핑 테이블: 권한키 × 대상(역할 / 팀 / 그룹 / 특정 회원)
hasPermission은 회원의 역할, 소속 팀, 그룹을 모은 뒤 매핑 중 하나라도 있으면 허용한다. 개인 단위 예외도 같은 구조로 처리된다.
마이그레이션 전략
- 최고 관리자 체크는 OR 조건으로 남겨둔다. 새 구조에 매핑이 빠져도 최소한 기존 관리자는 막히지 않는다.
- 기존 하드코딩 허용 목록을 그대로 매핑 데이터로 옮겨서 전환 전후 동작이 같게 만든다.
- 화면(버튼 숨김)과 서버(API 검증) 양쪽에 적용한다. 화면만 가리면 요청을 직접 보내서 우회할 수 있다.
조회 비용
요청마다 매핑을 조회하면 부담이 되므로, 로그인 시점에 권한키 Set을 만들어 세션에 두거나 짧은 TTL로 캐시한다. 권한 변경이 즉시 반영돼야 하는 경우와 트레이드오프라 캐시 무효화 시점을 같이 정해야 한다.