기본적으로 SSL Termination과 3-Tier Architecture 구조로 구성하였다. DB는 성능과 안정성을 위해 호스트 프로세스로 직접 운영하는 하이브리드 구성을 택했다.

 

서버 이중화 시 고려한 점

1. Sticky Session과 Session Clustering

Sticky Session은 LB가 특정 사용자의 요청을 처음 접속했던 서버로 보내주는 것으로 사용자의 브라우저 쿠키나 IP 주소를 확인한다. 서버가 죽었을 때 해당 서버에 있는 사용자 세션이 전부 날라가 버린다. 그래서 WAS 서버에서 로직으로 failover가 가능한 Session Clustering도 같이 고려해야한다. 보통 속도를 위해서 redis에 세션을 저장해서 클러스터링한다. Redis Sentinel 구조로 고가용성을 확보해야하고 굉장히 인프라적으로 복잡하다. 그런데 JWT를 사용하면 Stateless 방식이라서 둘 다 필요없다.

 

Sticky Session을 구성하는 방식

1. ip_hash

 

nginx가 HTTP 레벨(L7)에서 동작하지만, 클라이언트의 IP(L3 정보)를 기준으로 해싱(hash 알고리즘)해 항상 같은 서버로 보낸다.

L4의 알고리즘 방식과 관계없이 L7의 알고리즘 방식을 hash를 사용해 Client IP가 같으면 항상 Target WAS를 같게 해주는 것이다. 특정 서버로 트래픽이 몰릴 수 있다.

upstream backend_servers {
    ip_hash;
    server was1.example.com;
    server was2.example.com;
}

 

2. Cookie Insertion - Nginx Plus(유료) 버전만 가능

Nginx에서 들어오는 요청의 쿠키값을 본다. 서버에 고르게 트래픽이 분산되지만 유료 버전만 가능하여 인프라 종속성이 생긴다.

upstream backend_servers {
    server was1.example.com;
    server was2.example.com;

    # 로드밸런서가 'route'라는 이름의 쿠키를 삽입함
    sticky cookie route expires=1h domain=.example.com path=/;
}

 

결론은 JWT를 사용해서 인프라 관리 복잡성을 낮추는게 좋다!!!!!

 

메커니즘 역할
proxy_next_upstream 매 요청마다 실행됨. 현재 서버가 실패하면 그 요청 안에서 즉시 다음 서버로 재시도
max_fails / fail_timeout 실패 횟수가 누적되면, 이후 새로운 요청들이 아예 그 죽은 서버를 시도조차 안 하게 미리 건너뛰게 함 (일종의 캐시)

트러블 슈팅

서버 shutdown 시, 재시도를 하면서 무한로딩 발생한다. Docker 다운이나 HTTP 오류일때는 무한로딩이 발생하지 않고, 서버 다운일 경우에만 발생했다. 해당 문제는 타임아웃일 경우에만 발생하는 것으로 error나 http일때는 상관없다.

-> timeout 발생한 서버를 헬스체크 한번 발생했다면 DB의 Active-Stanby 형식처럼 down된 서버에 접속이 안되는지 찾아봤는데 이건 Nginx Plus(유료)에서 지원하는 Active Health Check 기능이다.

 

특히 이건 내가 proxy_connect_timeout + max_fails  + fail_timeout 의 정말 알맞은 값을 찾기 위해서 여러 시나리오를 생각하고 작성한 것이다. 무료버전에서 서버 shutdown으로 타임아웃 발생 시 웹 페이지에서 크롬으로 따지면 맨 위에서 빙글빙글 돌면서 무한 로딩이 발생하는 이유는 타임아웃 시간만큼 기다렸다가 upstream을 하기 때문이다. 그래서 fail_timeout 시간 내에 max_fails 만큼 오류가 나야되는데 proxy_connect_timeout을 잘 조절해야한다. 즉 30초 안에 3번 오류가 날때 타임아웃 오류일 경우엔 바로 다음 서버로 넘기는게 아니라 proxy_connect_timeout 시간만큼 기다렸다가 넘긴다. 그 모든게 fail_timeout 시간 안에 이루어져야한다. 30초 안에 사용자 3명이 동시에 접속할 경우 3명의 사용자(병렬처리)가 모두 proxy_connect_timeout 만큼 무한로딩을 겪고 다음 4번째 사용자부터 죽은 서버를 제외하고 바로 upstream된다.

