Skip to content

[게시판 + DB연동] 이민구 제출합니다.#41

Open
LEEMINGU03 wants to merge 37 commits into
LEEMINGU03from
LEEMINGU03-spring-db
Open

[게시판 + DB연동] 이민구 제출합니다.#41
LEEMINGU03 wants to merge 37 commits into
LEEMINGU03from
LEEMINGU03-spring-db

Conversation

@LEEMINGU03

Copy link
Copy Markdown

과제명

게시판 + DB 연동

💡 작업 내용

  • 게시판 CRUD
  • 댓글 CRUD
  • RsData
  • page,slice 사용

🔗 참고 링크

김영한 스프링 DB2편
관계 매핑 https://mroh1226.tistory.com/211

🤔 느낀 점 / 어려웠던 점

DB 설계가 중요하다는 것을 느끼게 되었습니다.
또한 Jpa를 따로 배워야할 만큼 많은 기능들이 있다는 것을 느끼게 되었습니다 .

@LEEMINGU03 LEEMINGU03 self-assigned this May 29, 2026
@LEEMINGU03 LEEMINGU03 linked an issue May 29, 2026 that may be closed by this pull request
4 tasks

@Donghwan814 Donghwan814 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

안녕하세요 민구님!
우선 이번 주차 과제 제출하시느라 고생 많으셨습니다!

이번 과제는 저번 주 과제와 크게 다르지 않은 부분이 많았고, 코드도 전반적으로 잘 작성해주셔서 이번에는 코드 자체의 수정 사항보다는 왜 이렇게 작성했는지, 어떤 의도로 사용했는지에 대한 질문 위주로 피드백을 남겼습니다.

이번 주차는 특히 DB 연동이 중요한 부분이라고 생각됩니다.
다만 제출해주신 이미지를 확인해보니 현재는 Postman으로 API 테스트한 결과만 첨부되어 있는 것 같습니다.

MySQL과 연동해서 작업해주신 만큼, 실제로 게시글 생성, 조회, 수정, 삭제가 MySQL DB에 정상적으로 반영되는지도 함께 확인할 수 있도록 DB 조회 결과 화면도 같이 첨부해주시면 더 좋을 것 같습니다!

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

현재 게시글 생성 API에서 200 OK를 사용해주신 것으로 보이는데, 200 OK도 요청 성공을 의미하기 때문에 완전히 틀린 것은 아니라고 생각합니다.

다만 게시글 생성처럼 새로운 리소스가 생성되는 경우에는 HTTP 표준상 201 Created를 사용하는 것이 더 의미가 명확합니다. 201 Created는 요청이 성공적으로 처리되었고, 그 결과 새로운 리소스가 생성되었음을 나타내는 상태 코드로 정의되어 있습니다.

혹시 200 OK를 사용하신 특별한 이유가 있으신지 궁금합니다!

@LEEMINGU03 LEEMINGU03 Jun 7, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

성공한 것에 대한 것은 전부 200OK라고 했습니다.
성공한 것도 분류 해서 작성해야하는 것을 망각했습니다.
201 Created로 수정하였습니다.

Comment thread springdb/docs/notFound.png Outdated

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

파일명을 notFound로 작성해주신 부분을 봤을 때, 존재하지 않는 게시글을 조회했을 때의 예외 처리를 의도하신 것으로 보입니다!

다만 Postman 테스트 결과를 확인해보니 현재는 400 Bad Request로 응답이 내려오고 있는 것 같습니다.

존재하지 않는 게시글 ID를 조회하는 상황은 요청 형식이 잘못된 경우라기보다는, 요청한 리소스가 서버에 존재하지 않는 경우에 더 가깝기 때문에 404 Not Found 상태코드를 사용하는 것이 더 적절할 것 같습니다.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

요청 형식은 맞고 서버에 존재 하지 않기 때문에 404 Not Found가 맞습니다.
수정 완료했습니다.

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/post/{id}/comment")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@RequestMapping으로 공통 경로를 묶어주신 점은 매우 잘하셨습니다!

@RequestMapping을 사용함으로 현재 CommentController에 있는 모든 API 주소는 http://localhost:8080/post/{id}/comment 이렇게 공통 주소로 시작 될 것입니다!

보통 @RequestMapping을 사용할 때는 공통 경로를 묶어주는 어노테이션으로 사용하기에 {id} 같이 PathVariable 값을 사용하지 않고 공통 리소스 경로만 묶어서 사용하는 경우가 많습니다!

ex) http://localhost:8080/post

