πŸ“ GitHub λ°”λ‘œκ°€κΈ°

Project Intro


1 AllInOneController 역할뢄리

πŸ“Œ μ„œλ²„μ—μ„œμ˜ μ²˜λ¦¬κ³Όμ • β†’ λŒ€λΆ€λΆ„ λΉ„μŠ·ν•¨
μ²˜λ¦¬κ³Όμ •μ„ 크게 3개둜 뢄리함 (Controller, Service, Repository)

1️⃣ Controller

  • β‘  Client Requestλ₯Ό λ°›μŒ

  • β‘‘ Request에 λŒ€ν•œ 처리λ₯Ό Serviceμ—κ²Œ μ „λ‹΄μ‹œν‚΄

  • β‘’ 처리 Response Clientμ—κ²Œ λ°˜ν™˜

2️⃣ Service

  • Client Requestλ₯Ό μ²˜λ¦¬ν•˜λŠ” μ‹€μ„Έ

  • λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 β†’ ν˜„μ—…μ—μ„œ μ„œλΉ„μŠ€μ½”λ“œκ°€ 계속 λΉ„λŒ€ν•΄μ§

  • DB 정보 β†’ Repository에 Request 보냄

3️⃣ Repository

  • DB 관리 (μ—°κ²°, ν•΄μ œ, μžμ›κ΄€λ¦¬)

  • DB CRUD μž‘μ—…μ²˜λ¦¬


2 AllInOneController μ½”λ“œλΆ„λ¦¬

1️⃣ 관심사뢄리

πŸ“ Controller

πŸ“ Service

πŸ“ Repository

2️⃣ μ½”λ“œλΆ„λ¦¬

πŸ“ Controller

// 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);

    }

}

πŸ“ Service

@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);

    }

}

πŸ“ Repository

@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;

    }

}

3 객체쀑볡생성 ν•΄κ²°

πŸ“Œ 객체쀑볡생성 ν•΄κ²° : ν•œ 클래슀 λ‚΄μ—μ„œ, λ©”μ„œλ“œλ§ˆλ‹€ 같은 객체가 μ€‘λ³΅ν•΄μ„œ 계속 μƒμ„±λ˜λŠ” 것 β†’ private final λ©€λ²„λ³€μˆ˜λ‘œ μ„ μ–Έν•΄μ„œ κ³΅μœ ν•˜μ—¬ μ‚¬μš©ν•˜λŠ” 방법

πŸ“ ProductController.java

ProductService productService = new ProductService(); β†’ μ‚­μ œ

πŸ“ ProductService.java

ProductRepository productRepository = new ProductRepository(); β†’ μ‚­μ œ

4 DI(μ˜μ‘΄μ„±μ£Όμž…)

1️⃣ κ°•ν•œκ²°ν•©

  • β‘  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 {β€’β€’β€’}

2️⃣ κ°•ν•œκ²°ν•© 문제점

  • Controller 5개 β†’ 각각 Service1 μƒμ„±ν•΄μ„œ μ‚¬μš©μ€‘

  • Repository1 μƒμ„±μž λ³€κ²½ β†’ λͺ¨λ“  Controller, Service μ½”λ“œλ³€κ²½ ν•„μš” 🚨

3️⃣ κ°•ν•œκ²°ν•© 해결법

πŸ“Œ κ°•ν•œκ²°ν•©ν•΄κ²°λ²• : 각 객체에 λŒ€ν•œ 객체생성 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;
    }
    
}

4️⃣ κ°•ν•œκ²°ν•© κ°œμ„ κ²°κ³Ό

  • Repository1 μƒμ„±μž λ³€κ²½ β†’ λ‹€λ₯Έ μ½”λ“œμ— 영ν–₯을 주지 μ•ŠμŒ

  • Service1 μƒμ„±μž λ³€κ²½ β†’ λ‹€λ₯Έ μ½”λ“œμ— 영ν–₯을 주지 μ•ŠμŒ


5 Spring IoC μ»¨ν…Œμ΄λ„ˆ

