2025년 5월 05일·7분 읽기

대규모 컴포넌트 라이브러리에서의 Vue 3 Composition API vs Options API

Vue 3의 Composition API와 Options API가 대규모 관리자 컴포넌트 라이브러리에서 재사용성, 테스트 용이성, 온보딩에 어떤 영향을 주는지 정리한 가이드.

대규모 컴포넌트 라이브러리에서의 Vue 3 Composition API vs Options API

큰 관리자 컴포넌트 라이브러리에서 이 선택이 중요한 이유

관리자 앱의 대규모 컴포넌트 라이브러리는 몇 개 버튼만 있는 마케팅 사이트와 다릅니다. 정렬과 일괄 작업이 있는 데이터 테이블, 필터 패널, 검증 규칙이 있는 폼, 드로어와 모달, 확인 흐름, 날짜 선택기나 권한 가드 같은 작은 유틸리티까지 수십 또는 수백 개의 빌딩 블록이 화면 전반에 반복됩니다.

이 패턴들이 여기저기 등장하기 때문에 팀은 종종 기한을 맞추기 위해 코드를 복사·수정합니다. 한 테이블엔 커스텀 필터 바가 생기고 다른 테이블엔 약간 다른 버전이 붙어 결국 다섯 개의 "거의 동일한" 버전이 생깁니다. 그때 Composition API vs Options API 선택은 개인 취향의 문제가 아니라 라이브러리 전체 건강에 영향을 주는 결정이 됩니다.

보통 먼저 깨지는 것은 일관성입니다. UI는 동작하지만 동작 방식이 흩어집니다. 한 곳의 모달은 Escape로 닫히는데 다른 곳은 그렇지 않을 수 있습니다. 같은 필드가 한 페이지에선 blur에서 검증되고 다른 페이지에선 submit에서 검증될 수 있습니다. 그다음 속도가 떨어집니다. 변경할 때마다 거의 중복된 파일을 뒤져야 하기 때문입니다. 마지막으로 자신감이 떨어집니다. 사람들은 어떤 리팩터가 어떤 영향을 줄지 예측할 수 없어 손대기를 꺼립니다.

공유 라이브러리에서 실무적으로 중요한 결정은 세 가지입니다:

  • 코드 재사용: 얽히지 않는 방식으로 공통 로직을 패키징하는 법.\n- 테스트: UI 중심의 깨지기 쉬운 테스트 없이 동작을 검증하는 쉬움.\n- 온보딩: 새로운 기여자가 컴포넌트를 읽고 안전하게 변경하는 속도.

간단한 예: 관리자 패널에 목록 페이지가 20개 있고 제품팀에서 '저장된 필터' 기능을 요청했다고 합시다. 테이블, 필터, URL 동기화 로직이 흩어져 있거나 일관되지 않으면 기능을 느리게 내놓거나 버그와 함께 내놓게 됩니다. 어떤 API 스타일을 택하느냐가 그 로직이 재사용 가능한 한 곳에 모일지, 각 화면에 얼마나 명확히 연결되는지, 새 사람이 얼마나 쉽게 확장할 수 있는지를 결정합니다.

Vue 3 관리자 앱(예: 웹 UI 레이어에 Vue 3을 사용하는 AppMaster 같은 플랫폼을 포함해)을 만든다면 이 선택을 일찍 하면 나중에 몇 달의 유지보수를 절약할 수 있습니다.

Options와 Composition이 일상 코드에서 어떻게 다른가

차이를 가장 빠르게 느끼는 방법은 큰 관리자 컴포넌트를 열고 이렇게 물어보는 것입니다: “이 기능의 동작을 어디에서 바꾸지?” 라이브러리에서는 이 질문을 매일 하게 됩니다.

Options API에서는 코드가 타입별로 묶입니다: 상태는 data, 동작은 methods, 파생값은 computed, 부수효과는 watch에 둡니다. 컴포넌트가 작을 때는 이 구조를 훑어보기 쉽습니다. 하지만 큰 테이블이나 폼에서는 한 기능(예: 일괄 작업이나 필드 검증)의 로직이 여러 블록에 걸쳐 흩어지는 일이 많습니다. 깔끔하게 유지할 수는 있지만 일관된 네이밍과 규율이 없으면 파일을 여기저기 오가며 읽어야 하는 흐름이 됩니다.

Composition API에서는 보통 기능별로 코드를 묶습니다. 관련된 상태, 파생값, 부수효과, 헬퍼를 가까이에 정의하고 공통 로직은 composable로 뽑습니다. 관리자 스타일 라이브러리에서는 사람들이 생각하는 방식과 잘 맞는 경우가 많습니다. "필터에 관한 모든 것"이 여기에 있고, "행 선택에 관한 모든 것"이 여기에 있는 식입니다. 또한 여러 컴포넌트에서 usePagination 같은 composable을 재사용해 중복을 줄일 수 있습니다.

