mLog 명판이 있는 산악 케이블카 역에서 흰색 3D 유령 운송기사가 서버 역에서 도착한 문서 소포를 노트북 옆으로 받아 오는 모습

받을 컴퓨터에서 출발지와 목적지를 정해 서버의 파일을 가져온다.

scp로 서버 파일을 내 PC에 가져오기: -P 포트·저장 경로·권한 오류 구분

서버·인프라 SSH 2026년 11월 6일

서버의 파일을 내 PC로 내려받으려면 내 PC 터미널에서 원격 파일을 앞에, 로컬 저장 위치를 뒤에 둔다. 접속 포트를 지정할 때는 scp -P처럼 대문자 P를 쓴다.

아래는 macOS·Linux의 Bash/zsh 계열 터미널에서 사용할 명령 예시다. server.example.net, ubuntu, 2222, 키 파일과 원격 경로는 모두 설명용이다. 실제 접속에 사용하는 값으로 바꾼다. Windows PowerShell의 드라이브 경로 표기는 이 글의 범위에서 제외한다.

이 글의 오류와 전송 결과는 별도로 만든 Linux 로컬 시험 환경에서 확인했다. 개인 운영 서버나 Mac에서 같은 장애를 겪고 해결했다는 사례로 쓰지 않았다.

1. 먼저 내 PC에 새 저장 폴더를 만든다

같은 이름의 로컬 파일이 있으면 덮어쓸 수 있다. 아래 예제는 매번 다른 임시 이름의 수신 폴더를 만들어 기존 파일과 섞이지 않게 한다. 생성된 폴더는 명령이 끝나도 남아 있다.

내 PC의 새 터미널 탭에서 실행한다.

mkdir -p "$HOME/Downloads" &&
dest=$(mktemp -d "$HOME/Downloads/scp-receive.XXXXXX") &&
scp -P 2222 -i "$HOME/.ssh/id_ed25519" \
  'ubuntu@server.example.net:/home/ubuntu/export/report.txt' \
  "$dest/" &&
printf '저장 위치: %s\n' "$dest"

&& 뒤의 명령은 앞의 명령이 성공했을 때만 실행된다. 마지막 저장 위치가 출력됐다면 그 폴더에서 report.txt를 확인한다. dest 변수는 이 명령을 실행한 셸에 있으므로, 아래 예제들도 같은 터미널 탭에서 이어서 사용한다.

키 파일은 서버의 공개키 파일이 아니라 내 PC가 인증에 사용하는 개인키다. 평소 SSH에서 다른 키 파일을 지정했다면 그 경로를 넣는다. 이미 SSH 설정 별칭을 사용한다면 뒤의 별칭 예제를 쓰면 된다.

명령은 다음 순서로 읽으면 된다.

  • -P 2222: 원격 SSH 접속 포트.
  • -i ...: 로컬 인증 키 경로.
  • ubuntu@server.example.net:/home/ubuntu/export/report.txt: 가져올 원격 파일.
  • "$dest/": 내 PC에 만든 수신 폴더.

일반적인 사용자@호스트:경로 표기에서는 콜론 뒤가 파일 경로다. 호스트:2222:/경로처럼 포트를 끼워 넣지 않는다. 이 글은 별도의 scp:// URI 문법을 사용하지 않는다.

OpenSSH의 scp 공식 매뉴얼은 원본과 대상의 표기, -P, -i 등의 옵션을 설명한다.

2. 서버에 로그인한 창에서 실행하면 내 PC로 오지 않는다

로컬은 항상 내 노트북이라는 뜻이 아니다. 지금 scp 프로세스를 실행하는 컴퓨터다.

예를 들어 서버에 SSH로 로그인한 상태에서 다른 서버의 파일을 .으로 받으면, 파일은 그 SSH 세션이 실행 중인 서버의 현재 디렉터리에 놓인다. 내 PC의 다운로드 폴더로 보내라는 의미는 없다.

먼저 명령 실행 위치를 확인한다.

hostname
pwd

