📚 인프런 강의와 스터디를 바탕으로 한 과제를 풀이한 포스트입니다.
<문제> API 3단 분리하고 스프링 컨테이너 이용하기
<문제 1 풀이>
FruitRepository
에서, id나 name을 받아 과일이 있는지 판단하는 sql문이 서로 다른 함수에서 중복 작성되었다. 같은 Respository 내에 다른 함수로 분리하여 작성했다. public boolean isFruitNotExist(long id) {
String readSql = "SELECT * FROM fruit WHERE id = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty();
}
public boolean isFruitNotExist(String name) {
String readSql = "SELECT * FROM fruit WHERE name = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, name).isEmpty();
}
FruitService
의 getFruit
함수와 updateFruit
함수는 다음과 같이 작성할 수 있다. // R
public FruitResponse getFruit(String name) {
if (fruitRepository.isFruitNotExist(name)) {
throw new IllegalArgumentException();
}
return fruitRepository.getFruit(name);
}
// U
public void updateFruit(FruitUpdateRequest request) {
if (fruitRepository.isFruitNotExist(request.getId())) {
throw new IllegalArgumentException();
}
fruitRepository.updateFruit(request.getId());
}
<실행 결과>
과일이 판매되었음을 체크하는 id: 3의 purchase 여부가 1로 변경되어 반영되었다.
입력한 과일 이름에 해당하는 데이터 중 판매 여부에 따른 판매액을 보여주는 getFruit
기능도 정상 작동하고 있다.
<문제 2 풀이> FruitRepository를 FruitMemoryRepository와 FruitMySqlRepository로 나누기
FruitRepository
를 만든다. FruitRepository
를 구현한 FruitMemoryRepository
와 FruitMySqlRepository
에 쓰이는 함수를 미리 선언해 두어야 한다.package com.group.libraryapp.repository.user;
import com.group.libraryapp.dto.user.response.FruitResponse;
import java.time.LocalDate;
public interface FruitRepository {
public boolean isFruitNotExist(long id);
public boolean isFruitNotExist(String name);
public void saveFruit(String name, LocalDate warehousingDate, long price);
public void updateFruit(long id);
public FruitResponse getFruit(String name);
}
FruitMySqlRepository
로 이름을 바꿨다. 인터페이스에서 미리 선언한 함수들을 @Override
뒤에 작성해 주었다.package com.group.libraryapp.repository.user;
import com.group.libraryapp.dto.user.request.FruitGroupby;
import com.group.libraryapp.dto.user.response.FruitResponse;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.time.LocalDate;
import java.util.List;
@Repository
public class FruitMySqlRepository implements FruitRepository {
private final JdbcTemplate jdbcTemplate;
public FruitMySqlRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public boolean isFruitNotExist(long id) {
String readSql = "SELECT * FROM fruit WHERE id = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty();
}
@Override
public boolean isFruitNotExist(String name) {
String readSql = "SELECT * FROM fruit WHERE name = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, name).isEmpty();
}
@Override
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 void updateFruit(long id) {
String sql = "UPDATE fruit SET purchase = true WHERE id = ?";
jdbcTemplate.update(sql, id);
}
@Override
public FruitResponse getFruit(String name) {
long salesAmount = 0;
long notSalesAmount = 0;
String sumsql = "SELECT purchase, sum(price) as total_price\n" +
"FROM fruit\n" +
"WHERE name = ? GROUP BY purchase";
List<FruitGroupby> responses = jdbcTemplate.query(sumsql, (rs, rowNum) -> {
boolean purchase = rs.getBoolean("purchase");
long total_price = rs.getLong("total_price");
return new FruitGroupby(purchase, total_price);
}, name);
for (FruitGroupby response : responses) {
if (response.isPurchase()) {
salesAmount += response.getTotal_price();
}
else {
notSalesAmount += response.getTotal_price();
}
}
return new FruitResponse(salesAmount, notSalesAmount);
}
}
@Primary
어노테이션을 붙여 Repository를 변경하며 동작하는 것을 확인하기 위해 FruitMemoryRepository
도 만든다. 리턴 타입에 따라 boolean의 경우 false를 리턴하고, void의 경우 실행 후 콘솔 창에 문자열을 출력하도록 했다. 객체를 리턴하는 경우에는 임의로 0을 넣어주었다.import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.time.LocalDate;
@Repository
@Primary
public class FruitMemoryRepository implements FruitRepository {
private final JdbcTemplate jdbcTemplate;
public FruitMemoryRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public boolean isFruitNotExist(long id) {
return false;
}
@Override
public boolean isFruitNotExist(String name) {
return false;
}
@Override
public void saveFruit(String name, LocalDate warehousingDate, long price) {
System.out.println("saveFruit of Fruit Memory Repository");
}
@Override
public void updateFruit(long id) {
System.out.println("updateFruit of Fruit Memory Repository");
}
@Override
public FruitResponse getFruit(String name) {
return new FruitResponse(0, 0);
}
}
<실행 결과>
@Primary
어노테이션을 FruitMemoryRepository
에 붙여서 saveFruit
과 updateFruit
을 실행할 수 있도록 HTTP 요청 Body를 보냈을 때의 결과는 다음과 같다. 아래 사진처럼 콘솔 창에 문자열을 출력하고 있다.@Primary
어노테이션을 FruitMySqlRepository
에 붙였을 때는 기존의 코드와 같이 정상적으로 sql문이 실행되고 DB에 반영되었다.<정리하며>