일상적으로 크게 다른 점은 의존성이 어떻게 드러나는가입니다.

  • Options API는 더 암시적으로 느껴질 수 있습니다: 어떤 메서드가 this.user, this.filters, this.loading에 의존하는데 메서드 내부를 읽어보면서 그걸 알게 됩니다.\n- Composition API는 더 명시적인 편입니다: 함수가 filtersloading을 클로저로 잡고 있다면 해당 변수들이 근처에 정의된 것을 볼 수 있고, 필요하면 헬퍼로 전달할 수도 있습니다.

단점은 Composition API가 모든 코드를 하나의 setup()에 덤핑하면 시끄러워질 수 있다는 점입니다.

실용적인 경험칙:

  • 컴포넌트가 주로 표현적이고 로직이 가벼우면 Options API를 선택하세요.\n- 라이브러리에 걸쳐 공유 규칙을 가진 여러 기능이 있는 컴포넌트라면 Composition API를 선택하세요.\n- Composition API를 선택하면 기능별로 코드를 묶는 단순한 레이아웃에 합의하세요(‘모든 ref를 먼저 선언’ 같은 규칙이 아니라).\n- Options API를 선택하면 네이밍을 엄격히 하고 관련 로직을 짧은 주석과 일관된 메서드명으로 묶어두세요.

둘 다 잘 작동할 수 있습니다. 핵심은 다음 변경이 명확하고 안전하게 느껴지도록 조직 스타일을 선택하는 것입니다.

코드 재사용: 어떤 방식이 깔끔하게 확장되는가

관리자 스타일 앱에서 재사용은 선택이 아니라 필수입니다. 화면 여러 곳에서 같은 동작이 반복되고 작은 불일치는 버그와 지원 요청으로 이어집니다.

대부분의 재사용 요구는 몇 가지 반복되는 범주에 속합니다: 백엔드와 맞는 정렬/필터/페이지네이션, 폼 검증과 에러 매핑, 권한 검사와 UI 게이팅, 쿼리 동기화(URL 파라미터, 저장된 뷰, 기본 필터), 테이블 선택 규칙을 포함한 일괄 작업 등.

Options API 재사용: 강력하지만 복잡도가 숨어들기 쉬움

Options API에서는 재사용이 주로 mixin, extends, 또는 플러그인으로 시작됩니다.

Mixin은 빠르지만 어디서 값이나 메서드가 왔는지 숨기기 때문에 확장성이 떨어집니다. 두 믹스인이 같은 메서드 이름을 쓰면 조용히 충돌하고 그 결과 동작을 디버깅하기 어려워집니다.

extends는 mixin보다 깔끔하게 느껴질 수 있지만 상속 구조 때문에 컴포넌트가 실제로 무엇을 하는지 이해하려면 여러 파일을 읽어야 하는 퍼즐이 생깁니다. 플러그인은 전역 디렉티브나 공유 서비스 같은 앱 수준 문제에는 좋지만 화면마다 다른 비즈니스 규칙을 담기엔 적합하지 않습니다.

재사용이 암시적으로 변할 때 지저분함이 찾아옵니다. 새 기여자는 "이 데이터는 어디서 왔지?"라는 질문을 코드베이스 전체를 검색하지 않고는 못 답할 수 있습니다.

Composition API 재사용: composable로 명시성이 유지됨

Composition API 재사용은 보통 composable을 중심으로 이뤄집니다: ref, computed, 핸들러를 반환하는 작은 함수들입니다. 큰 장점은 재사용이 컴포넌트 상단 근처에 보이게 되고 숨겨진 컴포넌트 컨텍스트에 의존하는 대신 파라미터를 전달할 수 있다는 점입니다.

예를 들어 usePagination은 기본값을 받아 일관된 형태로 변경을 방출할 수 있고, usePermissions는 현재 역할과 기능 이름을 받아 처리할 수 있습니다. 이때 선택은 문법 문제가 아니라 라이브러리가 암시적 상속보다 명시적 연결을 선호하느냐의 문제로 바뀝니다.

재사용을 예측 가능하게 유지하려면 각 재사용 단위를 작은 API처럼 다루세요: 명확한 이름, 입력과 출력 정의, 하나의 책임. 하나의 composable이 페이지네이션, 캐싱, 권한, 알림을 모두 다루기 시작하면 분리하세요. 후에 하나의 조각만 바꿔도 전체가 깨지지 않게 하는 것이 훨씬 쉽습니다.

폼과 테이블 컴포넌트를 고통 없이 재사용 가능하게 만들기

관리자 스타일 앱에서는 폼과 테이블이 컴포넌트 라이브러리가 성과를 내느냐 미로가 되느냐를 가르는 지점입니다. 두 API 모두 쓸 수 있습니다. 차이는 더티 상태, 에러 매핑, 제출 흐름 같은 공유 동작을 어떻게 패키징하느냐입니다.

