memi-board 0.3.1 → 0.4.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.
package/README.md CHANGED
@@ -2,27 +2,40 @@
2
2
 
3
3
  Nuxt 4 + Nuxt UI 프로젝트에 Firebase 기반 게시판을 바로 붙이는 패키지. 게시글 CRUD, 첨부파일(Storage), 댓글, 권한(작성자 본인/관리자), Firebase AI Logic(Gemini) 기반 AI 검열을 제공한다.
4
4
 
5
- 클라이언트 전용이다 — 서버나 Firebase Admin SDK가 필요 없다. 소비 프로젝트가 자신의 Firebase 프로젝트 설정만 넘기면 동작한다.
5
+ 클라이언트 전용이다 — 서버나 Firebase Admin SDK가 필요 없다.
6
6
 
7
- **Nuxt 모듈이 아니라 순수 Vue 3 컴포넌트/composable 패키지다.** `nuxt.config`를 건드리거나 `nuxt-vuefire`를 대신 설치해주지 않는다 — 호스트 프로젝트가 이미 자기 앱을 위해 갖추고 있을 Firebase/nuxt-vuefire 설정을 그대로 재사용하고, memi-board는 그 위에 컴포넌트와 composable만 얹는다. 여러 프로젝트에서 같은 게시판을 재사용하는 게 1차 목적이라, "설정 지점 하나(호스트 프로젝트)"를 유지하는 쪽을 우선했다.
7
+ ## 구조 (0.4+)
8
+
9
+ | 엔트리 | 역할 |
10
+ |--------|------|
11
+ | `memi-board` | **core** — `configureMemiBoard`, composables, types |
12
+ | `memi-board/nuxt` | **thin Nuxt 모듈** — UI SFC 를 호스트 파이프라인에 등록만 함 |
13
+
14
+ **의도적으로 하지 않는 일:** `nuxt-vuefire` 설치, Firebase config, 자동 라우트/미들웨어.
15
+ Firebase·인증 설정 지점은 항상 **호스트 하나**. UI 는 호스트가 SFC 를 컴파일해야 Nuxt UI auto-import(`UButton` 등)가 동작한다 — 그래서 prebundle 하지 않고 thin 모듈로 올린다.
16
+
17
+ 런타임 설정(`collectionPrefix` 등)은 모듈 options 가 아니라 호스트 플러그인의 `configureMemiBoard()` 한곳.
8
18
 
9
19
  ## 설치
10
20
 
11
- **사전 준비물**: Node.js + pnpm, Nuxt 4+ / `@nuxt/ui` 4+, Firestore·Storage·Authentication을 켜둔 Firebase 프로젝트, Firebase CLI(`firebase login` 완료, `firebase.json`/`.firebaserc` 연결). 아직 하나라도 없다면 → [docs/firebase-setup.md](docs/firebase-setup.md)에서 처음부터 순서대로 설명한다.
21
+ **사전 준비물**: Node.js + pnpm, Nuxt 4+ / `@nuxt/ui` 4+, Firestore·Storage·Authentication을 켜둔 Firebase 프로젝트. 아직 없다면 → [docs/firebase-setup.md](docs/firebase-setup.md).
12
22
 
13
23
  ```bash
14
24
  pnpm add memi-board @nuxt/ui nuxt-vuefire vuefire firebase
15
25
  ```
16
26
 
17
- `vuefire`/`firebase`/`vue-router`는 `peerDependencies`다 — 반드시 프로젝트에 직접 설치해야 한다(`vue-router`는 Nuxt가 내부적으로 이미 갖고 있어 보통 별도 설치가 필요 없다). memi-board가 자체적으로 이 패키지들을 번들하지 않는 이유는, 그러면 호스트 프로젝트가 설치한 버전과 memi-board 내부 버전이 **서로 다른 모듈 인스턴스**가 돼서 `useCurrentUser()`가 항상 `undefined`를 반환하거나 "VueFireAuth module was added" 에러가 나기 때문이다(vuefire의 인증 상태는 모듈 인스턴스별 내부 상태로 관리됨). 직접 설치하면 하나로 합쳐진다. `nuxt-vuefire`는 memi-board의 peer는 아니지만(memi-board는 vuefire만 직접 쓴다), Nuxt에서 Firebase를 연동하려면 결국 필요하다.
27
+ `vuefire`/`firebase` 는 peer — 호스트에 직접 설치해야 모듈 인스턴스가 하나다.
18
28
 
19
- ### 1. 호스트가 nuxt-vuefire를 직접 설정한다
20
-
21
- `nuxt.config.ts`:
29
+ ### 1. modules + vuefire (호스트)
22
30
 
