Coding Planet

[μ΄νŽ™ν‹°λΈŒ μžλ°”]chapter2 : [μ•„μ΄ν…œ3] private μƒμ„±μžλ‚˜ μ—΄κ±° νƒ€μž…μœΌλ‘œ μ‹±κΈ€ν„΄μž„μ„ λ³΄μ¦ν•˜λΌ λ³Έλ¬Έ

πŸ’» Java Study/Java 이둠 정리

[μ΄νŽ™ν‹°λΈŒ μžλ°”]chapter2 : [μ•„μ΄ν…œ3] private μƒμ„±μžλ‚˜ μ—΄κ±° νƒ€μž…μœΌλ‘œ μ‹±κΈ€ν„΄μž„μ„ λ³΄μ¦ν•˜λΌ

jhj.sharon 2023. 8. 9. 23:33
λ°˜μ‘ν˜•

μ•„μ΄ν…œ3: private μƒμ„±μžλ‚˜ μ—΄κ±° νƒ€μž…μœΌλ‘œ μ‹±κΈ€ν„΄μž„μ„ λ³΄μ¦ν•˜λΌ

μ‹±κΈ€ν„΄(sigleton)μ΄λž€ μΈμŠ€ν„΄μŠ€λ₯Ό 였직 ν•˜λ‚˜λ§Œ 생성할 수 μžˆλŠ” 클래슀λ₯Ό λ§ν•œλ‹€. μ‹±κΈ€ν„΄μ˜ μ „ν˜•μ μΈ μ˜ˆλ‘œλŠ” ν•¨μˆ˜μ™€ 같은 λ¬΄μƒνƒœ κ°μ²΄λ‚˜ 섀계상 μœ μΌν•΄μ•Όν•˜λŠ” μ‹œμŠ€ν…œ μ»΄ν¬λ„ŒνŠΈλ₯Ό λ“€ 수 μžˆλ‹€. 그런데 클래슀λ₯Ό μ‹±κΈ€ν„΄μœΌλ‘œ λ§Œλ“€λ©΄ 이λ₯Ό μ‚¬μš©ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈλ₯Ό ν…ŒμŠ€νŠΈν•˜κΈ°κ°€ μ–΄λ €μ›Œμ§ˆ 수 μžˆλ‹€. νƒ€μž…μ„ μΈν„°νŽ˜μ΄μŠ€λ‘œ μ •μ˜ν•œ λ‹€μŒ κ·Έ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄μ„œ λ§Œλ“  싱글턴이 μ•„λ‹ˆλΌλ©΄ μ‹±κΈ€ν„΄ μΈμŠ€ν„΄μŠ€λ₯Ό κ°€μ§œ(mock)κ΅¬ν˜„μœΌλ‘œ λŒ€μ²΄ν•  수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€. 즉, 클래슀λ₯Ό μ‹±κΈ€ν„΄μœΌλ‘œ λ§Œλ“€ 경우 ν•΄λ‹Ή 클래슀의 μΈμŠ€ν„΄μŠ€κ°€ μ‹œμŠ€ν…œ 전체에 ν•˜λ‚˜μ΄κΈ° λ•Œλ¬Έμ— ν…ŒμŠ€νŠΈμ™€ μ‹€μ œ ν™˜κ²½μ—μ„œμ˜ λ™μž‘μ΄ κ°•ν•˜κ²Œ 결합될 수 μžˆλ‹€.
싱글턴을 λ§Œλ“œλŠ” 방법은 보톡 두가지이닀. 두 방식 λͺ¨λ‘ μƒμ„±μžλŠ” private으둜 감좰두고 μœ μΌν•œ μΈμŠ€ν„΄μŠ€μ— μ ‘κ·Όν•  수 μžˆλŠ” μˆ˜λ‹¨μœΌλ‘œ public static 멀버λ₯Ό ν•˜λ‚˜ λ§Œλ“€μ–΄ λ‘”λ‹€.

 

 1. public static final ν•„λ“œ λ°©μ‹μ˜ μ‹±κΈ€ν„΄

 

