본문 바로가기
🛠 BackEnd/Spring

[ Spring ] Controller, Service Repository 코드 분리 (2/2)

by 깸뽀 2022. 10. 8.
728x90

📌 Controller, Service Repository 역할

 - 전체적인 흐름 

 

 

💻 AllInOneController ⇨ ProductController 로 변경

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
 
import java.sql.SQLException;
import java.util.List;
 
@RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class ProductController {
 
    // 신규 상품 등록
    @PostMapping("/api/products")
    public Product createProduct(@RequestBody ProductRequestDto requestDto) throws SQLException {
        ProductService productService = new ProductService();
        Product product = productService.createProduct(requestDto);
 
        // 응답 보내기
        return product;
    }
 
    // 설정 가격 변경
    @PutMapping("/api/products/{id}")
    public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto) throws SQLException {
        ProductService productService = new ProductService();
        Product product = productService.updateProduct(id, requestDto);
 
        // 응답 보내기 (업데이트된 상품 id)
        return product.getId();
    }
 
    // 등록된 전체 상품 목록 조회
    @GetMapping("/api/products")
    public List<Product> getProducts() throws SQLException {
        ProductService productService = new ProductService();
        List<Product> products = productService.getProducts();
 
        // 응답 보내기
        return products;
    }
}
cs

 

💻 ProductService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.sql.SQLException;
import java.util.List;
 
public class ProductService {
 
    public Product createProduct(ProductRequestDto requestDto) throws SQLException {
        // 요청받은 DTO 로 DB에 저장할 객체 만들기
        Product product = new Product(requestDto);
 
        ProductRepository productRepository = new ProductRepository();
        productRepository.createProduct(product);
 
        return product;
    }
 
    public Product updateProduct(Long id, ProductMypriceRequestDto requestDto) throws SQLException {
        ProductRepository productRepository = new ProductRepository();
        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 {
        ProductRepository productRepository = new ProductRepository();
        List<Product> products = productRepository.getProducts();
 
        return products;
    }
}
cs

 

 

💻 ProductRepository.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
 
public class ProductRepository {
 
    public void createProduct(Product product) throws SQLException {
        // DB 연결
        Connection connection = DriverManager.getConnection("jdbc:h2:mem:springcoredb""sa""");
 
        // 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 = DriverManager.getConnection("jdbc:h2:mem:springcoredb""sa""");
 
        // 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 = DriverManager.getConnection("jdbc:h2:mem:springcoredb""sa""");
 
        // 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 = DriverManager.getConnection("jdbc:h2:mem:springcoredb""sa""");
 
        // 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;
    }
}
cs

 

 

 


 

🔑 1. 객체 중복 생성 문제 해결

new ProductRepository() 코드가 중복되므로 ProductService가 생성될 때 딱 한 번만 생성해서 계속 사용하면 됨

 

< 수정 후 코드 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ProductService {
    
    // 멤버 변수 선언
    private final ProductRepository productRepository;
 
    // 생성자: ProductService() 가 생성될 때 호출됨
    public ProductService() {
        // 멤버 변수 생성
        this.productRepository = new ProductRepository();
    }
    
    public Product createProduct(ProductRequestDto requestDto) throws SQLException {
        // 요청받은 DTO 로 DB에 저장할 객체 만들기
        Product product = new Product(requestDto);
 
        // 멤버 변수 사용
        this.productRepository.createProduct(product);
 
        return product;
    }
cs

  

 

🔑  2. DI (의존성 주입) : 강한 결합의 문제 해결

  • Controller 5 개가 각각 Service1 을 생성하여 사용 중
  • Repository1 생성자 변경에 의해.. ⇒ 모든 Contoller모든 Service 의 코드 변경이 필요

 

💡 1. 각 객체에 대한 객체 생성은 딱 1번만
     2. 생성된 객체의 모든 곳에서 재사용

 

< 수정 후 코드 >

- Service

1
2
3
4
5
6
7
8
9
10
11
12
Class Service1 {
    private final Repository1 repitory1;
 
    // repository1 객체 사용
    public Service1(Repository1 repository1) {
        this.repository1 = new Repository1();
        this.repository1 = repository1;
    }
}
 
// 객체 생성
Service1 service1 = new Service1(repository1);
cs

- Controller

1
2
3
4
5
6
7
8
9
Class Controller1 {
    private final Service1 service1;
 
    // service1 객체 사용
    public Controller1(Service1 service1) {
        this.service1 = new Service1();
        this.service1 = service1;
    }
}
cs

- Repository

1
2
3
4
5
6
7
8
9
10
11
12
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);
cs

 

 

[ 결과 ] 

 

Repository1 생성자 변경은 이제 누구에게도 피해(?) 를 주지 않음

Service1 생성자가 변경되면? 모든 Contoller → Controller 변경 필요 X

결론적으로, 강한 결합 ⇒ 느슨한 결합

 

💡 DI(의존성 주입) 의 이해
    : 프로그램의 제어 흐름이 뒤바뀜
  • 일반적: 사용자가 자신이 필요한 객체를 생성해서 사용
  • IoC (제어의 역전)
    • 용도에 맞게 필요한 객체를 그냥 가져다 사용
      • "DI (Dependency Injection)" 혹은 한국말로 "의존성 주입"이라고 부름
    • 사용할 객체가 어떻게 만들어졌는지는 알 필요 없음
    • 실생활 예제) 가위의 용도별 사용
      • 음식을 자를 때 필요한 가위는? → 부엌가위 (생성되어 있는 객체 kitchenScissors)
      • 무늬를 내며 자를 때 필요한 가위는? → 핑킹가위 (생성되어 있는 객체 pinkingShears)
      • 정원의 나무를 다듬을 때 필요한 가위는? → 전지가위 (생성되어 있는 객체 pruningShears)

 

🔑  3. IoC 컨테이너 사용  

DI를 사용하기 위해서는 객체 생성이 우선되어야 했음. 과연 어디서 객체를 생성해야 할가?
바로, 스프링 프레임워크가 필요한 객체를 생성하여 관리하는 역할을 대신 해줌.
  • 빈 (Bean): 스프링이 관리하는 객체
  • 스프링 IoC 컨테이너: '빈'을 모아둔 통

 

📌 스프링 '빈'등록 방법

1. @Componen

 

1)클래스 선언 위에 설정

1
2
@Component
public class ProductService { ... }
cs

2)스프링 서버가 뜰 때 스프링 IoC 에 '빈' 저장

  • @Component 클래스에 대해서 스프링이 해 주는 일
    1
    2
    3
    4
    5
    // 1. ProductService 객체 생성
    ProductService productService = new ProductService();
     
    // 2. 스프링 IoC 컨테이너에 빈 (productService) 저장
    // productService -> 스프링 IoC 컨테이너
    cs
  • 스프링 '빈' 이름: 클래스의 앞글자만 소문자로 변경 public class ProductServiceproductService 

3) 빈' 아이콘 확인 → 스프링 IoC 에서 관리할 '빈' 클래스라는 표시

 

4) @Component 적용 조건 ⭐

  • @Component Scan 에 설정해 준 packages 위치와 하위 packages 들
    1
    2
    3
    @Configuration
    @ComponentScan(basePackages = "com.sparta.springcore")
    class BeanConfig { ... }
    cs

 

  • @SpringBootApplication 에 의해 default 설정이 되어 있음
  • 경로 : com.sparta.springcore/SpringcoreApplication.java

 

728x90

댓글