Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d8345fa
refactor: Feed 폴더 구조 변경
jbh010204 Dec 3, 2025
d75eae2
feat: FeedService를 command와 query로 분리
jbh010204 Dec 3, 2025
291ae9a
refactor: feed 도메인 내 어그레이트 약한 참조 관계로 변경
jbh010204 Dec 3, 2025
b30480a
refactor: Feed관련 DTO에서 사용하지 않는 메서드 삭제
jbh010204 Dec 3, 2025
7553103
refactor: Feed수정에 맞춰 테스트 변경
jbh010204 Dec 3, 2025
eb862fa
feat: FeedComment 약결합 분리 및 CQRS 패턴으로 변경
jbh010204 Dec 3, 2025
deec9d0
feat: FeedCommand 포트 확장
jbh010204 Dec 3, 2025
f80d05c
feat: FeedCommand 도메인 + 서비스 CQRS 분리
jbh010204 Dec 3, 2025
4a37258
feat: 댓글 조회시 필요한 유저 정보를 위해 UserData 포트/어댑터 추가
jbh010204 Dec 3, 2025
4179e1e
refactor: Feed와 FeedComment 느슨한 형태로 분리
jbh010204 Dec 4, 2025
76b8763
refactor: FeedResult 폴더 구조 Command의 DTO 위치로 변경
jbh010204 Dec 9, 2025
4b37f15
refactor: FeedQueryService 불필요한 트랜잭션 어노테이션 제거
jbh010204 Dec 9, 2025
f5971ef
feat: UserSnapshot DTO renamed to UserSnapShot and updated references
jbh010204 Dec 9, 2025
fb40dd7
refactor: FeedComment의 comment필드를 content로 명칭변경
jbh010204 Dec 9, 2025
ff62a4e
feat: rename FeedComment Aggregate to Comment
jbh010204 Dec 9, 2025
af6d726
feat: Feed 수정&삭제 기능 구현
jbh010204 Dec 9, 2025
0a43147
feat: Feed 통합 테스트 구현 및 단위 테스트 command용으로 분리
jbh010204 Dec 9, 2025
6065adb
Merge remote-tracking branch 'origin/develop' into feat/#35
jbh010204 Dec 9, 2025
fb1caef
docs: Feed 및 Comment Swaggeer 작성
jbh010204 Dec 9, 2025
7926140
refactor: test내부 클래스 stub 외부로 구조 변경
jbh010204 Dec 10, 2025
e7eebd9
refactor: User와 Profile 간접 참조 형태로 분리 및 연관된 코드 변경
jbh010204 Dec 12, 2025
d3a9022
feat: Replace UserDataPort with ProfileDataPort for user profile hand…
jbh010204 Dec 12, 2025
4367844
refactor: Rename entity from 'feed_comments' to 'comments' for clarity
jbh010204 Dec 12, 2025
c6aa091
refactor: ProfileSnapShot domain 패키지 구조로 이동
jbh010204 Dec 13, 2025
79cdea3
Merge remote-tracking branch 'origin/develop' into feat/#35
jbh010204 Dec 13, 2025
a37f38d
refactor: 피드 검증을 위한 FeedValidationPort and adapter 도입
jbh010204 Dec 13, 2025
2a54f69
refactor: author validation in Feed 도메인 내부 로직으로 변경
jbh010204 Dec 13, 2025
f2d6792
refactor: DB 스키마 변경에 따른 SQL feed_comments to comments 으로 변경
jbh010204 Dec 13, 2025
43a92c5
refactor: validation adapters의 스테레오타입을 Component로 변경
jbh010204 Dec 13, 2025
44ba7dd
refactor: ProfileJpaRepository 추가 및 프로필 저장 로직 구현
jbh010204 Dec 13, 2025
94e485b
fix : comments 객체와 다르게 구성된 sql문 수정
polyglot-k Dec 14, 2025
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
Empty file modified gradlew
100644 → 100755
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.example.bak.comment.application.command;