1) νŠΉμ§• 및 ν˜•μ‹

(1) Private μƒμ„±μž 

  • μƒμ„±μžλ₯Ό 'private'으둜 μ„ μ–Έν•˜μ—¬ μ™ΈλΆ€μ—μ„œ 이 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 수 μ—†κ²Œ ν•œλ‹€.
  • publicμ΄λ‚˜ protectedμƒμ„±μžκ°€ μ—†μœΌλ―€λ‘œ μƒμ„±μžκ°€ μ΄ˆκΈ°ν™”λ  λ•Œ λ§Œλ“€μ–΄μ§„ μΈμŠ€ν„΄μŠ€κ°€ 전체  μ‹œμŠ€ν…œμ—μ„œ ν•˜λ‚˜λΏμž„μ΄ 보μž₯λœλ‹€.
  • μ˜ˆμ™Έμ μœΌλ‘œ κΆŒν•œμ΄ μžˆλŠ” ν΄λΌμ΄μ–ΈνŠΈλŠ” λ¦¬ν”Œλ ‰μ…˜  API인 AccesibleObject.setAccessible을 μ‚¬μš©ν•΄ priavate μƒμ„±μžλ₯Ό 호좜 ν•  수 μžˆλ‹€. μ΄λŸ¬ν•œ 곡격을 λ°©μ–΄ν•˜λ €λ©΄ μƒμ„±μžλ₯Ό μˆ˜μ •ν•˜μ—¬ 두 번 μ§Έ 객체가 μƒμ„±λ˜λ €ν•  λ•Œ μ˜ˆμ™Έλ₯Ό λ˜μ§€κ²Œν•˜λ©΄ λœλ‹€.
AccessibleObject.setAccessibleλŠ” Java의 λ¦¬ν”Œλ ‰μ…˜ API에 μ†ν•˜λŠ” λ©”μ„œλ“œμ΄λ‹€.λ¦¬ν”Œλ ‰μ…˜ APIλŠ” ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ 쀑에 (λŸ°νƒ€μž„μ—μ„œ) Java 클래슀의 λ‚΄λΆ€ ꡬ쑰λ₯Ό κ²€μ‚¬ν•˜κ±°λ‚˜ μˆ˜μ •ν•  수 있게 ν•΄μ£ΌλŠ” κ°•λ ₯ν•œ κΈ°λŠ₯듀을 μ œκ³΅ν•œλ‹€.
AccessibleObject.setAccessible λ©”μ„œλ“œλŠ” Field, Method, 그리고 Constructor와 같은 λ¦¬ν”Œλ ‰μ…˜ 객체의 μ ‘κ·Ό μ œμ–΄λ₯Ό μš°νšŒν•  수 μžˆλ‹€.

(2) Public static final  ν•„λ“œ

  • 'public static final'둜 μ„ μ–Έλœ 'INSTANCE' ν•„λ“œλŠ” 클래슀 λ‘œλ“œ μ‹œ ν•œ 번만 μ΄ˆκΈ°ν™” λœλ‹€. 이 ν•„λ“œλŠ” 이 클래슀의 μœ μΌν•œ μΈμŠ€ν„΄μŠ€λ₯Ό μ°Έμ‘°ν•œλ‹€.

