๐ŸŒ€ ๊ฐ์ฒด์ง€ํ–ฅ ์„ค๊ณ„ 5์›์น™ - SOLID

Ssong94ยท2022๋…„ 7์›” 17์ผ
0

๐Ÿ“Œ Java

๋ชฉ๋ก ๋ณด๊ธฐ
2/4
post-thumbnail
post-custom-banner

๐ŸŒ€ SOLID ์›์น™์— ๋Œ€ํ•œ ํ™•์‹คํ•œ ๊ฐ€์ด๋“œ

SOLID๋ž€ ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ•˜๋ฉด์„œ ์ง€์ผœ์•ผํ•˜๋Š” 5๋Œ€ ์›์น™์ด๋‹ค.

๋ฌธ์ž์•ฝ์–ด๊ฐœ๋…
SSRP
(Single Responsibility Principle)
๋‹จ์ผ ์ฑ…์ž„ ์›์น™
(ํ•œ ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.)
OOCP
(Open/Closed Priciple)
๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™
(์†Œํ”„ํŠธ์›จ์–ด ์š”์†Œ๋Š” ํ™•์žฅ์—๋Š” ์—ด๋ ค ์žˆ์œผ๋‚˜ ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€ ์žˆ์–ด์•ผ ํ•œ๋‹ค.)
LLSP
(Listov Substitution Priciple)
๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™
(ํ”„๋กœ๊ทธ๋žจ์˜ ๊ฐ์ฒด๋Š” ํ”„๋กœ๊ทธ๋žจ์˜ ์ •ํ™•์„ฑ์„ ๊นจ๋œจ๋ฆฌ์ง€ ์•Š์œผ๋ฉด์„œ ํ•˜์œ„ ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.)
IISP
(Interface Segregation Principle)
์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™
(ํŠน์ • ํด๋ผ์ด์–ธํŠธ๋ฅผ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ๋ฒ”์šฉ ์ธํ„ฐํŽ˜์ด์Šค ํ•˜๋‚˜๋ณด๋‹ค ๋‚ซ๋‹ค.)
DDIP
(Dependency Inversion Principle)
์˜์กด๊ด€๊ณ„ ์—ญ์ „ ์›์น™
(ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” "์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ์ง€, ๊ตฌ์ฒดํ™”์— ์˜์กดํ•˜๋ฉด ์•ˆ๋œ๋‹ค.")

โœ… SRP / ๋‹จ์ผ ์ฑ…์ž„ ์›์น™

  • ๊ฐ ํด๋ž˜์Šค์— ํ•˜๋‚˜์˜ ์ฑ…์ž„๊ณผ ํ•˜๋‚˜์˜ ๋ชฉ์ ์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • ํ•œ ํด๋ž˜์Šค๊ฐ€ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค๋ฉด ๊ฒฐํ•ฉ๋„๊ฐ€ ๋†’์•„์ ธ ๋ณ€๊ฒฝ์ด ์ƒ๊ธธ ๋•Œ ์œ ์ง€๋ณด์ˆ˜์— ๋น„ํšจ์œจ์ ์ด๋‹ค.
  • ํด๋ž˜์Šค๋Š” ๋‹จ์ผ ๊ด€์‹ฌ์‚ฌ๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.
  • ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ฐพ๊ธฐ๊ฐ€ ๋” ์‰ฝ๋‹ค.
// ํ…์ŠคํŠธ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋œ ํด๋ž˜์Šค 
public class TextManipulator {
    private final String text;

    public setText(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
    
    public void appendText(String newText) {
        text = text.concat(newText);
    }
    
    public String findWordAndReplace(String word, String replacementWord) {
        if (text.contains(word)) {
            text = text.replace(word, replacementWord);
        }
        return text;
    }
    
    public String findWordAndDelete(String word) {
        if (text.contains(word)) {
            text = text.replace(word, "");
        }
        return text;
    }

	// ** ์ถœ๋ ฅ ๋ฉ”์„œ๋“œ **
    public void printText() {
        System.out.println(textManipulator.getText());
    }
}

์œ„์˜ ์ฝ”๋“œ๋Š” ๊ดœ์ฐฎ์•„ ๋ณด์ด์ง€๋งŒ SRP์˜ ์ข‹์€ ์˜ˆ๋Š” ์•„๋‹ˆ๋‹ค.
์œ„ ์ฝ”๋“œ๋Š” ๋‘ ๊ฐ€์ง€ ์ฑ…์ž„์ด ์žˆ๋‹ค.
1. ํ…์ŠคํŠธ ์กฐ์ž‘
2. ์ถœ๋ ฅ

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ธ์‡„ ํ…์ŠคํŠธ๋งŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค

// ํ…์ŠคํŠธ ์ถœ๋ ฅ ํด๋ž˜์Šค
public class TextPrinter {
    TextManipulator textManipulator;

