보험사와 데이터를 연계하는 업무를 맡았습니다. 매일 새벽 2시에 CSV 파일을 상대방 SFTP 서버에 올리고, 처리 결과 파일을 내려받아야 합니다. 처음에는 수동으로 했는데 이제 자동화해야 합니다. 개인키 인증 설정, 파일 올리는 스크립트, 전송 완료 확인 — 어떻게 하면 될까요?
외부 기관 파일 연계는 생각보다 자주 있는 업무입니다. SFTP 구조와 키 기반 인증, 배치 자동화 패턴을 실무 중심으로 익혀봅니다.
- 1SFTP, SCP, rsync의 차이를 설명하고 상황에 맞게 선택할 수 있다
- 2SSH 키 기반 SFTP 인증을 설정하고 연결을 테스트할 수 있다
- 3sftp -b 옵션으로 배치 업로드 스크립트를 작성할 수 있다
- 4전송 완료 여부를 파일 크기 비교와 md5 체크섬으로 검증할 수 있다
- 5chroot 격리의 개념과 경로 혼동 문제를 설명할 수 있다
파일 전송 도구 비교
SFTP vs SCP vs rsync — 언제 무엇을 쓰나
파일을 원격으로 전송하는 도구가 여러 개 있어 헷갈립니다. 각각 강점이 다르고, 외부 기관 연계에 적합한 것은 SFTP입니다.