(3) Static μ΄ˆκΈ°ν™”

  • 'INSTANCE'λŠ” 정적 μ΄ˆκΈ°ν™” λΈ”λ‘μ—μ„œ μ΄ˆκΈ°ν™”λœλ‹€(정적 μ΄ˆκΈ°ν™” 블둝(static intitialization block_은 ν΄λž˜μŠ€κ°€ JVM에 μ˜ν•΄ 처음 λ‘œλ“œλ  λ•Œ ν•œ 번만 μ‹€ν–‰λœλ‹€.).
  • JavaλŠ” 정적 μ΄ˆκΈ°ν™”κ°€ μŠ€λ ˆλ“œ μ•ˆμ „(Thread-safety)ν•˜κ²Œ μˆ˜ν–‰λ˜λ„λ‘ 보μž₯ ν•˜λ―€λ‘œ λ©€ν‹°μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œλ„ μ•ˆμ „ν•˜λ‹€. 즉, μ‹±κΈ€ν„΄ νŒ¨ν„΄μ—μ„œλŠ” 클래슀 λ‘œλ”© 및 μ΄ˆκΈ°ν™” 과정을 동기화 ν•˜λŠ” JVM의 λ™μž‘ 방식을 ν™œμš©ν•˜κΈ° λ•Œλ¬Έμ— μΈμŠ€ν„΄μŠ€μ˜ μŠ€λ ˆλ“œ μ•ˆμ „ν•œ μ΄ˆκΈ°ν™”λ₯Ό 보μž₯ν•œλ‹€.
import java.lang.reflect.Constructor;

public class Singleton {

    // 자기 μžμ‹ μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό static final ν•„λ“œλ‘œ 생성
    public static final Singleton INSTANCE = new Singleton();
    private static boolean isInstanceCreated = false;

    // private μƒμ„±μžλ‘œ μ™ΈλΆ€μ—μ„œ μΈμŠ€ν„΄μŠ€ 생성을 방지
    private Singleton() {
        if (isInstanceCreated) {
            throw new IllegalStateException("Instance already created!");
        }
        isInstanceCreated = true;
    }

    // ν•„μš”ν•œ λΉ„μ¦ˆλ‹ˆμŠ€ λ©”μ„œλ“œ
    public void someMethod() {
        // ...
    }
    
    public class ReflectionAttack {
    public static void main(String[] args) throws Exception {
        // 정상적인 λ°©λ²•μœΌλ‘œ μ‹±κΈ€ν„΄ μΈμŠ€ν„΄μŠ€ νšλ“
        Singleton instance1 = Singleton.INSTANCE;

        // λ¦¬ν”Œλ ‰μ…˜μ„ μ‚¬μš©ν•œ 곡격
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        constructor.setAccessible(true); // private μƒμ„±μžμ˜ μ ‘κ·Ό μ œμ–΄ 우회
        Singleton instance2 = constructor.newInstance();

        System.out.println(instance1 == instance2); // false
    }
}


//μ‚¬μš©λ°©λ²•
Singleton singleton = Singleton.INSTANCE;
singleton.someMethod();

 

2) μž₯점

  • ν•΄λ‹Ή ν΄λž˜μŠ€κ°€ μ‹±κΈ€ν„΄μž„μ΄ API에 λͺ…ν™•ν•˜κ²Œ λ“œλŸ¬λ‚œλ‹€. public static ν•„λ“œκ°€ finalμ΄λ―€λ‘œ μ ˆλŒ€λ‘œ λ‹€λ₯Έ 객체λ₯Ό μ°Έμ‘°ν•  수 μ—†λ‹€.
  • κ΅¬ν˜„μ΄ κ°„λ‹¨ν•˜λ‹€.
  • JVM의 ν΄λž˜μŠ€ λ‘œλ”© λ©”μ»€λ‹ˆμ¦˜μ„ ν™œμš©ν•˜μ—¬ μŠ€λ ˆλ“œ μ•ˆμ „성을 λ³΄μž₯ν•œλ‹€.

 

3) 단점

  • μ‹±κΈ€ν„΄ μΈμŠ€ν„΄μŠ€κ°€ μ‚¬μš©λ˜μ§€ μ•Šλ”라도 ν΄λž˜μŠ€κ°€ λ‘œλ“œλ  λ•Œ μƒμ„±λœλ‹€. (μ¦‰μ‹œ λ‘œλ”©, Eager Loading)
  • μΈμŠ€ν„΄μŠ€ μƒμ„± λ‘œμ§μ„ λ³€κ²½ν•˜κ±°λ‚˜, μ΄ˆκΈ°ν™”에 μΆ”κ°€ μž‘업이 ν•„μš”ν•œ κ²½μš° κ΅¬ν˜„이 λ³΅μž‘ν•΄μ§ˆ μˆ˜ μžˆλ‹€.

 

 2. 정적 νŒ©ν„°λ¦¬ λ°©μ‹μ˜ μ‹±κΈ€ν„΄

 