    public TextPrinter(TextManipulator textManipulator) {
        this.textManipulator = textManipulator;
    }

    public void printText() {
        System.out.println(textManipulator.getText());
    }

    public void printOutEachWordOfText() {
        System.out.println(Arrays.toString(textManipulator.getText().split(" ")));
    }

    public void printRangeOfCharacters(int startingIndex, int endIndex) {
        System.out.println(textManipulator.getText().substring(startingIndex, endIndex));
    }
}

์‘์ง‘๋ ฅ

SRP ์›์น™์— ๋”ฐ๋ผ ์šฐ๋ฆฌ ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ์„ ๊ณ ์ˆ˜ํ•œ๋‹ค.
TextManipulator ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋กœ ๋Œ์•„๊ฐ€๋ณด์ž.

...

public void appendText(String newText) {
    text = text.concat(newText);
}

public String findWordAndReplace(String word, String replacementWord) {
    if (text.contains(word)) {
        text = text.replace(word, replacementWord);
    }
    return text;
}

public String findWordAndDelete(String word) {
    if (text.contains(word)) {
        text = text.replace(word, "");
    }
    return text;
}

...

์—ฌ๊ธฐ์— ์ด ํด๋ž˜์Šค๊ฐ€ ํ•˜๋Š” ์ผ์— ๋Œ€ํ•œ ๋ช…ํ™•ํ•œ ํ‘œํ˜„์ด ๋˜์–ด ์žˆ๋‹ค. "ํ…์ŠคํŠธ ์กฐ์ž‘."

๊ทธ๋Ÿฌ๋‚˜ ์‘์ง‘๋ ฅ์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜์ง€ ์•Š๊ณ  ์ด ํด๋ž˜์Šค์˜ ์ฑ…์ž„์ด ๋ฌด์—‡์ธ์ง€์— ๋Œ€ํ•œ ๋ช…ํ™•ํ•œ ์ •์˜๊ฐ€ ์—†๋‹ค๋ฉด ํ…์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์€ ์„œ๋กœ ๋‹ค๋ฅธ ๋ณ„๊ฐœ์˜ ์ž‘์—…์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.
์ด ์ƒ๊ฐ์— ๋”ฐ๋ผ ์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ์ด ๋‘ ๊ฐœ์˜ ๋ณ„๋„ ํด๋ž˜์Šค์ธ WriteText ๋ฐ UpdateText ์—ฌ์•ผ ํ•œ๋‹ค๋Š” ๊ฒฐ๋ก ์„ ๋‚ด๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค .

ํ•˜์ง€๋งŒ ๋ณธ์งˆ์ ์œผ๋กœ ํ…์ŠคํŠธ ์กฐ์ž‘ ์ด๋ผ๋Š” ๋‹จ์ผ ๋ชฉ์ ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.


โœ… OCP / ๊ฐœ๋ฐฉ ํ์‡„ ์›์น™

  • ํ™•์žฅ์—๋Š” ์—ด๋ ค ์žˆ๊ณ  ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

ํ™•์žฅ์— ๋Œ€ํ•ด ์—ด๋ ค์žˆ๋‹ค.

โ†ช ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€(๊ตฌํ˜„)ํ•  ์ˆ˜ ์žˆ๋‹ค.ย ๋”ฐ๋ผ์„œ ํ™•์žฅ์ด ์—ด๋ ค์žˆ๋‹ค๋Š” ๋ง์€ ์ƒˆ๋กœ์šด ํƒ€์ž…(ํด๋ž˜์Šค, ๋ชจ๋“ˆ, ํ•จ์ˆ˜)์„ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ ๊ธฐ๋Šฅ์„ ํ™•์žฅ์‹œํ‚ค๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.

์ˆ˜์ •์— ๋Œ€ํ•ด ๋‹ซํ˜€์žˆ๋‹ค.

โ†ช ํ™•์žฅ์ด ๋ฐœ์ƒํ–ˆ์„ ๋•Œย ์ƒ์œ„ ๋ ˆ๋ฒจ์ด ์˜ํ–ฅ์„ย ๋ฏธ์น˜์ง€ ์•Š์•„์•ผย ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.
๋”ฐ๋ผ์„œ ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ๋ง์€ ํ™•์žฅ์ด ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœ ํ•˜๋Š” ์ชฝ์—์„œ ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.

์š”์•ฝ

๊ณ„๋ฐฉ ํ์‡„ ์›์น™์€ ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์œผ๋ฉด์„œ(Close)
๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€(Open)ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๊ฐ€ ๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ์›์น™์ด๋‹ค.

OCP ๋ฏธ์ค€์ˆ˜

๋”ํ•˜๊ธฐ ๋ฐ ๋นผ๊ธฐ์™€ ๊ฐ™์€ ์—ฌ๋Ÿฌ ์—ฐ์‚ฐ์ด ์žˆ์„ ์ˆ˜ ์žˆ๋Š” ๊ณ„์‚ฐ๊ธฐ ์•ฑ์„ ๋นŒ๋“œํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.


// ์ตœ์ƒ์œ„ ์ธํ„ฐํŽ˜์ด์Šค์ธ CalculatorOperation ๋ฅผ ์ •์˜
public interface CalculatorOperation {}
// ๋ง์…ˆ ํด๋ž˜์Šค
public class Addition implements CalculatorOperation {
    private double left;
    private double right;
    private double result = 0.0;

