Skip to content

feat: 아티클 생성로직 구현#16

Merged
rootTiket merged 13 commits intodevfrom
feat/#15
Jul 7, 2025
Merged

feat: 아티클 생성로직 구현#16
rootTiket merged 13 commits intodevfrom
feat/#15

Conversation

@rootTiket
Copy link
Contributor

@rootTiket rootTiket commented Jul 7, 2025

🚀 PR 요약

아티클 생성 로직을 구현했습니다

✨ PR 상세 내용

  1. 리마인드 생성은 없는 순수하게 생성하는 로직으로 구현했습니다
  2. 아티클 생성시 인당 같은 url은 하나만 가질 수 있기 때문에 도메인에 uniqueKey를 걸었습니다 f1f66a6
  3. 미리 중복 url을 검증할 수 있는 로직을 만들었습니다
  4. 현재 가진 카테고리가 존재하지 않을 경우 예외를 처리했습니다.
  5. 원활한 테스트를 위해 testFixture를 만들었습니다.
  6. 테스트 일관성을 위해 ApplicationTest 클래스를 만들었습니다. f9c4062

🚨 주의 사항

주의할 부분이 무엇인가요? - 지우고 작성

✅ 체크 리스트

  • 리뷰어 설정했나요?
  • Label 설정했나요?
  • 제목 양식 맞췄나요? (ex. feat: 기능 추가)
  • 변경 사항에 대한 테스트를 진행했나요?

close #15

Summary by CodeRabbit

  • New Features

    • Added the ability to create articles with URL, category, memo, and reminder time, including validation and duplicate checks.
    • Introduced a new endpoint for article creation.
    • Implemented error handling for duplicate articles and missing categories.
  • Bug Fixes

    • Enforced uniqueness for articles based on user and URL to prevent duplicates.
  • Tests

    • Added comprehensive tests for article creation, duplicate handling, and category ownership validation.
    • Introduced utilities for test data setup and database cleanup.

@rootTiket rootTiket requested a review from ose0221 July 7, 2025 12:36
@rootTiket rootTiket self-assigned this Jul 7, 2025
@rootTiket rootTiket added the FEAT label Jul 7, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 7, 2025

Walkthrough

This update introduces a complete article creation feature, including REST API endpoint, service and domain logic, exception handling, validation, and related tests. It also adds supporting infrastructure for category validation and test fixture management, enforces unique constraints on articles, and updates global exception codes for precise error reporting.

Changes

File(s) Change Summary
.../domain/article/application/ArticleManagementUsecase.java Added service class for article creation with transactional logic and duplicate URL check.
.../domain/article/application/command/ArticleCreateCommand.java Introduced record for encapsulating article creation command data.
.../domain/article/domain/entity/Article.java Added unique constraint on (user_id, url) at the entity level.
.../domain/article/domain/repository/ArticleRepository.java Added method to check existence of article by user and URL.
.../domain/article/domain/service/ArticleGetService.java Added service to check if an article exists for a user and URL.
.../domain/article/domain/service/ArticleSaveService.java Added service for saving articles transactionally.
.../domain/article/exception/ArticleAlreadyExistException.java Introduced exception for duplicate article creation attempts.
.../domain/article/presentation/ArticleController.java Added REST controller with POST endpoint for article creation.
.../domain/article/presentation/dto/request/ArticleCreateRequest.java Added request DTO with validation and conversion to command object.
.../domain/category/domain/repository/CategoryRepository.java Added method to find category by ID and user.
.../domain/category/domain/service/CategoryGetService.java Added service to retrieve category for a user or throw if not found.
.../domain/category/exception/CategoryNotFoundException.java Added exception for missing category scenarios.
.../global/exception/constant/ExceptionCode.java Updated/added exception codes for user not found, category not found, and article already exists.
.../test/java/com/pinback/pinback_server/domain/ApplicationTest.java Added base application test with cleanup logic using CustomRepository.
.../test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java Added tests for article creation and duplicate handling.
.../test/java/com/pinback/pinback_server/domain/category/domain/service/CategoryGetServiceTest.java Added test to verify category access control and exception throwing.
.../test/java/com/pinback/pinback_server/domain/fixture/CustomRepository.java Added repository to clear and reset test data in key tables.
.../test/java/com/pinback/pinback_server/domain/fixture/TestFixture.java Added fixture utilities for test entity creation (User, Category, Article).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller as ArticleController
    participant Usecase as ArticleManagementUsecase
    participant ArticleGet as ArticleGetService
    participant CategoryGet as CategoryGetService
    participant ArticleSave as ArticleSaveService

    Client->>Controller: POST /article (ArticleCreateRequest)
    Controller->>Usecase: createArticle(user, command)
    Usecase->>ArticleGet: checkExistsByUserAndUrl(user, url)
    ArticleGet-->>Usecase: exists? (bool)
    alt Article already exists
        Usecase-->>Controller: throw ArticleAlreadyExistException
    else Article does not exist
        Usecase->>CategoryGet: getCategoryAndUser(categoryId, user)
        CategoryGet-->>Usecase: Category
        Usecase->>ArticleSave: save(new Article)
        ArticleSave-->>Usecase: (void)
        Usecase-->>Controller: (void)
    end
    Controller-->>Client: ResponseDto<Void> (201 CREATED)
