※ 수준별반 학습이랑 숙련강의 주차에 나왔던 내용이 겹치기 때문에 DI / IOC는 스탠다드반 기준으로 작성.
Spring 숙련
✅ 객체 지향 설계
1️⃣ SOLID 원칙
객체 지향 설계의 5가지 기본 원칙, S/W 설계 에서 유지보수성, 확장성, 유연성을 높이기 위한 지침을 제공함.
- 단일 책임의 원칙 SRP
- 하나의 클래스는 하나의 책임만 가져야 한다.
- 클래스는 1가지 기능에 집중 해야함. 그 외 기능 담당 ❌
- 개방 폐쇄 원칙 OCP
- S/W 요소는 확장에는 열리고, 수정에는 닫혀야 있어야 한다.
- 새로운 기능을 추가 할때 기존 코드를 수정하지 않고, 확장하도록 설계
- 다형성을 활용하여 해결
- 문제점
- 객체의 생성, 사용 등을 자동으로 설정해주는 무엇인가 필요함.
- Spring 컨테이너 역할
- 객체의 생성, 사용 등을 자동으로 설정해주는 무엇인가 필요함.
- S/W 요소는 확장에는 열리고, 수정에는 닫혀야 있어야 한다.
- 리스코프 치환 원칙 LSP
- 자식 클래스는 언제나 부모 클래스를 대체 할수 있어야 한다.
- 부모 클래스를 사용하는 곳에 자식 클래스를 사용해도 프로그램 동작에 문제가 없어야 함.
- 자식 클래스는 언제나 부모 클래스를 대체 할수 있어야 한다.
- 인터페이스 분리 원칙 ISP
- 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
- 하나의 큰 인터페이스 보다 여러 개의 작은 인터페이스로 분리해야 한다.
- 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
- 의존관계 역전 원칙 DIP
- 구체적인 클래스에 의존 ❌, 인터페이스나 추상 클래스에 의존 하도록 설계
▶ Spring이 다형성 만으로 해결하지 못한 문제점을 OCP, DIP 원칙을 DI / IOC로 해결해줌.
✅ Spring Container와 Bean
1️⃣ Spring Container
보통 Java에서 new 키워드를 이용하여 객체 생성하였다.
하지만 이제부터 Spring Container 에서 객체를 생성 및 관리하고 의존성을 주입하는 역할을 담당 한다.

객체를 직접 생성 하는 경우, 객체간의 의존성 및 결합도가 높아진다 ▶ OCP, DIP 위반
Spring 컨테이너를 사용하면 인터페이스에만 의존하는 설계가 가능해진다 ▶ OCP, DIP 준수
Container 종류

2️⃣ Spring Bean
앞서 Spring 컨테이너가 관리하는 객체를 의미, Spring이 이 객체를 관리한 순간부터 Bean이 되며, Spring이 Bean을 생성, 초기화, 의존성 주입을 통해 관리함.

- Bean은 new 키워드 대신 사용하는 것이다.
- Spring Container가 제어한다.
- Spring Bean의 특징
- Spring 컨테이너에 의해 생성되고 관리된다.
- 기본적으로 Singleton으로 설정된다.
- 의존성 주입(DI)을 통해 다른 객체들과 의존 관계를 맺을 수 있다.
- 생성, 초기화, 사용, 소멸의 생명주기를 가진다.
✅ Singleton(싱글톤) 패턴
클래스의 인스턴스가 오직 하나만 생성되도록 보장하는 디자인 패턴이다.
등장 배경

- 요청을 할 때 마다 객체를 새로 생성되고 처리가 완료되면 소멸된다.
- 메모리 낭비가 아주 심하다.