    public Addition(double left, double right) {
        this.left = left;
        this.right = right;
    }

    // getters and setters

}

ํ˜„์žฌ๋กœ์จ๋Š” Addition ํด๋ž˜์Šค๊ฐ€ ํ•˜๋‚˜๋งŒ ์žˆ์œผ๋ฏ€๋กœ Subtraction ์ด๋ผ๋Š” ๋‹ค๋ฅธ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.

// ๋บ„์…ˆ ํด๋ž˜์Šค
public class Subtraction implements CalculatorOperation {
    private double left;
    private double right;
    private double result = 0.0;

    public Subtraction(double left, double right) {
        this.left = left;
        this.right = right;
    }

    // getters and setters
}

์ด์ œ ๊ณ„์‚ฐ๊ธฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๊ธฐ๋ณธ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•ด ๋ณด๊ฒ ๋‹ค.

public class Calculator {

    public void calculate(CalculatorOperation operation) {
        if (operation == null) {
            throw new InvalidParameterException("Can not perform operation");
        }

        if (operation instanceof Addition) {
            Addition addition = (Addition) operation;
            addition.setResult(addition.getLeft() + addition.getRight());
        } else if (operation instanceof Subtraction) {
            Subtraction subtraction = (Subtraction) operation;
            subtraction.setResult(subtraction.getLeft() - subtraction.getRight());
        }
    }
}

์œ„์˜ ์ฝ”๋“œ๋Š” ๊ดœ์ฐฎ์•„ ๋ณด์ด์ง€๋งŒ OCP์˜ ์ข‹์€ ์˜ˆ๋Š” ์•„๋‹ˆ๋‹ค.
๊ณฑํ•˜๊ธฐ ๋˜๋Š” ๋‚˜๋ˆ„๊ธฐ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜๋Š” ์ƒˆ๋กœ์šด ์š”๊ตฌ ์‚ฌํ•ญ์ด ๋“ค์–ด์˜ค๋ฉด Calculator ํด๋ž˜์Šค ์˜ ๊ณ„์‚ฐ ๋ฐฉ๋ฒ•์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ ์™ธ์—๋Š” ๋ฐฉ๋ฒ•์ด ์—†๋‹ค.
๋”ฐ๋ผ์„œ ์ด ์ฝ”๋“œ๋Š” OCP์™€ ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ๋งํ•  ์ˆ˜ ์žˆ๋‹ค.

OCP ์ค€์ˆ˜

public interface CalculatorOperation {
    void perform();
}

public class Addition implements CalculatorOperation {
    private double left;
    private double right;
    private double result;

    // constructor, getters and setters

    @Override
    public void perform() {
        result = left + right;
    }
}

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋นผ๊ธฐ ํด๋ž˜์Šค๋„ ๋น„์Šทํ•œ ๋…ผ๋ฆฌ๋ฅผ ๊ฐ–๋Š”๋‹ค.
์ถ”๊ฐ€ ๋ฐ ๋นผ๊ธฐ ์™€ ์œ ์‚ฌํ•˜๊ฒŒ ์ƒˆ๋กœ์šด ๋ณ€๊ฒฝ ์š”์ฒญ์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ ๋…ผ๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

public class Division implements CalculatorOperation {
    private double left;
    private double right;
    private double result;

    // constructor, getters and setters
    @Override
    public void perform() {
        if (right != 0) {
            result = left / right;
        }
    }
}

๋งˆ์ง€๋ง‰์œผ๋กœ Calculator ํด๋ž˜์Šค๋Š” ์ƒˆ๋กœ์šด ์—ฐ์‚ฐ์ž๋ฅผ ๋„์ž…ํ•  ๋•Œ ์ƒˆ๋กœ์šด ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

public class Calculator {