πŸ“Œ Spring Bean : Spring IoC μ»¨ν…Œμ΄λ„ˆκ°€ κ΄€λ¦¬ν•˜λŠ” μžλ°”κ°μ²΄ β†’ Spring에 μ˜ν•΄ μƒμ„±λ˜κ³  κ΄€λ¦¬λ˜λŠ” μžλ°”κ°μ²΄

πŸ“ Java ν”„λ‘œκ·Έλž¨
	- 각 객체 β†’ ν”„λ‘œκ·Έλž¨μ˜ 흐름을 κ²°μ •
    - 각 객체λ₯Ό κ°œλ°œμžκ°€ 직접 생성 및 μ‘°μž‘ (객체생성 β†’ λ©”μ„œλ“œν˜ΈμΆœ)
    - "λͺ¨λ“ μž‘업을 κ°œλ°œμžκ°€ μ œμ–΄ν•˜λŠ” ꡬ쑰"

πŸ“ IoC μ œμ–΄μ˜ μ—­μ „
	- 객체생성 β†’ κ΄€λ¦¬μœ„μž„μ£Όμ²΄μ— 맑김
    - κ°œλ°œμžκ°€ 직접 객체생성 ❌
  • Springμ—μ„œλŠ” new μƒμ„±μžλ₯Ό μ΄μš©ν•΄ μƒμ„±ν•œ 객체가 μ•„λ‹ˆλΌ,
    Spring에 μ˜ν•΄ κ΄€λ¦¬λ‹Ήν•˜λŠ” μžλ°”κ°μ²΄λ₯Ό μ‚¬μš©ν•¨

1️⃣ Spring Bean 등둝방법

πŸ“ @Component

  • 클래슀 μœ„μ— Annotation μ„ μ–Έ

  • μŠ€ν”„λ§ μ„œλ²„κ°€ 뜰 λ•Œ β†’ μŠ€ν”„λ§ IoC에 Bean μ €μž₯

// 1. ProductService 객체생성
ProductService productService = new ProductService();

// 2. Spring IoC μ»¨ν…Œμ΄λ„ˆμ— Bean(productService) μ €μž₯
// productService -> Spring IoC μ»¨ν…Œμ΄λ„ˆ
  • @ComponentScan(basePackages = "") packages μœ„μΉ˜ 및 ν•˜μœ„ packages듀에 적용됨

2️⃣ Spring Bean μ‚¬μš©λ°©λ²•

πŸ“ @Bean

  • β‘  직접 객체생성 β†’ Bean λ“±λ‘μš”μ²­

  • β‘‘ μŠ€ν”„λ§μ„œλ²„ 뜰 λ•Œ β†’ μŠ€ν”„λ§ IoC에 Bean μ €μž₯

  • β‘’ private final λ©€λ²„λ³€μˆ˜ μ„ μ–Έ μœ„μ— β†’ @Autowired μ„ μ–Έ

  • β‘£ Bean을 μ‚¬μš©ν•  ν•¨μˆ˜ μœ„μ— @Autowired μ„ μ–Έ

  • β‘€ Lombok β†’ @RequriedArgsConstructor ; @Autowired + μƒμ„±μž μƒλž΅κ°€λŠ₯ ⭐️


6 μ΅œμ’… Refactoring μ½”λ“œ

1️⃣ Product.java

<@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();
    }

}

2️⃣ ProductController.java

// 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);

    }

}

3️⃣ ProductService.java

@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();

    }

}

4️⃣ ProductRepository.java

public interface ProductRepository extends JpaRepository<Product, Long> {
}

7 κ΄€μ‹¬μƒν’ˆ μ΅œμ €κ°€μ—…λ°μ΄νŠΈ μŠ€μΌ€μ€„λŸ¬ 섀계

1️⃣ Product.java

@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();
    }

}

2️⃣ Timestamped.java

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public class Timestamped {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column
    private LocalDateTime modifiedAt;

}
  • Application.java β†’ @EnableJpaAuditing μΆ”κ°€ν•„μˆ˜

3️⃣ Scheduler.java

@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 μΆ”κ°€ν•„μˆ˜

4️⃣ ProductService.java

@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);

    }

}
profile
🐰 I'm Sunyeon-Jeong, mallang

0개의 λŒ“κΈ€

κ΄€λ ¨ μ±„μš© 정보