공유 폼 로직의 경우 Options API는 보통 mixin이나 공유 헬퍼로 밀어넣는 경향이 있습니다. 처음엔 편하지만 나중에는 "이 필드 에러는 어디서 오는가?"나 "왜 제출 버튼이 비활성화되는가?" 같은 기본 질문에 답하기 어려워집니다.

Composition API는 이러한 재사용을 composable(예: useDirtyState, useFormErrors, useSubmitFlow)로 옮겨오면 폼 컴포넌트가 무엇을 끌어오는지 명확히 보이기 때문에 이 점에서 더 투명합니다. 대규모 라이브러리에서는 이 명확성이 몇 줄을 아끼는 것보다 더 중요할 때가 많습니다.

컴포넌트 API를 안정적으로 유지하는 실용적인 방법은 공개 표면(public surface)을 계약처럼 취급하는 것입니다: props, emits, slots는 자주 바뀌지 않게 하세요. 이 계약은 어느 스타일이든 비슷하게 보이지만 Composition API는 내부를 한 번에 하나의 composable로 바꿀 수 있어 리팩터가 더 안전한 경우가 많습니다.

라이브러리가 커질 때도 유지되는 패턴:

  • 하나의 역할만 잘하는 베이스 컴포넌트(BaseInput, BaseSelect, BaseTable)를 만들고 이를 기능 컴포넌트로 조합하세요.\n- 레이아웃 유연성에는 props를 늘리기보다 slots를 선호하세요(액션 영역, 빈 상태, 셀 렌더링 등).\n- 이벤트를 일찍 정규화하세요(예: update:modelValue, submit, rowClick) so 앱이 내부 세부사항에 의존하지 않게 합니다.\n- 검증과 포맷팅은 입력 근처에 두되 비즈니스 규칙은 composable이나 부모 컨테이너에 두세요.

과도한 추상화는 흔한 함정입니다. 모든 필드 타입과 검증 규칙, 레이아웃 옵션을 다루는 ‘슈퍼 폼’은 결국 사용하기 어려워집니다. 좋은 규칙은: 베이스 컴포넌트에 다양한 팀의 요구를 커버하려고 소수 이상의 많은 props가 필요하면 아마 두 컴포넌트일 가능성이 높다는 것입니다.

때로는 중복이 맞는 선택일 수 있습니다. 한 화면만 특이한 멀티-로우 그룹 헤더가 필요하다면 작은 부분을 복사해 로컬로 유지하세요. 영리한 추상화는 유지보수 꼬리표가 길어지는 경향이 있고 새 기여자는 ‘정상’ 컴포넌트와 프레임워크 안의 프레임워크 차이를 이해하는 데 애를 먹습니다.

Composition과 Options 사이를 결정할 때는 데이터 흐름의 가독성을 우선하세요. 재사용도 좋지만 사용자 액션에서 emit되는 이벤트로 가는 경로를 숨길 만큼 좋으면 안 됩니다.

테스트 영향: 어떤 것이 검증하기 쉬운가

유지보수 부채 피하기
요구사항이 바뀔 때 애플리케이션을 재생성해 코드 유지보수를 줄이세요.
AppMaster 사용해보기

컴포넌트 라이브러리에서 테스트는 보통 세 가지 버킷으로 나뉩니다: 순수 로직(포맷팅, 검증, 필터링), 렌더링(주어진 상태에서 무엇이 보이는가), 상호작용(클릭, 입력, 키보드, emit). 선택한 API 스타일에 따라 첫 번째 버킷을 전체 컴포넌트 마운트 없이 검증할 수 있는 빈도가 달라집니다.

Options API 테스트는 종종 "컴포넌트를 마운트하고 인스턴스 상태를 조작한 뒤 DOM을 확인"하는 형태가 됩니다. 이 방법은 작동하지만 로직이 methods, computed, watch, 라이프사이클 훅에 섞여 있으면 더 큰 테스트를 만들기 쉽습니다. 실패하면 watcher 타이밍인지 라이프사이클 부작용인지 로직 자체인지 파악하느라 시간이 걸립니다.

Options API는 다음에 직관적입니다:

  • 마운트 시점에 의존하는 사용자 흐름(마운트 시 fetch, 라우트 변경 시 리셋)\n- watcher 기반 동작(자동 저장, 쿼리 동기화)\n- 컴포넌트 메서드에서의 이벤트 발행(save(), reset(), applyFilter())

Composition API는 균형을 이동시킵니다. 로직을 composable로 옮기면 해당 로직을 순수 함수처럼 단위 테스트할 수 있어 작은 입력과 명확한 출력을 기반으로 검증할 수 있습니다. 이렇게 하면 "마운트하고 클릭"하는 테스트 수가 줄고 실패 지점이 더 국소화됩니다. 또한 전역을 목킹하는 대신 fetch 함수, 날짜 포맷터, 권한 체크 같은 의존성을 composable에 주입해 제어하기 쉽습니다.

