Skip to content

Feat(extension): API 경로 수정 및 코드 정리#281

Merged
jjangminii merged 2 commits intodevelopfrom
feat/#280/extension-api-update
Feb 26, 2026
Merged

Feat(extension): API 경로 수정 및 코드 정리#281
jjangminii merged 2 commits intodevelopfrom
feat/#280/extension-api-update

Conversation

@jjangminii
Copy link
Collaborator

@jjangminii jjangminii commented Feb 26, 2026

📌 Related Issues

관련된 Issue를 태그해주세요. (e.g. - close #25)

📄 Tasks

  • 아티클 저장 api 버전 업데이트
  • 문구 변경

⭐ PR Point (To Reviewer)

  • 아티클 저장 api 저번 버전과 다른거 없고 그냥 버전만 업데이트해주면 된다고합니다

📷 Screenshot

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 카테고리 추가 및 관리 팝업 인터페이스 추가
    • 문서 저장 시 폼 유효성 검사 및 실시간 오류 메시지 표시
    • 저장 및 편집 작업 완료 시 토스트 알림 피드백 추가
    • 날짜/시간 선택 검증 기능 개선

@jjangminii jjangminii linked an issue Feb 26, 2026 that may be closed by this pull request
@vercel
Copy link

vercel bot commented Feb 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pinback-client-client Ready Ready Preview, Comment Feb 26, 2026 7:47am
pinback-client-landing Ready Ready Preview, Comment Feb 26, 2026 7:47am

@github-actions github-actions bot added the feat 기능 개발하라 개발 달려라 달려 label Feb 26, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

요약

API 엔드포인트를 v1에서 v3로 업데이트하고, MainPop 컴포넌트에 API 쿼리 훅을 통합하여 카테고리 관리, 아티클 저장/편집 기능을 구현했습니다.

변경 사항

Cohort / File(s) 요약
API 엔드포인트 및 타입 업데이트
apps/extension/src/apis/axios.ts
postArticle 엔드포인트를 /api/v1/articles에서 /api/v3/articles로 변경. postCategoriesRequest 인터페이스에 categoryName: string 필드 추가. 포맷팅 및 간격 조정.
MainPop 컴포넌트 기능 확장
apps/extension/src/pages/MainPop.tsx
API 쿼리 훅(useGetCategoriesExtension, useGetRemindTime, usePostArticle, usePutArticle) 통합. 카테고리 관리 팝업, 아티클 저장/편집 흐름 구현. 클라이언트 검증(날짜/시간/카테고리), 토스트 피드백, 새로운 UI 컴포넌트(AutoDismissToast, Textarea, InfoBox 등) 추가.

시퀀스 다이어그램

sequenceDiagram
    participant User as 사용자
    participant UI as MainPop 컴포넌트
    participant API as API 레이어
    participant Server as 서버

    User->>UI: 새 아티클 저장 요청 (카테고리, 메모, 시간)
    activate UI
    UI->>UI: 클라이언트 검증 (날짜/시간/카테고리)
    alt 검증 성공
        UI->>API: postArticle 호출 (카테고리, 메모, 시간)
        activate API
        API->>Server: POST /api/v3/articles
        activate Server
        Server-->>API: 저장 완료
        deactivate Server
        API-->>UI: 성공 응답
        deactivate API
        UI->>UI: useSaveBookmark 호출
        UI->>User: 토스트 알림 (저장 완료)
    else 검증 실패
        UI->>User: 인라인 오류 메시지 표시
        UI->>UI: 저장 버튼 비활성화
    end
    deactivate UI
Loading
sequenceDiagram
    participant User as 사용자
    participant UI as MainPop 컴포넌트
    participant API as API 레이어
    participant Server as 서버

    User->>UI: 기존 아티클 편집 요청 (카테고리, 메모, 시간)
    activate UI
    UI->>UI: 클라이언트 검증
    alt 검증 성공
        UI->>API: putArticle 호출 (articleId, 수정 필드)
        activate API
        API->>Server: PUT /api/v3/articles/{id}
        activate Server
        Server-->>API: 업데이트 완료
        deactivate Server
        API-->>UI: 성공 응답
        deactivate API
        UI->>User: 토스트 알림 (편집 완료)
        UI->>UI: 윈도우 닫기
    else 검증 실패
        UI->>User: 인라인 오류 메시지 표시
    end
    deactivate UI
Loading

예상 코드 리뷰 시간

🎯 3 (Moderate) | ⏱️ ~20 분

관련 PR

제안된 라벨

🛠️ Feature, api, frontend

제안된 리뷰어

  • jllee000
  • constantly-dev
  • karnelll

🐰 새로운 v3 API로 날아가,
카테고리 선택, 메모 저장, 시간까지!
검증이 든든하게 지켜주고,
토스트 피드백으로 따뜻한 인사를,
MainPop은 이제 똑똑한 토끼 🐇✨

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (1 warning, 2 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning PR은 이슈 #280의 목표인 API 경로 업데이트를 충족하나, 링크된 이슈 #25 Progress 컴포넌트 구현은 현재 변경사항과 무관합니다. 이슈 #25는 design-system 관련 기능이며 현재 PR은 extension API 업데이트이므로, 링크된 이슈를 이슈 #280만으로 수정하거나 PR 변경사항과의 관련성을 명확히 하세요.
Description check ❓ Inconclusive PR 설명이 관련 이슈, 작업 내역, 리뷰 포인트를 포함하여 템플릿 형식에 맞게 작성되었으나, 변경사항이 API 버전 업데이트뿐만 아니라 UI 컴포넌트 추가 및 상당한 기능 개선을 포함하고 있어 설명과 실제 변경사항 간 불일치가 있습니다. Tasks 섹션을 확장하여 MainPop 컴포넌트의 API 훅 추가, 카테고리 관리, 유효성 검사, 토스트 피드백 등의 기능 추가를 명시해주세요.
Out of Scope Changes check ❓ Inconclusive PR 변경사항에는 API 경로 수정 외에도 MainPop 컴포넌트의 대규모 리팩토링(API 훅 통합, 카테고리 관리, 유효성 검사 로직 추가)이 포함되어 있습니다. 이러한 광범위한 기능 변경사항이 이슈 #280 범위에 포함되어야 하는지, 또는 별도 이슈로 분리해야 하는지 확인하세요.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 API 경로 수정 및 코드 정리라는 주요 변경사항을 요약하고 있으며, 관련 이슈 #280의 목표와 일치합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#280/extension-api-update

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Feb 26, 2026

✅ Storybook chromatic 배포 확인:
🐿️ storybook

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
apps/extension/src/pages/MainPop.tsx (5)

107-129: ⚠️ Potential issue | 🔴 Critical

수정 모드 초기화 조건에서 카테고리 의존성을 제거해주세요.

Line 111-112 조건 때문에 카테고리 목록을 불러오지 않으면 초기화 effect가 실행되지 않고, Line 230에서 articleId: 0으로 수정 요청이 나갈 수 있습니다.

🔧 제안 수정안
-  useEffect(() => {
-    if (
-      type === 'edit' &&
-      savedData &&
-      categoryData?.data?.categories?.length
-    ) {
+  useEffect(() => {
+    if (type === 'edit' && savedData) {
       setMemo(savedData.memo ?? '');
       setIsArticleId(savedData.id ?? 0);

       if (savedData.remindAt) {
         const [rawDate, rawTime] = savedData.remindAt.split('T');
@@
       if (savedData.categoryResponse) {
         setSelected(savedData.categoryResponse?.categoryId.toString());
         setSelectedCategoryName(savedData.categoryResponse?.categoryName);
       }
     }
-  }, [type, savedData, categoryData?.data?.categories?.length]);
+  }, [type, savedData]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/extension/src/pages/MainPop.tsx` around lines 107 - 129, The effect that
initializes edit-mode state (the useEffect block referencing type === 'edit' and
savedData) should not depend on categoryData?.data?.categories?.length; remove
that dependency from the dependency array and only include type and savedData so
the initialization always runs when entering edit mode or when savedData
changes. Locate the useEffect that sets setMemo, setIsArticleId, setDate,
setTime, setIsRemindOn, setSelected, and setSelectedCategoryName and update its
dependency array to [type, savedData] (or equivalent) while leaving the internal
logic unchanged.

178-208: ⚠️ Potential issue | 🟠 Major

저장 시점에 리마인드 입력값을 재검증해주세요.

현재 검증은 Line 158-166 입력 이벤트에서만 갱신됩니다. 사용자가 값을 건드리지 않으면 빈 값이 Line 207/238의 combineDateTime으로 전송될 수 있습니다.

🔧 제안 수정안
   const handleSave = async () => {
     const currentDate = date;
     const currentTime = time;
+
+    if (isRemindOn) {
+      const nextDateError = validateDate(currentDate);
+      const nextTimeError = validateTime(currentTime);
+      setDateError(nextDateError);
+      setTimeError(nextTimeError);
+      if (nextDateError || nextTimeError) return;
+    }
+
     if (!selected || parseInt(selected) === 0) {
       alert('카테고리를 선택해주세요!');
       return;
     }

Also applies to: 237-239

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/extension/src/pages/MainPop.tsx` around lines 178 - 208, handleSave is
calling postArticle/postArticleUpdate and passing combineDateTime(saveData.date,
saveData.time) without re-validating date/time at save time, so empty or stale
values can be submitted if the user never touched the inputs; update handleSave
to perform final validation when isRemindOn is true: ensure date and time are
non-empty and valid (e.g., parseable/Date-check) before calling combineDateTime,
and if invalid show the same alert/validation used on input change and abort
save; apply the same final-check for the update branch (where postArticleUpdate
is called) so both branches use validated date/time values (refer to handleSave,
saveData, isRemindOn, combineDateTime, postArticle, postArticleUpdate).

227-247: ⚠️ Potential issue | 🟠 Major

성공 토스트를 API 성공 콜백에서만 열어주세요.

Line 227에서 먼저 토스트를 열어 실패 케이스에서도 “저장 완료” 메시지가 노출될 수 있습니다.

🔧 제안 수정안
     } else {
-      setToastIsOpen(true);
       putArticle(
         {
           articleId: isArticleId,
           data: {
@@
         },
         {
           onSuccess: () => {
-            window.close();
+            setToastIsOpen(true);
+            setTimeout(() => window.close(), 1000);
           },
         }
       );
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/extension/src/pages/MainPop.tsx` around lines 227 - 247, The toast is
being opened before the API call completes (setToastIsOpen(true) is invoked
prior to putArticle), which can show a success message even on failure; remove
the pre-call setToastIsOpen(true) and instead call setToastIsOpen(true) inside
the putArticle onSuccess callback (the same block that calls window.close()),
and optionally ensure onError/onFailure handlers do not open the toast; update
references around putArticle, onSuccess, and setToastIsOpen to make this change.

168-170: ⚠️ Potential issue | 🟠 Major

리마인드 OFF 상태에서는 날짜/시간 에러로 저장이 막히지 않게 처리해주세요.

Line 365의 disabled 조건이 isRemindOn을 고려하지 않아, OFF로 전환해도 기존 에러 상태가 남으면 저장이 계속 비활성화됩니다.

🔧 제안 수정안
   const handleSwitchChange = (checked: boolean) => {
     setIsRemindOn(checked);
+    if (!checked) {
+      setDateError('');
+      setTimeError('');
+    }
   };
@@
           <Button
             size="medium"
             onClick={handleSave}
-            disabled={isPopError || !!dateError || !!timeError}
+            disabled={isPopError || (isRemindOn && (!!dateError || !!timeError))}
           >
             저장
           </Button>

Also applies to: 362-366

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/extension/src/pages/MainPop.tsx` around lines 168 - 170, The save
button's disabled condition currently blocks saving based on date/time
validation even when reminders are turned off; update the disabled expression
used where the Save button is rendered (the JSX with the disabled prop around
lines referenced) so that date/time error checks are only applied when
isRemindOn is true (i.e., combine existing date/time error flags with
isRemindOn). Ensure the change references the component state isRemindOn (and
keep handleSwitchChange/setIsRemindOn as-is) so toggling the switch OFF bypasses
the date/time validation and allows saving.

171-176: ⚠️ Potential issue | 🟠 Major

getKSTISOString()은 현재 사용자의 로컬 타임존에 의존하며, KST 고정값을 반환하지 않습니다.

현재 구현은 getTimezoneOffset()을 사용하여 로컬 타임존 오프셋을 계산하기 때문에, KST 사용자(UTC+9)에서만 올바른 결과를 반환합니다. UTC나 다른 타임존의 사용자가 동일한 UTC 시간을 입력하면 서로 다른 결과를 얻게 됩니다. KST 고정이 목적이라면 UTC 기준 +9h로 하드코딩해야 합니다.

  function getKSTISOString() {
-    const now = new Date();
-    const offset = now.getTimezoneOffset() * 60000; // UTC 기준 오프셋 (분 단위)
-    const kst = new Date(now.getTime() - offset); // UTC → KST 보정
-    return kst.toISOString().slice(0, 19); // 밀리초, Z 제거
+    const KST_OFFSET_MS = 9 * 60 * 60 * 1000;
+    return new Date(Date.now() + KST_OFFSET_MS).toISOString().slice(0, 19);
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/extension/src/pages/MainPop.tsx` around lines 171 - 176, getKSTISOString
currently uses the client's local timezone via getTimezoneOffset, so it only
yields correct KST for users already in UTC+9; change it to compute KST
deterministically by taking the current UTC time and adding a fixed +9 hours
(e.g. now.getTime() + 9 * 60 * 60 * 1000) and then format to ISO (slice off
milliseconds and trailing Z) so the function always returns KST regardless of
the user's local timezone. Update the function name getKSTISOString to use this
UTC+9 calculation and ensure the output remains YYYY-MM-DDTHH:MM:SS (no
milliseconds or Z).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@apps/extension/src/pages/MainPop.tsx`:
- Around line 107-129: The effect that initializes edit-mode state (the
useEffect block referencing type === 'edit' and savedData) should not depend on
categoryData?.data?.categories?.length; remove that dependency from the
dependency array and only include type and savedData so the initialization
always runs when entering edit mode or when savedData changes. Locate the
useEffect that sets setMemo, setIsArticleId, setDate, setTime, setIsRemindOn,
setSelected, and setSelectedCategoryName and update its dependency array to
[type, savedData] (or equivalent) while leaving the internal logic unchanged.
- Around line 178-208: handleSave is calling postArticle/postArticleUpdate and
passing combineDateTime(saveData.date, saveData.time) without re-validating
date/time at save time, so empty or stale values can be submitted if the user
never touched the inputs; update handleSave to perform final validation when
isRemindOn is true: ensure date and time are non-empty and valid (e.g.,
parseable/Date-check) before calling combineDateTime, and if invalid show the
same alert/validation used on input change and abort save; apply the same
final-check for the update branch (where postArticleUpdate is called) so both
branches use validated date/time values (refer to handleSave, saveData,
isRemindOn, combineDateTime, postArticle, postArticleUpdate).
- Around line 227-247: The toast is being opened before the API call completes
(setToastIsOpen(true) is invoked prior to putArticle), which can show a success
message even on failure; remove the pre-call setToastIsOpen(true) and instead
call setToastIsOpen(true) inside the putArticle onSuccess callback (the same
block that calls window.close()), and optionally ensure onError/onFailure
handlers do not open the toast; update references around putArticle, onSuccess,
and setToastIsOpen to make this change.
- Around line 168-170: The save button's disabled condition currently blocks
saving based on date/time validation even when reminders are turned off; update
the disabled expression used where the Save button is rendered (the JSX with the
disabled prop around lines referenced) so that date/time error checks are only
applied when isRemindOn is true (i.e., combine existing date/time error flags
with isRemindOn). Ensure the change references the component state isRemindOn
(and keep handleSwitchChange/setIsRemindOn as-is) so toggling the switch OFF
bypasses the date/time validation and allows saving.
- Around line 171-176: getKSTISOString currently uses the client's local
timezone via getTimezoneOffset, so it only yields correct KST for users already
in UTC+9; change it to compute KST deterministically by taking the current UTC
time and adding a fixed +9 hours (e.g. now.getTime() + 9 * 60 * 60 * 1000) and
then format to ISO (slice off milliseconds and trailing Z) so the function
always returns KST regardless of the user's local timezone. Update the function
name getKSTISOString to use this UTC+9 calculation and ensure the output remains
YYYY-MM-DDTHH:MM:SS (no milliseconds or Z).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1ee533 and af69d6d.

📒 Files selected for processing (2)
  • apps/extension/src/apis/axios.ts
  • apps/extension/src/pages/MainPop.tsx

Copy link
Member

@constantly-dev constantly-dev left a comment

Choose a reason for hiding this comment

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

아하 version만 업데이트 하면 되는거였군요~~ 굿입니다 👍

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

Labels

feat 기능 개발하라 개발 달려라 달려

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 익스텐션 업데이트

2 participants