memi-board 0.52.0 → 0.52.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,35 +1,35 @@
1
- # 다른 Nuxt 사이트에 memi-board 붙이기
1
+ # Adding memi-board to another Nuxt site
2
2
 
3
- [README](../README.md) · [국제화·번역 설정](internationalization.md) · [Firebase 준비](firebase-setup.md) · [역할 마이그레이션](roles-migration.md)
3
+ [README](../README.md) · [Internationalization and translation](internationalization.md) · [Firebase setup](firebase-setup.md) · [Role migration](roles-migration.md)
4
4
 
5
- 기존 Nuxt 4 사이트에 게시판을 추가하는 절차다. 호스트는 라우트·Firebase 연결·규칙 배포를 맡고, memi-board는 게시판 컴포넌트와 composable을 제공한다.
5
+ This is the procedure for adding a board to an existing Nuxt 4 site. The host app handles routes, the Firebase connection, and rule deployment; memi-board provides the board components and composables.
6
6
 
7
- 이 가이드의 국제화·번역 옵션을 포함한 예제는 **memi-board 0.47.0 이상**을 기준으로 한다. 필수 버전은 Nuxt 4+, Nuxt UI 4+, Firebase 12+, Vue 3.5+, VueFire 3.2+다.
7
+ The examples in this guide, including the internationalization and translation options, assume **memi-board 0.47.0 or later**. Required versions: Nuxt 4+, Nuxt UI 4+, Firebase 12+, Vue 3.5+, VueFire 3.2+.
8
8
 
9
- ## 1. 필요한 패키지 설치
9
+ ## 1. Install the required packages
10
10
 
11
- 기존 Nuxt 프로젝트 루트에서 실행한다.
11
+ Run this from the root of the existing Nuxt project.
12
12
 
13
13
  ```bash
14
14
  pnpm add memi-board@^0.47.0 @nuxt/ui nuxt-vuefire vuefire firebase
15
15
  ```
16
16
 
17
- `dayjs`는 필요 없다. `@nuxtjs/i18n`은 사이트 언어 전환이 필요할 때만 추가한다. 운영 환경에서는 검증한 memi-board 버전과 lockfile을 함께 관리한다.
17
+ `dayjs` is not needed. Add `@nuxtjs/i18n` only if you need site language switching. In production, manage the memi-board version you have verified together with the lockfile.
18
18
 
19
- ## 2. 먼저 언어 모드 선택
19
+ ## 2. Choose a language mode first
20
20
 
21
- | 서비스 | 권장 설정 |
21
+ | Service | Recommended setting |
22
22
  |---|---|
23
- | 한국어 공지·문의 게시판 | `locale: 'ko', i18n: { enabled: false }` |
24
- | 영어 전용 서비스 | `locale: 'en', i18n: { enabled: false }` |
25
- | 여러 언어 UI, 글은 작성한 언어 그대로 | `i18n: { enabled: true }, translation: { enabled: false }` |
26
- | 게시판·글을 여러 언어로 번역 | `i18n: { enabled: true }, translation: { enabled: true }` |
23
+ | Korean notice/inquiry board | `locale: 'ko', i18n: { enabled: false }` |
24
+ | English-only service | `locale: 'en', i18n: { enabled: false }` |
25
+ | Multilingual UI, posts shown in the language they were written in | `i18n: { enabled: true }, translation: { enabled: false }` |
26
+ | Translate boards and posts into multiple languages | `i18n: { enabled: true }, translation: { enabled: true }` |
27
27
 
