[객체지ν–₯ν”„λ‘œκ·Έλž˜λ°] μ‰¬μš΄ μ˜ˆμ‹œμ™€ ν•¨κ»˜ λ³΄λŠ” SOLID 원칙

2025. 2. 12. 23:41Β·βš™οΈ CS & 기타 개발 자료

πŸ”Ž 주제

객체 μ§€ν–₯ ν”„λ‘œκ·Έλž˜λ°μ—λŠ” 'SOLID' 원칙이 μžˆλ‹€. Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, Dependency Inversion 이 5κ°€μ§€ 원칙을 λ§ν•˜λŠ”λ° μžμ„Έν•œ μ˜ˆμ‹œμ™€ ν•¨κ»˜ 5개의 κ°œλ…μ„ λͺ¨λ‘ 보고자 ν•œλ‹€.

βœ… S: 단일 μ±…μž„ 원칙(SRP)

단일 μ±…μž„ 원칙(Single Responsibility, SRP)λŠ” "ν΄λž˜μŠ€λŠ” ν•˜λ‚˜μ˜ μ±…μž„λ§Œ κ°€μ Έμ•Ό ν•œλ‹€"λŠ” 원칙이닀.

  • μ±…μž„: ν΄λž˜μŠ€κ°€ μ²˜λ¦¬ν•΄μ•Ό ν•˜λŠ” ν•˜λ‚˜μ˜ λ³€κ²½ 이유

μ‰½κ²ŒλŠ” ν΄λž˜μŠ€κ°€ λ‹΄λ‹Ήν•΄μ„œ μ²˜λ¦¬ν•΄μ•Ό ν•˜λŠ” ν•˜λ‚˜μ˜ "일"이라고 λ³Ό 수 μžˆλ‹€. ν΄λž˜μŠ€λŠ” μžμ‹ μ΄ 맑은 νŠΉμ •ν•œ "일"만 μ²˜λ¦¬ν•΄μ•Ό ν•œλ‹€. ν•˜λ‚˜μ˜ ν΄λž˜μŠ€λŠ” 단 ν•œκ°€μ§€μ˜ 이유둜만 λ³€κ²½λ˜μ–΄μ•Ό ν•œλ‹€.

ν•˜λ‚˜μ˜ ν΄λž˜μŠ€κ°€ λ§‘λŠ” μ±…μž„μ΄ λ§Žμ•„μ§ˆμˆ˜λ‘, ν΄λž˜μŠ€λŠ” λ³΅μž‘ν•΄μ§€κ³  μˆ˜μ •κ³Ό μœ μ§€λ³΄μˆ˜κ°€ μ–΄λ €μ›Œμ§„λ‹€. ν•œ 일을 λ³€κ²½ν–ˆλ‹€κ°€ λ‹€λ₯Έ 일에 영ν–₯을 끼칠 수 있기 λ•Œλ¬Έμ΄λ‹€.

β˜• μ˜ˆμ‹œ

μΌμƒμƒν™œμ„ μ˜ˆμ‹œλ‘œ μƒκ°ν•΄λ³΄μž.

μΉ΄νŽ˜μ—μ„œ 직원이 ν•œ λͺ…λ§Œ μžˆλ‹€. 이 직원은 컀피λ₯Ό μ œμ‘°ν•˜κ³ , 계산도 ν•˜κ³ , μ²­μ†Œλ„ ν•œλ‹€.

ν•œ λͺ…μ˜ 직원이 μ—¬λŸ¬ 업무λ₯Ό λ§‘κ³  μžˆλ‹€. λ§Œμ•½ 컀피 제쑰 λ ˆμ‹œν”Όκ°€ λ°”λ€Œκ³ , 포슀기 μ‹œμŠ€ν…œλ„ λ°”λ€Œκ³ , μ²­μ†Œ ꡬ역도 바뀐닀면 μ μ‘ν•˜κΈ° 쉽지 μ•Šμ„ 것이닀.

  • 직원=클래슀, 업무=μ±…μž„, λ ˆμ‹œν”Ό, μ‹œμŠ€ν…œ, ꡬ역 λ°”λ€œ=λ³€κ²½ 이유, 적응=클래슀 μˆ˜μ •/변경이라고 μƒκ°ν•΄λ³΄μž.

μΉ΄νŽ˜μ— 직원이 λŠ˜μ—ˆλ‹€. λ°”λ¦¬μŠ€νƒ€λŠ” 컀피λ₯Ό λ§Œλ“€κ³ , μΊμ…”λŠ” 계산을 ν•˜κ³ , μ²­μ†ŒλΆ€λŠ” μ²­μ†Œλ₯Ό ν•œλ‹€.

컀피 메뉴가 λ°”λ€Œμ–΄λ„ 캐셔와 μ²­μ†ŒλΆ€λŠ” μ‹ κ²½μ“°μ§€ μ•ŠλŠ”λ‹€. 포슀기 μ‹œμŠ€ν…œμ΄ λ°”λ€Œμ–΄λ„ λ°”λ¦¬μŠ€νƒ€μ™€ μ²­μ†ŒλΆ€λŠ” μ‹ κ²½μ“°μ§€ μ•ŠλŠ”λ‹€. μžμ‹ μ˜ μ±…μž„μ΄ μ•„λ‹ˆκΈ° λ•Œλ¬Έμ΄λ‹€.

업무 λΆ„μž₯(=μ±…μž„μ„ λ‚˜λˆ„λŠ” 것)이 효율적이고 μ•ˆμ •μ μΈ μš΄μ˜μ„ μœ„ν•œ 해닡이닀.

πŸ–₯️ μ˜ˆμ‹œ μ½”λ“œ

  • 단일 μ±…μž„ 원칙을 μ§€ν‚€μ§€ μ•Šμ€ 경우

컀피 μ œμ‘°μ™€ μ£Όλ¬Έ λ°›κΈ°, 두 κ°€μ§€ 일을 ν•˜κ³  μžˆλ‹€. 이 ν΄λž˜μŠ€λŠ” 음료 λ ˆμ‹œν”Όκ°€ λ°”λ€Œκ±°λ‚˜, λ˜λŠ” ν˜„κΈˆλ§Œ λ°›λŠ” λ“± 결제 μ‹œμŠ€ν…œμ΄ λ°”λ€ŒλŠ” λ“± 두 κ°€μ§€μ˜ 이유둜 변경될 수 μžˆλ‹€.

