imprun Platform

MongoDB 사용

클라우드 함수에서 MongoDB 데이터베이스 사용하기

MongoDB 사용

imprun 함수에서 MongoDB 데이터베이스를 사용하는 방법을 소개합니다.

Cloud SDK를 통한 데이터베이스 접근

imprun은 MongoDB 데이터베이스에 접근할 수 있는 두 가지 방법을 제공합니다:

  1. cloud.database() - 고수준 Database API (권장)
  2. cloud.mongo - 원시 MongoDB 드라이버

Database API 사용 (권장)

cloud.database()를 사용하면 간편하게 데이터베이스를 조작할 수 있습니다:

import cloud from '@imprun/sdk'
import type { FunctionContext } from '@imprun/sdk'

export default async function (ctx: FunctionContext) {
  const db = cloud.database()

  // 사용자 추가
  const result = await db.collection('users').add({
    name: '홍길동',
    email: 'hong@example.com',
    createdAt: new Date()
  })

  return {
    success: true,
    id: result.id
  }
}

문서 조회

export default async function (ctx: FunctionContext) {
  const db = cloud.database()

  // 단일 문서 조회
  const user = await db.collection('users')
    .where({ email: 'hong@example.com' })
    .getOne()

  // 여러 문서 조회
  const users = await db.collection('users')
    .where({ active: true })
    .get()

  return {
    user,
    users: users.data,
    total: users.total
  }
}

문서 업데이트

export default async function (ctx: FunctionContext) {
  const db = cloud.database()
  const { userId, name } = ctx.body

  // 단일 문서 업데이트
  const result = await db.collection('users')
    .where({ _id: userId })
    .update({
      name,
      updatedAt: new Date()
    })

  return {
    success: true,
    updated: result.updated
  }
}

문서 삭제

export default async function (ctx: FunctionContext) {
  const db = cloud.database()
  const { userId } = ctx.body

  // 단일 문서 삭제
  const result = await db.collection('users')
    .where({ _id: userId })
    .remove()

  return {
    success: true,
    deleted: result.deleted
  }
}

MongoDB 드라이버 직접 사용

더 세밀한 제어가 필요한 경우 MongoDB 드라이버를 직접 사용할 수 있습니다:

import cloud from '@imprun/sdk'
import type { FunctionContext } from '@imprun/sdk'

export default async function (ctx: FunctionContext) {
  const db = cloud.mongo.db

  // 컬렉션 가져오기
  const users = db.collection('users')

  // 문서 삽입
  const result = await users.insertOne({
    name: '홍길동',
    email: 'hong@example.com',
    age: 30,
    createdAt: new Date()
  })

  return {
    success: true,
    insertedId: result.insertedId
  }
}

Find 쿼리

export default async function (ctx: FunctionContext) {
  const db = cloud.mongo.db
  const users = db.collection('users')

  // 모든 사용자 조회
  const allUsers = await users.find({}).toArray()

  // 조건부 조회
  const activeUsers = await users.find({
    active: true,
    age: { $gte: 18 }
  }).toArray()

  // 정렬 및 제한
  const recentUsers = await users
    .find({})
    .sort({ createdAt: -1 })
    .limit(10)
    .toArray()

  return {
    total: allUsers.length,
    active: activeUsers.length,
    recent: recentUsers
  }
}

Aggregation

export default async function (ctx: FunctionContext) {
  const db = cloud.mongo.db
  const orders = db.collection('orders')

  // 집계 파이프라인
  const stats = await orders.aggregate([
    {
      $match: {
        status: 'completed',
        createdAt: { $gte: new Date('2025-01-01') }
      }
    },
    {
      $group: {
        _id: '$userId',
        totalSpent: { $sum: '$amount' },
        orderCount: { $sum: 1 }
      }
    },
    {
      $sort: { totalSpent: -1 }
    },
    {
      $limit: 10
    }
  ]).toArray()

  return {
    topCustomers: stats
  }
}

Update 연산

