프로젝트 배경
응용프로그램 배포 업무를 인수인계받아 작업하던 중 충격을 받았다. 응용프로그램 배포를 위해서는 윈도우 PC에서 압축 파일을 풀고, 새로운 응용프로그램을 넣어 다시 압축한 후 FileZilla로 윈도우 서버 FTP에 접속해서 업로드해야 했다.
업무 히스토리가 궁금해서 파악해본 결과, 맥과 윈도우의 호환성 문제로 인해 윈도우 PC에서만 압축 작업이 가능했고, 윈도우 서버에는 FileZilla를 통해서만 접근할 수 있다고 했다. 이런 방식으로 몇 년 동안 진행하고 있었던 것이다!
이런 방법은 휴먼 에러의 위험성도 크고, 무엇보다 시간이 너무 오래 걸려서 해결 방안에 대해 고민했다. 최근 바이브 코딩으로 열심히 프로젝트를 만들고 있었기에 매우 흥미로운 도전이었다.
그래서 바로 만들어보기로 했다.
나의 목표는 수동으로 처리하던 파일 압축, 해제, 관리 작업들을 웹 인터페이스를 통해 효율적으로 처리할 수 있는 도구를 만들어, 기존 1시간 업무를 5분으로 줄이는 것이었다.
기술 스택
- Backend: Fastify (Node.js)
- Frontend : Vanilla JavaScript
- 압축 라이브러리: archiver
- 파일 전송: FTP
- 배포: 리눅스 서버 (Docker)
바이브 코딩은 신이다
개발을 생각보다 아주 순조로웠다. 요구사항 프롬프트를 자세히 작성하니 클로드가 예쁘게 코드를 짜줬다. 윈도우 서버의 접근하기 위해 노드의 FTP 클라이언트로 연결을 했으며, 한 두시간 만에 원하는 요구사항으로 개발이 완료되었다.
로컬로 테스트를 했더니 압축과 풀기, 업로드, 재압축 모두 성공적이었다.
너무 기뻐서 주변 사람들에게 로컬로 시연해주며 으쓱해있었다!
그런데.. 배포를 했더니 문제가 발생하기 시작했다..
개발 과정에서 만난 문제들과 해결 과정
첫 번째 고난: FTP 연결 실패

로컬에서는 문제가 없었는데, 이상하게 배포를 하니 FTP 연결이 실패하는 문제가 발생했다.
코드의 문제보다는 서버 쪽 보안 문제인 것 같아 이것저것 찾아보니 네트워크 방화벽 문제였다.
- 원인 분석:
- 로컬(맥): FTP 완벽 작동 O
- 리눅스 서버: 데이터 포트 차단 X
- 원인:
- 서버 방화벽에 아웃바운드 설정을 해주지 않아서 발생한 문제였다.
- 해결:
- 회사에서 사용하고 있는 클라우드 서비스 업체 설정에 아웃바운드 방화벽 포트를 추가해서 해결되었다.
- 이제 배포된 사이트에서 FTP 접속이 가능해졌다!
두번째 시련: FTP 연결은 성공했지만 데이터 불러오기실패

또 다른 문제가 발생했다.
아웃바운드 설정으로 FTP 연결에 성공하여 모든 게 끝났다고 생각했지만, 데이터 조회가 되지 않았다. (ㅠㅠ)
이 문제를 해결하기 위해서는 FTP의 동작 방식을 이해해야했다.
FTP는 다른 프로토콜과 달리 두개의 연결을 사용한다고 한다.
- 제어 연결데이터 연결
- 제어 연결
- 데이터 연결
내가 1번 제어 연결을 성공했지만, 2번 데이터 연결에서 막힌것이었다.
그럼 데이터 연결 포트만 아웃바운드하면 되겠다고 단순하게 생각했는데, 데이터 연결 포트는 랜덤이었다... 수백 개의 포트를 개별 등록하는 것은 비현실적이었고, 모든 데이터 포트 범위를 허용하기엔 클라우드 업체에서 단일 포트만 허용할 수 있었다. (범위 지정 불가) ㅠㅠ

