심볼릭 상수를 이용하여 '의미'있는 상수의 값을 사용할 수 있지만, 그보단 Enum을 사용하는 것이 더 좋습니다. 왜 Enum을 사용해야 하는지 알아봅시다.
ㅤ
enum은 자바 가상 머신에 의해 Singleton
으로 생성되며 의미 있는 상수를 정의하고 프로그램의 안정성을 위해 사용됩니다.
enum CafeFoodCategory {
BREAD, CAKE, COFFEE, BEVERAGE
}
enum은 이러한 문제를 해결하기 위해 만들어졌습니다.
public void eat(int foodCategory) {
if (0 == foodCategory) {
System.out.println("빵");
} else if (1 == foodCategory) {
System.out.println("케이크");
} else if (2 == foodCategory) {
System.out.println("커피");
} else if (3 == foodCategory) {
System.out.println("음료");
} else {
System.out.println("존재하지않음");
}
}
이러한 메서드가 있다고 가정해봅시다. 해당 메서드는 0~3까지의 정수형 데이터를 넣으면 빵, 케이크, 커피, 음료를 출력하는 메서드입니다.
그리고 이러한 메서드를 사용하려면 일반적으로는 이렇게 할 겁니다.
eat(0);
eat(1);
메서드를 사용하는 코드만 본다면 eat(0), eat(1)이 무엇을 의미하는지 알 수 없습니다. 인자로 들어가는 0과 1의 의미를 확인하려면 eat 메서드 구현체를 직접 확인해야겠지요.
그래서 이러한 불편함과 문제를 해결하기 위해 정수를 심볼릭 상수로 만들어서 의미를 부여했습니다.
final class FoodCategory {
public static final int BREAD = 0;
public static final int CAKE = 1;
public static final int COFFEE = 2;
public static final int BEVERAGE = 3;
}
심볼릭 상수를 정의한 FoodCategory 클래스를 만든다면 좀 더 낫습니다. 어느 정도 코드의 의미를 파악할 수 있지요.
eat(FoodCategory.BREAD);
eat(FoodCategory.CAKE);
그러나 해당 코드에는 본질적인 문제가 있습니다. eat 메서드는 정수로 된 모든 인자를 전달받을 수 있기 때문에 이러한 코드 작성도 가능합니다.
eat(-1);
eat(-100);
eat 메서드를 구현한 개발자는 eat 메서드를 이렇게 사용하라고 만들지는 않았을 겁니다. 그러나 논리적으로는 발생할 수 있는 일입니다. 정수형 인자를 전달받기 전에 특정 정수만 전달할 수 있도록 할 수 없으니까요.
이럴 땐 enum을 이용하면 상수에 의미를 부여하면서 전달할 수 있는 인자를 제한할 수 있습니다.
ㅤ
FoodCategory를 정의한 enum
enum FoodCategory {
BREAD, CAKE, COFFEE, BEVERAGE
}
ㅤ
enum 상수를 전달 받는 eat 메서드
public void eat(FoodCategory foodCategory) {
if (FoodCategory.BREAD == foodCategory) {
System.out.println("빵");
} else if (FoodCategory.CAKE == foodCategory) {
System.out.println("케이크");
} else if (FoodCategory.COFFEE == foodCategory) {
System.out.println("커피");
} else if (FoodCategory.BAVERAGE == foodCategory) {
System.out.println("음료");
}
}
eat 메서드를 FoodCategory enum에 정의된 상수만 전달받을 수 있도록 변경합니다.
eat(FoodCategory.BREAD);
eat(FoodCategory.CAKE);
그러면 eat 메서드를 호출할 때 enum에 정의된 상수만 전달할 수 있습니다. 코드의 가독성도 좋아지고 코드의 안정성도 확보됩니다.
ㅤ
ordinal()
메서드는 enum 상수에 정의된 순서값을 반환합니다.
enum FoodCategory {
BREAD, CAKE, COFFEE, BEVERAGE
}
System.out.println(FoodCategory.CAKE.ordinal());
// 출력 : 1
단순히 순서값만 반환하기 때문에 정의된 상수의 위치가 달라지면 순서값이 달라질 수 있습니다. 특별한 상황이 아니라면 ordinal()에 반환된 값을 이용하여 조건을 판단하는 것을 하지 않는 것이 좋습니다.
ㅤ
name()
메서드는 enum 상수에 정의된 이름을 반환합니다.
enum FoodCategory {
BREAD, CAKE, COFFEE, BEVERAGE
}
System.out.println(FoodCategory.CAKE.name());
// 출력 : CAKE
만약에 상수의 이름을 이렇게 작성했다면 다음과 같이 출력됩니다.
enum FoodCategory {
Bread, Cake, Coffee, Beverage
}
System.out.println(FoodCategory.Cake.name());
// 출력 : Cake
ㅤ
enum 상수에 특정한 값을 정의할 수 있습니다.
enum FoodCategory {
BREAD(2800),
CAKE(17000),
COFFEE(4300),
BEVERAGE(2500);
int cost;
FoodCategory(int cost) {
this.cost = cost;
}
}
상수에 특정한 값을 지정할 때는 상수의 끝맺음에 반드시 세미콜론(;)을 작성해 주어야 합니다. 그리고 특정한 값의 정의에 맞게 생성자를 반드시 정의해 주어야 합니다. 그리고 enum 내에 변수를 선언하고 생성자를 통해 값을 저장하면 다음과 같이 사용할 수 있습니다.
System.out.println(FoodCategory.BREAD.cost);
System.out.println(FoodCategory.CAKE.cost);
System.out.println(FoodCategory.COFFEE.cost);
System.out.println(FoodCategory.BEVERAGE.cost);
// 출력
2800
17000
4300
2500
하나의 특정한 값이 아니라 여러 개의 특정한 값을 지정할 수 있습니다.
enum FoodCategory {
BREAD("빵", 2800),
CAKE("케이크", 17000),
COFFEE("커피", 4300),
BEVERAGE("음료", 2500);
int name;
int cost;
FoodCategory(String name, int cost) {
this.name = name;
this.cost = cost;
}
}
System.out.println(FoodCategory.CAKE.name);
System.out.println(FoodCategory.CAKE.cost);
// 출력
케이크
17000
ㅤ
enum의 특성상 내부적으로 singleton 방식으로 관리하기 때문에 enum의 생성자는 기본적으로 private로 처리됩니다.
// 불가능한 코드
FoodCategory foodCategory = new FoodCategory(…);
ㅤ
Singleton Pattern
은 인스턴스 생성 패턴 중 하나로 인스턴스를 한 번 생성하고 인스턴스의 내용을 여러 메서드에서 공유하여 사용할 수 있는 패턴입니다.
public class Configuration {
private static Configuration configuration;
private Configuration() {
}
public static Configuration getInstance() {
if (null == configuration) {
configuration = new Configuration();
}
return configuration;
}
}
Singleton Pattern
은 new
키워드로 인스턴스를 생성하지 못하게 함과 동시에 인스턴스를 생성할 수 있는 특정 정적 메서드를 통해서만 인스턴스를 생성하도록 하고 생성된 인스턴스는 정적 변수에 저장하여 인스턴스에 접근할 수 있도록 합니다.
인스턴스를 생성하는 방법은 Configuration.getInstance() 를 통해서만 생성할 수 있으며 configuration 정적 변수가 null
일 경우 생성자를 통해 인스턴스를 생성합니다.
Configuration configuration1 = Configuration.getInstance();
System.out.println(configuration1);
// 동일한 해시코드 Configuration@610455d6
Configuration configuration2 = Configuration.getInstance();
System.out.println(configuration2);
// 동일한 해시코드 Configuration@610455d6
여러번의 Configuration.getInstance() 호출에도 동일한 인스턴스의 참조 값을 반환하기 때문에 인스턴스를 생성함에 따른 메모리 공간을 절약할 수 있습니다.
ㅤ
Singleton 클래스
public class Configuration {
private static Configuration configuration;
private String cafeName = "카페";
private String cafeAddress = "저 먼나라 주소";
private Configuration() {
}
public static Configuration getInstance() {
if (null == configuration) {
configuration = new Configuration();
}
return configuration;
}
public String getCafeName() {
return cafeName;
}
public String getCafeAddress() {
return cafeAddress;
}
}
Configuration configuration1 = Configuration.getInstance();
System.out.println(configuration.getCafeName());
// 출력 "카페"
System.out.println(configuration.getCafeAddress());
// 출력 "저 먼나라 주소"
인스턴스 변수를 선언하여 사용하거나 메서드를 정의하여 일반적인 클래스처럼 사용할 수 있습니다.
그렇다면 이런 의문이 들 수도 있습니다.
인스턴스를 한 번 생성하고 값을 공유한다면 굳이 Singleton Pattern의 인스턴스가 아니라 정적 변수와 정적 메서드로 만들어도 되지 않나요? 라고 생각하셨으면 공부 잘 하신겁니다.
ㅤ
Singleton아닌 클래스
public class Configuration {
private static String cafeName = "카페";
private static String cafeAddress = "저 먼나라 주소";
public static String getCafeName() {
return cafeName;
}
public static String getCafeAddress() {
return cafeAddress;
}
}
System.out.println(Configuration.getCafeName());
// 출력 "카페"
System.out.println(Configuration.getCafeAddress());
// 출력 "저 먼나라 주소"
결과만 놓고 보면 Singleton Pattern
으로 만들어진 인스턴스와 정적 변수, 정적 메서드와 큰 차이가 없어보입니다.
다만 차이점은 인스턴스 지연 생성(Lazy Load) 입니다.
“자바 메모리 구조 - 기초” 강의에서 정적 변수와 정적 메서드는 Method Area에 저장되며 프로그램 시작 시 메모리에 할당되고 적재된다고 했었습니다. 그 말은 해당 정적 변수나 정적 메서드를 프로그램에서 사용하지 않는다 하더라도 메모리 공간을 차지하게 됩니다.
그러나 Singleton Pattern
은 호출 하지 않는다면 최소 인스턴스의 참조 값을 저장하는 configuration 정적 변수와 인스턴스를 생성하기 위한 Configuration.getInstance() 정적 메서드만 메모리에 할당됩니다.
진짜 필요할 때 Configuration.getInstance() 를 호출 하여 최초 메모리에 할당하는 지연 생성(Lazy Load) 이라는 차이점이 있습니다.
또한 이렇게 까지는 하지 않지만 configuration 정적 변수에 저장된 인스턴스의 참조 값을 null로 바꾸어 Heap 메모리에 저장된 Configuration 인스턴스를 Garbage Collector가 수집할 수 있도록 할 수 있습니다.
ㅤ
Singleton 인스턴스 해제
public class Configuration {
private static Configuration configuration;
…
public void free() {
configuration = null;
}
…
}
Configuration configuration = Configuration.getInstance();
…
configuration.free();
Singleton Pattern
클래스로 좀 더 유동적으로 메모리를 사용할 수 있기 때문에 정적 변수와 정적 메서드와는 차이가 있습니다.
이야... 자바에서 메모리 관리의 중요성도 깨닳고 간략하고 명료한 강의력에 감탄합니다 공부 잘하고 있습니다 좋은 양질의 강의를 무료로 배포해주셔서 정말 감사합니다.
enum CafeFoodCategory { BREAD, CAKE, COFFEE, BEVERAGE } 여기서 BEVERAGE에 " ; " 빠진거같습니다.
안녕하세요. 코드라떼입니다 :) 변수나 생성자를 명시적으로 정의하지 않은 enum의 경우 세미콜론을 작성하지 않더라도 정상 컴파일됩니다. 그러나 enum 상수 외의 구문이 작성되면 컴파일 단계에서 상수와 그 외의 코드를 분리해서 해석해야 하므로 세미콜론을 작성해야 합니다. 감사합니다 :)
5:07에 gender 생성자 부분에 this.value = value; 가 되어야하는 거 아닌가요??? this.value = sex;면 sex는 어디서 받아오는 건가요....??
안녕하세요. 코드라떼입니다 :) 먼저 불편함을 드려 죄송합니다. 영상 확인 결과 잘못 작성된 내용을 확인했습니다. this.value = value;가 맞습니다. 감사합니다.
추가노트에 Configuration configuration1 = Configuration.getInstance(); System.out.println(configuration2); // 동일한 해시코드 Configuration@610455d6 configuration2인데 1이라고 되어있어요. ㅎㅎ
자바배움이님 감사합니다^^ 수정했습니다.
1:20 정도에 정적상수변수 클래스명이 CafeFoodCategory로 나온 오타가 있는 것 같습니다
부끄럽네요.. 확인했습니다^^ 감사합니다!!
실습코드를 살펴봅시다.
심볼릭 상수를 이용하여 '의미'있는 상수의 값을 사용할 수 있지만, 그보단 Enum을 사용하는 것이 더 좋습니다. 왜 Enum을 사용해야 하는지 알아봅시다.
ㅤ
enum은 자바 가상 머신에 의해 Singleton
으로 생성되며 의미 있는 상수를 정의하고 프로그램의 안정성을 위해 사용됩니다.
enum CafeFoodCategory {
BREAD, CAKE, COFFEE, BEVERAGE
}
enum은 이러한 문제를 해결하기 위해 만들어졌습니다.
public void eat(int foodCategory) {
if (0 == foodCategory) {
System.out.println("빵");
} else if (1 == foodCategory) {
System.out.println("케이크");
} else if (2 == foodCategory) {
System.out.println("커피");
} else if (3 == foodCategory) {
System.out.println("음료");
} else {
System.out.println("존재하지않음");
}
}
이러한 메서드가 있다고 가정해봅시다. 해당 메서드는 0~3까지의 정수형 데이터를 넣으면 빵, 케이크, 커피, 음료를 출력하는 메서드입니다.
그리고 이러한 메서드를 사용하려면 일반적으로는 이렇게 할 겁니다.
eat(0);
eat(1);
메서드를 사용하는 코드만 본다면 eat(0), eat(1)이 무엇을 의미하는지 알 수 없습니다. 인자로 들어가는 0과 1의 의미를 확인하려면 eat 메서드 구현체를 직접 확인해야겠지요.
그래서 이러한 불편함과 문제를 해결하기 위해 정수를 심볼릭 상수로 만들어서 의미를 부여했습니다.
final class FoodCategory {
public static final int BREAD = 0;
public static final int CAKE = 1;
public static final int COFFEE = 2;
public static final int BEVERAGE = 3;
}
심볼릭 상수를 정의한 FoodCategory 클래스를 만든다면 좀 더 낫습니다. 어느 정도 코드의 의미를 파악할 수 있지요.
eat(FoodCategory.BREAD);
eat(FoodCategory.CAKE);
그러나 해당 코드에는 본질적인 문제가 있습니다. eat 메서드는 정수로 된 모든 인자를 전달받을 수 있기 때문에 이러한 코드 작성도 가능합니다.
eat(-1);
eat(-100);
eat 메서드를 구현한 개발자는 eat 메서드를 이렇게 사용하라고 만들지는 않았을 겁니다. 그러나 논리적으로는 발생할 수 있는 일입니다. 정수형 인자를 전달받기 전에 특정 정수만 전달할 수 있도록 할 수 없으니까요.
이럴 땐 enum을 이용하면 상수에 의미를 부여하면서 전달할 수 있는 인자를 제한할 수 있습니다.
ㅤ
FoodCategory를 정의한 enum
enum FoodCategory {
BREAD, CAKE, COFFEE, BEVERAGE
}
ㅤ
enum 상수를 전달 받는 eat 메서드
public void eat(FoodCategory foodCategory) {
if (FoodCategory.BREAD == foodCategory) {
System.out.println("빵");
} else if (FoodCategory.CAKE == foodCategory) {
System.out.println("케이크");
} else if (FoodCategory.COFFEE == foodCategory) {
System.out.println("커피");
} else if (FoodCategory.BAVERAGE == foodCategory) {
System.out.println("음료");
}
}
eat 메서드를 FoodCategory enum에 정의된 상수만 전달받을 수 있도록 변경합니다.
eat(FoodCategory.BREAD);
eat(FoodCategory.CAKE);
그러면 eat 메서드를 호출할 때 enum에 정의된 상수만 전달할 수 있습니다. 코드의 가독성도 좋아지고 코드의 안정성도 확보됩니다.
ㅤ
ordinal()
메서드는 enum 상수에 정의된 순서값을 반환합니다.
enum FoodCategory {
BREAD, CAKE, COFFEE, BEVERAGE
}
System.out.println(FoodCategory.CAKE.ordinal());
// 출력 : 1
단순히 순서값만 반환하기 때문에 정의된 상수의 위치가 달라지면 순서값이 달라질 수 있습니다. 특별한 상황이 아니라면 ordinal()에 반환된 값을 이용하여 조건을 판단하는 것을 하지 않는 것이 좋습니다.
ㅤ
name()
메서드는 enum 상수에 정의된 이름을 반환합니다.
enum FoodCategory {
BREAD, CAKE, COFFEE, BEVERAGE
}
System.out.println(FoodCategory.CAKE.name());
// 출력 : CAKE
만약에 상수의 이름을 이렇게 작성했다면 다음과 같이 출력됩니다.
enum FoodCategory {
Bread, Cake, Coffee, Beverage
}
System.out.println(FoodCategory.Cake.name());
// 출력 : Cake
ㅤ
enum 상수에 특정한 값을 정의할 수 있습니다.
enum FoodCategory {
BREAD(2800),
CAKE(17000),
COFFEE(4300),
BEVERAGE(2500);
int cost;
FoodCategory(int cost) {
this.cost = cost;
}
}
상수에 특정한 값을 지정할 때는 상수의 끝맺음에 반드시 세미콜론(;)을 작성해 주어야 합니다. 그리고 특정한 값의 정의에 맞게 생성자를 반드시 정의해 주어야 합니다. 그리고 enum 내에 변수를 선언하고 생성자를 통해 값을 저장하면 다음과 같이 사용할 수 있습니다.
System.out.println(FoodCategory.BREAD.cost);
System.out.println(FoodCategory.CAKE.cost);
System.out.println(FoodCategory.COFFEE.cost);
System.out.println(FoodCategory.BEVERAGE.cost);
// 출력
2800
17000
4300
2500
하나의 특정한 값이 아니라 여러 개의 특정한 값을 지정할 수 있습니다.
enum FoodCategory {
BREAD("빵", 2800),
CAKE("케이크", 17000),
COFFEE("커피", 4300),
BEVERAGE("음료", 2500);
int name;
int cost;
FoodCategory(String name, int cost) {
this.name = name;
this.cost = cost;
}
}
System.out.println(FoodCategory.CAKE.name);
System.out.println(FoodCategory.CAKE.cost);
// 출력
케이크
17000
ㅤ
enum의 특성상 내부적으로 singleton 방식으로 관리하기 때문에 enum의 생성자는 기본적으로 private로 처리됩니다.
// 불가능한 코드
FoodCategory foodCategory = new FoodCategory(…);
ㅤ
Singleton Pattern
은 인스턴스 생성 패턴 중 하나로 인스턴스를 한 번 생성하고 인스턴스의 내용을 여러 메서드에서 공유하여 사용할 수 있는 패턴입니다.
public class Configuration {
private static Configuration configuration;
private Configuration() {
}
public static Configuration getInstance() {
if (null == configuration) {
configuration = new Configuration();
}
return configuration;
}
}
Singleton Pattern
은 new
키워드로 인스턴스를 생성하지 못하게 함과 동시에 인스턴스를 생성할 수 있는 특정 정적 메서드를 통해서만 인스턴스를 생성하도록 하고 생성된 인스턴스는 정적 변수에 저장하여 인스턴스에 접근할 수 있도록 합니다.
인스턴스를 생성하는 방법은 Configuration.getInstance() 를 통해서만 생성할 수 있으며 configuration 정적 변수가 null
일 경우 생성자를 통해 인스턴스를 생성합니다.
Configuration configuration1 = Configuration.getInstance();
System.out.println(configuration1);
// 동일한 해시코드 Configuration@610455d6
Configuration configuration2 = Configuration.getInstance();
System.out.println(configuration2);
// 동일한 해시코드 Configuration@610455d6
여러번의 Configuration.getInstance() 호출에도 동일한 인스턴스의 참조 값을 반환하기 때문에 인스턴스를 생성함에 따른 메모리 공간을 절약할 수 있습니다.
ㅤ
Singleton 클래스
public class Configuration {
private static Configuration configuration;
private String cafeName = "카페";
private String cafeAddress = "저 먼나라 주소";
private Configuration() {
}
public static Configuration getInstance() {
if (null == configuration) {
configuration = new Configuration();
}
return configuration;
}
public String getCafeName() {
return cafeName;
}
public String getCafeAddress() {
return cafeAddress;
}
}
Configuration configuration1 = Configuration.getInstance();
System.out.println(configuration.getCafeName());
// 출력 "카페"
System.out.println(configuration.getCafeAddress());
// 출력 "저 먼나라 주소"
인스턴스 변수를 선언하여 사용하거나 메서드를 정의하여 일반적인 클래스처럼 사용할 수 있습니다.
그렇다면 이런 의문이 들 수도 있습니다.
인스턴스를 한 번 생성하고 값을 공유한다면 굳이 Singleton Pattern의 인스턴스가 아니라 정적 변수와 정적 메서드로 만들어도 되지 않나요? 라고 생각하셨으면 공부 잘 하신겁니다.
ㅤ
Singleton아닌 클래스
public class Configuration {
private static String cafeName = "카페";
private static String cafeAddress = "저 먼나라 주소";
public static String getCafeName() {
return cafeName;
}
public static String getCafeAddress() {
return cafeAddress;
}
}
System.out.println(Configuration.getCafeName());
// 출력 "카페"
System.out.println(Configuration.getCafeAddress());
// 출력 "저 먼나라 주소"
결과만 놓고 보면 Singleton Pattern
으로 만들어진 인스턴스와 정적 변수, 정적 메서드와 큰 차이가 없어보입니다.
다만 차이점은 인스턴스 지연 생성(Lazy Load) 입니다.
“자바 메모리 구조 - 기초” 강의에서 정적 변수와 정적 메서드는 Method Area에 저장되며 프로그램 시작 시 메모리에 할당되고 적재된다고 했었습니다. 그 말은 해당 정적 변수나 정적 메서드를 프로그램에서 사용하지 않는다 하더라도 메모리 공간을 차지하게 됩니다.
그러나 Singleton Pattern
은 호출 하지 않는다면 최소 인스턴스의 참조 값을 저장하는 configuration 정적 변수와 인스턴스를 생성하기 위한 Configuration.getInstance() 정적 메서드만 메모리에 할당됩니다.
진짜 필요할 때 Configuration.getInstance() 를 호출 하여 최초 메모리에 할당하는 지연 생성(Lazy Load) 이라는 차이점이 있습니다.
또한 이렇게 까지는 하지 않지만 configuration 정적 변수에 저장된 인스턴스의 참조 값을 null로 바꾸어 Heap 메모리에 저장된 Configuration 인스턴스를 Garbage Collector가 수집할 수 있도록 할 수 있습니다.
ㅤ
Singleton 인스턴스 해제
public class Configuration {
private static Configuration configuration;
…
public void free() {
configuration = null;
}
…
}
Configuration configuration = Configuration.getInstance();
…
configuration.free();
Singleton Pattern
클래스로 좀 더 유동적으로 메모리를 사용할 수 있기 때문에 정적 변수와 정적 메서드와는 차이가 있습니다.
이야... 자바에서 메모리 관리의 중요성도 깨닳고 간략하고 명료한 강의력에 감탄합니다 공부 잘하고 있습니다 좋은 양질의 강의를 무료로 배포해주셔서 정말 감사합니다.
enum CafeFoodCategory { BREAD, CAKE, COFFEE, BEVERAGE } 여기서 BEVERAGE에 " ; " 빠진거같습니다.
안녕하세요. 코드라떼입니다 :) 변수나 생성자를 명시적으로 정의하지 않은 enum의 경우 세미콜론을 작성하지 않더라도 정상 컴파일됩니다. 그러나 enum 상수 외의 구문이 작성되면 컴파일 단계에서 상수와 그 외의 코드를 분리해서 해석해야 하므로 세미콜론을 작성해야 합니다. 감사합니다 :)
5:07에 gender 생성자 부분에 this.value = value; 가 되어야하는 거 아닌가요??? this.value = sex;면 sex는 어디서 받아오는 건가요....??
안녕하세요. 코드라떼입니다 :) 먼저 불편함을 드려 죄송합니다. 영상 확인 결과 잘못 작성된 내용을 확인했습니다. this.value = value;가 맞습니다. 감사합니다.
추가노트에 Configuration configuration1 = Configuration.getInstance(); System.out.println(configuration2); // 동일한 해시코드 Configuration@610455d6 configuration2인데 1이라고 되어있어요. ㅎㅎ
자바배움이님 감사합니다^^ 수정했습니다.
1:20 정도에 정적상수변수 클래스명이 CafeFoodCategory로 나온 오타가 있는 것 같습니다
부끄럽네요.. 확인했습니다^^ 감사합니다!!
실습코드를 살펴봅시다.