1) νŠΉμ§• 및 ν˜•μ‹

 

(1) Private μƒμ„±μž & (2) Public static final  ν•„λ“œ 동일

(3) Public static λ©”μ„œλ“œ

  • μ™ΈλΆ€μ—μ„œ 이 λ©”μ„œλ“œλ₯Ό 톡해 μ‹±κΈ€ν„΄ μΈμŠ€ν„΄μŠ€μ— μ ‘κ·Όν•  수 μžˆλ‹€. 처음 호좜될 λ•Œ μ‹±κΈ€ν„΄ μΈμŠ€ν„΄μŠ€λ₯Ό μ΄ˆκΈ°ν™”ν•˜κ±°λ‚˜ 이미 μ΄ˆκΈ°ν™”λœ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€.
public class Singleton {
    // Private static instance
    private static final Singleton INSTANCE = new Singleton();

    // Private constructor
    private Singleton() {
        // Initialization logic
    }

    // Public static factory method
    public static Singleton getInstance() {
        return INSTANCE;
    }
    
    
    // 이 λ©”μ„œλ“œλ₯Ό μΆ”κ°€ν•˜μ—¬ 역직렬화 μ‹œ μ‹±κΈ€ν„΄ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜.
    private Object readResolve() {
        return INSTANCE;
    }
}


//μ‚¬μš©λ°©λ²•
Singleton singleton = Singleton.getInstance();

 

2) μž₯점

  • μ‹±κΈ€ν„΄ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œμ˜ 이름을 자유둭게 지정할 수 μžˆλ‹€.
  • λ‚˜μ€‘μ— 싱글턴이 μ•„λ‹ˆκ²Œ λ³€κ²½ν•˜λ”λΌλ„ APIλ₯Ό λ³€κ²½ν•˜μ§€ μ•Šκ³  κ΅¬ν˜„λ§Œ λ³€κ²½ν•  수 μžˆλ‹€. μœ μΌν•œ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•˜λ˜ νŒ©ν„°λ¦¬ λ©”μ„œλ“œκ°€ ν˜ΈμΆœν•˜λŠ” μŠ€λ ˆλ“œλ³„λ‘œ λ‹€λ₯Έ μΈμŠ€ν„΄μŠ€λ₯Ό λ„˜κ²¨μ£Όκ²Œ ν•  수 μžˆλ‹€.
  • 정적 νŒ©ν„°λ¦¬ 방식을 μ‚¬μš©ν•˜λ©΄, μ œλ„ˆλ¦­ 싱글턴을 λ§Œλ“€ 수 μžˆλ‹€.
  • 정적 νŒ©ν„°λ¦¬μ˜ λ©”μ„œλ“œ μ°Έμ‘°λ₯Ό κ³΅κΈ‰μž(supplier)둜 μ‚¬μš©ν•  수 μžˆλ‹€. μ΄λŸ¬ν•œ λ°©μ‹μœΌλ‘œ, λ©”μ„œλ“œ 참쑰와 κ³΅κΈ‰μžλ₯Ό ν•¨κ»˜ μ‚¬μš©ν•˜λ©΄ κΈ°μ‘΄ λ©”μ„œλ“œμ˜ λ‘œμ§μ„ μž¬μ‚¬μš©ν•˜λ©΄μ„œ ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ° μŠ€νƒ€μΌμ„ μ μš©ν•  수 μžˆλ‹€.
