운영·보안

개인 사이트 유지보수 체크리스트, 배포 때·매달·분기별로 나눴습니다

개인 사이트의 링크, 이미지, 인증서, 디스크, 업데이트, 권한과 백업을 배포 시·매월·분기별로 나눠 확인하는 실제 유지보수 방법입니다.

차콜 서버와 흰색 점검판, 오렌지색 렌치를 나란히 놓은 3D 클레이 이미지

개인 사이트는 한 번 만들어 공개하면 계속 같은 상태로 남아 있을 것 같았습니다. 특히 minml처럼 Astro로 만든 정적 사이트는 데이터베이스가 없고, 새 글을 올리지 않는 동안에는 서버에서 바뀌는 것도 많지 않아 보였습니다.

실제로 운영해보니 사이트가 열리는 것과 관리할 일이 없는 것은 달랐습니다. 글 안의 링크가 사라질 수 있고, 인증서는 만료일이 다가오며, 서버 업데이트 뒤에는 재부팅이 필요할 수 있습니다. 배포가 반복되면 과거 릴리스도 쌓입니다. 최근에는 홈페이지가 정상적으로 열리는 상태에서도 배포 파일 권한이 불필요하게 넓다는 사실을 발견했습니다.

처음에는 이 항목을 모두 월간 점검표에 넣으려고 했습니다. 하지만 새 글 주소와 파일 권한은 배포할 때마다 확인해야 하고, 백업 복구처럼 시간이 필요한 작업은 매달 형식적으로 반복하기보다 일정한 주기로 실제 시험하는 편이 나았습니다. 그래서 minml의 점검 항목을 배포할 때마다, 매달, 분기별 세 묶음으로 나눴습니다.

이 글은 모든 서버에 그대로 적용하는 보안 기준이 아닙니다. 개인이 정적 사이트와 Ubuntu 서버를 직접 운영하면서 2026년 8월 28일에 실제로 확인한 결과와, 그 결과를 다음 점검에 다시 사용할 수 있도록 정리한 기록입니다.

점검 결과를 정상·관찰·정리 예정으로 나눴습니다

체크리스트에 확인 표시만 남기면 다음 달에 무엇을 비교해야 할지 알기 어렵습니다. 그래서 결과를 세 상태로 구분했습니다.

상태 판단 기준 처리 방법
정상 현재 기준을 충족하고 추가 작업이 필요 없음 수치와 확인 날짜 기록
관찰 당장 문제는 아니지만 증가·만료 추이를 봐야 함 다음 점검에서 이전 값과 비교
정리 예정 서비스에 즉시 영향은 없지만 구성이 중복되거나 복잡함 별도 작업으로 범위를 확인한 뒤 수정

예를 들어 디스크 사용량 53%는 지금 바로 파일을 지울 상태로 보지 않았습니다. 다만 과거 릴리스가 23개 쌓여 있다는 사실과 함께 기록했습니다. 반대로 홈페이지가 200이어도 없는 주소가 200으로 열리면 잘못된 404 처리일 수 있으므로 정상으로 표시하지 않습니다.

배포할 때마다 확인하는 항목

배포 때문에 새로 생길 수 있는 문제는 다음 달까지 기다리지 않습니다. minml에서는 새 글을 올릴 때 빌드부터 공개 응답까지 자동 배포 과정에서 확인합니다.

1. 공개할 파일이 빌드에 모두 포함됐는지

이 글을 추가하기 직전 Astro 빌드에서는 다음 항목을 확인했습니다. 따라서 이 글이 공개되면 공개 글 수는 한 편 늘어납니다.

확인 항목 결과
공개 글 18편
공개에서 제외한 초안 10편
소개·문의·개인정보 페이지 3개
통합한 과거 주소의 리디렉션 6개
내부 경로 대상 28개
로컬 이미지 대상 19개

