# 14일차: Nginx, 부하 분산

min0._.9·2026년 1월 25일

우리FISA 일일 회고

목록 보기
11/15

1. Nginx

SPA 애플리케이션 배포

다음과 같은 명령어를 통해 빌드 단계를 수행할 수 있다.

npm run build # vite build

빌드 과정에서는 번들링을 통해 .jsx, .html, .css와 같이 개발자가 작성한 코드 파일들이 브라우저가 이해할 수 있는 단일 .html, .css, .js 파일로 압축된다.

번들링(Bundling)

번들링이란, 여러 개의 JS 파일을 하나의 파일로 묶어, 브라우저에서 효율적으로 로딩하고 실행될 수 있도록 최적화하는 과정이다.

Vite에서는 Rollup.js를 이용하여 번들링을 수행하고, 다음과 같은 기능을 제공한다.

  1. 트리 쉐이킹(Tree Shaking)
    : 사용되지 않는 코드나 import된 함수들을 제거하여 최종 번들 크기를 최소화한다. 작성한 코드를 토큰 단위로 분해(const, a, ….)하여 트리 형태(AST)로 만들어 사용되고 있는지 아닌지를 판별해, 사용되지 않는 것들은 제거한다.
    a. garbage collector
    : 참조 카운트가 없는 것들을 지운다.
  2. 코드 분할(Code Splitting)
    : 애플리케이션을 여러 개의 작은 청크로 나누어,필요한 부분만 로딩하도록 최적화한다.
  3. 다양한 플러그인 제공
    : 다양한 플러그인을 통해 추가적인 변환이나 압축, 변형 처리를 한다. (Minify, Uglify)
    a. Minify
    : 코드에서 공백, 주석, 불필요한 문자 등을 제거해 번들 사이즈를 축소한다.
    b. Uglify
    : 난독화. 코드에서 변수명과 함수명을 짧은 이름으로 변경해 알아보기 힘들게 한다.

Nginx 로깅

Nginx는 Nginx 웹 서버로의 모든 클라이언트의 요청에 대해 로그를 남긴다.

acccess.log

127.0.0.1 - - [19/Jan/2026:12:11:17 +0900] "GET / HTTP/1.1" 200 408 "-" "curl/8.17.0"
127.0.0.1 - - [19/Jan/2026:12:11:53 +0900] "GET /not HTTP/1.1" 404 153 "-" "curl/8.17.0"

error.log

2026/01/19 12:11:53 [error] 23364#10364: *29 CreateFile() "C:\woori-workspace\00.sw\nginx-1.28.1\nginx-1.28.1/nginx-practice/not" failed (2: The system cannot find the file specified), client: 127.0.0.1, server: localhost, request: "GET /not HTTP/1.1", host: "localhost"

모든 요청에 대해 로깅을 수행할 경우, 로그 파일의 용량이 커지게 되고, 매번 로그 처리 연산을 수행하느라 서버의 부하에 영향을 미칠 수 있다.

또한, 서로 관련 있는 요청별로 로그 파일을 구분하여 모아두는 것이 유지보수에 편하기 때문에 적절히 분리하는 게 좋다.

nginx.config

events {}  # 연결 처리 설정 (기본값 사용)
 
http {  # HTTP 요청 처리 설정 시작
    include mime.types;

    server {  # 서버 블록 시작
        listen 80;  # 80 포트에서 HTTP 요청 대기
        server_name localhost;  # 'localhost' 도메인에 대해 응답
        root nginx-practice;  # 정적 파일들이 위치한 루트 디렉토리
        index index.html;  # 기본 인덱스 파일 설정

        location /custom {
            access_log logs/custom.access.log; 
            return 200 'custom route'; 
        }
    }  # 서버 블록 종료
}  # HTTP 블록 종료

다음과 같이 /custom 으로의 새 분기를 추가하고, access_log에 해당 분기로 접근했을 때의 로그를 관리할 파일을 적어준다.

curl localhost/custom

으로 확인하면, 새로 추가한 custom.access.log 파일에 새 로그가 찍히는 걸 확인할 수 있다.

access_log에 로그 파일 대신 ‘off’ 옵션을 넣어주면 해당 페이지에 접근해도 로그가 찍히지 않게 할 수 있는데, 로그를 기록해야 하므로 보통은 잘 사용하지 않는다.

부하 분산

사용자가 서비스를 언제든지 이용할 수 있도록 하기 위해서는 서버가 높은 가용성을 가져야 한다. 일반적으로 같은 서버 인스턴스를 최소 2대 이상 가동해, 클라이언트의 요청 트래픽을 각각의 서버로 분산할 수 있다.

