[FEATURE] 폴더 API 연동#52
Conversation
| if (selectedIds.size > 0) { | ||
| assignCategory([...selectedIds], newId); | ||
| } | ||
| await addFolder(name, [...selectedIds]); |
There was a problem hiding this comment.
addFolder 성공 후 refreshLinks 실패 시 중복 폴더 생성 위험
현재 코드:
await addFolder(name, [...selectedIds]); // 서버에 폴더 생성 + 로컬 상태 갱신 ✓
await refreshLinks(); // 실패하면 catch로 이동
router.dismissAll(); // 실행 안 됨
router.replace({...}); // 실행 안 됨
addFolder 성공 후 refreshLinks()가 throw하면:
1. "폴더 생성 실패" Alert 표시 — 하지만 폴더는 이미 서버에 생성됨
2. isCreating = false로 복구, 화면 유지
3. 사용자가 다시 만들기 버튼 누름 → addFolder 재호출 → 서버에 중복 폴더 생성
folder-name.tsx의 중복 이름 검사는 로컬 folders 상태 기준이라 addFolder가 이미 상태에 추가했으므로 감지되지만, folder-url-select.tsx 화면에서는 이름 검사가 없어 직접 재시도하면 통과됩니다.
There was a problem hiding this comment.
피드백 감사합니다!
코드 흐름을 다시 확인해봤는데, 현재 구현 기준으로는 말씀하신 중복 생성 시나리오는 발생하지 않는 것으로 판단했습니다.
folder-url-select.tsx에서는 addFolder 이후 refreshLinks()를 호출하고 있지만, 실제 refreshLinks() 구현은 내부에서 fetchSavedLinks 실패를 catch한 뒤 setErrorMessage만 호출하고 다시 throw하지 않습니다.
따라서 refreshLinks()가 API 호출에 실패하더라도 folder-url-select.tsx의 catch로 이동하지 않고, 이후 router.dismissAll() / router.replace()가 그대로 실행됩니다. 즉 “폴더 생성 실패” Alert가 뜨고 화면에 남아 사용자가 다시 생성 버튼을 눌러 중복 생성되는 흐름은 현재 코드에서는 재현되지 않습니다.
다만 말씀하신 것처럼 addFolder 성공 이후 후속 작업까지 하나의 try/catch에 묶여 있는 구조는 오해의 여지가 있어 보입니다. 그래서 생성 성공 이후의 refreshLinks()는 폴더 생성 실패와 분리되도록 후속 작업으로 취급하는 방향으로 정리하겠습니다.
sunm2n
left a comment
There was a problem hiding this comment.
수고하셨습니다! 바로 머지해도 될 거 같습니다!
Closes #51
개요
폴더 화면에서 사용하던 mock 폴더 상태를 백엔드 Category API 기반 흐름으로 교체했습니다.
기존
context/folders-context.tsx는MOCK_FOLDERS를 기준으로 폴더 생성, 이름 수정, 삭제를 로컬 상태에서만 처리하고 있었습니다. 이번 작업에서는 백엔드LinClean-BE-spring의 폴더 API 개발 PR 기준으로GET /api/v1/categories,POST /api/v1/categories,PATCH /api/v1/categories/{id},DELETE /api/v1/categories/{id}를 실제 화면 흐름에 연결했습니다.Category API는 Clerk 인증 토큰이 필요한 보호 API이므로, issue #41에서 정리한 공통 API 클라이언트 패턴을 사용했습니다. 기능별 API 파일에서는 API base URL을 새로 만들지 않고,
fetch()나Authorization헤더를 직접 다루지 않도록 구성했습니다.또한 폴더 생성, 삭제, 이름 수정, 폴더 내 링크 제외 흐름에서 사용자 안내 toast와 확인창을 정리하고, 저장 링크 목록과 폴더 목록의 상태가 함께 갱신되도록 보완했습니다.
주요 구현 내용
api/categories.ts추가GET /api/v1/categories폴더 목록 조회 연동POST /api/v1/categories폴더 생성 연동linkIds로 함께 전달assignCategory중복 호출 제거PATCH /api/v1/categories/{id}폴더명 수정 연동DELETE /api/v1/categories/{id}폴더 삭제 연동assignCategory(..., null)호출 제거FoldersProvider위치를 탭 layout 기준으로 정리다음클릭 시 중복 폴더명 검증파일별 역할
api/categories.ts: Category API 타입 및 생성/조회/수정/삭제 호출 함수 추가context/folders-context.tsx: mock 폴더 상태를 Category API 기반 상태와 액션으로 교체app/(tabs)/_layout.tsx:FoldersProvider위치 조정app/(tabs)/(folder)/_layout.tsx: 중복 provider 제거app/(tabs)/(folder)/index.tsx: 폴더 목록 조회, 로딩/빈 목록/에러, 삭제 toast 처리app/(tabs)/(folder)/index.tsx: 폴더명 수정 모달에서 현재 폴더명과 동일한 입력값일 때 저장 버튼 비활성화 처리app/(tabs)/(folder)/folder-name.tsx: 폴더명 입력, 중복명 검증, 생성/수정 흐름 연결app/(tabs)/(folder)/folder-url-select.tsx: 폴더 생성 시 선택 링크linkIds전달app/(tabs)/(folder)/folder-add-url.tsx: 기존 링크의 폴더 추가 흐름 유지 및 폴더 목록 refresh 처리app/(tabs)/(folder)/[id].tsx: 폴더 상세 링크 제외, 제목 수정, toast, refresh 처리app/(tabs)/(home)/index.tsx: 링크 삭제 후 폴더 목록 refresh 처리app/(tabs)/(home)/saved-links.tsx: 실제 Category 목록 기반 필터 칩 표시해결한 이슈 목록
context/folders-context.tsx의 mock 상태 구조 확인api/categories.ts추가 및 Category API 타입/호출 함수 분리authenticatedApiRequest사용fetch()직접 호출하지 않음Authorization헤더 직접 주입하지 않음GET /api/v1/categories응답의items구조를 폴더 상태에 반영POST /api/v1/categories요청에name,linkIds전달assignCategory중복 호출 없이linkIds일괄 배치 기능 사용PATCH /api/v1/categories/{id}폴더명 수정 흐름 연결DELETE /api/v1/categories/{id}폴더 삭제 흐름 연결assignCategory(..., null)호출 제거SavedLinksProvider와FoldersProvider위치 검토 및 중복 생성 정리체크 사항
fetch()호출 없음Authorization헤더 주입 없음{ data: ... }응답 직접 unwrap 없음/auth/me수동 선호출 없음DELETE /api/v1/categories/{id}의204 No Content응답 처리참고
폴더 삭제 시 연결된 저장 링크의
categoryId정리는 백엔드에서 처리하므로, 프론트에서는 별도의 선행 링크 이동 API를 호출하지 않습니다.기존 저장 링크의 폴더 이동/제거 흐름은
PATCH /api/v1/saved-links/{id}/category기반 흐름을 유지했습니다.폴더 생성 시 선택한 링크가 있는 경우에는
POST /api/v1/categories요청의linkIds를 사용해 한 번에 연결되도록 처리했습니다.스크린샷