2 minute read

1. enum

상수들의 그룹 기능을 제공하는 프로그래밍 언어의 기능이다. java에서는 클래스를 통해 enum 기능이 구현된다.

enum Color {
    RED,
    GREEN,
    BLUE;
}

/**
 * 아래와 같이 변환된다.
 */
class Color
{
     public static final Color RED = new Color();
     public static final Color BLUE = new Color();
     public static final Color GREEN = new Color();
}

2. 특징

public enum PracticeStatus {
    /*
     * datas
     */
    PERFECT("PERFECT", "#00ff00"),
    GREAT("GREAT", "#aaff00"),
    GOOD("GOOD", "#ffdb4d"),
    FAIL("FAIL", "#ff3333");


    private static List<PracticeStatus> allList = List.of(PracticeStatus.values());

    private final String STATUS;
    private final String COLOR;

    /*
     * constructor
     */
    PracticeStatus(String STATUS, String COLOR) {
        this.STATUS = STATUS;
        this.COLOR = COLOR;
    }

    public String getStatus() {
        return STATUS;
    }

    public String getColor() {
        return COLOR;
    }

    public static List<PracticeStatus> findAll() {
        return allList;
    }
}

Enum클래스가 정의한 상수들은 클래스가 로딩될때 싱글톤으로 초기화된다. java가 제공하는 enum클래스는 변수, 메소드, 생성자를 가질수 있다. 따라서 멀티쓰레드 환경에서 문제가 될 여지가 있을지 항상 유념해야 한다. 아래의 은행계좌 ENUM을 살펴보자

3. ENUM과 멀티쓰레드

public enum Account {
    /*
     * datas
     */
    ACCOUNT1("John"),
    ACCOUNT2("Michael");

    private final String NAME;
    private int deposit;

    /*
     * constructor
     */
    Account(String NAME) {
        this.NAME = NAME;
        this.deposit = 0;
    }

    public int getDeposit() {
        return this.deposit;
    }

    /*
     * income
     */
    public void income(int value) {
        this.deposit += value;
    }
}

위 은행의 계좌엔 ACCOUNT1, ACCOUNT2를 가지고 있다. income메소드를 통해 입급을 할수 있지만, 만약 입금이 두쓰레드에 의해 동시에 발생한다면 어떨까? 애석하게도 income 메소드의 경우 atomic 하지 않다.

1000원씩 잔액이 1000원인 ACCOUNT1에 입금한다고 가정해보자, 두메소드는 같은 계좌의 잔액에 1000+1000=2000을 새로 설정하려 들지도 모른다. 그러면 최종잔액이 3000원이 아닌 2000원인 심각한 문제가 발생할수도 있다. 이문제의 해결을 위해 income 메소드를 아래와 같이 바꿔보았다.

public enum Account {
    public synchronized void income(int value) {
        this.deposit += value;
    }
}

메소드가 실행되면 락이 걸리게 되어 다른 쓰레드가 해당 Account 객체의 메소드를 실행시킬수 없다.

public enum PracticeStatus {
    /*
     * datas
     */
    PERFECT("PERFECT", "#00ff00"),
    GREAT("GREAT", "#aaff00"),
    GOOD("GOOD", "#ffdb4d"),
    FAIL("FAIL", "#ff3333");


    private static List<PracticeStatus> allList;

    private final String STATUS;
    private final String COLOR;

    /*
     * constructor
     */
    PracticeStatus(String STATUS, String COLOR) {
        this.STATUS = STATUS;
        this.COLOR = COLOR;
    }

    public String getStatus() {
        return STATUS;
    }

    public String getColor() {
        return COLOR;
    }

    public synchronized static List<PracticeStatus> findAll() {
        if (Objects.isNull(allList)) {
            allList = List.of(PracticeStatus.values());
        }
        return allList;
    }
}

이건 아까 소개했던 학습상태를 나타내는 ENUM, PracticeStatus 이다. ENUM 클래스는 values() 메소드를 통해 ENUM의 클래스들을 배열의 형태로 만들어주지만, 매소드가 매순간에 실행될때마다 새로 배열을 만드므로 비효율적이다. synchronized 키워드는 인스턴스뿐만아니라 클래스단위로도 설정할수 있다. 클래스메소드 실행시 락이 걸려서 두번 배열을 만드는 일을 방지해준다.

4. ENUM의 장점/단점

상태를 필요로 하지 않는, 혹은 상태가 항상 고정된 도메인을 표시하는데 적합하다. 상태가 계속 변동된다면 데이터 무결성에 문제가 생길수 있어서 데이터베이스 테이블을 통해 관리하는것이 좋다.

또한 어떤 한 그룹의 데이터들의 갯수가 적으면 ENUM이 적합하다. 관리자페이지의 경우 어떤 도메인에 대한 데이터를 읽어올때, 연관된 데이터를 같이 읽어올일이 많다. 이때 enum을 활용해주면 불필요한 join 쿼리 를 예방할수 있다.

Leave a comment