공들여 만든 웹 서비스가 출시 당일, 예상보다 많은 사용자가 몰려 서버가 다운된다면 그보다 아찔한 상황은 없을 것입니다. 서버가 한 번에 얼마나 많은 요청을 처리할 수 있는지, 어느 정도의 동시 접속자 수에서 속도가 느려지는지 미리 파악하는 과정이 바로 **부하 테스트(Load Testing)**입니다.

오늘은 별도의 복잡한 설치 없이 Node.js 환경에서 간편하게 사용할 수 있는 부하 테스트 도구인 Artillery를 활용하여 내 서버의 체력을 측정하는 방법을 알아보겠습니다.


1. 부하 테스트(Load Testing)는 왜 필요한가?

부하 테스트는 단순히 서버를 괴롭히는 것이 아니라, 시스템의 **병목 지점(Bottleneck)**을 찾아내기 위해 수행합니다.

  • 최대 수용 인원 파악: 현재 서버 사양(EC2 인스턴스 등)에서 동시 접속자 몇 명까지 안정적인지 확인합니다.
  • 리소스 부족 지점 발견: CPU가 먼저 차오르는지, 메모리가 부족한지, 혹은 DB 연결 개수가 모자란지 파악합니다.
  • Auto Scaling 기준 수립: 서버가 자동으로 늘어나야 하는 기준(CPU 70% 등)을 정하는 근거가 됩니다.

2. Artillery 설치 및 첫 테스트 시작

Artillery는 YAML 파일로 테스트 시나리오를 작성할 수 있어 매우 직관적입니다.

2.1 설치하기

npm을 통해 전역으로 설치하거나 npx로 바로 실행할 수 있습니다.

Bash
 
npm install -g artillery

2.2 간단한 명령어로 테스트하기

가장 기본적인 테스트는 명령어 한 줄로 가능합니다. 아래는 10명의 가상 사용자가 1초에 1번씩 총 100번의 요청을 보내는 예시입니다.

Bash
 
artillery quick --count 10 -n 100 https://your-api-domain.com

3. 시나리오 기반의 정밀 테스트 (YAML 설정)

실제 서비스와 유사한 환경을 만들기 위해 script.yml 파일을 작성하여 테스트를 수행하는 것이 좋습니다.

YAML
 
config:
  target: "https://api.example.com"
  phases:
    - duration: 60      # 60초 동안 테스트 진행
      arrivalRate: 5    # 초당 5명의 가상 사용자 투입 (평상시 트래픽)
    - duration: 120
      arrivalRate: 5
      rampTo: 50        # 2분 동안 초당 접속자를 50명까지 점진적으로 증가 (피크 타임)
  
scenarios:
  - name: "메인 페이지 접속 및 상품 상세 조회"
    flow:
      - get:
          url: "/"
      - think: 1        # 1초 대기 (사용자 행동 모의)
      - get:
          url: "/products/1"

3.3 테스트 실행 및 리포트 생성

Bash
 
artillery run -o report.json script.yml
# 결과를 HTML 리포트로 변환
artillery report report.json

4. 테스트 결과 해석하기 (지표 읽는 법)

테스트가 끝나면 터미널이나 HTML 리포트에 여러 수치가 나타납니다. 무엇을 중요하게 봐야 할까요?

  • Scenarios created/completed: 생성된 가상 사용자가 요청을 모두 마치고 성공적으로 종료되었는지 확인합니다.
  • RPS (Requests Per Second): 초당 처리량입니다. 서버가 초당 몇 개의 요청을 소화하는지 보여줍니다.
  • Latency (p95, p99): 응답 속도입니다. p95가 500ms라면 전체 사용자 중 95%가 0.5초 이내에 응답을 받았음을 의미합니다. 이 수치가 급격히 올라가면 서버가 힘겨워하고 있다는 신호입니다.
  • HTTP 2xx vs 5xx: 500번대 에러가 발생하기 시작하는 시점이 바로 내 서버의 한계치입니다.

5. 결론: 부하 테스트는 '신뢰'를 만드는 과정입니다

부하 테스트를 마쳤다면, 이제 "우리 서버는 동시 접속자 500명까지는 응답 속도 0.3초를 유지할 수 있습니다"라고 자신 있게 말할 수 있습니다. 수치화된 데이터는 인프라 증설이나 코드 최적화의 명확한 기준이 됩니다.

웹 서비스의 사용자가 늘어남에 따라 데이터베이스(DB)에 가해지는 부하도 기하급수적으로 증가합니다. 매번 똑같은 데이터를 조회하기 위해 무거운 디스크 기반 DB(MySQL, PostgreSQL 등)를 거치는 것은 비효율적입니다.