public class Cafe {

    // 컀피 제쑰
    public Coffee makeCoffee(String menu) {
        if(메뉴=="아메리카노") {
            원두 μΆ”μΆœ;
            λ¬Ό λ”°λ₯΄κΈ°;
        } else if(메뉴=="μΉ΄νŽ˜λΌλ–Ό") {
            원두 μΆ”μΆœ;
            우유 μŠ€νŒ€ν•˜κΈ°;
        }
        ...
    }

    // μ£Όλ¬Έ λ°›κΈ°
    public void createOrder(String menu) {
        ν¬μŠ€κΈ°μ—μ„œ 메뉴 선택;
         ...
    }

}
  • 단일 μ±…μž„ 원칙을 지킨 경우

λ°”λ¦¬μŠ€νƒ€λŠ” 컀피 제쑰λ₯Ό, μΊμ…”λŠ” 결제λ₯Ό μ²˜λ¦¬ν•œλ‹€.

public class Barista {
    // 컀피 제쑰
    public Coffee makeCoffee(String menu) {
        if(메뉴=="아메리카노") {
            원두 μΆ”μΆœ;
            λ¬Ό λ”°λ₯΄κΈ°;
        } else if(메뉴=="μΉ΄νŽ˜λΌλ–Ό") {
            원두 μΆ”μΆœ;
            우유 μŠ€νŒ€ν•˜κΈ°;
        }
        ...
    }
}

public class Casher {
    // μ£Όλ¬Έ λ°›κΈ°
    public void createOrder(String menu) {
        ν¬μŠ€κΈ°μ—μ„œ 메뉴 선택;
         ...
    }
}

각각의 ν΄λž˜μŠ€κ°€ λͺ…ν™•ν•˜κ²Œ μžμ‹ λ§Œμ˜ μ±…μž„μ„ κ°€μ§€λ―€λ‘œ ν…ŒμŠ€νŠΈ, μˆ˜μ •, μœ μ§€λ³΄μˆ˜κ°€ μ‰¬μš°λ©° λ…λ¦½μ μ΄λ―€λ‘œ μž¬μ‚¬μš©λ„ 쉽닀.

βœ… O: 개방-폐쇄 원칙(OCP)

개방-폐쇄 원칙(Open-Closed)은 ν™•μž₯μ—λŠ” μ—΄λ € μžˆμ–΄μ•Ό ν•˜κ³ , λ³€κ²½μ—λŠ” λ‹«ν˜€ μžˆμ–΄μ•Ό ν•œλ‹€λŠ” 원칙이닀.

μ•„λ‹ˆ.. 단일 μ±…μž„ μ›μΉ™μ—μ„œλŠ” μˆ˜μ •ν•˜κΈ° μ‰¬μ›Œμ§„λ‹€λ©΄μ„œμš”? λ³€κ²½μ—λŠ” λ‹«ν˜€ μžˆλ‹€λ‹ˆ, μˆ˜μ •ν•˜μ§€ λ§λΌλŠ” κ±΄κ°€μš”?

이 원칙은 λͺ¨λ“  변경에 λŒ€ν•΄ λ§ν•˜λŠ” 게 μ•„λ‹ˆλ‹€. ν™•μž₯될 λ•Œμ˜ 변경을 λ§ν•œλ‹€.

기쑴의 μ½”λ“œλ₯Ό λ³€κ²½ν•˜μ§€ μ•ŠμœΌλ©΄μ„œλ„ μ‹œμŠ€ν…œμ˜ κΈ°λŠ₯을 ν™•μž₯ν•  수 μžˆμ–΄μ•Ό ν•œλ‹€. 개방-폐쇄 원칙에 λ”°λ₯΄λ©΄, μƒˆλ‘œμš΄ κΈ°λŠ₯을 μΆ”κ°€ν•  λ•Œ 기쑴의 μ½”λ“œμ— 영ν–₯을 μ£Όμ§€ μ•Šκ³ λ„ μΆ”κ°€ν•  수 μžˆμ–΄μ•Ό ν•œλ‹€.

μš”μ§€λŠ”, 이미 잘 λŒμ•„κ°€λ˜ κΈ°μ‘΄ μ½”λ“œλ₯Ό ν™•μž₯ν•œλ‹€κ³  건듀이지 말고 μƒˆλ‘œμš΄ μš”κ΅¬μ‚¬ν•­λ§Œ κ΅¬ν˜„ν•˜λΌλŠ” λœ»μ΄λ‹€.

μ΄λ•Œ μ΄μš©ν•˜κΈ° 쒋은 것이 좔상화와 μΈν„°νŽ˜μ΄μŠ€λ‹€. μ˜ˆμ‹œ μ½”λ“œλ‘œ 보자.

πŸͺ™ μ˜ˆμ‹œ μ½”λ“œ

  • 개방 폐쇄 원칙을 μ§€ν‚€μ§€ μ•Šμ€ 경우

결제 μ‹œ 고객이 μ§€λΆˆν•΄μ•Ό ν•˜λŠ” 가격을 μ•Œλ €μ•Ό ν•œλ‹€.

public class Casher {
    // 가격 μ°ΎκΈ°
    public int getPrice(String menu) {
        if(메뉴==아메리카노)
            return 2000;
        else if(메뉴==μΉ΄νŽ˜λΌλ–Ό)
            return 3000;
         else if ...
    }
}

λ§Œμ•½ μ—¬κΈ°μ—μ„œ 신메뉴 버블티가 μΆ”κ°€λœλ‹€λ©΄? else if()을 μΆ”κ°€ν•΄μ•Ό ν•  것이닀. μ‹ λ©”λ‰΄λ‘œ ν™•μž₯λ˜μ§€λ§Œ κΈ°μ‘΄ μ½”λ“œκ°€ λ³€κ²½λ˜λŠ” 것이닀.

  • 개방 폐쇄 원칙을 지킨 경우

메뉴가 λͺ‡ κ°œκ°€ μΆ”κ°€λ˜λ“  기쑴의 μ½”λ“œλ“€μ€ λ³€ν•˜μ§€ μ•ŠλŠ”λ‹€.

interface Drink {
    int getPrice();
}

class Americano {
    public int getPrice() { return 2000; }
}

class CafeLatte {
    public int getPrice() { return 3000; }
}

class BubbleTea {
    public int getPrice() { return 5000; }
}

