개발바닥곰발바닥
Published 2022. 5. 11. 00:14
JPA Column Enum으로 관리하기 JAVA/Spring
728x90

JPA Column Enum으로 관리하기

회원 정보인 Member Entity에서 권한을 관리하는 state라는 필드가 있는데 기존에는 state를 String으로 사용했었다. 그런데 String으로 사용하면 생길 수 있는 문제점이 있는데, 현재 진행중인 프로젝트의 권한은 USER, ADMIN, ANONYMOUS(승인되지 않은 유저) 이렇게 세 개로 관리하는데 권한 변경하는 API에서 Request에 권한을 받을 때 잘 못된 값이 들어 올 수 있다는 위험이 존재한다.

이런 문제를 사전에 차단하기 위해 권한에 대한 Enum을 만들어 잘못된 입력 값이 들어오면 막아주도록 설계를 변경했다.

ROLE Enum

public enum Role {
    ROLE_USER("ROLE_USER"),
    ROLE_ANONYMOUS("ROLE_ANONYMOUS"),
    ROLE_ADMIN("ROLE_ADMIN");

    String role;

    Role(String role) {
        this.role = role;
    }

    public String value() {
        return role;
    }
}

Member Entity

public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "email")
    private String email;

    @Column(name = "department")
    private String department;

    @Column(name = "state")
    @Enumerated(EnumType.STRING)
    private Role state;
		
/*...*/
}

Entity에 있는 Enum이 데이터베이스에 그대로 저장되려면 @Enumerated(EnumType.STRING) 어노테이션을 사용하면 DB에 Enum 값이 그대로 String으로 저장된다.

CustomUserDetails

Spring Security를 사용하는 경우

CustomUserDetails의 createUserDetails에서 권한을 넣어주는 부분에서 Enum을 toString 메소드로 String으로 변환한 뒤에 SimpleGrantedAuthority로 권한을 지정해준다.

@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
    private final MemberRepository memberRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return memberRepository.findByLoginId(username).map(this::createUserDetails)
                .orElseThrow(() -> new UsernameNotFoundException(username + "는 데이터베이스에 없는 데이터입니다."));
    }

    private UserDetails createUserDetails(Member member) {

        String role = member.getState().value();
        GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role);

        return new org.springframework.security.core.userdetails.User(
                String.valueOf(member.getId()),
                member.getPassword(),
                Collections.singleton(grantedAuthority)
        );
    }
}

SecurityConfig

Security에서 admin에 대해 ADMIN 권한이 있어야만 접근이 가능하도록 설정할 수 있다.

권한을 지정할 때는 “ROLE_ADMIN”이었지만 Spring Security에서 prefix를 자동으로 "ROLE_"을 넣어주므로 이 때 hasRole에는 ROLE을 제외하고 뒷 부분인 ADMIN만 써주면 된다.

.authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**/*").permitAll()
                .antMatchers("/image/**").permitAll()
                .antMatchers("/auth/**").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers(HttpMethod.GET, "/product/**").permitAll() // 상품쪽은 조회만 권한없이 가능

번외. No enum constant 오류

이렇게 enum으로 변환하다가 가끔 No enum constant enum.값 이라는 에러가 발생할 때가 있다.

이 에러가 발생하는 원인은 Entity에서 사용하는 Enum에 존재하지 않는 값이 데이터베이스의 해당 컬럼에 존재할 경우 발생하는 오류이다. (대소문자를 구분한다.)

이 에러를 해결하려면 Enum에 존재하지 않는 값을 DB에서 삭제하거나, 반대로 DB에 존재하는 값을 Enum에 추가시켜주면 해결된다.

728x90
profile

개발바닥곰발바닥

@bestinu

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!