- 객체가 한번만 생성되어 리소스를 절약할 수 있다.
✔ Spring이 Bean을 등록하는 방법은 기본적으로 싱글톤 이다. 하지만, 요청할 때 마다 새로운 객체를 생성해서 반환하는 기능도 제공한다.
⚠ 싱글톤 패턴 주의점
객체의 인스턴스를 하나만 생성하여 공유하는 싱글톤 패턴의 객체는 상태를 유지(stateful)하면 안된다.
- 상태 유지(stateful)의 문제점
- 데이터의 불일치나 동시성 문제가 발생할 수 있다.
public class StatefulSingleton {
private static StatefulSingleton instance;
// 상태를 나타내는 필드
private int value;
// private 생성자
private StatefulSingleton() {}
// 싱글톤 인스턴스를 반환하는 메서드
public static StatefulSingleton getInstance() {
if (instance == null) {
instance = new StatefulSingleton();
}
return instance;
}
// 상태 변경 메서드
public void setValue(int value) {
this.value = value;
}
// 상태를 반환하는 메서드
public int getValue() {
return this.value;
}
}
// ====================================================================================================================================
public class MainApp {
public static void main(String[] args) {
// 클라이언트 1: 싱글톤 인스턴스를 가져와서 상태를 설정
StatefulSingleton client1 = StatefulSingleton.getInstance();
client1.setValue(42);
System.out.println("클라이언트 1이 설정한 값: " + client1.getValue());
// 클라이언트 2: 동일한 싱글톤 인스턴스를 사용해 상태를 변경
StatefulSingleton client2 = StatefulSingleton.getInstance();
client2.setValue(100);
System.out.println("클라이언트 2가 설정한 값: " + client2.getValue());
// 클라이언트 1이 다시 값을 확인
System.out.println("클라이언트 1이 다시 확인한 값: " + client1.getValue());
}
}
클라이언트 1이 설정한 값: 42
클라이언트 2가 설정한 값: 100
클라이언트 1이 다시 확인한 값: 100
- value 필드는 공유되는 필드인데, 특정 클라이언트가 값을 변경한다.
- ⭐Spring Bean은 항상 무상태(stateless)로 설계를 해야한다. 아주 중요! ⭐
- 특정 클라이언트에 의존적인 필드가 있거나 변경할 수 있으면 안된다.
스탠다드반 ⭐ DI / IOC ⭐
1️⃣ IOC (제어의 역전, Inversion of Control)
제어의 역전. 객체의 생성과 관리 권한을 개발자가 아닌 Spring 컨테이너가 담당.
- 기존 : 개발자가 new 키워드를 사용해 객체 생성 및 연결
- IoC: Spring 컨테이너가 자동으로 객체 생성 및 연결
스프링부트의 주요 특징중 하나이며, 현 개발자 들이 개발하는데에 있어서 편리함을 제공한다.
스프링 프레임워크가 지향하는 핵심적인 아키텍처 중 하나는 느슨한 결합도 이다.
▶ 컴포넌트가 강하게 연결 되어 있으면, 변경이 어렵고 테스트 하기 힘들다. 그러므로 느슨한 결합은 유연하고 확장 가흔한 애플리케이션을 가능하게 할 수 있다. → 인터페이스 를 활용

2️⃣ DI ( Dependency Injection )
객체 간의 의존 관계를 외부에서 주입해주는 방식 DI는 IOC의 일부분

- 종류:
- 생성자 주입 (Constructor Injection) ⭐
- 세터 주입 (Setter Injection)
- 필드 주입 (Field Injection)
- 장점:
- 코드 간 결합도 감소
- 테스트 용이성 향상
- 유지보수 용이
주로 개발자들은 생성자 주입을 일반적으로 쓰는 편이다.
📌 Spring에서의 DI 구현
- @Component : 빈 등록
- @Autowired : 의존성 주입 (DI) ( 생성자 주입시, 생성자가 하나인경우 생략 가능 ! ! )
- Spring이 Engine 객체를 생성해서 Car에 주입
✅ 생성자 주입 (Constructor Injection)
@Component
public class Car {
private final Engine engine;
@Autowired
public Car(Engine engine) {
this.engine = engine;
}
public void drive() {
engine.start();
}
}
장점:
- 객체 생성 시점에 의존성 주입이 보장되어, 불변 객체 구성에 유리함
- 테스트 코드 작성 시 명시적인 생성자를 통해 의존성 주입이 가능함
- 필수 의존성을 강제할 수 있어 컴파일 타임 오류를 방지함
- 스프링 프레임워크 팀과 대부분의 커뮤니티에서 가장 권장하는 방식
단점:
- 의존성이 많아질 경우 생성자 파라미터가 길어짐
- 순환 참조 발생 시 복잡도 증가
✅ 필드 주입 (Field Injection) - 비추천 😒
@Component
public class Engine {
public String start() {
return "Engine started!";
}
}
@Component
public class Car {
private final Engine engine;
@Autowired
public Car(Engine engine) {
this.engine = engine;
}
public void drive() {
System.out.println(engine.start() + " Driving...");
}
}
장점:
- 코드가 가장 간결하고 직관적임
단점:
- 테스트 시 Mock 주입이 어려움 (리플렉션 필요)
- 순수 자바 환경에서 사용하기 어려움 (스프링 프레임워크에 강한 의존성)
- 의존 관계가 외부에서 보이지 않아 명확성이 떨어짐
📌 ApplicationContext와 Bean
- ApplicationContext: Spring 컨테이너, 객체를 생성하고 관리함
- Bean: Spring이 관리하는 객체
- 빈 등록 방법:
- @Component, @Service, @Repository, @Controller ( 자동 )
- @Configuration + @Bean 메서드 ( 수동적인 방법 )
'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글
| Spring ( Session, Token, JWT ) (0) | 2025.03.30 |
|---|---|
| TIL 28 [ Spring ( Bean Validation, Cookie ) ] (0) | 2025.03.28 |
| TIL 26 [ Spring 일정관리 앱 과제 해설 + 학습법 & 동기부여 세션 ] (0) | 2025.03.26 |
| TIL 25 [ Spring 일정 관리 앱 과제 ( 필수 . . . JDBC..??) + 미니세션 ( API, ERD ) ] (0) | 2025.03.24 |
| TIL 24 [ Spring ( 3 Layer 아키텍처, DB, SQL, JDBC, SQL Mapper ) ] (0) | 2025.03.21 |