[ 목차 ]
728x90
반응형
들어가며
이번에는 Toast UI 를 활용해서 간단하게 메인 페이지에 팝업창을 띄우는 예시를 포스팅하게 되었다.
Toast UI 기본 설명
https://hunseop2772.tistory.com/360
POPUP 창 미리보기
제목은 헤더 쪽 문구를 표현하고 시작일과 종료일은 팝업이 뜨는 기간을 설정하게 하였다.
해당 설정한 날짜와를 DB에 저장하게 되고 이 날짜와 현재 날짜가 동일하면 메인페이지에 여러 개가 뜨게 만들었다. 팝업창에서 '오늘 하루 보지 않기' 를 클릭하게 되면 하루 동안 보이지 않는 기능이 있다. 해당 코드 일부는 하단에서 간단하게 주석으로 설명해보겠다.
1. 팝업 상태 관리와 필터링 (LoginForm.tsx):
// Redux로부터 팝업 데이터를 가져옴
const { popups } = useSelector((state: RootState) => state.noti);
// 현재 표시할 팝업들을 관리하는 state
const [currentPopups, setCurrentPopups] = useState([]);
useEffect(() => {
// popups가 존재하고 길이가 0보다 클 때만 실행
if (popups && popups.length > 0) {
// 현재 날짜를 YYYY-MM-DD 형식으로 가져옴
const today = new Date().toISOString().split('T')[0];
// Array.filter() 메소드를 사용하여 표시할 팝업 필터링
const validPopups = popups.filter(popup => {
// 팝업의 시작일과 종료일을 YYYY-MM-DD 형식으로 변환
const startDate = new Date(popup.popupStartDate).toISOString().split('T')[0];
const endDate = new Date(popup.popupEndDate).toISOString().split('T')[0];
// localStorage에서 "보지 않기" 설정 확인// `popup_${popup.id}_dontShowUntil` 형식의 키를 사용
const dontShowUntil = localStorage.getItem(`popup_${popup.id}_dontShowUntil`);
// 날짜 유효성 검사 (시작일 <= 오늘 <= 종료일)
const isValidDate = today >= startDate && today <= endDate;
// "보지 않기" 설정 확인// dontShowUntil이 없거나(null) 저장된 날짜가 오늘보다 이전인 경우 true
const shouldShow = !dontShowUntil || dontShowUntil < today;
// 두 조건이 모두 true일 때만 팝업 표시
return isValidDate && shouldShow;
});
// 필터링된 팝업들을 state에 저장
setCurrentPopups(validPopups);
}
}, [popups]);// popups가 변경될 때마다 실행
2. 팝업 닫기 처리 (LoginForm.tsx):
// useCallback을 사용하여 메모이제이션된 함수 생성
const handleClosePopup = useCallback((popupId: number, dontShowToday: boolean) => {
if (dontShowToday) {
// 오늘 날짜를 YYYY-MM-DD 형식으로 가져옴
const today = new Date().toISOString().split('T')[0];
// localStorage에 저장 - 키: popup_[팝업ID]_dontShowUntil, 값: 오늘 날짜
localStorage.setItem(`popup_${popupId}_dontShowUntil`, today);
}
// 현재 팝업 목록에서 해당 팝업 제거// prev: 이전 상태값
setCurrentPopups(prev => prev.filter(popup => popup.id !== popupId));
}, []);// 의존성 배열이 비어있으므로 컴포넌트가 마운트될 때 한 번만 생성
3. 팝업 컴포넌트 구현 (BoardDetailPop.tsx):
// 타입스크립트 인터페이스 정의
interface BoardDetailModalProps {
isOpen: boolean;// 팝업 열림 상태
onClose: (dontShowToday: boolean) => void;// 닫기 핸들러
board: any;// 팝업 데이터
initialPosition: { x: number; y: number };// 팝업 초기 위치
}
const BoardDetailPop: React.FC<BoardDetailModalProps> = ({
isOpen,
onClose,
board,
initialPosition
}) => {
// "오늘 하루 보지 않기" 체크박스 상태 관리
const [dontShowToday, setDontShowToday] = useState<boolean>(false);
// 팝업 닫기 핸들러
const handleClose = () => {
// 부모 컴포넌트로 체크박스 상태 전달
onClose(dontShowToday);
};
// board 데이터가 없으면 null 반환
if (!board) return null;
return (
<Modal
isOpen={isOpen}
onClose={handleClose}
title={board.title}
width={650}
height={900}
initialPosition={initialPosition}
>
<Box sx={{ p: 2, height: '100%', overflow: 'auto' }}>
{/* 팝업 내용 */}
<Viewer initialValue={board.content} />
{/* 팝업 타입이 POPUP일 때만 "오늘 하루 보지 않기" 표시 */}
{board.type === 'POPUP' && (
<Box sx={{
mt: 2,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<FormControlLabel
control={
<Checkbox
checked={dontShowToday}
onChange={(e) => setDontShowToday(e.target.checked)}
/>
}
label="오늘 하루 보지 않기"
/>
<Button variant="contained" onClick={handleClose}>
닫기
</Button>
</Box>
)}
</Box>
</Modal>
);
};
4. 팝업 렌더링 (LoginForm.tsx):
return (
<div id="login_wrap">
{/* 현재 표시할 팝업들을 map으로 순회하여 렌더링 */}
{currentPopups.map((popup, index) => (
<BoardDetailPop
key={popup.id}// React 리스트 렌더링시 필수 key prop
isOpen={true}
onClose={(dontShowToday) => handleClosePopup(popup.id, dontShowToday)}
board={popup}
// 팝업창들이 겹치지 않도록 위치 설정
initialPosition={{
x: 100 + (index * 50),
y: 20 + (index * 50)
}}
/>
))}
{/* 로그인 폼 렌더링 */}
</div>
);
주요 문법 포인트:
- 타입스크립트 인터페이스:
- BoardDetailModalProps로 props의 타입을 명확히 정의
- React.FC<Props>로 함수형 컴포넌트 타입 지정
- useState 훅:
- const [state, setState] = useState<Type>(initialValue)
- 제네릭으로 상태의 타입을 명시
- useEffect 훅:
- useEffect(() => { ... }, [dependencies])
- 의존성 배열의 값이 변경될 때마다 실행
- useCallback 훅:
- 함수를 메모이제이션하여 불필요한 리렌더링 방지
- useCallback(() => { ... }, [dependencies])
- localStorage API:
- setItem(key, value): 데이터 저장
- getItem(key): 데이터 조회
- 문자열 형태로만 데이터 저장 가능
- 조건부 렌더링:
- && 연산자 사용: {condition && <Component />}
- 삼항 연산자 사용: {condition ? <Component1 /> : <Component2 />}
- 배열 메서드:
- filter(): 조건에 맞는 요소만 필터링
- map(): 배열의 각 요소를 변환하여 새로운 배열 생성
이러한 방식으로 구현된 "오늘 하루 보지 않기" 기능은 사용자 경험을 향상시키면서도 효율적인 상태 관리와 데이터 지속성을 제공합니다.
반응형
'Next.js' 카테고리의 다른 글
Next.js의 렌더링 전략: SSR과 CSR, SSG (Hydration, Pre-rendering, Dynamic Routes, Build Time, ISR ) (4) | 2024.11.11 |
---|---|
React의 고차 컴포넌트(HOC) (0) | 2024.11.10 |
React 기본 용어 및 개념 (Component, Props, State, 랜더링....) 핵심 흐름 (3) | 2024.11.09 |
[Next.js] TOAST UI 이미지 Blob 처리 및 활용 1편 (1) | 2024.10.27 |
React에서 Redux, Reducer, 그리고 Redux-Saga를 활용한 상태 관리(Redux-DevTools) (0) | 2024.07.28 |