개방-폐쇄 원칙은 μœ μ—°ν•˜κ²Œ ν™•μž₯이 κ°€λŠ₯ν•˜λ©΄μ„œλ„ κΈ°μ‘΄ μ½”λ“œλ₯Ό κ±΄λ“œλ¦¬μ§€ μ•ŠμœΌλ―€λ‘œ μ•ˆμ •μ μ΄λ‹€.

βœ… L: λ¦¬μŠ€μ½”ν”„ μΉ˜ν™˜ 원칙(LSP)

λ¦¬μŠ€μ½”ν”„ μΉ˜ν™˜ 원칙(Liskov Substitution)은 μ„œλΈŒ νƒ€μž…μ€ μ–Έμ œλ‚˜ κ·Έκ²ƒμ˜ 베이슀 νƒ€μž…μœΌλ‘œ μΉ˜ν™˜λ  수 μžˆμ–΄μ•Ό ν•œλ‹€λŠ” 원칙이닀.

  • μ„œλΈŒ νƒ€μž… = μžμ‹ 클래슀
  • 베이슀 νƒ€μž… = λΆ€λͺ¨ 클래슀

λΆ€λͺ¨ 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” μ½”λ“œκ°€ μžμ‹ 클래슀둜 λŒ€μ²΄λ˜λ”λΌλ„ ν”„λ‘œκ·Έλž¨ λ™μž‘μ— λ¬Έμ œκ°€ μ—†μ–΄μ•Ό ν•œλ‹€λŠ” 의미둜 λ‹€ν˜•μ„±μ˜ μ•ˆμ •μ„±μ„ 보μž₯ν•˜λŠ” 원칙이닀. λΆ€λͺ¨ ν΄λž˜μŠ€μ™€ λ™μž‘ κ²°κ³Όκ°€ κ°™μ•„μ•Ό ν•œλ‹€κΈ° λ³΄λ‹€λŠ”, "λΆ€λͺ¨ 클래슀의 κ·œμΉ™μ„ κΉ¨μ§€ 마라"κ°€ 핡심이닀.

πŸ•ŠοΈ μ˜ˆμ‹œ μ½”λ“œ

  • λ¦¬μŠ€μ½”ν”„ μΉ˜ν™˜ 원칙을 μ§€ν‚€μ§€ μ•Šμ€ 경우

λΆ€λͺ¨ 클래슀 BirdλŠ” μƒˆμ˜ λΉ„ν–‰ λ™μž‘μ„ μ •μ˜ν•œλ‹€. 이 ν΄λž˜μŠ€μ—μ„œμ˜ μ§€μΌœμ Έμ•Ό ν•˜λŠ” κ·œμΉ™μ€ "μƒˆλŠ” λ‚  수 μžˆλ‹€"이닀. μƒˆμ— ν¬ν•¨λ˜λŠ” Penguin은 μžμ‹ 클래슀둜 λΉ„ν–‰ λ™μž‘μ„ κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€.

그런데 νŽ­κ·„μ€ λ‚  수 μ—†λŠ” μƒˆλ‹€! Penguinμ—μ„œλŠ” λΉ„ν–‰ λ™μž‘μ„ μ œλŒ€λ‘œ κ΅¬ν˜„ν•  수 μ—†λ‹€. Bird μΈμŠ€ν„΄μŠ€λ₯Ό Penguin μΈμŠ€ν„΄μŠ€λ‘œ λ°”κΎΈλ©΄ "μƒˆλŠ” λ‚  수 μžˆλ‹€"λŠ” κ·œμΉ™μ„ 지킬 수 μ—†λ‹€.

public abstact class Bird {
    public void fly() {
        System.out.println("λ‚ μ•„κ°‘λ‹ˆλ‹€.");
    }
}

public class Penguin extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException("νŽ­κ·„μ€ λ‚  수 μ—†μŠ΅λ‹ˆλ‹€.");
    }
}

public class Main {
    public static void main(String[] args) {
        Bird bird = new Bird();
        bird.fly(); // "λ‚ μ•„κ°‘λ‹ˆλ‹€."

        Bird penguin = new Penguin();
        penguin.fly(); // UnsupportedOperationException μ˜ˆμ™Έ λ°œμƒ
    }
}
  • λ¦¬μŠ€μ½”ν”„ 원칙을 지킨 경우

νŽ­κ·„μ΄λΌλŠ” λ°˜λ‘€λ₯Ό λ°œκ²¬ν–ˆμœΌλ‹ˆ λΆ€λͺ¨ 클래슀의 "μƒˆλŠ” λ‚  수 μžˆλ‹€"λŠ” κ·œμΉ™μ„ 깨보자. λ‹€ν–‰νžˆ μƒˆλ“€ λͺ¨λ‘ μ–΄λ–»κ²Œλ“  움직일 μˆ˜λŠ” μžˆλ‹€. "μƒˆλŠ” 움직인닀"λŠ” 것을 μ „μ œλ‘œ "λ‚  수 μžˆλŠ” μƒˆ"와 "μ›€μ§μ΄λŠ” μƒˆ(λ‚  수 μ—†λŠ” μƒˆ)"둜 λΆ„λ¦¬ν•΄λ³΄μž.

μ•„λž˜ μ˜ˆμ‹œμ—μ„œλŠ” FlyingBird와 Penguin ν΄λž˜μŠ€κ°€ λΆ€λͺ¨μ˜ 클래슀 Bird의 κ·œμΉ™ "μƒˆλŠ” 움직인닀"λ₯Ό 잘 μ§€ν‚€κ³ , 메인 ν΄λž˜μŠ€μ—μ„œ Bird μΈμŠ€ν„΄μŠ€λ₯Ό FlyingBird, Penguin μΈμŠ€ν„΄μŠ€λ‘œ 바꿔도 문제 없이 λ™μž‘ν•œλ‹€.

// μƒˆμ˜ 곡톡 λ™μž‘μ„ μ •μ˜ν•˜λŠ” μƒμœ„ 클래슀
abstract class Bird {
    public abstract void move(); // λͺ¨λ“  μƒˆκ°€ 이동할 수 있음
}

// λ‚  수 μžˆλŠ” μƒˆ
class FlyingBird extends Bird {
    public void fly() {
        System.out.println("λ‚ μ•„κ°‘λ‹ˆλ‹€!");
    }

    @Override
    public void move() {
        fly(); // λ‚ μ•„ 이동
    }
}

