입사 3일 차. 운영팀에서 급하게 메시지가 왔습니다. "서버에서 Nginx가 502를 뱉고 있어요. 로그 확인해주세요." SSH 접속은 됐습니다. 문제는 — 로그가 어디에 있는지 모른다는 겁니다. /log? /nginx? /var? 이것저것 ls를 쳐보지만 어지러운 파일 목록만 나옵니다. 결국 선임에게 전화했습니다. "거기 /var/log/nginx/ 보세요." 5초 만에 해결됐습니다.
Linux는 어디에 무엇을 두는지 규칙이 있습니다. 이 구조를 알면 낯선 서버에서도 5분 안에 원하는 파일을 찾을 수 있습니다.
파일시스템 탐색
- 1Linux 디렉토리 계층 구조(FHS)를 이해하고 /etc, /var/log, /tmp 같은 실무 경로를 설명할 수 있다
- 2pwd, cd, ls로 파일시스템을 자유롭게 탐색할 수 있다
- 3절대 경로와 상대 경로의 차이를 이해하고 상황에 맞게 사용할 수 있다
- 4mkdir, touch로 파일과 디렉토리를 생성하고 cp, mv로 복사·이동할 수 있다
- 5rm 명령어를 안전하게 사용하고 실수를 예방하는 패턴을 적용할 수 있다
mkdir -p ~/lab/filesystem && cd ~/lab/filesystem모든 실습은 이 디렉토리 안에서 진행합니다
touch file1.txt file2.log file3.conf && mkdir -p project/src project/docs실습에 사용할 기본 파일과 디렉토리 구조를 만듭니다
pwd/home/ubuntu/lab/filesystem 이 출력되면 준비 완료
Linux 파일시스템 계층 구조 (FHS)
장애가 나면 가장 먼저 로그를 봐야 합니다. 그런데 서버에 처음 들어간 신입이 로그 파일을 찾으려면 어디를 봐야 할까요? Nginx 설정은 어디 있고, 애플리케이션 데이터는 어느 디렉토리에 쌓이는지 — 이걸 모르면 서버 안에서 길을 잃습니다. Linux는 어디에 무엇을 두는지 FHS(Filesystem Hierarchy Standard)라는 규칙으로 정해두었고, 모든 배포판이 이 규칙을 따릅니다. /etc에는 설정, /var/log에는 로그, /home에는 사용자 데이터 — 이 구조를 알면 낯선 서버에 들어가도 5분 안에 원하는 파일을 찾을 수 있습니다.
확대
Linux의 모든 파일은 / (루트)에서 시작하는 하나의 트리 구조에 위치합니다. Windows의 C:, D:\ 같은 드라이브 구분 없이 단일 계층입니다. Filesystem Hierarchy Standard(FHS)가 어디에 무엇을 두는지 규칙을 정합니다.
# 루트 디렉토리 구조 한눈에 보기
ls /
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
실무에서 자주 가는 경로들입니다.
| 경로 | 용도 | 실무 예시 |
|---|---|---|
/etc | 설정 파일 모음 | /etc/nginx/nginx.conf, /etc/ssh/sshd_config |
/var/log | 로그 파일 | /var/log/nginx/access.log, /var/log/syslog |
/var/www | 웹 서버 문서 루트 | Nginx, Apache 기본 경로 |
/home/username | 사용자 홈 디렉토리 | ~ 로 단축 표기 |
/tmp | 임시 파일 | 재부팅 시 자동 삭제, 실습에 활용 |
/opt | 외부 소프트웨어 | /opt/splunk, /opt/datadog-agent |
/usr/bin | 일반 실행 파일 | ls, grep, git 등의 실제 위치 |
/proc | 프로세스/커널 가상 파일시스템 | /proc/cpuinfo, /proc/meminfo |
# Nginx 설정 파일 빠르게 찾기
ls /etc/nginx/
conf.d nginx.conf sites-available sites-enabled
절대 경로 vs 상대 경로
확대
쉘 스크립트를 처음 작성할 때 흔히 저지르는 실수 중 하나가 상대 경로를 그대로 쓰는 겁니다. 터미널에서 직접 실행할 때는 잘 되다가, cron으로 자동 실행하거나 다른 디렉토리에서 스크립트를 호출하면 갑자기 "파일을 찾을 수 없다"는 오류가 납니다. 실행 위치가 달라지면 상대 경로의 기준점도 달라지기 때문입니다. 언제 절대 경로를 써야 하고 언제 상대 경로가 편한지 — 이 구분을 머릿속에 새겨두면 이런 실수를 예방할 수 있습니다.
절대 경로 (Absolute Path): 루트(/)부터 시작. 현재 위치와 무관하게 항상 같은 파일을 가리킵니다.
cd /etc/nginx # 어디서 실행해도 /etc/nginx 로 이동
cat /etc/hosts # 호스트 파일 읽기
상대 경로 (Relative Path): 현재 위치(pwd)를 기준으로 합니다.
# 현재 위치가 /home/ubuntu 라고 가정
cd lab/filesystem # /home/ubuntu/lab/filesystem 으로 이동
cd .. # 한 단계 위 → /home/ubuntu/lab
cd ../../etc # 두 단계 위 후 etc → /etc
cd ~ # 홈 디렉토리로 이동 (어디서든)
cd - # 이전 디렉토리로 토글
스크립트나 cron 작업에서는 상대 경로 대신 절대 경로를 사용하세요. 실행 위치가 달라도 항상 같은 파일을 가리킵니다.
ls 출력 읽기 & 파일 정보 해석
확대
ls -la를 처음 보면 drwxr-xr-x 2 root root 4096 같은 문자열이 나열되고 무슨 뜻인지 알기 어렵습니다. 하지만 이 한 줄에는 파일 타입, 읽기/쓰기/실행 권한, 소유자, 크기, 수정 시각이 모두 들어 있습니다. Permission denied 오류가 나거나 "왜 이 파일이 실행이 안 되지?" 싶을 때 — 해답이 이 출력 안에 있습니다.
ls -la /etc/nginx/
total 24
drwxr-xr-x 6 root root 4096 Sep 15 10:23 .
drwxr-xr-x 97 root root 4096 Sep 15 10:23 ..
drwxr-xr-x 2 root root 4096 Sep 15 10:23 conf.d
-rw-r--r-- 1 root root 1490 Sep 15 10:23 nginx.conf
lrwxrwxrwx 1 root root 34 Sep 15 10:23 sites-enabled -> /etc/nginx/sites-available
첫 글자가 파일 타입입니다: d=디렉토리, -=일반 파일, l=심볼릭 링크. 그 뒤 9자리(rwxr-xr-x)는 소유자/그룹/그 외 순서로 읽기(r=4)·쓰기(w=2)·실행(x=1) 권한입니다.
실무에서 자주 쓰는 ls 조합입니다.
ls -lath /var/log/nginx/ # 최신 파일이 맨 위, 사람이 읽기 좋은 용량
ls -lS /var/log/ | head -10 # 가장 큰 파일 순서로 정렬
ls /etc/*.conf # 특정 확장자만 보기
du -sh /var/log/nginx/ # 디렉토리 전체 용량 요약
pwd로 현재 위치를 확인하고 cd로 이동하면서 실무에서 자주 가는 경로를 직접 탐색합니다.
# 현재 위치 확인
pwd
/home/ubuntu/lab/filesystem
# /etc 주요 파일 목록 확인
ls -la /etc/ | head -20
# /var/log 로그 디렉토리 확인
ls /var/log/
# 홈 디렉토리로 이동 후 다시 돌아오기
cd ~
pwd
cd -
pwd
# 상대 경로 이동 연습
cd /var/log
ls
cd ../../etc # 두 단계 올라간 뒤 etc 로 이동
pwd
cd ~/lab/filesystem # 실습 디렉토리로 복귀
/etc
ls -la /etc/ | head -20- pwd 를 실행해서 먼저 전체 경로를 확인한다 — /home/ubuntu/lab/filesystem 형태여야 정상. 경로가 다르면 실습 디렉토리가 아닌 다른 위치에서 실행 중인 것이므로 cd ~/lab/filesystem 으로 먼저 이동
- ls /var/log/ 에서 syslog 또는 messages (배포판별 상이), auth.log, 그리고 nginx/ dpkg.log 같은 서비스별 디렉토리 또는 파일이 보이면 정상 — 비어있거나 파일이 없으면 해당 서비스가 설치·실행되지 않은 것
- cd - 실행 후 출력된 경로가 이전에 있던 위치(/var/log 등)와 일치하는지 확인 — 경로가 출력되지 않으면 $OLDPWD 변수가 설정되지 않은 것(터미널을 새로 열었거나 첫 cd 이전 위치로의 이동)
- cd ../../etc 실행 후 pwd 결과가 /etc 이어야 정상 — 위치가 다르면 시작 위치에 따라 .. 개수가 맞지 않은 것. 상대 경로 이동 전에 pwd 로 현재 위치를 먼저 확인하는 습관이 필요
mkdir -p로 중첩 디렉토리를 한 번에 만들고, touch로 빈 파일을 생성합니다.
cd ~/lab/filesystem
# 중첩 디렉토리 한 번에 생성 (-p 옵션 없으면 상위 디렉토리가 없어 실패)
mkdir -p app/src/utils
mkdir -p app/logs app/configs
# 구조 확인
ls -R app/
app/:
configs logs src
app/configs:
app/logs:
app/src:
utils
# 빈 파일 생성
touch app/configs/app.conf
touch app/logs/app.log app/logs/error.log
# 파일 목록 확인 (타임스탬프 포함)
ls -la app/configs/
ls -la app/logs/
mkdir -p app/src/utils && touch app/configs/app.conf- ls -R app/ 출력에서 app/, app/configs:, app/logs:, app/src:, app/src/utils: 헤더가 각각 표시되어야 정상 — 헤더가 일부 없으면 mkdir -p 가 누락된 것. 헤더 수가 5개이면 계층 구조가 올바르게 생성된 것
- ls -la app/configs/ 에서 touch 로 만든 app.conf 의 크기(5번째 컬럼)가 정확히 0 바이트이어야 정상 빈 파일 — 0이 아닌 값이 있으면 다른 명령이 내용을 추가한 것
- ls -la 출력의 수정 시각(6~8번째 컬럼, "Sep 15 10:23" 형식)이 현재 시각 기준 1분 이내이어야 정상 — 수십 분 이상 차이가 나면 기존 파일에 touch 한 것이 아니라 오래된 파일을 확인하고 있는 것
cp는 원본을 남기고 복사하고, mv는 원본을 이동하거나 이름을 바꿉니다. 디렉토리를 복사할 때는 -r 옵션이 필수입니다.
cd ~/lab/filesystem
# 파일 복사 (백업 관행: .bak 또는 날짜 suffix)
cp app/configs/app.conf app/configs/app.conf.bak
ls app/configs/
app.conf app.conf.bak
# 디렉토리 전체 복사 (-r 없으면 오류)
cp -r app/ app-backup/
ls
app app-backup
# 파일 이름 변경 (같은 디렉토리 내 mv)
mv app/configs/app.conf.bak app/configs/app.conf.20240915
# 파일 이동 (잘라내기+붙여넣기)
mkdir archive
mv app/logs/app.log app/logs/error.log archive/
ls archive/
ls app/logs/
access.log error.log
(비어 있음)
cp -r app/ app-backup/- cp -r app/ app-backup/ 실행 후 ls 에 app 와 app-backup 이 모두 보여야 정상 — app-backup 만 있고 app 가 없으면 cp 대신 mv 를 쓴 것. cp 는 원본이 반드시 남는다
- mv archive/ 로 파일을 이동한 뒤 ls app/logs/ 가 비어있고 ls archive/ 에 옮긴 파일이 보여야 정상 — app/logs/ 에 여전히 파일이 있으면 이동이 아닌 복사(cp)가 실행된 것
- cp -r 없이 cp app/ app-copy 를 실행하면 "cp: -r not specified; omitting directory" 오류가 즉시 표시됨 — 오류 없이 완료되었다면 파일(디렉토리가 아닌)을 복사한 것
- mv app/configs/app.conf.bak app/configs/app.conf.20240915 실행 후 ls app/configs/ 에서 app.conf.bak 이 사라지고 app.conf.20240915 가 나타나야 정상 — 둘 다 있으면 mv 대신 cp 를 쓴 것
rm은 휴지통 없이 즉시 삭제합니다. -i 옵션으로 삭제 전 확인을 요청하는 습관을 들이세요.
⚠️ 아래 명령은
~/lab/filesystem실습 디렉터리 안의 파일만 대상으로 합니다. 운영 경로에서 실행하지 말고, 먼저pwd와ls로 대상을 확인하세요. 이 실습은 복구 연습을 위해 원본을 백업해 둡니다.
cd ~/lab/filesystem
pwd
test -f app/configs/app.conf.20240915 && test -f app-backup/configs/app.conf
# 삭제 전 확인 (-i 옵션) — y 입력 시에만 삭제
rm -i app/configs/app.conf.20240915
rm -i app-backup/configs/app.conf
rm: remove regular file 'app-backup/configs/app.conf'? y
# 삭제 대상이 사라졌는지 확인
test ! -e app/configs/app.conf.20240915 && echo "첫 파일 삭제 확인"
test ! -e app-backup/configs/app.conf && echo "두 번째 파일 삭제 확인"
# 백업에서 파일 하나를 복구해 원상 복구
cp app/configs/app.conf app-backup/configs/app.conf
test -f app-backup/configs/app.conf && echo "복구 확인"
# 디렉토리 전체 삭제가 필요할 때도 먼저 목록을 확인한 뒤 실행
ls -la app-backup/
rm -ri app-backup/
# 삭제됐는지 확인
ls
app archive
# 안전한 습관: 삭제 전 echo 로 대상 목록 확인
echo rm -r archive/ # 실제 삭제하지 않고 명령어만 출력
ls archive/ # 무엇이 삭제될지 확인
rm -r archive/ # 확인 후 실행
rm -i app-backup/configs/app.conf- rm -i 실행 시 "rm: remove regular file app-backup/configs/app.conf?" 프롬프트가 나타나야 정상 — 프롬프트 없이 바로 삭제되면 alias rm="rm -i" 가 설정되어 있지 않거나 -i 옵션을 누락한 것
- 삭제 후 ls 결과에서 대상이 사라졌는지 확인 — rm -r app-backup/ 후 app 와 archive 만 남아야 정상. app-backup 이 여전히 보이면 경로를 잘못 지정한 것(탭 완성으로 정확한 경로 확인 권장)
- echo rm -r archive/ 를 먼저 실행해 대상 경로가 올바른지 출력으로 확인한 뒤 실제 rm -r archive/ 실행 — echo 출력과 실제 명령이 다르면 변수 확장 오류 가능성이 있으므로 ls archive/ 로 내용 먼저 확인
경로 변수가 비어있을 때 rm -rf $DIR/* 실행
안전한 실행 조건: 변수에 반드시 값이 있음을 보장한 후 실행. 현재 위치(pwd)를 두 번 확인 후 실행.
실행 전 반드시 확인
- pwd 로 현재 위치 확인 — 의도한 디렉토리가 맞는지 검증
- echo rm -rf $DIR/* 로 실제 실행 전 대상 경로 출력 확인
- 스크립트에서는 변수 미설정 방어 구문(bash parameter expansion)으로 빈 변수 차단
- 프로덕션에서 rm -rf 실행 전 ls 로 삭제 대상 목록 먼저 확인
rm -rf $DIR/*위 항목을 모두 확인한 후 복사할 수 있습니다
상황: 파일을 백업 디렉토리로 복사하려는데 No such file or directory 오류가 납니다. 분명히 파일을 만들었는데 왜 안 될까요?
원인: 두 가지 원인이 많습니다. 첫째, 현재 디렉토리가 파일이 있는 위치가 아닌 경우. 둘째, 파일명 오타 — Linux 파일시스템은 대소문자를 구분하므로 Config.conf와 config.conf는 다른 파일입니다.
진단:
# 1. 현재 위치 확인
pwd
# 2. 파일이 정말 있는지 확인 (대소문자 무시 검색)
ls | grep -i "source"
# 3. 대상 디렉토리 존재 확인
ls -la /backup/ 2>&1
ls: cannot access '/backup/': No such file or directory
해결:
# 대상 디렉토리가 없으면 먼저 생성
mkdir -p /backup
# 정확한 파일명으로 재실행
cp source.txt /backup/dest.txt
# 디렉토리 복사 시 -r 옵션 누락 오류도 흔합니다
# cp: -r not specified; omitting directory 'mydir'
cp -r mydir/ /backup/
심화 — 심볼릭 링크를 지나면 경로 계산이 달라진다
심화: cd .. 의 목적지는 심볼릭 링크가 있으면 달라진다
앞에서 cd ../..로 상대 경로를 한 토막씩 계산했습니다. 그 계산은 경로가 평범한 디렉토리로만 이어질 때 정확합니다. 그런데 경로 중간에 심볼릭 링크가 끼면 ..가 '보이는 경로'를 따라가는지 '실제 디렉토리'를 따라가는지가 갈리고, 이 차이가 배포·정리 스크립트에서 엉뚱한 곳을 건드리는 사고로 이어집니다.
- 셸은 경로를 두 가지로 봅니다. 하나는 논리 경로(logical) — 사용자가
cd로 밟아온 문자열 그대로. 다른 하나는 물리 경로(physical) — 심볼릭 링크를 전부 푼 실제 디렉토리. 기본cd와pwd는 논리 경로를 유지합니다. - 그래서 심볼릭 링크 디렉토리로 들어간 뒤
cd ..는 물리 부모가 아니라 논리 부모로 갑니다. 예를 들어/var/www/current가/releases/v2를 가리킬 때,cd /var/www/current다음의cd ..는/releases가 아니라/var/www로 이동합니다. 눈에 보이던 경로(/var/www/current)의 부모로 돌아가는 것입니다. - 실제 위치를 알려면
pwd -P, 물리 기준으로 올라가려면cd -P ..를 씁니다. 스크립트에서 진짜 경로가 필요하면realpath나readlink -f로 링크를 먼저 풀어야 합니다.
ln -s /releases/v2 /var/www/current
cd /var/www/current
pwd # /var/www/current (논리 경로 — 밟아온 문자열)
pwd -P # /releases/v2 (물리 경로 — 링크를 푼 실제 위치)
cd ..
pwd # /var/www (논리 부모 — /releases 아님!)
- 왜 위험한가: 무중단 배포는
current를 심볼릭 링크로 두고 새 릴리스로 갈아끼우는 구조가 표준입니다. 이때cd $APP/current; cd ..; rm -rf 오래된것같은 상대 정리 스크립트는 논리 부모(공유 디렉토리)에서 동작해, 지우려던 릴리스가 아니라 엉뚱한 상위를 건드릴 수 있습니다. 상대 경로와 심볼릭 링크가 만나면 반드시 절대 경로로 못박거나 물리 경로를 먼저 풀어야 안전합니다.
정리하면, "..는 한 단계 위"라는 규칙은 참이지만 그 '위'가 논리 경로 기준이라는 단서가 붙습니다. 심볼릭 링크가 보이면 pwd와 pwd -P를 함께 확인하는 습관이 사고를 막습니다.
상황: 무중단 배포로 /srv/app/current가 /srv/app/releases/2026-07-01을 가리키는 구조입니다. 배포 후 정리 스크립트가 cd /srv/app/current 다음 cd ..로 올라가 오래된 릴리스를 지우려 했는데, 릴리스 디렉토리가 아니라 그 위의 공유 설정이 사라졌습니다.
원인: current는 심볼릭 링크라, cd /srv/app/current 뒤의 논리 경로는 /srv/app/current입니다. 여기서 cd ..는 링크의 실제 대상 부모(/srv/app/releases)가 아니라 논리 부모인 /srv/app으로 이동합니다. 스크립트는 /srv/app/releases 안에서 정리한다고 믿었지만 실제로는 /srv/app에서 동작해, 공유 파일과 다른 릴리스를 건드린 것입니다.
진단: 스크립트 중간에 pwd와 pwd -P를 나란히 출력합니다. 둘이 갈리면(/srv/app/current vs /srv/app/releases/2026-07-01) 심볼릭 링크를 밟았다는 증거입니다. readlink -f current로 링크의 실제 대상을 확인하고, 정리 로직이 실제로 어떤 경로를 대상으로 삼는지 rm 대신 echo로 먼저 찍어 확인합니다.
해결: 상대 경로와 심볼릭 링크를 섞지 않습니다. (1) 정리 대상 디렉토리를 절대 경로로 고정합니다 — RELEASES=/srv/app/releases; find "$RELEASES" -maxdepth 1 -mtime +14 .... (2) 실제 경로가 필요하면 cd -P나 realpath/readlink -f로 물리 경로를 먼저 구해서 씁니다. (3) 삭제 전 echo·ls로 실제 대상을 확인하는 습관(이 모듈에서 배운 안전 습관)을 심볼릭 링크 환경에서 특히 철저히 지킵니다. "cd ..의 목적지는 심볼릭 링크가 있으면 눈에 보이는 논리 경로를 따른다"를 기억하세요.
명령어·단축키 빠른 참조
이 모듈에서 다룬 파일시스템 탐색·파일 관리 명령을 실전 옵션과 함께 모았습니다.
| 명령어/단축키 | 용도 | 자주 쓰는 예 |
|---|---|---|
pwd | 현재 절대 경로 확인 | pwd(논리) vs pwd -P(심볼릭 링크 푼 물리) |
cd | 디렉토리 이동 | cd .., cd ~, cd -(이전), cd -P ..(물리) |
ls -la | 목록·권한·숨김 파일 | ls -lath(최신·용량), ls -lS(용량순) |
ls -R / ls -lt | 재귀 목록 / 최근 수정순 | ls -lt /var/log/nginx/ | head |
mkdir -p | 중첩 디렉토리 한 번에 생성 | mkdir -p app/src/utils |
touch | 빈 파일 생성·시각 갱신 | touch app.log error.log |
cp -r | 디렉토리 복사(원본 유지) | cp -r app/ app-backup/ |
mv | 이동·이름 변경(원본 사라짐) | mv old.conf old.conf.bak |
rm -i / -r | 확인 삭제·디렉토리 삭제 | rm -r app-backup/, 삭제 전 echo·ls로 확인 |
du -sh | 디렉토리 총 용량 | du -sh /var/log/nginx/ |
find | 이름·시간·크기 조건 검색 | find . -name '*.log' |
ln -s / readlink -f | 심볼릭 링크 생성·실제 경로 확인 | readlink -f /var/www/current |
관련 모듈로 더 깊이:
- 원격 리눅스 서버 안전 접속과 절대 무너지지 않는 기본 명령어 — 경로 이동의 전제가 되는 셸·터미널 사용의 기초
- chmod/chown으로 파일 읽기·쓰기·실행 권한 완벽 제어 — 디렉터리를 드나들고 파일을 읽을 수 있는지 결정하는 권한 체계
- grep/awk/sed로 거대한 로그 파일에서 원하는 행 찾기 — 찾은 파일의 내용을
grep·awk로 분석하는 다음 단계
다음 모듈에서는 파일 내용을 읽고 검색하는 방법 — cat, less, grep으로 로그 파일을 분석하는 실무 기술을 다룹니다.