어드민 프론트엔드 · 구조 레퍼런스

ADM FE 구조 가이드 폴더·화면 구조 / 컴포넌트 재사용 / API 연동 / 수정 시 주의점 — 한눈에 파악용 참고 자료

orderhero-admin-front

스택
Vue 3 · Vuetify 3
템플릿
Vuexy 9.0.0
빌드
Vite · TypeScript
서버상태
TanStack Query
화면(.vue)
~468
기준 커밋
73be2af

한 줄 요약 — 상용 어드민 템플릿 Vuexy 기반. 파일 기반 라우팅(src/pages/** = 라우트), 화면 약 75개 도메인. 대부분 목록 화면의 검색은 다이나믹 서치 모듈 하나가 담당(~80화면 공유). 이 모듈 · axios 인터셉터 · 권한 스토어가 수정 시 영향 범위가 가장 넓은 3대 공통 모듈이다.

01

폴더 · 화면 구조

1-1. src 최상위 — 템플릿 vs 커스텀 구분이 핵심

수정 범위를 가를 때 가장 먼저 보는 축. 대문자 @core/@layouts = Vuexy 템플릿(직접 수정 지양), 나머지는 오더히어로 팀 커스텀.

디렉토리역할출처
@core/Vuexy 코어 — 전역 컴포넌트·컴포저블·scss·config 스토어템플릿
@layouts/Vuexy 레이아웃 엔진 — 세로/가로 네비 렌더, casl.ts템플릿
layouts/실제 앱 레이아웃(default/blank) + 커스텀 확장커스텀
api/HTTP 레이어 전체 (→ 3장)커스텀
components/공용 컴포넌트 44개 하위폴더(테이블·모달·다이나믹서치·엑셀·폼)커스텀
composables/useConfirmModal·usePagination·useGroupSelection (auto-import)커스텀
core/소규모 커스텀 코어 — @core와 완전 별개, 혼동 주의커스텀
navigation/좌측 메뉴 정의(vertical/index.ts)커스텀
pages/파일 기반 라우팅 페이지 (~468 .vue)커스텀
plugins/부팅 플러그인(router·pinia·vueQuery·vuetify·iconify·msw)골격 +커스텀
store/Pinia 스토어 40여 도메인(order·product·permission·dynamicSearch)커스텀
utils/ · types/유틸·상수(constants_PermissionCode.ts) / 전역 타입커스텀
mocks/MSW 핸들러 — 현재 비활성 (→ 3-6)커스텀
@core(대문자·템플릿)core(소문자·커스텀) 는 다른 폴더다. 템플릿을 앱 코드로 오인해 수정하면 업그레이드 충돌이 생길 수 있어 구분이 중요하다.

1-2. 라우팅 = 파일 기반 (unplugin-vue-router)

명시적 라우트 파일이 없다. src/pages/** 파일이 곧 라우트가 되고, 라우트명은 PascalCase→kebab-case로 변환된다(타입은 typed-router.d.ts에 자동 생성). 인증 가드는 plugins/1.router/index.tsbeforeEach(토큰 없거나 JWT 만료 → /login).

URL파일route name
/ordersrc/pages/order/index.vueorder
/mfcCenterManagesrc/pages/mfcCenterManage/index.vuemfc-center-manage
/manage/accountsrc/pages/manage/account/index.vuemanage-account
/loginsrc/pages/login.vuelogin
참고: pages/<domain>/components/*.vue 도 라우트로 자동 등록된다(예: /order/components/OrderTable). 컴포넌트를 pages 하위에 두는 관례에 따른 것 — 동작 이슈는 아니고 인지만 해두면 된다.

1-3. 주요 화면 도메인 (약 75개 폴더 · 좌측 메뉴 28그룹)

시스템 관리

manage/account · permission · menu · key-value · contents · message

주문 핵심

order(index/detail/csDetail) · orderStatus · placementOrder(발주) · recall

상품

product · productCategoryManage · vipPricingManage · segmentsManage

가맹점 / 파트너

restaurantInfo · partnerInfo · partnerList · gradeManage · insuranceContract

정산

settlement* ×9 · depositHistory · cashFlowRecords · paymentManage ×5

회원 / CS

cs · challenge · notification · support

쿠폰 / 마케팅

couponList · campaignManage · popup·fcPopup · deepLinkManage · quickMenu*

입고

inbound · inboundIssue · hubTcInspection

MFC / 물류 어드민 내

mfcCenter · mfcCrew · mfcBox · mfcStock · mfcDeliveryOrder · mfcCalculate(Monthly)

MFC는 별도 프론트가 아니다. 위 7개 mfc* 페이지 + 물류비 정책(partnerInfo/.../LogisticsFeePolicy) 형태로 이 어드민 안에 들어있는 화면군이다.
02

UI 컴포넌트 구성 · 재사용 패턴

컴포넌트는 @core/components(템플릿 34개)와 src/components(커스텀 44 폴더)로 나뉜다. 둘 다 전역 auto-import.vue에서 태그만 쓰면 되고 수동 import는 필요 없다.

핵심 재사용 컴포넌트 4종

A · 다이나믹 서치 모듈 이 앱의 심장

components/dynamicsearch/ — 화면마다 menuCode prop만 넘기면 백엔드가 그 메뉴의 검색 컬럼·저장 프리셋을 내려주고 필터 UI를 동적 생성, @search emit으로 상위 테이블에 파라미터 전달. 약 80개 파일이 공유(order·settlement·coupon·popup·mfc·vipPricing …). 쿼리 querys/dynamicSearch/ · 상태 store/dynamicSearch/ · 타입 types/dynamicSearch.ts.

B · 데이터 테이블 두 방식 혼용

components/dataTable/(DataTable·DataTableSettingModal 컬럼설정 등). 대형 화면(OrderTable)은 Vuetify 원본 VDataTable을 직접 쓰기도 한다 — 공용 컴포넌트와 원본을 함께 사용. 수정 전 해당 화면이 어느 방식인지 확인.

C · 모달 / 다이얼로그 전역 상시 마운트

components/modals/ 20+. 전역 확인창useGlobalConfirmModal() + App.vue에 상시 마운트되어 어디서든 호출. 검색 선택 모달군 searchMember/Partner/Center/FC/Order/Admin. 도메인 모달은 각 페이지 components/에.

D · 엑셀 모듈 광범위 재사용

components/excel/ExcelModule + ExcelModuleButton + Container/EditModal/KeySelector. 다운로드·업로드가 많은 어드민 특성상 넓게 재사용(가이드 pages/documentation/excel/).

전역 마운트(App.vue): BasicSnackbar · Loading · GlobalConfirmModal · ScrollToTop이 항상 떠 있음 → 스낵바·로딩·확인창은 스토어(store/global, store/loading)로 트리거한다.
03

API 호출 · 연동 구조

계층 규칙: request(호출 함수) → query(Vue Query 래퍼) → page/component. 모든 호출은 중앙 axios 인스턴스 하나를 지난다.

page / componentOrderTable.vue
query 래퍼useMutationWithHandler
request 함수orderApi.ts · ORDER_URL
axios instanceapi/utils/instance.ts
백엔드{baseURL}/order/get-all

중앙 axios 인스턴스 — api/utils/instance.ts (반드시 이해)

  • baseURL = VITE_API_BASE_URL + VITE_CONTEXT_PATH, timeout 180초.
  • 요청 인터셉터localStorage.ACCESS_TOKEN을 JWT 파싱해 만료 아니면 Authorization: Bearer 주입.
  • 응답 인터셉터 — 헤더 Authorization 있으면 토큰 자동 갱신(슬라이딩). HTTP 2xx여도 body가 에러 포맷이면 reject(isApiErrorResponse) — 이 백엔드는 200에 에러 envelope를 담기도 해서 필수 로직. 401/특정 code → alertlocalStorage.clear()/login.

인증 · 인가 · 서버상태

주제방식
토큰 저장localStorage ACCESS_TOKEN (+adminId) — 쿠키 아님. parseJwt로 만료 판정, 응답 헤더로 슬라이딩 갱신
인가(권한)CASL 아님. usePermissionCodeStore().getPermission(PERMISSION_CODE.X)~75화면에서 v-if 게이팅. CASL은 네비 표시 판정에만 사용
서버상태TanStack Query. 뮤테이션 표준 래퍼 @core/composable/useMutationWithHandler(공통 에러+성공 메시지). 쿼리키 상수 querys/keys.ts
MSW mocks현재 비활성plugins/msw.tsapp.use(msw)가 주석 처리. 목 데이터가 적용되지 않는 게 정상
.env는 커밋되지 않는다. 클론 직후 VITE_API_BASE_URL·VITE_CONTEXT_PATH 등을 직접 세팅하지 않으면 getBaseUrl()이 경고 문자열을 반환해 모든 API가 404가 된다.
04

수정 시 주의점 핵심

영향 범위가 넓은 공통 영역 · 수정 주의 구역

다이나믹 서치 모듈 ~80화면

components/dynamicsearch/*에서 회귀가 나면 어드민 상당수 화면의 검색에 영향이 간다. menuCode 계약 + @search emit 시그니처를 유지하는 게 중요.

axios 인터셉터 전 호출

api/utils/instance.ts — 모든 호출·인증·에러·토큰갱신·/login 리다이렉트가 여기 하나. "200+에러 body" 판정(isApiErrorResponse)을 깨면 정상↔에러가 뒤바뀐다.

권한 스토어 ~75화면

store/permission/permissionCode.ts + utils/constants_PermissionCode.ts. getPermission 시그니처나 코드 상수 rename 시 버튼 노출이 대량으로 흔들린다.

Vuexy 템플릿 원본 수정 지양

src/@core/**·src/@layouts/**·plugins/vuetify·plugins/iconify·themeConfig.ts. 직접 수정 시 템플릿 업그레이드 충돌 + 전 화면 테마/네비 영향. 커스터마이즈는 src/layouts·src/components에서 감싸는 방식을 권장.

네비게이션 · 쿼리키 · 전역 UI 주의

navigation/vertical/index.ts(라우트명 어긋나면 죽은 링크) · querys/keys.ts(중복/오타 → 캐시 무효화 버그) · App.vue 전역 마운트 컴포넌트.

따라야 할 컨벤션

  • Auto-import 영역은 수동 import 금지 — vue API·router·@vueuse·pinia·lodash(_BigNumber + composables·utils·store가 자동 주입. 수동으로 넣으면 중복 경고. auto-imports.d.ts·components.d.ts·typed-router.d.ts자동 생성물 → 수동 편집 금지. (예외: useCookies·useStorage는 의도적 제외 → 명시 import)
  • 뮤테이션은 useMutationWithHandler로. request/query/schema 3계층 관례 유지.
  • 권한은 getPermission(PERMISSION_CODE.X) — 체계 이원화 방지를 위해 CASL $can은 신규 도입하지 않음.
  • PR 전 yarn typecheck(vue-tsc) · yarn lint · yarn fix(Prettier).

빌드 / 배포 gotcha

  • postinstallbuild:icons+msw:init 실행 — 아이콘 세트 변경 시 yarn build:icons 재실행. 실패하면 아이콘이 안 뜬다.
  • prod 빌드는 terserdrop_console프로덕션에서 console 디버깅 불가. vite-plugin-vue-devtools는 크래시 이슈로 의도적 off 상태(재활성 지양).
  • 서브패스 배포 시 asset base만 바뀌고 라우터 base는 / 고정. Node는 .nvmrc 버전.

처음 작업 시 자주 놓치는 것

  1. auto-import 대상을 수동 import → 중복/충돌
  2. @core/@layouts(대문자 @) 템플릿을 앱 코드로 오인해 수정
  3. DynamicSearchModule·instance.ts를 로컬 화면 목적으로 수정하다 80화면·전역에 회귀
  4. .env 미설정 상태로 실행 후 API 미동작으로 오인
  5. typed-router.d.ts·auto-imports.d.ts 자동생성물을 수동 편집
  6. 권한을 CASL로 신규 구현하다 실제 getPermission 체계와 이원화
05

빠른 진입 체크리스트

신규 투입 시 이 순서대로.

  1. .nvmrc 버전으로 Node 맞추고 yarn install (→ postinstall이 아이콘·msw init 수행)
  2. .env 생성 — 최소 VITE_API_BASE_URL + VITE_CONTEXT_PATH (팀에서 값 수령)
  3. yarn dev/login 진입, 로그인하면 ACCESS_TOKEN이 localStorage에 저장
  4. 만들 화면을 src/pages/<domain>/index.vue에서 찾기 (URL이 곧 경로)
  5. 목록/검색이면 DynamicSearchModule + DataTable 패턴 답습
  6. 새 API는 request/<domain>/ 함수 → querys/ 래퍼(useMutationWithHandler) → 화면 순
  7. 버튼/기능 노출은 getPermission(PERMISSION_CODE.X)
  8. PR 전 yarn typecheck && yarn lint