JAVA/소박한그룹 프로젝트

[210930] 임베디드 타입과 LocalDateTime, MapStruct를 이용한 게시글 작성일자, 수정일자 작업

kth990303 2021. 9. 30. 23:15
반응형

게시글 작성일자, 수정일자 기능을 넣고 싶어서 

JPA 시간에 배운 @Embedded, @Embeddable 기능을 사용하여 Period 변수를 추가하여 보았다.

 

임베디드 관련 내용은 아래 포스팅에서 볼 수 있다.

https://kth990303.tistory.com/65

 

[JPA] 값 타입 컬렉션, 임베디드 타입

만약 객체가 다른 객체의 테이블을 참조할 필요는 없는데, 해당 객체가 여러 가지 값을 가지고 있어야 하는 경우는 어떻게 할까? 예를 들자면, kth990303 학생과 aru0504 학생이 있다고 하자. kth990303

kth990303.tistory.com


개발환경

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일... 22시가 아닌 10시?
행복회로 미쳤고

 

위와 같이 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

 

github issue #14 5번 적용완료. · kth990303/BOJStudyList@87e4e08

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

github.com

이로써 9월말까지 기능추가하겠다는 약속을 겨우겨우 지킬 수 있었다. 정말 다행이다.

 

내일은 국군의 날이므로 쉬어가는 겸 백준, 그리고 Spring @Valid 공부를 마저 해야겠다.

반응형