아티클을 읽고 권한 관리 로직에 대한 고민

지인·2023년 11월 20일
0

ETC

목록 보기
11/11
post-thumbnail

다음 글은 프론트엔드 유저 권한 정책 유연하게 관리하기을 읽고 고민해본 것에 대한 내용을 담고 있습니다.

그냥 사용하는 것이랑 대체 뭐가 다른가?

여기서 그냥 사용한다는 것은 단순히 페이지에서 useRecoilValue로 전역상태를 불러와서 아래와 같이 사용한다는 것을 뜻한다. 왜냐, 어차피 기능이 동작하는 것은 똑같기 때문에 이런 생각이 들 수 있다.

{canAccess사장실 && <사장실출입버튼 />}	
{퇴사 && <사장실출입버튼 />}

{userRole === "사장" && <사장실출입버튼 />}
{!userRole === "사장" && <퇴사버튼 />}

1-1. 확장성

이런 경우를 생각해보자, 프로젝트가 점점 커지면서 점점 할 작업들이 많아지고 있던 중, 다음과 같은 요구사항을 만나게 된다.

"사장님"이라는 권한을 새로 만들어서 기존의 "관리자"가 사용할 수 있는 기능은 모두 사용하고 추가로 "사장님"만 사용할 수 있는 버튼이 생길 예정입니다.

만약, 각 페이지마다 권한을 확인하도록 했다면 모든 페이지를 돌며 아래와 같이 코드를 수정하는 작업을 해야할 것이다.

{userRole === "관리자" && userRole ==="사장님" && <관리자버튼 />}

반면, 중앙에서 권한을 관리했다면, 아래 코드 하나만 수정하면 끝날 것이다.

get canAccess사장실() {
  return this.eligible(['사장', '관리자']);
}

두번째 경우. 프론트와 백엔드 모두 JS를 사용할 때나 프로젝트들을 모노레포로 관리하고 있을 경우, 권한 관리 자체를 해당 파일에서 모두 처리하고 각 프로젝트마다 일관성 있게 권한을 관리할 수 있을 것이다. 이로 인해 코드의 재사용성을 높이고 중복을 크게 제거할 수 있을 것으로 기대된다.

  • 여러 프로젝트에서 동일한 권한 체크 로직을 활용할 수 있다.
  • 프로젝트가 성장하고 새로운 서브 프로젝트가 추가될 때, 이미 구축된 권한 관리 시스템을 쉽게 확장하고 적용할 수 있다.

1-2. 에러 분기에 대한 장점

리액트의 ErrorBoundary에서 공통적인 오류(권한이 null인 경우)에 대해서도 일괄적으로 처리할 수 있도록 해줄 수 있음은 물론이다. (이로 인해 오류에 대한 처리를 ErrorBoundary로 넘겨 로직의 분리를 더욱 세분화해줄 수 있다.

고민해볼 다른 부분

함수형으로 사용하는게 맞을까 객체지향으로 사용하는게 맞을까

객체지향형으로 만든 코드는 아래와 같고,

type RoleType = '사장' | '매니저' | '알바';

class PermissionPolicyChecker {
  private static instance: PermissionPolicyChecker;

  // 싱글톤 패턴 적용
  static getInstance(role: RoleType) {
    if (!PermissionPolicyChecker.instance) {
      PermissionPolicyChecker.instance = new PermissionPolicyChecker(role);
    }
    return PermissionPolicyChecker.instance;
  }

  /**
   * 권한은 크게 두 종류로 나눌 수 있습니다.
   * 1. 해당 권한인 경우에만 가능한 것
   * 2. 해당 권한인 경우에만 불가능한 것
   *
   * 이를 위해 메서드를 두 가지로 분리했습니다.
   */
  private readonly _role: RoleType;

  constructor(role: RoleType) {
    this._role = role;
  }

  // 이 권한을 가진 경우만 가능합니다.
  private eligible = (roles: RoleType[]) => {
    return roles.includes(this._role);
  };

  // 이 권한을 가진 경우만 불가능합니다.
  private ineligible = (roles: RoleType[]) => {
    return !roles.includes(this._role);
  };

  // 사장실 출입하기
  get canAccess사장실() {
    return this.eligible(['사장']);
  }
  
  // 퇴사하기
  get 퇴사() {
    return this.ineligible(['사장']);
  }
}

export default PermissionPolicyChecker;

위 코드를 함수형으로 바꾼 코드는 아래와 같다.

type RoleType = '사장' | '매니저' | '알바';

const eligible = (role: RoleType, roles: RoleType[]) => roles.includes(role);

const ineligible = (role: RoleType, roles: RoleType[]) => !roles.includes(role);

const createPermissionChecker = (role: RoleType) => ({
  // 사장실 출입 가능 여부 확인
  canAccess사장실: () => eligible(role, ['사장']),

  // 퇴사 가능 여부 확인
  퇴사: () => ineligible(role, ['사장'])
});

export default createPermissionChecker;
profile
안녕하세요

0개의 댓글

관련 채용 정보