기본 콘텐츠로 건너뛰기

8월, 2018의 게시물 표시

냉장고 가계부 프로젝트 8

FoodController의 메서드들은 요청 HTTP 방식으로 매핑하며, 응답결과에 HTTP상태코드와 본문을 실어서 보내는 방식을 사용하고 있다. Spring HATEOAS는 이 응답결과에 서버에 요청을 할 수 있는 URI를 같이 보내주어 클라이언트에서는 정적인 URI를 이용하여 통신을 하지않고, 동적으로 서버에 접근할 수 있다. 서버에서 URI를 제공한다면 클라이언트가 정적으로 서버의 자원에 접근할 때 사용할 URI 들을 가지고 있지 않아도 된다. Spring HATEOAS 모듈을 사용해서 FoodController를 리팩토링 한다. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-hateoas</artifactId> </dependency> pom.xml 파일에서 spring-boot-starter-web dependency를 위와 같이 변경한다. Food 클래스의 형태만 베낀 데이터 전달용 객체(DTO, Data Transfer Object)를 작성한다. package com.poseidon.fridge.model; import org.springframework.hateoas.ResourceSupport; import com.fasterxml.jackson.annotation.JsonProperty; public class FoodResource extends ResourceSupport { @JsonProperty long id; @JsonProperty String name; @JsonProperty int quantity; @JsonProperty String expiryDate; } ResourceSupport 클래스를 서브클래스로 하는 FoodResource 클래스를 작

냉장고 가계부 프로젝트 7

FoodControllerTests 클래스에서 자주 사용되는 테스트 객체를 @Before 애너테이션으로 테스트 전에 생성되도록 리팩토링하고 findAllFoods 메서드도 수정한다. private Food milk; private static final Long ID = 1L; @Before public void setUp() { milk = new Food("파스퇴르 우유 1.8L", 1, "2018-09-28"); milk.setId(ID); } FoodController.findAllFoods 메서드를 통해서 목록표를 리턴받으면 그중에서 특정 Food의 상세정보를 요청할 수 있다. 다음과 같이 테스트 메서드를 작성한다. @Test public void findById() throws Exception { given(jpaFoodRepository.findOne(ID)).willReturn(milk); mvc.perform(get("/foods/" + ID)) .andExpect(status().isOk()) .andExpect(content().json(mapper.writeValueAsString(milk))); } findById 메서드는 jpaFoodRepository의 findOne 메서드를 호출해서 단일 행 결과를 리턴한다. 그리고 /foods/1 과 같은 url을 get 방식으로 호출하여 결과가 milk객체의 json 타입으로 돌아오는것을 테스트한다. 다음은 FoodController 의 구현부분이다. @GetMapping("/{id}") public Food findById(@PathVariable final long id) { return jpaFoodReposit

냉장고 가계부 프로젝트 6

JdbcTemplate 을 사용하여 DB에 객체 데이터를 저장하고 관리하는 것보다 Java Persistence API (JPA) 기술 표준을 사용하면 생산성이 훨씬 향상된다. JPA 를 구현하는 프레임워크들 중에서는 Hibernate가 가장 많이 사용되고 있다. 이제는 JPA와 Hibernate가 같은 의미로 불리기도 한다. JdbcTemplate이나, MyBatis 를 이용하면 SQL Query문을 벤더에 맞게 직접 작성하게 되고, CRUD 같은 쿼리의 경우 단순하게 반복/중복적으로 표현된다. ex)MyBatis <select id="findByUsername" parameterType="String" resultType="User"> SELECT * FROM users WHERE username = #{username} </select> ex)JPA public interface UserRepository extends JpaRepository<User, String> { User findByUsername(String username); } 위의 MyBatis는  보통 UserDao.xml 의 쿼리문을 저장하는 XML 파일이 있고, UserDao.java 인터페이스가 따로 존재한다. 이렇듯 JPA는 생산성이 좋고, MyBatis는 쿼리문을 직접 작성하니까 해당 벤더에 특화된 쿼리문을 작성해서 성능에 이점이 있다. 이 두가지 장점을 다 가져가려면 단순한 쿼리는 JPA를 이용하고, 통계나 리포트같은 부분은 MyBatis를 이용하면 된다. JPA를 사용하기 위해서는 pom.xml 에 spring-boot-starter 를 data-jpa로 수정한다. <dependency> <groupId>org.springframework.boot</groupId> <artifactI

