스토리북 상태 변경
이번 팀 프로젝트를 하면서 미루고 미루던 공통 UI 컴포넌트들을
스토리북을 통해 제공을 하기로 마음을 먹었습니다.
단순하게 컴포넌트에 대한 UI 적인 스토리북을 만드는 것은 정말 많은 곳에서 찾아볼 수 있는데
Controls를 통해 제공하는 상태값을 통해 화면을 핸들링할 수 있는 요소에 대한 예제는 정말 없는 것 같아서
이번 기회를 통해 정리를 해두려고 합니다 😃
1. 컴포넌트 준비하기
일단 요번 예제에서 사용될 컴포넌트 입니다.
토글 형식으로 ON, OFF 상태를 바꿀 수 있는데요
아래의 코드를 보고 어떤 UI를 가지는지 혹시 예상이 가시나요?
interface Props {
offText: string;
onText: string;
isToggle: boolean;
setToggleTrue: () => void;
setToggleFalse: () => void;
}
interface AnimatedProp {
isToggle: boolean;
}
/**
*
* @param isToggle - 토글 상태값
* @param setToggleTrue - 토글 상태값을 true로 변경하는 함수
* @param setToggleFalse - 토글 상태값을 false로 변경하는 함수
* @example
<Toggle
offText='오전'
onText='오후'
isToggle={select}
setToggleTrue={setToggleTrue}
setToggleFalse={setToggleFalse}
/>
*/
const Toggle = ({
offText,
onText,
isToggle,
setToggleTrue,
setToggleFalse,
}: Props) => {
return (
<>
<ToggleUIWrapper>
<AnimatedLabel isToggle={isToggle} />
<StyledButton onClick={setToggleFalse} value={offText}>
<Text.Span
color={isToggle ? 'grey900' : 'white'}
style={{
transition: 'color 500ms ease-in-out',
}}
>
{offText}
</Text.Span>
</StyledButton>
<StyledButton onClick={setToggleTrue} value={onText}>
<Text.Span
color={isToggle ? 'white' : 'grey900'}
style={{ transition: 'color 500ms ease-in-out' }}
>
{onText}
</Text.Span>
</StyledButton>
</ToggleUIWrapper>
</>
);
};
export default Toggle;
React 개발을 해보셨다면 타입과 안의 태그들을 보고 어느 정도 유추는 가능 하지만
정확한 UI에 대해서는 예측이 조금 어려울 수 있습니다.
다음과 같이 바로 예제를 확인할 수 있다면 정말 좋겠죠?
2. 스토리북 코드 준비하기
이번 포스트의 주제인 useArgs에 대해 다뤄 보도록 하겠습니다.
React를 이용해서 상태관리를 하는 방법은 useState가 가장 보편적인 방법 중에 하나죠.
하지만 Storybook에 동일하게 useState를 이용해서 상태에 대해서 변경하려고 한다면
페이지의 상태와 Controls의 상태가 매치하지 않는 경우를 마주하게 됩니다.
그렇다면 이에 대해서는 어떻게 해결할 수 있을까요?
이런 것을 위해서 Storybook 팀에서 useArgs를 제공합니다.
https://storybook.js.org/docs/ember/addons/addons-api
근데 찾기가 힘든 게 단점
그렇다면 이를 이용한 실제 코드를 볼까요?
조금 특별한 점은 args에 대한 프로퍼티로 값에 대한 접근을 한다는 점입니다.
export default {
title: 'Toggle',
component: Toggle,
argTypes: {
isToggle: { control: 'boolean' },
onChange: { action: 'onChange' },
},
args: {
isToggle: false, // 1. 기본값을 false로 설정
onChange: () => console.log('토글 클릭'),
},
} as ComponentMeta<typeof Toggle>;
const Template: ComponentStory<typeof Toggle> = (args) => {
const [isToggle, setIsToggle] = useArgs(); // 2. useArgs()를 사용하여 isToggle을 관리
const setToggleFalse = () => {
setIsToggle({ isToggle: false });
args.setToggleFalse && args.setToggleFalse(); // 3. args를 사용하여 setToggleFalse를 호출
args.isToggle = false; // 4. args를 사용하여 isToggle을 false로 설정
};
const setToggleTrue = () => {
setIsToggle({ isToggle: true });
args.setToggleTrue && args.setToggleTrue();
args.isToggle = true;
};
return (
<Toggle
{...args}
isToggle={isToggle.isToggle}
setToggleTrue={setToggleTrue}
setToggleFalse={setToggleFalse}
/>
);
};
export const Default = Template.bind({});
Default.args = {
offText: 'OFF',
onText: 'ON',
isToggle: false,
setToggleFalse: () => console.log('토글 OFF'),
setToggleTrue: () => console.log('토글 ON'),
};
그럼 useArgs에 대한 type은 어떤 식으로 되어있을까요?
export declare function useArgs(): [Args, (newArgs: Args) => void, (argNames?: [string]) => void];
export declare type Args = {
[name: string]: any;
};
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
위와 같은 형식으로 되어 있는데
저희가 useState를 사용할 때 제네릭으로 타입을 지정하는 방식이 아닌 것을 알 수 있습니다.
Args에 대한 타입도 인덱스 시그니처 방식으로 되어 있는 것을 알 수 있죠.
이 점이 useArgs를 사용하면서 아쉬웠던 점이었습니다.
args에 선언한 프로퍼티들에 대해서 타이핑을 제대로 지원하지 않아서
args에 대한 프로퍼티 키를 직접 써야 한다는 단점이 있었습니다.
(제가 잘못 사용한 걸 수도 있습니다 😂)
3. 결과물
2개의 코드를 작성하고 나면 이제 다음과 같은 화면을 바라볼 수 있습니다.
이제 isToggle 자체에 False, True를 바꿔나가면서 애니메이션에 대해서 쉽게 확인할 수 있게 되었습니다.
스토리북은 컴포넌트가 어떤 모습을 가지게 되는지에 대해서
코드 레벨이 아닌 눈으로 직관적으로 바라볼 수 있다는 점이
어쨌든 화면을 개발해야 하는 프론트엔드 개발자 입장에서
바로 눈으로 컴포넌트가 어떤 UI를 가지는지 알 수 있다는 점이 매력적이라고 생각합니다.
특히 팀에 늦게 합류한 개발자나 개발하면서 모든 컴포넌트를 UI를 외울 수 없는 상황에는
도입할만한 가치가 충분하다고 느낍니다.
이외에도 여러 가지 편의기능들을 제공하지만 이 부분은 다음 포스트에서 다뤄보도록 하겠습니다.
끝으로 현재 진행 중인 프로젝트의 스토리북 링크를 남겨보도록 하겠습니다 😃
https://64041bd03de4f07060a931b9-ewvqzxeuxg.chromatic.com/?path=/story/toggle--default
'React' 카테고리의 다른 글
내 상태를 관리해주는 useState 직접 구현해보기 (0) | 2023.10.13 |
---|---|
왜 내 컴포넌트는 reRendering이 될까? (0) | 2023.10.06 |
[2] 무한 스크롤을 구현해 보자 with React Query (1) | 2023.05.29 |
스켈레톤 UI 그리고 Suspense (1) | 2023.03.31 |
[React] 최적화 관련 Hook에 대해서 알아보자(1) (0) | 2021.08.11 |