본문 바로가기
Clone Coding

Jira-clone - 서버 쿼리 리팩토링

by zzuny-code 2025. 10. 11.
반응형

 파일구조

src/features/auth/action.ts -> src/features/auth/queries.ts 로 변경

 

  • actions.ts: 서버 액션 (사용자 상호작용, form submission)
  • queries.ts: 데이터 조회 (read-only 서버 함수)

 

src/
├── lib/
│   └── appwrite.ts          # ⭐ 클라이언트 팩토리 함수
├── features/
│   ├── auth/
│   │   └── (action.ts 변경) queries.ts       # getCurrent()
│   ├── workspaces/
│   │   └── (action.ts 변경) queries.ts       # getWorkspaces(), getWorkspace()  특정 워크스페이스 조회 (권한 확인)
│   └── members/
│       └── utils.ts         # getMember()

 

 

src/lib/appwrite.ts

  • 재사용 가능한 클라이언트 초기화 함수 createSessionClient() 추가
  • Getter함수는 실제 호출할 때만 생성되서 메모리를 절약할 수 있다.
export async function createSessionClient(){
  const client = new Client()
    .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!)
    .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT!)

  const session = await cookies().get(AUTH_COOKIE);

  if(!session || !session.value){
    throw new Error("Unauthorized")
  }

  client.setSession(session.value);

  return{
    get account() {
      return new Account(client);
    },
    get databases() {
      return new Databases(client)
    }
  }
}

 

 

src/features/auth/queries.ts

"use server"

import { createSessionClient } from '@/lib/appwrite'

export const getCurrent = async () => {
    try{
        // const client = new Client()
        //     .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!)
        //     .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT!)

        // const session = await cookies().get(AUTH_COOKIE)

        // if(!session) return null

        // client.setSession(session.value);
        // const account = new Account(client);

        // createSessionClient: 일반 조회 (getCurrent, getWorkspaces)
        const {account} = await createSessionClient()

        return await account.get();
    }catch{
        return null
    }
}

 

 

src/features/workspaces/queries.ts

"use server"

import { cookies } from 'next/headers'
import { Client, Databases, Query, Account } from 'node-appwrite'

import { AUTH_COOKIE } from '@/features/auth/constants'
import { DATABASE_ID, MEMBERS_ID, WORKSPACES_ID } from '@/config'
import { Workspace } from './type'
import { getMember } from '../members/utils'
import { createSessionClient } from '@/lib/appwrite'

export const getWorkspaces = async () => {
    try{
        // const client = new Client()
        //     .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!)
        //     .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT!)

        // const session = await cookies().get(AUTH_COOKIE)

        // if(!session) return {documents: [], total:0}

        // client.setSession(session.value);
        // const databases = new Databases(client);
        // const account = new Account(client);

        const { databases, account } = await createSessionClient();

        const user = await account.get();

        // getMember 유틸로 권한 확인
        const members = await databases.listDocuments(
            DATABASE_ID,
            MEMBERS_ID,
            [Query.equal("userId", user.$id)]
        );

        if(members.total === 0){
            return {documents: [], total:0}
        }

        const workspaceIds = members.documents.map((member) => member.workspaceId)

        // 워크스페이스 정보 조회
        const workspace = await databases.listDocuments(
            DATABASE_ID,
            WORKSPACES_ID,
            [
                Query.orderDesc("$createdAt"),
                Query.contains("$id", workspaceIds)
            ]
        );

        return workspace

    }catch{
        return {documents: [], total:0}
    }
}

interface GetWorkspaceProps{
    workspaceId:string
}

export const getWorkspace = async ({workspaceId}:GetWorkspaceProps) => {
    try{
        // const client = new Client()
        //     .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!)
        //     .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT!)

        // const session = await cookies().get(AUTH_COOKIE)

        // if(!session) return null

        // client.setSession(session.value);
        // const databases = new Databases(client);
        // const account = new Account(client);

        const {databases, account} = await createSessionClient();

        const user = await account.get();

        const member = await getMember({
            databases,
            userId:user.$id,
            workspaceId
        })

        if(!member) return null

        const workspace = await databases.getDocument<Workspace>(
            DATABASE_ID,
            WORKSPACES_ID,
            workspaceId
        );

        return workspace

    }catch{
        return null
    }
}

 

핵심 

  • 팩토리 함수 패턴: 반복되는 초기화 코드를 한 곳에 집중
  • 파일 분리: actions.ts → queries.ts 로 의도 명확화
  • Getter 패턴: 필요한 것만 필요할 때 생성
  • 에러 처리: 중앙화된 함수에서 일관되게 처리
  • 확장성: 새로운 Appwrite 서비스 추가 시 appwrite.ts만 수정
  • 보안: "server-only" import로 클라이언트 실행 방지
반응형