- 에러바운더리 / Suspense
- 무의미한 </> 혹은 <div> 정리
메인페이지
- UnscheduledCards / ScheduledMain 으로 컴포넌트 분리인데, 명명방식 통일
공통컴포넌트
- Tab
- width, isJustify 등 기본값 설정해서 옵션설정 일부 줄이기
Icon
왜 개선하려 하는가 ?
(현재 코드)
export const Icon = ({ name, stroke = 'black', size = 16, strokeWidth = 2, showBackground = false, isFill = false, onIconClick, ...props }: IconProps) => { const [iconName, setIconName] = useState(name); const [isMouseHover, setIsMouseHover] = useState(false); const [isScaled, setIsScaled] = useState(false); const handleHeartMouseUp = () => { setIsScaled(true); setTimeout(() => { setIsScaled(false); }, 200); }; useEffect(() => { setIconName(name); }, [name]); const shapeStyle = { width: size, height: size, backgroundColor: showBackground ? theme.colors.grey.light : 'transparent', }; const iconStyle = { 'stroke-width': isFill ? 0 : strokeWidth, stroke: isMouseHover ? 'grey' : stroke, width: size, height: size, fill: isFill ? '#FF3040' : 'transparent', }; const icon = icons[iconName]; const svg = icon ? icon.toSvg(iconStyle) : ''; const base64 = Buffer.from(svg, 'utf8').toString('base64'); return ( <StIconWrapper onClick={onIconClick} onMouseOver={() => name !== 'calendar' && setIsMouseHover(true)} onMouseLeave={() => setIsMouseHover(false)} onMouseUp={() => iconName === 'heart' && handleHeartMouseUp()} size={size} isScaled={isScaled} {...props} style={{ ...props.style, ...shapeStyle }}> <img src={`data:image/svg+xml;base64,${base64}`} alt={iconName} /> </StIconWrapper> ); };
- 초기 렌더링에 불필요한 리소스들을 포함하고 있습니다.
초기에는 아이콘 컴포넌트를 다음과 같이 구상하였습니다.
<div> ... // 공통 컴포넌트 Icon <Icon name="right-chveron" /> ... </div>
Icon 컴포넌트에 이름만 집어넣으면, 이름에 맞는 아이콘을 화면에 출력하고 싶었습니다.
허나, 프로젝트에 사용된 아이콘 라이브러리인
feather-icons
는 이러한 기능을 지원하지 않았습니다.위와 같은 형태를 구현하기 위해,
import { icons } from 'feather-icons';
feather-icons의 모든 icon들을 import 한 후
const icon = icons['햇님']; const svg = icon ? icon.toSvg(iconStyle) : ''; const base64 = Buffer.from(svg, 'utf8').toString('base64'); return ( <img src={`data:image/svg+xml;base64,${base64}`} alt={iconName} /> )
위와 같이 props로 받아온 이름에 맞는 (햇님) 아이콘을 바인딩시켜 img 태그로 변환하여 화면에 뿌리는 방식으로 코딩 했습니다.
이는 처음에 구상한 작동 방식을 구현한 코드이지만, 모든 icon들을 import 해야하기에 트리 쉐이킹이 되지 않아 초기 렌더링에서 너무 많은 리소스가 낭비되는 문제가 있었습니다.
- 공통 컴포넌트인데, 특정 컴포넌트가 가져야 할 기능이 있습니다.
Icon 컴포넌트는 공통 컴포넌트임에도 불구하고, 아이콘 이름이 ‘calendar’, ‘heart’인 경우에 대한 로직이 포함되어 있습니다.
여러 아이콘이 사용해야할 공통 Icon 컴포넌트라는 역할에 어울리지 않습니다.
- Feather Icons 라이브러리에 강하게 결합되어 있습니다.
만일 해당 라이브러리를 사용할 수 없는 경우, 전체 Icon을 사용할 수 없는 것과 같은 문제가 생길 수 있습니다.
이 일이 얼마나 중요한가 ?
현재 코드는 라이브러리의 모든 icon들을 다 불러오는 방식 ⇒ 초기 렌더되는 Size가 큼 ⇒ 초기 화면을 보여주는데 오랜 시간이 걸립니다.
공통 컴포넌트에 특정 상황에서만 사용되는 함수가 있다? ⇒ 가독성 저하, 추후 유지보수하기 어렵습니다.
라이브러리 고장나면 아이콘을 못 쓸수도 있음
작업 과정
- feather-icons 라이브러리를 사용하는게 맞을까 ?
- feather-icons 라이브러리를 사용하려면, 어떻게 해도 전체 아이콘을 다 불러오는건 동일한 것 같다?
- 혹은 사용할 아이콘을 딱 정해서 그것만 초반에 불러오는 작업(스크립트 태그를 활용한)을 한다?
- tree-shaking을 사용하여 개별적인 아이콘들을 가져올 수 있음.
- feather-icons에 문제가 생겨도 다른 아이콘 라이브러리로만 교체한다면, Icon 공통 컴포넌트는 수정 할 필요가 없어짐.
이는 해당 라이브러리가 필요한 아이콘만을 개별적으로 가져오는 방식은 따로 지원하지 않기 때문…
<script src="https://unpkg.com/feather-icons/dist/feather.min.js"></script> <script> feather.replace({ icons: ['햇님', '달님', 'right-chevron'] }); </script>
추가할 아이콘들이 생길때마다 icons에 아이콘을 집어넣는 것이 불편하기도 하고, 추가시키는 것을 까먹는 휴먼 에러가 발생할 수도 있음.
⇒ 기존의 라이브러리가 아닌, 새로운 라이브러리(react-feather)를 사용해보자.
<햇님 size={48} />
위 작업 과정의 문제점
import 햇님 from react-feather export const App = () => { return ( <Icon width={48} height={48} ... > <카메라 color="yellow" size={24} ... > </Icon> ) }
위와 같이 Icon 공통 컴포넌트와 Icon에 각각 attribute들을 설정해줘야 한다.
액자와 사진으로 예시를 들어보자면,
공통 컴포넌트는 액자가 되는 것이고, Camera 아이콘은 액자에 끼울 사진이 되는 것이다.
이렇게 되면, 액자 크기를 어떻게 설정할 것인지 관리 한 번 해줘야 하고,
바다 사진을 넣을 것인지, 건물 사진을 넣을 것인지 등등 관리를 한 번 더 해줘야하고.
총 2번을 별도로 관리해야하는 코드가 나오게 된 것이다.
과연 이게 맞을까 ?
이를 피하기 위해, 다른 라이브러리를 찾아보았음.
- react-icons 를 사용해보자.
- 출력하길 원하는 아이콘 이름을 (import 해야하긴 한다만..) 공통 Icon 컴포넌트에 집어넣음으로써 호출한다.
- 트리 쉐이킹 문제도 해결.
- react-feather 라이브러리가 겪던 액자/사진 총 2줄되는 문제도 해결.
- 특정 Icon에(heart, calendar) 대해서 필요한 함수는 부모 컴포넌트(App)에서 처리하면 되므로, 공통 컴포넌트를 건들지 않을 수 있음.
- calendar +
- heart
- edit-3
- x
- bell
- log-out ⇒
FaArrowRightFromBracket
- plus
- edit
- trash-2
- search
- arrow-left
위 문제점들을 모두 해결할 수 있는 라이브러리를 찾아보았다.
// App.tsx import { FiCamera } from 'react-icons/fi'; export default function App() { return ( <FiCamera /> <Icon icon={FiCamera} color="white" size={48} /> ); } // Icon 공통 컴포넌트 import { CSSProperties } from 'react'; import { IconBaseProps, IconType } from 'react-icons'; export const Icon = ({ icon: IconComponent, color = 'black', size = 24, strokeWidth = 2, style, className, }: IconProps) => { const iconProps: IconBaseProps = { color, size, strokeWidth, style, className, }; return ( <액자> <IconComponent {...iconProps} /> </액자> ); };
이 라이브러리는 겪고 있던 문제들을 모두 해결해준 것 같다.

! 참고 ( React-icons chunk 분리하기)
npm install react-icons --save
사용중인 아이콘들 목록




