프론트엔드, 서버로부터 독립을 선포하다(1) - MSW로 개발 및 테스트 의존성 줄이기
프론트엔드는 애플리케이션에서 클라이언트 단에 위치하며 비즈니스 로직을 포함한 코드 영역을 말하며, 백엔드는 서버 단에 위치한 코드 영역을 의미한다. 이 두 요소는 각각 독립적으로 변경되고 발전하므로, 대다수의 애플리케이션 개발에서는 물리적으로 분리된 각기 다른 코드베이스를 가지는 경우가 많다.
그러나, 많은 애플리케이션들이 서버에서 데이터를 저장하고 클라이언트에서는 데이터를 캐시 하는 구조를 가지고 있기 때문에, 프론트엔드는 애플리케이션의 목표를 달성하기 위해 필연적으로 서버에 의존적일 수밖에 없다. 그리고 이런 의존성은 프론트엔드 개발과 테스트를 어렵게 하며, 변경에 취약하게 만든다.
API 명세는 이러한 의존성을 약하게 만드는 방법이다. 하지만 그 자체로는 충분하지 않다. API 명세를 만드는 데에도 시간이 걸리고, 명세는 명세일 뿐 실제 동작을 반영하지 않기 때문이다. 또한 API 명세는 약속일뿐이므로 어떤 상황에 따라 언제든지 변경될 수 있다.
이 글에서는 프론트엔드와 서버 간 의존성으로 인한 문제점 세 가지를 살펴보고, 이러한 의존성을 제거하는 방법은 무엇이고, 실제 프로젝트에 적용된 코드를 통해 어떻게 제거할 수 있는지에 대해서 알아보고자 한다. 프론트엔드를 독립적으로 운영, 개발, 관리할 수 있는 코드베이스를 구축하려는 사람이라면 도움이 될 것이다. 모든 프론트엔드가 필요시에 서버로부터 독립할 수 있기를 바란다.
독립 선언이 필요한 이유
먼저 오해하지 말자. 백엔드와 프론트엔드는 서로 아주 친한 친구다. 이 두 요소는 함께 협력하여 애플리케이션의 목적을 달성한다. 사용자 관점에서 보면, 한쪽이 없어서는 안 되므로 이들은 서로에게 의존적인 관계라고 볼 수 있다.
개발자의 관점에서 이 둘의 관계를 바라보면 문제가 드러난다. 뭔가 묘하게 하나가 다른 하나에 의존적이다. 약속 장소에서 기다릴 때 한 쪽은 다른 쪽이 도착할 때까지 기다리지만, 반대편은 그것을 기다리지 않고 먼저 떠나버린다. 마치 말로는 안 하지만 “너 나 없으면 안 되잖아ㅋㅋ”라고 하는 느낌이랄까? 이런 친구와는 조금 거리를 두는 것이 정신 건강에 좋지 않겠는가?
위의 예시는 이해를 돕기 위해 극단적으로 묘사했음을 이해바란다. 실제로는 모두가 같은 목표를 가지고 있기 때문에, 이런 악의적인 행동을 하지 않을 것이다. 하지만 한쪽에게만 의존하는 관계는 건강한 관계가 아니다. 한쪽이 의도하지 않았더라도 충분히 문제가 발생할 수 있다. 이러한 의존 관계가 어떤 문제를 초래할 수 있는지 좀 더 구체적으로 살펴보도록 하자
1. 개발 의존성
개발 의존성 문제는 프론트엔드 개발자라면 반드시 한번쯤은 경험했을 것이므로, 이해가 쉬울 것이다.
이해 관계자: A 기능 작업은 어제 마감이었는데. 혹시 진행 상황이 어떻게 되었나요?
프론트엔드 개발자: 아직 A 기능 API 개발이 완료되지 않아서... 일주일 정도 더 소요될 것 같습니다...
이해 관계자: 예? 일주일이나 더 걸린다고요?? API 개발이 얼마나 걸린다는 건가요?
프론트엔드 개발자: API 개발은 3일 더 걸린다고 통보받았고, 그 이후 제 작업은 약 3일 정도 소요될 것 같습니다.
이해 관계자: (3일동안 뭐 하고 있으시나요…?)
프론트엔드에서 구현해야 하는 기능이 백엔드의 API를 필요로 할 경우, 해당 부분이 완성되고 나서야 프론트엔드 개발을 진행할 수 있다. 이러한 개발 의존성은 프론트엔드와 백엔드의 작업 프로세스를 병렬적으로 진행하는 대신 폭포수(waterfall) 형태로 이루어지게 한다. 즉, 개발 프로세스를 비효율적으로 만드는 것이다.
프론트엔드가 백엔드 개발과 무관하게 독립적으로 개발을 진행하고, 나중에 백엔드 API 개발이 완료되면 API와 바로 연동하여 사용하도록 할 수는 없을까?
2. 테스트 의존성
프론트엔드에서 테스트 코드를 작성해 본 적이 있는가? Jest나 testing-library와 같은 테스트 도구의 등장 덕분에 프론트엔드에서도 컴포넌트 단위의 독립적이고 효율적인 테스트 코드 작성이 가능하게 되었다. 하지만 프론트엔드는 본질적으로 다른 외부 요소들에 의존적이며, 특히 비즈니스 로직이 서버 측에 있는 경우 API와 무관하게 테스트 코드를 작성하는 것은 사실상 불가능하다.
따라서, 더미 데이터나 테스트 도구가 제공하는 모킹 기능을 활용하여 API를 모킹 하면서 테스트를 진행해야 한다. 그러나 이 방법은 비용이 많이 들고, 모킹 코드를 작성하는데 시간이 많이 소요되며, 모킹 코드가 여러 곳에 분산되어 있어 관리가 어렵다. 또한, 이는 또 다른 의존성인 라이브러리에 대한 의존성을 발생시키며, 이로 인해 테스트 라이브러리를 교체하는 것이 더 어렵게 된다.
모킹 관심사만 따로 별도의 계층 및 모듈로 분리하여 중복을 제거하고, 테스트 라이브러리에 종속되지 않고 유지 및 관리할 방법이 없을까?
3. 데이터 모델 의존성
대부분의 애플리케이션 상태는 서버 측에서 저장되므로, 프론트엔드는 백엔드로부터 데이터를 받아 사용해야 한다. 이런 의존성 때문에 프론트엔드는 예상치 못한 변경인 API 명세 변경에 항상 노출되어 있다. 즉, 프론트엔드 코드에는 어떠한 변경이 없더라도 어느 날 코드가 의도한 대로 동작하지 않을 수 있는 것이다.
API 명세 변경은 피할 수 없는 상황에서 발생하며, 프론트엔드는 반드시 서버로부터 데이터를 받아야 하므로 이 의존성을 완전히 제거하는 것은 어렵다. 그러나 컴포넌트의 관점에서 보면, API 명세의 데이터 구조가 변경되더라도 컴포넌트는 그 목적과 책임을 달성하기 위한 데이터만 필요로 한다. 따라서 API 명세는 컴포넌트에게 단순히 세부사항일 뿐이다.
그러나 현실에서는 API 명세가 바뀔 경우, 수십 개의 컴포넌트를 수정해야 하는 상황이 종종 발생한다. 변경이 의도를 더 명확하게 드러내는 경우에는 환영할 만한 일이지만, 대부분은 이와 관련 없는 변경일 것이다.
API 명세가 변경되더라도 컴포넌트를 변경으로부터 보호할 수 있는 방법은 없을까?
개발 및 테스트 의존성 제거하기
개발 의존성과 테스트 의존성을 제거하는 가장 효과적인 방법은 애플리케이션에 모킹 계층을 도입하는 것이다. 이는 단순히 개발 시에 모킹 데이터를 사용하거나, 테스트 라이브러리로 모킹 코드를 작성하는 것이 아니다. 대신, 모킹 관심사를 처리하는 전용 계층을 만들어 개발 및 테스트 시에 해당 계층을 사용한다. 중요한 점은 애플리케이션 코드와 테스트 코드가 모킹 계층을 사용하는지, 아니면 실제 API 코드와 통신하는지 알 수 없게 만드는 것이 핵심이다.
API 명세가 나와 있는 경우, 모킹 계층을 API 명세에 맞게 구성하여 실제 API 개발과 독립적으로 개발을 진행할 수 있다. 애플리케이션 코드는 API 명세에 따라 정해진 규약이 이행되기만 하면, 모킹 계층을 통해 데이터를 받건 실제 API를 통해 데이터를 받건 알지 못하기에 개발 의존성을 제거할 수 있다. API 명세가 아직 없는 경우, 임시로 모킹 계층 코드를 개발한 후 API 명세가 제공되면 이에 맞게 수정하면 된다. 이 경우 변경이 어려울 수 있지만, 타입스크립트를 사용하면 더 수월하게 변경할 수 있다.
테스트 코드의 경우, 프론트엔드에서 검증하고 싶은 것이 API 명세가 제대로 오는지가 아니라 프론트엔드의 컴포넌트와 로직의 동작을 확인하고 싶은 것이기 때문에, API 명세에 따라 작성된 모킹 계층이 있다면, 모킹 여부를 모르게 테스트를 독립적으로 실행할 수 있다. 이 경우, 테스트 코드에서 API와 관련된 모킹 코드를 제거하고, 중복 없이 모킹 계층에서 모킹 코드를 관리할 수 있다.
모킹 코드를 애플리케이션에서 독립된 계층으로 개발하고 관리하는 데 도움이 되는 여러 모킹 라이브러리가 있다. 필자의 생각에는 모킹 라이브러리는 몇 가지 기준을 충족해야 한다. 첫째, 일반적으로 프론트엔드 개발은 브라우저에서 실행되지만 테스트 코드는 노드 환경에서 실행된다. 따라서 모킹 계층은 브라우저와 노드 모두에서 작동해야 한다. 둘째, 모킹 계층은 부수적인 코드이므로 개발과 관리가 필요 이상으로 복잡해지면 안 된다. 마지막으로, 모킹은 고객을 위한 것이 아니라 개발자를 위한 것이므로, 개발자 경험이 뛰어나야 한다.
필자는 위의 조건을 만족하는 모킹 라이브러리로 MSW(Mock Service Worker)를 선택하여 프로젝트에서 사용하고 있다. MSW에 대해 좀 더 알아보도록 하자
왜 MSW 인가?
MSW와 다른 모킹 라이브러리의 가장 큰 차이점은, 모킹에서 브라우저의 서비스 워커(service worker)를 사용한다는 점이다. 서비스 워커는 브라우저 백그라운드에서 실행되는 네트워크 프록시로 볼 수 있다. 즉, 애플리케이션에서 네트워크 요청이 발생하면, 서비스 워커라는 프록시를 거쳐 개발자가 작성한 동작(예: 캐싱)을 먼저 수행하고, 필요한 경우에만 네트워크에서 데이터를 가져온다.
이게 신의 한수인게, 실제 네트워크를 가로채는 방식이므로 실제 동작과 유사하게 작동하면서도 실제 목 서버를 운영하지 않기 때문에 훨씬 가볍다. 따라서 개발자의 경험이 매우 뛰어나다. 다른 기술과 비교하여 설명하면, axios-mock-adapter는 가볍고 편리하지만 실제 네트워크를 발생시키지 않고 axios에 의존적이다. 반면에, Postman Mock Server는 독립된 계층을 구성할 수 있지만, 실제 서버를 만들어야 하므로 무겁고 개발자 경험이 낮다. 그러나 MSW는 서비스 워커를 통해 이 두 가지 사이에 아주 적절한 수준을 달성하였다고 볼 수 있다.
또한, MSW는 노드와 브라우저 모두에서 실행할 수 있어, 하나의 코드베이스로 테스트와 개발에 이용할 수 있다. 대안으로 많이 언급되는 Nock의 경우 브라우저에서 동작하지 않는다.
개인적으로는, MSW의 사용법이 다른 라이브러리에 비해 가장 직관적이고 간단하다는 것도 장점이라고 생각한다. 핸들러를 정의하는 방식이 express와 매우 유사하기 때문이다. 또한, 요청 객체와 응답 객체를 작성하고 다루는 방법이 WHATWG Fetch API 명세를 준수하므로, 익숙하기 때문에 러닝 커브도 낮았다.
import { http, HttpResponse } from 'msw'
// msw 코드 예시
export const handlers = [
// Intercept "GET <https://example.com/user>" requests...
http.get('/user', () => {
// ...and respond to them using this JSON response.
return HttpResponse.json({
id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d',
firstName: 'John',
lastName: 'Maverick',
})
}),
]
애플리케이션 및 테스트 코드에 MSW 적용하기
1. MSW 설정하기
애플리케이션 코드와 테스트 코드에 MSW에 적용해보자. 먼저 MSW를 사용하려면, MSW를 설치하고 서비스 워커 파일인 mockserviceworker.js를 생성해야 한다. mockserviceworker.js는 npx msw init 명령어를 사용하여 생성할 수 있다. 생성한 파일을 정적 파일 디렉토리(NextJS의 경우, public 폴더)에 위치시켜야 한다.
다음으로 셋업을 위해 간단한 요청 핸들러를 만들어 보자.
import { http } from 'msw'
export const handlers = [
http.get('/resource', () => {
return new Response('Hello world!')
}),
]
이후의 셋업 방법은 브라우저와 노드에 따라 다르다. 먼저 노드를 셋업해 보자. 노드는 쉽게 위에서 만든 핸들러를 MSW가 제공하는 함수에 넣어주면 된다.
// app/mocks/server.mock.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
브라우저는 URL을 통해 동적으로 설정 가능한 부분이 있기 때문에 코드가 좀 더 길어질 수 있다.
// browser.mock.ts
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';
import { scenarios } from './scenarios.mock';
// 런타임에 모의 시나리오를 전환하기 위한 코드
const scenarioName = new URLSearchParams(window.location.search).get('scenario')
const runtimeScenarios = scenarios[scenarioName] || []
// 런타임에 모킹 여부를 결정하기 위한 코드
const isMocking = new URLSearchParams(window.location.search).get('isMocking');
const runtimeHandlers = isMocking !== 'false' ? [...runtimeScenarios, ...handlers] : [];
export const worker = setupWorker(...runtimeHandlers);
기본적인 구조는 노드 설정과 같이 핸들러를 가져와 setupWorker 함수에 넣어 생성된 worker를 모듈 외부로 노출하기만 하면 된다. 이때 브라우저의 경우 searchParam을 사용하면 동작을 변경할 때 코드를 재실행 할 필요 없이 런타임에서 모킹 동작을 제어할 수 있다. 예를 들어, 정상적인 시나리오와 에러 시나리오를 분리하여 동작시킬 수 있고, 모킹을 끄고 실제 API와 연결하도록 설정할 수도 있다.
셋업이 끝났으니 다음으로, 애플리케이션 코드에 MSW를 적용해보자. 위에서 정의한 워커와 서버를 가져와 실행시켜 주면 된다. SSR이 있는 경우, 브라우저뿐만 아니라 노드도 모킹해야 한다(물론, 글을 작성하는 시점에서는 MSW는 RSC와 호환되지 않고 있다).
// 컴포넌트 구조 루트 위치에 아래 코드를 동작시킨다.
if (process.env.NODE_ENV !== 'production') {
if (typeof window !== 'undefined') {
const { worker } = await import('../../../mocks/browser.mock');
worker.start();
} else {
// SSR를 위해 노드 서버 단에서도 모킹해준다.
const { server } = await import('../../../mocks/server.mock');
server.listen()
}
MSW는 mockserviceworkorker.js 파일을 가져오는 데 시간이 걸리며, 서비스 워커를 등록하는 동안에는 모킹이 되지 않는 상태에서 네트워크 요청이 발생할 수 있다. 만약 이게 문제가 될 경우, 아래와 같은 컴포넌트를 만들어 네트워크 호출을 지연시킬 수 있다.
'use client';
import { useEffect, useState } from 'react';
export default function MSWProvider({ children }: React.PropsWithChildren) {
const [init, setInit] = useState(process.env.NODE_ENV !== 'development');
useEffect(() => {
async function enableMocking() {
if (typeof window !== 'undefined') {
const { worker } = await import('../../../mocks/browser.mock');
await worker.start();
setInit(true);
}
}
if (!init) {
enableMocking();
}
}, [init]);
if (!init) {
return null;
}
return <>{children}</>;
}
마지막으로 테스트 코드에 MSW를 적용하려면 Jest 기준으로 Jest setup 파일에 아래와 같이 작성해 주면 된다.
// jest.setup.tsx
import { server } from './app/mocks/server.mock';
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
2. MSW로 모킹 코드 작성 및 관리하기
위 설정을 통해 애플리케이션 코드와 테스트 코드에서 모킹을 사용할 수 있게 되었다 그러나. 모킹 계층을 만드는데 MSW를 설정하는 것보다 중요한 것은 "모킹 코드를 어느 정도 수준까지 유지 관리해야 하는가?"이다. 가장 간단한 모킹 코드는 단순히 목 데이터를 전달하는 것부터 시작하여, 실제 백엔드 동작을 모의하는 수준까지 관리할 수 있다.
필자의 경우 MSW의 요청 핸들러에 실제 백엔드와 유사하게 동작을 모의하는 Action 코드를 따로 두어 실제 네트워크 동작을 최대한 유사하게 모의하도록 관리하고 있다. 이렇게 관리하는 이유는 필자의 애플리케이션이 SPA으로 사용자와 인터렉션이 많기 때문에 이를 최대한 모사하기 위해서 이다. 만약 블로그와 같은 정적인 사이트라면 이렇게 까지 하지는 않았을 것이다. 각자 상황에 따라 모킹 수준을 결정하면 될 것 같다.
// app/mocks/db/index.ts
import { MockIndicatorAction, mockIndicatorAction } from './indicator-action.mock';
type MockDatabaseAction = MockIndicatorAction & // ...;
// 데이터를 Action으로 관리한다. Action은 도메인 별로 분리허고 있다.
export const mockDatabaseAction: MockDatabaseAction = {
...mockIndicatorAction,
// ...
};
type MockDatabaseStore = {
// ...
indicators: IndicatorResponse[];
indicatorsValue: IndicatorValueResponse[];
};
// Mock 데이터를 실제 데이터베이스처럼 관리한다
const initialState: MockDatabaseStore = {
// ...
indicators: [
// ...
],
indicatorsValue: [
// ...
],
// ...
}
function initStore(): MockDatabaseStore {
return JSON.parse(JSON.stringify(initialState));
}
export let mockDatabaseStore = initStore();
// 테스트시 초기화를 위한 함수
export const resetMockDatabase = () => {
mockDatabaseStore = initStore();
};
목 데이터를 조작하는 동작은 액션 코드에 작성되며, 필자는 이 액션 코드는 도메인 별로 분리하여 관리하고 있다. 아래는 'indicator'라는 지표에 대한 데이터 조작을 다루는 액션 코드의 예시이다. 예시에는 포함되어 있지 않지만, 데이터 변경 동작 로직도 작성하며 모의하고 있다. 참고로 서버가 측 데이터 타입인 Response 타입을 목 계층에서 사용하면, 서버 측에서 명세가 변경되었을 때 목 계층도 쉽게 변경할 수 있다.
// app/mocks/db/indicator-action.mock.ts
// 데이터 타입으로 서버의 응답값인 Response 타입을 사용한다
export type MockIndicatorAction = {
getIndicators: () => IndicatorListResponse;
getIndicator: (id: string) => IndicatorByTypeResponse | undefined;
getIndicatorBySymbol: (symbol: string) => IndicatorByTypeResponse[];
// ...
};
export const mockIndicatorAction: MockIndicatorAction = {
getIndicators: () => {
return {
data: mockDatabaseStore.indicators,
meta: {
total: mockDatabaseStore.indicators.length,
hasNextData: false,
cursor: 1,
},
};
},
getIndicatorBySymbol: (symbol) => {
return mockDatabaseStore.indicators.filter((indicator) => indicator.symbol.indexOf(symbol) > -1);
},
getIndicator: (id) => {
return mockDatabaseStore.indicators.find((indicator) => indicator.id === id);
},
// ...
}
핸들러는 해당 Action 코드를 호출하고 응답값을 받아서 전달하는 역할만 수행한다. 필자는 핸들러도 도메인에 따라 분리하고 있다.
// app/mocks/handlers/indicator-handler.mock.ts
export const indicatorHandlers = [
http.get(`${API_PATH.indicators}/list`, async () => {
await delayRandom();
return HttpResponse.json(mockDatabaseAction.getIndicators());
}),
http.get(`${API_PATH.indicators}/search`, async ({ request }) => {
const url = new URL(request.url);
const symbol = url.searchParams.get('symbol');
await delayRandom();
return HttpResponse.json(mockDatabaseAction.getIndicatorBySymbol(symbol ?? ''));
}),
// ...
]
분리한 핸들러 파일은 하나로 종합하며 내보내고, 워커랑 서버를 셋업할 때 종합한 핸들러를 넣어주면 된다.
// app/mocks/handlers/index.ts
import { customForecastIndicatorHandlers } from './custom-forecast-indicator-handler.mock';
import { indicatorHandlers } from './indicator-handler.mock';
import { indicatorBoardMetadataHandlers } from './indicator-board-metadata-handler.mock';
export const handlers = [...indicatorBoardMetadataHandlers, ...customForecastIndicatorHandlers, ...indicatorHandlers];
핸들러를 분리하는 방식으로 필자는 도메인 단위 분리를 선택했지만, 핸들러의 경우 API 주소를 기반으로 폴더 구조를 분리하는 방법도 좋다고 생각한다. 이 방법은 NextJS의 라우팅 방식과도 유사하다.
MSW 실제 후기
1. 모킹 계층 개발 및 관리에 드는 비용은 어느정도인가?
위 코드를 통해 알 수 있듯이, 모킹 계층을 유지하는 것은 공짜가 아니다 실제 동작을 모방하기 위한 코드를 작성해야 하며, API가 변경되면 동기화를 위해 모킹 계층도 변경해야 한다. 모킹 계층은 애플리케이션의 부수적인 요소이므로, 항상 경제성을 고려하여 모킹 계층 수준을 유지해야 한다.
필자의 경우, 백엔드의 동작과 로직을 최대한 유사하게 모방하는 수준으로 관리하고 있어, 관리 비용이 큰 편이다. 가장 비싼 부분은 API 명세 변경 시 모킹 계층도 동기화를 위해 변경해야 하는 점이다. 처음에는 이 부분이 가장 우려되었지만, 모킹 계층의 코드를 관심사에 따라 적절히 분리하고 타입스크립트를 함께 사용하면, 변경이 발생했을 때 문제가 되는 부분을 타입스크립트 컴파일러가 찾아주기 때문에, 변경은 생각보다 어렵지 않았다.
오히려 모킹 계층을 유지함으로 써 얻는 개발 독립성과 테스트 독립성이 더 생산성과 개발자 경험 측면에서 도움이 된다고 생각하기 때문에 모킹 계층을 유지 및 관리하고 있다.
오히려 모킹 계층을 유지함으로써 얻는 개발 독립성과 테스트 독립성이 생산성과 개발자 경험이 매우 만족스러웠다. 따라서 모킹 계층을 계속 유지하고 관리하고 있다.
2. 모든 걸 다 모킹 해야 하는가?
다시 한번 강조하지만, 모킹 코드는 부수적인 요소이므로, 경제성을 고려하여 작성해야 한다. 만약 모킹 대상이 비싼 경우, 모킹을 하지 않는 것을 고려해야 한다. 필자의 경우, 차트를 그리기 위한 지표 값 데이터를 일, 주, 월 등의 인터벌에 따라 다르게 제공해야 하는데, 이 데이터를 인터벌로 만드는 모킹 로직을 생성하는 것이 비용이 많이 들어, 모킹 하지 않고 있다.
이런 경우, 대안이 몇 가지가 있다. 하나의 방법은 이 경우에만 로직으로 처리하지 않고, 정해진 리터럴 데이터를 제공하는 것이다. 또 다른 방법은 인터벌 값에 따라 데이터가 변경되는 사실을 확인하여, 실제 API가 도입될 때는 정상적으로 동작할 것이라는 것을 신뢰하도록 하는 것이다.
3. 개발자 경험을 더 향상시킬 수는 없을까?
사실 필자의 프로젝트는 엔터프라이즈급은 아니다. 그러나 엔터프라이즈 급 애플리케이션에는 하나의 페이지에 많은 API와 다양한 케이스가 존재한다. URL을 통해 동적으로 시나리오를 조정하는 방법을 앞서 다루었지만, 다양한 케이스를 처리하는 데는 한계가 있다. 이런 경우에는 개발자 경험을 향상하기 위해 모킹을 관리하는 대시보드와 같은 UI 라이브러리를 만들어 관리하는 방법이 생각해 볼 수 있다.
물론 이렇게 하면 모킹 계층을 관리하는 비용이 증가하게 된다. 따라서 이는 인력이 충분히 확보되었을 때 시도해 볼 만한 일이다. 또는 혼자서 사용할 수 있는 가벼운 UI를 만들어 점진적으로 발전시키며 나중에 라이브러리화 시키는 방안을 고려해 볼 수 있다. 필자의 경우 인력이 부족하고 필요성을 크게 느끼지 못해 시도하지 않았지만, 모킹 계층을 독립적으로 관리하기로 결정했다면 충분히 고려해 볼 만한 가치가 있다고 생각한다
<분량이 길어져 데이터 모델 의존성을 제거하는 방법은 다음 편에서 다루도록 한다>
'개발 이야기' 카테고리의 다른 글
프론트엔드 렌더링 패러다임의 변화와 의미(ft. RSC, Streaming SSR, PPR) (35) | 2024.07.09 |
---|---|
프론트엔드, 서버로부터 독립을 선포하다(2) - 뷰 모델로 데이터 모델 의존성 줄이기 (35) | 2024.06.30 |
지속 가능하고 효율적인 코드 리뷰를 하는 방법 (33) | 2024.06.17 |
Cypress로 E2E 테스트 작성하기(ft. App action vs Page object model) (30) | 2024.06.12 |
프론트엔드에서 로깅은 왜 필요할까?(ft. 의존성 주입과 선언형 프로그래밍) (32) | 2024.06.04 |