이전 포스팅에서는, EnumGrade 클래스와 DiscountGrade 클래스를 리팩토링해서 줄였다. 

 

이제 메인 메서드를 줄여볼 차례이다.

 

bronze~diamond 까지 동일한 코드가 반복적이다. 

Enum 클래스를 사용하면  ".values()"를 통해서 각 등급을 배열로 처리해서  BRONZE(0인덱스)~DIAMOND(3인덱스) 에 넣을 수 있다.

해서 요내용을 반복문(루프)를 통해서 배열을 출력과 동시에 줄여준다 .

package thisjavaexam.enumtest.enumis;

public class GradePriceMain {
		
	public static void main(String[] args) {
		
		int price = 20000;
		//각 등급을 배열로 집어넣어 처리하면서 출력까지 진행 
		EnumGrade[] grade = EnumGrade.values();
		for(int i = 0 ; i < grade.length; i ++) {
			//아래 매서드를 선언해서 등급 ,가격을 불러온다.
			printDiscountPercent(grade[i],price);
		}
	}
	// enum 상수는 static이므로, 메모리를 메서드영역에서 불러와야 하기때문에 static 메서드에서 직접 접근 가능해야 한다.
	public static void printDiscountPercent(EnumGrade grade,int price) {
		System.out.println(grade.name() + " 의 할인율 적용 금액은 " + price + "입니다.");
	}
}

 

 

나머지 기존코드도 추가 업로드 

package thisjavaexam.enumtest.enumis;

public enum EnumGrade {
	//각각 퍼센트를 직접 기입할 수 있다. 
	BRONZE(5),
	SILVER(10),
	GOLD(15),
	DIAMOND(20);

	//할인율 변수를 선언하는데 final 이기에 앞으로 들어올 값을 위해 초기화 하지 않았다. 
	private final int discountPercent;
	
	//이제 이 클래스의 생성자를 만들고 
	EnumGrade(int discountPercent) {
		this.discountPercent = discountPercent;
	}
	
	//getter를 통해서 외부에서 접근하도록 한다. 
	public int getDiscountPercent() {
		return discountPercent;
	}
	
	//최종 결과 값의 할인율을 계산해서 리턴한다. 
	public int finalPrice(int price) {
		return price * discountPercent / 100;
	}	
}
package thisjavaexam.enumtest.enumis;
 
public class DiscountGrade {
	// 메인클래스에서 넘어올 가격만 받아서 할인율은 EnumGrade 클래스로 토스한다.
	public int discountPercent(EnumGrade grade, int price) { 
		return grade.finalPrice(price);
	}
}

 

3개의 클래스로 정리했다. 

 

이제 배열로 처리하도록 작업했으니, 다른 등급도 enum클래스에 할인율과 함께 추가할수 있다. 

vip만 30% 추가해서 실행결과 

 

엥??...

 

이런 버그쟁이 ...

 

public static void printDiscountPercent(EnumGrade grade,int price) {

System.out.println(grade.name() + " 의 할인율 적용 금액은 " + grade.finalPrice(price) + "입니다.");

}

 

자꾸 사소한 실수해버린다 .

 

 

Enum 학습 우선 여기까지! ..

 

참고로 학습한 내용을 참조하여 다시 구성한 코드입니다.

 

이 부분의 grade = EnumGrade.Bronze 에  할인율 5%를 적용하는것을 Enum클래스에 그대로 적용할 수 있다. 

이제 grade를 모두 정리했기 때문에 else 상황에서 발생할 "할인이 없습니다." 문구는 필요없다. (사실 저건 오류방지용이였다.)

만약 할인이 없는 그레이드를 만들려면  BRONZE 보다 아래단계를 추가해야겠지...

Enum클래스도 매서드를 만들 수 있기 때문에 , 맨아래 리턴값도 Enum클래스에서 한번에 연산해버릴 수 있다. 

 

해서 아래와 같이 줄여버렸다. 

package thisjavaexam.enumtest.enumis;
 
public class DiscountGrade {
	// 메인클래스에서 넘어올 가격만 받아서 할인율은 EnumGrade 클래스로 토스한다.
	public int discountPercent(EnumGrade grade, int price) { 
		return grade.finalPrice(price);
	}
}

 

대신 Enum클래스가 좀늘었다.

package thisjavaexam.enumtest.enumis;

public enum EnumGrade {
	//각각 퍼센트를 직접 기입할 수 있다. 
	BRONZE(5),
	SILVER(10),
	GOLD(15),
	DIAMOND(20);

