Conversation
sinsehwan
left a comment
There was a problem hiding this comment.
고생하셨습니다! 코드 구조가 전체적으로 깔끔하고 좋네요 그런데 현재 pr 병합 대상이 main이라, 리뷰 내용 확인해주시고 본인 이름 브랜치로 다시 pr날려주시면 될 것 같습니다!
| @Bean | ||
| CommandLineRunner init(PostRepository repo) { | ||
| return args -> { | ||
| if (repo.count() == 0) { | ||
| repo.save(Post.builder() | ||
| .content("hello h2") | ||
| .username("hong") | ||
| .likes(0) | ||
| .build()); | ||
| } | ||
| }; | ||
| } |
There was a problem hiding this comment.
h2 사용하실 때 더미 데이터 필요하시면 data.sql 파일에 작성하시고 application.properties에 더미데이터 생성 경로 적어두는 방식으로도 구성할 수 있습니다! 이러면 코드에 데이터 초기화 로직이 안 들어가도 돼서 깔끔하게 구성 가능합니다.
# data.sql
INSERT INTO post (content, username, likes) VALUES ('hello h2', 'hong', 0);| @ExceptionHandler(MethodArgumentNotValidException.class) | ||
| public ResponseEntity<?> handleValidation(MethodArgumentNotValidException e) { | ||
| String msg = e.getBindingResult().getFieldErrors().stream() | ||
| .map(err -> err.getField() + ": " + err.getDefaultMessage()) | ||
| .findFirst().orElse("Validation error"); | ||
| return ResponseEntity.badRequest().body(msg); |
There was a problem hiding this comment.
예외 처리까지 구성해주셨네요! 그런데 현재 방식에서는 검증 실패 내역 중 첫 번째만 반환하는 구성이라 추후 joining등을 사용해서 검증 실패 내용을 전부 알려주는 형식이 더 좋을 것 같습니다!
| @Transactional | ||
| public Post create(CreateReq req) { | ||
| Post p = Post.builder() | ||
| .content(req.content) | ||
| .username(req.username) | ||
| .likes(0) | ||
| .build(); | ||
| return postRepository.save(p); | ||
| } | ||
|
|
||
| @Transactional(readOnly = true) | ||
| public Post get(Long id) { | ||
| return postRepository.findById(id) | ||
| .orElseThrow(() -> new IllegalArgumentException("Post not found: " + id)); | ||
| } |
There was a problem hiding this comment.
읽기 전용 시 readOnly=true 잘 적용해주셨네요!
|
|
||
| @Entity @Table(name="posts") | ||
| @EntityListeners(AuditingEntityListener.class) | ||
| @Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder |
There was a problem hiding this comment.
Setter를 최대한 지양하고 update 형식으로 객체에게 일을 시키는 관점으로 리팩토링 해 보시면 더 좋을 것 같습니다!
| private Res toRes(Post p) { | ||
| Res r = new Res(); | ||
| r.id = p.getId(); | ||
| r.content = p.getContent(); | ||
| r.username = p.getUsername(); | ||
| r.likes = p.getLikes(); | ||
| r.createdAt = p.getCreatedAt(); | ||
| r.updatedAt = p.getUpdatedAt(); | ||
| return r; | ||
| } |
There was a problem hiding this comment.
이것도 조금 더 객체지향적으로 본다면 Res에 Post를 인자로 받는 생성자를 추가하고 Res 생성은 Res에만 위임하면 Controller 계층의 코드를 더 간결하게 구성할 수 있을 것 같습니다.
| @PostMapping | ||
| public Res create(@Valid @RequestBody CreateReq req) { | ||
| return toRes(postService.create(req)); | ||
| } | ||
|
|
||
| // Read one | ||
| @GetMapping("/{id}") | ||
| public Res get(@PathVariable Long id) { | ||
| return toRes(postService.get(id)); | ||
| } | ||
|
|
||
| // Read all | ||
| @GetMapping |
There was a problem hiding this comment.
현재 Dto를 바로 반환하는 구조네요! 그런데 이 경우 서버에서 항상 기본값인 200 상태코드만 반환하게 됩니다. ResponseEntity를 적용해보시면 좋을 것 같습니다.
|
|
||
| // Update | ||
| @PutMapping("/{id}") | ||
| public Res update(@PathVariable Long id, @Valid @RequestBody UpdateReq req) { |
There was a problem hiding this comment.
Res라는 변수명은 나중에 다른 ResponseDto가 생기면 헷갈릴 수도 있을 것 같습니다! 의도가 드러나도록 조금 더 구체적으로 명명하면 더 좋을 것 같습니다
변경점 👍
Post CRUD 구현
POST /api/posts : 게시글 생성
GET /api/posts : 게시글 목록 조회
GET /api/posts/{id} : 단건 조회
PUT /api/posts/{id} : 내용 수정
DELETE /api/posts/{id} : 삭제
POST /api/posts/{id}/like : 좋아요 증가
도메인 & 계층
Post 엔티티(Auditing: createdAt, updatedAt)
PostRepository(JPA)
PostService(트랜잭션, 예외 처리)
PostController(REST API, DTO 매핑)
환경 구성
H2 in-memory DB 설정 및 콘솔 활성화(/h2-console)
Lombok, Validation 의존성 추가
예외/검증
@Valid 기반 요청 검증(@notblank, @SiZe)
전역 예외 핸들러: 400(검증 실패), 404(리소스 없음)
부트스트랩 데이터
앱 시작 시 CommandLineRunner로 더미 데이터 1건 삽입(hello h2)
버그 해결 💊
해결한 버그
컴파일 실패(Lombok 미설치) → lombok 의존성 및 annotation processor 추가
검증 어노테이션 미인식 → spring-boot-starter-validation 추가
H2 콘솔 404 → com.h2database:h2 추가 및 spring.h2.console.enabled=true 설정
테스트 💻
변경점을 테스트하기 위한 방법 기술
로컬에서 다음 순서로 확인했습니다.
./gradlew clean bootRun
H2 콘솔: http://localhost:8080/h2-console
JDBC URL: jdbc:h2:mem:devsns | USER: sa | PW: (빈값)
PostMan
1)Create → 2) List → 3) Get One → 4) Update → 5) Like → 6) Delete → 7) Get After Delete(404)
스크린샷 🖼
변경된 부분에 대한 스크린샷

H2 콘솔 결과
스프링 실행 로그

POSTMAN 테스트
