Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.function.Supplier;

public enum DraftErrorCode implements ErrorCode {

Copy link
Preview

Copilot AI Sep 18, 2025

Choose a reason for hiding this comment

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

[nitpick] The empty line and comment placement creates inconsistent formatting. Remove the blank line after the enum declaration or move the comment to align with other comment groupings.

Suggested change

Copilot uses AI. Check for mistakes.

// user input
DRAFT_BLOG_ID_REQUIRED("블로그 ID를 반드시 제공해야 합니다.", HttpStatus.BAD_REQUEST),
DRAFT_TITLE_MIN_LENGTH("블로그 제목은 반드시 3글자 이상입니다.", HttpStatus.BAD_REQUEST),
Expand All @@ -19,7 +20,10 @@ public enum DraftErrorCode implements ErrorCode {
DRAFT_GONE("더 이상 존재하지 않는 게시물입니다.", HttpStatus.GONE),
DRAFT_FORBIDDEN("권한이 없습니다.", HttpStatus.FORBIDDEN),
DRAFT_ALREADY_EXIST("임시글이 이미 존재합니다.", HttpStatus.CONFLICT),
DEFAULT("임시글 조작 오류", HttpStatus.INTERNAL_SERVER_ERROR);
DEFAULT("임시글 조작 오류", HttpStatus.INTERNAL_SERVER_ERROR),

// series status
SERIES_ARTICLE_NOT_FOUND("시리즈 아티클을 찾을 수 없습니다.", HttpStatus.NOT_FOUND);

private final String message;
private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

import nettee.blolet.article.domain.Draft;
import nettee.blolet.article.domain.DraftImage;
import nettee.blolet.article.domain.SeriesArticle;
import nettee.blolet.article.domain.sub.DraftStatus;
import nettee.blolet.article.readmodel.DraftReadModels.DraftDetail;

import java.util.Optional;

public interface DraftCommandPort {

Optional<DraftDetail> findById(String id);
Optional<DraftDetail> findDraftById(String id);

Optional<SeriesArticle> findSeriesArticleById(String id);

Draft save(Draft draft);

Expand All @@ -18,4 +21,10 @@ public interface DraftCommandPort {
void updateStatus(String id, DraftStatus draftStatus);

DraftImage save(DraftImage draftImage);

Draft updateTitle(String draftId, String title);

Draft updatePath(String draftId, String path);

SeriesArticle updateSeriesArticle(String draftId, String seriesId, String articleId);
Copy link
Preview

Copilot AI Sep 18, 2025

Choose a reason for hiding this comment

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

The updateSeriesArticle method is declared in the port interface but is not implemented or used in the service. This unused method should be removed to avoid confusion.

Suggested change
SeriesArticle updateSeriesArticle(String draftId, String seriesId, String articleId);

Copilot uses AI. Check for mistakes.

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import nettee.blolet.article.application.usecase.DraftCreateUseCase;
import nettee.blolet.article.application.usecase.DraftDeleteUseCase;
import nettee.blolet.article.application.usecase.DraftImageCreateUseCase;
import nettee.blolet.article.application.usecase.DraftPatchUseCase;
import nettee.blolet.article.application.usecase.DraftUpdateUseCase;
import nettee.blolet.article.domain.Draft;
import nettee.blolet.article.domain.DraftImage;
import nettee.blolet.article.domain.SeriesArticle;
import nettee.blolet.article.domain.sub.DraftStatus;
import nettee.blolet.blog.export.client.api.BlogClient;
import nettee.upload.port.ImageStorage;
Expand All @@ -16,10 +18,18 @@

import static nettee.blolet.article.exception.DraftErrorCode.DRAFT_FORBIDDEN;
import static nettee.blolet.article.exception.DraftErrorCode.DRAFT_NOT_FOUND;
import static nettee.blolet.article.exception.DraftErrorCode.SERIES_ARTICLE_NOT_FOUND;

@Service
@RequiredArgsConstructor
public class DraftCommandService implements DraftCreateUseCase, DraftUpdateUseCase, DraftDeleteUseCase, DraftImageCreateUseCase {
public class DraftCommandService implements
DraftCreateUseCase,
DraftUpdateUseCase,
DraftDeleteUseCase,
DraftImageCreateUseCase,
DraftPatchUseCase
Copy link
Member

Choose a reason for hiding this comment

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

🎨 라인이 끝나지 않을 때 줄바꿈 시, 들여쓰기는 2탭입니다!
(중괄호, 세미콜론이 등장하기 전 줄바꿈)

{

private final DraftCommandPort draftCommandPort;
private final BlogClient blogClient;
private final ImageStorage imageStorage;
Expand All @@ -38,7 +48,7 @@ public Draft updateDraft(String userId, Draft draft) {

@Override
public void deleteDraft(String userId, String draftId) {
var blogId = draftCommandPort.findById(draftId)
var blogId = draftCommandPort.findDraftById(draftId)
.orElseThrow(DRAFT_NOT_FOUND::exception)
.blogId();
validateOwnership(userId, blogId);
Expand All @@ -48,7 +58,7 @@ public void deleteDraft(String userId, String draftId) {

@Override
public DraftImage createDraftImage(String userId, String draftId, MultipartFile file, String targetName) {
var blogId = draftCommandPort.findById(draftId)
var blogId = draftCommandPort.findDraftById(draftId)
.orElseThrow(DRAFT_NOT_FOUND::exception)
.blogId();
validateOwnership(userId, blogId);
Expand All @@ -72,4 +82,34 @@ private void validateOwnership(String userId, String draftId) {
throw DRAFT_FORBIDDEN.exception();
}
}

@Override
public Draft patchTitle(String userId, String draftId, String title) {
Copy link
Member

Choose a reason for hiding this comment

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

🎨 이 순서로 배치하는 게 유지보수하기에 좋습니다!

계속 추가하다 보면 자주 실수할 수 있는 영역이네요.

class {
    fields

    public methods

    private methods
}

var draft = draftCommandPort.findDraftById(draftId)
.orElseThrow(DRAFT_NOT_FOUND::exception);

validateOwnership(userId, draft.blogId());

return draftCommandPort.updateTitle(draft.id(), title);
}

@Override
public Draft patchPath(String userId, String draftId, String path) {
var draft = draftCommandPort.findDraftById(draftId)
.orElseThrow(DRAFT_NOT_FOUND::exception);

validateOwnership(userId, draft.blogId());

return draftCommandPort.updatePath(draft.id(), path);
}

@Override
public SeriesArticle patchSeriesArticle(String userId, String draftId, String seriesId, String articleId) {
var seriesArticle = draftCommandPort.findSeriesArticleById(draftId)
.orElseThrow(SERIES_ARTICLE_NOT_FOUND::exception);

validateOwnership(userId, draftId);
Copy link
Member

Choose a reason for hiding this comment

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

❗️ 블로그 소유권을 확인하는 함수입니다.

지금처럼 실수가 생기지 않도록 함수 이름을 수정해 둬야 할 것 같기도 하네요. 저도 헷갈렸던 경험이 있어서 이참에 수정하는 게 좋을 것 같습니다!

이 요청 처리에는 사용자로부터 드래프트 소유권, 시리즈 소유권을 모두 확인해야 하고, 두 자원(드래프트, 시리즈)이 같은 블로그에 속해야 하므로 이하 확인이 필요해 보여요.

사전 조건

  • 드래프트의 블로그 아이디와 시리즈의 블로그 아이디가 일치.
  • 사용자가 해당 블로그의 소유권을 갖고 있음.

바쁜 중에 작업하다 보면 이렇게 리뷰로 확인할 수 있는 부분이 있어 리뷰 문화의 중요함을 알게 되네요.
앞으로 제 실수도 자주 발견되면 좋겠습니다!


return draftCommandPort.updateSeriesArticle(seriesArticle.getDraftId(), seriesId, articleId);
}
Copy link
Member

Choose a reason for hiding this comment

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

❗️ 시리즈 아티클을 등록하는 것이 본작업입니다.

지금 코드는 약간 오해를 담고 있는 것 같아요. 👀

구현할 기능

  • 시리즈 아티클 등록
    : 지금 구조와 반대로 '이미 등록되어 있습니다.' 등으로 필터
    (같은 드래프트, 같은 시리즈에 대한 연결일 때)
  • 드래프트에 연결된 시리즈 정보 수정 (비정규화 시)

사전 조건(Preconditions)

  • 요청한 사용자가 시리즈 및 드래프트에 대한 수정 권한을 갖고 있어야 합니다.
  • 시리즈와 드래프트가 같은 블로그에 속해야 합니다.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package nettee.blolet.article.application.usecase;

import nettee.blolet.article.domain.Draft;
import nettee.blolet.article.domain.SeriesArticle;

public interface DraftPatchUseCase {

Draft patchTitle(String userId, String draftId, String title);

Draft patchPath(String userId, String draftId, String path);

SeriesArticle patchSeriesArticle(String userId, String draftId, String seriesId, String articleId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class DraftCommandAdapter implements DraftCommandPort {
private final DraftEntityMapper mapper;

@Override
public Optional<DraftDetail> findById(String id) {
public Optional<DraftDetail> findDraftById(String id) {
var draft = draftJpaRepository.findById(id)
.orElseThrow(DRAFT_NOT_FOUND::exception);
return mapper.toOptionalDraftDetail(draft);
Expand Down