다음과 같은 로드 밸런서를 통해 여러 서버로 트래픽이 분산 될 수 있다.

배포 실습

npm run preview

개발 환경(npm run dev)에서는 5173 포트로 실행했지만, 빌드 환경 미리보기로 하면 4173 포트로 실행된다.

Nginx

대표적인 상용 웹 서버다.

nginx.config

events {}  # 연결 처리 설정 (기본값 사용)
 
http {  # HTTP 요청 처리 설정 시작
    server {  # 서버 블록 시작
        listen 80;  # 80 포트에서 HTTP 요청 대기
        server_name localhost;  # 'localhost' 도메인에 대해 응답
        root nginx-practice;  # 정적 파일들이 위치한 루트 디렉토리
        index index.html;  # 기본 인덱스 파일 설정
    }  # 서버 블록 종료
}  # HTTP 블록 종료

nginx.config 가 유효한지 검사하는 명령어

nginx -t

작성한 파일 내용으로 리로드 하는 명령어

(서버를 껐다 켜지 않고도 바뀐 config 내용을 적용할 수 있다)

 nginx -s reload

curl을 통해 style.css가 잘 적용되었는지 본다.

curl -I localhost/style.css

라우팅

location block 활용

nginx.config

location /hello {
            return 200 '/hello requested'; 
            }

server 설정 안에 해당 블록을 넣어주면, 다음과 같이 localhost/hello에서 해당 문구를 보여준다.

부하 분산

초기 세팅 과정은 다음과 같다.

실습용 서버 세팅

server_a.js

const http = require('http');
 
const port = 3001;
 
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  console.log("Server A");
  
  res.end('Node.js Server A\n');
});
 
server.listen(port, () => {
  console.log(`Server A running on http://localhost:${port}\n`);
});
 

nginx.config

location / {
            proxy_pass 'http://localhost:3001/';
        }

서버 안에 해당 코드를 넣어주면 localhost로 접근해도 server_a로 이동한다.

부하 분산 테스트

  • Round-Robin : Nginx에서 기본 옵션으로 설정된 부하분산 기법으로, 업스트림 풀에 지정된 서버의 순서에 따라 균일하게 요청을 분산한다.
    upstream node_servers {
            server localhost:3001 weight=1;
            server localhost:3002 weight=2; # 3001보다 더 많은 요청을 처리
    }

서버 A를 끄면 B, C에서만 분산되어 응답한다. A를 다시 켜주면 잠시 후 A, B, C가 순차적으로 응답하며 부하가 분산된다.

  • Least connection Nginx와의 연결 횟수가 적은 서버를 먼저 활용하는 방식이다.
    upstream node_servers {
    		least_conn; # 리스트 커넥션 알고리즘 적용
            server localhost:3001
            server localhost:3002
    }

+) 현업에서는 nginx를 많이 쓴다. 대체 프로그램으로 haproxy가 있음!

Rate limiting: 요청 비율 제한

일정 시간 동안 허용되는 요청의 수를 제한한다. 특정 사용자의 IP에 대해 요청 비율을 제한하도록 적용한다.

비정상적인 대량의 요청을 차단한다.

API 남용을 방지하고, 서버의 리소스를 보호한다.

[실습] 짝꿍의 IP를 제한해보자.

Nginx의 디렉티브

  • limit_req_zone
  • limit_req
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

  • rate
    • 초당 허용한 요청 횟수
    • rate = 2r/s 일 때 : 1초에 2개의 요청이 허용된다.
      (하나만 while true로 쏨)

  • burst
    • burst(bucket 최대용량)를 초과한 순간 감당 안 되어서 503 error 에러가 발생한다. leaky bucket을 이용한다.

예를 들어 burst=5 이고 rate=1r/s이면, 한 요청당 1L의 물이 들고 최대 5L까지 바구니에 들어갈 수 있다.

5L를 넘는 요청에 대해서는 에러를 발생시키고, 1초마다 1L의 물이 비워진다(샌다.)


  • rate = 3r/s, burst=2일 때
    • 터미널 두 개에서 요청했을 때
      : 같은 초 당 3개의 요청을 잘 처리함
    • 터미널 3개가 시작되었을 때
      : 같은 초 당 3개의 요청을 처리하고, 그 외의 요청에 대해서는 503 error

  • rate = 3r/s일 때
    : 각각의 기기에 대해서 3개의 요청을 허용하는 걸까, 전체 요청에 대해서 3개를 허용하는 걸까?

전체 요청에 대해서 3개를 허용하는 거였다!

0개의 댓글