// νŽ­κ·„μ€ λ‚ μ§€ λͺ»ν•˜λŠ” μƒˆ
class Penguin extends Bird {
    @Override
    public void move() {
        System.out.println("ν—€μ—„μΉ©λ‹ˆλ‹€!");
    }
}

public class Main {
    public static void main(String[] args) {
        Bird sparrow = new FlyingBird(); // μ°ΈμƒˆλŠ” λ‚  수 있음
        sparrow.move(); // "λ‚ μ•„κ°‘λ‹ˆλ‹€!"

        Bird penguin = new Penguin(); // νŽ­κ·„μ€ ν—€μ—„μΉ¨
        penguin.move(); // "ν—€μ—„μΉ©λ‹ˆλ‹€!"
    }
}

λ¦¬μŠ€μ½”ν”„ μΉ˜ν™˜ 원칙을 μ§€ν‚€λ©΄ λ‹€ν˜•μ„±μ„ ν™œμš©ν•œ μ½”λ“œμ—μ„œ 문제 λ°œμƒμ„ λ°©μ§€ν•  수 μžˆλ‹€. λΆ€λͺ¨ 클래슀의 κ·œμΉ™μ„ μœ„λ°˜ν•œ μžμ‹ 클래슀의 μ˜ˆμƒν•˜μ§€ λͺ»ν•œ 였λ₯˜λ₯Ό λ§‰μž.

βœ… I: μΈν„°νŽ˜μ΄μŠ€ 뢄리 원칙(ISP)

μΈν„°νŽ˜μ΄μŠ€ 뢄리 원칙(Interface Segregation)은 μΈν„°νŽ˜μ΄μŠ€λŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” λ©”μ„œλ“œμ— μ˜μ‘΄ν•˜μ§€ μ•Šλ„λ‘ 섀계해야 ν•œλ‹€λŠ” 원칙이닀.

  • μ˜μ‘΄μ΄λž€? ν•œ ν΄λž˜μŠ€λ‚˜ λͺ¨λ“ˆμ΄ λ‹€λ₯Έ ν΄λž˜μŠ€λ‚˜ λͺ¨λ“ˆμ— ν•„μš”ν•œ μžμ›μ„ μ‚¬μš©ν•˜κ±°λ‚˜ κΈ°λŠ₯을 μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄ ν•΄λ‹Ή ν΄λž˜μŠ€λ‚˜ λͺ¨λ“ˆμ— 직접적인 영ν–₯을 λ°›λŠ” 관계이닀.

ν•˜λ‚˜μ˜ 큰 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ—¬λŸ¬ 개의 μž‘μ€ μΈν„°νŽ˜μ΄μŠ€λ‘œ λ‚˜λˆ μ„œ, νŠΉμ • ν΄λΌμ΄μ–ΈνŠΈκ°€ μžμ‹ μ—κ²Œ ν•„μš” μ—†λŠ” κΈ°λŠ₯은 κ΅¬ν˜„ν•˜μ§€ μ•Šκ³ , μžμ‹ μ΄ ν•„μš”ν•œ κΈ°λŠ₯만 μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•˜μž.

λ°”λ‘œ μ˜ˆμ‹œλ₯Ό λ³΄λŠ” 게 λ‚«λ‹€.

πŸ–¨οΈ μ˜ˆμ‹œ μ½”λ“œ

  • μΈν„°νŽ˜μ΄μŠ€ 뢄리 원칙을 μ§€ν‚€μ§€ μ•Šμ€ 경우

ν”„λ¦°νŠΈ, μŠ€μΊ”, 팩슀 κΈ°λŠ₯이 μžˆλŠ” 볡합기가 μžˆλ‹€. 이 λ³΅ν•©κΈ°λ‘œλΆ€ν„° μœ λž˜ν•œ ν”„λ¦°ν„°λ₯Ό ν•˜λ‚˜ λ§Œλ“€κ³  μ‹Άλ‹€. ν”„λ¦°ν„°μ—λŠ” 였직 ν”„λ¦°νŠΈ κΈ°λŠ₯만 μžˆλ‹€.

SimplePrinter ν΄λž˜μŠ€λŠ” 였직 ν”„λ¦°ν„°λ‘œμ„œλ§Œ λ™μž‘ν•˜λ©΄ λ˜λŠ”λ° Device μΈν„°νŽ˜μ΄μŠ€ λ•Œλ¬Έμ— μžκΈ°μ—κ²Œ ν•„μš”λ„ μ—†λŠ” μŠ€μΊ”κ³Ό νŒ©μŠ€κΉŒμ§€ κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€. κ²°κ³ΌλŠ” λ‹Ήμ—°νžˆ μ˜ˆμ™Έ λ°œμƒμ΄λ‹€.

public interface Device {
    void print(String content);
    void scan(String content);
    void fax(String content);
}

//ν”„λ¦°ν„°λŠ” ν”„λ¦°νŠΈ 밖에 λͺ»ν•˜λŠ”λ° μŠ€μΊ”κ³Ό νŒ©μŠ€λ„ λ°˜λ“œμ‹œ κ΅¬ν˜„ν•΄μ•Ό 함
public class SimplePrinter implements Device {
    @Override
    public void print(String content) {
        System.out.println("Printing: " + content);
    }

    @Override
    public void scan(String content) {
        throw new UnsupportedOperationException("Scan not supported by this device.");
    }

    @Override
    public void fax(String content) {
        throw new UnsupportedOperationException("Fax not supported by this device.");
    }
}
  • μΈν„°νŽ˜μ΄μŠ€ 뢄리 원칙을 지킨 경우

DeviceλΌλŠ” ν•˜λ‚˜μ˜ 큰 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μͺΌκ°œμž. λ‹€μ–‘ν•œ ν΄λΌμ΄μ–ΈνŠΈκ°€ 각자 ν•„μš”ν•œ μΈν„°νŽ˜μ΄μŠ€λ§Œ κ΅¬ν˜„ν•  수 μžˆλ‹€.

// μΈν„°νŽ˜μ΄μŠ€ 뢄리
public interface Fax {
    void fax(String content);
}

public interface Printer {
    void print(String content);
}

public interface Scanner {
    void scan(String content);
}

//λ‹¨μˆœ ν”„λ¦°ν„°
public class SimplePrinter implements Printer{
    @Override
    public void print(String content) {
        System.out.println("Printing : " + content);
    }
}

// 볡합기
public class MultifunctionDevice implements Printer, Scanner, Fax{

