My-world 개발 보고서 : TS에서 img import하기, React의 이벤트 위임, DOM 요소 각각에 이벤트 걸기

강원지·2023년 5월 2일
0

이미지 import 에러

typescript 에러 발생


=>'assets/note.png' 모듈 또는 해당 형식 선언을 찾을 수 없습니다.

해결방법

src의 하위에 custom.d.ts파일을 만들고 다음 내용을 추가하여 이미지 확장자 파일을 허용한다.

declare module "*.jpg";
declare module "*.png";
declare module "*.jpeg";
declare module "*.gif";

참고
https://www.carlrippon.com/using-images-react-typescript-with-webpack5/
https://velog.io/@hunmok1027/Typescript-image-import

이벤트 위임

바탕화면의 아이콘 클릭 시 모달창이 열리는 기능을 구현하던 중, 코드 간결화와 성능 개선 등 여러 장점을 위해 img의 상위 태그인 div에 이벤트를 바인딩하여 img에서 발생한 이벤트들을 처리하고자 하였다.

클릭이 된 e.target의 dataset을 조회하고자
1. 타입도 바꿔보고 (그래도 eventTarget의 형식에는 dataset 또는 id 속성이 없다)
2. currentTarget으로 조회하여 closest("img") 찾아보았지만 (closest는 자기자신을 포함한 상위의 태그들로부터 결과를 얻기 때문에 적절하지 않다.)
명확한 정답을 찾지 못했다.

결국 이벤트 위임을 하여 개별 식별자로 이벤트 발생 위치를 구분하는 것이 react에서 권장하지 않는 방법같다는 판단을 내렸다. 이제부터 이유를 찾아보겠다.

react에서 이벤트 위임을 권장하지 않는 이유

리액트에서는 이미 모든 이벤트를 이벤트 위임으로 처리하고 있기 때문이다. 이벤트가 DOM 노드들에 하나하나 붙는 것이 아니라 document 레벨에서 처리하고 있다고 한다.

다음과 같이 코드를 바꾸어 문제를 해결했다.

const handleDoubleClick = (e: MouseEvent) => {
  if (!show) dispatch(openModal(e.currentTarget.id));
};
<img id="BasicModal" onDoubleClick={handleDoubleClick} src={BASIC_ICON} alt="icon2"/>

참고
https://bityoungjae.medium.com/react%EC%9D%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%9C%84%EC%9E%84-b08f84e16250

DOM 요소에 각각에 이벤트 걸기

위에서 아이콘을 더블클릭했을 때 어플리케이션의 모달창을 여는 기능을 만들었다면 이번에는 아이콘을 한 번 클릭했을 때 아이콘이 focus 되어보이도록 배경을 파랗게 만드는 기능을 만들어 볼 것이다.

  1. 적용할 style을 만든다. 나는 해당 스타일의 클래스네임을 colorChange라고 지었다.
.colorChange {
  background-color: rgba(4, 110, 248, 0.24);
}
  1. 클릭된 app icon의 이름을 저장하는 onClickApp 함수를 만들어 목표 태그에 바인딩해준다.
  const onClickApp = (e: MouseEvent) => {
    setClickedImg(e.currentTarget.id);
  };
  ...
  <div onClick={onClickApp} ...>...<div/>
  1. 목표 태그를 클릭하면 style이 적용되도록 클래스네임을 다음과 같이 설정한다.
    클릭된 이미지 id와 현재 모달의 타입이 같다면 포커스해준다.
	className={clickedImg === modal.type ? "colorChange" : ""}
  1. focus된 아이콘 바깥을 클릭하면 focus가 나가도록 img태그를 감싸는 상위 태그에 clickedImg에 빈 문자열을 저장하는 이벤트를 바인딩한다.

전체 코드

export const Background = () => {
  const { show } = useSelector((state: RootState) => state.ModalReducer);
  const dispatch = useDispatch();
  const outside = useRef() as React.MutableRefObject<HTMLDivElement>;

  //icon focus
  const [clickedImg, setClickedImg] = useState("");
  const onClickApp = (e: MouseEvent) => {
    setClickedImg(e.currentTarget.id);
  };
  const onClickOutside = (e: MouseEvent) => {
    if (e.target === outside.current) setClickedImg("");
  };

  //open modal
  const handleDoubleClick = (e: MouseEvent) => {
    if (!show) dispatch(openModal(e.currentTarget.id));
  };
  return (
    <div className="background">
      <Square />
      <div className="applications" ref={outside} onClick={onClickOutside}>
        {ModalComponents.map((modal) => {
          return (
            <div
              key={modal.type}
              id={modal.type}
              onClick={onClickApp}
              className={clickedImg === modal.type ? "colorChange" : ""}
              onDoubleClick={handleDoubleClick}
            >
              <img src={modal.img} alt={modal.desc} />
              <span>{modal.name}</span>
            </div>
          );
        })}
      </div>
      <TodoApp />
    </div>
  );
};

남은 할 일

  • 어플리케이션 상세 구현
    • 노트, 윈도우
    • 닫기, 드래그 위치 이동
  • 스타일링
  • 에러
    • applications 높이

0개의 댓글