Skip to content

Conversation

@PHJ2000
Copy link

@PHJ2000 PHJ2000 commented Oct 10, 2025

변경점 👍

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 콘솔 결과
image

스프링 실행 로그
image

POSTMAN 테스트
image

image image image image image image

@PHJ2000 PHJ2000 changed the title feat(post): add CRUD with like + H2 config Feat/week 1 Oct 10, 2025
Copy link
Contributor

@sinsehwan sinsehwan left a comment

Choose a reason for hiding this comment

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

고생하셨습니다! 코드 구조가 전체적으로 깔끔하고 좋네요 그런데 현재 pr 병합 대상이 main이라, 리뷰 내용 확인해주시고 본인 이름 브랜치로 다시 pr날려주시면 될 것 같습니다!

Comment on lines +18 to +29
@Bean
CommandLineRunner init(PostRepository repo) {
return args -> {
if (repo.count() == 0) {
repo.save(Post.builder()
.content("hello h2")
.username("hong")
.likes(0)
.build());
}
};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

h2 사용하실 때 더미 데이터 필요하시면 data.sql 파일에 작성하시고 application.properties에 더미데이터 생성 경로 적어두는 방식으로도 구성할 수 있습니다! 이러면 코드에 데이터 초기화 로직이 안 들어가도 돼서 깔끔하게 구성 가능합니다.

# data.sql
INSERT INTO post (content, username, likes) VALUES ('hello h2', 'hong', 0);

Comment on lines +17 to +22
@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);
Copy link
Contributor

Choose a reason for hiding this comment

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

예외 처리까지 구성해주셨네요! 그런데 현재 방식에서는 검증 실패 내역 중 첫 번째만 반환하는 구성이라 추후 joining등을 사용해서 검증 실패 내용을 전부 알려주는 형식이 더 좋을 것 같습니다!

Comment on lines +19 to +33
@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));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

읽기 전용 시 readOnly=true 잘 적용해주셨네요!


@Entity @Table(name="posts")
@EntityListeners(AuditingEntityListener.class)
@Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder
Copy link
Contributor

Choose a reason for hiding this comment

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

Setter를 최대한 지양하고 update 형식으로 객체에게 일을 시키는 관점으로 리팩토링 해 보시면 더 좋을 것 같습니다!

Comment on lines +58 to +67
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;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

이것도 조금 더 객체지향적으로 본다면 Res에 Post를 인자로 받는 생성자를 추가하고 Res 생성은 Res에만 위임하면 Controller 계층의 코드를 더 간결하게 구성할 수 있을 것 같습니다.

Comment on lines +23 to +35
@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
Copy link
Contributor

Choose a reason for hiding this comment

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

현재 Dto를 바로 반환하는 구조네요! 그런데 이 경우 서버에서 항상 기본값인 200 상태코드만 반환하게 됩니다. ResponseEntity를 적용해보시면 좋을 것 같습니다.


// Update
@PutMapping("/{id}")
public Res update(@PathVariable Long id, @Valid @RequestBody UpdateReq req) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Res라는 변수명은 나중에 다른 ResponseDto가 생기면 헷갈릴 수도 있을 것 같습니다! 의도가 드러나도록 조금 더 구체적으로 명명하면 더 좋을 것 같습니다

@PHJ2000 PHJ2000 self-assigned this Nov 18, 2025
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.

2 participants