    public void calculate(CalculatorOperation operation) {
        if (operation == null) {
            throw new InvalidParameterException("Cannot perform operation");
        }
        operation.perform();
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํด๋ž˜์Šค๋Š” ์ˆ˜์ •์„ ์œ„ํ•ด ๋‹ซํžˆ๊ณ  ํ™•์žฅ์„ ์œ„ํ•ด ์—ด๋ฆฐ๋‹ค.


โœ… LSP / ๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™

ํ”„๋กœ๊ทธ๋žจ์˜ ๊ฐ์ฒด๋Š” ํ”„๋กœ๊ทธ๋žจ์˜ ์ •ํ™•์„ฑ์„ ๊นจ๋œจ๋ฆฌ์ง€ ์•Š์œผ๋ฉด์„œ ํ•˜์œ„ ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
(ํ•˜์œ„ ์œ ํ˜•์€ ๊ธฐ๋ณธ ์œ ํ˜•์œผ๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.)

  • ์ฆ‰, ๋ถ€๋ชจํด๋ž˜์Šค๋ฅผ ์ž์‹ํด๋ž˜์Šค๋กœ ์น˜ํ™˜ํ•ด๋„ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•ด์•ผ ํ•œ๋‹ค.
  • ์ž์‹ํด๋ž˜์Šค๊ฐ€ ๋ถ€๋ชจํด๋ž˜์Šค๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ๋ถ€๋ชจ์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ์‹œํ‚ค๋ฉด LSP์›์น™์„ ์œ„๋ฐ˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์„ ํ–‰๋˜์–ด์•ผ ํ•  ์›์น™

LSP ์น˜ํ™˜ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๋ ค๋ฉด ๋จผ์ € ๊ฐœ๋ฐฉ/ํ์‡„ ์›๋ฆฌ(SOLID์˜ "O")๋ฅผ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.
Open/Closed ์›์น™์˜ ๋ชฉํ‘œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์„ค๊ณ„ ํ•˜์—ฌ ์ƒˆ๋กœ์šด ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ๋งŒ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋„๋ก ๊ถŒ์žฅํ•œ๋‹ค.
์ด๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋ฉด ๋Š์Šจํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜์–ด ์‰ฝ๊ฒŒ ์œ ์ง€ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ๋œ๋‹ค.

LSP ๋ฏธ์ค€์ˆ˜

์•„๋ž˜๋Š” OCP๋ฅผ ์ค€์ˆ˜ํ•œ ์€ํ–‰ ์•ฑ ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ์ด๋‹ค.

์œ„์˜ ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ์€ ๊ดœ์ฐฎ์•„ ๋ณด์ด์ง€๋งŒ LSP์˜ ์ข‹์€ ์˜ˆ๋Š” ์•„๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์—์„œ CurrentAccount ๋ฐ SavingsAccount๊ฐ€ ํ™•์žฅํ•˜๋Š” ์ƒˆ๋กœ์šด ์ถ”์ƒ Account ํด๋ž˜์Šค๋ฅผ ๋„์ž…ํ–ˆ๋‹ค.

BankingAppWithdrawalService ๋Š” ๋” ์ด์ƒ ๊ตฌ์ฒด์ ์ธ ๊ณ„์ขŒ ํด๋ž˜์Šค์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.
์ด์ œ ์ถ”์ƒ ํด๋ž˜์Šค์—๋งŒ ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒˆ๋กœ์šด ๊ณ„์ขŒ ์œ ํ˜•์ด ๋„์ž…๋  ๋•Œ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ BankingAppWithdrawalService๋Š” ์ƒˆ ๊ณ„์ขŒ ์œ ํ˜•์ด ์žˆ๋Š” ํ™•์žฅ์— ๋Œ€ํ•ด ์—ด๋ ค ์žˆ์ง€๋งŒ ์ƒˆ ์œ ํ˜•์€ ํ†ตํ•ฉ์„ ์œ„ํ•ด ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ์ ์—์„œ ์ˆ˜์ •์„ ์œ„ํ•ด ๋‹ซํ˜€์žˆ๋‹ค.(OCP ์ค€์ˆ˜)

์œ„ ์˜ˆ์ œ๋ฅผ Java ์ฝ”๋“œ๋กœ ๋ณด์ž
๋จผ์ € Account ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•ด ๋ณธ๋‹ค.

public abstract class Account {
    protected abstract void deposit(BigDecimal amount);

    /**
     * Reduces the balance of the account by the specified amount
     * provided given amount > 0 and account meets minimum available
     * balance criteria.
     *
     * @param amount
     */
    protected abstract void withdraw(BigDecimal amount);
}

๊ทธ๋ฆฌ๊ณ  BankingAppWithdrawalService๋ฅผ ์ •์˜ํ•ด๋ณธ๋‹ค.

public class BankingAppWithdrawalService {
    private Account account;

    public BankingAppWithdrawalService(Account account) {
        this.account = account;
    }

    public void withdraw(BigDecimal amount) {
        account.withdraw(amount);
    }
}

์ด์ œ ์œ„ ๋””์ž์ธ์ด LSP์˜ ์›์น™์„ ์–ด๋–ป๊ฒŒ ์œ„๋ฐ˜ํ–ˆ๋Š”์ง€ ์‚ดํŽด๋ณด์ž.

์€ํ–‰์€ ๊ณ ๊ฐ์—๊ฒŒ ๊ณ ๊ธˆ๋ฆฌ์˜ ์ •๊ธฐ์˜ˆ๊ธˆ์„ ์ œ๊ณตํ•˜๊ณ ์ž ํ•œ๋‹ค.
์ด๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด FixedTermDepositAccount ํด๋ž˜์Šค๋ฅผ ๋„์ž…ํ•˜์ž.

// ์ •๊ธฐ์˜ˆ๊ธˆ ํด๋ž˜์Šค
public class FixedTermDepositAccount extends Account {
    // Overridden methods...
}

๊ดœ์ฐฎ์€๋“ฏํ•˜๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ์€ํ–‰์€ ์ •๊ธฐ์˜ˆ๊ธˆ ๊ณ„์ขŒ์— ๋Œ€ํ•œ ์ถœ๊ธˆ์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
์ด๋Š” ๊ณง FixedTermDepositAccount ํด๋ž˜์Šค๊ฐ€ Account๊ฐ€ ์ •์˜ํ•œ ์ธ์ถœ ๋ฐฉ๋ฒ•์„ ์˜๋ฏธ ์žˆ๊ฒŒ ์ œ๊ณตํ•  ์ˆ˜ ์—†์Œ์„ ์˜๋ฏธํ•œ๋‹ค.
(์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๋Š” ๋ฉ”์„œ๋“œ์—์„œ UnsupportedOperationException์„ throw ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๊ธด ํ•˜์ง€๋งŒ ์˜ณ์€ ๋ฐฉ๋ฒ•์ด ์•„๋‹ˆ๋‹ค.)

์˜ˆ์—์„œ FixedTermDepositAccount๋Š” Account์˜ ํ•˜์œ„ ์œ ํ˜•์ด ์•„๋‹ˆ๋‹ค.
๋ชจ๋“  ๊ณ„์ขŒ ์œ ํ˜•์ด ์ถœ๊ธˆ์„ ํ—ˆ์šฉํ•œ๋‹ค๊ณ  ์ž˜๋ชป ๊ฐ€์ •ํ–ˆ๋‹ค.
์ถœ๊ธˆ์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” FixedTermDepositAccount๋ฅผ ํฌํ•จํ•œ Account์˜ ๋ชจ๋“  ํ•˜์œ„ ์œ ํ˜•์€ ์ถœ๊ธˆ ๋ฐฉ์‹์„ ์ƒ์†๋ฐ›์•˜๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
์•„๋ž˜์˜ ๋‹ค๋ฅด๊ฒŒ ์„ค๊ณ„๋œ LSP ์ค€์ˆ˜ ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋ณด์ž.

LSP ์ค€์ˆ˜

๋ชจ๋“  ๊ณ„์ขŒ๊ฐ€ ์ธ์ถœ์„ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ธ์ถœ ๋ฉ”์„œ๋“œ๋ฅผ Account ํด๋ž˜์Šค์—์„œ ์ƒˆ๋กœ์šด ์ถ”์ƒ ํ•˜์œ„ ํด๋ž˜์Šค WithdrawableAccount๋กœ ์˜ฎ๊ฒผ๋‹ค.
CurrentAccount์™€ SavingsAccount ๋ชจ๋‘ ์ธ์ถœ์„ ํ—ˆ์šฉํ•œ๋‹ค.
๊ทธ๋ž˜์„œ ๊ทธ๊ฒƒ๋“ค์€ ์ƒˆ๋กœ์šด WithdrawableAccount์˜ ์„œ๋ธŒํด๋ž˜์Šค๊ฐ€ ๋˜์—ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  FixedTermDepositAccount๋Š” ์ž…๊ธˆ๋งŒ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด BankingAppWithdrawalService๋Š” ์ด์ œ WithdrawableAccount๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

public class BankingAppWithdrawalService {
    private WithdrawableAccount withdrawableAccount;

    public BankingAppWithdrawalService(WithdrawableAccount withdrawableAccount) {
        this.withdrawableAccount = withdrawableAccount;
    }

    public void withdraw(BigDecimal amount) {
        withdrawableAccount.withdraw(amount);
    }
}

์‰ฝ๊ฒŒ ์š”์•ฝ

ํ’€์–ด์„œ ์“ด ๊ฒŒ ๋„ˆ๋ฌด ์žฅํ™ฉํ•ด์„œ ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šธ๋“ฏํ•˜์—ฌ
์‰ฌ์šด ์˜ˆ์‹œ๋ฅผ ๊ตฌํ•ด์™”๋‹ค.

์˜ณ์€ ์˜ˆ์‹œ ex) ๋™๋ฌผ ํ–ฅ์œ ๊ณ ๋ž˜ = new ๊ณ ๋ž˜();

์œ„์˜ ์˜ณ์€ ์˜ˆ์‹œ๋กœ ๋ณด๋ฉด ๋™๋ฌผ์ด๋ผ๋Š” ์ตœ์ƒ์œ„ ํƒ€์ž…์€ ํ•˜์œ„์˜ ๋ชจ๋“  ํƒ€์ž…์„ ๊ต์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฐ์‹์œผ๋กœ ์ƒ, ํ•˜์œ„ ๊ด€๊ณ„๋ฅผ ๋‚˜๋ˆŒ ๋•Œ์—๋„ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋งž๋Š” ๊ตฌ์กฐ๋กœ ์„ค๊ณ„ํ•˜์—ฌ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

์ถœ์ฒ˜: https://jeongkyun-it.tistory.com/156 [๋‚˜์˜ ๊ณผ๊ฑฐ์ผ์ง€:ํ‹ฐ์Šคํ† ๋ฆฌ]


โœ… ISP / ์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ์˜ ์›์น™

ํŠน์ • ํด๋ผ์ด์–ธํŠธ๋ฅผ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ๋ฒ”์šฉ ์ธํ„ฐํŽ˜์ด์Šค ํ•˜๋‚˜๋ณด๋‹ค ๋‚ซ๋‹ค.

