과제 4. API

과제 4. API

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. (1, 사과, 3000원, 판매 O)

  2. (2, 사과, 4000원, 판매 X)

  3. (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을 통해 실행해봤다.

다음처럼 상태에 따라 팔린 금액과 팔리지 않은 금액이 잘 반환되었다!

댓글을 작성해보세요.

채널톡 아이콘