최악의 경우는 1명의 사용자가 홀로 접속해서 30초 내에 3번의 클릭을 했는데 proxy_connect_timeout 시간을 너무 길게 잡아서 fail_timeout 시간을 넘어 max_fails 가 초기화되는 경우이다. 이러면 정상 서버로 넘어갈 때 계속 비정상 서버를 체크하면서 proxy_connect_timeout 만큼 기다리기에 계속 로딩이 오래걸린다고 느끼게 된다.

또 다른 최악의 상황은 3명의 사용자가 순차적으로 접속하는 경우이다. 이런 경우엔 upstream되면서 fail_timeout이 초기화되지 않고 첫번째 요청을 기준으로 한다. 즉 fail_timeout - proxy_connect_timeout 시간 만큼만 죽은 서버를 제외해서 fail_timeout보다 훨씬 빠르게 죽은 서버를 다시 찔러본다.

 

1. 동시 접속 3명 (병렬)

t=0: 3명 동시 요청 시작
t=30: 3명 다 거의 동시에 타임아웃 → 실패 카운트 1,2,3이 "거의 동시"에 쌓임 (창은 t=30에 막 시작)
      → 이 순간 마킹됨. 창이 "막 시작"했으니 fail_timeout(30초)이 거의 통째로 남아있음
t=30~60: 이 구간(약 30초) 동안 죽은 서버 스킵됨 ✅

 

2. 1명 순차 클릭 (= 3. 3명 순차 접속과 동일)

t=0:  요청1 → 죽은 서버 시도 → 30초 대기
t=30: 실패 카운트 1 (창 시작: t=30~60) → 정상 서버로 재시도 → 응답
      (요청2가 바로 이어서 들어옴, 1명이 다시 클릭하든 다른 사용자든 동일)
t=30: 요청2 → 죽은 서버 시도 → 30초 대기
t=60: 실패 카운트 시도... 근데 창(t=30~60)이 "지금 막" 끝나는 시점!
      → fail_timeout(30초)이 이미 다 지나버려서 카운터가 리셋됨
      → 이 실패가 "카운트 2"가 아니라 새로운 창의 "카운트 1"이 됨 (창 재시작: t=60~90)
t=60: 요청3 → 죽은 서버 시도 → 30초 대기
t=90: 또다시 창(t=60~90)이 끝나는 시점에 걸림 → 다시 리셋, 카운트 1로 재시작

결과: max_fails=3에 영원히 도달하지 못함 ❌
      → 서버는 절대 unavailable로 마킹되지 않음
      → 모든 요청이 각각 30초씩 무한 로딩을 반복

 

결론은 max_fails × proxy_connect_timeout이 fail_timeout보다 충분히 작아야 한다. 이상적으로는 fail_timeout ≥ max_fails × proxy_connect_timeout × 2 정도의 여유를 두면, 어떤 시나리오(동시/순차)에서도 안정적인 스킵 구간이 확보된다.


proxy_next_upstream + proxy_connect_timeout

Nginx가 WAS 1번으로 보냈는데 타임아웃이나 에러가 났을 때, 사용자에게 에러를 주지 않고 즉시 WAS 2번으로 재시도하게 만드는 설정이다. 오픈소스에서 사용할 수 있는 옵션이다. 둘 다 정상적으로 서버가 살아있다면 해당 옵션은 무시된다.

upstream backend_servers {
    server was1:8080 max_fails=3 fail_timeout=30s;
    server was2:8080 max_fails=3 fail_timeout=30s;
}

server {
    location / {
        proxy_pass http://backend_servers;
        
        # 타임아웃, 에러, HTTP 500번대 발생 시 다음 서버로 즉시 넘김
        # error timeout은 기본값이라서 http_500 http_502 http_503 http_504를 적어야지 의미가 있다.
        # 그렇다고 error timeout를 빼고 http만 적는다면 error timeout일때 upstream이 안되니 주의필요
        proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
        
        # 타임아웃 발생 시 해당 시간만큼 빙글빙글이 발생하게 된다.
        proxy_connect_timeout 15s;
        
        # 다음 서버로 넘길 때의 타임아웃 제한 (너무 오래 걸리면 안 되니까)
        proxy_next_upstream_timeout 10s;
        proxy_next_upstream_tries 2;
    }
}

 

