Skip to content

Refactor#237 apply debounce#240

Open
hye410 wants to merge 10 commits intodevfrom
Refactor#237-apply-debounce
Open

Refactor#237 apply debounce#240
hye410 wants to merge 10 commits intodevfrom
Refactor#237-apply-debounce

Conversation

@hye410
Copy link
Copy Markdown
Collaborator

@hye410 hye410 commented Apr 28, 2025

💡 관련이슈

#237

🍀 작업 요약

  • 통신 시 사용할 debounce 함수 적용

💬 리뷰 요구 사항

  • 본인이 작업한 곳 api 통신 잘 되는지 확인 부탁드립니다.
  • 혹시 빼먹은 곳이 있다면 알려주세요!
  • 마이페이지 > 면접 상세 기록, 이력서 상세 기록 삭제 버튼을 빠르게 클릭 시 에러 발생합니다만, 시간 관계 상 추후 리팩토링에서 수정하는 것으로 하겠습니다.

💛 미리보기

as-is

화면-기록-2025-04-28-오후-5 09 43

to-be

화면-기록-2025-04-28-오후-5 13 35

✔️ 이슈 닫기

Closes #237
Ref #이슈번호 // 해당 이슈에 대한 작업이 완전히 끝나지 않은 경우

Summary by CodeRabbit

  • 신규 기능

    • 비동기 함수에 사용할 수 있는 커스텀 디바운스 훅이 추가되었습니다.
  • 버그 수정

    • 이력서 상세 조회 시 존재하지 않는 이력서에 대해 404가 아닌 200 상태 코드로 응답하도록 변경되었습니다.
  • 리팩터링

    • 여러 인터뷰 및 이력서 관련 버튼 동작에 디바운스 처리가 적용되어, 빠른 연속 클릭 시 중복 요청이 방지됩니다.
    • 이력서 삭제 및 편집, 인터뷰 취소, 인터뷰 시작/완료 등의 로직이 비동기 디바운스 방식으로 개선되었습니다.
    • 이력서 폼 제출 및 모달 오픈 동작에 디바운스가 적용되었습니다.
  • 스타일

    • 버튼 포커스 시 테두리가 제거되어 더욱 깔끔한 UI를 제공합니다.
  • 기타

    • 사용하지 않는 디바운스 훅 파일이 삭제되었습니다.
    • 일부 쿼리 캐시 무효화 및 네비게이션 처리 방식이 단순화되었습니다.

@hye410 hye410 added 📬 API 서버 API 통신 🔨 Refactor 코드 리팩토링 💎다혜 labels Apr 28, 2025
@hye410 hye410 self-assigned this Apr 28, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2025

Walkthrough

이번 변경 사항은 전반적으로 여러 기능에서 중복 클릭이나 빠른 연속 요청을 방지하기 위해 debounce 처리를 도입한 리팩토링입니다. 기존의 useFuncDebounce 대신 새로운 비동기 지원 debounce 훅인 useAsyncFuncDebounce를 도입하여, 비동기 요청(삭제, 저장, 시작 등)에 debounce를 적용했습니다. 또한, 일부 버튼의 포커스 스타일이 수정되었으며, 쿼리 실행 제어, 에러 처리 위치 변경, 삭제 및 저장 로직이 비동기 및 debounce로 일원화되었습니다. 기존 debounce 훅 파일은 삭제되었습니다.

Changes

