diff --git a/Makefile b/Makefile
index c6ec458c08fa07bbabc476780adc5caeb7ac3aee..168b34dcaf3e900bf20fab262418fee078578e2d 100644
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@ down-frontend:
 .PHONY: run-apiserver
 run-apiserver:
 	docker-compose build apiserver
-	docker-compose up -d apiserver mysql minio
+	docker-compose up -d apiserver mysql minio redis
 
 .PHONY: logs-apiserver
 logs-apiserver:
diff --git a/docker-compose.yml b/docker-compose.yml
index eae925417ed08a2d0727fff6b943a78849815a0c..1fe7682a6487419fb3ae83bf34c36527424e9a4b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -8,6 +8,7 @@ services:
       MYSQL_PASSWORD: localadmin
     ports:
       - "3307:3306"
+    command: ["mysqld", "--require-secure-transport=OFF"]
     volumes:
       - ./webapp/backend/ddl:/docker-entrypoint-initdb.d/
       - ./mysql:/var/lib/mysql:delegated
@@ -24,6 +25,11 @@ services:
       - ./minio/data:/data
     command: server --address ":9000" --console-address ":9001" /data
 
+  redis:
+    image: redis
+    ports:
+      - "6380:6379"
+
   frontend:
     image: frontend
     build:
@@ -44,7 +50,8 @@ services:
       MYSQL_PORT: 3306
       MYSQL_USER: admin
       MYSQL_PASSWORD: localadmin
-
+      REDIS_HOST: redis
+      REDIS_PORT: 6379
     ports:
       - "8180:8080"
     volumes:
diff --git a/webapp/backend/apiserver/controllers/crewController.js b/webapp/backend/apiserver/controllers/crewController.js
index 2bbf38a2418cf70106d47ae2441a93cef7efbd3d..2d93c922a6aaa57ec59a13223117b883e062f285 100644
--- a/webapp/backend/apiserver/controllers/crewController.js
+++ b/webapp/backend/apiserver/controllers/crewController.js
@@ -1,12 +1,15 @@
 const Crew = require('../models/Crew.js');
 const Event = require('../models/Event.js');
 const User = require('../models/User.js');
-const UserCrew = require('../models/UserCrew.js'); 
-const UserEvent = require('../models/UserEvent.js'); 
+const UserCrew = require('../models/UserCrew.js');
+const UserEvent = require('../models/UserEvent.js');
+const { redisClient, capCheckLuaScript } = require('../datastore/redis.js');
 
 // 한 페이지당 기본 아이템 수
 const itemsPerPage = 10;
 