  • ํ•˜๋‚˜์˜ ํฐ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์† ๋ฐ›๊ธฐ ๋ณด๋‹ค๋Š”, ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌ์ฒด์ ์ด๊ณ  ์ž‘์€ ๋‹จ์œ„๋“ค๋กœ ๋ถ„๋ฆฌ์‹œ์ผœ ๊ผญ ํ•„์š”ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์ƒ์†ํ•˜๋Š” ๊ฒƒ์ด ๋‚ซ๋‹ค.
    ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋ช…ํ™•ํ•ด์ง€๊ณ , ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ์•„์ง„๋‹ค. (SRP ๋‹จ์ผ์ฑ…์ž„์›์น™๊ณผ ์œ ์‚ฌ)

ISP ๋ฏธ์ค€์ˆ˜

์ง€๋ถˆ ์œ ํ˜•์— ๋Œ€ํ•œ ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ.

// ๊ฒฐ์ œ ์ธํ„ฐํŽ˜์ด์Šค
public interface Payment {
 
    // original methods
    ...
    void intiateLoanSettlement();
    void initiateRePayment();
}
public class LoanPayment implements Payment {

    @Override
    public void initiatePayments() {
        throw new UnsupportedOperationException("This is not a bank payment");
    }
    
    @Override
    public void initiateRePayment() {
        // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List<Object> getPayments() {
        // ...
    }