보험사와 파일 연계를 시작했는데, 자동화 스크립트가 매일 새벽 실패합니다. 어제는 SCP로 올렸더니 상대방이 "파일이 안 보인다"고 하고, rsync를 써보니 방화벽에서 막힙니다. 파트너사마다 요구하는 전송 방식이 다르고, 각 도구의 동작 방식을 모르면 장애 원인도 파악하기 어렵습니다. 외부 기관 파일 연계에서 가장 많이 쓰이는 도구는 SFTP입니다. 포트 22번 하나로 디렉터리 탐색, 업로드, 다운로드, 권한 확인을 모두 처리할 수 있어 방화벽 규정을 충족하기 쉽고, 대부분의 금융·공공 기관 시스템이 SFTP 서버를 표준으로 운영합니다.
| 도구 | 전송 방식 | 대화형 | 배치 | 증분 동기화 | 재시작 | 주 사용 |
|---|---|---|---|---|---|---|
| SCP | SSH 위 복사 | 불가 | 가능 | 불가 | 불가 | 단순 파일 복사 |
| SFTP | SSH 서브시스템 | 가능 | 가능 | 불가 | 부분 가능 | 외부 기관 연계 |
| rsync | SSH + rsync 프로토콜 | 불가 | 가능 | 가능 | 가능 | 백업, 미러링 |
SFTP가 외부 기관 연계에 적합한 이유:
- 상대방 서버 디렉터리 구조를 탐색하며 파일 위치 확인 가능
- 표준 SSH 22번 포트만 열면 돼 방화벽 규정 충족이 쉬움
- 대부분의 파트너 시스템(은행, 보험사, 공공기관)이 SFTP를 표준으로 지원
- chroot 격리로 서버 측 보안 설정 용이
SSH 키 기반 SFTP 인증 설정
비밀번호 인증은 자동화에서 사용할 수 없습니다. 배치 스크립트가 비밀번호를 코드에 넣는 것은 보안상 금지입니다. SSH 키 기반 인증으로 패스워드 없이 자동 접속하는 구조를 만들어야 합니다.
# 1. 전용 SFTP 키 쌍 생성 (배치 작업용 별도 키 권장)
ssh-keygen -t ed25519 -f ~/.ssh/sftp_partner_key -N "" -C "sftp-batch@myapp"
# -N "" → passphrase 없음 (자동화용)
# 생성 파일: ~/.ssh/sftp_partner_key (개인키)
# ~/.ssh/sftp_partner_key.pub (공개키)
# 2. 개인키 권한 설정 (반드시)
chmod 600 ~/.ssh/sftp_partner_key
chmod 700 ~/.ssh/
# 3. 공개키를 파트너에게 전달 (보안 채널로)
cat ~/.ssh/sftp_partner_key.pub
# ssh-ed25519 AAAAC3NzaC1lZDI... sftp-batch@myapp
# 이 내용을 파트너 서버의 ~/.ssh/authorized_keys에 추가 요청
# 4. 접속 테스트
sftp -i ~/.ssh/sftp_partner_key -o StrictHostKeyChecking=no \
batchuser@sftp-partner.example.com
# 5. SSH config로 편하게 관리 (~/.ssh/config)
# Host sftp-partner
# HostName sftp-partner.example.com
# User batchuser
# IdentityFile ~/.ssh/sftp_partner_key
# Port 22
# StrictHostKeyChecking accept-new
# config 설정 후 접속
sftp sftp-partner
SFTP 배치 스크립트
SFTP 접속 후 사용하는 기본 명령어를 익힙니다. 자동화 전에 대화형으로 먼저 경로와 권한을 확인합니다.
sftp -i ~/.ssh/sftp_key batchuser@sftp-server
# SFTP 프롬프트에서 사용하는 명령어
sftp> pwd # 원격 현재 경로
sftp> ls -la # 원격 파일 목록
sftp> cd /upload # 원격 디렉터리 이동
sftp> lpwd # 로컬 현재 경로 (l 접두어 = local)
sftp> lcd /tmp # 로컬 디렉터리 이동
sftp> put data.csv # 로컬→원격 업로드
sftp> get result.csv # 원격→로컬 다운로드
sftp> ls -la data.csv # 업로드 후 원격 파일 크기 확인
sftp> bye # 종료
sftp -i ~/.ssh/sftp_key batchuser@sftp-server- sftp 접속 후 비밀번호 프롬프트 없이
sftp>프롬프트가 나타나는지 확인 (키 인증 성공 여부) pwd명령 결과가 원격 서버의 기대 경로와 일치하는지 확인put후ls -la로 원격에 파일이 생성되고 크기가 0바이트 이상인지 확인
-b 옵션은 명령어 파일을 stdin처럼 읽어 순차적으로 실행합니다. 이를 이용해 배치 업로드 스크립트를 만듭니다.
#!/bin/bash
# /opt/batch/scripts/upload_daily.sh
SFTP_HOST="sftp-partner.example.com"
SFTP_USER="batchuser"
SFTP_KEY="/home/appuser/.ssh/sftp_partner_key"
LOCAL_FILE="/opt/batch/data/export_$(date +%Y%m%d).csv"
REMOTE_DIR="/upload/incoming"
LOG_FILE="/opt/batch/logs/sftp_$(date +%Y%m%d).log"
echo "[$(date)] 배치 업로드 시작" >> "$LOG_FILE"
# SFTP 명령어 파일 생성 (임시)
SFTP_BATCH_FILE=$(mktemp)
cat > "$SFTP_BATCH_FILE" << 'SFTP_EOF'
cd /upload/incoming
put /opt/batch/data/export_20260530.csv
ls -la export_20260530.csv
bye
SFTP_EOF
# 실제 스크립트에서는 날짜를 동적으로 처리
REMOTE_FILENAME="export_$(date +%Y%m%d).csv"
printf "cd %s\nput %s\nls -la %s\nbye\n" \
"$REMOTE_DIR" "$LOCAL_FILE" "$REMOTE_FILENAME" \
> "$SFTP_BATCH_FILE"
# SFTP 실행
sftp -i "$SFTP_KEY" \
-o BatchMode=yes \
-o StrictHostKeyChecking=accept-new \
-b "$SFTP_BATCH_FILE" \
"${SFTP_USER}@${SFTP_HOST}" >> "$LOG_FILE" 2>&1
EXIT_CODE=$?
rm -f "$SFTP_BATCH_FILE"
if [ $EXIT_CODE -eq 0 ]; then
echo "[$(date)] 업로드 성공" >> "$LOG_FILE"
else
echo "[$(date)] 업로드 실패 (exit code: $EXIT_CODE)" >> "$LOG_FILE"
# 알림 발송 등 추가 처리
exit 1
fi
sftp -i /home/appuser/.ssh/sftp_key -b /tmp/sftp_commands.txt batchuser@sftp-server- 스크립트 실행 후 로그 파일에
[날짜] 업로드 성공메시지가 기록됐는지 확인 sftp -b실행 exit code가 0인지 확인 (echo $?)- 원격 서버에서
ls -la출력에 파일이 나타나고 크기가 로컬과 동일한지 확인
파일이 전송됐다고 가정하지 말고, 실제로 완전히 전송됐는지 확인합니다. 두 가지 방법을 씁니다.
# 방법 1: 파일 크기 비교
LOCAL_SIZE=$(stat -c%s /opt/batch/data/export_20260530.csv)
REMOTE_SIZE=$(ssh -i ~/.ssh/sftp_key batchuser@sftp-server \
"stat -c%s /upload/incoming/export_20260530.csv" 2>/dev/null)
if [ "$LOCAL_SIZE" = "$REMOTE_SIZE" ]; then
echo "파일 크기 일치: ${LOCAL_SIZE} bytes"
else
echo "크기 불일치! 로컬: ${LOCAL_SIZE}, 원격: ${REMOTE_SIZE}"
fi
# 방법 2: md5 체크섬 비교 (더 정확)
LOCAL_MD5=$(md5sum /opt/batch/data/export_20260530.csv | awk '{print $1}')
REMOTE_MD5=$(ssh -i ~/.ssh/sftp_key batchuser@sftp-server \
"md5sum /upload/incoming/export_20260530.csv" | awk '{print $1}')
if [ "$LOCAL_MD5" = "$REMOTE_MD5" ]; then
echo "체크섬 일치 — 전송 완료 확인"
else
echo "체크섬 불일치 — 재전송 필요"
fi
# 방법 3: 마커 파일 방식 (파트너와 약속한 규약)
# 전송 완료 후 .done 파일 생성
sftp -i ~/.ssh/sftp_key batchuser@sftp-server << 'EOF'
put export_20260530.csv /upload/incoming/export_20260530.csv
symlink /upload/incoming/export_20260530.csv /upload/incoming/export_20260530.done
EOF
ssh -i ~/.ssh/sftp_key batchuser@sftp-server stat /upload/incoming/export_20260530.csv- sftp -i 접속 시 비밀번호 프롬프트 없이 바로 sftp> 프롬프트가 나왔는가 (키 인증 성공)
- 배치 스크립트 실행 후 로그 파일에 '업로드 성공'이 기록됐는가
- 원격 파일 ls -la 출력의 파일 크기가 로컬과 일치하는가
- md5sum 비교 시 두 해시값이 동일한가
- chmod 600 ~/.ssh/sftp_key 설정이 적용됐는가 (ls -la로 확인)
chroot 보안 격리
chroot — SFTP 사용자를 지정 디렉터리에 가두기
외부에서 SFTP로 접속하는 계정이 서버 전체 파일 시스템을 볼 수 있으면 보안 위험입니다. sshd_config의 chroot 설정으로 SFTP 사용자를 특정 디렉터리에 격리합니다.
# /etc/ssh/sshd_config 에서 SFTP 전용 그룹 chroot 설정
Match Group sftpusers
ChrootDirectory /sftp/%u
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
# 주의: ChrootDirectory는 root 소유, 755 이상 권한이어야 함
# chroot 대상 경로 생성
sudo mkdir -p /sftp/batchuser/upload
sudo chown root:root /sftp/batchuser # root 소유 필수
sudo chmod 755 /sftp/batchuser
sudo chown batchuser:sftpusers /sftp/batchuser/upload
sudo chmod 775 /sftp/batchuser/upload
chroot 경로 혼동 주의:
chroot 설정 후 SFTP 사용자가 접속하면 /sftp/batchuser가 루트(/)로 보입니다. 사용자는 cd /upload로 이동하지만, 실제 서버 경로는 /sftp/batchuser/upload입니다. 파트너에게 경로를 안내할 때 사용자 관점의 경로(/upload)로 알려줘야 혼동이 없습니다.

