게시글 작성일자, 수정일자 기능을 넣고 싶어서
JPA 시간에 배운 @Embedded, @Embeddable 기능을 사용하여 Period 변수를 추가하여 보았다.
임베디드 관련 내용은 아래 포스팅에서 볼 수 있다.
https://kth990303.tistory.com/65
개발환경
Java 11
MapStruct 1.4.2
JPA + MySQL 8.0.19
Spring Boot
lombok
디렉토리 구조
Post.class
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Post {
// 필드변수
@Embedded
private PostPeriod postPeriod;
public Post(String title, String contents, Member member, PostPeriod postPeriod) {
// ...
this.postPeriod=postPeriod;
}
// update Entity
public void updatePost(String title, String contents, PostPeriod postPeriod){
this.title=title;
this.contents=contents;
this.postPeriod=postPeriod;
}
// ...
}
@Embedded로 PostPeriod 타입을 매핑해준다.
또한, post를 수정할 때에 수정일자를 변경해줘야 하므로 updatePost 메소드도 변경해주었다.
PostPeriod.class
@Embeddable
@Getter
public class PostPeriod {
private String creationDate;
private String editDate;
public PostPeriod() {
}
public PostPeriod(String creationDate, String editDate) {
this.creationDate = creationDate;
this.editDate = editDate;
}
}
기본 생성자는 필수!
그리고 프론트 view 단에서 수정일자와 생성일자를 get으로 받아와서 표기해줘야 하므로 lombok @Getter를 해주자.
PostService.class
public Long enroll(PostDto postDto){
// ...
// 첫 생성일 땐 생성일자와 수정일자 동일하게
LocalDateTime now=LocalDateTime.now();
// 00시~24시 꼴로 하려면 kk:mm:ss, 13시가 아닌 1시로 표기하려면 hh:mm:ss
String creationDate = now.format(DateTimeFormatter.ofPattern("yyyy/MM/dd kk:mm:ss"));
PostPeriod postPeriod = new PostPeriod(creationDate, creationDate);
Post post=new Post(postDto.getTitle(), postDto.getContents(), loginMember, postPeriod);
// ...
return postRepository.save(post).getId();
}
주석에서 알 수 있듯이 yyyy/MM/dd kk:mm:ss 에서 소문자를 함부로 변경하면 안된다.
나는 이 소문자는 딱히 상관없는줄 알고 맨 처음에 YYYY/MM/DD hh:mm:ss 로 진행했다가 결과가 아래처럼 이상하게 나오는 현상을 겪었다.
위와 같이 9월 273일과 같은 경험을 하고 싶지 않다면 알파벳을 형식에 맞게 잘 지켜주자.
이제 남은 것은 DTO 변경이랑 Mapper 변경이다.
어떤 dto를 변경해야할까?
게시글을 등록하는 api에 해당하는 dto는 당연히 작성일자가 추가되므로 변경해줘야겠지만,
게시글을 수정하는 api에 해당하는 dto는 사실 변경할 필요가 없다.
그 이유는 수정 api인 post/{id}/edit 코드가 아래와 같기 때문이다.
Optional<Post> post = postRepository.findById(id);
try{
if(post.isEmpty())
throw new NoSuchElementException();
// 수정일자 변경
// ...
post.get().updatePost(postDto.getTitle(), postDto.getContents(), postPeriod);
} catch (Exception e){
e.printStackTrace();
}
entity의 변경메소드를 직접 이용해주기 때문이다.
만약, 변경 작업 DTO가 따로 존재한다면 적절히 변경해주면 된다.
게시글을 등록하는 api에 해당하는 dto에 해당하는 PostIdDto.class
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class PostIdDto {
private Long id;
private String title;
private String contents;
private MemberIdDto memberIdDto;
private Long views;
private PostPeriod postPeriod;
public PostIdDto(Long id, String title, String contents, MemberIdDto memberIdDto,
Long views, PostPeriod postPeriod) {
this.id = id;
this.title = title;
this.contents = contents;
this.memberIdDto = memberIdDto;
this.views = views;
this.postPeriod = postPeriod;
}
}
PostIdMapper.class
@Mapper(componentModel = "spring")
public interface PostIdMapper extends GenericMapper<PostIdDto, Post> {
MemberIdMapper memberIdMapper= Mappers.getMapper(MemberIdMapper.class);
@Override
default Post toEntity(PostIdDto postIdDto){
if ( postIdDto == null ) {
return null;
}
// ...
PostPeriod postPeriod = postIdDto.getPostPeriod();
Post post = new Post( title, contents, member, postPeriod );
return post;
}
default PostIdDto toDto(Post post){
if(post==null){
return null;
}
// ...
PostPeriod postPeriod=post.getPostPeriod();
PostIdDto postIdDto=new PostIdDto(id, title, contents, memberIdDto, views, postPeriod);
return postIdDto;
}
}
어차피 PostPeriod 타입 하나만 추가된 것이기 때문에 크게 바뀌는 부분이 없다.
실행 결과
수정했을 때 수정일자 변경도 잘 된다.
역시 스프링부트+JPA는 객체지향 프레임워크 + 객체지향에 적절하게 엔티티 매핑을 해주는 ORM이다보니 유지보수하기 편한 듯하다.
View 단에 해당하는 thymeleaf는 아래 github commit 내역에서 변경되는 부분을 확인하도록 하자.
요즘은 react, vue로 api 통신해서 많은 사람이 필요로 할 것 같진 않지만 혹시 모르므로 이 기능추가로 변경된 github commit 내용을 올린다.
https://github.com/kth990303/BOJStudyList/commit/87e4e088a9f2431972338cc8c2140aa4d06b4306
이로써 9월말까지 기능추가하겠다는 약속을 겨우겨우 지킬 수 있었다. 정말 다행이다.
내일은 국군의 날이므로 쉬어가는 겸 백준, 그리고 Spring @Valid 공부를 마저 해야겠다.