	//할인율 변수를 선언하는데 final 이기에 앞으로 들어올 값을 위해 초기화 하지 않았다. 
	private final int discountPercent;
	
	//이제 이 클래스의 생성자를 만들고 
	EnumGrade(int discountPercent) {
		this.discountPercent = discountPercent;
	}
	
	//getter를 통해서 외부에서 접근하도록 한다. 
	public int getDiscountPercent() {
		return discountPercent;
	}
	
	//최종 결과 값의 할인율을 계산해서 리턴한다. 
	public int finalPrice(int price) {
		return price * discountPercent / 100;
	}	
}

 

 

메인 클래스는 그냥 그대로 두었다. 

package thisjavaexam.enumtest.enumis;

public class GradePriceMain {
		
	public static void main(String[] args) {
		
		int price = 20000;
		
		DiscountGrade discountGrade = new DiscountGrade();

		int bronze = discountGrade.discountPercent(EnumGrade.BRONZE, price);
		int silver = discountGrade.discountPercent(EnumGrade.SILVER, price);	
		int gold = discountGrade.discountPercent(EnumGrade.GOLD, price);
		int diamond = discountGrade.discountPercent(EnumGrade.DIAMOND, price);
		
		System.out.println("BRONZE 의 할인율 5% 적용금액 : " + bronze);
		System.out.println("SILVER 의 할인율 10% 적용금액 : " + silver);
		System.out.println("GOLD 의 할인율 15% 적용금액 : " + gold);
		System.out.println("DIAMOND 의 할인율 20% 적용금액 : " + diamond);
	}
}

 

이렇게 해도 정상적으로 잘 동작한다

 

 

package thisjavaexam.enumtest.enumis;

public enum EnumGrade {
	BRONZE,
	SILVER,
	GOLD,
	DIAMOND
}

 

이넘 클래스로 바꿨으니 슬슬 수정을 추가해 보자..

 

기존의 클래스를 지워버리니 .. 수정해달라고 오류를 뱉어낸다 .

기존의 참조 FinalGrade 클래스를 삭제해버렸다 .

 

이제 아래 주석의 2곳을 수정하면 된다. 

package thisjavaexam.enumtest.enumis;
 
public class DiscountGrade {
	//파라미터를 String이 아닌 이넘클래스 타입으로 받도록 수정 
	public int discountPercent(EnumGrade grade, int price) { 
		int discountPercent = 0;
		
		// (grade.equals(FinalGrade.getBronze()) 에서 수정한다. 문자열과 다르개 enum은 ==도 잘 인식한다.  
		if (grade == EnumGrade.BRONZE) {
			discountPercent = 5;
		} else if (grade == EnumGrade.SILVER) {
			discountPercent = 10;
		} else if (grade == EnumGrade.GOLD) {
			discountPercent = 15;
		} else if (grade == EnumGrade.DIAMOND) {
			discountPercent = 20;
		} else {
			System.out.println(grade + "할인이 없습니다.");
		}
		return price * discountPercent / 100;
	}
}

 

 

실행 부 또한 파라미터 값을 수정하면 된다. 

package thisjavaexam.enumtest.enumis;

public class GradePriceMain {
		
	public static void main(String[] args) {
		
		int price = 20000;
		
		DiscountGrade discountGrade = new DiscountGrade();
		//문자열을 enum에서 불러오는 형식으로 수정 
		int bronze = discountGrade.discountPercent(EnumGrade.BRONZE, price);
		int silver = discountGrade.discountPercent(EnumGrade.SILVER, price);	
		int gold = discountGrade.discountPercent(EnumGrade.GOLD, price);
		int diamond = discountGrade.discountPercent(EnumGrade.DIAMOND, price);
		
		System.out.println("BRONZE 의 할인율 5% 적용금액 : " + bronze);
		System.out.println("SILVER 의 할인율 10% 적용금액 : " + silver);
		System.out.println("GOLD 의 할인율 15% 적용금액 : " + gold);
		System.out.println("DIAMOND 의 할인율 20% 적용금액 : " + diamond);
	}
}

 

 

 

이제 Enum을 사용한 클래스로 변경처리 했다. 

챗 지피티의 힘을 빌려 정리 해둠.

정리하면 Final 상수 자동처리 + static 자동처리로 추후 값을 비교할때 유리함 + switch문에 숫자대신 쓸수있음 

