memi-board 0.9.0 → 0.10.0
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.
- package/README.md +107 -42
- package/dist/index.d.ts +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/module.d.ts +2 -1
- package/dist/module.mjs +51 -30
- package/dist/module.mjs.map +1 -1
- package/dist/runtime/components/Attachments.vue +2 -2
- package/dist/runtime/components/CommentForm.vue +4 -4
- package/dist/runtime/components/CommentItem.vue +2 -2
- package/dist/runtime/components/CommentList.vue +1 -1
- package/dist/runtime/components/CommentThread.vue +2 -2
- package/dist/runtime/components/Detail.vue +5 -5
- package/dist/runtime/components/Editor.vue +2 -2
- package/dist/runtime/components/List.vue +159 -3
- package/dist/runtime/components/ListDefault.vue +5 -4
- package/dist/runtime/components/ListImage.vue +5 -4
- package/dist/runtime/components/ListVideo.vue +5 -4
- package/dist/runtime/components/Settings.vue +2 -2
- package/dist/runtime/components/SignIn.vue +2 -2
- package/dist/runtime/components/Users.vue +2 -3
- package/dist/runtime/components/VersionHistory.vue +1 -1
- package/docs/firebase-setup.md +1 -1
- package/package.json +20 -43
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Nuxt 4 + Nuxt UI + Firebase 프로젝트에 **클라이언트 전용 게시판**
|
|
|
14
14
|
|
|
15
15
|
| 항목 | 요구 |
|
|
16
16
|
|------|------|
|
|
17
|
-
| 런타임 | Nuxt **4+**
|
|
17
|
+
| 런타임 | Nuxt **4+** |
|
|
18
18
|
| UI | `@nuxt/ui` **4+** (컴포넌트가 `UButton` 등 auto-import 전제) |
|
|
19
19
|
| 데이터 | Firebase **Firestore + Auth + Storage** |
|
|
20
20
|
| Vue | `vue` 3.5+, `vuefire` 3.2+, `firebase` 12+ |
|
|
@@ -22,21 +22,20 @@ Nuxt 4 + Nuxt UI + Firebase 프로젝트에 **클라이언트 전용 게시판**
|
|
|
22
22
|
|
|
23
23
|
**의도적으로 하지 않는 일:** Firebase 초기화, 라우트/미들웨어 등록, App Check 초기화, 서버 검열.
|
|
24
24
|
|
|
25
|
-
## 구조
|
|
25
|
+
## 구조
|
|
26
26
|
|
|
27
27
|
| 엔트리 | 역할 |
|
|
28
28
|
|--------|------|
|
|
29
|
-
| `memi-board` | **
|
|
30
|
-
| `memi-board/
|
|
29
|
+
| `memi-board` | **Nuxt 모듈** — 설정, SFC auto-import, peer dedupe, `dayjs` 개발 서버 최적화 |
|
|
30
|
+
| `memi-board/runtime` | composables, types, 유틸리티 (**Vue 컴포넌트 export 없음**) |
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
일반 사용자는 `nuxt.config.ts`의 `memiBoard`만 설정한다. 별도 Nuxt 플러그인은 필요 없다.
|
|
33
33
|
|
|
34
34
|
```
|
|
35
35
|
호스트 Nuxt 앱
|
|
36
36
|
├── nuxt-vuefire + firebaseConfig ← 인증·Firestore
|
|
37
37
|
├── (선택) App Check 플러그인 ← AI Logic Enforce 시 필수에 가까움
|
|
38
|
-
├── memi-board
|
|
39
|
-
├── plugins/memi-board.ts ← configureMemiBoard
|
|
38
|
+
├── memi-board ← Nuxt 모듈 + 런타임 설정
|
|
40
39
|
├── pages/board/* ← 라우트는 호스트 소유
|
|
41
40
|
└── firestore.rules + storage.rules ← docs 예시 병합 후 배포
|
|
42
41
|
```
|
|
@@ -46,10 +45,10 @@ Nuxt 4 + Nuxt UI + Firebase 프로젝트에 **클라이언트 전용 게시판**
|
|
|
46
45
|
**사전 준비:** Node + pnpm, Firebase 프로젝트(Firestore·Storage·Auth). 처음부터면 → [docs/firebase-setup.md](docs/firebase-setup.md).
|
|
47
46
|
|
|
48
47
|
```bash
|
|
49
|
-
pnpm add memi-board @nuxt/ui nuxt-vuefire vuefire firebase
|
|
48
|
+
pnpm add memi-board @nuxt/ui nuxt-vuefire vuefire firebase dayjs
|
|
50
49
|
```
|
|
51
50
|
|
|
52
|
-
`vuefire` / `firebase` / `nuxt-vuefire`
|
|
51
|
+
`vuefire` / `firebase` / `nuxt-vuefire` / `dayjs`는 **호스트에 직접 설치**한다. TipTap은 `@nuxt/ui`와 `memi-board`가 관리하므로 호스트에서 별도로 설치하거나 버전을 맞출 필요가 없다.
|
|
53
52
|
|
|
54
53
|
### 1. modules + vuefire
|
|
55
54
|
|
|
@@ -59,8 +58,21 @@ export default defineNuxtConfig({
|
|
|
59
58
|
modules: [
|
|
60
59
|
'@nuxt/ui',
|
|
61
60
|
'nuxt-vuefire',
|
|
62
|
-
'memi-board
|
|
61
|
+
'memi-board',
|
|
63
62
|
],
|
|
63
|
+
memiBoard: {
|
|
64
|
+
// Firestore: {prefix}Posts, {prefix}Users, {prefix}Settings
|
|
65
|
+
collectionPrefix: 'board',
|
|
66
|
+
auth: { providers: ['google', 'apple'] }, // + 'emailPassword'
|
|
67
|
+
moderation: {
|
|
68
|
+
enabled: true,
|
|
69
|
+
model: 'gemini-3.5-flash-lite',
|
|
70
|
+
onError: 'block', // 'allow' | 'block'
|
|
71
|
+
moderateImages: false,
|
|
72
|
+
useLimitedUseAppCheckTokens: false,
|
|
73
|
+
// localBlocklist: ['금칙어'],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
64
76
|
vuefire: {
|
|
65
77
|
// 콘솔 → 프로젝트 설정 → 내 앱 → 웹 앱 SDK snippet 과 동일해야 함
|
|
66
78
|
// (appId 가 다른 웹 앱이면 App Check / AI Logic 이 실패한다)
|
|
@@ -74,33 +86,18 @@ export default defineNuxtConfig({
|
|
|
74
86
|
},
|
|
75
87
|
auth: { enabled: true },
|
|
76
88
|
},
|
|
89
|
+
routeRules: {
|
|
90
|
+
// 게시판은 Firebase Auth/Firestore를 사용하는 클라이언트 전용 화면이다.
|
|
91
|
+
'/board/**': { ssr: false },
|
|
92
|
+
'/board-settings': { ssr: false },
|
|
93
|
+
'/board-users': { ssr: false },
|
|
94
|
+
},
|
|
77
95
|
})
|
|
78
96
|
```
|
|
79
97
|
|
|
80
|
-
|
|
98
|
+
`memiBoard` 옵션은 Nuxt 모듈이 런타임 플러그인으로 자동 전달한다. `app/plugins/memi-board.ts`를 만들지 않는다.
|
|
81
99
|
|
|
82
|
-
|
|
83
|
-
// app/plugins/memi-board.ts
|
|
84
|
-
import { configureMemiBoard } from 'memi-board'
|
|
85
|
-
|
|
86
|
-
export default defineNuxtPlugin(() => {
|
|
87
|
-
configureMemiBoard({
|
|
88
|
-
// Firestore: {prefix}Posts, {prefix}Users, {prefix}Settings
|
|
89
|
-
collectionPrefix: 'board',
|
|
90
|
-
auth: { providers: ['google', 'apple'] }, // + 'emailPassword'
|
|
91
|
-
moderation: {
|
|
92
|
-
enabled: true,
|
|
93
|
-
model: 'gemini-3.5-flash-lite', // 권장. 2.5-flash 는 신규 프로젝트에서 미제공일 수 있음
|
|
94
|
-
onError: 'block', // 'allow' | 'block' — AI 실패 시 저장 허용/거부 (기본 allow)
|
|
95
|
-
moderateImages: false,
|
|
96
|
-
useLimitedUseAppCheckTokens: false, // App Check 세션 토큰으로도 가능
|
|
97
|
-
// localBlocklist: ['금칙어'],
|
|
98
|
-
},
|
|
99
|
-
})
|
|
100
|
-
})
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### 3. Tailwind `@source` (필수)
|
|
100
|
+
### 2. Tailwind `@source` (필수)
|
|
104
101
|
|
|
105
102
|
패키지 SFC 안의 클래스가 purge되지 않도록:
|
|
106
103
|
|
|
@@ -111,7 +108,7 @@ export default defineNuxtPlugin(() => {
|
|
|
111
108
|
@source "../../../node_modules/memi-board/dist/runtime/components";
|
|
112
109
|
```
|
|
113
110
|
|
|
114
|
-
###
|
|
111
|
+
### 3. Security Rules + 인덱스 배포
|
|
115
112
|
|
|
116
113
|
1. [docs/firestore.rules.example](docs/firestore.rules.example) → 호스트 `firestore.rules`에 병합
|
|
117
114
|
(`memiBoard*` 이름을 쓰는 `collectionPrefix`에 맞게 치환)
|
|
@@ -145,15 +142,29 @@ export default defineNuxtPlugin(() => {
|
|
|
145
142
|
|
|
146
143
|
> ⚠️ npm 패키지 tarball에도 `docs/` 가 포함된다(0.4.7+). 구버전이면 GitHub의 `docs/`를 보면 된다.
|
|
147
144
|
|
|
145
|
+
### 4. 최초 관리자와 카테고리 만들기
|
|
146
|
+
|
|
147
|
+
Rules는 일반 사용자가 스스로 관리자가 되는 것을 막는다. 따라서 최초 한 번은 다음 순서가 필요하다.
|
|
148
|
+
|
|
149
|
+
1. 아래 게시판 페이지를 만든 뒤 Google 또는 Apple로 로그인한다.
|
|
150
|
+
2. Firebase Console → Firestore에서 `{prefix}Users/{uid}` 문서를 찾는다.
|
|
151
|
+
3. 해당 문서의 `role`을 문자열 `admin`으로 변경한다.
|
|
152
|
+
- `collectionPrefix: 'board'`라면 `boardUsers/{uid}`
|
|
153
|
+
- UID는 Firebase Console → Authentication → Users에서도 확인할 수 있다.
|
|
154
|
+
4. `/board-settings`에서 첫 카테고리를 만든다. 카테고리가 없으면 글을 작성할 수 없다.
|
|
155
|
+
5. 이후 역할 변경은 `/board-users`의 `MemiBoardUsers`에서 처리한다.
|
|
156
|
+
|
|
157
|
+
> 최초 관리자 지정은 신뢰할 수 있는 프로젝트 운영자가 Firebase Console에서만 수행한다. 클라이언트 코드에 관리자 UID나 우회 규칙을 넣지 않는다.
|
|
158
|
+
|
|
148
159
|
## 사용법
|
|
149
160
|
|
|
150
161
|
라우팅·미들웨어는 **호스트 소유**. 컴포넌트는 모듈 auto-import (`MemiBoardList` 등).
|
|
151
|
-
`import { MemiBoardEditor } from 'memi-board'
|
|
162
|
+
`import { MemiBoardEditor } from 'memi-board/runtime'`는 **안 된다**. 컴포넌트는 모듈이 auto-import하고, composable과 type만 `memi-board/runtime`에서 명시적으로 가져올 수 있다.
|
|
152
163
|
|
|
153
164
|
```vue
|
|
154
165
|
<!-- app/pages/board/index.vue -->
|
|
155
166
|
<script setup lang="ts">
|
|
156
|
-
import { useMemiBoardAuth } from 'memi-board'
|
|
167
|
+
import { useMemiBoardAuth } from 'memi-board/runtime'
|
|
157
168
|
const { isSignedIn } = useMemiBoardAuth()
|
|
158
169
|
</script>
|
|
159
170
|
|
|
@@ -161,7 +172,10 @@ const { isSignedIn } = useMemiBoardAuth()
|
|
|
161
172
|
<div>
|
|
162
173
|
<UButton v-if="isSignedIn" to="/board/new" label="새 글쓰기" />
|
|
163
174
|
<!-- 단순: 상세 /board/{id} -->
|
|
164
|
-
<MemiBoardList
|
|
175
|
+
<MemiBoardList
|
|
176
|
+
post-link-base="/board"
|
|
177
|
+
introduction="우리 서비스의 소식과 이야기를 나누는 게시판입니다."
|
|
178
|
+
/>
|
|
165
179
|
<!-- 카테고리 path: get-post-link 권장 -->
|
|
166
180
|
<!--
|
|
167
181
|
<MemiBoardList
|
|
@@ -205,7 +219,7 @@ export default defineNuxtRouteMiddleware(async () => {
|
|
|
205
219
|
<script setup lang="ts">
|
|
206
220
|
definePageMeta({ middleware: 'board-auth' })
|
|
207
221
|
const router = useRouter()
|
|
208
|
-
// MemiBoardEditor 는 auto-import (import from 'memi-board' 불필요)
|
|
222
|
+
// MemiBoardEditor 는 auto-import (import from 'memi-board/runtime' 불필요)
|
|
209
223
|
</script>
|
|
210
224
|
|
|
211
225
|
<template>
|
|
@@ -219,6 +233,26 @@ const router = useRouter()
|
|
|
219
233
|
|
|
220
234
|
전체 페이지 예시는 `playground/app/pages/board/` 참고.
|
|
221
235
|
|
|
236
|
+
### 관리 페이지
|
|
237
|
+
|
|
238
|
+
기본 Rules는 게시판의 `{prefix}Users/{uid}.role == 'admin'`을 검사한다. 컴포넌트도 같은 role을 확인하므로 기본 설치에서는 `authorized`를 전달하지 않는다.
|
|
239
|
+
|
|
240
|
+
```vue
|
|
241
|
+
<!-- app/pages/board-settings.vue -->
|
|
242
|
+
<template>
|
|
243
|
+
<MemiBoardSettings />
|
|
244
|
+
</template>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
```vue
|
|
248
|
+
<!-- app/pages/board-users.vue -->
|
|
249
|
+
<template>
|
|
250
|
+
<MemiBoardUsers />
|
|
251
|
+
</template>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
호스트의 별도 사이트 관리자 체계를 연동할 때만 `authorized`를 사용할 수 있다. 이 prop은 UI 표시만 허용하며 Firestore Rules를 우회하지 않는다. 이 경우에도 해당 사용자를 게시판 admin으로 등록하거나 호스트 Rules를 같은 관리자 체계에 맞게 수정해야 한다. 관리 URL이 일반 사용자에게 노출되지 않도록 호스트 라우트 가드를 추가하는 것을 권장한다.
|
|
255
|
+
|
|
222
256
|
### 제공하는 API
|
|
223
257
|
|
|
224
258
|
**컴포넌트 (auto-import):**
|
|
@@ -323,13 +357,16 @@ export default defineNuxtPlugin(() => {
|
|
|
323
357
|
## 다른 프로젝트에 붙일 때 체크리스트
|
|
324
358
|
|
|
325
359
|
- [ ] 동일 스택: Nuxt 4 + `@nuxt/ui` 4 + `nuxt-vuefire` + Firebase
|
|
326
|
-
- [ ] `pnpm add memi-board
|
|
327
|
-
- [ ] `modules`에 `memi-board
|
|
328
|
-
- [ ]
|
|
360
|
+
- [ ] `pnpm add memi-board @nuxt/ui nuxt-vuefire vuefire firebase dayjs`
|
|
361
|
+
- [ ] `modules`에 `@nuxt/ui`, `nuxt-vuefire`, `memi-board`
|
|
362
|
+
- [ ] 게시판·관리 경로에 `ssr: false`
|
|
363
|
+
- [ ] `nuxt.config.ts`의 `memiBoard` 설정 (`collectionPrefix` 충돌 없게)
|
|
329
364
|
- [ ] CSS `@source` …/memi-board/dist/runtime/components
|
|
330
365
|
- [ ] Rules 예시 병합 + **deploy**
|
|
331
366
|
- [ ] 카테고리 필터 사용 시 indexes deploy
|
|
332
367
|
- [ ] 페이지·미들웨어 직접 작성 (또는 playground 복사)
|
|
368
|
+
- [ ] 최초 로그인 후 Firebase Console에서 `{prefix}Users/{uid}.role = 'admin'`
|
|
369
|
+
- [ ] 설정 페이지에서 첫 카테고리 생성
|
|
333
370
|
- [ ] (검열) AI Logic 활성화 + 권장 모델 + App Check
|
|
334
371
|
- [ ] `pnpm why vuefire` 로 이중 인스턴스 없는지 확인
|
|
335
372
|
|
|
@@ -351,7 +388,25 @@ App Check 토큰, `appId` 일치, 모델명(3.x), AI Logic 활성화, 네트워
|
|
|
351
388
|
|
|
352
389
|
### monorepo `link:` / `file:` 로 개발할 때
|
|
353
390
|
|
|
354
|
-
|
|
391
|
+
링크하기 전에 패키지 의존성과 `dist`를 준비한다.
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
# memi-board 저장소
|
|
395
|
+
pnpm install
|
|
396
|
+
pnpm build
|
|
397
|
+
|
|
398
|
+
# 호스트 저장소
|
|
399
|
+
pnpm add link:../memi-board
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
`src/components` 수정은 Nuxt 모듈이 링크 소스를 직접 읽으므로 즉시 반영된다. `src/module.ts` 또는 core composable을 수정하면 `pnpm build`를 다시 실행하고 호스트 개발 서버를 재시작한다. 모듈이 Vue·Vue Router·VueFire·Firebase·TipTap dedupe와 `dayjs` 최적화를 자동 적용한다. 배포 전에는 의존성을 **npm 버전**으로 되돌린다.
|
|
403
|
+
|
|
404
|
+
링크 상태에서 새 Tailwind 클래스를 바로 시험하려면 호스트 CSS에 소스 경로를 하나 더 둔다.
|
|
405
|
+
|
|
406
|
+
```css
|
|
407
|
+
@source "../../../node_modules/memi-board/src/components";
|
|
408
|
+
@source "../../../node_modules/memi-board/dist/runtime/components";
|
|
409
|
+
```
|
|
355
410
|
|
|
356
411
|
## 로컬 개발 (이 리포)
|
|
357
412
|
|
|
@@ -370,6 +425,16 @@ firebase emulators:start --project demo-memi-board
|
|
|
370
425
|
NUXT_PUBLIC_USE_EMULATORS=1 pnpm dev
|
|
371
426
|
```
|
|
372
427
|
|
|
428
|
+
## npm 배포
|
|
429
|
+
|
|
430
|
+
npm의 6자리 OTP를 인자로 전달하면 빌드, 타입 검사, 패키지 dry-run을 모두 통과한 뒤에만 배포한다.
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
pnpm release 123456
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
`pnpm publish 123456`은 pnpm이 `123456`을 OTP가 아닌 폴더 경로로 해석하므로 사용하지 않는다.
|
|
437
|
+
|
|
373
438
|
## 버전 · 라이선스
|
|
374
439
|
|
|
375
440
|
- UI: `<MemiBoardVersionHistory />`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Runtime API — composables / config / types only. UI SFC는 루트 Nuxt 모듈이 등록. */
|
|
2
2
|
export { configureMemiBoard, useMemiBoardConfig } from './config';
|
|
3
3
|
export type { MemiBoardConfig, MemiBoardAuthOptions, MemiBoardModerationOptions } from './config';
|
|
4
4
|
export { useMemiBoardAuth } from './composables/useMemiBoardAuth';
|
package/dist/index.js
CHANGED
|
@@ -1058,6 +1058,21 @@ function Be() {
|
|
|
1058
1058
|
//#endregion
|
|
1059
1059
|
//#region src/data/versionHistory.ts
|
|
1060
1060
|
var Ve = [
|
|
1061
|
+
{
|
|
1062
|
+
version: "0.10.0",
|
|
1063
|
+
date: "2026-08-05",
|
|
1064
|
+
highlights: [
|
|
1065
|
+
"Nuxt 설정 한 곳에서 게시판 설치와 Firebase 연결을 모두 설정할 수 있어요",
|
|
1066
|
+
"설치 프로젝트가 TipTap과 Day.js의 세부 번들 설정을 직접 맞추지 않아도 돼요",
|
|
1067
|
+
"게시글 목록 이동을 NuxtLink로 통일해 SSR과 CSR 환경에서 안정적으로 열려요",
|
|
1068
|
+
"게시판 아래에서 제작 정보와 사용 기술, 지금까지의 버전 이야기를 볼 수 있어요"
|
|
1069
|
+
]
|
|
1070
|
+
},
|
|
1071
|
+
{
|
|
1072
|
+
version: "0.9.1",
|
|
1073
|
+
date: "2026-08-04",
|
|
1074
|
+
highlights: ["npm으로 설치한 프로젝트에서 사용자 관리 화면이 정상적으로 빌드돼요"]
|
|
1075
|
+
},
|
|
1061
1076
|
{
|
|
1062
1077
|
version: "0.9.0",
|
|
1063
1078
|
date: "2026-08-04",
|