Skip to content
Open
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
24 changes: 24 additions & 0 deletions src/main/java/com/leets/blog/controller/ApiControllerAdvice.java
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예외 핸들러가 정말 깔끔하네요! 특히 ErrorType에 따른 로그 레벨 분기는 운영 시 불필요한 노이즈를 줄여주는 세심한 설계인 것 같아 인상 깊습니다.

저도 공부하다 알게되어 한 가지 제안을 드리자면, ResponseEntity 대신 ResponseEntity>처럼 타입을 명시해보는 건 어떨까요?
현재 방식도 동작에는 문제가 없지만, Swagger 같은 문서화 도구를 사용하는 경우 응답 스펙을 자동으로 인식하지 못하는 경우가 있어서, 타입을 구체화하면 API 스펙 공유나 협업 측면에서 도움이 될 수 있을 것 같습니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.leets.blog.controller;

import com.leets.blog.support.error.PostException;
import com.leets.blog.support.response.ApiResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class ApiControllerAdvice {
private final Logger log = LoggerFactory.getLogger(getClass());

@ExceptionHandler(PostException.class)
public ResponseEntity<?> handlePostException(PostException e) {
switch (e.getErrorType().getLogLevel()) {
case ERROR -> log.error("PostException : {}", e.getMessage(), e);
case WARN -> log.warn("PostException : {}", e.getMessage(), e);
default -> log.info("PostException : {}", e.getMessage(), e);
}
return new ResponseEntity<>(ApiResponse.error(e.getErrorType(), e.getData()), e.getErrorType().getStatus());
}
}
50 changes: 50 additions & 0 deletions src/main/java/com/leets/blog/controller/v1/PostController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.leets.blog.controller.v1;

