본문 바로가기
activity/인프런 워밍업 클럽

[인프런 워밍업 클럽 0기] 여섯 번째 과제 - API 역할 분리(Controller, Service, Repository)

by dani0312 2024. 2. 25.

강의

스프링 컨테이너의 의미와 사용 방법

Controller를 인스턴스화하지 않고도 메서드를 호출할 수 있었던 이유, JdbcTemplate설정을 해주지 않아도 사용할 수 있었던 이유에 대해 학습하였다. `@RestController` 이 어노테이션 때문이었다. 이것이 UserController를 API의 진입 지점으로 만들어 줄 뿐만 아니라, 이 클래스를 스프링 빈으로 등록시킨다. 서버가 시작할 때 스프링 컨테이너를 만들고 이 컨테이너에 스프링 빈을 넣으며 이때 인스턴스화도 함께 이뤄진다는 것에 대해 배울 수 있었다. 


과제

진도표 6일차와 연결됩니다

우리는 스프링 컨테이너의 개념을 배우고, 기존에 작성했던 Controller 코드를 3단 분리해보았습니다. 앞으로 API를 개발할 때는 이 계층에 맞게 각 코드가 작성되어야 합니다! 🙂

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

 

◾문제1

 

 

◾풀이

서론

강의 4일차의 과제는 아래와 같다. 과일 정보를 등록하는 POST API, 팔린 과일을 기록하는 PUT API, 그리고 특정 과일에 대한 팔린 금액과 팔리지 않은 금액의 합을 조회하는 GET API 를 개발하였다.  

2024.02.22 - [activity/인프런 워밍업 클럽] - [인프런 워밍업 클럽 0기] 네 번째 과제 - API 개발하기

 

프로젝트 구조는 아래와 같이 되어있다. controller에서 모든 역할을 다하고 있으며 데이터 응답과 요청에 대한 데이터를 다루는 dto패키지가 있다.  

controller에서 모든 역할을 하던 것을 repository와 service 클래스를 만들어 3개의 역할로 구분할 것이다.

Controller > Service > Repostiroy
Controller는 Service를 필요로 하고, Service는 Repository를 필요로 하고, Repository는 JdbcTemplate을 필요로 한다.

 

강의에서 진행한 UserController를 3단분리한 것을 살펴보면 아래와 같다. 

A → B의 구조는 A는 B를 필요로 한다. 즉 A는 B를 의존하고 있는 것이다.

 

UserController → UserSerivce

UserSerivce → UserRepository

UserRepository → JdbcTemplate

 

그리하여 각 클래스들이 필요로 하는 것을 필드로 선언해두고, 이를 각 클래스의 생성자에 매개변수로 두어 필요한 것을 이 생성자가 만들어질 때 주입받도록 한다. 이렇게 의존관계를 설정해서 주입받을 수 

 

3단 분리

DTO는 4일차 과제에서 전혀 변경된 사항이 없다.

 

🔻controller

@RestController
@RequestMapping("/api/v1")
public class FruitController {
    private final FruitService fruitService;

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

    // 과일 정보 저장 API
    @PostMapping("/fruit")
    public void saveFruit(@RequestBody FruitCreateRequest request){
        fruitService.saveFruit(request.getName(),request.getWarehousingDate(),request.getPrice());
    }

    // 과일 정보 변경 API
    @PutMapping("/fruit")
    public void updateFruit(@RequestBody FruitUpdateRequest request) {
        fruitService.updateFruit(request.getId());
    }

    // 팔린 금액과 팔리지 않은 금액 조회 API
    @GetMapping("/fruit/stat")
    public GetSalesAmountResponse getSalesAmount(@RequestParam String name){
        return fruitService.getSalesAmount(name);
    }
}

 

🔻service

@Service
public class FruitService {
    private final FruitRepository fruitRepository;

    public FruitService(FruitRepository fruitRepository) {
        this.fruitRepository = fruitRepository;
    }
    public void saveFruit(String name, LocalDate warehousingDate,long price){
        fruitRepository.saveFruit(name,warehousingDate,price);
    }
    public void updateFruit(long id){
        fruitRepository.updateFruit(id);
    }
    public GetSalesAmountResponse getSalesAmount(String name){
        return fruitRepository.getSalesAmount(name);
    }
}

 

🔻repository

@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 void updateFruit(long id){
        String sql = "UPDATE fruit SET is_sold = 1 WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }
    public GetSalesAmountResponse getSalesAmount(String name){
        String soldSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = 1";
        String notSoldSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = 0";

        long soldPrice = jdbcTemplate.queryForObject(soldSql,long.class,name);
        long notSoldPrice = jdbcTemplate.queryForObject(notSoldSql, long.class, name);

        return new GetSalesAmountResponse(soldPrice,notSoldPrice);
    }
}

 

API 테스트

3단 분리 후 로직이 정상 작동하는지 테스트해보았다.

✔️과일 등록 API 확인

 

 

 

✔️과일 판매 기록 API 확인

 

 

✔️과일 판매/미판매 총액 API 확인


◾문제2

 

 

◾풀이

🔻service

@Service
public class FruitService {
    private final FruitRepository fruitRepository;

    public FruitService(FruitRepository fruitRepository) {
        this.fruitRepository = fruitRepository;
    }
    public void saveFruit(String name, LocalDate warehousingDate,long price){
        fruitRepository.saveFruit(name,warehousingDate,price);
    }
    public void updateFruit(long id){
        fruitRepository.updateFruit(id);
    }
    public GetSalesAmountResponse getSalesAmount(String name){
        return fruitRepository.getSalesAmount(name);
    }
}

 

🔻repository

@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 void updateFruit(long id){
        String sql = "UPDATE fruit SET is_sold = 1 WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }
    public GetSalesAmountResponse getSalesAmount(String name){
        String soldSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = 1";
        String notSoldSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = 0";

        long soldPrice = jdbcTemplate.queryForObject(soldSql,long.class,name);
        long notSoldPrice = jdbcTemplate.queryForObject(notSoldSql, long.class, name);

        return new GetSalesAmountResponse(soldPrice,notSoldPrice);
    }
}

 

 

@Primary
@Repository
public class FruitMemoryRepository implements FruitRepository{
    private List<Fruit> fruits;
    private long id = 1;

    public FruitMemoryRepository(List<Fruit> fruits) {
        this.fruits = fruits;
    }
    public void saveFruit(String name, LocalDate warehousingDate, long price){
        Fruit fruit = new Fruit(id++,name, warehousingDate, price);
        fruits.add(fruit);
        System.out.println("fruit = " + fruit);
    }
    public void updateFruit(long id){
        for (Fruit fruit : fruits) {
            if(fruit.getId()==id)
                fruit.setIsSold(true);
        }
    }
    public GetSalesAmountResponse getSalesAmount(String name){

        long soldPrice = 0;
        long notSoldPrice = 0;

        for (Fruit fruit : fruits) {
            if (fruit.getName().equals(name)) {
                if(fruit.isSold())
                    soldPrice += fruit.getPrice();
                else
                    notSoldPrice += fruit.getPrice();
            }
        }
        return new GetSalesAmountResponse(soldPrice,notSoldPrice);
    }
}

 

 

 

 


Reference

자바와 스프링 부트로 생애 최초 서버 만들기 [서버 개발 올인원 패키지] / 최태현 / 인프런 강의

 


/* 내가 추가한 코드 */ /* 내가 추가한 코드 끝끝 */