현재 CS 퀴즈 프로젝트에서는 회원 역할을 통해 권한을 부여하고 있다.
Ver1에선 대표적으로 아래와 같이 권한을 구분한다.
- 현재 활동중인 부원(MEMBER) : 문제 풀이 가능
- 교육팀원(EDUCATION) : 문제 업로드 및 풀이 진행, 결과 확인 가능
- 운영진(ADMIN): 부원 관리 기능 및 모든 기능 가능
//MemberRole.java
REFUSED("ROLE_REFUSED"),
GENERAL("ROLE_GENERAL"),
MEMBER("ROLE_MEMBER"),
OLD_MEMBER("ROLE_OM"),
ADMIN("ROLE_ADMIN"),
EDUCATION("ROLE_EDUCATION");
이외에도 현재 활동 중이지 않은 OM, 거절된 사용자, 승인 대기중인 부원 등 총 6가지의 역할이 존재한다.
지금까진 특정 API에선 사용자의 역할에 따른 사용자의 권한을 확인하기 위해 아래와 같이 직접 하나하나의 조건문을 사용했다.
// 변경하려는 회원의 역할을 확인하는 과정
if (member.getRole() == MemberRole.GENERAL || member.getRole() == REFUSED || member.getRole() == OLD_MEMBER) {
throw new AppException(ErrorCode.ROLE_IS_NOT_MATCH);
}
// WebSocketHandler.java
private Boolean isMemberClient(MemberRole memberRole) {
return switch (memberRole) {
case EDUCATION -> false;
case MEMBER, ADMIN -> true;
default -> {
log.warn("Unknown role for member {}.", memberRole);
throw new AppException(ErrorCode.MEMBER_CANT_ACCESS);
}
};
}
하지만, 이렇게 코드를 작성할 경우 가독성과 유지보수적인 면에서 좋지 않다.
최초 코드를 작성한 개발자는 이해가 쉽지만 이후 해당 코드를 읽게 될 개발자의 이해가 어렵다는 점, 또한 신규 기능에 따른 새로운 역할이 추가되었을때 사용자를 유지 보수해야할 포인트가 많아진다.
또한, 아래와 같이 사용자가 Client임을 확인하는 private 메서드를 Enum이 아닌 WebsocketHandler.java 와 같은 관련이 없는 클래스에서 구분하고 있다.
이를 Enum 그룹화를 통해 개선해보고자 한다.
사용되는 곳을 묶어서 정리하니 아래와 같았다.
- 현재 부원의 역할을 변경할 때
- 변경하려는 부원의 역할이 현재 활동 중인 부원(MEMBER, EDUCATION, ADMIN )인 경우에만 변경을 허용해야한다.
- 현재 활동 중인 부원을 OM으로 변경할 때
- 변경하려는 부원의 역할이 현재 활동 중인 부원(MEMBER, EDUCATION, ADMIN )인 경우에만 변경을 허용해야한다.
- 웹소켓 연결 요청 시
- 연결을 시도하는 사용자가 관리자 (EDUCATION) 이라면 → MANAGER로 세션에 등록한다.
- 연결을 시도하는 사용자가 문제풀이가 가능한 부원 (EDUCATION, ADMIN)이라면 CLIENTS로 세션에 등록한다.
따라서, CLIENT여부, 현재 활동 중인 부원여부 2가지를 구분해야했다.
따라서, MemberRoleGroup이라는 별도의 Enum을 만들고 List<MemberRole>을 Enum의 필드로 받아 멤버의 권한을 명시적으로 관리하고자 했다.
@Getter
@AllArgsConstructor
public enum MemberRoleGroup {
ACTIVE_MEMBERS("현재 활동 중인 멤버", List.of(MemberRole.MEMBER, MemberRole.ADMIN, MemberRole.EDUCATION)),
CLIENTS("교육 중 문제 풀이가 가능한 멤버", List.of(MemberRole.MEMBER, MemberRole.ADMIN)),
MANAGERS("교육 진행 관리자", List.of(MemberRole.EDUCATION, MemberRole.ADMIN));
private final String description;
private final List<MemberRole> roles;
public static boolean hasRole(MemberRoleGroup group, MemberRole role) {
return group.getRoles().contains(role);
}
}
이렇게 변경하고 나니 2가지 장점이 생겼다.
- 역할이 추가되거나 제거되더라도 변경 포인트가 줄어든다.반대로, ADMIN을 제외한다고 해도 해당 부분에서만 수정을 하면 된다.
- 가령, CLIENTS에 다른 역할 (OM)을 추가하게 요구사항이 바뀐다면 MemberRoleGroup 의 CLIENTS 필드에 OM을 추가하면 된다.
- 책임을 명확히 함에 따른 코드 가독성이 올라간다.
- MemberRoleGroup은 회원의 역할을 구분하는 클래스이다. 해당 클래스의 static 메서드를 통해 사용자의 역할을 구분하니 책임이 명확하다. 또한, 아래와 같이 hasRole 함수를 통해 사용자의 역할을 확인하니 제3자가 코드를 읽더라도 이해가 쉽다.
수정 후
// 활동 중인 부원이 아니라면, 예외를 발생한다.
if (!MemberRoleGroup.hasRole(MemberRoleGroup.ACTIVE_MEMBERS, member.getRole())) {
throw new AppException(ErrorCode.ROLE_IS_NOT_MATCH);
}
// CLIENTS그룹에 해당 사용자 역할이 있으면 true
if (MemberRoleGroup.hasRole(MemberRoleGroup.CLIENTS, memberRole)) {
sendQuiz(educationId, session);
}
'프로젝트 > COTATO.KR' 카테고리의 다른 글
선착순 로직 개선기 (2) - Redis 분산락 (0) | 2024.07.05 |
---|---|
선착순 로직 개선기 (1) - 낙관적 락 활용 (1) | 2024.07.05 |
정답 제출 API 멱등성 처리하기 (0) | 2024.06.01 |
멱등성을 통한 정답 제출 API 중복 요청 방지 (이론) (0) | 2024.06.01 |
퀴즈 객체 양방향 매핑 없애기 (0) | 2024.04.24 |