프로젝트를 진행하면서 유저 등급, 게시글 카테고리, 예외 처리 문구 등등 상수값을 사용해야 되는 경우가 많이 있었습니다. 상수들을 테이블로 만들어 DB에 저장하고 FK로 연결하여 사용할 수 있는 방법이 있었지만 이런 방법은
1. 조회를 위해 매번 DB에 접근
2. 컴파일 불가능
3. 변경사항이 생기면 매번 DB 수정
위와 같은 문제점이 있습니다. 이러한 문제점을 해결하기 위해 Enum을 사용해보기로 했습니다.
Enum이란 Enumeration의 앞 글자로 열거라는 의미를 갖습니다. 관련이 있는 상수들의 집합입니다. 자바에서는 final로 String과 같은 문자열이나 숫자들을 나타내는 기본 자료형의 값을 고정할 수 있습니다. 이렇게 고정된 값을 상수라고 합니다. 영어로는 constant입니다. 어떤 클래스가 상수만으로 작성되어 있으면 반드시 class로 선언할 필요는 없습니다. 이럴 때 class로 선언된 부분에 enum이라고 선언하면 이 객체는 상수의 집합이다. 라는 것을 명시적으로 나타냅니다.
생성자는 private로 작성해야됩니다. Default 생성자 또한 private입니다.
사용하기 위해서는 BodyPart.Back 이런 식으로 작성해야됩니다.
public enum BodyPart {
BACK, CHEST
}
enum 클래스는 상수와 함께 메서드를 정의할 수 있습니다. 이 메서드는 enum 상수와 관련된 동작을 수행하거나, enum 상수를 이용한 값을 반환할 수 있습니다.
BodyPart.BACK.getMessage() -> "등" 이러한 형식으로 사용할 수 있습니다.
@Getter
public enum BodyPart {
BACK("등"),
CHEST("가슴"),
private final String message;
BodyPart(String message) {
this.message = message;
}
}
enum 클래스는 싱글톤 패턴을 구현하는 데 사용됩니다. 이는 enum 클래스 내에서 정의된 상수가 애플리케이션 전체에서 단 하나의 인스턴스만 존재함을 보장하기 때문입니다.
'values()' 메서드는 enum 클래스의 모든 상수를 배열 형태로 반환합니다.
BodyPart[] bodyParts = BodyPart.values();
'valueOf()' 메서드는 문자열로 표현된 상수 이름을 이용하여, 해당 상수의 참조(인스턴스)를 반환합니다. 만약 해당하는 상수가 없을 경우 IllegalArgumentException이 발생합니다.
BodyPart bodyPart = BodyPart.valueOf("BACK");
'name()' 메서드는 상수의 이름을 문자열 형태로 반환합니다.
String name = BodyPart.BACK.name(); // "BACK"
'ordinal()' 메서드는 상수의 순서를 반환합니다. 첫 번째 상수는 0부터 시작합니다.
int ordinal = BodyPart.BACK.ordinal(); // 0
'compareTo()' 메서드는 해당 enum 상수와 다른 enum 상수를 비교합니다. 비교 결과는 순서에 따라 -1, 0, 1 중 하나의 값을 반환합니다.
int result = BodyPart.BACK.compareTo(BodyPart.CHEST); // -1
'equals()' 메서드는 해당 enum 상수와 다른 객체를 비교합니다. 비교 결과는 boolean 형태로 반환합니다.
boolean isEqual = BodyPart.BACK.equals(BodyPart.CHEST); // false
아래와 같은 Enum 클래스가 있습니다.
public enum PostType {
Q_AND_A, KNOWLEDGE, SHOW_OFF, COMPETITION, FREE
}
public enum WorkOutCategory {
HEALTH, PILATES, YOGA, JOGGING, ETC
}
아래 보이는 것은 Enum을 사용해서 HTTP요청을 보내고 조건에 해당하는 게시글을 조회하는 것입니다.
Http요청 : http://localhost:8080/post?woryOutCategory=HEALTH,YOGA&username=10000DOO&page=0&size=2
@Data
public class PostListDto {
private String username;
private PostType postType;
private WorkOutCategory workOutCategory;
private String createdAt;
private String title;
private Long postId;
private int mediaListCount;
private int likeCount;
private Long views;
private Long commentCount;
@Builder
public PostListDto(String username, PostType postType, WorkOutCategory workOutCategory, String createdAt,
String title, int mediaListCount, int likeCount, Long views, Long commentCount, Long postId) {
this.username = username;
this.postType = postType;
this.workOutCategory = workOutCategory;
this.createdAt = createdAt;
this.title = title;
this.mediaListCount = mediaListCount;
this.likeCount = likeCount;
this.views = views;
this.commentCount = commentCount;
this.postId = postId;
}
}
위의 코드는 DTO로 사용되는 클래스라 사용되지 않지만 Entity에서 사용되는 @Enumerated는 자바의 Enum타입을 Entity의 속성으로 사용할 수 있게 해줍니다.
EnumTpye에는 두가지가 존재합니다.
EnumType.ORDINAL : enum 순서 값을 DB에 저장
EnumType.STRING : enum 이름을 DB에 저장
{
"status": 200,
"data": {
"postListDto": [
{
"username": "10000DOO",
"postType": "SHOW_OFF",
"workOutCategory": "HEALTH",
"createdAt": "4일 전",
"title": "테스트 게시글 입니다.",
"postId": 1,
"mediaListCount": 0,
"likeCount": 0,
"views": 5,
"commentCount": null
}
],
"hasNext": false,
"isFirst": true
}
}
사용법1을 사용하여 상수를 Enum으로 사용했습니다.
아래 코드처럼 Enum 클래스를 작성할 수 있습니다.
@Getter
public enum ErrorCode {
NOT_FOUND_LOGIN_ID("이미 존재하는 아이디입니다."),
NOT_FOUND_EMAIL("이미 존재하는 이메일입니다."),
NOT_FOUND_USERNAME("이미 존재하는 이름입니다."),
BAD_CREDENTIALS_EXCEPTION("비밀번호가 틀렸습니다. 다시 시도해주세요."),
INTERNAL_AUTHENTICATION_SERVICE_EXCEPTION("아이디가 틀렸습니다. 다시 시도해주세요."),
AUTHENTICATION_EXCEPTION("로그인 실패입니다. 다시 시도해주세요."),
NOT_FOUND_EXCEPTION_DIARY("존재하지 않는 다이어리입니다."),
NOT_FOUND_EXCEPTION_POST("존재하지 않는 게시글입니다."),
NOT_FOUND_EXCEPTION_MEDIA("존재하지 않는 미디어입니다."),
NOT_FOUND_EXCEPTION_MEMBER("존재하지 않는 유저입니다."),
NOT_FOUND_EXCEPTION_COMMENT("존재하지 않는 댓글입니다."),
IO_FAIL_EXCEOPTION("파일 I/O 서버 오류입니다."),
WRONG_CONTENT_TYPE("잘못된 Content-Type입니다."),
NOT_EXIST_SAME_POST_IN_PARENT_AND_CHILD_COMMENT("원댓글과 같은 게시글에 존재하지 않습니다."),
WRONG_TOKEN("잘못된 토큰입니다. 토큰 재발급이 불가능하니 다시 로그인 부탁드립니다."),
NO_EXIST_TOKEN("존재하지 않는 토큰입니다. 토큰 재발급이 불가능하니 다시 로그인 부탁드립니다."),
EXPIRED_TOKEN("토큰이 만료 되었습니다. 다시 로그인 해주세요."),
NOT_LIKE_PRESSED("좋아요 정보가 없습니다."),
ALREADY_PRESSED("이미 좋아요를 누르셨습니다."),
DATE_FORMAT_EXCEPTION("날짜 형식 변환에 실패하였습니다."),
NO_MATCHED_POST_TYPE("잘못된 게시글 타입입니다."),
NO_MATCHED_EXERCISE_CATEGORY("잘못된 운동 카테고리입니다.");
private final String message;
ErrorCode(String message) {
this.message = message;
}
}
@Getter를 사용해서 각 인스턴스의 message 값을 가져와 사용할 수 있습니다.
Member findMember = memberRepository.findByUsername(SecurityUtil.getCurrentUsername())
.orElseThrow(() -> new EntityNotFoundException(ErrorCode.NOT_FOUND_EXCEPTION_MEMBER.getMessage()));
Post findPost = postRepository.findNotDeletedById(writeCommentDto.getPostId())
.orElseThrow(() -> new EntityNotFoundException(ErrorCode.NOT_FOUND_EXCEPTION_POST.getMessage()));
프로젝트에서는 위와 같이 예외처리에서 에러메시지를 넣어주는데 사용했습니다.
//결과
{
"status": 400,
"error": "존재하지 않는 게시글입니다."
}
{
"status": 400,
"error": "존재하지 않는 유저입니다."
}
1. 상수를 문자열로 사용하는 것보다 관리가 편리합니다.
다른 곳에서 사용할 때 자동완성 가능, Enum 클래스에서만 변경하면 전부 반영, 컴파일 에러로 잘못된 점 확인 가능
2. 사용할 값을 제한할 수 있습니다.
3. 여러 클래스에서 재사용하기 쉽습니다.
https://velog.io/@kyle/자바-Enum-기본-및-활용
https://velog.io/@yrc97/Spring-Java-Enum-활용하기-Enum-Code-API
https://lng1982.tistory.com/280
https://quantum-jumpin.tistory.com/18