Spring Boot로 SFTP에 멀티파트 파일 안전 업로드
Spring Boot에서 SSHJ 기반 SFTP 전송으로 멀티파트 파일을 안전하게 업로드하기 위한 설정, 코드 예제, 보안 및 예외 처리 중심의 구현 방법
목차
소개
웹 애플리케이션에서 사용자가 업로드한 멀티파트 파일을 SFTP 서버로 전송하는 요구가 많다. 이 글에서는 Spring Boot 환경에서 SSHJ 라이브러리를 사용해 안전하게 파일을 업로드하는 방법을 단계별로 설명한다. 초급자도 이해할 수 있도록 설정, 코드 예제, 보안 고려사항과 예외 처리 방안을 함께 제시한다.
필수 전제
사전 준비
- Spring Boot 2.x 이상 프로젝트
- SSHJ 라이브러리 의존성 추가
- SFTP 서버 접속 정보(호스트, 포트, 계정, 인증 방식)
의존성 추가
build 도구에 SSHJ와 Spring Web 의존성을 추가한다. Maven 예시는 다음과 같다.
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>sshj</artifactId>
<version>0.34.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
설정 파일
application.properties 또는 application.yml에 SFTP 접속 정보를 분리해 둔다. 민감 정보는 환경변수나 시크릿 매니저로 관리한다.
sftp.host=sftp.example.com
sftp.port=22
sftp.user=sftp_user
sftp.password=sftp_password
sftp.remoteDir=/data/uploads
핵심 구현
SftpService 구현
SSHJ를 이용해 연결을 열고 SFTPClient로 파일을 전송한다. 연결은 사용 후 반드시 닫아 리소스 누수를 방지한다.
package com.example.sftp;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import net.schmizz.sshj.sftp.SFTPClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@Service
public class SftpService {
@Value("${sftp.host}")
private String host;
@Value("${sftp.port}")
private int port;
@Value("${sftp.user}")
private String user;
@Value("${sftp.password}")
private String password;
@Value("${sftp.remoteDir}")
private String remoteDir;
public void upload(MultipartFile file) throws IOException {
SSHClient ssh = new SSHClient();
try {
// 실제 운영에서는 호스트 키 검증 구현 권장
ssh.addHostKeyVerifier(new PromiscuousVerifier());
ssh.connect(host, port);
ssh.authPassword(user, password);
SFTPClient sftp = ssh.newSFTPClient();
try {
String remotePath = remoteDir + "/" + file.getOriginalFilename();
sftp.put(file.getInputStream(), remotePath);
} finally {
sftp.close();
}
} finally {
try { ssh.disconnect(); } catch (Exception ignored) {}
try { ssh.close(); } catch (Exception ignored) {}
}
}
}
업로드 컨트롤러
컨트롤러에서 MultipartFile을 받고 서비스에 전달한다. 파일 크기 제한은 Spring 설정으로 제어한다.
package com.example.sftp;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@RestController
public class UploadController {
private final SftpService sftpService;
public UploadController(SftpService sftpService) {
this.sftpService = sftpService;
}
@PostMapping("/upload")
public ResponseEntity upload(@RequestParam("file") MultipartFile file) {
try {
sftpService.upload(file);
return ResponseEntity.ok("uploaded");
} catch (Exception e) {
return ResponseEntity.status(500).body("upload failed: " + e.getMessage());
}
}
}
보안 고려사항
- 비밀번호 대신 공개키 인증을 사용해 접속 보안 강화.
- 호스트 키 검증을 구현해 중간자 공격 방지. PromiscuousVerifier는 테스트용.
- 시크릿은 환경변수나 시크릿 매니저에 보관해 코드에 직접 노출 금지.
- 전송 전 파일 크기와 확장자 검증으로 악성 파일 업로드 차단.
예외 처리와 안정성
네트워크 장애와 I/O 오류를 고려해 재시도 정책과 로그를 준비한다. 대용량 파일은 스트리밍 기반 전송을 사용하고 메모리 과다 사용을 피한다. 연결 풀이나 재사용 로직을 도입하면 빈번한 연결/해제 비용을 줄일 수 있다.
테스트 방법
- 로컬 SFTP 서버(예: OpenSSH)로 통합 테스트 수행
- 경계값 테스트: 파일 크기, 동시 업로드 수 확인
- 오류 시나리오: 인증 실패, 디스크 공간 부족 등 재현
운영에서의 권장 구성
운영 환경에서는 키 기반 인증, 엄격한 호스트 키 검증, 시크릿 관리, 모니터링과 알림을 결합한다. 또한 업로드 실패 시 재시도와 보상 조치(예: 임시 저장소 보관)를 설계하면 안정성이 향상된다.
결론
Spring Boot에서 SSHJ를 사용하면 SFTP로 멀티파트 파일을 안전하게 전송할 수 있다. 핵심은 안전한 인증, 호스트 키 검증, 예외 처리와 리소스 관리를 철저히 하는 것이다. 위 예제와 권장 사항을 참고하면 실무 환경에서 신뢰성 있는 파일 전송을 구현하는 데 도움이 된다.