동시성 문제란?
여러개의 프로세스나 스레드가 동시에 같은 리소스, 예를 들자면 데이터베이스나 캐시, 파일 등에 접근 할 때 예상 치 못한 데이터 충돌, 중복 실행, 무결성 문제가 발생하는 상황이다.
대표적인 예시로는 재고 감소, 중복 결제 방지, 특정 작업의 중복 실행 방지 등이 필요 할 때 동시성 제어가 필요하다.
Redis를 이용한 동시성 해결 방법

Redis를 활용하여 분산 락(distributed lock)을 사용하면, 하나의 작업이 끝날 때까지 다른 프로세스가 동일한 리소스를 변경하지 못하도록 막을 수 있다.
Redis를 이용한 대표적인 동시성 해결 기법은 다음과 같다
- SETNX (SET if Not Exists) 사용
- Redlock 알고리즘 (분산 락 강화 버전) 사용
이 두가지 방법을 nodejs, docker 환경에서 실습해보자. 이번 포스팅에서는 SETNX를 이용한 락을 구현해보겠다. nodejs나 docker, 기본적인 redis 기능에 대한 설명은 생략한다.
기본적인 SETNX를 이용한 락 구현
Redis의 SETNX를 활용하여 한 번에 하나의 프로세스만 특정 리소스를 접근하도록 제한하는 방법이다.
동작 방식
- 작업 전에 락을 설정 (SETNX + TTL 설정)
- 다른 프로세스가 동일한 리소스에 접근하려하면 거부
- 작업이 끝나면 락을 해제
예제 코드
1. 프로젝트 폴더 및 파일 구성
mkdir redis-lock-example && cd redis-lock-example
mkdir src
touch docker-compose.yml
touch src/index.js
touch Dockerfile
2. docker-compose.yml
version: "3.8"
services:
redis:
image: redis:latest
container_name: my-redis
ports:
- "6379:6379"
app:
build: .
container_name: node-app
depends_on:
- redis
environment:
- REDIS_HOST=redis
- REDIS_PORT=6379
volumes:
- .:/usr/src/app
command: ["node", "src/index.js"]
3. Dockerfile 작성
# Node.js 이미지 사용
FROM node:18
# 작업 디렉터리 설정
WORKDIR /usr/src/app
# package.json 복사 및 패키지 설치
COPY package.json ./
RUN npm install
# 소스 코드 복사
COPY . .
# 실행 명령어
CMD ["node", "src/index.js"]
4. package.json 작성
npm init -y
npm install ioredis
5. src/index.js
import Redis from "ioredis";
// Redis 연결
const redis = new Redis({
host: process.env.REDIS_HOST || "localhost",
port: process.env.REDIS_PORT || 6379,
});
async function acquireLock(key, ttl) {
const lock = await redis.set(key, "LOCKED", "NX", "PX", ttl);
return lock === "OK"; // 락을 획득했는지 여부 반환
}
async function releaseLock(key) {
await redis.del(key);
}
async function processWithLock() {
const lockKey = "my_resource_lock";
const lockTTL = 5000; // 5초 동안 락 유지
const lockAcquired = await acquireLock(lockKey, lockTTL);
if (!lockAcquired) {
console.log("🔒 다른 프로세스가 사용 중. 다시 시도하세요.");
return;
}
try {
console.log("✅ 락 획득! 작업 수행 중...");
await new Promise((resolve) => setTimeout(resolve, 3000)); // 3초 동안 작업 수행
} finally {
await releaseLock(lockKey);
console.log("🔓 락 해제 완료!");
}
}
// 테스트 실행
processWithLock();
그 다음 docker-compose up --build 명령어를 실행한다.
테스트 방법
- 터미널을 2개 열고
- 각각의 터미널에서 다음 명령어를 실행한다.
docker-compose run --rm app
그렇게 되면 첫 번째 프로세스가 락을 획득하고 실행 중일 때, 두 번째 프로세스는 락을 못 잡고 대기한다.
- 첫번째 실행
- ✅ 락 획득! 작업 수행 중... 🔓 락 해제 완료!
- 두번째 실행
- 🔒 다른 프로세스가 사용 중. 다시 시도하세요.
사용하기도 편하고 직관적이다. 문제는 실 서비스를 운영할 때 redis를 한개만 띄워놓지 않는다는 것이다. 만약 단일 redis 노드가 아니라 여러개가 있다면 어떻게 해야 될까? 다음 포스팅에서 Redlock 알고리즘을 이용한 분산 락을 구현해보겠다.
소스 코드는 다음 링크에 있다.
https://github.com/chobkyu/node-redis-distributed-lock
공부하면서 정리한 내용입니다. 모든 지적 감사히 받겠습니다:)
'backend' 카테고리의 다른 글
[Server] Nginx (0) | 2025.03.26 |
---|---|
[Redis] Redis를 이용한 동시성 이슈 해결 실습 - Redlock (0) | 2025.02.20 |