SPRING

Spring과 객체 지향 설계

도원좀비 2025. 3. 25. 19:18

🌱 Spring과 객체 지향 설계

1️⃣ Spring의 등장 배경

  • OCP, DIP를 직접 구현하려면 설정과 객체 관리가 너무 많아진다.
  • 그래서 Spring이 등장

2️⃣ Spring의 역할

Spring  기능설명
IoC 객체의 제어권을 개발자가 아닌 Spring이 가지도록
DI 필요한 의존성을 자동으로 주입 (의존 관계 관리)
Bean 관리 객체 생성, 주입, 소멸 등 생명주기를 관리

3️⃣Spring Container와 Bean

 Spring Container

  • BeanFactory: 가장 기본적인 컨테이너
  • ApplicationContext: 실무에서 주로 사용하는 확장형 컨테이너

일반적으로 Spring Container = ApplicationContext


 Spring Bean

  • Spring Container가 관리하는 객체
  • 대부분 Singleton으로 생성됨

Bean 등록 방법

 

방법 설명
XML <bean> 태그로 등록
Annotation @Component, @Service, @Repository, @Controller 등
Java Config @Configuration, @Bean 메서드 사용

4️⃣IoC (Inversion of Control, 제어의 역전)

📚 객체의 생성과 관리 권한을 개발자가 아닌 Spring 컨테이너가 담당하는 것을 의미

 

기존에는 개발자가 필요한 객체를 직접 생성하고 관리했지만, Spring에서는 컨테이너가 객체를 대신 생성하고 주입하며 소멸까지 관리

 

💡 IoC의 비유

 

  • 개발자(요리사)는 필요한 객체(재료)를 직접 준비하지 않고, Spring(셰프)이 대신 준비.
  • 객체의 생성과 생명 주기를 Spring이 관리함으로써 결합도가 낮고 유연한 코드를 작성 가능

5️⃣ DI (Dependency Injection, 의존성 주입)

📚 Spring이 객체 간의 의존성을 자동으로 주입해주는 방식입니다. IoC를 구현하는 핵심 수단

 

💡 DI의 비유

 

  • 개발자는 의존 객체를 직접 생성하지 않고, Spring이 알아서 필요한 객체를 주입

 

직접 객체를 생성하는 방식 (결합도 높음)

MyRepository repo = new MyRepositoryImpl();
MyService myService = new MyServiceImpl(repo);
myService.doSomething();
  • 클라이언트 코드가 구체 구현체에 의존
  • 다른 구현체가 생기면 클라이언트 코드도 수정 필요

IoC + DI 적용 (결합도 낮춤)

더보기
@Service
public class MyIocService implements MyService {
    private final MyRepository myRepository;

    @Autowired
    public MyIocService(MyRepository myRepository) {
        this.myRepository = myRepository;
    }

    @Override
    public void doSomething() {
        myRepository.queryDatabase();
    }
}

 

@Repository
public class MyIocRepository implements MyRepository {
    @Override
    public void queryDatabase() {
        System.out.println("IOC 데이터베이스 쿼리 실행");
    }
}

 

@ComponentScan(basePackages = "com.example")
public class MyIocApp {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyIocApp.class);
        MyService service = context.getBean(MyService.class);
        service.doSomething();
    }
}

 

  • 구현체가 바뀌어도 클라이언트 코드는 영향을 받지 않음
  • DI를 통해 코드 유연성과 테스트 용이성 확보

6️⃣ Singleton Pattern

📚 인스턴스를 오직 하나만 생성하고, 공유하도록 설계된 패턴.

💡 왜 필요한가?

  • 웹 애플리케이션에서는 수많은 요청이 동시에 발생
  • 요청마다 객체를 새로 생성하면 메모리 낭비 심각
  • 싱글톤을 사용하면 객체 하나를 공유하여 효율적인 자원 사용 가능
더보기
public class SingletonImpl {
    private static SingletonImpl instance;

    private SingletonImpl() {}

    public static SingletonImpl getInstance() {
        if (instance == null) {
            instance = new SingletonImpl();
        }
        return instance;
    }

    public void showMessage() {
        System.out.println("싱글톤 인스턴스 사용: " + this);
    }
}

 

외부에서 객체 생성을 막고, getInstance()로 하나의 인스턴스를 공유

 

싱글톤의 문제점

 

  • DIP 위반: 인터페이스가 아닌 구체 구현에 의존하게 됨
  • 유연성 부족: 구현 변경 시 클라이언트 코드에 영향
  • 그래서 Spring이 직접 관리

Spring의 싱글톤 컨테이너

  • Spring에서 등록되는 대부분의 Bean은 기본적으로 싱글톤
  • 별도 설정 없이도 효율적인 자원 관리 가능
  • 개발자가 직접 싱글톤 코드를 구현할 필요 없음

싱글톤의 주의점: 상태 유지 금지

  • 공유 객체인 싱글톤에서 상태(state)를 유지하면 안 됨
  • 상태가 공유되면 데이터 불일치, 동시성 문제 발생
  • Spring Bean은 항상 무상태(stateless) 로 설계해야 함
더보기
public class StatefulSingleton {
    private int value;
    // ... getInstance, setValue, getValue 등 구현
}
client1.setValue(42);
client2.setValue(100);
System.out.println(client1.getValue()); // 100 (❌ 예상: 42)

 


요약

개념 설명
IoC 객체의 생성과 제어권을 Spring 컨테이너에게 위임
DI 객체 간 의존성을 Spring이 주입해주는 방식
Singleton 객체 인스턴스를 하나만 생성하여 공유
Spring Bean 기본적으로 싱글톤으로 관리되며 상태를 유지하지 않도록 설계