Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/main/java/com/example/be/domain/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import lombok.*;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
Expand Down Expand Up @@ -32,6 +33,9 @@ public class Post {
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments;

@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PostLike> likes = new ArrayList<>();

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/com/example/be/domain/PostLike.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.example.be.domain;

import jakarta.persistence.*;
import lombok.*;

import java.time.LocalDateTime;

@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = {"user_id", "post_id"})
})
public class PostLike {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

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

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id")
private Post post;

private LocalDateTime createDate;
}
22 changes: 22 additions & 0 deletions src/main/java/com/example/be/repository/PostLikeRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.example.be.repository;

import com.example.be.domain.Post;
import com.example.be.domain.PostLike;
import com.example.be.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Optional;

public interface PostLikeRepository extends JpaRepository<PostLike, Long> {

Optional<PostLike> findByUserAndPost(User user, Post post);

boolean existsByUserAndPost(User user, Post post);

long countByPost(Post post);

@Query("SELECT COUNT(pl) FROM PostLike pl WHERE pl.post.id = :postId")
long countByPostId(@Param("postId") Long postId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
public interface PostRepository extends JpaRepository<Post, Long> {
// PostRepository에 추가
@Query("SELECT p FROM Post p LEFT JOIN FETCH p.comments ORDER BY p.createDate DESC")
Page<Post> findAllWithCommentsOrderByCreateDateDesc(Pageable pageable);
Page<Post> findAllByOrderByCreateDateDesc(Pageable pageable);

// ID로 게시글 조회 (댓글 포함)
@EntityGraph(attributePaths = {"comments", "comments.user", "user"})
Expand Down
89 changes: 89 additions & 0 deletions src/main/java/com/example/be/service/PostLikeServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.example.be.service;

import com.example.be.apiPayload.code.status.ErrorStatus;
import com.example.be.apiPayload.exception.handler.UserHandler;
import com.example.be.domain.Post;
import com.example.be.domain.PostLike;
import com.example.be.domain.User;
import com.example.be.repository.PostLikeRepository;
import com.example.be.repository.PostRepository;
import com.example.be.repository.UserRepository;
import com.example.be.web.dto.PostDTO;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Optional;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class PostLikeServiceImpl {

private final JwtUtilServiceImpl jwtUtilService;
private final UserRepository userRepository;
private final PostRepository postRepository;
private final PostLikeRepository postLikeRepository;

@Transactional
public PostDTO.PostLikeResponseDTO togglePostLike(Long postId, HttpServletRequest request) {
// 토큰에서 사용자 정보 가져오기
String accessToken = jwtUtilService.extractTokenFromCookie(request, "accessToken");
if (accessToken == null) {
throw new UserHandler(ErrorStatus._NOT_FOUND_USER);
}

String userId = jwtUtilService.getUserIdFromToken(accessToken);
User user = userRepository.findByUserId(UUID.fromString(userId))
.orElseThrow(() -> new UserHandler(ErrorStatus._NOT_FOUND_USER));

// 게시글 정보 가져오기
Post post = postRepository.findById(postId)
.orElseThrow(() -> new UserHandler(ErrorStatus._NOT_FOUND_POST));

// 이미 좋아요를 눌렀는지 확인
Optional<PostLike> existingLike = postLikeRepository.findByUserAndPost(user, post);

boolean isLiked;
if (existingLike.isPresent()) {
// 좋아요가 이미 있으면 삭제 (좋아요 취소)
postLikeRepository.delete(existingLike.get());
isLiked = false;
} else {
// 좋아요가 없으면 추가
PostLike postLike = PostLike.builder()
.user(user)
.post(post)
.createDate(LocalDateTime.now())
.build();
postLikeRepository.save(postLike);
isLiked = true;
}

// 좋아요 수 조회
long likeCount = postLikeRepository.countByPost(post);

return PostDTO.PostLikeResponseDTO.builder()
.postId(postId)
.liked(isLiked)
.likeCount(likeCount)
.build();
}

// 특정 게시글에 대한 사용자의 좋아요 여부 확인
public boolean isPostLikedByUser(Post post, User user) {
return postLikeRepository.existsByUserAndPost(user, post);
}

// 특정 게시글의 좋아요 수 조회
public long getPostLikeCount(Post post) {
return postLikeRepository.countByPost(post);
}

// 특정 게시글 ID의 좋아요 수 조회
public long getPostLikeCount(Long postId) {
return postLikeRepository.countByPostId(postId);
}
}
35 changes: 31 additions & 4 deletions src/main/java/com/example/be/service/PostServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ public class PostServiceImpl {
private final JwtUtilServiceImpl jwtUtilService;
private final UserRepository userRepository;
private final PostRepository postRepository;
private final PostLikeServiceImpl postLikeService;

public CommonDTO.IsSuccessDTO write(PostDTO.postRequestDTO request, HttpServletRequest req) {

//글 작성 메서드
public CommonDTO.IsSuccessDTO write(PostDTO.postRequestDTO request, HttpServletRequest req) {
String accessToken = jwtUtilService.extractTokenFromCookie(req, "accessToken");

// 토큰이 없는 경우 처리
Expand Down Expand Up @@ -63,7 +65,7 @@ public CommonDTO.IsSuccessDTO write(PostDTO.postRequestDTO request, HttpServletR

public PostDTO.PageResponseDTO getPosts(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
Page<Post> postPage = postRepository.findAllWithCommentsOrderByCreateDateDesc(pageable);
Page<Post> postPage = postRepository.findAllByOrderByCreateDateDesc(pageable);

List<PostDTO.postResponseDTO> postDtoList = postPage.getContent().stream()
.map(post -> PostDTO.postResponseDTO.builder()
Expand All @@ -73,8 +75,8 @@ public PostDTO.PageResponseDTO getPosts(int page, int size) {
.userName(post.getUser().getName())
.createDate(post.getCreateDate().toLocalDate())
.isDone(post.isDone())
// 댓글 개수 추가
.commentCount(post.getComments() != null ? post.getComments().size() : 0)
.likeCount(postLikeService.getPostLikeCount(post))
.build())
.collect(Collectors.toList());

Expand All @@ -89,10 +91,29 @@ public PostDTO.PageResponseDTO getPosts(int page, int size) {
}

// 게시글 상세 조회 메서드
public PostDTO.PostDetailResponseDTO getPostDetail(Long postId) {
public PostDTO.PostDetailResponseDTO getPostDetail(Long postId, HttpServletRequest request) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new UserHandler(ErrorStatus._NOT_FOUND_POST));

// 현재 로그인한 사용자 정보 가져오기 (좋아요 여부 확인용)
User currentUser = null;
boolean isLiked = false;

try {
String accessToken = jwtUtilService.extractTokenFromCookie(request, "accessToken");
if (accessToken != null) {
String userId = jwtUtilService.getUserIdFromToken(accessToken);
currentUser = userRepository.findByUserId(UUID.fromString(userId)).orElse(null);

// 현재 사용자가 이 게시글에 좋아요를 눌렀는지 확인
if (currentUser != null) {
isLiked = postLikeService.isPostLikedByUser(post, currentUser);
}
}
} catch (Exception e) {
// 비로그인 사용자도 게시글을 볼 수 있도록 예외를 무시하고 진행
}

// 댓글 목록 변환
List<PostDTO.CommentResponseDTO> commentDtoList = post.getComments().stream()
.map(comment -> PostDTO.CommentResponseDTO.builder()
Expand All @@ -104,6 +125,9 @@ public PostDTO.PostDetailResponseDTO getPostDetail(Long postId) {
.build())
.collect(Collectors.toList());

// 좋아요 수 조회
long likeCount = postLikeService.getPostLikeCount(post);

// 게시글 상세 정보 변환
return PostDTO.PostDetailResponseDTO.builder()
.id(post.getId())
Expand All @@ -114,6 +138,9 @@ public PostDTO.PostDetailResponseDTO getPostDetail(Long postId) {
.isDone(post.isDone())
.commentCount(commentDtoList.size())
.comments(commentDtoList)
// 좋아요 정보 추가
.likeCount(likeCount)
.liked(isLiked)
.build();
}
}
17 changes: 15 additions & 2 deletions src/main/java/com/example/be/web/controller/PostController.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import com.example.be.domain.User;
import com.example.be.repository.UserRepository;
import com.example.be.service.JwtUtilServiceImpl;
import com.example.be.service.PostLikeServiceImpl;
import com.example.be.service.PostServiceImpl;
import com.example.be.service.UserServiceImpl;
import com.example.be.web.dto.CommonDTO;
import com.example.be.web.dto.PostDTO;
import com.example.be.web.dto.UserDTO;
Expand All @@ -24,6 +26,7 @@
public class PostController {

private final PostServiceImpl postService;
private final PostLikeServiceImpl postLikeService;

@PostMapping("/write")
@Operation(summary = "게시글 작성 API")
Expand All @@ -42,8 +45,18 @@ public ApiResponse<PostDTO.PageResponseDTO> getPosts(
@GetMapping("/{postId}")
@Operation(summary = "게시글 상세 조회 API", description = "게시글 ID로 게시글 상세 정보와 댓글 목록을 조회합니다.")
public ApiResponse<PostDTO.PostDetailResponseDTO> getPostDetail(
@Parameter(description = "게시글 ID") @PathVariable Long postId) {
return ApiResponse.onSuccess(postService.getPostDetail(postId));
@Parameter(description = "게시글 ID") @PathVariable Long postId,
HttpServletRequest request) {
return ApiResponse.onSuccess(postService.getPostDetail(postId, request));
}

// 좋아요 토글 API 추가
@PostMapping("/{postId}/like")
@Operation(summary = "게시글 좋아요 토글 API", description = "게시글에 좋아요를 누르거나 취소합니다.")
public ApiResponse<PostDTO.PostLikeResponseDTO> togglePostLike(
@Parameter(description = "게시글 ID") @PathVariable Long postId,
HttpServletRequest request) {
return ApiResponse.onSuccess(postLikeService.togglePostLike(postId, request));
}

}
2 changes: 1 addition & 1 deletion src/main/java/com/example/be/web/dto/CommonDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ public class CommonDTO {
@AllArgsConstructor
@Schema(title = "COMMON_RES_06 : API 실행 성공 여부 응답 DTO")
public static class IsSuccessDTO{
Boolean isSuccess;
private Boolean isSuccess;
}
}
33 changes: 22 additions & 11 deletions src/main/java/com/example/be/web/dto/PostDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,24 @@ public class PostDTO {
@AllArgsConstructor

public static class postRequestDTO{
String title;
String content;
private String title;
private String content;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class postResponseDTO {
Long id;
String title;
String content;
String userName;
private Long id;
private String title;
private String content;
private String userName;
private long likeCount;
@JsonFormat(pattern = "yyyy-MM-dd")
LocalDate createDate;
int commentCount;
boolean isDone;
private LocalDate createDate;
private int commentCount;
private boolean isDone;
}

@Builder
Expand Down Expand Up @@ -78,14 +79,24 @@ public static class PostDetailResponseDTO {
private String title;
private String content;
private String userName;

private long likeCount;
private boolean liked;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createDate;

private boolean isDone;
private int commentCount;
private List<CommentResponseDTO> comments;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "게시글 좋아요 응답 DTO")
public static class PostLikeResponseDTO {
private Long postId;
private boolean liked;
private long likeCount;
}

}