    @Override
    public void print(String content) {
        System.out.println("MultiDevice printing: " + content);
    }

    @Override
    public void scan(String content) {
        System.out.println("MultiDevice scanning: " + content);
    }

    @Override
    public void fax(String content) {
        System.out.println("MultiDevice faxing: " + content);
    }
}

ν΄λΌμ΄μ–ΈνŠΈκ°€ λΆˆν•„μš”ν•œ λ©”μ„œλ“œ κ΅¬ν˜„μ„ κ°•μ œλ°›μ§€ μ•Šκ³ , 큰 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μˆ˜μ •ν•  λ•Œ, μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” ν΄λΌμ΄μ–ΈνŠΈμ—λ„ 영ν–₯을 λ―ΈμΉ  κ°€λŠ₯성이 쀄어듀기 λ•Œλ¬Έμ— 클래슀 κ°„ μ˜μ‘΄μ„±λ„ κ°μ†Œν•œλ‹€.

βœ… D: 의쑴 μ—­μ „ 원칙(DIP)

의쑴 μ—­μ „ 원칙(Dependency Inversion)은 κ³ μˆ˜μ€€ λͺ¨λ“ˆμ€ μ €μˆ˜μ€€ λͺ¨λ“ˆμ— μ˜μ‘΄ν•΄μ„œλŠ” μ•ˆ 되며, λ‘˜ λ‹€ 좔상화 μΈν„°νŽ˜μ΄μŠ€μ— μ˜μ‘΄ν•΄μ•Ό ν•œλ‹€λŠ” 원칙이닀.

  • κ³ μˆ˜μ€€κ³Ό μ €μˆ˜μ€€? κ³ μˆ˜μ€€ λͺ¨λ“ˆμ€ μ‹œμŠ€ν…œμ˜ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직과 μ „λ°˜μ μΈ λ™μž‘μ„ μ„€κ³„ν•˜λŠ” 좔상적인 뢀뢄이닀. μ €μˆ˜μ€€ λͺ¨λ“ˆμ€ 데이터 μ €μž₯, ν•˜λ“œμ›¨μ–΄μ™€μ˜ μƒν˜Έμž‘μš© λ“± μƒμœ„ λͺ¨λ“ˆμ΄ μ›ν•˜λŠ” μ‹€μ§ˆμ μΈ μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” ꡬ체적으둜 κ΅¬ν˜„ν•˜λŠ” λͺ¨λ“ˆμ΄λ‹€.

πŸ“š μ˜ˆμ‹œ μ½”λ“œ 1

주문을 μ²˜λ¦¬ν•˜κ³  λ°μ΄ν„°λ² μ΄μŠ€μ— μ£Όλ¬Έ 데이터λ₯Ό μ €μž₯ν•˜μž.

  • 의쑴 μ—­μ „ 원칙을 μ§€ν‚€μ§€ μ•Šμ€ 경우

OrderService ν΄λž˜μŠ€λŠ” 'μ£Όλ¬Έ'μ΄λΌλŠ” λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ μ²˜λ¦¬ν•˜λŠ” κ³ μˆ˜μ€€ λͺ¨λ“ˆμ΄λ‹€. MySQLOrderRepository ν΄λž˜μŠ€λŠ” μ£Όλ¬Έ 데이터λ₯Ό μ‹€μ§ˆμ μœΌλ‘œ μ €μž₯ν•˜κΈ° μœ„ν•œ μ €μˆ˜μ€€ λͺ¨λ“ˆμ΄λ‹€.

λ§Œμ•½ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό λ°”κΎΈκ³  μ‹Άλ‹€λ©΄, MySQL λ°μ΄ν„°λ² μ΄μŠ€μ— μ˜μ‘΄ν•˜λ˜ κ³ μˆ˜μ€€ λͺ¨λ“ˆOrderService 클래슀λ₯Ό μˆ˜μ •ν•΄μ•Ό ν•œλ‹€.

// μ €μˆ˜μ€€ λͺ¨λ“ˆ -> λ°μ΄ν„°λ² μ΄μŠ€(MySQL)λ₯Ό μ‚¬μš©ν•˜μ—¬ 데이터λ₯Ό μ €μž₯
// ꡬ체적인 μ €μž₯ 방식(λ°μ΄ν„°λ² μ΄μŠ€ 기술)을 κ΅¬ν˜„ν•œ 클래슀
public class MySQLOrderRepository {
    public void save(String orderData) {
        System.out.println("[MySQL] Order saved: " + orderData);
    }
}

// μ£Όλ¬Έμ΄λΌλŠ” λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ κ΅¬ν˜„ν•œ κ³ μˆ˜μ€€ λͺ¨λ“ˆ
public class OrderService {
    // κ³ μˆ˜μ€€ λͺ¨λ“ˆμΈλ°, μ €μˆ˜μ€€μ— 의쑴
    // : MySQLμ΄λΌλŠ” νŠΉμ • DB에 λŒ€ν•΄μ„œλ§Œ 영ν–₯을 λ°›μŒ. λ§Œμ•½ DB μ’…λ₯˜κ°€ λ°”λ€Œλ©΄ OrderService μ½”λ“œλ₯Ό μˆ˜μ •ν•΄μ•Ό 함.
    private MySQLOrderRepository repository = new MySQLOrderRepository();

    public void createOrder(String orderData) {
        System.out.println("Creating order...");
        repository.save(orderData); // ꡬ체 ν΄λž˜μŠ€μ— 의쑴
    }
}

public class Main {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        orderService.createOrder("Order #1234");
    }
}
  • 의쑴 μ—­μ „ 원칙을 지킨 경우

λ°μ΄ν„°λ² μ΄μŠ€μ— μ£Όλ¬Έ 데이터λ₯Ό μ €μž₯ν•˜λŠ” 것을 μΈν„°νŽ˜μ΄μŠ€λ‘œ λ°”κΎΈμž. μΈν„°νŽ˜μ΄μŠ€ OrderRepositoryλ₯Ό κ΅¬ν˜„ν•˜λŠ” ν΄λž˜μŠ€λ“€μ΄ 각 λ°μ΄ν„°λ² μ΄μŠ€ λͺ¨λ“ˆμ΄λ‹€. OrderServiceλŠ” 이 μΈν„°νŽ˜μ΄μŠ€λ§Œ μ΄μš©ν•˜λ©΄ 되고, 메인 ν΄λž˜μŠ€μ—μ„œ μ˜μ‘΄μ„±μ„ μ£Όμž…ν•˜λ©΄ λœλ‹€. μƒˆλ‘œμš΄ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό 좔가해도, λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό λ°”κΎΈκ³  싢어도 OrderServiceλŠ” μˆ˜μ •ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€.

