2차 프로젝트를 진행하면서 DB에 있는 전체 데이터를 보여주는 Table을 만들었습니다. MUI Data Grid와 같은 좋은 라이브러리들이 있지만, 아직 TypeScript를 공부하지 않아서 사용을 하지 못했습니다. 그래서 임시방편으로 table 태그를 이용해 직접 표를 만들었습니다. 코드가 지저분해지고 많은 기능을 넣을 수는 없어서 아쉬웠지만, 직접 table을 만들어 보는 것도 좋은 경험이었습니다.

  표를 다 만든 후에, th 항목에 마우스를 올리면 설명이 표시되게 하고 싶었는데, 마침 Radix Tooltip이라는 좋은 라이브러리를 찾아서 아래와 같이 구현할 수 있었습니다. 사용법도 간단합니다.


1. 설치

// install
npm install @radix-ui/react-tooltip

// import
import * as Tooltip from '@radix-ui/react-tooltip';

 

  터미널에 위와 같이 입력해서 설치한 후, Radix Tooltip을 사용할 페이지에서 import 해주세요.


2. 구조

import React from 'react';
import * as Tooltip from '@radix-ui/react-tooltip';
import { PlusIcon } from '@radix-ui/react-icons';
import './styles.css';

const TooltipDemo = () => {
  return (
    <Tooltip.Provider>
      <Tooltip.Root>
        <Tooltip.Trigger asChild>
          <button className="IconButton">
            <PlusIcon />
          </button>
        </Tooltip.Trigger>
        <Tooltip.Portal>
          <Tooltip.Content className="TooltipContent" sideOffset={5}>
            Add to library
            <Tooltip.Arrow className="TooltipArrow" />
          </Tooltip.Content>
        </Tooltip.Portal>
      </Tooltip.Root>
    </Tooltip.Provider>
  );
};

export default TooltipDemo;

 

  Radix Tooltip 페이지에서 제공하는 Demo Code입니다. Tooltip.Provier 태그가 전체를 감싸고 있고, 그 아래를 Tooltip.Root 태그가 감싸고 있습니다. Tooltip.Root 태그는 Tooltip.Trigger, Tooltip.Portal 태그를 감싸고 있는 구조입니다. 정리하면 다음과 같습니다.

 

  • Tooltip.Provider : 전체 Tooltip을 감싸는 태그
  • Tooltip.Root : Tooltip이 표시될 th 하나를 감싸는 태그
    • Tooltip.Trigger : Tooltip이 작동될 th 요소가 있는 태그
    • Tooltip.Portal : Tooltip event가 작동되면 표시될 메시지가 있는 Tooltip.Content 태그를 감싸는 태그

  전체적인 구조는 위와 같고, 사용하실 때는 Tooltip.Provider로 Tooltip이 들어갈 태그 전체를 감싸신 후에, th 하나마다 Tooltip.Root로 감싸주고, 그 안에 Tooltip.Trigger와 Tooltip.Portal로 나눠주시면 됩니다.


3. 사용 예시

<Tooltip.Provider delayDuration={0}>                              // Provider
  <Tooltip.Root>                                                    // Root 1
    <Tooltip.Trigger asChild>                                         // Trigger
      <th
        className='academyCount'
        onClick={(e) => handleThClick(e.target.className)}
      >
        *학원 수{getSortIcon('academyCount', sortColumn, sort)}
      </th>
    </Tooltip.Trigger>
    <Tooltip.Portal>                                                   // Portal
      <Tooltip.Content className='TooltipContent' sideOffset={10}>
        학원 수 (평생직업 교육학원), 자치구 기준
        <Tooltip.Arrow className='TooltipArrow' />
      </Tooltip.Content>
    </Tooltip.Portal>
  </Tooltip.Root>
  <Tooltip.Root>                                                    // Root 2
    <Tooltip.Trigger asChild>                                          // Trigger
      <th
        className='libraryCount'
        onClick={(e) => handleThClick(e.target.className)}
      >
        *도서관 수{getSortIcon('libraryCount', sortColumn, sort)}
      </th>
    </Tooltip.Trigger>                                                  // Portal
    <Tooltip.Portal>
      <Tooltip.Content className='TooltipContent' sideOffset={10}>
        도서관 (공공도서관, 작은도서관, 장애인도서관)
        <Tooltip.Arrow className='TooltipArrow' />
      </Tooltip.Content>
    </Tooltip.Portal>
  </Tooltip.Root>
</Tooltip.Provider>;

 

  예시 코드를 보시면, 전체 Tooltip 태그를 Tooltip.Provider가 감싸고 있고, Tooltip.Root 태그 두 개를 사용하여 두 개의 th 태그에 tooltip을 표시하고 있습니다. Tooltip.Trigger 안에는 th 태그 내용이 들어가 있고, Tooltip.Portal 안에는 tooltip에 표시될 Tooltip.Content가 있습니다. 코드가 길어질 뿐, 코드 자체는 간단합니다.


4. CSS 적용

 

  CSS를 적용하려면 CSS 파일을 생성한 후에, 공식 홈페이지에 있는 CSS 내용을 복사 붙여넣기 해주시고, css 파일을 import 해주시면 됩니다.


5. 주요 옵션

<Tooltip.Provider delayDuration={0} disableHoverableContent={false}> // 사용 예시

1. Provider

1) delayDuration (기본값: 700)

  요소 위에 마우스를 올려놨을 때, 몇 초 후에 tooltip이 표시될지 선택하는 옵션입니다. 저는 바로 뜨기를 원해서 0으로 설정했습니다.

 

