[인프런 워밍업 클럽 스터디 1기 과제 #6] API 분리

qk·2024년 5월 13일
post-thumbnail

과제 #4 에서 만들었던 API를 분리해보며, Controller - Service - Repository 계층에 익숙해져 봅시다!

기존 코드

FruitController.java

@RestController
public class FruitController {
    private final JdbcTemplate jdbcTemplate;

    public FruitController(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    @PostMapping("/api/v1/fruit")
    public void saveFruit(@RequestBody FruitAddRequest request){
        String sql = "INSERT INTO fruit (name, warehousingDate, price) VALUES (?, ?, ?)";
        jdbcTemplate.update(sql, request.getName(), request.getWarehousingDate(), request.getPrice());
    }

    @PutMapping("/api/v1/fruit")
    public void updateFruitStatus(@RequestBody FruitUpdateRequest request) throws IllegalAccessException {
        String readSql = "SELECT * FROM fruit WHERE id = ?";
        boolean isFruitNotExist = jdbcTemplate.query(readSql , (rs, rowNum) -> 0, request.getId()).isEmpty();
        if(isFruitNotExist){
            throw new IllegalAccessException();
        }
        String sql = "UPDATE fruit SET sold = 'Y' WHERE id = ?";
        jdbcTemplate.update(sql, request.getId());
    }

    @GetMapping("/api/v1/fruit/stat")
    public FruitResponse saleState (@RequestParam String name) throws IllegalAccessException {
        String readSql = "SELECT * FROM fruit WHERE name = ?";
        boolean isFruitNotExist = jdbcTemplate.query(readSql , (rs, rowNum) -> 0, name).isEmpty();
        if(isFruitNotExist){
            throw new IllegalAccessException();
        }

        String salesSql = "SELECT COALESCE(SUM(price), 0) FROM fruit WHERE name = ? and sold = 'Y'";
        String notSalesSql = "SELECT COALESCE(SUM(price), 0) FROM fruit WHERE name = ? and sold = 'N'";

        long salesAmount = jdbcTemplate.queryForObject(salesSql,Integer.class, name);
        long notSalesAmount = jdbcTemplate.queryForObject(notSalesSql,Integer.class, name);

        return new FruitResponse(salesAmount, notSalesAmount);
    }
}

❗문제점

Controller 하나에 모든 기능이 구현되어 있다.

문제 1

Controller - Service - Repository 분리
FruitController.java

@RestController
public class FruitController {
    private final FruitService fruitService;

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

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

    @PutMapping("/api/v1/fruit")
    public void updateFruit(@RequestBody FruitUpdateRequest request) {
        fruitService.updateFruit(request);
    }

    @GetMapping("/api/v1/fruit/stat")
    public FruitResponse salesStat(@RequestParam("name") String name){
        return fruitService.saleState(name);
    }
}

FruitService.java

@Service
public class FruitService {

    private final FruitRepository fruitRepository;

    public FruitService(FruitRepository fruitRepository) {
        this.fruitRepository = fruitRepository;
    }

    public void saveFruit(FruitAddRequest request){
        fruitRepository.saveFruit(request.getName(), request.getWarehousingDate(), request.getPrice());
    }

    public void updateFruit(FruitUpdateRequest request){
        boolean isFruitNotExist  = fruitRepository.findById(request.getId());
        if (isFruitNotExist) {
            throw new IllegalArgumentException("과일이 존재하지 않습니다.");
        }
        fruitRepository.updateFruit(request.getId());
    }

    public FruitResponse saleState(String name){
        boolean isFruitNotExist  = fruitRepository.findByName(name);
        if (isFruitNotExist) {
            throw new IllegalArgumentException("과일이 존재하지 않습니다.");
        }
        long salesAmount = fruitRepository.salesAmount(name);
        long notSalesAmount = fruitRepository.notSalesAmount(name);
        return new FruitResponse(salesAmount, notSalesAmount);
    }
}

FruitRepository.java

@Repository
public class FruitRepository {
    private final JdbcTemplate jdbcTemplate;

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

    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 boolean findById(long id){
        String readSql = "SELECT * FROM fruit WHERE id = ?";
        return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty();
    }

    public void updateFruit(long id) {
        String sql = "UPDATE fruit SET sold = 'Y' WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }

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

    public long salesAmount(String name) {
        String salesAmountSql = "SELECT COALESCE(SUM(price), 0) FROM fruit WHERE name = ? AND sold = 'Y'";
        return jdbcTemplate.queryForObject(salesAmountSql, Integer.class, name);
    }

    public long notSalesAmount(String name) {
        String notSalesAmountSql = "SELECT COALESCE(SUM(price), 0) FROM fruit WHERE name = ? AND sold = 'N'";
        return jdbcTemplate.queryForObject(notSalesAmountSql, Integer.class, name);
    }
}

문제 2

기존에 작성했던 FruitRepositoryFruitMemoryRepositoryFruitMySqlRepository로 나누고 @Primary 어노테이션을 활용해 두 Repository를 바꿔가며 동작시킬 수 있도록 코드를 변경해보기
FruitRepository.java

@Repository
public interface FruitRepository {

    public void saveFruit(String name, LocalDate warehousingDate, long price);

    public boolean findById(long id);

    public void updateFruit(long id);

    public boolean findByName(String name);

    public long salesAmount(String name);

    public long notSalesAmount(String name);


}

FruitMemoryRepository.java

@Repository
public class FruitMemoryRepository implements FruitRepository{

    public void saveFruit(String name, LocalDate warehousingDate, long price){
    }

    public boolean findById(long id){
        return false;
    }

    public void updateFruit(long id) {
    }

    public boolean findByName(String name){
        return false;
    }

    public long salesAmount(String name) {
        return 0;
    }

    public long notSalesAmount(String name) {
        return 0;
    }
}

❗오류 발생

🔨오류 해결

@Primary 어노테이션 사용
FruitMySqlRepository.java

@Primary
@Repository
public class FruitMySqlRepository implements FruitRepository {
    private final JdbcTemplate jdbcTemplate;

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

    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 boolean findById(long id){
        String readSql = "SELECT * FROM fruit WHERE id = ?";
        return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty();
    }

    public void updateFruit(long id) {
        String sql = "UPDATE fruit SET sold = 'Y' WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }

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

    public long salesAmount(String name) {
        String salesAmountSql = "SELECT COALESCE(SUM(price), 0) FROM fruit WHERE name = ? AND sold = 'Y'";
        return jdbcTemplate.queryForObject(salesAmountSql, Integer.class, name);
    }

    public long notSalesAmount(String name) {
        String notSalesAmountSql = "SELECT COALESCE(SUM(price), 0) FROM fruit WHERE name = ? AND sold = 'N'";
        return jdbcTemplate.queryForObject(notSalesAmountSql, Integer.class, name);
    }

}

출처

자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]

0개의 댓글