Skip to content
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

Seminar/3 #4

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
13 changes: 13 additions & 0 deletions demo/src/main/java/org/sopt/demo/DemoApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.sopt.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.sopt.demo.common;

import org.sopt.demo.common.dto.ErrorResponse;
import org.sopt.demo.exception.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.Objects;

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ErrorResponse.of(HttpStatus.BAD_REQUEST.value(), Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()));
}
@ExceptionHandler(NotFoundException.class)
protected ResponseEntity<ErrorResponse> handleNotFoundException(NotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ErrorResponse.of(e.getErrorMessage()));
}
}
41 changes: 41 additions & 0 deletions demo/src/main/java/org/sopt/demo/controller/BlogController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.sopt.demo.controller;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.sopt.demo.common.dto.SuccessMessage;
import org.sopt.demo.common.dto.SuccessStatusResponse;
import org.sopt.demo.service.BlogService;
import org.sopt.demo.service.dto.BlogCreateRequest;
import org.sopt.demo.service.dto.BlogTitleUpdateRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

private final BlogService blogService;

@PostMapping("/blog")
public ResponseEntity<SuccessStatusResponse> createBlog(
@RequestHeader(name = "memberId") Long memberId,
@RequestBody BlogCreateRequest blogCreateRequest
Copy link

Choose a reason for hiding this comment

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

과제에 따라 여기에도 검증 로직을 추가해야할 거 같아요!

) {
return ResponseEntity.status(HttpStatus.CREATED).header(
"Location",
blogService.create(memberId, blogCreateRequest))
.body(SuccessStatusResponse.of(SuccessMessage.BLOG_CREATE_SUCCESS));
}

@PatchMapping("/blog/{blogId}/title")
public ResponseEntity updateBlogTitle(
@PathVariable Long blogId,
@Valid @RequestBody BlogTitleUpdateRequest blogTitleUpdateRequest
) {
blogService.updateTitle(blogId, blogTitleUpdateRequest);
return ResponseEntity.noContent().build();
}
}

52 changes: 52 additions & 0 deletions demo/src/main/java/org/sopt/demo/controller/MemberController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.sopt.demo.controller;

import lombok.RequiredArgsConstructor;

import org.sopt.demo.service.MemberService;
import org.sopt.demo.service.dto.MemberCreateDto;
import org.sopt.demo.service.dto.MemberFindDto;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.net.URI;
import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/member")
public class MemberController {

private final MemberService memberService;

@PostMapping
public ResponseEntity createMember(
@RequestBody MemberCreateDto memberCreateDto
) {
return ResponseEntity.created(URI.create(memberService.createMember(memberCreateDto)))
.build();
}

@GetMapping("/{memberId}")
public ResponseEntity<MemberFindDto> findMemberById(@PathVariable Long memberId) {
return ResponseEntity.ok(memberService.findMemberById(memberId));
}

@DeleteMapping("/{memberId}")
public ResponseEntity deleteMemberById(@PathVariable Long memberId){
memberService.deleteMemberById(memberId);
return ResponseEntity.noContent().build();
}

@GetMapping
public ResponseEntity<List<MemberFindDto>> getAllMembers() {
return ResponseEntity.ok(memberService.getAllMembers());
}

}
27 changes: 27 additions & 0 deletions demo/src/main/java/org/sopt/demo/controller/PostController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.sopt.demo.controller;

import lombok.RequiredArgsConstructor;
import org.sopt.demo.common.dto.SuccessStatusResponse;
import org.sopt.demo.service.PostService;
import org.sopt.demo.service.dto.PostCreateRequest;
import org.sopt.demo.common.dto.SuccessMessage;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

private final PostService postService;
@PostMapping("/posts")
Copy link

Choose a reason for hiding this comment

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

현재 위 blog와 member controller에서는 rest api의 url을 단수형 (member, blog)로 해주신 상황인데 여기에는 왜 posts인 복수형을 사용하셨는지 궁금합니다! 아래 블로그 참고하셔서 은서님만의 컨벤션을 정해주시면 좋을 거 같아요!!

https://blog.pumpkin-raccoon.com/115

public ResponseEntity<SuccessStatusResponse> createPost(
@RequestHeader Long blogId,
@RequestBody PostCreateRequest postCreateRequest) {
Copy link

Choose a reason for hiding this comment

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

마찬가지로 검증해주는 과정 필요합니다!

Copy link
Member

Choose a reason for hiding this comment

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

궁찬님께서 이미 언급해주셨지만, Post 되는 객체에 대해 Request body validation이 누락되어 있는거 같아요!
파라미터로 @RequestBody 어노테이션 옆에 @Valid를 작성하면, RequestBody로 들어오는 객체에 대한 검증을 수행할 수 있습니다:)

return ResponseEntity.status(HttpStatus.CREATED).header(
"Location",
postService.create(blogId, postCreateRequest))
.body(SuccessStatusResponse.of(SuccessMessage.POST_CREATE_SUCCESS));
}
}
21 changes: 21 additions & 0 deletions demo/src/main/java/org/sopt/demo/domain/BaseTimeEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.sopt.demo.domain;

