SFTP 업로드가 하나 실패해도 멈추지 않도록: 오류 정책과 서버 선택 흐름 개선
2026년 10월 24일 업데이트 — 탭과 예약 대기열까지 이어진 SFTP 도구
현재 SFTPTools-Mac은 서버별 탭을 사용하며 같은 서버도 독립된 탭으로 여러 번 연결할 수 있다. 각 탭은 로컬·원격 경로를 따로 유지한다. 탭을 닫을 때 마지막 경로를 저장하지만 앱을 다시 실행한다고 연결된 탭을 자동 복원하지는 않는다. 같은 서버에서는 마지막으로 닫힌 탭의 경로가 우선한다.
접속 정보 관리창은 크기와 위치를 기억하고 저장 성공·실패를 확인창으로 알려준다. 저장된 비밀번호가 없으면 접속할 때 입력받으며 비밀번호와 PEM 키 비밀번호는 Keychain에서 관리한다. 접속 정보 복제도 지원한다. 화면의 접속 정보와 편집 중인 값을 구분하려던 기존 개선이 이 관리 흐름으로 이어졌다.

위 이미지는 현재 소스보다 나중에 빌드된 로컬 앱의 접속 정보 관리창이다. 접속 정보 파일을 비워 둔 임시 저장 공간으로 실행해 촬영했다. 실제 서버에 연결하거나 파일을 전송하지 않았으며 빈 목록은 기능이 없는 상태를 뜻하지 않는다.
업로드 중에 추가한 항목은 대기열 끝에 예약된다. 진행 중이거나 예약된 업로드가 있으면 해당 서버 탭과 앱의 일반 종료를 막는다. 대기열에서 제외한 항목은 목록에서 없애되 완료 요약에는 제외 개수를 남긴다. 서버 연결이 끊긴 탭은 재접속할 수 있다.
오류가 났을 때 건너뛰기·대기열 제외·현재 예약 중단을 고르는 방식은 유지된다. 같은 업로드의 이후 오류에 선택을 재사용할 수 있지만 이것이 전송 오류를 자동으로 모두 해결한다는 뜻은 아니다. 이번에는 화면과 현재 소스를 대조했으며 실제 서버 전송 검증을 새로 수행하지 않았다. 아래에는 오류 정책과 서버 선택을 처음 다듬었던 개발 기록을 남긴다.
이전 개발 기록
아래는 최초 작성 당시의 개발 기록이다. 현재 기능과 화면은 위 업데이트 내용을 우선한다.
여러 파일이나 폴더를 서버로 전송할 때 가장 곤란한 상황은 일부 항목의 오류 때문에 전체 작업이 중단되는 것입니다. 권한이 없는 파일 하나, 사라진 경로 하나만 있어도 나머지 정상 파일까지 다시 선택해야 한다면 대량 업로드의 부담이 커집니다.
최근 macOS용 SFTP 도구에서는 이 문제를 중심으로 업로드 오류 처리와 저장 서버 선택 방식을 개선했습니다. 별도의 백엔드와 프론트엔드로 나뉜 구조가 아니라, SwiftUI 화면과 상태 관리 계층이 macOS 기본 SFTP 명령 실행 서비스를 호출하는 단일 데스크톱 애플리케이션입니다.
기존에는 무엇이 불편했나
첫 오류가 전체 업로드 오류가 되던 구조
기존 업로드 반복문은 파일이나 폴더 전송 중 예외가 발생하면 해당 대기열 항목을 오류로 표시한 뒤 예외를 다시 상위로 전달했습니다. 상위 작업 처리기는 이를 전체 전송 실패로 간주했기 때문에, 사용자가 남은 파일을 계속 보낼 방법이 없었습니다.
또한 부분적으로 성공한 파일과 처리하지 못한 파일을 구분하기 어려웠습니다. 진행률만 보면 작업이 어디까지 끝났는지, 실패한 항목이 최종 집계에 어떻게 반영됐는지 명확하지 않았습니다.
선택과 접속이 겹치던 서버 목록
저장 서버 행에는 단일 클릭 선택과 이중 클릭 접속이 함께 연결돼 있었습니다. 같은 화면 요소에서 두 제스처를 각각 처리하면 첫 클릭의 선택 반응과 두 번째 클릭의 접속 판별이 서로 영향을 줄 수 있습니다.
저장 서버로 접속할 때도 편집 화면의 현재 인증값에 의존할 여지가 있었습니다. 단순히 서버를 선택한 상황과 실제 접속 정보를 편집하는 상황을 명확히 구분할 필요가 있었습니다.
업로드 오류를 사용자가 결정하도록 바꾸다
새 구현에서는 업로드 실패를 세 가지 정책으로 나눴습니다.
- 건너뛰고 계속: 실패한 항목을 원래 작업 범위에 남겨 두되, 처리 결과를 ‘건너뜀’으로 기록하고 다음 항목으로 이동합니다.
- 대기열에서 제외 후 계속: 아직 처리하지 못한 수를 전체 전송 수에서 빼고, 해당 항목을 ‘제외’ 상태로 표시합니다.
- 전체 업로드 중단: 예외를 상위 처리기로 전달해 기존처럼 전체 작업을 오류 상태로 종료합니다.
건너뛰기와 제외를 선택할 때는 이번 업로드의 이후 오류에 같은 선택 적용 옵션을 사용할 수 있습니다. 이 옵션을 켜면 이후 실패부터는 대화상자를 반복해서 열지 않습니다. 전체 중단은 실수로 남은 오류에 자동 적용되지 않도록 반복 정책에 저장하지 않습니다.
항목 전송 시도
→ 성공: 완료 수 증가
→ 실패: 건너뛰기 / 제외 / 전체 중단 선택
→ 건너뛰기: 원래 전체 수 유지
→ 제외: 미처리 수만큼 전체 수 감소
→ 중단: 전체 작업 오류 처리핵심 구현 구조와 동작 흐름
전송 계획과 실행을 분리
업로드는 먼저 선택된 항목과 접속된 서버 탭을 확인하고, 대상 원격 경로와 덮어쓰기 여부를 사용자에게 확인합니다. 같은 이름의 항목이 서버에 있으면 덮어쓰기, 건너뛰기 또는 취소 정책을 먼저 적용합니다.
폴더는 하위 디렉터리와 파일을 미리 조사해 전송 계획을 만듭니다. 원격 폴더를 준비한 다음 파일을 일정한 크기의 묶음으로 나눠 전송합니다. 현재 구현은 파일 업로드를 80개 단위로 처리하고, 원격 폴더 생성과 검증 명령도 더 큰 묶음으로 나눠 한 번에 지나치게 많은 명령을 실행하지 않도록 구성돼 있습니다.
- 접속된 서버와 선택된 로컬 항목을 검사합니다.
- 원격 경로와 덮어쓰기 설정을 확인합니다.
- 폴더라면 하위 디렉터리와 파일 목록으로 업로드 계획을 만듭니다.
- 파일별 대기열 항목을 만들고 SFTP 출력의 백분율을 진행률로 반영합니다.
- 항목 오류가 발생하면 사용자의 실패 정책을 적용합니다.
- 남은 전송을 마친 뒤 서버 목록을 새로고침하고 성공·건너뜀·제외 수를 요약합니다.
예외 상태가 진행률에 덮어써지지 않게 보호
비동기 전송에서는 늦게 도착한 진행률 콜백이 이미 ‘건너뜀’이나 ‘제외’로 바뀐 항목을 다시 ‘전송 중’ 또는 ‘완료’로 덮어쓸 수 있습니다. 이를 막기 위해 완료·건너뜀·제외를 종료 상태로 정의하고, 종료된 항목에는 이후 진행률 갱신을 적용하지 않습니다.
전송 대기열은 진행 중인 항목을 우선 따라가고, 진행 중 항목이 없으면 최근의 오류·건너뜀·제외 항목을 보여줍니다. 모든 작업이 끝나면 성공 수뿐 아니라 건너뛴 수와 제외한 수도 상태 메시지에 함께 표시합니다.
서버 클릭 판별을 독립된 상태로 관리
저장 서버 목록은 전용 화면 구성요소로 분리됐습니다. 클릭 추적기는 마지막으로 누른 서버와 클릭 시각을 기억합니다. 첫 클릭은 기다리지 않고 즉시 선택하고, 같은 서버를 운영체제의 이중 클릭 허용 시간 안에 다시 눌렀을 때만 접속을 실행합니다. 다른 서버를 누르거나 시간이 지나면 새로운 첫 클릭으로 처리합니다.
서버를 단순 선택할 때는 편집 폼을 불필요하게 덮어쓰지 않습니다. 사용자가 명시적으로 수정 화면을 연 경우에만 선택한 프로필을 폼에 적용합니다. 저장된 서버 접속은 보안 저장소의 인증값을 사용하고, 새 접속이나 편집 중 접속은 폼의 값을 사용하도록 경로도 분리했습니다.
사용자는 어떻게 이용하나
- 저장 서버 목록에서 서버를 한 번 눌러 선택합니다. 곧바로 접속하려면 같은 서버를 이중 클릭하거나 선택 후 ‘열기’ 또는 ‘접속’을 누릅니다.
- 왼쪽 로컬 파일 영역에서 업로드할 파일이나 폴더를 선택하고 업로드 버튼을 누릅니다.
- 확인 창에서 원격 대상 경로와 기존 항목 덮어쓰기 여부를 확인합니다.
- 업로드 중 오류가 발생하면 건너뛰기, 대기열 제외 또는 전체 중단을 선택합니다. 오류가 여러 번 예상되면 이후 오류에 같은 선택을 적용할 수 있습니다.
- 하단 전송 대기열에서 파일별 진행률과 완료·오류·건너뜀·제외 상태를 확인합니다. 작업이 끝나면 최종 요약에서 각 결과의 수를 확인합니다.
‘건너뛰기’는 실패 항목을 원래 작업의 일부로 기록하며 계속 진행하고, ‘제외’는 아직 처리하지 못한 수를 전체 작업량에서 제거한다는 차이가 있습니다.
안정성과 보안에서 고려한 점
- 운영체제 기본 도구 활용: 별도 SFTP 구현 대신 macOS의 SFTP 실행 파일을 사용하며, 명령 출력은 실행 중 별도 흐름에서 계속 읽어 진행률을 갱신합니다.
- 명령과 경로 이스케이프: 로컬·원격 경로와 자동 입력 스크립트 문자열을 각각의 문법에 맞게 이스케이프해 공백이나 특수문자가 있는 경로를 안전하게 전달합니다.
- 비밀값 분리 저장: 접속 프로필에는 호스트, 사용자 이름, 경로 같은 설정만 저장하고 비밀번호와 개인 키 암호는 macOS Keychain에 분리합니다.
- 빈 값으로 기존 비밀번호를 삭제하지 않음: 비밀값 입력이 비어 있으면 Keychain 갱신 명령 자체를 실행하지 않습니다. 사용자가 명시적으로 비밀값 저장을 해제하거나 프로필을 삭제할 때만 관련 항목을 삭제합니다.
- 필요할 때만 보안 입력창 표시: 저장된 비밀번호가 없거나 암호화된 개인 키에 암호가 필요한 경우 보안 입력 필드를 표시합니다. 입력값은 SFTP 명령 문자열이나 작업 로그에 직접 넣지 않습니다.
- 상태와 집계의 일관성: 부분 성공 뒤 발생한 오류의 미처리 수를 계산해 건너뜀과 제외 결과에 각각 맞게 진행률과 전체 수를 보정합니다.
회귀 방지를 위해 같은 선택을 이후 오류에 재사용하는 정책, 재질문이 필요한 경우, 첫 클릭과 이중 클릭 판별을 다루는 단위 테스트도 추가됐습니다. 기존 테스트에는 조각나서 들어오는 SFTP 진행률 출력의 해석과 빈 비밀값의 Keychain 보존 동작이 포함돼 있습니다.
최종적으로 개선된 점
이번 변경의 핵심은 단순히 오류 대화상자를 하나 추가한 것이 아닙니다. 업로드 실패를 전체 작업의 종료 조건으로만 취급하던 구조를 항목별 의사결정으로 바꾸고, 그 선택이 진행률·대기열·최종 요약에 일관되게 반영되도록 했습니다.
서버 선택 역시 첫 클릭은 즉시 반응하고 이중 클릭만 접속으로 이어지도록 명확해졌습니다. 저장된 인증값과 편집 중인 값을 분리하면서, 서버 선택 때문에 편집 상태가 예상치 않게 바뀌거나 잘못된 인증값을 참조할 가능성도 줄였습니다.
그 결과 여러 파일을 전송하는 사용자는 일부 오류가 있어도 나머지 작업을 이어갈 수 있고, 어떤 항목이 성공하고 건너뛰거나 제외됐는지 마지막까지 확인할 수 있게 됐습니다.
댓글 0
댓글 남기기