[워밍업 클럽 BE-0기] 6일차 과제 - 레이어 나누기와 @Primary 사용해보기

[워밍업 클럽 BE-0기] 6일차 과제 - 레이어 나누기와 @Primary 사용해보기

오늘 과제는 Controller 코드를 3 계층으로 나누는 것과 FruitMemoryReposiotry와 FruitMySqlRepository라는 두 개의 구현체 중에 하나만을 주입받아 사용하도록 하는 것이었습니다.

 

하지만 저는 이전의 과제에서부터 계층을 나누어서 코드를 작성했기 때문에 그 부분은 생략하고 Repository쪽의 코드를 확인해보겠습니다. 처음부터 JPA를 사용했으므로, 새로운 코드가 필요했습니다. 따라서 아래와 같이 작성했습니다.

 

FruitRepository

public interface FruitRepository {

	void save(FruitCreateRequest request);

	void updateStatus(Long id);

	Map<String, Long> statForFruit(String name);
}

 

FruitMySqlRepository

@Repository
@Primary
public class FruitMysqlRepository implements FruitRepository {

	private final JdbcTemplate jdbcTemplate;

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


	@Override
	public void save(FruitCreateRequest request) {
		String sql = "insert into fruit (name, warehousing_date, price) values(?, ?, ?)";
		jdbcTemplate.update(sql, request.getName(), request.getWarehousingDate(), request.getPrice());
	}

	@Override
	public void updateStatus(Long id) {
		String sql = "update fruit set is_sold = CASE WHEN is_sold = 0 THEN 1 ELSE 0 END WHERE id = ?";
		jdbcTemplate.update(sql, id);
	}

	@Override
	public Map<String, Long> statForFruit(String name) {
		String sql = "SELECT \n"
				+ "    SUM(CASE WHEN is_sold = 1 THEN price ELSE 0 END) AS salesAmount,\n"
				+ "    SUM(CASE WHEN is_sold = 0 THEN price ELSE 0 END) AS notSalesAmount\n"
				+ "FROM\n"
				+ "    fruit\n"
				+ "WHERE \n"
				+ "    name = ?;";

		Map<String, Object> result = jdbcTemplate.queryForMap(sql, name);

		Map<String, Long> stats = new HashMap<>();
		stats.put("salesAmount", ((Number)result.get("salesAmount")).longValue());
		stats.put("notSalesAmount", ((Number)result.get("notSalesAmount")).longValue());
		return stats;
	}
}

 

FruitMemoryRepository

@Repository
public class FruitMemoryRepository implements FruitRepository {
	private List<Fruit> fruits = new ArrayList<>();
	private Long id = 1L;

	@Override
	public void save(FruitCreateRequest request) {
		Fruit fruit = new Fruit(id++, request.getName(), request.getWarehousingDate(), request.getPrice());
		fruits.add(fruit);
		System.out.println(fruits.size());
	}

	@Override
	public void updateStatus(Long id) {
		Fruit fruit = fruits.stream().filter(f -> Objects.equals(f.getId(), id))
				.findFirst().orElseThrow(IllegalArgumentException::new);
		fruit.changeStatus();
	}

	@Override
	public Map<String, Long> statForFruit(String name) {
		List<Fruit> fruitsByName = fruits.stream()
				.filter(f -> f.getName().equals(name)).collect(Collectors.toList());

		Long salesAmount = fruitsByName.stream().filter(Fruit::getSold)
				.mapToLong(Fruit::getPrice).sum();

		Long notSalesAmount = fruitsByName.stream().filter(f -> !f.getSold())
				.mapToLong(Fruit::getPrice).sum();

		HashMap<String, Long> result = new HashMap<>();
		result.put("salesAmount", salesAmount);
		result.put("notSalesAmount", notSalesAmount);
		return result;
	}
}

저는 현재 FruitMySqlRepository에 @Primary 어노테이션을 달아주었습니다. 그러면 과연, 스프링 컨테이너가 띄워졌을 때, FruitMySqlRepository가 구현체로 등록되었는 지 확인해보겠습니다. 이를 위해 아래와 같은 코드를 사용했습니다.

@SpringBootApplication
public class LibraryAppApplication {

  public static void main(String[] args) {
    ConfigurableApplicationContext applicationContext = SpringApplication.run(LibraryAppApplication.class, args);

    FruitRepository bean = applicationContext.getBean(FruitRepository.class);
    System.out.println("주입된 FruitRepository의 구현체: " + bean.getClass().getSimpleName());
  }
}

스프링 부트 애플리케이션을 실행하고 난 뒤 FruitRepository에 주입되어있는 빈의 이름을 가져오게 했는데요. 실행 결과는 아래와 같습니다.

image

FruitMySqlRepository가 잘 주입된 것을 확인할 수 있습니다. 다시 FruitMemoryRepository에 @Primary 를 달아주면!

image

역시 잘 확인할 수 있었네요. 그런데... 저 뒤에 EnhancerBySpringCGLIB~~ 라는 놈은 무엇일까요?

더 깊게 알아보아야할 것을 얻은 것 같습니다. 6일차 과제 이상입니다.

댓글을 작성해보세요.