item 2 leekyunghee - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

์ƒ์„ฑ์ž์— ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋งŽ๋‹ค๋ฉด ๋นŒ๋”๋ฅผ ๊ณ ๋ คํ•˜๋ผ

  • ์ •์  ํŒฉํ† ๋ฆฌ์™€ ์ƒ์„ฑ์ž์—๋Š” ์„ ํƒ์  ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋งŽ์„ ๋•Œ ์ ์ ˆํžˆ ๋Œ€์‘ํ•˜๊ธฐ ์–ด๋ ต๋‹ค๋Š” ์ œ์•ฝ์ด ์žˆ๋‹ค.

์ ์ธต์  ์ƒ์„ฑ์ž ํŒจํ„ด

  • ํ•„์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋งŒ ๋ฐ›๋Š” ์ƒ์„ฑ์ž
  • ํ•„์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์„ ํƒ ๋งค๊ฐœ๋ณ€์ˆ˜ 1๊ฐœ๋ฅผ ๋ฐ›๋Š” ์ƒ์„ฑ์ž
  • ์„ ํƒ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ 2๊ฐœ๊นŒ์ง€ ๋ฐ›๋Š” ์ƒ์„ฑ์ž ํ˜•ํƒœ
  • ์„ ํƒ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ „๋ถ€๋‹ค ๋ฐ›๋Š” ์ƒ์„ฑ์ž๋กœ ๋Š˜๋ ค๊ฐ€๋Š” ๋ฐฉ์‹

๋‹ค์Œ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž.

public class NutritionFacts {
    private final int servingSize; // (ml/1ํšŒ ์ œ๊ณต๋Ÿ‰) ํ•„์ˆ˜ 
    private final int servings;    // (ํšŒ/์ด nํšŒ ์ œ๊ณต๋Ÿ‰) ํ•„์ˆ˜
    private final int calories;    // (1ํšŒ ์ œ๊ณต๋Ÿ‰๋‹น)   ์„ ํƒ
    private final int fat;         // (g/1ํšŒ ์ œ๊ณต๋Ÿ‰)   ์„ ํƒ
    private final int sodium;      // (mg/1ํšŒ ์ œ๊ณต๋Ÿ‰)  ์„ ํƒ
    private final int carbohydrate;  // (g/1ํšŒ ์ œ๊ณต๋Ÿ‰) ์„ ํƒ
    
    public NutritionFacts(int servingSize, int servings) {
        this(servingSize, servings, 0);
    }
    
    public NutritionFacts(int servingSize, int servings, int calories) {
        this(servingSize, servings, calories, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat) {
        this(servingSize, servings, calories, fat, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
        this(servingSize, servings, calories, fat, sodium, 0);
    }
    
    public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
         this.servingSize = servingSize;
         this.servings = servings;
         this.calories = calories;         
         this.fat = fat;
         this.sodium = sodium;
         this.carbohydrate = carbohydrate;
    }
}

์ด ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ์›ํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ชจ๋‘ ํฌํ•จํ•œ ์ƒ์„ฑ์ž ์ค‘ ๊ฐ€์žฅ ์งง์€ ๊ฒƒ์„ ๊ณจ๋ผ ํ˜ธ์ถœํ•˜๋ฉด ๋œ๋‹ค.

NutritionFacts cocaCola = new NutritionFacts(240, 8,  100, 0, 35,  27);

๋ณดํ†ต ์ด๋Ÿฐ ์ƒ์„ฑ์ž๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •ํ•˜๊ธธ ์›์น˜ ์•Š๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๊นŒ์ง€ ํฌํ•จํ•˜๊ธฐ ์‰ฌ์›€. ๊ฐœ์ˆ˜๊ฐ€ ๋”์šฑ ๋Š˜์–ด๋‚˜๊ฑฐ๋‚˜ ์„ค์ •ํ•˜๊ธฐ ์›์น˜์•Š๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๊นŒ์ง€ ๊ฐ’์„ ์ง€์ •ํ•ด์ค˜์•ผํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์žˆ๋‹ค. ์ ์ธต์  ์ƒ์„ฑ์ž ํŒจํ„ด๋„ ์“ธ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ๋งŽ์•„์งˆ ๊ฒฝ์šฐ ํด๋ผ์ด์–ด์–ธํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ์ฝ๊ธฐ ์–ด๋ ต๋‹ค.

์ž๋ฐ” ๋นˆ์ฆˆ ํŒจํ„ด