파일 개수를 승인 조건처럼 사용하지는 않습니다. 이 수치는 지난 배포와 비교해 글이나 이미지가 의도치 않게 빠지지 않았는지 확인하는 기준입니다. 초안 10편도 공개 빌드와 사이트맵에서는 제외되는지 함께 검사했습니다.

2. 새 페이지와 이미지가 실제 주소에서 열리는지

로컬 빌드 성공과 운영 서버 배포 성공은 다릅니다. 그래서 홈페이지뿐 아니라 새 글, 대표 이미지와 사이트맵을 각각 확인합니다. 이번 공개 점검 결과는 다음과 같았습니다.

공개 주소 유형 기대값 결과
홈페이지 200 200
사이트맵 200 200
최근 발행 글 200 200
최근 대표 이미지 200 200
존재하지 않는 시험 주소 404 404
통합된 과거 글 주소 301 301
같은 서버의 별도 사이트 200 200

없는 주소와 과거 주소를 따로 보는 이유가 있습니다. 존재하지 않는 모든 주소가 홈페이지로 열리면 방문자에게는 페이지가 있는 것처럼 보일 수 있고, 검색 시스템도 정상 문서와 오류 문서를 구분하기 어려워집니다. 통합한 글은 새 주소로 영구 이동하도록 301을 유지합니다.

같은 서버의 별도 사이트는 수정 대상이 아닙니다. 다만 공용 Nginx 설정을 실수로 바꾸지 않았는지 확인하기 위해 응답만 읽습니다.

3. 새 릴리스 권한과 복구 지점이 준비됐는지

새 릴리스는 공개 전환 전에 디렉터리 755, 일반 파일 644로 맞춥니다. 그룹이나 기타 사용자가 쓸 수 있는 경로가 하나라도 남으면 배포를 중단합니다. 자세한 원인과 수정 과정은 Astro 배포 파일 권한을 755·644로 고친 기록에 정리했습니다.

이번 점검에서는 현재 릴리스의 쓰기 가능 경로가 0개였고, 디렉터리와 첫 HTML 파일도 각각 755/644였습니다. 새 배포에 실패했을 때 돌아갈 과거 릴리스도 삭제하지 않고 남겨뒀습니다.

제가 현재 사용하는 배포 명령은 새 주소를 검사 대상으로 직접 넘깁니다.

npm run deploy -- --check-path=/새-글-주소/ --check-path=/새-이미지-주소/

새 페이지나 자산이 여러 개라면 --check-path를 각각 추가합니다. 권한 검사와 공개 응답이 모두 통과해야 배포 완료로 봅니다.

매달 확인하는 서버 상태

배포하지 않아도 시간과 함께 달라지는 값은 매달 비교합니다. 이 단계는 설정을 즉시 바꾸는 작업이 아니라 먼저 현재 수치를 읽고 기록하는 작업입니다.

1. 디스크 용량과 inode를 함께 봅니다

본문과 이미지는 작은 편이지만 빌드 결과를 릴리스별로 보관하면 파일이 계속 늘어납니다. 디스크 공간이 남아 있어도 작은 파일을 아주 많이 만들면 inode가 먼저 부족해질 수 있어 두 값을 함께 확인합니다.

df -hP /
df -iP /

이번 서버의 루트 디스크 사용량은 53%, inode 사용량은 4%였습니다. 당장 정리할 수준이라고 판단하지는 않았습니다. 하지만 롤백용 릴리스가 23개였기 때문에 다음 점검에서도 사용량과 릴리스 개수를 함께 비교할 예정입니다.

오래된 릴리스를 이번 점검에서 바로 지우지는 않았습니다. 어떤 릴리스가 현재 링크와 복구 기준으로 사용되는지 확인하기 전에 개수만 보고 삭제하면 롤백할 선택지를 잃을 수 있기 때문입니다. 보존 개수와 삭제할 정확한 경로를 정하는 일은 별도 작업으로 남겼습니다.

2. 인증서 만료일과 자동 갱신을 따로 확인합니다

브라우저에 자물쇠가 보인다는 사실만으로 다음 달에도 인증서가 유효하다고 보장되지는 않습니다. 인증서의 시작일과 만료일을 읽고, 자동 갱신 타이머도 별도로 확인합니다.

openssl x509 -in "/etc/letsencrypt/live/example.com/fullchain.pem" \
  -noout -dates
systemctl is-enabled certbot.timer
systemctl is-active certbot.timer

이번 점검일 기준 인증서는 약 58일 남아 있었고 Certbot 타이머는 활성화된 상태였습니다. 따라서 즉시 수동 갱신하지 않고 정상으로 기록했습니다. Certbot 공식 문서도 대부분의 설치에서 예약 작업으로 자동 갱신을 구성하며, Linux에서는 systemd 타이머나 cron을 확인할 수 있다고 설명합니다.

만료일이 남았다는 결과와 자동 갱신이 실제로 동작한다는 결과는 다릅니다. 분기 점검에서는 갱신 시험까지 별도로 확인하기로 했습니다.

3. 업데이트 서비스와 재부팅 필요 여부를 봅니다

서버가 자동으로 보안 업데이트를 설치하더라도 새 커널이 적용되려면 재부팅이 필요할 수 있습니다. 이전 점검에서는 이 상태를 확인한 뒤 유지보수 재부팅을 진행했습니다. 이번에는 재부팅 필요 표시가 없었고 이전 유지보수 때 적용한 커널로 정상 실행 중이었습니다.

uname -r
test -f /var/run/reboot-required \
  && echo "reboot required" \
  || echo "reboot not required"
systemctl is-active unattended-upgrades

자동 업데이트 서비스도 실행 중이었습니다. Ubuntu 공식 보안 문서에 따르면 Ubuntu Server의 unattended-upgrades는 보안 업데이트를 자동 적용하도록 제공됩니다. 다만 서비스가 활성화됐다는 사실만으로 모든 제3자 저장소나 재부팅까지 자동 처리된다고 가정하지는 않습니다.

4. 웹 서버와 최근 경고를 함께 확인합니다

서비스 상태가 active여도 반복되는 경고가 있을 수 있습니다. minml에서는 Nginx, 자동 업데이트와 침입 방지 서비스의 상태를 확인하고 최근 Nginx 경고 로그를 함께 읽습니다.

systemctl is-active nginx
journalctl -u nginx --since "30 days ago" -p warning --no-pager

이번에는 세 서비스가 모두 실행 중이었고, journal 기준 최근 30일 Nginx 경고 항목은 없었습니다. 로그가 없다는 사실이 서버 전체에 문제가 없다는 증거는 아닙니다. 적어도 확인한 서비스와 기간에서는 새 경고가 없었다는 뜻으로만 기록했습니다.

분기별로 직접 판단할 항목

매달 명령 한 줄로 확인 표시를 남기기 어려운 항목은 분기별로 시간을 정해 직접 검토합니다.

1. 백업 파일이 아니라 복구 가능성을 확인합니다

점검을 시작할 때는 로컬 소스의 Git 상태가 깨끗하고, 서버에 현재 릴리스와 과거 릴리스가 남아 있다는 것까지 확인했습니다. 이것만으로 완전한 백업 복구 시험을 했다고 말할 수는 없습니다.

분기 점검에서는 별도 위치의 소스와 이미지를 이용해 실제 빌드가 되는지, 새 빈 경로에서 사이트를 복원할 수 있는지 확인할 예정입니다. 무엇을 보관해야 하는지는 Astro 블로그 백업과 복구 방법에 정리했습니다.

2. SSH 키와 방화벽 규칙을 사람이 읽습니다

자동 검사는 방화벽이 활성화됐는지는 알려주지만, 남아 있는 규칙이 지금도 필요한지는 결정하지 못합니다. 이번 서버의 방화벽은 활성 상태였지만 같은 원격 접속 허용 목적의 규칙이 이름만 다른 형태로 중복되어 있었습니다.