서버 셸을 보고 있다면 내 PC에서 새 터미널 탭을 여는 편이 명확하다. SSH 세션에서 exit해 로컬 셸로 돌아올 수도 있지만, 중첩해서 접속했다면 한 번의 exit만으로 내 PC가 아닐 수 있다. 다시 호스트와 경로를 확인한다.

이번 재현에서는 클라이언트의 작업 디렉터리를 별도 폴더로 바꾸고 목적지에 .을 넣었다. 받은 파일은 실제로 그 폴더에 생성됐다. 전송이 성공했는데 파일을 못 찾는다면, 서버 파일을 다시 지우거나 재전송하기 전에 scp를 실행한 컴퓨터와 목적지 경로부터 찾자.

3. SSH의 -p와 scp의 -P를 구분한다

같은 서버를 대상으로 해도 포트 옵션 표기는 다르다.

# 로그인 또는 원격 명령: 소문자 p
ssh -p 2222 -i "$HOME/.ssh/id_ed25519" ubuntu@server.example.net

# 파일 다운로드: 대문자 P
scp -P 2222 -i "$HOME/.ssh/id_ed25519" \
  'ubuntu@server.example.net:/home/ubuntu/export/report.txt' "$dest/"

scp -p는 포트 설정이 아니다. 원본의 수정·접근 시각과 파일 모드 비트를 보존하는 옵션이다. 따라서 scp -p 2222 ...로 바꿔 쓰면 기대한 포트 지정이 되지 않는다. 이 차이는 ssh 매뉴얼과 scp 매뉴얼에서 확인할 수 있다.

같은 서버에 자주 접속한다면 내 PC의 ~/.ssh/config에 별칭을 추가할 수 있다. 기존 파일을 통째로 덮어쓰지 말고 해당 호스트 블록을 추가하거나 수정한다.

Host sample-server
    HostName server.example.net
    User ubuntu
    Port 2222
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

그러면 다음처럼 쓸 수 있다.

ssh sample-server
scp sample-server:/home/ubuntu/export/report.txt "$dest/"

IdentitiesOnly yes는 설정한 인증 키를 중심으로 사용하도록 제한해, 에이전트가 제공하는 다른 키가 섞이는 상황을 줄인다. IdentityFile은 여러 개가 누적될 수 있으므로 “이 줄 하나면 반드시 키 하나만 사용한다”는 의미로 해석하지 않는다.

실제로 선택된 설정은 서버에 접속하지 않고 확인할 수 있다.

ssh -G sample-server |
  awk '$1 ~ /^(hostname|user|port|identityfile|identitiesonly)$/ {print}'

ssh -G는 설정을 보여 주는 명령이지 접속 성공 검사 자체는 아니다. 별칭과 설정 우선순위는 ssh_config 공식 문서를 기준으로 확인한다.

4. 폴더와 공백 경로는 각각 처리한다

폴더는 -r을 추가한다

scp -r -P 2222 -i "$HOME/.ssh/id_ed25519" \
  'ubuntu@server.example.net:/home/ubuntu/export/bundle' "$dest/"

여기서는 이미 존재하는 dest 폴더 아래에 bundle을 받는 예시다. 이번 시험에서는 bundle/nested/data.txt와 bundle/.hidden까지 복사되는 것을 확인했다. 같은 디렉터리에 -r 없이 요청했을 때는 다음 오류로 종료됐다. <LAB>만 시험용 임시 경로를 대신한 표시다.

scp: download <LAB>/remote/bundle/: not a regular file

-r은 디렉터리를 재귀 복사하며, 공식 문서상 탐색 중 만난 심볼릭 링크도 따라간다. 링크가 섞인 운영 폴더를 무조건 통째로 받기보다 전송하려는 파일 범위를 먼저 확인한다. 실제로 사용 중인 DB 데이터 디렉터리를 복사하는 것을 일관된 DB 백업으로 간주해서도 안 된다.

공백이 있으면 경로를 하나의 인자로 묶는다

