Develop Study/Spring
(25.12.29) Java Spring 에서의 SFTP
API를 통해서 서버간 데이터를 주고 받을 수 있지만, 비동기 방식으로 외부 서버와 서비스가 연동이 되어있을 때는,
파일 전송을 위해 SFTP 를 구성하기 된다
실제 업무에서도 SFTP를 통해 외부 서버와 파일을 주고 받으면서 서비스가 운용되므로 해당 부분을 JavaSpring 서버단 기준으로 정리하고, 또, 레거시인 기술과 다른 Integrated 된 모듈을 활용하는 방법으로도 정리하고자 한다.
SFTP
- SSH File Transfer Protocol, 네트워크 프로토콜로, 원격 시스템 간에 파일을 안전하게 전송하고 관리하기 위한 규칙(통신 규약)
- SSH 연결을 통해 파일 전송, 디렉토리 조작, 목록 열람 같은 작업을 처리
특징
- 네트워크 트래픽 전부 암호화 (평문 전송 없음)
- 기본적으로 SSH(포트 22) 위에서 동작
- 인증(비밀번호/키) + 전송 모두 암호화된 채널로 수행
- 업로드/다운로드뿐 아니라 디렉토리 조작도 가능
- 서버 간 자동 전송에도 적합하여 자동화/스크립트 친화적
SFTP 서버
- SFTP 서버 = SFTP 프로토콜을 지원하는 서비스
- 단독 소프트웨어 / 서버 운영체제의 SSH 서버가 SFTP 기능을 포함
- 접속할 수 있게 포트(보통 22)를 열어놓고 SSH/SFTP 요청을 받아 파일 업로드/다운로드를 처리하는 서버
- NAS 역시 해당 기능을 가질 경우 SFTP 서버가 가능
JSch 를 사용한 SFTP
- Java Secure Channel, Java에서 SSH 프로토콜을 사용하기 위한 라이브러리
- Java 코드로 SSH 서버에 접속하고, 그 위에서 SFTP 같은 작업을 자동으로 수행가능
- SSH 자체를 구현하는 게 아니라 SSH 프로토콜을 Java에서 사용할 수 있게 감싸놓은 도구
단점
- JSch는 2000년대 초반에 만들어진 라이브러리
- 공식 릴리즈가 수년간 사실상 멈춰 있음
- 보안 패치 / 알고리즘 추가가 매우 느림
- Algorithm negotiation fail
- 모든 관리가 수동이기 때문에, connect close 관리가 없다면, 세션 누수, 커넥션 고갈, 좀비 세션이 존재 가능성
- Session ChannelSftp 객체 재사용 불가 → 멀티쓰레드시 장애 발생
- Batch , 스케쥴러 환경에 치명적
Example
public class SFTPUtil {
private String host;
private int port;
private String user;
private String passwd;
private int connectTimeout = 3000;
public SFTPUtil(String host, int port, String user, String passwd) {
this.host = host;
this.port = port;
this.user = user;
this.passwd = passwd;
}
public boolean send(File sourceFile, String directory) {
boolean result = false;
Session session = null;
Channel channel = null;
ChannelSftp channelSftp = null;
FileInputStream fis = null;
JSch jsch = new JSch();
try {
session = jsch.getSession(this.user, this.host, this.port);
session.setPassword(this.passwd);
session.setConfig("StrictHostKeyChecking", "no");
session.connect(this.connectTimeout);
channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
if (!existsDir(channelSftp, directory)) {
makeDir(channelSftp, directory);
}
fis = new FileInputStream(sourceFile);
String uploadFileName = Paths.get(directory, sourceFile.getName()).toString();
channelSftp.cd(directory);
channelSftp.put(fis, sourceFile.getName());
result = true;
}
...
private boolean existsDir(ChannelSftp channelSftp, String directory) {
boolean exists = false;
try {
SftpATTRS attrs = channelSftp.stat(directory);
exists = (attrs != null);
} catch (Exception e) {
}
return exists;
}
private void makeDir(ChannelSftp channelSftp, String directory) {
SftpATTRS attrs = null;
String pathInfo = File.separator;
String FS = File.separatorChar == '\\\\' ? "\\\\\\\\" : File.separator;
String[] dirs = directory.split(FS);
int length = dirs.length;
String dir;
StringBuffer sb = new StringBuffer(pathInfo);
for (int i = 0; i < length; i++) {
if (dirs[i] != null && !dirs[i].isEmpty()) {
attrs = null;
sb.append(dirs[i]).append(File.separator);
dir = sb.toString();
try {
attrs = channelSftp.stat(dir);
} catch (Exception e) {
}
if (attrs == null) {
try {
channelSftp.mkdir(dir);
channelSftp.cd(dir);
} catch (SftpException e) {
}
}
}
}
...
}
리소스 객체
- Session : SSH 연결 (TCP + 인증)
- Channel : SSH 위의 논리 채널
- ChannelSftp : SFTP 전용 채널
- FileInputStream : 로컬 파일 스트림
→ SFTP = SSH Session + SFTP Channel
SSH 세션 생성 & 연결 FLOW
JSch jsch = new JSch();
session = jsch.getSession(this.user, this.host, this.port);
session.setPassword(this.passwd);
session.setConfig("StrictHostKeyChecking", "no");
session.connect(this.connectTimeout);
- JSch 클라이언트 생성
- SSH 세션 객체 생성
- 비밀번호 인증 방식 설정
- 서버 호스트 키 검증 비활성화
- 지정된 시간 내 SSH 연결 시도
SFTP 채널 열기
channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
- SSH 연결 위에 SFTP 전용 채널 생성
- **ls cd put get**같은 파일 시스템 명령 가능
서버 디렉토리 확인 후 파일 업로드
if (!existsDir(channelSftp, directory)) {
makeDir(channelSftp, directory);
}
fis = new FileInputStream(sourceFile);
channelSftp.cd(directory);
channelSftp.put(fis, sourceFile.getName());
- 디렉토리가 없다면 새로 만듬
- 로컬 파일을 스트림으로 열고
- SFTP 서버 디렉토리로 이동
- 스트림 내용을 서버로 전송 → 종료
Spring Integration SFTP를 활용한 SFTP
- Spring에서 제공하는 SFTP 전용 모듈
- JSch 가 아닌 Apache MINA SSHD 내부 구현을 통한 SFTP
특징
- 커넥션 관리 자동
- 예외 처리 정돈됨
- Spring Bean 기반 설정
- 배치 / 파일 전송 / 연계 작업에 최적
→ JSch로 직접 하던 걸 Spring 스타일로 감싸준 것
Factory
@Bean
public DefaultSftpSessionFactory sftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory();
factory.setHost("localhost");
factory.setPort(22);
factory.setUser("user");
factory.setPassword("password");
factory.setAllowUnknownKeys(true);
return factory;
}
JSch의
- JSch
- Session
- setConfig("StrictHostKeyChecking", "no")
- 전부 대체
Fileupload
public class SpringSftpUploader {
private final DefaultSftpSessionFactory sessionFactory;
public SpringSftpUploader(DefaultSftpSessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public boolean send(File sourceFile, String remoteDir) {
try (SftpSession session = sessionFactory.getSession();
FileInputStream fis = new FileInputStream(sourceFile)) {
if (!session.exists(remoteDir)) {
session.mkdir(remoteDir);
}
String remotePath = remoteDir + "/" + sourceFile.getName();
session.write(fis, remotePath);
return true;
} catch (Exception e) {
return false;
}
}
}
- SftpSession session = sessionFactory.getSession() 로 JSch 의 연결/인증/채털관리 모두 추상화되어 관리가 가능자
- 디렉토리가 없을경우 검증이 필요없이 자동으로 만들어줌
참고자료
https://aws.amazon.com/ko/what-is/sftp/
Secure File Transfer Protocol이란 무엇인가요? SFTP - 설명됨 - AWS
SFTP가 무엇인지, 기업에서 SFTP를 사용하는 방식과 이유, AWS에서 SFTP를 사용하는 방법에 대해 알아보세요.
aws.amazon.com
https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol
물론 멀티스레드/싱글스레드 환경인지, 고도화 작업에 많은 리소스가 필요한지 점검을 해야겠지만, 여러 방법을 통해,
자동으로 스케쥴러와 연동해 주기적으로 데이터를 옮기거나 전달할때 유용하게 사용할 수 있을 것이다.

'Develop Study > Spring' 카테고리의 다른 글
| (25.10.22) Java Spring 에서의 Caching (0) | 2025.10.22 |
|---|---|
| (25.09.02) Java Spring 의 스케쥴러 @scheduled의 shedlock (0) | 2025.09.02 |
| (25.07.16) Java Spring Boot 환경에서 CTE 동작 확인 (MyBatis 와 JPA 활용) (2) | 2025.07.16 |
| (25.05.08) Spring Boot 설정값 주입 : @ConfigurationProperties 활용 (0) | 2025.05.08 |
| (25.04.30) Spring Security 인가 Authorization Filter 의 인가 실패 AccessDeniedHandler & AuthenticationEntryPoint (1) | 2025.04.30 |