구체적인 예: 정렬, 페이지네이션, 선택 행을 가진 재사용 가능한 AdminTable이 있다고 합시다. Composition API라면 선택 로직을 useRowSelection()에 두고 테이블을 렌더링하지 않고도 토글, 클리어, 전체 선택, 페이지 간 유지 같은 동작을 테스트할 수 있습니다. 그후 템플릿이 버튼, 체크박스, emit 이벤트를 올바르게 연결하는지 확인하는 작은 컴포넌트 테스트만 유지하면 됩니다.

테스트를 작고 읽기 쉽게 유지하려면(스타일과 무관하게):

  • 비즈니스 규칙은 순수 함수나 composable에 두고 watcher에 넣지 마세요.\n- 사이드 이펙트(fetch, storage, 타이머)는 주입된 의존성 뒤에 숨기세요.\n- 컴포넌트당 몇 가지 집중된 통합 테스트를 선호하세요, 하나의 거대한 ‘모두’ 테스트는 피하세요.\n- 상태와 이벤트 이름을 라이브러리 전반에 걸쳐 일관되게 명명하세요(테스트 설정이 단순해집니다).\n- 메서드 A가 watcher B가 실행되어야만 동작하는 식의 숨겨진 결합을 피하세요.

테스트 안정성 향상이 목표라면 라이프사이클 중심 동작을 줄이고 DOM 없이 검증 가능한 독립적 로직 단위를 늘리는 쪽으로 밀어보세요.

새 기여자 온보딩: 사람들이 얼마나 빨리 생산적이 되는가

배포 방식을 선택하세요
AppMaster Cloud에 배포하거나 자체 클라우드에 배포하거나 소스 코드를 내보내세요.
앱 배포

대규모 관리자 컴포넌트 라이브러리에서 온보딩은 Vue를 가르치는 것보다 사람들에게 코드를 찾게 하고 같은 규칙을 따르게 하며 안전하게 변경할 수 있다는 확신을 주는 것이 더 중요합니다. 대부분의 지연은 세 가지 격차에서 옵니다: 네비게이션(로직이 어디 있는가), 규약(여기서는 어떻게 하는가), 자신감(다섯 개 화면을 망치지 않고 어떻게 변경하나).

Options API에서는 구조가 익숙하므로 초반 진입은 빠릅니다: props, data, computed, methods, watchers. 대가로 실제 동작은 종종 흩어져 있습니다. ‘서버 사이드 필터링’ 같은 한 기능이 watcher, computed, 두 개의 methods, 그리고 mixin에 나뉠 수 있습니다. 각 블록은 읽을 수 있지만 스토리를 잇는 데 시간이 듭니다.

Composition API의 온보딩 장점은 관련 로직이 함께 있을 수 있다는 점입니다: 상태, 부수효과, 헬퍼가 한 자리에 모입니다. 비용은 composable 숙련도입니다. 새 기여자는 useTableState() 같은 패턴과 반응형 값이 여러 composable을 통해 흐르는 방식을 배워야 합니다. 경계가 명확하지 않으면 파일 사이를 지도 없이 뛰어다니는 것처럼 느껴질 수 있습니다.

몇 가지 규약으로 대부분의 질문을 제거할 수 있습니다(어떤 스타일이든):

  • 예측 가능한 구조를 사용하세요: components/, composables/, types/, tests/.\n- 네이밍 패턴을 정하고 지키세요(예: useX, XTable, XForm).\n- 짧은 docblock을 추가하세요: 컴포넌트가 하는 일, 주요 props, 주요 이벤트.\n- 하나의 “이스케이프 해치” 규칙을 정의하세요(새 composable이나 헬퍼를 언제 추가해도 되는지).\n- 선호 패턴을 보여주는 작은 “골든” 컴포넌트를 유지하세요.

예: 팀이 Vue 3 관리자 패널을 생성한 뒤 커스터마이즈한다면(예: AppMaster로 생성된 웹 앱을 개발자가 확장하는 경우), 테이블 동작(정렬, 필터, 페이지네이션)을 조정할 수 있는 한 곳과 UI 연결을 조정할 수 있는 한 곳(slots, column renderers, row actions)이 있을 때 온보딩이 크게 좋아집니다. 이 명확성은 선택한 API보다 더 중요합니다.

스타일을 선택하고 안전하게 도입하는 단계별 방법

대규모 관리자 UI 라이브러리에서는 질문을 확정하는 가장 안전한 방법은 잘 경계된 기능 하나로 시작해 파일럿처럼 다루는 것입니다. 전체 리팩터가 아니라 파일럿을 하세요.

