주제
스프링 컨테이너의 개념을 배우고, 기존에 작성했던 Controller 코드를 3단 분리해보았다.
앞으로 API를 개발할 때는 이 계층에 맞게 각 코드가 작성되어야 한다
#4 에서 만들었던 API를 분리해보며, Controller - Service - Repository 계층에 익숙해져 보자!
주요 내용
키워드
3단 분리
3단 분리
요구사항 1
FruitController
package com.group.libraryapp.controller.fruit;
@RequestMapping("/api/v1/fruit")
@RestController
public class FruitController {
private final FruitService fruitService;
public FruitController(FruitService fruitService) {
this.fruitService = fruitService;
}
@PostMapping
public void saveFruit(@RequestBody FruitRequest request) {
fruitService.saveFruit(request);
}
@PutMapping
public void updateFruit(@RequestBody FruitRequest request) {
fruitService.updateFruit(request);
}
@GetMapping("/status")
public FruitResponse getFruitStatus(@RequestParam String name) {
return fruitService.getFruitStatus(name);
}
}
Java
복사
FruitService
package com.group.libraryapp.service.fruit;
@Service
public class FruitService {
private final FruitRepository fruitRepository;
public FruitService(FruitRepository fruitRepository) {
this.fruitRepository = fruitRepository;
}
public void saveFruit(FruitRequest request) {
fruitRepository.saveFruit(request.getName(), request.getWarehousingDate(), request.getPrice());
}
public void updateFruit(FruitRequest request) {
fruitRepository.updateFruit(request.getId());
}
public FruitResponse getFruitStatus(String name) {
return fruitRepository.getFruitStatus(name);
}
}
Java
복사
FruitRepository
package com.group.libraryapp.repository.fruit;
@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 readSql = "SELECT * FROM fruit WHERE id = ?";
boolean isFruitNotExit = jdbcTemplate.query(readSql, (rs, rowNum) -> 0,id).isEmpty();
//존재 하지않는다면 예외처리
if (isFruitNotExit) {
throw new IllegalStateException();
}
//있으면 업데이트
String sql = "UPDATE fruit SET status = 'SOLD' WHERE id = ?";
jdbcTemplate.update(sql, id);
}
public FruitResponse getFruitStatus(String name) {
// 과일 이름을 기준으로 데이터베이스에서 과일 정보를 조회
String readFruitInfo = "SELECT * FROM fruit WHERE name = ?";
// 해당 과일이 존재하지 않는 경우 예외 처리
boolean isFruitNotExist = jdbcTemplate.query(readFruitInfo, (rs, rowNum) -> 0, name).isEmpty();
if (isFruitNotExist) {
throw new IllegalStateException();
}
// 과일이 판매된 총액을 조회
String soldSql = "SELECT COALESCE(SUM(price), 0) FROM fruit WHERE name = ? AND status = 'SOLD'";
// 과일이 판매되지 않은 총액을 조회
String notSoldSql = "SELECT COALESCE(SUM(price), 0) FROM fruit WHERE name = ? AND status = 'NOT_SOLD'";
// 조회 결과를 각 변수에 저장
long salesAmount = jdbcTemplate.queryForObject(soldSql, Long.class, name);
long notSalesAmount = jdbcTemplate.queryForObject(notSoldSql, Long.class, name);
// 조회된 금액 정보를 반환
return new FruitResponse(salesAmount, notSalesAmount);
}
}
Java
복사
요구사항 2
정리 하면서 서비스랑 레파지토리 더 쪼개서 나눠봄.
getFruitStatus쪽도 더 나눌 수 있을거 같다.
FruitService
package com.group.libraryapp.service.fruit;
@Service
public class FruitService {
private final FruitRepository fruitRepository;
public FruitService(FruitRepository fruitRepository) {
this.fruitRepository = fruitRepository;
}
public void saveFruit(FruitRequest request) {
fruitRepository.saveFruit(request.getName(), request.getWarehousingDate(), request.getPrice());
}
public void updateFruit(FruitRequest request) {
if (fruitRepository.isFruitNotExist(request.getId())) {
throw new IllegalArgumentException("과일을 찾을 수 없습니다");
}
fruitRepository.updateFruit(request.getId());
}
public FruitResponse getFruitStatus(String name) {
if(fruitRepository.isFruitNotExist(name)){
throw new IllegalArgumentException();
}
return fruitRepository.getFruitStatus(name);
}
}
Java
복사
FruitRepository
package com.group.libraryapp.repository.fruit;
public interface FruitRepository {
void saveFruit(String name, LocalDate warehousingDate, long price);
boolean isFruitNotExist(long id);
boolean isFruitNotExist(String name);
void updateFruit(long id);
FruitResponse getFruitStatus(String name);
}
Java
복사
FruitMySqlRepository
package com.group.libraryapp.repository.fruit;
@Repository
@Primary
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);
}
@Override
public boolean isFruitNotExist(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 status = 'SOLD' WHERE id = ?";
jdbcTemplate.update(sql, id);
}
public boolean isFruitNotExist(String name){
String readFruitInfo = "SELECT * FROM fruit WHERE name = ?";
return jdbcTemplate.query(readFruitInfo, (rs, rowNum) -> 0, name).isEmpty();
}
public FruitResponse getFruitStatus(String name) {
// 과일이 판매된 총액을 조회
String soldSql = "SELECT COALESCE(SUM(price), 0) FROM fruit WHERE name = ? AND status = 'SOLD'";
// 과일이 판매되지 않은 총액을 조회
String notSoldSql = "SELECT COALESCE(SUM(price), 0) FROM fruit WHERE name = ? AND status = 'NOT_SOLD'";
// 조회 결과를 각 변수에 저장
long salesAmount = jdbcTemplate.queryForObject(soldSql, Long.class, name);
long notSalesAmount = jdbcTemplate.queryForObject(notSoldSql, Long.class, name);
// 조회된 금액 정보를 반환
return new FruitResponse(salesAmount, notSalesAmount);
}
}
Java
복사
FruitMemoryRepository
package com.group.libraryapp.repository.fruit;
@Repository
public class FruitMemoryRepository implements FruitRepository {
public void saveFruit(String name, LocalDate warehousingDate, long price) {}
@Override
public boolean isFruitNotExist(long id) {
return false;
}
@Override
public boolean isFruitNotExist(String name) {
return false;
}
public void updateFruit(long id) {}
public FruitResponse getFruitStatus(String name) {
return null;
}
}
Java
복사
참고
챗GPT