냉장고 가계부 프로젝트 5

JdbcFoodServiceTests 클래스에 save 기능 테스트 메서드를 작성한다. save는 데이터가 존재하면 수정하고, 데이터가 없으면 등록한다. save기능은 JdbcFoodService 클래스에서 제공한다. 아래는 테스트 메서드이다. @Test public void save() { JdbcFoodService jdbcFoodService = new JdbcFoodService(); jdbcFoodService.setJdbcTemplate(dataSource); Food cola = new Food("코카콜라 500mL", 2, "2018-10-30"); cola = jdbcFoodService.save(cola); assertThat(cola.getId(), notNullValue()); cola.decreaseQuantity(1); jdbcFoodService.save(cola); Food savedCola = findById(cola.getId()); assertThat(savedCola.getQuantity(), equalTo(1)); } jdbcFoodService 클래스를 생성하고 setJdbcTemplate 설정자 메서드로 dataSource 객체를 주입해주고있다. cola 객체를 새로 생성해서 save 메서드의 파라미터로 전달하면 기존 데이터베이스에 cola 정보가 없으므로(id 멤버변수가 null) insert 메서드를 호출한다. 그뒤에, cola 객체를 수정한 후 다시 save메서드를 호출하면 데이터베이스에 cola 정보가 이미 있으므로, update 메서드를 호출한다. 아래는 JdbcFoodService 클래스이다. package com.poseidon.fridge; import java.sql.Conne

냉장고 가계부 프로젝트 4

목록표를 데이터베이스에 저장할 수 있다. 우선, 데이터베이스 저장 기능을 추가하기 전에 소스코드를 정리한다. Food 클래스의 equals 메서드도 Guava의 equal메서드를 이용한다. @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Food other = (Food) obj; return Objects.equal(id, other.id); } 테스트하면서 생성했던 Food 클래스와 MemoryFoodService 클래스는 src/main/java로 옮긴다. com.poseidon.fridge 패키지 아래에 model 과 service 패키지를 각각 생성하고 Food 클래스는 model 로 MemoryFoodService 는 service 패키지 아래로 옮긴다. FoodTests 클래스에 Food 클래스 테스트 메서드와 MemoryFoodService 테스트 메서드가 섞여있는것도 정리한다. MemoryFoodServiceTests 클래스를 새로 만들고 FoodTests에서 MemoryFoodService 관련 테스트 메서드들을 옮기고 FoodTests를 정리한다. DB는 간단하게 HSQLDB을 사용하고, JDBC는 스프링에서 제공하는 JdbcTemplate를 사용한다. pom.xml 에 라이브러리 의존성을 추가/수정한다. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>

냉장고 가계부 프로젝트 3

목록표에 식품을 추가, 삭제, 수정 할 수 있다. FoodTests에서 목록표를 가져오는 기능을 테스트한다. 테스트 메서드명은 foodList라고 정한다. @Test public void foodList() { } 목록표는 ArrayList로 구현한다. ArrayList를 멤버변수로 가지는 MemoryFoodService 객체를 생성하고, 목록을 가져오는 메서드인 findAll을 테스트해본다. @Test public void foodList() { MemoryFoodService memoryFoodService = new MemoryFoodService(); List<Food> foods = memoryFoodService.findAll(); assertThat(foods.size(), equalTo(0)); } 아직 MemoryFoodService 클래스도 없고, findAll메서드도 없으므로, 컴파일 에러가 난다. 냉장고 가계부 프로젝트 1, 2편에서 보폭을 작게 가져갔다면 이젠 조금 빠르게 진행해도 될것같다. 만약 신호등이 빨간불로 바뀐다면 그때가서 보폭을 줄이면 된다. 바로 구현하고 테스트한다. package com.poseidon.fridge; import java.util.ArrayList; import java.util.List; public class MemoryFoodService { private List<Food> foods = new ArrayList<>(); public List<Food> findAll() { return foods; } } MemoryFoodService 클래스는 foods 라는 멤버변수를 가지고, findAll 메서드가 있다. findAll메서드는 foods 멤버변수를 반환한다. 이제 테스트는 성공한다

