이 튜토리얼에서는 웹 소켓을 사용하여 채팅 애플리케이션을 구축해 보겠습니다. 웹 소켓은 실시간 데이터 전송이 필요한 애플리케이션을 구축하려는 경우 정말 유용합니다.
이 튜토리얼이 끝나면 자체 소켓 서버를 설정하고, 실시간으로 메시지를 보내고 받고, Redis에 데이터를 저장하고, 렌더링 및 Google Cloud Run에 애플리케이션을 배포할 수 있게 됩니다.
채팅 애플리케이션을 구축할 예정입니다. 간략하게 설명하기 위해 서버만 설정하겠습니다. 자신만의 프런트엔드 프레임워크를 사용하여 따라할 수 있습니다.
이 채팅 애플리케이션에는 방이 있으며 사용자는 방에 참여하여 채팅을 시작할 수 있습니다. 모든 것을 단순하게 유지하기 위해 사용자 이름이 고유하지 않다고 가정합니다. 그러나 각 방에는 특정 사용자 이름을 가진 사용자가 한 명만 있을 수 있습니다.
먼저 필수 종속성을 설치해야 합니다.
npm i express cors socket.io -D @types/node
우리는 소켓 서버를 설정하기 위해 http 모듈을 사용할 것입니다. 우리 앱이 터미널에서 실행될 것이기 때문에 모든 출처를 허용해야 합니다.
import express from "express"; import cors from "cors" import { Server } from "socket.io"; import { createServer } from "http" const app = express(); const server = createServer(app); // create a socket server. const io = new Server(server, { cors: { origin: "*", credentials: true, } }); // listen to connections errors io.engine.on("connection_error", console.log) app.use(cors()) const PORT = 3000; server.listen(PORT, () => console.log(`Server running on port ${PORT}`));
우리는 Redis를 사용하여 회의실 및 사용자 정보와 함께 메시지를 저장할 것입니다. upstash redis(무료)를 사용할 수 있습니다. Upstash 대시보드에서 새 Redis 인스턴스를 생성합니다. 생성 후에는 Redis 인스턴스에 연결하는 데 사용할 수 있는 redis URL을 받게 됩니다.
원하는 Redis 클라이언트를 설치하세요. 저는 ioredis를 사용할 예정입니다.
npm i ioredis
다음으로 Redis 클라이언트를 초기화하고 얻은 연결 URL을 사용하여 Redis 서버에 연결하겠습니다.
/** /src/index.ts */ import { Redis } from "ioredis" if (!process.env.REDIS_URL) throw new Error("REDIS_URL env variable is not set"); const redis = new Redis(process.env.REDIS_URL); // listen to connection events. redis.on("connect", () => console.log("Redis connected")) redis.on("error", console.log)
사용자는 방을 만들거나 기존 방에 참여할 수 있습니다. 객실은 고유한 객실 ID로 식별됩니다. 각 회원은 전체가 아닌 방 내에서 고유한 사용자 이름을 갖습니다.
Redis 세트에 룸 ID를 저장하여 서버의 모든 활성 룸을 추적할 수 있습니다.
우리의 목적에 따라 사용자 이름은 방 내에서만 고유합니다. 그래서 우리는 방 ID와 함께 세트에 저장합니다. 이렇게 하면 룸 ID와 회원 ID의 조합이 전 세계적으로 고유하게 됩니다.
방 생성을 위한 소켓 이벤트를 설정할 수 있습니다. 방을 생성할 때 생성을 요청한 회원도 방에 추가합니다.
io.on("connection", () => { // ... socket.on("create:room", async (message) => { console.log("create:room", message) const doesRoomExist = await redis.sismember("rooms", message.roomId) if (doesRoomExist === 1) return socket.emit("error", { message: "Room already exist."}) const roomStatus = await redis.sadd("rooms", message.roomId) const memStatus = await redis.sadd("members", message.roomId + "::" + message.username) if (roomStatus === 0 || memStatus === 0) return socket.emit("error", { message: "Room creation failed." }) socket.join(message.roomId) io.sockets.in(message.roomId).emit("create:room:success", message) io.sockets.in(message.roomId).emit("add:member:success", message) }) }
기존 방에 새 구성원을 추가하려면 먼저 해당 방에 해당 구성원이 이미 존재하는지 확인해야 합니다.
io.on("connection", () => { // ... socket.on("add:member", async (message) => { console.log("add:member", message) const doesRoomExist = await redis.sismember("rooms", message.roomId) if (doesRoomExist === 0) return socket.emit("error", { message: "Room does not exist." }) const doesMemExist = await redis.sismember("members", message.roomId + "::" + message.username) if (doesMemExist === 1) return socket.emit("error", { message: "Username already exists, please choose another username." }) const memStatus = await redis.sadd("members", message.roomId + "::" + message.username) if (memStatus === 0) return socket.emit("error", { message: "User creation failed." }) socket.join(message.roomId) io.sockets.in(message.roomId).emit("add:member:success", message) }) socket.on("remove:member", async (message) => { console.log("remove:member", message) const doesRoomExist = await redis.sismember("rooms", message.roomId) if (doesRoomExist === 0) return socket.emit("error", { message: "Room does not exist." }) await redis.srem("members", message.roomId + "::" + message.username) socket.leave(message.roomId) io.sockets.in(message.roomId).emit("remove:member:success", message) }) }
마지막으로 채팅 이벤트를 생성합니다.
io.on("connection", () => { socket.on("create:chat", (message) => { console.log("create:chat", message) redis.lpush("chat::" + message.roomId, message.username + "::" + message.message) io.sockets.in(message.roomId).emit("create:chat:success", message) }) }
소켓 서버에는 지속적인 연결이 필요하며 서버리스 환경에서는 작동하지 않습니다. 따라서 vercel에서는 소켓 서버를 배포할 수 없습니다.
Render, fly.io, Google Cloud Run 등 다양한 위치에 배포할 수 있습니다.
렌더링 시 배포가 간단합니다. dockerfile이 있으면 해당 dockerfile에서 프로젝트를 자동으로 빌드합니다. Render에는 무료 등급이 있지만 무료 등급에서는 콜드 스타트가 가능하다는 점을 명심하세요.
여기 내 dockerfile이 있습니다.
# syntax=docker/dockerfile:1 ARG NODE_VERSION=20.13.1 ARG PNPM_VERSION=9.4.0 FROM node:${NODE_VERSION}-bookworm AS base ## set shell to bash SHELL [ "/usr/bin/bash", "-c" ] WORKDIR /usr/src/app ## install pnpm. RUN --mount=type=cache,target=/root/.npm \ npm install -g pnpm@${PNPM_VERSION} # ------------ FROM base AS deps # Download dependencies as a separate step to take advantage of Docker's caching. # Leverage a cache mount to /root/.local/share/pnpm/store to speed up subsequent builds. # Leverage bind mounts to package.json and pnpm-lock.yaml to avoid having to copy them # into this layer. RUN --mount=type=bind,source=package.json,target=package.json \ --mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \ --mount=type=cache,target=/root/.local/share/pnpm/store \ pnpm install --prod --frozen-lockfile # ----------- FROM deps AS build ## downloading dev dependencies. RUN --mount=type=bind,source=package.json,target=package.json \ --mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \ --mount=type=cache,target=/root/.local/share/pnpm/store \ pnpm install --frozen-lockfile COPY . . RUN pnpm run build # ------------- FROM base AS final ENV NODE_ENV=production USER node COPY package.json . # Copy the production dependencies from the deps stage and also # the built application from the build stage into the image. COPY --from=deps /usr/src/app/node_modules ./node_modules COPY --from=build /usr/src/app/dist ./dist EXPOSE 3000 ENTRYPOINT [ "pnpm" ] CMD ["run", "start"]
콜드 스타트를 방지하고 렌더링할 수 있는 무료 대안을 원한다면 Google Cloud Run을 사용해야 합니다. Cloud Run에 배포하는 단계는 이 문서의 범위를 벗어나지만 수행해야 할 작업의 간단한 목록은 다음과 같습니다.
아래 제공된 dockerfile에서 docker 이미지를 빌드하세요.
Google Artifact Registry 서비스를 사용하여 아티팩트 저장소를 만듭니다.
Docker 이미지 이름을
이미지를 유물 저장소에 푸시하세요.
Google Cloud Run에 이미지를 배포합니다. 콜드 스타트를 방지하려면 최소 활성 인스턴스를 1로 설정하세요.
이번 튜토리얼은 여기까지입니다.
읽어주셔서 감사합니다 ❣️
위 내용은 Socket.io 및 Redis를 사용하여 채팅 애플리케이션을 구축하고 배포합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!