안녕하세요. 메리입니다.
채용공고를 보던 중 상태관리 Mobx에 대한 내용이 종종 보이더라고요.
그래서 "농실농실" 서비스 개선 할 겸 Mobx를 사용해 Toast alert를 만들어 보았습니다.
아참! 그리고 디스콰이엇에 농실농실 관련 포스팅이 올라갔습니다. 많은 관심 부탁드려요~🎉
1. Mobx
Mobx는 저희가 흔히 알고 있는 Redux와 또 다른 상태 관리 라이브러리입니다.
객체지향 느낌의 라이브러리로 보일러플레이트 코드로 Component와 State를 연결하는 방식과 달리
데코레이터(애노테이션)를 통해 해결하기 때문에 구조가 간단하다는 장점이 있습니다.
npm trends에서는 Redux가 1등, Apollo Client가 2등, Mobx가 3등인 것을 확인하실 수 있습니다.
1-1. Observable
Mobx가 동작하는 가장 기본 개념은 아래와 같습니다.
@observable 데코레이터로 지정한 State는 관찰대상으로 지정되고 State는 값이 변경될 때마다 Rerendering 됩니다.
- Observable State : 관찰받고 있는 상태
- Computed Value : 연산된 값
- Reactions : 반응 (Computed Value가 바뀜에 따라 해야 하는 일)
- Actions : 행동 (Observable State가 바뀜에 따라 해야 하는 액션)
2. Mobx 사용해 보기
설치
mobx와 mobx-react를 설치합니다. mobx-react는 v6부터 hooks 문법을 지원합니다.
npm i mobx mobx-react
저는 react-hook-form을 사용해 필수값 체크 alert를 toast로 제작하고 싶었습니다.
고려해야 할 사항은 아래와 같았습니다.
- 필수값 alert가 여러 개 뜰 수 있어야 함
- 위에서 아래로 보이는 애니메이션이 있어야 함
- 일정시간이 지나면 사라져야 함
Store
스토어를 만들기 위해 app > store > store.tsx 파일을 생성해 줍니다.
import { observable } from "mobx";
export type ToastData = {
id: number;
content: string;
};
interface Toast {
toastList: ToastData[];
currentId: number;
addToast: (content: string) => void;
removeToast: (id: number) => void;
}
export const Toast = observable<Toast>({
toastList: [],
currentId: 0,
removeToast(id) {
const index = this.toastList.findIndex((v) => v.id === id);
if (id !== -1) {
this.toastList.splice(index, 1);
}
},
addToast(content) {
this.toastList.push({ id: this.currentId, content });
this.currentId++;
//일정 시간이 지나면 삭제
setTimeout(() => {
Toast.removeToast(this.currentId);
}, 4000 + Toast.currentId * 500);
},
});
Mobx에서 스토어를 만들기 위해서는 다음과 같이 Toast라는 객체를 선언한 후 observable로 감싸주어야 합니다.
observable이 상태가 변화하는지 관찰해 줍니다.
addToast나 removeToast 같은 액션들도 스토어 안쪽에서 같이 작성해서 사용하도록 합니다.
hooks
컴포넌트마다 스토어를 사용하기 위해 useStore 컴포넌트를 아래와 같이 작성해 줍니다.
import { Toast } from "./stores/store";
const useToast = () => ({ Toast });
export default useToast;
3. Toast alert 만들기
Toast alert들을 보여주는 컴포넌트를 하나 만들었습니다.
app > component > Toast.tsx
import { CircleAlert } from "lucide-react";
import { useObserver } from "mobx-react";
import styled, { keyframes } from "styled-components";
import useToast from "@hook/useToast";
const ToastCard = styled.li<{ index: number }>`
display: flex;
gap: 8px;
align-items: center;
padding: 8px;
box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
position: relative;
min-width: 260px;
min-height: 46px;
background-color: rgb(255, 255, 255);
font-size: 1.4rem;
font-weight: 500;
animation: slide-bottom ${(props) => `${3.5 + props.index * 0.5}s`}
cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
animation-delay: ${(props) => `${props.index * 0.5}s`};
border-radius: 16px;
svg {
width: 24px;
height: 24px;
stroke: var(--primary-color);
}
@keyframes slide-bottom {
0% {
transform: translateY(${(props) => `${(props.index + 1) * -200}%`});
}
10% {
transform: translateY(0);
}
90% {
transform: translateY(0);
}
100% {
transform: translateY(${(props) => `${(props.index + 1) * -200}%`});
display: none;
}
}
`;
const ToastItem = () => {
const {
Toast: { toastList },
} = useToast();
//toastList를 가져와야 하기에 useObserver로 감싸줍니다.
return useObserver(() => (
<ul className="toast_wrap">
{toastList.map((ele, index) => {
return (
<ToastCard key={ele.id} index={index}>
<CircleAlert />
{ele.content}
</ToastCard>
);
})}
</ul>
));
};
export default ToastItem;
//toastList를 가져와야 하기에 useObserver로 감싸줍니다.
toastList는 스토어에서 데이터를 가져와야 그려줄 수 있기에 해당 컴포넌트를 useObserver로 감 싸워야 합니다.
또한 style-components의 props 사용해 동적으로 애니메이션 변화를 주었습니다.
애니메이션 부연설명은 여기에📌
저의 경우 4초 후 alert를 없애고자 아래와 같이 사용했습니다.
- 애니메이션
animation: slide-bottom ${(props) => `${3.5 + props.index * 0.5} s`} cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
animation-delay: ${(props) => `${props.index * 0.5} s`};
toast 스토어에서는 4초 후 해당 toast를 제거해 줍니다.
때문에 애니메이션의 기본 유지시간을 3.5초로 잡았습니다.
또한, toast 알림이 여러 개 나올 수 있기 때문에 delay 시간을 추가로 부여했습니다.
(ex, 첫 번째 toast 0초 -> 두 번째 toast 0.5초 -> 세 번째 toast 알림 1초)
delay 시간을 주었기 때문에 animation-duration도 변경이 필요했습니다.
(ex, 첫 번째 toast 3.5초 -> 두 번째 toast 4초 -> 세 번째 toast 4.5초)
app > search > search.tsx
toast alert를 보여주기 위해서 react-hook-form이 onInvalid 될 때 error 객체를 toast 스토어에 추가해 주었습니다.
※ 해당 부분은 코드를 일부만 공개하도록 하겠습니다.
const onInvalid = async (errors: { [key: string]: any }) => {
Object.entries(errors).forEach(([key, error]) => {
Toast.addToast(error?.message);
});
};
이런 식으로 반복문을 돌면서 Toast alert에서 보일 메시지를 저장해 주었습니다.
3. 확인
이렇게 간단하게 Mobx를 사용해서 Toast alert를 만들어보았습니다.
전 사실, 상태관리 라이브러리라곤 Redux 밖에 몰랐는데,
Mobx라는 새로운 라이브러리를 사용해 볼 수 있어서 재미있었습니다.
그저 충격스러운것은 오늘이 아직 수요일 밖에 안됬다는 것.
저희는 스터디를 통해 글을 기록하고 있습니다. 피드백은 언제나 환영입니다 :)
'프론트엔드' 카테고리의 다른 글
AWS + Next.js + Nginx 대환장 파티 경험하기(2) (0) | 2024.07.31 |
---|---|
AWS + Next.js + Nginx 대환장 파티 경험하기 (1) | 2024.07.25 |
Next.js 모니터링 하기(feat. Sentry.io) (0) | 2024.05.24 |
인스타그램 API 연결하기 - 2편 : API 연결 (0) | 2024.05.07 |
인스타그램 API 연결하기 - 1편 : 기본 설정 (1) | 2024.05.07 |