11import { useNavigate } from 'react-router-dom' ;
22import { ToggleTab , ReviewStepLayout , TheaterList } from '@/components' ;
3- import { useState , useEffect } from 'react' ;
3+ import { useState , useEffect , useRef , useCallback } from 'react' ;
44import { useReviewStore } from '@/store' ;
5- import { useTheatersQuery } from '@/hooks/queries/useTheatersQuery ' ;
5+ import { getTheaters } from '@/api/theater/theater.api ' ;
66import type { CinemaFormat } from '@/types/onboarding' ;
7+ import type { Theater } from '@/types/theater' ;
78
89export default function CinemaSelect ( ) {
910 const { isInitialized } = useReviewStore ( ) ;
@@ -12,21 +13,81 @@ export default function CinemaSelect() {
1213 const [ selectedTab , setSelectedTab ] = useState < CinemaFormat > ( 'IMAX' ) ;
1314 const [ selectedAuditorium , setSelectedAuditorium ] = useState < string | null > ( null ) ;
1415
15- const { data : theaters } = useTheatersQuery ( { type : selectedTab , page : 1 , size : 10 } ) ;
16+ // 무한스크롤용 상태
17+ const [ theaters , setTheaters ] = useState < Theater [ ] > ( [ ] ) ;
18+ const [ page , setPage ] = useState ( 1 ) ;
19+ const [ hasNext , setHasNext ] = useState ( true ) ;
20+ const [ isLoading , setIsLoading ] = useState ( false ) ;
21+ const observerRef = useRef < HTMLDivElement > ( null ) ;
1622
23+ // 초기화 조건
1724 useEffect ( ( ) => {
1825 if ( ! isInitialized ) {
1926 navigate ( '/review' ) ;
2027 }
2128 } , [ isInitialized , navigate ] ) ;
2229
30+ // theater 목록 초기화 (탭 변경 시)
31+ useEffect ( ( ) => {
32+ const reset = async ( ) => {
33+ setPage ( 1 ) ;
34+ setTheaters ( [ ] ) ;
35+ setHasNext ( true ) ;
36+ try {
37+ const res = await getTheaters ( { type : selectedTab , page : 1 , size : 10 } ) ;
38+ setTheaters ( res . content ) ;
39+ setHasNext ( res . hasNext ) ;
40+ setPage ( 2 ) ;
41+ } catch ( err ) {
42+ console . error ( '초기 로딩 실패:' , err ) ;
43+ }
44+ } ;
45+ reset ( ) ;
46+ } , [ selectedTab ] ) ;
47+
48+ // theater 추가 로딩
49+ const loadMore = useCallback ( async ( ) => {
50+ if ( isLoading || ! hasNext ) return ;
51+ setIsLoading ( true ) ;
52+ try {
53+ const res = await getTheaters ( { type : selectedTab , page, size : 10 } ) ;
54+ setTheaters ( ( prev ) => [ ...prev , ...res . content ] ) ;
55+ setHasNext ( res . hasNext ) ;
56+ setPage ( ( prev ) => prev + 1 ) ;
57+ } catch ( err ) {
58+ console . error ( '추가 로딩 실패:' , err ) ;
59+ } finally {
60+ setIsLoading ( false ) ;
61+ }
62+ } , [ isLoading , hasNext , selectedTab , page ] ) ;
63+
64+ // IntersectionObserver 연결
65+ useEffect ( ( ) => {
66+ const observer = new IntersectionObserver (
67+ ( [ entry ] ) => {
68+ if ( entry . isIntersecting && ! isLoading && hasNext ) {
69+ loadMore ( ) ;
70+ }
71+ } ,
72+ { rootMargin : '100px' , threshold : 0.7 } ,
73+ ) ;
74+
75+ if ( observerRef . current ) observer . observe ( observerRef . current ) ;
76+
77+ return ( ) => {
78+ if ( observerRef . current ) observer . unobserve ( observerRef . current ) ;
79+ } ;
80+ } , [ loadMore , isLoading , hasNext ] ) ;
81+
82+ // 탭 변경
2383 const handleTabChange = ( tab : string ) => {
2484 setSelectedTab ( tab as CinemaFormat ) ;
2585 setSelectedAuditorium ( null ) ;
2686 } ;
2787
88+ // 다음 단계로 이동
2889 const handleNext = ( ) => {
29- const selected = theaters ? .find ( ( d ) => d . auditoriumId === selectedAuditorium ) ;
90+ const selected = theaters . find ( ( d ) => d . auditoriumId === selectedAuditorium ) ;
3091 if ( ! selected ) return ;
3192
3293 navigate ( '/review/info' , {
@@ -58,10 +119,11 @@ export default function CinemaSelect() {
58119
59120 { /* 영화관 목록 */ }
60121 < TheaterList
61- data = { theaters ?? [ ] }
122+ data = { theaters }
62123 selected = { selectedAuditorium ? [ selectedAuditorium ] : [ ] }
63124 onSelect = { ( id ) => setSelectedAuditorium ( id ) }
64125 />
126+ { hasNext && < div ref = { observerRef } className = "h-[100px]" /> }
65127 </ ReviewStepLayout >
66128 ) ;
67129}
0 commit comments