react-icons 효율적으로 사용하기

donggae·2023년 11월 17일
1

React

목록 보기
7/8
post-thumbnail

react-Icons 공식 문서를 보면서 작업을 하던 도중 효율적으로 사용하기 위한
내가 시도한 방법에 대해서 정리 해보려고 한다.

기존 사용 방법

import { IconName } from 'react-icons/IconCategories'

위와 같이 아이콘 이름 + 아이콘의 카테고리를 정해서 import를 하는데 뭔가 불편했고,
아이콘이 한두개면 괜찮지만 아이콘이 많아질수록 쌓여가는 아이콘 관련 import가 너무 불편했다.

다른 사람들은 어떻게 사용할까 하면서 찾아봤는데 원하는 답을 얻지는 못하였다

넵!


내가 개선한 방법 + 사용 방법

1. 가장 먼저 react-icons를 사용하기 위해 설치를 해보자

$ npm install react-icons

2. 내가 프로젝트에서 사용할 아이콘의 카테고리 및 아이콘 리스트를 정리를 한다.

나는 Font Awesome6를 사용하기로 했고, 아이콘들의 리스트를 정했다.

3. icon 스타일 + 리스트를 기본적으로 사용할 파일을 생성한다.

프로젝트마다 폴더 구조도 다르겠지만 나의 구조는 각각의 컴포넌트들의
기본 스타일 + 전체적으로 쓸 컬러 + 각각의 규칙을 정하여 theme 폴더에서 관리를 했고,
여기에 iocn를 추가하여 작성하였다

4. 내가 쓸 아이콘의 스타일을 정해보자

// themes/default/components/icon.js

import { FaAngleLeft, FaAngleRight, FaAngleUp, FaAngleDown, FaXmark, FaRegCircleXmark, FaPlus, FaMagnifyingGlass, FaBars, FaEye, FaEyeSlash, FaRotateRight, FaGear } from 'react-icons/fa6';

const baseStyle = {
  display: 'inline-flex',
  justifyContent: 'center',
  alignItems: 'center',
};

const iconTypes = {
  arrowLeft: FaAngleLeft,
  arrowRight: FaAngleRight,
  arrowUp: FaAngleUp,
  arrowDown: FaAngleDown,
  close: FaXmark,
  closeFill: FaRegCircleXmark,
  plus: FaPlus,
  search: FaMagnifyingGlass,
  hamburger: FaBars,
  visible: FaEye,
  unvisible: FaEyeSlash,
  refresh: FaRotateRight,
  system: FaGear,
};

const iconSizes = {
  lg: {
    width: '24px',
    height: '24px',
  },
  md: {
    width: '20px',
    height: '20px',
  },
  sm: {
    width: '16px',
    height: '16px',
  },
};

export default { baseStyle, iconTypes, iconSizes };
  • 아이콘의 기본 스타일, 사이즈, 타입을 정의한다.

5. 전역으로 쓸 준비 + 폴더 구조 설명

// themes/default/index.js

import components from './components';
import tokens from './tokens';

export default {
  ...tokens,
  components,
};
  • 내가 쓸 컴포넌트들의 기본 스타일 + 규칙들이 정해져있는 파일들을 export해준다
// themes/index.js

export { default as defaultTheme } from './default';
  • 마지막으로 다시 이거를 deafultTheme로 묶어 export를 해준다.
import { createContext, useContext } from 'react';
import { defaultTheme } from '../themes';
import PropTypes from 'prop-types';

const ThemeContext = createContext();

// eslint-disable-next-line react-refresh/only-export-components
export const useTheme = () => {
  return useContext(ThemeContext);
};

export const ThemeProvider = ({ children }) => {
  return <ThemeContext.Provider value={defaultTheme}>{children}</ThemeContext.Provider>;
};

ThemeProvider.propTypes = {
  children: PropTypes.node, 
};
  • 전역으로 쓰기 위해서 useContext를 사용하였고 해당 defaultTheme를 다른 컴포넌트에서
    useTheme를 사용하여 쓸 수 있게 된다.
