Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] 결과 수정 - 상세 페이지 api 연동 #112

Merged
merged 13 commits into from
Feb 8, 2025

Conversation

kongnayeon
Copy link
Member

@kongnayeon kongnayeon commented Feb 8, 2025

관련 이슈

#103

변경 사항

UT 이슈로 아주 급하게 구현을 했습니다,, 코드가 정말 돌아"만" 갑니다... 추후 리팩토링 해야 할 부분이 많은데 UT 끝나고 최대한 빨리 반영할 수 있도록 하겠습니다!

다른 건 dependency 거의 없어서 괜찮을 것 같은데 공통 컴포넌트 변경 사항이 있어 알려드립니다!

  • Accordion 컴포넌트 제어 방식도 지원하도록 수정
  • IconButton isLoading 상태 추가

레퍼런스

Summary by CodeRabbit

  • New Features

    • 외부 이미지 최적화를 통해 더욱 선명한 이미지 제공
    • 포스트 편집 인터페이스에 내비게이션 및 드래그 앤 드롭 기능 추가
    • 로그 뷰 기능 도입으로 콘텐츠 이력 확인 가능
    • 버튼에 로딩 스피너와 클릭 효과 등 강화된 인터랙티브 요소 도입
  • Style

    • 전체 화면 레이아웃 및 유연한 공간 배분 적용
    • 업데이트된 테마 색상(새로운 퍼플 톤) 반영
    • 향상된 호버 및 비활성 상태 스타일 적용

@kongnayeon kongnayeon self-assigned this Feb 8, 2025
Copy link

coderabbitai bot commented Feb 8, 2025

Walkthrough

이번 PR은 Next.js 이미지 도메인 설정 추가, 새로운 의존성(@tanstack/react-query-devtools) 도입, 포스트의 정렬을 위한 displayOrder 속성 추가 등 다양한 변경사항을 포함합니다. 또한 스타일(CSS) 업데이트, 여러 React 컴포넌트(예: EditDetail, EditSidebar, EditPost, DragGuide, Log 관련 컴포넌트)와 커스텀 훅(useAdjacentPosts) 도입, 쿼리 및 mutation 관련 코드 개선, UI 컴포넌트(Accordion, IconButton, Spinner 등) 및 테마 관련 파일 업데이트가 이루어졌습니다.

Changes

파일/그룹 변경 요약
apps/web/next.config.mjs, package.json Next.js 이미지 도메인(instead-dev.s3.ap-northeast-2.amazonaws.com) 추가 및 @tanstack/react-query-devtools 의존성 도입
.../(prompt)/edit/[agentId]/[postGroupId]/constants.ts INITIAL_CONTENT_ITEMS의 각 객체에 displayOrder 속성 추가
.../detail/*.css.ts, DroppableContent.css.ts, IconButton.css.ts, Spinner.css.ts, LogContentItem.css.ts, LogSidebar.css.ts Flex, 높이, 커서 등 다양한 CSS 스타일 업데이트 및 신규 스타일 추가
.../detail/*.tsx, ContentItem.tsx, DragGuide.tsx, Log.tsx, EditPromptField.tsx, EditSidebar.tsx, PostEditor.tsx, UploadedImages.tsx 새로운 컴포넌트 추가 및 기존 컴포넌트에 onClick, isSelected, Context API, drag-and-drop, 폼 핸들링 등의 기능 개선
.../hooks/useAdjacentPosts.tsx, page.tsx, LogPage.tsx, QueryClientProvider.tsx, useGroupPostsQuery.ts, usePostHistoryQuery.ts 서버사이드 토큰, 쿼리 옵션 및 인접 포스트 네비게이션을 위한 커스텀 훅과 페이지 로직 개선
src/store/mutation/, src/store/query/ 여러 mutation, query 훅 및 관련 타입(interface) 추가/수정으로 데이터 캐싱 및 업데이트 로직 강화
packages/ui/**/* (Accordion, Badge, IconButton, Spinner, useCallbackRef, useControllableState) 제네릭, isLoading, Spinner variant 등 UI 컴포넌트 및 유틸리티 함수 개선
packages/theme/src/tokens/colors.ts 색상 팔레트에 purple700 추가

Sequence Diagram(s)

sequenceDiagram
    participant U as 사용자
    participant EP as EditDetailPage
    participant SFB as ServerFetchBoundary
    participant ED as EditDetail
    participant ES as EditSidebar
    participant EPst as EditPost
    participant Q as useGroupPostsQuery / useAdjacentPosts

    U->>EP: 편집 페이지 요청 (agentId, postGroupId)
    EP->>SFB: 서버 토큰 및 쿼리 옵션 요청
    SFB->>ED: EditDetail 컴포넌트 렌더링
    ED->>Q: 포스트 정보 및 인접 포스트 조회
    ED->>ES: 사이드바 렌더링 (드래그 앤 드롭 포함)
    ED->>EPst: 포스트 편집 기능 제공
    U->>EPst: 이전/다음 버튼 클릭 → 포스트 네비게이션
Loading

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • minseong0324

Poem

토끼가 말해요,
코드 속 바람이 솔솔 불어오네,
이미지와 스타일 한가득 채워진 날,
최신 훅과 컴포넌트 춤추네,
PR 속 변화에 함께 뛰노는 날개! 🐰💻
보라빛 빛깔의 꿈을 향해 달려가자!

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/constants.ts

