Step 3 Post 도메인 테크스펙 문서 - 100-hours-a-week/16-team-katopia-fe GitHub Wiki
Post 도메인 테크스펙(Post)
배경(Background)
프로젝트 목표
Post 도메인은 사용자가 자신의 코디/착용 경험을 게시글로 생성하고, 다른 사용자의 게시글을 피드 형태로 소비하며 상호작용(좋아요, 북마크) 할 수 있도록 하는 본 서비스의 핵심 콘텐츠 도메인이다.
본 도메인의 목표는 다음과 같다.
- 게시글 생성 경험의 진입 장벽 최소화
- 이미지 업로드, 텍스트 입력, 태그 설정 등 복합 입력을 단일 플로우로 제공
- 실패 시 입력값을 보존하여 재시도 가능하게 설계
- 콘텐츠 소비 흐름의 안정성 확보
- 이미지 중심 피드에서 스크롤, 렌더링, 상호작용이 끊기지 않도록 최적화
- 명확한 상호작용 모델 제공
- 좋아요 / 북마크 상태를 즉시 반영하여 사용자의 액션 피드백 강화
핵심 결과(Key Results)
- KR1 : 게시글 작성 플로우 진입 → 업로드 완료까지 평균 소요 시간 최소화
- KR2: 게시글 작성 실패 시 입력 값 유지율 100%
- KR3: 피드 스크롤 중 이미지 로딩 실패/깜빡임 발생률 0건
- KR4: 좋아요/북마크 클릭 후 UI 반영 지연 체감 0
- KR5: 게시글 수정/삭제 후 피드 상태 일관성 유지(캐시 불일치 0건)
문제 정의(Problems)
-
콘텐츠 생성의 복잡성
- 게시글 작성은 다음 입력을 동시에 요구함.
- 이미지 업로드(복수)
- 텍스트 입력
- 태그
→ 이 중 하나라도 실패하면, 사용자는 다시 처음부터 작성해야 하는 경우가 많음.
- 입력 실패/네트워크 오류 시 작성 중 데이터 손실 가능성이 큼.
- 게시글 작성은 다음 입력을 동시에 요구함.
-
이미지 중심 피드는 성능 부담
- Post 도메인의 피드는 이미지 비중이 매우 높음.
- 스크롤 중 지속적으로 리렌더링 발생
- 최적화 없이 구현 시 스크롤 지연, 이미지 깜빡임, 메모리 사용량 증가하게 된다.
-
상호작용 상태 동기화 문제
- 좋아요 / 북마크는 서버 상태 반영, 동시에 여러 화면에 반영되어야 함.
- 캐시 전략이 명확하지 않으면 클릭했는데 상태가 안바뀌는 UX가 될 수 있고, 화면마다 다른 상태 노출 가능성이 높아짐.
-
수정/삭제 이후 상태 불일치
- 게시글 수정/삭제 이후 피드, 상세 페이지, 마이페이지에서 동일한 게시글 상태가 유지되어야 함.
- 단일 화면만 갱신되면 캐시 불일치, “삭제 했는데 남아있는 게시글” 같은 치명적 UX 발생.
목표가 아닌 것(Non-goals)
이번 프로젝트에서 다루지 않은 내용:
- 게시글 추천 및 랭킹 알고리즘 : 좋아요 수 기반 인기 게시글, 개인화 추천 피드, 랭킹 정렬 로직은 본 도메인 범위에 포함하지 않으며 서버 정책 및 데이터 분석을 포함한 확장 과제로 분리한다.
- 고급 미디어 처리 기능 : 이미지 편집(크롭, 필터, 보정), 동영상 업로드 및 재생, AI 기반 자동 태깅 기능은 지원하지 않으며 기본 이미지 업로드 및 렌더링 까지만 제공한다.
- 게시글 공개 범위 및 접근 제어 : 게시글 공개/비공개, 친구 공개, 차단 사용자 제외 노출 등 접근 제어 정책은 Auth/Privacy 설계와 밀접하게 연관되어 있어 후속 과제로 분리한다.
- 게시글 수정 이력 및 복구 가능 : 게시글 수정 히스토리 관리, 삭제 게시글 복구 기능은 데이터 보존 정책과 연계된 기능으로 이번 범위에서는 다루지 않는다.
설계 및 기술자료(Architecture and Technical Documentation)
아키텍처 개요 (Architecture Overview)
-
프레임워크/라이브러리 : React(v19) + Next.js(v16, App Router)
- 게시글 피드, 상세, 작성/수정 페이지를 파일 기반 라우팅으로 명확히 분리
- 이미지 중심 콘텐츠와 사용자 인터랙션이 많은 특성을 고려하여 CSR 중심으로 설계
-
상태 관리(State Management): Zustand
-
게시글 작성/ 수정 중 임시 입력 데이터 관리
-
좋아요/ 북마크 UI 상태 관리
-
모달, 액션 시트 등 Post 도메인 전역 UI 상태 관리
→ Redux 대비 보일러플레이트가 적고, ContextAPI 대비 렌더링 범위 제어가 용이
-
-
UI 컴포넌트: Post 도메인 전용 로컬 컴포넌트 개발
- 공통 : Button, Icon 등
- 로컬 : PostCard, PostActionBar, ImageGrid, LikeButton, BookmarkButton 등
→ 공통 UI 일관성과 도메인 책임 분리 동시 확보
-
스타일링 : TailwindCSS
- 이미지 비율 유지, 그리드 / 리스트 레이아웃 대응 용이
- 상태 기반 스타일링을 클래스 조합으로 명확히 표현
- 모바일 퍼스트 레이아웃 및 반응형 대응을 단순하게 유지
-
폼 관리 : React Hook Form
- 게시글 작성/수정 폼 관리
- 이미지 업로드 + 텍스트 입력 + 태그 입력 등 복합 폼 처리
- 제출 실패 시 입력값 유지
-
데이터 패칭 : Tanstack Query
- 게시글 목록(피드) :
useQuery/useInfiniteQuery - 게시글 상세 조회 :
useQuery - 게시글 작성 / 수정/ 삭제 :
useMutation - 좋아요 / 북마크 : Optimistic Update 적용, 서버 응답 실패 시 롤백 처리, 관련 게시글 캐시 동기화
- 게시글 목록(피드) :
-
SSR/ISR/CSR 적용 범위
/post(게시글 피드) : 사용자 인터랙션 및 이미지 스크롤 중심→ CSR/post/{postId}(게시글 상세) : 기본 CSR, 공유 / SEO 요구 발생 시 SSR 적용 가능/post/create,/post/edit/{postId}(게시글 작성/수정) : 사용자 중심 페이지 → CSR
주요 페이지 / 컴포넌트 구조
페이지(Pages)
/post: 게시글 피드 페이지(Home Feed)- 주요 기능
- 전체 게시글 피드 조회
- 게시글 카드 단위 렌더링
- 게시글 좋아요 / 북마크
- 게시글 클릭 시 상세 페이지 진입
- 무한 스크롤 기반 추가 로딩
- 사용 컴포넌트
PostFeedLayoutPostListPostCardPostImageCarouselPostActionBarLikeButtonBookmarkButton
PostSkeleton
- 데이터 로딩 시점
- 페이지 최초 진입 시
GET /api/posts?size=…
- 스크롤 하단 도달 시
nextCursor기반 추가 요청
- 페이지 최초 진입 시
- 라우팅 :
/post→ 게시글 피드 유지, 게시글 클릭 →/post/{postId}
- 주요 기능
/post/{postId}: 게시글 상세 페이지- 주요 기능
- 게시글 상세 정보 조회
- 게시글 이미지 전체 보기
- 좋아요 / 북마크
- 게시글 수정 / 삭제(작성자 본인)
- 댓글 영역 진입
- 사용 컴포넌트
PostDetailLayoutPostHeaderPostImageViewerPostContentPostActionBarLikeButtonBookmarkButton
PostMoreActionSheetPostDeleteConfirmModal
- 데이터 로딩 시점
- 페이지 진입 시 →
GET /api/posts/{id}
- 페이지 진입 시 →
- 라우팅 :
/post/{postId}, 수정 클릭 →/post/edit/{postId}, 삭제 완료 → 이전 페이지(/post)로 이동
- 주요 기능
/post/create: 게시글 작성 페이지- 주요 기능
- 이미지 업로드(1장 이상)
- 게시글 내용 입력
- 작성 취소 / 작성 완료
- 작성 중 이탈 방지(confirm modal)
- 사용 컴포넌트
PostFormLayoutPostFormHeaderPostImageUploaderPostContentInputPostSubmitButtonPostCancelConfirmModal
- 데이터 로딩 시점 : 서버 데이터 사전 로딩 없음 , 모든 입력은 클라이언트 상태 기반
- 라우팅 :
/post/create, 작성 완료 →/post/{postId}, 취소 → 이전 페이지
- 주요 기능
/post/edit/{postId}: 게시글 수정 페이지- 주요 기능
- 기존 게시글 데이터 프리필
- 내용 수정
- 수정 취소 / 완료
- 변경 사항 미저장 상태 이탈 방지
- 사용 컴포넌트
PostFormLayoutPostFormHeaderPostImageUploaderPostContentInputPostSubmitButtonPostCancelConfirmModal
- 데이터 로딩 시점
- 페이지 진입 시 →
GET /api/posts/{postId}
- 페이지 진입 시 →
- 라우팅 :
/post/edit/{postId}, 수정 완료 →/post/{postId}
- 주요 기능
주요 컴포넌트(Components)
-
PostFeedLayout개요(Overview)
- 역할 : 게시글 피드 페이지의 전체 레이아웃을 담당하는 컨테이너 컴포넌트
- 상단 헤더, 하단 네비게이션을 포함한 피드 전용 화면 구조를 제공한다.
- 책임
- 피드 영역 스크롤 컨테이너 제공
- Safe Area 및 하단 네비게이션 영역 고려한 레이아웃 유지
- 피드 내부 컴포넌트를 감싸는 역할만 수행
- 설계 의도
- 페이지 레이아웃과 데이터/리스트 로직을 분리하여 레이아웃 변경 시 피드 로직에 영향이 없도록 설계
- 스타일링
- 모바일 기준 full-height 레이아웃
- 하단 네비게이션 영역 padding 확보
- 역할 : 게시글 피드 페이지의 전체 레이아웃을 담당하는 컨테이너 컴포넌트
-
PostList개요(Overview)
-
역할 : 게시글 피드에서 게시글 목록을 순차적으로 렌더링하는 리스트 컴포넌트
-
책임
- 게시글 데이터 배열을 받아
PostCard단위로 렌더링 - 무한 스크롤 트리거 영역 제공
- 로딩/추가 로딩 상태에 따른 Skeleton 표시
- 게시글 데이터 배열을 받아
-
props & Interface
interface PostListProps { posts: PostSummary[]; isLoading: boolean; isFetchingNextPage: boolean; onReachEnd: () => void; // infinite scroll trigger } -
에러 처리 / 엣지 케이스
- posts가 비어있을 경우 EmptyState 렌더링
- 최초 로딩중에는
PostSkeleton표시
-
-
PostCard개요(Overview)
-
역할 : 게시글 피드에서 게시글 1개를 카드 단위로 표현하는 핵심 컴포넌트
-
책임
- 게시글 작성자 정보 표시
- 게시글 이미지 미리보기
- 좋아요 / 북마크 UI 제공
- 카드 클릭 시 상세 페이지 진입 트리거 제공
-
props & Interface
interface PostCardProps { postId: number; author: { memberId: number; nickname: string; profileImageUrl?: string | null; }; imageUrls: string[]; content: string; likeCount: number; liked: boolean; bookmarked: boolean; onClick: () => void; onToggleLike: () => void; onToggleBookmark: () => void; } -
에러 처리 / 엣지 케이스
- 이미지 로딩 실패 시 fallback 이미지 노출
- content가 길 경우 line-clamp 처리
-
-
PostImageCarousel개요(Overview)
-
역할 : 게시글 카드 내부에서 여러 이미지를 슬라이드 형태로 보여주는 컴포넌트
-
책임
- 이미지 배열을 순서대로 렌더링
- 좌우 스와이프 / 페이지 인디케이터 제공
-
props & Interface
interface PostImageCarouselProps { imageUrls: string[]; } -
에러 처리 / 엣지 케이스
- 이미지 배열이 비어있을 경우 placeholder 렌더링
-
-
PostActionBar: 게시글 카드 하단의 공통 액션 영역을 담당 -
LikeButton: 게시글 좋아요 토글 버튼 -
BookmarkButton: 게시글 북마크 토글 버튼 -
PostSkeleton: 게시글 피드 로딩 중 표시되는 Skeleton UI -
PostDetailLayout: 게시글 상세 페이지의 전체 레이아웃 담당 -
PostHeader: 게시글 상단의 메타 정보 및 액션 진입 영역을 담당하는 헤더 컴포넌트 -
PostContent: 게시글 본문 텍스트 영역을 담당 -
PostMoreActionSheet개요(Overview)
-
역할 : 게시글 우측 상단 “⋯” 클릭 시 노출되는 액션 시트
-
책임
- 작성자 본인 여부에 따라 액션 분기
- 본인 : 수정 / 삭제
- 작성자 본인 여부에 따라 액션 분기
-
props & Interface
interface PostMoreActionSheetProps { isOpen: boolean; isOwner: boolean; onEdit: () => void; onDelete: () => void; onClose: () => void; } -
에러 처리 / 엣지 케이스
- isOwner가 false인 경우
- 수정 / 삭제 버튼 미노출
- isOwner가 false인 경우
-
-
PostDeleteConfirmModal개요(Overview)
-
역할 : 게시글 삭제 전 최종 확인을 위한 모달 컴포넌트
-
책임
- 삭제 여부 재확인
- 실수로 인한 데이터 손실 방지
-
props & Interface
interface PostDeleteConfirmModalProps { isOpen: boolean; isLoading?: boolean; onConfirm: () => void; onCancel: () => void; } -
에러 처리 / 엣지 케이스
- 삭제 API 실패 시
- 모달 유지
- 토스트 메시지 표시
- 삭제 API 실패 시
-
-
PostFormLayout: 게시글 작성 페이지의 전체 레이아웃을 담당하는 컨테이너 컴포넌트 -
PostFormHeader: 게시글 작성 페이지 상단의 네비게이션 및 액션 영역 -
PostImageUploader개요(Overview)
-
역할 : 게시글에 첨부할 이미지를 업로드하고 미리보기를 제공하는 컴포넌트
-
책임
- 이미지 파일 선택
- 선택된 이미지 미리보기 렌더링
- 이미지 삭제
-
props & Interface
interface PostImageUploaderProps { images: File[]; onAddImages: (files: File[]) => void; onRemoveImage: (index: number) => void; } -
에러 처리 / 엣지 케이스
- 이미지 0장인 경우
- “이미지를 1장 이상 추가해주세요.” 헬퍼텍스트 노출
- 허용되지 않은 파일 타입 / 용량 초과시
- 에러 헬퍼 텍스트 노출
- 이미지 0장인 경우
-
-
PostContentInput: 게시글 텍스트 내용을 입력받는 컴포넌트 -
PostSubmitButton: 게시글 작성 완료를 트리거하는 하단 고정 버튼 -
PostCancelConfirmModal: 작성 중 페이지 이탈 시 노출되는 확인 모달-
책임
- 작성중인 내용이 사라질 수 있음을 명확히 고지
- 취소 / 계속 작성 선택 제공
-
props / Interface
interface PostCancelConfirmModalProps { isOpen: boolean; onConfirmLeave: () => void; onCancelLeave: () => void; }
-
상태 관리 전략 (State Management Strategy)
1. Global State (Zustand Stores)
-
postFormStore: 게시글 작성/수정 중 발생하는 임시 입력 상태 관리-
초기화 시점:
- 작성 페이지 혹은 수정 페이지 진입 시
resetForm()을 통해 초기화 - API 제출 성공 후
/post/detail로 이동하기 직전 초기화하여 잔여 데이터 삭제.
- 작성 페이지 혹은 수정 페이지 진입 시
-
주요 액션:
setImages(files)/removeImage(index): 이미지 업로드 및 삭제setContent(text): 본문 텍스트 업데이트( 자동으로isDirty : true처리 )startSubmit()/finishSubmit(): 제출 프로세스 시작 및 종료 제어resetForm(): 전체 상태 초기값으로 리셋
-
Store 구조
interface PostFormState { images: File[]; content: string; isDirty: boolean; isSubmitting: boolean; // Actions setImages: (files: File[]) => void; removeImage: (index: number) => void; setContent: (value: string) => void; startSubmit: () => void; finishSubmit: () => void; resetForm: () => void; }
-
-
postUIStore: Post 도메인 내 공통 UI요소(모달, 액션 시트 등) 제어-
초기화 시점:
- 각 모달의
close액션 호출 시 관련 상태 초기화. - 페이지를 완전히 이탈할 경우 전체 초기화
- 각 모달의
-
주요 액션:
- openMoreAction(postId) : 특정 게시글의 더보기 메뉴(수정/삭제) 노출
- openDeleteModal(postId) : 특정 게시글 삭제 확인창 노출
closeAll(): 모든 UI 요소를 닫고activePostId를null로 초기화
-
Store 구조:
interface PostUIState { activePostId?: number; // 현재 제어 대상이 되는 게시글 ID isDeleteModalOpen: boolean; isMoreActionOpen: boolean; // Actions openDeleteModal: (postId: number) => void; openMoreAction: (postId: number) => void; closeAll: () => void; } -
상태 관리 경계:
- Tanstack Query : 서버로부터 받아오는 게시글 목록, 좋아요 상태, 북마크 데이터 등 서버 기준의 데이터를 관리하며, Optimistic Update를 통해 즉각적인 UI 반응을 구현한다.
- Zustand : 사용자가 서버에 전송하기 전의 임시 입력값이나 컴포넌트 트리 깊숙한 곳에서 전역으로 띄워야 하는 UI 상태에 집중한다.
-
-
Local State (React
useState,useReducer)PostContentInput: 입력창 포커스 여부, 실시간 글자 수 카운트 표시PostImageUploader: 이미지 업로드 중 로딩 상태, 드래그 앤 드롭 활성화 여부, 브라우저 메모리용 미리보기(Preview) URLPostCard: 마우스 호버(Hover) 상태에 따른 특정 버튼 노출 여부
-
Server Cache State (Tanstack Query)
서버가 진실의 근원(Source of Truth)인 데이터는 캐싱 라이브러리를 통해 관리하여 자동 갱신 및 데이터 무결성을 유지한다. ex) 메인 피드의 게시글 리스트 데이터, 상세 페이지의 댓글 목록, 사용자의 ‘좋아요’ 및 ‘북마크’ 여부 서버 동기화
API 연동 (API Integration)
-
호출할 백엔드 API 목록:
GET /api/posts?size={size}&after={after}: 게시글 목록 조회 (커서 기반 페이징)GET /api/posts/{id}: 게시글 상세 조회PATCH /api/posts/{id}: 게시글 수정 (내용, 이미지, 태그 수정)DELETE /api/posts/{id}: 게시글 삭제POST /api/posts/{id}/likes: 게시글 좋아요 등록DELETE /api/posts/{id}/likes: 게시글 좋아요 해제POST /api/posts/{id}/bookmarks: 게시글 북마크 등록DELETE /api/posts/{id}/bookmarks: 게시글 북마크 해제
-
API 호출 처리
- Post 도메인 API 호출은 프로젝트 컨벤션에 따라 axios를 사용한다.
- 서버 상태 관리는 React Query를 활용하여 중복 호출 방지, 캐시 관리, 자동 재요청을 처리한다.
- API 요청 상태에 따라 Skeleton UI 또는 Spinner를 노출하며 오류 발생 시 토스트 메시지로 사용자에게 안내한다.
GET /api/posts?size={size}&after={after}: 게시글 목록 조회 (커서 기반 페이징)- 호출 시점
- 홈 피드 페이지 진입 시
- 스크롤 하단 도달 시(무한 스크롤)
- 처리 방식
useInfiniteQuery를 통해 호출
- 캐시 / 상태 반영
- 응답 데이터는 React Query 캐시에 페이지 단위로 저장
- 마지막 게시글 ID를 기준으로 다음 페이지 요청
- 게시글 수정/삭제 발생 시 invalidateQueries([’posts’])로 목록 캐시 갱신
- 호출 시점
GET /api/posts/{id}: 게시글 상세 조회- 호출 시점
- 게시글 상세 페이지 진입 시
- 처리 방식
- useQuery를 통해 호출
- 캐시/상태 반영
- 조회된 게시글 데이터는 [’post’, id] 키로 캐싱
- 좋아요/북마크 변경 시 해당 쿼리만 선택적으로 갱신
- 호출 시점
PATCH /api/posts/{id}: 게시글 수정 (내용, 이미지, 태그 수정)- 호출 시점
- 게시글 수정 페이지에서 저장 버튼 클릭 시
- 처리 방식
useMutation을 통해 호출
- 캐시/상태 반영
- 성공 시
invalidateQueries([’post’, id]) - 목록 화면이 존재하는 경우
invalidateQueries([’posts’])를 함께 호출하여 데이터 동기화
- 성공 시
- 호출 시점
DELETE /api/posts/{id}: 게시글 삭제- 호출 시점
- 삭제 모달에서 삭제 버튼을 클릭 시
- 처리 방식
useMutation을 통해 호출
- 캐시/상태 반영
- 성공 시 invalidateQueries([’posts’])
- 현재 상세 페이지에서 목록 페이지로 이동
- 호출 시점
POST /api/posts/{id}/likes: 게시글 좋아요 등록- 호출 시점
- 좋아요 버튼 클릭 시
- 처리 방식
useMutation+ Optimistic UI 적용
- 캐시 / 상태 반영
- 요청 직후 좋아요 상태 및 카운트 로컬 반영
- 실패 시 이전 상태로 rollback
- 성공 시
invalidateQueries(['post', id])
- 호출 시점
DELETE /api/posts/{id}/likes: 게시글 좋아요 해제- 호출 시점
- 좋아요 활성 상태에서 버튼 클릭 시
- 처리 방식
useMutation+ Optimistic UI 적용
- 캐시/상태 반영
- POST와 동일한 방식으로 optimistic update 및 캐시 갱신
- 호출 시점
POST /api/posts/{id}/bookmarks: 게시글 북마크 등록- 호출 시점
- 북마크 버튼 클릭 시
- 처리 방식
useMutation사용
- 캐시/상태 반영
- 성공 시
invalidateQueries(['post', id]) - 필요 시 북마크 목록 쿼리도 함께 갱신
- 성공 시
- 호출 시점
DELETE /api/posts/{id}/bookmarks: 게시글 북마크 해제- 호출 시점
- 북마크 해제 버튼 클릭 시
- 처리 방식
useMutation사용
- 캐시/상태 반영
- 성공 시 관련 캐시 무효화 및 UI 상태 동기화
- 호출 시점
라우팅 (Routing)
- Next.js App Router 기반 File-based Routing 사용:
- 사용자 행동 흐름과 URL 구조를 1:1로 매핑하여 직관적인 탐색 구조 설계
- 주요 라우트 구조:
/post: 게시글 피드(무한 스크롤 탐색)/post/{postId}: 게시글 상세(조회 및 인터랙션)/post/create: 게시글 작성(이미지 업로드 + 내용 입력)/post/edit/{postId}: 게시글 수정(기존 데이터 프리필)
- Client-side Navigation 적극 활용:
- 피드와 상세 페이지 간 이동 시 전체 리로드 없이 상태를 유지하여 스크롤 위치 및 캐시 보존
- 이동 정책 : 작성/수정 성공 시
/post/{postId}로, 삭제 성공 시/post로 이동.
- 인가 및 접근 제어:
- 로그인 필수 페이지:
/post/create,/post/edit/{postId}는 비로그인 사용자 접근 시 즉시 차단 및 로그인 유도(바텀시트 또는/home리다이렉트). - 권한 검증: 수정/삭제는 작성자 본인만 가능하도록 프론트 UI 차단 및 서버측 최종 검증 병행.
- 로그인 필수 페이지:
폼 처리 및 유효성 검증(Forms & Validation)
- React Hook Form을 사용한 복합 폼 관리
- 이미지와 텍스트가 결합된 복합 폼의 상태 관리를 위해 각 페이지 최상단에서
useForm()호출. - PostContentInput, PostImageUploader 등 입력부 컴포넌트를 분리하여 useFormContext 또는 Controller를 통해 응집도 높은 구조 설계.
- 이미지와 텍스트가 결합된 복합 폼의 상태 관리를 위해 각 페이지 최상단에서
- 유효성 검증 로직(Client-side)
- Zod 스키마 연동 :
resolver를 활용해 선언적 검증 로직 적용.- 내용 : 필수 입력 및 최대 글자 수 제한.
- 이미지 : 최소 1장 이상 필수, 허용되지 않는 파일 형식 및 크기 제한.
- 검증 시점 :
onChange(입력 중),onBlur(포커스 아웃),handleSubmit(제출 시) 등 요구사항에 맞춰 유연하게 적용.
- Zod 스키마 연동 :
- 서버 측(백엔드) 검증 대응:
- 서버 응답 에러(4xx) 발생 시(예: 이미지 누락, 권한 없음), 해당 에러를 폼 필드 또는 토스트 메시지로 노출.
- 실패 복구 UX: 제출 실패 시에도 사용자가 입력한 데이터는 유지하며,
isSubmitting상태를 해제하여 즉시 재시도 가능하도록 처리.
- 이탈 방지 처리 (Unsaved Changes):
- 폼이 수정된 상태(
isDirty === true)에서 페이지 이탈 시 Confirm 모달을 노출하여 사용자 실수로 인한 데이터 유실 방지.
- 폼이 수정된 상태(
용어 정의 (Glossary)
- line-clamp: CSS 속성 중 하나로, 텍스트가 특정 줄 수(line)를 넘어갈 경우 그 뒷부분을 자르고 말줄임표(...) 처리를 해주는 기능
- 페이지 인디케이터 : 사용자에게 "현재 전체 중 어느 위치에 있는지"를 시각적으로 알려주는 UI 요소
- Optimistic Update (낙관적 업데이트): 서버의 응답을 기다리지 않고 UI를 먼저 성공 상태로 변경한 뒤, 서버 요청이 실패할 경우에만 이전 상태로 되돌리는 UX 최적화 기법.
- Inversion of Control (제어의 역전 - 폼 처리):
useFormContext등을 통해 하위 컴포넌트가 직접 폼 상태를 구독하게 하여 Props Drilling을 방지하는 구조 - Safe Area: 모바일 기기의 노치(Notch)나 하단 바 등 화면의 기능적 요소에 콘텐츠가 가려지지 않도록 확보된 안전한 표시 영역