서버에서 500 오류가 반복되거나 Node.js 서비스가 갑자기 종료됐을 때, 로그 파일은 있는데 어디를 봐야 할지 모르는 상황을 해결하는 글입니다. 특히 로그가 수천 줄 이상 쌓여 있을 때 grep 명령어로 로그 검색하는 방법을 알고 있으면 오류 문구, 발생 시간, 반복 패턴을 먼저 좁힐 수 있습니다.
운영 중에는 브라우저에 보이는 오류 화면만 보고 판단하면 원인을 놓치기 쉽습니다. 실제로는 애플리케이션 로그, systemd journal, Nginx error.log, PM2 logs처럼 로그가 여러 곳에 나뉘어 남는 경우가 많습니다. 그래서 grep을 쓰기 전에 어떤 로그를 볼 것인지, 현재 사용자에게 읽기 권한이 있는지, 검색어가 실제 로그 문구와 맞는지를 함께 확인해야 합니다.
이 글에서 해결할 문제
- 로그 파일이 너무 길어서 오류 문구를 빠르게 찾기 어렵다
- grep을 실행했는데 아무 결과가 나오지 않는다
- Permission denied 때문에 로그를 읽지 못한다
- 파일 로그와 journalctl 로그 중 어디를 봐야 할지 헷갈린다
- 대소문자, 경로, 권한 문제 때문에 실제 오류를 놓친다
여기서는 Debian/Ubuntu 계열 서버를 우선 기준으로 설명합니다. CentOS, Rocky Linux, AlmaLinux 같은 계열에서는 일부 로그 파일 위치가 다를 수 있으므로, 배포판과 서비스 설정에 따라 조정해서 봐야 합니다.
먼저 확인할 핵심 요약
| 확인 단계 | 명령어 예시 | 판단 기준 |
|---|---|---|
| 현재 위치 확인 | pwd |
상대 경로로 잘못 검색하고 있지 않은지 확인 |
| 로그 파일 존재 확인 | ls -lh /var/log/app/app.log |
파일이 실제로 있고 크기가 증가하는지 확인 |
| 기본 검색 | grep 'ERROR' /var/log/app/app.log |
정확한 대문자 문구가 있는지 확인 |
| 대소문자 무시 | grep -i 'error' /var/log/app/app.log |
ERROR, Error, error를 함께 검색 |
| 줄 번호 포함 | grep -n 'ERROR' /var/log/app/app.log |
나중에 less, sed 등으로 앞뒤 문맥 확인 가능 |
| 서비스 로그 확인 | journalctl -u myapp.service |
systemd로 실행한 서비스인지 확인 |
실제 서버 운영 중 헷갈리기 쉬운 지점
grep은 문자열을 찾는 명령어라 단순해 보이지만, 서버 운영에서는 명령어보다 주변 조건 때문에 막히는 경우가 많습니다. 처음에는 애플리케이션 오류처럼 보였지만 실제로는 로그 파일 경로가 달랐거나, 실행 사용자가 달라 로그가 다른 위치에 생성되는 경우도 있습니다.
특히 Node.js 서비스를 직접 실행할 때와 systemd 또는 PM2로 실행할 때는 로그 위치가 달라질 수 있습니다. 터미널에서 직접 실행하면 화면에 보이던 오류가 서비스로 등록한 뒤에는 journalctl이나 PM2 로그에만 남는 식입니다. 이런 상황에서는 같은 검색어를 쓰더라도 어느 파일을 대상으로 검색하는지가 더 중요합니다.
- 배포 계정으로 접속했지만 로그 파일은 root 또는 adm 그룹만 읽을 수 있다
- 애플리케이션 로그가 파일이 아니라 systemd journal에 남는다
- Nginx 오류는 앱 로그가 아니라
/var/log/nginx/error.log에 남는다 - PM2로 실행한 Node.js 앱은
pm2 logs에서 먼저 보이는 경우가 있다 - 로그 시간이 서버 시간대와 달라 실제 발생 시점을 헷갈릴 수 있다
처음 의심하기 쉬운 원인과 실제 원인의 차이
grep 결과가 없으면 많은 경우 검색어가 틀렸다고 생각합니다. 하지만 운영 서버에서는 검색어보다 경로, 권한, 로그 저장 위치가 원인인 경우가 더 자주 보입니다.
| 처음 의심하기 쉬운 원인 | 실제 원인일 수 있는 것 | 확인 방법 |
|---|---|---|
| 에러가 발생하지 않았다 | 다른 로그 파일에 남고 있다 | systemctl status, journalctl, Nginx 로그 확인 |
| grep 명령어가 틀렸다 | 파일 경로가 다르다 | pwd, ls -lh로 파일 위치 확인 |
| 로그가 비어 있다 | 읽기 권한이 없다 | ls -l, whoami, id 확인 |
| 앱 코드 오류다 | 포트 충돌이나 서비스 기동 실패다 | systemctl status, ss -tulpen 확인 |
실제로 자주 막히는 상황
상황 1. 500 오류가 나는데 앱 로그에서 아무것도 안 보이는 경우
웹 화면에는 500 오류가 보이지만 애플리케이션 로그에서 검색 결과가 없을 수 있습니다. 이때는 Nginx가 먼저 오류를 반환했거나, 앱까지 요청이 전달되지 않았을 가능성도 있습니다.
sudo grep -i 'error' /var/log/nginx/error.log
sudo grep -i 'upstream' /var/log/nginx/error.log
sudo는 로그 파일을 읽기 위한 용도로만 사용합니다. 운영 서버에서 권한 문제를 만났다고 해서 로그 디렉터리 권한을 넓게 바꾸는 방식은 피하는 것이 좋습니다. 먼저 현재 권한과 소유자를 확인해야 합니다.
whoami
id
ls -lh /var/log/nginx/error.log
상황 2. systemd 서비스가 바로 종료되는 경우
서비스가 실행되자마자 종료된다면 파일 로그보다 systemd 상태와 journal을 먼저 보는 편이 빠릅니다. 서버 운영에서는 재시작을 반복하기보다, 왜 종료됐는지 남아 있는 마지막 로그를 먼저 확인해야 합니다.
systemctl status myapp.service
sudo journalctl -u myapp.service -n 80 --no-pager
그다음 필요한 문구만 grep으로 좁힙니다.
sudo journalctl -u myapp.service -n 200 --no-pager | grep -i 'error'
상황 3. PM2로 실행한 Node.js 앱 오류를 찾는 경우
PM2로 Node.js 앱을 실행했다면 앱 로그가 일반 파일이 아니라 PM2 로그로 관리될 수 있습니다. 이 경우에는 먼저 프로세스 이름과 상태를 확인합니다.
pm2 list
pm2 logs myapp --lines 100
필요하면 PM2 로그를 grep으로 필터링할 수 있습니다.
pm2 logs myapp --lines 300 --nostream | grep -i 'error'
환경에 따라 --nostream 옵션 지원 여부가 다를 수 있습니다. 명령이 동작하지 않으면 pm2 logs --help로 현재 설치된 PM2 버전의 옵션을 확인합니다.
원인 분리: grep 전에 확인할 순서
문제가 생겼을 때 바로 넓은 범위로 grep -r을 돌리면 원하는 결과를 얻기 전에 불필요하게 많은 파일을 읽을 수 있습니다. 로그가 많은 서버에서는 I/O 부하가 생길 수 있으므로, 먼저 범위를 좁히는 것이 좋습니다.
- 오류가 발생한 서비스 이름을 확인한다.
- 서비스가 systemd, PM2, 직접 실행 중 어떤 방식인지 확인한다.
- 로그 파일 위치를 확인한다.
- 현재 사용자가 해당 로그를 읽을 수 있는지 확인한다.
- 검색어를 넓은 표현에서 좁은 표현으로 조정한다.
- 필요할 때만 여러 파일 또는 디렉터리 검색으로 범위를 넓힌다.
1단계: 서비스 상태 확인
systemctl status myapp.service
● myapp.service - My App
Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
Active: failed (Result: exit-code) since Sun 2026-05-10 10:12:35 KST; 2min ago
Process: 1234 ExecStart=/usr/bin/node /srv/myapp/server.js
Main PID: 1234 (code=exited, status=1/FAILURE)
Active: failed라면 grep으로 파일을 뒤지기 전에 journalctl에서 종료 직전 로그를 먼저 확인합니다.
2단계: 로그 파일 존재와 권한 확인
ls -lh /var/log/app/app.log
ls -ld /var/log/app
-rw-r----- 1 root adm 84K May 10 10:15 /var/log/app/app.log
drwxr-x--- 2 root adm 4.0K May 10 09:50 /var/log/app
위 예시처럼 파일 소유자가 root이고 그룹이 adm이면 일반 사용자에게 읽기 권한이 없을 수 있습니다. 이때는 파일 권한을 임의로 바꾸기보다, 운영 정책에 맞게 sudo로 읽는 것이 안전합니다.
3단계: 가장 좁은 검색어로 시작
sudo grep -i -n 'error' /var/log/app/app.log
120:2026-05-10 10:12:31 ERROR failed to connect to database
124:2026-05-10 10:12:35 ERROR retry attempt 1 failed
-i는 대소문자를 무시하고, -n은 줄 번호를 보여줍니다. 줄 번호가 있으면 이후에 해당 구간의 앞뒤 문맥을 확인하기 쉽습니다.
4단계: 앞뒤 문맥 확인
에러 한 줄만 보면 원인이 부족할 수 있습니다. 이럴 때는 -A, -B, -C 옵션으로 앞뒤 줄을 함께 봅니다.
sudo grep -i -n -C 3 'failed to connect' /var/log/app/app.log
117-2026-05-10 10:12:28 INFO loading config
118-2026-05-10 10:12:29 INFO connecting database
119-2026-05-10 10:12:30 WARN connection timeout: 1000ms
120:2026-05-10 10:12:31 ERROR failed to connect to database
121-2026-05-10 10:12:32 INFO retry scheduled
122-2026-05-10 10:12:35 ERROR retry attempt 1 failed
-C 3은 일치한 줄 기준 앞뒤 3줄을 함께 보여줍니다. 로그 원인을 볼 때는 오류 줄 바로 위에 설정 로딩, 포트 바인딩, DB 연결 시도 같은 중요한 단서가 남는 경우가 많습니다.
잘못된 예시
예시 1. 현재 위치를 모르고 상대 경로로 검색
grep 'ERROR' logs/app.log
grep: logs/app.log: No such file or directory
이 경우 grep이 문제가 아니라 현재 디렉터리와 파일 경로가 맞지 않는 것입니다. 먼저 현재 위치와 파일 존재 여부를 확인합니다.
pwd
ls -lh
ls -lh logs/app.log
예시 2. 대소문자를 구분해서 검색해 결과를 놓침
grep 'error' /var/log/app/app.log
로그에는 ERROR, Error처럼 기록되어 있을 수 있습니다. 처음 확인할 때는 대소문자를 무시하고 넓게 찾는 편이 낫습니다.
grep -i 'error' /var/log/app/app.log
예시 3. 너무 넓은 범위를 바로 재귀 검색
sudo grep -r -i 'error' /var/log
이 명령은 동작할 수 있지만, 로그가 많은 서버에서는 많은 파일을 읽게 됩니다. 운영 중인 서버라면 먼저 서비스별 로그 파일을 좁혀서 확인하고, 정말 필요할 때만 범위를 넓히는 것이 좋습니다.
sudo grep -i 'error' /var/log/nginx/error.log
sudo journalctl -u myapp.service -n 200 --no-pager | grep -i 'error'
수정 예시: 오류 문구를 찾지 못할 때 점검 흐름
아래는 실제 운영에서 자주 쓰는 흐름입니다. 핵심은 grep 명령어를 바꾸기 전에 대상 로그가 맞는지를 먼저 확인하는 것입니다.
pwd
whoami
id
ls -lh /var/log/app/app.log
sudo tail -n 50 /var/log/app/app.log
sudo grep -i -n 'error' /var/log/app/app.log
tail -n 50은 마지막 50줄만 확인하는 읽기 명령입니다. 로그 파일을 수정하지는 않지만, 매우 큰 로그 파일을 반복해서 읽으면 서버에 부담이 될 수 있으므로 필요한 범위부터 확인하는 편이 좋습니다.
파일 로그가 없을 때의 수정 방향
파일이 없다면 애플리케이션이 로그 파일을 만들지 못했거나, 로그가 다른 곳에 남고 있을 수 있습니다. 바로 설정을 수정하기보다 서비스 실행 방식을 확인합니다.
systemctl status myapp.service
sudo journalctl -u myapp.service -n 100 --no-pager
만약 Node.js 앱을 PM2로 실행 중이라면 다음도 함께 확인합니다.
pm2 list
pm2 logs myapp --lines 100
서비스 실행 방식이 확인된 뒤에야 로그 파일 설정, PM2 로그 경로, systemd unit 파일의 표준 출력 설정 등을 검토하는 것이 안전합니다.
수정 후 확인 방법
오류 원인을 찾고 설정이나 환경을 조정했다면, 다시 같은 로그에서 오류가 줄었는지 확인해야 합니다. 단순히 서비스가 켜졌는지만 보지 말고, 로그에 같은 오류가 반복되는지 확인합니다.
systemctl status myapp.service
sudo journalctl -u myapp.service -n 50 --no-pager
sudo journalctl -u myapp.service -n 200 --no-pager | grep -i 'error'
Nginx를 거쳐 접속하는 서비스라면 앱 로그와 Nginx 로그를 분리해서 봅니다.
curl -I http://127.0.0.1:3000
sudo grep -i 'error' /var/log/nginx/error.log
curl로 내부 포트가 응답하는데 외부 도메인만 실패한다면, 애플리케이션 자체보다 Nginx 프록시, 방화벽, DNS, 프록시 서비스 등 다른 구간을 확인해야 할 수 있습니다. 이 글의 범위는 grep을 이용한 로그 검색이므로, 여기서는 먼저 로그와 서비스 상태를 기준으로 원인을 분리하는 데 집중합니다.
Ubuntu와 CentOS 계열에서 로그 위치를 볼 때 주의할 점
Debian/Ubuntu에서는 시스템 관련 로그를 /var/log/syslog에서 보는 경우가 많습니다. 반면 CentOS 계열에서는 /var/log/messages를 함께 확인하는 경우가 있습니다. 다만 최근 리눅스 서버는 systemd 기반인 경우가 많아 journalctl이 공통적으로 유용합니다.
| 환경 | 자주 확인하는 위치 | 예시 명령어 |
|---|---|---|
| Ubuntu/Debian | /var/log/syslog, journalctl |
sudo grep -i 'error' /var/log/syslog |
| Nginx | /var/log/nginx/error.log |
sudo grep -i 'upstream' /var/log/nginx/error.log |
| systemd 서비스 | journalctl -u 서비스명 |
sudo journalctl -u myapp.service -n 100 --no-pager |
| CentOS 계열 | /var/log/messages, journalctl |
sudo grep -i 'error' /var/log/messages |
로그 위치는 배포판뿐 아니라 서비스 설정에 따라 달라질 수 있습니다. 파일이 없다고 해서 바로 로그가 없는 것으로 판단하지 말고, 서비스 실행 방식과 설정을 함께 확인하는 것이 좋습니다.
자주 쓰는 grep 옵션 정리
| 옵션 | 역할 | 예시 |
|---|---|---|
-i |
대소문자 무시 | grep -i 'error' app.log |
-n |
줄 번호 표시 | grep -n 'ERROR' app.log |
-C 숫자 |
앞뒤 문맥 표시 | grep -C 3 'timeout' app.log |
-A 숫자 |
일치한 줄 이후 표시 | grep -A 5 'Exception' app.log |
-B 숫자 |
일치한 줄 이전 표시 | grep -B 5 'Exception' app.log |
-r |
디렉터리 재귀 검색 | grep -r 'ERROR' /var/log/app |
-r은 편리하지만 범위가 넓으면 많은 파일을 읽습니다. 운영 서버에서는 정확한 파일 하나를 먼저 보고, 필요할 때 디렉터리 단위로 넓히는 순서를 권장합니다.
재발 방지 체크리스트
- 서비스별 로그 위치를 문서나 배포 메모에 남겼다
- systemd 서비스인지, PM2 실행인지, 직접 실행인지 구분했다
- 로그 파일 권한과 소유자를 확인했다
- 권한 문제를 만났을 때 임의로 권한을 넓히지 않았다
- 오류 검색 시
-i,-n,-C옵션을 상황에 맞게 사용했다 - 파일 로그가 없을 때
journalctl도 함께 확인했다 - Nginx, 애플리케이션, PM2, systemd 로그를 구분해서 확인했다
- 큰 디렉터리에 바로 재귀 검색하지 않고 범위를 좁혀서 검색했다
- 로그 시간대가 헷갈리면
date,timedatectl로 서버 시간을 확인했다
FAQ
Q1. grep을 실행했는데 아무 출력도 없으면 오류가 없다는 뜻인가요?
항상 그렇지는 않습니다. 검색어가 실제 로그 문구와 다르거나, 대소문자가 다르거나, 다른 파일에 로그가 남고 있을 수 있습니다. 먼저 grep -i로 대소문자를 무시해 보고, 파일 경로와 서비스 실행 방식을 함께 확인하는 것이 좋습니다.
Q2. Permission denied가 나오면 어떻게 해야 하나요?
먼저 whoami, id, ls -l로 현재 사용자와 파일 권한을 확인합니다. 로그 확인이 필요한 상황이라면 운영 정책에 맞게 sudo grep으로 읽을 수 있습니다. 다만 문제를 빨리 해결하려고 로그 파일 권한을 넓게 바꾸는 방식은 보안상 피하는 것이 좋습니다.
Q3. journalctl과 로그 파일 중 무엇을 먼저 봐야 하나요?
systemd로 실행되는 서비스라면 systemctl status와 journalctl -u 서비스명을 먼저 보는 편이 빠릅니다. Nginx 오류는 /var/log/nginx/error.log, PM2로 실행한 Node.js 앱은 pm2 logs에서 먼저 확인하는 것이 좋습니다. 환경에 따라 로그 위치가 다를 수 있으므로, 서비스 실행 방식을 기준으로 판단해야 합니다.