전체 코드는 여기에서 보실 수 있습니다.

먼저 코드를 보여드리도록 하겠습니다.

Controller, Service, PostEntity

- PostController

@PostMapping("/post")
    @ResponseStatus(value = HttpStatus.CREATED)
    @PreAuthorize("isAuthenticated()")
    @ApiOperation(value = "게시물 생성", notes = "게시물을 생성합니다.")
    public SuccessResponse<PostDTO> createPost(@Valid @RequestBody PostDTO postDTO, Principal principal) {
        log.info(principal.getName());
        PostDTO post = postService.createPost(postDTO, principal.getName());

        return SuccessResponse.success(post);
    }

 

이전 Json으로 로그인하기를 했을때 UserController의 login 메서드에서 email, password를 받아 UsernamePasswordAuthenticationToken을 만들어주고 이것을 UserAuthenticationProvider에 넘겨주었습니다. Provider에서 넘겨준 토큰으로 email과 password를 꺼내 DB에서 유저 정보를 조회해 로그인이 성공하면 UsernamePasswordAuthenticationToken으로 Authentication을 리턴하였습니다. 그로인해 로그인이 Principal를 통해 현재 로그인한 유저의 정보를 가져올 수 있습니다.

- PostService

public PostDTO createPost(PostDTO postDTO, String email) {
        Optional<User> byEmail = userRepository.findByEmail(email);

        User user = byEmail.orElseThrow(() -> new UsernameNotFoundException("게시글 작성 권한이 없습니다."));

        Post post = Post.builder()
                .title(postDTO.getTitle())
                .contents(postDTO.getContents())
                .createAt(LocalDateTime.now())
                .build();

        post.mappingCategory(postCategoryRepository.findByName(postDTO.getCategory()));
        post.mappingUser(user);

        Post savePost = postRepository.save(post);

        return PostDTO.builder()
                .id(savePost.getId())
                .build();
    }

받은 email로 DB에서 유저를 찾은 다음에 post에 매핑시켜 줍니다.

- UserEntity, PostEntity

- UserEntity

@Entity
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String email;

    private String password;

    private String nickname;

    private LocalDateTime createdBy;

    @Builder.Default
    private String role = "ROLE_USER";

    private String authority;

    private boolean enabled = true;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
    private List<Post> postList = new ArrayList<>();

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public void encryptPassword(String password) {
        this.password = BCrypt.hashpw(password, BCrypt.gensalt());
    }

    public void mappingPost(Post post) {
        postList.add(post);
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Set<GrantedAuthority> auth = new HashSet<>();
        auth.add(new SimpleGrantedAuthority(authority));

        return auth;
    }

    @Override
    public String getUsername() {
        return this.getEmail();
    }

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return this.enabled;
    }


}

- PostEntity

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    private String contents;

    private LocalDateTime createAt;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "post_category_id")
    private PostCategory category;

    @Builder.Default
    private Long viewCount = 0L;

    public void addViewCount() {
        this.viewCount += 1;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

////////////////////////////////////////////////////////////////////////////////////////////////

    public void changeTitle(String title) {
        this.title = title;
    }

    public void changeContents(String contents) {
        this.contents = contents;
    }

    public void mappingCategory(PostCategory postCategory) {
        this.category = postCategory;
        postCategory.mappingPost(this);
    }

    public void mappingUser(User user) {
        this.user = user;
        user.mappingPost(this);
    }
}

유저는 포스트를 여러개 작성 할수 있고 포스트는 유저 1명에게 속해 있어야하니 유저 : 포스트 는 1 : N 관계 입니다. 그러므로 User Entity에는 @ManyToOne 으로 매핑 시켜 주고, 포스트에는 @OneToMany로 매핑 시켜줍니다. N인 Post가 매핑관계의 주인이 되는게 좋으므로 mappedBy를 통해 Post의 user 필드를 지정해 주고 Post Entity에 유저를 매핑시켜줄 연관관계 메서드를 작성해줍니다.

여기 까지 진행한 후 게시물을 만들고 getPost를 통해 게시물을 조회하게 되면 쿼리가 2개가 발생하게 됩니다. Post를 가져올때 한번, Post에 매핑된 User를 가져올때 한번 일어나게 되는데요 이것을 쿼리 한번에 조회하기 위해 PostRepository를 수정해줍니다.

PostRepository

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
    @EntityGraph(attributePaths = {"category", "user"})
    Optional<Post> findById(Long postId);
}

지난시간 postCategory 쿼리를 최적화 하기위해 @EntityGraph에 category를 지정해줬는데 user를 추가시켜주어 쿼리 1번에 유저까지 가져올수 있도록 합니다.

그 후 게시물 조회를 해보면 유저까지 한번에 가져오는걸 알 수 있습니다.

+ Recent posts