Oops! Something went wrong! :(

ESLint: 9.17.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@repo/eslint-config' imported from /eslint.config.mjs
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/EditDetail.css.ts

Oops! Something went wrong! :(

ESLint: 9.17.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@repo/eslint-config' imported from /eslint.config.mjs
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

apps/web/next.config.mjs

Oops! Something went wrong! :(

ESLint: 9.17.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@repo/eslint-config' imported from /eslint.config.mjs
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

  • 42 others
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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. (Beta)
  • @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.

@kongnayeon kongnayeon marked this pull request as ready for review February 8, 2025 00:15
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: 19

🔭 Outside diff range comments (2)
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/ContentItem/ContentItem.tsx (1)

72-76: 이벤트 핸들러 누락

onRemove, onModify, onDrag props가 정의되어 있지만 IconButton에 연결되지 않았습니다.

다음과 같이 수정하는 것을 추천드립니다:

       <div className={iconHoverStyle}>
-        <IconButton icon="trash" />
-        <IconButton icon="pencil" />
-        <IconButton className={cursorGrabStyle} icon="sixDots" />
+        <IconButton icon="trash" onClick={onRemove} />
+        <IconButton icon="pencil" onClick={onModify} />
+        <IconButton 
+          className={cursorGrabStyle} 
+          icon="sixDots" 
+          onMouseDown={onDrag}
+        />
       </div>
apps/web/src/components/common/DNDController/compounds/ContentItem/ContentItem.tsx (1)

78-80: 이미지 렌더링 로직 개선 필요

이미지가 있을 때 단순히 'x'를 표시하는 것은 적절하지 않습니다.

다음과 같이 이미지를 표시하는 것을 제안합니다:

- <div>x</div>
+ <img
+   src={image}
+   alt="콘텐츠 썸네일"
+   width={32}
+   height={32}
+   style={{ objectFit: 'cover', borderRadius: '4px' }}
+ />
🧹 Nitpick comments (32)
apps/web/src/store/query/usePostHistoryQuery.ts (1)

7-13: 타입 정의 개선이 필요합니다.

다음 사항들을 개선하면 좋을 것 같습니다:

  1. createdAt의 타입이 Date | string으로 정의되어 있어 타입 일관성이 떨어질 수 있습니다.
  2. type 필드의 'EACH'와 'ALL' 값의 의미를 설명하는 문서화가 필요합니다.

다음과 같이 개선해보세요:

+/** Post history entry with type indicating if it applies to single or all posts */
 export interface PostHistoryQuery {
   id: number;
-  createdAt: Date | string;
+  createdAt: string; // ISO 8601 format
   prompt: string;
   response: string;
+  /** Type of history entry:
+   * - EACH: Applies to individual post
+   * - ALL: Applies to all posts in group
+   */
   type: 'EACH' | 'ALL';
 }
apps/web/src/store/mutation/useDeletePostMutation.ts (1)

8-11: EditPageParams와 타입 중복 검토 필요

MutationDeletePost 인터페이스가 EditPageParams와 동일한 구조를 가지고 있습니다. 타입 중복을 피하기 위해 EditPageParams를 재사용하는 것을 고려해보세요.

-export interface MutationDeletePost {
-  agentId: number;
-  postGroupId: number;
-}

+export type MutationDeletePost = EditPageParams;
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/_components/LogSidebar/LogSidebar.css.ts (1)

4-9: 반응형 디자인 개선이 필요합니다

사이드바의 고정 너비(42.3rem)와 높이(100vh)는 작은 화면에서 문제를 일으킬 수 있습니다.

다음과 같은 개선을 제안합니다:

 export const sidebarWrapper = style({
-  width: '42.3rem',
+  width: 'min(42.3rem, 100%)',
   height: '100vh',
   flexShrink: '0',
   backgroundColor: `${tokens.colors.grey0}`,
+  '@media': {
+    'screen and (max-width: 768px)': {
+      width: '100%'
+    }
+  }
 });
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/_components/LogContentItem/LogContentItem.css.ts (2)

4-15: 접근성과 다크 모드 지원이 필요합니다

wrapper 컴포넌트에 다음 개선사항들이 필요합니다:

 export const wrapper = style({
   width: '100%',
-  height: '13.4rem',
+  minHeight: '13.4rem',
   padding: '1.2rem',
   display: 'flex',
   flexDirection: 'column',
   gap: '0.8rem',
   borderRadius: `${tokens.radius[10]}`,
+  cursor: 'pointer',
+  transition: 'background-color 0.2s ease',
   ':hover': {
     backgroundColor: `${tokens.colors.grey25}`,
   },
+  '@media': {
+    '(prefers-color-scheme: dark)': {
+      backgroundColor: `${tokens.colors.grey800}`,
+      ':hover': {
+        backgroundColor: `${tokens.colors.grey700}`,
+      }
+    }
+  }
 });

17-23: 텍스트 접근성 개선이 필요합니다

promptText에 다음 접근성 관련 스타일을 추가하는 것이 좋습니다:

 export const promptText = style({
   width: '100%',
   whiteSpace: 'nowrap',
   overflow: 'hidden',
   textOverflow: 'ellipsis',
   marginRight: '0.8rem',
+  lineHeight: '1.5',
+  '@media': {
+    '(prefers-color-scheme: dark)': {
+      color: `${tokens.colors.grey100}`
+    }
+  }
 });
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/_components/LogSidebar/LogSidebar.tsx (1)

13-19: 접근성 개선이 필요합니다

IconButton에 적절한 aria 레이블을 추가해 주세요:

   return (
     <div className={sidebarWrapper}>
       <div className={closeArea}>
-        <IconButton icon="x" color="grey300" onClick={handleXClick} />
+        <IconButton 
+          icon="x" 
+          color="grey300" 
+          onClick={handleXClick}
+          aria-label="닫기"
+        />
       </div>
     </div>
   );
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/_components/LogContentItem/LogContentItem.tsx (1)

41-48: Text 컴포넌트 스타일 상수화를 제안합니다.

반복되는 Text 컴포넌트의 스타일 속성을 상수로 분리하면 재사용성과 일관성을 높일 수 있습니다.

다음과 같이 스타일을 상수화하는 것을 제안합니다:

+const PROMPT_TEXT_STYLE = {
+  color: 'grey800' as const,
+  fontSize: 16,
+  fontWeight: 'medium' as const,
+};
+
 <Text
   className={promptText}
-  color="grey800"
-  fontSize={16}
-  fontWeight="medium"
+  {...PROMPT_TEXT_STYLE}
 >
packages/ui/src/components/Accordion/AccordionRoot.tsx (1)

17-17: TODO 주석이 남아있습니다.
추후 유지보수를 위해 실제 수정 계획을 구체적으로 남겨둘 것을 권장합니다.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/EditSidebar.tsx (1)

95-102: skeletonData 생성 로직은 간편합니다.
임시 로딩용 데이터라, 실제 API 연동 시 삭제할 계획을 명시해 두면 좋겠습니다.

apps/web/src/components/common/DNDController/compounds/DroppableContent/DroppableContent.css.ts (1)

4-5: 주석을 영문으로 변경해주세요.

국제화 및 협업을 위해 코드 내 주석은 영문으로 작성하는 것을 권장드립니다.

-  // NOTE minHeight를 20rem에서 fit-content로 바꿔도 사이드 이펙트가 없을까요?
-  // 두 페이지에서 모두 사용하려면 fit-content로 가야 할 것 같아서요!
+  // NOTE: Will changing minHeight from 20rem to fit-content have any side effects?
+  // We should use fit-content as this component is used across two pages
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/DragGuide/DragGuide.css.ts (2)

4-15: container 스타일의 변수명을 더 구체적으로 지정하면 좋겠습니다.

현재 container라는 일반적인 이름보다는 dragGuideContainer와 같이 더 구체적인 이름을 사용하면 컴포넌트의 목적을 더 잘 나타낼 수 있습니다.

-export const container = style({
+export const dragGuideContainer = style({

17-20: description 스타일도 더 구체적인 이름으로 변경하면 좋겠습니다.

마찬가지로 descriptiondragGuideDescription으로 변경하여 스타일의 용도를 명확히 하면 좋겠습니다.

-export const description = style({
+export const dragGuideDescription = style({
apps/web/next.config.mjs (1)

17-19: 이미지 도메인을 환경 변수로 관리하면 좋겠습니다.

S3 도메인을 하드코딩하는 대신 환경 변수로 관리하면 환경별로 유연하게 설정할 수 있습니다.

  images: {
-    domains: ['instead-dev.s3.ap-northeast-2.amazonaws.com'],
+    domains: [process.env.NEXT_PUBLIC_S3_DOMAIN].filter(Boolean),
  },
packages/ui/src/components/Accordion/hooks/useCallbackRef.tsx (2)

7-21: 콜백이 undefined일 때의 처리를 개선하면 좋겠습니다.

현재 구현은 콜백이 undefined일 때 조용히 실행을 건너뛰지만, 개발 모드에서는 경고를 표시하는 것이 디버깅에 도움이 될 수 있습니다.

  return React.useMemo(
-    () => ((...args) => callbackRef.current?.(...args)) as T,
+    () => ((...args) => {
+      if (process.env.NODE_ENV === 'development' && !callbackRef.current) {
+        console.warn('useCallbackRef: callback is undefined');
+      }
+      return callbackRef.current?.(...args);
+    }) as T,
    []
  );

3-6: JSDoc 주석에 예제 코드를 추가하면 좋겠습니다.

사용 예시를 포함하면 다른 개발자들이 이 훅을 더 쉽게 이해하고 활용할 수 있습니다.

 /**
  * A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a
  * prop or avoid re-executing effects when passed as a dependency
+ *
+ * @example
+ * ```tsx
+ * const MyComponent = () => {
+ *   const callback = useCallbackRef((value: string) => {
+ *     console.log(value);
+ *   });
+ *   return <Child onChange={callback} />;
+ * };
+ * ```
  */
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/DragGuide/DragGuide.tsx (1)

8-29: 접근성 및 국제화 개선이 필요합니다.

  1. 드래그 앤 드롭 영역에 대한 접근성 속성이 누락되어 있습니다.
  2. "끌어서 여기에 놓아주세요" 텍스트가 하드코딩되어 있어 국제화에 어려움이 있을 수 있습니다.

다음과 같이 개선해보세요:

 export function DragGuide({ description }: DragGuideProps) {
   return (
-    <div className={styles.container}>
+    <div 
+      className={styles.container}
+      role="region"
+      aria-label="드래그 앤 드롭 가이드"
+    >
       <Text.H2
         className={styles.description}
         fontSize={20}
         fontWeight="semibold"
         color="grey500"
       >
         {description}
       </Text.H2>
       <Text.H2
         className={styles.description}
         fontSize={20}
         fontWeight="semibold"
         color="grey500"
       >
-        끌어서 여기에 놓아주세요
+        {t('common.dragAndDropHere')}
       </Text.H2>
     </div>
   );
 }
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/EditSidebar.css.ts (1)

26-29: 스크롤 이벤트 최적화를 고려해주세요.

overflow-y: scroll이 적용된 요소에서 스크롤 이벤트를 사용하는 경우, 성능 최적화가 필요할 수 있습니다.

스크롤 이벤트를 사용하는 경우 다음과 같은 최적화를 고려해보세요:

  • debounce/throttle 적용
  • passive: true 옵션 사용
  • will-change 속성 활용
packages/ui/src/components/IconButton/IconButton.tsx (1)

21-27: 로딩 상태에 따른 조건부 렌더링 구현

로딩 상태일 때는 Spinner를, 아닐 때는 Icon을 표시하도록 구현되었습니다. 하지만 불필요한 Fragment(<>...</>) 사용이 있습니다.

다음과 같이 Fragment를 제거하여 코드를 간소화할 수 있습니다:

  {isLoading ? (
-   <>
      <Spinner color="icon" size="icon" />
-   </>
  ) : (
    <Icon name={icon} type={iconType} />
  )}
packages/ui/src/components/Spinner/Spinner.tsx (1)

9-13: JSDoc 주석 업데이트 필요

colorsize prop에 'icon' 옵션이 추가되었지만, JSDoc 주석에는 이 변경사항이 반영되지 않았습니다.

다음과 같이 JSDoc을 업데이트해주세요:

/**
 * @param {SpinnerProps} props - 스피너 속성
- * @property {color} [color='white'] - 'black' | 'white' 스피너 색상
- * @property {size} [size='small'] - 'small' | 'large' 스피너 크기
+ * @property {color} [color='white'] - 'black' | 'white' | 'icon' 스피너 색상
+ * @property {size} [size='small'] - 'small' | 'large' | 'icon' 스피너 크기
 */
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/ContentItem/ContentItem.css.ts (1)

11-11: 중복된 cursor 스타일 속성이 발견되었습니다.

cursorPointerStyle이 이미 정의되어 있는데 contentItemStyle에 동일한 cursor: 'pointer' 속성이 추가되었습니다. 코드 중복을 피하기 위해 cursorPointerStyle을 재사용하는 것이 좋습니다.

다음과 같이 수정하는 것을 제안합니다:

export const contentItemStyle = style({
  display: 'inline-flex',
  flexDirection: 'row',
  alignItems: 'center',
  gap: '1.6rem',
  height: '6.4rem',
  width: '100%',
  padding: '1.6rem 0.8rem 1.2rem 1.2rem',
-  cursor: 'pointer',
});

// contentItemStyle에 cursorPointerStyle을 조합하여 사용
export const interactiveContentItemStyle = style([contentItemStyle, cursorPointerStyle]);
apps/web/src/store/mutation/useModifyPostMutation.ts (1)

33-35: 성공 메시지를 상수로 분리해 주세요.

성공 메시지가 하드코딩되어 있어 재사용이 어렵고 일관성 유지가 어려울 수 있습니다.

다음과 같이 개선하는 것을 제안합니다:

+ const MESSAGES = {
+   SUCCESS: '저장되었습니다.',
+   ERROR: '포스트 수정 중 오류가 발생했습니다.',
+ } as const;

    onSuccess: () => {
-     toast.success('저장되었습니다.');
+     toast.success(MESSAGES.SUCCESS);
    },
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/EditDetail.tsx (1)

10-10: TODO 주석에 대한 이슈를 생성하시겠습니까?

상태 관리 라이브러리 변경에 대한 TODO 주석이 있습니다. 이를 추적하고 관리하기 위해 이슈로 생성하는 것이 좋습니다.

이 작업을 추적하기 위한 이슈를 생성해드릴까요?

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/PostEditor/UploadedImages.tsx (2)

21-21: 이벤트 처리 주석을 일관성 있게 작성해 주세요.

이벤트 처리에 대한 주석이 한글과 영어가 혼용되어 있습니다. 또한 동일한 목적의 이벤트 처리가 여러 곳에서 반복되고 있습니다.

이벤트 처리 로직을 분리하고 주석을 일관성 있게 작성하는 것을 제안합니다:

+ // 이미지 관련 이벤트 처리를 위한 유틸리티 함수
+ const preventImageEvent = (e: React.MouseEvent) => {
+   e.preventDefault(); // 기본 동작 방지
+   e.stopPropagation(); // 이벤트 버블링 방지
+ };

  <div
    key={image}
    className={styles.imageWrapper}
-   onClick={(e) => e.preventDefault()} // 이미지 클릭 시 삭제 방지
+   onClick={preventImageEvent} // 이미지 클릭 이벤트 방지
  >
    // ...
    onClick={(e) => {
-     e.preventDefault(); // 기본 동작 차단
-     e.stopPropagation(); // 이벤트 전파 차단
+     preventImageEvent(e);
      onRemove(image);
    }}

Also applies to: 34-35


25-25: 이미지 alt 텍스트를 더 의미있게 작성해 주세요.

현재 alt 텍스트가 이미지 URL을 포함하고 있어 스크린 리더 사용자에게 불필요한 정보를 제공할 수 있습니다.

  <Image
    src={image}
-   alt={`업로드된 이미지 ${image}`}
+   alt="업로드된 콘텐츠 이미지"
    width={IMAGE_SIZE}
    height={IMAGE_SIZE}
    className={styles.image}
  />
apps/web/src/store/mutation/usePatchPromptMutation.ts (1)

49-53: 에러 처리를 개선해 주세요.

현재 에러 처리가 너무 일반적입니다. 구체적인 에러 케이스별로 처리하는 것이 좋겠습니다.

-    onError: (error) => {
-      if (error instanceof Error) {
-        toast.error(error.message);
-      }
-    },
+    onError: (error) => {
+      if (error instanceof Error) {
+        switch(error.name) {
+          case 'NetworkError':
+            toast.error('네트워크 연결을 확인해주세요.');
+            break;
+          case 'ValidationError':
+            toast.error('입력값을 확인해주세요.');
+            break;
+          default:
+            toast.error('오류가 발생했습니다. 다시 시도해주세요.');
+        }
+      }
+    },
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/constants.ts (1)

4-4: 임시 데이터임을 명확히 표시해 주세요.

"MEMO: 임시 더미" 주석은 좋지만, TODO 주석을 추가하여 실제 데이터로 교체가 필요함을 명시하는 것이 좋겠습니다.

-//MEMO: 임시 더미
+//MEMO: 임시 더미
+//TODO: 실제 데이터로 교체 필요
packages/ui/src/components/Accordion/hooks/useControllableState.tsx (2)

4-8: 타입 정의에 JSDoc 문서 추가 필요

타입 정의의 각 속성에 대한 설명이 없어 사용자가 이해하기 어려울 수 있습니다.

다음과 같이 JSDoc을 추가하는 것을 추천드립니다:

 type UseControllableStateParams<T> = {
+  /** 제어된 상태일 때 사용되는 값 */
   prop?: T | undefined;
+  /** 제어되지 않은 상태일 때의 초기값 */
   defaultProp?: T | undefined;
+  /** 상태가 변경될 때 호출되는 콜백 함수 */
   onChange?: (state: T) => void;
 };

27-38: setValue 함수에 타입 안전성 강화 필요

현재 구현에서는 nextValueundefined일 때의 처리가 명시적이지 않습니다.

다음과 같이 타입 가드를 추가하는 것을 추천드립니다:

       (nextValue) => {
+        if (nextValue === undefined) {
+          console.warn('setValue was called with undefined value');
+          return;
+        }
         if (isControlled) {
           const setter = nextValue as SetStateFn<T>;
apps/web/src/types/post.ts (1)

18-23: 상수 값에 대한 설명 주석 필요

각 상수 값의 의미와 사용 목적을 이해하기 어렵습니다.

다음과 같이 각 값에 대한 설명을 추가하는 것을 추천드립니다:

 export const POST_PURPOSE = {
+  /** 정보 제공 목적의 포스트 */
   INFORMATION: 'INFORMATION',
+  /** 의견 제시 목적의 포스트 */
   OPINION: 'OPINION',
+  /** 유머러스한 내용의 포스트 */
   HUMOR: 'HUMOR',
+  /** 마케팅 목적의 포스트 */
   MARKETING: 'MARKETING',
 } as const;
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/ContentItem/ContentItem.tsx (1)

26-35: JSDoc 주석 업데이트 필요

컴포넌트의 JSDoc 주석이 최신 props와 일치하지 않습니다.

다음과 같이 업데이트하는 것을 추천드립니다:

 /**
  * ContentItem 컴포넌트의 Props 타입
  *
- * @property {string} [image] - 이미지 URL. 제공되지 않으면 기본 아이콘이 표시됩니다.
+ * @property {PostImage[]} [image] - 이미지 배열. 제공되지 않으면 기본 아이콘이 표시됩니다.
  * @property {string} [title] - 표시할 제목 텍스트.
  * @property {string} updatedAt - ISO 8601 형식의 날짜 문자열로, 마지막 업데이트 시간을 나타냅니다.
+ * @property {() => void} onClick - 아이템 클릭 시 호출되는 콜백 함수.
  * @property {() => void} onRemove - 삭제 아이콘 클릭 시 호출되는 콜백 함수.
  * @property {() => void} onModify - 수정 아이콘 클릭 시 호출되는 콜백 함수.
  * @property {() => void} onDrag - 드래그 아이콘 마우스 다운 시 호출되는 콜백 함수.
+ * @property {boolean} [isSelected] - 선택 상태 여부
  */
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPost/EditPost.tsx (1)

29-39: 네비게이션 버튼의 접근성 개선 필요

네비게이션 버튼에 적절한 aria-label이 누락되어 있어 스크린 리더 사용자의 접근성이 저하될 수 있습니다.

다음과 같이 aria-label을 추가하는 것을 제안합니다:

  <IconButton
    icon="arrowLineTop"
    disabled={!canMoveUp}
    onClick={routePreviousPost}
+   aria-label="이전 포스트로 이동"
  />
  <IconButton
    icon="arrowLineBottom"
    disabled={!canMoveDown}
    onClick={routeNextPost}
+   aria-label="다음 포스트로 이동"
  />
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPromptField/EditPromptField.tsx (1)

44-62: useEffect 의존성 배열 최적화 필요

isPending 상태 변경에 따른 로딩 상태 관리 로직이 불필요하게 자주 실행될 수 있습니다.

다음과 같이 의존성 배열을 최적화하는 것을 제안합니다:

  useEffect(() => {
    if (isPending) {
      if (isEntire) {
        setLoadingPosts(editingPosts);
      } else {
        setLoadingPosts([Number(postId)]);
      }
    } else {
      if (isEntire) {
        setLoadingPosts((prev) =>
          prev.filter((id) => !editingPosts.includes(id))
        );
      } else {
        setLoadingPosts((prev) => prev.filter((id) => id !== Number(postId)));
      }
    }
-  }, [isPending]);
+  }, [isPending, isEntire, editingPosts, postId]);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e0ba717 and e870651.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (46)
  • apps/web/next.config.mjs (1 hunks)
  • apps/web/package.json (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/constants.ts (5 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/EditDetail.css.ts (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/EditDetail.tsx (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/ContentItem/ContentItem.css.ts (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/ContentItem/ContentItem.tsx (3 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/DragGuide/DragGuide.css.ts (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/DragGuide/DragGuide.tsx (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPost/EditPost.tsx (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPromptField/EditPromptField.tsx (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/EditSidebar.css.ts (3 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/EditSidebar.tsx (2 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/PostEditor/PostEditor.tsx (6 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/PostEditor/UploadedImages.tsx (2 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_hooks/useAdjacentPosts.tsx (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/page.css.ts (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/page.tsx (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/Log.tsx (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/_components/LogContentItem/LogContentItem.css.ts (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/_components/LogContentItem/LogContentItem.tsx (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/_components/LogSidebar/LogSidebar.css.ts (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/_components/LogSidebar/LogSidebar.tsx (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/page.tsx (1 hunks)
  • apps/web/src/app/page.tsx (1 hunks)
  • apps/web/src/components/common/DNDController/compounds/ContentItem/ContentItem.tsx (3 hunks)
  • apps/web/src/components/common/DNDController/compounds/DroppableContent/DroppableContent.css.ts (1 hunks)
  • apps/web/src/components/common/DNDController/hooks/useDragAndDrop.ts (1 hunks)
  • apps/web/src/components/providers/Providers.tsx (2 hunks)
  • apps/web/src/store/mutation/useDeletePostMutation.ts (3 hunks)
  • apps/web/src/store/mutation/useModifyPostMutation.ts (1 hunks)
  • apps/web/src/store/mutation/useModifyPostsMutation.ts (1 hunks)
  • apps/web/src/store/mutation/usePatchPromptMutation.ts (1 hunks)
  • apps/web/src/store/query/QueryClientProvider.tsx (1 hunks)
  • apps/web/src/store/query/useGroupPostsQuery.ts (1 hunks)
  • apps/web/src/store/query/usePostHistoryQuery.ts (1 hunks)
  • apps/web/src/types/post.ts (3 hunks)
  • packages/theme/src/tokens/colors.ts (1 hunks)
  • packages/ui/src/components/Accordion/AccordionRoot.tsx (1 hunks)
  • packages/ui/src/components/Accordion/hooks/useCallbackRef.tsx (1 hunks)
  • packages/ui/src/components/Accordion/hooks/useControllableState.tsx (1 hunks)
  • packages/ui/src/components/Badge/index.ts (1 hunks)
  • packages/ui/src/components/IconButton/IconButton.css.ts (1 hunks)
  • packages/ui/src/components/IconButton/IconButton.tsx (1 hunks)
  • packages/ui/src/components/Spinner/Spinner.css.ts (2 hunks)
  • packages/ui/src/components/Spinner/Spinner.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/components/common/DNDController/hooks/useDragAndDrop.ts
🔇 Additional comments (52)
apps/web/src/store/query/usePostHistoryQuery.ts (1)

22-23: TODO 주석을 해결해주세요.

코드에 TODO FIXME 주석이 남아있습니다. 이 주석이 무엇을 의미하는지 명확히 하고 해결해주세요.

이 TODO 주석과 관련된 작업을 도와드릴까요?

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/page.tsx (2)

22-24: 컴포넌트 구조가 잘 구성되어 있습니다.

ServerFetchBoundary를 사용하여 서버 사이드 데이터 페칭을 적절히 처리하고 있습니다.


15-20: 🛠️ Refactor suggestion

문자열 매개변수의 숫자 변환 검증이 필요합니다.

매개변수를 Number()로 변환할 때 잘못된 문자열이 입력되면 NaN이 될 수 있습니다. 이를 방지하기 위한 유효성 검사가 필요합니다.

다음과 같이 유효성 검사를 추가하는 것을 제안합니다:

+  const agentId = Number(params.agentId);
+  const postGroupId = Number(params.postGroupId);
+  const postId = Number(params.postId);
+
+  if (isNaN(agentId) || isNaN(postGroupId) || isNaN(postId)) {
+    throw new Error('Invalid ID parameters');
+  }
+
   const serverFetchOptions = PostHistoryQueryQueryOptions(
-    Number(params.agentId),
-    Number(params.postGroupId),
-    Number(params.postId),
+    agentId,
+    postGroupId,
+    postId,
     tokens
   );
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/log/[postId]/_components/LogContentItem/LogContentItem.tsx (1)

50-50: TODO 주석을 해결해주세요.

디자인 시안과의 일치 여부를 확인하고 TODO 주석을 제거해주세요.

디자인 시안과 비교하여 필요한 스타일 조정을 진행해주시기 바랍니다.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/page.tsx (5)

1-4: Imports가 적절히 구성되어 있습니다.
이전 컴포넌트를 제거하고 필요한 의존성만 가져오는 부분이 가독성을 높여줍니다.


6-8: 타입 선언이 명확합니다.
params 객체 안에 agentId, postGroupId를 문자열로 받아 사용하는 구조가 직관적입니다. 다만, Number 변환 시 예외 처리를 고려해볼 수도 있습니다.


10-10: 컴포넌트 시그니처가 확장되었습니다.
이제 EditDetailPage가 props를 통해 URL params를 전달받아 처리하므로, 유지보수성이 향상되었습니다.


11-16: 토큰과 쿼리 옵션 처리 시 예외 처리를 고려해보세요.
getServerSideTokens() 함수가 실패하거나 Number 변환이 올바르게 되지 않을 때를 위한 방어 로직이 있으면 안정적입니다.


18-20: ServerFetchBoundary 구성이 적절합니다.
EditDetail 컴포넌트를 서버 페치 범위 내에서 안전하게 감싸 데이터 로딩과 에러 처리를 명확히 분리할 수 있습니다.

packages/ui/src/components/Accordion/AccordionRoot.tsx (7)

3-10: import 문과 useControllableState 의존성 추가가 적절해 보입니다.
React 관련 훅과 별도 훅을 잘 가져와 코드 가독성이 좋습니다.

Also applies to: 13-13


18-38: AccordionProps 제네릭 설계가 훌륭합니다.
단일 모드와 다중 모드를 구분하여 타입 안전성을 높인 점이 매우 인상적입니다.


40-59: AccordionRootInner 함수 시그니처가 명확합니다.
type에 따른 defaultValue 처리 로직이 직관적이며, 제네릭을 통해 다양한 케이스를 지원할 수 있습니다.


62-76: 기본 openValues 계산 로직이 명료합니다.
단일/다중 모드를 모두 고려한 처리로 예상치 못한 에러(예: 배열 변환)가 줄어들 것으로 보입니다.


78-101: useControllableState 훅 사용으로 제어/비제어 상태를 일관성 있게 관리합니다.
단일/다중 모드별 onValueChange를 구분 처리하는 방식이 합리적입니다.


102-130: 열림 상태(normalizedOpenValues), toggleValue, isValueOpen 로직이 잘 분리되어 있습니다.
단일 모드에서 중복으로 열리는 상황을 방지하는 처리가 안정적입니다.


132-139: AccordionContext.Provider 및 forwardRef 사용이 적절합니다.
Context를 통해 상태를 공유하고, 제네릭 사용자 정의로 확장 가능성을 높여주어 재사용성이 뛰어납니다.

Also applies to: 142-148, 151-159

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/EditSidebar.tsx (18)

2-2: 새로운 의존성과 공용 컴포넌트 import 구성이 좋아 보입니다.
Drag & Drop, Accordion 기능 등 필요한 모듈을 깔끔하게 정리했습니다.

Also applies to: 5-5, 13-16


20-21: useGroupPostsQuery, Post, POST_STATUS import는 관련 기능을 명확히 보여줍니다.
코드 가독성과 유지보수에 도움이 됩니다.

Also applies to: 22-22, 23-25


28-29: useCreateMorePostsMutation, useDeletePostMutation, DetailPageContext 사용이 자연스럽습니다.
Context를 통해 상위 컴포넌트 상태를 공유하는 방식이 일관성 있어 보입니다.

Also applies to: 32-34


36-46: EditSidebarContent 컴포넌트의 초기 상태 설정이 직관적입니다.
loadingPosts, agentId, postGroupId 등 필요한 요소들을 잘 관리하고 있습니다.


48-52: useCreateMorePostsMutation를 통한 게시글 추가 로직이 명확합니다.
param 주입 방식에 대한 TODO 주석이 있으므로, 적절한 시점에 수정이 필요해 보입니다.


54-57: useDeletePostMutation 설정이 적절합니다.
파라미터(agentId, postGroupId)를 명확히 지정해 특별한 문제는 없어 보입니다.


59-66: Accordion 기본값 처리 방식이 훌륭합니다.
URL 파라미터(postParam)로부터 상태를 결정하는 로직이 사용자 경험을 개선합니다.

Also applies to: 67-69


71-73: handleClick 함수가 라우팅을 단순화합니다.
URL 쿼리로 postId를 넘기는 직관적인 접근입니다.


75-78: handlePlusClick에서 Accordion 상태를 'EDITING'으로 전환하는 로직이 좋습니다.
새로운 글 추가 직후 편집 모드로 전환되어 사용자 경험이 매끄럽습니다.


80-93: handleDeletePost에서 모달 로직이 안정적입니다.
확인 모달로 사용자의 실수 방지를 돕는 점이 우수합니다.


105-105: Breadcrumb와 제목 표기가 직관적입니다.
TODO 주석대로 중복 로직을 컴포넌트화하면 재사용성이 더욱 높아질 것으로 보입니다.

Also applies to: 106-106, 108-108, 114-114


122-127: Accordion 컴포넌트로 펼침 상태를 단일 모드로 제어하는 구조가 좋습니다.
onValueChange 훅을 통해 동적으로 상태를 업데이트해주는 방식이 깔끔합니다.


128-178: 생성된 글(GENERATED) 영역의 드래그 앤 드롭 로직이 잘 구현되었습니다.
DndController.SortableList로 UI 동작을 직관적으로 설계하고 스켈레톤 처리로 로딩 상태까지 고려했습니다.


179-218: 수정 중인 글(EDITING) 영역도 동일한 패턴으로 구현되었습니다.
드래그 & 드롭 일관성이 유지되고, handleDeletePost나 handleClick을 재사용할 수 있어 효율적입니다.


219-261: 업로드할 글(READY_TO_UPLOAD) 영역도 재사용 패턴을 따르고 있습니다.
DragGuide를 통해 빈 상태에 대한 안내가 한눈에 들어옵니다.


267-272: EditSidebar에서 useModifyPostsMutation 설정이 깔끔합니다.
agentId와 postGroupId 모두 숫자로 변환해 호출 충돌 가능성을 줄였습니다.


274-277: 게시글 정렬 로직이 displayOrder를 기준으로 명확하게 동작합니다.
기존 배열을 직접 정렬하므로, 불변성 유지가 필요한 경우에는 주의가 필요할 수 있습니다.


279-319: DndController 상위 래핑으로 정렬 상태를 중앙에서 관리합니다.
onDragEnd에서 displayOrder를 일괄 수정하고, 서버 반영을 위해 modifyPosts를 호출하는 접근이 자연스럽습니다.

Also applies to: 320-323

packages/ui/src/components/Badge/index.ts (1)

1-2: 타입 내보내기가 적절히 확장되었습니다.

Badge 컴포넌트의 타입 시스템이 BadgeVariant를 포함하도록 확장되어 타입 안정성이 향상되었습니다.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/page.css.ts (1)

3-7: 레이아웃 동작 검증이 필요합니다.

height를 100vh로 설정하고 flexShrink: 0을 추가한 변경사항이 다음 시나리오에서 의도한 대로 동작하는지 확인해주세요:

  • 콘텐츠가 뷰포트 높이를 초과하는 경우
  • 모바일 브라우저에서 주소 표시줄이 사라지는 경우
  • 키보드가 올라오는 경우
apps/web/src/components/common/DNDController/compounds/DroppableContent/DroppableContent.css.ts (1)

4-7: minHeight 변경에 대한 검증이 필요합니다.

fit-content로의 변경이 적절해 보이나, 다음 사항들의 검증이 필요합니다:

  • 드래그 앤 드롭 영역이 너무 작아지지 않는지
  • 빈 상태일 때의 시각적 표현이 적절한지
  • 두 페이지 모두에서 일관된 사용자 경험을 제공하는지
apps/web/src/store/query/QueryClientProvider.tsx (1)

7-13: queryClient를 싱글톤으로 분리한 것이 좋습니다!

queryClient를 상수로 분리하여 싱글톤 패턴을 적용한 것은 애플리케이션 전반에 걸쳐 일관된 상태 관리를 가능하게 하는 좋은 개선입니다.

apps/web/src/components/providers/Providers.tsx (1)

5-5: React Query Devtools가 올바르게 추가되었습니다.

개발 도구가 기본적으로 닫힌 상태로 시작되도록 설정되어 있어 좋습니다.

프로덕션 빌드에서 devtools가 제외되는지 확인하시기 바랍니다. 다음 스크립트로 확인할 수 있습니다:

Also applies to: 19-19

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/DragGuide/DragGuide.tsx (1)

4-6: Props 인터페이스가 잘 정의되어 있습니다.

타입 정의가 명확하고 간단합니다.

packages/ui/src/components/IconButton/IconButton.css.ts (1)

21-25: TODO 주석을 해결해주세요.

디자이너와 색상에 대한 논의가 필요하다는 TODO 주석이 있습니다. PR 병합 전에 이 부분을 해결하는 것이 좋겠습니다.

디자이너와 논의 후 적절한 색상값으로 업데이트해주세요.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/EditSidebar.css.ts (1)

31-34: 레이아웃이 잘 구성되어 있습니다.

Flexbox를 활용한 레이아웃 구성이 적절합니다.

packages/ui/src/components/IconButton/IconButton.tsx (1)

4-4: 로딩 상태 표시를 위한 타입 및 의존성 추가

IconButton 컴포넌트에 로딩 상태를 표시하기 위한 isLoading prop과 Spinner 컴포넌트가 추가되었습니다.

Also applies to: 12-12

apps/web/src/store/query/useGroupPostsQuery.ts (1)

26-27: 캐시 설정 검토 필요

staleTimegcTimeInfinity로 설정하면 데이터가 절대 만료되지 않습니다. 이는 메모리 누수나 오래된 데이터 문제를 일으킬 수 있습니다.

실시간성이 필요하지 않은 데이터인지 확인이 필요합니다. 적절한 시간 값(예: 5분 또는 1시간)을 설정하는 것을 고려해보세요.

apps/web/src/store/mutation/useModifyPostsMutation.ts (2)

30-32: TODO 주석 해결 필요

성공 시 토스트 알림 표시 여부에 대한 결정이 필요합니다.

사용자 경험(UX) 관점에서 성공 알림이 필요한지 검토해주세요. 일반적으로 성공적인 저장 작업 후에는 피드백을 제공하는 것이 좋습니다.


33-37: 에러 처리가 잘 구현되었습니다

Error 인스턴스 체크를 통한 타입 안전성 확보와 사용자 친화적인 에러 메시지 표시가 잘 구현되었습니다.

packages/theme/src/tokens/colors.ts (1)

29-29: 색상 토큰이 적절하게 추가되었습니다!

purple 색상 스케일에 맞게 적절한 값이 추가되었습니다.

apps/web/src/store/mutation/usePatchPromptMutation.ts (1)

46-47: TODO 주석을 해결해 주세요.

성공 메시지가 임시로 하드코딩되어 있습니다. 메시지를 상수로 분리하거나 i18n 시스템을 사용하는 것이 좋겠습니다.

packages/ui/src/components/Spinner/Spinner.css.ts (1)

39-42: 스피너 컴포넌트에 아이콘 변형이 잘 추가되었습니다!

크기와 색상이 디자인 시스템의 토큰을 잘 활용하고 있습니다.

Also applies to: 80-82

apps/web/src/types/post.ts (1)

90-90: newsCategory 타입 변경에 대한 마이그레이션 계획 필요

newsCategory 속성이 필수에서 선택적으로 변경되었습니다. 기존 코드에서 이 필드를 필수로 사용하는 부분이 있을 수 있습니다.

다음 스크립트로 newsCategory 사용 현황을 확인해주세요:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# newsCategory 필드 사용 현황 확인
rg "newsCategory" -A 3 -B 3

Length of output: 2364


newsCategory 필드 마이그레이션 검토 결과

  • 앱 생성 관련 코드(apps/web/src/app/create/)에서는 newsCategory 필드를 조건부로 할당하는 등, 이미 선택적(optional) 사용에 맞춰 로직이 수정되었습니다.
  • types 정의(apps/web/src/app/create/types.ts, apps/web/src/types/post.ts)에서도 해당 필드가 선택적으로 선언되어 있어 프론트엔드 코드 전반에서는 문제 없이 변경이 반영된 것으로 보입니다.
  • 다만 백엔드 또는 다른 서비스에서 해당 필드를 무조건 기대하는 부분이 있다면 별도의 마이그레이션 검토가 필요할 수 있습니다.
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/PostEditor/PostEditor.tsx (1)

161-167: 주석 처리된 드래그 앤 드롭 기능 정리 필요

주석 처리된 드래그 앤 드롭 기능 코드가 남아있습니다. 해당 기능의 구현 여부를 명확히 결정해야 합니다.

드래그 앤 드롭 기능이 필요한지 확인이 필요합니다. 필요하다면 구현을 완료하고, 불필요하다면 관련 코드를 제거해주세요.

apps/web/src/app/page.tsx (1)

536-549: 변경사항이 적절해 보입니다!

multiple 타입의 Accordion에서 defaultValue를 제거한 것은 적절한 변경입니다. multiple 선택 모드에서는 초기 선택값이 필요하지 않기 때문입니다.

Comment on lines +31 to +32
staleTime: Infinity,
gcTime: Infinity,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

캐시 설정을 재검토해주세요.

staleTimegcTimeInfinity로 설정하면 메모리 문제가 발생할 수 있습니다. 적절한 시간 제한을 설정하는 것이 좋습니다.

다음과 같이 수정하는 것을 고려해보세요:

-    staleTime: Infinity,
-    gcTime: Infinity,
+    staleTime: 5 * 60 * 1000, // 5분
+    gcTime: 30 * 60 * 1000,   // 30분
📝 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
staleTime: Infinity,
gcTime: Infinity,
staleTime: 5 * 60 * 1000, // 5분
gcTime: 30 * 60 * 1000, // 30분

Comment on lines +36 to +44
export function useGroupPostsQuery(
agentId: number,
postGroupId: number,
postId: number
) {
return useSuspenseQuery(
PostHistoryQueryQueryOptions(agentId, postGroupId, postId)
);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

함수 이름과 에러 처리를 개선해주세요.

  1. 함수 이름 useGroupPostsQuery가 실제 기능(포스트 히스토리 조회)과 일치하지 않습니다.
  2. 에러 처리가 누락되어 있습니다.

다음과 같이 개선하는 것을 추천드립니다:

-export function useGroupPostsQuery(
+export function usePostHistoryQuery(
   agentId: number,
   postGroupId: number,
   postId: number
 ) {
   return useSuspenseQuery(
-    PostHistoryQueryQueryOptions(agentId, postGroupId, postId)
+    PostHistoryQueryQueryOptions(agentId, postGroupId, postId),
+    {
+      retry: 3,
+      onError: (error) => {
+        console.error('Failed to fetch post history:', error);
+        // 에러 처리 로직 추가
+      }
+    }
   );
 }
📝 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
export function useGroupPostsQuery(
agentId: number,
postGroupId: number,
postId: number
) {
return useSuspenseQuery(
PostHistoryQueryQueryOptions(agentId, postGroupId, postId)
);
}
export function usePostHistoryQuery(
agentId: number,
postGroupId: number,
postId: number
) {
return useSuspenseQuery(
PostHistoryQueryQueryOptions(agentId, postGroupId, postId),
{
retry: 3,
onError: (error) => {
console.error('Failed to fetch post history:', error);
// 에러 처리 로직 추가
}
}
);
}

Comment on lines +36 to +39
// TODO 위 로직과 통일 예정
queryClient.invalidateQueries({
queryKey: ['postGroup', 'Agents'],
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

쿼리 무효화 로직 통합 필요

현재 두 개의 invalidateQueries 호출이 있으며, TODO 주석에서 언급된 대로 이 로직들을 통합하는 것이 좋습니다. 리팩토링 시 다음 사항들을 고려해보세요:

  1. 관련된 모든 쿼리를 한 번에 무효화
  2. 쿼리 키 구조의 일관성 유지
  3. 불필요한 리렌더링 방지
onSuccess: () => {
  toast.success('게시글이 삭제되었어요.');
- queryClient.invalidateQueries(
-   getAllPostsQueryOptions({ agentId, postGroupId })
- );
- // TODO 위 로직과 통일 예정
- queryClient.invalidateQueries({
-   queryKey: ['postGroup', 'Agents'],
- });
+ // 관련된 모든 쿼리 한 번에 무효화
+ queryClient.invalidateQueries({
+   queryKey: [
+     {
+       entity: 'posts',
+       agentId,
+       postGroupId,
+     },
+   ],
+ });
},
📝 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
// TODO 위 로직과 통일 예정
queryClient.invalidateQueries({
queryKey: ['postGroup', 'Agents'],
});
onSuccess: () => {
toast.success('게시글이 삭제되었어요.');
// 관련된 모든 쿼리 한 번에 무효화
queryClient.invalidateQueries({
queryKey: [
{
entity: 'posts',
agentId,
postGroupId,
},
],
});
},

Comment on lines +1 to +3
export function Log() {
return <div>dd</div>;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

구현이 필요한 컴포넌트입니다

현재 컴포넌트가 단순히 "dd" 텍스트만 반환하고 있습니다. 로그 기능에 대한 실제 구현이 필요합니다.

다음 사항들을 고려해 주세요:

  • 컴포넌트 Props 타입 정의
  • 로그 데이터 표시 로직 구현
  • 에러 상태 처리
  • 로딩 상태 처리

Comment on lines +5 to +13
export function LogSidebar() {
const router = useRouter();
const { agentId, postGroupId } = useParams();
const searchParams = useSearchParams();
const postId = searchParams.get('post');
const handleXClick = () => {
router.push(`/edit/${agentId}/${postGroupId}/detail?post=${postId}`);
};
return (
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 처리와 로딩 상태 관리가 필요합니다

라우터 파라미터 처리에 있어 다음 개선사항들이 필요합니다:

 export function LogSidebar() {
   const router = useRouter();
   const { agentId, postGroupId } = useParams();
   const searchParams = useSearchParams();
   const postId = searchParams.get('post');
+  
+  if (!agentId || !postGroupId || !postId) {
+    return <div>잘못된 접근입니다</div>;
+  }
+
   const handleXClick = () => {
+    try {
       router.push(`/edit/${agentId}/${postGroupId}/detail?post=${postId}`);
+    } catch (error) {
+      console.error('Navigation failed:', error);
+      // 에러 처리 로직 추가 필요
+    }
   };
📝 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
export function LogSidebar() {
const router = useRouter();
const { agentId, postGroupId } = useParams();
const searchParams = useSearchParams();
const postId = searchParams.get('post');
const handleXClick = () => {
router.push(`/edit/${agentId}/${postGroupId}/detail?post=${postId}`);
};
return (
export function LogSidebar() {
const router = useRouter();
const { agentId, postGroupId } = useParams();
const searchParams = useSearchParams();
const postId = searchParams.get('post');
if (!agentId || !postGroupId || !postId) {
return <div>잘못된 접근입니다</div>;
}
const handleXClick = () => {
try {
router.push(`/edit/${agentId}/${postGroupId}/detail?post=${postId}`);
} catch (error) {
console.error('Navigation failed:', error);
// 에러 처리 로직 추가 필요
}
};
return (
// ... rest of the component JSX
);
}

render={({ field }) => (
<Checkbox
label="수정 중인 글을 모두 업그레이드하기"
defaultChecked
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

체크박스 defaultChecked 속성 제거 필요

defaultChecked 속성과 checked 속성이 동시에 사용되고 있어 React 컴포넌트의 제어 상태가 모호해질 수 있습니다.

다음과 같이 defaultChecked 속성을 제거하는 것을 제안합니다:

  <Checkbox
    label="수정 중인 글을 모두 업그레이드하기"
-   defaultChecked
    checked={field.value}
    onChange={field.onChange}
  />

@@ -63,6 +64,7 @@ export const ContentItem = forwardRef<HTMLDivElement, ContentItemProps>(
dragListeners,
className,
isLoading,
isSelected = 'false',
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

타입 오류 수정 필요

isSelected의 기본값이 문자열로 설정되어 있으나, 타입은 boolean으로 정의되어 있습니다.

다음과 같이 수정하는 것을 제안합니다:

- isSelected = 'false',
+ isSelected = false,
📝 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
isSelected = 'false',
isSelected = false,

Comment on lines +134 to 139
const handleFiles = async (files: FileList) => {
// 최대 4개 파일만 처리 (필요에 따라 maxFiles 값을 조정하세요)
const fileArray = Array.from(files).slice(0, 4);
const urls = fileArray.map((file) => URL.createObjectURL(file));
setImages(urls);
const uploadedUrls = await uploadImages(fileArray);
setValue('imageUrls', uploadedUrls);
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

이미지 업로드 에러 처리 필요

이미지 업로드 실패 시에 대한 에러 처리가 누락되어 있습니다.

다음과 같이 에러 처리를 추가하는 것을 제안합니다:

  const handleFiles = async (files: FileList) => {
    const fileArray = Array.from(files).slice(0, 4);
-   const uploadedUrls = await uploadImages(fileArray);
-   setValue('imageUrls', uploadedUrls);
+   try {
+     const uploadedUrls = await uploadImages(fileArray);
+     setValue('imageUrls', uploadedUrls);
+   } catch (error) {
+     console.error('이미지 업로드 실패:', error);
+     // TODO: 사용자에게 에러 메시지 표시
+   }
  };
📝 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
const handleFiles = async (files: FileList) => {
// 최대 4개 파일만 처리 (필요에 따라 maxFiles 값을 조정하세요)
const fileArray = Array.from(files).slice(0, 4);
const urls = fileArray.map((file) => URL.createObjectURL(file));
setImages(urls);
const uploadedUrls = await uploadImages(fileArray);
setValue('imageUrls', uploadedUrls);
};
const handleFiles = async (files: FileList) => {
// 최대 4개 파일만 처리 (필요에 따라 maxFiles 값을 조정하세요)
const fileArray = Array.from(files).slice(0, 4);
try {
const uploadedUrls = await uploadImages(fileArray);
setValue('imageUrls', uploadedUrls);
} catch (error) {
console.error('이미지 업로드 실패:', error);
// TODO: 사용자에게 에러 메시지 표시
}
};

Comment on lines +70 to +72
useEffect(() => {
console.log('imageUrl', watch('imageUrls'));
}, [watch('imageUrls')]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

불필요한 콘솔 로그 제거 필요

개발 디버깅용 콘솔 로그가 프로덕션 코드에 남아있습니다.

다음 코드를 제거해주세요:

- useEffect(() => {
-   console.log('imageUrl', watch('imageUrls'));
- }, [watch('imageUrls')]);
📝 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
useEffect(() => {
console.log('imageUrl', watch('imageUrls'));
}, [watch('imageUrls')]);

@@ -17,6 +17,7 @@
"@repo/theme": "workspace:^",
"@repo/ui": "workspace:^",
"@tanstack/react-query": "^5.66.0",
"@tanstack/react-query-devtools": "^5.66.0",
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

devDependencies로 이동을 제안합니다.

@tanstack/react-query-devtools는 개발 도구이므로 dependencies가 아닌 devDependencies에 위치하는 것이 더 적절합니다. 이렇게 하면 프로덕션 빌드 크기를 줄일 수 있습니다.

다음과 같이 수정하는 것을 제안합니다:

-    "@tanstack/react-query-devtools": "^5.66.0",

devDependencies 섹션에 추가:

+    "@tanstack/react-query-devtools": "^5.66.0",

@kongnayeon kongnayeon merged commit ab88781 into develop Feb 8, 2025
3 of 22 checks passed
@kongnayeon kongnayeon deleted the feat/#0fab5af59 branch February 8, 2025 02:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant