11'use client' ;
22
3- import { useState , useEffect } from 'react' ;
3+ import { useState , useEffect , useRef } from 'react' ;
44import {
55 ChevronDown ,
66 ChevronUp ,
@@ -42,6 +42,8 @@ export default function PlanSection({
4242
4343 const [ showMenu , setShowMenu ] = useState ( false ) ;
4444
45+ const menuRef = useRef < HTMLDivElement > ( null ) ;
46+
4547 // 1. 초기 데이터 로드
4648 useEffect ( ( ) => {
4749 const loadInitialTasks = async ( ) => {
@@ -59,10 +61,29 @@ export default function PlanSection({
5961 setIsTasksLoading ( false ) ;
6062 }
6163 } ;
62-
6364 loadInitialTasks ( ) ;
6465 } , [ userId , planId ] ) ; // userId나 planId가 바뀔 때만 실행됨
6566
67+ // 1-2. 마우스 클릭 감지
68+ useEffect ( ( ) => {
69+ const handleClickOutside = ( event : MouseEvent ) => {
70+ // 메뉴가 열려있고, 클릭된 요소가 menuRef 내부가 아니라면 닫기
71+ if (
72+ showMenu &&
73+ menuRef . current &&
74+ ! menuRef . current . contains ( event . target as Node )
75+ ) {
76+ setShowMenu ( false ) ;
77+ }
78+ } ;
79+ document . addEventListener ( 'mousedown' , handleClickOutside ) ;
80+
81+ // 언마운트 시 제거
82+ return ( ) => {
83+ document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
84+ } ;
85+ } , [ showMenu ] ) ;
86+
6687 // 2. 하위 항목 상태(완료/미완료) 토글 핸들러
6788 const handleToggleTask = async ( itemId : string , currentStatus : boolean ) => {
6889 try {
@@ -141,7 +162,7 @@ export default function PlanSection({
141162 { completedCount } /{ totalCount }
142163 </ span >
143164 </ div >
144- < div className = "relative z-10" >
165+ < div className = "relative z-10" ref = { menuRef } >
145166 < button
146167 onClick = { ( e ) => {
147168 e . stopPropagation ( ) ; // 부모 클릭(아코디언 토글) 방지
0 commit comments