[spring] (3) DI와 IoC

orca·2022년 11월 1일
0

Spring

목록 보기
3/13
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;
    }
}

강한 결합을 해결하기 위해

  1. 각 클래스에 대한 객체의 생성은 한번만!
  2. 서비스에서 레파지토리 객체가 이용된다면 컨트롤러에서도 해당 객체를 사용 가능하게!

DI, Dependency Injection

원래는 클래스에서 자신이 필요한 객체를 직접 생성했지만

public ProductService() {
        this.productRepository = new ProductTempRepository();
    }

이제 용도에 맞게 필요한 객체를 그냥 가져다가 사용하기만 하자!

public ProductService(ProductTempRepository productRepository) {
        this.productRepository = productRepository;
    }

IoC, Inversion of Control

프로그램의 제어의 흐름이 뒤바뀌는 것
원래는 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 컨테이너

결론은 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;
    }
}

0개의 댓글