A가 B를 참조하고 있을때,
B의 기능이 추가 또는 변경되거나 형식이 바뀌어서 그 영향이 A에게 미친다면 강한 결합
현재 아래의 코드들은 강한 결합으로 묶여있음
@RequiredArgsConstructor
@RestController
public class ProductController {
private final ProductService productService;
public ProductController() {
this.productService = new ProductService();
}
public Product createProduct(@RequestBody ProductRequestDto requestDto) throws SQLException {
Product product = productService.createProduct(requestDto);
return product;
}
}
public class ProductService {
private final ProductTempRepository productRepository;
public ProductService() {
this.productRepository = new ProductTempRepository();
}
public Product createProduct(ProductRequestDto requestDto) throws SQLException {
Product product = new Product(requestDto);
productRepository.createProduct(product);
return product;
}
}
public class ProductTempRepository {
public void createProduct(Product product) throws SQLException {
Connection connection = DriverManager.getConnection("jdbc:h2:mem:testdb", "haden", "haden");
PreparedStatement ps = connection.prepareStatement("select max(id) as id from product");
ResultSet rs = ps.executeQuery();
if(rs.next()){
product.setId(rs.getLong("id") + 1);
}else{
throw new SQLException("error");
}
ps = connection.prepareStatement("insert into product(id, title, image, link, lprice, myprice) values(?, ?, ?, ?, ?, ?)");
ps.setLong(1, product.getId());
ps.setString(2, product.getTitle());
ps.setString(3, product.getImage());
ps.setString(4, product.getLink());
ps.setInt(5, product.getPrice());
ps.executeUpdate();
ps.close();
connection.close();
}
}
아래와 같이 ProductTempRepository가
dbUrl, dbId, dbPassword를 생성자로 받는 것으로 변경이된다고 가정해보자
public class ProductTempRepository {
private final String dbUrl;
private final String dbId;
private final String dbPassword;
public ProductTempRepository(String dbUrl, String dbId, String dbPassword) {
this.dbUrl = dbUrl;
this.dbId = dbId;
this.dbPassword = dbPassword;
}
public void createProduct(Product product) throws SQLException {
Connection connection = DriverManager.getConnection(dbUrl, dbId, dbPassword);
PreparedStatement ps = connection.prepareStatement("select max(id) as id from product");
ResultSet rs = ps.executeQuery();
if(rs.next()){
product.setId(rs.getLong("id") + 1);
}else{
throw new SQLException("error");
}
ps = connection.prepareStatement("insert into product(id, title, image, link, lprice, myprice) values(?, ?, ?, ?, ?, ?)");
ps.setLong(1, product.getId());
ps.setString(2, product.getTitle());
ps.setString(3, product.getImage());
ps.setString(4, product.getLink());
ps.setInt(5, product.getPrice());
ps.executeUpdate();
ps.close();
connection.close();
}
}
ProductTempRepository 가 변경된다면, 아래와 같이
ProductService, ProductController를 모두 수정해야할것
public class ProductService {
private final ProductTempRepository productRepository;
private final String dbUrl;
private final String dbId;
private final String dbPassword;
public ProductService(String dbUrl, String dbId, String dbPassword) {
this.dbUrl = dbUrl;
this.dbId = dbId;
this.dbPassword = dbPassword;
this.productRepository = new ProductTempRepository(this.dbUrl, this.dbId, this.dbPassword);
}
public Product createProduct(ProductRequestDto requestDto) throws SQLException {
Product product = new Product(requestDto);
productRepository.createProduct(product);
return product;
}
}
@RestController
public class ProductController {
private final ProductService productService;
private final String dbUrl="";
private final String dbId="";
private final String dbPassword="";
public ProductController() {
this.dbUrl = dbUrl;
this.dbId = dbId;
this.dbPassword = dbPassword;
this.productService = new ProductService(dbUrl, dbId, dbPassword);
}
@PostMapping("/api/products")
public Product createProduct(@RequestBody ProductRequestDto requestDto) throws SQLException {
Product product = productService.createProduct(requestDto);
return product;
}
}
강한 결합을 해결하기 위해
원래는 클래스에서 자신이 필요한 객체를 직접 생성했지만
public ProductService() {
this.productRepository = new ProductTempRepository();
}
이제 용도에 맞게 필요한 객체를 그냥 가져다가 사용하기만 하자!
public ProductService(ProductTempRepository productRepository) {
this.productRepository = productRepository;
}
프로그램의 제어의 흐름이 뒤바뀌는 것
원래는 Service 단에서 Repository 객체를 생성하지만 이제는 Repository 객체가 Service 객체가 만들어지는 시점에 Service 단에 주입되기 때문
IoC를 적용해보자
public class ProductService {
private final ProductTempRepository productRepository;
public ProductService(ProductTempRepository productRepository) {
this.productRepository = productRepository;
}
public Product createProduct(ProductRequestDto requestDto) throws SQLException {
Product product = new Product(requestDto);
productRepository.createProduct(product);
return product;
}
}
@RequiredArgsConstructor
@RestController
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@PostMapping("/api/products")
public Product createProduct(@RequestBody ProductRequestDto requestDto) throws SQLException {
Product product = productService.createProduct(requestDto);
return product;
}
}
결론은 IoC를 구현하는 것 = DI를 하는 것
이제 용도에 맞게 필요한 객체를 그냥 가져다가 사용하기만 하면 됨
스프링 프레임워크가 필요한 객체를 생성하고 관리함
스프링 빈 : 스프링이 관리하는 객체
스프링 IoC 컨테이너 : 스프링 빈을 모아둔 통
📌 스프링 프레임워크는 서버가 뜰 때 스프링 IoC 컨테이너에 스프링 빈을 저장한다
@Component : 해당 클래스에 대해 빈을 등록할 때 사용하는 도구
@Autowired : 빈을 가져옴
@Component
public class ProductService {
@Autowired
private final ProductTempRepository productRepository;
public ProductService(ProductTempRepository productRepository) {
this.productRepository = productRepository;
}
public Product createProduct(ProductRequestDto requestDto) throws SQLException {
Product product = new Product(requestDto); productRepository.createProduct(product);
return product;
}
}