7 ๋ถ„ ์†Œ์š”

๐Ÿ”ง DI (Dependency Injection)๋ž€?

DI(์˜์กด์„ฑ ์ฃผ์ž…)์€ ๊ฐ์ฒด๊ฐ€ ์ง์ ‘ ํ•„์š”ํ•œ ์˜์กด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ , ์™ธ๋ถ€์—์„œ ์ฃผ์ž…๋ฐ›๋Š” ๋ฐฉ์‹


๐Ÿ“Œ ์˜ˆ: โ€œ์ž๋™์ฐจ(Car)โ€์— โ€œ์—”์ง„(Engine)โ€์ด ํ•„์š”ํ•  ๋•Œ

โŒ ์ง์ ‘ ์ƒ์„ฑ (์˜์กด์„ฑ ์ง์ ‘ ๊ด€๋ฆฌ)

class Car {
    private Engine engine = new Engine(); 
    // Car๊ฐ€ ์ง์ ‘ Engine์„ ์ƒ์„ฑํ•จ
}

โœ… ์˜์กด์„ฑ ์ฃผ์ž… ๋ฐฉ์‹

class Car {
    private Engine engine;

    public Car(Engine engine) {  // ์™ธ๋ถ€์—์„œ Engine์„ ์ฃผ์ž…๋ฐ›์Œ
        this.engine = engine;
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Car๋Š” ์–ด๋–ค ์ข…๋ฅ˜์˜ ์—”์ง„์ด ๋“ค์–ด์˜ค๋Š”์ง€ ๋ชฐ๋ผ๋„ ๋˜๊ณ , ํ…Œ์ŠคํŠธ๋„ ๋” ์œ ์—ฐํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿงฉ ์ฃผ์š” ์šฉ์–ด ์ •๋ฆฌ

์šฉ์–ด ์„ค๋ช…
Dependency (์˜์กด์„ฑ) ํ•œ ๊ฐ์ฒด๊ฐ€ ๋‹ค๋ฅธ ๊ฐ์ฒด์— ๊ธฐ๋Šฅ์ ์œผ๋กœ ์˜์กดํ•˜๊ณ  ์žˆ์„ ๋•Œ, ๊ทธ ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ โ€œ์˜์กด์„ฑโ€์ด๋ผ๊ณ  ๋ถ€๋ฆ„. ์˜ˆ: Car โ†’ Engine
Injection (์ฃผ์ž…) ์˜์กด ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ , ์™ธ๋ถ€์—์„œ ๋„ฃ์–ด์ฃผ๋Š” ํ–‰์œ„
DI Container ์˜์กด์„ฑ๋“ค์„ ๊ด€๋ฆฌํ•˜๊ณ  ์ฃผ์ž…ํ•ด์ฃผ๋Š” ์ปจํ…Œ์ด๋„ˆ. Spring์˜ ApplicationContext๊ฐ€ ๋Œ€ํ‘œ์ 
Constructor Injection ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์˜์กด์„ฑ์„ ์ฃผ์ž… (๊ฐ€์žฅ ๊ถŒ์žฅ๋˜๋Š” ๋ฐฉ์‹)
Setter Injection setter ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์˜์กด์„ฑ ์ฃผ์ž…
Field Injection ํ•„๋“œ์— ์ง์ ‘ ์ฃผ์ž… (@Autowired ๋“ฑ). ๊ฐ„๋‹จํ•˜์ง€๋งŒ ํ…Œ์ŠคํŠธ ์–ด๋ ค์›€
IoC (Inversion of Control) โ€œ์ œ์–ด์˜ ์—ญ์ „โ€ โ€” ๊ฐ์ฒด์˜ ์ƒ์„ฑ๊ณผ ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ๋ฅผ ๊ฐœ๋ฐœ์ž๊ฐ€ ์•„๋‹ˆ๋ผ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋‹ด๋‹นํ•˜๋Š” ๊ฐœ๋…. DI๋Š” IoC๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ํ•œ ๋ฐฉ์‹

๐Ÿ—๏ธ DI๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

  • ๊ฒฐํ•ฉ๋„ ๋‚ฎ์ถค โ†’ ์ฝ”๋“œ๊ฐ€ ๋” ์œ ์—ฐํ•˜๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅ
  • ํ…Œ์ŠคํŠธ ์‰ฌ์›€ โ†’ ๊ฐ€์งœ(Mock) ๊ฐ์ฒด ์ฃผ์ž…ํ•ด์„œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ
  • ์œ ์ง€๋ณด์ˆ˜ ํŽธ๋ฆฌ โ†’ ์ƒˆ๋กœ์šด ์˜์กด์„ฑ์œผ๋กœ ์‰ฝ๊ฒŒ ๊ต์ฒด ๊ฐ€๋Šฅ
  • ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ โ†’ ๊ฐ์ฒด๋Š” ์ž์‹ ์˜ ๋กœ์ง์—๋งŒ ์ง‘์ค‘ํ•˜๊ณ  ์˜์กด์„ฑ ๊ด€๋ฆฌ๋Š” ์™ธ๋ถ€์— ๋งก๊น€

๐Ÿ” ์ž์ฃผ ํ•จ๊ป˜ ์–ธ๊ธ‰๋˜๋Š” ๊ฐœ๋…

๊ฐœ๋… ์„ค๋ช…
Service, Repository ๋“ฑ ์—ญํ•  ๋ถ„๋ฆฌ DI๋ฅผ ํ†ตํ•ด ๊ฐ ๊ณ„์ธต ๊ฐ„ ๊ฒฐํ•ฉ์„ ๋А์Šจํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ
@Component, @Service, @Repository Spring์ด ์ž๋™์œผ๋กœ Bean์œผ๋กœ ๋“ฑ๋กํ•ด์„œ DI ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ
@Autowired, @Inject, @Qualifier ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ์กฐ๊ฑด์„ ์ง€์ •ํ•จ

๐Ÿ”ง Spring์—์„œ์˜ DI ์˜ˆ์ œ

๐ŸŽฏ ๋ชฉํ‘œ

  • Car๊ฐ€ Engine์— ์˜์กด
  • ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ Engine์„ ์ฃผ์ž… (DI)
  • DI๋ฅผ ํ†ตํ•ด Car๋Š” ์–ด๋–ค Engine์ด๋“  ๋ฐ›์•„์„œ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„

๐Ÿ”น ์ธํ„ฐํŽ˜์ด์Šค ๋ฐ ๊ตฌํ˜„์ฒด

public interface Engine {
    void start();
}
@Component
public class GasEngine implements Engine {
    @Override
    public void start() {
        System.out.println("Gas engine starting...");
    }
}
@Component
public class ElectricEngine implements Engine {
    @Override
    public void start() {
        System.out.println("Electric engine starting...");
    }
}

๐Ÿ”น Car ํด๋ž˜์Šค (DI ์ ์šฉ)

๋ฐฉ๋ฒ• 1: ์ƒ์„ฑ์ž ์ฃผ์ž… (๊ฐ€์žฅ ๊ถŒ์žฅ๋จ)

@Component
public class Car {

    private final Engine engine;

    @Autowired  // ์ƒ๋žต ๊ฐ€๋Šฅ (Spring 4.3 ์ด์ƒ)
    public Car(Engine engine) {
        this.engine = engine;
    }

    public void drive() {
        engine.start();
        System.out.println("Car is moving");
    }
}

๋ฐฉ๋ฒ• 2: ํ•„๋“œ ์ฃผ์ž… (ํ…Œ์ŠคํŠธ ๋ถˆํŽธ, ๋น„๊ถŒ์žฅ)

@Component
public class Car {

    @Autowired
    private Engine engine;

    public void drive() {
        engine.start();
        System.out.println("Car is moving");
    }
}

๋ฐฉ๋ฒ• 3: Setter ์ฃผ์ž… (์„ ํƒ์  ์˜์กด์„ฑ์— ์ ํ•ฉ)

@Component
public class Car {

    private Engine engine;

    @Autowired
    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public void drive() {
        engine.start();
        System.out.println("Car is moving");
    }
}

๐Ÿ”ง ํ•„๋“œ ์ฃผ์ž…์ด ํ…Œ์ŠคํŠธ์—์„œ ๋ถˆํŽธํ•œ ์ด์œ 

โ— ๋ฌธ์ œ: ์ฃผ์ž…์„ ์ง์ ‘ ํ•  ์ˆ˜ ์—†๋‹ค

ํ•„๋“œ ์ฃผ์ž…์€ ์•„๋ž˜์ฒ˜๋Ÿผ private ํ•„๋“œ์— @Autowired๋ฅผ ๋ถ™์—ฌ ์ž๋™ ์ฃผ์ž…

@Component
public class Car {
    @Autowired
    private Engine engine;

    public void drive() {
        engine.start();
    }
}

์ด๋Ÿฐ ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ์ง์ ‘ engine์„ ์ฃผ์ž…ํ•  ์ˆ˜ ์—†๋‹ค

  • Car car = new Car(); ๋กœ ์ƒ์„ฑํ•ด๋„ engine์ด null
  • engine์„ ์ฃผ์ž…ํ•˜๋ ค๋ฉด ๋ฆฌํ”Œ๋ ‰์…˜์„ ์จ์•ผ ํ•จ:
@Test
void testDrive_withReflection() throws Exception {
    Car car = new Car();
    Engine mockEngine = mock(Engine.class);

    Field field = Car.class.getDeclaredField("engine");
    field.setAccessible(true);
    field.set(car, mockEngine);  // ๊ฐ•์ œ๋กœ ์ฃผ์ž…
    car.drive();
}

โœ… ์ƒ์„ฑ์ž ์ฃผ์ž…๊ณผ ๋น„๊ต

์ƒ์„ฑ์ž ์ฃผ์ž…์ด๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ:

@Test
void testDrive() {
    Engine mockEngine = mock(Engine.class);
    Car car = new Car(mockEngine);  // ์ง์ ‘ ์ฃผ์ž… ๊ฐ€๋Šฅ

    car.drive();

    verify(mockEngine).start();  // ๊ฒ€์ฆ๋„ ๊ฐ€๋Šฅ
}

๐Ÿ’ก ํ•„๋“œ ์ฃผ์ž…์˜ ๋‹ค๋ฅธ ๋‹จ์ 

ํ•ญ๋ชฉ ๋‚ด์šฉ
โŒ ๋ถˆ๋ณ€์„ฑ ์—†์Œ ์ƒ์„ฑ ์‹œ์ ์— ์ฃผ์ž…๋˜์ง€ ์•Š์•„์„œ final ํ‚ค์›Œ๋“œ ๋ชป ์”€
โŒ ๋ช…์‹œ์  ์˜์กด์„ฑ ์—†์Œ ์–ด๋–ค ์˜์กด์„ฑ์ด ํ•„์š”ํ•œ์ง€ ์ƒ์„ฑ์ž ์‹œ๊ทธ๋‹ˆ์ฒ˜๋กœ๋Š” ์•Œ ์ˆ˜ ์—†์Œ
โŒ ํ…Œ์ŠคํŠธ ์–ด๋ ค์›€ ์œ„์—์„œ ๋ณธ ๊ฒƒ์ฒ˜๋Ÿผ mock ์ฃผ์ž…, ์˜์กด์„ฑ ๊ต์ฒด๊ฐ€ ์–ด๋ ต๊ณ  ์šฐํšŒ์ 
โŒ ์œ ์ง€๋ณด์ˆ˜ ์–ด๋ ค์›€ ์ฝ”๋“œ๋งŒ ๋ณด๊ณ ๋Š” ์–ด๋–ค ๊ฐ์ฒด๊ฐ€ ๋“ค์–ด์˜ค๋Š”์ง€ ์ถ”๋ก ์ด ์–ด๋ ค์›€
ย  ย 

โœ… ์ •๋ฆฌ

  • ํ•„๋“œ ์ฃผ์ž…์€ ํŽธ๋ฆฌํ•ด ๋ณด์ด์ง€๋งŒ, ์œ ๋‹› ํ…Œ์ŠคํŠธ, ๋ถˆ๋ณ€์„ฑ, ๋ช…์‹œ์„ฑ ์ธก๋ฉด์—์„œ ๋ชจ๋‘ ๋ถˆ๋ฆฌํ•จ
  • ๊ทธ๋ž˜์„œ Spring ๊ณต์‹ ๊ฐ€์ด๋“œ, Josh Long ๊ฐ™์€ ์ „๋ฌธ๊ฐ€๋“ค๋„ โ€œํ•ญ์ƒ ์ƒ์„ฑ์ž ์ฃผ์ž…์„ ์“ฐ๋ผโ€๊ณ  ๊ถŒ์žฅ ํ•จ
  • ํ…Œ์ŠคํŠธ์—์„œ๋Š” ํŠนํžˆ mock ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•˜๊ธฐ ์–ด๋ ค์›Œ์„œ ์ •๋ง ๋ถˆํŽธํ•จ

โœ… Setter ์ฃผ์ž…์ด โ€œ์„ ํƒ์  ์˜์กด์„ฑโ€์— ์ ํ•ฉํ•œ ์ด์œ 

์–ด๋–ค ์˜์กด์„ฑ์€ ์žˆ์œผ๋ฉด ์ข‹์ง€๋งŒ, ์—†์–ด๋„ ๋™์ž‘์ด ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ โ†’ ๊ผญ ์ƒ์„ฑ์ž์— ๋„ฃ์„ ํ•„์š”๊ฐ€ ์—†์Œ
โ†’ ๊ทธ๋ž˜์„œ setter๋กœ ํ•„์š”ํ•  ๋•Œ๋งŒ ์ฃผ์ž…ํ•˜๋„๋ก ์„ค๊ณ„

@Component
public class MyService {

    private Logger logger;

    @Autowired(required = false)  // ์ฃผ์ž… ์•ˆ ๋ผ๋„ ์˜ค๋ฅ˜ ์•ˆ ๋‚จ
    public void setLogger(Logger logger) {
        this.logger = logger;
    }

    public void doSomething() {
        if (logger != null) {
            logger.log("Starting...");
        }

        System.out.println("Doing something important!");
    }
}
  • Logger๋Š” ์—†์–ด๋„ ๋กœ์ง์€ ๋Œ์•„๊ฐ
  • ์žˆ์„ ๊ฒฝ์šฐ์—๋งŒ ๋กœ๊น… ์ˆ˜ํ–‰
  • ๋”ฐ๋ผ์„œ Setter ์ฃผ์ž…์ด ์ ํ•ฉ
โœ… Setter ์ฃผ์ž…์˜ ์žฅ์ 
ํ•ญ๋ชฉ ์„ค๋ช…
โœ” ์„ ํƒ์  ์˜์กด์„ฑ ํ‘œํ˜„ ๊ฐ€๋Šฅ @Autowired(required = false)๋กœ ์žˆ์œผ๋ฉด ์ฃผ์ž…, ์—†์œผ๋ฉด ๋„˜์–ด๊ฐ
โœ” ๋‚˜์ค‘์— ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ Bean ์ƒ์„ฑ ์ดํ›„ ๋‹ค๋ฅธ ๊ฐ์ฒด๋กœ๋„ ์ฃผ์ž… ๊ฐ€๋Šฅ (๋™์ ์œผ๋กœ ๋ฐ”๊พธ๊ณ  ์‹ถ์„ ๋•Œ)
โœ” ํ…Œ์ŠคํŠธ์—์„œ mock ์‰ฝ๊ฒŒ ์ฃผ์ž… ๊ฐ€๋Šฅ setLogger(mockLogger)์ฒ˜๋Ÿผ ์ฃผ์ž… ๊ฐ€๋Šฅ
โŒ Setter ์ฃผ์ž…์˜ ๋‹จ์ 
ํ•ญ๋ชฉ ์„ค๋ช…
โŒ ๋ถˆ๋ณ€ ๊ฐ์ฒด ์„ค๊ณ„ ๋ถˆ๊ฐ€ setter๋Š” ๊ฐ’ ๋ณ€๊ฒฝ ํ—ˆ์šฉ โ†’ ์ƒํƒœ๊ฐ€ ๋ฐ”๋€” ์ˆ˜ ์žˆ์Œ
โŒ ์˜์กด์„ฑ ๋ˆ„๋ฝ ๋ฐฉ์ง€ ์–ด๋ ค์›€ ์ฃผ์ž…์„ ๊นœ๋นกํ•ด๋„ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์•ˆ ์žก์Œ (๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜ ์œ ๋ฐœ ๊ฐ€๋Šฅ)
โŒ ๋ช…์‹œ์  ์˜์กด์„ฑ ํ‘œํ˜„ ์•ฝํ•จ ํด๋ž˜์Šค๋งŒ ๋ณด๊ณ ๋Š” ํ•„์ˆ˜์ธ์ง€ ์„ ํƒ์ธ์ง€ ๋ฐ”๋กœ ํŒŒ์•… ์–ด๋ ค์›€

โœ… ๋น„๊ต ์š”์•ฝ

๊ตฌ๋ถ„ ์ƒ์„ฑ์ž ์ฃผ์ž… Setter ์ฃผ์ž… ํ•„๋“œ ์ฃผ์ž…
๐Ÿ“Œ ์šฉ๋„ ํ•„์ˆ˜ ์˜์กด์„ฑ ์„ ํƒ์  ์˜์กด์„ฑ ๊ฐ€๋ณ๊ฒŒ ์“ฐ๊ธฐ ์‰ฌ์›€ (but ๋น„๊ถŒ์žฅ)
๐Ÿ”’ ๋ถˆ๋ณ€์„ฑ ์œ ์ง€ ๊ฐ€๋Šฅ ์œ ์ง€ ๋ถˆ๊ฐ€ ์œ ์ง€ ๋ถˆ๊ฐ€
๐Ÿ” ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ ๋งค์šฐ ์ข‹์Œ ์ข‹์Œ ๋‚˜์จ
๐Ÿงฉ ๋ช…์‹œ์„ฑ ๋†’์Œ ๋‚ฎ์Œ ๋‚ฎ์Œ

๐Ÿ”š ์ •๋ฆฌ

  • Setter ์ฃผ์ž…์€ โ€œํ•„์ˆ˜๋Š” ์•„๋‹Œ, ์˜ต์…˜์ ์ธโ€ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•  ๋•Œ ์‚ฌ์šฉ
  • ๊ทธ ์™ธ์—๋Š” ๋Œ€๋ถ€๋ถ„ ์ƒ์„ฑ์ž ์ฃผ์ž…์ด ์ •์„
  • Spring์—์„œ๋Š” setter ์ฃผ์ž…๋„ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋ฌด๋ถ„๋ณ„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด **์„ค๊ณ„๊ฐ€ ๋ชจํ˜ธํ•ด์ง€๊ณ  ์œ ์ง€๋ณด์ˆ˜ ์–ด๋ ค์›Œ์ง

๐Ÿ”ง 2. DIP (Dependency Inversion Principle)

๐Ÿ“˜ DIP๋ž€?

์˜์กด์„ฑ ์—ญ์ „ ์›์น™:
โ€œ์ƒ์œ„ ๋ชจ๋“ˆ(๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง)์€ ํ•˜์œ„ ๋ชจ๋“ˆ(๊ตฌํ˜„)์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋˜๋ฉฐ,
๋‘˜ ๋‹ค ์ถ”์ƒ(์ธํ„ฐํŽ˜์ด์Šค)์— ์˜์กดํ•ด์•ผ ํ•œ๋‹ค.โ€

โŒ DIP ์œ„๋ฐ˜ ์˜ˆ

public class Car {
    private GasEngine engine = new GasEngine();  
    // ๊ตฌํ˜„์ฒด์— ์˜์กด (๊ฐ•ํ•œ ๊ฒฐํ•ฉ)
}

Car๋Š” GasEngine์ด๋ผ๋Š” ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กด โ†’ ํ™•์žฅ์„ฑ ๋‚ฎ๊ณ , ํ…Œ์ŠคํŠธ ์–ด๋ ค์›€

โœ… DIP ๋งŒ์กฑ ์˜ˆ

public class Car {
    private Engine engine;

    public Car(Engine engine) {  // ์ธํ„ฐํŽ˜์ด์Šค(์ถ”์ƒ)์— ์˜์กด
        this.engine = engine;
    }
}

Car๋Š” Engine์ด๋ผ๋Š” ์ถ”์ƒ ํƒ€์ž…์—๋งŒ ์˜์กด โ†’ ์œ ์—ฐํ•˜๊ณ  ํ…Œ์ŠคํŠธ ์šฉ์ด

โœ… 1. Engine ์ถ”์ƒํ™”(์ธํ„ฐํŽ˜์ด์Šค) ์ •์˜
public interface Engine {
    void start();
}
  • Engine์€ ์ธํ„ฐํŽ˜์ด์Šค์ด๋ฏ€๋กœ โ€œํ–‰์œ„(๊ธฐ๋Šฅ)โ€๋งŒ ์ •์˜
  • Car๋Š” ์ด์ œ ์ด ์ถ”์ƒ ํƒ€์ž…๋งŒ ์•Œ๋ฉด ๋จ
โœ… 2. GasEngine ๊ตฌํ˜„์ฒด ์ •์˜ (DIP ๋งŒ์กฑ)
public class GasEngine implements Engine {
    @Override
    public void start() {
        System.out.println("Gas engine started.");
    }
}
  • GasEngine์€ Engine ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค
  • ์ด ๊ตฌํ˜„์ฒด๋Š” ์‹ค์ œ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํด๋ž˜์Šค
โœ… 3. Car ํด๋ž˜์Šค๋Š” ์ถ”์ƒํ™”์—๋งŒ ์˜์กด
public class Car {
    private final Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void drive() {
        engine.start();
    }
}

  • ์ด์ œ Car๋Š” ๋” ์ด์ƒ GasEngine์ด ๋ญ”์ง€ ๋ชฐ๋ผ๋„ ๋จ
  • ์˜ค์ง Engine ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ(start())๋งŒ ์‚ฌ์šฉ
โœ… 4. ์‚ฌ์šฉํ•˜๋Š” ์ชฝ์—์„œ ์ฃผ์ž… (DI)
public class Main {
    public static void main(String[] args) {
        Engine engine = new GasEngine(); // ์–ด๋–ค ๊ตฌํ˜„์ฒด๋ฅผ ์“ธ์ง€๋Š” ์—ฌ๊ธฐ์„œ ๊ฒฐ์ •
        Car car = new Car(engine);

        car.drive(); // "Gas engine started." ์ถœ๋ ฅ
    }
}

  • Car๋Š” Engine ํƒ€์ž…์„ ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ DIP ๋งŒ์กฑ
  • ๊ตฌํ˜„์ฒด(GasEngine, ElectricEngine ๋“ฑ)๋Š” ์ฃผ์ž…์„ ํ†ตํ•ด ๊ต์ฒด ๊ฐ€๋Šฅ
โœ… ํ…Œ์ŠคํŠธ๋„ ์‰ฌ์›Œ์ง (๋ชจ์˜ ๊ฐ์ฒด)
public class MockEngine implements Engine {
    public boolean started = false;

    @Override
    public void start() {
        started = true;
    }
}
@Test
void testCarDrives() {
    MockEngine mock = new MockEngine();
    Car car = new Car(mock);

    car.drive();

    assertTrue(mock.started);
}

๐Ÿ’ก ํ•ต์‹ฌ ์š”์•ฝ

๊ตฌ๋ถ„ DIP ์œ„๋ฐ˜ DIP ๋งŒ์กฑ
์˜์กด์„ฑ GasEngine ์ง์ ‘ ์ƒ์„ฑ Engine ์ธํ„ฐํŽ˜์ด์Šค ์ฃผ์ž…
๊ฒฐํ•ฉ๋„ ๋†’์Œ (๋ณ€๊ฒฝ ์–ด๋ ค์›€) ๋‚ฎ์Œ (์œ ์—ฐํ•จ)
ํ…Œ์ŠคํŠธ ์–ด๋ ค์›€ (Mock ๋ถˆ๊ฐ€) ์‰ฌ์›€ (์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋Œ€์ฒด ๊ฐ€๋Šฅ)
ํ™•์žฅ์„ฑ GasEngine๋งŒ ๊ฐ€๋Šฅ ElectricEngine ๋“ฑ ๋‹ค๋ฅธ ์—”์ง„๋„ ๊ฐ€๋Šฅ

๐Ÿ” DIP vs DI ์ฐจ์ด

๊ฐœ๋… ์„ค๋ช…
DIP (์›์น™) ์„ค๊ณ„ ์›์น™. โ€œ์ถ”์ƒํ™”์— ์˜์กดํ•˜๋ผ, ๊ตฌํ˜„์— ์˜์กดํ•˜์ง€ ๋งˆ๋ผ.โ€
DI (๊ธฐ์ˆ ) ๊ตฌํ˜„ ๊ธฐ์ˆ . โ€œ์˜์กด ๊ฐ์ฒด๋ฅผ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…ํ•ด๋ผ.โ€ (์ด ์›์น™์„ ์‹คํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜)

์ฆ‰, DI๋Š” DIP๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ์ˆ˜๋‹จ ์ค‘ ํ•˜๋‚˜๋‹ค.

โœ… DIP(์˜์กด์„ฑ ์—ญ์ „ ์›์น™) ํ•ต์‹ฌ ์ •๋ฆฌ

์ƒ์œ„ ๋ชจ๋“ˆ(์ •์ฑ…)์ด ํ•˜์œ„ ๋ชจ๋“ˆ(๊ตฌํ˜„)์— ์˜์กดํ•˜์ง€ ๋ง๊ณ ,
๋‘˜ ๋‹ค ์ถ”์ƒํ™”(์ธํ„ฐํŽ˜์ด์Šค)์— ์˜์กดํ•˜๋ผ๋Š” ์›์น™.

๐Ÿ“Œ DIP๋ฅผ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด ๊ผญ ์•Œ์•„์•ผ ํ•  ์ถ”๊ฐ€ ๊ฐœ๋…๋“ค

1. ์ƒ์œ„/ํ•˜์œ„ ๋ชจ๋“ˆ์ด ๋ญ๋ƒ?

  • ์ƒ์œ„ ๋ชจ๋“ˆ: ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง, ๊ทœ์น™, ์ •์ฑ… ๋“ฑ (์˜ˆ: OrderService)
  • ํ•˜์œ„ ๋ชจ๋“ˆ: ๊ตฌ์ฒด์ ์ธ ๊ตฌํ˜„ (์˜ˆ: MemoryOrderRepository, JdbcOrderRepository)
// DIP ์œ„๋ฐ˜ (์ƒ์œ„๊ฐ€ ํ•˜์œ„์— ์ง์ ‘ ์˜์กด)
public class OrderService {
    private MemoryOrderRepository repository = new MemoryOrderRepository();
}
// DIP ๋งŒ์กฑ (์ƒ์œ„๋„ ํ•˜์œ„๋„ ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กด)
public class OrderService {
    private final OrderRepository repository;

    public OrderService(OrderRepository repository) {
        this.repository = repository;
    }
}

2. DIP๋ฅผ ์ ์šฉํ•˜๋ฉด ์–ป๋Š” ์‹ค์งˆ์  ์ด์ 

์ด์  ์„ค๋ช…
โœ… ํ™•์žฅ์„ฑ ์ƒˆ๋กœ์šด ๊ตฌํ˜„์ฒด๋ฅผ ์‰ฝ๊ฒŒ ๋ถ™์ผ ์ˆ˜ ์žˆ์Œ (RedisRepo, MongoRepo ๋“ฑ)
โœ… ํ…Œ์ŠคํŠธ ์šฉ์ด FakeRepo, MockRepo๋ฅผ ๋ฐ”๋กœ ์ฃผ์ž…ํ•ด์„œ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ
โœ… ์œ ์ง€๋ณด์ˆ˜ ํŽธ์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๊ตฌํ˜„์ด ๋ถ„๋ฆฌ๋˜์–ด ๋ณ€๊ฒฝ ์˜ํ–ฅ โ†“
โœ… ์˜์กด ๋ฐฉํ–ฅ ๋ช…ํ™• ์ƒ์œ„ ์ •์ฑ…์ด ๊ตฌํ˜„ ์„ธ๋ถ€์‚ฌํ•ญ์— ๋Œ๋ ค๊ฐ€์ง€ ์•Š์Œ

3. DIP๋Š” SRP(๋‹จ์ผ ์ฑ…์ž„ ์›์น™)์™€ ์ง์ด๋‹ค

  • DIP๋Š” ๋‹จ์ผ ์ฑ…์ž„ ์›์น™(SRP)๊ณผ ํ•จ๊ป˜ โ€œ์—ญํ• ๊ณผ ๊ตฌํ˜„์„ ๋ถ„๋ฆฌโ€ํ•˜๋Š” ์„ค๊ณ„๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ค˜์•ผํ•จ.
  • ์˜ˆ: OrderService๋Š” โ€œ์ฃผ๋ฌธ ์ฒ˜๋ฆฌโ€๋ผ๋Š” ์—ญํ• ๋งŒ ์ง‘์ค‘ํ•˜๊ณ , ์ €์žฅ ๋ฐฉ์‹์€ ๊ด€์‹ฌ ์—†์Œ.

4. DIP๋ฅผ ์‹คํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ DI

  • DIP๋Š” ์„ค๊ณ„ ์›์น™์ด๊ณ ,
  • DI(Dependency Injection)๋Š” ๊ทธ๊ฑธ ๊ตฌํ˜„ํ•˜๋Š” ๊ธฐ์ˆ /ํŒจํ„ด
  • Spring์€ DI๋ฅผ ํ†ตํ•ด DIP๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์‹คํ˜„ํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.

๐Ÿ‘‰ DIP๋ฅผ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜๋ ค๋ฉด:

  • ๋‹จ์ˆœํžˆ โ€œ์ธํ„ฐํŽ˜์ด์Šค ์จ์•ผ ํ•œ๋‹คโ€๋Š” ์ˆ˜์ค€์„ ๋„˜์–ด์„œ์„œ,
  • โ€œ์˜์กด ๋ฐฉํ–ฅ์ด ์ถ”์ƒํ™”๋กœ ํ–ฅํ•˜๋„๋ก ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒโ€์ด ํ•ต์‹ฌ์ด๊ณ ,
  • DI๋Š” ๊ทธ๊ฑธ ๋„์™€์ฃผ๋Š” ๊ธฐ์ˆ ์ด๋ผ๋Š” ์ ์„ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.

์นดํ…Œ๊ณ ๋ฆฌ:

์—…๋ฐ์ดํŠธ:

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