23
31
  ```ts
32
+ // nuxt.config.ts
24
33
  export default defineNuxtConfig({
25
- modules: ['@nuxt/ui', 'nuxt-vuefire'],
34
+ modules: [
35
+ '@nuxt/ui',
36
+ 'nuxt-vuefire',
37
+ 'memi-board/nuxt', // UI SFC 등록 (vuefire/라우트 안 건드림)
38
+ ],
26
39
  vuefire: {
27
40
  config: {
28
41
  apiKey: '...',
@@ -37,49 +50,40 @@ export default defineNuxtConfig({
37
50
  })
38
51
  ```
39
52
 
40
- `firebaseConfig` 값은 Firebase 콘솔 → 프로젝트 설정 → 일반 → "내 앱"에서 복사한다 (자세한 위치는 [docs/firebase-setup.md](docs/firebase-setup.md#3-firebaseconfig-값-가져오기) 참고). 이미 다른 기능(예: 매장 운영자 로그인 등) 때문에 nuxt-vuefire를 쓰고 있는 프로젝트라면 이 단계는 그대로 건너뛴다 — memi-board는 그 설정을 그대로 재사용한다.
41
-
42
- ### 2. memi-board를 설정한다
43
-
44
- 플러그인 파일 하나(예: `app/plugins/memi-board.ts`)에서 앱 부팅 시 한 번 호출한다:
53
+ ### 2. configureMemiBoard (플러그인)
45
54
 
46
55
  ```ts
56
+ // app/plugins/memi-board.ts
47
57
  import { configureMemiBoard } from 'memi-board'
48
58
 
49
59
  export default defineNuxtPlugin(() => {
50
60
  configureMemiBoard({
51
- collectionPrefix: 'board', // Firestore 컬렉션 접두사 (boardPosts, boardUsers, boardSettings)
52
- auth: { providers: ['google', 'apple'] }, // 'emailPassword'도 선택 가능
61
+ collectionPrefix: 'board', // boardPosts, boardUsers, boardSettings
62
+ auth: { providers: ['google', 'apple'] },
53
63
  moderation: { enabled: true, onError: 'allow' },
54
64
  })
55
65
  })
56
66
  ```
57
67
 
58
- 값을 넘기지 않으면 `collectionPrefix: 'memiBoard'`, `auth.providers: ['google', 'apple']`, `moderation.enabled: true`가 기본값이다.
59
-
60
- ### 3. Tailwind가 memi-board 컴포넌트를 스캔하게 한다
61
-
62
- Tailwind v4는 기본적으로 `node_modules`를 스캔 대상에서 제외한다 — memi-board의 컴포넌트가 쓰는 유틸리티 클래스(`flex`, `gap-2` 등)가 호스트의 최종 CSS에 아예 생성되지 않아 **레이아웃이 깨진 채로(스타일 없이) 렌더링**된다. 호스트의 CSS 진입점에 `@source`로 memi-board의 빌드 결과물을 명시적으로 추가해야 한다:
68
+ ### 3. Tailwind `@source`
63
69
 
64
70
  ```css
65
71
  /* app/assets/css/main.css */
66
72
  @import "tailwindcss";
67
73
  @import "@nuxt/ui";
68
- @source "../../../node_modules/memi-board/dist";
74
+ @source "../../../node_modules/memi-board/dist/runtime/components";
69
75
  ```
70
76
 
71
- 프로젝트 구조에 따라 `node_modules`까지의 상대 경로는 조정한다(CSS 파일 기준 상대 경로).
72
-
73
- > ⚠️ **설치는 여기서 끝이 아니다.** 이 패키지는 서버가 없어서 Firestore/Storage Security Rules를 직접 배포해야 글쓰기·댓글이 동작한다 — 아래 [Security Model](#security-model--반드시-읽어야-하는-부분) 섹션을 반드시 따라 한다. 건너뛰면 글쓰기 버튼을 눌렀을 때 `permission-denied`가 난다.
77
+ > ⚠️ Firestore/Storage Security Rules 도 배포해야 글쓰기가 된다 — [Security Model](#security-model--반드시-읽어야-하는-부분).
74
78
 
75
79
  ## 사용법
76
80
 
77
- 컴포넌트를 원하는 페이지에 직접 배치한다 — 라우팅은 항상 호스트 프로젝트가 소유한다.
81
+ 라우팅은 호스트 소유. 컴포넌트는 모듈 auto-import (`MemiBoardList` …).
78
82
 
79
83
  ```vue
80
84
  <!-- app/pages/board/index.vue -->
81
85
  <script setup lang="ts">
82
- import { MemiBoardList, MemiBoardSignIn, useMemiBoardAuth } from 'memi-board'
86
+ import { useMemiBoardAuth } from 'memi-board'
83
87
  const { isSignedIn } = useMemiBoardAuth()
84
88
  </script>
85
89
 
@@ -95,7 +99,6 @@ const { isSignedIn } = useMemiBoardAuth()
95
99
  ```vue
96
100
  <!-- app/pages/board/[id].vue -->
97
101
  <script setup lang="ts">
98
- import { MemiBoardDetail } from 'memi-board'
99
102
  const route = useRoute()
100
103
  const router = useRouter()
101
104
  </script>
@@ -109,7 +112,7 @@ const router = useRouter()
109
112
  </template>
110
113
  ```
111
114
 
112
- 글쓰기(`/board/new`)·수정(`/board/[id]/edit`) 라우트는 로그인 여부를 호스트가 직접 가드한다 — memi-board는 라우트 미들웨어를 제공하지 않는다(라우팅은 호스트 관심사):
115
+ 글쓰기(`/board/new`)·수정 라우트 가드도 호스트가 직접 (`vuefire` `getCurrentUser`):
113
116
 
114
117
  ```ts
115
118
  // app/middleware/board-auth.ts
@@ -1,12 +1,18 @@
1
1
  import { BoardCategory } from '../types';
2
+ /** 설정 문서가 없을 때 UI·시드에 쓰는 기본 카테고리. */
3
+ export declare const DEFAULT_CATEGORIES: BoardCategory[];
2
4
  /**
3
- * 게시판 설정({prefix}Settings/config) — 지금은 카테고리 옵션만 다룬다.
4
- * 문서가 없으면 기본 카테고리를 그대로 보여주고, 관리자가 게시판을 방문하는 순간
5
- * 그 기본값으로 문서를 한 번 만들어 둔다(이후엔 Firebase 콘솔/향후 관리 UI에서 직접 수정).
6
- * 일반 사용자는 절대 이 문서를 쓰지 않는다(생성 실패해도 조용히 무시 — 기본값으로 계속 동작).
5
+ * 게시판 설정({prefix}Settings/config) — 카테고리 옵션 목록.
6
+ *
7
+ * - 읽기: 전체 공개 (useDocument 실시간)
8
+ * - 문서 없음: DEFAULT 로 보여 주고, 로그인 사용자가 처음 쓰거나 추가할 때 시드 생성
9
+ * - 카테고리 추가: 로그인 사용자 (글쓰기 폼에서 즉시 추가)
7
10
  */
8
11
  export declare function useMemiBoardSettings(): {
9
12
  categories: import('vue').ComputedRef<BoardCategory[]>;
10
13
  categoryLabel: (id: string | undefined) => string | undefined;
11
14
  settingsPending: import('vue').Ref<boolean, boolean>;
15
+ ensureSettings: () => Promise<void>;
16
+ addCategory: (label: string) => Promise<string>;
17
+ DEFAULT_CATEGORIES: BoardCategory[];
12
18
  };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /** Core API — composables / config / types only. UI SFC 는 memi-board/nuxt 모듈이 등록. */
1
2
  export { configureMemiBoard, useMemiBoardConfig } from './config';
2
3
  export type { MemiBoardConfig, MemiBoardAuthOptions, MemiBoardModerationOptions } from './config';
3
4
  export { useMemiBoardAuth } from './composables/useMemiBoardAuth';
@@ -7,15 +8,8 @@ export { useMemiBoardComments } from './composables/useMemiBoardComments';
7
8
  export type { AddCommentInput } from './composables/useMemiBoardComments';
8
9
  export { useMemiBoardModeration } from './composables/useMemiBoardModeration';
9
10
  export { useMemiBoardStorage } from './composables/useMemiBoardStorage';
10
- export { useMemiBoardSettings } from './composables/useMemiBoardSettings';
11
- export { default as MemiBoardList } from './components/List.vue';
12
- export { default as MemiBoardDetail } from './components/Detail.vue';
13
- export { default as MemiBoardEditor } from './components/Editor.vue';
14
- export { default as MemiBoardCommentList } from './components/CommentList.vue';
15
- export { default as MemiBoardCommentForm } from './components/CommentForm.vue';
16
- export { default as MemiBoardAttachments } from './components/Attachments.vue';
17
- export { default as MemiBoardSignIn } from './components/SignIn.vue';
18
- export { default as MemiBoardVersionHistory } from './components/VersionHistory.vue';
11
+ export { useMemiBoardSettings, DEFAULT_CATEGORIES } from './composables/useMemiBoardSettings';
19
12
  export { versionHistory } from './data/versionHistory';
20
13
  export type { VersionHistoryEntry } from './data/versionHistory';
14
+ export { formatDate } from './utils/formatDate';
21
15
  export type { Attachment, ModerationStatus, PostModel, PostDetail, CommentModel, BoardUserRole, BoardCategory, BoardSettingsModel, ModerationResult, } from './types';