[Next.js] divdivdiv : 메인 화면 아이콘 컴포넌트화

2023. 8. 22. 12:54· Next.js
목차
  1. 아이콘 컴포넌트
  2. 클릭 이벤트 함수
  3. 아이콘 상태 관리
  4. 메인 화면 리렌더링 이슈 처리
  5. 컴포넌트 반환

아이콘 컴포넌트

divdivdiv의 메인 화면 아이콘은 총 세 가지 종류(폴더, 이미지, 포춘쿠키)로 구성되어 있으며, 각각의 아이콘은 서로 다른 정보와 기능을 갖고 있다. 그러나 일부 스타일을 공유하고 있고, 더블 클릭 시 동작을 수행하며, 사용자가 원하는 위치로 드래그할 수 있다는 공통적인 특성이 있다. 따라서 이 아이콘들을 우선 하나의 컴포넌트로 묶은 뒤, 아이콘 상태를 개별적으로 관리해야 할 필요가 생겼다. 컴포넌트 이름은 DraggableComponent()로 지었다.

  function DraggableComponent(props: {
    className: string;
    path: string;
    type: string;
    title: TitleProps;
    width: number;
    height: number;
  }) {
    const { className, path, type, title, width, height } = props;

    const handleIconClick = (type: string) => {
      // ...
    };

    const draggableContent = (
      <div
        className={`${styles["icon"]} ${styles[className]}`}
        onDoubleClick={() => {
          isMobile ? undefined : handleIconClick(type);
        }}
        onClick={() => {
          isMobile ? handleIconClick(type) : undefined;
        }}
      >
        <div
          className={styles["icon-image"]}
          style={{
            // ...
          }}
        ></div>
        <div
          className={styles["icon-title"]}
          style={{
            // ...
          }}
        >
          <div>{title[language]}</div>
        </div>
      </div>
    );

    return isMobile ? (
      <React.Fragment>{draggableContent}</React.Fragment>
    ) : (
      <Draggable>{draggableContent}</Draggable>
    );
  }

모바일 화면이 아닌 경우에만 아이콘에 드래그 기능을 넣기 위해 최초 렌더링 시 현재 화면의 뷰포트 너비를 판별하는 변수를 만들었다. window.innerWidth가 630 미만일 때는 모바일 화면으로 처리되어 드래그 기능 없이 아이콘을 터치(onClick)하면 이벤트가 발생한다. 반대의 경우에는 아이콘에 드래그 기능이 추가되며, 더블클릭 했을 때(onDoubleClick)에만 이벤트가 발생한다.

 

클릭 이벤트 함수

조건문을 통해 클릭 이벤트를 하나로 묶었다. 아이콘 타입에 따라 실행되는 함수가 달라진다.

const handleIconClick = (type: string) => {
  if (type === "fortune") {
    handleFortuneClick();
  } else if (type === "folder") {
    window.open(path, "_blank");
  } else if (type === "image") {
    handleImageClick(path);
  }
};

 

아이콘 상태 관리

아이콘 타입별 이미지 크기는 서로 조금씩 다르기 때문에, 아이콘의 타입에 맞는 너비와 높이를 한꺼번에 관리할 수 있게 iconSize라는 객체를 따로 만들어두었다. 아이콘 제목 또한 효과적인 상태 관리를 위해 별도의 라이브러리에 iconTitle 객체를 만들었다.

const iconSize = {
  folder: {
    width: 80,
    height: 65,
  },
  image: {
    width: 72,
    height: 96,
  },
  fortune: {
    width: 80,
    height: 83,
  },
};
// data.ts

export const iconTitle = {
  blog: {
    en: "Blog",
    ko: "블로그",
  },
  music: {
    en: "Carver Chart",
    ko: "카버 차트",
  },
  // ...
};

 

메인 화면 리렌더링 이슈 처리

한편 이미지 타입 아이콘의 경우 더블 클릭하면 모달 창이 열리는데, 이때 한 가지 이슈가 발생했다. 모달창을 여닫을 때마다 사용자가 변경한 아이콘 위치가 초기화되었다. 이를 방지하기 위해 DraggableComponent의 부모 컴포넌트에 해당하는 Icons를 만들고, React의 memo 기능을 활용해 props의 값이 변경되지 않을 때는 리렌더링되지 않도록 처리했다. 

function Icons({ setImgSrc, setImgAlt, setShowImage, language, isMobile }: IconsProps) {
  // ...

  function DraggableComponent(props: {
    className: string;
    path: string;
    type: string;
    title: TitleProps;
    width: number;
    height: number;
  }) {
    const { className, path, type, title, width, height } = props;

    const handleIconClick = (type: string) => {
      // ...
    };

    const draggableContent = (
      // ...
    );

    return isMobile ? (
      <React.Fragment>{draggableContent}</React.Fragment>
    ) : (
      <Draggable>{draggableContent}</Draggable>
    );
  }

  return (
    // ...
  );
}
const MemoizedIcons = memo(Icons);

 

컴포넌트 반환

부모 컴포넌트에서는 다음과 같은 형태로 반환해 각각의 아이콘을 화면에 표시했다.

export default function Main() {
  // ...

  return (
    <React.Fragment>
      <DraggableComponent
        className="icon-blog"
        path="https://blog.divdivdiv.com"
        type="folder"
        title={iconTitle.blog}
        width={iconSize.folder.width}
        height={iconSize.folder.height}
      />
      <DraggableComponent
        className="icon-music"
        path="/music"
        type="folder"
        title={iconTitle.music}
        width={iconSize.folder.width}
        height={iconSize.folder.height}
      />
      // ...
      <DraggableComponent
        className="icon-cat"
        path="cat"
        type="image"
        title={iconTitle.cat}
        width={iconSize.image.width}
        height={iconSize.image.height}
      />
      <DraggableComponent
        className="icon-me"
        path="me"
        type="image"
        title={iconTitle.me}
        width={iconSize.image.width}
        height={iconSize.image.height}
      />
      <DraggableComponent
        className="icon-fortune"
        path="fortune"
        type="fortune"
        title={iconTitle.fortune}
        width={iconSize.fortune.width}
        height={iconSize.fortune.height}
      />
      // ...
    </React.Fragment>
  );
}
저작자표시 비영리 변경금지 (새창열림)
  1. 아이콘 컴포넌트
  2. 클릭 이벤트 함수
  3. 아이콘 상태 관리
  4. 메인 화면 리렌더링 이슈 처리
  5. 컴포넌트 반환
'Next.js' 카테고리의 다른 글
  • [Next.js] divdivdiv : Non-SSR 컴포넌트
  • [Next.js] divdivdiv : 모달 창 만들기
  • [Next.js] Next.js와 SEO
  • [Next.js] Data Fetching
카버
카버
카버의 코딩일기카버 님의 블로그입니다.
카버
카버의 코딩일기
카버
  • All (414)
    • JavaScript (36)
    • CSS (1)
    • TypeScript (6)
    • React (17)
    • Redux (6)
    • Next.js (13)
    • Gatsby (2)
    • 코딩 테스트 (305)
      • programmers (238)
      • Baekjoon (51)
      • CroCoder (15)
    • ETC (28)
      • Error (9)
      • CS (8)
      • Terminal (2)
      • GitHub (1)
hELLO · Designed By 정상우.v4.2.2
카버
[Next.js] divdivdiv : 메인 화면 아이콘 컴포넌트화
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.