 
    과제 4. API
              
              
              
              
              2024.02.24
            
            1. 과일 가게에 입고된 과일 정보를 저장하는 api를 만들어보자!
method는 post를 쓰고 path는 /api/v1/fruit을 이용한다.
create table fruits (
    id bigint auto_increment,
    name varchar(25),
    warehousingDate date,
    price bigint,
    status varchar(10) CHECK (status IN ('SOLD', 'NOT_SOLD'))
);// DTO
package com.group.libraryapp.dto.HW;
import java.time.LocalDate;
public class FruitRequest {
    private String name;
    private LocalDate warehousingDate;
    private long price;
    private String status;
    public FruitRequest(String name, LocalDate warehousingDate, long price, String status) {
        this.name = name;
        this.warehousingDate = warehousingDate;
        this.price = price;
        this.status = status;
    }
    public String getName() {
        return name;
    }
    public LocalDate getWarehousingDate() {
        return warehousingDate;
    }
    public long getPrice() {
        return price;
    }
    public void setStatus(String status) {
        this.status = status;
    }
    public String getStatus() {
        return status;
    }
}과일 가게에 입고된 과일 정보를 저장하는 API를 만들기 위해 먼저 과일 정보를 담을 수 있는 요청 바디를 정의하는 DTO 클래스를 생성했다.
DTO를 사용하여 POST 요청을 처리하는 컨트롤러를 만들었다.
@Autowired
    private JdbcTemplate jdbcTemplate;
    @PostMapping("/api/v1/fruit")
    public FruitRequest addFruit(@RequestBody FruitRequest fruitRequest) {
        // 기본 상태를 'NOT_SOLD'로 설정
        fruitRequest.setStatus("NOT_SOLD");
        String sql = "INSERT INTO fruits (name, warehousingDate, price, status) VALUES (?, ?, ?, ?)";
        jdbcTemplate.update(sql, fruitRequest.getName(), fruitRequest.getWarehousingDate(),
                fruitRequest.getPrice(), fruitRequest.getStatus());
        return fruitRequest; // 저장된 과일 정보 반환
    }
다음과 같이 잘 작동한다!
한 걸음 더!
위 API에서 long을 사용한 이유는 무엇일까?
-> 과일 가격과 같이 큰 정수가 될 수 있는 경우에 long을 사용하는 것이 좋을 것 같다. 과일 가격이 매우 높거나, 가격을 특정 단위로 표현할 때 int의 최대값을 초과할 수 있기 때문이다.
2. 과일이 팔리게 되면, 우리 시스템에 팔린 과일 정보를 기록해야 한다.
// DTO
package com.group.libraryapp.dto.HW;
public class FruitSoldRequest {
    private long id;
    // 기본 생성자를 명시적으로 추가
    public FruitSoldRequest() {
    }
    // id 필드에 대한 세터 메서드
    public void setId(long id) {
        this.id = id;
    }
    // id 필드에 대한 게터 메서드
    public long getId() {
        return id;
    }
}
@PutMapping("/api/v1/fruit")
    public ResponseEntity<Void> markFruitAsSold(@RequestBody FruitSoldRequest request) {
        String sql = "UPDATE fruits SET status = 'SOLD' WHERE id = ?";
        int updatedRows = jdbcTemplate.update(sql, request.getId());
        if (updatedRows == 0) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok().build();
    }
이렇게 보내고 나면 상태가 아래처럼 SOLD로 바뀐다.
3. 우리는 특정 과일을 기준으로 팔린 금액, 팔리지 않은 금액을 조회하고 싶다.
- (1, 사과, 3000원, 판매 O) 
- (2, 사과, 4000원, 판매 X) 
- (3, 사과, 3000원, 판매 O) 
데이터베이스를 다음과 같이 예시처럼 바꿔주었다.
// DTO
package com.group.libraryapp.dto.HW;
public class FruitStatResponse {
    private long salesAmount;
    private long notSalesAmount;
    // 생성자
    public FruitStatResponse(long salesAmount, long notSalesAmount) {
        this.salesAmount = salesAmount;
        this.notSalesAmount = notSalesAmount;
    }
    // 게터 및 세터
    public long getSalesAmount() {
        return salesAmount;
    }
    public void setSalesAmount(long salesAmount) {
        this.salesAmount = salesAmount;
    }
    public long getNotSalesAmount() {
        return notSalesAmount;
    }
    public void setNotSalesAmount(long notSalesAmount) {
        this.notSalesAmount = notSalesAmount;
    }
}
@GetMapping("/api/v1/fruit/stat")
    public FruitStatResponse getFruitStat(@RequestParam("name") String name) {
        String sql = "SELECT status, SUM(price) as totalAmount FROM fruits WHERE name = ? GROUP BY status";
        AtomicLong salesAmount = new AtomicLong(0);
        AtomicLong notSalesAmount = new AtomicLong(0);
        jdbcTemplate.query(sql, new Object[]{name}, (rs, rowNum) -> {
            String status = rs.getString("status");
            long totalAmount = rs.getLong("totalAmount");
            if ("SOLD".equals(status)) {
                salesAmount.addAndGet(totalAmount);
            } else if ("NOT_SOLD".equals(status)) {
                notSalesAmount.addAndGet(totalAmount);
            }
            return null;
        });
        return new FruitStatResponse(salesAmount.get(), notSalesAmount.get());
    }이렇게 DTO와 컨트롤러를 설정해주고 POSTMAN을 통해 실행해봤다.
다음처럼 상태에 따라 팔린 금액과 팔리지 않은 금액이 잘 반환되었다!
댓글을 작성해보세요.