28
- 설정 생략 시 두 기능은 모두 활성 상태다. 콘텐츠 번역에는 별도 Firebase AI Logic 준비가 필요하다. 자동 번역 토글의 의미와 실제 실행 시점은 [국제화·번역 설정](internationalization.md#4-번역이-실행되고-저장되는-시점)을 참고한다.
28
+ If omitted, both features are enabled. Content translation requires a separate Firebase AI Logic setup. For what the auto-translate toggle means and when translation actually runs, see [Internationalization and translation](internationalization.md#4-when-translations-run-and-are-saved).
29
29
 
30
- ## 3. Nuxt 설정에 병합
30
+ ## 3. Merge into the Nuxt config
31
31
 
32
- 다음은 **한국어 전용, AI 검열·번역을 사용하지 않는** 시작 예제다. 기존 `modules`, CSS, Firebase 설정이 있다면 중복 등록하지 않고 병합한다.
32
+ The following is a starter example that is **Korean-only and does not use AI moderation or translation**. If you already have `modules`, CSS, or Firebase settings, merge rather than registering them twice.
33
33
 
34
34
  ```ts
35
35
  // nuxt.config.ts
@@ -53,7 +53,7 @@ export default defineNuxtConfig({
53
53
  auth: { providers: ['google'] },
54
54
  moderation: { enabled: false },
55
55
  seo: {
56
- siteName: '우리 서비스',
56
+ siteName: 'Our Service',
57
57
  siteUrl: 'https://example.com',
58
58
  basePath: '/board'
59
59
  }
@@ -68,11 +68,11 @@ export default defineNuxtConfig({
68
68
  })
69
69
  ```
70
70
 
71
- Firebase 값은 **붙일 사이트의 Firebase 웹 앱 설정**을 사용한다. Storage 버킷 이름은 프로젝트마다 다를 수 있으므로 콘솔의 실제 값을 복사한다. Firebase Admin 서비스 계정 키는 클라이언트 설정에 넣지 않는다.
71
+ Use the **Firebase web app config of the site you are adding the board to**. Storage bucket names can differ per project, so copy the actual value from the console. Never put a Firebase Admin service account key in the client config.
72
72
 
73
- 공개 목록·상세의 SSR은 유지한다. `moderation.enabled: false`여도 기본 로컬 금칙어 검사는 남는다. 글쓰기 AI의 교정·문장 다듬기까지 끄는 옵션은 이 설정에 포함되지 않으며, 해당 AI 메뉴를 사용할 때는 AI Logic 연결이 필요하다.
73
+ Keep SSR for the public list and detail pages. Even with `moderation.enabled: false`, the default local banned-word check remains. This config has no option to turn off the writing AI's proofreading and sentence polishing; using those AI menus requires an AI Logic connection.
74
74
 
75
- ## 4. 스타일과 UApp 연결
75
+ ## 4. Styles and UApp
76
76
 
77
77
  ```css
78
78
  /* app/assets/css/main.css */
@@ -81,10 +81,10 @@ Firebase 값은 **붙일 사이트의 Firebase 웹 앱 설정**을 사용한다.
81
81
  @source "../../../node_modules/memi-board/dist/runtime/components";
82
82
  ```
83
83
 
84
- `@source`는 위 CSS 파일 위치 기준이다. 다른 위치의 CSS를 쓰면 상대 경로를 맞춘다.
84
+ `@source` is relative to the CSS file above. If your CSS lives elsewhere, adjust the relative path.
85
85
 
86
86
  ```vue
87
- <!-- app/app.vue — 기존 UApp이 있으면 중복으로 감싸지 않는다 -->
87
+ <!-- app/app.vue — if a UApp already exists, don't wrap it twice -->
88
88
  <template>
89
89
  <UApp>
90
90
  <NuxtPage />
@@ -92,13 +92,13 @@ Firebase 값은 **붙일 사이트의 Firebase 웹 앱 설정**을 사용한다.
92
92
  </template>
93
93
  ```
94
94
 
95
- 레이아웃이 있는 앱은 기존 `<NuxtLayout>` 구조를 유지한다. Nuxt UI 내부 문구도 한국어로 고정하려면 `@nuxt/ui/locale`의 `ko`를 가져와 `<UApp :locale="ko">`로 지정한다. 호스트의 `<html lang>`도 호스트가 관리한다.
95
+ Apps with layouts keep their existing `<NuxtLayout>` structure. To also pin Nuxt UI's internal strings to Korean, import `ko` from `@nuxt/ui/locale` and set `<UApp :locale="ko">`. The host app also manages `<html lang>`.
96
96
 
97
- ## 5. 규칙과 인덱스 배포
97
+ ## 5. Deploy rules and indexes
98
98
 
99
- 설치한 패키지의 다음 파일을 호스트 파일에 병합한다. 이미 사이트 규칙이 있으면 통째로 덮어쓰지 않는다.
99
+ Merge the following files from the installed package into the host files. If the site already has rules, don't overwrite them wholesale.
100
100
 
101
- | 패키지 파일 | 호스트 파일 |
101
+ | Package file | Host file |
102
102
  |---|---|
103
103
  | `node_modules/memi-board/docs/firestore.rules.example` | `firestore.rules` |
104
104
  | `node_modules/memi-board/docs/storage.rules.example` | `storage.rules` |
@@ -108,9 +108,9 @@ Firebase 값은 **붙일 사이트의 Firebase 웹 앱 설정**을 사용한다.
108
108
  firebase deploy --only firestore:rules,storage,firestore:indexes
109
109
  ```
110
110
 
111
- `firebase.json`에는 인덱스 파일 경로도 지정한다. Storage 규칙의 배포 버킷은 `vuefire.config.storageBucket`과 같아야 한다. 교차 서비스 IAM까지 포함한 최초 설정은 [Firebase 준비](firebase-setup.md#storage-규칙의-firestore-조회-권한-부모호스트-프로젝트-필수-설정)를 따른다. npm 설치나 웹 사이트 배포가 이 배포를 대신하지 않는다.
111
+ Also set the index file path in `firebase.json`. The bucket the Storage rules are deployed to must match `vuefire.config.storageBucket`. For the initial setup including cross-service IAM, follow [Firebase setup](firebase-setup.md#firestore-read-access-for-storage-rules-required-host-app-setup). Installing from npm or deploying the website does not replace this deployment.
112
112
 
113
- ## 6. 관리 페이지와 첫 게시판 만들기
113
+ ## 6. Admin pages and the first board
114
114
 
115
115
  ```vue
116
116
  <!-- app/pages/board-settings.vue -->
@@ -126,30 +126,30 @@ firebase deploy --only firestore:rules,storage,firestore:indexes
126
126
  </template>
127
127
  ```
128
128
 
129
- 로그인용 페이지 또는 기존 계정 화면에 `<MemiBoardSignIn />`을 추가하고 다음 순서로 초기화한다.
129
+ Add `<MemiBoardSignIn />` to a sign-in page or an existing account screen, then initialize in this order:
130
130
 
131
- 1. Firebase Authentication에서 Google 공급자와 사이트의 승인 도메인을 설정한다.
132
- 2. 게시판의 로그인 컴포넌트로 로그인하여 `memiBoardUsers/{uid}` 문서를 생성한다. 호스트 자체 로그인만 있다면 게시판 인증 composable도 사용하는 페이지를 연다.
133
- 3. 신뢰할 수 있는 Firebase Console 또는 서버 Admin SDK에서 해당 문서의 `role`을 `owner`로 지정한다.
134
- 4. 새로고침 또는 재로그인 후 `/board-settings`에서 ID가 `free`인 게시판을 만든다.
135
- 5. `/board-users`에서 필요한 계정에 `admin`·`manager`·`staff` 역할을 지정하고, 게시판 설정에서 담당자를 배정한다.
131
+ 1. In Firebase Authentication, set up the Google provider and the site's authorized domains.
132
+ 2. Sign in with the board's sign-in component to create the `memiBoardUsers/{uid}` document. If the host only has its own sign-in, open a page that also uses the board auth composable.
133
+ 3. From the trusted Firebase Console or a server-side Admin SDK, set that document's `role` to `owner`.
134
+ 4. After reloading or signing in again, create a board with the ID `free` in `/board-settings`.
135
+ 5. In `/board-users`, assign `admin`/`manager`/`staff` roles to the accounts that need them, and assign people in the board settings.
136
136
 
137
- 최초 owner 지정과 owner 이관은 클라이언트 역할 선택 메뉴로 하지 않는다. 라우트 접근 가드는 호스트가 추가하고, 실제 데이터 접근은 규칙으로 검증한다. 호스트 로그인 공급자가 게시판과 같은 Firebase Auth를 사용하면 로그인 상태를 공유한다.
137
+ Don't assign the first owner or transfer ownership through the client role selection menu. The host app adds route access guards, and actual data access is enforced by the rules. If the host's sign-in provider uses the same Firebase Auth as the board, sign-in state is shared.
138
138
 
139
- ## 7. 목록·글쓰기·상세 페이지 연결
139
+ ## 7. List, new post, and detail pages
140
140
 
141
- 권장 경로는 다음과 같다. `/board` 시작 페이지와 메뉴 구성은 호스트가 정한다.
141
+ The recommended paths are below. The host app decides the `/board` landing page and menu structure.
142
142
 
143
- | 파일 | URL | 전달할 값 |
143
+ | File | URL | Value to pass |
144
144
  |---|---|---|
145
- | `app/pages/board/[boardId]/index.vue` | `/board/free` | 목록의 `boardId` |
146
- | `app/pages/board/[boardId]/new.vue` | `/board/free/new` | 에디터의 `boardId` |
147
- | `app/pages/board/[boardId]/[slug]/index.vue` | `/board/free/글-slug` | slug를 조회해 상세의 `postId`로 전달 |
148
- | `app/pages/board/[boardId]/[slug]/edit.vue` | `/board/free/글-slug/edit` | 조회한 문서 ID로 기존 글 수정 |
145
+ | `app/pages/board/[boardId]/index.vue` | `/board/free` | `boardId` for the list |
146
+ | `app/pages/board/[boardId]/new.vue` | `/board/free/new` | `boardId` for the editor |
147
+ | `app/pages/board/[boardId]/[slug]/index.vue` | `/board/free/post-slug` | Look up the slug and pass it as the detail's `postId` |
148
+ | `app/pages/board/[boardId]/[slug]/edit.vue` | `/board/free/post-slug/edit` | Edit the existing post using the looked-up document ID |
149
149
 
150
- 컴포넌트는 Nuxt 모듈이 auto-import한다. `memi-board/runtime`에서는 composable과 타입만 가져온다.
150
+ Components are auto-imported by the Nuxt module. Import only composables and types from `memi-board/runtime`.
151
151
 
152
- ### 목록
152
+ ### List
153
153
 
154
154
  ```vue
155
155
  <!-- app/pages/board/[boardId]/index.vue -->
@@ -178,9 +178,9 @@ await useMemiBoardListSeo({
178
178
  </template>
179
179
  ```
180
180
 
181
- `canWrite`는 이 예제에서 로그인 여부만 표시한다. 스태프·관리자 전용 게시판을 운영할 때는 호스트가 게시판의 `writeRole`과 담당 범위까지 검사해 버튼을 표시한다. 규칙은 버튼 표시와 별개로 쓰기 권한을 검증한다.
181
+ In this example, `canWrite` only reflects whether the user is signed in. For staff- or admin-only boards, the host app should also check the board's `writeRole` and assignment scope before showing the button. The rules enforce write permission independently of whether the button is shown.
182
182
 
183
- 목록의 개별 설정 링크에 대응하는 페이지도 만든다.
183
+ Also create the page that the list's per-board settings link points to.
184
184
 
185
185
  ```vue
186
186
  <!-- app/pages/board-settings/[boardId].vue -->
@@ -194,9 +194,9 @@ const boardId = computed(() => String(route.params.boardId || ''))
194
194
  </template>
195
195
  ```
196
196
 
197
- 매니저 진입 가드에는 `canConfigureBoard(boardId)`, 콘텐츠 관리에는 `canManageBoard(boardId)`를 사용한다. 설정 컴포넌트도 기본적으로 담당 설정 권한을 확인한다.
197
+ Use `canConfigureBoard(boardId)` for the manager entry guard and `canManageBoard(boardId)` for content management. The settings component also checks assigned settings permission by default.
198
198
 
199
- ### 글쓰기
199
+ ### New post
200
200
 
201
201
  ```vue
202
202
  <!-- app/pages/board/[boardId]/new.vue -->
@@ -217,9 +217,9 @@ const { isSignedIn, rolePending } = useMemiBoardAuth()
217
217
  </template>
218
218
  ```
219
219
 
220
- 새 글의 `saved` 이벤트는 **slug**를 전달하고, 기존 글 수정의 `saved` 이벤트는 **Firestore 문서 ID**를 전달한다. 수정 페이지에서는 현재 URL의 slug를 유지해 이동한다. 에디터 자체가 게시판 언어를 적용한다.
220
+ For a new post, the `saved` event passes the **slug**; when editing an existing post, the `saved` event passes the **Firestore document ID**. On the edit page, navigate using the slug from the current URL. The editor itself applies the board language.
221
221
 
222
- ### 상세
222
+ ### Detail
223
223
 
224
224
  ```vue
225
225
  <!-- app/pages/board/[boardId]/[slug]/index.vue -->
@@ -262,14 +262,14 @@ watch([boardId, slug, seoPost], async ([board, currentSlug, seo], _, onCleanup)
262
262
  @list="navigateTo(`/board/${boardId}`)"
263
263
  @deleted="navigateTo(`/board/${boardId}`)"
264
264
  />
265
- <p v-else-if="unavailable">글을 찾을 수 없거나 접근 권한이 없습니다.</p>
266
- <p v-else>불러오는 중입니다.</p>
265
+ <p v-else-if="unavailable">The post could not be found, or you don't have access to it.</p>
266
+ <p v-else>Loading...</p>
267
267
  </template>
268
268
  ```
269
269
 
270
- `boardId`는 게시판 설정의 문서 ID, `postId`는 글의 Firestore 문서 ID, `slug`는 URL용 문자열이다. 상세 컴포넌트에 slug를 `postId`로 넣지 않는다. 위 예제의 공개 상세 SEO는 SSR에서 채우고, 초안·비공개 글의 클라이언트 조회는 로그인과 규칙을 따른다.
270
+ `boardId` is the board settings document ID, `postId` is the post's Firestore document ID, and `slug` is the URL string. Don't pass the slug as `postId` to the detail component. In the example above, SEO for public detail pages is filled in during SSR, while client-side reads of drafts and private posts follow sign-in state and the rules.
271
271
 
272
- ### 수정
272
+ ### Edit
273
273
 
274
274
  ```vue
275
275
  <!-- app/pages/board/[boardId]/[slug]/edit.vue -->
@@ -294,7 +294,7 @@ watch([boardId, slug, rolePending, isSignedIn], async (_, __, onCleanup) => {
294
294
  if (active && post && canEdit(post)) postId.value = post.id ?? ''
295
295
  }
296
296
  catch {
297
- // 로그인·권한·삭제·네트워크 오류는 아래 안내로 처리한다.
297
+ // Sign-in, permission, deletion, and network errors are handled by the message below.
298
298
  }
299
299
  }
300
300
  if (active) pending.value = false
@@ -302,7 +302,7 @@ watch([boardId, slug, rolePending, isSignedIn], async (_, __, onCleanup) => {
302
302
  </script>
303
303
 
304
304
  <template>
305
- <p v-if="pending">불러오는 중입니다.</p>
305
+ <p v-if="pending">Loading...</p>
306
306
  <MemiBoardEditor
307
307
  v-else-if="postId"
308
308
  :board-id="boardId"
@@ -310,46 +310,46 @@ watch([boardId, slug, rolePending, isSignedIn], async (_, __, onCleanup) => {
310
310
  @saved="navigateTo(`/board/${boardId}/${encodeURIComponent(slug)}`)"
311
311
  @cancel="navigateTo(`/board/${boardId}/${encodeURIComponent(slug)}`)"
312
312
  />
313
- <p v-else>글을 수정할 권한이 없거나 글을 불러올 수 없습니다.</p>
313
+ <p v-else>You don't have permission to edit this post, or it could not be loaded.</p>
314
314
  </template>
315
315
  ```
316
316
 
317
- ## 8. 역할과 서비스별 명칭
317
+ ## 8. Roles and service-specific labels
318
318
 
319
319
  ```ts
320
320
  memiBoard: {
321
321
  roleLabels: {
322
- user: '고객',
323
- staff: '매장 직원',
324
- manager: '매장 관리자',
325
- admin: '운영자',
326
- owner: '최고 관리자'
322
+ user: 'Customer',
323
+ staff: 'Store staff',
324
+ manager: 'Store manager',
325
+ admin: 'Operator',
326
+ owner: 'Super admin'
327
327
  }
328
328
  }
329
329
  ```
330
330
 
331
- 명칭만 바뀌며 권한은 바뀌지 않는다. manager는 `allowedManagerUids`에 지정된 게시판의 설정과 스태프 배정을 관리한다. 매장·조직과 게시판을 연결하고 담당 배정을 동기화하는 책임은 호스트에 있다. 단순히 같은 Firebase 프로젝트를 쓴다고 조직 경계가 자동 생성되지 않는다.
331
+ Only the labels change; permissions do not. A manager manages the settings and staff assignments of the boards listed in `allowedManagerUids`. Linking stores or organizations to boards and syncing assignments is the host app's responsibility. Simply sharing the same Firebase project does not automatically create organization boundaries.
332
332
 
333
- 기본 파일 규칙에서 타인 파일 수정·삭제는 전역 admin/owner만 가능하다. 담당 staff/manager의 타인 첨부 교체·파일 정리가 필요한 서비스는 게시판 배정을 확인하는 서버 API를 추가한다. 기본 Storage 파일 읽기는 공개다. 자세한 역할과 기존 데이터 이전은 [역할 마이그레이션](roles-migration.md)을 따른다.
333
+ Under the default file rules, only a global admin/owner can modify or delete other users' files. Services where assigned staff/managers need to replace other users' attachments or clean up files should add a server API that verifies board assignment. Default Storage file reads are public. For details on roles and migrating existing data, see [Role migration](roles-migration.md).
334
334
 
335
- ## 9. 다른 사이트와 데이터 분리
335
+ ## 9. Separating data from other sites
336
336
 
337
- | 구성 | 권장 방식 |
337
+ | Setup | Recommended approach |
338
338
  |---|---|
339
- | 별도 서비스·고객사의 독립 사이트 | Firebase 프로젝트도 분리 |
340
- | 같은 서비스 안의 여러 게시판 | 같은 프로젝트에서 `boardId`로 구분 |
341
- | 한 프로젝트 안의 다른 컬렉션 이름 | `postsCollection` 등 6개 이름과 Rules·Storage 경로·인덱스를 함께 변경 |
339
+ | Independent sites for separate services or clients | Use separate Firebase projects too |
340
+ | Multiple boards within the same service | Same project, separated by `boardId` |
341
+ | Different collection names within one project | Change the 6 names such as `postsCollection` together with the rules, Storage paths, and indexes |
342
342
 
343
- 컬렉션 이름 옵션이나 `boardId`는 그 자체로 보안상 테넌트 격리를 보장하지 않는다. 기본 admin/owner는 해당 컬렉션 전체를 관리한다. 서로 독립적인 관리자 경계가 필요하면 프로젝트를 분리하거나 호스트 규칙을 별도로 설계한다.
343
+ Collection name options and `boardId` do not, by themselves, guarantee tenant isolation for security. The default admin/owner manages the entire collection. If you need independent administrator boundaries, separate the projects or design the host rules separately.
344
344
 
345
- ## 10. 배포 전 확인
345
+ ## 10. Pre-deployment checklist
346
346
 
347
- - 첫 owner 로그인 후 `free` 게시판 생성, 일반 계정의 글·댓글 작성 확인.
348
- - 글 상세 진입 시 slug → 문서 ID 조회와 원문 표시 확인.
349
- - 선택한 국제화 모드에 맞춰 언어·번역 UI와 AI 요청 여부 확인.
350
- - 매니저가 담당 게시판만 설정하고 다른 게시판은 설정하지 못하는지 확인.
351
- - 앱이 쓰는 Storage 버킷에 규칙이 배포됐는지 확인하고 이미지·첨부 업로드 확인.
352
- - 공개 목록·상세 HTML에 OG 메타가 있는지 배포 URL에서 확인.
353
- - 웹 앱 배포와 Firebase 규칙·인덱스 배포 결과를 각각 확인.
347
+ - After the first owner signs in, verify creating the `free` board and writing posts and comments with a regular account.
348
+ - On opening a post detail page, verify the slug → document ID lookup and that the original content is shown.
349
+ - Verify the language/translation UI and whether AI requests are made, according to the chosen internationalization mode.
350
+ - Verify that a manager can configure only their assigned boards and not other boards.
351
+ - Verify that the rules are deployed to the Storage bucket the app uses, and test image and attachment uploads.
352
+ - Verify on the deployed URL that the public list and detail HTML contain OG meta tags.
353
+ - Check the results of the web app deployment and the Firebase rules/index deployment separately.
354
354
 
355
- 오류별 확인 순서는 [README 문제 해결](../README.md#문제-해결)을 참고한다.
355
+ For troubleshooting steps by error, see [README Troubleshooting](../README.md#troubleshooting).