이번 미션은 프론트엔드에 관한 영역도 섞여있어서, 강사님께서 꼼꼼하게 다 실습하길 요구하시진 않으셨다. 하지만 HTTP2 적용과 같은 내가 반드시 알아야할 과정도 포함되어 있었기 때문에 그러한 부분은 실습을 해보았고, redis를 이용한 was 성능 향상과 같은 백엔드의 영역에 집중해서 적용하지 않은 경우와 적용한 경우의 평균 속도를 비교해보았다. 그런 의미에서 미션 요구사항을 다시 정리해보면 아래와 같다.
화면 응답 개선하기 요구사항
정적 파일(웹 폰트 및 번들) 경량화 (실습생략)
Reverse Proxy 개선(HTTP2 적용 및 gzip 적용)
Redis 이용한 성능개선
2. 미션 실습
1) Reverse Proxy 개선(HTTP2 적용 및 gzip 적용)
아래는 강의 자료에서 예제로 알려준 nginx 설정이며 나의 실습 사이트에 그대로 적용하였다. http2는 SSL 계층 위에서만 동작한다고 하는데, 그렇지 않은 곳에 적용하면 어떻게 되는지 실험해보고 싶다.
## CPU Core에 맞는 적절한 Worker 프로세스를 할당
worker_processes auto;
events { worker_connections 10240; } ## Worker Process가 수용할 수 있는 Connection 수
http {
gzip on; ## http 블록 수준에서 gzip 압축 활성화
gzip_comp_level 9;
gzip_vary on;
gzip_types text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/rss+xml text/javascript image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype;
## Proxy 캐시 파일 경로, 메모리상 점유할 크기, 캐시 유지기간, 전체 캐시의 최대 크기 등 설정
proxy_cache_path /tmp/nginx levels=1:2 keys_zone=mycache:10m inactive=10m max_size=200M;
## 캐시를 구분하기 위한 Key 규칙
proxy_cache_key "$scheme$host$request_uri $cookie_user";
upstream app {
least_conn; ## 현재 connections이 가장 적은 server로 reqeust를 분배
server 172.17.0.1:8080 max_fails=3 fail_timeout=3s;
server 172.17.0.1:8081 max_fails=3 fail_timeout=3s;
}
# Redirect all traffic to HTTPS
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/fistkim.kro.kr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/fistkim.kro.kr/privkey.pem;
# Disable SSL
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# 통신과정에서 사용할 암호화 알고리즘
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
# Enable HSTS
# client의 browser에게 http로 어떠한 것도 load 하지 말라고 규제합니다.
# 이를 통해 http에서 https로 redirect 되는 request를 minimize 할 수 있습니다.
add_header Strict-Transport-Security "max-age=31536000" always;
# SSL sessions
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
## proxy_set_header : 뒷단 서버로 전송될 헤더 값을 다시 정의해주는 지시어
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
location ~* \.(?:css|js|gif|png|jpg|jpeg)$ {
proxy_pass http://app;
## 캐시 설정 적용 및 헤더에 추가
# 캐시 존을 설정 (캐시 이름)
proxy_cache mycache;
# X-Proxy-Cache 헤더에 HIT, MISS, BYPASS와 같은 캐시 적중 상태정보가 설정
add_header X-Proxy-Cache $upstream_cache_status;
# 200 302 코드는 20분간 캐싱
proxy_cache_valid 200 302 10m;
# 만료기간을 1 달로 설정
expires 1M;
# access log 를 찍지 않는다.
access_log off;
}
location / {
proxy_pass http://app;
}
}
}
적용하였더니 실제로 아래와 같이 캐시를 적용한 대로 header에 반영되어 있었다.
2) Redis 이용한 성능개선
특정한 상황을 부여하여 Redis 적용 전과 후의 TPS를 비교하는 실습이다. Redis 개념 자체에 대해서는 잘 정리된 자료들이 많기 때문에 따로 정리할 필요는 없을 것 같다.
Redis 적용 시나리오
출근시간에 ‘경로검색’ 기능을 사용하는 동시접속 유저가 많고, 특히 이 중 ‘경로검색’ 기능을 통해서 회사가 밀집한 ‘강남역’을 도착역으로 지정하여 검색하는 인원이 많다고 가정
출발역은 기본적으로 저장되어 있는 ‘양재역’, ‘교대역’, ‘남부터미널역’ 이며 도착역은 ‘강남역’으로 부하테스트 실행
아래 스크립트로 k6 를 이용한 load 테스트를 실시하였다.
import http from 'k6/http';
import {check} from 'k6';
export let options = {
stages: [
{duration: '5s', target: 100},
{duration: '20s', target: 200},
{duration: '5s', target: 0},
],
};
const BASE_URL = 'https://fistkim.kro.kr';
export default () => {
// lending page
let homeUrl = `${BASE_URL}`;
let lendingPageResponse = http.get(homeUrl);
check(lendingPageResponse, {
'lending page running': (response) => response.status === 200
});
// Yangjae search line
let YangjaeSearchLineUrl = `${BASE_URL}/paths/?source=1&target=2`;
let YangjaeSearchLineResponse = http.get(YangjaeSearchLineUrl);
check(YangjaeSearchLineResponse, {
'Yangjae line searching success': (response) => response.status === 200
});
// Gyodae search line
let GyodaeSearchLineUrl = `${BASE_URL}/paths/?source=3&target=2`;
let GyodaeSearchLineResponse = http.get(GyodaeSearchLineUrl);
check(GyodaeSearchLineResponse, {
'Gyodae line searching success': (response) => response.status === 200
});
// South-Terminal search line
let SouthTerminalSearchLineUrl = `${BASE_URL}/paths/?source=4&target=2`;
let SouthTerminalSearchLineResponse = http.get(SouthTerminalSearchLineUrl);
check(SouthTerminalSearchLineResponse, {
'South-Terminal line searching success': (response) => response.status === 200
});
};