memi-board 0.29.0 → 0.30.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 CHANGED
@@ -240,13 +240,16 @@ const { post: postSeo } = await useMemiBoardPostSeo({
240
240
  1. [docs/firestore.rules.example](docs/firestore.rules.example) → 호스트 `firestore.rules`에 병합
241
241
  (`memiBoardPosts`/`memiBoardComments`/`memiBoardLikes`/`memiBoardSettings`/`memiBoardUsers` — 전부 최상위 flat 컬렉션)
242
242
  2. [docs/storage.rules.example](docs/storage.rules.example) → `storage.rules` 병합
243
- 3. 목록/작성자별 쿼리에 필요한 복합 인덱스 배포 (아래)
244
- 4. 배포:
243
+ 3. `firebase.json`의 Storage `bucket`이 `vuefire.config.storageBucket`과 같은지 확인
244
+ (`*.appspot.com` vs `*.firebasestorage.app` — [firebase-setup.md](docs/firebase-setup.md) 참고)
245
+ 4. 목록/작성자별 쿼리에 필요한 복합 인덱스 배포 (아래)
246
+ 5. 배포:
245
247
  ```bash
246
248
  firebase deploy --only firestore:rules,storage,firestore:indexes
247
249
  ```
248
250
 
249
251
  **Rules를 파일에만 넣고 배포 안 하면** 글쓰기에서 `permission-denied` — 가장 흔한 설치 실수.
252
+ **Storage 규칙을 앱이 쓰는 버킷이 아닌 곳에 배포하면** 본문 이미지에서 `storage/unauthorized`.
250
253
 
251
254
  #### 복합 인덱스
252
255
 
@@ -376,6 +379,7 @@ const router = useRouter()
376
379
 
377
380
  **에디터 이미지:** 툴바 / 붙여넣기 / 드롭 → Firebase Storage
378
381
  `memiBoardPosts/{postId}/images/*` + `.../images/thumbnails/*` (최적화 이미지+썸네일).
382
+ 글 문서는 아직 없을 수 있고, 붙여넣기는 `image/png` 등이 온다. Storage 예시 규칙은 쓰기를 **로그인 + 5MB + `image/*`** 로 본다. 규칙은 **앱 `storageBucket`과 같은 버킷**에 배포해야 한다.
379
383
 
380
384
  본문 이미지는 휴대폰 원본 기준 최대 25MB까지 선택할 수 있다. 5MB를 넘으면 브라우저에서 최대 2560px·JPEG 85%로 최적화하고, 결과가 여전히 5MB를 넘으면 2048px·75%로 한 번 더 줄인 뒤 업로드한다. 5MB 이하 이미지는 원본을 유지한다.
381
385
  본문 HTML에는 원본 URL. 수정 중 버려진 파일은 호스트 스케줄러로 정리 (`extractEditorImageUrls` 로 본문 참조 비교).
@@ -505,6 +509,15 @@ export default defineNuxtPlugin(() => {
505
509
 
506
510
  App Check 토큰, `appId` 일치, 모델명(3.x), AI Logic 활성화, 네트워크. 브라우저 콘솔의 `moderation AI failed` warn 확인.
507
511
 
512
+ ### 에디터 이미지 `storage/unauthorized`
513
+
514
+ 본문 붙여넣기·업로드가 `User does not have permission to access 'memiBoardPosts/…/images/…'` 로 실패하면:
515
+
516
+ 1. 로그인 상태인지 확인
517
+ 2. `vuefire.config.storageBucket`과 `firebase.json` `storage.bucket`이 **같은 이름**인지 확인. 구 프로젝트는 `*.appspot.com`(앱)과 `*.firebasestorage.app`(CLI 기본)이 갈라질 수 있다 — 앱이 쓰는 쪽에 규칙을 배포한다.
518
+ 3. 호스트 `storage.rules`에 `docs/storage.rules.example`의 `memiBoardPosts/{postId}/images` 규칙이 있는지 확인. 쓰기는 `image/*` 이어야 한다(붙여넣기 PNG).
519
+ 4. `firebase deploy --only storage` 후 다시 올려 본다.
520
+
508
521
  ### monorepo `link:` / `file:` 로 개발할 때
509
522
 
510
523
  링크하기 전에 패키지 의존성과 `dist`를 준비한다.
@@ -563,13 +576,13 @@ NUXT_PUBLIC_USE_EMULATORS=1 pnpm dev
563
576
 
564
577
  ## npm 배포
565
578
 
566
- npm의 6자리 OTP를 인자로 전달하면 빌드, 타입 검사, 패키지 dry-run을 모두 통과한 뒤에만 배포한다.
579
+ npm Trusted Publishing(OIDC)을 사용한다. 장기 `NPM_TOKEN`이나 로컬 OTP는 저장하지 않는다.
567
580
 
568
- ```bash
569
- pnpm release 123456
570
- ```
581
+ 1. `package.json`, `CHANGELOG.md`, 인앱 `versionHistory`의 버전을 갱신하고 `main`에 푸시한다.
582
+ 2. GitHub에서 `v{package.json version}` 태그로 Release를 발행한다(예: `v0.29.1`).
583
+ 3. `.github/workflows/publish.yml`이 태그-패키지 버전 일치, 타입 검사, 빌드, 패키지 dry-run을 확인한 뒤 npm에 배포한다.
571
584
 
572
- `pnpm publish 123456`은 pnpm이 `123456`을 OTP가 아닌 폴더 경로로 해석하므로 사용하지 않는다.
585
+ npm 패키지 설정의 Trusted Publisher는 GitHub 저장소 `fkkmemi/memi-board`, workflow `publish.yml`로 등록한다. GitHub Environment `npm-publish`에 승인자를 지정하면 Release 발행 뒤 실제 npm 배포 전에 한 번 더 승인할 수 있다.
573
586
 
574
587
  ## 버전 · 라이선스
575
588
 
package/dist/index.js CHANGED
@@ -658,12 +658,12 @@ function mt(t) {
658
658
  return s(l())?.listView === "image";
659
659
  }
660
660
  function _(e) {
661
- let t = g();
662
- if (!Qe(e.content)) throw Error(t ? "내용을 입력해 주세요." : "본문에 글자를 입력해 주세요. 이미지나 첨부만으로는 게시할 수 없습니다.");
661
+ let t = g(), n = Qe(e.content), r = $e(e.content, e.attachments);
663
662
  if (t) {
664
- if (!$e(e.content, e.attachments)) throw Error("사진을 올려 주세요.");
663
+ if (!r) throw Error("사진을 올려 주세요.");
665
664
  return;
666
665
  }
666
+ if (!n && !r) throw Error("본문에 글자를 입력하거나 이미지를 첨부해 주세요.");
667
667
  if (!e.title?.trim()) throw Error("제목을 입력해 주세요.");
668
668
  }
669
669
  function y() {
@@ -1724,6 +1724,22 @@ function Wt(e) {
1724
1724
  //#endregion
1725
1725
  //#region src/data/versionHistory.ts
1726
1726
  var Gt = [
1727
+ {
1728
+ version: "0.30.0",
1729
+ date: "2026-08-13",
1730
+ highlights: [
1731
+ "본문 글 없이 사진만 첨부해도 글을 올릴 수 있어요",
1732
+ "목록은 게시판 설정에서 정한 보기 방식을 사용하고, 테스트용 보기 전환 버튼은 숨겼어요",
1733
+ "글쓰기 중 사진 업로드 권한과 Storage 버킷 설정 안내를 보강했어요"
1734
+ ],
1735
+ note: "이미지 업로드가 거부되면 호스트의 Storage 규칙과 firebase.json 버킷 설정을 최신 예시로 확인해 주세요."
1736
+ },
1737
+ {
1738
+ version: "0.29.1",
1739
+ date: "2026-08-13",
1740
+ highlights: ["프로필 이름이나 사진을 바꾸면 예전에 작성한 글과 댓글의 작성자 정보도 함께 바뀌어요", "나를 대상으로 작성된 답글의 표시 이름도 새 프로필 이름으로 맞춰져요"],
1741
+ note: "이 기능을 쓰려면 호스트가 Firestore 보안 규칙을 최신 예시로 갱신해야 해요."
1742
+ },
1727
1743
  {
1728
1744
  version: "0.27.0",
1729
1745
  date: "2026-08-12",