결과는 .. 실패!
다른 방법으로는 윈도우 서버 포트를 고정하는 것이었는데, 이는 다소 위험 부담이 있었다. (포트를 고정해버리면 보안상 취약할 것 같았다.)
FTP는 정말 구식 프로토콜었다. 포트 구조도 복잡하고 방화벽에 친화적이지도 않았다...
이렇게 포기해야하나 싶었는데 팀장님이 힌트를 주셨다. 'SFTP'를 찾아보라고 !
세번째, FTP -> SFTP 전환하기, 'SFTP'가 뭘까? 🤔
우선 FTP와 SFTP의 차이점을 찾아보았다.
- FTP
💡 포트: 21번(제어) + 랜덤 포트(데이터) ← 문제의 원인!
보안: 평문 전송연결: 이중 연결 구조
- SFTP
💡 포트: 22번 하나만
보안: SSH 암호화 통신연결: 단일 연결 구조
포트 하나만 사용한다고" 이거다!
랜덤 포트 문제 완전 해결할 수 있었다. 22번 포트 하나만 아웃바운드 요청하면 되는 것!
SFTP로 바꿔보자.
- 라이브러리 교체
기존 fpt 라이브러리를 제거하고 ssh2-sftp-client를 깔았다.
- 연결 설졍 변경
// Before: FTP
const FtpClient = require('ftp');
const client = new FtpClient();
client.connect({
host: 'windows-server.com',
port: 21,
user: 'username',
password: 'password'
});
// After: SFTP
const SftpClient = require('ssh2-sftp-client');
const sftp = new SftpClient();
await sftp.connect({
host: 'windows-server.com',
port: 22, // 단일 포트!
username: 'username',
password: 'password'
});
- api 메서드 변경
// Before: FTP
client.list('/', (err, list) => {
client.put(localFile, remoteFile, callback);
});
// After: SFTP
const fileList = await sftp.list('/');
await sftp.put(localFile, remoteFile);
네 번째: 데이터 불러오기는 성공했지만... 파일 업로드 실패

SFTP 접속과 데이터 조회까지는 성공했는데 또 다른 문제가 발생했다.
파일을 업로드했더니 파일 생성은 되지만 0byte로 만들어지면서 "Write stream error: Permission denied" 에러가 떠버렸다.
윈도우 서버에 파일 권한 설정이 읽기 전용으로 되어있어서, 모든 권한을 허용하여 해결했다.
다섯 번째: nginx 파일 업로드 사이즈 문제
배포까지 완료되고 본격적으로 사용하기 전 업로드 테스트 중에, 100MB 이상인 파일을 업로드했더니 413 에러를 뱉었다.
이 문제는 따로 포스팅 했다! 참고바란다.
https://blog.naver.com/hblee-/223935845082
최종 결과
FTP의 복잡한 포트 문제를 SFTP의 단일 포트(22번) 사용으로 깔끔하게 해결할 수 있었고, 더 안정적인 시스템을 구축할 수 있었다!
핵심 구현 기능들
- SFTP 연결 및 인증
- 파일 업로드/다운로드 - 대용량 파일 처리 가능
- 서버 파일 조회
- 파일 존재 확인 - 중복 업로드 방지
- 파일 삭제 - 서버 파일 관리
운영팀과 회의

웹 사이트 가이드를 만들어 전달했고, 긍정적인 반응을 받았다 ! 과정은 힘들었지만, 또 이런맛에 개발하는 거지..
운영팀에서 더 필요한 피드백을 받아서 기능 추가를 할 계획이다!
회고
자동화하려는 목표는 상당 부분 달성했지만, 네트워크 환경과 보안 정책의 제약으로 완벽한 해결책을 찾기는 어려웠다.
특히 FTP 프로토콜의 복잡성과 보안, 호스팅 업체의 방화벽 정책 등이 가장 큰 걸림돌이었다.
이런 네트워크 문제는 아직 바이브 코딩으로 해결할 수 없다! 그러니 네트워크 CS 지식의 기본기를 탄탄히 해야겠다고 다짐했다.
배포 과정에서 삽질이 많았지만 이 과정에서 다양한 네트워크 프로토콜, 배포 그리고 보안 정책에 대해 원리를 생각해볼 수 있었다.
개발은 문제 해결의 연속이며, 그 과정에서 짜릿함을 느끼는 것 같다.
때로는 기술적 완벽함보다 현실적 제약 안에서의 최선의 선택이 더 중요하다는 것을 다시금 느낄 수 있었다 !
그래도 힘들었지만 재미있는 사이드 프로젝트였다 ㅎㅎ