-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FE] Icon component 변경 #897
base: fe-dev
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
토다리 고생 많았습니다~~ 아이콘 컴포넌트부터 저번 디렉터리 구조 변경을 생각하면 많은 파일 변경이 있음에도 더 나은 방안이라면 실행하는 모습이 멋있습니다. 덕분에 더 정리된 구조 안에서 작업을 할 수 있는 것 같아요!
<Icon iconType="editPencil" /> | ||
<IconEdit /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이제 iconType을 받지 않고 적절한 icon을 바로 import 해서 사용하는 방식으로 변경됐네요.
<IconMeatballs />, | ||
<IconXCircle />, | ||
<IconChevron />, | ||
<IconSearch />, | ||
<IconConfirmCircle />, | ||
<IconErrorCircle />, | ||
<IconTrash />, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
스토리북 확인해봤는데 아이콘이 어떤 사이즈에 어떤 모습으로 보이는지 확인이 바로 가능하니깐 너무 좋네요 :)
스북 장인 대토다리
export const IconCheck = ({color = 'primary', size, width, height, ...rest}: Omit<SvgProps, 'children'>) => { | ||
const {theme} = useTheme(); | ||
const w = width ?? size ?? 16; | ||
const h = height ?? size ?? 16; | ||
|
||
return ( | ||
<Svg size={size} width={w} height={h} {...rest}> | ||
<path | ||
d={`M${w * 0.75} ${h * 0.3}L${w * 0.375} ${h * 0.675}L${w * 0.25} ${h * 0.55}`} | ||
stroke={theme.colors[color]} | ||
stroke-width={w * 0.125} | ||
stroke-linecap="round" | ||
stroke-linejoin="round" | ||
/> | ||
</Svg> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
새로운 아이콘이 필요하면 이런 형식으로 컴포넌트를 제작해서 사용하면 되는거죠?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞숨당~
export const IconChevron = ({ | ||
color = 'tertiary', | ||
size, | ||
width, | ||
height, | ||
direction = 'down', | ||
...rest | ||
}: Omit<SvgProps, 'children'> & {direction?: direction}) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
direction prop을 받아서 돌려버리는 것 좋네요~ 특히 up left right down 모두 대비한 것도 좋아요
<path | ||
fill-rule="evenodd" | ||
clip-rule="evenodd" | ||
d={`M${10 * x} ${20 * y}C${11.3132 * x} ${20 * y} ${12.6136 * x} ${19.7413 * y} ${13.8268 * x} ${19.2388 * y}C${15.0401 * x} ${18.7362 * y} ${16.1425 * x} ${17.9997 * y} ${17.0711 * x} ${17.0711 * y}C${17.9997 * x} ${16.1425 * y} ${18.7362 * x} ${15.0401 * y} ${19.2388 * x} ${13.8268 * y}C${19.7413 * x} ${12.6136 * y} ${20 * x} ${11.3132 * y} ${20 * x} ${10 * y}C${20 * x} ${8.68678 * y} ${19.7413 * x} ${7.38642 * y} ${19.2388 * x} ${6.17317 * y}C${18.7362 * x} ${4.95991 * y} ${17.9997 * x} ${3.85752 * y} ${17.0711 * x} ${2.92893 * y}C${16.1425 * x} ${2.00035 * y} ${15.0401 * x} ${1.26375 * y} ${13.8268 * x} ${0.761205 * y}C${12.6136 * x} ${0.258658 * y} ${11.3132 * x} ${-1.95685e-8 * y} ${10 * x} ${0 * y}C${7.34784 * x} ${3.95203e-8 * y} ${4.8043 * x} ${1.05357 * y} ${2.92893 * x} ${2.92893 * y}C${1.05357 * x} ${4.8043 * y} ${0 * x} ${7.34784 * y} ${0 * x} ${10 * y}C${0 * x} ${12.6522 * y} ${1.05357 * x} ${15.1957 * y} ${2.92893 * x} ${17.0711 * y}C${4.8043 * x} ${18.9464 * y} ${7.34784 * x} ${20 * y} ${10 * x} ${20 * y}ZM${15.2978 * x} ${7.37778 * y}C${15.3953 * x} ${7.26627 * y} ${15.4696 * x} ${7.13638 * y} ${15.5162 * x} ${6.99574 * y}C${15.5628 * x} ${6.85511 * y} ${15.5808 * x} ${6.70657 * y} ${15.5692 * x} ${6.55887 * y}C${15.5575 * x} ${6.41117 * y} ${15.5165 * x} ${6.26729 * y} ${15.4484 * x} ${6.1357 * y}C${15.3804 * x} ${6.0041 * y} ${15.2866 * x} ${5.88745 * y} ${15.1728 * x} ${5.7926 * y}C${15.059 * x} ${5.69775 * y} ${14.9274 * x} ${5.62663 * y} ${14.7857 * x} ${5.5834 * y}C${14.644 * x} ${5.54018 * y} ${14.495 * x} ${5.52574 * y} ${14.3476 * x} ${5.54092 * y}C${14.2003 * x} ${5.5561 * y} ${14.0574 * x} ${5.60061 * y} ${13.9275 * x} ${5.67181 * y}C${13.7976 * x} ${5.74302 * y} ${13.6832 * x} ${5.83949 * y} ${13.5911 * x} ${5.95556 * y}L${9.59333 * x} ${10.7522 * y}C${9.20778 * x} ${11.2144 * y} ${8.99111 * x} ${11.4711 * y} ${8.81889 * x} ${11.6278 * y}L${8.81222 * x} ${11.6344 * y}L${8.80444 * x} ${11.6289 * y}C${8.61778 * x} ${11.4878 * y} ${8.37889 * x} ${11.2522 * y} ${7.95445 * x} ${10.8267 * y}L${6.34111 * x} ${9.21445 * y}C${6.13155 * x} ${9.01205 * y} ${5.85088 * x} ${8.90005 * y} ${5.55956 * x} ${8.90259 * y}C${5.26823 * x} ${8.90512 * y} ${4.98954 * x} ${9.02197 * y} ${4.78354 * x} ${9.22798 * y}C${4.57753 * x} ${9.43399 * y} ${4.46067 * x} ${9.71267 * y} ${4.45814 * x} ${10.004 * y}C${4.45561 * x} ${10.2953 * y} ${4.5676 * x} ${10.576 * y} ${4.77 * x} ${10.7856 * y}L${6.38222 * x} ${12.3978 * y}L${6.42778 * x} ${12.4433 * y}C${6.79111 * x} ${12.8067 * y} ${7.13889 * x} ${13.1556 * y} ${7.46445 * x} ${13.4011 * y}C${7.82778 * x} ${13.6767 * y} ${8.30444 * x} ${13.9344 * y} ${8.91444 * x} ${13.9078 * y}C${9.52556 * x} ${13.88 * y} ${9.97667 * x} ${13.5789 * y} ${10.3144 * x} ${13.2722 * y}C${10.6144 * x} ${12.9978 * y} ${10.9311 * x} ${12.6189 * y} ${11.2589 * x} ${12.2244 * y}L${11.3 * x} ${12.1756 * y}L${15.2978 * x} ${7.37778 * y}Z`} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오..... 상당한 작업이 느껴져요;;;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GPT야 고마워
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리펙토링 하느라 고생 많았습니다 또달~!! 이제 더 편하고 자유롭게 사용할 수 있게 된 것 같아요 👍
const Svg: React.FC<SvgProps> = ({children, color, height, isUsingFill = false, size, viewBox, width, ...rest}) => { | ||
const {theme} = useTheme(); | ||
|
||
return ( | ||
<svg | ||
fill={isUsingFill ? (color ? theme.colors[color] : 'currentColor') : 'none'} | ||
height={height ?? size ?? 24} | ||
viewBox={viewBox ?? `0 0 ${width ?? size ?? 24} ${height ?? size ?? 24}`} | ||
width={width ?? size ?? 24} | ||
xmlns="http://www.w3.org/2000/svg" | ||
{...rest} | ||
> | ||
{children} | ||
</svg> | ||
); | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
어떤 종류가 있는지 확인하려면 하나의 Icon에서 type으로 분기해서 사용하는게 더 편하지 않을까라고 생각했는데요!
svg안에 넣을 path를 들고다니면서 매번 주입하는게 쉬운 일은 아니니 자체를 컴포넌트로 만들게 된 것 같네요. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞습니다 ㅜㅜ
<path | ||
fill-rule="evenodd" | ||
clip-rule="evenodd" | ||
d={`M${10 * x} ${20 * y}C${15.5228 * x} ${20 * y} ${20 * x} ${15.5228 * y} ${20 * x} ${10 * y}C${20 * x} ${4.47715 * y} ${15.5228 * x} ${0 * y} ${10 * x} ${0 * y}C${4.47715 * x} ${0 * y} ${0 * x} ${4.47715 * y} ${0 * x} ${10 * y}C${0 * x} ${15.5228 * y} ${4.47715 * x} ${20 * y} ${10 * x} ${20 * y}ZM${6.70711 * x} ${5.29289 * y}C${6.31658 * x} ${4.90237 * y} ${5.68342 * x} ${4.90237 * y} ${5.29289 * x} ${5.29289 * y}C${4.90237 * x} ${5.68342 * y} ${4.90237 * x} ${6.31658 * y} ${5.29289 * x} ${6.70711 * y}L${8.58579 * x} ${10 * y}L${5.29289 * x} ${13.2929 * y}C${4.90237 * x} ${13.6834 * y} ${4.90237 * x} ${14.3166 * y} ${5.29289 * x} ${14.7071 * y}C${5.68342 * x} ${15.0976 * y} ${6.31658 * x} ${15.0976 * y} ${6.70711 * x} ${14.7071 * y}L${10 * x} ${11.4142 * y}L${13.2929 * x} ${14.7071 * y}C${13.6834 * x} ${15.0976 * y} ${14.3166 * x} ${15.0976 * y} ${14.7071 * x} ${14.7071 * y}C${15.0976 * x} ${14.3166 * y} ${15.0976 * x} ${13.6834 * y} ${14.7071 * x} ${13.2929 * y}L${11.4142 * x} ${10 * y}L${14.7071 * x} ${6.70711 * y}C${15.0976 * x} ${6.31658 * y} ${15.0976 * x} ${5.68342 * y} ${14.7071 * x} ${5.29289 * y}C${14.3166 * x} ${4.90237 * y} ${13.6834 * x} ${4.90237 * y} ${13.2929 * x} ${5.29289 * y}L${10 * x} ${8.58579 * y}L${6.70711 * x} ${5.29289 * y}Z`} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
가로세로 길이에 따라 path를 유동적으로 변경시키려고 이렇게 리터럴을 사용한 것으로 보여지는데 맞나요?
그럼 혹시 새로운 아이콘을 추가해야할때는 ai에게 이 d attr를 x, y에 따라 달라지도록 작성해달라고 명령해서 받아야하나요..?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그렇습니다~! 본문에서도 말했던 것 처럼 더 편하게 만들 수 있는 방법이 존재할 것 같아요.
이 부분에 대해서도 한번 더 고민해 보고,
새로 icon을 가져오는 누구라도 편하게 쓸 수 있도록 만들어볼게용
@@ -26,7 +28,7 @@ export const ListButton: React.FC<ListButtonProps> = forwardRef<HTMLButtonElemen | |||
{suffix} | |||
</Text> | |||
<IconButton variants="none"> | |||
<Icon iconType="rightChevron" /> | |||
<IconChevron direction="right" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
어으 아이콘을 쓰는 곳이 많은데 다 바꾸느라 고생많았습니다아 😭
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
토다리가 Icon을 사용하기 쉽게 만들어줘서 앞으로 너무 편할 것 같아요! 고마워용
| typeof IconPictureSquare | ||
>; | ||
|
||
const IconShowcase = ({IconComponent, args}: {IconComponent: any; args: any}) => ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
징챠 스토리북에도 꼼꼼하게 예시 남겨줘서 이해하기 넘 쉬웠어요! 쵝오
import {SvgProps} from './Icon.type'; | ||
import {svgStyle, svgWrapperStyle} from './Svg.style'; | ||
|
||
const Svg: React.FC<SvgProps> = ({children, color, size, width, height, direction, ...rest}) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사용하기 쉽게 만들어주신 덕분에 앞으로 Icon을 활용할 때 너무 편할 것 같아요!
앞으로 열심히 사용하겠습니댱~!
issue
구현 목적
1. �재사용 가능한 Icon component
현재 svg파일을 ReactComponent로 import해서 사용하고 있음.
하지만, 이렇게 사용하게 되면 색상도, 크기도 자유롭게 변경할 수 없는 문제점이 있음.
이에 따라 색상과 크기를 자유롭게 변경할 수 있는 IconComponent들을 구현함.
2. 유지보수의 어려움
위에서 설명한것과 마찬가지로, 크기가 다른 아이콘이 필요할 때, 별도의 Icon을 만드는 상황이 발생함 (ex. EditPencil, EditPencilSmall 등) 또한, 새로운 아이콘이 추가될 때 마다, type을 선언해주고 기본 색상을 설정하는 등 다양한 조치가 필요함.
하지만 지금 단계의 구현에서 이것이 완벽하게 해결되지 않은 것은 마찬가지
3. 사용하지 않는 svg 파일이 많음
구현 사항
1. Svg.tsx 구현
Svg component를 통해서, 색상, 크기, fill / stroke 여부를 쉽게 반영할 수 있도록 구현함
children에는 svg 내부 속성인 path, def 등을 받음
2. Icon Component 구현
위에서 구현한 Svg component에 크기를 전달하고, 해당 사이즈에 맞게 path의 d 속성이 유동적으로 변경될 수 있도록 구현함
이렇게 구현했을때의 또다른 장점은, Chevron과 같이 방향을 설정하거나, on/off 등을 설정하는 등 더 다양한 variation을 가질 수 있는 Icon들에 대해서 쉽고 간편하게 대응할 수 있다는 것입니다.
구현한 IconChevron에서도 direction을 prop으로 받아 이를 적용시켰습니다.
3. 다양한 component의 variation을 확인할 수 있는 storybook
storybook/Icon에서 다양한 Icon들의 크기 및 색상 변경점들을 확인할 수 있습니다.
PR 댓글의 chromatic을 확인하셔도 좋을 것 같아요
4. 사용하지 않거나 필요하지 않은 svg 삭제 및 아이콘 적용
기존에 중복되는 Icon (chevron / chevronRight, pencil, pencilSmall) 등을 제거하고, 사용하지 않는 아이콘들 또한 제거하였습니다.
이후, 기존의 Icon들을 구현된 새로운 Icon들로 대체하였습니다.
논의하고 싶은 부분(선택)
조금 더 사용하기 쉬운 SVG component 방식을 도입하고 싶었습니다.
하지만, 현재에도 새로운 IconCoponent를 만들 때 생각보다 많은 공수가 필요하다고 생각합니다.
남은 Design 검수 작업이 마무리 되어 갈 때, 한번 더 효율적으로 사용할 수 있는 방법을 적용해보고자 합니다.
현재 구현 방식은 각 svg path에 기존 크기의 일정 비율을 곱해서 다양한 크기에 대응하도록 하는 방법인데, 이 markup을 파싱해서 모든 svg path에 대응할 수 있게 할 수 있을 것 같아요.
🫡 참고사항
2025.01.07 17:57 변경사항
구현 목적
기존에 구현된 방식으로는, 새로운 svg를 추가할 때 마다
path
의d
attribute를 수정해줘야 하고, 복잡한 svg의 경우 이를 설정하는게 쉽지 않은 문제가 있습니다.또한, w, h 등 기본 default 크기 또한 지정해줘야 하는 번거로움이 있습니다.
이에 svg 파일을 가지고 있기만 하다면, 쉽게 사용 가능한
Icon
component를 만들 수 있게 변경하였습니다.구현 사항
Svg.tsx
svg component 내부에서 children(svg 파일)을 파싱하여 기본적인 viewBox, fill 등의 정보를 가져옵니다.
이후 이 정보들과 유저가 넘긴 prop들을 적절히 적용하여 쉽게 IconComponent를 사용할 수 있도록 합니다.
사용 방법
svg 파일을 import한 뒤, Svg component 하위에 넣어주기만 하면 됩니다.
fill, stroke color를 변경할 필요도, d 속성을 변경할 필요도 없습니다.
element.props를 사용한 것이 아니라, 문자열 parsing을 한 이유
vite 에서 svg를 가져올 때, svg를 바로 가져오는 것이 아니라, vite 내부에서 svg를 react-component로 변환하는 과정을 거칩니다. 이 과정에서 element.props를 이용할 수 없게 됩니다.
따라서, chidlren(svg)의 속성값들을 가져오기 위해. element를 문자열로 변환한 뒤, 정규표현식을 이용해 attribute를 가져온 것입니다.