트러블슈팅
원인: 키 기반 인증에 필요한 파일의 권한이 잘못됐거나, 공개키가 서버에 올바르게 등록되지 않았습니다.
# 1. 클라이언트 측 권한 확인
ls -la ~/.ssh/
# drwx------ → .ssh 디렉터리: 700 이어야 함
# -rw------- → 개인키: 600 이어야 함
# 권한 수정
chmod 700 ~/.ssh
chmod 600 ~/.ssh/sftp_partner_key
# 2. SSH 디버그 모드로 접속해 원인 확인
sftp -i ~/.ssh/sftp_partner_key -vvv batchuser@sftp-server 2>&1 | head -50
# "Offering public key" 후 "Server refused our key" 메시지 → 서버 측 authorized_keys 문제
# "Permission denied" 바로 나오면 → 클라이언트 키 권한 문제
# 3. 서버 측 authorized_keys 확인 (서버 접근 가능한 경우)
sudo cat /home/batchuser/.ssh/authorized_keys
# 공개키(sftp_partner_key.pub 내용)가 있는지 확인
# 4. 서버 측 .ssh 디렉터리 권한 확인
sudo ls -la /home/batchuser/.ssh/
# .ssh: 700, authorized_keys: 600 이어야 함
sudo chmod 700 /home/batchuser/.ssh
sudo chmod 600 /home/batchuser/.ssh/authorized_keys
# 5. SELinux 문제 (CentOS/RHEL)
sudo restorecon -Rv /home/batchuser/.ssh/
원인: chroot 설정으로 인해 사용자 관점의 경로와 실제 서버 경로가 다릅니다. 파트너에게 잘못된 경로를 알려줬거나, 파트너 시스템이 서버 실제 경로로 조회하고 있습니다.
# 1. SFTP 접속 후 현재 경로 확인
sftp -i ~/.ssh/sftp_key batchuser@sftp-server
sftp> pwd
# Remote working directory: /upload ← 사용자 관점 경로
# (실제 서버 경로는 /sftp/batchuser/upload)
# 2. 서버 측에서 실제 파일 위치 확인
sudo find /sftp/batchuser -name "export_*.csv" -ls
# 3. 파트너에게 경로 재확인
# 파트너가 서버에서 직접 접근하는 경로: /sftp/batchuser/upload/파일명
# 파트너가 SFTP로 접근하는 경로: /upload/파일명
# 둘 다 동일한 파일을 가리키지만 표현이 다름
# 4. 업로드 후 즉시 경로 확인하는 검증 추가
sftp -i ~/.ssh/sftp_key batchuser@sftp-server << 'EOF'
put /opt/batch/data/export_20260530.csv
ls -la /upload/export_20260530.csv
EOF
# sftp 출력에 파일이 보이면 정상 업로드됨
실제 업무에서 이 지식이 쓰이는 상황:
외부 기관과의 파일 연계는 금융, 공공, 의료 도메인에서 특히 자주 있습니다. 실무에서 반복되는 세 가지 상황입니다.
1. 신규 외부 기관 연계 설정:
# 표준 체크리스트
# □ 전용 배치 계정 생성 (batchuser_파트너명)
# □ 전용 키 쌍 생성 (기존 키 재사용 금지)
# □ 공개키 전달 (보안 채널: 암호화된 이메일 또는 직접 전달)
# □ 개인키 권한 600 확인
# □ SSH config에 등록
# □ 방화벽 아웃바운드 22번 허용 (상대방 IP 명시)
# □ 접속 테스트 (sftp -vvv로 디버그)
# □ 배치 스크립트 crontab 등록
2. 배치 전송 실패 대응:
# 로그 먼저 확인
tail -50 /opt/batch/logs/sftp_$(date +%Y%m%d).log
# exit code 확인 → 0이 아니면 재전송
# 재전송 전 원격 파일 존재 여부 확인 (중복 방지)
3. 전송 파일 감사: 외부 기관과의 파일 연계는 감사 대상입니다. 무엇을 언제 보냈는지 로그와 체크섬을 파일로 남겨두는 것이 중요합니다. 장애 발생 시 "우리가 파일을 제대로 보냈는지"를 증명해야 할 때가 있습니다.
다음 모듈에서는 JDBC Connection Pool 설정과 DB 장애 시 서비스 격리 실무를 다룹니다.