23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
개발 프로젝트에서 퍼블리셔와 개발자 간의 원활한 협업은 매우 중요합니다.
특히 저희 프로젝트에서는 스토리북(Storybook)을 이용해서 프로젝트 중간에 디자인 검수와 웹 접근성 검사를 수시로 진행 했기 때문에 항상 스토리북이 최신 상태로 잘 동작해야 했습니다.
이번 포스팅에서는 스토리북으로 제작된 퍼블 산출물이 개발과정에서 깨지는 원인을 분석하고, 이를 해결하기 위해 고민했던 내용을 공유합니다.
퍼블리셔는 디자인 가이드에 맞춰 화면을 컴포넌트 형태로 개발합니다.
개발자는 API 연동을 포함한 비즈니스 로직 개발을 하고 필요 시, 퍼블리셔의 컴포넌트도 수정 할 수 있습니다.
프로젝트에서 스토리북은 디자인과 웹 접근성 검수를 위해 수시로 사용되므로, 항상 깨지지 않도록 유지해야 합니다.
그러나 협업 과정에서 사소한 실수로 인해 스토리북이 깨지는 경우가 빈번히 발생했습니다.
환경적 차이로 인한 깨짐
퍼블리셔가 만든 컴포넌트가 스토리북에서 사용될때와 실제 서비스에서 사용될때는 여러가지 환경적 차이가 있습니다.
스토리북에서는 API 요청을 하지 않아 보유 데이터 차이 발생
특정 상황에서 세팅되는 쿠키의 차이
실제 서비스에서만 생성되는 Hook 사용
특정 절차를 건너뛰고 바로 접근하는 이유등으로 내부 상태 설정 안됨
Props 수정 시 실수
퍼블리셔가 정의한 props를 개발자가 수정할 때, 관련 스토리를 함께 수정해야 합니다.
다른 컴포넌트에서 해당 컴포넌트를 사용하는 부분도 함께 확인해야 합니다.
퍼블리셔와 개발자의 산출물 차이
퍼블리셔 산출물은 화면에 필요한 모든 값을 props로 받지만, 개발자는 일부 값만 props로 받는 경우가 있습니다.
예) 개발 코드에서 dataId만 props로 받고, 나머지는 API로 요청하는 경우
중첩된 컴포넌트 구조에서는 하위 컴포넌트의 props 변경이 상위 컴포넌트 스토리를 깨뜨릴 수 있습니다.
장점:
퍼블리셔와 개발자가 익숙한 방식으로 빠르게 개발 가능
단점:
이슈가 발생할 때마다 상황을 파악하고 대응해야 하므로 비효율적
근본적인 Props 차이 문제 해결 불가
장점:
개발자가 원하는 대로 자유롭게 작업 가능
단점:
중복 파일이 많아지며 관리가 어려워짐
퍼블리셔가 디자인을 수정하면 개발자가 diff를 확인하고 적용해야 함
장점:
실제 서비스와 유사한 환경에서 테스트 가능
단점:
퍼블리셔가 UI를 확인할 수 있는 스토리 레벨 테스트가 어려움
장점:
개발 시 상황별 처리 방안에 대한 고민을 줄이고 정해진 규칙을 따르는 것으로 스토리북 무결성 유지 가능
단점:
개발자의 자유도가 제약될 수 있음
이번 프로젝트에서는 위에서 언급한 문제를 해결하기 위해 몇가지 개발 규칙을 마련하고 이를 묶어서 Container In Component (CIC) 패턴이라고 이름 붙였습니다.
기존의 Container/Presentational Component 패턴을 기반으로, 퍼블리셔와 개발자의 협업을 원활히 하고 스토리북 무결성을 유지하기 위해 규칙을 추가한 방식입니다.
퍼블리셔와 개발자의 협업을 원활히 하고, 스토리북 무결성을 유지합니다.
중복 파일 생성을 방지하여 파일 관리를 단순화합니다.
중첩된 컴포넌트 구조에서도 스토리북이 깨지지 않도록 합니다.
하나의 Component가 Presentational Component 모드와 Container Component 모드로 동작합니다.
Presentational Component: 스토리북에서 사용 (퍼블리셔의 작업 영역)
Container Component: 실제 비즈니스 로직을 실행해서 데이터를 생성 후 Presentational Component를 이용해서 화면에 노출합니다. (프론트엔드 개발자의 작업 영역)
두 컴포넌트의 분기는 실행 환경을 자동으로 인식하여 처리됩니다.
이 분기 처리는 StorybookShield라는 함수로 구현했습니다. 이 함수는 현재 실행 환경이 스토리북인지 확인한 뒤, 환경에 따라 적절한 Component를 반환 합니다.
모든 스토리북용 props는 한개의 디렉토리 하위에 정의하고 관리
퍼블리셔는 Presentational Component 개발 시에 스토리를 작성하고 스토리에 사용된 샘플 Props는 모두 한개의 디렉토리 하위에 정의를 해 둡니다.
개별 스토리등에 Props들이 흩어져 있을 경우, 개발자가 Presentational Component를 수정 하고 매칭되는 스토리 Props를 찾아서 수정할때 빈번했던 실수를 줄이기 위한 규칙.
Presentational Component와 매칭되는 Container Component는 동일 파일에 관리
실제 서비스 개발 시에 두개의 파일을 동시에 열어야 되는 경우가 매우 빈번했는데, 이런 번거로움 해소 방안
코드 블럭은 명확히 나누어서 작업자간 충돌 방지
// Presentational Component: 퍼블리셔가 작성하는 부분
export function View(props: ViewProps) {
return <div>{props.content}</div>;
}
// Container Component: 프론트엔드 개발자가 작성하는 부분
export default StorybookShield(View, () => {
const content = fetchDataFromAPI();
return <View {...content} />;
});StorybookShield는 현재 실행 환경이 스토리북인지 여부를 판단합니다.
만약 스토리북 환경이라면 Presentational Component를 바로 반환하며, 그렇지 않다면 내부 로직을 실행하는 Container Component로 동작합니다.
스토리북 여부 판단은 zustand의 store를 참조하여 이루어지며, 이 값은 스토리북의 decorators를 통해 설정되도록 했습니다.
퍼블리셔와 개발자의 역할 분리: 퍼블리셔는 퍼블 산출물에 집중하고, 개발자는 비즈니스 로직 개발에 집중할 수 있습니다.
스토리북 Props 수정 실수 방지: 스토리북에 사용되는 샘플 props를 한 곳에서 관리하므로, 수정 대상이 되는 props를 조금 더 명확하게 찾을 수 있습니다.
무결성 보장: 스토리북에서 개별 컴포넌트가 정상 동작하면, 중첩된 상태에서도 깨지지 않습니다.
CIC 패턴으로 모든 스토리북이 깨지는 문제가 저절로 해결되지는 않습니다.
다만 한군데에 정의 해둔 Presentational Component의 샘플 Props만 현행화를 해 준다면, 해당 컴포넌트가 어떤 상황에서 사용 되든지 스토리북에서 깨지지 않고 정상 노출이 될 수 있었습니다.
덕분에 스토리북 무결성 유지를 위해 사용되는 공수를 크게 줄일 수 있었습니다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.