결론적으로는 무료 nginx에서 proxy_next_upstream과 proxy_connect_timeout의 값을 조절하여 정상 서버로 넘기는 것을 보장할 수 있다.

 

Nginx Plus(유료)에서 지원하는 Active Health Check 기능을 구현할 수 있는 무료 nginx에서의 2가지 방법

운영중인 서비스에 적용하기에는 리스크가 커서 적용하지는 못했다. 시간상 여유가 있다면 개발 혹은 스테이징 서버에서 충분한 테스트 후에 적용해야한다.

 

1. nginx_upstream_check_module로 Nginx Plus(유료)에서 지원하는 Active Health Check 기능을 구현할 수 있는데, nginx 코어의 upstream 라운드로빈 로직 자체를 수정해야해서 nginx를 처음부터 통째로 다시 빌드해야하고 2014년 이후로 사실상 유지보수가 멈춘 프로젝트라서 추천하지 않는다. 기존 yum으로 설치된 nginx는 삭제하고 소스 재빌드해야한다.

 

2. OpenResty(Nginx+Lua 통합 배포판)를 쓰면 순수 Lua 스크립트로 active health check를 구현할 수 있다. 그러나 config 파일 경로 이동도 필수적이고 Nginx 서비스 다운타임도 필수적이라 영향도 파악에 너무 많은 시간이 소요될거같아서 따로 더 이상 찾아보지는 않았다.

 

정적 모듈 vs 동적 모듈

정적 모듈: Nginx 컴파일 시 소스코드에 포함되어 함께 빌드되며, 항상 메모리에 로드되어 있어서 사용하지 않아도 리소스를 차지한다. 정적모듈은 적용 시 무중단이 불가능하다.
동적 모듈: 별도의 .so 파일로 컴파일되어 필요할 때만 로드하며 유연성이 높고 메모리 효율적이다. 동적모듈은 적용 시 무중단이 가능하다.
 
동적모듈 예시
 

  • ngx_http_image_filter_module: 이미지 처리
  • ngx_http_geoip_module: IP 기반 지리 정보
  • ngx_http_xslt_module: XML/XSLT 변환
  • ngx_stream_module: TCP/UDP 로드밸런싱
  • ngx_http_perl_module: Perl 스크립트 실행

 

so 파일은 뭘까?

.so 파일은 Shared Object 파일이며, 리눅스/유닉스 시스템에서 사용하는 동적 링크 라이브러리(Dynamic Link Library) 파일이다. 동적모듈 최종 파일이라고 보면 된다.
 

동적모듈의 장점

  • 런타임에 로드: 프로그램 실행 중에 필요할 때 메모리에 적재
  • 코드 공유: 여러 프로그램이 동일한 .so 파일을 공유해서 사용 가능
  • 메모리 효율: 중복 코드를 메모리에 한 번만 로드
  • 독립적 업데이트: 메인 프로그램 재컴파일 없이 라이브러리만 교체 가능
  • 무중단 배포 가능

 

1. 동적모듈 소스 다운로드

nginx version을 확인해서 이와 맞는 모듈 소스코드를 다운받아 서버에 업로드 한 뒤 파일의 압축을 푼다.
나는 nginx version이 1.28이라서 해당 tar.gz 파일을 다운받았다. (버전이 맞지 않는다면 nignx -t에서 오류 발생하기에 버전 확인 필수!)
C 파일(.c)과 헤더 파일(.h), config 파일 등으로 구성되어 있다.
공식 다운로드 사이트: https://nginx.org/download/

tar -xzvf nginx-1.28.0.tar.gz

 
 

2. 동적모듈 빌드

동적모듈을 빌드할때 각종 오류가 나는데 보통 의존성 라이브러리가 설치되어있지 않아 오류가 발생했다.
어떤 모듈인지에 따라 필요한 의존성 라이브러리는 각각 다르며, 오류 문구에서 나오는 라이브러리를 설치하면 된다.