✅ 자바 enum의 주요 특징

1. 타입 안정성 제공

  • enum은 컴파일 타임에 값 검증을 해줘서 잘못된 값이 들어가는 걸 방지해.
public enum Grade { BRONZE, SILVER, GOLD }

Grade g = Grade.GOLD;        // ✅ OK
Grade g2 = Grade.valueOf("VIP"); // ❌ 런타임 예외

✔️ String처럼 오타로 인한 오류를 컴파일 시점에서 방지할 수 있음.


2. 내부적으로 클래스이며, 싱글턴 인스턴스를 갖는다

  • enum은 내부적으로 final static 객체를 각각 하나씩 만들어 두는 클래스야.
  • 즉, Grade.GOLD == Grade.GOLD는 항상 true (같은 인스턴스)
Grade g1 = Grade.GOLD;
Grade g2 = Grade.GOLD;
System.out.println(g1 == g2); // ✅ true

3. switch 문에서 사용할 수 있다

switch (grade) {
    case BRONZE: ...
    case GOLD: ...
}

✔️ 정수 값 없이도 switch-case 처리 가능


4. 필드와 메서드를 가질 수 있다

public enum Grade {
    BRONZE(5), SILVER(10), GOLD(15);

    private final int discount;

    Grade(int discount) {
        this.discount = discount;
    }

    public int getDiscount() {
        return discount;
    }
}

✔️ 각 enum 상수에 **값과 동작(메서드)**을 연결할 수 있음.


5. 메서드 오버라이딩 가능 (상수별 동작 지정 가능)

public enum Grade {
    BRONZE {
        public int discount(int price) { return price * 5 / 100; }
    },
    GOLD {
        public int discount(int price) { return price * 15 / 100; }
    };

    public abstract int discount(int price);
}

✔️ 각 상수마다 동작을 다르게 정의 가능! (전략 패턴처럼 사용 가능)


6. 내장 메서드 지원

메서드 설명

values() 모든 enum 상수를 배열로 반환
valueOf(String) 문자열 → enum 상수로 변환
name() 상수 이름 문자열 반환
ordinal() 선언된 순서 (0부터 시작)

7. 직렬화와 비교에 강함

  • == 비교 가능 (객체 주소 동일)
  • Serializable, Comparable 자동 구현됨

🚨 주의할 점

항목 설명

valueOf()는 예외 발생 가능 잘못된 문자열 입력 시 IllegalArgumentException 발생
ordinal()은 구조 변경 시 깨질 수 있음 enum 순서를 바꾸면 의도치 않은 동작 가능성 있음 (주의해서 사용)

✅ 요약

기능 설명

타입 안정성 오타나 잘못된 값 방지
고정된 인스턴스 == 비교 안전
값과 메서드 포함 가능 각 상수에 필드/메서드 부여 가능
switch 지원 간편한 분기 처리
내장 기능 풍부 values(), valueOf(), ordinal() 등 제공

 

 

 

 

 

자 여기까지 지피티 엉아가 준내용이고 

이제 이..코드를 

 

요래 바꿀수 있다. 

앞서 문제점을 보안하기 위해서 아래와 같은 클래스를 하나 더 만들었다. 

잘못입력 하지못하게 final로 상수로 만들어두었고..

 

package thisjavaexam.enumtest.enumis;

public class FinalGrade {
	// 외부에서 수정 할 수 없도록 private 선, final(상수)로 수정할 수 없도록 필드 선언  
	private static final String BRONZE = "BRONZE";
	private static final String SILVER = "SILVER";
	private static final String GOLD = "GOLD";
	private static final String DIAMOND = "DIAMOND";
	
	//외부에서 접근 가능하도록 게터 선언 
	public static String getBronze() {
		return BRONZE;
	}
	public static String getSilver() {
		return SILVER;
	}
	public static String getGold() {
		return GOLD;
	}
	public static String getDiamond() {
		return DIAMOND;
	}
}

 

요걸이제.. 사용하는 DiscountGrade의 내용을 조금 수정했다. (주석처리)

package thisjavaexam.enumtest.enumis;
 
public class DiscountGrade {
	 
