๐จ TroubleShotting
๋ฌธ์ ๋ฐ์์ ๋ฐฐ๊ฒฝ - As Is
ํฌํ ์๊ฐ์ ์ค์ ํ๋ ํ์ด๋จธ์์ Input์ ํด๋ฆญํ๋ฉด ํด๋น ์๊ฐ ์ ํ์ฐฝ(TimePicker)์ด ์ด๋ฆฌ๋๋ก ํ๊ณ ,
์ด๋ ค ์๋ ์ํ์์ ์ธ๋ถ๋ฅผ ํด๋ฆญํ๋ฉด TimePicker๊ฐ ๋ซํ๋๋ก ๊ตฌํํ๊ณ ์์๋ค.
์ด ๊ณผ์ ์์ ๋ ๊ฐ์ง ํฐ ์ด์๋ฅผ ๊ฒช์๋ค:
โ๏ธ ๋ฌธ์
[true, false] ์ํ์์ Input์ ํด๋ฆญํ๋ฉด handleClickOutside๊ฐ ์คํ๋์ด [false, false]๋ก ๋ซํ๋ค๊ฐ,
๊ณง์ด์ด ๋ค์ [true, false]๋ก ๋ค์ ์ด๋ฆฌ๋ ํ์์ด ๋ฐ์ํจ.
# 1) ์ด ์ํ์์ [false, false]๋ก ์ํ๋ฅผ ๋ณ๊ฒฝํ๊ณ ์ ํจ
timeOpen => [true, false]
# 2) ๊ทธ๋ฐ๋ฐ [false, false]๊ฐ ๋์๋ง์ ๊ณง๋ฐ๋ก ๋ค์ [true, false]๊ฐ ๋จ
timeOpen => [false, false]
timeOpen => [true, false]
// ๋น์ ์ฝ๋
useEffect(() => {
if (timeRef && timeRef.current) {
const handleClickOutside = (e: MouseEvent) => {
if (timeOpen.some((t) => t) && timeRef.current.some((ref) => ref && !ref.contains(e.target as Node))) {
setTimeOpen(Array(2).fill(false));
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}
}, []);
...
<div style={{ display: "flex", alignItems: "center", gap: "5px", position: "relative" }}
onClick={() => setTimeOpen((prev) => prev.map((p, i) => (i === idx ? true : p)))}
ref={(el: any) => {timeRef.current[idx] = el;}}
>
2. ์์ธ ๋ถ์
โ ์์ธ 1. useEffect์ ์์กด์ฑ ๋ฐฐ์ด์ด ๋น๋ฐฐ์ด๋ก ๋์ด ์์ด timeOpen ์ํ๊ฐ ํด๋ก์ ์ ๊ฐํ
- useEffect์ ์์กด์ฑ ๋ฐฐ์ด์ด ๋น๋ฐฐ์ด์ด๋ฉด, ๋ด๋ถ ํจ์๋ ์ธ๋ถ์ ์ต์ state๋ฅผ ์๋์ผ๋ก ์ฐธ์กฐํ์ง ์๊ณ , ์ ์ ๋น์์ ๊ฐ์ ๊ธฐ์ตํ ํด๋ก์ ์ ๊ฐํ๊ฒ ๋๋ค.
- ์ด๋ก ์ธํด ์ํ๋ ๋ฐ๋์์ง๋ง handleClickOutside์์๋ ์ฌ์ ํ ์ด์ ์ timeOpen ๊ฐ์ ์ฐธ์กฐํ๊ณ ์์์
โ ์์ธ 2. ์ด๋ฒคํธ ์คํ ์์์ ๋ํ ์ดํด ๋ถ์กฑ
- ์ฌ์ฉ์๊ฐ Input์ ํด๋ฆญ
- onClick → setTimeOpen(idx) ์คํ → ๐ ๋ ๋๋ง ์์ฝ
- ๋์์ document.mousedown ์ด๋ฒคํธ ๋ฐ์ → handleClickOutside ์คํ๋จ
- ๊ทธ๋์ setTimeOpen([false, false]) ์คํ๋จ
- ํ์ง๋ง ์ด ์์ ์ดํ์ ์๋์ onClick์ ์ํ ์ํ ๋ณ๊ฒฝ์ด ๋ค์ ๋ฐ์๋จ → ๋ค์ true๋ก ๋๋์๊ฐ
- onClick์ ๋ฆฌ์กํธ๊ฐ ์์ฒด์ ์ผ๋ก mousedown + mouseup ๋ ๋ค ๋ฐ์ํ ํ ๋ง๋ค์ด๋ธ ํฉ์ฑ ์ด๋ฒคํธ(synthetic event) ์ด๋ฏ๋ก mousedown์ด ํญ์ ๋จผ์ ์ผ์ด๋จ
2. ํด๊ฒฐ๋ฐฉ์
๐ง ๋ฐฉ๋ฒ 1. stopPropagation()
- Input ์๋จ div์ onMouseDown={(e) => e.stopPropagation()} ์ถ๊ฐ
- document๊น์ง ์ด๋ฒคํธ๊ฐ ์ ํ๋์ง ์์ handleClickOutside๊ฐ ์คํ๋์ง ์์
- ๊ฒฐ๊ณผ: ๋ซํ์ง ์์ → ์ํ ์ ์ง๋จ ([true, false])
๐ง ๋ฐฉ๋ฒ 2. setTimeout(() => ..., 0)
- mousedown ์ด๋ฒคํธ ํธ๋ค๋ฌ์ธ handleClickOutside ํจ์ ๋ด๋ถ ๋ก์ง์ ํ ๋ฐ์ ๋ฆ๊ฒ ์คํ๋๋๋ก defer ์ฒ๋ฆฌ
- onClick์ด ๋จผ์ ์ผ์ด๋๋๋ก ๋ณ๊ฒฝํด์ฃผ์์ด
- ๊ฒฐ๊ณผ: ๋ซํ๋ ๋์ ์ฑ๊ณต ([false, false])
useEffect(() => {
if (timeRef && timeRef.current) {
const handleClickOutside = (e: MouseEvent) => {
setTimeout(() => {
if (timeOpen.some((t) => t) && timeRef.current.some((ref) => ref && !ref.contains(e.target as Node))) {
setTimeOpen(Array(2).fill(false));
}
}, 0);
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}
}, [timeOpen]);
๐ก ์๋กญ๊ฒ ์๊ฒ๋ ์
๐ง ํด๋ก์ + useEffect
- useEffect(() => {...}, [])๋ ๋ด๋ถ ํจ์๊ฐ ์ต์ด ๋ ๋๋ง ๋น์์ ์ํ๋ง ๊ธฐ์ตํจ
- ์ต์ ์ํ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ์์กด์ฑ ๋ฐฐ์ด ๋๋ useRef๋ก ์ํ ์ถ์ ํ์
๐ DOM ์ด๋ฒคํธ ์ ํ ์์
1 | ์บก์ฒ๋ง | ๋ถ๋ชจ → ์์, document์ addEventListener(..., 3rd์ธ์ true)๊ฐ ์์ผ๋ฉด ์คํ๋จ |
2 | ํ๊ฒ | ํด๋ฆญํ ์์ ์์ฒด์์ ์คํ๋จ (์ด๋ฒคํธ ๋ฆฌ์ค๋๊ฐ ์๋ค๋ฉด) |
3 | ๋ฒ๋ธ๋ง | ์์ → ๋ถ๋ชจ, document.addEventListener(..., false) ์คํ๋จ → ์ฌ๊ธฐ์ handleClickOutside |
4 | ๋ฆฌ์กํธ ํฉ์ฑ ์ด๋ฒคํธ | ๋ฆฌ์กํธ์ ํฉ์ฑ ์ด๋ฒคํธ , onClick={() => ...} ์คํ → ์ฌ๊ธฐ์ setTimeOpen true๋ก |
- ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก๋ ๋ฒ๋ธ๋ง ๋จ๊ณ์์ ํจ์๊ฐ ์คํ๋๋ค. true๋ฅผ 3rd์ธ์๋ก ๋๊ธฐ์ง ์๋ํ!
๐งฉ React ํฉ์ฑ ์ด๋ฒคํธ (onClick)
- DOM์ mousedown → mouseup ์ด๋ฒคํธ ํ๋ฆ์ด ๋๋ ๋ค ๋ฐ์
- React ๋ด๋ถ์์ ์ถ์ํ๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฐฉ์์ด๋ฏ๋ก ๋ฒ๋ธ๋ง๋ณด๋ค ๋ฆ๊ฒ ์คํ๋จ
๐์ ํ์ ๊ธฐ์ค
"๋๋ ์ํ๊ฐ ๋ณ๊ฒฝ๋์ด์ timeOpen์ด false๊ฐ ๋๊ธธ ์ํ๋ ์ํฉ์ด์์ง."
๋ฐ๋ผ์ stopPropagation()์ฒ๋ผ ์ด๋ฒคํธ ์์ฒด๋ฅผ ์ฐจ๋จํ๋ ๋ฐฉ์์ด ์๋๋ผ,
์คํ ์์๋ฅผ ๋ฐ๊พธ๋ setTimeout()์ด ๋ ์ ํฉํ ํด๊ฒฐ์ฑ
์ด์์
๐ฉ๋ง๋ฌด๋ฆฌ
์ด๋ฒ ์ด์๋ ๋จ์ํ "์ธ๋ถ ํด๋ฆญ ๊ฐ์ง" ๊ธฐ๋ฅ์ฒ๋ผ ๋ณด์์ง๋ง,
๊ทธ ์์๋ ํด๋ก์ ์ useEffect์ ๊ด๊ณ, React์ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฐฉ์, DOM์ ์ด๋ฒคํธ ์ ํ ํ๋ฆ,
๊ทธ๋ฆฌ๊ณ stopPropagation๊ณผ setTimeout์ ์๋๋ ํ์ด๋ฐ ์ ์ด ์ฐจ์ด๊น์ง ์ค์ํ ๊ฐ๋
์ด ์จ์ด ์์๋ค.
์ด์ ๋ ์ด๋ค ์ํฉ์ stopPropagation()์ ์จ์ผ ํ๊ณ , ์ด๋ค ์ํฉ์ setTimeout()์ด ๋ ์ ํฉํ์ง๋ ํ๋จํ ์ ์๋ค.
๋ํ, ์ธ๋ถ ํด๋ฆญ ๊ฐ์ง ๋ก์ง์ ๊ตฌํํ ๋ ์ด๋ค ์์ ์ ์ํ๊ฐ ๋ฐ์๋๋์ง๋ ๋ ๋ช
ํํ ์ดํดํ๊ฒ ๋์๋ค.