//좔상화 계측
public interface OrderRepository {
    void save(String orderData);
}

// MySQL μ €μˆ˜μ€€ λͺ¨λ“ˆ
public class MySQLOrderRepository implements OrderRepository{
    @Override
    public void save(String orderData) {
        System.out.println("[MYSQL] order saved: " + orderData);
    }
}

//OracleDB μ €μˆ˜μ€€ λͺ¨λ“ˆ
public class OracleOrderRepository implements OrderRepository{
    @Override
    public void save(String orderData) {
        System.out.println("[ORACLE] order saved: " + orderData);
    }
}

public class OrderService {
    // κ³ μˆ˜μ€€ λͺ¨λ“ˆ

    // 좔상 μΈν„°νŽ˜μ΄μŠ€μ— 의쑴
    private OrderRepository repository;

    // μƒμ„±μž μ£Όμž…
    public OrderService(OrderRepository repository) {
        this.repository = repository;
    }

    public void createOrder(String orderData) {
        System.out.println("Creating order >>>");
        repository.save(orderData);
    }

}

public class Main {
    public static void main(String[] args) {
        // MySQL μ‚¬μš©
        OrderRepository mySql = new MySQLOrderRepository();
        OrderService mySqlOrder = new OrderService(mySql);
        mySqlOrder.createOrder("Order from MYSQL DB");

        // Oracle 둜 ꡐ체
        OrderRepository oracle = new OracleOrderRepository();
        OrderService oracleOrder = new OrderService(oracle);
        oracleOrder.createOrder("Order from ORACLE DB");
    }
}

πŸ“© μ˜ˆμ‹œ μ½”λ“œ 2

κ³ κ°μ—κ²Œ μ‹ μƒν’ˆ μ•Œλ¦Όμ„ λ³΄λ‚΄μž. μ•Œλ¦Όμ€ 이메일과 문자(SMS)둜 보낼 수 μžˆλ‹€.

  • 의쑴 μ—­μ „ 원칙을 μ§€ν‚€μ§€ μ•Šμ€ 경우

NotificationService ν΄λž˜μŠ€λŠ” κ³ κ°μ—κ²Œ 두 κ°€μ§€ λ°©μ‹μœΌλ‘œ μ•Œλ¦Όμ„ λ³΄λ‚΄λŠ” 'μ•Œλ¦Ό 전솑' κΈ°λŠ₯을 κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€. EmailSender와 SmsSender ν΄λž˜μŠ€λŠ” 각각 μ‹€μ œλ‘œ κ³ κ°μ—κ²Œ 이메일 λ˜λŠ” 문자λ₯Ό 보낸닀.

λ§Œμ•½ λͺ¨λ°”일 μ–΄ν”Œλ‘œλ„ 보내렀면, ν•΄λ‹Ή 클래슀λ₯Ό λ§Œλ“€κ³  NotificationServiceμ—μ„œ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€κ³ , sendNotification() λ©”μ†Œλ“œλ₯Ό μˆ˜μ •ν•΄μ•Ό ν•œλ‹€.

class EmailSender {
    public void sendEmail(String message) {
        System.out.println("이메일 전솑: " + message);
    }
}

class SmsSender {
    public void sendSms(String message) {
        System.out.println("SMS 전솑: " + message);
    }
}

class NotificationService {
    private EmailSender emailSender = new EmailSender();
    private SmsSender smsSender = new SmsSender();

    public void sendNotification(String message) {
        emailSender.sendEmail(message);
        smsSender.sendSms(message);
    }
}

public class Main {
    public static void main(String[] args) {
        NotificationService notificationService = new NotificationService();
        notificationService.sendNotification("의쑴 μ—­μ „ 원칙 ν…ŒμŠ€νŠΈ");
    }
}
  • 의쑴 μ—­μ „ 원칙을 지킨 경우

λͺ¨λ“  μ‹€μ§ˆμ μΈ μ•Œλ¦Ό 전솑 μ €μˆ˜μ€€ λͺ¨λ“ˆμ΄ μΈν„°νŽ˜μ΄μŠ€ MessageSenderλ₯Ό κ΅¬ν˜„ν•œλ‹€. κ³ μˆ˜μ€€ λͺ¨λ“ˆ NotificationService ν΄λž˜μŠ€λŠ” λͺ¨λ°”일 μ–΄ν”Œ μ•Œλ¦ΌκΉŒμ§€ μΆ”κ°€ λ˜μ–΄λ„ μ½”λ“œλ₯Ό μˆ˜μ •ν•  ν•„μš”κ°€ μ—†λ‹€.

// 좔상화: μ•Œλ¦Ό λ°œμ†‘ μΈν„°νŽ˜μ΄μŠ€
interface MessageSender {
    void send(String message);
}

// ꡬ체적 κ΅¬ν˜„: 이메일 λ°œμ†‘
class EmailSender implements MessageSender {
    @Override
    public void send(String message) {
        System.out.println("이메일 전솑: " + message);
    }
}

// ꡬ체적 κ΅¬ν˜„: SMS λ°œμ†‘
class SmsSender implements MessageSender {
    @Override
    public void send(String message) {
        System.out.println("SMS 전솑: " + message);
    }
}

// ꡬ체적 κ΅¬ν˜„: λͺ¨λ°”일 μ–΄ν”Œ μ•Œλ¦Ό
class AppSender implements MessageSender {
    @Override
    public void send(String message) {
        System.out.println("μ•± μ•Œλ¦Ό: " + message);
    }
}

// μƒμœ„ λͺ¨λ“ˆ: μ•Œλ¦Ό μ„œλΉ„μŠ€
class NotificationService {
    private final List<MessageSender> senders;

    public NotificationService(List<MessageSender> senders) {
        this.senders = senders;
    }

    public void sendNotification(String message) {
        senders.forEach(sender -> sender.send(message));
    }
}

