๐ ์๋ฒ์์์ ์ฒ๋ฆฌ๊ณผ์ โ ๋๋ถ๋ถ ๋น์ทํจ
์ฒ๋ฆฌ๊ณผ์ ์ ํฌ๊ฒ 3๊ฐ๋ก ๋ถ๋ฆฌํจ (Controller, Service, Repository)
โ Client Request๋ฅผ ๋ฐ์
โก Request์ ๋ํ ์ฒ๋ฆฌ๋ฅผ Service์๊ฒ ์ ๋ด์ํด
โข ์ฒ๋ฆฌ Response Client์๊ฒ ๋ฐํ
Client Request๋ฅผ ์ฒ๋ฆฌํ๋ ์ค์ธ
๋น์ฆ๋์ค ๋ก์ง โ ํ์ ์์ ์๋น์ค์ฝ๋๊ฐ ๊ณ์ ๋น๋ํด์ง
DB ์ ๋ณด โ Repository์ Request ๋ณด๋
DB ๊ด๋ฆฌ (์ฐ๊ฒฐ, ํด์ , ์์๊ด๋ฆฌ)
DB CRUD ์์ ์ฒ๋ฆฌ
// JSON ํํ์ ๋ฐ์ดํฐ๋ฐํ
@RestController
@RequestMapping("/api")
public class ProductController {
// ๊ด์ฌ์ํ ๋ฑ๋ก
@PostMapping("/products")
public ProductResponseDto createProduct(@RequestBody ProductRequestDto productRequestDto) throws SQLException {
ProductService productService = new ProductService();
return productService.createProduct(productRequestDto);
}
// ๊ด์ฌ์ํ ์กฐํ
@GetMapping("/products")
public List<ProductResponseDto> getProducts() throws SQLException {
ProductService productService = new ProductService();
return productService.getProducts();
}
// ๊ด์ฌ์ํ ์ต์ ๊ฐ ๋ฑ๋ก
@PutMapping("/products/{id}")
public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto productMypriceRequestDto) throws SQLException {
ProductService productService = new ProductService();
return productService.updateProduct(id, productMypriceRequestDto);
}
}
@Component // Bean ๋ฑ๋ก
public class ProductService {
// ๊ด์ฌ์ํ ๋ฑ๋ก
public ProductResponseDto createProduct(ProductRequestDto productRequestDto) throws SQLException {
Product product = Product.of(productRequestDto);
ProductRepository productRepository = new ProductRepository();
return productRepository.createProduct(product);
}
// ๊ด์ฌ์ํ ์กฐํ
public List<ProductResponseDto> getProducts() throws SQLException {
ProductRepository productRepository = new ProductRepository();
return productRepository.getProducts();
}
// ๊ด์ฌ์ํ ์ต์ ๊ฐ ๋ฑ๋ก
public Long updateProduct(Long id, ProductMypriceRequestDto productMypriceRequestDto) throws SQLException {
ProductRepository productRepository = new ProductRepository();
Product product = productRepository.getProduct(id);
if (product == null) {
throw new NullPointerException("ํด๋น ์ํ์ ์กด์ฌํ์ง ์์ต๋๋ค");
}
return productRepository.updateProduct(product.getId(), productMypriceRequestDto);
}
}
@Component // Bean ๋ฑ๋ก
public class ProductRepository {
// ๊ด์ฌ์ํ ๋ฑ๋ก
public ProductResponseDto createProduct(Product product) throws SQLException {
// DB ์ฐ๊ฒฐ
Connection connection = DriverManager.getConnection("jdbc:h2:mem:db;MODE=MYSQL", "mallang", "");
// DB Query ์์ฑ
PreparedStatement preparedStatement = connection.prepareStatement("select max(id) as id from product");
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
product.setId(resultSet.getLong("id") + 1);
} else {
throw new SQLException("Product ํ
์ด๋ธ์ ๋ง์ง๋ง id ๊ฐ์ ์ฐพ์์ค์ง ๋ชปํ์ต๋๋ค");
}
preparedStatement = connection.prepareStatement("insert into product(id, title, image, link, lprice, myprice)values(?, ?, ?, ?, ?, ?)");
preparedStatement.setLong(1, product.getId());
preparedStatement.setString(2, product.getTitle());
preparedStatement.setString(3, product.getImage());
preparedStatement.setString(4, product.getLink());
preparedStatement.setInt(5, product.getLprice());
preparedStatement.setInt(6, product.getMyprice());
// DB Query ์คํ
preparedStatement.executeUpdate();
// DB ์ฐ๊ฒฐํด์
preparedStatement.close();
return ProductResponseDto.of(product);
}
// ๊ด์ฌ์ํ ์กฐํ
public List<ProductResponseDto> getProducts() throws SQLException {
List<ProductResponseDto> productResponseDtoList = new ArrayList<>();
// DB ์ฐ๊ฒฐ
Connection connection = DriverManager.getConnection("jdbc:h2:mem:db;MODE=MYSQL", "mallang", "");
// DB Query ์์ฑ ๋ฐ ์คํ
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from product");
// DB Query ๊ฒฐ๊ณผ -> ProductResponseDtoList๋ก ๋ณํ
// DB Query ๊ฒฐ๊ณผ -> productResponseDtoList ๋ณํ
while (resultSet.next()) {
Product product = new Product();
product.setId(resultSet.getLong("id"));
product.setTitle(resultSet.getString("title"));
product.setImage(resultSet.getString("image"));
product.setLink(resultSet.getString("link"));
product.setLprice(resultSet.getInt("lprice"));
product.setMyprice(resultSet.getInt("myprice"));
productResponseDtoList.add(ProductResponseDto.of(product));
}
// DB ์ฐ๊ฒฐํด์
resultSet.close();
connection.close();
return productResponseDtoList;
}
// ๊ด์ฌ์ํ ์ต์ ๊ฐ ๋ฑ๋ก
public Long updateProduct(Long id, ProductMypriceRequestDto productMypriceRequestDto) throws SQLException {
// DB ์ฐ๊ฒฐ
Connection connection = DriverManager.getConnection("jdbc:h2:mem:db;MODE=MYSQL", "mallang", "");
// DB Query ์์ฑ
PreparedStatement preparedStatement = connection.prepareStatement("update product set myprice = ? where id = ?");
preparedStatement.setInt(1, productMypriceRequestDto.getMyprice());
preparedStatement.setLong(2, id);
// DB Query ์คํ
preparedStatement.executeUpdate();
// DB ์ฐ๊ฒฐํด์
preparedStatement.close();
connection.close();
return null;
}
public Product getProduct(Long id) throws SQLException {
Product product = new Product();
// DB ์ฐ๊ฒฐ
Connection connection = DriverManager.getConnection("jdbc:h2:mem:db;MODE=MYSQL", "mallang", "");
// DB Query ์์ฑ
PreparedStatement preparedStatement = connection.prepareStatement("select * from product where id = ?");
preparedStatement.setLong(1, id);
// DB Query ์คํ
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
product.setId(resultSet.getLong("id"));
product.setTitle(resultSet.getString("title"));
product.setImage(resultSet.getString("image"));
product.setLink(resultSet.getString("link"));
product.setLprice(resultSet.getInt("lprice"));
product.setMyprice(resultSet.getInt("myprice"));
}
// DB ์ฐ๊ฒฐํด์
resultSet.close();
preparedStatement.close();
connection.close();
return product;
}
}
๐ ๊ฐ์ฒด์ค๋ณต์์ฑ ํด๊ฒฐ
: ํ ํด๋์ค ๋ด์์, ๋ฉ์๋๋ง๋ค ๊ฐ์ ๊ฐ์ฒด๊ฐ ์ค๋ณตํด์ ๊ณ์ ์์ฑ๋๋ ๊ฒ โprivate final
๋ฉค๋ฒ๋ณ์๋ก ์ ์ธํด์ ๊ณต์ ํ์ฌ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
ProductService productService = new ProductService(); โ ์ญ์
ProductRepository productRepository = new ProductRepository(); โ ์ญ์
Controller1
โ Service1
๊ฐ์ฒด ์์ฑํ์ฌ ์ฌ์ฉpublic class Controller1 {
private final Service1 service1;
public Controller1() {
this.service1 = new Service1();
}
}
Service1
โ Repository
๊ฐ์ฒด ์์ฑํ์ฌ ์ฌ์ฉpublic class Service1 {
private final Repository1 repository1;
public Service() {
this.repository1 = new Repository1;
}
}
Repository1
๊ฐ์ฒด์ ์ธpublic class Repository1 {โขโขโข}
Controller
5๊ฐ โ ๊ฐ๊ฐ Service1
์์ฑํด์ ์ฌ์ฉ์ค
Repository1
์์ฑ์ ๋ณ๊ฒฝ โ ๋ชจ๋ Controller
, Service
์ฝ๋๋ณ๊ฒฝ ํ์ ๐จ
๐ ๊ฐํ๊ฒฐํฉํด๊ฒฐ๋ฒ
: ๊ฐ ๊ฐ์ฒด์ ๋ํ ๊ฐ์ฒด์์ฑ 1๋ฒ๋ง โ ์์ฑ๋ ํ๋์ ๊ฐ์ฒด๋ฅผ ๋ชจ๋ ๊ณณ์์ ์ฌ์ฌ์ฉ โญ๏ธ
Repository1
ํด๋์ค ์ ์ธ ๋ฐ ๊ฐ์ฒด์์ฑ โ repository1
public class Repository1{โขโขโข}
// ๊ฐ์ฒด์์ฑ
Repository1 repository1 = new Repository1();
Service1
ํด๋์ค ์ ์ธ ๋ฐ ๊ฐ์ฒด์์ฑ(repository1 ์ฌ์ฌ์ฉ) โ service1
class Service1 {
private final Repository1 repository1;
// repository1 ๊ฐ์ฒด ์ฌ์ฌ์ฉ
public Service1(Repository1 repository1) {
this.repository1 = repository1;
}
}
Controller1
์ ์ธ(service1 ์ฌ์ฌ์ฉ)class Controller1 {
private final Service1 service1;
// service1 ๊ฐ์ฒด ์ฌ์ฌ์ฉ
public Controller1(Service1 service1) {
this.service1 = service1;
}
}
Repository1
์์ฑ์ ๋ณ๊ฒฝ โ ๋ค๋ฅธ ์ฝ๋์ ์ํฅ์ ์ฃผ์ง ์์
Service1
์์ฑ์ ๋ณ๊ฒฝ โ ๋ค๋ฅธ ์ฝ๋์ ์ํฅ์ ์ฃผ์ง ์์
๐ Spring Bean
: Spring IoC ์ปจํ ์ด๋๊ฐ ๊ด๋ฆฌํ๋ ์๋ฐ๊ฐ์ฒด โ Spring์ ์ํด ์์ฑ๋๊ณ ๊ด๋ฆฌ๋๋ ์๋ฐ๊ฐ์ฒด
๐ Java ํ๋ก๊ทธ๋จ
- ๊ฐ ๊ฐ์ฒด โ ํ๋ก๊ทธ๋จ์ ํ๋ฆ์ ๊ฒฐ์
- ๊ฐ ๊ฐ์ฒด๋ฅผ ๊ฐ๋ฐ์๊ฐ ์ง์ ์์ฑ ๋ฐ ์กฐ์ (๊ฐ์ฒด์์ฑ โ ๋ฉ์๋ํธ์ถ)
- "๋ชจ๋ ์์
์ ๊ฐ๋ฐ์๊ฐ ์ ์ดํ๋ ๊ตฌ์กฐ"
๐ IoC ์ ์ด์ ์ญ์
- ๊ฐ์ฒด์์ฑ โ ๊ด๋ฆฌ์์์ฃผ์ฒด์ ๋งก๊น
- ๊ฐ๋ฐ์๊ฐ ์ง์ ๊ฐ์ฒด์์ฑ โ
new
์์ฑ์๋ฅผ ์ด์ฉํด ์์ฑํ ๊ฐ์ฒด๊ฐ ์๋๋ผ,ํด๋์ค ์์ Annotation ์ ์ธ
์คํ๋ง ์๋ฒ๊ฐ ๋ฐ ๋ โ ์คํ๋ง IoC์ Bean ์ ์ฅ
// 1. ProductService ๊ฐ์ฒด์์ฑ
ProductService productService = new ProductService();
// 2. Spring IoC ์ปจํ
์ด๋์ Bean(productService) ์ ์ฅ
// productService -> Spring IoC ์ปจํ
์ด๋
@ComponentScan(basePackages = "")
packages ์์น ๋ฐ ํ์ packages๋ค์ ์ ์ฉ๋จโ ์ง์ ๊ฐ์ฒด์์ฑ โ Bean ๋ฑ๋ก์์ฒญ
โก ์คํ๋ง์๋ฒ ๋ฐ ๋ โ ์คํ๋ง IoC์ Bean ์ ์ฅ
โข private final
๋ฉค๋ฒ๋ณ์ ์ ์ธ ์์ โ @Autowired
์ ์ธ
โฃ Bean์ ์ฌ์ฉํ ํจ์ ์์ @Autowired
์ ์ธ
โค Lombok โ @RequriedArgsConstructor
; @Autowired
+ ์์ฑ์ ์๋ต๊ฐ๋ฅ โญ๏ธ
<@Entity
@Getter @Setter
@NoArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
// Id ์๋์์ฑ ๋ฐ ์ฆ๊ฐ
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String image;
@Column(nullable = false)
private String link;
@Column(nullable = false)
private int lprice;
@Column(nullable = false)
private int myprice;
// ์์ฑ์
@Builder
private Product(String title, String image, String link, int lprice) {
this.title = title;
this.image = image;
this.link = link;
this.lprice = lprice;
this.myprice = 0;
}
// ์ ์ ํฉํ ๋ฆฌ๋ฉ์๋
public static Product of(ProductRequestDto productRequestDto) {
return Product.builder()
.title(productRequestDto.getTitle())
.image(productRequestDto.getImage())
.link(productRequestDto.getLink())
.lprice(productRequestDto.getLprice())
.build();
}
// ๊ด์ฌ์ํ ์ต์ ๊ฐ ๋ฑ๋ก (update)
public void update(ProductMypriceRequestDto productMypriceRequestDto) {
this.myprice = productMypriceRequestDto.getMyprice();
}
}
// JSON ํํ์ ๋ฐ์ดํฐ๋ฐํ
@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class ProductController {
// ๊ฐ์ฒด์ค๋ณต์์ฑ ํด๊ฒฐ -> ๋ฉค๋ฒ๋ณ์ ์ ์ธ
private final ProductService productService;
// ๊ด์ฌ์ํ ๋ฑ๋ก
@PostMapping("/products")
public ProductResponseDto createProduct(@RequestBody ProductRequestDto productRequestDto) {
return productService.createProduct(productRequestDto);
}
// ๊ด์ฌ์ํ ์กฐํ
@GetMapping("/products")
public List<ProductResponseDto> getProducts() {
return productService.getProducts();
}
// ๊ด์ฌ์ํ ์ต์ ๊ฐ ๋ฑ๋ก
@PutMapping("/products/{id}")
public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto productMypriceRequestDto) {
return productService.updateProduct(id, productMypriceRequestDto);
}
}
@Service
@RequiredArgsConstructor
public class ProductService {
// ๊ฐ์ฒด์ค๋ณต์์ฑ ํด๊ฒฐ -> ๋ฉค๋ฒ๋ณ์ ์ ์ธ
private final ProductRepository productRepository;
// ๊ด์ฌ์ํ ๋ฑ๋ก
@Transactional
public ProductResponseDto createProduct(ProductRequestDto productRequestDto) {
Product product = productRepository.saveAndFlush(Product.of(productRequestDto));
return ProductResponseDto.of(product);
}
// ๊ด์ฌ์ํ ์กฐํ
@Transactional(readOnly = true)
public List<ProductResponseDto> getProducts() {
List<ProductResponseDto> productResponseDtoList = new ArrayList<>();
List<Product> productList = productRepository.findAll();
for (Product product : productList) {
productResponseDtoList.add(ProductResponseDto.of(product));
}
return productResponseDtoList;
}
// ๊ด์ฌ์ํ ์ต์ ๊ฐ ๋ฑ๋ก
@Transactional
public Long updateProduct(Long id, ProductMypriceRequestDto productMypriceRequestDto) {
Product product = productRepository.findById(id).orElseThrow(
() -> new NullPointerException("ํด๋น ์ํ์ ์กด์ฌํ์ง ์์ต๋๋ค;")
);
product.update(productMypriceRequestDto);
return product.getId();
}
}
public interface ProductRepository extends JpaRepository<Product, Long> {
}
@Entity
@Getter @Setter
@NoArgsConstructor
public class Product extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
// Id ์๋์์ฑ ๋ฐ ์ฆ๊ฐ
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String image;
@Column(nullable = false)
private String link;
@Column(nullable = false)
private int lprice;
@Column(nullable = false)
private int myprice;
// ์์ฑ์
@Builder
private Product(String title, String image, String link, int lprice) {
this.title = title;
this.image = image;
this.link = link;
this.lprice = lprice;
this.myprice = 0;
}
// ์ ์ ํฉํ ๋ฆฌ๋ฉ์๋
public static Product of(ProductRequestDto productRequestDto) {
return Product.builder()
.title(productRequestDto.getTitle())
.image(productRequestDto.getImage())
.link(productRequestDto.getLink())
.lprice(productRequestDto.getLprice())
.build();
}
// ๊ด์ฌ์ํ ์ต์ ๊ฐ ๋ฑ๋ก (update)
public void update(ProductMypriceRequestDto productMypriceRequestDto) {
this.myprice = productMypriceRequestDto.getMyprice();
}
// ๊ด์ฌ์ํ ์ต์ ๊ฐ ์
๋ฐ์ดํธ
public void updateLprice(ItemDto itemDto) {
this.lprice = itemDto.getLprice();
}
}
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public class Timestamped {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column
private LocalDateTime modifiedAt;
}
Application.java
โ @EnableJpaAuditing
์ถ๊ฐํ์@Slf4j
@Component
@RequiredArgsConstructor
public class Scheduler {
private final NaverApiService naverApiService;
private final ProductService productService;
private final ProductRepository productRepository;
// ์ด, ๋ถ, ์, ์ผ, ์, ์ฃผ ์์
@Scheduled(cron = "0 0 1 * * *")
public void updateLprice() throws InterruptedException {
log.info("์ต์ ๊ฐ ์
๋ฐ์ดํธ ์คํ");
List<Product> productList = productRepository.findAll();
for (Product product : productList) {
// 1์ด์ ํ ์ํ์ฉ ์กฐํ
TimeUnit.SECONDS.sleep(1);
String title = product.getTitle();
List<ItemDto> itemDtoList = naverApiService.searchItems(title);
ItemDto itemDto = itemDtoList.get(0);
// 1๋ฒ์งธ ๊ด์ฌ์ํ ์ต์ ๊ฐ๋ฅผ ์
๋ฐ์ดํธ
Long id = product.getId();
productService.updateBySearch(id, itemDto);
}
}
}
Application.java
โ @EnableScheduling
์ถ๊ฐํ์@Service
@RequiredArgsConstructor
public class ProductService {
// ๊ฐ์ฒด์ค๋ณต์์ฑ ํด๊ฒฐ -> ๋ฉค๋ฒ๋ณ์ ์ ์ธ
private final ProductRepository productRepository;
// ๊ด์ฌ์ํ ๋ฑ๋ก
@Transactional
public ProductResponseDto createProduct(ProductRequestDto productRequestDto) {
Product product = productRepository.saveAndFlush(Product.of(productRequestDto));
return ProductResponseDto.of(product);
}
// ๊ด์ฌ์ํ ์กฐํ
@Transactional(readOnly = true)
public List<ProductResponseDto> getProducts() {
List<ProductResponseDto> productResponseDtoList = new ArrayList<>();
List<Product> productList = productRepository.findAll();
for (Product product : productList) {
productResponseDtoList.add(ProductResponseDto.of(product));
}
return productResponseDtoList;
}
// ๊ด์ฌ์ํ ์ต์ ๊ฐ ๋ฑ๋ก
@Transactional
public Long updateProduct(Long id, ProductMypriceRequestDto productMypriceRequestDto) {
Product product = productRepository.findById(id).orElseThrow(
() -> new NullPointerException("ํด๋น ์ํ์ ์กด์ฌํ์ง ์์ต๋๋ค")
);
product.update(productMypriceRequestDto);
return product.getId();
}
// ๊ด์ฌ์ํ ์ต์ ๊ฐ ์
๋ฐ์ดํธ
public void updateBySearch(Long id, ItemDto itemDto) {
Product product = productRepository.findById(id).orElseThrow(
() -> new NullPointerException("ํด๋น ์ํ์ ์กด์ฌํ์ง ์์ต๋๋ค")
);
// Product Entity method ์คํ
product.updateLprice(itemDto);
}
}