🔥 혹시 아직도 더 개선할 부분이 있을까?
43) 강한 결합의 문제점
['강한 결합' 이해를 위한 예제]
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 { ... }
만약, 다음과 같이 변경된다면..
1. Repository1 객체 생성 시 DB 접속 id, pw 를 받아서 DB 접속 시 사용
- 생성자에 DB 접속 id, pw 를 추가
```java
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** 의 코드 변경이 필요

44) 강한 결합 해결방법
👉 그렇다면 **"강한 결합"**을 해결할 방법이 없을까?
각 객체에 대한 객체 생성은 딱 1번만!!
생성된 객체를 모든 곳에서 재사용!!!
Repository1 클래스 선언 및 객체 생성 → repository1
```java
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;
}
}
만약, 다음과 같이 변경된다면,
1. Repository1 객체 생성 시 DB 접속 id, pw 를 받아서 DB 접속 시 사용
- 생성자에 DB 접속 id, pw 를 추가
```java
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
결론적으로, 강한 결합 ⇒ 느슨한 결합

45) DI (의존성 주입)의 이해

👉 "제어의 역전 (IoC: Inversion of Control)"
프로그램의 제어 흐름이 뒤바뀜
46) DI 적용 시도
ProductRepository 객체 생성 시 DB 접속을 위한 url, id, passwrod 를 모두 받아서 생성
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class ProductRepository {
private final String dbUrl;
private final String dbId;
private final String dbPassword;
public ProductRepository(String dbUrl, String dbId, String dbPassword) {
this.dbUrl = dbUrl;
this.dbId = dbId;
this.dbPassword = dbPassword;
}
public void createProduct(Product product) throws SQLException {
// DB 연결
Connection connection = getConnection();
// DB Query 작성
PreparedStatement ps = connection.prepareStatement("select max(id) as id from product");
ResultSet rs = ps.executeQuery();
if (rs.next()) {
// product id 설정 = product 테이블의 마지막 id + 1
product.setId(rs.getLong("id") + 1);
} else {
throw new SQLException("product 테이블의 마지막 id 값을 찾아오지 못했습니다.");
}
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.getLprice());
ps.setInt(6, product.getMyprice());
// DB Query 실행
ps.executeUpdate();
// DB 연결 해제
ps.close();
connection.close();
}
public Product getProduct(Long id) throws SQLException {
Product product = new Product();
// DB 연결
Connection connection = getConnection();
// DB Query 작성
PreparedStatement ps = connection.prepareStatement("select * from product where id = ?");
ps.setLong(1, id);
// DB Query 실행
ResultSet rs = ps.executeQuery();
if (rs.next()) {
product.setId(rs.getLong("id"));
product.setImage(rs.getString("image"));
product.setLink(rs.getString("link"));
product.setLprice(rs.getInt("lprice"));
product.setMyprice(rs.getInt("myprice"));
product.setTitle(rs.getString("title"));
}
// DB 연결 해제
rs.close();
ps.close();
connection.close();
return product;
}
public void updateMyprice(Long id, int myprice) throws SQLException {
// DB 연결
Connection connection = getConnection();
// DB Query 작성
PreparedStatement ps = connection.prepareStatement("update product set myprice = ? where id = ?");
ps.setInt(1, myprice);
ps.setLong(2, id);
// DB Query 실행
ps.executeUpdate();
// DB 연결 해제
ps.close();
connection.close();
}
public List<Product> getProducts() throws SQLException {
List<Product> products = new ArrayList<>();
// DB 연결
Connection connection = getConnection();
// DB Query 작성 및 실행
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("select * from product");
// DB Query 결과를 상품 객체 리스트로 변환
while (rs.next()) {
Product product = new Product();
product.setId(rs.getLong("id"));
product.setImage(rs.getString("image"));
product.setLink(rs.getString("link"));
product.setLprice(rs.getInt("lprice"));
product.setMyprice(rs.getInt("myprice"));
product.setTitle(rs.getString("title"));
products.add(product);
}
// DB 연결 해제
rs.close();
connection.close();
return products;
}
private Connection getConnection() throws SQLException {
return DriverManager.getConnection(dbUrl, dbId, dbPassword);
}
}import java.sql.SQLException;
import java.util.List;
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public Product createProduct(ProductRequestDto requestDto) throws SQLException {
// 요청받은 DTO 로 DB에 저장할 객체 만들기
Product product = new Product(requestDto);
productRepository.createProduct(product);
return product;
}
public Product updateProduct(Long id, ProductMypriceRequestDto requestDto) throws SQLException {
Product product = productRepository.getProduct(id);
if (product == null) {
throw new NullPointerException("해당 아이디가 존재하지 않습니다.");
}
int myprice = requestDto.getMyprice();
productRepository.updateMyprice(id, myprice);
return product;
}
public List<Product> getProducts() throws SQLException {
List<Product> products = productRepository.getProducts();
return products;
}
}import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.sql.SQLException;
import java.util.List;
@RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
@RestController // JSON으로 데이터를 주고받음을 선언합니다.
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;
}
// 설정 가격 변경
@PutMapping("/api/products/{id}")
public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto) throws SQLException {
Product product = productService.updateProduct(id, requestDto);
// 응답 보내기 (업데이트된 상품 id)
return product.getId();
}
// 등록된 전체 상품 목록 조회
@GetMapping("/api/products")
public List<Product> getProducts() throws SQLException {
List<Product> products = productService.getProducts();
// 응답 보내기
return products;
}
}