Skip to content

feat: 클립 내용 수정 API 구현 (#31)#31

Open
Dobbymin wants to merge 1 commit intomainfrom
feat#31-update-clip
Open

feat: 클립 내용 수정 API 구현 (#31)#31
Dobbymin wants to merge 1 commit intomainfrom
feat#31-update-clip

Conversation

@Dobbymin
Copy link
Copy Markdown
Member

@Dobbymin Dobbymin commented Sep 25, 2025

📝 요약 (Summary)

Swagger 문서 스펙에 따라 클립 내용 수정 API를 3-layer 아키텍처로 구현하였습니다. 사용자가 특정 클립 ID를 통해 자신의 클립 내용(제목, URL, 태그, 메모)을 안전하게 수정할 수 있는 기능을 제공합니다.

✅ 주요 변경 사항 (Key Changes)

• PUT /api/clips/:clipId 엔드포인트 추가
• 3-layer 아키텍처 완전 구현 (Repository → Service → Controller)
• 소유자 검증 로직 포함 (user_id 기반 보안 체크)
• 태그 처리: 기존 태그 재사용 및 새 태그 자동 생성
• 부분 필드 수정 지원 (title, url, tagName, memo)
• 체계적인 에러 처리 (404, 400, 500 상태 코드)
• 유효성 검사 및 데이터 정제 (공백 제거, null/undefined 필터링)
• 단위 테스트 작성 (Repository/Service/Controller 각 계층)

💻 상세 구현 내용 (Implementation Details)

🏗️ Repository Layer (updateClipById.js)

• Supabase를 활용한 DB 클립 수정 쿼리 구현
• 소유자 검증을 위한 user_id 조건 포함
• 수정된 클립의 기본 정보 (id, title, url, memo, tag_id) 반환
• PGRST116 에러 코드를 통한 404 처리

const { data, error } = await supabase
  .from('clips')
  .update(updateData)
  .eq('id', clipId)
  .eq('user_id', userId) // 소유자 검증
  .select('id, title, url, memo, tag_id')
  .single();

🔧 Service Layer (updateClip.js)

• clipId, userId 유효성 검사 (숫자, 양수 확인)
• 수정 필드 유효성 검사 및 데이터 정제
• 태그 처리 로직: 기존 태그 재사용 또는 새 태그 자동 생성
• 부분 필드 수정 지원 (일부 필드만 수정 가능)
• 성공 시 수정된 필드 목록 포함한 사용자 친화적 메시지 반환

// 태그 처리 로직
if (filteredFields.tagName) {
  const existingTag = await findTagByName(filteredFields.tagName, userId);
  
  if (existingTag) {
    tagId = existingTag.id;
  } else {
    const newTag = await createTag(filteredFields.tagName, userId);
    tagId = newTag.id;
  }
}

🌐 Controller Layer (handleUpdateClip.js)

• Express req.params에서 clipId 추출
• req.user에서 사용자 인증 정보 확인
• req.body에서 수정 필드들 추출 (title, url, tagName, memo)
• 표준화된 성공/에러 응답 포맷 사용
• HTTP 상태 코드 자동 매핑 (200, 401, 400, 404, 500)

🛣️ Router 연동

• Express PUT 메서드 라우트 등록
• 기존 인증 미들웨어와 연동
• RESTful API 패턴 준수

app.put('/api/clips/:clipId', handleUpdateClip);

🚀 트러블 슈팅 (Trouble Shooting)

1. 태그 처리 로직 설계

문제: 클립 수정 시 새로운 태그명이 제공될 때, 기존 태그를 재사용할지 새로 생성할지 결정 필요
해결: 먼저 기존 태그를 조회하고, 없으면 새로 생성하는 2단계 로직으로 구현

2. 부분 필드 수정 지원

문제: 모든 필드를 필수로 받을지, 제공된 필드만 수정할지 결정 필요
해결: 유효한 필드만 필터링하여 부분 수정을 지원하도록 구현

3. 소유자 검증 보안

문제: 클립 수정 시 다른 사용자의 클립을 수정하는 보안 취약점 방지 필요
해결: Repository 레벨에서 user_id 조건을 포함하여 DB 차원에서 소유권 검증

⚠️ 알려진 이슈 및 참고 사항 (Known Issues & Notes)

테스트 실행 이슈

• 모든 단위 테스트가 성공적으로 통과
• Repository(5개), Service(12개), Controller(11개) 총 28개 테스트 케이스
• 로컬 서버에서 정상적으로 동작 확인됨

성능 고려사항

• 태그 처리 시 2번의 DB 쿼리 발생 가능 (기존 태그 조회 + 새 태그 생성)
• 추후 태그 캐싱 또는 배치 처리 고려 가능

API 동작 특징

• 수정할 데이터가 제공되지 않으면 400 에러 반환
• 존재하지 않는 클립 또는 타인 소유 클립 수정 시 404 에러
• 태그명만 제공된 경우에도 정상 처리 (기존 태그 재사용 또는 새 태그 생성)

📸 스크린샷 (Screenshots)

Swagger API 문서:

• PUT /api/clips/{clipId} 엔드포인트 정상 표시
• 200, 400, 404 응답 스키마 정의 완료
• 요청 본문 스키마 정의: title, url, tagName, memo 필드

로컬 서버 실행:

Server is running on port 8081
[nodemon] clean exit - waiting for changes before restart

테스트 실행 결과:

✅ Repository 테스트: 5/5 passed
✅ Service 테스트: 12/12 passed  
✅ Controller 테스트: 11/11 passed
총 28개 테스트 케이스 성공

#️⃣ 관련 이슈 (Related Issues)

• feat#31-update-clip 브랜치로 개발 진행
• Swagger 문서 스펙 기반 구현
• 클립 삭제 API(#28)와 일관된 아키텍처 적용

🧪 테스트 커버리지

Repository Layer 테스트

• ✅ 소유자 검증 포함 정상 클립 수정 성공
• ✅ 존재하지 않는 클립 또는 타인 소유 클립 404 에러
• ✅ 데이터베이스 에러 500 처리
• ✅ 올바른 Supabase 쿼리 호출 검증
• ✅ 부분 데이터 수정 처리

Service Layer 테스트

• ✅ 유효한 clipId, userId로 성공 처리
• ✅ 기존 태그 재사용 로직 검증
• ✅ 새로운 태그 자동 생성 로직 검증
• ✅ 부분 필드 수정 지원 확인
• ✅ 유효성 검사 (clipId, userId, updateData)
• ✅ 데이터 정제 (공백 제거, null 필터링)
• ✅ Repository 에러 전파 확인
• ✅ 경계값 테스트 (유효하지 않은 필드, 빈 데이터)

Controller Layer 테스트

• ✅ HTTP 요청/응답 처리
• ✅ params에서 clipId 추출
• ✅ req.user에서 사용자 ID 추출
• ✅ req.body에서 수정 필드들 추출
• ✅ 성공/에러 응답 포맷팅
• ✅ HTTP 상태 코드 자동 매핑
• ✅ 인증 실패 401 에러 처리
• ✅ 400/404/500 에러 시나리오

🤖 Copilot 리뷰 가이드라인

리뷰어 및 Copilot에게 요청사항:

  • 모든 리뷰는 한국어로 작성해주세요
  • Controller → Service → Repository 패턴 준수 여부 확인
  • CustomError 및 responseFormatter 사용 여부 체크
  • 파일 명명 규칙 (handleUpdateClip.js, updateClip.js) 확인
  • ES modules import에서 .js 확장자 포함 여부 확인

특별히 검토해주세요:

  • 보안: 소유자 검증 로직 구현 - user_id 기반 DB 레벨 검증
  • 에러 처리: CustomError로 체계적 처리 (400, 404, 500)
  • 코드 일관성: 클립 삭제 API와 동일한 패턴 적용

추가 리뷰 포인트:
• 🔍 태그 처리 로직의 효율성 검토 (기존 태그 재사용 vs 새 태그 생성)
• 🔍 부분 필드 수정 로직의 완전성 검토
• 🔍 데이터 유효성 검사 및 정제 로직 검토
• 🔍 소유자 검증의 보안성 및 완전성 검토

Summary by CodeRabbit

  • 신기능

    • 클립 수정 API(PUT /api/clips/:clipId) 추가: 제목, URL, 메모, 태그명을 개별 또는 부분 업데이트 가능.
    • 태그명이 없을 경우 자동 생성 후 클립에 연결.
    • 인증 필요: 인증 누락 시 401, 권한/존재하지 않는 리소스는 404 등 일관된 상태 코드와 응답 제공.
  • 테스트

    • 컨트롤러, 서비스, 리포지토리 전 영역에 대한 단위 테스트 추가로 정상/에러/부분 업데이트/태그 처리 시나리오 검증.

- PUT /api/clips/:clipId 엔드포인트 추가
- 소유자 검증 로직 포함 (user_id 기반 보안 체크)
- 태그 처리: 기존 태그 재사용 및 새 태그 자동 생성
- 유효성 검사: clipId, userId, 수정 필드 검증
- 부분 필드 수정 지원 (title, url, tagName, memo)
- 3-layer 아키텍처: Repository→Service→Controller
- 단위 테스트: Repository(5개), Service(12개), Controller(11개)
- Swagger 문서 스펙 완전 준수
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Sep 25, 2025

📝 Walkthrough

Walkthrough

클립 업데이트 기능을 추가. PUT /api/clips/:clipId 라우트를 신설해 인증 사용자만 제목/URL/메모/태그를 수정 가능. 컨트롤러-서비스-레포지토리 계층을 구현하고 Supabase를 통해 소유자 조건으로 업데이트. 표준 성공/에러 응답 처리 및 세부 단위 테스트 추가.

Changes

Cohort / File(s) Summary
Router
src/routes/router.js
PUT \/api\/clips\/:clipId 라우트 추가, handleUpdateClip에 매핑.
Controller
src/apis/clip/controller/handleUpdateClip.js
handleUpdateClip 신규: 인증 검증, 본문에서 title,url,tagName,memo 추출, 서비스 호출, 200/401/에러 상태별 표준 응답.
Service
src/apis/clip/service/updateClip.js
updateClip 신규: 입력 검증, 허용 필드만 정제·trim, 태그명 처리(기존 조회·없으면 생성), updateClipById 호출, 업데이트 필드 목록 반환.
Repository (Clips)
src/apis/clip/repository/updateClipById.js
updateClipById 신규: clips 테이블 대상 id+user_id 조건 업데이트, 선택 컬럼 반환. PGRST116→404 CLIP_NOT_FOUND, 기타→500 CLIP_UPDATE_ERROR.
Tests — Controller
tests/apis/clip/controller/handleUpdateClip.test.js
성공/부분 업데이트/태그 포함/인증 누락/400·404·500 매핑/페이로드 정제 검증. 모듈 목킹 및 표준 응답 검증.
Tests — Service
tests/apis/clip/service/updateClip.test.js
필드 정제, 기존/신규 태그 처리, 부분 업데이트, 입력 유효성 에러, 레포지토리 에러 전파 등 포괄 테스트.
Tests — Repository
tests/apis/clip/repository/updateClipById.test.js
소유자 조건 체이닝 검증, 부분 업데이트, 404/500 에러 매핑, Supabase 호출 흐름 테스트.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant R as Router (/api/clips/:clipId)
  participant C as Controller (handleUpdateClip)
  participant S as Service (updateClip)
  participant TR as Tag Repo (findTagByName/createTag)
  participant CR as Clip Repo (updateClipById)
  participant DB as Supabase (clips/tags)

  U->>R: PUT /api/clips/:clipId {title,url,tagName,memo}
  R->>C: req(params, user, body)
  C->>C: 인증 확인 (user.id)
  alt 인증 실패
    C-->>U: 401 에러 응답
  else 인증 성공
    C->>S: updateClip(clipId, userId, fields)
    S->>S: 입력 검증/필드 정제
    opt tagName 제공됨
      S->>TR: findTagByName(userId, tagName)
      alt 태그 존재
        TR-->>S: tag {id}
      else 미존재
        S->>TR: createTag(userId, tagName)
        TR-->>S: tag {id}
      end
    end
    S->>CR: updateClipById(clipId, userId, updateData{title,url,memo,tag_id?})
    CR->>DB: update clips where id & user_id
    DB-->>CR: updated row or error
    alt 성공
      CR-->>S: updated clip
      S-->>C: {message, id, updatedFields}
      C-->>U: 200 성공 응답
    else 에러
      CR-->>S: throw CustomError(404/500)
      S-->>C: throw
      C-->>U: status(from error) 에러 응답
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

✨ Feature, 📡 API, 📦 DB

Poem

살짝 깎은 URL, 토닥인 제목 줄,
꼬리 흔드는 태그들, 새 집을 찾았지! 🐇
메모 한 스푼, trim으로 반짝—
PUT-팅 소리와 함께 저장 완료!
깡총, 깡총—업데이트는 나의 특기야.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed PR 제목은 Conventional Commit 관례를 따르며 “클립 내용 수정 API 구현”이라는 주요 변경 사항을 명확하고 간결한 한 문장으로 요약하고 있어 변경 내역을 스캔하는 팀원이 빠르게 이해할 수 있습니다. PR 번호를 포함한 것은 일반적인 관례로 본질을 왜곡하지 않고 오히려 이슈 추적에 도움이 됩니다.
Description Check ✅ Passed PR 설명은 템플릿에서 요구하는 모든 섹션(요약, 주요 변경 사항, 상세 구현 내용, 트러블 슈팅, 알려진 이슈, 스크린샷, 관련 이슈, Copilot 리뷰 가이드라인)을 충실히 포함하고 있으며 각 항목에 구체적인 코드 스니펫과 테스트 결과까지 제시되어 있어 구조와 요구 정보를 완벽히 준수하고 있습니다.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat#31-update-clip

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

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @Dobbymin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 Swagger 문서 스펙에 따라 클립 내용을 수정하는 API를 구현합니다. 사용자가 자신의 클립(제목, URL, 태그, 메모)을 안전하게 업데이트할 수 있도록 하며, 3-layer 아키텍처를 적용하여 모듈화된 코드와 강력한 보안 검증을 제공합니다.

Highlights

  • PUT /api/clips/:clipId 엔드포인트 추가: 클립 내용을 수정하기 위한 새로운 RESTful API 엔드포인트가 추가되었습니다.
  • 3-layer 아키텍처 구현: Controller, Service, Repository 계층으로 구성된 3-layer 아키텍처를 통해 클립 수정 API가 완전히 구현되었습니다.
  • 소유자 검증 로직 포함: 사용자 ID를 기반으로 클립 소유자를 검증하는 보안 로직이 Repository 계층에 포함되어 안전한 데이터 수정을 보장합니다.
  • 태그 처리 로직: 클립 수정 시 제공된 태그명에 대해 기존 태그를 재사용하거나, 존재하지 않는 경우 새로운 태그를 자동으로 생성하는 로직이 구현되었습니다.
  • 부분 필드 수정 지원: 클립의 제목, URL, 태그명, 메모 중 일부 필드만 제공하여도 수정이 가능하도록 부분 필드 수정 기능이 지원됩니다.
  • 체계적인 에러 처리 및 유효성 검사: 404 (클립 없음), 400 (잘못된 요청), 500 (서버 오류) 등 다양한 HTTP 상태 코드를 활용한 체계적인 에러 처리와 함께, 입력 데이터에 대한 유효성 검사 및 공백 제거, null/undefined 필터링 등의 데이터 정제가 이루어집니다.
  • 단위 테스트 작성: Controller, Service, Repository 각 계층에 대한 단위 테스트가 작성되었으며, 모든 테스트 케이스가 성공적으로 통과되었습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

클립 내용 수정 API 구현을 위한 PR 잘 보았습니다. 전반적으로 3-layer 아키텍처를 잘 따르고 있으며, 컨트롤러, 서비스, 리포지토리 각 계층의 역할 분리가 명확합니다. 특히 DB 레벨에서 소유자 검증을 구현하여 보안을 강화한 점과, PostgREST 에러 코드를 분석하여 구체적인 에러 처리를 한 점이 인상적입니다. 테스트 코드 또한 각 계층별로 꼼꼼하게 작성되어 코드의 안정성을 높여줍니다. 한 가지 아쉬운 점은 서비스 로직에서 필드 값을 빈 문자열로 업데이트할 수 없는 작은 버그가 있어, 이 부분에 대한 수정을 제안합니다. 자세한 내용은 코드 리뷰 댓글을 참고해주세요. 훌륭한 작업입니다!

Comment on lines +64 to +68
const updateData = {};
if (filteredFields.title) updateData.title = filteredFields.title;
if (filteredFields.url) updateData.url = filteredFields.url;
if (filteredFields.memo) updateData.memo = filteredFields.memo;
if (tagId) updateData.tag_id = tagId;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

현재 updateData를 구성하는 로직은 if (filteredFields.title)과 같이 falsy 값을 확인하고 있어, title 등의 필드를 빈 문자열("")로 수정하려는 경우 의도치 않게 누락될 수 있습니다. 예를 들어, 사용자가 클립의 제목을 지우고 싶어 빈 문자열을 보내면, 이 변경사항이 DB에 반영되지 않습니다.

filteredFields에 프로퍼티가 존재하는지 여부로 확인하도록 수정하면 이 문제를 해결할 수 있습니다. in 연산자를 사용하는 것을 추천합니다.

Suggested change
const updateData = {};
if (filteredFields.title) updateData.title = filteredFields.title;
if (filteredFields.url) updateData.url = filteredFields.url;
if (filteredFields.memo) updateData.memo = filteredFields.memo;
if (tagId) updateData.tag_id = tagId;
const updateData = {};
if ('title' in filteredFields) updateData.title = filteredFields.title;
if ('url' in filteredFields) updateData.url = filteredFields.url;
if ('memo' in filteredFields) updateData.memo = filteredFields.memo;
if (tagId) updateData.tag_id = tagId;

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: 1

🧹 Nitpick comments (2)
src/apis/clip/controller/handleUpdateClip.js (1)

20-23: 컨트롤러에서 undefined 필드 제외(선택)로 페이로드 최소화

서비스가 필터링을 수행하긴 하지만, 컨트롤러에서 undefined를 사전에 제거하면 불필요한 키 전달을 줄일 수 있습니다. 테스트가 현재 undefined 전달을 기대하고 있으므로 적용 시 테스트 갱신이 필요합니다.

다음과 같이 updateFields를 구성하는 방식을 고려해 보세요:

-    const { title, url, tagName, memo } = req.body;
-    const updateFields = { title, url, tagName, memo };
+    const { title, url, tagName, memo } = req.body;
+    const updateFields = Object.fromEntries(
+      Object.entries({ title, url, tagName, memo }).filter(([, v]) => v !== undefined)
+    );
tests/apis/clip/service/updateClip.test.js (1)

150-174: 빈 문자열 시나리오 테스트 보강 제안

서비스 로직 결정(A안: 무시 / B안: 비우기 허용)에 따라 다음 케이스를 추가해 주세요.

  • 입력이 ' ' 등 공백뿐인 경우: trim 후 처리 기대값(A안이면 NO_VALID_UPDATE_DATA 400, B안이면 해당 필드가 ''로 업데이트).
  • tagName이 공백뿐인 경우: 태그 업데이트 미수행(A안) 또는 tag_id 미변경(B안).
  • updatedFields가 실제 업데이트된 필드와 일치하는지 검증.

원하시면 제가 테스트 케이스 패치를 만들어 드리겠습니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a8e93bc and c93392e.

📒 Files selected for processing (7)
  • src/apis/clip/controller/handleUpdateClip.js (1 hunks)
  • src/apis/clip/repository/updateClipById.js (1 hunks)
  • src/apis/clip/service/updateClip.js (1 hunks)
  • src/routes/router.js (2 hunks)
  • tests/apis/clip/controller/handleUpdateClip.test.js (1 hunks)
  • tests/apis/clip/repository/updateClipById.test.js (1 hunks)
  • tests/apis/clip/service/updateClip.test.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
{app.js,src/**/*.js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

변수명과 함수명은 영어로, 주석은 한국어로 작성한다.

Files:

  • src/routes/router.js
  • src/apis/clip/repository/updateClipById.js
  • src/apis/clip/service/updateClip.js
  • src/apis/clip/controller/handleUpdateClip.js
src/routes/router.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

라우팅은 src/routes/router.js에서 관리하고 경로는 /api/{도메인}/{액션} 형식을 따른다.

Files:

  • src/routes/router.js
src/apis/**/repository/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

리포지토리는 데이터베이스 접근 로직을 담당하며 파일명은 find{Entity}.js 형태를 따른다 (예: findProfileByUserId.js).

Files:

  • src/apis/clip/repository/updateClipById.js
src/apis/**/service/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

서비스는 비즈니스 로직을 구현하며 파일명은 {verb}{Noun}.js 형태를 따른다 (예: loginUser.js, createUser.js).

Files:

  • src/apis/clip/service/updateClip.js
src/apis/**/controller/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/apis/**/controller/*.js: 컨트롤러는 요청 데이터를 추출하고 서비스 호출 후 표준 응답 포맷으로 응답하며 try/catch로 에러를 처리한다.
컨트롤러 파일명은 handle{Action}.js 형태를 따른다 (예: handleUserLogin.js).
모든 API 응답은 src/utils/responseFormatter.js의 createSuccessResponse/createErrorResponse를 통해 표준 포맷으로 반환한다.

Files:

  • src/apis/clip/controller/handleUpdateClip.js
🧬 Code graph analysis (7)
src/routes/router.js (1)
src/apis/clip/controller/handleUpdateClip.js (2)
  • handleUpdateClip (9-36)
  • handleUpdateClip (9-36)
src/apis/clip/repository/updateClipById.js (3)
src/apis/clip/controller/handleUpdateClip.js (1)
  • userId (12-12)
src/apis/clip/service/updateClip.js (1)
  • updateData (64-64)
src/utils/errors.js (1)
  • CustomError (1-13)
src/apis/clip/service/updateClip.js (3)
src/apis/clip/controller/handleUpdateClip.js (2)
  • userId (12-12)
  • updateFields (22-22)
src/utils/errors.js (1)
  • CustomError (1-13)
src/apis/clip/repository/updateClipById.js (2)
  • updateClipById (16-33)
  • updateClipById (16-33)
tests/apis/clip/controller/handleUpdateClip.test.js (3)
src/apis/clip/service/updateClip.js (2)
  • updateClip (18-78)
  • updateClip (18-78)
src/apis/clip/controller/handleUpdateClip.js (2)
  • handleUpdateClip (9-36)
  • handleUpdateClip (9-36)
src/utils/errors.js (1)
  • CustomError (1-13)
src/apis/clip/controller/handleUpdateClip.js (1)
src/apis/clip/service/updateClip.js (2)
  • updateClip (18-78)
  • updateClip (18-78)
tests/apis/clip/service/updateClip.test.js (3)
src/apis/clip/repository/updateClipById.js (2)
  • updateClipById (16-33)
  • updateClipById (16-33)
src/apis/clip/service/updateClip.js (4)
  • updateClip (18-78)
  • updateClip (18-78)
  • existingTag (52-52)
  • newTag (58-58)
src/utils/errors.js (1)
  • CustomError (1-13)
tests/apis/clip/repository/updateClipById.test.js (2)
src/apis/clip/repository/updateClipById.js (3)
  • supabase (17-23)
  • updateClipById (16-33)
  • updateClipById (16-33)
src/utils/errors.js (1)
  • CustomError (1-13)
🔇 Additional comments (4)
src/routes/router.js (1)

10-10: PUT /api/clips/:clipId 라우팅 연동 LGTM

핸들러 임포트 및 PUT 라우트 연결이 현재 Clip API의 REST 스타일과 일관됩니다. 권한은 전역 conditionalAuth로 강제되고, 상세조회만 optionalAuth를 사용해 정책도 일관적입니다.

업데이트 라우트가 상세 조회(GET /api/clips/:clipId)보다 먼저 선언되어도 충돌은 없지만, 미들웨어 적용 순서/정책을 다시 한 번 확인해 주세요(특히 authExcludePaths가 /api/auth로만 한정되는 점). 필요 시 제가 간단한 라우팅 점검 스크립트를 드릴 수 있습니다.

Also applies to: 30-30

tests/apis/clip/repository/updateClipById.test.js (1)

32-51: 레포지토리 성공 플로우 검증 LGTM

체인 호출(eq → select → single)과 반환 데이터 검증이 명확합니다. PGRST116/기타 에러 케이스도 분리 테스트되어 신뢰도가 높습니다.

tests/apis/clip/controller/handleUpdateClip.test.js (1)

38-62: 컨트롤러 성공 케이스 커버리지 충분, 표준 응답 포맷 사용 확인

서비스 호출 파라미터 구성, 성공 응답 포맷, 200 상태코드 검증이 명확합니다. 부분 업데이트/태그 포함 케이스도 별도 검증되어 좋습니다.

src/apis/clip/service/updateClip.js (1)

38-42: 빈 문자열('') 처리에 따른 updatedFields와 DB 업데이트 불일치
trim 후 빈 문자열은 filteredFields에 포함되나
if (filteredFields.x) truthy 체크로 updateData에 반영되지 않아
updatedFields 반환값과 실제 DB 업데이트 결과가 달라질 수 있습니다.
– 모든 updateData 매핑은 truthy 체크 대신
Object.prototype.hasOwnProperty.call(filteredFields, '필드명') 사용 권장
A) 빈 문자열 무시: 필터링 단계에 normalized !== '' 조건 추가
B) 빈 문자열 허용(필드 비우기 지원): 필터링 단계 유지, 매핑만 개선

위 두 안 중 의도하신 동작을 선택해 주세요.

* @param {string} [updateData.title] 클립 제목
* @param {string} [updateData.url] 클립 URL
* @param {string} [updateData.memo] 클립 메모
* @param {number} [updateData.tagId] 태그 ID
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

JSDoc 필드명 불일치: tagId → tag_id

서비스/DB 컬럼과 일치하도록 JSDoc을 정정하세요. 현재 코드는 tag_id를 사용합니다.

다음 주석 변경을 제안합니다:

- * @param {number} [updateData.tagId] 태그 ID
+ * @param {number} [updateData.tag_id] 태그 ID
📝 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
* @param {number} [updateData.tagId] 태그 ID
* @param {number} [updateData.tag_id] 태그 ID
🤖 Prompt for AI Agents
In src/apis/clip/repository/updateClipById.js around line 12, the JSDoc
parameter name is currently "tagId" but the code/DB uses "tag_id"; update the
JSDoc to use "tag_id" to match the service/DB field. Edit the @param line to
replace "tagId" with "tag_id" and keep the rest of the description intact so the
documentation accurately reflects the actual parameter name used by the
implementation.

@Dobbymin Dobbymin self-assigned this Sep 26, 2025
@Dobbymin Dobbymin added 📡 API 비동기 통신 코드를 짜는 경우 ✨ Feature 새로운 기능 추가 및 구현하는 경우 labels Sep 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📡 API 비동기 통신 코드를 짜는 경우 ✨ Feature 새로운 기능 추가 및 구현하는 경우

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant