콘텐츠가 많다면 정적 사이트를 웹 서버에 배포할 때 지나치게 오랜 시간이 걸릴 수도 있다. 배포 과정이 길어지면 여러가지 문제가 발생할 수 있다. 제한된 환경에서 추가적인 도구없이 딱 필요한 파일들만 파악하여 웹 서버에 전송해주는 bash script를 작성해보자.
Next.js 정적 사이트 효율적으로 배포하기 - Deploying Next.js static exports with bash
Introduction
정적 사이트 배포 방법
이 블로그는 카페24에서 제공하는 아주 간단하고 저렴한 웹 서버에 배포되어 있다. 이 서버의 역할은 단지 www
라는 디렉토리에 들어있는 html 파일들을 브라우저에서 열람할 수 있게 해주는 것 뿐이다. 예를 들어, www/test.html
라는 파일이 웹 서버에 존재한다면, 브라우저에서 https://<SERVER_ADDRESS>/test.html
로 접근이 가능하다.
로컬 개발 환경에서 블로그에 새 글을 쓰거나 UI 코드 변경을 하는 경우 build 과정을 거치면 out
이라는 디렉토리가 생기고 이 안에 여러 html, js, css 파일들이 생성된다. Next.js의 static export 기능을 활성화해두었기 때문이다. 이 out
디렉토리 안의 내용물들을 웹 서버의 www
디렉토리에 옮기기만 하면 간단하게 배포가 완료된다.
문제 인식
블로그 운영 초기에는 콘텐츠가 별로 없었기 때문에 원격 서버에 있는 파일들을 rm -rf
명령어로 모두 제거한 뒤 로컬에 빌드된 파일들을 scp
명령어를 통해 웹 서버로 옮기는 것만으로 충분했다. 그런데, 콘텐츠가 많아지다보니 옮겨야 하는 파일 수와 크기가 점점 증가해서 배포 시간에 딜레이가 생기기 시작했다. rm -rf
를 실행한 순간부터 모든 파일이 웹 서버에 복사되기 전까지 블로그에 접근하는 사람들과 크롤러들이 404 에러를 만나게 된다.
문제는 점점 심해졌다. 구글이나 네이버 등의 검색 서비스에서 색인을 진행하는 과정에서 404 에러가 발생하여 일부 콘텐츠들이 검색 결과에 노출되지 않는 현상이 있었다. 그리고, 개인적으로는 금전적인 패널티도 얻게 되었다. 참여중인 글쓰기 커뮤니티에 특정 시점까지 글을 작성해서 URL을 제출하지 않으면 일종의 벌금을 내야 하는 정책이 있다. 마감 시간 직전에 잘 제출해놓고, 실제 제출 여부를 판단하는 봇이 블로그를 방문하는 시점에 블로그 기능 수정으로 인한 배포 과정이 진행되면서 미제출처리가 된 적이 있다.
이 글에서 다루는 것
더 좋은 방법을 찾지 않고 게을러서 그런 것 아니냐고 하면 할 말이 없지만, 굳이 핑계를 대자면 웹 서버 환경이 매우 열악해서 apt
같은 패키지 관리자를 사용할 수 없다는 점을 강조하고 싶다. git
이나 rsync
같은 편리한 명령어도 사용이 불가능하다. 따라서, 이러한 편리한 도구들 없이 직접 효율적으로 빌드된 파일들을 전송할 수 있어야 한다.
$ git
bash2: git: command not found
$ rsync
bash2: rsync: command not found
따라서, 이 글에서는 별도의 도구 설치 없이 로컬 빌드 파일과 웹 서버에 업로드된 파일들을 효율적으로 동기화해주는 bash script를 작성하는 방법에 대해 알아보겠다. 실제로 이 "파일 동기화 방법"을 구현하여 이 블로그의 배포 과정에 적용한 결과, 전송 효율성이 약 80% 이상 향상되었고 전송 속도도 약 20% 이상 향상되었다. 그리고 배포 과정에서 발생하는 블로그의 down time은 기존 약 1분 정도였는데 개선 후 0초에 수렴하게 되었다.
파일 동기화 방법 소개
개선 목표
목표는 배포 과정에서 발생하는 down time을 최소화하는 것이다. Down time을 최소화하려면 적어도 웹 서버의 모든 파일을 지우는 rm -rf
명령어를 사용하면 안된다. 그리고, 콘텐츠의 양이 점점 많아진다는 것도 고려해야 한다. 빌드 결과물들 전체를 웹 서버에 전송하는 것이 아니라 딱 필요한 것들만 전송해야 한다.
평가 방법
개선하기 전과 후의 비교를 위해 평가 메트릭으로 "transfer efficiency improvement"라는 것을 정의하겠다. Transfer efficiency improvement는 로컬에서 웹 서버로 전송해야 하는 파일의 전체 용량에 비해 일부 파일만 전송할 때의 용량이 얼마나 작은지를 가늠할 수 있는 지표다. "전송 효율성이 몇 % 증가했는가?"에 대한 물음에 답하기 위한 값이라고 생각하면 된다. 다음과 같은 식으로 정리하겠다.
transfer_efficiency_improvement = (1 - (partial_size / total_size)) * 100
추가적으로 배포 과정에 소요되는 시간도 측정하여 전송 효율성과 마찬가지로 "몇 % 정도 빨라졌는지" 평가해보도록 하겠다.
개선 방법 요약
여러가지 편리한 방법들이 있겠지만, 웹 서버 환경의 제약때문에 "모든 작업은 로컬에서 진행해야 한다"는 가정으로 출발해야 한다. 따라서, 다음과 같은 방식으로 꼭 필요한 파일들만 전송하는 기능을 구현해보도록 하겠다.
- 로컬 개발 환경에서 static export로 빌드하여
out
디렉토리를 얻는다. - 웹 서버의
www
디렉토리 내 모든 파일을 로컬live
디렉토리에 다운로드한다. diff
를 통해 로컬의out
과live
디렉토리의 차이를 파악한다.- 차이가 나는 파일들만 추려서
scp
와rm
명령어를 통해 웹 서버와 동기화한다.
파일 동기화 구현하기
구현 산출물 정의
추가적인 도구들의 설치없이 명령어로 간편하게 진행 가능해야 하기 때문에 bash script 형태로 작성하도록 하겠다. 이때, 다음과 같이 다양한 기능을 제공하여 실제 동기화 작업 외에도 평가 또는 정합성 검증 등에 활용할 수 있도록 만들어보자.
test
: 주어진 두 디렉토리가 정확히 동기화되었는지 확인sync
: 실제 파일 동기화 작업 진행analyze
: 개선 전후의 transfer efficiency 분석
예를 들어, 다음과 같이 사용할 수 있는 형태로 구현할 것이다.
$ bash deploy.sh -s source -d destination test
$ bash deploy.sh -s source -d destination sync
$ bash deploy.sh -s source -d destination analyze
로컬 개발 환경의 운영체제에 따라 명령어가 다소 변경될 수도 있다. 이 글에서는 macOS에서 실행하는 것을 가정하겠다.
입력 부분 구현
사용자가 스크립트를 실행할 때, 옵션을 통해 동기화를 진행할 두 디렉토리와 test
, sync
, analyze
중에서 실행할 함수를 선택할 수 있도록 해주자. 다음의 코드를 deploy.sh
파일의 하단부에 작성해주면 된다.
#!/bin/bash
...
while getopts "s:d:" opt; do
case "$opt" in
s) src="$OPTARG" ;;
d) dst="$OPTARG" ;;
*) echo "usage: $0 -s source -d destination {test|sync|analyze}"; exit 1;;
esac
done
shift $((OPTIND - 1))
case "$1" in
test) test ${src:-"out"} ${dst:-"live"};;
sync) sync ${src:-"out"} ${dst:-"live"};;
analyze) analyze ${src:-"out"} ${dst:-"live"};;
*) echo "usage: $0 -s source -d destination {test|sync|analyze}"; exit 1;;
esac
현재 로컬 개발 환경에는 Next.js 블로그를 빌드하는 경우 기본적으로 out
디렉토리에 결과물이 저장되도록 설정되어 있다. 그리고, 웹 서버의 파일들을 로컬에 다운로드할 때는 live
디렉토리에 저장할 것이다. 이렇게 미리 정해둔 원칙이 있다면, 매번 -s
와 -d
옵션으로 번거롭게 직접 디렉토리를 지정할 필요는 없다. 따라서, ${src:-"out"}
과 ${dst:-"live"}
등 옵션이 입력되지 않는 경우 기본값을 사용하도록 처리하였다.
정합성 검증 기능 구현
정합성 검증을 위한 test
기능은 어떻게 구현하면 좋을까? 파일 동기화를 수행하기 전과 후에 각각 test
를 실행하여 차이를 확인할 수 있도록 해주면 편리할 것이다. 다음과 같이 두 디렉토리 사이의 차이를 출력하고 line 수를 count하여 현재 총 몇 개의 파일이 다른지 알려주도록 최대한 간단하게 구현해보자.
test() {
count=0
while IFS= read -r file; do
echo $file
((count++))
done < <(diff -rq $1 $2 | grep -v ".DS_Store")
echo "Total files: $count"
}
위 코드에서 핵심은 diff -rq $1 $2
명령어를 활용하는 것이다. 함수 인자로 주어진 두 디렉토리에 diff -rq
명령어를 사용하면 다음과 같이 세 가지의 정보를 포함한 파일 목록을 간편하게 확인할 수 있다.
$1
에는 있지만$2
에는 없는 파일$1
에는 없지만$2
에는 있는 파일$1
과$2
에 둘 다 있지만 내용이 다른 파일
위 세 가지 경우는 다음과 같은 형식으로 출력된다. test
함수 실행 결과를 통해 살펴보자. 이후 이 형식을 바탕으로 transfer efficiency 분석 로직 analyze
와 파일 동기화 로직 sync
를 작성해야 하므로 잘 기억해두어야 한다.
Only in out/_next: dCQDvQk02J6cVNonbYuj_
Only in live/_next/data: n5QP2lnWVncuk9vC1ciP0
Files out/404/index.html and live/404/index.html differ
Total files: 3
한 가지 코드에서 주의할 점은 while 루프를 작성할 때 < <(command)
문법으로 process substitution을 적용해서 subshell이 아니라 현재 shell에서 실행되도록 해야 count가 제대로 증가된다는 것이다. 그리고 grep -v ".DS_Store"
를 통해 macOS에서 생성하는 파일이 포함되지 않도록 필터링해주는 것도 잊으면 안된다.
이 test
함수를 실행했을 때, 최종 결과가 Total files: 0
으로 출력된다면 두 디렉토리 사이의 파일 동기화가 완료된 것이라고 판단할 수 있다. 이어지는 sync
를 통해 실제 파일 동기화 작업을 진행한 후 작업 완료 여부를 판단하기 위한 기준으로 활용하도록 하자.
파일 동기화 기능 구현
두 디렉토리에서 감지된 차이를 해결하기 위해서는 앞서 살펴본 세 가지 케이스에 대해 각각 다르게 처리해주어야 한다.
$1
에는 있지만$2
에는 없는 파일 -> 복사$1
에는 없지만$2
에는 있는 파일 -> 삭제$1
과$2
에 둘 다 있지만 내용이 다른 파일 -> 덮어쓰기
우선 복사는 다음과 같이 작성하면 된다.
server_path="${SERVER_USER}@${SERVER_IP}:/${SERVER_USER}"
remote_directory="www"
copy() {
while IFS= read -r file; do
src_path=$(echo $file | sed 's|: |/|' | awk '{print $3}')
dst_path=$(echo $src_path | sed "s|^$1/|$2/|")
remote_path=$(echo $src_path | sed "s|^$1/|$remote_directory/|")
# cp -r $src_path $dst_path
scp -r $src_path $server_path/$remote_path
done < <(diff -rq $1 $2 | grep -v ".DS_Store" | grep "Only in $1")
}
앞서 살펴봤던 형식 중 "Only in ..." 형식을 활용하여 source 디렉토리에만 포함된 파일을 걸러내는 과정이다. 코드 결과로 dst_path
에 로컬 환경의 live
디렉토리 하위 경로가 담기고, remote_path
에 웹 서버 기준인 www
디렉토리 하위 경로가 담긴다. 로컬에서 테스트 목적으로 동기화를 하기 위해서는 cp
명령어와 dst_path
를, 그리고 실제 웹 서버에 파일 전송하려면 scp
명령어와 remote_path
를 사용하면 된다.
삭제도 복사와 유사하다. "Only in ..."에서 source 디렉토리 대신 destination 디렉토리를 넣어주면 된다. 마찬가지로 로컬 테스트와 실제 배포 과정에 서로 다른 경로와 명령어를 사용해야 한다는 점만 주의하면 된다. ssh
명령어를 통해 원격 서버의 파일을 삭제할 때는 비밀번호를 요구할 수도 있으므로, 매번 비밀번호를 입력하지 않아도 되도록 미리 설정해두자.
delete() {
while IFS= read -r file; do
dst_path=$(echo $file | sed 's|: |/|' | awk '{print $3}')
remote_path=$(echo $dst_path | sed "s|^$2/|$remote_directory/|")
echo $dst_path $server_path/$remote_path
# rm -rf $dst_path
ssh -n ${SERVER_USER}@${SERVER_IP} "rm -rf $remote_path"
done < <(diff -rq $1 $2 | grep -v ".DS_Store" | grep "Only in $2")
}
덮어쓰기도 큰 차이는 없다. 단, "Only in ..." 대신 grep -E
옵션과 정규표현식을 활용하여 "Files ... differ" 형식의 라인들을 걸러내면 된다.
overwrite() {
while IFS= read -r file; do
src_path=$(echo $file | awk '{print $2}')
dst_path=$(echo $file | awk '{print $4}')
remote_path=$(echo $file | awk '{print $4}' | sed "s|^$2/|$remote_directory/|")
# cp -r $src_path $dst_path
scp -r $src_path $server_path/$remote_path
done < <(diff -rq $1 $2 | grep -v ".DS_Store" | grep -E "Files.*differ")
}
이제 세 가지 함수를 sync
라는 함수에 담기만 하면 완성이다. 이때, down time을 최소화하기 위해서 웹 서버에 이미 배포되어 있는 파일을 삭제하는 로직은 가장 마지막에 수행하도록 약간의 트릭을 사용하였다. 해당 파일들을 참조하고 있는 페이지가 있는 경우, 사용자가 브라우저를 통해 페이지를 조회할 때 일시적으로 오류가 발생할 수 있기 때문이다.
sync() {
copy $1 $2
overwrite $1 $2
delete $1 $2
echo "Complete."
}
전송 효율 분석 기능 구현
열심히 구현한 파일 동기화 로직이 기존에 무식하게 모든 파일을 전송하는 것에 비해 얼마나 효율적인지 평가하기 위해 분석 로직을 구현할 차례다. 앞서 평가 방법에서 언급한 transfer efficiency improvement 메트릭을 출력해주는 함수를 작성해보자.
analyze() {
# Before
total_count=$(find "$1" -type f | wc -l | awk '{print $1}')
total_size=$(find "$1" -type f -exec stat -f %z {} + | awk '{s+=$1} END {print s}')
echo "Before:"
echo "Total files: $total_count"
echo "Total file size: $(echo "scale=2; $total_size/1024/1024" | bc) MB"
# After
partial_count=0
partial_size=0
while IFS= read -r file; do
src_path=$(echo $file | sed 's|: |/|' | awk '{print $3}')
file_size=$(stat -f %z "$src_path")
partial_size=$((partial_size + file_size))
((partial_count++))
done < <(diff -rq $1 $2 | grep -v ".DS_Store" | grep "Only in $1")
while IFS= read -r file; do
src_path=$(echo $file | awk '{print $2}')
file_size=$(stat -f %z "$src_path")
partial_size=$((partial_size + file_size))
((partial_count++))
done < <(diff -rq $1 $2 | grep -v ".DS_Store" | grep -E "Files.*differ")
echo ""
echo "After:"
echo "Total files: $partial_count"
echo "Total file size: $(echo "scale=2; $partial_size/1024/1024" | bc) MB"
echo ""
transfer_efficiency_improvement=$(echo " scale=2; (1 - ($partial_size / $total_size)) * 100" | bc)
echo "The transfer efficiency can be improved by $transfer_efficiency_improvement%."
}
이 함수는 우선 source 디렉토리의 전체 파일 개수와 용량을 파악하여 출력해준다. 그리고 앞서 살펴본 파일 동기화 로직에 의해 영향을 받을 파일들만 선별하여 개수와 용량을 출력해준다. 이 정보를 바탕으로 transfer efficiency improvement를 계산하여 파일 동기화 로직을 적용했을 때 얼마나 효율성이 향상되는지 출력해준다.
코드가 다소 길어서 복잡해보이지만, 자세히 보면 앞서 파일 동기화 로직에서 살펴봤던 복사와 덮어쓰기 부분과 코드가 거의 유사한 것을 확인할 수 있다. 제거의 경우 단순히 원격 서버에서 일부 파일을 제거하는 작업이라 전송을 위한 네트워크 비용이 발생하지 않기 때문에 제외하였다.
파일 동기화를 활용한 정적 사이트 배포 결과
테스트 준비
먼저 변경사항이 발생한 블로그를 static export로 빌드하여 out
디렉토리를 생성해두자. 그리고 scp
명령어를 통해 원격 웹 서버에 있는 파일들을 로컬의 live
디렉토리에 다운로드해두자. 웹 서버 접속 정보는 환경변수에 미리 설정해두어야 한다. 로컬 테스트와 실제 배포 과정을 독립적으로 수행하기 위해 live
디렉토리의 내용들을 live_backup
에 백업해두자.
$ npm run build
$ scp -r ${SERVER_USER}@${SERVER_IP}:/${SERVER_USER}/www live/
$ cp -r live live_backup
효율성 분석
이제 앞서 구현해둔 analyze
기능을 사용하면 기존 통째로 out
파일을 업로드하는 것에 비해 얼마나 효율적으로 파일 동기화를 할 수 있는지 확인할 수 있다. 대략 블로그 글 하나와 이미지 하나가 추가되었을 때의 결과를 확인해보겠다.
$ bash deploy.sh -s out -d live analyze
Before:
Total files: 1777
Total file size: 47.28 MB
After:
Total files: 83
Total file size: 7.53 MB
The transfer efficiency can be improved by 85.00%.
기존에는 거의 1500개 이상의 파일을 복사했어야 하지만, 파일 동기화를 통해 약 100개 미만의 파일만 전송해도 된다는 사실을 파악할 수 있다. 그리고 파일 용량에 대한 전송 효율성도 약 80% 이상 개선된다는 것을 확인할 수 있다.
로컬 테스트
실제 웹 서버에 배포하기 전에 로직이 정상적으로 동작하는지 로컬 개발 환경에서 정합성을 확인해보는 과정이 필요하다. 앞서 구현해둔 test
기능을 통해 현재 로컬에 빌드된 파일들과 블로그에 배포된 파일들의 차이를 분석해보자. 현재 87개의 파일이 다르다는 것을 확인할 수 있다.
$ bash deploy.sh -s out -d live test
...
Total files: 87
이제 앞서 구현해둔 sync
기능을 통해 파일 동기화를 진행할 차례다. copy
, delete
, overwrite
함수에서 scp
와 ssh
명령어를 사용하는 부분들을 주석처리해두고, cp
와 rm
을 사용하는 부분들만 남긴 뒤 sync
기능을 실행해보자.
$ bash deploy.sh -s out -d live sync
Complete.
파일 동기화 작업이 완료된 뒤에 다시 test
기능을 실행해보면 out
과 live
디렉토리에 전혀 차이가 없다는 것을 확인할 수 있다. 이로써 로직의 정합성 검증이 완료되었다. 이제 남은 것은 실제 원격 웹 서버의 배포 과정에 이 로직을 활용하는 것이다.
$ bash deploy.sh -s out -d live test
Total files: 0
웹 서버에 파일 배포
실제로 원격 웹 서버에 로직을 사용해볼 차례다. 로컬 테스트에서 사용한 live
디렉토리는 제거한 뒤에 아직 동기화가 진행되지 않은 live_backup
디렉토리의 이름을 live
로 변경하자.
$ rm -rf live
$ mv live_backup live
로컬 테스트 시에 주석처리해둔 scp
와 ssh
명령어들을 다시 복원해두자. 그리고 로컬 테스트에서 사용했던 cp
와 rm
명령어는 다시 주석처리해두자. 그리고, 배포에 소요되는 시간을 측정하기 위해 sync
함수에 다음과 같이 소요 시간을 측정하고 출력해주는 코드를 삽입해두자.
sync() {
start_time=$(date +%s)
copy $1 $2
overwrite $1 $2
delete $1 $2
end_time=$(date +%s)
elapsed_time=$((end_time - start_time))
echo "Elapsed time: ${elapsed_time}s"
echo "Complete."
}
스크립트를 실행해보면 파일 동기화를 위해 43초가 소모되었다는 것을 확인할 수 있다. 매우 빠르게 배포가 진행된다는 것을 알 수 있다. 심지어 웹 서버에 이미 배포되어 있는 파일을 제거하는 과정이 최소화되기 때문에 블로그의 down time은 거의 0초에 수렴할 것이다.
$ bash deploy.sh -s out -d live sync
Elapsed time: 43s
Complete.
제대로 배포가 되었는지 정합성 검증을 위해 원격 웹 서버의 www
파일들을 다시 로컬의 live
디렉토리에 다운로드한 뒤에 test
기능을 수행하여 정합성 검증을 해보자. 로컬 개발 환경에 빌드된 디렉토리와 차이가 없음을 확인할 수 있다.
$ rm -rf live
$ scp -r ${SERVER_USER}@${SERVER_IP}:/${SERVER_USER}/www live/
$ bash deploy.sh -s out -d live test
Total files: 0
기존 방식과 비교
그렇다면 기존에 빌드 결과물 전체를 웹 서버로 복사하던 과정은 얼마나 시간이 소요되고 있었을까? 비교를 위해 다음과 같이 deploy.sh
스크립트에 시간 측정과 단순 복사 과정을 포함한 dump
라는 함수를 추가해주자. 명령어로 수행할 수 있도록 case문에도 추가하였다.
dump() {
start_time=$(date +%s)
ssh ${SERVER_USER}@${SERVER_IP} "cd /$SERVER_USER/$remote_directory && rm -rf * .well-known"
scp -r $1/* $1/.well-known $server_path/$remote_directory
ssh ${SERVER_USER}@${SERVER_IP} "rm -rf /$SERVER_USER/$remote_directory/.DS_Store"
end_time=$(date +%s)
elapsed_time=$((end_time - start_time))
echo "Elapsed time: ${elapsed_time}s"
echo "Complete."
}
while getopts "s:d:" opt; do
case "$opt" in
s) src="$OPTARG" ;;
d) dst="$OPTARG" ;;
*) echo "usage: $0 -s source -d destination {test|analyze|sync|dump}"; exit 1 ;;
esac
done
shift $((OPTIND - 1))
case "$1" in
test) test ${src:-"out"} ${dst:-"live"};;
analyze) analyze ${src:-"out"} ${dst:-"live"};;
sync) sync ${src:-"out"} ${dst:-"live"};;
dump) sync ${src:-"out"} ${dst:-"live"};;
*) echo "usage: $0 -s source -d destination {test|analyze|sync|dump}"; exit 1;;
esac
dump
기능을 사용해서 기존 배포 과정의 소요 시간을 확인해보면 다음과 같다.
$ bash deploy.sh -s out -d live dump
Elapsed time: 57s
Complete.
기존 방식(57초)에 비해 파일 동기화를 활용한 방식(43초)이 시간 효율성 측면에서 약 25% 정도 개선되었다. 앞서 analyze
기능을 통해 살펴봤듯이 dump
는 파일 동기화 방식에 비해 전송을 해야 하는 파일의 수가 더욱 많고 파일의 용량도 더욱 크기 때문이다. 네트워크 속도가 느린 환경에서는 차이가 더욱 벌어질 것이라고 쉽게 예상할 수 있다.
그리고 기존 dump
방식은 웹 서버에 업로드된 파일들을 모두 제거한 뒤 로컬의 모든 파일들을 복사한다. 이는 파일 제거 후 사이트 페이지 렌더링에 필요한 파일들이 모두 복사되기 전까지 웹 사이트에 down time이 발생한다는 뜻이다. 결과를 해석해보면 기존에 블로그에 새로운 글을 배포할 때마다 약 1분 정도의 시간동안 사용자들이 브라우저에서 404 에러 페이지를 만나고 있었다는 뜻이다. sync
의 경우 down time이 0에 수렴하기 때문에 큰 개선이라고 할 수 있다.
Discussion
전체 내용 요약
지금까지 써드파티 도구들을 사용하는 것이 극한으로 제한된 웹 서버 환경에 Next.js의 static export로 빌드한 정적 사이트를 효율적으로 배포하는 방법에 대해 알아보았다. 코드는 다소 복잡하지만, 로컬 환경에 웹 서버의 코드를 다운로드한 뒤 diff -rq
명령어를 통해 차이를 감지하여 필요한 파일들만 전송하고 불필요한 파일들은 제거한다는 것이 파일 동기화 로직의 핵심이다. 로직의 정합성 및 효율성 검증을 위해 추가적인 기능들도 함께 살펴보았다.
구현한 로직을 테스트한 결과, 정적 사이트 빌드 결과물 전체를 복사하는 것에 비해 전송 효율성은 약 80% 이상 향상되었고, 시간 효율성은 약 20% 이상 향상된 것을 확인할 수 있었다. 사실 전송 속도보다는 웹 사이트의 down time을 최소화하는 것이 중요한데, 이 글에서 소개한 파일 동기화 방식의 경우 이미 배포되어 있는 파일을 삭제하는 과정이 최소화되기 때문에 배포 과정에서 사용자들이 404 에러를 만날 확률은 거의 0에 수렴한다고 볼 수 있다.
추가 개선사항
이미 충분히 개선이 되었다고 볼 수 있지만, 배포 속도를 더 향상시키려면 bash script에 작성한 함수들에서 매번 diff -rq
를 사용하지 않고 차이나는 파일 목록을 미리 파일에 저장해둔 뒤 참조하는 방식을 사용하면 된다.
또한 Next.js를 통해 빌드한 정적 사이트의 경우 단순히 콘텐츠를 추가함에 따라 변경되는 빌드 결과물의 범위가 매우 제한적이라는 사실에 착안하면 추가적인 성능 향상을 달성할 수 있다. 변경이 되는 일부 파일들만 특정하여 로직을 수행하는 것이다.
그리고 현재 파일 동기화 로직에서는 기본적으로 while 루프를 돌면서 scp
명령어를 수행하기 때문에 비효율적이다. scp
명령어 호출을 최소화하도록 batch로 파일들을 묶어서 한꺼번에 처리하는 등의 추가 개선을 통해 로직 수행에 소요되는 시간을 줄일 수 있다.
주의사항
사실 웹 서버에서 git
이나 rsync
등의 도구들을 다운로드하여 사용할 수 있다면, 이 글에서 다룬 복잡한 과정들을 수행하지 않고 더욱 편리하고 쉽게 정적 사이트를 배포할 수 있다. 이 삽질성 글이 탄생한 이유는 저렴하다는 이유로 카페24를 선택한 필자에게 있다. 머리가 안좋으면 몸이 고생한다는 말이나, 공짜를 좋아하면 머리가 빠진다는 말에 대해 다시 생각해보게 된다.
이 글에서 다룬 파일 동기화 방식의 맹점도 있다. 블로그의 down time이 최소화되는 것은 맞지만, 전체 배포 과정에 소요되는 시간을 엄격하게 생각해보면 오히려 기존의 무식한 복사 방식보다 더욱 오래걸린다. 왜냐하면 웹 서버에 배포되어 있는 파일들을 로컬에 다운로드받아야 한다는 제약이 있기 때문이다. 결국 빌드 파일 전체가 네트워크를 통해 옮겨지는 것은 동일하지만, 추가적인 동기화 과정이 있기 때문에 소요 시간은 더욱 오래걸리는 것이다.
이러한 한계를 이 글에서 굳이 언급하지 않은 이유는 사실 간단한 트릭을 적용해서 웹 서버에 배포되어 있는 전체 파일을 다운로드받지 않아도 되도록 만들 수 있기 때문이다. 파일 동기화를 수행한 뒤 로컬에 남아있는 out
디렉토리를 live
라는 이름으로 변경해주기만 하면 된다. 파일 동기화 로직의 정합성은 이미 검증되었기 때문에, 추가적으로 웹 서버의 파일들을 다운로드하지 않아도 로컬의 live
디렉토리와 내용이 같다는 것이 보장된다. 물론 이 방법도 블로그를 여러 사람이 관리하거나 로컬 개발 환경이 자주 변경되는 경우에는 유효하지 않다는 문제가 있다.
More Posts
Cache invalidation 전략
Cache는 일종의 임시 data이다. DB의 내용이 변경된다면 cache도 따라서 최신화되어야 한다. Cache에 오래된 data가 남아있으면 사용자에게 잘못된 정보를 제공할 수도 있다. 따라서, 유효하지 않은 cache data를 무효화하는 것이 중요하다. 다양한 캐시 무효화 전략을 살펴보자.
Argo workflow에 kubernetes resource request와 limit을 설정하는 방법
Kubernetes 환경에서 argo workflow를 통해 파이프라인을 실행할 때는 자원 할당에 대한 고민이 필요하다. 그런데, 일반적인 방식으로는 의도대로 자원 할당이 되지 않는다. Kubernetes 환경의 안정적인 운영을 위해 argo workflow에 자원 설정을 하는 방법을 알아보자.
Kubernetes legacy version들의 최후 - Kubernetes apt install GPG error
2024년 11월 초부터 ubuntu에 kubernetes 클러스터를 구성하기 위해 kubeadm, kubelet, kubectl 설치하려는데 GPG error가 발생하고 있다. Kubernetes의 package repository에서 이전 버전들을 더 이상 제공하지 않는 것일까? 어떤 문제인지 살펴보자.
Comments