돌아가기 앱 엔트리포인트
## HTML 엔트리포인트

### main - index.html (프로젝트 루트)

```html
<!doctype html>
<html lang="ko">
  <head>
    <meta charset="utf-8" />
    <meta content="IE=edge" http-equiv="X-UA-Compatible" />
    <meta content="width=device-width,initial-scale=1.0" name="viewport" />
    <meta content="no-cache, no-store, must-revalidate" http-equiv="Cache-Control" />
    <meta content="no-cache" http-equiv="Pragma" />
    <meta content="0" http-equiv="Expires" />
    <link href="/favicon.ico" rel="icon" />
    <link href="/cdn/Pretendard-1.3.9/web/static/pretendard.css" rel="stylesheet" />
    <script src="/env.js"></script>
    <script>
      // API 서버 preconnect 동적 추가
      ;(function () {
        var apiUrl = (window.__ENV__ && window.__ENV__.VITE_API_BASE_URL) || ''
        if (!apiUrl) return
        try {
          var origin = new URL(apiUrl).origin
          var link = document.createElement('link')
          link.rel = 'preconnect'
          link.href = origin
          link.crossOrigin = 'anonymous'
          document.head.appendChild(link)
        } catch (e) {}
      })()
    </script>
    <title>데이터메이커 시냅스</title>
  </head>
  <body style="background-color: #1a1f2c; margin: 0;">
    <noscript><strong>JavaScript를 활성화해주세요.</strong></noscript>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>
```

### staging - public/index.html (Webpack 템플릿)

```html
<!doctype html>
<html lang="">
  <head>
    <meta charset="utf-8" />
    <meta content="IE=edge" http-equiv="X-UA-Compatible" />
    <meta content="width=device-width,initial-scale=1.0" name="viewport" />
    <meta content="no-cache, no-store, must-revalidate" http-equiv="Cache-Control" />
    <link href="<%= BASE_URL %>favicon.ico" rel="icon" />
    <link href="<%= BASE_URL %>cdn/Pretendard-1.3.9/web/static/pretendard.css" rel="stylesheet" />
    <title><%= process.env.BRAND_TITLE || '데이터메이커 시냅스' %></title>
  </head>
  <body style="background-color: #1e2533; margin: 0;">
    <noscript><strong>JavaScript를 활성화해주세요.</strong></noscript>
    <div id="app"></div>
  </body>
</html>
```

### HTML 차이점

| 항목 | main | staging |
|------|------|---------|
| 위치 | 프로젝트 루트 `index.html` | `public/index.html` |
| 템플릿 문법 | 없음 (정적 HTML) | `<%= %>` (HtmlWebpackPlugin) |
| 스크립트 로드 | `<script type="module" src="/src/main.js">` | Webpack이 자동 주입 |
| 환경변수 | `env.js` 런타임 로드 + preconnect | `process.env` 빌드타임 주입 |
| 배경색 | `#1a1f2c` | `#1e2533` |
| 타이틀 | 하드코딩 | 환경변수 기반 (`BRAND_TITLE`) |

## 앱 엔트리포인트 (main.js)

### main (Vue 3.x)

```javascript
import { createApp } from 'vue'
import App from '@/app/app.vue'
import store from '@/app/app-store'
import router from '@/app/app-routes'
import i18n from '@/i18n'
import { initSentry } from '@/plugins/sentry'
import { registerPlugins } from '@/plugins'
import { registerComponents } from '@/plugins/components'

const app = createApp(App)

app.use(store)
app.use(router)
app.use(i18n)

initSentry(app, router)
registerPlugins(app)
registerComponents(app)

app.mount('#app')

// Service Worker 등록
if ('serviceWorker' in navigator) {
  /* ... */
}
```

### staging (Vue 2.x)