냉장고 가계부 프로젝트 2

식품의 개수는 냉장고에서 꺼낸 개수만큼 줄어든다. 냉장고 가계부 프로젝트 1 에서는 프로젝트를 생성하고 Food클래스를 구현하였다. 새로운 기능추가 요건은 식품의 개수를 줄이는 것이다. FoodTests 클래스에 개수를 줄이는 테스트 메서드를 추가한다. 그리고 cola 인스턴스를 생성한다. @Test public void decreaseQuantity() { Food cola = new Food("코카콜라 500mL", 2, "2018-10-30"); } Food 객체의 수량을 줄이는 메서드를 다음과 같이 만들고 확인해본다. @Test public void decreaseQuantity() { Food cola = new Food("코카콜라 500mL", 2, "2018-10-30"); cola.decreaseQuantity(1); assertThat(cola.getQuantity(), equalTo(1)); } decreaseQuantity 메서드를 만든 기억이 없다. Quick Fix로 재빨리 구현하고, 테스트를 실행한다. public void decreaseQuantity(int quantity) { // TODO Auto-generated method stub } decreaseQuantity 메서드를 구현했지만, 신호등은 빨간불이다. 1편에서 말한것처럼 무슨 수를 써서라도 신호등 불을 초록으로 만들어야 한다. public void decreaseQuantity(int quantity) { this.quantity = 1; } 이제 신호등 불은 초록불이 되었지만, 이건 아니라고 당연히 생각한다. 테스트를 추가하면 바로 확인할 수 있다. @Test public void dec

냉장고 가계부 프로젝트 1

냉장고 가계부는 아래와 같은 기능이 필요하다. 저장된 식품의 이름, 개수와 유통기한을 적을 수 있는 목록 표. 바나나 | 2 | 2018.08.30 사과 | 5 | 2018.09.15 우유 | 1 | 2018.08.25 ... 이것을 자바로 구현한다. 먼저 준비해야 할 도구는 Spring Tool Suite 와 JDK 1.8 을 설치해야 한다. 그리고 Package Explorer 에서 New -> Spring Starter Project 를 클릭한다. 아래와 같이 설정한다. 빌드 타입을 Maven, 패키징 방식은 jar, 자바 버전은 8 이다. Next > 를 선택하고 Spring Boot Version을 1.5.15로 가장 낮은 것으로 선택한다. 스프링 의존 모듈은 선택하지 않고 Finish를 선택한다. 생성된 프로젝트의 디렉터리 구조는 다음과 같다. src/test/java 에서 com.poseidon.fridge 패키지 내에 새로운 class 파일을 생성한다. 파일이름은 FoodTests 라고 정한다. FoodTests 클래스에 다음과 같이 JUnit테스트 메서드를 하나 만든다. package com.poseidon.fridge; import org.junit.Test; public class FoodTests { @Test public void createFood() { } } createFood 메서드는 냉장고 속의 식품을 표현하고 만들어내는 테스트 메서드이다. 이제 command + F11 을 눌러서 실행해보면 다음과 같은 초록색 불이 들어온다. 이제 Food 객체를 생성하고 생성자에 이름과 개수, 유통기한을 파라미터로 입력한다. 인스턴스 이름은 milk로 한다. package com.poseidon.fridge; import org.junit.Test; public class FoodTests { @Te