인스타그램 클론 코딩을 하는 중, 알림 탭에서 오늘 날짜에 해당하는 컨텐츠를 필터링 해서 표시하는 작업을 하고 있었다.
컨텐츠 data 에 map 을 돌려 오늘 날짜에 해당되는 컨텐츠면 todayAlarm
이라는 state 배열에 담아지는 구조로 코드를 구현했는데, 오늘에 해당하는 컨텐츠는 하나임에도 똑같은 컨텐츠가 배열 두 번씩 담기는 것이었다.
function AlarmSidebar({ isOpen }) {
const alarmData = [
{
User: {
id: 'nawm_eeee',
nickname: '김진명',
image: '/user-01.jpg',
},
type: 'comment',
content: '안녕하세요 피드가 예쁘네요!',
createdAt: new Date(new Date().setDate(new Date().getDate() - 0)), // 오늘
},
{
User: {
id: 'veenoo',
nickname: '조수빈',
image: '/user.jpg',
},
type: 'mention',
content: '이거 봄?ㅋㅋㅋㅋ',
createdAt: new Date(new Date().setDate(new Date().getDate() - 0)), // 오늘
},
{
User: {
id: 'h._seung',
nickname: '랍뷰희승',
image: '/user.jpg',
},
type: 'mention',
content: '그러자!!',
createdAt: new Date(new Date().setDate(new Date().getDate() - 12)), // 12일 전
},
{
User: {
id: 'jin_woo',
nickname: '김김진진우우',
image: '/user.jpg',
},
type: 'comment',
content: '잘나왔네',
createdAt: new Date(new Date().setDate(new Date().getDate() - 7)), // 7일 전
},
]
const [todayAlarm, setTodayAlarm] = useState([])
useEffect(() => {
alarmData.map((data, index) => {
const currentDay = new Date()
const day = data?.createdAt
// 오늘 날짜인지 체크
if (
currentDay.getFullYear() === day.getFullYear() &&
currentDay.getMonth() === day.getMonth() &&
currentDay.getDate() === day.getDate()
) {
console.log('오늘입니다')
setTodayAlarm((prev) => [...prev, data]);
} else {
console.log('오늘이 아님')
}
})
}, [alarmData.length])
console.log(todayAlarm)
return (
<div
role="dialog"
className={cx(style.sidebar, isOpen && style.opened, style.alarm)}
>
<div className={style.sidebarInner}>
<h3 className={style.sidebarTitle}>알림</h3>
<div className={style.sideBarContent}>
<h4 className={style.sidebarSubTitle}>
<span>오늘</span>
</h4>
</div>
</div>
</div>
)
}
export default AlarmSidebar
map 이 두 번 돌아가는 것으로 보아 렌더링이 한 번 더 일어난 것으로 예상되었고, 구글링을 해본 결과 그 원인을 알아냈다.
리액트는 StrickMode 옵션이 기본으로 설정이 되어있는데, 해당 옵션이 켜져있을 땐 개발모드에서 렌더링이 두 번씩 실행된다고 한다.
StrickMode 의 역할은 아래와 같이 어떠한 기능 사용에 대한 경고나, 부작용 검사 등이 있다고 한다. 공식문서
이 모드를 해제하려면 next.config.js 에서 reactStrictMode 옵션을 false 로 지정해주면 된다.
하지만 개발 모드에서 진행되는 경고나 검사는 필요하기 때문에 StrictMode 를 해제하는 것은 지양하는 것이 좋다.
위의 방법을 쓰기보단, 오늘에 해당하는 컨텐츠 배열 안에 (두 번째 렌더링 때)똑같은 컨텐츠가 들어가지 않도록 방지하는 쪽으로 수정을 했다. tempTodayAlarm
이라는 임시 배열을 하나 만든 뒤 아래 내용으로 필터링된 값을 다시 setTodayAlarm
의 인수값으로 넣었다.
!tempTodayAlarm.some(alarm => alarm.content === data.content)
useEffect(() => {
// 오늘 알림을 임시 배열로 수집
const tempTodayAlarm = [];
alarmData.map((data, index) => {
const currentDay = new Date()
const day = data?.createdAt
// 오늘 날짜인지 체크
if (
currentDay.getFullYear() === day.getFullYear() &&
currentDay.getMonth() === day.getMonth() &&
currentDay.getDate() === day.getDate()
) {
console.log('오늘 입니다')
if (!tempTodayAlarm.some(alarm => alarm.content === data.content)) {
tempTodayAlarm.push(data);
}
} else {
console.log('오늘이 아님')
}
})
// 상태를 한 번만 업데이트
setTodayAlarm(tempTodayAlarm);
}, [alarmData.length])
결과: 오늘에 해당하는 컨텐츠가 정상적으로 한 번씩만 push 된 것을 알 수 있다.