[MSW , React-query] 한정된 시간안에 MVP를 개발하는 방법
Problem! 타 직군 일정에 의존적
이미 만들어진 서비스를 유지보수 하는 경우라면 잘 와닿지 않을 수 있지만, 회사에서 신규 기능을 개발하거나 개인 프로젝트를 진행하다보면 프로젝트 초기 프론트엔드 개발자의 워크로드가 상대적으로 타직군에 비해 낮다 는것을 알 수 있다.
프론트엔드 개발자라는 포지션이 가지는 특징이라고 볼 수 있는데 디자이너의 디자인 대로 UI를 만들고 백엔드 API를 연동해 서비스를 만들어 가야 하기 때문에 이런 상황에 상대적으로 다른 "타직군 일정에 의존적" 이라고 할 수 있다.
(중간에서 이리치이고 저리치이고한다)
이 세상은 코드가 아니야!
개발자라면 본능적으로 이런 의존성에서 벗어나고 싶어할 것이다. 다만 현실은 코드로 이루어지지 않았고, 일정이라는게 내 마음대로 되는 게 아니다. 그렇다고 가만히 월급루팡하고 있거나 타직군 일정을 재촉하는건 프로답지 못하다. 우리는 방법을 찾아야한다.
> Mocking 활용
그래서 필자의 경우는 보통 이런식으로 일정을 분배하고는 한다. 기획이 완료되고 충분히 이해를 했다면, 디자인이 어느정도 완료되기 전까지 적절한 기술 스택들을 고르고, 테스트 코드를 작성하던가 개발환경을 구축한다.
그 이후 디자인이 어느 정도 완료 되었다면, 모킹데이터 파일을 만들고 이를 바탕으로 컴포넌트를 설계한다.
const UserCard = ({ userName, userImage, createdAt, content}) =>{
return (
<>
<img src={userImage}/>
<span>{userName}</span>
<span>{createdAt}</span>
<span>{content}</span>
</>
)
}
예를 들어 왼쪽과 같은 UI 가 디자인으로 나왔다면, 백엔드 개발자와 합의를 하고, UI 가 응답 받을 데이터를 짐작해서 더미 데이터를 오른쪽과 같은 더미데이터 파일을 만들어서 컴포넌트를 만드는 것이다.
파일 기반 Mocking과 실제 서버간의 괴리
이렇게 하면 어느정도 효율적인 일정 배분을 할 수 있지만 여전히 아쉬운 점들이 있는데, 실제 서버와 통신하면서 나올 수 있는 여러가지 문제에 대비하기가 어렵다. 파일로 선언해놓고 import해 사용하는 Mock Data 는 동기방식으로 처리되고 서버와의 통신은 런타임에 비동기적으로 처리되기 때문이다
예를들어 Skeleton, Spinner 등의 대기 상태 UI나, Error 상황에 보여줄 Fallback UI 를 띄우는 등의 기능을 개발하기 어렵다.
> MSW (Mock Service Worker)
2022.11.11 - [프로젝트/플랜트하이커 - 식물생활 종합앱] - [MSW] MSW를 이용한 데이터 모킹 (Mock Service Worker)
다른 개발자들도 이런 문제를 인지하고 있기 때문에 Mirage.js나 Json server 등 다양한 라이브러리가 나와있다. 그 중 필자가 즐겨 사용하는 라이브러리는 MSW 인데
MSW 는 브라우저의 Service worker 단에서 동작하는 라이브러리로 서버로 통신을 요청하면, MSW가 중간에서 이를 가로채 대신 대답을 해주는 방식이다. 이 덕분에 에러응답이나 지연된 응답등도 모킹할 수 있어서 최대한 실제 서버와 유사한 환경을 재현할 수 있다.
추가로 Mock 데이터는 Storybook이나 Testing 에서도 사용되는데 간단한 설정으로 stroybook이나 testing library에서도 사용 할 수 있어 꽤나 유용하다.
Problem. API 응답값이 달라진다면?
So far, so good !
지금까지 설명한대로 진행된다면 실제 서버와 통신을 해도 큰 문제가 없을 것 같다. Base URL 하나만 바꾸면 딱 하고 모든 기능이 정상적으로 동작하는 꿈을 꿀 수 도 있다
하지만 현실은 녹록치않다
Api 서버의 응답값으로 날아오는 JSON 의 key 값이 단 한 글자만 틀리더라도 우리가 만든 UI는 깨지고 런타임에 온갖 에러를 볼 수 있다. 모킹을 통해 개발한 UI가 몇개 안된다면 금방 해결할 수 있을 수 도 있다. 하지만 이미 많은 컴포넌트들을 만들어놨다면 하나하나 찾아서 수정하는게 벌써부터 막막하다.
이런 상황을 피하기 위해서는 프로젝트 시작초기부터 서버의 응답을 한 곳에서 관리하는것 (전역적으로) 이 좋다. 필자는 이러한 상황들을 피하기 위해 React-query를 사용하는데, React-query 는 서버로 부터 응답받은 데이터를 한곳에서 상태로 관리하는데 도움을 주는 라이브러리이다.
해결책. Adapter Pattern
다행히도 프로그래밍 패턴 중, 이런 상황을 위한 패턴이 있다. 바로 Adapert Pattern 인데, 어댑터 패턴은 기존의 인터페이스를 새로운 인터페이스로 변환하여, 호환성 없는 클래스들이 함께 동작할 수 있도록 하는 디자인 패턴이다.
말그대로 우리가 해외여행 갈 때 사용하는 어댑터처럼 실제응답값과 컴포넌트 사이에 변환을 수행해주는 어댑터를 이용한다면 이런 문제를 해결할 수 있을것이다.
React Query의 select 옵션을 활용하면, 서버에서 받아온 데이터를 쉽게 가공하여 컴포넌트에 맞는 형태로 변환할 수 있다
import { useQuery } from 'react-query';
const dataAdapter = (rawData) => {
// 서버에서 받아온 데이터를 필요에 맞게 가공
return {
...rawData,
createdAt : rawData.createdDate,
content : rawData.contentText
};
};
const MyComponent = () => {
const { data, error, isLoading } = useQuery('myQueryKey', fetchData, {
select: dataAdapter, // 어댑터 함수를 select 옵션으로 전달
});
return <UserCard {...data}/>
}
위와 같은 형태로 데이터를 변환해주는 어댑터 함수를 작성해서, React-query 의 Select Option의 인자로 전달해주면 기존에 작성했던 코드를 수정할 필요 없이, 간단하게 서버의 응답을 우리가 만든 컴포넌트에 맞는 형태로 변환할 수 있다.