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

[feat] 3차 세미나 과제 코드입니다. #6

Open
wants to merge 1 commit 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
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.sopt.homework3;

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

@SpringBootApplication
public class Homework3Application {

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

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


import jakarta.persistence.EntityNotFoundException;
import org.sopt.homework3.common.dto.ErrorResponse;
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(EntityNotFoundException.class)
protected ResponseEntity<ErrorResponse> handleEntityNotFoundException(EntityNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ErrorResponse.of(HttpStatus.NOT_FOUND.value(), e.getMessage()));
}
@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()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.sopt.homework3.common.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum ErrorMessage {
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "ID에 해당하는 사용자가 존재하지 않습니다."),
BLOG_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "ID에 해당하는 블로그가 없습니다."),
NON_MEMBER(HttpStatus.NOT_EXTENDED.value(), "블로그 소유자가 아니므로 권한이 없습니다."),
POST_NOT_FOUND(HttpStatus.NOT_EXTENDED.value(), "아이디에 해당하는 블로그의 포스트가 없습니다.")
;
private final int status;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.sopt.homework3.common.dto;

public record ErrorResponse(
int status,
String message
) {
public static ErrorResponse of(int status, String message) {
return new ErrorResponse(status, message);
}
public static ErrorResponse of(ErrorMessage errorMessage) {
return new ErrorResponse(errorMessage.getStatus(), errorMessage.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.sopt.homework3.common.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum SuccessMessage{

BLOG_CREATE_SUCCESS(HttpStatus.CREATED.value(), "블로그 생성이 완료되었습니다."),
BLOG_POST_CREATE_SUCCESS(HttpStatus.CREATED.value(), "블로그 글이 작성되었습니다."),
BLOG_POST_GET_SUCCESS(HttpStatus.CREATED.value(),"블로그 글을 성공적으로 가져왔습니다."),
BLOG_POST_NOT_FOUND(HttpStatus.NOT_FOUND.value(),"블로그 글을 찾을 수 없습니다.")

;
private final int status;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.sopt.homework3.common.dto;


public record SuccessStatusResponse<T>(
int status,
String message,
T data
) {
public static SuccessStatusResponse<Void> of(SuccessMessage successMessage) {
return new SuccessStatusResponse<>(successMessage.getStatus(), successMessage.getMessage(), null);
}

public static <T> SuccessStatusResponse<T> of(SuccessMessage successMessage, T data) {
return new SuccessStatusResponse<>(successMessage.getStatus(), successMessage.getMessage(), data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.sopt.homework3.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

/*
Auditing 기능을 통해, 엔티티 생성되고 변경되는 시점 감지해서
생성시각/수정시각/생성한 사람/수정한 사람 기록 가능
*/
@Configuration
@EnableJpaAuditing //JPA가 엔티티를 감시할 수 있게 해줌
public class JpaAuditingConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package org.sopt.homework3.controller;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.sopt.homework3.common.dto.ErrorMessage;
import org.sopt.homework3.common.dto.SuccessMessage;
import org.sopt.homework3.common.dto.SuccessStatusResponse;
import org.sopt.homework3.domain.Blog;
import org.sopt.homework3.domain.Post;
import org.sopt.homework3.exception.CustomizedException;
import org.sopt.homework3.exception.NotFoundException;
import org.sopt.homework3.repository.BlogRepository;
import org.sopt.homework3.service.BlogService;
import org.sopt.homework3.service.dto.BlogCreateRequest;
import org.sopt.homework3.service.dto.BlogPostGetRequest;
import org.sopt.homework3.service.dto.BlogTitleUpdateRequest;
import org.sopt.homework3.service.dto.BlogWriteRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

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

private final BlogService blogService;
private final BlogRepository blogRepository;

@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();
}

//여기서부터 숙제 코드
@PostMapping("/blog/{blogId}/post") //글쓰기
public ResponseEntity<SuccessStatusResponse> writeBlogPost(
@PathVariable Long blogId,
@RequestHeader(name = "memberId") Long memberId,
@Valid @RequestBody BlogWriteRequest blogWriteRequest
) {
blogService.validateMember(blogId, memberId); //블로그 소유자인지 확인
blogService.writePost(blogId, memberId, blogWriteRequest);
return ResponseEntity.status(HttpStatus.CREATED)
.body(SuccessStatusResponse.of(SuccessMessage.BLOG_POST_CREATE_SUCCESS));
}

@DeleteMapping("/blog/{blogId}") //블로그 삭제
public ResponseEntity<Void> deleteBlog(@PathVariable Long blogId) {
blogService.deleteBlog(blogId);
return ResponseEntity.noContent().build();
}

@GetMapping("/blog/{blogId}")
public ResponseEntity<List<BlogPostGetRequest>> findPostsByBlogId(
@PathVariable Long blogId,
@RequestHeader(name = "memberId") Long memberId
){
Blog blog = blogService.getPostById(blogId, blogId);
List<BlogPostGetRequest> blogPostGetRequests = blog.getPosts().stream()
.map(BlogPostGetRequest::of)
.collect(Collectors.toList());
return ResponseEntity.ok(blogPostGetRequests);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.sopt.homework3.controller;

import lombok.RequiredArgsConstructor;
import org.sopt.homework3.service.MemberService;
import org.sopt.homework3.service.dto.MemberCreateDto;
import org.sopt.homework3.service.dto.MemberFindDto;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.net.URI;

@Transactional
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/member") //어디로 요청이 들어올거냐?를 보는 것.
public class MemberController {

private final MemberService memberService;

@PostMapping
public ResponseEntity postMember(@RequestBody MemberCreateDto memberCreate) {
return ResponseEntity.created(URI.create(memberService.createMember(memberCreate))).build();
//createMember가 MemberService의 함수이다.
}

@GetMapping("/{memberId}")
public ResponseEntity<MemberFindDto> findMemberById(@PathVariable Long memberId){
return ResponseEntity.ok(memberService.findMemberById(memberId));
//findMemberById가 MemberService의 함수이다.
}

@DeleteMapping("/{memberId}")
public ResponseEntity deleteMember(@PathVariable Long memberId){
memberService.deleteMemberById(memberId); //deleteMemberById가 MemberService의 함수이다.

return ResponseEntity.noContent().build();
/*
클라이언트에게 반환할 데이터가 없으므로, HTTP 상태 코드 204(No Content)를 함께 응답합니다.
이는 요청이 성공적으로 처리되었지만 응답으로 반환할 데이터가 없음을 나타냅니다.
*/

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.sopt.homework3.controller;

import org.sopt.homework3.controller.dto.ApiResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/*
* 이 컨트롤러는 /test 경로로 들어오는 GET 요청에는 "test API" 문자열을 반환하고,
* /test/json 경로로 들어오는 GET 요청에는 JSON 형식의 응답을 반환
*/

@RestController
@RequestMapping("/test")
public class TestController {

@GetMapping
public String test(){
return "test API";
}

@GetMapping("/json")
public ApiResponse testJson(){
return ApiResponse.create("test API with JSON");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.sopt.homework3.controller.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;


/*
JSON 통신을 해야 하므로, 이 클래스를 활용하여 JSON 객체를 만드는 것이다.
리스폰스 객체의 필드에 접근해야하기 때문에 @Getter 어노테이션이 없으면 통신이 되지 않음.
*/
@AllArgsConstructor //생성자 자동으로 생성해줌
@Getter
public class ApiResponse {
private String content;

public static ApiResponse create(String content){
return new ApiResponse(content);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.sopt.homework3.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 createAt;

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

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

import java.util.ArrayList;
import java.util.List;

@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();
}

//여기서부터 숙제코드
@OneToMany(mappedBy = "blog", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private List<Post> posts = new ArrayList<>();
Comment on lines +45 to +46
Copy link
Contributor

Choose a reason for hiding this comment

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

FetchType을 Eager로 설정한 이유가 있을까요!?

Copy link
Member Author

Choose a reason for hiding this comment

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

다시 한 번 복습하다 보니 fetch = FetchType.LAZY 로 설정하는 것이 좋겠다는 것을 알게되었네요..! 감사합니다아~!


public void addPost(Member member, BlogWriteRequest blogWriteRequest) {
Post newPost = new Post(this, blogWriteRequest.title(), blogWriteRequest.contents(), member);
this.posts.add(newPost);
}
}
Loading