public class Main {
    public static void main(String[] args) {
        // ν•„μš”ν•œ λ°œμ†‘ 방식을 μΆ”κ°€
        List<MessageSender> senders = Arrays.asList(new EmailSender(), new SmsSender(), new AppSender());

        NotificationService notificationService = new NotificationService(senders);
        notificationService.sendNotification("의쑴 μ—­μ „ 원칙 ν…ŒμŠ€νŠΈ");
    }
}

이처럼 ꡬ체적인 κ΅¬ν˜„μ΄ λ³€κ²½λ˜μ–΄λ„ 더 이상 κ³ μˆ˜μ€€ λͺ¨λ“ˆμ˜ μ½”λ“œλŠ” λ³€ν•˜μ§€ μ•ŠλŠ”λ‹€. κ³ μˆ˜μ€€ λͺ¨λ“ˆμ΄ μΈν„°νŽ˜μ΄μŠ€μ— μ˜μ‘΄ν•˜κ³ , μ €μˆ˜μ€€ λͺ¨λ“ˆμ— μ˜μ‘΄ν•˜μ§€ μ•ŠλŠ”λ‹€.

πŸ”‘ μš”μ•½

  • 단일 μ±…μž„ 원칙
    • "ν΄λž˜μŠ€λŠ” ν•˜λ‚˜μ˜ μ±…μž„λ§Œ κ°€μ Έμ•Ό ν•œλ‹€."
    • ν•˜λ‚˜μ˜ ν΄λž˜μŠ€λŠ” 단 ν•œκ°€μ§€μ˜ 이유둜만 λ³€κ²½λ˜μ–΄μ•Ό ν•œλ‹€.
    • βœ… μœ μ§€λ³΄μˆ˜: μ½”λ“œ 변경이 ν•„μš”ν•œ μ΄μœ κ°€ λΆ„λ¦¬λ˜κ³  λͺ…ν™•ν•˜μ—¬ μˆ˜μ •μ΄ 쉽닀.
    • βœ… μž¬μ‚¬μš©μ„±: 각 ν΄λž˜μŠ€κ°€ λ…λ¦½μ μ΄λ―€λ‘œ λ‚˜μ€‘μ— ν•΄λ‹Ή κΈ°λŠ₯이 ν•„μš”ν•œ μƒν™©μ—μ„œ μ‰½κ²Œ μž¬μ‚¬μš©μ΄ κ°€λŠ₯ν•˜λ‹€.
    • βœ… ν…ŒμŠ€νŠΈ: ν•œ κ°€μ§€ 일을 μž˜ν•˜λŠ”μ§€ ν…ŒμŠ€νŠΈν•˜λ©΄ λ˜λ‹ˆ 쉽닀.
  • 개방-폐쇄 원칙
    • "ν™•μž₯μ—λŠ” μ—΄λ € μžˆμ–΄μ•Ό ν•˜κ³ , λ³€κ²½μ—λŠ” λ‹«ν˜€ μžˆμ–΄μ•Ό ν•œλ‹€."
    • βœ… μ•ˆμ •μ„±: κΈ°μ‘΄ μ½”λ“œλ₯Ό κ±΄λ“œλ¦¬μ§€ μ•ŠμœΌλ‹ˆ, 이미 잘 λŒμ•„κ°€λ˜ κΈ°λŠ₯은 μ—λŸ¬κ°€ λ‚˜μ§€ μ•ŠλŠ”λ‹€.
    • βœ… μœ μ—°μ„±, ν™•μž₯μ„±: μƒˆλ‘œμš΄ κΈ°λŠ₯을 μ‰½κ²Œ μΆ”κ°€ν•˜κΈ° νŽΈν•˜λ‹€.
    • 좔상화와 μΈν„°νŽ˜μ΄μŠ€λ₯Ό 잘 ν™œμš©ν•˜μž!
  • λ¦¬μŠ€μ½”ν”„ μΉ˜ν™˜ 원칙
    • "μ„œλΈŒνƒ€μž…μ€ μ–Έμ œλ‚˜ κ·Έκ²ƒμ˜ λ² μ΄μŠ€νƒ€μž…μœΌλ‘œ ꡐ체할 수 μžˆμ–΄μ•Ό ν•œλ‹€."
    • μžμ‹ ν΄λž˜μŠ€λŠ” λΆ€λͺ¨ 클래슀의 κΈ°λŠ₯을 λŒ€μ²΄ν•˜λ”λΌλ„ λ™μž‘ν•΄μ•Ό ν•œλ‹€.
    • βœ… λ‹€ν˜•μ„±μ˜ μ•ˆμ •μ„±: λΆ€λͺ¨ 클래슀의 κ·œμΉ™μ„ μžμ‹ ν΄λž˜μŠ€λ„ μ§€μΌœμ„œ λ‹€ν˜•μ„±μ„ μ§€ν‚€μž. λ‹€ν˜•μ„±μ€ ν™•μž₯κ³Ό μœ μ§€λ³΄μˆ˜λ₯Ό μœ„ν•œ μœ μš©ν•œ κ°œλ…μ΄λ‹€.
  • μΈν„°νŽ˜μ΄μŠ€ 뢄리 원칙
    • "ν΄λΌμ΄μ–ΈνŠΈλŠ” μžμ‹ μ΄ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” λ©”μ„œλ“œμ— μ˜μ‘΄ν•˜μ§€ μ•Šμ•„μ•Ό ν•œλ‹€."
    • ν•˜λ‚˜μ˜ 큰 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ—¬λŸ¬ 개의 μž‘μ€ μΈν„°νŽ˜μ΄μŠ€λ‘œ λ‚˜λˆ μ„œ, νŠΉμ • ν΄λΌμ΄μ–ΈνŠΈκ°€ μžμ‹ μ΄ ν•„μš”ν•œ κΈ°λŠ₯만 μ‚¬μš©ν•  수 μžˆλ„λ‘ μ„€κ³„ν•˜μž.
    • βœ… μœ μ§€λ³΄μˆ˜: 큰 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μˆ˜μ •ν•  λ•Œ, μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” ν΄λΌμ΄μ–ΈνŠΈμ— 영ν–₯이 κ°€μ§€ μ•ŠλŠ”λ‹€.
    • βœ… μœ μ—°μ„±: ν΄λΌμ΄μ–ΈνŠΈκ°€ 각자 ν•„μš”ν•œ μΈν„°νŽ˜μ΄μŠ€λ§Œ κ΅¬ν˜„ν•  수 μžˆλ‹€.
  • 의쑴 μ—­μ „ 원칙
    • "κ³ μˆ˜μ€€ λͺ¨λ“ˆμ€ μ €μˆ˜μ€€ λͺ¨λ“ˆμ— μ˜μ‘΄ν•΄μ„œλŠ” μ•ˆ 되며, λ‘˜ λ‹€ μΆ”μƒν™”λœ μΈν„°νŽ˜μ΄μŠ€μ— μ˜μ‘΄ν•΄μ•Ό ν•œλ‹€."
    • κ³ μˆ˜μ€€ λͺ¨λ“ˆ: λΉ„μ¦ˆλ‹ˆμŠ€ 둜직과 μ „λ°˜μ μΈ λ™μž‘μ„ μ„€κ³„ν•˜λŠ” λͺ¨λ“ˆ
    • μ €μˆ˜μ€€ λͺ¨λ“ˆ: μƒμœ„ λͺ¨λ“ˆμ΄ μ›ν•˜λŠ” μ‹€μ§ˆμ μΈ μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” ꡬ체적으둜 κ΅¬ν˜„ν•˜λŠ” λͺ¨λ“ˆ
    • βœ… μœ μ§€λ³΄μˆ˜μ„±: μ €μˆ˜μ€€ λͺ¨λ“ˆμ˜ ꡬ체적인 κ΅¬ν˜„μ΄ λ³€κ²½λ˜λ„ κ³ μˆ˜μ€€ λͺ¨λ“ˆμ€ 영ν–₯λ°›μ§€ μ•ŠλŠ”λ‹€.
    • βœ… ν™•μž₯μ„±: κ³ μˆ˜μ€€ λͺ¨λ“ˆμ„ κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄ μƒˆλ‘œμš΄ μ €μˆ˜μ€€ λͺ¨λ“ˆμ΄ ν•„μš”ν•΄λ„ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ΄μš©ν•˜λ©΄ κ³ μˆ˜μ€€ λͺ¨λ“ˆμ€ μˆ˜μ •ν•  ν•„μš”κ°€ μ—†λ‹€.

