카카오스타일 파트너센터가 스토리마다 따로 정의하던 MSW mock을 한 곳으로 모으고, 1,000개가 넘는 기존 mock을 recast 기반 스크립트로 자동 이관한 기록이다. 구조 제안, 파일 규칙 결정, 스토리북 연결, 오버라이드 방식, 마이그레이션 자동화까지 순서대로 이어진다. AST가 무엇인지부터 설명하는 대목이 함께 있다.
핵심 포인트- 스토리마다 mock을 두면 문서화 효과는 있지만 API 변경 시 관련 mock을 다 찾아 고쳐야 하고 누락으로 스토리가 깨진다.
- 파트너센터는 단일 API 서버를 보고 페이지마다 쓸 수 있는 API도 같아서 중앙화 이득이 크다고 판단했다.
- 폴더 규칙을 새로 정하는 대신 GraphQL 파일 옆에 fixtures와 mocks 파일을 나란히 두는 방식을 골랐다.
- webpack의 require.context로 흩어진 파일을 한 번에 불러와 전역 handlers로 만든다.
- handlers를 객체로 주면 애드온이 키 단위로 병합하므로 snake_case 키를 규칙으로 삼아 스토리에서 개별 오버라이드가 가능해진다.
- 1,000개 이상의 mock을 손으로 옮기면 16시간이 넘고 git 충돌도 잦아 recast로 자동화했다.
상세 정리- 기존 방식: 애드온 예시대로 각 스토리의 parameters 안 msw handlers에 graphql 쿼리와 rest 핸들러를 직접 나열했다.
- 기존 방식의 장점: 컴포넌트가 필요로 하는 API가 문서로 남고, 정의된 mock만 쓰므로 테스트 케이스가 의도치 않게 변하지 않는다.
- 기존 방식의 단점: 공통 컴포넌트에 연관된 API가 바뀌면 영향받는 모든 스토리를 고쳐야 한다.
- 새 구조의 골자: 응답을 상수로 두는 fixtures와 msw 핸들러를 두는 mocks를 분리하고 스토리는 그 목록을 그대로 참조한다.
- 중앙화의 이득: 중복이 줄어 API 변경 대응이 쉬워지고, 컴포넌트에 API 호출이 추가돼도 스토리별 대응이 필요 없어 작성 난이도가 내려간다.
- 중앙화의 비용: 전역에 API가 있다는 가정이 들어가 컴포넌트를 프로젝트 밖으로 빼기 어려워지고, 관리 가이드라인과 오버라이드 방식이 필요하며 기존 스토리를 전부 고쳐야 한다.
- 폴더 규칙 검토: 한 파일이나 한 폴더에 모으는 안은 GraphQL 관련 규칙까지 동시에 정해야 해서 혼란이 크다고 보고 접었다.
- 선택한 규칙: 프로젝트 전체 규칙은 없어도 작업자와 도메인별 암묵적 체계는 있으므로 그것을 살려 graphql 파일 옆에 fixtures와 mocks 파일을 나란히 두기로 했다.
- fixtures 규칙: GraphQL 쿼리 이름을 그대로 따라 대문자 스네이크로 상수를 만들고 타입도 지정하기를 권한다. query와 mutation은 이름이 겹치지 않아 구분하지 않는다.
- mocks 규칙: 동적 로직이 들어가거나 REST처럼 쿼리 이름이 없는 요청이 들어간다. REST는 대응하는 graphql 파일이 없어 별도 파일로 모았다.
- 자동 수집: require.context에 node_modules를 제외하는 정규식을 주어 fixtures와 mocks 파일을 모두 임포트하고, 이름을 캐멀케이스로 바꿔 실제 API 함수가 존재하는지 확인한 뒤 query와 mutation 핸들러를 함께 만든다.
- 오버라이드: 애드온이 handlers 객체를 키 단위로 병합하므로 전역에서 스네이크 케이스 키로 넣어두면 스토리에서 같은 키를 지정해 덮어쓸 수 있다. 전체를 비활성화하려면 배열로 넘긴다.
- 마이그레이션 판단: 하나에 1분씩 잡아도 16시간이 넘고 코드베이스 전반이 바뀌어 충돌이 잦으며, 다른 프로젝트에도 같은 요구가 있을 것으로 봐 자동화를 택했다.
- 도구 선택: jscodeshift는 파일 하나만 수정할 수 있어 여러 파일을 열고 쓰는 recast를 썼다.
- 추출 로직: 스토리 파일을 AST로 바꿔 export default 아래 parameters, msw, handlers를 순서대로 타고 들어가 graphql 호출을 찾는다. recast에는 선택자 기능이 없어 노드를 직접 순회한다.
- 분류 로직: 응답이 상수로 떨어지고 내부에서 req나 res 인자를 쓰지 않으면 fixtures로, 로직이 있으면 호출째로 mocks로 보낸다.
- 매핑 로직: graphql 패키지의 parse로 스키마 파일을 훑어 쿼리 이름과 파일 경로의 대응표를 만든다. REST는 이름이 없어 임의 이름으로 대체했다.
- 정리 로직: mock 호출을 지운 뒤 부모 노드가 비면 부모까지 지워 빈 parameters 객체가 남지 않게 하고, ast-types의 scope로 mock이 참조하던 변수의 import를 추려 옮기고 남은 코드가 안 쓰면 지운다.
- 후처리: 코딩 스타일이 깨져 prettier를 따로 돌렸고, 기존 fixtures 이름이 겹쳐 자기 자신을 대입하는 구문이 생기는 경우는 수작업으로 재export로 바꿨다.
- 결과: 스토리를 쓸 때 API를 신경 쓰지 않아도 되면서 작성 난이도가 크게 내려가고 스토리북 오류가 줄었다고 정리한다.
왜 읽나스토리북 mock이 스토리마다 흩어져 관리가 안 되고 있다면 중앙화 설계와 함께, 손댈 파일이 1,000개일 때 AST 스크립트로 옮기는 절차까지 참고할 수 있다.