์นดํ ๊ณ ๋ฆฌ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋๋กญ๋ค์ด ๋ฉ๋ด๊ฐ ์ด๋ฆฌ๊ณ ๋ค์ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋๋กญ๋ค์ด ๋ฉ๋ด๋ฅผ ๋ซ๊ณ ์ถ์์ง๋ง ๋ด๊ฐ ์ํ๋ ๋๋ก ๋์ํ์ง ์์๋ค.


import { useEffect, useState } from 'react'
import styles from './Category.module.css'
import TECH_IMAGE from '../../utils/constant/techImage'
import ARROW_ICON from '../../utils/constant/arrowIcon'
const ALL = 'ALL'
const CATEGORY = 'category'
const CATEGORY_ARRAY = [
'ALL',
'HTML',
'CSS',
'JAVASCRIPT',
'REACT',
'TYPESCRIPT',
'NEXT.JS',
'GIT',
'CS'
]
function Category() {
const [isOpen, setIsOpen] = useState(false)
const [currentCategory, setCurrentCategory] = useState(ALL)
useEffect(() => {
document.addEventListener('click', handleClick)
return () => {
document.removeEventListener('click', handleClick)
}
}, [])
const handleClick = (e) => {
if (e.target.dataset.status === CATEGORY) {
setIsOpen(!isOpen)
return
}
setIsOpen(false)
}
const handleCurrentCategory = (e) => {
setCurrentCategory(e.target.textContent)
}
return (
<>
<div className={styles.category}>
<div data-status={CATEGORY} className={styles.buttonBox}>
<button data-status={CATEGORY} className={styles.button}>
<img
className={styles.categoryIcon}
src={TECH_IMAGE[currentCategory]}
alt={`์นดํ
๊ณ ๋ฆฌ ${currentCategory} ์์ด์ฝ`}
/>
{currentCategory}
</button>
<img
data-status={CATEGORY}
className={styles.arrowIcon}
src={ARROW_ICON[isOpen]}
alt="์นดํ
๊ณ ๋ฆฌ ํ ๊ธ ๋ฒํผ"
/>
</div>
{isOpen && (
<ul className={styles.listBox}>
{CATEGORY_ARRAY.map((category, idx) => {
return (
<li
onClick={(e) => handleCurrentCategory(e)}
key={idx}
className={styles.list}
>
<img
className={styles.categoryIcon}
src={TECH_IMAGE[category]}
alt={`์นดํ
๊ณ ๋ฆฌ ${category} ์์ด์ฝ`}
/>
{category}
</li>
)
})}
</ul>
)}
</div>
</>
)
}
export default Category
์๋ ์ฝ๋์ ๊ฐ์ด isOpen์ ์ฝ์์ ์ถ๋ ฅํด๋ณด๋ฉด ๋๋กญ๋ค์ด์ด ์ด๋ ค์์์๋ false๋ก ์ถ๋ ฅ๋๋ ๊ฒ์ ํ์ธํ๋ค.
useEffect(() => {
document.addEventListener('click', handleClick)
return () => {
document.removeEventListener('click', handleClick)
}
}, [])
const handleClick = (e) => {
console.log(isOpen)
if (e.target.dataset.status === CATEGORY) {
setIsOpen(!isOpen)
return
}
setIsOpen(false)
}
๋๋กญ๋ค์ด์ด ์ด๋ ค์์ผ๋ฉด isOpen์ด true์ํ์ธ๋ฐ ์ false๊ฐ ์ถ๋ ฅ๋ ๊น?
์ด ๋ฌธ์ ๋ JavaScript์ ํด๋ก์ (Closure) ๊ฐ๋ ๊ณผ ๊ด๋ จ์ด ์๋ค.
ํด๋ก์ (Closure)๋?
ํจ์๊ฐ ์์ค๋ ๋ ํด๋น ํจ์๊ฐ ์ํ ๋ ์์ปฌ ํ๊ฒฝ์ ๊ธฐ์ตํ๋ ๊ฒ์ด๋ค. ์ค์ํ ์ ์ ํจ์๊ฐ ์์ฑ๋ ๋ ์ธ๋ถ ๋ณ์์ ๋ํ ์ฐธ์กฐ๋ฅผ ๊ธฐ์ตํ๋ ๊ฒ์ด๋ค.
addEventListener์ ๋ฑ๋ก๋ ์ฝ๋ฐฑ ํจ์๊ฐ ์ต์ด์ state ๋ณ์์ ๊ฐ์ ์ฐธ์กฐํ ๋์ ๊ฐ์ ๊ธฐ์ตํ๊ณ ์๊ณ
์ด ์ฝ๋ฐฑ ํจ์๋ ์ดํ์๋ state์ ๋ณ๊ฒฝ ์ฌํญ์ ์์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ๋ฆฌ๋ ๋๋ง ํ์๋ ์ด์ ์ฐธ์กฐ ๊ฐ์ ์ ์งํ๋ค.
๋ด ์ฝ๋์์ isOpen์ false๋ก ์ด๊ธฐ ๊ฐ์ ์ค์ ํ๊ธฐ ๋๋ฌธ์ handleClickํจ์์์ isOpen์ ์ฐธ์กฐ ๊ฐ์ ์ธ์ ๋ false๋ก ๊ธฐ์ต๋์ด์ setIsOpen(!isOpen)์ ํด๋ ๋๋กญ๋ค์ด ๋ฉ๋ด๊ฐ false๊ฐ ๋์ง ์์๋ ๊ฒ์ด๋ค.
1. useEffect๋ฅผ ๋ ๋๋ง ํ ๋ ๋ง๋ค ์คํ
โช ์ด๋ ๊ฒ ํ๋ฉด ๋ ๋๋ง์ด ๋ ๋ ๋ง๋ค useEffect๋ฅผ ์คํ ํ๊ธฐ ๋๋ฌธ์ state์ ๊ฐ์ ์ถ์ ํ ์ ์๋ค.
// ๋ ๋๋ง ๋ ๋ ๋ง๋ค ์คํ
useEffect(() => {
document.addEventListener('click', handleClick)
return () => {
document.removeEventListener('click', handleClick)
}
})
const handleClick = (e) => {
console.log(isOpen)
if (e.target.dataset.status === CATEGORY) {
setIsOpen(!isOpen)
return
}
setIsOpen(false)
}
2. useEffect์ dependency array(์์กด์ฑ ๋ฐฐ์ด)์ state ๊ฐ ์ถ๊ฐํ๊ธฐ
โช ์ด ๋ฐฉ๋ฒ๋ 1๋ฒ ๋ฐฉ๋ฒ๊ณผ ๋น์ทํ๊ฒ state ๊ฐ์ด ๋ณํ ๋ ๋ง๋ค useEffect๋ฅผ ์คํํ์ฌ ๋งค๋ฒ ์ต์ state ๊ฐ์ ์ฐธ์กฐํ ์ ์๋ค.
// isOpen(state) ๊ฐ์ด ๋ณํ ๋ ๋ง๋ค ์คํ
useEffect(() => {
document.addEventListener('click', handleClick)
return () => {
document.removeEventListener('click', handleClick)
}
}, [isOpen])
const handleClick = (e) => {
console.log(isOpen)
if (e.target.dataset.status === CATEGORY) {
setIsOpen(!isOpen)
return
}
setIsOpen(false)
}
3. setterํจ์์ ํจ์ํ ์
๋ฐ์ดํธ ์ฌ์ฉ
โช ํจ์ํ ์
๋ฐ์ดํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ด์ ์ํ ๊ฐ์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์์์ ์๋ก์ด ์ํ ๊ฐ์ ์ค์ ํ ์ ์๋ค.
useEffect(() => {
document.addEventListener('click', handleClick)
return () => {
document.removeEventListener('click', handleClick)
}
}, [])
const handleClick = (e) => {
console.log(isOpen)
if (e.target.dataset.status === CATEGORY) {
// ์ด์ state ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
setIsOpen((prev) => !prev)
return
}
setIsOpen(false)
}
๋๋ 2๋ฒ ๋ฐฉ๋ฒ์ด ์ฝ๋ ๊ฐ๋ ์ฑ์ด๋ ์๋ ํ์ ์ด ์ ๋๋ค๊ณ ์๊ฐํ์๋๋ฐ ๋ฉํ ๋๊ป ์ฌ์ญค๋ณด๋๊น 2๋ฒ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ ๊ฒ๋ ๊ด์ฐฎ์ง๋ง 3๋ฒ ํจ์ํ ์ ๋ฐ์ดํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์์ ์ฑ์ด๋ ๋ฉ๋ชจ๋ฆฌ์ ์ธ ์ธก๋ฉด์์ ์ข์ ๊ฒ ๊ฐ๋ค๊ณ ๋ง์ํด์ฃผ์ ์ 3๋ฒ ๋ฐฉ๋ฒ์ ํตํด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์๋ค!

Web: Event์ addEventListner ์์๋ณด๊ธฐ (๊ฐ๋
, React์์ ์ฃผ์ํ ์ )
โช ๐ ์ฝ๋์ ์คํ๋ฆฐํธ ๊ฐ์ ํ์ธ ๋ด์ฐฌ๋์ ์ถ์ฒ