  • ์„ ํƒ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋งŽ์„ ๋•Œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋Œ€์•ˆ
  • ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—†๋Š” ์ƒ์„ฑ์ž๋กœ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“  ํ›„, ์„ธํ„ฐ(setter) ๋ฉ”์„œ๋“œ๋“ค์„ ํ˜ธ์ถœํ•ด ์›ํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ๋ฐฉ์‹
  • ์ ์ธต์  ์ƒ์„ฑ์ž ํŒจํ„ด๋ณด๋‹ค ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์ง€์ง€๋งŒ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ธฐ ์‰ฝ๊ณ  ๊ทธ ๊ฒฐ๊ณผ ๋” ์ฝ๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ๊ฐ€ ๋˜์—ˆ๋‹ค.
NutritionFacts cocaCola = new NutritionFacts();
cocaChola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);

์ž๋ฐ”๋นˆ์ฆˆ ํŒจํ„ด์˜ ๋‹จ์ 

  • ๊ฐ์ฒด ํ•˜๋‚˜๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๋ฉ”์„œ๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ํ˜ธ์ถœํ•ด์•ผ ํ•˜๊ณ  ๊ฐ์ฒด๊ฐ€ ์™„์ „ํžˆ ์ƒ์„ฑ๋˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ผ๊ด€์„ฑ(consistency)์ด ๋ฌด๋„ˆ์ง„ ์ƒํƒœ์— ๋†“์ด๊ฒŒ ๋œ๋‹ค.
  • ํด๋ž˜์Šค๋ฅผ ๋ถˆ๋ณ€(์•„์ดํ…œ 17)์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์—†์œผ๋ฉฐ ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ์„ ์–ป์œผ๋ ค๋ฉด ์ถ”๊ฐ€ ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค.

๋นŒ๋” ํŒจํ„ด

  • ์ ์ธต์  ์ƒ์„ฑ์ž ํŒจํ„ด์˜ ์•ˆ์ „์„ฑ๊ณผ ์ž๋ฐ” ๋นˆ์ฆˆ ํŒจํ„ด์˜ ๊ฐ€๋…์„ฑ์„ ๊ฒธ๋น„
  • ํด๋ผ์ด์–ธํŠธ๋Š” ํ•„์š”ํ•œ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๋งŒ๋“œ๋Š” ๋Œ€์‹ , ํ•„์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋งŒ์œผ๋กœ ์ƒ์„ฑ์ž(ํ˜น์€ ์ •์  ํŒฉํ„ฐ๋ฆฌ)๋ฅผ ํ˜ธ์ถœํ•ด ๋นŒ๋” ๊ฐ์ฒด๋ฅผ ์–ป๋Š”๋‹ค.
  • ๋นŒ๋” ๊ฐ์ฒด๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ผ์ข…์˜ ์„ธํ„ฐ ๋ฉ”์„œ๋“œ๋“ค๋กœ ์›ํ•˜๋Š” ์„ ํƒ ๋งค๊ฐœ๋ณ€์ˆ˜๋“ค์„ ์„ค์ •ํ•œ๋‹ค.
  • ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—†๋Š” build ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด ๋“œ๋””์–ด ์šฐ๋ฆฌ์—๊ฒŒ ํ•„์š”ํ•œ (๋ณดํ†ต์€ ๋ถˆ๋ณ€์ธ) ๊ฐ์ฒด๋ฅผ ์–ป๋Š”๋‹ค.
  • ๋นŒ๋”๋Š” ์ƒ์„ฑํ•  ํด๋ž˜์Šค ์•ˆ์— ์ •์  ๋ฉค๋ฒ„ ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค์–ด ๋‘๋Š”๊ฒŒ ๋ณดํ†ต์ด๋‹ค.
public class NutritionFacts {
   private final int servingSize;
   private final int servings;
   private final int calories;
   private final int fat;
   private final int sodium;
   private final int carbohydrate;

   public static class Builder {
       // ํ•„์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜ 
       private final int servingSize;
       private final int servings;
      
       // ์„ ํƒ ๋งค๊ฐœ๋ณ€์ˆ˜ - ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค. 
       private int calories = 0;
       private int fat = 0;
       private int sodium = 0;
       private int carbohydrate = 0;

       public Builder(int servingSize, int servings) {
           this.servingSize = servingSize;
           this.servings = servings;
       }
       
       public Builder calories(int val) {
           calories = val;
           return this;
       } 
       public Builder fat(int val) {
           fat = val;
           return this;
       } 
       public Builder sodium(int val) {
           sodium = val;
           return this;
       } 

       public Builder carbohydrate(int val) {
           carbohydrate = val;
           return this;
       }
       
       public NutritionFacts(Builder builder) {
            servingSize = builder.servingSize;
            servings    = builder.servings;
            calories    = builder.calories;
            fat         = builder.fat;
            sodium      = builder.sodium;
            carbohydrate = builder.carbohydrate;

       }


   }

}


}