명확한 동작과 재사용성이 높은 모듈 하나(예: 테이블 필터링이나 폼 검증)를 고릅니다. 코드를 건드리기 전에 현재 무엇을 하는지 적어두세요: 입력(props, 쿼리 파라미터, 사용자 액션), 출력(이벤트, emit, URL 변경), 경계 사례(빈 상태, 리셋, 서버 에러).

다음으로 경계를 정하세요. 무엇을 컴포넌트 내부에 남길지(렌더링, DOM 이벤트, 접근성)와 공유 코드로 옮길지를(필터 파싱, 디바운스, API 파라미터 생성, 기본 상태) 결정합니다. 여기서 라이브러리가 자주 실수하는 지점은 UI 결정을 공유 코드로 옮겨 재사용을 어렵게 만드는 것입니다.

실용적인 롤아웃 계획:

  • 패턴을 명확히 보여주고 여러 화면에서 사용되는 컴포넌트를 하나 고르세요.\n- 작은 명시적 API를 가진 공유 단위(composable 또는 헬퍼)를 추출하세요.\n- 실제 관리자 시나리오 기반의 집중 테스트를 그 단위에 먼저 추가하세요.\n- 새 단위를 사용해 선택한 컴포넌트를 끝까지 리팩터하세요.\n- 같은 패턴이 확장되는지 확인하기 위해 또 하나의 컴포넌트에 적용하세요.

공유 API는 밋밋하고 명확하게 유지하세요. 예를 들어 useTableFilters()는 초기 필터를 받아 filters, apply(), reset(), toRequestParams() 같은 인터페이스를 노출하면 됩니다. 전역 상태에서 마법처럼 읽어들이는 동작은 피하세요, unless 그것이 앱의 확고한 규칙이라면 명시적으로 하세요.

파일럿 후에는 기여자가 복사해서 쓸 수 있는 짧은 내부 가이드라인과 한 가지 예제를 배포하세요. 긴 문서보다 한 가지 규칙이 더 효과적입니다. 예: “모든 테이블 필터 로직은 composable에 있고 컴포넌트는 UI 컨트롤 바인딩과 apply() 호출만 한다.”

확장 전에 간단한 완료 정의(done)를 사용하세요:

  • 새 코드가 두 컴포넌트에서 같은 방식으로 읽힌다.\n- 테스트가 전체 UI를 마운트하지 않고 공유 로직을 커버한다.\n- 새 기여자가 관련 없는 파일을 건드리지 않고 필터 규칙을 변경할 수 있다.

AppMaster 같은 노코드 도구로 생성한 관리자 포털도 있다면 동일한 파일럿 마인드셋을 적용하세요: 하나의 워크플로(예: 승인)를 골라 동작을 정의한 뒤 패턴을 표준화하고 확장하세요.

이번 주 한 가지 할 일이 있다면 Users 페이지를 템플릿으로 삼아 코드 리뷰에서 그것을 강제하세요. 하나의 명확한 예제가 긴 스타일 가이드보다 일관성에 더 도움됩니다.

대규모 라이브러리에서 흔한 실수와 함정

동작을 일관되게 만들기
승인, 저장, 확인 같은 흐름을 시각적 비즈니스 프로세스로 표준화하세요.
시작하기

큰 컴포넌트 라이브러리의 가장 큰 문제는 문법이 아닙니다. 작은 지역적 결정들이 쌓여 재사용, 테스트, 유지보수를 어렵게 만듭니다.

흔한 함정 중 하나는 패턴을 혼용하는 것입니다. 라이브러리의 절반은 Options API, 절반은 Composition API를 쓰는데 규칙이 없다면 새 컴포넌트마다 스타일 논쟁이 벌어집니다. 또한 동일한 문제에 대해 서로 다른 형태로 중복된 해결책이 생깁니다. 둘 다 허용하려면 명확한 정책을 쓰세요: 새 컴포넌트는 한 스타일을 사용하고 레거시는 필요한 경우에만 수정하며 공유 로직은 한 곳에 둡니다.

또 다른 함정은 ‘god composable’입니다. useAdminPage()useTable()처럼 시작한 하나가 라우팅, 페칭, 캐싱, 선택, 다이얼로그, 토스트, 권한까지 흡수하면 테스트하기 어려워지고 재사용성도 떨어집니다. 화면마다 필요한 것은 일부뿐인데 전체 복잡도를 떠안게 됩니다.

Watcher도 문제의 출처가 됩니다. 동기화가 맞지 않을 때 watcher를 추가하기 쉬운데 비동기 데이터와 디바운스 입력과 함께 타이밍 버그가 나중에 드러납니다. 사람들이 "가끔 선택이 초기화돼요"라고 보고하면 재현하는 데 몇 시간을 쓸 수 있습니다.

