diff --git a/build.gradle b/build.gradle index 14aa9f4a..21ec5175 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,9 @@ dependencies { // test testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + //aop + implementation 'org.springframework.boot:spring-boot-starter-aop' } tasks.named('test') { diff --git a/src/main/java/com/blog/domain/comment/controller/dto/CommentController.java b/src/main/java/com/blog/domain/comment/controller/CommentController.java similarity index 73% rename from src/main/java/com/blog/domain/comment/controller/dto/CommentController.java rename to src/main/java/com/blog/domain/comment/controller/CommentController.java index 0855adae..e8f21fa9 100644 --- a/src/main/java/com/blog/domain/comment/controller/dto/CommentController.java +++ b/src/main/java/com/blog/domain/comment/controller/CommentController.java @@ -1,10 +1,9 @@ -package com.blog.domain.comment.controller.dto; +package com.blog.domain.comment.controller; -import com.blog.domain.comment.controller.dto.request.CommentRequest; -import com.blog.domain.comment.controller.dto.request.CommentUpdatedRequest; +import com.blog.domain.comment.controller.request.CommentRequest; +import com.blog.domain.comment.controller.request.CommentUpdatedRequest; import com.blog.domain.comment.service.CommentService; -import com.blog.domain.post.service.PostService; import com.blog.global.response.ApiResponse; import com.blog.global.security.aop.GetUserId; import jakarta.validation.Valid; @@ -23,14 +22,14 @@ public CommentController(CommentService commentService) { // 댓글 등록 @PostMapping - public ApiResponse registerComment(@GetUserId long userId, @Valid @RequestBody CommentRequest request) { + public ApiResponse registerComment(@GetUserId Long userId, @Valid @RequestBody CommentRequest request) { commentService.registerComment(request, userId); return ApiResponse.ok("정상적으로 등록되었습니다."); } //댓글 수정 @PutMapping("/{commentId}") - public ApiResponse updateComment(@GetUserId long userId, @PathVariable long commentId, @Valid @RequestBody CommentUpdatedRequest request) { + public ApiResponse updateComment(@GetUserId Long userId, @PathVariable long commentId, @Valid @RequestBody CommentUpdatedRequest request) { commentService.updateComment(userId, commentId, request); return ApiResponse.ok("정상적으로 수정되었습니다."); @@ -38,7 +37,7 @@ public ApiResponse updateComment(@GetUserId long userId, @PathVariable l //댓글 삭제 @DeleteMapping("/{commentId}") - public ApiResponse deleteComment(@GetUserId long userId, @PathVariable long commentId) { + public ApiResponse deleteComment(@GetUserId Long userId, @PathVariable long commentId) { commentService.deleteComment(userId, commentId); return ApiResponse.ok("정상적으로 삭제되었습니다."); } diff --git a/src/main/java/com/blog/domain/comment/controller/dto/request/CommentRequest.java b/src/main/java/com/blog/domain/comment/controller/request/CommentRequest.java similarity index 82% rename from src/main/java/com/blog/domain/comment/controller/dto/request/CommentRequest.java rename to src/main/java/com/blog/domain/comment/controller/request/CommentRequest.java index a0f89459..effed969 100644 --- a/src/main/java/com/blog/domain/comment/controller/dto/request/CommentRequest.java +++ b/src/main/java/com/blog/domain/comment/controller/request/CommentRequest.java @@ -1,4 +1,4 @@ -package com.blog.domain.comment.controller.dto.request; +package com.blog.domain.comment.controller.request; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/com/blog/domain/comment/controller/dto/request/CommentUpdatedRequest.java b/src/main/java/com/blog/domain/comment/controller/request/CommentUpdatedRequest.java similarity index 77% rename from src/main/java/com/blog/domain/comment/controller/dto/request/CommentUpdatedRequest.java rename to src/main/java/com/blog/domain/comment/controller/request/CommentUpdatedRequest.java index efe6400e..8ca61140 100644 --- a/src/main/java/com/blog/domain/comment/controller/dto/request/CommentUpdatedRequest.java +++ b/src/main/java/com/blog/domain/comment/controller/request/CommentUpdatedRequest.java @@ -1,4 +1,4 @@ -package com.blog.domain.comment.controller.dto.request; +package com.blog.domain.comment.controller.request; import jakarta.validation.constraints.NotBlank; @@ -6,4 +6,3 @@ public record CommentUpdatedRequest( @NotBlank(message = "댓글 내용은 비어 있을 수 없습니다.") String content) {} - diff --git a/src/main/java/com/blog/domain/comment/controller/dto/response/CommentResponse.java b/src/main/java/com/blog/domain/comment/controller/response/CommentResponse.java similarity index 94% rename from src/main/java/com/blog/domain/comment/controller/dto/response/CommentResponse.java rename to src/main/java/com/blog/domain/comment/controller/response/CommentResponse.java index 55333d5d..1ab5759d 100644 --- a/src/main/java/com/blog/domain/comment/controller/dto/response/CommentResponse.java +++ b/src/main/java/com/blog/domain/comment/controller/response/CommentResponse.java @@ -1,4 +1,4 @@ -package com.blog.domain.comment.controller.dto.response; +package com.blog.domain.comment.controller.response; import com.blog.domain.comment.domain.Comment; diff --git a/src/main/java/com/blog/domain/comment/domain/Comment.java b/src/main/java/com/blog/domain/comment/domain/Comment.java index 384fa4f8..448db558 100644 --- a/src/main/java/com/blog/domain/comment/domain/Comment.java +++ b/src/main/java/com/blog/domain/comment/domain/Comment.java @@ -1,6 +1,6 @@ package com.blog.domain.comment.domain; -import com.blog.domain.comment.controller.dto.request.CommentRequest; +import com.blog.domain.comment.controller.request.CommentRequest; import com.blog.global.common.BaseDomain; import java.time.LocalDateTime; diff --git a/src/main/java/com/blog/domain/comment/service/CommentService.java b/src/main/java/com/blog/domain/comment/service/CommentService.java index fd990dc2..c64f4edc 100644 --- a/src/main/java/com/blog/domain/comment/service/CommentService.java +++ b/src/main/java/com/blog/domain/comment/service/CommentService.java @@ -1,11 +1,10 @@ package com.blog.domain.comment.service; -import com.blog.domain.comment.controller.dto.request.CommentRequest; -import com.blog.domain.comment.controller.dto.request.CommentUpdatedRequest; -import com.blog.domain.comment.controller.dto.response.CommentResponse; +import com.blog.domain.comment.controller.request.CommentRequest; +import com.blog.domain.comment.controller.request.CommentUpdatedRequest; +import com.blog.domain.comment.controller.response.CommentResponse; import com.blog.domain.comment.domain.Comment; import com.blog.domain.comment.repository.CommentRepository; -import com.blog.domain.post.domain.Post; import com.blog.domain.post.repository.PostRepository; import com.blog.domain.user.domain.User; import com.blog.domain.user.repository.UserRepository; @@ -36,22 +35,19 @@ public CommentService(CommentRepository commentRepository, UserRepository userRe // 댓글 등록 @Transactional public void registerComment(@Valid CommentRequest request, Long userId) { - Post post = postRepository.findById(request.postId()) + postRepository.findById(request.postId()) .orElseThrow(() -> new CustomException(POST_NOT_FOUND)); - // 댓글 생성 및 저장 Comment comment = Comment.of(userId, request); commentRepository.save(comment); postRepository.incrementCommentCount(request.postId()); - System.out.println(post.getCommentCount()); } // 댓글 수정 @Transactional public void updateComment(Long userId, Long commentId, CommentUpdatedRequest request) { - Comment comment = getCommentsByCommentId(commentId); - validateCommentOwner(comment, userId); + Comment comment = getAuthorizedComment(userId, commentId); comment.updateContent(request.content()); commentRepository.update(comment); } @@ -59,16 +55,11 @@ public void updateComment(Long userId, Long commentId, CommentUpdatedRequest req // 댓글 삭제 @Transactional public void deleteComment(Long userId, Long commentId) { - Comment comment = getCommentsByCommentId(commentId); - validateCommentOwner(comment, userId); - - int count = commentRepository.deleteByCommentId(commentId); // 이름 수정 - Long postId = comment.getPostId(); // postId 가져오기 - - postRepository.decreaseCommentCount(postId, count); + Comment comment = getAuthorizedComment(userId, commentId); + int count = commentRepository.deleteByCommentId(commentId); + postRepository.decreaseCommentCount(comment.getPostId(), count); } - // 모든 댓글 삭제 @Transactional public void deleteAllCommentsByPostId(Long postId) { @@ -92,12 +83,13 @@ public List getAllCommentsByPostId(Long postId) { .toList(); } - private void validateCommentOwner(Comment comment, Long userId) { + // 댓글 조회 + 권한 검증 메서드 추가 + private Comment getAuthorizedComment(Long userId, Long commentId) { + Comment comment = getCommentsByCommentId(commentId); if (!comment.getUserId().equals(userId)) { throw new CustomException(ACCESS_DENY); } + return comment; } - - } diff --git a/src/main/java/com/blog/domain/post/controller/PostController.java b/src/main/java/com/blog/domain/post/controller/PostController.java index a021fc7d..3490d67d 100644 --- a/src/main/java/com/blog/domain/post/controller/PostController.java +++ b/src/main/java/com/blog/domain/post/controller/PostController.java @@ -1,8 +1,8 @@ package com.blog.domain.post.controller; -import com.blog.domain.post.controller.dto.request.PostRequest; -import com.blog.domain.post.controller.dto.response.PostListResponse; -import com.blog.domain.post.controller.dto.response.PostResponse; +import com.blog.domain.post.controller.request.PostRequest; +import com.blog.domain.post.controller.response.PostListResponse; +import com.blog.domain.post.controller.response.PostResponse; import com.blog.domain.post.service.PostService; import com.blog.global.exception.CustomException; import com.blog.global.exception.ErrorCode; @@ -18,7 +18,6 @@ public class PostController { private final PostService postService; -// private final TokenService tokenService; public PostController(PostService postService) { this.postService = postService; @@ -27,13 +26,7 @@ public PostController(PostService postService) { // 글 등록 @PostMapping public ApiResponse createPost(@GetUserId Long userId, @Valid @RequestBody PostRequest postRequest) { - - // 로그인이 되어 있지 않으면 로그인 페이지 URL 반환 - if (userId == null) { - return ApiResponse.fail(new CustomException(ErrorCode.USER_NOT_FOUND)); - } - - postService.savePost(userId, postRequest); + postService.createPost(userId, postRequest); return ApiResponse.ok("정상적으로 등록되었습니다."); } @@ -42,7 +35,6 @@ public ApiResponse createPost(@GetUserId Long userId, @Valid @RequestBod public ApiResponse> getPostList(@GetUserId Long userId) { List posts = postService.getPostList(userId); return ApiResponse.ok(posts); - } // 글 상세 조회 diff --git a/src/main/java/com/blog/domain/post/controller/dto/request/PostContentDto.java b/src/main/java/com/blog/domain/post/controller/request/PostContentDto.java similarity index 95% rename from src/main/java/com/blog/domain/post/controller/dto/request/PostContentDto.java rename to src/main/java/com/blog/domain/post/controller/request/PostContentDto.java index 94bb3657..f4e22087 100644 --- a/src/main/java/com/blog/domain/post/controller/dto/request/PostContentDto.java +++ b/src/main/java/com/blog/domain/post/controller/request/PostContentDto.java @@ -1,4 +1,4 @@ -package com.blog.domain.post.controller.dto.request; +package com.blog.domain.post.controller.request; import com.blog.domain.post.domain.ContentType; import com.blog.domain.post.domain.PostContent; diff --git a/src/main/java/com/blog/domain/post/controller/dto/request/PostRequest.java b/src/main/java/com/blog/domain/post/controller/request/PostRequest.java similarity index 88% rename from src/main/java/com/blog/domain/post/controller/dto/request/PostRequest.java rename to src/main/java/com/blog/domain/post/controller/request/PostRequest.java index 9f046a3d..b20935c3 100644 --- a/src/main/java/com/blog/domain/post/controller/dto/request/PostRequest.java +++ b/src/main/java/com/blog/domain/post/controller/request/PostRequest.java @@ -1,4 +1,4 @@ -package com.blog.domain.post.controller.dto.request; +package com.blog.domain.post.controller.request; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; diff --git a/src/main/java/com/blog/domain/post/controller/dto/response/AuthorSummary.java b/src/main/java/com/blog/domain/post/controller/response/AuthorSummary.java similarity index 87% rename from src/main/java/com/blog/domain/post/controller/dto/response/AuthorSummary.java rename to src/main/java/com/blog/domain/post/controller/response/AuthorSummary.java index ac1c25fd..79be6402 100644 --- a/src/main/java/com/blog/domain/post/controller/dto/response/AuthorSummary.java +++ b/src/main/java/com/blog/domain/post/controller/response/AuthorSummary.java @@ -1,4 +1,4 @@ -package com.blog.domain.post.controller.dto.response; +package com.blog.domain.post.controller.response; import com.blog.domain.user.domain.User; diff --git a/src/main/java/com/blog/domain/post/controller/dto/response/PostListResponse.java b/src/main/java/com/blog/domain/post/controller/response/PostListResponse.java similarity index 80% rename from src/main/java/com/blog/domain/post/controller/dto/response/PostListResponse.java rename to src/main/java/com/blog/domain/post/controller/response/PostListResponse.java index 25588e1f..a2d6c175 100644 --- a/src/main/java/com/blog/domain/post/controller/dto/response/PostListResponse.java +++ b/src/main/java/com/blog/domain/post/controller/response/PostListResponse.java @@ -1,13 +1,11 @@ -package com.blog.domain.post.controller.dto.response; +package com.blog.domain.post.controller.response; -import com.blog.domain.post.controller.dto.request.PostContentDto; -import com.blog.domain.post.domain.ContentType; +import com.blog.domain.post.controller.request.PostContentDto; import com.blog.domain.post.domain.Post; import com.blog.domain.post.domain.PostContent; import java.time.LocalDateTime; import java.util.List; -import java.util.stream.Collectors; public record PostListResponse( long postId, diff --git a/src/main/java/com/blog/domain/post/controller/dto/response/PostResponse.java b/src/main/java/com/blog/domain/post/controller/response/PostResponse.java similarity index 84% rename from src/main/java/com/blog/domain/post/controller/dto/response/PostResponse.java rename to src/main/java/com/blog/domain/post/controller/response/PostResponse.java index a7ded9ba..78e9cf9b 100644 --- a/src/main/java/com/blog/domain/post/controller/dto/response/PostResponse.java +++ b/src/main/java/com/blog/domain/post/controller/response/PostResponse.java @@ -1,7 +1,7 @@ -package com.blog.domain.post.controller.dto.response; +package com.blog.domain.post.controller.response; -import com.blog.domain.comment.controller.dto.response.CommentResponse; -import com.blog.domain.post.controller.dto.request.PostContentDto; +import com.blog.domain.comment.controller.response.CommentResponse; +import com.blog.domain.post.controller.request.PostContentDto; import com.blog.domain.post.domain.Post; import com.blog.domain.post.domain.PostContent; import com.blog.domain.user.domain.User; @@ -9,7 +9,7 @@ import java.time.LocalDateTime; import java.util.List; -import static com.blog.domain.post.controller.dto.response.AuthorSummary.toAuthorSummary; +import static com.blog.domain.post.controller.response.AuthorSummary.toAuthorSummary; public record PostResponse( long postId, diff --git a/src/main/java/com/blog/domain/post/domain/Post.java b/src/main/java/com/blog/domain/post/domain/Post.java index 1a4270b1..932838ee 100644 --- a/src/main/java/com/blog/domain/post/domain/Post.java +++ b/src/main/java/com/blog/domain/post/domain/Post.java @@ -1,6 +1,6 @@ package com.blog.domain.post.domain; -import com.blog.domain.post.controller.dto.request.PostRequest; +import com.blog.domain.post.controller.request.PostRequest; import com.blog.global.common.BaseDomain; import jakarta.validation.constraints.NotBlank; import java.time.LocalDateTime; @@ -8,7 +8,7 @@ public class Post extends BaseDomain { private Long id; - private Long userId; + private long userId; private String title; private int commentCount = 0; @@ -36,12 +36,10 @@ public Post(Long id, Long userId, String title,int commentCount, LocalDateTime c this.commentCount = commentCount; } - public static Post of(Long userId, PostRequest postRequest) { return new Post(userId, postRequest.title()); } - // Getter public Long getId() { return id; @@ -61,16 +59,10 @@ public int getCommentCount() { // 도메인 메서드 public void updateTitle(@NotBlank String title) { - if (title == null || title.isBlank()) { - throw new IllegalArgumentException("제목은 비어있을 수 없습니다."); - } this.title = title; } - public void updateUpdatedAt(LocalDateTime now) { - if (now == null) { - throw new IllegalArgumentException("업데이트 시간은 비어있을 수 없습니다."); - } + public void updateUpdatedAt(@NotBlank LocalDateTime now) { this.updatedAt = now; } diff --git a/src/main/java/com/blog/domain/post/domain/PostContent.java b/src/main/java/com/blog/domain/post/domain/PostContent.java index c21fb072..030a5593 100644 --- a/src/main/java/com/blog/domain/post/domain/PostContent.java +++ b/src/main/java/com/blog/domain/post/domain/PostContent.java @@ -3,7 +3,7 @@ public class PostContent { private Long id; - private Long postId; + private long postId; private ContentType type; // TEXT, IMAGE private String content; private int sequence; // 순서 @@ -22,6 +22,15 @@ public PostContent(Long postId, ContentType type, String content, int sequence) this.sequence = sequence; } + public static PostContent text(Long postId, String text, int sequence) { + return new PostContent(postId,ContentType.TEXT,text,sequence); + } + + public static PostContent image(Long postId, String imageUrl, int sequence) { + return new PostContent(postId, ContentType.IMAGE, imageUrl, sequence); + } + + // getter public int getSequence() { return sequence; } @@ -42,12 +51,5 @@ public Long getId() { return id; } - public static PostContent text(Long postId, String text, int sequence) { - return new PostContent(postId,ContentType.TEXT,text,sequence); - } - - public static PostContent image(Long postId, String imageUrl, int sequence) { - return new PostContent(postId, ContentType.IMAGE, imageUrl, sequence); - } } diff --git a/src/main/java/com/blog/domain/post/repository/PostContentRepository.java b/src/main/java/com/blog/domain/post/repository/PostContentRepository.java index 8d3fa4ab..3f7e3e0a 100644 --- a/src/main/java/com/blog/domain/post/repository/PostContentRepository.java +++ b/src/main/java/com/blog/domain/post/repository/PostContentRepository.java @@ -1,7 +1,6 @@ package com.blog.domain.post.repository; import com.blog.domain.post.domain.ContentType; -import com.blog.domain.post.domain.Post; import com.blog.domain.post.domain.PostContent; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @@ -56,6 +55,7 @@ public void update(List postContents) { } } + // post삭제 public void deleteByPostId(Long postId) { String sql = "DELETE FROM post_content WHERE post_id = ?"; jdbcTemplate.update(sql, postId); diff --git a/src/main/java/com/blog/domain/post/service/PostContentMapper.java b/src/main/java/com/blog/domain/post/service/PostContentMapper.java new file mode 100644 index 00000000..43ca1457 --- /dev/null +++ b/src/main/java/com/blog/domain/post/service/PostContentMapper.java @@ -0,0 +1,26 @@ +package com.blog.domain.post.service; + +import com.blog.domain.post.controller.request.PostContentDto; +import com.blog.domain.post.domain.PostContent; +import com.blog.global.exception.CustomException; +import com.blog.global.exception.ErrorCode; + +import java.util.List; +import java.util.stream.Collectors; + +public class PostContentMapper { + + public static PostContent mapFromDto(Long postId, PostContentDto postContentDto) { + return switch (postContentDto.type()) { + case TEXT -> PostContent.text(postId, postContentDto.data(), 0); + case IMAGE -> PostContent.image(postId, postContentDto.data(), 0); + default -> throw new CustomException(ErrorCode.POST_TYPE_NOT_FOUND); + }; + } + + public static List mapFromDtos(Long postId, List postContentDtos) { + return postContentDtos.stream() + .map(dto -> mapFromDto(postId, dto)) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/blog/domain/post/service/PostService.java b/src/main/java/com/blog/domain/post/service/PostService.java index fc7b4e2d..3de49407 100644 --- a/src/main/java/com/blog/domain/post/service/PostService.java +++ b/src/main/java/com/blog/domain/post/service/PostService.java @@ -1,12 +1,12 @@ package com.blog.domain.post.service; -import com.blog.domain.comment.controller.dto.response.CommentResponse; +import com.blog.domain.comment.controller.response.CommentResponse; import com.blog.domain.comment.service.CommentService; -import com.blog.domain.post.controller.dto.request.PostContentDto; -import com.blog.domain.post.controller.dto.response.PostListResponse; -import com.blog.domain.post.controller.dto.response.PostResponse; +import com.blog.domain.post.controller.request.PostContentDto; +import com.blog.domain.post.controller.response.PostListResponse; +import com.blog.domain.post.controller.response.PostResponse; import com.blog.domain.post.domain.Post; -import com.blog.domain.post.controller.dto.request.PostRequest; +import com.blog.domain.post.controller.request.PostRequest; import com.blog.domain.post.domain.PostContent; import com.blog.domain.post.repository.PostContentRepository; import com.blog.domain.post.repository.PostRepository; @@ -21,9 +21,9 @@ import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.IntStream; @Service +@Transactional public class PostService { private final PostRepository postRepository; @@ -38,50 +38,29 @@ public PostService(PostRepository postRepository, PostContentRepository postCont this.commentService = commentService; } - - // 글 저장 - @Transactional - public void savePost(Long userId, @Valid PostRequest postRequest) { + public void createPost(Long userId, PostRequest postRequest) { + PostValidator.validateUser(userRepository, userId); Post post = Post.of(userId, postRequest); - Long postId = postRepository.save(post); // 먼저 저장하여 postId 확보 - - int sequence = 0; - for (PostContentDto postContentDto : postRequest.contents()) { - PostContent content = createPostContent(postId, postContentDto, sequence++); - postContentRepository.save(content); - } - } + Long postId = postRepository.save(post); - // 글 내용 저장 메서드 추출 - private PostContent createPostContent(Long postId, PostContentDto postContentDto, int sequence) { - return switch (postContentDto.type()) { - case TEXT -> PostContent.text(postId, postContentDto.data(), sequence); - case IMAGE -> PostContent.image(postId, postContentDto.data(), sequence); - default -> throw new CustomException(ErrorCode.POST_TYPE_NOT_FOUND); - }; + // PostContent 저장 + postRequest.contents().forEach((contentDto) -> postContentRepository.save(PostContentMapper.mapFromDto(postId, contentDto))); } - // 개별 조회 public PostResponse getPost(Long userId, Long postId) { - User user = validateUser(userId); - Post post = validatePost(postId); + User user = PostValidator.validateUser(userRepository, userId); // User validation + Post post = PostValidator.validatePostAccess(postRepository, postId, userId); // Post & owner validation List postContent = postContentRepository.findByPostIdOrderBySequence(postId); - boolean isOwner = post.getUserId().equals(userId);// 본인 글인지 판단 + boolean isOwner = post.getUserId().equals(userId); List commentResponse = commentService.getAllCommentsByPostId(postId); - return PostResponse.withComments( - PostResponse.from(post, postContent, user, isOwner), - commentResponse - ); + return PostResponse.withComments(PostResponse.from(post, postContent, user, isOwner), commentResponse); } - - // 전체 조회 public List getPostList(Long userId) { List posts = postRepository.findAllByUserId(userId); - // 유저가 쓴 글만 가져오기 return posts.stream() .map(post -> { List postContents = postContentRepository.findByPostIdOrderBySequence(post.getId()); @@ -92,9 +71,9 @@ public List getPostList(Long userId) { @Transactional public void updatePost(Long userId, Long postId, @Valid PostRequest postRequest) { - User user = validateUser(userId); - Post post = validatePost(postId); - validateOwner(post, userId); + PostValidator.validatePostAccess(postRepository, postId, userId); + Post post = postRepository.findById(postId) + .orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND)); updatePostInfo(post, postRequest); updatePostContents(postId, postRequest); @@ -106,54 +85,27 @@ private void updatePostInfo(Post post, PostRequest postRequest) { postRepository.update(post); } - private void updatePostContents(Long postId, PostRequest postRequest) { - List newContents = IntStream.range(0, postRequest.contents().size()) - .mapToObj(i -> { - var dto = postRequest.contents().get(i); - return new PostContent(postId, dto.type(), dto.data(), i); - }) - .collect(Collectors.toList()); - + @Transactional + public void updatePostContents(Long postId, PostRequest postRequest) { + List newContents = PostContentMapper.mapFromDtos(postId, postRequest.contents()); postContentRepository.update(newContents); } @Transactional public void deletePost(Long userId, Long postId) { - User user = validateUser(userId); - Post post = validatePost(postId); - validateOwner(post, userId); - + PostValidator.validatePostAccess(postRepository, postId, userId); // User, Post, Owner validation commentService.deleteAllCommentsByPostId(postId); deletePostContents(postId); - deletePostEntity(post); + deletePostEntity(postRepository.findById(postId).orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND))); } - private void deletePostContents(Long postId) { + @Transactional + public void deletePostContents(Long postId) { postContentRepository.deleteByPostId(postId); } - private void deletePostEntity(Post post) { + @Transactional + public void deletePostEntity(Post post) { postRepository.deleteById(post.getId()); } - - // 검증 메서드 - private User validateUser(Long userId) { - return userRepository.findByUserId(userId) - .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); - } - - private Post validatePost(Long postId) { - return postRepository.findById(postId) - .orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND)); - } - - private void validateOwner(Post post, Long userId) { - if (!post.getUserId().equals(userId)) { - throw new CustomException(ErrorCode.UNAUTHORIZED_POST_ACCESS); - } - } - - - - } diff --git a/src/main/java/com/blog/domain/post/service/PostValidator.java b/src/main/java/com/blog/domain/post/service/PostValidator.java new file mode 100644 index 00000000..825dff05 --- /dev/null +++ b/src/main/java/com/blog/domain/post/service/PostValidator.java @@ -0,0 +1,29 @@ +package com.blog.domain.post.service; + +import com.blog.domain.post.domain.Post; +import com.blog.domain.post.repository.PostRepository; +import com.blog.domain.user.domain.User; +import com.blog.domain.user.repository.UserRepository; +import com.blog.global.exception.CustomException; +import com.blog.global.exception.ErrorCode; + +public class PostValidator { + + // 유저 검증 + public static User validateUser(UserRepository userRepo, Long userId) { + return userRepo.findByUserId(userId) + .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); + } + + // 게시글 검증 및 소유자 검증을 한번에 처리 + public static Post validatePostAccess(PostRepository postRepo, Long postId, Long userId) { + Post post = postRepo.findById(postId) + .orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND)); + + if (!post.getUserId().equals(userId)) { + throw new CustomException(ErrorCode.UNAUTHORIZED_POST_ACCESS); + } + + return post; + } +} diff --git a/src/main/java/com/blog/domain/user/controller/AuthController.java b/src/main/java/com/blog/domain/user/controller/AuthController.java index 767a210d..9b970260 100644 --- a/src/main/java/com/blog/domain/user/controller/AuthController.java +++ b/src/main/java/com/blog/domain/user/controller/AuthController.java @@ -1,18 +1,11 @@ package com.blog.domain.user.controller; -import com.blog.domain.user.controller.dto.request.JoinRequest; import com.blog.domain.user.domain.User; import com.blog.domain.user.service.KakaoService; -import com.blog.domain.user.service.LoginService; -import com.blog.domain.user.service.TokenService; import com.blog.domain.user.service.UserService; import com.blog.global.exception.CustomException; -import com.blog.global.exception.ErrorCode; import com.blog.global.response.ApiResponse; -import com.blog.global.security.CustomTokenUtil; import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import java.util.Map; diff --git a/src/main/java/com/blog/domain/user/controller/JoinController.java b/src/main/java/com/blog/domain/user/controller/JoinController.java index d134aee0..6c6a1dbd 100644 --- a/src/main/java/com/blog/domain/user/controller/JoinController.java +++ b/src/main/java/com/blog/domain/user/controller/JoinController.java @@ -1,15 +1,11 @@ package com.blog.domain.user.controller; -import com.blog.domain.user.controller.dto.request.JoinRequest; +import com.blog.domain.user.controller.request.JoinRequest; import com.blog.domain.user.service.UserService; -import com.blog.global.exception.CustomException; -import com.blog.global.exception.ErrorCode; import com.blog.global.response.ApiResponse; +import com.blog.global.security.aop.GetUserId; import jakarta.validation.Valid; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.security.NoSuchAlgorithmException; @@ -25,10 +21,15 @@ public JoinController(UserService userService) { @PostMapping public ApiResponse join(@Valid @RequestBody JoinRequest joinRequest) throws NoSuchAlgorithmException { - userService.join(joinRequest); return ApiResponse.ok("회원가입이 완료되었습니다."); } + @DeleteMapping + public ApiResponse deleteUser(@GetUserId Long userId) { + userService.deleteUser(userId); + return ApiResponse.ok("회원탈퇴가 완료되었습니다."); + + } } diff --git a/src/main/java/com/blog/domain/user/controller/LoginController.java b/src/main/java/com/blog/domain/user/controller/LoginController.java index 4987a8fd..61a17474 100644 --- a/src/main/java/com/blog/domain/user/controller/LoginController.java +++ b/src/main/java/com/blog/domain/user/controller/LoginController.java @@ -1,18 +1,13 @@ package com.blog.domain.user.controller; -import com.blog.domain.user.controller.dto.request.LoginRequest; -import com.blog.domain.user.domain.User; -import com.blog.domain.user.repository.TokenStore; +import com.blog.domain.user.controller.request.LoginRequest; import com.blog.domain.user.service.LoginService; import com.blog.domain.user.service.TokenService; -import com.blog.global.exception.CustomException; import com.blog.global.response.ApiResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; -import java.util.Map; -import static com.blog.global.exception.ErrorCode.*; - @RestController @RequestMapping("/login") @@ -21,7 +16,7 @@ public class LoginController { private final LoginService loginService; private final TokenService tokenService; - public LoginController(LoginService loginService, TokenService tokenService, TokenStore tokenStore) { + public LoginController(LoginService loginService, TokenService tokenService) { this.loginService = loginService; this.tokenService = tokenService; } @@ -29,32 +24,25 @@ public LoginController(LoginService loginService, TokenService tokenService, Tok // 이메일 로그인 @PostMapping - public ApiResponse login(@RequestBody LoginRequest loginRequest, HttpServletResponse response) { - + public ApiResponse login(@RequestBody @Valid LoginRequest loginRequest, HttpServletResponse response) { // 사용자 정보 검증 String accessToken = loginService.authenticateUser(loginRequest.email(), loginRequest.password(), response); - return ApiResponse.ok(accessToken); } // refresh 토큰으로 accessToken 재발급 @PostMapping("/refresh") public ApiResponse refresh(HttpServletRequest request) { - String newAccessToken = tokenService.getAccessTokenFromRequest(request); - return ApiResponse.ok(newAccessToken); } // 로그아웃 @PostMapping("/logout") - public ApiResponse logout(HttpServletRequest request) { - - tokenService.deleteRefreshTokenByLogout(request); - + public ApiResponse logout(HttpServletRequest request,HttpServletResponse response) { + tokenService.deleteRefreshTokenByLogout(request,response); return ApiResponse.ok("로그아웃 성공"); } - } diff --git a/src/main/java/com/blog/domain/user/controller/MyPageController.java b/src/main/java/com/blog/domain/user/controller/MyPageController.java new file mode 100644 index 00000000..bdc4912a --- /dev/null +++ b/src/main/java/com/blog/domain/user/controller/MyPageController.java @@ -0,0 +1,46 @@ +package com.blog.domain.user.controller; + +import com.blog.domain.user.controller.request.UpdateRequest; +import com.blog.domain.user.controller.response.MypageResponse; +import com.blog.domain.user.domain.User; +import com.blog.domain.user.service.MyPageService; +import com.blog.global.exception.CustomException; +import com.blog.global.exception.ErrorCode; +import com.blog.global.response.ApiResponse; +import com.blog.global.security.aop.GetUserId; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/mypage") +public class MyPageController { + + private final MyPageService myPageService; + + public MyPageController(MyPageService myPageService) { + this.myPageService = myPageService; + } + + // 정보 조회 + @GetMapping + public ApiResponse getMyPage(@GetUserId Long userId) { + User user = myPageService.getMyPageInfo(userId); + if (user == null) { + throw new CustomException(ErrorCode.USER_NOT_FOUND); + } + MypageResponse myPageInfo = MypageResponse.from(user); + + return ApiResponse.ok(myPageInfo); + } + + // 정보 수정 + @PutMapping + public ApiResponse updateMyPage(@GetUserId Long userId, + @RequestBody @Valid UpdateRequest userRequest) { + + myPageService.updateMyPageInfo(userId, userRequest); + return ApiResponse.ok("정상적으로 수정되었습니다."); + } + + +} diff --git a/src/main/java/com/blog/domain/user/controller/dto/request/JoinRequest.java b/src/main/java/com/blog/domain/user/controller/request/JoinRequest.java similarity index 93% rename from src/main/java/com/blog/domain/user/controller/dto/request/JoinRequest.java rename to src/main/java/com/blog/domain/user/controller/request/JoinRequest.java index f1816a4b..0322af75 100644 --- a/src/main/java/com/blog/domain/user/controller/dto/request/JoinRequest.java +++ b/src/main/java/com/blog/domain/user/controller/request/JoinRequest.java @@ -1,4 +1,4 @@ -package com.blog.domain.user.controller.dto.request; +package com.blog.domain.user.controller.request; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; diff --git a/src/main/java/com/blog/domain/user/controller/dto/request/LoginRequest.java b/src/main/java/com/blog/domain/user/controller/request/LoginRequest.java similarity index 81% rename from src/main/java/com/blog/domain/user/controller/dto/request/LoginRequest.java rename to src/main/java/com/blog/domain/user/controller/request/LoginRequest.java index dffe0472..da08546c 100644 --- a/src/main/java/com/blog/domain/user/controller/dto/request/LoginRequest.java +++ b/src/main/java/com/blog/domain/user/controller/request/LoginRequest.java @@ -1,4 +1,4 @@ -package com.blog.domain.user.controller.dto.request; +package com.blog.domain.user.controller.request; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; diff --git a/src/main/java/com/blog/domain/user/controller/request/UpdateRequest.java b/src/main/java/com/blog/domain/user/controller/request/UpdateRequest.java new file mode 100644 index 00000000..5d49289c --- /dev/null +++ b/src/main/java/com/blog/domain/user/controller/request/UpdateRequest.java @@ -0,0 +1,27 @@ +package com.blog.domain.user.controller.request; + +import java.time.LocalDateTime; + +import jakarta.validation.constraints.*; + +public record UpdateRequest( + + @NotBlank(message = "이름은 필수입니다.") + String name, + + @Email(message = "유효한 이메일 형식이어야 합니다.") + @NotBlank(message = "이메일은 필수입니다.") + String email, + + @NotBlank(message = "닉네임은 필수입니다.") + String nickname, + + @Past(message = "생일은 과거 날짜여야 합니다.") + LocalDateTime birth, + + String profileImage, + + @Size(max = 255, message = "자기소개는 255자 이하로 입력해주세요.") + String introduction + +) {} diff --git a/src/main/java/com/blog/domain/user/controller/response/MypageResponse.java b/src/main/java/com/blog/domain/user/controller/response/MypageResponse.java new file mode 100644 index 00000000..28705cf8 --- /dev/null +++ b/src/main/java/com/blog/domain/user/controller/response/MypageResponse.java @@ -0,0 +1,22 @@ +package com.blog.domain.user.controller.response; + +import com.blog.domain.user.domain.User; + +public record MypageResponse ( + String userName, + String nickName, + String email, + String profileImageUrl, + String introduction + +){ + public static MypageResponse from(User user) { + return new MypageResponse( + user.getName(), + user.getNickname(), + user.getEmail(), + user.getProfileImage(), + user.getIntroduction() + ); + } +} diff --git a/src/main/java/com/blog/domain/user/domain/User.java b/src/main/java/com/blog/domain/user/domain/User.java index dce788af..6a8742a5 100644 --- a/src/main/java/com/blog/domain/user/domain/User.java +++ b/src/main/java/com/blog/domain/user/domain/User.java @@ -1,12 +1,11 @@ package com.blog.domain.user.domain; +import com.blog.domain.user.controller.request.UpdateRequest; import com.blog.global.common.BaseDomain; - import java.time.LocalDateTime; public class User extends BaseDomain { - private Long id; private String email; private String password; @@ -17,6 +16,7 @@ public class User extends BaseDomain { private String profileImage; private Provider provider; + // constructor public User() { } @@ -31,25 +31,35 @@ public User(String email, String password, String name, String nickname, LocalDa this.provider = Provider.valueOf(provider); } - - public User(Long id, String email, String password) { this.id = id; this.email = email; this.password = password; } - // 🔹 정적 팩토리 메서드 추가 (더 명확한 의미 전달) - public static User createSocialUser(String email, String password, String name, String nickname, LocalDateTime birth, String introduction, String profileImage, String provider) { - return new User(email, password, name, nickname, birth, introduction, profileImage, provider); + public User(long id, String email, String password, String name, String nickname, LocalDateTime localDateTime, String introduction, String profileImage, String provider) { + this.id = id; + this.email = email; + this.password = password; + this.name = name; + this.nickname = nickname; + this.birth = localDateTime; + this.introduction = introduction; + this.profileImage = profileImage; + this.provider = Provider.valueOf(provider); } - public static User createBasicUser(Long id, String email, String password) { - return new User(id, email, password); + public User(String email, String password, String name, String nickname, LocalDateTime birth, String introduction, String profileImage) { + this.email = email; + this.password = password; + this.name = name; + this.nickname = nickname; + this.birth = birth; + this.introduction = introduction; + this.profileImage = profileImage; } - - + // getter public Long getId() { return id; } @@ -62,15 +72,14 @@ public String getPassword() { return password; } - public String getNickname() { return nickname; } - public String getProfileImage() { return profileImage; } + public String getName() { return name; } @@ -87,16 +96,14 @@ public Provider getProvider() { return provider; } - public void setId(Long id) { - this.id = id; - } - public void setEmail(String email) { - this.email = email; - } - - public void setPassword(String password) { - this.password = password; + // 도메인 메서드 + public void updateMyPageInfo(UpdateRequest req) { + if (req.name() != null) this.name = req.name(); + if (req.nickname() != null) this.nickname = req.nickname(); + if (req.introduction() != null) this.introduction = req.introduction(); + if (req.birth() != null) this.birth = req.birth(); + if (req.profileImage() != null) this.profileImage = req.profileImage(); } } diff --git a/src/main/java/com/blog/domain/user/repository/UserRepository.java b/src/main/java/com/blog/domain/user/repository/UserRepository.java index 7a13b471..3ff49bec 100644 --- a/src/main/java/com/blog/domain/user/repository/UserRepository.java +++ b/src/main/java/com/blog/domain/user/repository/UserRepository.java @@ -70,6 +70,7 @@ public Optional findByUserId(Long userId) { String sql = "SELECT * FROM `user` WHERE id = ?"; try { RowMapper rowMapper = (rs, rowNum) -> new User( + rs.getLong("id"), rs.getString("email"), rs.getString("password"), rs.getString("name"), @@ -86,4 +87,64 @@ public Optional findByUserId(Long userId) { throw new CustomException(ErrorCode.USER_NOT_FOUND); } } + + public void deleteUser(Long userId) { + String sql = "DELETE FROM user WHERE id = ?"; + + try { + int deletedCount = jdbcTemplate.update(sql, userId); + + if (deletedCount == 0) { + // 삭제된 행이 없다면 예외 발생 + throw new CustomException(ErrorCode.USER_NOT_FOUND); + } + } catch (DataAccessException e) { + // 데이터 접근 오류 시 예외 처리 + throw new CustomException(ErrorCode.INVALID_REFRESH_TOKEN); // 예외 코드에 맞게 변경 + } + } + + // 마이페이지 정보 가져오기 + public Optional findByUserIdForMyPage(Long userId) { + String sql = "SELECT * FROM `user` WHERE id = ?"; + try { + RowMapper rowMapper = (rs, rowNum) -> new User( + rs.getLong("id"), + rs.getString("email"), + rs.getString("password"), + rs.getString("name"), + rs.getString("nickname"), + rs.getDate("birth") != null ? rs.getDate("birth").toLocalDate().atStartOfDay() : null, + rs.getString("introduction"), + rs.getString("profile_image"), + rs.getString("provider") + ); + + return Optional.ofNullable(jdbcTemplate.queryForObject(sql, new Object[]{userId}, rowMapper)); + + } catch (EmptyResultDataAccessException e) { + throw new CustomException(ErrorCode.USER_NOT_FOUND); + } + } + + // 마이페이지 수정 + public User updateUser(User user) { + String sql = "UPDATE user SET name = ?, nickname = ?, introduction = ?, birth = ?, profile_image = ? WHERE id = ?"; + LocalDateTime birth = user.getBirth(); // LocalDateTime + java.sql.Date sqlBirth = (birth != null) ? java.sql.Date.valueOf(birth.toLocalDate()) : null; + + jdbcTemplate.update( + sql, + user.getName(), + user.getNickname(), + user.getIntroduction(), + sqlBirth, + user.getProfileImage(), + user.getId() + ); + return user; + } + } + + diff --git a/src/main/java/com/blog/domain/user/service/KakaoService.java b/src/main/java/com/blog/domain/user/service/KakaoService.java index 22f878e9..e5cde5ae 100644 --- a/src/main/java/com/blog/domain/user/service/KakaoService.java +++ b/src/main/java/com/blog/domain/user/service/KakaoService.java @@ -64,7 +64,6 @@ private String extractAccessToken(ResponseEntity response) { // 카카오 유저 정보 가져오기 public Map getKakaoUserInfo(String accessToken) { - System.out.println(accessToken); RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); diff --git a/src/main/java/com/blog/domain/user/service/MyPageService.java b/src/main/java/com/blog/domain/user/service/MyPageService.java new file mode 100644 index 00000000..3cbf54b0 --- /dev/null +++ b/src/main/java/com/blog/domain/user/service/MyPageService.java @@ -0,0 +1,40 @@ +package com.blog.domain.user.service; + +import com.blog.domain.user.controller.request.UpdateRequest; +import com.blog.domain.user.domain.User; +import com.blog.domain.user.repository.UserRepository; +import com.blog.global.exception.CustomException; +import com.blog.global.exception.ErrorCode; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class MyPageService { + + private final UserRepository userRepository; + + public MyPageService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + //정보 조회 + public User getMyPageInfo(long userId) { + return userRepository.findByUserIdForMyPage(userId) + .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); + + } + + //정보 수정 + @Transactional + public User updateMyPageInfo(long userId, UpdateRequest req) { + User existingUser = userRepository.findByUserIdForMyPage(userId) + .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); + + // 변경 사항만 반영 + existingUser.updateMyPageInfo(req); + + return userRepository.updateUser(existingUser); + } + + +} diff --git a/src/main/java/com/blog/domain/user/service/TokenService.java b/src/main/java/com/blog/domain/user/service/TokenService.java index 59137785..b44f4779 100644 --- a/src/main/java/com/blog/domain/user/service/TokenService.java +++ b/src/main/java/com/blog/domain/user/service/TokenService.java @@ -6,6 +6,8 @@ import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseCookie; import org.springframework.stereotype.Service; import java.util.Map; @@ -62,9 +64,7 @@ public String getAccessTokenFromRequest(HttpServletRequest request) { } - // 로그아웃할때, 토큰을 삭제 - public void deleteRefreshTokenByLogout(HttpServletRequest request) { - + public void deleteRefreshTokenByLogout(HttpServletRequest request, HttpServletResponse response) { String token = getTokenFromRequest(request); // 토큰에서 사용자 정보 추출 (디코딩) @@ -73,7 +73,18 @@ public void deleteRefreshTokenByLogout(HttpServletRequest request) { // userId가 Long 형식이라면 Long userId = Long.valueOf(user.get("userId").toString()); + // 토큰 삭제 tokenStore.removeToken(userId); + + // 쿠키에서 userId 삭제 + ResponseCookie cookie = ResponseCookie.from("userId", "") + .httpOnly(true) + .secure(true) + .path("/") + .maxAge(0) // 즉시 만료 + .build(); + + response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); } // 쿠키에 UserId 추가하기 diff --git a/src/main/java/com/blog/domain/user/service/UserService.java b/src/main/java/com/blog/domain/user/service/UserService.java index 75a53002..ac7939be 100644 --- a/src/main/java/com/blog/domain/user/service/UserService.java +++ b/src/main/java/com/blog/domain/user/service/UserService.java @@ -1,12 +1,13 @@ package com.blog.domain.user.service; -import com.blog.domain.user.controller.dto.request.JoinRequest; +import com.blog.domain.user.controller.request.JoinRequest; import com.blog.domain.user.domain.User; import com.blog.domain.user.repository.UserRepository; import com.blog.global.exception.CustomException; import com.blog.global.exception.ErrorCode; import com.blog.global.security.PasswordUtil; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.security.NoSuchAlgorithmException; import java.util.Map; @@ -69,4 +70,12 @@ private User createMember(JoinRequest joinRequest, String encryptedPassword) { ); } + // 유저 탈퇴 + @Transactional + public void deleteUser(long userId) { + User user = userRepository.findByUserId(userId) + .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); + userRepository.deleteUser(user.getId()); + } + } diff --git a/src/main/java/com/blog/global/exception/ErrorCode.java b/src/main/java/com/blog/global/exception/ErrorCode.java index 852c3d42..7689fdbf 100644 --- a/src/main/java/com/blog/global/exception/ErrorCode.java +++ b/src/main/java/com/blog/global/exception/ErrorCode.java @@ -1,53 +1,53 @@ package com.blog.global.exception; import org.springframework.http.HttpStatus; - public enum ErrorCode { - // Test Error - TEST_ERROR(100, HttpStatus.BAD_REQUEST, "테스트 에러입니다."), - // 400 Bad Request - BAD_REQUEST(400, HttpStatus.BAD_REQUEST, "잘못된 요청입니다."), - // 403 Forbidden - FORBIDDEN(403, HttpStatus.FORBIDDEN, "접속 권한이 없습니다."), - // 404 Not Found - NOT_FOUND_END_POINT(404, HttpStatus.NOT_FOUND, "요청한 대상이 존재하지 않습니다."), - // 500 Internal Server Error - INTERNAL_SERVER_ERROR(500, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."), + // Test Error + TEST_ERROR(400_001, HttpStatus.BAD_REQUEST, "테스트 에러입니다."), - // Custom Error (추후에 프론트랑 맞출 수 있음) - DUPLICATE_EMAIL(1001, HttpStatus.CONFLICT, "이미 사용 중인 이메일입니다."), - USER_NOT_FOUND(1002, HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다."), - INVALID_CREDENTIALS(1003, HttpStatus.UNAUTHORIZED, "인증 정보가 올바르지 않습니다."), - INVALID_REFRESH_TOKEN(1004, HttpStatus.UNAUTHORIZED, "재발급 토큰이 올바르지 않습니다."), - INVALID_ACCESS_TOKEN(1005, HttpStatus.UNAUTHORIZED, "접근 토큰이 올바르지 않습니다."), - INVALID_TOKEN(1006, HttpStatus.UNAUTHORIZED, "토큰이 생성되지 않았습니다"), - USER_NOT_FOUND_IN_COOKIE(1007, HttpStatus.NOT_FOUND,"쿠키에서 사용자정보를 찾을 수 없습니다."), + // 400 Bad Request + BAD_REQUEST(400_000, HttpStatus.BAD_REQUEST, "잘못된 요청입니다."), + INVALID_FILE_FORMAT(400_001, HttpStatus.BAD_REQUEST, "업로드된 파일 형식이 올바르지 않습니다."), + INVALID_INPUT(400_002, HttpStatus.BAD_REQUEST, "입력값이 올바르지 않습니다."), + NULL_VALUE(400_003, HttpStatus.BAD_REQUEST, "Null 값이 들어왔습니다."), + + // 401 Unauthorized + INVALID_CREDENTIALS(401_001, HttpStatus.UNAUTHORIZED, "인증 정보가 올바르지 않습니다."), + INVALID_REFRESH_TOKEN(401_002, HttpStatus.UNAUTHORIZED, "재발급 토큰이 올바르지 않습니다."), + INVALID_ACCESS_TOKEN(401_003, HttpStatus.UNAUTHORIZED, "접근 토큰이 올바르지 않습니다."), + INVALID_TOKEN(401_004, HttpStatus.UNAUTHORIZED, "토큰이 생성되지 않았습니다"), + INVALID_LOGIN(401_005, HttpStatus.UNAUTHORIZED, "로그인이 필요합니다."), - POST_NOT_FOUND(2001, HttpStatus.NOT_FOUND, "요청한 게시글을 찾을 수 없습니다."), - UNAUTHORIZED_POST_ACCESS(2002, HttpStatus.FORBIDDEN, "해당 게시글에 접근할 권한이 없습니다."), - POST_TYPE_NOT_FOUND(2003, HttpStatus.NOT_FOUND, "게시글 타입을 찾을 수 없습니다."), + // 403 Forbidden + FORBIDDEN(403_000, HttpStatus.FORBIDDEN, "접속 권한이 없습니다."), + ACCESS_DENY(403_001, HttpStatus.FORBIDDEN, "접근이 거부되었습니다."), + UNAUTHORIZED_POST_ACCESS(403_002, HttpStatus.FORBIDDEN, "해당 게시글에 접근할 권한이 없습니다."), + // 404 Not Found + NOT_FOUND_END_POINT(404_000, HttpStatus.NOT_FOUND, "요청한 대상이 존재하지 않습니다."), + USER_NOT_FOUND(404_001, HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다."), + USER_NOT_FOUND_IN_COOKIE(404_002, HttpStatus.NOT_FOUND, "쿠키에서 사용자정보를 찾을 수 없습니다."), + POST_NOT_FOUND(404_003, HttpStatus.NOT_FOUND, "요청한 게시글을 찾을 수 없습니다."), + POST_TYPE_NOT_FOUND(404_004, HttpStatus.NOT_FOUND, "게시글 타입을 찾을 수 없습니다."), + COMMENT_NOT_FOUND(404_005, HttpStatus.NOT_FOUND, "요청한 댓글을 찾을 수 없습니다."), - COMMENT_NOT_FOUND(3001, HttpStatus.NOT_FOUND, "요청한 댓글을 찾을 수 없습니다."), - INVALID_FILE_FORMAT(4001, HttpStatus.BAD_REQUEST, "업로드된 파일 형식이 올바르지 않습니다."), - INVALID_INPUT(4002, HttpStatus.BAD_REQUEST, "입력값이 올바르지 않습니다."), - ACCESS_DENY(4003, HttpStatus.FORBIDDEN, "접근이 거부되었습니다."), - NULL_VALUE(4004, HttpStatus.BAD_REQUEST, "Null 값이 들어왔습니다."); + // 409 Conflict + DUPLICATE_EMAIL(409_001, HttpStatus.CONFLICT, "이미 사용 중인 이메일입니다."), + // 500 Internal Server Error + INTERNAL_SERVER_ERROR(500_000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); private final Integer code; private final HttpStatus httpStatus; private final String message; - // Constructor - ErrorCode(int i, HttpStatus httpStatus, String s) { - this.code = i; + ErrorCode(int code, HttpStatus httpStatus, String message) { + this.code = code; this.httpStatus = httpStatus; - this.message = s; + this.message = message; } - // Getter public Integer getCode() { return code; } @@ -59,5 +59,4 @@ public HttpStatus getHttpStatus() { public String getMessage() { return message; } - } diff --git a/src/main/java/com/blog/global/logging/LoggingAspect.java b/src/main/java/com/blog/global/logging/LoggingAspect.java new file mode 100644 index 00000000..e4f85f0b --- /dev/null +++ b/src/main/java/com/blog/global/logging/LoggingAspect.java @@ -0,0 +1,33 @@ +package com.blog.global.logging; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.*; +import org.springframework.stereotype.Component; + +import java.util.Arrays; + +@Aspect +@Component +public class LoggingAspect { + + @Pointcut("execution(* com.blog..*Service.*(..))") + public void logPointcut() {} + + @Before("logPointcut()") + public void logBefore(JoinPoint joinPoint) { + System.out.println("📌 BEFORE: " + joinPoint.getSignature().toShortString()); + System.out.println("📦 Arguments: " + Arrays.toString(joinPoint.getArgs())); + } + + @AfterReturning(pointcut = "logPointcut()", returning = "result") + public void logAfterReturning(JoinPoint joinPoint, Object result) { + System.out.println("✅ AFTER RETURN: " + joinPoint.getSignature().toShortString()); + System.out.println("🎁 Returned: " + result); + } + + @AfterThrowing(pointcut = "logPointcut()", throwing = "e") + public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { + System.out.println("❌ EXCEPTION in: " + joinPoint.getSignature().toShortString()); + System.out.println("⚠️ Exception: " + e.getMessage()); + } +} diff --git a/src/main/java/com/blog/global/response/ApiResponse.java b/src/main/java/com/blog/global/response/ApiResponse.java index a4086545..035f8b2f 100644 --- a/src/main/java/com/blog/global/response/ApiResponse.java +++ b/src/main/java/com/blog/global/response/ApiResponse.java @@ -20,10 +20,6 @@ public static ApiResponse ok(@Nullable final T data) { return new ApiResponse<>(HttpStatus.OK, true, data, null); } - public static ApiResponse created(@Nullable final T data) { - return new ApiResponse<>(HttpStatus.CREATED, true, data, null); - } - public static ApiResponse fail(final CustomException e) { return new ApiResponse<>(e.getErrorCode().getHttpStatus(), false, null, ExceptionDto.of(e.getErrorCode())); } diff --git a/src/main/java/com/blog/global/security/aop/UserIdArgumentResolver.java b/src/main/java/com/blog/global/security/aop/UserIdArgumentResolver.java index 1e11dc98..706fe31e 100644 --- a/src/main/java/com/blog/global/security/aop/UserIdArgumentResolver.java +++ b/src/main/java/com/blog/global/security/aop/UserIdArgumentResolver.java @@ -1,6 +1,8 @@ package com.blog.global.security.aop; import com.blog.domain.user.service.TokenService; +import com.blog.global.exception.CustomException; +import com.blog.global.exception.ErrorCode; import jakarta.servlet.http.HttpServletRequest; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; @@ -26,7 +28,12 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); - return tokenService.getUserIdFromCookie(request); // 👈 여기에서 꺼내줌 + Long userId = tokenService.getUserIdFromCookie(request); + if (userId == null) { + throw new CustomException(ErrorCode.USER_NOT_FOUND_IN_COOKIE); + } + + return userId; } }