NutritionFacts ํด๋ž˜์Šค๋Š” ๋ถˆ๋ณ€์ด๋ฉฐ ๋ชจ๋“  ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ธฐ๋ณธ๊ฐ’๋“ค์„ ํ•œ๊ณณ์— ๋ชจ์•„๋’€๋‹ค. ๋นŒ๋”์˜ ์„ธํ„ฐ ๋ฉ”์„œ๋“œ๋“ค์€ ๋นŒ๋” ์ž์‹ ์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฐ์‡„์ ์œผ๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์ด ํ๋ฅด๋“ฏ ์—ฐ๊ฒฐ๋œ๋‹ค๋Š” ๋œป์˜ ํ”Œ๋ฃจ์–ธํŠธ API(fluent API) ํ˜น์€ ๋ฉ”์„œ๋“œ ์—ฐ์‡„(mehod chaining)

// ์‹ค์ œ ์œ„์˜ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();

๋นŒ๋” ํŒจํ„ด์€ ์Šค์นผ๋ผ์— ์žˆ๋Š” ๋ช…๋ช…๋œ ์„ ํƒ์  ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ‰๋‚ด๋‚ธ ๊ฒƒ ์ž˜๋ชป๋œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ตœ๋Œ€ํ•œ ์ผ์ฐ ๋ฐœ๊ฒฌํ•˜๋ ค๋ฉด ๋นŒ๋”์˜ ์ƒ์„ฑ์ž์™€ ๋ฉ”์„œ๋“œ์—์„œ ์ž…๋ ฅ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๊ฒ€์‚ฌํ•˜๊ณ  build ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” ์ƒ์„ฑ์ž์—์„œ ์—ฌ๋Ÿฌ ๋งค๊ฐœ๋ณ€์ˆ˜์— ๊ฑธ์นœ ๋ถˆ๋ณ€์‹์„ ๊ฒ€์‚ฌํ•˜์ž. ๊ณต๊ฒฉ์— ๋Œ€๋น„ํ•ด ์ด๋Ÿฐ ๋ถˆ๋ณ€์‹์„ ๋ณด์žฅํ•˜๋ ค๋ฉด ๋นŒ๋”๋กœ๋ถ€ํ„ฐ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ณต์‚ฌํ•œ ํ›„ ํŒจ๋‹น ๊ฐ์ฒด ํ•„๋“œ๋“ค๋„ ๊ฒ€์‚ฌํ•ด์•ผํ•œ๋‹ค. (์•„์ดํ…œ 50) ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์ž˜๋ชป๋˜์—ˆ๋Š”์ง€ ์•Œ๋ ค์ฃผ๋Š” ๋ฉ”์„ธ์ง€๋ฅผ ๋‹ด์•„ IllegalArgumentException์„ ๋˜์ง€๋ฉด ๋œ๋‹ค. ๋นŒ๋” ํŒจํ„ด์€ ๊ณ„์ธต์ ์œผ๋กœ ์„ค๊ณ„๋œ ํด๋ž˜์Šค์™€ ํ•จ๊ป˜ ์“ฐ๊ธฐ์— ์ข‹๋‹ค. ๊ฐ ๊ณ„์ธต์˜ ํด๋ž˜์Šค์— ๊ด€๋ จ ๋นŒ๋”๋ฅผ ๋ฉค๋ฒ„๋กœ ์ •์˜ํ•˜์ž. ์ถ”์ƒ ํด๋ž˜์Šค๋Š” ์ถ”์ƒ ๋นŒ๋”๋ฅผ, ๊ตฌ์ฒด ํด๋ž˜์Šค๋Š” ๊ตฌ์ฒด ๋นŒ๋”๋ฅผ ๊ฐ–๊ฒŒ ํ•œ๋‹ค.

// ๊ณ„์ธต์ ์œผ๋กœ ์„ค๊ณ„๋œ ํด๋ž˜์Šค์™€ ์ž˜ ์–ด์šธ๋ฆฌ๋Š” ๋นŒ๋” ํŒจํ„ด
public abstract class Pizza {
    public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE } 
    final Set<Topping> toppings;
    
    abstract static class Builder<T extends Builder<T>> {
        EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
        public T addTopping(Topping topping) { 
                addToppings.add(Objects.requireNonNULL(topping));
                return self();
        }   
        // ํ•˜์œ„  ํด๋ž˜์Šค๋Š” ์ด ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ์ •์˜ ํ•˜์—ฌ "this"๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•ด์•ผํ•œ๋‹ค.  
        abstract Pizza build();
        protected abstract T self();
    }
    Pizza(Builder<?> builder) {
        toppings = builder.toppings.clone();   // Item 50์ฐธ์กฐ  
    } 
}

