🧩

관리자 사이트 Starter 프로젝트 구축

상태
완료
프로젝트 유형
팀 프로젝트
프로젝트 기간
Jan 1, 2024 → Feb 28, 2025
사용 기술
Vue.js 3
Pinia
Java
Spring Boot
Figma
JPA
QueryDSL
Storybook
Vite
프로젝트 소개
Vue.js 3, Spring Boot 기반 사내 다수 프로젝트 적용을 위한 관리자 사이트(Admin) Starter 템플릿 구축 프로젝트입니다.
링크

관리자 사이트 Starter 프로젝트 구축

🔎 프로젝트 개요


📅 프로젝트 기간: 2024.01 ~ 2025.07

💡프로젝트 소개

Vue.js 3, Spring Boot 기반 사내 다수 프로젝트에 적용 가능한 관리자 사이트 Starter 템플릿 구축 프로젝트입니다.
운영 중인 프로젝트와 신규 개발 프로젝트의 관리자 기능(관리자 메뉴, 관리자 관리, 메뉴 관리, 게시판 관리 등)의 공통 요구사항을 분석하여 표준화된 템플릿을 구축했으며, 개발 생산성 향상과 개발 소요 시간 단축을 목표로 3개의 프로젝트에 실제 적용했습니다.

⚙️ Skills

  • Frontend: Vue.js 3, Pinia, Storybook
  • Backend: Java, Spring Boot, Spring Data JPA, MyBatis, MySQL
  • Build: Vite

🤔 왜 Vue.js를 선택했는가?

프론트엔드 프레임워크/라이브러리로는 React, Angular, Svelte 등 다양한 선택지가 있었지만, Vue.js를 선택한 이유는 다음과 같습니다.
  • 기존 기술 스택과의 유사성
    • 템플릿 문법이 JSP, Thymeleaf 등 기존 사용하던 서블릿 페이지 기술과 유사
    • React의 JSX 문법에 비해 팀원들의 러능커브가 낮고, HTML 기반 템플릿 구조로 인한 직관적인 학습 곡선 보장
    • 팀원 간 기술 스택 습득 편차 및 시간적 소요 최소화
  • 프레임워크로서의 구조화된 체계
    • React는 라이브러리이지만 Vue는 프레임워크로서 더 명확한 구조 제공
    • Vue Router, Pinia 등 공식 생태계 라이브러리가 체계적으로 정립됨
    • 프로젝트 구조, 상태 관리, 라우팅에 대한 Best Practice가 명확
  • 반응형 기반 상태 관리
    • Reactivity System이 내장되어 있어 복잡한 상태 관리 로직 단순화
    • Composition API를 통한 로직 재사용성 극대화
→ 팀 차원의 실용적 선택
  • 기존 JSP 개발 경험이 있는 팀원들의 빠른 적응 가능
  • Angular의 과도한 복잡성과 React의 자유도 사이에서 균형점 제공
  • 프레임워크 차원의 가이드라인으로 코드 품질 일관성 확보

🧩 담당 역할


  • Vue.js 3 기반 프론트엔드 템플릿 프로젝트 설계 및 구축 주도
  • Atomic Design 패턴 기반 컴포넌트 구조 설계 및 개발
  • Storybook 도입 및 컴포넌트 문서화
  • 전역 상태 관리(Pinia) 구조 설계 및 구현
  • 서버 상태 관리를 위한 커스텀 composable 개발
  • 개발 가이드 문서 작성 및 팀 내 지식 공유

🧨 문제 인식


❓Starter 프로젝트가 필요했던 이유?

  • 표준화된 템플릿 부재: 프로젝트마다 관리자 기능을 처음부터 구현, 개발 생산성 저하 및 소요 시간 증가
  • JSP 기반 구조의 한계: 기존 프로젝트의 JSP 기반 페이지 중심 구조로 인한 컴포넌트 재사용성 부족, 기능 추가 시 반복적인 구현 작업 발생
  • 상태 관리 체계 부재: 페이지네이션, 로딩 처리, 메시지 처리 등이 화면마다 분산되어 중복 구현 반복, 일관성 없는 구현으로 인한 유지보수 어려움

🛠️ 해결 방안


🧩 Atomic Design 기반 컴포넌트 구조 설계

