개빡친다. 기껏 AllinoneController 강의를 4시간걸쳐 이해하고 다 봤더니 코드를 절약할 수 있다고 하고 코드 분리를 시키지를 않나. 분리시키는 과정을 또 두시간걸쳐 보고 이해했더니 이제는 강한 결합 어쩌고 DI를 써서 코드를 절약할 수있다고 또 코드를 마구잡이로 삭제하고 새로 쓴다. 뭐하잔거지? 걍 첨부터 제일 좋은 방법을 가르치는 생각을 못하는 빡대가린가? 이제는 이짓도 마지막이라 생각하고 의존성주입 DI에 대해서 기록하려 한다.
DI 강한 결합의 문제점을 설명하겠다.
예를 들어
1. Controller1이 Service1 객체를 생성해서 사용한다 하자.
public class Controller1 {
private final Service1 service1;
public Controller1() {
this.service1 = new Service1();
}
}
service1을 매개변수로 받아서 생성자에서 sevice1을 객체로 받아서 사용하겠다고 한다.
2. Service1이 Repository1 객체를 생성해서 사용한다.
public class Service1 {
private final Repository1 repository1;
public Service1() {
this.repository1 = new Repository1();
}
}
Repository1가 repository1을 매개변수로 갖고 똑같이 객체로 받아서 사용한다.
3.Repostioty 객체 선언
public class Repository1 { ... }
이렇게 아무거나 그냥 객체로 받고 있는상태로 만든후
만약에 Repository1 객체 생성할때 DB접속 id,pw를 받아서 DB접속시 사용한다고 바뀌면?
public class Repository1 {
public Repository1(String id, String pw) {
// DB 연결
Connection connection = DriverManager.getConnection("jdbc:h2:mem:springcoredb", id, pw);
}
}
그럼 일단 id,pw를 받아서 추가해주어야한다.
이렇게 Repository의 생성자 변경으로 인해 상속 연결 되어 있던 service와 controller의 코드까지 싸그리 바뀌어야한다.
만약 controller가 한개가 아니라 여러개의 controller가 service1에 객체로 연결 되어 있다면?
생각만 해도 머리아프다.
이럴때 쓰는게 DI 의존성 주입이다.
방법은 :
1.각 객체에 대한 객체 생성은 딱1번만
2.생성된 객체를 모든 곳에서 재사용
1. Repository 클래스 선언 및 객체 생성
public class Repository1 { ... }
// 객체 생성
Repository1 repository1 = new Repository1();
거꾸로 가는것이다.
2. Service1클래스 선언 및 객체 생성(repository1 사용) service1
Class Service1 {
private final Repository1 repitory1;
// repository1 객체 사용
public Service1(Repository1 repository1) {
this.repository1 = repository1;
}
}
// 객체 생성
Service1 service1 = new Service1(repository1);
달라진점은 객체에서 repository1을 사용한다는점.
3.마지막으로 Controller1선언 (service1사용)
Class Controller1 {
private final Service1 service1;
// service1 객체 사용
public Controller1(Service1 service1) {
this.service1 = new Service1();
this.service1 = service1;
}
}
4. 만약, 다음과 같이 변경된다면,
- 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
- 일반적: 사용자가 자신이 필요한 객체를 생성해서 사용
- IoC (제어의 역전)
- 용도에 맞게 필요한 객체를 그냥 가져다 사용
- "DI (Dependency Injection)" 혹은 한국말로 "의존성 주입"이라고 부릅니다.
- 사용할 객체가 어떻게 만들어졌는지는 알 필요 없음
- 용도에 맞게 필요한 객체를 그냥 가져다 사용
ProductRepository
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);
}
}
productService
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;
}
}
ProductController
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;
}
}
댓글