diff --git a/cow_mvc_practice_erd.png b/cow_mvc_practice_erd.png new file mode 100644 index 0000000..8c6dd45 Binary files /dev/null and b/cow_mvc_practice_erd.png differ diff --git a/src/main/java/com/cow/cow_mvc_practice/CowMvcPracticeApplication.java b/src/main/java/com/cow/cow_mvc_practice/CowMvcPracticeApplication.java index 007c332..3553cf7 100644 --- a/src/main/java/com/cow/cow_mvc_practice/CowMvcPracticeApplication.java +++ b/src/main/java/com/cow/cow_mvc_practice/CowMvcPracticeApplication.java @@ -2,12 +2,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication +@EnableJpaAuditing public class CowMvcPracticeApplication { public static void main(String[] args) { SpringApplication.run(CowMvcPracticeApplication.class, args); } - } diff --git a/src/main/java/com/cow/cow_mvc_practice/comment/controller/CommentController.java b/src/main/java/com/cow/cow_mvc_practice/comment/controller/CommentController.java new file mode 100644 index 0000000..8a3906e --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/comment/controller/CommentController.java @@ -0,0 +1,21 @@ +package com.cow.cow_mvc_practice.comment.controller; + +import com.cow.cow_mvc_practice.comment.dto.request.CreateCommentRequest; +import com.cow.cow_mvc_practice.comment.dto.response.CreatedCommentResponse; +import com.cow.cow_mvc_practice.comment.service.CommentService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/post/{postId}/comment") +@RequiredArgsConstructor +public class CommentController { + + private final CommentService commentService; + + @PostMapping("/new") + public CreatedCommentResponse create(@PathVariable Long postId, @RequestBody final CreateCommentRequest commentRequest) { + return commentService.create(postId, commentRequest); + } +} + diff --git a/src/main/java/com/cow/cow_mvc_practice/comment/dto/request/CreateCommentRequest.java b/src/main/java/com/cow/cow_mvc_practice/comment/dto/request/CreateCommentRequest.java new file mode 100644 index 0000000..7646aff --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/comment/dto/request/CreateCommentRequest.java @@ -0,0 +1,17 @@ +package com.cow.cow_mvc_practice.comment.dto.request; + +import com.cow.cow_mvc_practice.comment.entity.Comment; +import lombok.Getter; + +@Getter +public class CreateCommentRequest { + + private Long id; + private String content; + + public Comment toEntity() { + return Comment.builder() + .content(this.content) + .build(); + } +} diff --git a/src/main/java/com/cow/cow_mvc_practice/comment/dto/response/CreatedCommentResponse.java b/src/main/java/com/cow/cow_mvc_practice/comment/dto/response/CreatedCommentResponse.java new file mode 100644 index 0000000..5ac4b02 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/comment/dto/response/CreatedCommentResponse.java @@ -0,0 +1,35 @@ +package com.cow.cow_mvc_practice.comment.dto.response; + +import com.cow.cow_mvc_practice.comment.entity.Comment; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class CreatedCommentResponse { + + private final Long id; + private final Long postId; + private final Long memberId; + private final String content; + private final LocalDateTime createTime; + + @Builder + private CreatedCommentResponse(final Long id, final Long postId, final Long memberId, final String content, final LocalDateTime createTime) { + this.id = id; + this.postId = postId; + this.memberId = memberId; + this.content = content; + this.createTime = createTime; + } + + public static CreatedCommentResponse from(final Comment comment) { + return CreatedCommentResponse.builder() + .id(comment.getId()) + .postId(comment.getPost().getId()) + .memberId(comment.getMember().getId()) + .content(comment.getContent()) + .createTime(comment.getCreatedAt()).build(); + } +} diff --git a/src/main/java/com/cow/cow_mvc_practice/comment/entity/Comment.java b/src/main/java/com/cow/cow_mvc_practice/comment/entity/Comment.java new file mode 100644 index 0000000..2e54948 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/comment/entity/Comment.java @@ -0,0 +1,40 @@ +package com.cow.cow_mvc_practice.comment.entity; + +import com.cow.cow_mvc_practice.member.entity.Member; +import com.cow.cow_mvc_practice.post.entity.Post; +import com.cow.cow_mvc_practice.utill.TimestampedEntity; +import jakarta.persistence.*; +import lombok.*; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@AttributeOverride(name = "id", column = @Column(name = "comment_id")) +public class Comment extends TimestampedEntity { + + private String content; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id") + private Post post; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @Builder + private Comment(final String content, final Post post, final Member member) { + this.content = content; + this.post = post; + this.member = member; + } + + public void addMember(Member member) { + this.member = member; + } + + public void addPost(Post post) { + this.post = post; + post.getComments().add(this); + } +} \ No newline at end of file diff --git a/src/main/java/com/cow/cow_mvc_practice/comment/repository/CommentJPARepository.java b/src/main/java/com/cow/cow_mvc_practice/comment/repository/CommentJPARepository.java new file mode 100644 index 0000000..b95be42 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/comment/repository/CommentJPARepository.java @@ -0,0 +1,9 @@ +package com.cow.cow_mvc_practice.comment.repository; + +import com.cow.cow_mvc_practice.comment.entity.Comment; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CommentJPARepository extends JpaRepository { + + int countByPostId(Long id); +} diff --git a/src/main/java/com/cow/cow_mvc_practice/comment/service/CommentService.java b/src/main/java/com/cow/cow_mvc_practice/comment/service/CommentService.java new file mode 100644 index 0000000..6311231 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/comment/service/CommentService.java @@ -0,0 +1,10 @@ +package com.cow.cow_mvc_practice.comment.service; + +import com.cow.cow_mvc_practice.comment.dto.request.CreateCommentRequest; +import com.cow.cow_mvc_practice.comment.dto.response.CreatedCommentResponse; + +public interface CommentService { + + CreatedCommentResponse create(Long postId, CreateCommentRequest createCommentRequest); +} + diff --git a/src/main/java/com/cow/cow_mvc_practice/comment/service/CommentServiceImpl.java b/src/main/java/com/cow/cow_mvc_practice/comment/service/CommentServiceImpl.java new file mode 100644 index 0000000..a1d1d36 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/comment/service/CommentServiceImpl.java @@ -0,0 +1,45 @@ +package com.cow.cow_mvc_practice.comment.service; + +import com.cow.cow_mvc_practice.comment.dto.request.CreateCommentRequest; +import com.cow.cow_mvc_practice.comment.dto.response.CreatedCommentResponse; +import com.cow.cow_mvc_practice.comment.entity.Comment; +import com.cow.cow_mvc_practice.comment.repository.CommentJPARepository; +import com.cow.cow_mvc_practice.member.entity.Member; +import com.cow.cow_mvc_practice.member.repository.MemberJPARepository; +import com.cow.cow_mvc_practice.post.entity.Post; +import com.cow.cow_mvc_practice.post.repository.PostJPARepository; +import jakarta.persistence.EntityNotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional +public class CommentServiceImpl implements CommentService { + + private final MemberJPARepository memberJPARepository; + private final PostJPARepository postJPARepository; + private final CommentJPARepository commentJPARepository; + + @Override + public CreatedCommentResponse create(Long postId, CreateCommentRequest createCommentRequest) { + Member member = findMember(createCommentRequest); + Post post = findPost(postId); + Comment comment = createCommentRequest.toEntity(); + comment.addMember(member); + comment.addPost(post); + commentJPARepository.save(comment); + return CreatedCommentResponse.from(comment); + } + + private Post findPost(Long postId) { + return postJPARepository.findById(postId) + .orElseThrow(() -> new EntityNotFoundException("게시글을 찾을 수 없습니다.")); + } + + private Member findMember(CreateCommentRequest createCommentRequest) { + return memberJPARepository.findById(createCommentRequest.getId()) + .orElseThrow(() -> new EntityNotFoundException("사용자를 찾을 수 없습니다.")); + } +} \ No newline at end of file diff --git a/src/main/java/com/cow/cow_mvc_practice/member/controller/MemberController.java b/src/main/java/com/cow/cow_mvc_practice/member/controller/MemberController.java index 6329396..08bf3b6 100644 --- a/src/main/java/com/cow/cow_mvc_practice/member/controller/MemberController.java +++ b/src/main/java/com/cow/cow_mvc_practice/member/controller/MemberController.java @@ -2,18 +2,13 @@ import java.util.List; -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.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import com.cow.cow_mvc_practice.member.controller.dto.request.MemberRequest; -import com.cow.cow_mvc_practice.member.controller.dto.response.MemberResponse; +import com.cow.cow_mvc_practice.member.dto.request.UpdateMemberRequest; +import com.cow.cow_mvc_practice.member.dto.response.FoundMemberResponse; +import com.cow.cow_mvc_practice.member.dto.response.UpdatedMemberResponse; +import org.springframework.web.bind.annotation.*; +import com.cow.cow_mvc_practice.member.dto.request.CreateMemberRequest; +import com.cow.cow_mvc_practice.member.dto.response.CreatedMemberResponse; import com.cow.cow_mvc_practice.member.service.MemberService; - import lombok.RequiredArgsConstructor; @RestController @@ -21,41 +16,26 @@ @RequiredArgsConstructor public class MemberController { - private final MemberService memberService; - - /* 기본 */ - // @PostMapping("/new") - // public String create(@RequestBody final MemberRequest memberRequest) { - // memberService.join(memberRequest); - // return "회원저장 성공!"; - // } - // - // @GetMapping("/{memberId}") - // public String findMember(@PathVariable final Long memberId) { - // Member member = memberService.findOne(memberId); - // return "member 아이디: " + member.getId() + ", member 이름: " + member.getName(); - // } - // - - /* MemberResponse dto 적용 */ - @PostMapping("/new") - public MemberResponse create(@RequestBody final MemberRequest memberRequest) { - return memberService.join(memberRequest.getName()); - } - - @GetMapping("/{memberId}") - public MemberResponse findMember(@PathVariable final Long memberId) { - return memberService.findOne(memberId); - } - - @GetMapping() - public MemberResponse findMemberQuery(@RequestParam final Long memberId) { - return memberService.findOne(memberId); - } - - @GetMapping("all") - public List findMembers() { - return memberService.findAll(); - } + private final MemberService memberService; + + @PostMapping("/new") + public CreatedMemberResponse create(@RequestBody final CreateMemberRequest createMemberRequest) { + return memberService.create(createMemberRequest); + } + + @GetMapping("/{memberId}") + public FoundMemberResponse find(@PathVariable final Long memberId) { + return memberService.find(memberId); + } + + @GetMapping("/all") + public List findAll() { + return memberService.findAll(); + } + + @PatchMapping("/{memberId}") + public UpdatedMemberResponse update(@PathVariable("memberId") Long memberId, @RequestBody final UpdateMemberRequest updateMemberRequest) { + return memberService.update(memberId, updateMemberRequest); + } } diff --git a/src/main/java/com/cow/cow_mvc_practice/member/controller/dto/request/MemberRequest.java b/src/main/java/com/cow/cow_mvc_practice/member/controller/dto/request/MemberRequest.java deleted file mode 100644 index c0dcd9c..0000000 --- a/src/main/java/com/cow/cow_mvc_practice/member/controller/dto/request/MemberRequest.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.cow.cow_mvc_practice.member.controller.dto.request; - -import com.cow.cow_mvc_practice.member.entity.Member; - -import lombok.Getter; - -@Getter -public class MemberRequest { - String name; - - public Member toEntity() { - return Member.builder() - .name(name) - .build(); - } -} diff --git a/src/main/java/com/cow/cow_mvc_practice/member/dto/request/CreateMemberRequest.java b/src/main/java/com/cow/cow_mvc_practice/member/dto/request/CreateMemberRequest.java new file mode 100644 index 0000000..618ca39 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/member/dto/request/CreateMemberRequest.java @@ -0,0 +1,16 @@ +package com.cow.cow_mvc_practice.member.dto.request; + +import com.cow.cow_mvc_practice.member.entity.Member; +import lombok.Getter; + +@Getter +public class CreateMemberRequest { + + private String name; + + public Member toEntity() { + return Member.builder() + .name(this.name) + .build(); + } +} diff --git a/src/main/java/com/cow/cow_mvc_practice/member/controller/dto/request/UpdateMemberRequest.java b/src/main/java/com/cow/cow_mvc_practice/member/dto/request/UpdateMemberRequest.java similarity index 58% rename from src/main/java/com/cow/cow_mvc_practice/member/controller/dto/request/UpdateMemberRequest.java rename to src/main/java/com/cow/cow_mvc_practice/member/dto/request/UpdateMemberRequest.java index 94429b9..ab6ed6b 100644 --- a/src/main/java/com/cow/cow_mvc_practice/member/controller/dto/request/UpdateMemberRequest.java +++ b/src/main/java/com/cow/cow_mvc_practice/member/dto/request/UpdateMemberRequest.java @@ -1,4 +1,4 @@ -package com.cow.cow_mvc_practice.member.controller.dto.request; +package com.cow.cow_mvc_practice.member.dto.request; import lombok.Getter; diff --git a/src/main/java/com/cow/cow_mvc_practice/member/dto/response/CreatedMemberResponse.java b/src/main/java/com/cow/cow_mvc_practice/member/dto/response/CreatedMemberResponse.java new file mode 100644 index 0000000..5dd0a5c --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/member/dto/response/CreatedMemberResponse.java @@ -0,0 +1,25 @@ +package com.cow.cow_mvc_practice.member.dto.response; + +import com.cow.cow_mvc_practice.member.entity.Member; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class CreatedMemberResponse { + + private final Long id; + private final String name; + + @Builder + private CreatedMemberResponse(final Long id, final String name) { + this.id = id; + this.name = name; + } + + public static CreatedMemberResponse from(final Member member) { + return CreatedMemberResponse.builder() + .id(member.getId()) + .name(member.getName()) + .build(); + } +} diff --git a/src/main/java/com/cow/cow_mvc_practice/member/controller/dto/response/MemberResponse.java b/src/main/java/com/cow/cow_mvc_practice/member/dto/response/FoundMemberResponse.java similarity index 52% rename from src/main/java/com/cow/cow_mvc_practice/member/controller/dto/response/MemberResponse.java rename to src/main/java/com/cow/cow_mvc_practice/member/dto/response/FoundMemberResponse.java index 9e3a77b..c9fde02 100644 --- a/src/main/java/com/cow/cow_mvc_practice/member/controller/dto/response/MemberResponse.java +++ b/src/main/java/com/cow/cow_mvc_practice/member/dto/response/FoundMemberResponse.java @@ -1,23 +1,23 @@ -package com.cow.cow_mvc_practice.member.controller.dto.response; +package com.cow.cow_mvc_practice.member.dto.response; import com.cow.cow_mvc_practice.member.entity.Member; - import lombok.Builder; import lombok.Getter; @Getter -public class MemberResponse { +public class FoundMemberResponse { + private final Long id; private final String name; @Builder - private MemberResponse(final Long id, final String name) { + private FoundMemberResponse(final Long id, final String name) { this.id = id; this.name = name; } - public static MemberResponse from(final Member member) { - return MemberResponse.builder() + public static FoundMemberResponse from(final Member member) { + return FoundMemberResponse.builder() .id(member.getId()) .name(member.getName()) .build(); diff --git a/src/main/java/com/cow/cow_mvc_practice/member/dto/response/UpdatedMemberResponse.java b/src/main/java/com/cow/cow_mvc_practice/member/dto/response/UpdatedMemberResponse.java new file mode 100644 index 0000000..9d1463a --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/member/dto/response/UpdatedMemberResponse.java @@ -0,0 +1,25 @@ +package com.cow.cow_mvc_practice.member.dto.response; + +import com.cow.cow_mvc_practice.member.entity.Member; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class UpdatedMemberResponse { + + private final Long id; + private final String name; + + @Builder + private UpdatedMemberResponse(final Long id, final String name) { + this.id = id; + this.name = name; + } + + public static UpdatedMemberResponse from(final Member member) { + return UpdatedMemberResponse.builder() + .id(member.getId()) + .name(member.getName()) + .build(); + } +} diff --git a/src/main/java/com/cow/cow_mvc_practice/member/entity/Member.java b/src/main/java/com/cow/cow_mvc_practice/member/entity/Member.java index 00d2b1c..43acc71 100644 --- a/src/main/java/com/cow/cow_mvc_practice/member/entity/Member.java +++ b/src/main/java/com/cow/cow_mvc_practice/member/entity/Member.java @@ -4,50 +4,27 @@ import java.util.List; import com.cow.cow_mvc_practice.post.entity.Post; +import com.cow.cow_mvc_practice.utill.BaseEntity; +import jakarta.persistence.*; +import lombok.*; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Member { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "member_id") - private Long id; - - private String name; +@Entity +@AttributeOverride(name = "id", column = @Column(name = "member_id")) +public class Member extends BaseEntity { - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) - private final List posts = new ArrayList<>(); + private String name; - @Builder - private Member(final Long id, final String name) { - this.id = id; - this.name = name; - } + @OneToMany(mappedBy = "member", cascade = CascadeType.PERSIST) + private final List posts = new ArrayList<>(); - public static Member from(String name) { - return Member.builder() - .name(name) - .build(); - } + @Builder + private Member(final String name) { + this.name = name; + } - public static Member of(Long id, String name) { - return Member.builder() - .id(id) - .name(name) - .build(); - } + public void updateName(String name) { + this.name = name; + } } diff --git a/src/main/java/com/cow/cow_mvc_practice/member/repository/MemberRepository.java b/src/main/java/com/cow/cow_mvc_practice/member/repository/MemberRepository.java deleted file mode 100644 index b9cd48b..0000000 --- a/src/main/java/com/cow/cow_mvc_practice/member/repository/MemberRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.cow.cow_mvc_practice.member.repository; - -import java.util.List; -import java.util.Optional; - -import com.cow.cow_mvc_practice.member.entity.Member; - -public interface MemberRepository { - - void save(Member member); - - Optional findById(Long memberId); - - Optional> findAll(); -} diff --git a/src/main/java/com/cow/cow_mvc_practice/member/repository/MemberRepositoryImpl.java b/src/main/java/com/cow/cow_mvc_practice/member/repository/MemberRepositoryImpl.java deleted file mode 100644 index dcfaceb..0000000 --- a/src/main/java/com/cow/cow_mvc_practice/member/repository/MemberRepositoryImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.cow.cow_mvc_practice.member.repository; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import org.springframework.stereotype.Repository; - -import com.cow.cow_mvc_practice.member.entity.Member; - -@Repository -public class MemberRepositoryImpl implements MemberRepository { - /** - * 원래라면 HashMap이 아닌, concurrentHashMap이 적합함. HashMap은 멀티 쓰레드 환경에서 사용할 수 없기 때문.(동시성 이슈) - */ - private static Map store = new HashMap<>(); - - - @Override - public void save(Member member) { - store.put(member.getId(), member); - } - - @Override - public Optional findById(Long memberId) { - return Optional.ofNullable(store.get(memberId)); - } - - @Override - public Optional> findAll() { - return Optional.of((List)store.values()); - } -} diff --git a/src/main/java/com/cow/cow_mvc_practice/member/service/MemberService.java b/src/main/java/com/cow/cow_mvc_practice/member/service/MemberService.java index fef1272..c07fc1e 100644 --- a/src/main/java/com/cow/cow_mvc_practice/member/service/MemberService.java +++ b/src/main/java/com/cow/cow_mvc_practice/member/service/MemberService.java @@ -1,22 +1,21 @@ package com.cow.cow_mvc_practice.member.service; -import java.util.ArrayList; import java.util.List; -import com.cow.cow_mvc_practice.member.controller.dto.request.MemberRequest; -import com.cow.cow_mvc_practice.member.controller.dto.response.MemberResponse; -import com.cow.cow_mvc_practice.member.entity.Member; +import com.cow.cow_mvc_practice.member.dto.request.CreateMemberRequest; +import com.cow.cow_mvc_practice.member.dto.request.UpdateMemberRequest; +import com.cow.cow_mvc_practice.member.dto.response.CreatedMemberResponse; +import com.cow.cow_mvc_practice.member.dto.response.FoundMemberResponse; +import com.cow.cow_mvc_practice.member.dto.response.UpdatedMemberResponse; public interface MemberService { - /* 기본 */ - // void join(MemberRequest memberRequest); - // Member findOne(Long memberId); + CreatedMemberResponse create(CreateMemberRequest createMemberRequest); + FoundMemberResponse find(Long memberId); - /* MemberResponse dto 적용 */ - MemberResponse findOne(Long memberId); - MemberResponse join(String name); - List findAll(); + List findAll(); + + UpdatedMemberResponse update(Long memberId, UpdateMemberRequest updateMemberRequest); } diff --git a/src/main/java/com/cow/cow_mvc_practice/member/service/MemberServiceImpl.java b/src/main/java/com/cow/cow_mvc_practice/member/service/MemberServiceImpl.java index a6e32a0..a7229de 100644 --- a/src/main/java/com/cow/cow_mvc_practice/member/service/MemberServiceImpl.java +++ b/src/main/java/com/cow/cow_mvc_practice/member/service/MemberServiceImpl.java @@ -3,13 +3,15 @@ import java.util.List; import java.util.stream.Collectors; +import com.cow.cow_mvc_practice.member.dto.request.CreateMemberRequest; +import com.cow.cow_mvc_practice.member.dto.request.UpdateMemberRequest; +import com.cow.cow_mvc_practice.member.dto.response.FoundMemberResponse; +import com.cow.cow_mvc_practice.member.dto.response.UpdatedMemberResponse; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; - -import com.cow.cow_mvc_practice.member.controller.dto.response.MemberResponse; +import com.cow.cow_mvc_practice.member.dto.response.CreatedMemberResponse; import com.cow.cow_mvc_practice.member.entity.Member; import com.cow.cow_mvc_practice.member.repository.MemberJPARepository; - import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; @@ -18,44 +20,39 @@ @Transactional public class MemberServiceImpl implements MemberService { - private final MemberJPARepository memberRepository; - // private final MemberRepository memberRepository; - - /* 기본 */ - // @Override - // public void join(MemberRequest memberRequest) { - // Member member = Member.from(memberRequest.getId(), memberRequest.getName()); - // memberRepository.save(member); - // } - // - // @Transactional(readOnly = true) - // @Override - // public Member findOne(Long memberId) { - // return memberRepository.findById(memberId) - // .orElseThrow(() -> new EntityNotFoundException("[Error] 사용자를 찾을 수 없습니다.")); - // } - - /* MemberResponse dto 적용 */ - @Override - public MemberResponse join(String name) { - Member member = Member.from(name); - memberRepository.save(member); - return MemberResponse.from(member); - } - - @Transactional(readOnly = true) - @Override - public MemberResponse findOne(Long memberId) { - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new EntityNotFoundException("[Error] 사용자를 찾을 수 없습니다.")); - return MemberResponse.from(member); - } - - @Override - public List findAll() { - List members = memberRepository.findAll(); - return members.stream() - .map(MemberResponse::from) - .collect(Collectors.toList()); - } + private final MemberJPARepository memberRepository; + + @Override + public CreatedMemberResponse create(CreateMemberRequest createMemberRequest) { + Member member = createMemberRequest.toEntity(); + memberRepository.save(member); + return CreatedMemberResponse.from(member); + } + + @Transactional(readOnly = true) + @Override + public FoundMemberResponse find(Long memberId) { + Member member = findById(memberId); + return FoundMemberResponse.from(member); + } + + @Transactional(readOnly = true) + @Override + public List findAll() { + List members = memberRepository.findAll(); + return members.stream().map(FoundMemberResponse::from).collect(Collectors.toList()); + } + + @Override + public UpdatedMemberResponse update(Long memberId, UpdateMemberRequest updateMemberRequest) { + Member member = findById(memberId); + member.updateName(updateMemberRequest.getName()); + memberRepository.save(member); + return UpdatedMemberResponse.from(member); + } + + private Member findById(Long memberId) { + return memberRepository.findById(memberId). + orElseThrow(() -> new EntityNotFoundException("[Error] 사용자를 찾을 수 없습니다.")); + } } \ No newline at end of file diff --git a/src/main/java/com/cow/cow_mvc_practice/post/controller/PostController.java b/src/main/java/com/cow/cow_mvc_practice/post/controller/PostController.java new file mode 100644 index 0000000..8734a35 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/post/controller/PostController.java @@ -0,0 +1,39 @@ +package com.cow.cow_mvc_practice.post.controller; + +import com.cow.cow_mvc_practice.post.dto.request.CreatePostRequest; +import com.cow.cow_mvc_practice.post.dto.request.DeletePostRequest; +import com.cow.cow_mvc_practice.post.dto.response.CreatePostResponse; +import com.cow.cow_mvc_practice.post.dto.response.FindPostResponse; +import com.cow.cow_mvc_practice.post.service.PostService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/post") +@RequiredArgsConstructor +public class PostController { + + private final PostService postService; + + @PostMapping("/new") + public CreatePostResponse create(@RequestBody final CreatePostRequest createPostRequest) { + return postService.create(createPostRequest); + } + + @GetMapping("{postId}") + public FindPostResponse find(@PathVariable final Long postId) { + return postService.find(postId); + } + + @GetMapping("/all") + public List findAll() { + return postService.findAll(); + } + + @DeleteMapping("/{postId}") + public void delete(@PathVariable final Long postId, @RequestBody final DeletePostRequest deletePostRequest) { + postService.delete(postId, deletePostRequest); + } +} \ No newline at end of file diff --git a/src/main/java/com/cow/cow_mvc_practice/post/dto/request/CreatePostRequest.java b/src/main/java/com/cow/cow_mvc_practice/post/dto/request/CreatePostRequest.java new file mode 100644 index 0000000..9821b01 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/post/dto/request/CreatePostRequest.java @@ -0,0 +1,19 @@ +package com.cow.cow_mvc_practice.post.dto.request; + +import com.cow.cow_mvc_practice.post.entity.Post; +import lombok.Getter; + +@Getter +public class CreatePostRequest { + + private Long id; + private String title; + private String content; + + public Post toEntity() { + return Post.builder() + .title(this.title) + .content(this.content) + .build(); + } +} diff --git a/src/main/java/com/cow/cow_mvc_practice/post/dto/request/DeletePostRequest.java b/src/main/java/com/cow/cow_mvc_practice/post/dto/request/DeletePostRequest.java new file mode 100644 index 0000000..0196630 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/post/dto/request/DeletePostRequest.java @@ -0,0 +1,9 @@ +package com.cow.cow_mvc_practice.post.dto.request; + +import lombok.Getter; + +@Getter +public class DeletePostRequest { + + private Long id; +} diff --git a/src/main/java/com/cow/cow_mvc_practice/post/dto/response/CreatePostResponse.java b/src/main/java/com/cow/cow_mvc_practice/post/dto/response/CreatePostResponse.java new file mode 100644 index 0000000..4503ef2 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/post/dto/response/CreatePostResponse.java @@ -0,0 +1,33 @@ +package com.cow.cow_mvc_practice.post.dto.response; + +import com.cow.cow_mvc_practice.post.entity.Post; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class CreatePostResponse { + + private final Long postId; + private final String title; + private final String content; + private final LocalDateTime created_at; + + @Builder + private CreatePostResponse(final Long postId, final String title, final String content, LocalDateTime created_at) { + this.postId = postId; + this.title = title; + this.content = content; + this.created_at = created_at; + } + + public static CreatePostResponse from(Post post) { + return CreatePostResponse.builder() + .postId(post.getId()) + .title(post.getTitle()) + .content(post.getContent()) + .created_at(post.getCreatedAt()) + .build(); + } +} diff --git a/src/main/java/com/cow/cow_mvc_practice/post/dto/response/DeletePostResponse.java b/src/main/java/com/cow/cow_mvc_practice/post/dto/response/DeletePostResponse.java new file mode 100644 index 0000000..71ee523 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/post/dto/response/DeletePostResponse.java @@ -0,0 +1,33 @@ +package com.cow.cow_mvc_practice.post.dto.response; + +import com.cow.cow_mvc_practice.post.entity.Post; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class DeletePostResponse { + + private final Long postId; + private final String title; + private final String content; + private final LocalDateTime created_at; + + @Builder + private DeletePostResponse(final Long postId, final String title, final String content, LocalDateTime created_at) { + this.postId = postId; + this.title = title; + this.content = content; + this.created_at = created_at; + } + + public static DeletePostResponse from(Post post) { + return DeletePostResponse.builder() + .postId(post.getId()) + .title(post.getTitle()) + .content(post.getContent()) + .created_at(post.getCreatedAt()) + .build(); + } +} diff --git a/src/main/java/com/cow/cow_mvc_practice/post/dto/response/FindPostResponse.java b/src/main/java/com/cow/cow_mvc_practice/post/dto/response/FindPostResponse.java new file mode 100644 index 0000000..1635c38 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/post/dto/response/FindPostResponse.java @@ -0,0 +1,36 @@ +package com.cow.cow_mvc_practice.post.dto.response; + +import com.cow.cow_mvc_practice.post.entity.Post; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class FindPostResponse { + + private final Long postId; + private final String title; + private final String content; + private final LocalDateTime created_at; + private final int commentCount; + + @Builder + private FindPostResponse(final Long postId, final String title, final String content, LocalDateTime created_at, int commentCount) { + this.postId = postId; + this.title = title; + this.content = content; + this.created_at = created_at; + this.commentCount = commentCount; + } + + public static FindPostResponse from(Post post, int commentCount) { + return FindPostResponse.builder() + .postId(post.getId()) + .title(post.getTitle()) + .content(post.getContent()) + .created_at(post.getCreatedAt()) + .commentCount(commentCount) + .build(); + } +} diff --git a/src/main/java/com/cow/cow_mvc_practice/post/entity/Post.java b/src/main/java/com/cow/cow_mvc_practice/post/entity/Post.java index aec70d8..1fab0ef 100644 --- a/src/main/java/com/cow/cow_mvc_practice/post/entity/Post.java +++ b/src/main/java/com/cow/cow_mvc_practice/post/entity/Post.java @@ -1,49 +1,39 @@ package com.cow.cow_mvc_practice.post.entity; +import com.cow.cow_mvc_practice.comment.entity.Comment; import com.cow.cow_mvc_practice.member.entity.Member; +import com.cow.cow_mvc_practice.utill.TimestampedEntity; +import jakarta.persistence.*; +import lombok.*; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import java.util.List; @Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) // 생성자를 통해서 값 변경 목적으로 접근하는 메시지들 차단 +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity -public class Post { - @Id - @Column(name = "post_id") - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String title; - - private String content; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") - private Member member; - - @Builder - private Post(final String title, final String content, final Member member) { - this.title = title; - this.content = content; - this.member = member; - } - - public static Post from(final String title, final String content, final Member member) { - return Post.builder() - .title(title) - .content(content) - .member(member) - .build(); - } -} \ No newline at end of file +@AttributeOverride(name = "id", column = @Column(name = "post_id")) +public class Post extends TimestampedEntity { + + private String title; + + private String content; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) + private List comments; + + @Builder + private Post(final String title, final String content, final Member member) { + this.title = title; + this.content = content; + this.member = member; + } + + public void addMember(Member member) { + this.member = member; + member.getPosts().add(this); + } +} diff --git a/src/main/java/com/cow/cow_mvc_practice/post/repository/PostJPARepository.java b/src/main/java/com/cow/cow_mvc_practice/post/repository/PostJPARepository.java new file mode 100644 index 0000000..3b5262b --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/post/repository/PostJPARepository.java @@ -0,0 +1,6 @@ +package com.cow.cow_mvc_practice.post.repository; + +import com.cow.cow_mvc_practice.post.entity.Post; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostJPARepository extends JpaRepository {} diff --git a/src/main/java/com/cow/cow_mvc_practice/post/service/PostService.java b/src/main/java/com/cow/cow_mvc_practice/post/service/PostService.java new file mode 100644 index 0000000..e64b89f --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/post/service/PostService.java @@ -0,0 +1,19 @@ +package com.cow.cow_mvc_practice.post.service; + +import com.cow.cow_mvc_practice.post.dto.request.CreatePostRequest; +import com.cow.cow_mvc_practice.post.dto.request.DeletePostRequest; +import com.cow.cow_mvc_practice.post.dto.response.CreatePostResponse; +import com.cow.cow_mvc_practice.post.dto.response.FindPostResponse; + +import java.util.List; + +public interface PostService { + + CreatePostResponse create(CreatePostRequest createPostRequest); + + FindPostResponse find(Long postId); + + void delete(Long postId, DeletePostRequest deletePostRequest); + + List findAll(); +} diff --git a/src/main/java/com/cow/cow_mvc_practice/post/service/PostServiceImpl.java b/src/main/java/com/cow/cow_mvc_practice/post/service/PostServiceImpl.java new file mode 100644 index 0000000..33f15c2 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/post/service/PostServiceImpl.java @@ -0,0 +1,72 @@ +package com.cow.cow_mvc_practice.post.service; + +import com.cow.cow_mvc_practice.comment.repository.CommentJPARepository; +import com.cow.cow_mvc_practice.member.entity.Member; +import com.cow.cow_mvc_practice.member.repository.MemberJPARepository; +import com.cow.cow_mvc_practice.post.dto.request.CreatePostRequest; +import com.cow.cow_mvc_practice.post.dto.request.DeletePostRequest; +import com.cow.cow_mvc_practice.post.dto.response.CreatePostResponse; +import com.cow.cow_mvc_practice.post.dto.response.FindPostResponse; +import com.cow.cow_mvc_practice.post.entity.Post; +import com.cow.cow_mvc_practice.post.repository.PostJPARepository; +import jakarta.persistence.EntityNotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Transactional +public class PostServiceImpl implements PostService { + + private final PostJPARepository postJPARepository; + private final MemberJPARepository memberJPARepository; + private final CommentJPARepository commentJPARepository; + + @Override + public CreatePostResponse create(CreatePostRequest createPostRequest) { + Member member = findMember(createPostRequest.getId()); + Post post = createPostRequest.toEntity(); + post.addMember(member); + postJPARepository.save(post); + return CreatePostResponse.from(post); + } + + @Transactional(readOnly = true) + @Override + public FindPostResponse find(Long postId) { + Post post = findPost(postId); + int commentCount = commentJPARepository.countByPostId(postId); + return FindPostResponse.from(post, commentCount); + } + + @Override + public void delete(Long postId, DeletePostRequest deletePostRequest) { + Post post = findPost(postId); + Member member = findMember(deletePostRequest.getId()); + if (post.getMember().getId().equals(member.getId())) { + postJPARepository.delete(post); + } + } + + @Override + public List findAll() { + List posts = postJPARepository.findAll(); + return posts.stream() + .map(CreatePostResponse::from) + .collect(Collectors.toList()); + } + + private Member findMember(Long memberId) { + return memberJPARepository.findById(memberId) + .orElseThrow(() -> new EntityNotFoundException("회원을 찾을 수 없습니다.")); + } + + private Post findPost(Long postId) { + return postJPARepository.findById(postId).orElseThrow(() -> + new EntityNotFoundException("게시글이 존재하지 않습니다.")); + } +} diff --git a/src/main/java/com/cow/cow_mvc_practice/utill/BaseEntity.java b/src/main/java/com/cow/cow_mvc_practice/utill/BaseEntity.java new file mode 100644 index 0000000..6587571 --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/utill/BaseEntity.java @@ -0,0 +1,16 @@ +package com.cow.cow_mvc_practice.utill; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; + +@Getter +@MappedSuperclass +public abstract class BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; +} diff --git a/src/main/java/com/cow/cow_mvc_practice/utill/TimestampedEntity.java b/src/main/java/com/cow/cow_mvc_practice/utill/TimestampedEntity.java new file mode 100644 index 0000000..fc2c2ab --- /dev/null +++ b/src/main/java/com/cow/cow_mvc_practice/utill/TimestampedEntity.java @@ -0,0 +1,21 @@ +package com.cow.cow_mvc_practice.utill; + +import jakarta.persistence.EntityListeners; +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@Getter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public abstract class TimestampedEntity extends BaseEntity { + + @CreatedDate + @Column(name = "created_at", nullable = false, updatable = false) + protected LocalDateTime createdAt; +} + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7cb9e72..abb868e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -4,7 +4,7 @@ spring: jpa: open-in-view: true hibernate: - ddl-auto: validate + ddl-auto: create show-sql: true properties: hibernate: @@ -33,4 +33,7 @@ logging: hibernate: type: descriptor: - sql: trace \ No newline at end of file + sql: trace + +server: + port: 8081 \ No newline at end of file