+const countKeyPattern = 'crew:${crewID}:member_count';
+
 // 크루 생성 컨트롤러
 exports.createCrew = async (req, res) => {
   try {
@@ -46,38 +49,61 @@ exports.createCrew = async (req, res) => {
   }
 };
 
-
 // 크루 가입 컨트롤러
 exports.joinCrew = async (req, res) => {
-  try {
-    const { crewID } = req.params;
-    const { role } = req.body;
+  const {crewID} = req.params;
+  const {role} = req.body;
 
-    const userID = req.user.userID;
+  const userID = req.user.userID;
 
-    if (!crewID) {
-      return res.status(400).json({ error: '필수 데이터가 누락되었습니다.' });
-    }
+  if (!crewID) {
+    return res.status(400).json({error: '필수 데이터가 누락되었습니다.'});
+  }
 
-    const crew = await Crew.findByPk(crewID);
-    if (!crew) {
-      return res.status(404).json({ error: '해당 크루가 존재하지 않습니다.' });
-    }
+  const crew = await Crew.findByPk(crewID);
+  if (!crew) {
+    return res.status(404).json({error: '해당 크루가 존재하지 않습니다.'});
+  }
 
-    const userCrew = await UserCrew.create({
-      crewID,
-      userID,
-      role: role || 'General',
-    });
+  const existingUserCrew = await UserCrew.findOne({where: {crewID, userID}});
+  if (existingUserCrew) {
+    return res.status(409).json({error: '이미 해당 크루에 가입되어 있습니다.'});
+  }
 
-    res.status(201).json({
-      crewID: userCrew.crewID,
-      userID: userCrew.userID,
-      role: userCrew.role,
-    });
+  const capacity = parseInt(crew.capacity, 10);
+
+  try {
+    const result = await redisClient.sendCommand([
+      'EVAL',
+      capCheckLuaScript,
+      '1',
+      countKeyPattern,
+      capacity.toString()
+    ]);
+
+    // 인원이 초과하지 않은 경우, 가입 진행.
+    if (result === 1) {
+      const newUserCrew = await UserCrew.create({
+        crewID,
+        userID,
+        role: role || 'General',
+      });
+
+      return res.status(201).json({
+        crewID: newUserCrew.crewID,
+        userID: newUserCrew.userID,
+        role: newUserCrew.role,
+      });
+    } else {
+      return res.status(409).json({error: '크루의 최대 인원 수를 초과했습니다.'});
+    }
   } catch (error) {
+    // 가입 실패시 가입 카운트를 감소시킴.
+    await redisClient.decr(countKeyPattern);
+
     console.error('크루 가입 중 오류:', error);
-    res.status(500).json({ error: '크루 가입 중 오류가 발생했습니다.' });
+
+    res.status(500).json({error: '크루 가입 중 오류가 발생했습니다.'});
   }
 };
 
@@ -103,6 +129,8 @@ exports.leaveCrew = async (req, res) => {
     }
 
     await userCrew.destroy();
+    // 가입 인원 카운트 감소
+    await redisClient.decr(countKeyPattern);
 
     res.status(200).json({});
   } catch (error) {
@@ -123,9 +151,14 @@ exports.getCrew = async (req, res) => {
       return res.status(404).json({ error: '해당 크루가 존재하지 않습니다.' });
     }
 
-    // UserCrew에서 해당 크루에 가입한 인원 수 계산
-    const currentMemberCount = await UserCrew.count({
-      where: { crewID },
+    const currentMemberCount = redisClient.get(countKeyPattern).then((result) => {
+        return parseInt(result, 10);
+    }).catch((error) => {
+        console.error('크루 가입 수 조회중 오류', error);
+
+        return UserCrew.count({
+          where: { crewID },
+        });
     });
 
     // 크루 데이터에 currentMemberCount 추가
diff --git a/webapp/backend/apiserver/controllers/eventController.js b/webapp/backend/apiserver/controllers/eventController.js
index e968d6a0c295605b600fbb76da6bcd919f353878..a12cbad451185f4a9d54d0f55b0d6d137f0684a0 100644
--- a/webapp/backend/apiserver/controllers/eventController.js
+++ b/webapp/backend/apiserver/controllers/eventController.js
@@ -2,11 +2,15 @@ const Event = require('../models/Event');
 const EventParticipants = require('../models/EventParticipants');
 const {Op} = require('sequelize');
 const moment = require('moment'); // 날짜 포맷팅 라이브러리
-const UserEvent = require('../models/UserEvent.js'); 
+const UserEvent = require('../models/UserEvent.js');
+const {redisClient, capCheckLuaScript} = require("../datastore/redis");
+const UserCrew = require("../models/UserCrew");
 
 // 한 페이지당 기본 아이템 수
 const itemsPerPage = 10;
 
+const countKeyPattern = 'event:${eventID}:participant_count';
+
 // 이벤트 목록 조회 (단일 엔드포인트)
 exports.getEvents = async (req, res) => {
     console.log('이벤트 목록 조회 컨트롤러 실행'); // 로그 확인용
@@ -82,45 +86,51 @@ exports.createEvent = async (req, res) => {
 // 특정 이벤트 조회 컨트롤러
 exports.getEventById = async (req, res) => {
     try {
-      const { eventID } = req.params;
-  
-      // 이벤트 정보 조회
-      const event = await Event.findByPk(eventID, {
-        attributes: [
-          'eventID',
-          'regionID',
-          'name',
-          'sportTypeId',
-          'eventDate',
-          'capacity',
-          'feeCondition',
-          'userID',
-          'createdDate',
-        ],
-      });
-  
-      if (!event) {
-        return res.status(404).json({ error: '해당 이벤트가 존재하지 않습니다.' });
-      }
-  
-      // 현재 참여한 인원 수 계산
-      const currentMemberCount = await UserEvent.count({
-        where: { eventID: event.eventID },
-      });
-  
-      // 이벤트 데이터에 현재 참여한 인원 수 추가
-      const response = {
-        ...event.toJSON(),
-        currentMemberCount, // 현재 참여한 인원 수
-      };
-  
-      res.status(200).json(response);
+        const {eventID} = req.params;
+
+        // 이벤트 정보 조회
+        const event = await Event.findByPk(eventID, {
+            attributes: [
+                'eventID',
+                'regionID',
+                'name',
+                'sportTypeId',
+                'eventDate',
+                'capacity',
+                'feeCondition',
+                'userID',
+                'createdDate',
+            ],
+        });
+
+        if (!event) {
+            return res.status(404).json({error: '해당 이벤트가 존재하지 않습니다.'});
+        }
+
+        // 현재 참여한 인원 수 계산
+        const currentMemberCount = redisClient.get(countKeyPattern).then((result) => {
+            return parseInt(result, 10);
+        }).catch((error) => {
+            console.error('이벤트 참여자 수 조회중 오류', error);
+
+            return UserEvent.count({
+                where: {eventID: event.eventID},
+            });
+        });
+
+        // 이벤트 데이터에 현재 참여한 인원 수 추가
+        const response = {
+            ...event.toJSON(),
+            currentMemberCount, // 현재 참여한 인원 수
+        };
+
+        res.status(200).json(response);
     } catch (error) {
-      console.error('이벤트 조회 중 오류:', error);
-      res.status(500).json({ error: '이벤트 조회 중 오류가 발생했습니다.' });
+        console.error('이벤트 조회 중 오류:', error);
+        res.status(500).json({error: '이벤트 조회 중 오류가 발생했습니다.'});
     }
-  };
-  
+};
+
 
 // 이벤트 수정 컨트롤러
 exports.updateEvent = async (req, res) => {
@@ -176,40 +186,54 @@ exports.deleteEvent = async (req, res) => {
 
 // 이벤트 참여 컨트롤러
 exports.participateEvent = async (req, res) => {
-    try {
-        const {eventID} = req.params;
+    const {eventID} = req.params;
 
-        const userID = req.user.userID;
+    const userID = req.user.userID;
 
-        if (!eventID || !userID) {
-            return res.status(400).json({error: '필수 데이터가 누락되었습니다.'});
-        }
-
-        const event = await Event.findByPk(eventID);
-        if (!event) {
-            return res.status(404).json({error: '해당 이벤트가 존재하지 않습니다.'});
-        }
+    if (!eventID || !userID) {
+        return res.status(400).json({error: '필수 데이터가 누락되었습니다.'});
+    }
 
-        const existingParticipant = await EventParticipants.findOne({where: {eventID, userID}});
-        if (existingParticipant) {
-            return res.status(409).json({error: '이미 이벤트에 참여 중입니다.'});
-        }
+    const event = await Event.findByPk(eventID);
+    if (!event) {
+        return res.status(404).json({error: '해당 이벤트가 존재하지 않습니다.'});
+    }
 
-        const participantCount = await EventParticipants.count({where: {eventID, status: 'attending'}});
-        if (event.capacity && participantCount >= event.capacity) {
-            return res.status(409).json({error: '이벤트 정원이 초과되었습니다.'});
-        }
+    const existingParticipant = await EventParticipants.findOne({where: {eventID, userID}});
+    if (existingParticipant) {
+        return res.status(409).json({error: '이미 이벤트에 참여 중입니다.'});
+    }
 
-        const newParticipant = await EventParticipants.create({
-            userID,
-            eventID,
-            participationDate: new Date(),
-            status: 'attending',
-        });
+    const capacity = parseInt(event.capacity, 10);
 
-        res.status(201).json(newParticipant);
+    try {
+        const result = await redisClient.sendCommand([
+            'EVAL',
+            capCheckLuaScript,
+            '1',
+            countKeyPattern,
+            capacity.toString()
+        ]);
+
+        // 인원이 초과하지 않은 경우, 참여 진행.
+        if (result === 1) {
+            const newParticipant = await EventParticipants.create({
+                userID,
+                eventID,
+                participationDate: new Date(),
+                status: 'attending',
+            });
+
+            res.status(201).json(newParticipant);
+        } else {
+            return res.status(409).json({error: '이벤트의 최대 참여 인원 수를 초과했습니다.'});
+        }
     } catch (error) {
+        // 참여 실패시 가입 카운트를 감소시킴.
+        await redisClient.decr(countKeyPattern);
+
         console.error('이벤트 참여 중 오류:', error);
+
         res.status(500).json({error: '이벤트 참여 중 오류가 발생했습니다.'});
     }
 };
@@ -241,6 +265,8 @@ exports.cancelParticipation = async (req, res) => {
         // 상태 변경 (참여 취소)
         participant.status = 'canceled';
         await participant.save();
+        // 참여 인원 카운트 감소
+        await redisClient.decr(countKeyPattern);
 
         res.status(200).json({
             message: '이벤트 참여가 성공적으로 취소되었습니다.',
diff --git a/webapp/backend/apiserver/controllers/userController.js b/webapp/backend/apiserver/controllers/userController.js
index 162bb7564ce90aca4b5d85550dfffc034ff20f3c..6cd7b7f444cf6503ed9bf38317a3ac1aa212473f 100644
--- a/webapp/backend/apiserver/controllers/userController.js
+++ b/webapp/backend/apiserver/controllers/userController.js
@@ -106,7 +106,7 @@ exports.signUp = async (req, res) => {
         } catch (error) {
             await tx.rollback();
             console.error('사용자 생성 중 오류:', error);
-            res.status(500).json({error: '사용자 생성 중 오류가 발생생했습니다.'});
+            res.status(500).json({error: '사용자 생성 중 오류가 발생했습니다.'});
         }
     } catch (error) {
         console.error('사용자 생성 중 오류:', error);
diff --git a/webapp/backend/apiserver/datastore/redis.js b/webapp/backend/apiserver/datastore/redis.js
new file mode 100644
index 0000000000000000000000000000000000000000..088e58d2549ca7006d09b540fae04436a8da29ca
--- /dev/null
+++ b/webapp/backend/apiserver/datastore/redis.js
@@ -0,0 +1,24 @@
+const redis = require('redis');
+const RedisConfig = require('../../config/redis');
+
+const redisClient = redis.createClient({
+    url: `redis://${RedisConfig.HOST}:${RedisConfig.PORT}`
+});
+
+redisClient.connect();
+
+// Lua 스크립트 정의
+exports.capCheckLuaScript = `
+local key = KEYS[1]
+local capacity = tonumber(ARGV[1])
+
+local current = tonumber(redis.call("GET", key) or "0")
+if current < capacity then
+  redis.call("INCR", key)
+  return 1
+else
+  return 0
+end
+`;
+
+exports.redisClient = redisClient;
diff --git a/webapp/backend/config/redis.js b/webapp/backend/config/redis.js
new file mode 100644
index 0000000000000000000000000000000000000000..a791de9788ec00ee044c6b776cb8b3dcf42ffde3
--- /dev/null
+++ b/webapp/backend/config/redis.js
@@ -0,0 +1,7 @@
+// NOTE: we do not consider the cluster for now.
+const RedisConfig = {
+    HOST: process.env.REDIS_HOST || 'localhost',
+    PORT: process.env.REDIS_PORT || '6380',
+}
+
+module.exports = RedisConfig;
\ No newline at end of file
diff --git a/webapp/backend/package-lock.json b/webapp/backend/package-lock.json
index ec114290230492995b3d3774d5bf18a81c5c5032..936ed4a6e4dbca62e3594e3dbe7e89aa15baa790 100644
--- a/webapp/backend/package-lock.json
+++ b/webapp/backend/package-lock.json
@@ -11,6 +11,7 @@
         "morgan": "^1.10.0",
         "multer": "^1.4.5-lts.1",
         "mysql2": "^3.11.4",
+        "redis": "^4.7.0",
         "sequelize": "^6.37.5",
         "uuid": "^11.0.3"
       },
@@ -18,6 +19,65 @@
         "nodemon": "^3.1.7"
       }
     },
+    "node_modules/@redis/bloom": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
+      "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@redis/client": "^1.0.0"
+      }
+    },
+    "node_modules/@redis/client": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz",
+      "integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==",
+      "license": "MIT",
+      "dependencies": {
+        "cluster-key-slot": "1.1.2",
+        "generic-pool": "3.9.0",
+        "yallist": "4.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@redis/graph": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
+      "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@redis/client": "^1.0.0"
+      }
+    },
+    "node_modules/@redis/json": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz",
+      "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@redis/client": "^1.0.0"
+      }
+    },
+    "node_modules/@redis/search": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz",
+      "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@redis/client": "^1.0.0"
+      }
+    },
+    "node_modules/@redis/time-series": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz",
+      "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@redis/client": "^1.0.0"
+      }
+    },
     "node_modules/@types/debug": {
       "version": "4.1.12",
       "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@@ -304,6 +364,15 @@
         "fsevents": "~2.3.2"
       }
     },