JSP 기반 페이지 중심 구조를 컴포넌트 기반으로 전환하여 재사용성을 극대화했습니다.
구현 내용
  • Atoms → Molecules → Organisms → Templates → Pages 계층 구조 설계
  • Atoms 30+개, Molecules 10+개, Organisms 10+개 컴포넌트 개발
  • 버튼, 입력 필드 등 기본 UI 요소부터 복합 폼, 테이블까지 체계적으로 분리
효과
  • 중복 코드 최소화 및 유지보수 효율성 향상
  • 코드 품질 일관성 확보

🧩 Scoped Slot을 활용한 유연한 컴포넌트 설계

각 프로젝트의 디자인 요구사항(theme color, 마크업 구조 등)이 다르기 때문에, Scoped Slot을 활용하여 사용자가 커스텀하게 스타일과 마크업을 변경할 수 있도록 컴포넌트를 설계했습니다.
설계 원칙
  • 컴포넌트 로직과 UI를 분리하여 재사용성 극대화
  • Scoped Slot을 통해 개발자가 필요에 따라 class, style, 마크업 구조 커스터마이징 가능
  • 기본 스타일 제공 + 오버라이드 가능한 구조
커스터마이징 가능한 Button 컴포넌트 예시
<!-- BaseButton.vue --> <template> <button :type="type" :disabled="disabled" @click="handleClick" > <slot name="default" :loading="loading" :disabled="disabled"> {{ label }} </slot> </button> </template> <script setup> defineProps({ type: { type: String, default: 'button' }, label: { type: String, default: '' }, disabled: { type: Boolean, default: false }, loading: { type: Boolean, default: false } }); const emit = defineEmits(['click']); const handleClick = (e) => { emit('click', e); }; </script>
프로젝트별 커스터마이징 사용 예시
<!-- 프로젝트 A - 파란색 테마 --> <BaseButton @click="handleSubmit"> <template #default="{ loading, disabled }"> <span class="custom-blue-button" :class="{ 'is-loading': loading, 'is-disabled': disabled }"> 제출하기 </span> </template> </BaseButton> <!-- 프로젝트 B - 빨간색 테마 + 아이콘 --> <BaseButton @click="handleDelete"> <template #default="{ loading }"> <span class="custom-red-button"> <i class="icon-trash" v-if="!loading"></i> <i class="icon-spinner" v-else></i> 삭제하기 </span> </template> </BaseButton>
커스터마이징 가능한 Table 컴포넌트 예시
<!-- DataTable.vue --> <template> <table> <thead> <slot name="header" :columns="columns"> <tr> <th v-for="col in columns" :key="col.key"> {{ col.label }} </th> </tr> </slot> </thead> <tbody> <slot name="body" :data="data" :columns="columns"> <tr v-for="(row, idx) in data" :key="idx"> <td v-for="col in columns" :key="col.key"> {{ row[col.key] }} </td> </tr> </slot> </tbody> </table> </template> <script setup> defineProps({ columns: { type: Array, required: true }, data: { type: Array, required: true } }); </script>
Table 컴포넌트 사용 예시
<!-- 커스텀 스타일 + 마크업 --> <DataTable :columns="columns" :data="tableData"> <template #header="{ columns }"> <tr class="custom-header-style"> <th v-for="col in columns" :key="col.key" :class="`header-${col.key}`"> <div class="header-content"> <span>{{ col.label }}</span> <i class="sort-icon"></i> </div> </th> </tr> </template> <template #body="{ data, columns }"> <tr v-for="row in data" :key="row.id" class="custom-row-style"> <td v-for="col in columns" :key="col.key" :class="`cell-${col.key}`"> <span class="custom-cell-content">{{ row[col.key] }}</span> </td> </tr> </template> </DataTable>
효과
  • 프로젝트별 디자인 요구사항에 유연하게 대응
  • 컴포넌트 로직 재사용 + UI 커스터마이징 독립성 확보

🧩 전역 상태 관리 및 서버 상태 관리 체계 구축

Pinia를 활용한 전역 상태 관리와 커스텀 composable을 통한 서버 상태 관리 체계를 구축했습니다.
Pinia 기반 전역 상태 관리
각 프로젝트마다 인증 체계가 다르기 때문에 인증은 제외하고, 공통적으로 필요한 상태만 전역으로 관리했습니다.
  • Pagination Store: 페이지네이션 상태 중앙 관리
  • Loading Store: 전역 로딩 상태 관리
  • Client Store: Provider → Context 형태로 구성하여 HTTP 클라이언트 설정 관리