파일/경로 요약 변경 내용 요약
src/hooks/customs/use-async-func-debounce.ts 비동기/동기 함수용 debounce 훅 useAsyncFuncDebounce 신규 도입
src/hooks/customs/use-func-debounce.ts 기존 debounce 훅(useFuncDebounce) 파일 삭제
src/features/interview-history/interview-detail-field.tsx
src/features/interview/alert-in-progress.tsx
src/features/interview/select-interviewer-box.tsx
src/features/interview/timer.tsx
면접 관련 기능(시작, 삭제, 완료 등)에 debounce 적용 및 기존 debounce 방식에서 비동기 debounce로 변경
src/features/resume-list/resume-detail-field.tsx
src/features/resume/draft-resumes-modal.tsx
src/features/resume/hooks/use-resume-form.ts
src/features/resume/resume-form.tsx
자소서 삭제, 저장, 드래프트 목록 등 자소서 관련 주요 액션에 debounce 적용 및 confirm/notification 로직 개선
src/features/resume-list/hooks/use-resume-query.ts resumeId가 없을 때 쿼리 비활성화 옵션 추가
src/features/resume/hooks/use-add-resume-mutation.ts
src/features/resume/hooks/use-delete-resume-mutation.ts
쿼리 무효화 로직을 onSettled로 통일, 불필요한 라우팅 및 조건부 무효화 제거
src/app/api/resume/[id]/route.ts 없는 자소서 조회 시 HTTP 404 → 200으로 변경, 응답 status 명시적 지정
src/app/global-style.css .button-hover-focus에 포커스 시 border 제거 스타일(focus:border-0) 추가

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI_Component
    participant useAsyncFuncDebounce
    participant API

    User->>UI_Component: 버튼 클릭 (삭제/저장/시작 등)
    UI_Component->>useAsyncFuncDebounce: debounce 래핑된 함수 호출
    alt debounce 허용
        useAsyncFuncDebounce->>API: 비동기 요청 실행
        API-->>useAsyncFuncDebounce: 응답 반환
        useAsyncFuncDebounce-->>UI_Component: 결과 Promise 반환
    else debounce 차단
        useAsyncFuncDebounce-->>UI_Component: Promise reject(null)
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
AI 면접 시작 시 debounce (#237)
대답 완료 시 debounce 및 setTimeout 처리 (#237)
자소서 저장 시 debounce (#237)
마이페이지 > 면접 기록 삭제 요청 debounce (#237)
마이페이지 > 자소서 삭제 요청 debounce (#237)

Possibly related PRs

Suggested labels

🐞 BugFix

Suggested reviewers

  • choichangyeon
  • parkminjo

Poem

🐰
클릭이 너무 바빠서,
토끼가 debounce를 깔았죠!
저장도, 삭제도, 시작도
한 번만, 천천히,
중복 없는 세상에
토끼는 오늘도 깡총깡총!
🥕✨

✨ 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.
    • 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.
  • @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
Copy Markdown

@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: 3

🧹 Nitpick comments (1)
src/features/interview/timer.tsx (1)

55-84: 성능 및 사용자 경험 개선 제안

면접 완료 처리 중 사용자에게 로딩 상태를 시각적으로 표시하면 좋을 것 같습니다. 버튼에 로딩 상태를 추가하여 사용자가 처리 중임을 인지할 수 있도록 하는 것이 좋겠습니다.

const handleCompletedButtonClick = async () => {
+  const [isLoading, setIsLoading] = useState(false);
  try {
+    setIsLoading(true);
    if (characterId) {
      //@TODO: 캐릭터 아이디 있을 때만
      handleExperienceUp(INTERVIEW_COMPLETION);
      alert('경험치 획득 완료!'); //@TODO: 경험치 정의 완료된 후에 alert 리팩토링하면서 상수로 빼겠습니다.
    }

    setCompleted(true);
    // ... 나머지 코드
  } catch (error) {
    Notify.failure((error as Error).message);
+  } finally {
+    setIsLoading(false);
  }
};

// 그리고 버튼 부분에서 아래와 같이 로딩 상태 표시
<Button square onClick={debouncedCompleteInterview} disabled={isLoading}>
  {isLoading ? '처리 중...' : '면접 완료하기'}
</Button>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9c2eb49 and edc90e9.

📒 Files selected for processing (15)
  • src/app/api/resume/[id]/route.ts (1 hunks)
  • src/app/global-style.css (1 hunks)
  • src/features/interview-history/interview-detail-field.tsx (4 hunks)
  • src/features/interview/alert-in-progress.tsx (3 hunks)
  • src/features/interview/select-interviewer-box.tsx (3 hunks)
  • src/features/interview/timer.tsx (4 hunks)
  • src/features/resume-list/hooks/use-resume-query.ts (1 hunks)
  • src/features/resume-list/resume-detail-field.tsx (2 hunks)
  • src/features/resume/draft-resumes-modal.tsx (2 hunks)
  • src/features/resume/hooks/use-add-resume-mutation.ts (1 hunks)
  • src/features/resume/hooks/use-delete-resume-mutation.ts (1 hunks)
  • src/features/resume/hooks/use-resume-form.ts (5 hunks)
  • src/features/resume/resume-form.tsx (3 hunks)
  • src/hooks/customs/use-async-func-debounce.ts (1 hunks)
  • src/hooks/customs/use-func-debounce.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • src/hooks/customs/use-func-debounce.ts
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/features/interview/alert-in-progress.tsx (1)
src/hooks/customs/use-async-func-debounce.ts (1)
  • useAsyncFuncDebounce (10-27)
src/features/interview-history/interview-detail-field.tsx (1)
src/hooks/customs/use-async-func-debounce.ts (1)
  • useAsyncFuncDebounce (10-27)
src/features/interview/select-interviewer-box.tsx (1)
src/hooks/customs/use-async-func-debounce.ts (1)
  • useAsyncFuncDebounce (10-27)
src/features/resume/resume-form.tsx (1)
src/hooks/customs/use-async-func-debounce.ts (1)
  • useAsyncFuncDebounce (10-27)
src/features/interview/timer.tsx (3)
src/store/use-interview-store.ts (1)
  • useInterviewStore (21-28)
src/constants/interview-constants.ts (1)
  • INTERVIEW_LIMIT_COUNT (11-11)
src/hooks/customs/use-async-func-debounce.ts (1)
  • useAsyncFuncDebounce (10-27)
🔇 Additional comments (38)
src/app/api/resume/[id]/route.ts (1)

41-41: 명시적인 상태 코드 추가로 일관성 향상

성공 응답에 명시적으로 상태 코드를 추가한 것은 코드의 일관성을 향상시킵니다. 이제 모든 응답이 명시적인 상태 코드를 가지게 되었습니다.

src/features/resume-list/hooks/use-resume-query.ts (1)

14-14: 쿼리 실행 조건 추가로 불필요한 API 호출 방지

enabled: !!resumeId 옵션을 추가하여 유효한 resumeId가 있을 때만 쿼리가 실행되도록 한 것은 좋은 최적화입니다. 이는 PR의 디바운스 구현 목표와 일치하며, 불필요한 API 호출을 방지합니다.

src/features/interview/alert-in-progress.tsx (1)

14-14: 디바운스 훅 도입 및 적용

useAsyncFuncDebounce 훅을 가져와서 삭제 뮤테이션에 적용한 것은 PR의 목표에 부합합니다. 이 변경으로 인해 빠른 연속 클릭으로 인한 오류를 방지할 수 있습니다.

Also applies to: 35-35

src/features/resume/draft-resumes-modal.tsx (1)

13-13: 디바운스 훅 도입 및 적용

useAsyncFuncDebounce 훅을 가져와서 자소서 삭제 뮤테이션에 적용한 것은 PR의 목표에 부합합니다. 이 변경으로 인해 빠른 연속 클릭으로 인한 오류를 방지할 수 있습니다.

Also applies to: 30-30

src/features/interview/select-interviewer-box.tsx (3)

9-9: useAsyncFuncDebounce 훅 추가를 올바르게 구현했습니다.

debounce 기능을 위한 필요한 의존성을 적절히 추가하셨습니다.


36-36: 면접 시작 함수에 debounce 적용이 적절합니다.

2000ms의 debounce 시간은 중복 API 요청을 방지하기에 충분합니다. 이렇게 하면 사용자가 '면접 시작하기' 버튼을 빠르게 여러 번 클릭해도 단일 요청만 전송되어 중복 인터뷰 생성이나 서버 부하를 방지할 수 있습니다.


84-84: 버튼 클릭 핸들러에 debounce 함수 적용이 잘 이루어졌습니다.

기존의 직접 호출 대신 debounced 버전의 함수를 사용하여 중복 클릭으로 인한 문제를 방지하는 좋은 패턴입니다.

src/app/global-style.css (1)

132-132: 버튼 포커스 스타일 개선이 적절합니다.

focus:border-0를 추가하여 버튼에 포커스가 가해질 때 테두리를 제거함으로써, 배경색(bg-primary-orange-600)과 텍스트 색상(text-cool-gray-10)과의 시각적 일관성이 향상되었습니다. 이는 UI/UX 관점에서 좋은 개선입니다.

src/features/resume/resume-form.tsx (3)

4-13: import 문 재정렬 및 useAsyncFuncDebounce 추가가 적절합니다.

필요한 의존성을 잘 구성하고 정리했습니다. 특히 새로운 debounce 훅을 추가한 것은 코드 일관성을 유지하는 데 도움이 됩니다.


49-49: 드래프트 목록 호출 함수에 debounce 적용이 적절합니다.

500ms의 debounce 시간은 모달 토글과 refetch 작업에 적절합니다. 이를 통해 사용자가 빠르게 연속 클릭하는 경우에도 모달이 안정적으로 동작하고 불필요한 refetch가 방지됩니다.


107-107: 버튼 클릭 핸들러에 debounce 함수 적용이 잘 구현되었습니다.

ResumeFormActionButton의 onClick 핸들러를 debounce 처리된 함수로 교체한 것은 UI 응답성과 안정성을 향상시키는 좋은 접근 방식입니다.

src/features/resume/hooks/use-add-resume-mutation.ts (1)

36-40: 쿼리 무효화 로직 개선이 적절합니다.

이전에는 성공 케이스에 따라 조건부로 쿼리 무효화를 처리했지만, 이제는 onSettled 콜백에서 일관되게 처리하도록 변경했습니다. 이러한 접근 방식은:

  1. 성공이나 실패와 관계없이 캐시가 항상 최신 상태로 유지됩니다.
  2. 조건부 로직을 제거함으로써 코드가 단순화되었습니다.
  3. HISTORY, RESUMES, TABS_COUNT 쿼리에 대한 무효화 로직이 한 곳에 모여 유지보수성이 향상되었습니다.

이런 패턴은 일관성 있는 상태 관리를 위한 좋은 관행입니다.

src/features/interview-history/interview-detail-field.tsx (4)

13-13: 기존 debounce hook을 새로운 async 지원 hook으로 교체하였습니다.

useFuncDebounce에서 useAsyncFuncDebounce로 변경하여 비동기 작업에 대한 debounce 지원을 추가했습니다.


42-42: 비동기 삭제 작업에 debounce 적용이 적절히 구현되었습니다.

인터뷰 ID를 인자로 받는 삭제 작업에 useAsyncFuncDebounce를 적용하여 중복 요청을 방지하는 패턴이 잘 구현되었습니다.


54-57: 삭제 로직이 올바르게 debounce 처리되었습니다.

기존 직접 호출 방식에서 debouncedDelete 함수를 사용하는 방식으로 변경했습니다. 이제 짧은 시간 내에 여러 번 삭제 요청이 발생해도 한 번만 실행되어 API 호출 최적화가 이루어집니다.


135-137: 삭제 버튼 클릭 핸들러가 간소화되었습니다.

중복 debounce 처리를 제거하고 confirmDeleteHistory를 직접 호출하도록 변경했습니다. 이제 debounce는 실제 API 호출 부분(deleteInterviewAsyncMutation)에만 적용되어 더 명확한 구조를 가집니다.

src/features/resume/hooks/use-delete-resume-mutation.ts (1)

30-36: 캐시 무효화 로직이 간결하고 일관되게 개선되었습니다.

onSettled 콜백이 다음과 같이 개선되었습니다:

  1. 조건부 로직 통합 - queryKey가 RESUMES일 때 추가 무효화 처리
  2. 항상 제공된 queryKey에 대한 무효화 수행

이 변경으로 라우팅 로직이 제거되고 캐시 관리에 집중하여 책임이 명확해졌습니다.

src/features/resume/hooks/use-resume-form.ts (5)

14-14: 비동기 디바운스 훅 추가가 적절합니다.

기존 useDebounce와 함께 비동기 지원 디바운스 훅인 useAsyncFuncDebounce를 추가하여 폼 제출과 같은 비동기 작업에 디바운스를 적용할 수 있게 되었습니다.


37-37: 비동기 뮤테이션 처리 방식이 개선되었습니다.

mutateAsync에서 mutate로 변경하여 디바운스 처리와 일관성을 유지하도록 개선되었습니다.


82-82: 뮤테이션 호출 방식이 디바운스 패턴에 맞게 수정되었습니다.

await 키워드가 제거되고 동기식 mutate 호출 방식으로 변경되었습니다. 이는 디바운스 패턴과 일치하며 실행 흐름을 더 명확하게 만듭니다.


96-96: 제출 핸들러에 디바운스가 적절히 적용되었습니다.

handleSubmit 함수를 useAsyncFuncDebounce로 감싸 디바운스된 버전을 생성했습니다. 이로써 빠른 연속 제출 클릭으로 인한 중복 API 호출을 방지합니다.


132-132: 디바운스된 제출 핸들러가 적절히 노출되었습니다.

원본 제출 함수 대신 디바운스된 버전을 반환하도록 변경되었습니다. 이는 컴포넌트에서 디바운스 처리된 함수를 직접 사용할 수 있게 해줍니다.

src/hooks/customs/use-async-func-debounce.ts (1)

1-27: 비동기 디바운스 훅이 효과적으로 구현되었습니다.

이 새로운 훅은 다음과 같은 장점이 있습니다:

  1. Promise를 반환하여 async/await 패턴과 호환됩니다.
  2. 디바운스 기간 내에 호출 시 즉시 reject하여 명확한 에러 처리가 가능합니다.
  3. 제네릭 타입을 사용하여 다양한 함수 시그니처를 지원합니다.
  4. 기본 1500ms의 지연 시간을 제공하면서 사용자 정의도 가능합니다.

이 구현은 PR의 목적인 "API 통신 프로세스에 디바운스 함수 적용"을 완벽히 지원합니다.

src/features/resume-list/resume-detail-field.tsx (9)

7-7: 적절한 import 추가로 debounce 기능 구현 준비 완료

기존 코드에 새로운 비동기 debounce 훅과 메시지 상수, 오류 처리 및 UI 알림 기능을 위한 import가 적절히 추가되었습니다.

Also applies to: 15-15, 19-20, 23-23


28-31: 메시지 중앙화로 유지보수성 향상

메시지 상수를 구조 분해 할당하여 사용함으로써 코드의 가독성과 유지보수성이 향상되었습니다.


39-39: 비동기 처리를 위한 mutateAsync 사용

mutate 대신 mutateAsync를 사용하여 debounce된 비동기 함수에서 적절하게 await할 수 있도록 변경되었습니다.


41-44: 이벤트 핸들러 단순화

불필요한 매개변수를 제거하고 함수를 단순화하여 가독성이 향상되었습니다.


46-51: 개선된 삭제 확인 다이얼로그 구현

기본 window.confirm 대신 더 사용자 친화적인 showNotiflixConfirm을 사용하여 일관된 UI 경험을 제공합니다.


53-53: 비동기 debounce 함수 적용

useAsyncFuncDebounce 훅을 사용하여 빠른 연속 삭제 요청을 방지하는 기능이 추가되었습니다.


55-63: 견고한 오류 처리 및 성공/실패 알림 구현

삭제 함수에 try-catch를 사용한 오류 처리와 함께 성공/실패 알림을 추가하여 사용자 경험이 향상되었습니다.


102-103: 이벤트 핸들러 적용

수정 버튼의 이벤트 핸들러가 새로 정의된 함수를 사용하도록 적절히 업데이트되었습니다.


105-106: 삭제 확인 다이얼로그 연결

삭제 버튼의 이벤트 핸들러가 새로 정의된 확인 다이얼로그 함수를 호출하도록 적절히 업데이트되었습니다.

src/features/interview/timer.tsx (6)

18-18: 비동기 debounce 훅 import 추가

새로운 useAsyncFuncDebounce 훅을 가져와 면접 완료 기능에 debounce를 적용할 준비가 되었습니다.


52-60: STT 오류 방지를 위한 지연 시간 추가

사용자가 '말하기' 버튼을 너무 빨리 클릭할 때 발생하는 STT 오류를 방지하기 위해 1초의 지연 시간이 추가되었습니다. 주석에 이유가 명확히 설명되어 있어 좋습니다.


86-88: 관련 변수 위치 재배치

questionIndexisFinalQuestionAsked 변수를 재배치하여 관련 로직을 더 가깝게 모아 코드 조직이 개선되었습니다.


89-89: 면접 완료 기능에 debounce 적용

useAsyncFuncDebounce 훅을 사용하여 면접 완료 버튼의 빠른 연속 클릭을 방지하는 기능이 추가되었습니다. 1500ms의 적절한 지연 시간이 설정되었습니다.


90-95: 오류 알림 위치 재배치

오류 알림 로직이 반환문 가까이 이동하여 코드 구조가 개선되었습니다.


116-117: 면접 완료 버튼에 debounce 함수 적용

면접 완료 버튼의 onClick 핸들러가 debounce된 함수를 사용하도록 업데이트되어, 빠른 연속 클릭으로 인한 API 요청 오류를 방지합니다.


if (!response) {
return NextResponse.json({ message: NOT_FOUND }, { status: 404 });
return NextResponse.json({ message: NOT_FOUND }, { status: 200 });
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 28, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

404 대신 200 상태 코드 사용에 대한 주의 필요

일반적으로 리소스를 찾을 수 없는 경우에는 404 상태 코드를 반환하는 것이 RESTful API 설계의 표준 관행입니다. 이를 200으로 변경하면 클라이언트가 상태 코드만으로는 자원의 존재 여부를 판단할 수 없고, 응답 본문을 검사해야 합니다.

이 변경이 디바운스 구현의 일부로 필요한지, 아니면 다른 이유가 있는지 고려해보세요.


🌐 Web query:

REST API에서 리소스를 찾을 수 없을 때 200 상태 코드 사용은 적절한가?

💡 Result:

REST API에서 리소스가 없을 때 200 상태 코드 사용의 적절성

REST API에서 클라이언트가 요청한 리소스를 찾을 수 없을 때, 200 OK 상태 코드를 사용하는 것은 일반적으로 적절하지 않습니다. 표준적인 관점과 실제 현업 사례를 아래와 같이 정리할 수 있습니다.

1. 표준 권장 사항

  • HTTP/REST에서 200 OK는 서버가 요청을 정상적으로 처리하여, 요청한 리소스를 성공적으로 반환할 때 사용합니다.
  • 반대로, 요청한 리소스가 존재하지 않을 때는 404 Not Found 상태 코드를 사용하는 것이 원칙입니다. 이 코드는 “클라이언트가 요청한 리소스를 서버에서 찾을 수 없다”는 명확한 의미를 전달합니다[1][3][4][5][7][9].

2. 예시와 예외적인 논의

  • 예를 들어, GET /users/123로 특정 사용자를 조회했는데 해당 사용자가 존재하지 않으면 404 Not Found로 명확히 알려주는 것이 API 사용성 및 유지보수성 측면에서 바람직합니다[1][5].
  • 다만, 여러 리소스를 _목록_으로 조회하는 API(GET /users?city=seoul)에서 결과가 없는 경우에는 200 OK 상태 코드와 빈 배열을 반환하는 것도 흔히 쓰는 패턴입니다. 이 경우, 요청 자체는 정상적으로 처리되었지만 결과 데이터가 없다는 의미이기 때문입니다[5][6].

3. 200 OK를 잘못 사용하는 사례

“상태 코드는 200으로 성공인데, body 내용에는 실패에 관한 내용을 리턴하고 있다 (…) 이런 설계는 무조건 잘못된 것이니 수정하자.”
— REST API에서의 HTTP 상태 코드, 상태 메시지[8]

  • 즉, 200 OK로 응답하면서 실제로는 실패나 오류 상태(예: 리소스 없음)를 응답 바디에 담아 보내는 방식은 클라이언트와의 의사소통을 혼란스럽게 하므로 바람직하지 않습니다[8].

4. 결론 및 정리

  • 자원을 단건으로 조회할 때, 리소스가 없으면 404 Not Found를 반환해야 합니다.
  • 2XX(성공) 코드와 404(리소스 없음) 코드를 상황에 맞게 정확하게 구분하는 것이 RESTful API의 원칙적 설계입니다.
  • 단, “목록 조회”에서 값이 비어 있는 경우라면 200 OK와 빈 배열도 용인됩니다. 하지만 단일 자원을 물을 때 리소스 부재를 200 OK로 반환하는 것은 잘못된 방식입니다[1][5][6].

정리:
REST API에서 리소스를 찾을 수 없는 경우에는 반드시 404 Not Found 상태 코드를 사용해야 하며, 200 OK 사용은 적절하지 않습니다. 목록 조회 등에서 결과가 없을 때만 200 OK와 빈 결과를 보낼 수 있습니다.

Citations:


404 상태 코드로 응답하도록 수정 필요

단일 리소스를 조회했을 때 해당 리소스가 없으면 200 대신 404 Not Found를 반환해야 RESTful API 설계 원칙에 부합합니다.

  • 수정 위치

    • 파일: src/app/api/resume/[id]/route.ts
    • 라인: 38
  • 제안하는 변경사항

- return NextResponse.json({ message: NOT_FOUND }, { status: 200 });
+ return NextResponse.json({ message: NOT_FOUND }, { status: 404 });

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

이건 추후에 수정할 사항인데 지금 404로 오류를 뱉어주게 되면 네트워크 탭에서 누가봐도 오류난 거 같아서 임시로 200 처리해두겠습니다... ( with. @ImJaeOne )😅

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

아이고 토끼야...

Comment on lines +58 to 60
cancelFunction: async () => {
await debouncedDelete(
{ interviewId, status, options: OPTIONS },
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

비동기 함수로 변경 및 디바운스 적용

취소 함수를 비동기 함수로 변경하고 디바운스된 삭제 함수를 호출하도록 수정한 것은 적절합니다. 이렇게 하면 사용자가 빠르게 여러 번 클릭해도 API 호출이 제한되어 오류를 방지할 수 있습니다.

하지만 디바운스 함수가 요청을 거부할 경우(너무 빠르게 다시 호출된 경우) 오류 처리가 없다는 점에 주의해야 합니다.

-      cancelFunction: async () => {
-        await debouncedDelete(
+      cancelFunction: async () => {
+        try {
+          await debouncedDelete(
           { interviewId, status, options: OPTIONS },
           {
             onSuccess: () => {
               queryClient.removeQueries({ queryKey: [IN_PROGRESS] });
               Notify.success(DELETE_SUCCESS);
             },
             onError: (error: Error) => {
               Notify.failure(DELETE_ERROR);
             },
           }
-        );
+          );
+        } catch (error) {
+          // 디바운스로 인해 요청이 거부된 경우 처리
+          console.log('Request debounced, please wait before trying again');
+        }
       },
📝 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
cancelFunction: async () => {
await debouncedDelete(
{ interviewId, status, options: OPTIONS },
// src/features/interview/alert-in-progress.tsx
cancelFunction: async () => {
try {
await debouncedDelete(
{ interviewId, status, options: OPTIONS },
{
onSuccess: () => {
queryClient.removeQueries({ queryKey: [IN_PROGRESS] });
Notify.success(DELETE_SUCCESS);
},
onError: (error: Error) => {
Notify.failure(DELETE_ERROR);
},
}
);
} catch (error) {
// 디바운스로 인해 요청이 거부된 경우 처리
console.log('Request debounced, please wait before trying again');
}
},

Comment on lines +34 to 36
okFunction: async () => {
await debouncedDelete(resumeId);
if (activeResumeId === resumeId) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

비동기 함수로 변경 및 디바운스 적용

확인 함수를 비동기 함수로 변경하고 디바운스된 삭제 함수를 호출하도록 수정한 것은 적절합니다. 이렇게 하면 사용자가 빠르게 여러 번 클릭해도 API 호출이 제한되어 오류를 방지할 수 있습니다.

하지만 디바운스 함수가 요청을 거부할 경우(너무 빠르게 다시 호출된 경우) 오류 처리가 없다는 점에 주의해야 합니다.

-      okFunction: async () => {
-        await debouncedDelete(resumeId);
+      okFunction: async () => {
+        try {
+          await debouncedDelete(resumeId);
+        } catch (error) {
+          // 디바운스로 인해 요청이 거부된 경우 처리
+          console.log('Request debounced, please wait before trying again');
+        }
         if (activeResumeId === resumeId) {
           setResumeId(null);
         }
       },
📝 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
okFunction: async () => {
await debouncedDelete(resumeId);
if (activeResumeId === resumeId) {
okFunction: async () => {
try {
await debouncedDelete(resumeId);
} catch (error) {
// 디바운스로 인해 요청이 거부된 경우 처리
console.log('Request debounced, please wait before trying again');
}
if (activeResumeId === resumeId) {
setResumeId(null);
}
},

Copy link
Copy Markdown
Collaborator

@choichangyeon choichangyeon left a comment

Choose a reason for hiding this comment

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

일괄 적용해주셔서 감사합니다!

Copy link
Copy Markdown
Collaborator

@ImJaeOne ImJaeOne left a comment

Choose a reason for hiding this comment

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

놓치고 있었던 부분인데 신경 써주셔서 감사합니다!

Copy link
Copy Markdown
Collaborator

@parkminjo parkminjo left a comment

Choose a reason for hiding this comment

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

debounce까지 고려해주시다 감사합니다

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

Labels

💎다혜 📬 API 서버 API 통신 🔨 Refactor 코드 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Refactoring] 전반적인 debounce 적용

4 participants