이처럼 SOLID 원칙은 객체 μ§€ν–₯ ν”„λ‘œκ·Έλž˜λ°μ„ λ”μš± 효과적으둜 μˆ˜ν–‰ν•˜κΈ° μœ„ν•œ μ›μΉ™μœΌλ‘œ 이루어져 μžˆλ‹€. ν•˜μ§€λ§Œ μ—„κ²©νžˆ μ§€μΌœμ•Ό ν•˜λŠ” μ›μΉ™λ³΄λ‹€λŠ” 적절히 상황에 맞게 ν™œμš©ν•˜λŠ” "도ꡬ"λΌλŠ” 것을 μžŠμ§€ μ•Šμ•„μ•Ό ν•œλ‹€. 무쑰건적인 원칙 μ μš©λ³΄λ‹€λŠ” μ™œ 이 원칙을 μ μš©ν•΄μ•Ό ν• κΉŒ?λ₯Ό 고민해보기λ₯Ό.

λ°˜μ‘ν˜•

'βš™οΈ CS & 기타 개발 자료' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

μ›Ή μ„œλ²„(Web Server) vs WAS(Web Application Server) κ°œλ… 이해와 차이 비ꡐ feat. Tomcat  (0) 2025.02.17
ν”„λ ˆμž„μ›Œν¬ vs 라이브러리 κ°œλ…  (0) 2025.02.16
[Git] git fetch와 git pull의 이해  (0) 2025.02.12
[Git] git rebase와 git merge의 이해  (0) 2025.02.12
[Git] Git Flow μ „λž΅μ˜ 이해와 ν˜‘μ—… μ‹œ ν™œμš©  (0) 2025.02.12
'βš™οΈ CS & 기타 개발 자료' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€
  • μ›Ή μ„œλ²„(Web Server) vs WAS(Web Application Server) κ°œλ… 이해와 차이 비ꡐ feat. Tomcat
  • ν”„λ ˆμž„μ›Œν¬ vs 라이브러리 κ°œλ…
  • [Git] git fetch와 git pull의 이해
  • [Git] git rebase와 git merge의 이해
μ†Œμ˜ πŸ€
μ†Œμ˜ πŸ€
Hello World ✨
  • μ†Œμ˜ πŸ€
    Soyoung's Dev Lab
    μ†Œμ˜ πŸ€
  • 전체
    였늘
    μ–΄μ œ
  • κΈ€μ“°κΈ° 관리
    • λΆ„λ₯˜ 전체보기 (48)
      • πŸ“’ κ²Œμ‹œνŒ (0)
      • πŸ“š 자료ꡬ쑰 & μ•Œκ³ λ¦¬μ¦˜ (1)
      • 🌿Spring (16)
      • β˜•Java (8)
      • πŸ“Š λ°μ΄ν„°λ² μ΄μŠ€ (3)
      • πŸ“€ 배포 (4)
        • Docker (4)
        • AWS (0)
      • βš™οΈ CS & 기타 개발 자료 (14)
      • πŸ–₯️ ν”„λ‘œμ νŠΈ (0)
      • πŸ‘©‍πŸ’» ν™œλ™ & ν›„κΈ° (0)
      • 🍡 이야기 (2)
  • λΈ”λ‘œκ·Έ 메뉴

    • νƒœκ·Έ
  • 링크

    • github
    • velog
  • 곡지사항

  • 인기 κΈ€

  • νƒœκ·Έ

    Java
    GIT
    자료ꡬ쑰
    λ°μ΄ν„°λ² μ΄μŠ€
    Spring Security
    Spring
    μœ„ν΄λ¦¬ 페이퍼
    배포
    μ½”λ“œμž‡ μŠ€ν”„λ¦°νŠΈ
    객체지ν–₯ν”„λ‘œκ·Έλž˜λ°
    μ„œλ²„
    docker
    μ•Œκ³ λ¦¬μ¦˜
    μ½”λ”©ν…ŒμŠ€νŠΈ
    개발
  • 졜근 λŒ“κΈ€

  • hELLOΒ· Designed Byμ •μƒμš°.v4.10.3
μ†Œμ˜ πŸ€
[객체지ν–₯ν”„λ‘œκ·Έλž˜λ°] μ‰¬μš΄ μ˜ˆμ‹œμ™€ ν•¨κ»˜ λ³΄λŠ” SOLID 원칙
μƒλ‹¨μœΌλ‘œ

ν‹°μŠ€ν† λ¦¬νˆ΄λ°”