중복 규칙이 곧바로 추가 취약점을 만든다고 판단하지는 않았습니다. 두 규칙 모두 이미 허용하던 같은 서비스 범위였기 때문입니다. 다만 나중에 규칙을 검토할 때 혼동을 줄 수 있어 ‘정리 예정’으로 기록했습니다. 원격 접속 규칙은 잘못 지우면 서버에 다시 들어가지 못할 수 있으므로 이번 점검에서 즉시 삭제하지 않았습니다.

SSH 키도 개수만 세지 않고 소유자가 누구인지, 더 이상 사용하는 장비가 아닌지 확인해야 합니다. AI에게 서버 점검을 요청할 때 작업 범위를 나누는 방식은 AI에게 SSH 서버 작업을 맡길 때 확인하는 7가지에서 자세히 설명했습니다.

3. 오래된 글과 정책 페이지를 다시 읽습니다

링크가 200으로 열린다고 내용까지 현재와 맞는 것은 아닙니다. 가격, 지원 버전, 서비스 정책과 운영 방식이 바뀌었는데 과거 설명이 그대로 남을 수 있습니다. 분기별로 다음 항목을 직접 읽습니다.

  • 현재 사용하지 않는 서비스를 여전히 사용 중이라고 적지 않았는가
  • 가격·버전·정책에 확인 날짜와 공식 출처가 있는가
  • 개인정보처리방침이 실제 사이트 기능과 일치하는가
  • 합친 글의 과거 주소가 올바른 새 글로 이동하는가
  • 초안으로 돌린 글이 사이트맵이나 목록에 나타나지 않는가

이 항목은 HTTP 상태나 빌드 결과만으로 자동 판정하기 어렵습니다. 그래서 페이지가 존재하는지 확인하는 자동 검사와, 내용이 여전히 맞는지 판단하는 사람의 검토를 구분했습니다.

이번 점검에서 남긴 실제 결과

2026년 8월 점검 결과를 한 표로 줄이면 다음과 같습니다.

항목 결과 판단
공개 페이지·이미지·사이트맵 기대한 200 정상
없는 주소와 과거 주소 404, 301 정상
현재 릴리스 권한 디렉터리 755, 파일 644, 쓰기 가능 경로 0개 정상
디스크·inode 53%, 4% 관찰
인증서 약 58일 남음, 자동 갱신 타이머 활성 정상
업데이트·커널 자동 업데이트 실행, 재부팅 불필요 정상
Nginx 최근 경고 확인한 기간에 없음 정상
보관된 릴리스 23개 보존 기준 검토 예정
방화벽 활성, 원격 접속 허용 규칙 일부 중복 정리 예정
백업 소스와 이전 릴리스 존재 확인 실제 복구 시험 예정

정리 예정 항목이 있다고 해서 이번 점검을 실패로 보지는 않았습니다. 중요한 것은 발견 즉시 불안해서 설정을 지우는 게 아니라, 서비스 영향과 복구 방법을 확인한 뒤 별도 작업으로 처리하는 것입니다.

기록만 해둔 세 항목도 이후에 실제로 처리했습니다

첫 점검에서 관찰 또는 정리 예정으로 남겼던 항목 가운데 범위와 복구 방법을 확인할 수 있는 세 가지는 별도 작업으로 마무리했습니다. 원래 표는 2026년 8월 28일의 판단을 남기기 위해 수정하지 않고, 후속 결과를 아래처럼 추가합니다.

후속 작업 2026년 9월 11일 기준 결과
배포 파일 권한 새 릴리스마다 디렉터리 755, 일반 파일 644로 정규화하고 쓰기 가능 경로가 있으면 중단
유지보수 재부팅 필요한 업데이트를 적용한 뒤 재부팅하고 Nginx와 두 사이트의 응답을 다시 확인
오래된 릴리스 현재 릴리스를 포함해 최근 5개만 남기도록 자동화하고, 삭제 전 드라이런과 경로 검증 추가