export default async function (ctx: FunctionContext) {
  const db = cloud.mongo.db
  const users = db.collection('users')

  // 단일 문서 업데이트
  await users.updateOne(
    { email: 'hong@example.com' },
    {
      $set: { lastLogin: new Date() },
      $inc: { loginCount: 1 }
    }
  )

  // 여러 문서 업데이트
  await users.updateMany(
    { active: false },
    { $set: { status: 'inactive' } }
  )

  // Upsert (없으면 생성)
  await users.updateOne(
    { email: 'new@example.com' },
    {
      $set: {
        name: '새 사용자',
        createdAt: new Date()
      }
    },
    { upsert: true }
  )

  return { success: true }
}

Delete 연산

export default async function (ctx: FunctionContext) {
  const db = cloud.mongo.db
  const users = db.collection('users')

  // 단일 문서 삭제
  await users.deleteOne({ email: 'hong@example.com' })

  // 여러 문서 삭제
  await users.deleteMany({ active: false })

  return { success: true }
}

인덱스 관리

export default async function (ctx: FunctionContext) {
  const db = cloud.mongo.db
  const users = db.collection('users')

  // 단일 필드 인덱스 생성
  await users.createIndex({ email: 1 }, { unique: true })

  // 복합 인덱스 생성
  await users.createIndex({ name: 1, createdAt: -1 })

  // 텍스트 인덱스
  await users.createIndex({ bio: 'text' })

  // 인덱스 목록 조회
  const indexes = await users.listIndexes().toArray()

  return { indexes }
}

트랜잭션

여러 작업을 원자적으로 실행할 수 있습니다:

export default async function (ctx: FunctionContext) {
  const client = cloud.mongo.client
  const db = cloud.mongo.db

  const session = client.startSession()

  try {
    await session.withTransaction(async () => {
      const users = db.collection('users')
      const orders = db.collection('orders')

      // 사용자 포인트 차감
      await users.updateOne(
        { _id: ctx.body.userId },
        { $inc: { points: -100 } },
        { session }
      )

      // 주문 생성
      await orders.insertOne(
        {
          userId: ctx.body.userId,
          amount: 100,
          status: 'pending',
          createdAt: new Date()
        },
        { session }
      )
    })

    return { success: true }

  } catch (error) {
    return {
      success: false,
      error: error.message
    }
  } finally {
    await session.endSession()
  }
}

페이지네이션

export default async function (ctx: FunctionContext) {
  const db = cloud.mongo.db
  const users = db.collection('users')

  const page = parseInt(ctx.query.page || '1')
  const limit = parseInt(ctx.query.limit || '20')
  const skip = (page - 1) * limit

  const [data, total] = await Promise.all([
    users.find({})
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(limit)
      .toArray(),
    users.countDocuments({})
  ])

  return {
    data,
    pagination: {
      page,
      limit,
      total,
      totalPages: Math.ceil(total / limit)
    }
  }
}
export default async function (ctx: FunctionContext) {
  const db = cloud.mongo.db
  const posts = db.collection('posts')

  // 텍스트 인덱스 생성 (최초 1회만)
  await posts.createIndex({ title: 'text', content: 'text' })

  // 텍스트 검색
  const searchQuery = ctx.query.q || ''
  const results = await posts.find({
    $text: { $search: searchQuery }
  }).toArray()

  return {
    query: searchQuery,
    results
  }
}

Change Streams (실시간 변경 감지)

export default async function (ctx: FunctionContext) {
  const db = cloud.mongo.db
  const users = db.collection('users')

  // Change Stream 생성
  const changeStream = users.watch()

  changeStream.on('change', (change) => {
    console.log('데이터 변경 감지:', change)

    // WebSocket으로 클라이언트에 알림
    cloud.sockets.forEach((socket) => {
      if (socket.readyState === 1) {
        socket.send(JSON.stringify({
          type: 'db-change',
          data: change
        }))
      }
    })
  })

  return { message: 'Change Stream 시작됨' }
}

환경 변수로 DB URI 관리

데이터베이스 연결 URI는 환경 변수에서 가져옵니다:

export default async function (ctx: FunctionContext) {
  // DB URI는 자동으로 설정됨
  const dbUri = process.env.DB_URI

  console.log('DB URI:', dbUri)

  return {
    connected: !!cloud.mongo.db,
    uri: dbUri?.replace(/\/\/(.+):(.+)@/, '//$1:****@') // 비밀번호 숨김
  }
}

MongoDB에 대한 더 자세한 내용은 MongoDB 공식 문서를 참고하세요.

다음 단계