yanachuwan9sm / til

Today's I Learned for me.
0 stars 0 forks source link

FullStack Discord Clone #2

Closed yanachuwan9sm closed 10 months ago

yanachuwan9sm commented 11 months ago

Next.js 13(App Router)、Socket.io、Prisma、Tailwind、MySQL をさらっ〜と手で動かして学ぶ。

video: https://www.youtube.com/watch?v=ZbX4Ok9YX94&t=2112s source code: https://github.com/AntonioErdeljac/next13-discord-clone

yanachuwan9sm commented 11 months ago

進捗memo

Date 進捗 メモ
10/6 1:06:00min 途中から Prisma 、データモデリングに寄り道して進捗遅め
10/11 1:39:00min
10/12 1:44:00min ほぼほぼ react-hook-form や デザインパターンに寄り道
10/13 1:48:00min uploadthing とは? どんな仕組みなのか?で死ぬほど寄り道
10/20 2:32:00min uploadthing の仕組みをひたすらソースコード見ながら理解してみる
10/24 2:46:00min 単純にそんなに進めてない
10/25 3:26:00min prisma少し復習しないと忘れてるかも。3:19:00辺りの型定義の必要性がわかってないかも。
10/26 3:43:00min とりあえず0.5hぐらい進める。
10/31 4:53:00min
yanachuwan9sm commented 10 months ago

revalidateTagserver actions 使うことで UPDATE処理はAPI介さなくても出来る、、!? 原点回帰感ある。→ いたるところでこの操作を自由に行うと、操作に対してどこが更新されるのかが追えなくなる可能性はあるな。

https://nextjs.org/docs/app/building-your-application/data-fetching/forms-and-mutations#custom-invocation-using-starttransition

https://zenn.dev/cybozu_frontend/articles/server-actions-and-revalidate

https://azukiazusa.dev/blog/nextjs-server-action/

https://zenn.dev/rorisutarou/articles/3ee144c4ab4748

yanachuwan9sm commented 10 months ago

T3 Stack の開発者が作ったストレージサービス「uploadthing 」(初耳)

https://github.com/pingdotgg/uploadthing/blob/main/assets/Diagram.png

FileRouterを使用してNext.js APIルート(/api/uploadthing)を作成 👉 利用場面に応じて(認証が必要 or ファイルサイズ制限)エンドポイントを変えれるのいいね。

https://docs.uploadthing.com/api-reference/server#file-routes

// @/app/api/uploadthing/core.ts

import { createUploadthing, type FileRouter } from "uploadthing/next";

const f = createUploadthing();

const auth = (req: Request) => ({ id: "fakeId" }); // Fake auth function

export const ourFileRouter = {
  // 独自のファイルルートを定義できる。
  // 独自の認証を挟む事もできる(clerkやらnextauthやら)
  profilePicture: f(["image"]).middleware(() => {}).onUploadComplete(async ()=> {}),
  mediaPost: f(["image", "pdf", "video"]).middleware(() => {}).onUploadComplete(async ()=> {}),
} satisfies FileRouter;

export type OurFileRouter = typeof ourFileRouter;

仕組み

image

  1. フロント(Next.js) はブラウザからファイル(typeとname)をアップロードをカスタムサーバー(API Routes)にリクエスト
  2. カスタムサーバーは認証(middlewareの部分)を行い、PASSした場合にサーバー(uploadthing)にリクエスト
  3. 署名付きURLを Amazon S3にリクエスト & メタデータとコールバックURLをDB(uploadthing)に保存する
  4. サーバーはsdkから署名付きURLを受け取り、カスタムサーバーを通じて、フロントにわたす。
  5. フロントはもらったURLで直接ファイルをアップロード(PUT)する。
  6. Amazon S3 トリガー(FileKey)から Lambda 関数を呼び出す
  7. Lambda 関数 は uploadthing の webhook を呼び出す
  8. FileKeyをもとに、DB(uploadthing)からメタデータとコールバックURLを取得
  9. サーバーはカスタムサーバーのwebhookをメタデータとともに呼び出し、"onUploadComplete "をトリガーする。(ここ) 👉 サーバー側のWebhookをカスタムサーバーで受信してる認識でOK?

普通に画像アップロード機能を入れるとなると、下記を全て自分でやらないといけないから、 サクッと画像アップロード機能を作りたい!という場合には良いサービスかもしれない。 (ただアップロードされた画像はURLを知っていれば誰でも閲覧する事が出来るようになってる。要改善。)

カスタムサーバー(memo)

https://github.com/pingdotgg/uploadthing/blob/main/packages/uploadthing/src/next.ts

独自のファイルルートをどこで判定しているか? -> https://github.com/pingdotgg/uploadthing/blob/main/packages/uploadthing/src/internal/handler.ts#L146-L147

yanachuwan9sm commented 10 months ago

Tailwind CSS やら cva をうま〜く使うには? 以下のボイラープレートがめちゃめちゃ参考になる。

https://github.com/shadcn-ui/taxonomy

yanachuwan9sm commented 10 months ago

モーダル実装が参考になる。

// use-modal-store.ts : 開閉に関する状態と処理はカスタムhook化
export type ModalType = 'createServer' | 'invite';

interface ModalStore {
  type: ModalType | null;
  isOpen: boolean;
  onOpen: (type: ModalType, data?: ModalData) => void;
  onClose: () => void;
}

export const useModal = create<ModalStore>((set) => ({
  type: null,
  isOpen: false,
  onOpen: (type, data = {}) => set({ isOpen: true, type, data }),
  onClose: () => set({ type: null, isOpen: false }),
}));

// modal-provider.tsx : モーダルコンポーネントを一元管理するproviderコンポーネント
export const ModalProvider = () => {
  const [isMounted, setIsMounted] = useState(false);

  useEffect(() => {
    setIsMounted(true);
  }, []);

  if (!isMounted) return null;

  return (
    <>
      <CreateServerModal />
      <InviteModal />
    </>
  );
};

// create-server-modal.tsx / 「サーバー作成」の役割を持つモーダルコンポーネント
export const CreateServerModal = () => {
  const { isOpen, onClose, type } = useModal();

  const isModalOpen = isOpen && type === 'createServer';

  // 省略

  return (
    <Dialog open={isModalOpen} onOpenChange={handleClose}>
      <DialogContent className="bg-white text-black p-0 overflow-hidden">
  ・・・・
      </DialogContent>
    </Dialog>
  );
};

// 呼び出し元
xport const NavigationAction = () => {
  const { onOpen } = useModal();
  return (
    <div>
      <ActionTooltip side="right" align="center" label="Add a server">
        <button
          className="group flex items-center"
          onClick={() => onOpen('createServer')}
        >
          Button Click
        </button>
      </ActionTooltip>
    </div>
  );
};

// Root
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <ClerkProvider>
      <html lang="en" suppressHydrationWarning>
        <body className={cn(inter.className, 'bg-while dark:bg-[#313338]')}>
          <NextThemeProvider>
            <ModalProvider />
            {children}
          </NextThemeProvider>
        </body>
      </html>
    </ClerkProvider>
  );
}
yanachuwan9sm commented 10 months ago

この記事でも Recoil ではあるけど似たような実装をしてる