// App.js

import { ThemeProvider } from './theme/ThemeContext';

function App() {
  return (
    <ThemeProvider>
		{/* 각각의 프로젝트에 맞는 구성 */}
    </ThemeProvider>
  );
}

export default App;
  • 프로젝트마다 상이할테니 생략된 점 최상위에서 ThemeProvider를 통해 전역 설정을 해준다.

6. Icon 컴포넌트를 내 입맛대로 설정해보자

가장 먼저 내가 고려한 것은 아이콘을 유동적으로 사용하기 위함이고 그게 아니였다면
그냥 주먹구구식 import를 사용해서 썼을 것이다.
컴포넌트를 사용하는게 편하게 사용하기 위함인데 너무 불편했었다.

나만의 Icon컴포넌트 emotion과 함께 응용하여 만들어보자

// components/Icon/Icon.jsx

import styled from '@emotion/styled'
import { useTheme } from '../../../theme/ThemeContext';

const StyledIcon = styled.span`
  ${(props) => props.theme.components.icon.baseStyle}

  .Icon {
    ${(props) => props.theme.components.icon.iconSizes[props.size]}
    color:${(props) => props.theme.colors[props.color]};
  }
`;
  • react-icons를 통해 컴포넌트를 사용하면 svg 태그를 단독으로 렌더링 해주는데,
    이거는 조금 별로 안 좋은 방법이고 입맛대로 수정하기 위해선 감싸는 태그가 하나 필요하기에
    span를 통해 스타일을 해준다.

  • 아이콘들에 따라 import를 하기 싫기에 name + size + color를 통해 나만의 컴포넌트로 설정해보았다.

  • 해당 프로젝트에서 하나하나 width=""를 지정하기엔 너무 번거로우니 프로젝트의 일관성을 위해
    정해진 size, color를 props를 받을 수 있게 한다.

// components/Icon/Icon.jsx

const Icon = ({ name, size, color }) => {
  const PREFIX = 'Icon';
  const theme = useTheme();
  const SelectedIcon = theme.components.icon.iconTypes[name];

  return (
    <StyledIcon size={size} theme={theme} color={color}>
      <SelectedIcon className={PREFIX} />
    </StyledIcon>
  );
};

Icon.defaultProps = {
  size: 'md',
  color: 'black',
};

Icon.propTypes = {
  name: PropTypes.string.isRequired,
  size: PropTypes.oneOf(['lg', 'md', 'sm']),
  color: PropTypes.string,
};

export default Icon;
  • 기본 size, color를 정해두고 필요시마다 size, color를 조정한다

다르게 생각하는 사람도 있을것이다.

물론 이것보다 폴더구조를 간단하게 해서 iocn파일을 만들고 거기에서 icon 리스틀 정하고
Icon컴포넌트에 icon 리스트를 import를 해도 된다

하지만 정답은 없고 이러한 방법도 해보고 저러한 방법도 해봐야 어떤게 좋고 나쁨을 구분할 수 있기에
나는 전체적인 기본 스타일을 관리하는 파일이 있었으면 하는 생각에 이러한 구조를 선택하게 되었다.

7. Icon컴포넌트를 통해 이제 사용해보자

기본 스타일을 위한 다른 코드들은 생략을 한 점은 양해를 구한다.

// pages/components

import Icon from '../../components/hds/Icon/Icon';

const TestPages = () => {
  const theme = useTheme();

  return (
    <div>
      <StyledPreview theme={theme}>
        {Object.keys(iconMap).map((name, idx) => (
          <IconPreview key={idx} className="IconPreview">
            <Icon name={name} color={randomColor} />
            <p>{name}</p>
          </IconPreview>
        ))}
      </StyledPreview>
    </div>
  );
};
export default TestPages;

아주 잘 나옵니다 구우우욷

정답은 아니지만 확실히 import줄이 한줄로 끝이 났고 필요할 때 마다 유동적으로 쓸 수 있게되었다.

모든 질문 의견 환영합니다.

profile
아자자자

0개의 댓글