    @Override
    public void intiateLoanSettlement() {
        // ...
    }

}

์ด์ œ Payment ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋ณ€๊ฒฝ๋˜๊ณ  ๋” ๋งŽ์€ ๋ฉ”์†Œ๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์œผ๋ฏ€๋กœ ๋ชจ๋“  ๊ตฌํ˜„ ํด๋ž˜์Šค๋Š” ์ด์ œ ์ถ”๊ฐ€๋œ ์ƒˆ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.
์œ„ ์ฝ”๋“œ์—์„œ LoanPayment ๊ตฌํ˜„ ํด๋ž˜์Šค๋Š” ์‹ค์ œ ํ•„์š” ์—†์ด initialPayments()๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

๋”ฐ๋ผ์„œ BankPayment ํด๋ž˜์Šค๋Š” ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

public class BankPayment implements Payment {

    @Override
    public void initiatePayments() {
        // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List<Object> getPayments() {
        // ...
    }

    @Override
    public void intiateLoanSettlement() {
        throw new UnsupportedOperationException("This is not a loan payment");
    }

    @Override
    public void initiateRePayment() {
        throw new UnsupportedOperationException("This is not a loan payment");
    }
}

์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ๋˜์–ด๋ฒ„๋ฆฐ๋‹ค.
์•ˆ ์“ฐ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ UnsupportedOperationException์„ throwํ•  ๋ฟ์ด๋‹ค.
์ด๊ฒƒ์˜ ๋ฌธ์ œ๋Š”
์ถ”๊ฐ€๋œ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์›์น˜ ์•Š์€๋ฐ ๊ฐ•์ œ์ ์œผ๋กœ ๊ตฌํ˜„์„ ํ•ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋Š” ๊ณง ๋งŽ์€ ๋ถ€์ž‘์šฉ์„ ์ดˆ๋ž˜ํ•œ๋‹ค.

ISP ์ค€์ˆ˜

์ƒˆ ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋ณด์ž.

// ์ธํ„ฐํŽ˜์ด์Šค
public interface Payment {
    Object status();
    List<Object> getPayments();
}

๋‘ ๊ฐ€์ง€ ์œ ํ˜•์˜ ๊ฒฐ์ œ๋ฅผ ์œ„ํ•œ ๋‘ ๊ฐœ์˜ ์ธํ„ฐํŽ˜์ด์Šค ์ถ”๊ฐ€

1. ์ฒซ ๋ฒˆ์งธ ์ธํ„ฐํŽ˜์ด์Šค
public interface Bank extends Payment {
    void initiatePayments();
}

2. ๋‘ ๋ฒˆ์งธ ์ธํ„ฐํŽ˜์ด์Šค
public interface Loan extends Payment {
    void intiateLoanSettlement();
    void initiateRePayment();
}

๊ฐœ์ •๋œ BankPayment ๊ตฌํ˜„ ํด๋ž˜์Šค

public class BankPayment implements Bank {

