[인프런 워밍업 클럽] 과제6

Jiwon·2024년 5월 12일
0
post-thumbnail

📚 인프런 강의와 스터디를 바탕으로 한 과제를 풀이한 포스트입니다.

<문제> API 3단 분리하고 스프링 컨테이너 이용하기

<문제 1 풀이>

  • 기본적인 3단 분리의 틀은 과제 4와 동일하다.
  • 지난 과제 4에서 미리 3단 분리를 해둬서 작성한 코드에서 추가로 정리가 필요한 부분을 더 정리했다.

  • FruitRepository에서, id나 name을 받아 과일이 있는지 판단하는 sql문이 서로 다른 함수에서 중복 작성되었다. 같은 Respository 내에 다른 함수로 분리하여 작성했다.
    ** FruitRepository
    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();
    }
  • 파라미터로 받는 값이 id, name인지에 따라 쿼리문이 달라지기 때문에 오버로딩했다. 이렇게 함수를 분리하면 해당 함수가 사용되는 FruitServicegetFruit 함수와 updateFruit 함수는 다음과 같이 작성할 수 있다.
    ** FruitService
	// 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를 구현한 FruitMemoryRepositoryFruitMySqlRepository에 쓰이는 함수를 미리 선언해 두어야 한다.
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);

}
  • 기존에 작성한 FruitRepository 클래스는 sql문을 다루는 함수들을 포함하고 있으므로 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에 붙여서 saveFruitupdateFruit을 실행할 수 있도록 HTTP 요청 Body를 보냈을 때의 결과는 다음과 같다. 아래 사진처럼 콘솔 창에 문자열을 출력하고 있다.
  • @Primary 어노테이션을 FruitMySqlRepository에 붙였을 때는 기존의 코드와 같이 정상적으로 sql문이 실행되고 DB에 반영되었다.

<정리하며>

  • 역시 주어진 코드를 똑같이 따라 치는 것보다 직접 수정하는 것이 훨씬 시간이 오래 걸리고 많이 배우게 된다. 미리 3단 분리를 해두어서 아쉽기는 하지만, 그래도 기존의 코드를 더 깔끔하게 만들었다는 점은 뿌듯하다.
profile
écoute les murmures du cœur

0개의 댓글

관련 채용 정보