4번째 과제 [인프런 워밍업 클럽 스터디 BE 1주차]

heisje·2024년 2월 22일
0
post-thumbnail
post-custom-banner

문제

우리는 작은 과일 가게를 운영하고 있습니다. 과일 가게에 입고된 '과일 정보'를 저장하는 API를 만들어 봅시다. 스펙은 다음과 같습니다.

  • HTTP method: POST
  • HTTP path : /api/v1/fruit
  • HTTP 요청 Body
{
	"name" : String,
  	"warehousingDate" : LocalDate,
  	"price" : long
}
  • 응답: 성공 시 200

API에서 long을 사용하는 이유

feat-GPT…

  1. 큰 수치 처리: int 타입은 최대 약 21억 (2^31 - 1)까지의 값을 저장할 수 있습니다. 반면, long 타입은 약 922경 (2^63 - 1)까지 저장할 수 있습니다. 금융이나 상업 분야에서는 종종 매우 큰 수치를 다루어야 하며, int의 범위가 부족할 수 있습니다.
  2. 통화 단위와 소수점: 특히 통화를 다룰 때, 소수점 이하의 값까지 고려해야 할 수 있습니다. 예를 들어, 가격이 센트 단위까지 포함되어 있다면, 실제 가격을 저장하기 위해 실제 금액에 100을 곱한 값(센트로 변환된 값)을 저장하는 방법을 사용할 수 있습니다. 이 경우 더 큰 수의 저장 공간이 필요할 수 있습니다.
  3. 안전 마진: long을 사용하면 값의 오버플로우(값이 너무 커서 변수의 저장 범위를 넘어서는 상황) 위험을 줄일 수 있습니다. 이는 특히 금융 애플리케이션에서 중요한 고려 사항입니다.
  4. 다양한 화폐 단위: 서로 다른 나라의 화폐 단위가 크게 다를 수 있으며, 어떤 나라의 화폐 단위는 매우 작은 값이지만 다른 나라에서는 매우 큰 값일 수 있습니다. long을 사용하면 이러한 다양성을 더 잘 수용할 수 있습니다.
  5. 일관성과 확장성: 시스템을 설계할 때 향후 확장 가능성을 고려해야 합니다. 초기에는 int 범위 내에서 충분할지라도, 비즈니스가 성장하면서 더 큰 수치를 다루어야 할 필요성이 생길 수 있습니다. 처음부터 long을 사용함으로써 나중에 발생할 수 있는 범위 문제를 예방할 수 있습니다.

+추가

박싱타입(Integer, Long)과 기본타입(int, long)의 가장 큰 차이점은 null을 사용할 수 있냐 없냐 차이이다.

제출 중 겪었던 문제

문제1. 저장은 정확히 되나 404에러가 난다.

{
    "timestamp": "2024-02-22T14:35:37.716+00:00",
    "status": 404,
    "error": "Not Found",
    "path": "/api/v2/fruit"
}

해결방법1. @RestController 대신 @Controller로 작성했다.

문제2. 데이터가 정확히 들어오지 않아도 저장이 잘 됐다.

(nam:”사과”) 처럼 오타를 내도 값이 null로 들어갔다.

문제3. queryForObject를 사용하긴 했지만 .아직 이해를 못했다.

제출코드 총합

query

2번 이후 is_sale을 추가했다.

CREATE DATABASE library;

USE library;
SHOW TABLES;

CREATE TABLE fruit(
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(20),
    warehousingDate DATETIME,
    price INT,
    is_saled BOOLEAN DEFAULT true
);

select * from fruit;

DROP TABLE fruit;

FruitController

package com.group.libraryapp.controller;

import com.group.libraryapp.dto.FruitRequest;
import com.group.libraryapp.service.FruitService;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/api/v2")
public class FruitController {

    JdbcTemplate jdbcTemplate;
    FruitService fruitService;

    public FruitController(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        this.fruitService = new FruitService(jdbcTemplate);
    }

    @PostMapping("/fruit")
    public void saveFruit(@RequestBody FruitRequest request){
        fruitService.saveFruit(request);
    }

    @PutMapping("/fruit")
    public void sellFruit(@RequestParam long id){
        fruitService.sellFruit(id);
    }

    @GetMapping("/fruit")
    public Map<String, Long> getFruit(@RequestParam String name){
        return fruitService.findFruit(name);
    }

}

FruitRequest

package com.group.libraryapp.dto;

import java.time.LocalDate;

public class FruitRequest {
    private final String name;
    private final LocalDate warehousingDate;
    private final long price;

    public FruitRequest(String name, LocalDate warehousingDate, long price) {
        this.name = name;
        this.warehousingDate = warehousingDate;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public LocalDate warehousingDate() {
        return warehousingDate;
    }

    public long getPrice() {
        return price;
    }
}

FruitService

package com.group.libraryapp.service;

import com.group.libraryapp.dto.FruitRequest;
import com.group.libraryapp.repository.FruitRepositroy;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class FruitService {

    private final FruitRepositroy fruitRepositroy;

    public FruitService(JdbcTemplate jdbcTemplate) {
        fruitRepositroy = new FruitRepositroy(jdbcTemplate);
    }

    public void saveFruit(FruitRequest request){
        fruitRepositroy.saveFruit(request.getName(), request.warehousingDate(), request.getPrice());
    }

    public void sellFruit(long id){
        if (fruitRepositroy.isExistFruit(id)){
            throw new IllegalArgumentException();
        }
        fruitRepositroy.sellFruit(id);
    }

    public Map<String, Long> findFruit(String name){
        long salesAmount = fruitRepositroy.calculateSalesAmount(name);
        long notSalesAmount = fruitRepositroy.calculateNotSalesAmount(name);
        Map<String, Long> response = new HashMap<>();
        response.put("salesAmount", salesAmount);
        response.put("notSalesAmount", notSalesAmount);
        return response;
    }
}

FruitRepository

package com.group.libraryapp.repository;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.time.LocalDate;

@Repository
public class FruitRepositroy {

    JdbcTemplate jdbcTemplate;

    public FruitRepositroy(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public boolean isExistFruit(long id){
        String readSql = "SELECT * FROM fruit WHERE id = ?";
        return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty();
    }

    public void saveFruit(String name, LocalDate warehousingDate, long price){
        String sql = "INSERT INTO fruit (name, warehousingDate, price) VALUES (?, ?, ?)";
        jdbcTemplate.update(sql, name, warehousingDate, price);
    }

    public void sellFruit(long id){
        String sql = "UPDATE fruit set is_sale = false where id = ?";
        jdbcTemplate.update(sql, id);
    }

    public long calculateSalesAmount(String name) {
        String sql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sale = true";
        return jdbcTemplate.queryForObject(sql, new Object[]{name}, Long.class);
    }

    public long calculateNotSalesAmount(String name) {
        String sql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sale = false";
        return jdbcTemplate.queryForObject(sql, new Object[]{name}, Long.class);
    }
}

3일차 과제 [인프런 워밍업 클럽 스터디 BE 1주차]

인프런워밍업클럽스터디백엔드

profile
김희제의 기술블로그
post-custom-banner

0개의 댓글