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 | 기본적으로 싱글톤으로 관리되며 상태를 유지하지 않도록 설계 |