오늘은 메모리 기반의 데이터 저장소인 **Redis(Remote Dictionary Server)**를 도입하여 시스템의 응답 속도를 혁신적으로 개선하고, DB의 부담을 덜어주는 캐시 서버 구축 방법을 알아보겠습니다.


1. Redis란 무엇이며 왜 필요한가?

Redis는 '인메모리(In-memory)' 데이터 구조 저장소입니다. 데이터를 디스크가 아닌 RAM에 저장하기 때문에 읽기/쓰기 속도가 압도적으로 빠릅니다.

1.1 캐싱 전략: Look-Aside 패턴

가장 많이 사용되는 방식은 클라이언트가 데이터를 요청할 때, 먼저 Redis(캐시)에 데이터가 있는지 확인하는 것입니다.

  1. Cache Hit: Redis에 데이터가 있다면 즉시 반환 (매우 빠름)
  2. Cache Miss: Redis에 없다면 DB에서 조회 후 Redis에 저장하고 반환

이 과정을 통해 반복적인 DB 쿼리를 줄여 전체적인 서비스 성능을 최적화할 수 있습니다.


2. Ubuntu에서 Redis 설치 및 초기 설정

이제 실습을 통해 서버에 Redis를 설치해 보겠습니다.

2.1 패키지 설치

Bash
 
sudo apt update
sudo apt install redis-server -y

2.2 설정 파일 최적화

Redis의 동작 방식을 제어하기 위해 설정 파일을 수정합니다.

Bash
 
sudo nano /etc/redis/redis.conf
  • 메모리 제한 설정: 서버의 전체 메모리를 다 쓰지 않도록 제한을 둡니다.(LRU 정책은 메모리가 가득 찼을 때 가장 오래 사용되지 않은 데이터를 삭제하여 공간을 확보합니다.)
  • Plaintext
     
    maxmemory 256mb
    maxmemory-policy allkeys-lru
    

2.3 서비스 재시작 및 확인

Bash
 
sudo systemctl restart redis-server
sudo systemctl status redis-server

3. Redis CLI를 이용한 데이터 조작 기초

설치가 완료되었다면 redis-cli를 통해 간단한 명령어를 연습해 볼 수 있습니다. Redis는 Key-Value 쌍으로 데이터를 저장합니다.

  • 데이터 저장: set user:1 "Yumina"
  • 데이터 조회: get user:1
  • 만료 시간 설정(중요): setex session:key 3600 "data" (3600초 뒤에 자동으로 데이터가 삭제되도록 설정하여 메모리를 효율적으로 관리합니다.)

4. 실무 도입 시 주의사항 (Best Practice)

Redis는 강력하지만, 잘못 사용하면 데이터 유실이나 메모리 부족 문제가 발생할 수 있습니다.

  1. 휘발성 데이터 위주로 저장: Redis는 메모리 기반이므로 서버가 꺼지면 데이터가 사라질 수 있습니다. 사라져도 다시 DB에서 불러올 수 있는 '캐시' 용도로 사용하는 것이 가장 안전합니다.
  2. 보안 설정: Redis의 기본 포트(6379)는 외부 공격에 취약할 수 있습니다. 반드시 redis.conf에서 비밀번호(requirepass)를 설정하고, 방화벽을 통해 허용된 IP만 접속 가능하게 제한하세요.
  3. 적절한 만료 시간(TTL): 모든 데이터에 적절한 유효 기간을 설정하여 메모리 파편화를 방지해야 합니다.

5. 결론: 빠르고 견고한 서버를 위한 선택

Redis 도입은 현대 백엔드 아키텍처에서 선택이 아닌 필수 과정 중 하나입니다. 단순한 캐시 서버를 넘어 세션 관리, 실시간 랭킹 시스템, 메시지 큐 등 활용도가 무궁무진합니다.

웹 서비스를 운영하다 보면 성능 최적화와 보안이라는 두 마리 토끼를 잡아야 하는 순간이 옵니다. 특히 Node.js, Python(Django/Flask), Go 등으로 만든 애플리케이션 서버를 외부 네트워크에 직접 노출하는 것은 보안상 위험할 뿐만 아니라 성능 면에서도 효율적이지 않습니다.

이때 가장 대중적으로 사용되는 해결책이 바로 Nginx를 활용한 리버스 프록시(Reverse Proxy) 구성입니다. 오늘은 리버스 프록시의 개념부터 실제 설정 방법, 그리고 이를 통해 얻을 수 있는 이점까지 상세히 정리해 보겠습니다.


1. 리버스 프록시(Reverse Proxy)란 무엇인가?