가장 최근 배포에서도 새 릴리스의 권한 검사를 통과한 뒤에만 공개 링크를 전환했습니다. 홈페이지, 새 주제 페이지와 전체 글 목록은 200, 실제 없는 주소는 404, 통합한 과거 주소는 301을 반환했습니다. 같은 서버의 별도 사이트도 수정하지 않고 응답만 확인했습니다. 이번 배포에서는 보존 기준을 넘은 과거 릴리스 1개가 정리되어 배포 후 릴리스 수는 5개가 됐습니다.

이 후속 작업으로 체크리스트의 역할도 분명해졌습니다. 점검표는 문제를 발견하는 문서에서 끝나지 않고, 반복 가능한 자동 검사로 옮길 항목과 사람이 정기적으로 판단할 항목을 구분하는 출발점이었습니다. 배포 권한과 릴리스 개수처럼 기준이 명확한 것은 자동화했고, SSH 키 소유자와 오래된 글의 정확성처럼 맥락이 필요한 항목은 여전히 사람이 읽습니다.

제가 다음 점검에도 사용할 체크리스트

배포할 때마다

  • Git 작업 상태와 전체 빌드 확인
  • 공개 글, 내부 링크와 이미지 검사
  • 새 릴리스 디렉터리 755, 일반 파일 644
  • 그룹·기타 사용자 쓰기 가능 경로 0개
  • 홈페이지, 새 페이지, 새 이미지와 사이트맵 200
  • 실제 없는 주소 404, 지정한 과거 주소 301
  • 같은 서버의 다른 사이트가 그대로 열리는지 확인
  • 실패 시 직전 릴리스로 돌아갈 수 있는지 확인

매달

  • 디스크와 inode 사용량을 지난달 수치와 비교
  • 인증서 만료일과 자동 갱신 타이머 확인
  • 자동 업데이트와 재부팅 필요 여부 확인
  • Nginx와 보안 관련 서비스 상태 확인
  • 최근 경고 로그와 반복 오류 확인
  • 보관된 릴리스 개수와 증가량 확인

분기별

  • 별도 위치에서 소스·이미지 빌드 또는 복구 시험
  • Certbot 자동 갱신 시험과 결과 로그 확인
  • 사용하지 않는 SSH 키와 계정 확인
  • 방화벽의 중복·불필요한 규칙 검토
  • 오래된 가격·정책·지원 버전 확인
  • 개인정보처리방침과 실제 기능 비교
  • 초안, 리디렉션과 사이트맵 구성을 직접 검토

체크리스트의 목적은 무조건 수정하는 것이 아닙니다

이번 점검에서는 서버 설정을 하나도 바꾸지 않았습니다. 디스크 53%와 릴리스 23개를 확인했지만 바로 삭제하지 않았고, 방화벽 중복 규칙도 접근 경로를 잃을 위험을 고려해 정리 대상으로만 남겼습니다. 반대로 배포 때마다 달라질 수 있는 파일 권한과 새 주소의 응답은 자동 검사로 즉시 막았습니다.

제가 유지보수 체크리스트에서 얻고 싶은 결과는 모든 칸에 ‘완벽’이라고 쓰는 것이 아닙니다. 지난번과 비교할 수 있는 숫자, 지금 처리할 문제와 나중에 검토할 항목, 그리고 실패했을 때 돌아갈 지점을 남기는 것입니다. 이 기록이 있어야 다음 달의 60%가 단순한 증가인지, 인증서 갱신이 예정대로 됐는지, 오래된 릴리스가 계속 쌓이는지를 판단할 수 있습니다.

앞으로 실제 복구 시험과 방화벽 규칙 정리를 진행하면 그 결과도 이 글에 추가할 예정입니다. 지금의 결론은 간단합니다. 개인 사이트도 배포가 끝나는 순간부터 운영이 시작되고, 좋은 점검표는 확인 항목뿐 아니라 언제 확인하고 어떤 결과부터 행동할지까지 정해둬야 했습니다.

참고한 공식 문서