대부분 라이브러리가 문제로 향하고 있다는 적신호:

  • 컴포넌트가 특정 props와 이벤트 순서에서만 동작한다.\n- composable이 전역 상태를 읽고 쓰는데 그게 명확하지 않다.\n- 여러 watcher가 같은 상태를 갱신한다.\n- 리팩터할 때 소비자 화면들이 작은 방식으로 계속 깨진다.\n- 기여자들이 "그 파일"을 위험하다고 피한다.

마지막 함정은 리팩터 중 공개 API를 깨는 것입니다. 관리자 앱에서 테이블, 필터, 필드 컴포넌트는 빠르게 퍼집니다. prop 이름을 바꾸거나 emit 이벤트를 수정하거나 slot 동작을 바꾸면 수십 개 화면이 조용히 깨질 수 있습니다.

더 안전한 접근법은 컴포넌트 API를 계약처럼 다루는 것입니다: 삭제 대신 점진적 폐기(deprecate)를 하고 한동안 호환성 shim을 유지하며 소비자 방식대로 컴포넌트를 마운트하는 간단한 사용 테스트를 추가하세요. AppMaster 같은 도구로 생성되는 Vue 3 관리자 인터페이스가 있다면 이 점은 더 중요합니다. 일관된 컴포넌트 계약이 화면 재사용을 쉽게 하고 변경을 예측 가능하게 합니다.

패턴을 결정하기 전에 빠르게 점검할 것들

노코드인데 여전히 Vue
노코드 워크플로와 Vue 3 UI 레이어를 결합해 개발자가 커스터마이즈할 수 있게 하세요.
지금 시작

Composition API, Options API, 또는 혼합을 선택하기 전에 실제 컴포넌트에서 몇 가지 빠른 점검을 하세요. 목표는 단순합니다: 로직을 빠르게 찾고 안전하게 재사용하며 관리자들이 실제로 의존하는 부분을 테스트하기 쉽게 만드는 것.

1) 누군가 로직을 빨리 찾을 수 있나?

필터 + 테이블 + 권한 + 일괄 작업이 있는 전형적인 관리자 컴포넌트를 열고 코드를 처음 보는 사람이라고 생각해 보세요.

좋은 신호는 기여자가 2분 안에 "필터 로직은 어디에 있지?"나 "버튼이 비활성화되는 이유는 무엇인가?"를 답할 수 있는 경우입니다. Options API면 보통 computed, methods, watcher에 로직이 분명히 나뉘어 있어야 하고, Composition API면 setup()이 작은 명명된 블록(또는 composable)으로 조직되어 있고 거대한 함수 하나가 되지 않아야 합니다.

2) 공유 유틸리티는 함수처럼 동작하나, 마법처럼 동작하지 않나?

선택한 패턴에 관계없이 공유 코드는 명확한 입력과 출력을 가져야 하고 사이드 이펙트는 최소여야 합니다. 헬퍼가 전역 상태를 건드리거나 전달된 객체를 변형하거나 네트워크 호출을 눈에 띄지 않게 트리거하면 재사용은 위험해집니다.

간단한 체크:

  • composable이나 헬퍼 시그니처를 보고 무엇을 반환할지 예측할 수 있나?\n- 추가 숨겨진 설정 없이 두 컴포넌트에서 사용할 수 있나?\n- 테스트에서 해킹 없이 상태를 리셋할 수 있나?

3) 테스트가 관리자 동작을 중심으로 되어 있나?

관리자 스타일 앱은 예측 가능한 방식으로 실패합니다: 필터가 잘못 적용되거나 권한 때문에 액션이 보이면 안 되는데 보이거나, 폼 검증이 일관되지 않거나 편집 후 테이블 상태가 깨집니다.

내부 구현 세부사항(예: watcher vs ref)을 테스트하기보다 동작을 중심으로 테스트하세요: “역할 X일 때 작업 Y가 숨겨진다”, “저장 시 에러가 나면 사용자 입력이 유지된다”, “필터 변경이 쿼리를 업데이트하고 빈 상태 메시지가 바뀐다” 같은 테스트는 리팩터해도 안정적입니다.

4) 비동기 상태에 대한 표준이 있나?

라이브러리는 옵션 로드, 필드 검증, 테이블 행 불러오기, 실패 시 재시도 같은 작은 비동기 흐름을 많이 가집니다. 각 컴포넌트가 자체적인 loading/error 처리를 발명하면 온보딩과 디버깅이 느려집니다.

비동기 상태(loading, error, retries, cancellation)에 대한 하나의 명확한 형태를 정하세요. Composition API는 보통 재사용 가능한 useAsyncX() composable을 권장하고 Options API는 data() 상태와 공유 메서드를 표준화할 수 있습니다. 중요한 것은 일관성입니다.

5) 컴포넌트 공개 API는 안정적이고 자기 설명적인가?