    @Override
    public void initiatePayments() {
        // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List<Object> getPayments() {
        // ...
    }
}

๊ฐœ์ •๋œ LoanPayment ๊ตฌํ˜„ ํด๋ž˜์Šค

public class LoanPayment implements Loan {

    @Override
    public void intiateLoanSettlement() {
        // ...
    }

    @Override
    public void initiateRePayment() {
        // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List<Object> getPayments() {
        // ...
    }
}

โœ… DIP / ์˜์กด๊ด€๊ณ„ ์—ญ์ „์˜ ์›์น™

ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” "์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ์ง€, ๊ตฌ์ฒดํ™”์— ์˜์กดํ•˜๋ฉด ์•ˆ๋œ๋‹ค."

  • DIP๋Š” ์˜์กด์„ฑ ์ฃผ์ž…(DI)๋„ ์•„๋‹ˆ๊ณ  ์ œ์–ด์˜ ์—ญ์ „(IOC)๋„ ์•„๋‹ˆ๋‹ค.
  1. ๊ณ ์ˆ˜์ค€ ๋ชจ๋“ˆ์€ ์ €์ˆ˜์ค€ ๋ชจ๋“ˆ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค ์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  2. ์ถ”์ƒํ™”๋Š” ์„ธ๋ถ€ ์‚ฌํ•ญ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์„ธ๋ถ€ ์‚ฌํ•ญ์€ ์ถ”์ƒํ™”์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    โ†ช ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด ์˜์กด ๊ด€๊ณ„๋ฅผ ๋งบ์„ ๋•Œ ๋ณ€ํ™”ํ•˜๊ธฐ ์‰ฌ์šด ๊ฒƒ์— ์˜์กดํ•˜๊ธฐ๋ณด๋‹ค๋Š” ๋ณ€ํ™”ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์— ์˜์กดํ•˜๋ผ๋Š” ๋ง์ด๋‹ค.

DIP ๋ฏธ์ค€์ˆ˜

๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž

// ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž
public class BackEndDeveloper {
    public void writeJava() {}
}

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž

// ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž
public class FrontEndDeveloper {
    public void writeJavascript() {}
}

ํ”„๋กœ์ ํŠธ Class์—์„œ ์œ„์˜ ๋‘ ๊ฐ€์ง€๋ฅผ ๋ชจ๋‘ ์‚ฌ์šฉํ•œ๋‹ค.

public class Project {

    private BackEndDeveloper backEndDeveloper = new BackEndDeveloper();
    private FrontEndDeveloper frontEndDeveloper = new FrontEndDeveloper();

    public void implement() {
        backEndDeveloper.writeJava();
        frontEndDeveloper.writeJavascript();
    }
}

์œ„์˜ ์ฝ”๋“œ๋Š” ๊ดœ์ฐฎ์•„ ๋ณด์ด์ง€๋งŒ DIP์˜ ์ข‹์€ ์˜ˆ๋Š” ์•„๋‹ˆ๋‹ค.

