11import React , { useState , useEffect , useRef } from 'react' ;
2+ import debounce from 'lodash/debounce' ;
23import { OOTDContainer , FeedContainer } from './styles' ;
34import Feed from './Feed/index' ;
45import { getPostListApi } from '@apis/post' ;
56import type { PostSummary } from '@apis/post/dto' ;
67import { handleError } from '@apis/util/handleError' ;
78import type { ModalProps } from '@components/Modal/dto' ;
89import Modal from '@components/Modal' ;
9- import debounce from 'lodash/debounce' ;
1010
1111const OOTD : React . FC = ( ) => {
1212 const [ feeds , setFeeds ] = useState < PostSummary [ ] > ( [ ] ) ;
@@ -16,12 +16,15 @@ const OOTD: React.FC = () => {
1616
1717 const [ reachedEnd , setReachedEnd ] = useState ( false ) ;
1818 const [ isFetching , setIsFetching ] = useState ( false ) ;
19- const feedPageRef = useRef ( 1 ) ;
19+
20+ const feedPageRef = useRef ( 1 ) ; // 현재 페이지 번호를 참조하는 변수, 리렌더링 없이 값만 업데이트 하기 위해 상태가 아닌 useRef 사용
21+
22+ // 세션 스토리지에서 이전 스크롤 위치를 가져와 초기화
2023 const savedScrollPosition = sessionStorage . getItem ( 'scrollPosition' ) ;
2124 const scrollPositionRef = useRef ( Number ( savedScrollPosition ) || 0 ) ;
2225
23- const observerRef = useRef < IntersectionObserver | null > ( null ) ;
24- const loadMoreRef = useRef < HTMLDivElement | null > ( null ) ;
26+ const observerRef = useRef < IntersectionObserver | null > ( null ) ; // IntersectionObserver 인스턴스를 참조하는 변수
27+ const loadMoreRef = useRef < HTMLDivElement | null > ( null ) ; // 더 많은 데이터를 로드할 때 관찰할 마지막 요소의 DOM을 참조
2528
2629 // 전체 게시글(피드) 조회 API
2730 const getPostList = async ( ) => {
@@ -51,42 +54,46 @@ const OOTD: React.FC = () => {
5154 useEffect ( ( ) => {
5255 if ( reachedEnd && observerRef . current && loadMoreRef . current ) {
5356 observerRef . current . unobserve ( loadMoreRef . current ) ;
54- return ; // 더 이상 옵저버 실행 안 함
57+ return ; // 데이터의 끝에 다다르면 옵저버 해제. (더이상 피드가 없으면)
5558 }
5659
57- observerRef . current = new IntersectionObserver (
60+ observerRef . current = new IntersectionObserver ( // Intersection Observer 생성
5861 debounce ( ( entries ) => {
5962 const target = entries [ 0 ] ;
6063 console . log ( 'Intersection Observer:' , target . isIntersecting ) ;
6164 if ( target . isIntersecting && ! isFetching && ! reachedEnd ) {
65+ // 요소가 화면에 보이고 있고, 요청 중이 아니며며 끝에 도달하지 않았다면 api 호출
6266 getPostList ( ) ;
6367 }
64- } , 300 ) , // 디바운스 적용
68+ } , 300 ) , // 디바운스 적용해 스크롤 이벤트 제어. 스크롤마다 이벤트 호출하는 것이 아닌 마지막 스크롤 이후 300ms동안 동작이 없으면 이벤트 호출
6569 {
6670 root : null ,
67- rootMargin : '100px' , // 미리 데이터 로드
68- threshold : 0 , // 요소가 조금이라도 보이면 트리거
71+ rootMargin : '100px' , // // 요소가 보이기 100px 전에 미리 데이터 로드
72+ threshold : 0 , // 요소가 아주 조금이라도 보이면 트리거
6973 } ,
7074 ) ;
7175
76+ // 옵저버를 마지막 요소에 연결
7277 if ( loadMoreRef . current ) {
7378 observerRef . current . observe ( loadMoreRef . current ) ;
7479 }
7580 return ( ) => {
81+ // 컴포넌트가 언마운트되거나 의존성이 변경될 때 옵저버 해제
7682 if ( observerRef . current && loadMoreRef . current ) {
7783 observerRef . current . unobserve ( loadMoreRef . current ) ;
7884 }
7985 } ;
8086 } , [ isFetching , reachedEnd ] ) ;
8187
8288 useEffect ( ( ) => {
83- // 초기 데이터 로드
89+ // 첫 로드 시 API 호출
8490 getPostList ( ) ;
8591
86- // 세션 저장된 스크롤 위치 복원
92+ // 세션 저장된 이전 스크롤 위치 복원
8793 window . scrollTo ( 0 , scrollPositionRef . current ) ;
8894
8995 return ( ) => {
96+ // 컴포넌트 언마운트 시 현재 스크롤 위치를 세션 스토리지에 저장
9097 sessionStorage . setItem ( 'scrollPosition' , String ( window . scrollY ) ) ;
9198 } ;
9299 } , [ ] ) ;
@@ -106,6 +113,7 @@ const OOTD: React.FC = () => {
106113 < Feed feed = { feed } />
107114 </ div >
108115 ) ) }
116+ { /* Intersection Observer가 감지할 마지막 요소 */ }
109117 < div ref = { loadMoreRef } style = { { height : '1px' , backgroundColor : 'transparent' } } />
110118 </ FeedContainer >
111119 { isStatusModalOpen && < Modal { ...statusModalProps } /> }
0 commit comments