컴포넌트를 제품처럼 취급하세요. props, emitted events, slots가 계약입니다. 그 계약이 자주 바뀌면 모든 관리자 화면이 깨지기 쉽습니다.

의도(intent)를 설명하는 주석(기계적 설명이 아니라)은 큰 도움이 됩니다: props가 의미하는 것, 보장되는 이벤트, 내부로 간주되는 것. AppMaster로 생성된 내부 도구를 만든다면 같은 마인드셋이 도움이 됩니다: 안정적 빌딩 블록이 미래의 화면을 더 빠르게 만듭니다.

예시 시나리오와 팀을 위한 다음 단계

재구성할 관리자 'Users' 페이지를 상상해 보세요: 상태, 역할, 생성일자 필터가 있는 필터 바, 선택 가능한 행이 있는 테이블, 비활성화/삭제/내보내기 같은 일괄 작업, 역할 기반 접근(관리자만 일괄 삭제 가능, 매니저는 역할 수정 가능).

Composition API와 Options API로 코드 조직은 달라지지만 UI는 같을 수 있습니다.

Options API에서는 보통 하나의 큰 컴포넌트에 필터와 선택 상태를 data로, 파생 상태를 computed로, 페칭과 일괄 작업, 권한 검사를 methods에 두는 경향이 있습니다. 재사용은 mixin이나 공유 헬퍼로 나타납니다. 익숙하지만 관련 로직이 흩어질 가능성이 있습니다(페칭은 methods, 쿼리 동기는 watchers, 권한은 computed 등).

Composition API에서는 보통 쿼리와 필터, 테이블 선택 및 일괄 작업, 권한을 각각의 composable로 분리합니다. 페이지 컴포넌트는 이 조각들을 조립하는 역할을 하고 각 관심사의 로직은 함께 유지됩니다. 비용은 명확한 네이밍과 폴더 규약이 필요하다는 점입니다. 기여자들이 setup 안의 모든 것이 마법처럼 보이지 않게 해야 합니다.

관리자 라이브러리에서 재사용은 보통 URL 동기화 필터, 서버사이드 테이블 패턴(페이지네이션, 정렬, 전체 선택, 일괄 작업 가드), 권한 검사와 UI 게이팅, 일관된 빈/로딩 상태에서 자연스럽게 등장합니다.

대부분 팀에 맞는 다음 단계 계획:

  1. 새 코드의 기본 스타일을 정하고 예외는 문서화된 이유가 있을 때만 허용하세요.\n2. 규약을 정의하세요: composable은 어디에 두고, 어떻게 이름 짓고, 무엇을 임포트하며 무엇을 반환해야 하는지.\n3. 작은 참조 페이지(예: 이 Users 페이지)를 골든 스탠더드로 삼으세요.\n4. 재사용 가능한 부분(필터, 권한, 일괄 작업)을 먼저 테스트하세요, 시각적 레이아웃이 아니라 기능 중심으로.\n5. 일부 관리자 화면에서 속도가 맞는다면 노코드 도구(예: AppMaster)로 생성하고, 손으로 쓰는 라이브러리는 진정으로 고유한 부분에 집중하세요.

AppMaster로 이미 생성 중이라면 생성된 부분과 수동으로 작성한 부분에서 동일한 마인드셋을 유지하세요: 안정적 컴포넌트 계약과 명시적 단위로 묶인 공유 로직. 노코드를 내부 도구에 도입하려는 팀에게는 AppMaster가(브랜드명) 백엔드, 웹, 모바일을 포함한 전체 애플리케이션을 생성하고 Vue 3 웹 UI를 표준화할 수 있도록 도와줍니다.

이번 주 한 가지 하실 일: Users 페이지를 템플릿으로 정하고 코드 리뷰에서 그것을 강제하세요. 하나의 명확한 예제가 긴 문서보다 일관성에 더 큰 영향을 줍니다.

자주 묻는 질문

대규모 Vue 3 관리자 컴포넌트 라이브러리에는 어떤 API를 선택해야 하나요?

대부분의 반복 동작(필터링, 페이지네이션, 일괄 작업, 권한 게이팅 등)이 라이브러리 전반에 걸쳐 있다면 Composition API를 기본으로 권합니다. 공통 로직을 composable로 추출하기 쉽고 의존성이 더 명확해집니다. 반대로 컴포넌트가 주로 표현(presentational)이고 로직이 가벼우면 Options API가 더 적합할 수 있습니다.

Options API와 Composition API의 가장 큰 일상적 차이는 무엇인가요?

Options API는 data, methods, computed, watch처럼 타입별로 코드를 묶습니다. 그래서 하나의 기능 로직이 파일 여러 곳에 흩어지는 일이 자주 발생합니다. Composition API는 기능별로 코드를 묶는 경향이 있어 ‘필터링’이나 ‘선택’ 관련 모든 것이 한곳에 모이기 쉽습니다. 핵심은 다음 변경을 찾기 쉽고 안전하게 적용할 수 있게 하는 쪽을 택하는 것입니다.

