개그를 모아볼 수 있는 페이지
사용 기술 - react-query, styled-components, react, type-script, react-js-pagination
깃허브 페이지 - https://github.com/Nidurolak/asd/blob/master/src/pages/GagList.tsx
이 페이지 역시 골자는 메인 페이지와 비슷하다. 다른 페이지, 개그를 풀어보는 페이지로의 이동을 관리하는 페이지이지만 여기서는 비/로그인 상태를 체크하는 추가적인 코드가 활용되었다. 마켓컬리같은 홈쇼핑 페이지를 보면 비로그인 상태일 때의 장바구니와 로그인 상태일 때의 장바구니가 서로 다른 경우가 있다. 이런 경우는 대게 비로그인 상태일 때에는 로컬 스토리지에 따로 저장한 리스트를 활용하는 것인데, 이것을 따라한 것이다.
const { isLoading } = useQuery ([ "getList" , { page : currentPageNum , size : 15 , sort : selectedOption }],
() => getGagPage ({ page : currentPageNum , size : 15 , sort : selectedOption }),
{
onSuccess : ({ data }) => {
//비회원이라면
if ( getCookie ( "token" ) == null || undefined ){
setTotalNum ( data . data . totalElements )
setContentlist ( data . data . content )
const savedSolvedList = getLocalStorage ( "solvedList" );
if ( savedSolvedList !== null ) { setSolvedlist ( savedSolvedList . split ( ',' ). map ( Number )); }
}
//회원이라면
else {
setTotalNum ( data . data . gagResponseDtoPage . totalElements )
setContentlist ( data . data . gagResponseDtoPage . content )
setSolvedlist ( data . data . visitedGags )
} } } )
이런 구분은 쿼리 연결부분에서 볼 수 있는데, 로그인 상태라면 로컬 스토리지에서 풀어본 개그의 목록을 가져오고, 로그인 상태라면 api가 보내준 리스트를 활용한다.
return ( < BackgroundBox >
< UpsideBox >
<>
< div >
< RadioButton
onClick = { () => handleButtonClick ( 'newest' ) }
style = { { fontWeight : selectedOption === 'newest' ? 'bold' : 'normal' } }
>
최신개그
</ RadioButton >
< span > | </ span >
< RadioButton
onClick = { () => handleButtonClick ( 'weekly' ) }
style = { { fontWeight : selectedOption === 'weekly' ? 'bold' : 'normal' } }
>
주간개그
</ RadioButton >
< span > | </ span >
< RadioButton
onClick = { () => handleButtonClick ( 'alltime' ) }
style = { { fontWeight : selectedOption === 'alltime' ? 'bold' : 'normal' } }
>
인기개그
</ RadioButton >
</ div >
</>
</ UpsideBox >
< GagOverlay ></ GagOverlay >
{ isLoading === false ?( < ListBox >
{ ( contentlist )?. map (( item ) => {
return ( < GagListComp solved = { solvedlist . includes ( item . gagId ) ? true : false} title = { item . title } author = { item . author } answerRate = { item . answerRate } agree = { item . agree } ajae = { item . ajae } gagId = { item . gagId } ></ GagListComp > )})
}
</ ListBox > )
:( < GagListComp solved = {false} author = '김호이' answerRate = {null} agree = { 0 } ajae = { 0 } gagId = { 0 } title = '' ></ GagListComp > ) }
< PageBox >
< Pagination
activePage = { currentPageNum }
itemsCountPerPage = { 15 }
totalItemsCount = { totalNum }
pageRangeDisplayed = { 5 }
prevPageText = { "‹" }
nextPageText = { "›" }
onChange = { ( page ) => setCurrentPageNum ( page ) } />
</ PageBox >
</ BackgroundBox >
);
}
각각의 개그 풀어보기로 가는 버튼은 GagListComp란 이름의 컴포넌트로 분리하여 적용하였다. 페이지네이션은 앞서 사용기술에 적어두었던 라이브러리를 활용한 것으로, 순서대로
현재 페이지
페이지 당 놓일 컴포넌트의 갯수
총 컴포넌트 갯수(여기에 위 숫자를 나눈 것이 총 페이지로 변환)
노출시킬 페이지 범위
이전 페이지 이동 버튼 텍스트
다음 페이지 이동 버튼 텍스트
클릭 시 발동 함수(현재 페이지 숫자 변경용)
로 작동한다.
-개그 리스트 컴포넌트-
const OnDelete = async ( data : any ) => {
const ondel = await DeleteMutation . mutateAsync ( data )
}
const DeleteMutation = useMutation < any >( deleteMyGag , {
onSuccess : ({ data }) => {
window . alert ( "삭제 성공" )
window . location . reload ()
},
onError : ( error ) => {
console . log ( "전송 실패" )
},
});
const handleCloseButtonClick = ( e : React . MouseEvent ) => {
e . stopPropagation ();
if ( window . confirm ( "이 개그를 정말로 삭제하시겠습니까?" )){
OnDelete ({ Id : props . gagId })
}
};
개그 리스트 컴포넌트는 후술할 마이 페이지에서도 사용되는 컴포넌트이다 보니, 여기서는 안 쓰이지만 삭제 관련 api 연결이 있다. api 연결 타입이 대부분 any인데, 지정 타입 설정에서 확인이 불가능한 에러들을 겪고 있어서 임시 방편으로 any를 설정하였다.
return ( < GagBox solved = { props . solved } onClick = { () => { navigate ( `/GagDetail/?id= ${ props . gagId } ` )} } >
< h3 > { props . title } </ h3 >
< h4 > { props . author } </ h4 >
< GagBoxInsideBoxWrapper >
< GagBoxInsideBox >
< span > 정답률 < br /> { props . answerRate !== null ? props . answerRate + "%" : '-' } </ span >
</ GagBoxInsideBox >
< GagBoxInsideBox >
< span > 인정 < br /> { props . agree } </ span >
< span > vs < br /> </ span >
< span > 아재 < br /> { props . ajae } </ span >
</ GagBoxInsideBox >
</ GagBoxInsideBoxWrapper >
{ location . pathname == "/Profile" ? < CloseButton onClick = { handleCloseButtonClick } > X </ CloseButton > : null }
</ GagBox > )
버튼 컴포넌트의 div 구성 자체는 상당히 심플하다. 마지막에 프로필 페이지인지 아닌지만 체크해서 삭제 버튼을 노출시킬지 아닐지 정하고 끝이다.