2) skipDelayDuration (기본값: 300)

  tooltip 요소가 표시된 후 다른 tooltip으로 이동했을 때 설정한 delayDuration을 Reset 하는 시간을 설정합니다. 예를 들어, 학원 수에 마우스를 올려서 tooltip이 표시되었으면, skipDelayDuration에서 설정한 시간 내(300)에 다른 요소로 이동하면 바로 tooltip이 뜹니다. 하지만 skipDelayDuration에서 설정한 시간 이후에 다시 tooltip에 마우스를 올린다면, delayDuration에서 설정한 시간 후에 tooltip이 표시됩니다. skipDelayDuration을 0으로 설정한다면, 마우스를 이동할 때마다 항상 delayDuration에서 설정한 시간 후에 tooltip이 표시될 것입니다.

 

3) disableHoverableContent (Boolean 값)

  이 옵션을 false로 설정하면 tooltip 메시지 위에 마우스를 올려놓아도, tooltip이 사라지지 않지만, true로 설정하면 tooltip 메시지에 마우스를 올려놓으면 tooltip이 사라집니다. true로 설정하면 접근성에 영향을 미칠 수도 있다고 하니 기본값인 false로 설정하는 게 좋습니다.

<Tooltip.Provider delayDuration={0} disableHoverableContent={true}> // 사용 예시

2. Root

1) defaultOpen (Boolean 값)

  true로 설정하면 처음 랜더링 될 때 tooltip이 표시됩니다.

<Tooltip.Root defaultOpen={true}> // 사용 예시

3. Content

1) sideOffset (기본값: 0)

  Trigger 요소로부터 위로 얼마만큼 떨어져서 표시할 것 인지 설정합니다. 설정한 숫자가 표시할 수 있는 높이를 초과하면 아래로 표시가 됩니다.

 

2) align (기본값: 'center, ENUM = ['start', 'center', 'end']), alignOffset (기본값: 0)

  Trigger 요소로부터 왼쪽이나 오른쪽으로 얼마만큼 떨어져서 표시할 것인지 설정합니다. 기본값은 center이므로, align 값을 생략하고 alignOffset 값을 입력하면 적용이 되지 않지만, align을 'start'로 설정한 후에 alignOffset 값을 입력하면, 입력한 값만큼 오른쪽으로 표시가 됩니다. 반대로 align 값을 'end'로 설정하면 alignOffset에서 입력한 값만큼 왼쪽에서 표시가 됩니다.

<Tooltip.Content sideOffset={10} align='start' alignOffset={50}>

 

  그 외에 여러 가지 옵션들이 있지만, 기본 옵션만으로도 멋진 tooltip을 구현할 수 있기 때문에 생략하겠습니다. 필요한 옵션이 있다면 공식 문서를 참고해 주세요.


참고 문헌

1. Tooltip - Radix Primitives

2. github 예제 코드

WARNING in [eslint]
src\App.js
  Line 22:48:  Array.prototype.map() expects a return value from arrow function  array-callback-return

 

  React에서 map 함수를 사용했을 때 결과가 제대로 나오지 않으면서 "Array.prototype.map() expects a return value from arrow function"이라는 에러가 발생할 때가 있습니다. React는 JSX문법을 사용하는데, HTML 태그를 리턴해주는 경우 JavaScript에서 사용했을 때 처럼 중괄호를 이용해서 map 함수를 작성하면 위의 에러가 발생합니다.

// JavaScript
const A = [1, 2, 3, 4, 5];

A.map((data) => {
  console.log(`${data}번 째 console.log`);
  console.log(data);
});

// JSX
const Breeds = (props) => {
  return (
    <dl>
      {props.list.map(({ breed, description }) => {      // 중괄호({ })가 아닌 괄호(( ))를 써야 함
        <Fragment key={breed}>
          <dt>{breed}</dt>
          <dd>{description}</dd>
        </Fragment>;
      })}
    </dl>
  );
};

 

  JavaScript에서 map 함수를 이용해서 여러 줄을 반환할 때 중괄호를 써야 하지만, JSX에서는 중괄호를 괄호로 바꿔주어야 에러가 발생하지 않고 함수가 제대로 실행됩니다.

import { Fragment } from "react";

const Breeds = (props) => {
  return (
    <dl>
      {props.list.map(({ breed, description }) => (    // 코드 수정한 부분
        <Fragment key={breed}>
          <dt>{breed}</dt>
          <dd>{description}</dd>
        </Fragment>;
      ))}    // 코드 수정한 부분
    </dl>
  );
};

const lists = [
  { breed: "치와와", description: "작은 품종의 개." },
  { breed: "코기", description: "귀여운 품종의 개." },
  { breed: "컴벌랜드 시프도그", description: "멸종된 품종의 개." },
];

function App() {
  return <Breeds list={lists} />;
}

export default App;

 

  이렇게 코드를 수정해주면 lists의 객체들이 제대로 화면에 표시되는 것을 볼 수 있습니다. Breeds 함수의 return 부분 처럼 여러 줄의 JSX 표현식을 반환할 때는 괄호를 사용해야 하기 때문에, map 함수를 이용해서 태그를 반환해줄 때에도 중괄호가 아닌 괄호를 사용해야 합니다.

'코딩 공부 > React' 카테고리의 다른 글

[React] Radix Tooltip을 이용한 Table Tooltip 만들기  (0) 2024.06.26

+ Recent posts