일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 백준
- 거품정렬
- 연결리스트
- 트라이
- 스터디
- 이진트리탐색
- 우선순위 큐
- 코테준비
- 분할정복
- 큐
- 프로그래머스
- Timsort
- 힙
- 선택정렬
- 팀정렬
- 삽입정렬
- 자료구조
- MSA
- 코딩테스트
- 코테
- collections.sort
- LinkedList
- stack
- 퀵정렬
- 스택
- 파싱
- heap
- 해시함수
- 15552번
- divide and conquer
- Today
- Total
Little bIT awesome
[스프링] DI & IoC 본문
IoC (Inversion of Control)
일반적으로 프로그램의 흐름은 각 오브젝트가 능동적으로 자신이 사용할 클래스를 결정하고, 언제 어떻게 그 오브젝트를 만들지를 결정한다.
제어의 역전은 이런 제어 흐름의 개념을 거꾸로 뒤집는 것.
제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하거나 생성하지 않는다. 또한, 자신도 어디서 어떻게 사용되는 지 알 수 없다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임하기 때문
프레임워크는 제어의 역전 개념이 적용된 대표적인 기술
프레임워크를 사용하면 객체의 생명 주기를 모두 프레임워크에 위임할 수 있다. 즉, 외부 라이브러리가 애플리케이션 코드를 호출하고, 흐름을 제어한다.
라이브러리를 사용하는 애플리케이션 코드는 애플리케이션의 흐름을 직접 제어한다. 단지 동작하는 중에 필요한 기능이 있을 때 능동적으로 라이브러리를 사용할 뿐이다.
반면, 프레임워크는 애플리케이션 코드가 프레임워크에 의해 사용된다. 보통 프레임워크 위에 개발한 클래스를 등록해두고, 프레임워크가 흐름을 주도하는 중에 개발자가 만든 애플리케이션 코드를 사용하도록 만드는 방식
IoC 컨테이너 (IoC 프레임워크)
애플리케이션 컴포넌트의 생성과 관계설정, 사용, 생명주기 관리 등을 관장한다.
스프링은 애플리케이션 전반에 걸쳐 IoC를 적용 및 관리해주는 IoC 프레임워크에 해당한다.
객체 관리 주체가 프레임워크가 되기 때문에 개발자는 로직에 집중할 수 있게된다.
의존성 주입 (Dependency Injection)
외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴
인터페이스를 사이에 둬서 클래스 레벨에서는 의존관계가 고정되지 않도록하고 런타임시에 관계를 동적으로 주입
유연성을 확보하고 결합도를 낮출 수 있게 해준다.
의존성
B 객체가 변하면 A에 영향을 미친다 : "의존한다"
대표적으로 A가 B를 사용하는 경우, A에서 B에 정의된 메소드를 호출하는 경우
public class Store {
private Pencil pencil;
}
Store 객체가 Pencil 객체를 사용하고 있다 → Store 객체가 Pencil 객체를 의존
주입
내부가 아니라 외부에서 객체를 생성해서 넣어주는 것
의존성 주입
즉 의존성 주입은 내부에서 사용할 객체를 외부에서 생성해서 넣어주는 것
의존성 주입은 다음의 "세 가지 조건"을 충족해야 한다.
1. 클래스 모델이나 코드에는 런타임 시점의 의존관계가 들어나지 않아야 한다. 따라서 인터페이스에만 의존하고 있어야 한다.
2. 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제3의 존재가 결정한다.
3. 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입) 해줌으로써 만들어진다.
DI 가 필요한 이유
public class Store {
private Pencil pencil;
public Store() {
this.pencil = new Pencil();
}
}
위의 클래스는 크게 다음의 문제를 가진다.
1. 두 클래스가 강하게 결합되어 있다.
Store에서 Pencil이 아닌 다른 상품을 판매하고자 하면 Store를 사용하는 곳이 아니라 Store 구현 클래스 생성자에 변경이 필요하다.(OCP 위반)
이에 대해 상속을 사용하여 다른 상품을 파는 Store를 파생시키는 방식을 떠올릴 수 있지만 상속은 제약이 많고 확장성이 떨어지므로 피하는 것이 좋다.
※ 상속 단점
- 다중 상속의 제한 : 재사용성 감소, 설계의 유연성 감소
- 구현 상속의 문제 (부모 클래스의 메서드와 속성을 자식 클래스가 그대로 물려받을 때)
1. 캡슐화 깨짐 : 내부 구현을 자식 클래스에 드러낸다 (ex 오버라이드할 때, 부모의 내부 동작을 알아야 한다. )
2. 변경에 취약 : 부모 클래스 변경 시 자식 클래스도 변경해야 한다. (클래스 간 강한 결합)
2. 객체들 간의 관계가 아니라 클래스 간의 관계가 맺어진다.
즉, Store 클래스가 특정 클래스인 Pencil의 구체적인 구현에 의존하기 때문에 Pencil에 변경이 생기면 Store 클래스를 수정해야 할 수 있다. 이러한 강한 결합은 유지보수성과 확장성을 저해한다.
DI를 통한 문제 해결
Pencil, Food 등 여러가지 제품을 하나로 표현하기 위해서 Product 인터페이스가 필요하다.
public interface Product { ... }
public class Pencil implements Product { ... }
public class Food implements Product { ... }
Store에서 구현 클래스를 의존하던 부분을 제거하고 인터페이스를 통해 외부에서 주입 받는 방식으로 수정한다.
이 때 다형성을 활용한다.
다형성 : Product 인터페이스를 사용함으로써 구현 클래스들을 동일한 방식으로 동작시킬 수 있다. 코드의 유연성과 확장성을 높여준다.
public class Store {
private Product product;
public Store(Product product) {
this.product = product;
}
}
Store 객체를 사용하는 Client 측에서 구현 객체를 생성하고 주입해준다.
이로써 특정 구현 클래스에 의존하지 않게 되어 클래스 내부의 변경 없이 다양한 객체를 주입받을 수 있게된다.
public class StoreClient {
public void store() {
Product pencil = new Pencil();
Stroe store = new Store(pencil);
}
}
스프링 IoC 컨테이너
스프링 컨테이너가 관리하는 객체를 빈(Bean)이라고 한다.
이 빈들을 관리한다는 의미로 컨테이너를 빈 팩토리(Bean Factory)라고 부른다.
주로 부가 기능을 가진 ApplicationContext를 사용
- BeanFactory
- 객체를 생성하고 DI를 처리하는 기능을 담당
- Bean을 등록, 생성, 조회, 반환 (팩토리 디자인 패턴을 구현)
- ApplicationContext
- BeanFactory를 상속 → BeanFactory의 기능 + 부가기능(국제화, 이벤트 처리, AOP)
빈 설정 방법
1. 어노테이션 기반 설정
Spring 2.5부터 도입
코드가 간결해지고 가독성이 높아진다는 장점이 있다.
클래스에 @Component 어노테이션을 붙이면 스프링이 해당 클래스를 모두 스캔하여 스프링 빈으로 등록한다.
@SpringBootApplication에 포함되어 있는 @ComponentScan을 통해 컴포넌트를 자동 스캔한다
(따라서, @SpringBootApplication 하위에 있는 패키지만 컴포넌트 스캔의 대상이 된다.)
@Service, @Controller, @Repository은 모두 @Component를 포함한다. → 어플리케이션 실행 시 모두 빈으로 등록됨
@Component
public class MyComponent { ... }
@Service
public class MyService {
// 서비스 계층의 클래스
}
@Repository
public class MyRepository {
// 데이터 접근 계층의 클래스
}
@RestController
public class MyController {
// 웹 계층의 클래스
}
2. Java Config
스프링이 실행되면 자동으로 @Comfiguration이 붙은 클래스를 찾아서 구성 정보로 사용한다.
이 클래스에서 빈을 등록하고 DI 처리를 할 수 있다.
@Configuration
public class AppConfig {
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
구성 정보에서 빈 객체로 등록하고 싶은 메서드에 @Bean 어노테이션을 추가하여 스프링 컨테이너에 직접 빈으로 등록한다.
스프링 컨테이너에는 <key(빈 이름), value(빈 객체)> 쌍으로 등록되는데 빈 이름에는 메서드의 이름, 빈 객체에는 실제 반환하는 객체를 담아 저장한다.
의존성 주입 방법
1. 생성자 주입
생성자에 @Autowired 를 붙이면 스프링 컨테이너에 등록된 빈에서 찾아서 주입해준다.
@Service
public class CoffeeService {
private final MemberRepository memberRepository;
private final CoffeeRepository coffeeRepository;
@Autowired
public CoffeeService(MemberRepository memberRepository, CoffeeRepository coffeeRepository) {
this.memberRepository = memberRepository;
this.coffeeRepository = coffeeRepository;
}
}
- 생성자 호출 시점에 한 번만 호출되는 것을 보장, 런타입에 의존관계가 변경되는 것을 방지함.
따라서, 객체의 불변성을 보장하고, 필요한 모든 의존성이 주입되었는지 컴파일 시점에 확인할 수 있다.
- NPE를 방지할 수 있다. (의존성이 누락되면 객체 생성 시점에 오류가 발생하므로)
- 순환 참조를 감지할 수 있다.
- 생성자가 한 개만 존재하는 경우 @Autowired 생략 가능
- 주입받을 필드를 final로 선언 가능 (불변성, 스레드 안전성, 실수 방지(컴파일 시점에 확인 가능하기 때문))
- 테스트가 용이하다. 단위 테스트 시 Mock 객체 등을 사용하여 의존성을 쉽게 주입할 수 있다.
- 코드 가독성 향상 : 의존관계를 명확히 표현할 수 있다.
2. 수정자 주입 (Setter)
@Autowired가 있는 수정자들을 자동으로 의존관계를 주입한다.
@Service
public class CoffeeService {
private MemberRepository memberRepository;
private CoffeeRepository coffeeRepository;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setCoffeeRepository(CoffeeRepository coffeeRepository) {
this.coffeeRepository = coffeeRepository;
}
}
- 선택과 변경 가능성이 있는 의존 관계에 사용
- 의존성이 변경될 수 있으므로 불변성을 보장할 수 없다.
- 자바 빈 프로퍼티 규약의 수정자 메소드 방식을 사용
- set필드명 메서드를 생성하여 의존 관계를 주입
- @Autowired 필수 입력(입력하지 않으면 실행되지 않는다.
3. 필드 주입
필드에 @Autowired를 붙여서 바로 주입하는 방식
@Service
CoffeeService {
@Autowired
private final MemberRepository memberRepository;
@Autowired
private final CoffeeRepository coffeeRepository;
}
- 코드가 간결해진다.
- DI 컨테이너 안에서만 작동되므로 순수 자바 코드로 테스트하기 어렵다. (POJO 원칙 위반)
- 애플리케이션의 실제 코드와 상관없는 특정 테스트를 하고 싶을 때 사용
- 의존성을 수동 주입 및 변경하려면 결국 setter가 필요하다.
- 만약 의존관계를 필수적으로 넣고 싶지 않으면 @Autowired(required=false) 옵션 처리를 통해 필수가 아님을 명시할 수 있다. (NPE 발생 가능)
- 가독성 하락 : 의존관계가 외부로 드러나지 않아 객체의 책임을 파악하기 어렵다.
4. 일반 메서드 주입
일반 메서드를 통해서 의존관계를 주입
@Service
public class CoffeeService {
private MemberRepository memberRepository;
private CoffeeRepository coffeeRepository;
@Autowired
public void method(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
- 여러 필드를 한번에 받을 수 있다는 장점이 있지만 생성자 주입도 동일한 장점을 가짐 (잘 사용하지 않음)
Spring IOC Annotation 종류
- @Component 컴포넌트 계열 어노테이션 : 스프링 컨테이너에 해당 클래스의 객체를 생성하도록 명시
- @Repository : 영속성 계층에서 사용
- @Service : 비즈니스(서비스) 계층에서 사용
- @Controller : 프레젠테이션 계층에서 사용
- @Component : 모든 컴포넌트에 대한 제너릭 스테레오 타입
- 위 세가지 경우에 해당하지 않는 기타 자원 클래스에 사용 - @DI(Dependency Injection) 계열 어노테이션 : 스프링 컨테이너로부터 의존성(의존대상객체)를 주입받고자 할 때 사용
- @AutoWired : 의존 대상 객체를 타입으로 검색해 주입 - 주로 많이 사용!
(단, 동일한 인터페이스의 구현 객체가 여러 개 있을 경우 Exception 발생
- 이를 해소하기 위해 @Quelifier("bean name")을 사용해 특정 빈을 지정하거나,
@Primary를 사용해 기본으로 사용할 빈을 지정해야 함 )
- @Resouce : 의존 대상 객체를 타입으로 검색 + name을 통해 주입
(@Resource(name="beanId")를 명시하면 bean id로 검색해 주입) - @Value: 외부 프로퍼티 파일이나 환경변수를 통해 값을 주입받는 어노테이션
- 값 주입 : 문자열, 숫자, 불리언 등의 값을 클래스의 필드에 주입 가능
- 프로퍼티 파일 지원 : Spring의 application.properties 또는 application.yml 파일에 정의된 프로퍼티 값을 주입
- Spring Expression Language (SpEL)를 사용하여 동적으로 값을 계산하거나 조합 가능
빈 스코프
빈 스코프란 빈이 존재할 수 있는 범위를 의미
스프링은 다음과 같은 스코프들을 제공한다.
- 싱글톤 : 디폴트 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프. 싱글톤 스코프의 빈을 조회하면, 스프링 컨테이너는 항상 같은 인스턴스를 반환한다.
- 프로토타입 : 스프링 컨테이너는 프로토타입 빈의 생성과 의존 관계 주입까지만 관여하고, 더는 관리하지 않는 매우 짧은 범위의 스코프. 프로토타입 스코프의 빈을 조회하면, 스프링 컨테이너는 항상 새로운 인스턴스를 생성하여 반환한다.
- 웹 관련 스코프 : 웹 환경에서만 동작. 스프링 컨테이너가 해당 스코프의 종료 시점까지 관리
- request : 웹 요청이 들어오고 나갈 때까지 유지되는 스코프
- session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
- application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프
싱글톤과 오브젝트의 상태
멀티 스레드 환경에서 싱글톤 오브젝트에 여러 스레드가 동시 접근하여 사용할 수 있다. 따라서, 싱글톤은 항상 무상태 방식으로 만들어져야 한다. 이때, 요청에 대한 정보나, DB나 서버의 리소스로부터 생성한 정보는 파라미터, 로컬 변수, 리턴 값 등을 이용한다. 이들은 독립적 공간에 저장되기 때문에 싱글톤이라 해도 여러 스레드가 변수 값에 접근할 수 없다.
https://mangkyu.tistory.com/150
[Spring] 의존성 주입(Dependency Injection, DI)이란? 및 Spring이 의존성 주입을 지원하는 이유
1. 의존성 주입(Dependency Injection)의 개념과 필요성 [ 의존성 주입(Dependency Injection) 이란? ] Spring 프레임워크는 3가지 핵심 프로그래밍 모델을 지원하고 있는데, 그 중 하나가 의존성 주입(Dependency Inj
mangkyu.tistory.com
https://hudi.blog/inversion-of-control/
제어의 역전 (Inversion Of Control, IoC)
학습 동기 우아한테크코스 레벨2에서 스프링을 본격적으로 사용하면서, 스프링에서 제공하는 의존성 주입과 스프링 빈(Bean)을 접하게 되었다. 이 개념들에 익숙해지기 전 밀접하게 관련이 있어
hudi.blog
https://dev-coco.tistory.com/80
[Spring] IoC 컨테이너 (Inversion of Control) 란?
IoC (Inversion of Control)? IoC를 네이버 영어사전에서 번역해보면 제어 반전을 뜻하고 있습니다. IoC(제어 반전)이란, 객체의 생성, 생명주기의 관리까지 모든 객체에 대한 제어권이 바뀌었다는 것을
dev-coco.tistory.com
https://creamilk88.tistory.com/161
[Spring] 스프링 IOC/DI Annotation 어노테이션
[ annoation이 나온 배경 ] 메타 데이터 (meta-data) : 데이터의 데이터 ex) 택배 상자위에 붙어 있는 택배 안 내용물에 대한 상품 정보를 생각하면 된다! 메타 데이터는 설정정보의 역할을 하고 이는 xml
creamilk88.tistory.com
https://ittrue.tistory.com/227#google_vignette
[Spring] 스프링 의존성 주입(DI : Dependency Injection) 4가지 방법 (의존 관계 자동 주입)
의존성 주입(DI : Dependency Injection) 방법 의존성 주입에는 4가지 방법이 존재한다. 생성자 주입 수정자 주입 (setter 주입) 필드 주입 일반 메서드 주입 스프링 공식 문서에서는 생성자 주입을 권장하
ittrue.tistory.com
'백엔드 > Spring' 카테고리의 다른 글
given - when - then pattern (0) | 2024.01.03 |
---|---|
Mock 객체 (0) | 2024.01.03 |
스프링 입문 강의 완강! (0) | 2023.07.27 |
[스프링 핵심 원리] 섹션 3 객체 지향 원리 적용 (0) | 2023.02.28 |
[스프링 핵심 원리] 섹션 2 예제 만들기 (0) | 2023.02.28 |