Food 클래스의 멤버변수가 많아지면서 생성자를 이용한 객체생성이 복잡해졌다. 식품명과 수량은 반드시 입력해야 하고, 유통기한은 별도의 로직이 들어가야한다. id, fridge 필드 또한 setter 를 이용할 경우도 있고 아닐 경우도 있다. 이를 해결하고자, Food 클래스에 빌더를 추가한다.
불필요한 setter, 사용하지 않는 메서드(decreaseQuantity)는 삭제하고, 내부에 Builder 정적 클래스를 생성했다. 객체생성에 필수 필드인 name, quantity는 빌더 생성자에서 바로 전달받고, 나머지 필드는 선택적으로 받을 수 있다.
expiryDate 필드는 유통기한을 나타내는데 사용자가 유통기한을 입력하지 않으면 입력한 시점에서부터 DEFAULT_EXPIRY_DAYS 값만큼 N일 더한 값을 필드에 할당한다.
setDefaultExpiryDate 메서드는 현재날짜로부터 N을 더하는 기능을 담당한다.
Food 클래스가 크게 변경되었으므로, 컴파일에러가 나는 부분을 전부 수정하고, new 생성자를 이용해서 객체를 생성했던 부분들도 전부 빌더를 이용하도록 수정한다. 수정이 완료되면 테스트를 통해 문제가 없는지 확인한다.
그런데, Date 타입의 expiryDate를 Java8 의 LocalDate를 사용해서 변경하는 방법은 좀 불편하다. 필드를 LocalDate 타입으로 변경한다.
변경 후 setDefaultExpiryDate 메서드의 코드가 좀 줄었다. 굳이 힘들게 Date타입으로 캐스팅하지 않아도 된다. expiryDate 필드에 선언되었던 @Temporal 애너테이션을 없앴다. @Temporal 애너테이션은 java.util.Date, java.util.Timestamp 클래스에일 경우에만 선언한다.
변경된 내용으로 인해 컴파일 에러가 난 부분을 수정하고, 테스트 클래스도 수정하고 나서 테스트를 수행하면 실패한다. 하이버네이트 의존라이브러리를 등록해야한다.
pom.xml에 다음과 같이 추가한다.
jackson-datatype-jsr310은 JSON 타입으로 컨버팅할 때 LocalDate 값이 장황하게 나오는 부분을 줄여준다.
출처: Java 8 Date(Time) 와 JPA 그리고 스프링 부트, Spring Jpa java8 date (LocalDateTime) 와 Jackson
fridge-web 프로젝트에서도 마찬가지로 FoodCommand 클래스를 LocalDate 타입으로 변경하고, 컴파일 에러가 나는 부분을 수정한다. 테스트를 실행하면 에러가 발생하는데 마찬가지로 jackson-data-type-jsr310 의존성을 추가한다. 의존성을 추가하고 나서 다시 테스트를 수행하면 thymeleaf 에서 오류가 발생한다. localDate 값을 #dates.format 형식으로 변환하는 부분이 java.util.Date 타입이기 때문이다. 여기는 라이브러리를 추가하고 thymeleaf utility도 #temporals.format 으로 수정해야한다.
이제 다시 테스트를 수행해서 문제가 없는지 확인한다.
출처: HOW-TO: JAVA 8 DATE & TIME WITH THYMELEAF AND SPRING BOOT
이전글: 냉장고 가계부 프로젝트 23
다음글: 냉장고 가계부 프로젝트 25
@Entity(name="food") public class Food { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; @ManyToOne @JsonIgnore private Fridge fridge; private String name; private int quantity; @Temporal(TemporalType.DATE) private Date expiryDate; public static final int DEFAULT_EXPIRY_DAYS = 7; protected Food() {} public Long getId() { return id; } public Fridge getFridge() { return fridge; } public void setFridge(Fridge fridge) { this.fridge = fridge; } public String getName() { return name; } public int getQuantity() { return quantity; } public Date getExpiryDate() { return expiryDate; } private void setDefaultExpiryDate() { this.expiryDate = Date.from(LocalDate.now().plusDays(DEFAULT_EXPIRY_DAYS).atStartOfDay(ZoneId.systemDefault()).toInstant()); } @Override public int hashCode() { return Objects.hashCode(getId()); } @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.equals(getId(), other.getId()); } public static class Builder { private Long id; private final String name; private final int quantity; private Date expiryDate; private Fridge fridge; public Builder(String name, int quantity) { this.name = name; this.quantity = quantity; } public Builder id(Long id) { this.id = id; return this; } public Builder expiryDate(Date expiryDate) { this.expiryDate = expiryDate; return this; } public Builder fridge(Fridge fridge) { this.fridge = fridge; return this; } public Food build() { return new Food(this); } } private Food(Builder builder) { this.id = builder.id; this.name = builder.name; this.quantity = builder.quantity; this.expiryDate = builder.expiryDate; if(this.expiryDate == null) { setDefaultExpiryDate(); } this.fridge = builder.fridge; } }
불필요한 setter, 사용하지 않는 메서드(decreaseQuantity)는 삭제하고, 내부에 Builder 정적 클래스를 생성했다. 객체생성에 필수 필드인 name, quantity는 빌더 생성자에서 바로 전달받고, 나머지 필드는 선택적으로 받을 수 있다.
expiryDate 필드는 유통기한을 나타내는데 사용자가 유통기한을 입력하지 않으면 입력한 시점에서부터 DEFAULT_EXPIRY_DAYS 값만큼 N일 더한 값을 필드에 할당한다.
setDefaultExpiryDate 메서드는 현재날짜로부터 N을 더하는 기능을 담당한다.
Food 클래스가 크게 변경되었으므로, 컴파일에러가 나는 부분을 전부 수정하고, new 생성자를 이용해서 객체를 생성했던 부분들도 전부 빌더를 이용하도록 수정한다. 수정이 완료되면 테스트를 통해 문제가 없는지 확인한다.
그런데, Date 타입의 expiryDate를 Java8 의 LocalDate를 사용해서 변경하는 방법은 좀 불편하다. 필드를 LocalDate 타입으로 변경한다.
@Entity(name="food") public class Food { ... private LocalDate expiryDate; // Getter/Setter public LocalDate getExpiryDate() { return expiryDate; } private void setDefaultExpiryDate() { this.expiryDate = LocalDate.now().plusDays(DEFAULT_EXPIRY_DAYS); } // hashCode, equals public static class Builder { private Long id; private final String name; private final int quantity; private LocalDate expiryDate; private Fridge fridge; public Builder(String name, int quantity) { this.name = name; this.quantity = quantity; } public Builder id(Long id) { this.id = id; return this; } public Builder expiryDate(LocalDate expiryDate) { this.expiryDate = expiryDate; return this; } public Builder fridge(Fridge fridge) { this.fridge = fridge; return this; } public Food build() { return new Food(this); } } private Food(Builder builder) { this.id = builder.id; this.name = builder.name; this.quantity = builder.quantity; this.expiryDate = builder.expiryDate; if(this.expiryDate == null) { setDefaultExpiryDate(); } this.fridge = builder.fridge; } }
변경 후 setDefaultExpiryDate 메서드의 코드가 좀 줄었다. 굳이 힘들게 Date타입으로 캐스팅하지 않아도 된다. expiryDate 필드에 선언되었던 @Temporal 애너테이션을 없앴다. @Temporal 애너테이션은 java.util.Date, java.util.Timestamp 클래스에일 경우에만 선언한다.
변경된 내용으로 인해 컴파일 에러가 난 부분을 수정하고, 테스트 클래스도 수정하고 나서 테스트를 수행하면 실패한다. 하이버네이트 의존라이브러리를 등록해야한다.
pom.xml에 다음과 같이 추가한다.
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-java8</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> </dependency>
jackson-datatype-jsr310은 JSON 타입으로 컨버팅할 때 LocalDate 값이 장황하게 나오는 부분을 줄여준다.
출처: Java 8 Date(Time) 와 JPA 그리고 스프링 부트, Spring Jpa java8 date (LocalDateTime) 와 Jackson
fridge-web 프로젝트에서도 마찬가지로 FoodCommand 클래스를 LocalDate 타입으로 변경하고, 컴파일 에러가 나는 부분을 수정한다. 테스트를 실행하면 에러가 발생하는데 마찬가지로 jackson-data-type-jsr310 의존성을 추가한다. 의존성을 추가하고 나서 다시 테스트를 수행하면 thymeleaf 에서 오류가 발생한다. localDate 값을 #dates.format 형식으로 변환하는 부분이 java.util.Date 타입이기 때문이다. 여기는 라이브러리를 추가하고 thymeleaf utility도 #temporals.format 으로 수정해야한다.
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> </dependency>
이제 다시 테스트를 수행해서 문제가 없는지 확인한다.
출처: HOW-TO: JAVA 8 DATE & TIME WITH THYMELEAF AND SPRING BOOT
이전글: 냉장고 가계부 프로젝트 23
다음글: 냉장고 가계부 프로젝트 25