Loading

Suggested reviewers

  • ose0221

Poem

Hopping through code with a twitch of my nose,
I see new articles bloom where the spring wind blows.
Unique URLs, categories checked,
Exceptions for duplicates—errors deflect!
With fixtures and tests, the garden is neat—
This bunny’s quite proud of this feature’s heartbeat.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (11)
src/main/java/com/pinback/pinback_server/domain/article/exception/ArticleAlreadyExistException.java (1)

6-10: Consider adding context parameters for better error reporting.

The exception implementation is correct and follows the established pattern. However, consider adding optional parameters to provide more context about which URL and user caused the duplicate.

Here's an enhanced version that maintains backward compatibility:

 public class ArticleAlreadyExistException extends ApplicationException {
 	public ArticleAlreadyExistException() {
 		super(ExceptionCode.ARTICLE_ALREADY_EXIST);
 	}
+	
+	public ArticleAlreadyExistException(String url, String username) {
+		super(ExceptionCode.ARTICLE_ALREADY_EXIST, 
+			String.format("Article with URL '%s' already exists for user '%s'", url, username));
+	}
 }
src/test/java/com/pinback/pinback_server/domain/ApplicationTest.java (1)

12-12: Consider renaming the class for better clarity.

The class name ApplicationTest is misleading since it doesn't contain actual tests. Consider renaming it to BaseIntegrationTest or ApplicationTestBase to better reflect its purpose as a test base class.

