필터 옵션 무효화, dispatch, 클로저

강연주·2025년 4월 18일

📚 TIL

목록 보기
155/186

동일 옵션 버튼 (예를 들면 서울을 두 번 연속 클릭)이 한 번 더 클릭됐을 시 서울이 해제되게 만들고 싶었다. 즉 옵션 내용과 클릭되는 내용을 비교해서 무효화 로직을 넣고 싶었는데, 생각나는 게 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에서 반복문으로 여러 요소를 생성할 때 각 요소가 고유한 데이터를 기억하게 하는 일반적인 패턴입니다.

profile
아무튼, 개발자

0개의 댓글