일반적인 프록시(Forward Proxy)가 클라이언트의 요청을 대신 전달한다면, 리버스 프록시는 서버의 앞단에 위치하여 클라이언트의 요청을 받아 내부 서버로 전달하는 역할을 합니다.

사용자는 실제 애플리케이션 서버(예: 3000번 포트)에 직접 접속하는 것이 아니라, 80(HTTP) 또는 443(HTTPS) 포트를 통해 Nginx에 먼저 도달하게 됩니다. Nginx는 이 요청을 적절한 내부 서버로 토스(Toss)해 주는 '중개인' 역할을 수행합니다.


2. 리버스 프록시 도입의 3가지 핵심 이점

2.1 보안 강화 (Security)

실제 서비스가 돌아가는 서버의 IP와 포트를 외부로부터 숨길 수 있습니다. 공격자는 Nginx의 IP만 볼 수 있으며, 내부 인프라 구조를 파악하기 어렵게 됩니다. 또한, Nginx 단에서 WAF(웹 방화벽) 설정을 추가하여 악의적인 요청을 1차적으로 걸러낼 수 있습니다.

2.2 부하 분산 (Load Balancing)

하나의 Nginx 뒷단에 여러 대의 애플리케이션 서버를 두고, 요청을 골고루 나누어 줄 수 있습니다. 특정 서버에 장애가 발생하더라도 다른 서버로 요청을 유도하여 서비스 가용성을 높일 수 있습니다.

2.3 정적 파일 캐싱 및 압축 (Performance)

이미지, CSS, JS와 같은 정적 자원을 Nginx가 직접 캐싱하거나 gzip 압축을 통해 전송 속도를 높일 수 있습니다. 이는 애플리케이션 서버가 비즈니스 로직에만 집중할 수 있도록 부담을 덜어줍니다.


3. Nginx 리버스 프록시 실전 설정법

이제 Ubuntu 환경에서 Nginx를 설치하고 리버스 프록시를 설정하는 과정을 살펴보겠습니다. (내부 서버는 localhost:3000에서 동작 중이라고 가정합니다.)

3.1 Nginx 설치

먼저 패키지 목록을 업데이트하고 Nginx를 설치합니다.

Bash
 
sudo apt update
sudo apt install nginx -y

3.2 설정 파일 생성 및 수정

기본 설정 파일을 수정하기보다는, 새로운 설정 파일을 만들어 관리하는 것이 좋습니다.

Bash
 
sudo nano /etc/nginx/sites-available/my-app

파일 내부에 아래 내용을 입력합니다. (도메인이 있다면 server_name에 작성하세요.)

Nginx
 
server {
    listen 80;
    server_name your-domain.com; # 도메인이 없다면 서버 IP 입력

    location / {
        # 요청을 내부 서버(3000번 포트)로 전달
        proxy_pass http://localhost:3000;
        
        # 실제 클라이언트의 정보를 전달하기 위한 헤더 설정
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        
        # 클라이언트의 실제 IP 주소 전달
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

3.3 설정 활성화 및 테스트

작성한 설정을 활성화하고 구문에 에러가 없는지 확인합니다.

Bash
 
# 설정 파일 심볼릭 링크 생성
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/

# Nginx 설정 테스트
sudo nginx -t

# Nginx 재시작
sudo systemctl restart nginx

4. 자주 발생하는 문제와 해결법 (Troubleshooting)

리버스 프록시 설정 후 흔히 겪는 에러 두 가지를 짚어보겠습니다.

  • 502 Bad Gateway: Nginx는 정상이나, proxy_pass로 연결된 내부 애플리케이션 서버가 죽어있을 때 발생합니다. pm2 status 등으로 서버 상태를 확인하세요.
  • 413 Request Entity Too Large: 클라이언트가 업로드하는 파일 용량이 Nginx 기본 제한을 초과할 때 발생합니다. http나 server 블록에 client_max_body_size 20M; 설정을 추가하여 해결할 수 있습니다.

5. 결론: 현대 웹 아키텍처의 필수 요소

리버스 프록시는 단순한 전달자를 넘어, 웹 서비스의 안정성과 확장성을 책임지는 핵심 인프라 기술입니다. 특히 최근처럼 마이크로서비스 아키텍처(MSA)가 각광받는 환경에서는 Nginx를 활용한 게이트웨이 구성이 더욱 중요해지고 있습니다.

오늘 안내해 드린 기본 설정을 바탕으로, 이후에는 HTTPS 보안 인증서(SSL) 적용과 로드 밸런싱 설정까지 확장해 보시길 권장합니다. 안정적인 서버 운영으로 사용자에게 더 나은 경험을 제공해 보세요!

+ Recent posts