커스텀 Composable을 통한 서버 상태 관리
서버 요청 시 자동으로 로딩 상태와 연결되는 커스텀 composable을 개발했습니다.
// useApiRequest.js import { ref } from 'vue'; import { useLoadingStore } from '@/stores/loading'; export function useApiRequest() { const loadingStore = useLoadingStore(); const data = ref(null); const error = ref(null); const execute = async (apiCall) => { loadingStore.startLoading(); error.value = null; try { data.value = await apiCall(); return data.value; } catch (e) { error.value = e; throw e; } finally { loadingStore.stopLoading(); } }; return { data, error, execute }; }
컴포넌트에서 사용 예시
const { data, error, execute } = useApiRequest(); const fetchUsers = async () => { await execute(() => api.getUsers()); };
Alert/Confirm 공통 피드백 컴포넌트
사용자 피드백을 일관되게 처리하기 위한 공통 컴포넌트를 구현했습니다.
// useAlert.js import { useAlertStore } from '@/stores/alert'; export function useAlert() { const alertStore = useAlertStore(); const showAlert = (message, type = 'info') => { alertStore.show({ message, type }); }; const showConfirm = (message, onConfirm) => { alertStore.showConfirm({ message, onConfirm }); }; return { showAlert, showConfirm }; }
메시지 체계 표준화
message 객체를 통한 메시지 관리 체계를 도입하고, base message 파일을 제공했습니다.
// messages/base.js export const baseMessages = { common: { save: '저장되었습니다.', delete: '삭제되었습니다.', error: '오류가 발생했습니다.', confirm: '계속 진행하시겠습니까?' }, validation: { required: '필수 입력 항목입니다.', email: '올바른 이메일 형식이 아닙니다.', minLength: '최소 {min}자 이상 입력해주세요.' } }; // 프로젝트별 확장 // messages/project.js import { baseMessages } from './base'; export const messages = { ...baseMessages, user: { saveSuccess: '사용자가 저장되었습니다.', deleteConfirm: '사용자를 삭제하시겠습니까?' } };
효과
  • 컴포넌트 복잡도 감소 (로딩, 에러 처리 로직 분리)
  • 상태 관리의 일관성 확보
  • 사용자 피드백 처리 표준화
  • 메시지 관리의 체계화로 다국어 지원 기반 마련

🧩 개발 가이드 문서화 및 전파

Storybook을 활용한 컴포넌트 가이드 제공
개발한 컴포넌트를 팀 전체가 쉽게 활용할 수 있도록 Storybook을 도입했습니다.
구현 내용
  • 모든 공통 컴포넌트에 대한 Storybook 문서 작성
  • 컴포넌트별 Props, Scoped Slot 사용 예시, 커스터마이징 방법 제공
  • 프로젝트별 스타일 적용 예시를 시각적으로 확인 가능
효과
  • 팀원들이 컴포넌트 사용법과 커스터마이징 방법을 빠르게 파악
  • 디자이너, 기획자도 확인 가능한 시각적 문서화
  • 커뮤니케이션 비용 감소 및 협업 효율성 증대
사내 개발 가이드 제공을 통한 개발 문서 공유 문화 주도
Microsoft Loop를 활용하여 내부 개발 가이드 문서를 체계적으로 작성했습니다.
문서 내용
  • Atomic Design 기반 컴포넌트 계층 구조 및 사용 가이드
  • Scoped Slot을 활용한 컴포넌트 커스터마이징 방법
  • 상태 관리 흐름 및 Store 활용법
  • 커스텀 composable 사용 가이드
  • 메시지 체계 및 확장 방법
  • 개발 체크리스트 및 코드 컨벤션
효과
  • 신규 팀원 온보딩 시간 단축
  • 코드 일관성 유지
  • 지식의 팀 내 내재화

🚀 주요 성과


표준화된 템플릿 구축

UI 요소 → 템플릿 → 페이지로 이어지는 계층적 구조 확립, 관리자 기능의 표준 아키텍처 정립, Git Repository에서 Pull 받아 바로 적용 가능한 형태로 제공

실제 프로젝트 적용

3개의 프로젝트에 Starter 템플릿 적용, 신규 개발 및 고도화 프로젝트의 관리자 시스템 구축 시간 단축

