-
Notifications
You must be signed in to change notification settings - Fork 0
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
base: main
Are you sure you want to change the base?
Seminar/3 #4
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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())); | ||
} | ||
} |
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 | ||
) { | ||
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(); | ||
} | ||
} | ||
|
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()); | ||
} | ||
|
||
} |
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") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 위 blog와 member controller에서는 rest api의 url을 단수형 (member, blog)로 해주신 상황인데 여기에는 왜 posts인 복수형을 사용하셨는지 궁금합니다! 아래 블로그 참고하셔서 은서님만의 컨벤션을 정해주시면 좋을 거 같아요!! |
||
public ResponseEntity<SuccessStatusResponse> createPost( | ||
@RequestHeader Long blogId, | ||
@RequestBody PostCreateRequest postCreateRequest) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 마찬가지로 검증해주는 과정 필요합니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 궁찬님께서 이미 언급해주셨지만, Post 되는 객체에 대해 Request body validation이 누락되어 있는거 같아요! |
||
return ResponseEntity.status(HttpStatus.CREATED).header( | ||
"Location", | ||
postService.create(blogId, postCreateRequest)) | ||
.body(SuccessStatusResponse.of(SuccessMessage.POST_CREATE_SUCCESS)); | ||
} | ||
} |
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; | ||
} |
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(); | ||
} | ||
} |
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Blog에서는 빌더를 사용하지 않고 Member에서는 빌더를 사용한 이유가 있을까요?? |
||
return Member.builder() | ||
.name(name) | ||
.age(age) | ||
.part(part) | ||
.build(); | ||
} | ||
} |
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 | ||
} |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위의 코드는 아래와 같이 간단하게 줄여서 쓸 수 있습니다.
현재 은서님은 create 메소드가 기본생성자를 호출한 후 필드들을 초기화하도록 코드를 구현하셨는데, 이미 매개변수가 있는 생성자를 정의해두었기 때문에 이 생성자를 활용하여 객체 생성과 필드 초기화를 동시에 처리하는 것이 더 합리적일 것 같아요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제가 놓친 부분에 대한 리뷰 좋습니다~ |
||
} |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저 또한 코드 리뷰를 통해서 공통적으로 지적받은 부분인데요,, 코드의 공백라인 부분에서 일관성이 없는 부분이 보입니다. 가령 바로 위의 코드인 |
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.
과제에 따라 여기에도 검증 로직을 추가해야할 거 같아요!