import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;


@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {

@CreatedDate
private LocalDateTime createdAt;

@LastModifiedDate
private LocalDateTime updatedAt;
}
39 changes: 39 additions & 0 deletions demo/src/main/java/org/sopt/demo/domain/Blog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.sopt.demo.domain;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.sopt.demo.service.dto.BlogCreateRequest;
import org.sopt.demo.service.dto.BlogTitleUpdateRequest;

@Entity
@Getter
@NoArgsConstructor
public class Blog extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@OneToOne(fetch = FetchType.LAZY)
private Member member;

@Column(length = 200)
private String title;

private String description;

private Blog(Member member, String title, String description) {
this.member = member;
this.title = title;
this.description = description;
}

public static Blog create(Member member, BlogCreateRequest blogCreateRequest) {
return new Blog(member, blogCreateRequest.title(), blogCreateRequest.description());
}

public void updateTitle(BlogTitleUpdateRequest blogTitleUpdateRequest) {
this.title = blogTitleUpdateRequest.title();
}
}
43 changes: 43 additions & 0 deletions demo/src/main/java/org/sopt/demo/domain/Member.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.sopt.demo.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor
public class Member {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String name;

@Enumerated(EnumType.STRING)
private Part part;

private int age;

@Builder
private Member(String name, Part part, int age) {
this.name = name;
this.part = part;
this.age = age;
}

public static Member create(String name, Part part, int age) {
Copy link

Choose a reason for hiding this comment

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

Blog에서는 빌더를 사용하지 않고 Member에서는 빌더를 사용한 이유가 있을까요??
만약 세미나 코드를 따라치는 과정에서 크게 신경을 쓰지 못하셨다면 다음 세미나인 합세 전까지는 본인의 컨벤션을 정하시는게 좋아보여요~!

return Member.builder()
.name(name)
.age(age)
.part(part)
.build();
}
}
5 changes: 5 additions & 0 deletions demo/src/main/java/org/sopt/demo/domain/Part.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.sopt.demo.domain;

public enum Part {
SERVER, IOS, ANDROID, WEB, DESIGN, PLAN
}
41 changes: 41 additions & 0 deletions demo/src/main/java/org/sopt/demo/domain/Post.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.sopt.demo.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor
public class Post extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String title;

private String content;

@ManyToOne(fetch = FetchType.LAZY)
private Blog blog;

public Post(String title, String content, Blog blog) {
this.title = title;
this.content = content;
this.blog = blog;
}

public static Post create(Blog blog, String title, String content) {
Post post = new Post();
post.blog = blog;
post.title = title;
post.content = content;
return post;
}
Comment on lines +34 to +40
Copy link
Member

Choose a reason for hiding this comment

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

위의 코드는 아래와 같이 간단하게 줄여서 쓸 수 있습니다.

public static Post create(Blog blog, String title, String content) {
    return new Post(title, content, blog);
}

현재 은서님은 create 메소드가 기본생성자를 호출한 후 필드들을 초기화하도록 코드를 구현하셨는데, 이미 매개변수가 있는 생성자를 정의해두었기 때문에 이 생성자를 활용하여 객체 생성과 필드 초기화를 동시에 처리하는 것이 더 합리적일 것 같아요.

Copy link

Choose a reason for hiding this comment

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

제가 놓친 부분에 대한 리뷰 좋습니다~

}
15 changes: 15 additions & 0 deletions demo/src/main/java/org/sopt/demo/exception/BusinessException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.sopt.demo.exception;


import lombok.Getter;
import org.sopt.demo.common.dto.ErrorMessage;

@Getter
public class BusinessException extends RuntimeException{
private ErrorMessage errorMessage;

public BusinessException(ErrorMessage errorMessage) {
super(errorMessage.getMessage());
this.errorMessage = errorMessage;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.sopt.demo.exception;

import org.sopt.demo.common.dto.ErrorMessage;

public class NotFoundException extends BusinessException {
public NotFoundException(ErrorMessage errorMessage) {
super(errorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.sopt.demo.repository;

import org.sopt.demo.domain.Blog;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BlogRepository extends JpaRepository<Blog, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.sopt.demo.repository;

import org.sopt.demo.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.sopt.demo.repository;

import org.sopt.demo.domain.Post;
import org.springframework.data.jpa.repository.JpaRepository;

public interface PostRepository extends JpaRepository<Post, Long> {


}
Comment on lines +6 to +9
Copy link
Member

Choose a reason for hiding this comment

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

저 또한 코드 리뷰를 통해서 공통적으로 지적받은 부분인데요,, 코드의 공백라인 부분에서 일관성이 없는 부분이 보입니다. 가령 바로 위의 코드인 MemberRepository와 비교해보았을 때, 현재 코드에서는 불필요한 공백 라인이 눈에 띕니다!
인텔레제이에서는 맥북의 경우, Command + Option + L 단축키를 사용하시면 코드라인 자동 정렬이 가능하다고 합니다:D

Loading