+    "node_modules/cluster-key-slot": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+      "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -700,6 +769,15 @@
         "is-property": "^1.0.2"
       }
     },
+    "node_modules/generic-pool": {
+      "version": "3.9.0",
+      "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
+      "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
     "node_modules/get-intrinsic": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
@@ -1541,6 +1619,23 @@
         "node": ">=8.10.0"
       }
     },
+    "node_modules/redis": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz",
+      "integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==",
+      "license": "MIT",
+      "workspaces": [
+        "./packages/*"
+      ],
+      "dependencies": {
+        "@redis/bloom": "1.2.0",
+        "@redis/client": "1.6.0",
+        "@redis/graph": "1.1.1",
+        "@redis/json": "1.0.7",
+        "@redis/search": "1.2.0",
+        "@redis/time-series": "1.1.0"
+      }
+    },
     "node_modules/retry-as-promised": {
       "version": "7.0.4",
       "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz",
@@ -2111,6 +2206,12 @@
       "engines": {
         "node": ">=0.4"
       }
+    },
+    "node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "license": "ISC"
     }
   }
 }
diff --git a/webapp/backend/package.json b/webapp/backend/package.json
index 4ec4ab7393e00ca826de7c4acf7d922c60a81310..333c8e2f494f8cadf7b845bf64da29f544807247 100644
--- a/webapp/backend/package.json
+++ b/webapp/backend/package.json
@@ -6,6 +6,7 @@
     "morgan": "^1.10.0",
     "multer": "^1.4.5-lts.1",
     "mysql2": "^3.11.4",
+    "redis": "^4.7.0",
     "sequelize": "^6.37.5",
     "uuid": "^11.0.3"
   },