# Nginx 소스 디렉토리로 이동
cd /usr/local/src/nginx-1.28.0

# configure 실행 (동적 모듈 추가)
# 의존성 라이브러리 설치가 모두 완료되어야 오류없이 실행 가능
./configure --with-compat --with-http_xslt_module=dynamic

# 모듈에 따라 실행 문법 확인
./configure --help | grep {모듈명}

# 모듈만 빌드
# 이 명령으로 .so 파일(공유 라이브러리)이 생성 
# 빌드된 .so 파일은 objs/ 디렉토리에 생성
make modules

# 빌드된 .so 파일을 모듈 디렉토리로 복사
# 모듈을 빌드하면 nginx-1.28.0/objs 디렉토리 밑에 so 파일이 생성된다.
cp objs/ngx_http_geoip2_module.so /usr/lib64/nginx/modules/

 

⭐ 동적모듈 빌드 옵션 설명

./configure

Nginx 빌드 환경을 설정하는 스크립트입니다. Makefile을 /usr/local/src/nginx-1.28.0 디렉토리 안에 생성합니다.

--with-compat

호환성 모드 활성화

  • Nginx 바이너리와 동적 모듈 간의 ABI(Application Binary Interface) 호환성을 보장 ( 이미 설치된 Nginx에 나중에 동적 모듈만 따로 추가할 때 필수 옵션)
  • 이게 없으면 모듈이 제대로 로드되지 않을 수 있음

--with-http_xslt_module=dynamic

XSLT 모듈을 동적 모듈로 빌드

  • 모듈에 따라서 --with-{모듈명}=dynamic 형식
  • --with-http_xslt_module: XSLT 모듈 포함
  • =dynamic: 정적이 아닌 동적 모듈(.so 파일)로 빌드하라는 의미
  • =dynamic을 안 붙이면 정적 모듈로 컴파일되며, make modules이 안 먹히고, so 파일도 안 만들어진다.

3. 모듈 로드 및 사용

# nginx.conf 파일 수정
vi /etc/nginx/nginx.conf

