[WarmingUp 06] 관심사 분리

밍디·2024년 5월 13일

WarmingUp

목록 보기
6/7
post-thumbnail

기존

@RestController
public class FruitController {
    private final JdbcTemplate jdbcTemplate;

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

    @PostMapping("/api/v1/fruit")
    public void saveFruit(@RequestBody FruitRequestDTO 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 updateFruit(@RequestBody FruitRequestDTO request) {
        String readSql = "SELECT * FROM fruit WHERE id = ?";
        boolean isFruitNotExist  = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, request.getId()).isEmpty();

        if(isFruitNotExist) {
            throw new IllegalArgumentException();
        }

        String sql = "UPDATE fruit SET soleOutYN = 'Y' WHERE id = ?";
        jdbcTemplate.update(sql, request.getId());
    }

    @GetMapping("/api/v1/fruit/stat")
    public FruitResponseDTO salesStat(@RequestParam String name){
        String readSql = "SELECT * FROM fruit WHERE name = ?";
        boolean isFruitNotExist  = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, name).isEmpty();

        if(isFruitNotExist) {
            throw new IllegalArgumentException();
        }

        String salesAmountSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND soleOutYN = 'Y'";
        String notSalesAmountSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND soleOutYN = 'N'";
        long salesAmount = jdbcTemplate.queryForObject(salesAmountSql, long.class, name);
        long notSalesAmount = jdbcTemplate.queryForObject(notSalesAmountSql, long.class, name);

        return new FruitResponseDTO(salesAmount, notSalesAmount);
    }

}

비즈니스 로직, db 연결이 다 같이 있음...

문제 1

Controller-Service-Repository 분리

Controller

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

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

    @PostMapping
    public void saveFruit(@RequestBody FruitRequestDTO request){
        fruitService.saveFruit(request);
    }

    @PutMapping
    public void updateFruit(@RequestBody FruitRequestDTO request) {
        fruitService.updateFruit(request);
    }

    @GetMapping("/stat")
    public FruitResponseDTO salesStat(@RequestParam("name") String name){
        return fruitService.salesStat(name);
    }

}

Service

@Service
public class FruitService {
    private final FruitRepository fruitRepository;

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

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

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

    public FruitResponseDTO salesStat(String name){
        boolean isFruitNotExist  = fruitRepository.findByName(name);
        if(isFruitNotExist) {throw new IllegalArgumentException("과일이 존재하지 않습니다.");}

        long salesAmount = fruitRepository.salesAmount(name);
        long notSalesAmount = fruitRepository.noSalesAmount(name);
        return new FruitResponseDTO(salesAmount, notSalesAmount);
    }

}

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 fruits (name, warehousingDate, price) VALUES (?, ?, ?)";
        jdbcTemplate.update(sql, name, warehousingDate, price);
    }

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

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

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

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

    public long noSalesAmount(String name) {
        String notSalesAmountSql = "SELECT SUM(price) FROM fruits WHERE name = ? AND soldYN = 'N'";
        return jdbcTemplate.queryForObject(notSalesAmountSql, long.class, name);
    }

}

문제 2

FruitRepository를 FruitMemoryRepository와 FruitMySqlRepository로 나누고 @Primary 사용해보기 (+@Qualifier)

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 noSalesAmount(String name);

}

FruitMemoryRepository와 FruitMySqlRepository가 FruitRepository 인터페이스를 구현하도록 만들고

서버를 실행하면?

Service에서 어떤 FruitRepository 가져와야할지 몰라 에러가 난다.

@Primary

FruitMySqlRepository 위에 @Primary를 붙여주면?


로그를 찍어보면 FruitMySqlRepository가 들어와 있는걸 확인할 수 있다.
@Primary는 여러개의 인스턴스 중 하나를 결정해서 빈으로 등록한다.


@Qualifier

@Qualifier는 빈에 이름을 줘서 특정 빈을 가져올 때 사용한다.

@Qualifier("mainRepo")
@Repository
public class FruitMemoryRepository implements FruitRepository {...}

FruitMemoryRepository에 @Qualifier를 사용해서 'mainRepo' 라는 이름을 부여한다.

public FruitService(@Qualifier("mainRepo") FruitRepository fruitRepository) {
	log.error(fruitRepository.toString());
    this.fruitRepository = fruitRepository;
}

FruitRepository 의존성을 주입할 때 @Qualifier를 사용해서 'mainRepo' 이름을 가진 빈을 가져온다.


로그를 찍어보면 FruitMemoryRepository가 들어오는걸 있는걸 확인할 수 있다.


@Primary와 @Qualifier를 동시에 사용하면?

@Primary
@Repository
public class FruitMySqlRepository implements FruitRepository {}

vs

@Qualifier("mainRepo")
@Repository
public class FruitMemoryRepository implements FruitRepository {}

@Qualifier가 이김

0개의 댓글