	public int discountPercent(String grade, int price) { 
		int discountPercent = 0;
		
		// 기존 grade.equlas("BRONZE") 에서 이젠 FinalGrade 클래스의 getXXX 를 가져와야한다.
		if (grade.equals(FinalGrade.getBronze())) {
			discountPercent = 5;
		} else if (grade.equals(FinalGrade.getSilver())) {
			discountPercent = 10;
		} else if (grade.equals(FinalGrade.getGold())) {
			discountPercent = 15;
		} else if (grade.equals(FinalGrade.getDiamond())) {
			discountPercent = 20;
		} else {
			System.out.println(grade + "할인이 없습니다.");
		}
		return price * discountPercent / 100;
	}
}

 

 

이래서 실행은 잘된다 .

 

요건..앞선 포스팅과 달라진 내용이 없어서 코드없이 캡쳐했다. 

 

 

하지만..

❗ 그런데도 생길 수 있는 "잠재적 문제점"

값 자체는 여전히 "String"이다

아무리 getter로 감쌌다 해도, String 파라미터로 받는 이상 외부에서 "이상한 문자열"을 넣는 걸 막을 수는 없음

 

외부에서 비슷한 “짝퉁” 클래스 만들 수도 있음

public class FakeGrade {
    public static final String BRONZE = "BRONZE";
}
 
discountPercent(FakeGrade.BRONZE, price) → 이 값도 문자열이기 때문에 동일하게 통과될 수 있음

 

 

여러가지 보안사항들이 있기에...

결과적으로 Enum을 쓰면 해결방법이 더 쉬워진다고 한다. 

 

 

 

코드 작성 목표 - 4개등급별 할인율을 5~20% 까지 적용하여 구매금액에서 할인되는 내용을 작성 

ENUM사용하기 전 Class 단계에서부터 Enum을 사용하는 단계까지 단계별로 포스트를 이어나가기로함 .

나로써는 좋은 연습이 될 것 같다는 생각이 들었다. 

 

 

먼저 할인율을 계산하기 위해서 클래스 + 매서드를 만들었다. 

package thisjavaexam.enumtest.enumis;
//할인율을 계산하는 클래스를 만들어 활용한다. 
public class DiscountGrade {
	
	//할인율 계산을 위한 매서드 생성.  등급 과 구매금액을 받아 처리 
	public int discountPercent(String grade, int price) {
		// 할인율 (discountPercent)를 계산하지만 지역변수이므로  초기값 설정필요 
		int discountPercent = 0;
		
		//각 그레이드 별로 할인율 5~20프로 까지 , 등급이 아니라면 할인없음/  설정,String으로 비교하므로 == 이 아닌 equals를 활용
		if (grade.equals("BRONZE")) {
			discountPercent = 5;
		} else if (grade.equals("SILVER")) {
			discountPercent = 10;
		} else if (grade.equals("GOLD")) {
			discountPercent = 15;
		} else if (grade.equals("DIAMOND")) {
			discountPercent = 20;
		} else {
			System.out.println(grade + "할인이 없습니다.");
		}
		//최종 할인금액을 계산. 구매금액 * 할인 /100(율) 
		return price * discountPercent / 100;
	}
}

 

실제 할인율이 적용된 금액을 출력해보자. 

package thisjavaexam.enumtest.enumis;
//할인율에 따라 정상적으로 반영되는 메인 클래스 선언
public class GradePriceMain {
		
	public static void main(String[] args) {
		
		//20000원 만큼 결제금액이 있다는 가정
		int price = 20000;
		
		//DiscountGrade 클래스의 생성자 
		DiscountGrade discountGrade = new DiscountGrade();
		//생성자를 통해서 bronze에 할인율을 적용
		int bronze = discountGrade.discountPercent("BRONZE" , price);
		int silver = discountGrade.discountPercent("SILVER", price);	
		int gold = discountGrade.discountPercent("GOLD", price);
		int diamond = discountGrade.discountPercent("DIAMOND", price);
	
		//적용된 할인율 출력 시도
		System.out.println("BRONZE 의 할인율 5% 적용금액 : " + bronze);
		System.out.println("SILVER 의 할인율 10% 적용금액 : " + silver);
		System.out.println("GOLD 의 할인율 15% 적용금액 : " + gold);
		System.out.println("DIAMOND 의 할인율 20% 적용금액 : " + diamond);
	}
}

 

 

20000원에대한 할인율로 적절하게 잘 적용된 금액으로 반영 되었다 . 

하지만, 이렇게 대소문자 구분이 없거나, 오타, 혹은 없는 등급을 정의해도 컴파일 오류가 발생하지않아, 나중에 실행중 오류를 직접 찾아야 하는 문제가 있을 수 있다. 

 

 

 

+ Recent posts