import com.example.bak.comment.application.command.port.CommentCommandPort;
import com.example.bak.comment.application.command.port.ProfileDataPort;
import com.example.bak.comment.domain.Comment;
import com.example.bak.comment.domain.ProfileSnapShot;
import com.example.bak.feed.application.command.port.FeedValidationPort;
import com.example.bak.global.exception.BusinessException;
import com.example.bak.global.exception.ErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class CommentCommandService {

private final CommentCommandPort commentCommandPort;
private final FeedValidationPort feedValidationPort;
private final ProfileDataPort profileDataPort;

public void createComment(Long feedId, String content, Long userId) {
if (!feedValidationPort.existsById(feedId)) {
throw new BusinessException(ErrorCode.FEED_NOT_FOUND);
}

ProfileSnapShot userProfile = profileDataPort.findSnapshotByUserId(userId)
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));

Comment newComment = Comment.create(
feedId,
content,
userProfile.userId(),
userProfile.nickname()
);

commentCommandPort.save(newComment);
}

public void updateComment(Long commentId, String content, Long userId) {
Comment comment = commentCommandPort.findById(commentId)
.orElseThrow(() -> new BusinessException(ErrorCode.COMMENT_NOT_FOUND));

if (!comment.isWrittenBy(userId)) {
throw new BusinessException(ErrorCode.UNAUTHORIZED_ACTION);
}

comment.updateComment(content);
commentCommandPort.save(comment);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.bak.comment.application.command.port;

import com.example.bak.comment.domain.Comment;
import java.util.Optional;

public interface CommentCommandPort {

Comment save(Comment comment);

Optional<Comment> findById(Long commentId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.bak.comment.application.command.port;

import com.example.bak.comment.domain.ProfileSnapShot;
import java.util.Optional;

public interface ProfileDataPort {

Optional<ProfileSnapShot> findSnapshotByUserId(Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.bak.comment.application.query;

import com.example.bak.comment.application.query.dto.CommentInfo;
import com.example.bak.comment.application.query.port.CommentQueryPort;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CommentQueryService {

private final CommentQueryPort commentQueryPort;

public List<CommentInfo> getComments(Long feedId) {
return commentQueryPort.findByFeedId(feedId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.example.bak.comment.application.query.dto;

import com.example.bak.comment.domain.Comment;

/**
* Comment의 기본 정보를 담는 DTO Feed 상세 조회 시 포함됨
*/
public record CommentInfo(
Long id,
Long authorId,
String authorName,
String content
) {

public static CommentInfo from(Comment comment) {
return new CommentInfo(
comment.getId(),
comment.getAuthorId(),
comment.getAuthorNickname(),
comment.getContent()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.bak.comment.application.query.port;

import com.example.bak.comment.application.query.dto.CommentInfo;
import java.util.List;

public interface CommentQueryPort {

List<CommentInfo> findByFeedId(Long feedId);
}
69 changes: 69 additions & 0 deletions src/main/java/com/example/bak/comment/domain/Comment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.example.bak.comment.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity(name = "comments")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Comment {

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

@Column(name = "feed_id", nullable = false)
private Long feedId;

@Column(nullable = false)
private Long authorId;

@Column(nullable = false)
private String authorNickname;

@Column(nullable = false)
private String content;

private Comment(Long id, Long feedId, String content, Long authorId,
String authorNickname) {
this.id = id;
this.feedId = feedId;
this.content = content;
this.authorId = authorId;
this.authorNickname = authorNickname;
}

private Comment(Long feedId, String content, Long authorId, String authorNickname) {
this.feedId = feedId;
this.content = content;
this.authorId = authorId;
this.authorNickname = authorNickname;
}

public static Comment create(Long feedId, String comment, Long authorId,
String authorNickname) {
return new Comment(feedId, comment, authorId, authorNickname);
}

public static Comment testInstance(Long id, Long feedId, String comment, Long authorId,
String authorNickname) {
return new Comment(id, feedId, comment, authorId, authorNickname);
}

public void updateComment(String comment) {
this.content = comment;
}

public boolean isWrittenBy(Long authorId) {
return Objects.equals(this.authorId, authorId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.bak.comment.domain;

public record ProfileSnapShot(Long userId, String nickname) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.bak.comment.infra.persistence;

import com.example.bak.comment.application.command.port.CommentCommandPort;
import com.example.bak.comment.domain.Comment;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class CommentCommandAdapter implements CommentCommandPort {

private final CommentJpaRepository commentJpaRepository;

@Override
public Comment save(Comment comment) {
return commentJpaRepository.save(comment);
}

@Override
public Optional<Comment> findById(Long commentId) {
return commentJpaRepository.findById(commentId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.bak.comment.infra.persistence;

import com.example.bak.comment.domain.Comment;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CommentJpaRepository extends JpaRepository<Comment, Long> {

List<Comment> findByFeedId(Long feedId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.bak.comment.infra.persistence;

import com.example.bak.comment.application.query.dto.CommentInfo;
import com.example.bak.comment.application.query.port.CommentQueryPort;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class CommentQueryAdapter implements CommentQueryPort {

private final CommentJpaRepository commentJpaRepository;

@Override
public List<CommentInfo> findByFeedId(Long feedId) {
return commentJpaRepository.findByFeedId(feedId).stream()
.map(CommentInfo::from)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.example.bak.feedcomment.presentation;
package com.example.bak.comment.presentation;

import com.example.bak.feedcomment.application.FeedCommentService;
import com.example.bak.feedcomment.application.dto.CommentInfo;
import com.example.bak.feedcomment.presentation.dto.CommentRequest;
import com.example.bak.feedcomment.presentation.swagger.FeedCommentSwagger;
import com.example.bak.comment.application.command.CommentCommandService;
import com.example.bak.comment.application.query.CommentQueryService;
import com.example.bak.comment.application.query.dto.CommentInfo;
import com.example.bak.comment.presentation.dto.CommentRequest;
import com.example.bak.comment.presentation.swagger.FeedCommentSwagger;
import com.example.bak.global.common.response.ApiResponse;
import com.example.bak.global.common.response.ApiResponseFactory;
import com.example.bak.global.common.utils.UriUtils;
import com.example.bak.global.security.annotation.AuthUser;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -21,38 +23,44 @@
@RestController
@RequestMapping("/api/v1")
@RequiredArgsConstructor
public class FeedCommentController implements FeedCommentSwagger {
public class CommentController implements FeedCommentSwagger {

private final FeedCommentService feedCommentService;
private final CommentCommandService commentCommandService;
private final CommentQueryService commentQueryService;

@Override
@PostMapping("/feeds/{feedId}/comments")
public ResponseEntity<ApiResponse> createComment(
@PathVariable Long feedId,
@RequestBody CommentRequest request
@RequestBody CommentRequest request,
@AuthUser Long userId
) {
feedCommentService.createComment(
commentCommandService.createComment(
feedId,
request.content(),
request.userId()
userId
);
ApiResponse response = ApiResponseFactory.successVoid("댓글을 성공적으로 생성하였습니다.");
return ResponseEntity.created(UriUtils.current())
.body(response);
}

@Override
@GetMapping("/feeds/{feedId}/comments")
public ResponseEntity<ApiResponse> getComment(@PathVariable Long feedId) {
List<CommentInfo> comments = feedCommentService.getComments(feedId);
public ResponseEntity<ApiResponse> getComments(@PathVariable Long feedId) {
List<CommentInfo> comments = commentQueryService.getComments(feedId);
ApiResponse response = ApiResponseFactory.success("댓글을 성공적으로 조회하였습니다.", comments);
return ResponseEntity.ok(response);
}

@Override
@PutMapping("/comments/{commentId}")
public ResponseEntity<ApiResponse> updateComment(
@PathVariable Long commentId,
@RequestBody CommentRequest request
@RequestBody CommentRequest request,
@AuthUser Long userId
) {
feedCommentService.updateComment(commentId, request.content(), request.userId());
commentCommandService.updateComment(commentId, request.content(), userId);
ApiResponse response = ApiResponseFactory.successVoid("댓글을 성공적으로 수정하였습니다.");
return ResponseEntity.ok(response);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.bak.comment.presentation.dto;

public record CommentRequest(String content) {

}
Loading