scp -P 2222 -i "$HOME/.ssh/id_ed25519" \
  'ubuntu@server.example.net:/home/ubuntu/export/report 01.txt' \
  "$dest/report 01.txt"

이 예제는 현재 OpenSSH의 기본 SFTP 방식과 로컬 셸의 인용 처리를 전제로 한다. 이번 시험에서도 공백이 포함된 원격 파일과 로컬 파일명을 따옴표로 묶어 전송했고 내용이 일치했다.

OpenSSH는 9.0 릴리스부터 scp의 기본 전송 방식을 SFTP로 바꿨다. 예전 SCP 프로토콜용으로 따옴표를 겹겹이 넣은 예제를 그대로 붙이면 오히려 실패할 수 있다. 먼저 ssh -V와 자신의 man scp를 확인한다. -O는 예전 SCP 프로토콜로 전환하는 옵션이지 모든 경로 오류의 해결책이 아니다.

5. Permission denied는 어느 단계에서 났는지 본다

인증 단계에서 막힌 경우

Permission denied (publickey)처럼 파일 경로를 열기 전 인증이 거절된다면, 파일 권한을 변경하기 전에 대상 호스트·계정·포트·키가 평소 SSH와 같은지 대조한다.

ssh -v -p 2222 -i "$HOME/.ssh/id_ed25519" \
  ubuntu@server.example.net 'id -un'

서버의 공개키 인증 정책 자체를 점검해야 한다면 SSH 비밀번호 차단과 root 로그인 정책으로 이어서 확인할 수 있다. 파일을 받기 위해 비밀번호 인증이나 root 로그인을 새로 열 필요부터 있다고 가정하지 않는다.

접속은 됐지만 원격 파일을 읽지 못한 경우

이번 시험에서는 합성 파일의 읽기 권한을 제거하자 인증 이후 다음 오류가 발생했다.

scp: remote open "<LAB>/remote/unreadable.txt": Permission denied

셸 사용도 허용된 서버라면 같은 계정으로 파일과 상위 경로를 확인한다.

ssh -p 2222 -i "$HOME/.ssh/id_ed25519" ubuntu@server.example.net \
  'ls -ld /home/ubuntu/export; ls -l /home/ubuntu/export/report.txt'

파일 읽기 권한뿐 아니라 상위 디렉터리를 통과할 수 있어야 한다. ACL 등 추가 접근 정책도 원인이 될 수 있다. 로컬에서 sudo scp를 붙여도 원격 계정이 자동으로 sudo 사용자가 되지는 않는다.

필요한 자료가 관리자 전용 위치에 있다면 서버 관리 절차에 따라 전송이 허용된 파일만 해당 계정이 읽을 수 있는 별도 내보내기 폴더로 준비한다. 오류를 없애려고 운영 데이터 폴더 전체에 chmod -R 777이나 소유권 변경을 적용하지 않는다.

파일이 없거나 로컬 저장 위치가 잘못된 경우

이번에 존재하지 않는 원격 파일을 요청했을 때는 다음 오류가 나왔다.

scp: <LAB>/remote/missing.txt: No such file or directory

이 문구만 보고 항상 서버 파일이 없다고 단정하면 안 된다. 실제 메시지에 나온 경로가 원격 원본인지 로컬 대상인지 읽어야 한다. 대소문자, 절대 경로, 로컬 폴더 존재 여부를 확인한다. 초기 확인에서는 원격 홈 축약이나 와일드카드보다 파일 하나의 절대 경로를 지정하면 비교할 항목이 줄어든다.

Connection refused나 시간 초과는 파일 경로를 바꿀 단계보다 앞선 연결 문제다. 평소 SSH가 도달하는 주소와 포트를 먼저 확인한다. 반대로 SSH 로그인은 되는데 subsystem request failed가 나온다면 서버의 SFTP 제공 여부나 제한 정책을 관리자에게 확인한다. 이 오류는 이번 실험에서 재현한 항목은 아니다.

서버 신원 경고는 인증 오류와 분리한다

