삶의 공유

[Spring] Spring DI(Dependency Injection) 원리-1 본문

Web Dev/BackEnd

[Spring] Spring DI(Dependency Injection) 원리-1

dkrehd 2025. 4. 17. 23:25
728x90
반응형

Java Spring DI 원리 이해하기: 다형성과 객체 생성 방식 비교

Spring의 핵심 기능 중 하나인 DI(Dependency Injection, 의존성 주입)는 객체 간의 의존 관계를 외부에서 주입해줌으로써 코드의 유연성과 재사용성을 높여줍니다. 이번 글에서는 다음과 같은 간단한 Java 코드 예제를 통해 DI의 개념과 다형성을 활용한 객체 생성 방식의 차이를 설명합니다.


✅ 예제 코드: 객체 생성 방식 비교

class Car {}
class SportCar extends Car {}
class Truck extends Car {}

class Engine {}
class Door {}

public class Main {
    public static void main(String[] args) throws Exception {
        Car car = (Car)getObject("car"); // 다형성 활용
        Engine engine = (Engine)getObject("engine");
        System.out.println("engine = " + engine);
        System.out.println("car = " + car);
    }

    // 하드코딩 방식의 객체 생성 로직을 별도 함수로 분리
    static Object getObjectHardcoded(String key) {
        if(key.equals("car"))
            return new SportCar();
        else if (key.equals("engine"))
            return new Engine();
        else if (key.equals("door"))
            return new Door();
        else
            throw new IllegalArgumentException("Unknown key: " + key);
    }

    // 설정 파일을 이용한 동적 객체 생성 방식
    static Object getObject(String key) throws Exception {
        Properties prop = new Properties();
        Class clazz = null;
        prop.load(new FileReader("config.txt"));
        String className = prop.getProperty(key);
        clazz = Class.forName(className);
        return clazz.newInstance();
    }

    static Car getCar() {
        return new SportCar();
    }
}

📄 config.txt 예시

car=com.test.sbtch3.di1.Truck
engine=com.test.sbtch3.di1.Engine
door=com.test.sbtch3.di1.Door

🔍 각 방식의 차이점 및 장단점

1️⃣ 하드코딩 방식 (getObjectHardcoded() 방식)

if(key.equals("car")) return new SportCar();

✅ 장점

  • 구현이 간단하고 직관적이다.
  • 디버깅이 쉽다.

❌ 단점

  • 객체 생성 로직이 코드에 박혀 있어 확장성이 낮다.
  • 새로운 클래스 추가 시 소스코드를 수정해야 함 → OCP(Open-Closed Principle) 위반

2️⃣ 메서드 리턴 방식 (getCar())

static Car getCar() {
    return new SportCar();
}

✅ 장점

  • 메서드 단위로 객체 반환 로직을 캡슐화할 수 있음
  • 간단한 DI 구조 흉내 가능

❌ 단점

  • 객체 변경 시 메서드를 다시 수정해야 함 (유연성 부족)
  • 여전히 클래스명을 코드 내에서 고정적으로 사용 → 런타임 수정 어려움

3️⃣ Properties + Class.forName() 방식 (getObject() 방식)

String className = prop.getProperty(key);
Class clazz = Class.forName(className);
return clazz.newInstance();

✅ 장점

  • 동적 로딩을 통해 컴파일 시점이 아닌 런타임에 클래스 결정 가능
  • 외부 설정 파일(config.txt)만 수정하면 코드 변경 없이 객체 주입 가능
  • Spring DI가 내부적으로 사용하는 방식과 매우 유사

❌ 단점

  • Reflection 기반이기 때문에 성능이 약간 저하될 수 있음
  • 클래스명이 오타이거나 해당 클래스가 존재하지 않을 경우 예외 발생
  • 타입 안정성이 떨어짐 → 형변환 필요

💡 다형성과 DI의 관계

현재 예제에서 핵심은 다음 라인입니다:

Car car = (Car)getObject("car");
  • 이처럼 상위 타입(Car) 으로 받아서 하위 객체(SportCar 또는 Truck) 를 주입받을 수 있는 것이 다형성의 핵심입니다.
  • 이 덕분에 객체 생성 로직을 변경하더라도 사용하는 쪽(Main)은 그대로 유지될 수 있습니다.

Spring은 이와 유사하게 XML, Java Config, Annotation 등을 통해 객체를 등록하고, 이름이나 타입으로 주입합니다.


🔚 결론

방식유연성유지보수다형성주입 주체

하드코딩 방식 낮음 코드 수정 필요 제한적 직접 생성
메서드 리턴 방식 보통 메서드 수정 필요 사용 가능 직접 생성
Properties + Class.forName 높음 설정 파일로 관리 완전 활용 외부 설정

Spring의 DI는 바로 이 동적 객체 생성 원리를 활용해, 개발자가 객체 생성에 신경 쓰지 않고도 협업, 테스트, 유지보수에 유리한 구조를 제공합니다.
이 글을 통해 DI의 원리와 다형성이 실제로 어떻게 코드에 녹아드는지 감을 잡으셨기를 바랍니다 😄

반응형