-
Notifications
You must be signed in to change notification settings - Fork 13
Feat/week 1 #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feat/week 1 #9
Conversation
sinsehwan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다! 코드 구조가 전체적으로 깔끔하고 좋네요 그런데 현재 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.
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);| @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
예외 처리까지 구성해주셨네요! 그런데 현재 방식에서는 검증 실패 내역 중 첫 번째만 반환하는 구성이라 추후 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.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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 계층의 코드를 더 간결하게 구성할 수 있을 것 같습니다.
| @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.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 테스트