대규모 라이브러리에서 mixin이 문제가 되는 이유는 무엇인가요?

Options API에서는 mixin이나 extends를 통해 재사용을 시작하는 경우가 많은데, 이 방식은 메서드나 computed가 어디에서 왔는지 숨기고 네이밍 충돌을 일으킬 수 있습니다. Composition API에서는 보통 composable로 재사용을 만들고 입력과 출력을 명확히 하므로 컴포넌트에서 연결이 보입니다. 공유 라이브러리에서는 명시적인(revealed) 재사용 방식이 더 오래 유지되는 경향이 있습니다.

composable이 ‘god composable’로 변하는 것을 어떻게 막을 수 있나요?

각 composable을 작은 API로 다루세요: 한 가지 역할, 명확한 파라미터, 예측 가능한 반환값. 페이지네이션, 캐싱, 권한, 알림을 한 composable에 모두 넣기 시작하면 분리해서 작은 단위로 나누는 것이 좋습니다. 작게 유지하면 테스트하기 쉽고 재사용하기 쉬우며 의도치 않은 사이드 이펙트를 줄입니다.

과도한 추상화 없이 재사용 가능한 테이블과 폼을 실용적으로 만드는 방법은?

공개 계약(public contract)을 안정적으로 유지하세요: props, emit 이벤트, slots는 자주 바꾸지 마세요. 입력 포맷팅과 기본 검증은 필드 컴포넌트 근처에 두고 비즈니스 규칙은 composable 또는 컨테이너 컴포넌트로 분리하세요. 이렇게 하면 내부를 리팩터해도 각 화면을 일괄적으로 바꿀 필요가 없습니다.

관리자 UI 라이브러리에서 어느 API가 테스트를 더 쉽게 하나요?

Composition API는 로직을 composable이나 순수 함수로 옮기면 전체 컴포넌트를 마운트하지 않고도 단위 테스트하기 쉬워지는 경향이 있습니다. Options API는 watcher나 라이프사이클에 의존하는 경우가 많아 컴포넌트 마운트 기반 테스트가 늘어나는 편입니다. 어떤 스타일이든 비즈니스 규칙을 UI와 분리하면 테스트가 작고 안정적입니다.

많은 컴포넌트에서 로딩과 에러 상태를 일관되게 처리하려면 어떻게 해야 하나요?

비동기 상태는 loading, error, 재시도 혹은 취소 같은 단일 형태(shape)로 표준화하세요. 각 컴포넌트가 저마다의 규약을 만들면 디버깅이 느려집니다. 이 표준은 Composition API나 Options API 모두로 구현할 수 있지만 라이브러리 전반에 동일하게 보여야 합니다.

대규모 컴포넌트 라이브러리에 새 기여자를 온보딩할 때 무엇이 가장 도움이 되나요?

Options API는 처음엔 구조가 익숙해 하루 차분히 시작하기 좋습니다만, 실제 동작이 여러 블록과 mixin에 흩어져 있어 연결고리를 찾는 데 시간이 듭니다. Composition API는 composable과 폴더 규약을 익히면 관련 동작이 모여 있어 생산성이 빨라집니다. 가장 큰 효과는 하나의 ‘골든’ 예제 컴포넌트를 제공하고 코드 리뷰에서 그 패턴을 강제하는 것입니다.

스타일을 리스크 없이 안전하게 전환하려면 어떻게 해야 하나요?

리스크가 큰 리팩터 대신 파일럿으로 접근하세요. 테이블 필터링이나 폼 에러 매핑처럼 경계가 분명하고 재사용성이 높은 하나의 기능을 골라 공유 단위를 추출하고, 그 단위에 대한 테스트를 먼저 작성한 뒤 한 컴포넌트를 끝까지 리팩터하세요. 패턴이 두 컴포넌트에서 통한다면 점진적으로 확대합니다.

우리 컴포넌트 라이브러리가 유지보수하기 어려워지고 있다는 징후는 무엇인가요?

여러 군데에 거의 동일한 코드가 쌓이거나 watcher 체인이 서로 충돌하거나 특정 props/이벤트 순서에서만 동작하는 컴포넌트가 늘어나면 경고 신호입니다. 또한 사람들이 어떤 파일은 건드리기 위험하다고 피한다면 그건 계약(contract)이 불명확하고 재사용이 명확하지 않다는 신호입니다. 변경 시에는 바로 삭제하지 말고 점진적 폐기(deprecate)를 고려하세요.

쉬운 시작
멋진만들기

무료 요금제로 AppMaster를 사용해 보세요.
준비가 되면 적절한 구독을 선택할 수 있습니다.

시작하다