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
08aadd6
[feat] 관련 에러 코드 추가 (#86)
hd0rable Jul 21, 2025
1d596e3
[feat] feed 도메인 규칙 책임 추가(#86)
hd0rable Jul 21, 2025
24dced5
[feat] FeedCommandController.updateFeed (#86)
hd0rable Jul 21, 2025
6435b25
[feat] 피드 수정시 영속성 커맨드 어댑터 작성 (#86)
hd0rable Jul 21, 2025
315c2b0
[feat] FeedCommandPort.update (#86)
hd0rable Jul 21, 2025
40656b7
[refactor] 피드 생성/수정시 카테고리 검증 안하도록 수정 및 s3 삭제관련 todo 추가 (#86)
hd0rable Jul 21, 2025
e8df46c
[refactor] 피드 생성/수정 response dto 통일 (#86)
hd0rable Jul 21, 2025
9540bf9
[refactor] 피드 생성/수정 response dto 통일 (#86)
hd0rable Jul 21, 2025
26c43ed
[refactor] 피드 생성/수정시 카테고리 검증 안하도록 수정 및 s3 삭제관련 todo 추가 (#86)
hd0rable Jul 21, 2025
b09b505
[feat] jpa 엔티티 한번에 업데이트하는 updateFrom 추가 (#86)
hd0rable Jul 21, 2025
310cb9f
[fix] feed<->content 양방향 매핑관계 설정후, 피드도메인 엔티티 변환시 누락되었던 content 매핑관계 추…
hd0rable Jul 21, 2025
7abfbdf
[test] 피드 생성 테스트코드 수정 (#86)
hd0rable Jul 21, 2025
2e5b31e
[test] 피드 수정 통합 테스트코드 (#86)
hd0rable Jul 21, 2025
7cc67d1
[test] 피드 수정 컨트롤러 단위 테스트코드 작성 (#86)
hd0rable Jul 21, 2025
e84d924
[test] 테스트팩토리에 피드/피드태그/콘텐츠포함피드 생성 코드 추가(#86)
hd0rable Jul 21, 2025
88211fd
[feat] TagJpaRepository.findAllByFeedId (#86)
hd0rable Jul 21, 2025
4121382
[feat] FeedTagJpaRepository.deleteAllByFeedJpaEntity (#86)
hd0rable Jul 21, 2025
cbb46c8
[feat] 피드 수정 커맨드 작성 (#86)
hd0rable Jul 21, 2025
0431eb7
[feat] 피드 수정 request dto (#86)
hd0rable Jul 21, 2025
5af7c9c
[feat] 피드 수정 유즈케이스 구현체 서비스 작성 (#86)
hd0rable Jul 21, 2025
b4f35f1
[feat] 피드 수정 유즈케이스 작성 (#86)
hd0rable Jul 21, 2025
b4464d6
[refactor] 피드 수정시 엔티티가 부모 필드 값 수정가능하도록 제어자 protected로 수정 (#86)
hd0rable Jul 21, 2025
c8e4b61
[refactor] 카테고리 검증 로직 사라지면서 안쓰는 함수 삭제 (#86)
hd0rable Jul 21, 2025
2d96332
Merge remote-tracking branch 'origin/develop' into feat/#86-feed-update
hd0rable Jul 21, 2025
7311c58
[refactor] 충돌 해결 (#86)
hd0rable Jul 21, 2025
ce71420
[test] 테스트 이름 수정 (#86)
hd0rable Jul 21, 2025
fc79d91
[test] 테스트 이름 수정 (#86)
hd0rable Jul 21, 2025
17f4408
[test] 테스트 이름 수정 (#86)
hd0rable Jul 21, 2025
bd8adf8
[refactor] final 추가 (#86)
hd0rable Jul 21, 2025
89e73f7
[refactor] 도메인 내부에서 피드 생성/수정 로직 리펙 추가 (#92)
hd0rable Jul 21, 2025
f660133
[refactor] 메서드명 수정 (#86)
hd0rable Jul 21, 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
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ public enum ErrorCode implements ResponseCode {
FEED_NOT_FOUND(HttpStatus.NOT_FOUND, 160000, "존재하지 않는 FEED 입니다."),
TAG_NAME_NOT_MATCH(HttpStatus.BAD_REQUEST, 160001, "일치하는 태그 이름이 없습니다."),
TAG_NOT_FOUND(HttpStatus.NOT_FOUND, 160002, "존재하지 않는 TAG 입니다."),
INVALID_FEED_CREATE(HttpStatus.BAD_REQUEST, 160003, "유효하지 않은 FEED 생성 요청 입니다."),
INVALID_FEED_COMMAND(HttpStatus.BAD_REQUEST, 160003, "유효하지 않은 FEED 생성/수정 요청 입니다."),
FEED_UPDATE_FORBIDDEN(HttpStatus.FORBIDDEN, 160004, "피드 수정 권한이 없습니다."),

/**
* 170000 : Image File error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
import konkuk.thip.common.dto.BaseResponse;
import konkuk.thip.common.security.annotation.UserId;
import konkuk.thip.feed.adapter.in.web.request.FeedCreateRequest;
import konkuk.thip.feed.adapter.in.web.response.FeedCreateResponse;
import konkuk.thip.feed.adapter.in.web.request.FeedUpdateRequest;
import konkuk.thip.feed.adapter.in.web.response.FeedIdResponse;
import konkuk.thip.feed.application.port.in.FeedCreateUseCase;
import konkuk.thip.feed.application.port.in.FeedUpdateUseCase;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
Expand All @@ -21,11 +21,23 @@
public class FeedCommandController {

private final FeedCreateUseCase feedCreateUseCase;
private final FeedUpdateUseCase feedUpdateUseCase;

//피드 작성
@PostMapping("/feeds")
public BaseResponse<FeedCreateResponse> createFeed(@RequestPart("request") @Valid final FeedCreateRequest request,
@RequestPart(value = "images", required = false) final List<MultipartFile> images,
@UserId final Long userId) {
return BaseResponse.ok(FeedCreateResponse.of(feedCreateUseCase.createFeed(request.toCommand(userId),images)));
public BaseResponse<FeedIdResponse> createFeed(@RequestPart("request") @Valid final FeedCreateRequest request,
@RequestPart(value = "images", required = false) final List<MultipartFile> images,
@UserId final Long userId) {
return BaseResponse.ok(FeedIdResponse.of(feedCreateUseCase.createFeed(request.toCommand(userId),images)));
}

// 피드 수정 (책 빼고 변경가능)
@PatchMapping("/feeds/{feedId}")
public BaseResponse<FeedIdResponse> updateFeed(@RequestBody @Valid final FeedUpdateRequest request,
@PathVariable("feedId") final Long feedId,
@UserId final Long userId) {

return BaseResponse.ok(FeedIdResponse.of(feedUpdateUseCase.updateFeed(request.toCommand(userId,feedId))));

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@ public record FeedCreateRequest(
@NotNull(message = "방 공개 설정 여부는 필수입니다.")
Boolean isPublic,

String category,

List<String> tagList
) {
public FeedCreateCommand toCommand(Long userId) {
return new FeedCreateCommand(
isbn,
contentBody,
isPublic,
category,
tagList,
userId
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package konkuk.thip.feed.adapter.in.web.request;

import konkuk.thip.feed.application.port.in.dto.FeedUpdateCommand;

import java.util.List;

public record FeedUpdateRequest(

String contentBody,

Boolean isPublic,

List<String> tagList,

List<String> remainImageUrls
) {
public FeedUpdateCommand toCommand(Long userId, Long feedId) {
return new FeedUpdateCommand(
contentBody,
isPublic,
tagList,
remainImageUrls,
userId,
feedId
);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package konkuk.thip.feed.adapter.in.web.response;

public record FeedIdResponse(Long feedId) {
public static FeedIdResponse of(Long feedId) {
return new FeedIdResponse(feedId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import jakarta.persistence.*;
import konkuk.thip.book.adapter.out.jpa.BookJpaEntity;
import konkuk.thip.feed.domain.Feed;
import konkuk.thip.post.adapter.out.jpa.PostJpaEntity;
import konkuk.thip.user.adapter.out.jpa.UserJpaEntity;
import lombok.AccessLevel;
Expand Down Expand Up @@ -40,4 +41,12 @@ public FeedJpaEntity(String content, Integer likeCount, Integer commentCount, Us
this.bookJpaEntity = bookJpaEntity;
this.contentList = contentList;
}

public void updateFrom(Feed feed) {
this.content = feed.getContent();
this.isPublic = feed.getIsPublic();
this.reportCount = feed.getReportCount();
this.likeCount = feed.getLikeCount();
this.commentCount = feed.getCommentCount();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
import konkuk.thip.feed.domain.Feed;
import konkuk.thip.feed.domain.Tag;
import konkuk.thip.user.adapter.out.jpa.UserJpaEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

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

@Component
@RequiredArgsConstructor
public class FeedMapper {

private final ContentMapper contentMapper;

public FeedJpaEntity toJpaEntity(Feed feed, UserJpaEntity userJpaEntity, BookJpaEntity bookJpaEntity) {
return FeedJpaEntity.builder()
.content(feed.getContent())
Expand Down Expand Up @@ -41,6 +45,9 @@ public Feed toDomainEntity(FeedJpaEntity feedJpaEntity, List<TagJpaEntity> tagJp
.map(TagJpaEntity::getValue)
.map(Tag::from)
.toList())
.contentList(feedJpaEntity.getContentList().stream()
.map(contentMapper::toDomainEntity)
.toList())
.createdAt(feedJpaEntity.getCreatedAt())
.modifiedAt(feedJpaEntity.getModifiedAt())
.status(feedJpaEntity.getStatus())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@
import konkuk.thip.user.adapter.out.jpa.UserJpaEntity;
import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

import java.util.List;

import static konkuk.thip.common.exception.code.ErrorCode.*;

@Slf4j
@Repository
@RequiredArgsConstructor
public class FeedCommandPersistenceAdapter implements FeedCommandPort {
Expand All @@ -38,6 +36,16 @@ public class FeedCommandPersistenceAdapter implements FeedCommandPort {
private final FeedMapper feedMapper;
private final ContentMapper contentMapper;

@Override
public Feed findById(Long id) {
FeedJpaEntity feedJpaEntity = feedJpaRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(FEED_NOT_FOUND));

List<TagJpaEntity> tagJpaEntityList = tagJpaRepository.findAllByFeedId(feedJpaEntity.getPostId());

return feedMapper.toDomainEntity(feedJpaEntity, tagJpaEntityList);
}

@Override
public Long save(Feed feed) {

Expand All @@ -53,26 +61,38 @@ public Long save(Feed feed) {
FeedJpaEntity savedFeed = feedJpaRepository.save(feedJpaEntity);

// Content가 존재하면 ContentJpaEntity 생성 및 Feed 연관관계 설정
saveContents(feed, savedFeed);
applyFeedContents(feed, savedFeed);
// 태그가 존재하면 태그 피드 매핑 생성 및 저장
saveFeedTags(feed, savedFeed);
applyFeedTags(feed, savedFeed);

return savedFeed.getPostId();
}

private void saveContents(Feed feed, FeedJpaEntity feedJpaEntity) {
if (feed.getContentList().isEmpty()) return;
@Override
public Long update(Feed feed) {
FeedJpaEntity feedJpaEntity = feedJpaRepository.findById(feed.getId())
.orElseThrow(() -> new EntityNotFoundException(FEED_NOT_FOUND));
feedJpaEntity.updateFrom(feed);

feedJpaEntity.getContentList().clear(); // 피드 수정시 기존 영속성 컨텍스트 내 엔티티 연결 제거
applyFeedContents(feed, feedJpaEntity);

List<ContentJpaEntity> contentJpaEntities = feed.getContentList().stream()
feedTagJpaRepository.deleteAllByFeedJpaEntity(feedJpaEntity); // 피드 수정시 기존 피드의 모든 FeedTag 매핑 row 삭제
applyFeedTags(feed, feedJpaEntity);

return feedJpaEntity.getPostId();
}

private void applyFeedContents(Feed feed, FeedJpaEntity feedJpaEntity) {
if (feed.getContentList().isEmpty()) return;
List<ContentJpaEntity> contents = feed.getContentList().stream()
.map(content -> contentMapper.toJpaEntity(content, feedJpaEntity))
.toList();

contentJpaEntities.forEach(feedJpaEntity.getContentList()::add);
feedJpaEntity.getContentList().addAll(contents);
}

private void saveFeedTags(Feed feed, FeedJpaEntity feedJpaEntity) {
private void applyFeedTags(Feed feed, FeedJpaEntity feedJpaEntity) {
if (feed.getTagList().isEmpty()) return;

for (Tag tag : feed.getTagList()) {
TagJpaEntity tagJpaEntity = tagJpaRepository.findByValue(tag.getValue())
.orElseThrow(() -> new EntityNotFoundException(TAG_NOT_FOUND));
Expand All @@ -86,5 +106,4 @@ private void saveFeedTags(Feed feed, FeedJpaEntity feedJpaEntity) {
}
}


}
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
package konkuk.thip.feed.adapter.out.persistence.repository.FeedTag;

import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity;
import konkuk.thip.feed.adapter.out.jpa.FeedTagJpaEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface FeedTagJpaRepository extends JpaRepository<FeedTagJpaEntity, Long>{

@Modifying
@Query("DELETE FROM FeedTagJpaEntity ft WHERE ft.feedJpaEntity = :feedJpaEntity")
void deleteAllByFeedJpaEntity(@Param("feedJpaEntity") FeedJpaEntity feedJpaEntity);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@

import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

public interface TagJpaRepository extends JpaRepository<TagJpaEntity, Long>{
Optional<TagJpaEntity> findByValue(String value);

@Query("SELECT ft.tagJpaEntity FROM FeedTagJpaEntity ft WHERE ft.feedJpaEntity.postId = :feedId")
List<TagJpaEntity> findAllByFeedId(@Param("feedId") Long feedId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package konkuk.thip.feed.application.port.in;

import konkuk.thip.feed.application.port.in.dto.FeedUpdateCommand;

public interface FeedUpdateUseCase {
Long updateFeed(FeedUpdateCommand command);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ public record FeedCreateCommand(

Boolean isPublic,

String category,

List<String> tagList,

Long userId
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package konkuk.thip.feed.application.port.in.dto;

import java.util.List;

public record FeedUpdateCommand(

String contentBody,

Boolean isPublic,

List<String> tagList,

List<String> remainImageUrls,

Long userId,

Long feedId
)
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@

public interface FeedCommandPort {
Long save(Feed feed);
Long update(Feed feed);
Feed findById(Long id);
}
Loading