혹시 민구님께서 @RequestMapping("/post/{id}/comment") 이렇게 지정해주신 특별한 이유가 있으신지 의견을 들어보고 싶습니다!

@LEEMINGU03 LEEMINGU03 Jun 7, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

모든 댓글 api 경로가 /post/{id}/comment로 시작해서 이렇게 묶었습니다.
수정완료하였습니다.

@PostMapping
public ResponseEntity<RsData<CommentResponse>> newComment(@RequestBody CommentRequest commentRequest, @PathVariable Long id) {
CommentResponse commentResponse = commentService.newComment(id, commentRequest);
RsData<CommentResponse> rsData = new RsData<>("200-1", "댓글이 등록되었습니다", commentResponse);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

댓글 생성 응답값에 위에 이미지 업로드 부분에서 설명했던 것처럼, 댓글 생성 API는 새로운 댓글 리소스가 만들어지는 요청이기 때문에 200 OK도 동작은 하지만, 의미상으로는 201 Created를 사용하는 것이 더 명확해 보입니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

수정 완료 하였습니다.

Comment on lines +27 to +60
//댓글 단건 조회
@GetMapping("/{commentId}")
public ResponseEntity<RsData<CommentResponse>> oneComment(@PathVariable Long commentId) {
CommentResponse commentResponse = commentService.findOneComment(commentId);
RsData<CommentResponse> rsData = new RsData<>("200-1", "해당 댓글 조회 완료되었습니다", commentResponse);
return ResponseEntity.status(rsData.statusCode()).body(rsData);
}

//댓글 수정
@PutMapping("/{commentId}")
public ResponseEntity<RsData<CommentResponse>> updateCommet(@PathVariable Long commentId,@RequestBody CommentRequest request){
CommentResponse commentResponse = commentService.updateComment(commentId,request);
RsData<CommentResponse> rsData = new RsData<>("200-1","댓글이 수정되었습니다",commentResponse);
return ResponseEntity.status(rsData.statusCode()).body(rsData);
}

// 댓글 전체 조회하기(slice)
@GetMapping
public ResponseEntity<RsData<Slice<CommentResponse>>> getComment(
@PathVariable Long id,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Slice<CommentResponse> commentResponses = commentService.getCommentWitrhSlice(id, page, size);
RsData<Slice<CommentResponse>> rsData = new RsData<>("200-1", "댓글 조회가 완료되었습니다", commentResponses);
return ResponseEntity.status(rsData.statusCode()).body(rsData);
}

@DeleteMapping("/{commentId}")
public ResponseEntity<RsData<Void>> deleteComment(@PathVariable Long commentId){
commentService.deleteComment(commentId);

RsData<Void> rsData = new RsData<>("200-1", "댓글이 정상적으로 삭제 되었습니다");
return ResponseEntity.status(rsData.statusCode()).body(rsData);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

댓글 조회, 수정, 삭제 API를 각각 역할별로 잘 분리해주신 점 좋았습니다!
특히 댓글 단건 조회/수정/삭제를 commentId 기준으로 처리하려고 하신 의도도 명확하게 보였습니다.

다만 현재 클래스 상단에서 @RequestMapping("/post/{id}/comment"){id} 값을 공통 경로에 포함해주셨기 때문에, 실제 API 주소에는 게시글 id와 댓글 id가 둘 다 들어가게 됩니다.

예를 들면 아래와 같은 형태가 됩니다.

/post/1/comment/3

여기서 1은 게시글 id, 3은 댓글 id라고 판단이 됩니다!

그런데 현재 조회, 수정, 삭제 메서드에서는 @PathVariablecommentId만 가져오고 있어서, 스프링에서는 commentId 값만 메서드 파라미터로 전달하게 됩니다.

즉, URL에는 게시글 id가 포함되어 있지만 실제 로직에서는 해당 id 값을 사용하지 않기 때문에, “이 댓글이 정말 해당 게시글에 속한 댓글인지” 확인하는 검증 로직이 빠질 수 있습니다.

민구님께서 의도하신 구조가 특정 게시글 안에서 특정 댓글을 조회/수정/삭제하는 방식이라면, idcommentId를 함께 받아서 Service 계층에서 검증해보면 더 안정적인 구조가 될 것 같습니다.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

id도 추가하여 검증해보는 로직 구현해보겠습니다!

@LEEMINGU03 LEEMINGU03 Jun 7, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

게시물 id 추가하여 검증로직 작성했습니다

@PathVariable Long id,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Slice<CommentResponse> commentResponses = commentService.getCommentWitrhSlice(id, page, size);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

getCommentWitrhSlice 이 부분 메서드명에서 오타가 발생한 것 같습니다.

.getCommentWithSlice를 의도 하셨을 것이라 생각이 듭니다! 이 부분 수정 해주시면 좋을 것 같습니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

수정 완료하였습니다

Comment on lines +85 to +89
// 댓글 삭제
public void deleteComment(Long commentId){
Comment comment = commentRepository.findById(commentId).orElseThrow(IllegalArgumentException::new);
commentRepository.delete(comment);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

삭제 메서드에는 따로 @Transactional을 붙이지 않으신 이유가 있을까요?

현재는 삭제 메서드에 따로 @Transactional이 없기 때문에, 위에서 사용한 @Transactional(readOnly = true)가 그대로 적용될 수 있을 것 같습니다.

삭제는 데이터를 변경하는 작업인데 readOnly = true로 동작하게 되면 의도한 삭제 로직이 정상적으로 반영되지 않거나 문제가 발생할 가능성이 있어 보입니다.

이 부분은 @Transactional(readOnly = true)가 삭제 같은 변경 작업에서 어떤 영향을 주는지 한 번 찾아보면서 학습해보시면 좋을 것 같습니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

빼먹은거 같습니다!
readOnly = true로 하면 jpa기능을 사용하지 못해 DB반영이 되지 못하고 예외각 발생할 수 있을 거같습니다 .!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

수정완료했습니다.

@PostMapping
public ResponseEntity<RsData<PostResponse>> newPost(@Valid @RequestBody PostNewRequest requestDto){
PostResponse postResponse = postService.newPost(requestDto);
RsData<PostResponse> rsData = new RsData<>("200-1","게시물이 등록되었습니다",postResponse);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이 부분도 201 Created를 사용해보면 조금 더 명확하게 전달할 수 있을 것 같습니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

수정 완료하였습니다!

Comment on lines +90 to +94
// 단건 삭제
public void deletePost(Long id){
Post post = postRepository.findById(id).orElseThrow(IllegalArgumentException::new);
postRepository.delete(post);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이 부분도 위에서 설명한 내용처럼 @transactional 삭제 메서드 참고해서 적용해보면 좋을 것 같습니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

넵!! 수정 완료하였습니다!

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

환경변수를 활용해서 DB 접속 정보를 분리해주신 부분 좋았습니다!

DATABASE_URL, DATABASE_USERNAME, DATABASE_PASSWORD처럼 민감한 정보들을 직접 코드에 작성하지 않고 외부 환경변수로 관리하도록 설정해주신 점이 좋았습니다.

.gitignore도 확인해봤을 때 .env 파일을 통해 민감한 정보를 따로 관리하고 계신 것으로 보여서, 보안 측면에서도 잘 적용해주신 것 같습니다.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

넵! 감사합니다!!!

@leesj0706

Copy link
Copy Markdown

안녕하세요 민구님! 이번 주차는 지난 과제에 DB 연동이 추가된 형태인데요. 지난주 리뷰에서 함께 이야기 나누었던 코드 개선 포인트들을 어떻게 고민하고 적용해 보셨는지 중심적으로 여쭤보고자 합니다! 편하게 답변해 주세요.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

여기에 적힌 resultCode 값 중에 200-1이 눈에 띄는데요. 혹시 HTTP 상태 코드인 200을 기반으로 내부적인 세부 명세(예: 200번 성공 중 첫 번째 케이스)를 정의하려고 하신 걸까요? 어떤 의도로 작성하셨는지 궁금합니다!"

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

같은 성공이여도 어떤 성공인지 세부적으로 분리해보기 위해서 200-1 같은 기능을 넣어보기 위해 연습해보았습니다.
하지만 지금 성공이 전부 200-1 로 되어있어 수정 하였습니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

README에 상세 코드 정보 넣어뒀습니다!

spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.open-in-view=false
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

JPA 설정에서 ddl-auto 설정을 update로 지정해 주셨네요! 혹시 특별히 update 설정을 선택하신 이유나 의도가 있으신지 궁금합니다.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

엔티티 칼럼을 추가할때 테이블에 자동으로 반영되기 위해 update로 설정했습니다

import org.springframework.data.jpa.repository.JpaRepository;

public interface CommentRepository extends JpaRepository<Comment, Long> {
Slice<Comment> findByPostId(Long Id, Pageable pageable);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

지난 리뷰 내용을 바탕으로 Slice와 Page를 공부해 적용해 주셨군요! 피드백 반영이 아주 깔끔하게 잘 되었습니다. 수고하셨습니다.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

넵! 감사합니다

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 이민구 10주차 과제 - 게시판 + DB연동

3 participants