서버에서 디스크 사용률 경고를 받으면 저는 늘 같은 순서로 확인합니다. df로 어느 파일시스템이 찼는지 보고, du로 폴더를 좁히고, find로 큰 파일을 뽑고, 그래도 용량이 안 줄면 삭제된 채 열려 있는 파일을 확인합니다. 순서를 건너뛰고 find부터 전체 디스크에 돌리면 시간만 쓰고, 정작 가득 찬 파일시스템이 아닌 곳을 뒤지게 됩니다.
아래 출력은 기존 Mac(zsh) 출력 예제, 매뉴얼 기준 Linux 설명, 2026-10-02에 별도로 실행한 클라우드 Linux 작은 파일 실험으로 구분했습니다. 새 실험의 환경과 범위는 해당 절에 적었습니다.
1. df -h로 어느 파일시스템이 찼는지 뵅니다
가장 먼저 볼 것은 경고가 난 경로와 그 경로가 속한 파일시스템입니다. 예를 들어 /var를 별도 파일시스템으로 마운트한 서버라면 루트에는 여유가 있어도 /var만 가득 찰 수 있습니다.
df -h
Filesystem Size Used Avail Capacity iused ifree %iused Mounted on
/dev/disk3s3s1 460Gi 12Gi 60Gi 17% 459k 625M 0% /
/dev/disk3s1 460Gi 378Gi 60Gi 87% 3.7M 625M 1% /System/Volumes/Data
위 Mac 출력의 사용률 열은 Capacity이고 GNU coreutils df에서는 Use%로 표시됩니다. 경고 기준은 운영 환경마다 다르므로 사용률뿐 아니라 Avail, Mounted on, 경고가 가리키는 경로를 함께 확인하세요. 위 표의 17%와 87%만으로 90% 경고가 발생했다거나 두 볼륨의 여유 공간이 독립적이라고 판단할 수는 없습니다.
APFS는 같은 컨테이너 안의 여러 볼륨이 여유 공간을 공유할 수 있습니다. 이 Mac 출력만으로 Linux의 별도 파일시스템 상황을 설명하지 않도록 구분하세요. 볼륨 이름이 다르다는 이유만으로 공간도 완전히 분리됐다고 가정하지 말고 파일시스템과 컨테이너 구성을 확인합니다. Apple의 APFS 공간 공유 설명
그래서 정리하려는 폴더가 경고 대상 파일시스템에 속하는지부터 확인해야 합니다. 서로 독립된 파일시스템이라면 다른 쪽 파일을 정리해도 문제 파일시스템의 여유 공간은 늘지 않습니다. 폴더가 어느 마운트에 걸려 있는지는 findmnt로 폴더의 디스크와 마운트 위치를 확인하는 방법에 정리해 두었습니다.
용량은 남았는데 쓰기가 실패한다면 inode 고갈을 의심합니다. 리눅스에서는 df -i의 IUse% 열을 보고, 이 값이 100%면 빈 공간이 있어도 새 파일을 만들지 못합니다. 작은 파일이 수백만 개 쌓이는 세션 디렉터리나 메일 큐에서 자주 생깁니다.
df -i
2. du로 폴더 단위 범위를 좁힙니다
파일시스템을 골랐으면 그 마운트 지점이나 조사할 폴더를 정합니다. 아래 GNU 도구 예제에서는 조사 경로를 /var로 통일했습니다. 먼저 findmnt -T /var와 df -hT /var로 경로를 확인하고, 실제 대상이 다르면 아래 du와 find의 /var를 같은 경로로 바꾸세요. /var가 루트의 일부라면 du /var는 그 폴더의 합계일 뿐 루트 전체 합계는 아닙니다. 깊이 1로 표시하면 큰 하위 폴더를 읽기 쉽게 비교할 수 있습니다.
sudo du -xh --max-depth=1 /var | sort -rh | head -10
--max-depth=1은 합계의 표시 깊이를 한 단계로 제한합니다. 하위 파일의 사용량을 합산하려면 그 아래도 순회하므로 이 옵션만으로 탐색량이 줄거나 빨라지는 것은 아닙니다. sort -rh는 800M과 1.2G처럼 단위가 붙은 값을 크기순으로 정렬합니다. -x는 시작 경로와 다른 파일시스템의 디렉터리로 내려가지 않도록 합니다. 별도 마운트도 조사해야 한다면 그 경로를 따로 선택해 확인하세요.
제 맥의 BSD du에는 --max-depth가 없어서 -d 1을 씁니다. 테스트용으로 만든 디렉터리에서 실제로 실행한 출력입니다.
du -h -d 1 . | sort -rh
1.2G .
800M ./logs
340M ./backup
95M ./cache
logs가 800M으로 가장 크므로 다음 대상은 logs입니다. 경로만 바꿔 같은 명령을 한 번 더 돌립니다. 현재 폴더의 항목만 보고 싶다면 아래 형태도 자주 씁니다.
du -sh * | sort -rh
800M logs
340M backup
95M cache
큰 하위 폴더를 따라 조사 범위를 좁힙니다. Permission denied 같은 오류가 나오면 일부 파일이 합계에서 빠질 수 있으므로, 처음부터 2>/dev/null로 숨기지 말고 오류와 결과를 함께 확인하세요. sudo를 써도 모든 접근 오류가 해결된다고 가정하지 않습니다. du는 선택한 범위의 하위 파일을 순회하므로 데이터 규모와 환경에 따라 시간이 걸리고 디스크 읽기 부하가 생깁니다. du(1) 매뉴얼과 GNU du의 표시 깊이·파일시스템 옵션에서 정의를 확인할 수 있습니다.
3. find로 큰 파일을 직접 뽑습니다
특정한 큰 파일을 찾고 싶다면 크기 조건으로 후보를 추릴 수 있습니다. 아래는 앞 단계와 같은 /var를 조사하는 GNU find 예제입니다. 실제 조사 경로로 바꿔 사용하며, 파일 크기 조건을 넣어도 선택한 범위의 탐색 자체가 생략되지는 않습니다.
sudo find /var -xdev -type f -size +500M -printf '%s %p\n' | sort -nr | head -20
-xdev는 시작 경로와 다른 파일시스템으로 내려가지 않습니다. 따라서 /var가 별도 마운트인데 find / -xdev로 실행하면 그 안의 파일을 조사하지 못합니다. 루트 파일시스템을 조사할 때만 시작 경로를 /로 선택하세요. -size +500M의 M은 MiB(1,048,576바이트) 단위이며, 여기서는 500MiB를 넘는 파일을 찾습니다. -printf '%s %p\n'은 논리적 바이트 크기와 경로를 출력하고 sort -nr는 그 숫자로 정렬합니다. 이 크기는 실제 할당된 디스크 사용량과 다를 수 있습니다. -printf는 GNU 확장이라 BSD 계열 find에는 없습니다. GNU find의 파일시스템 경계 · 크기 조건과 단위
Linux 작은 파일 실험: 큰 파일 순위가 뒤집히는 경우
2026-10-02에 격리된 클라우드 Linux 환경에서 가상 파일 두 개로 직접 확인했습니다. 환경은 Linux 6.18.44 x86_64, overlayfs, GNU findutils 4.10.0, GNU coreutils 9.7(du·sort·stat), Bash 5.2.37, Python 3.12.14입니다. 운영 서버나 앞의 Mac을 측정한 결과가 아닙니다.
다음 코드는 쓰기 가능한 실습 폴더에서 Bash로 실행합니다. 현재 위치에 새 임시 폴더를 만들고, 논리적 크기 합계가 1.5MiB인 파일 두 개만 생성합니다. xb는 같은 이름이 이미 있으면 실패하므로 기존 파일을 덮어쓰지 않습니다. sparse 파일은 끝 위치에 한 바이트만 써서 중간에 빈 영역을 남깁니다. 삭제 명령은 없으며 실습 파일은 그대로 남습니다.
set -euo pipefail
export LC_ALL=C
demo=$(mktemp -d ./size-demo.XXXXXX)
cd -- "$demo"
python3 - <<'PY'
import os
from pathlib import Path
with Path("written-512KiB.bin").open("xb") as f:
f.write(bytes(range(256)) * 2048)
f.flush()
os.fsync(f.fileno())
with Path("sparse-1MiB.bin").open("xb") as f:
f.seek(1024 * 1024 - 1)
f.write(b"X")
f.flush()
os.fsync(f.fileno())
PY
이어서 같은 폴더에서 아래 조회 명령을 실행합니다. $는 프롬프트 표시이므로 복사할 때 빼세요. 다음은 실제 관찰한 바이트 단위 출력이며, 할당량은 파일시스템에 따라 달라질 수 있습니다.
$ find . -xdev -type f -printf '%s\t%p\n' | sort -nr
1048576 ./sparse-1MiB.bin
524288 ./written-512KiB.bin
$ find . -xdev -type f -exec du -B1 -- {} + | sort -nr
524288 ./written-512KiB.bin
4096 ./sparse-1MiB.bin
$ find . -xdev -type f -exec du -b -- {} + | sort -nr
1048576 ./sparse-1MiB.bin
524288 ./written-512KiB.bin
find의 %s로 정렬하면 1MiB sparse 파일이 먼저 나오지만, du -B1은 4,096바이트로 집계해 512KiB 파일 뒤에 놓습니다. stat으로도 각각 512바이트 블록 8개와 1,024개를 확인했습니다. du -b는 du --apparent-size -B1과 같아 논리적 크기를 보여 줍니다. 할당된 사용량을 바이트 단위로 비교하려면 -B1과 구분해야 합니다. 새 폴더 두 곳에서 반복한 결과는 같았고, 조회 전후 파일 내용도 변하지 않았습니다.
이 실험은 작은 일반 파일의 순위만 비교합니다. 4,096바이트는 보편적인 값이나 삭제 후 회수될 공간의 약속이 아닙니다. 하부 저장 장치, 압축·공유 블록·스냅샷, df와의 차이, 마운트 경계, 속도는 검증하지 않았습니다. 파일명은 줄바꿈 없는 단순한 이름으로 제한했으며, 크기 순위만 보고 삭제 여부를 정하지 마세요. 옵션 정의는 GNU du의 apparent-size·bytes 설명과 GNU find의 크기 출력 지시자에서 확인할 수 있습니다.
-printf를 못 쓰는 환경에서는 크기 조건만 걸고 결과를 ls에 넘깁니다. 아래 두 명령은 앞의 Mac 테스트 디렉터리를 대상으로 한 기존 예제입니다.
find . -type f -size +100M
./logs/access.log
./backup/db-20260910.dump
./logs/app/app.log
find . -type f -size +100M -exec ls -lh {} \; | awk '{print $5, $9}' | sort -rh
620M ./logs/app/app.log
340M ./backup/db-20260910.dump
180M ./logs/access.log
find는 크기순으로 출력하지 않으므로 정렬은 항상 따로 붙여야 합니다. 이미 폴더를 특정했다면 ls -lSh 한 줄이 더 짧습니다. S가 크기순 정렬입니다.
ls -lSh
total 368640
-rw-r--r-- 1 user staff 180M 9 11 00:18 access.log
drwxr-xr-x 3 user staff 96B 9 11 00:18 app
ls는 하위 디렉터리 안까지 재귀로 세지 않으므로 위 출력에서 app 폴더는 96B로 나옵니다. 폴더 안쪽 합계는 du가, 파일 하나하나는 find와 ls가 담당한다고 나누어 두면 헷갈리지 않습니다. 조건 옵션은 find(1) 매뉴얼에 정리되어 있습니다.
4. 자주 쌓이는 위치와 정리 전에 확인할 조건
아래는 용량을 차지할 수 있는 위치와 관련 도구의 예시입니다. 먼저 확인 열로 대상을 조사하세요. 정리 열의 명령은 파일·로그·컨테이너 등을 변경하거나 삭제할 수 있으므로 그대로 일괄 실행하는 체크리스트가 아닙니다. 대상과 보존 정책, 필요한 백업, 서비스 영향, 담당자의 작업 승인을 확인한 뒤 해당 도구와 서비스의 절차를 따르세요. 아래 정리 명령은 매뉴얼 기준 설명이며 이 글을 위해 실행한 결과는 아닙니다.
| 위치 | 쌓이는 것 | 확인 | 정리 |
|---|---|---|---|
| /var/log | 로테이트되지 않은 애플리케이션 로그 | du -xh -d 1 /var/log | logrotate -f /etc/logrotate.conf |
| systemd 저널 | journal 바이너리 로그 | journalctl --disk-usage | journalctl --vacuum-size=200M |
| /var/lib/docker | 이미지 레이어, 중지된 컨테이너, 빌드 캐시 | docker system df | docker system prune |
| /var/cache/apt, /var/cache/dnf | 내려받은 패키지 파일 | du -sh /var/cache/apt | apt-get clean, dnf clean all |
| /var/crash, coredump 경로 | 프로세스 코어덤프 | ls -lSh /var/crash | 내용 확인 후 개별 삭제 |
| 홈의 .cache | 빌드 캐시, 패키지 캐시 | du -xh -d 1 ~/.cache | 해당 서비스 중지 후 삭제 |
docker 정리는 특히 주의가 필요합니다. docker system prune은 중지된 컨테이너와 미사용 네트워크, 댗글링 이미지를 지우고, -a를 붙이면 실행 중이 아닌 이미지까지 전부 지웁니다. 되돌릴 수 없으므로 운영 서버에서는 docker system df로 이미지, 컨테이너, 볼륨, 빌드 캐시가 각각 얼마인지 확인한 뒤에 실행합니다. 용량 대부분이 빌드 캐시인 경우도 많아서 docker builder prune만으로 끝나기도 합니다.
실행 중인 로그는 먼저 해당 서비스가 지원하는 로그 회전·재열기 절차를 확인하세요. truncate는 일반적인 안전 정리 명령이 아닙니다. 아래 명령은 지정한 파일의 기존 내용을 전부 버립니다. 감사·장애 분석 등에 필요한 로그의 보존 기간을 확인하고, 필요한 내용을 보관 정책에 맞는 별도 위치에 백업한 뒤 복구 가능 여부까지 확인해야 합니다. 실제 대상이 로그인지, 쓰는 프로그램이 외부 잘라내기를 지원하는지, 작업 중 영향과 담당자 승인이 확인된 경우에만 예외적으로 검토하세요. 어느 조건이라도 불명확하면 실행하지 마세요. /var/log/app/app.log는 예시 경로입니다.
sudo truncate -s 0 /var/log/app/app.log
파일 자체와 열린 참조가 남아 있더라도 로그 내용 보존이나 이후 기록의 정상 동작까지 보장되지는 않습니다. 크기를 줄였다는 사실만으로 문제가 해결됐다고 판단하지 말고 서비스 상태, 새 로그 기록, 같은 경로의 df를 다시 확인하세요. 데이터가 잘려 나가는 동작은 GNU truncate 설명을 참고하세요.
지웠는데 df 용량이 안 줄어들 때
큰 로그를 rm으로 지웠는데 df -h 숫자가 그대로인 경우가 있습니다. 대개 프로세스가 그 파일을 아직 열고 있어서, 디렉터리에서만 이름이 사라지고 파일시스템은 여전히 블록을 붙들고 있는 상태입니다. lsof +L1로 링크 수가 0인 열린 파일을 찾은 뒤 서비스가 지원하는 로그 재열기·회전 절차를 확인합니다. 재시작이 필요하면 서비스 영향을 검토하고 승인된 절차를 따르세요. 모든 열린 참조가 닫힌 뒤 실제 여유 공간이 돌아왔는지는 df로 다시 확인합니다. du로는 이미 안 보이는데 df만 크게 나오는 것이 전형적인 신호입니다. 확인 절차와 다른 원인들은 파일을 지웠는데 df 용량이 안 줄어드는 이유에서 df와 du의 차이부터 짚어 두었습니다.
5. ncdu로 대화형으로 훑습니다
du와 find를 반복해 내려가는 대신 화면에서 바로 이동하며 보고 싶다면 ncdu가 편합니다. 지정한 경로를 한 번 스캔한 뒤 크기순 목록을 띄우고, 방향키로 폴더를 오가며 그 자리에서 삭제까지 할 수 있습니다.
sudo apt install ncdu
sudo dnf install ncdu
brew install ncdu
sudo ncdu -x /var
앞 단계와 같은 /var 예시이며 실제 조사 경로로 바꿉니다. -x는 시작 경로와 다른 파일시스템으로 넘어가지 않게 합니다. 방향키와 엔터로 탐색하고 q로 종료합니다. d는 조회가 아니라 선택 항목 삭제이므로 진단 중에는 사용하지 말고, 삭제가 필요하면 앞의 보존·백업·서비스 영향 조건부터 확인하세요. 결과를 파일로 저장해 나중에 다시 읽을 수도 있습니다.
sudo ncdu -o scan.json -x /var
ncdu -f scan.json
제 맥에는 설치되어 있지 않아 which ncdu가 not found를 반환했고, 위 사용법은 ncdu 매뉴얼 기준입니다. 스캔 자체는 du와 같은 전체 순회이므로 부하가 걱정되는 시간대에는 피하는 편이 좋습니다.
자주 묻는 질문
du로 재 합계와 df 사용량이 왜 다른가요
두 명령이 세는 대상이 다르기 때문입니다. df는 파일시스템이 보고하는 사용량을 그대로 읽고, du는 지정한 경로를 순회하며 찾을 수 있는 파일의 크기를 더합니다. 삭제됐지만 열려 있는 파일, 권한 때문에 못 읽은 경로, 다른 마운트에 가려진 디렉터리, 예약 블록이 있으면 두 숫자는 벌어집니다. df가 크고 du가 작으면 열린 삭제 파일부터 확인합니다.
du가 너무 오래 걸리고 Permission denied가 쏟아집니다
df와 findmnt로 조사할 파일시스템을 먼저 확인하고 필요한 시작 경로를 고르세요. -x는 다른 파일시스템으로 넘어가지 않게 하고 깊이 1은 출력만 간추립니다. 깊이 제한만으로 하위 탐색량이 줄지는 않습니다. 실제로 더 작은 경로를 선택하면 조사 범위를 줄일 수 있지만 소요 시간은 파일 수와 환경에 따라 다릅니다. Permission denied를 숨겨도 읽지 못한 파일은 합계에 들어오지 않습니다. 허용된 권한 범위 안에서 오류 원인부터 확인하세요.
큰 파일을 찾았는데 지워도 되는지 어떻게 판단하나요
먼저 파일의 용도와 보존 정책, 어떤 프로세스가 사용 중인지 확인합니다. 로그라면 서비스가 지원하는 회전·재열기 절차를 우선하고, truncate는 앞서 적은 데이터 손실·백업·서비스 영향 조건을 충족할 때만 검토하세요. 데이터베이스 파일, 이미지 레이어, 백업 아카이브는 담당자 확인 전까지 삭제하거나 이동하지 않습니다. 급하더라도 다른 마운트로 옮기는 작업은 경로 의존성과 실행 중인 서비스에 영향을 줄 수 있으므로 승인된 절차가 필요합니다.
2026-10-02 보완: 조사 경로의 일관성, du 표시 깊이와 탐색 범위, APFS 공간 공유, 로그 정리 전 보존 조건을 공식 문서로 확인해 설명을 보완했습니다. 이번 보완 과정에서 운영 서버 명령이나 삭제·잘라내기를 실행하지 않았으며, 새 성능 측정이나 용량 회수 실측을 추가하지 않았습니다.
'개발 문제 해결' 카테고리의 다른 글
| curl 응답 확인: 상태 코드·헤더·본문·종료 코드 구분하기 (0) | 2026.09.12 |
|---|---|
| 파이썬 JSON 파일 저장·읽기: 한글과 JSONDecodeError 확인 (0) | 2026.09.12 |
| adb 명령어 모음: 기기 연결 안 될 때부터 apk 설치와 로그 추출까지 (0) | 2026.09.11 |
| MySQL 오류 코드 1064 해결: near 뒤 문자열로 원인 찾는 순서 (0) | 2026.09.11 |
| Linux 파일을 지웠는데 용량이 안 줄어들 때: df와 du가 다른 이유 (0) | 2026.09.08 |
댓글