```javascript
import Vue from 'vue'
import App from '@/app/app.vue'
import store from '@/app/app-store'
import router from '@/app/app-routes'
import i18n from '@/i18n'

// Vue 2 플러그인 등록
import VueCompositionAPI from '@vue/composition-api'
import { BootstrapVue } from 'bootstrap-vue'
import VueToast from 'vue-toast-notification'
import VueWorker from 'vue-worker'
import VJsoneditor from 'v-jsoneditor'
import CKEditor from '@ckeditor/ckeditor5-vue2'
import VueFormulate from '@braid/vue-formulate'

Vue.use(VueCompositionAPI)
Vue.use(BootstrapVue)
Vue.use(VueToast)
Vue.use(VueWorker)
Vue.use(VJsoneditor)
Vue.use(CKEditor)
Vue.use(VueFormulate)

// 글로벌 컴포넌트 등록
Vue.component('v-select', vSelect)
Vue.component('page-header', PageHeader)
// ... 기타 글로벌 컴포넌트

// Sentry 초기화
Sentry.init({ Vue, dsn: '...' })

new Vue({
  router,
  store,
  i18n,
  render: (h) => h(App)
}).$mount('#app')
```

:::note[핵심 차이점]
- Vue 3: `createApp()` + `app.use()` 체인
- Vue 2: `new Vue({}).$mount()` + `Vue.use()` 전역 등록
- Vue 2에서는 `@vue/composition-api` 백포트 필요
- Vue 2에서는 BootstrapVue, CKEditor 등 추가 플러그인 사용
:::

## 라우터 설정

### main (Vue Router 4)

```javascript
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    ...dashboardRoutes,
    ...annotatorRoutes,
    ...authRoutes,
    { path: '/:pathMatch(.*)*', redirect: '/auth/login' }
  ]
})

// Navigation Guard
router.beforeEach(async (to, from, next) => {
  // 인증 및 테넌트 확인
})
```

### staging (Vue Router 3)

```javascript
import VueRouter from 'vue-router'

const router = new VueRouter({
  mode: 'history',
  routes: [
    ...dashboardRoutes,
    ...annotatorRoutes,
    { path: '*', redirect: '/auth/login' }
  ]
})

// Navigation Guard
router.beforeEach(async (to, from, next) => {
  // 인증 및 테넌트 확인
})
```

| 항목 | main | staging |
|------|------|---------|
| 생성 방식 | `createRouter()` | `new VueRouter()` |
| History | `createWebHistory()` | `mode: 'history'` |
| Catch-all | `/:pathMatch(.*)*` | `*` |

## 상태 관리 (Vuex)

### main (Vuex 4)

```javascript
import { createStore } from 'vuex'
import createPersistedState from 'vuex-persist'

const store = createStore({
  modules: {
    annotator,
    imageAnnotator,
    pcdAnnotator,
    textAnnotator,
    audioAnnotator,
    videoAnnotator,
    promptAnnotator,
    dashboard
  },
  plugins: [new VuexPersistence({ /* ... */ }).plugin]
})
```

### staging (Vuex 3)

```javascript
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'

const store = new Vuex.Store({
  modules: {
    annotator,
    imageAnnotator,
    pcdAnnotator,
    textAnnotator,
    audioAnnotator,
    videoAnnotator,
    promptAnnotator,
    dashboard
  },
  plugins: [createPersistedState({ /* ... */ })]
})
```

| 항목 | main | staging |
|------|------|---------|
| 생성 방식 | `createStore()` | `new Vuex.Store()` |
| Persistence | `vuex-persist` | `vuex-persistedstate` |

## 국제화 (i18n)

### main (vue-i18n 11)

```javascript
import { createI18n } from 'vue-i18n'

const i18n = createI18n({
  legacy: false, // Composition API 모드
  globalInjection: true, // $t 템플릿에서 사용 가능
  locale: 'ko',
  fallbackLocale: 'ko',
  messages: {
    /* import.meta.glob('./locales/*.json') */
  }
})
```

### staging (vue-i18n 8)

```javascript
import VueI18n from 'vue-i18n'

const i18n = new VueI18n({
  locale: 'ko',
  fallbackLocale: 'ko',
  messages: {
    /* require.context('./locales', ...) */
  }
})
```

| 항목 | main | staging |
|------|------|---------|
| 생성 방식 | `createI18n()` | `new VueI18n()` |
| API 모드 | Composition (`legacy: false`) | Options (legacy) |
| 파일 로드 | `import.meta.glob` | `require.context` |