- λ©”μ„œλ“œ μ°Έμ‘°(Method Reference)
: λžŒλ‹€ ν‘œν˜„μ‹μ˜ 또 λ‹€λ₯Έ κ°„λ‹¨ν•œ ν˜•νƒœ. κΈ°μ‘΄ λ©”μ„œλ“œμ˜ λ™μž‘μ„ μž¬μ‚¬μš©ν•˜κ±°λ‚˜ νŠΉμ • λ©”μ„œλ“œλ₯Ό ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ κ΅¬ν˜„μœΌλ‘œ μ°Έμ‘°ν•˜κ³  싢을 λ•Œ μ‚¬μš©ν•œλ‹€. λ©”μ„œλ“œ μ°Έμ‘°λŠ” '::' μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•˜μ—¬ ν‘œν˜„ν•œλ‹€.

- κ³΅κΈ‰μž(Supplier)
: κ³΅κΈ‰μžλŠ” λ§€κ°œλ³€μˆ˜λ₯Ό 받지 μ•Šκ³  값을 λ°˜ν™˜ν•˜λŠ” 'get()' λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.
Supplier<Singleton> singletonSupplier = Singleton::getInstance;

// 이제 supplierλ₯Ό μ‚¬μš©ν•˜μ—¬ Singleton μΈμŠ€ν„΄μŠ€λ₯Ό 얻을 수 μžˆλ‹€.
Singleton singleton = singletonSupplier.get();

 

 

** μ‹±κΈ€ν„΄μ˜ 직렬화(Serializable)

(직렬화에 λŒ€ν•œ 포슀트 μ°Έμ‘° : https://sharonprogress.tistory.com/manage/posts/)

  • μ‹±κΈ€ν„΄ ν΄λž˜μŠ€κ°€ 'Serializable'μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λ©΄, 객체의 직렬화 및 역직렬화λ₯Ό 톡해 μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 수 μžˆλ‹€. μ΄λ‘œμΈν•΄ μ‹±κΈ€ν„΄μ˜ 기본원칙이 κΉ¨μ§€κ²Œ λœλ‹€. 
  • μœ„μ—μ„œ μ†Œκ°œν•œ λ‘˜ 쀑 ν•˜λ‚˜μ˜ λ°©μ‹μœΌλ‘œ λ§Œλ“  μ‹±κΈ€ν„΄ 클래슀λ₯Ό μ§λ ¬ν™”ν•˜λ €λ©΄ λ‹¨μˆœνžˆ Serializable을 μ„ μ–Έν•˜λŠ” κ²ƒλ§ŒμœΌλ‘œλŠ” λΆ€μ‘±ν•˜λ‹€. λͺ¨λ“  μΈμŠ€ν„΄μŠ€ ν•„λ“œλ₯Ό μΌμ‹œμ (transient)이라고 μ„ μ–Έν•΄μ•Όν•˜κ³  readResolve λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•΄μ•Όν•œλ‹€.
  • readResolve()λ©”μ„œλ“œλŠ” 역직렬화 μ‹œ μ‹±κΈ€ν„΄ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•˜λ„λ‘ 보μž₯ν•  수 μžˆλ‹€. μ‹€μ œλ‘œ readResolve()λŠ” 역직렬화 쀑에 ν˜ΈμΆœλ˜μ–΄ λŒ€μ²΄ν•  객체λ₯Ό λ°˜ν™˜ν•œλ‹€.

 

 

3. μ—΄κ±° νƒ€μž… λ°©μ‹μ˜ μ‹±κΈ€ν„΄ - λ°”λžŒμ§ν•œ 방법

1) νŠΉμ§• 및 ν˜•μ‹

  • μ—΄κ±° νƒ€μž… λ°©μ‹μ˜ 싱글턴은 싱글턴을 κ΅¬ν˜„ν•˜λŠ” κ°€μž₯ λ°”λžŒμ§ν•œ 방법 쀑에 ν•˜λ‚˜μ΄λ‹€. public ν•„λ“œ 방법과 λΉ„μŠ·ν•˜μ§€λ§Œ 더 κ°„κ²°ν•˜κ³  μΆ”κ°€ λ…Έλ ₯없이 직렬화 ν•  수 μžˆλ‹€.  enum에 μ •μ˜λœ 각 μƒμˆ˜λŠ” ν•΄λ‹Ή enum νƒ€μž…μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό λ‚˜νƒ€λ‚Έλ‹€.
  • enumμ—μ„œ μƒμ„±μžλ₯Ό μ •μ˜ν•˜λŠ” 것은 κ°€λŠ₯ν•˜λ‹€. κ·ΈλŸ¬λ‚˜, enum의 μƒμ„±μžλŠ” 항상 private μ΄λ―€λ‘œγ…œ μ™ΈλΆ€μ—μ„œ enum의 μƒμ„±μžλ₯Ό ν˜ΈμΆœν•˜μ—¬ μƒˆ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“œλŠ” 것은 λΆˆκ°€λŠ₯ν•˜λ‹€.