처음 접속할 때 표시되는 호스트 키 지문은 신뢰할 수 있는 별도 경로의 서버 지문과 대조한다. 기존 호스트 키가 바뀌었다는 경고는 일반적인 파일 권한 오류가 아니다. 서버 재설치 등 정당한 변경인지 확인하기 전에는 호스트 검증을 끄거나 known_hosts 전체를 삭제해서 넘기지 않는다. ssh_config의 호스트 키 검증 규칙을 기준으로 판단한다.

6. 다운로드 성공과 백업 성공을 구분한다

첫 예제의 scp가 성공하면 수신 폴더를 확인한다.

ls -l "$dest/report.txt"

중요한 완료 파일은 원본과 수신본의 SHA-256도 비교할 수 있다. 전송·해시 계산 중 바뀌지 않는 파일을 대상으로 한다.

# 원격 Linux에 GNU sha256sum이 있는 경우
ssh -p 2222 -i "$HOME/.ssh/id_ed25519" ubuntu@server.example.net \
  'sha256sum /home/ubuntu/export/report.txt'

# 내 PC에 shasum이 있는 경우
shasum -a 256 "$dest/report.txt"

양쪽 출력의 파일 경로가 아니라 해시 값을 비교한다. Linux 클라이언트에서는 sha256sum "$dest/report.txt"를 써도 된다. 각 도구의 입력·검증 방식은 GNU sha256sum 매뉴얼과 shasum 문서에서 확인할 수 있다.

이번 합성 파일 전송에서는 원본과 다운로드 파일의 바이트 및 SHA-256 일치를 확인했다. 이것은 파일 전송 결과를 검증한 것이며, DB나 애플리케이션을 복원할 수 있다는 보장은 아니다. 백업 아카이브를 받는 상황이라면 Ghost·MySQL 백업 무결성 점검의 내용 확인·복원 검증도 별도로 이어 간다.

Tailscale의 Taildrop으로 보낸 파일을 찾는 중이라면 scp의 목적지 폴더 규칙과 섞지 말자. 그 경우는 Ubuntu Taildrop 수신함에서 파일 가져오기가 해당 절차다.

검증 환경과 확인 범위

2026-11-06, Debian 13.6 / Linux x86_64 / OpenSSH 10.0p2 Debian-7+deb13u4에서 확인했다. 127.0.0.1에만 바인딩한 임시 sshd와 합성 파일을 사용했고, 생성한 서버 키를 별도 known_hosts에 미리 등록해 검증했다. 기존 SSH 설정과 사용자 서버는 변경하지 않았다.

10개 검증 항목은 SSH 키 인증, 비표준 포트를 지정한 파일 다운로드, -r 없는 폴더 전송 실패, 하위·숨김 파일을 포함한 폴더 복사, 공백 경로, . 목적지, 없는 원본, 원격 읽기 권한 거부, 같은 이름의 대상 덮어쓰기, SSH 별칭의 유효 포트·키 설정이다. 예상 실패 사례도 예상한 오류로 끝났을 때 검증 통과로 계산했다.

테스트 종료 후 임시 서버를 중지하고 시험용 키와 파일을 정리했다. macOS 실기기, 실제 OCI 회선, 회사망, Windows PowerShell, 구형 SCP 프로토콜, 중단된 대용량 전송의 재개는 시험하지 않았다. 내려받기 전에 내 PC에서 실행하는지, 포트와 계정이 맞는지, 원본을 읽을 수 있는지, 받을 폴더가 맞는지를 확인하는 것이 이 글의 적용 범위다.

아래 ZIP에는 재현 코드와 실행 결과가 들어 있다. 시험 환경과 제한 사항은 README에서 함께 확인할 수 있다.

태그

mLog

Spring Boot와 PostgreSQL로 서비스를 개발하고, Oracle Cloud에서 Docker로 mLog를 운영합니다. 쿼리가 느려지거나 컨테이너가 뜨지 않을 때 어떤 로그와 명령으로 원인을 찾았는지 기록합니다. 직접 확인한 결과와 공식 문서를 참고한 예제를 구분해 적습니다.