import com.leets.blog.dto.request.AddPostRequest;
import com.leets.blog.dto.request.UpdatePostRequest;
import com.leets.blog.dto.response.PostResponse;
import com.leets.blog.service.PostService;
import com.leets.blog.support.response.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/v1/posts")
@RequiredArgsConstructor
public class PostController {

private final PostService postService;

@GetMapping
public ApiResponse<List<PostResponse>> getPosts() {
List<PostResponse> result = postService.getPosts();
return ApiResponse.success(result);
}

@GetMapping("/{postId}")
public ApiResponse<PostResponse> getPostByPostId(@PathVariable Long postId) {
PostResponse result = postService.getPostByPostId(postId);
return ApiResponse.success(result);
}

@PostMapping
public ApiResponse<PostResponse> writePost(@RequestBody AddPostRequest request) {
PostResponse result = postService.addPost(request);
return ApiResponse.success(result);
}

@PutMapping("/{postId}")
public ApiResponse<PostResponse> reWritePost(@PathVariable Long postId, @RequestBody UpdatePostRequest request) {
PostResponse result = postService.updatePost(postId, request);
return ApiResponse.success(result);
}

@DeleteMapping("/{postId}")
public ApiResponse<?> deletePost(@PathVariable Long postId) {
postService.deletePost(postId);
return ApiResponse.success();
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.leets.blog.controller.v1;

import com.leets.blog.dto.StringRequest;
import com.leets.blog.dto.StringResponse;
import com.leets.blog.dto.request.StringRequest;
import com.leets.blog.dto.response.StringResponse;
import com.leets.blog.service.DtoConverter;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/leets/blog/dto/request/AddPostRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.leets.blog.dto.request;

import com.leets.blog.support.error.ErrorType;
import com.leets.blog.support.error.PostException;

public record AddPostRequest(
String title,
String content
) {
public AddPostRequest {
// 제목이 10자를 초과할 경우 예외 발생
if (title == null || title.length() < 10) {
throw new PostException(ErrorType.INVALID_EXCEPTION);
}

// 내용이 비어있는지 확인하는 추가 검증
if (content == null || content.isBlank()) {
throw new PostException(ErrorType.INVALID_EXCEPTION);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.leets.blog.dto;
package com.leets.blog.dto.request;

public record StringRequest(
String string
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/leets/blog/dto/request/UpdatePostRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.leets.blog.dto.request;

import com.leets.blog.support.error.ErrorType;
import com.leets.blog.support.error.PostException;

public record UpdatePostRequest(
String title,
String content
) {
public UpdatePostRequest {
// 제목이 10자를 초과할 경우 예외 발생
if (title != null && title.length() > 10) {
throw new PostException(ErrorType.INVALID_EXCEPTION);
}

// 내용이 비어있는지 확인하는 추가 검증
if (content == null || content.isBlank()) {
throw new PostException(ErrorType.INVALID_EXCEPTION);
}
}
}
8 changes: 8 additions & 0 deletions src/main/java/com/leets/blog/dto/response/PostResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.leets.blog.dto.response;

public record PostResponse(
Long postId,
String title,
String content
) {
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.leets.blog.dto;
package com.leets.blog.dto.response;

public record StringResponse(
String string_one,
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/leets/blog/entity/Post.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.leets.blog.entity;

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

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

@Entity
@Table(name = "post")
@Getter
public class Post {

public Post() {}
Expand Down Expand Up @@ -39,4 +41,12 @@ public Post(String title, String content) {
// Post는 여러 개의 Comment와 연결됨 (1:N)
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();

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

public void setContent(String content) {
this.content = content;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.leets.blog.repository;

import com.leets.blog.entity.Comment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
}
9 changes: 9 additions & 0 deletions src/main/java/com/leets/blog/repository/PostRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.leets.blog.repository;

import com.leets.blog.entity.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
}
9 changes: 9 additions & 0 deletions src/main/java/com/leets/blog/repository/UserRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.leets.blog.repository;

import com.leets.blog.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
34 changes: 30 additions & 4 deletions src/main/java/com/leets/blog/service/DtoConverter.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,41 @@
package com.leets.blog.service;

import com.leets.blog.dto.StringRequest;
import com.leets.blog.dto.StringResponse;
import org.springframework.stereotype.Service;
import com.leets.blog.dto.response.StringResponse;
import com.leets.blog.dto.request.AddPostRequest;
import com.leets.blog.dto.request.UpdatePostRequest;
import com.leets.blog.dto.response.PostResponse;
import com.leets.blog.entity.Post;
import org.springframework.stereotype.Component;

@Service
@Component
public class DtoConverter {

// NOTE : StringRequest -> StringResponse
public StringResponse convert(String string) {
return new StringResponse(string, string);
}

public Post toPost(AddPostRequest request) {
return new Post(
request.title(),
request.content()
);
}

public Post toPost(UpdatePostRequest request) {
return new Post(
request.title(),
request.content()
);
}

public PostResponse toDTO(Post post) {
return new PostResponse(
post.getId(),
post.getTitle(),
post.getContent()
);
}


}
34 changes: 34 additions & 0 deletions src/main/java/com/leets/blog/service/PostFinder.java
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

서비스 역할별 파일 구조가 잘 분리되어 있는 것 같아요!! 배워갑니다!~!

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.leets.blog.service;

import com.leets.blog.entity.Post;
import com.leets.blog.repository.PostRepository;
import com.leets.blog.support.error.ErrorType;
import com.leets.blog.support.error.PostException;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class PostFinder {

private final PostRepository postRepository;

public PostFinder(PostRepository postRepository) {
this.postRepository = postRepository;
}

public List<Post> findPosts() {
List<Post> posts = postRepository.findAll();

if (posts.isEmpty()) {
throw new PostException(ErrorType.NOT_EXIST_POST);
}
Comment on lines +23 to +25
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

게시글이 없는 경우도 예외 처리로 하신 것 같은데, 서비스를 시작한 초기 상태처럼 게시글이 아예 없는 경우에도 예외가 발생하게 되는 건지 궁금합니다!


return posts;
}

public Post findPostByPostId(Long postId) {
return postRepository.findById(postId)
.orElseThrow(() -> new PostException(ErrorType.NOT_FOUND_POST));
}
}
39 changes: 39 additions & 0 deletions src/main/java/com/leets/blog/service/PostManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.leets.blog.service;

import com.leets.blog.entity.Post;
import com.leets.blog.repository.PostRepository;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class PostManager {

private final PostRepository postRepository;

public PostManager(PostRepository postRepository) {
this.postRepository = postRepository;
}

@Transactional
public Post add (Post post) {
return postRepository.save(post);
}

// NOTE : userId 제외
@Transactional
public Post update (Long postId, Post post) {

Post savedPost = postRepository.findById(postId).orElseThrow(RuntimeException::new);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostFinder에서는 커스텀 예외를 사용하고 계신 것 같은데, PostManager.update()에서는 RuntimeException을 사용하신 이유가 있을까요?


savedPost.setTitle(post.getTitle());
savedPost.setContent(post.getContent());

return savedPost;
}

// NOTE : userId 제외
@Transactional
public void delete (Long postId) {
postRepository.deleteById(postId);
}
}
Loading