[FE] 테크 스펙 - 100-hours-a-week/5-yeosa-wiki GitHub Wiki
작성일 : 25 04 28
배경 (Background)
메인 페이지와 앨범 페이지에서 UX를 높이는 것을 목표로 한다.
앨범 생성 시에 효율적으로 S3 서버로 전송하여 URL을 생성한다.
목표가 아닌 것 (Non-goals) (Optional)
이번 프로젝트에서 다루지 않는 내용.
- 사진 최대 전송 개수 개선
- 커뮤니티 기능
- 전체 사진 보관함 기능
- 내 활동 기능
설계 및 기술 자료 (Architecture and Technical Documentation)
페이지
/main
: 메인 페이지
-
주요 기능 : 지도 사진 클러스터링, 앨범 목록.
-
사용 컴포넌트
Map
(지도)AlbumList
(앨범 목록)ImageGrid
AlbumThumbnail
-
데이터 로딩 시점
- 페이지 진입 시 : 전체 앨범 목록 조회
GET /api/album/monthly?yearMonth={yearmonth}
호출 - 앨범 선택 시 : 앨범 요약 조회
GET /api/album/{albumId}/summary
- 페이지 진입 시 : 전체 앨범 목록 조회
/album
: 앨범 페이지
-
주요 기능 : 앨범 요약 정보 확인, 사진 기록 표시, 카테고리 선택, 링크 공유
-
사용 컴포넌트
Card
Collection
Highlight
Header
-
데이터 로딩 시점
페이지 진입 시 앨범 상세 조회GET /api/album/{albumId}
호출 -
라우팅 :
/album/{albumId}
/collection
: 보관함 페이지
- 주요 기능 : 선택 사진 제거, 이미지 자세히 보기
- 사용 컴포넌트 :
- 이미지 컴포넌트
- 데이터 로딩 시점 : 페이지 진입 시
GET /api/album/{albumId}
(최신 데이터 갱신을 위해서 로딩 후 필터링) - 라우팅 :
/album/{albumId}/collection/{collectionId}
/album/editor
: 앨범 생성 페이지
- 주요 기능 : 사진 추가. 앨범 생성
- 사용 컴포넌트
Input
ImageGrid
/login
- 주요 기능 : OAuth를 이용한 로그인
/share
: 링크 전송시 미리보기 페이지
주요 컴포넌트
main 페이지
앨범 목록 컴포넌트
-
개요
- 역할 : 월별 앨범 목록 표시. 선택 및 앨범 상세 페이지로 진입. 무한 스크롤 기능을 수행
-
Props & Interface
- startMonth : 월별로 앨범(썸네일)을 보여준다. 현재 월을 첫번째 월로 가진다. 컴포넌트 마운트 시, 현재 날짜를 기준으로 설정한다.
- endMonth : 현재 로딩된 마지막 월을 나타낸다. (25년 3월 -> 2503 과 같이 나타낸다.)
- isEndPage : 사용자가 가지고 있는 앨범을 더 로드할 수 있다면 true이다.
interface AlbumList {
startMonth : number
endMonth : number
isEndPAge : boolean
onAlbumSelect?: (albumId: string) => void;
onMonthChange?: (newMonth: number) => void;
}
- 내부 상태 & 이벤트
- month : 현재 주시하고 있는 월
- useInfiniteQuery : 무한 스크롤에 이용
- onAlbumSelect : 선택 앨범 변경 시 부모에게 알리는 콜백 함수
- onMonthChange : 주시하고 있는 월이 변경되었을 때, 부모에게 알리는 콜백 함수
ImageGrid 컴포넌트
-
개요
- 역할 : 이미지 그리드 표시,
DnD를 사용한 순서 변경 기능, Lazy Loading 기능 - (앨범 상세 페이지에서도 재사용?)
- 역할 : 이미지 그리드 표시,
-
Props & Interface
- row : 열 개수 (default 4)
- gap : 이미지 사이 간격 (default 0)
- images : 이미지 목록
- isEmpty : 비어있는 경우 다른 UI 표시.
interface ImageGrid{
row : number
gap? : number
isEmpty : boolean
images: ImageItem[];
onImageClick?: (imageId: string) => void;
loadingPlaceholder?: ReactNode;
}
interface ImageItem {
id: number;
url: string;
isLoaded?: boolean;
shouldLoad?: boolean;
}
- 내부 상태 & 이벤트
- isLoading : 로딩 상태
- onImageClick : 이미지 클릭 시 부모에게 알리는 콜백 함수
앨범 썸네일 컴포넌트 :
-
개요
- 역할 : 앨범의 대표 사진을 표시, 공동작업자가 있는 경우 표시한다.
-
Props & Interface
- imageUrl : 썸네일로 표시할 이미지
- hasCoworkers : 개인 앨범인지, 공유 앨범인지 나타낸다.
- coworkerIds : 공동작업자의 정보 목록. 공동 작업자의 프로필 사진을 표시하는데 사용한다.
- albumId : 앨범으로 이동 시 사용한다.
- isSelected : 앨범이 선택된 상태인지 나타냄. 공유 받은 사람의 초기 화면이나, 되돌아왔을 때에 사용
interface AlbumThumbnail {
imageUrl : string
hasCoworkers : boolean
coworkerIds : number[]
albumId : number;
onSelect?: (isSelected: boolean) => void;
isSelected?: boolean;
}
내부 상태 & 이벤트
- isSelected : 선택 시 지도 컴포넌트를 변경시키고, 다시 클릭 시, 앨범 페이지로 이동한다.
- isLoading : 이미지 로딩 상태
- hasError : 오류 상태
- onSelect : 선택되었을 때, 부모 컴포넌트에게 알리는 콜백함수
지도 컴포넌트
- 개요
- 역할 : 지도 스크롤 및 확대 등 조작, 마커 클러스터 표시, 대표 사진 표시, 경로 표시
- Props & Interface
- isAlbumSelected : 앨범이 선택되었을 경우와 아닌 경우, UI의 차이를 주기 위한 속성
- selectedAlbumId : 현재 선택된 앨범의 Id
- photoLocations : 앨범에 포함된 사진들의 위치 및 표시에 필요한 데이터
- initialCenter : 초기 중심 좌표
- initialZoom : 초기 줌 레벨
- clusterThreshold : 클러스터를 하나로 모으는 단위
- onMapBoundsChange : 맵 바운더리가 변경되었을 때 로직을 처리하는 함수
interface Map{
isAlbumSelected : boolean;
selectedAlbumId?: number;
photoLocations?: PhotoLocation[];
initialCenter?: Coordinates;
initialZoom?: number;
clusterThreshold?: number;
onMapBoundsChange?: (bounds: MapBounds) => void;
}
interface PhotoLocation {
id: string;
coordinates: Coordinates;
thumbnailUrl: string;
timestamp?: number;
}
interface Coordinates {
latitude: number;
longitude: number;
}
interface MapBounds {
north: number;
south: number;
east: number;
west: number;
}
- 상태 및 이벤트
- center : 현재 지도 중심 좌표
- zoom : 현재 지도 줌 레벨
- clusters : 클러스터 목록
- bounds : 현재 지도 경계
- isLoading : 로딩 상태
album 페이지
앨범 컴포넌트
-
개요
- 역할 : 앨범 내의 이미지 관리. 데이터 페칭을 수행.
-
Props & Interface
interface Album {
id : number
title : string
collections : Collection[];
imageCount: number;
coverImageUrl?: string;
owner: User;
collaborators?: User[];
isShared: boolean;
}
- 내부 상태 & 이벤트
- collections : 카테고리 별 보관함
보관함 컴포넌트 :
-
개요
- 역할 : 태그에 해당하는 사진을 보유하며, 삭제 기능을 수행한다.
-
Props & Interface
- id : 보관함 인덱스
- title : 보관함 제목 (태그명 또는 검토할 항목)
- images : 포함된 사진 목록
- onRemove : 사진 삭제 시, 부모에게 알리는 콜백
interface Collection {
id : number
title : string
images : Image[]
onRemove?: (imageId : number) => void;
}
- 내부 상태 & 이벤트
- images : 이미지 목록
- handleRemove(selectedList) : 사진 삭제 시 부모 컴포넌트에 알림
Card 컴포넌트
-
개요
- 역할 : 대표 사진 및 요약 정보와, 지도를 보여준다.
-
Props & Interface
interface Card {
imageUrl : boolean
center : Coordinates
}
- 내부 상태 & 이벤트
- isFront : 카드 앞 뒷면 구분
- handleFlip : 카드 상태 변경 처리
Image 컴포넌트
- 개요
- 역할 : 이미지 표시, 이미지 최적화 기능 수행
- Next.js 의 이미지 컴포넌트 활용
- Props & Interface
- imageId : 이미지 식별자
- imageUrl : 이미지 경로
interface Image{
id : number
url : string
index : number
}
상태 관리 전략 (State Management Strategy)
Global State (Zustand Stores) :
- 중앙 스토어
- 사용자 정보
- 인증 상태
- 로딩 상태
메인 페이지
albumStore
imageStore
locationStore
앨범 페이지 - UI 상태 - 선택된 이미지 목록, 개수 - 편집 모드 상태 - 모달 상태