# nginx.conf 파일 최상단에 추가
# 모듈을 사용하기 위함
include /usr/share/nginx/modules/*.conf;

 
/usr/share/nginx/modules/ 안에 conf 파일을 만들어서 모듈을 로드한다.

# conf 파일 생성 후 파일 안에 load_module 추가
vi /usr/share/nginx/modules/mod-http-image-filter.conf

# 파일 안에 추가
load_module "/usr/lib64/nginx/modules/ngx_http_image_filter_module.so";

 

4. 설정 확인 및 재시작

# 설정 파일 문법 검사
nginx -t

# Nginx 재시작 (무중단)
systemctl reload nginx

 
설정파일 문법 검사 시, 아래와 같이 OK와 successful을 확인했다면 재시작한다.
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
 
 

정적/동적에 따른 Nginx 재시작 방법

# 정적모듈: Nginx 바이너리 자체가 교체되므로 재시작 필요 (중지 발생)
systemctl restart nginx  

# 동적모듈: .so 파일만 교체 후 reload만으로 가능 (무중단)
systemctl reload nginx

우선 Nginx 설정 파일은 크게 2가지로 나뉜다.
구조를 나눔으로써 운영 영향도를 줄이고 설정 충돌 방지, 서비스 추가/삭제가 쉬우며 롤백이 쉽다.
 

nginx 설정 구조

/etc/nginx/
├── nginx.conf          # 메인 설정 파일
├── conf.d/                # 서버별 설정 include
│   ├── default.conf
│   └── api.conf
│   └── web.conf
 
 

수정 시 영향도 확인

  • nginx.conf 수정 → nginx 전체 영향
  • conf.d/*.conf 수정 → 해당 서비스만 영향

 
 

1. 메인 설정 (Main Config) - /etc/nginx/nginx.conf 

-> nginx 전체 프로세스/동작 방식을 정의하는 루트 설정
 
1-1. 전역(Global) 설정

user  nginx;
worker_processes  auto;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

 

  • user : nginx 프로세스 실행 계정
  • worker_processes : 워커 프로세스 개수
  • error_log : 에러 로그
  • pid : PID 파일

2-2. 이벤트 블록
동시에 처리 가능한 커넥션 수

events {
    worker_connections  1024;
}

 
2-3. http 블록
HTTP 서버 전역 설정

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format main '$remote_addr - $remote_user ...';
    access_log /var/log/nginx/access.log main;

    sendfile        on;
    keepalive_timeout 65;

    include /etc/nginx/conf.d/*.conf; //서비스 설정파일 include
    
    map $http_upgrade $connection_upgrade {
    default upgrade;   # 1순위: 아무것도 매칭 안 되면 이거
    '' close;          # 2순위: 빈 문자열이면 이거
    }
}
  • MIME 타입
  • 로그 포맷
  • gzip
  • proxy 관련 기본값
  • SSL 기본값
  • upstream 정의 가능
  • 중요! 서비스별 설정을 직접 다 넣기보다는 include 로 분리하는 구조

 

트러블슈팅

  일반 HTTP WebSocket
연결요청할 때마다 새로 연결한 번 연결하면 계속 유지
방향클라이언트 → 서버 (단방향)양방향 자유롭게
사용 예웹페이지, REST API, 이미지 로딩채팅, 알림, 게임, 실시간 데이터

 
만약에 WebSoket을 사용하는 시스템이라면 서비스 설정 파일 안에 해당 Proxy header가 들어있을것이다.

       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection $connection_upgrade;

 
그렇다면 메인 설정 파일의 http 블록 안에 아래 코드가 들어가야하며 그렇지 않으면 오류 발생한다.

    map $http_upgrade $connection_upgrade {
    default upgrade;   # 1순위: 아무것도 매칭 안 되거나 빈 문자열이 아니면 이거
    '' close;          # 2순위: 빈 문자열이면 이거
    }

 
케이스 1: WebSocket 요청

  • $http_upgrade = "websocket" (Upgrade 헤더 값)
  • 매칭 확인:
    • close 매칭 ❌ (빈 문자열 아님)
    • default upgrade 매칭 ⭕
  • 결과: $connection_upgrade = "upgrade"

케이스 2: 일반 HTTP 요청

  • $http_upgrade = "" (빈 문자열, Upgrade 헤더 없음)
  • 매칭 학인:
    • close 매칭 ⭕ (여기서 끝!)
  • 결과: $connection_upgrade = "close"
  • default는 확인조차 안 함

케이스 3: 이상한 값이 들어온 경우

  • $http_upgrade = "something-weird"
  • 매칭 확인:
    • close 매칭 ❌ (빈 문자열 아님)
    • default upgrade 매칭 ⭕
  • 결과: $connection_upgrade = "upgrade"

 

2. 서비스 설정 (Service / Virtual Host) - /etc/nginx/conf.d/*.conf

HTTPS + WebSocket 예시
아래 코드는 nginx를 proxy server로 사용한 예시이다.

server {
    listen 443 ssl;
    server_name chat.example.com;
    
    # SSL 해독처리로 HTTPS 여기서 처리!
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    
    location / {
        proxy_pass http://localhost:8080; # proxy server
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade; # HTTP/WebSocket 처리
        proxy_set_header Connection $connection_upgrade; # HTTP/WebSocket 처리
        proxy_set_header Host $host;
    }
}

 
 

1. HTTPS 요청 (일반 웹페이지)

브라우저 → https://chat.example.com (443 포트)
         → nginx (SSL 해독)
         → http://localhost:8080 (평문 HTTP로 전달)

 

2. WebSocket 연결 (wss://)

브라우저 → wss://chat.example.com (443 포트)
         → nginx (SSL 해독 + WebSocket 업그레이드)
         → ws://localhost:8080 (평문 WebSocket으로 전달)

 

Proxy Server란 뭘까?

-> 중간 다리 역할을 해주는 서버이다.
-> Apache (with mod_proxy), Caddy, Envoy 같은 Proxy Server가 존재한다.
 
사용자 브라우저 → nginx (80포트) → Node.js 앱 (3000포트)
 
사용자는 80, 443 인터넷 포트만 알고 접속하면 알아서 여러 서버의 포트로 연결시켜준다.
 

  • 보안: 실제 서버(3000포트) 직접 노출 안 함
  • 로드밸런싱: 여러 서버에 트래픽 분산
  • SSL 처리: nginx가 HTTPS 처리, 앱은 HTTP만
  • 정적 파일: 이미지 같은 건 nginx가 직접 서빙

왜 nginx가 정적 파일 서빙에 빠를까?

-> 이미지 요청 → nginx가 디스크에서 바로 읽어서 전송하기 때문!
 

  • 엄청 빠름: C로 작성됨, 파일 읽기에 최적화
  • 백엔드 부하 감소: Node.js가 파일 처리 안 해도 됨 ( 이미지 요청 → nginx → Node.js → 파일 읽기 → nginx → 브라우저 ) 
  • 효율적 메모리: 파일 캐싱, sendfile 시스템콜 사용

 
 

💎 Django Framework

특징

  • MVT 패턴
  • ORM 기반 프레임워크

 

 

MVT 패턴이란?

Model: 데이터 관련 DB

View: Model과 View를 연결해주며, 함수 정의

Tamplate: 실제로 화면을 보여주는 인터페이스

 

ORM 기반 프레임워크란?

객체 지행 프로그래밍에서 객체라는 개념을 구현한 클래스와 RDB에 쓰이는 데이터인 테이블을 자동으로 연결하는 것으로 SQL문이 필요 없다.

 

장고 MVT 패턴과 파일 구조

장고는 Urls.py에서 넘어온 요청을 View.py의 함수와 연결되고 함수에서 model.py에서 데이터를 읽어와서 함수를 수행한 뒤 render 함수를 통해서 template에 딕셔너리 형태로 데이터를 넘겨주고 넘겨받은 데이터를 가지고 html에서 작업을 수행한다.

 

Django의 장점

  1. Python의 다양한 라이브러리 사용 가능하여 개발 속도가 빠르고 생산성이 크다
  2. 인증, 관리와 같은 기본적인 기능들이 기본 모듈로 제공된다. -> 특히 Admin을 사용하여 관리

Django의 단점

  1. 인터프리터 언어라서 실행 속도가 느리다. -> 성능이 떨어진다
  2. Django는 프레임워크 특화 기능보다 ptython으로 문제를 해결하는 편이다.

 

💎 Spring Framework

자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크

 

특징

  • MVC 패턴
  • 관점 지향 프로그래밍(AOP) - 관심사 분리
  • 의존성 주입(DI) -  각각의 계층이나 서비스들 간에 의존성이 존재할 경우 프레임워크가 서로 연결시켜줌
  • 제어역행(IOC) - 컨트롤의 제어권이 사용자가 아니라 프레임워크에게 있어서 스프링에서 사용자 코드 호출
  • 경량 컨테이너 - 크기와 부하의 측면에서 경량, 애플리케이션 객체의 생명 주기와 설정을 포함하고 관리측면에서 컨테이너

 

MVC 패턴에서 Model은 3가지로 분리 ❗

    • DAO (Data Access Object) -실제로 DB에 접근하는 객체 (DB(DB 전용 객체)
    • DTO (Data Transfer Object) – 데이터 교환을 위한 자바 객체 (데이터를 레이어 간 전달하는 것이 목적으로 속성으로는 SetterGetter을 가지고 있음
    • Service 비즈니스 로직이 들어가는 부분 (Controller가 적절한 비즈니스 로직을 가지고 있는service를 선택해서 선택된 service가 적절한 처리를 하여 반환)

👀 Spring Framework 실행 흐름도

Request -> DispatcherServlet -> HandlerMapping -> (Controller -> Service -> DAO -> DB -> DAO -> Service -> Controller) -> DispatcherServlet -> ViewResolver -> View -> DispatcherServlet -> Response

 

자세히 보기

  1. 클라이언트가 Request 요청 시 DispatcherServlet이 web.xml에  등록된 내용을 가로챈다. 최초의 web.xml 에서는 이 '/' default라서 이 범위를 바꿔주면 특정 URL만 적용 가능하다.
  2. DispatcherServlet이 가로챈 요청을 HandlerMapping에게 보내 해당 요청을 처리할 수 있는 Controller가 있는지 확인
  3. 실제 로직 처리
  4. 로직 처리 후 ViewResolver를 통해 ViewResolver가 view 화면을 찾는다.
  5. 찾은 View 화면은 View에 보내면 이 결과를 다시 DispatcherServlet에게 보내고 DispatcherServlet는 최종 클라이언트에게 전송한다.

 

Spring FrameWork 장점

  • 프레임워크 특화 기능들이 많아서 간단한 컴포넌트로 복잡한 애플리케이션을 구성할 수 있음
  • AOP -> 코드를 깔끔하게 작성 가능, 재사용성 올라감
  • DI -> 설정 파일 부분에서 객체를 가지고 오니까는 다양한 DB 사용, DB마다 메서드 바꿀 필요 없음

 

Spring FrameWork 단점

  • Spring Boot가 아닐 시 초기 환경 구축에 많은 시간이 소요
  • 설정이 복잡
  • 컴포넌트로 존재하지 않는 기능을 사용할 경우 개발 공수가 크다.

 

💎 Node.js Express Framework

Node.js Express Framework 장점

  • 싱글그레드, 비동기 IO 처리에 기반한 빠른 속도 -> CPU 대기시간 최소화, CPU 부하 적음 -> 많은 커넥션을 동시에 처리해야 하는 구조에 적합
  • Chrome의 V8 자바스크립트 엔진을 기반으로 성능이 뛰어남
  • 파일 I/O나 네트워크 처리를 이벤트 드리븐 방식으로 처리하기 때문에 빠른 처리가 가능함

 

Node.js Express Framework 단점

  • 싱글 스레드 모델이라 하나의 작업에 시간이 오래 걸리면 시스템 전체의 성능이 급격히 낮아진다.
  • 불편한 비동기 프로그래밍 모델 -> Async & Await 모듈
  • 멀티코어를 사용하려면 Cluster 모듈 이용, 세션 공유 시 부가적인 작업 필요

 

멀티코어를 이용하고 무중단 서버를 가능하게 하는 PM2

exec_mode  항목값을 ‘cluster’로 설정하면 애플리케이션을 클러스터 모드로 실행하겠다는 의미이고, instance  항목값을 ‘0’으로 설정하면 CPU 코어 수 만큼 프로세스를 생성

🤞 Node.js 용어 정리

Node.js : 런타임 환경

Express : NodeJS Framework

NPM : NodeJS package manager

PM2 : NodeJS process manager

 

 

참고자료

https://devpad.tistory.com/24

https://elky.tistory.com/652

https://engineering.linecorp.com/ko/blog/pm2-nodejs/

'Server' 카테고리의 다른 글

인증 방법인 Cookie와 Session에 대해 알아보자  (0) 2021.03.24
JWT에 대해 알아보자 ❗  (0) 2021.01.16
Web Server와 WAS의 정의와 차이  (0) 2021.01.15

HTTP 프로토콜은 무상태 프로토콜로써 어떠한 상태도 유지하지 않고 비연결성이다. (Stateless + Connectionless)

따라서 로그인을 하면 테이터 요청에 대해 일시적으로 이루어진다.

 

그러면 서버에게 정보를 요청한 사용자가 이전의 사용자와 같은 사용자인지 확인하는 것이 필요하다. 즉 인증이 필요한 것이다.

 

인증이란 무엇일까 ❓

✌ 인증

API 요청에 대해 사용가능한 사용자인지 확인하는 절차로 클라이언트가 주장하는 사용자와 같은 사용자인지 확인한다.

 

✌ 인가

사용자가 특정 자원에 대한 접근 권한이 있는지 권한을 체크하는 것으로 클라이언트가 하고자 하는 작업이 해당 클라이언트에게 허가된 작업인지 확인한다. 따라서 사용자의 권한 레벨에 따라 접근할 수 있는 부분을 제한할 수 있다.

 

 

인증 방법에 대해 비교해보자 ❗

인증 방법 Cookie Session
저장 위치 클라이언트 서버
보안 클라이언트 로컬에 저장되기 때문에 변질되거나 스파이핑 당할 수 있음 쿠키를 이용해서 세션 ID만 저장/구분해서 서버에서 처리하기 때문에 비교적 보안성 좋음
만료 만료 시간을 정할 수 있지만 브라우저를 종료해도 계속해서 정보가 남아 있음 브라우저가 종료되면 만료시간에 상관없이 삭제

 

💡 Cookie

클라이언트 로컬에 저장되는 키와 값이 들어있는 작은 데이터 파일

쿠키는 단순한 키 - 값쌍이고 일정 시간 동안 저장할 수 있고 클라이언트 쪽에 300개까지 저장 가능하다.

서버로부터 쿠키가 오면 웹 부라우저는 쿠키를 저장해 두었다가 요청 시 브라우저가 자동으로 쿠키 같이 보냄

쿠키는 요청과 응답의 헤더에 저장된다.

 

💡 Session

일정 시간 동안 같은 브라우저로부터 들어오는 일련의 요구를 하나의 상태로 보고 그 상태를 유지하는 기술

 

웹 브라우저를 통해 웹 서버에 접속한 이후로 브라우저를 종료할 때까지 유지되는 상태이다.

클라이언트는 발급받은 세션 ID를 쿠키를 사용하여 저장하고 세션은 서버 메모리에 저장된다.

서버가 재시작(메모리가 리셋)되면 세션 데이터는 사라진다.

세션은 서버의 자원을 사용하기에 무분별하게 만들다보면 서버의 메모리가 감당할 수 없어질 수 있고 속도가 느려질 수 있다. 

 

 

 

 

 

'Server' 카테고리의 다른 글

Django, Spring, Node.js의 특징과 장단점  (1) 2021.07.14
JWT에 대해 알아보자 ❗  (0) 2021.01.16
Web Server와 WAS의 정의와 차이  (0) 2021.01.15

AOP(Aspect Oriented Programming)는 관점 지향 프로그래밍으로 하나의 비즈니스 로직은 관심사와 핵심적인 로직으로 이루어져 있다. 

 

여기서 관심사소스 코드상에서 계속 반복되는 부분으로 예외처리, 인증 등이 해당된다. 즉 주변 로직, 부가기능으로 볼 수 있다.

 

💡 관심사의 예

1. 파라미터가 올바르게 들어왔을까??

 

2. 사용자의 인증을 적절히 받아왔을까??

 

3. 이 작업에서 발생할 수 있는 예외 처리는 어떻게 해야 할까??

 

즉 AOP의 목표는 핵심적인 로직에서 관심사를 분리하여 재사용하겠다는 것이다. 이것을 관심사의 분리(=Separate concerns)라고 부른다. 관심사를 분리하면 코드의 반복을 줄일 수 있기 때문이다.

 

AOP 개발방식에서 개발자가 주의해야 하는 것은 무엇일까 ❓

관심사와 핵심 로직은 컴파일 혹은 실행시간에 결합되기에 어떤 관심사를 붙일 것인지만 고려하면 된다!!

 

👀 AOP용어 살펴보기

Target : Aspect를 적용하는 곳 (클래스, 메서드..) 즉, 핵심 비즈니스 로직을 의미한다.

Proxy : 내부적으로 관심사들을 거쳐서 Target을 호출하도록 자동 혹은 수동으로 작성한다.

Proxy 객체를 통해 Target 객체의 JoinPoint 호출한다.

JoinPoint : Target 객체가 가지는 여러 메서드 

 

Aspect과 JoinPoint가 결합되는 것을 자세히 살펴보자 ❗

Aspect : 흩어진 관심사를 모듈화 한 것을 의미한다.

Advice : 실질적인 부가기능을 담은 구현체로 관심사를 의미한다.

Pointcut : 관심사와 비즈니스 로직이 결합되는 지점을 결정하는 것으로 Joinpoint가 가지는 여러 메서드 중 어떤 메서드로 연결할지 설정한다.

 

💡 Pointcut 선언 Annotation

구분 설명
execution(@excution) 메서드를 기준으로 Pointcut 설정
within(@within) 클래스를 기준으로 Pointcut 설정
this 주어진 인터페이스를 구현한 객체에 Pointcut 설정
args(@args) 특정한 파라미터를 가지는 대상들에 Pointcut 설정
@annotaion 특정한 어노테이션이 적용된 대상들에 Pointcut 설정

 

 

+ Recent posts