38) Controller, Service, Repository 로 코드 분리
👉 AllInOneController 클래스의 이름부터 변경해 볼까요?
AllInOneController.java → ProductController.java
클래스이름 변경 방법
AllInOneController 클래스에서 마우스 오른쪽 버튼을 누른 후, Refactor → Rename 클릭
혹은 키보드 단축키로도 가능합니다. Shift + F6

Rename 팝업창이 뜨면, "AllInOneController" 를 "ProductController" 로 변경해 준 후 "Refactor" 버튼을 클릭해 주세요.


[코드스니펫] ProductController.java
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;
}
}
[코드스니펫] ProductService.java
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;
}
}
[코드스니펫] ProductRepository.java
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;
}
}
39) 리팩토링 후 UI 를 통해 기능 검증
40) 리팩토링 후 개선점 확인
한 개의 클래스에 너무 많은 양의 코드가 존재
→ 3개의 클래스에 역할 별로 코드가 정리됨
현업에서는 코드 추가 혹은 변경 요청이 계속 생김
신규 상품 등록 시 Client 에게 응답 (Response) 하는 값 변경
→ Controller 의 역할
최저가 (Myprice) 업데이트 조건 변경
→ Service 의 역할
DB 테이블 이름 변경
→ Repository 의 역할