  1. ๊ณ ์ˆ˜์ค€ ๋ชจ๋“ˆ์€ ์ €์ˆ˜์ค€ ๋ชจ๋“ˆ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค ์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.)
  2. ์ถ”์ƒํ™”๋Š” ์„ธ๋ถ€ ์‚ฌํ•ญ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์„ธ๋ถ€ ์‚ฌํ•ญ์€ ์ถ”์ƒํ™”์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค.)

๋ณด์‹œ๋‹ค์‹œํ”ผ Project ํด๋ž˜์Šค๋Š” ๊ณ ์ˆ˜์ค€ ๋ชจ๋“ˆ(์ƒ์œ„ ๋ ˆ๋ฒจ ๋ชจ๋“ˆ)์ด๋ฉฐ BackEndDeveloper ๋ฐ FrontEndDeveloper์™€ ๊ฐ™์€ ์ €์ˆ˜์ค€ ๋ชจ๋“ˆ(ํ•˜์œ„ ๋ชจ๋“ˆ)์— ์˜์กดํ•˜๊ณ  ์žˆ๋‹ค.
์˜์กด๊ด€๊ณ„ ์—ญ์ „ ์›์น™ ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„์„ ์œ„๋ฐ˜ํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค.

๋˜ํ•œ Project.class์˜ implement() ๋ฉ”์„œ๋“œ๋ฅผ ๋ณด๋ฉด writeJava()์™€ writeJavaScript() ๋ฉ”์„œ๋“œ๊ฐ€ ํ•ด๋‹น ํด๋ž˜์Šค์— ๋ฐ”์ธ๋”ฉ๋œ ๋ฉ”์„œ๋“œ์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
ํ”„๋กœ์ ํŠธ ๋ฒ”์œ„์— ๊ด€ํ•ด์„œ ๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘ ์„ธ๋ถ€ ์‚ฌํ•ญ์ด๋‹ค.
์˜์กด๊ด€๊ณ„ ์—ญ์ „ ์›์น™ ๋‘ ๋ฒˆ์งธ ๋ถ€๋ถ„์„ ์œ„๋ฐ˜ํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค.

DIP ์ค€์ˆ˜

์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

public interface Developer {
    void develop();
}

์ถ”์ƒํ™”๋ฅผ ๋„์ž…ํ•˜์ž.
๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฆฌํŒฉํ„ฐ๋ง ๋œ๋‹ค.

// ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž
public class BackEndDeveloper implements Developer {

    @Override
    public void develop() {
        writeJava();
    }

    private void writeJava() {
    }

}

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฆฌํŒฉํ„ฐ๋ง ๋œ๋‹ค.

// ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž
public class FrontEndDeveloper implements Developer {

    @Override
    public void develop() {
        writeJavascript();
    }

    public void writeJavascript() {
    }

}

๋‹ค์Œ ๋‹จ๊ณ„๋Š” ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„์˜ ์œ„๋ฐ˜์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋กœ์ ํŠธ ํด๋ž˜์Šค๋ฅผ ๋ฆฌํŒฉํ„ฐ๋งํ•˜์—ฌ FrontEndDeveloper ๋ฐ BackendDeveloper ํด๋ž˜์Šค์— ์˜์กดํ•˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

public class Project {

    private List<Developer> developers;

    public Project(List<Developer> developers) {
        this.developers = developers;
    }

    public void implement() {
        developers.forEach(d->d.develop());
    }

}

๊ทธ ๊ฒฐ๊ณผ Project ํด๋ž˜์Šค๋Š” ํ•˜์œ„ ์ €์ˆ˜์ค€ ๋ชจ๋“ˆ์ด ์•„๋‹ˆ๋ผ ์ถ”์ƒํ™”์— ์˜์กดํ•˜๊ฒŒ ๋œ๋‹ค.
๋˜ํ•œ ๋‚ฎ์€ ์ˆ˜์ค€์˜ ๋ชจ๋“ˆ๊ณผ ๊ทธ ์„ธ๋ถ€ ์‚ฌํ•ญ์€ ์ถ”์ƒํ™”์— ์˜์กดํ•œ๋‹ค.
์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‘ ๋ฒˆ์งธ ๋ถ€๋ถ„์˜ ์œ„๋ฐ˜๋„ ํ•ด๊ฒฐ์ด ๋œ๋‹ค.


๋

profile
๋‚ด๊ฐ€ ๋ณผ๋ผ๊ณ  ์”€.
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€