이 글은 스파르타 코딩클럽 웹개발 심화반 강의를 듣고 작성하였습니다.
"강한 결합" 이해를 위한 예제
Contoller1 이 Service1 객체를 생성하여 사용
public class Controller1 {
private final Service1 service1;
public Controller1() {
this.service1 = new Service1();
}
}
Service1 이 Repostiroy1 객체를 생성하여 사용
public class Service1 {
private final Repository1 repository1;
public Service1() {
this.repository1 = new Repository1();
}
}
Repostiroy1 객체 선언
public class Repository1 { ... }
만약, 다음과 같이 변경된다면?
-> Repository1 객체 생성 시 DB 접속 id, pw 를 받아서 DB 접속 시 사용, 생성자에 DB 접속 id, pw 를 추가
public class Repository1 {
public Repository1(String id, String pw) {
// DB 연결
Connection connection = DriverManager.getConnection("jdbc:h2:mem:springcoredb", id, pw);
}
}
"강한 결합"의 문제점
Controller 5 개가 각각 Service1 을 생성하여 사용 중
Repository1 생성자 변경에 의해, 모든 Contoller 와 모든 Service 의 코드 변경이 필요
"강한 결합" 해결 방법?
1. 각 객체에 대한 객체 생성은 딱 1번만 생성하기
2. 생성된 객체를 모든 곳에서 재사용
예제
Repository1 클래스 선언 및 객체 생성 → repository1
public class Repository1 { ... }
// 객체 생성
Repository1 repository1 = new Repository1();
Service1 클래스 선언 및 객체 생성 (repostiroy1 사용) → service1
Class Service1 {
private final Repository1 repitory1;
// repository1 객체 사용
public Service1(Repository1 repository1) {
this.repository1 = new Repository1();
this.repository1 = repository1;
}
}
// 객체 생성
Service1 service1 = new Service1(repository1);
Contoller1 선언 ( service1 사용)
Class Controller1 {
private final Service1 service1;
// service1 객체 사용
public Controller1(Service1 service1) {
this.service1 = new Service1();
this.service1 = service1;
}
}
만약, 다음과 같이 변경된다면 :
Repository1 객체 생성 시 DB 접속 id, pw 를 받아서 DB 접속 시 사용 -> 생성자에 DB 접속 id, pw 를 추가
public class Repository1 {
public Repository1(String id, String pw) {
// DB 연결
Connection connection = DriverManager.getConnection("jdbc:h2:mem:springcoredb", id, pw);
}
}
// 객체 생성
String id = "sa";
String pw = "";
Repository1 repository1 = new Repository1(id, pw);
"강한 결합" 개선 결과 ⇒ "느슨한 결합"
⇒ Repository1 생성자 변경은 이제 누구에게도 피해를 주지 않음
⇒ Service1 생성자가 변경되면? 모든 Contoller → Controller 변경 필요 X
결론적으로, 강한 결합 ⇒ 느슨한 결합
DI (의존성 주입)의 이해
"제어의 역전 (IoC: Inversion of Control)"
프로그램의 제어 흐름이 뒤바뀜
일반적: 사용자가 자신이 필요한 객체를 생성해서 사용
IoC (제어의 역전)
- 용도에 맞게 필요한 객체를 그냥 가져다 사용
- "DI (Dependency Injection)" 혹은 한국말로 "의존성 주입"이라고 부릅니다.
- 사용할 객체가 어떻게 만들어졌는지는 알 필요 없음
DI를 사용하기 위해 IoC 컨테이너 사용하기
DI 를 사용하기 위해서는 객체 생성이 우선 되어야 하는데,
어디서 객체 생성을 해야 할까?
-> "스프링 프레임워크"가 필요한 객체를 생성하여 관리하는 역할을 대신해준다.
- 빈 (Bean): 스프링이 관리하는 객체
- 스프링 IoC 컨테이너: "빈" 을 모아둔 통
스프링 "Bean" 등록 방법
@Component
클래스 선언 위에 설정
@Component
public class ProductService { ... }
스프링 서버가 뜰 때 스프링 IoC 에 '빈' 저장 -> @Component 클래스에 대해서 스프링이 해 주는 일
// 1. ProductService 객체 생성
ProductService productService = new ProductService();
// 2. 스프링 IoC 컨테이너에 빈 (productService) 저장
// productService -> 스프링 IoC 컨테이너
'빈' 아이콘 확인 → 스프링 IoC 에서 관리할 '빈' 클래스라는 표시
@Component 적용 조건
@ComponentScan 에 설정해 준 packages 위치와 하위 packages 들
@Configuration
@ComponentScan(basePackages = "com.sparta.springcore")
class BeanConfig { ... }
@SpringBootApplication 에 의해 default 설정이 되어 있음
@Bean
직접 객체를 생성하여 빈으로 등록 요청
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfiguration {
@Bean
public ProductRepository productRepository() {
String dbUrl = "jdbc:h2:mem:springcoredb";
String dbId = "sa";
String dbPassword = "";
return new ProductRepository(dbUrl, dbId, dbPassword);
}
}
스프링 서버가 뜰 때 스프링 IoC 에 '빈' 저장
// 1. @Bean 설정된 함수 호출
ProductRepository productRepository = beanConfiguration.productRepository();
// 2. 스프링 IoC 컨테이너에 빈 (productRepository) 저장
// productRepository -> 스프링 IoC 컨테이너
스프링 "Bean" 사용 방법
@Autowired
멤버변수 선언 위에 @Autowired → 스프링에 의해 DI (의존성 주입) 됨
@Component
public class ProductService {
@Autowired
private ProductRepository productRepository;
// ...
}
'빈' 을 사용할 함수 위에 @Autowired → 스프링에 의해 DI 됨
@Component
public class ProductService {
private final ProductRepository productRepository;
@Autowired
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
// ...
}
@Autowired 적용 조건
스프링 IoC 컨테이너에 의해 관리되는 클래스에서만 가능
@Autowired 생략 조건
- Spring 4.3 버젼 부터 @Autowired 생략가능
- 생성자 선언이 1개 일 때만 생략 가능
파라미터가 다른 생성자들
public class A {
@Autowired // 생략 불가
public A(B b) { ... }
@Autowired // 생략 불가
public A(B b, C c) { ... }
}
Lombok 의 @RequiredArgsConstructor 를 사용하면 다음과 같이 코딩 가능
@RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class ProductController {
private final ProductService productService;
// 생략 가능
// @Autowired
// public ProductController(ProductService productService) {
// this.productService = productService;
// }
}