-public class ApplicationTest {
+public abstract class BaseIntegrationTest {
src/test/java/com/pinback/pinback_server/domain/fixture/CustomRepository.java (1)

20-21: Ensure consistent ID reset and database portability.

Two observations:

  1. Consider resetting the users table ID counter for consistency
  2. The ALTER TABLE ... RESTART WITH syntax is H2-specific
 		entityManager.createNativeQuery("ALTER TABLE article ALTER COLUMN article_id RESTART WITH 1").executeUpdate();
 		entityManager.createNativeQuery("ALTER TABLE category ALTER COLUMN category_id RESTART WITH 1").executeUpdate();
+		entityManager.createNativeQuery("ALTER TABLE users ALTER COLUMN user_id RESTART WITH 1").executeUpdate();
src/main/java/com/pinback/pinback_server/domain/category/domain/service/CategoryGetService.java (1)

19-21: Consider renaming method for clarity.

The method name getCategoryAndUser is slightly misleading as it retrieves a category for a user, not both entities. Consider renaming to better reflect the operation:

-	public Category getCategoryAndUser(long categoryId, User user) {
+	public Category getCategoryByIdAndUser(long categoryId, User user) {

Otherwise, the implementation is clean and follows good practices with proper exception handling.

src/test/java/com/pinback/pinback_server/domain/category/domain/service/CategoryGetServiceTest.java (1)

34-44: Consider adding positive test case for completeness.

The test covers the exception scenario well, but consider adding a positive test case to verify successful category retrieval when the user owns the category:

@DisplayName("카테고리 소유자일 경우 정상적으로 조회된다.")
@Test
void getCategoryWhenOwner() {
    // given
    User user = userRepository.save(user());
    Category category = categoryRepository.save(category(user));
    
    // when
    Category result = categoryGetService.getCategoryAndUser(category.getId(), user);
    
    // then
    assertThat(result).isNotNull();
    assertThat(result.getId()).isEqualTo(category.getId());
}

The existing test implementation is solid and follows good practices.

src/test/java/com/pinback/pinback_server/domain/fixture/TestFixture.java (1)

19-21: Consider i18n implications for test data.

The hard-coded Korean text "테스트카테고리" in the category name might cause issues in international development environments or CI/CD pipelines that don't support Korean characters. Consider using English for test fixtures:

-	public static Category category(User user) {
-		return Category.create("테스트카테고리", user);
-	}
+	public static Category category(User user) {
+		return Category.create("Test Category", user);
+	}

Otherwise, the fixture design is excellent with good separation of concerns and consistent factory methods.

src/main/java/com/pinback/pinback_server/domain/article/presentation/dto/request/ArticleCreateRequest.java (2)

11-11: Consider more precise validation message.

The message "url을 비어있을 수 없습니다" (URL cannot be empty) is accurate for @NotEmpty validation. However, consider externalizing validation messages to a properties file for better maintainability and internationalization support.


14-14: Improve validation message precision.

The message "카테고리 ID는 비어있을 수 없습니다" (Category ID cannot be empty) is slightly imprecise since Long values are null rather than empty. Consider using "카테고리 ID는 필수입니다" (Category ID is required) for better clarity.

src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java (1)

28-35: Consider returning the created article.

The method returns void but could return the created Article entity, which would be useful for getting the generated ID and other computed fields.

-public void createArticle(User user, ArticleCreateCommand command) {
+public Article createArticle(User user, ArticleCreateCommand command) {
     if (articleGetService.checkExistsByUserAndUrl(user, command.url())) {
         throw new ArticleAlreadyExistException();
     }
     Category category = categoryGetService.getCategoryAndUser(command.categoryId(), user);
     Article article = Article.create(command.url(), command.memo(), user, category);
-    articleSaveService.save(article);
+    return articleSaveService.save(article);
src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java (2)

44-46: Consider using relative dates in tests.

The hard-coded future date LocalDateTime.of(2025, 8, 6, 0, 0, 0) makes the test brittle and may cause issues over time.

-LocalDateTime.of(2025, 8, 6, 0, 0, 0)
+LocalDateTime.now().plusDays(1)

58-71: Consider adding CategoryNotFoundException test.

The test coverage is good for duplicate handling, but missing a test case for when the category doesn't exist or doesn't belong to the user.

Add a test method:

@DisplayName("존재하지 않는 카테고리로 아티클을 생성하면 예외가 발생한다.")
@Test
void createArticle_CategoryNotFound() {
    User user = userRepository.save(user());
    ArticleCreateCommand command = new ArticleCreateCommand("testUrl", 999L, "테스트메모", LocalDateTime.now().plusDays(1));
    
    assertThatThrownBy(() -> articleManagementUsecase.createArticle(user, command))
        .isInstanceOf(CategoryNotFoundException.class);
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14dce9f and f9c4062.

📒 Files selected for processing (18)
  • src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java (1 hunks)
  • src/main/java/com/pinback/pinback_server/domain/article/application/command/ArticleCreateCommand.java (1 hunks)
  • src/main/java/com/pinback/pinback_server/domain/article/domain/entity/Article.java (2 hunks)
  • src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepository.java (1 hunks)
  • src/main/java/com/pinback/pinback_server/domain/article/domain/service/ArticleGetService.java (1 hunks)
  • src/main/java/com/pinback/pinback_server/domain/article/domain/service/ArticleSaveService.java (1 hunks)
  • src/main/java/com/pinback/pinback_server/domain/article/exception/ArticleAlreadyExistException.java (1 hunks)
  • src/main/java/com/pinback/pinback_server/domain/article/presentation/ArticleController.java (1 hunks)
  • src/main/java/com/pinback/pinback_server/domain/article/presentation/dto/request/ArticleCreateRequest.java (1 hunks)
  • src/main/java/com/pinback/pinback_server/domain/category/domain/repository/CategoryRepository.java (1 hunks)
  • src/main/java/com/pinback/pinback_server/domain/category/domain/service/CategoryGetService.java (1 hunks)
  • src/main/java/com/pinback/pinback_server/domain/category/exception/CategoryNotFoundException.java (1 hunks)
  • src/main/java/com/pinback/pinback_server/global/exception/constant/ExceptionCode.java (1 hunks)
  • src/test/java/com/pinback/pinback_server/domain/ApplicationTest.java (1 hunks)
  • src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java (1 hunks)
  • src/test/java/com/pinback/pinback_server/domain/category/domain/service/CategoryGetServiceTest.java (1 hunks)
  • src/test/java/com/pinback/pinback_server/domain/fixture/CustomRepository.java (1 hunks)
  • src/test/java/com/pinback/pinback_server/domain/fixture/TestFixture.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/main/java/com/pinback/pinback_server/domain/category/domain/repository/CategoryRepository.java (1)
src/test/java/com/pinback/pinback_server/domain/fixture/CustomRepository.java (1)
  • Repository (9-23)
src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepository.java (1)
src/test/java/com/pinback/pinback_server/domain/fixture/CustomRepository.java (1)
  • Repository (9-23)
src/test/java/com/pinback/pinback_server/domain/category/domain/service/CategoryGetServiceTest.java (2)
src/test/java/com/pinback/pinback_server/domain/fixture/TestFixture.java (1)
  • TestFixture (9-31)
src/main/java/com/pinback/pinback_server/domain/category/exception/CategoryNotFoundException.java (1)
  • CategoryNotFoundException (6-10)
src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java (2)
src/main/java/com/pinback/pinback_server/domain/category/domain/service/CategoryGetService.java (1)
  • Service (13-22)
src/main/java/com/pinback/pinback_server/domain/article/exception/ArticleAlreadyExistException.java (1)
  • ArticleAlreadyExistException (6-10)
src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java (3)
src/test/java/com/pinback/pinback_server/domain/fixture/TestFixture.java (1)
  • TestFixture (9-31)
src/test/java/com/pinback/pinback_server/domain/ApplicationTest.java (1)
  • SpringBootTest (10-21)
src/main/java/com/pinback/pinback_server/domain/article/exception/ArticleAlreadyExistException.java (1)
  • ArticleAlreadyExistException (6-10)
🔇 Additional comments (21)
src/main/java/com/pinback/pinback_server/domain/category/domain/repository/CategoryRepository.java (2)

3-4: LGTM - Clean imports added.

The Optional and User imports are correctly added to support the new repository method.


13-13: LGTM - Repository method follows Spring Data JPA conventions.

The findByIdAndUser method signature correctly follows Spring Data JPA naming conventions and will automatically generate the appropriate query to find a category by both ID and user ownership.

src/main/java/com/pinback/pinback_server/domain/category/exception/CategoryNotFoundException.java (1)

1-11: LGTM - Exception class follows established patterns.

The CategoryNotFoundException correctly extends ApplicationException and uses the predefined ExceptionCode.CATEGORY_NOT_FOUND. The implementation is clean and consistent with the codebase's exception handling approach.

src/main/java/com/pinback/pinback_server/domain/article/domain/entity/Article.java (2)

16-16: LGTM - UniqueConstraint import added.

The import is correctly added to support the unique constraint annotation.


25-28: LGTM - Unique constraint properly enforces business rule.

The unique constraint on user_id and url columns correctly implements the business requirement that each user can have only one article with the same URL. The column names match the entity's field mappings.

src/main/java/com/pinback/pinback_server/domain/article/application/command/ArticleCreateCommand.java (1)

1-12: Verify remindTime field aligns with PR objectives.

The command record structure is well-designed, but the inclusion of remindTime seems inconsistent with the PR description stating "pure article creation logic without any reminder creation." Please confirm if reminder functionality should be included in this implementation.

src/main/java/com/pinback/pinback_server/domain/article/domain/service/ArticleSaveService.java (1)

11-20: LGTM - Service follows established patterns.

The ArticleSaveService correctly follows the established service layer pattern with proper transaction management. While it's a thin wrapper around the repository, it maintains architectural consistency and provides a clear abstraction layer.

src/main/java/com/pinback/pinback_server/domain/article/domain/repository/ArticleRepository.java (2)

7-7: Import addition is correct.

The User entity import is properly added to support the new method signature.


12-12: Repository method follows JPA conventions correctly.

The existsByUserAndUrl method signature follows Spring Data JPA naming conventions and will automatically generate the appropriate query. The boolean return type is suitable for existence checks.

src/test/java/com/pinback/pinback_server/domain/ApplicationTest.java (1)

17-20: Database cleanup strategy is appropriate.

The @AfterEach cleanup approach ensures test isolation by resetting the database state after each test. This is a good practice for integration tests.

src/main/java/com/pinback/pinback_server/global/exception/constant/ExceptionCode.java (3)

13-13: HTTP status code comment correction is accurate.

The comment correction from 403 to 401 is appropriate for token-related exceptions (unauthorized vs forbidden).


20-21: New exception codes follow established patterns.

The new exception codes CATEGORY_NOT_FOUND ("c40402") and the updated USER_NOT_FOUND ("c40401") maintain the logical sequence within the 404 category.


29-29: Article duplicate exception code is well-defined.

The ARTICLE_ALREADY_EXIST exception code ("c40902") is appropriately placed under the 409 Conflict category with a clear Korean message.

src/main/java/com/pinback/pinback_server/domain/article/domain/service/ArticleGetService.java (2)

11-13: Service annotations are correctly applied.

The combination of @Service, @RequiredArgsConstructor, and @Transactional(readOnly = true) is appropriate for a read-only service class.


17-19: Method implementation is clean and focused.

The checkExistsByUserAndUrl method has a clear, descriptive name and appropriately delegates to the repository. The implementation follows the single responsibility principle.

src/test/java/com/pinback/pinback_server/domain/fixture/CustomRepository.java (1)

16-18: Consider deletion order for foreign key constraints.

The current deletion order may cause foreign key constraint violations if referential integrity is enforced. Since article likely references category and users, consider deleting in reverse dependency order:

-		entityManager.createNativeQuery("DELETE FROM article").executeUpdate();
-		entityManager.createNativeQuery("DELETE FROM category").executeUpdate();
-		entityManager.createNativeQuery("DELETE FROM users").executeUpdate();
+		entityManager.createNativeQuery("DELETE FROM article").executeUpdate();
+		entityManager.createNativeQuery("DELETE FROM category").executeUpdate();
+		entityManager.createNativeQuery("DELETE FROM users").executeUpdate();

Actually, the current order appears correct (delete child tables first), but verify this matches your actual foreign key relationships.

src/main/java/com/pinback/pinback_server/domain/article/presentation/ArticleController.java (1)

23-27: Excellent controller implementation following best practices.

The controller demonstrates:

  • Clean separation of concerns with delegation to the use case layer
  • Proper validation with @Valid annotation
  • Appropriate use of @CurrentUser for authentication
  • Correct HTTP status response with ResponseDto.created()

The implementation follows Spring Boot conventions and REST best practices effectively.

src/test/java/com/pinback/pinback_server/domain/fixture/TestFixture.java (1)

23-30: Well-designed factory methods for flexible test scenarios.

The article factory methods provide good flexibility:

  • article(User user) creates a complete article with a new category
  • articleWithCategory(User user, Category category) allows reusing existing categories

This design supports various testing scenarios effectively while maintaining clean, readable test code.

src/main/java/com/pinback/pinback_server/domain/article/presentation/dto/request/ArticleCreateRequest.java (1)

22-29: LGTM! Clean conversion method.

The toCommand() method provides a clean conversion from request DTO to command object, maintaining clear separation of concerns between presentation and application layers.

src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java (1)

17-25: LGTM! Proper service structure and dependencies.

The service follows Spring best practices with appropriate annotations and dependency injection. The default @Transactional(readOnly = true) is a good defensive approach.

src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java (1)

25-28: LGTM! Proper test class structure.

The test class correctly extends ApplicationTest and uses appropriate Spring Boot test annotations with transactional support.

Comment on lines +26 to +35
//TODO: 리마인드 로직 추가 필요
@Transactional
public void createArticle(User user, ArticleCreateCommand command) {
if (articleGetService.checkExistsByUserAndUrl(user, command.url())) {
throw new ArticleAlreadyExistException();
}
Category category = categoryGetService.getCategoryAndUser(command.categoryId(), user);
Article article = Article.create(command.url(), command.memo(), user, category);
articleSaveService.save(article);
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Critical: RemindTime parameter is completely ignored.

The method accepts remindTime through the command but doesn't use it anywhere. This creates a significant disconnect between the API contract and the actual implementation.

Consider one of these approaches:

// Option 1: Use remindTime in Article creation
-Article article = Article.create(command.url(), command.memo(), user, category);
+Article article = Article.create(command.url(), command.memo(), user, category, command.remindTime());

// Option 2: If reminder logic is not ready, document the limitation
+// TODO: remindTime is currently ignored until reminder logic is implemented

Verify if the Article.create() method supports remindTime parameter:


🏁 Script executed:

#!/bin/bash
# Search for Article.create method signatures
ast-grep --pattern 'class Article {
  $$$
  static $_ create($$$) {
    $$$
  }
  $$$
}'

Length of output: 90


I couldn’t locate the Article.create signature with AST grep. Let’s fall back to a file search and regex:


🏁 Script executed:

#!/bin/bash
# Locate Article.java
file=$(fd Article.java)
echo "Located file: $file"

# Show the class declaration
rg -n 'class Article' "$file"

# Show all static create method signatures
rg -n 'static .*\bcreate\b' "$file"

Length of output: 666


Include remindTime in article creation or document its omission

The remindTime value provided in ArticleCreateCommand is never used because the Article.create(...) factory method only accepts (url, memo, user, category) and doesn’t support a reminder timestamp. You need to either wire it through the domain model or explicitly note that it’s pending implementation.

• In src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java:createArticle you call:

Article.create(command.url(), command.memo(), user, category);

but never use command.remindTime().

• In src/main/java/com/pinback/pinback_server/domain/article/domain/entity/Article.java:55 the signature is:

public static Article create(String url, String memo, User user, Category category) { … }

Consider one of these fixes:

Option 1: Extend the create signature and pass remindTime through:

// ArticleManagementUsecase.java
- Article article = Article.create(command.url(), command.memo(), user, category);
+ Article article = Article.create(
+     command.url(),
+     command.memo(),
+     user,
+     category,
+     command.remindTime()
+ );

// Article.java
- public static Article create(String url, String memo, User user, Category category) {
+ public static Article create(
+     String url,
+     String memo,
+     User user,
+     Category category,
+     LocalDateTime remindTime
+ ) {
    Article article = new Article(…);
+   article.setRemindTime(remindTime);
    return article;
}

Option 2: Leave remindTime unhandled for now but document it:

// TODO: `remindTime` is ignored until reminder scheduling logic is implemented
@Transactional
public void createArticle(User user, ArticleCreateCommand command) {
    …
}
🤖 Prompt for AI Agents
In
src/main/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecase.java
around lines 26 to 35, the remindTime from ArticleCreateCommand is not used in
the Article.create call because the factory method does not accept it. To fix
this, either extend the Article.create method signature in the domain entity to
include remindTime and pass it through here, or add a clear TODO comment in
createArticle noting that remindTime is currently ignored until reminder
scheduling is implemented.

Comment on lines +39 to +41
@DisplayName("사용자는 아티클을 생성할 수 있다.")
@Test
void articleSaveService() {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix misleading test method name.

The method name articleSaveService() is misleading as it tests the use case, not the save service specifically.

-@DisplayName("사용자는 아티클을 생성할 수 있다.")
-@Test
-void articleSaveService() {
+@DisplayName("사용자는 아티클을 생성할 수 있다.")
+@Test
+void createArticle_Success() {
🤖 Prompt for AI Agents
In
src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java
around lines 39 to 41, rename the test method from articleSaveService() to a
name that clearly reflects it is testing the article management use case, such
as articleManagementUsecaseTest() or testArticleCreationUsecase(), to avoid
confusion with a specific save service.

Comment on lines +52 to +55
assertThat(article.getUrl()).isEqualTo(command.url());
assertThat(article.getMemo()).isEqualTo(command.memo());
assertThat(article.getCategory()).isEqualTo(category);
assertThat(article.getIsRead()).isFalse();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for ignored remindTime parameter.

The test doesn't verify that remindTime is properly stored, which would catch the issue where this parameter is currently ignored in the implementation.

 assertThat(article.getUrl()).isEqualTo(command.url());
 assertThat(article.getMemo()).isEqualTo(command.memo());
 assertThat(article.getCategory()).isEqualTo(category);
 assertThat(article.getIsRead()).isFalse();
+assertThat(article.getRemindTime()).isEqualTo(command.remindTime());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assertThat(article.getUrl()).isEqualTo(command.url());
assertThat(article.getMemo()).isEqualTo(command.memo());
assertThat(article.getCategory()).isEqualTo(category);
assertThat(article.getIsRead()).isFalse();
assertThat(article.getUrl()).isEqualTo(command.url());
assertThat(article.getMemo()).isEqualTo(command.memo());
assertThat(article.getCategory()).isEqualTo(category);
assertThat(article.getIsRead()).isFalse();
assertThat(article.getRemindTime()).isEqualTo(command.remindTime());
🤖 Prompt for AI Agents
In
src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java
around lines 52 to 55, the test is missing validation for the remindTime
parameter. Add an assertion to verify that the article's remindTime matches the
command's remindTime to ensure this parameter is correctly stored and handled in
the implementation.

Comment on lines +50 to +56
//then
Article article = articleRepository.findById(1L).get();
assertThat(article.getUrl()).isEqualTo(command.url());
assertThat(article.getMemo()).isEqualTo(command.memo());
assertThat(article.getCategory()).isEqualTo(category);
assertThat(article.getIsRead()).isFalse();
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid hard-coded ID assumptions.

Using findById(1L) assumes specific ID generation behavior, making the test brittle. Consider using repository queries or storing the created article's ID.

-//then
-Article article = articleRepository.findById(1L).get();
+//then
+List<Article> articles = articleRepository.findByUser(user);
+assertThat(articles).hasSize(1);
+Article article = articles.get(0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//then
Article article = articleRepository.findById(1L).get();
assertThat(article.getUrl()).isEqualTo(command.url());
assertThat(article.getMemo()).isEqualTo(command.memo());
assertThat(article.getCategory()).isEqualTo(category);
assertThat(article.getIsRead()).isFalse();
}
//then
List<Article> articles = articleRepository.findByUser(user);
assertThat(articles).hasSize(1);
Article article = articles.get(0);
assertThat(article.getUrl()).isEqualTo(command.url());
assertThat(article.getMemo()).isEqualTo(command.memo());
assertThat(article.getCategory()).isEqualTo(category);
assertThat(article.getIsRead()).isFalse();
}
🤖 Prompt for AI Agents
In
src/test/java/com/pinback/pinback_server/domain/article/application/ArticleManagementUsecaseTest.java
around lines 50 to 56, avoid using a hard-coded ID (1L) in findById as it
assumes specific ID generation and makes the test brittle. Instead, capture the
ID of the article when it is created or saved, then use that stored ID to
retrieve the article for assertions. Alternatively, use repository queries that
do not rely on fixed IDs to fetch the article.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 아티클 생성 기능 구현

1 participant