public enum Singleton {
    INSTANCE;

    public void someMethod() {
        // μ‹±κΈ€ν„΄ 객체의 λ™μž‘μ„ μ •μ˜
    }
}

//μ‚¬μš© 방법
Singleton singleton = Singleton.INSTANCE;
singleton.someMethod();

 

2) μž₯점

  • κ°„κ²°μ„±: μ½”λ“œκ°€ 맀우 κ°„κ²°ν•˜λ©°, λͺ…μ‹œμ μœΌλ‘œ μ‹±κΈ€ν„΄μž„μ„ λ‚˜νƒ€λ‚Έλ‹€.
  • 직렬화에 μ•ˆμ „: μ—΄κ±° νƒ€μž…μ˜ 직렬화와 역직렬화가 JVM에 μ˜ν•΄ κ΄€λ¦¬λ˜λ―€λ‘œ, μƒˆλ‘œμš΄ 객체가 μƒμ„±λ˜λŠ” λ¬Έμ œκ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.
  • λ¦¬ν”Œλ ‰μ…˜μ— μ•ˆμ „: Java의 enum μƒμ„±μžλŠ” 항상 private이닀. 이것은 μ–Έμ–΄ μžμ²΄μ— μ˜ν•΄ κ°•μ œλ˜λ―€λ‘œ, λ¦¬ν”Œλ ‰μ…˜μ„ μ‚¬μš©ν•˜μ—¬ μƒμ„±μžμ˜ μ ‘κ·Ό μ œν•œμ„ λ³€κ²½ν•  수 μ—†λ‹€. λ˜ν•œ enum νƒ€μž…μ€ μƒμˆ˜μ˜ 수만큼만 μΈμŠ€ν„΄μŠ€λ₯Ό κ°€μ§ˆ 수 있고 μ΄λŠ” JVMμ—μ„œ κ΄€λ¦¬λ˜κΈ° λ•Œλ¬Έμ— λ¦¬ν”Œλ ‰μ…˜μ„ μ‚¬μš©ν•˜μ—¬ μΆ”κ°€ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“œλŠ” 것은 λΆˆκ°€λŠ₯ν•˜λ‹€.
  • λ©€ν‹°μŠ€λ ˆλ”©μ— μ•ˆμ „: μ—΄κ±° νƒ€μž…μ˜ μƒμ„±μ€ μžλ°”μ—μ„œ μŠ€λ ˆλ“œ μ•ˆμ „ν•˜κ²Œ μˆ˜ν–‰λœλ‹€.

3) 단점

  • λ§Œλ“€λ €λŠ” 싱글턴이 Enum μ™Έμ˜ 클래슀λ₯Ό 상속해야 ν•œλ‹€λ©΄ 이 방법은 μ‚¬μš©ν•  수 μ—†λ‹€.
  • μ—΄κ±° νƒ€μž…μ΄ λ‹€λ₯Έ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λ„λ‘ μ„ μ–Έν•  수 λŠ” μžˆλ‹€.
λ°˜μ‘ν˜•
Comments