동일 옵션 버튼 (예를 들면 서울을 두 번 연속 클릭)이 한 번 더 클릭됐을 시 서울이 해제되게 만들고 싶었다. 즉 옵션 내용과 클릭되는 내용을 비교해서 무효화 로직을 넣고 싶었는데, 생각나는 게 event.target.value밖에 없었고 문법을 잘 모르겠는 거다.
🖥️ PlaceButtons
import React from "react";
import { TypeRegionType } from "@/types/meetupType";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "@/stores/store";
import { setPlace } from "@/stores/filterSlice";
const PlaceButtons = () => {
const dispatch = useDispatch();
const { place } = useSelector((state: RootState) => state.filter);
const places: TypeRegionType[] = ["서울", "경기", "인천", "강원", "대전", "세종", "충남", "충북", "부산", "울산", "경남", "경북", "대구", "광주", "전남", "전북", "제주", "미정", "전국"];
const handlePlaceSelect = (newRegion: TypeRegionType) => {
dispatch(setPlace(newRegion));
};
return (
<>
<div>
<div>
{places.map(placeItem => (
<button
value={event?.target.value}
key={placeItem}
onClick={
// 현재 placeItem이 막 클릭된 placeItem과 같다면 resetFilter를,
아니라면 handlePlaceSelect(placeItem)을 실행
// 그러려면 막 클릭된 placeItem 값을 가져와서 평가한 후
함수 린터값을 적용해야겠지
(event) => handlePlaceSelect(placeItem)
}
className={px-3 py-1 text-sm rounded-lg border ${place === placeItem ? "bg-blue-200 border-blue-400 text-blue-950" : "bg-gray-100 border-gray-400 text-gray-800"}}
>
{placeItem}
</button>
))}
</div>
</div>
</>
);
};
export default PlaceButtons;
조건부로 값을 설정하면 되긴 하는데 이게 왜 되는지?
const handlePlaceSelect = (newPlace: TypeRegionType) => {
// 현재 선택된 값과 새로 클릭한 값이 같으면 null로 설정 (필터 해제)
if (place === newPlace) {
dispatch(setPlace(null));
} else {
// 다른 값이면 새 값으로 설정
dispatch(setPlace(newPlace));
}
};
그럼 event.target.value는 필요없어?
편집
Event.target.value는 이 경우에 필요하지 않습니다.
React에서 map 함수로 생성된 버튼 클릭 이벤트에서는 이미 클릭된 아이템의 값을 placeItem 매개변수로 전달받고 있기 때문입니다. 클로저를 통해 각 버튼은 자신의 placeItem 값을 "기억"하고 있습니다.
버튼 클릭 이벤트 핸들러를 다음과 같이 작성하면 됩니다:
typescript
onClick={() => handlePlaceSelect(placeItem)}
이렇게 하면 클릭된 버튼에 해당하는 placeItem 값이 handlePlaceSelect 함수로 전달됩니다. 이 값과 현재 선택된 값(place)을 비교하여 같으면 null로 설정하고, 다르면 새 값으로 설정하는 로직이 동작합니다.
event.target.value는 주로 input 필드나 select 요소처럼 HTML 자체적으로 value 속성을 가진 요소에서 사용하는 방식입니다. 버튼에 value 속성을 추가할 수도 있지만, 이 경우 클로저 방식이 더 간결하고 React 스타일에 맞습니다.
그리고 resetFilter()를 그냥 때려넣었더니 안 된다. dispatch(resetFilter()) 하셔야져..
클로저(Closure)란?
클로저는 함수와 그 함수가 선언된 어휘적 환경(Lexical Environment)의 조합입니다. 더 쉽게 말하면, 함수가 자신이 생성될 당시의 외부 변수를 기억하고 접근할 수 있는 능력입니다.
클로저의 핵심 개념
함수는 자신이 선언된 환경(스코프)의 변수를 기억합니다.
이 기억은 함수가 해당 스코프를 벗어나서 실행되더라도 유지됩니다.
우리 코드에서의 클로저 적용
jsx{places.map(placeItem => (
<button
key={placeItem}
onClick={() => handlePlaceSelect(placeItem)}
className={`...`}
>
{placeItem}
</button>
))}
구조 및 동작 원리:

세부 동작 설명:
클로저 생성:
places.map(placeItem => ...) 반복문이 실행될 때, 각 placeItem 값("서울", "경기" 등)에 대해 새로운 함수(() => handlePlaceSelect(placeItem))가 생성됩니다.
이 함수는 생성될 당시의 placeItem 값을 클로저를 통해 "기억"합니다.
버튼 렌더링:
각 버튼은 자신만의 클로저를 가진 onClick 핸들러를 받습니다.
"서울" 버튼의 onClick은 () => handlePlaceSelect("서울")
"경기" 버튼의 onClick은 () => handlePlaceSelect("경기")
이런 식으로 각 버튼이 자신의 값을 기억합니다.
사용자 상호작용:
사용자가 "서울" 버튼을 클릭하면, 해당 버튼의 onClick 핸들러가 실행됩니다.
이 핸들러는 클로저를 통해 "서울"이라는 값을 기억하고 있기 때문에, handlePlaceSelect("서울")이 호출됩니다.
클로저의 효과:
각 버튼은 다른 버튼들과 독립적으로 자신의 placeItem 값을 기억합니다.
클릭 이벤트가 발생할 때, 어떤 버튼이 클릭되었는지 추가 정보(event.target.value 등)없이도 알 수 있습니다.
이벤트 핸들러가 실행될 때 이미 map 함수의 반복은 끝났을 수 있지만, 각 핸들러는 여전히 자신이 생성될 당시의 placeItem 값을 기억합니다.
이런 방식은 React에서 반복문으로 여러 요소를 생성할 때 각 요소가 고유한 데이터를 기억하게 하는 일반적인 패턴입니다.