개발 생산성 향상

컴포넌트 재사용성 증가로 중복 개발 최소화, 가이드 문서화로 개발 진입 장벽 낮춤, 일관된 구조로 유지보수 효율성 증대

프로젝트별 유연한 커스터마이징 지원

Scoped Slot을 통한 디자인 유연성 확보, CSS 라이브러리 없이도 프로젝트별 요구사항 대응 가능

🔧 프로젝트 진행 시 어려웠던 점과 해결 과정


💡Atomic Design 적용의 어려움

컴포넌트 분류 기준의 모호함
Atom, Molecule, Organism을 구분하는 명확한 기준이 없어 초기 설계 시 많은 고민이 있었습니다. 예를 들어, 검색 입력 필드가 버튼과 결합되었을 때 이것이 Molecule인지 Organism인지, Label이 포함된 Input이 Atom인지 Molecule인지 등의 경계가 모호했습니다.
해결 과정
팀 내에서 다음과 같은 분류 기준을 정립했습니다:
  • Atoms: 더 이상 분해할 수 없는 기본 UI 요소 (Button, Input, Label, Icon 등)
  • Molecules: 2-3개의 Atoms가 결합되어 하나의 기능을 수행 (LabeledInput, SearchBox 등)
  • Organisms: 여러 Molecules/Atoms가 결합된 독립적인 인터페이스 영역 (Header, DataTable, FilterPanel 등)
실무 적용 과정에서 "재사용 가능성"과 "기능적 독립성"을 핵심 판단 기준으로 삼았고, 애매한 경우 팀 논의를 통해 결정했습니다. 이러한 과정을 문서화하여 향후 판단 기준으로 활용했습니다.
Atomic Design의 장단점 분석
프로젝트 진행 중 Atomic Design 패턴의 실제 장단점을 체감할 수 있었습니다.
장점
  • 컴포넌트의 명확한 계층 구조로 인한 재사용성 극대화
  • 작은 단위부터 테스트 가능하여 안정성 확보
  • 새로운 팀원도 구조를 빠르게 이해 가능
단점
  • 과도한 추상화로 인한 Props Drilling 발생 가능성
  • 단순한 UI도 여러 계층을 거쳐야 해서 개발 속도 저하 우려
  • 폴더 구조가 복잡해져 파일 탐색 시간 증가
대응 방안
장점은 최대화하고 단점은 최소화하기 위해:
  • Props Drilling은 Pinia를 통한 전역 상태 관리로 해결
  • 자주 사용되는 패턴은 Template 레벨에서 조합하여 제공
  • Storybook을 통해 컴포넌트 검색 및 활용도 향상

💡 프로젝트별 디자인 커스터마이징 문제

문제 상황
각 프로젝트마다 theme color, 폰트, spacing 등 디자인 요구사항이 달라서, 하나의 컴포넌트로 모든 프로젝트를 커버하기 어려웠습니다. 고정된 디자인 및 레이아웃이 있는 방식이 아니라 디자인 팀에서 프로젝트 고객 성향에 맞게 디자인을 변경하여 제공하기 때문에 유연한 구성에 대한 고민이 굉장히 많았습니다.
해결 과정
Scoped Slot을 도입하여 컴포넌트의 로직과 UI를 분리했습니다. 이를 통해:
  • 컴포넌트는 비즈니스 로직과 상태 관리만 담당
  • 각 프로젝트에서 UI 구조와 스타일을 자유롭게 커스터마이징
  • 로직 재사용 + 디자인 유연성을 동시에 확보
이는 일종의 Headless Component 패턴과 유사한 접근이었으며, 초기 개발 시간은 다소 증가했지만 장기적으로 유지보수성이 크게 향상되었습니다.

💡 상태 관리 체계 구축의 고민

전역 상태 vs 지역 상태의 경계
어떤 상태를 전역으로 관리하고 어떤 상태를 컴포넌트 레벨에서 관리할지 결정하는 것이 어려웠습니다. 특히 인증 상태의 경우 프로젝트마다 구현 방식이 달라 더욱 복잡했습니다.
해결 과정
다음과 같은 기준을 수립했습니다:
전역 상태 관리 (Pinia)
  • 여러 컴포넌트에서 공유되는 상태 (Pagination, Loading)
  • 애플리케이션 전역 설정 (Client Configuration)
  • Alert/Confirm 등 전역 UI 상태