Pizza.Builder ํด๋ž˜์Šค๋Š” ์žฌ๊ท€์  ํƒ€์ž… ํ•œ์ •(์•„์ดํ…œ 30)์„ ์ด์šฉํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค. ์—ฌ๊ธฐ์— ์ถ”์ƒ ๋ฉ”์„œ๋“œ์ธ self๋ฅผ ๋”ํ•ด ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ๋Š” ํ˜•๋ณ€ํ™˜ํ•˜์ง€ ์•Š๊ณ ๋„ ๋ฉ”์„œ๋“œ ์—ฐ์‡„๋ฅผ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋‹ค. self ํƒ€์ž…์ด ์—†๋Š” ์ž๋ฐ”๋ฅผ ์œ„ํ•œ ์ด ์šฐํšŒ ๋ฐฉ๋ฒ•์„ ์‹œ๋ฎฌ๋ ˆ์ดํŠธํ•œ ์…€ํ”„ ํƒ€์ž… ๊ด€์šฉ๊ตฌ๋ผ ํ•œ๋‹ค.

์ƒ์„ฑ์ž๋กœ๋Š” ๋ˆ„๋ฆด ์ˆ˜ ์—†๋Š” ์‚ฌ์†Œํ•œ ์ด์ ์œผ๋กœ ๋นŒ๋”๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ฐ€๋ณ€์ธ์ˆ˜(varargs) ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ๊ฐ์„ ์ ์ ˆํ•œ ๋ฉ”์„œ๋“œ๋กœ ๋‚˜๋ˆ  ์„ ์–ธํ•˜๋ฉด ๋œ๋‹ค. ๋ฉ”์„œ๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•˜๋„๋ก ํ•˜๊ณ  ๊ฐ ํ˜ธ์ถœ ๋•Œ ๋„˜๊ฒจ์ง„ ๋งค๊ฐœ๋ณ€์ˆ˜๋“ค์„ ํ•˜๋‚˜์˜ ํ•„๋“œ๋กœ ๋ชจ์„ ์ˆ˜๋„ ์žˆ๋‹ค. ๋นŒ๋“œ ํŒจํ„ด์€ ๋นŒ๋“œ ํ•˜๋‚˜๋กœ ์—ฌ๋Ÿฌ ๊ฐ์ฒด๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ณ  ๋นŒ๋”์— ๋„˜๊ธฐ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๋‹ค. ๊ฐ์ฒด๋งˆ๋‹ค ๋ถ€์—ฌ๋˜๋Š” ์ผ๋ จ๋ฒˆํ˜ธ์™€ ๊ฐ™์€ ํŠน์ • ํ•„๋“œ๋Š” ๋นŒ๋”๊ฐ€ ์•Œ์•„์„œ ์ฑ„์šฐ๋„๋ก ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

๋นŒ๋” ํŒจํ„ด์˜ ๋‹จ์ 

  • ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๊ทธ์— ์•ž์„œ ๋นŒ๋”๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด์•ผํ•œ๋‹ค.
  • ๋นŒ๋” ์ƒ์„ฑ ๋น„์šฉ์ด ํฌ์ง€๋Š” ์•Š์ง€๋งŒ ์„ฑ๋Šฅ์— ๋ฏผ๊ฐํ•œ ์ƒํ™ฉ์—์„œ๋Š” ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜์žˆ๋‹ค.
  • ์ฝ”๋“œ๊ฐ€ ์žฅํ™ฉํ•ด์„œ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ 4๊ฐœ๋Š” ๋˜์–ด์•ผํ•œ๋‹ค.

ํ•ต์‹ฌ ์ •๋ฆฌ

๋งค๊ฐœ๋ณ€์ˆ˜ ์ค‘ ๋‹ค์ˆ˜๊ฐ€ ํ•„์ˆ˜๊ฐ€ ์•„๋‹ˆ๊ฑฐ๋‚˜ ๊ฐ™์€ ํƒ€์ž…์ธ ๊ฒฝ์šฐ ํ˜น์€ ์ƒ์„ฑ์ž๋‚˜ ์ •์  ํŒฉํ„ฐ๋ฆฌ๊ฐ€ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋งŽ๋‹ค๋ฉด ๋นŒ๋” ํŒจํ„ด์„ ์„ ํƒํ•˜๋Š”๊ฒŒ ๋‚ซ๋‹ค.
๋นŒ๋”๋Š” ์ ์ธต์  ์ƒ์„ฑ์ž ๋ณด๋‹ค ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๋ฅผ ์ฝ๊ธฐ ์‰ฝ๊ณ  ๊ฐ„๊ฒฐํ•˜๋ฉฐ ์ž๋ฐ” ๋นˆ์ฆˆ ๋ณด๋‹ค ํ›จ์”ฌ ์•ˆ์ „ํ•˜๋‹ค. 
โš ๏ธ **GitHub.com Fallback** โš ๏ธ