지역 상태 관리 (Composition API)
  • 특정 컴포넌트에서만 사용되는 폼 상태
  • 일회성 UI 상태 (모달 열림/닫힘 등)
프로젝트별 구현 (커스터마이징)
  • 인증 상태 및 로직 (각 프로젝트의 인증 체계가 다름)
  • 비즈니스 로직 특화 상태

🤔 아쉬운 점 및 개선 방향


프로젝트를 진행하며 좋은 성과를 냈지만, 몇 가지 아쉬운 점과 향후 개선 방향을 고민했습니다.

라이브러리화 및 버전 관리 체계 부재

현재 상황
Git Repository에서 풀받아 각 프로젝트에 복사하는 형태, 템플릿 업데이트 시 반영이 어렵고 프로젝트 간 버전 불일치 발생 가능
개선 방향
GitLab Private Repository를 활용한 npm 패키지 배포
  • 컴포넌트 라이브러리를 npm 패키지로 배포하여 버전 관리 체계 구축
  • @company/admin-ui 형태로 private registry에 배포
  • 프로젝트별로 원하는 버전 선택 및 설치 가능
Changelog 기반 버전 관리
  • Semantic Versioning (Major.Minor.Patch) 도입
  • CHANGELOG.md를 통한 변경사항 추적
  • Breaking changes, 새로운 기능, 버그 수정 명확히 구분
기대 효과
  • 템플릿 업데이트 시 각 프로젝트에서 선택적으로 업그레이드 가능
  • 안정적인 버전 관리로 예기치 않은 Breaking changes 방지
  • CI/CD 파이프라인 연동으로 자동 배포 가능

디자인 시스템 도입을 통한 체계적인 디자인 관리

현재 상황
Scoped Slot으로 커스터마이징은 가능하지만, 프로젝트별로 theme color 등을 매번 수동 설정, 회사 차원의 통일된 디자인 가이드라인 부재
개선 방향
Design Token 기반 디자인 시스템 도입
  • Color Palette, Typography, Spacing 등을 Design Token으로 정의
  • CSS Variables 또는 Tailwind Config로 theme 설정 중앙화
  • 프로젝트별로 theme 파일만 교체하여 전체 색상/스타일 변경 가능
// design-tokens/default-theme.js export const defaultTheme = { colors: { primary: '#3B82F6', secondary: '#10B981', danger: '#EF4444', // ... }, spacing: { xs: '4px', sm: '8px', md: '16px', // ... } }; // design-tokens/client-a-theme.js export const clientATheme = { colors: { primary: '#8B5CF6', // 고객 A의 primary color secondary: '#EC4899', danger: '#DC2626', }, spacing: defaultTheme.spacing // spacing은 기본값 상속 };
Figma과 연동된 디자인 시스템
  • 디자이너가 Figma에서 정의한 Design Token을 코드로 자동 변환
  • Storybook과 Figma 연동으로 디자인-개발 간 일관성 확보
기대 효과
  • 회사 자체 디자인 체계 확립 및 브랜드 아이덴티티 유지
  • 고객별 요구사항에 맞춰 theme만 교체하는 유연한 커스터마이징
  • 디자이너-개발자 간 협업 효율성 증대
  • 디자인 일관성 유지 및 유지보수 비용 절감
이러한 개선 방향을 통해 단순한 템플릿을 넘어 회사 차원의 체계적인 프론트엔드 인프라로 발전시킬 수 있을 것으로 기대합니다.

📚 기술적 성장


설계 역량

대규모 프로젝트에 적용 가능한 확장 가능한 아키텍처 설계 경험, Atomic Design 패턴의 실무 적용 노하우 습득, Scoped Slot을 활용한 유연한 컴포넌트 설계 경험

상태 관리 및 추상화

Pinia를 활용한 효율적인 전역 상태 관리 구조 설계, 서버 상태 관리를 위한 커스텀 composable 패턴 개발, 로직과 UI 분리를 통한 재사용성 극대화

협업 및 문서화

Storybook을 활용한 효과적인 컴포넌트 공유 경험, 기술 문서화를 통한 팀 지식 전파 경험

프로젝트 주도

템플릿 프로젝트 전체 설계 및 구축 주도, 여러 프로젝트에 적용 가능한 범용 구조 개발 경험