Files

FTPDJ — 대외기관 파일 수신 어댑터 (인수인계 문서)

대상 소스: src/com/eactive/eai/adapter/ftp/FTPDJ.java

관련 가이드: DJERP_1000_AP_BAP(FTP)개발가이드_v0.9_20260603.pptx — EMS 등록 절차·화면 조작 중심

최종 갱신: 2026-09-09

0. 이 문서에 대하여

FTPDJ를 넘겨받아 유지보수할 개발자를 위한 문서입니다. PPT 가이드가 "화면에서 어떻게 등록하는가"를 다룬다면, 이 문서는 "등록한 값이 코드에서 어떻게 해석되어 동작하는가" 를 다룹니다.

특히 FTPDJ의 동작은 4장~10장이 그 내용이며 이 문서의 핵심입니다.

값 검증이 없어 오타가 조용히 다른 동작으로 이어지는 지점이 몇 군데 있습니다. 6장(protocol)과 7장(io.bandwidth)은 반드시 읽고 넘어가십시오.


1. 저장소 · 빌드 · 배포

1.1 Git 저장소

origin   https://git.eactive.synology.me:8090/eapim/djbank/bapweb.git
브랜치 용도
master 주 브랜치
soxor 작업 브랜치 (FTPDJ 개발이 진행된 곳)

FTPDJ 관련 주요 커밋:

커밋 일자 내용
adeec70 2026-06-05 FileFetchRequestHandler 구현 (HTTP 요청 진입점 신규)
7f98f97 2026-09-07 취약점 patch version (의존 jar 교체)

1.2 빌드

Ant 기반입니다. Maven/Gradle 설정은 없으며 jar는 WebContent/WEB-INF/lib에 직접 커밋되어 있습니다.

ant war        # clean → compile → dist/BAPWeb.war 생성
ant build      # 컴파일만 (build/classes)
항목
Java source/target 17 (build.xml)
컴파일 classpath WebContent/WEB-INF/lib/*.jar (단 ezgatormodule2.jar 제외) + libs/*.jar
산출물 dist/BAPWeb.war
WAS WebLogic, context-root /BAPWeb (WebContent/WEB-INF/weblogic.xml)

WebContent/WEB-INF/oldlib은 취약점 조치로 교체된 구버전 jar 보관용입니다. 클래스패스에도 WAR에도 포함되지 않습니다(build.xml의 war 태스크에서 제외 처리). 되돌릴 일이 없다면 삭제해도 무방합니다.


2. FTPDJ는 무엇인가

EMS(관리자포탈)의 대외기관 등록 → 송수신구분코드에서 FTPDJ를 선택하면 이 클래스가 전송 어댑터로 동작합니다. DJBank 공용 SFTP 프록시를 경유해 대외기관의 파일을 내려받는(Download) 전용 어댑터입니다.

항목 내용
클래스 com.eactive.eai.adapter.ftp.FTPDJ
상속 / 구현 Transfer 상속, SocketService 구현
지원 동작 수신(recvFile)만 지원
미지원 sendFile() 호출 시 UnsupportedOperationException
전송 라이브러리 JSch 기반 (FTPUtil.listFilesJSCH, FTPUtil.retrieve)

SocketService의 나머지 메서드(connect, isActive, getCurrentSocket 등)는 인터페이스 계약을 채우기 위한 빈 구현입니다. 실제 로직은 recvFile() 한 곳에서 시작합니다. 소스를 처음 읽는다면 여기부터 보십시오.


3. 전체 동작 흐름

[1] HTTP 요청 (action=DOWNLOAD, remoteCode, remoteFile)
        │  FileFetchRequestHandler
        ▼
[2] Job_Queue 테이블에 작업 등록  →  즉시 응답 (status=QUEUED, jobId=UUID)
        │  스케줄러가 매분 0초 / 30초에 큐를 집어감
        ▼
[3] FTPDJ.recvFile(batchDoc)                        ← 이 문서의 범위
        │
        ├─ FtpEnv.init()      : 접속정보(DB) + 프로퍼티(DB)로 실행 환경 조립   ← 핵심
        ├─ listFilesJSCH()    : 원격 디렉터리 목록 조회 후 대상 파일 존재 확인
        ├─ fileDownProcess()  : 다운로드 → RECVREAL 수신 → RECVARCH 이동 → check 파일 생성
        └─ callbackProcess()  : callback.url 이 있으면 BAT에 완료 통보 후 응답코드 검증

비동기 구조입니다. HTTP 요청은 큐에 넣고 바로 QUEUED를 반환하며, 실제 전송은 최대 30초 뒤 스케줄러가 시작합니다. 요청자는 action=STATUSjobId를 조회해 진행 상태를 확인합니다. 요청/응답 JSON 규격은 PPT 가이드 4장을 참조하십시오.


4. 프로퍼티 목록 (TelegramInfo{업무구분_대외기관})

FTPDJ가 실제로 읽는 프로퍼티는 아래 8개가 전부입니다.

기본값 용도 비고
remote.path "" 원격지 기준 경로 #YYYYMMDD# 치환 지원. 요청이 절대경로면 무시됨 (5장)
protocol ""sftp_passwd 전송 프로토콜 값 매핑 주의 (6장)
proxy.server "" SFTP 프록시 서버 IP:PORT 형식. 비우면 직접 접속
connection.timeout 30 접속 타임아웃(초) 숫자만. 문자 포함 시 NumberFormatException
read.timeout 30 읽기 타임아웃(초) 동일
io.bandwidth 0 전송 대역 제한 0=무제한. K/M 표기 지원 (7장)
ftp.transfer.type BINARY FTP 전송 모드 ASCII가 필요한 경우에만 지정
callback.url "" 수신 완료 통보 URL 비우면 콜백 생략 (10장)

접속 정보(IP·포트·사용자 ID·비밀번호/SSH Key)와 수신 대상 파일명은 프로퍼티가 아니라 대외기관 연결 정보 및 HTTP 요청에서 배치 헤더로 전달됩니다. 프로퍼티에서 찾지 마십시오.


5. remote.path 조립 규칙

원격 경로는 프로퍼티 단독으로 결정되지 않고 요청으로 들어온 remoteFile과 조합됩니다.

remotePath  = 프로퍼티 remote.path
requestPath = remoteFile  디렉터리 부분
remoteFile  = remoteFile  파일명 부분

remotePath = Paths.get(remotePath).resolve(requestPath).normalize() + "/"

5.1 요청이 절대경로면 remote.path는 무시된다

Java Path.resolve()는 인자가 절대경로일 때 기준 경로를 버리고 인자를 그대로 반환합니다. PPT 가이드의 "remote.path : 요청시 full path 이면 무시됨" 이 이 동작입니다.

remote.path 요청 remoteFile 최종 원격 경로 파일명
/data/recv test.txt /data/recv/ test.txt
/data/recv sub/test.txt /data/recv/sub/ test.txt
/data/recv /home/soxor/sftp/svr/test.txt /home/soxor/sftp/svr/remote.path 무시 test.txt
"" (미등록) /home/soxor/sftp/svr/test.txt /home/soxor/sftp/svr/ test.txt

요청자가 절대경로를 보내는 방식이면 remote.path는 등록하지 않아도 됩니다. 반대로 remote.path로 접근 경로를 통제하려는 의도라면 이 구조로는 불가능합니다 — 요청자가 절대경로를 보내면 어디든 접근됩니다. 경로 제한이 요건이라면 별도 검증 로직을 추가해야 합니다.

5.2 날짜 토큰 치환

조합이 끝난 경로에서 #YYYYMMDD# 패턴을 찾아 실제 일자로 치환합니다.

정규식: #YYYYMMDD([+-]\d+)?#
표기 의미 예 (오늘이 2026-09-09)
#YYYYMMDD# 당일 20260909
#YYYYMMDD-1# 전일 20260908
#YYYYMMDD-7# 7일 전 20260902
#YYYYMMDD+1# 익일 20260910

등록 예:

remote.path = /data/recv/#YYYYMMDD#      →  /data/recv/20260909/
remote.path = /data/recv/#YYYYMMDD-1#    →  /data/recv/20260908/

동작상 유의점:

  1. 치환이 발생하면 계산된 일자가 배치 헤더의 기준일자(BaseDate)로 함께 설정됩니다. 로그·모니터링의 기준일자가 이 값을 따릅니다.
  2. 치환 중 예외가 나도 작업은 중단되지 않고 어댑터 로그에만 기록됩니다. 이 경우 경로에 #YYYYMMDD# 문자열이 그대로 남아 원격 경로 조회 단계에서 실패합니다.
  3. 경로 끝의 /는 없으면 자동으로 붙습니다.

6. protocol 값 매핑 (주의)

가장 오해하기 쉬운 항목입니다. 매칭은 열거형 이름 기준이고, 매칭에 실패해도 오류 없이 조용히 기본값으로 처리됩니다.

등록값(대소문자 무관) 해석 내부 코드 전송 방식
ftp FTP 0 평문 FTP
sftp_passwd SFTP + 비밀번호 1 SFTP
sftp_key SFTP + SSH Key 2 SFTP
미등록 / 공백 / 그 외 모든 값 SFTP + 비밀번호 1 SFTP

즉 아래가 모두 동일하게 sftp_passwd로 동작합니다.

protocol =                 (미등록)
protocol = sftp            ← 가이드 표기지만 매핑 대상이 아님
protocol = SFTP
protocol = sftp_password   ← 오타

지켜야 할 규칙은 두 가지입니다.

  • FTP를 쓰려면 반드시 ftp로 정확히 등록해야 합니다. 오타 시 SFTP로 접속을 시도해 원인 파악이 어려운 실패가 납니다.
  • SSH Key 인증을 쓰려면 반드시 sftp_key 등록해야 합니다. 비워두거나 sftp로 두면 비밀번호 인증으로 동작합니다.

오타가 로그에 남지 않으므로 접속 실패 시 프로퍼티 오타를 우선 의심하십시오. 개선 후보: 매칭 실패 시 경고 로그를 남기거나 예외를 던지도록 Protocol.get()을 수정하는 것이 안전합니다.


7. io.bandwidth 단위 표기

문자열을 그대로 파싱하지 않고 치환 후 정수 변환합니다.

.replace("K", "000").replace("M", "000000").replace(",", "")
등록값 실제 적용값 설명
0 또는 미등록 0 제한 없음
500K 500000
10M 10000000
1,024 1024 콤마 제거
500k 예외 발생 소문자 k는 치환되지 않음 → NumberFormatException
500KB 예외 발생 B가 남음

대문자 K / M만 사용하십시오. 표기가 잘못되면 FtpEnv.init() 단계에서 예외가 나 다운로드가 시작되지 않습니다.


8. 파일 저장 위치 프로퍼티 (ResponseRcvDirInfo)

수신 파일이 놓이는 위치는 대외기관 프로퍼티가 아니라 공통 그룹 ResponseRcvDirInfo 에서 결정됩니다. (EMS 메뉴: 환경정보 → 공통코드관리 → 프라퍼티 관리)

용도 FTPDJ에서의 역할
rcv.real.directory 수신 중 임시 디렉터리 다운로드가 먼저 떨어지는 곳
rcv.arch.directory 수신 완료 보관 디렉터리 완료 후 이동되는 최종 위치. check 파일도 여기
rcv.error.directory 오류 파일 디렉터리 실패 시 받다 만 파일을 이동
rcv.root.directory 빈 완료표시 파일 위치 0바이트 파일 생성
rcv.root.ext check 파일 확장자 미등록 시 .chk
retension.day.* 폴더별 보존일수 값(일) 경과 시 자동 삭제. 공백/비정수면 삭제 안 함

8.1 경로 조립과 파일 생성 순서

작업 등록 시 수신 디렉터리는 rcv.real.directory/업무구분코드/대외기관코드 로 설정됩니다.

① 다운로드      {rcv.real.directory}/{업무구분}/{대외기관}/{파일명}
② 이동          {rcv.arch.directory}/{업무구분}/{대외기관}/{파일명}
③ check 파일    {rcv.arch.directory}/{업무구분}/{대외기관}/{파일명}{rcv.root.ext}
④ 빈 표시 파일  {rcv.root.directory}/{업무구분}/{대외기관}/{파일명}
  • ①→② 이동은 REPLACE_EXISTING이므로 같은 이름의 기존 파일은 덮어씁니다.
  • ③, ④는 생성 전에 기존 파일이 있으면 먼저 삭제하고 새로 만듭니다.
  • 다운로드 중 실패하면 ①의 파일을 rcv.error.directory로 옮기고, 이동조차 실패하면 조용히 무시합니다.

순서를 바꾸지 마십시오. check 파일(③)은 반드시 본 파일 이동(②) 이후에 생성됩니다. 수신측(BAT)이 check 파일을 폴링하는 구조이므로, check 파일이 보이는 시점에는 본 파일이 이미 최종 위치에 있어야 합니다. 순서를 뒤집으면 BAT가 미완성 파일을 집어갈 수 있습니다.


9. check 파일 포맷

rcv.arch.directory 아래 {파일명}.chk로 생성되며, 기존 EAI(BAT)가 폴링으로 읽어갑니다.

%-30s %8s %010d %c %-100s @@
위치(1-based) 길이 내용
1 30 30 InterfaceID (거래구분코드 bizCode), 좌측정렬 공백 패딩
31 38 8 기준일자 YYYYMMDD (생성 시점의 현재일자)
39 48 10 파일 건수, 0 패딩 (항상 0000000001)
49 1 상태값 — 생성 시 '0' 고정
50 149 100 파일명, 좌측정렬 공백 패딩
150 151 2 종료 표시 @@

151바이트입니다. 49번째 바이트의 상태값은 콜백 응답 검증에서 다시 쓰입니다(10장). 상수 BAT_RET_CODE_IDX = 49가 이 위치를 가리킵니다.

BAT와의 인터페이스 규격입니다. 포맷을 바꾸면 수신측 파싱이 깨집니다. 변경 시 반드시 BAT 담당자와 합의하십시오.


10. callback.url 동작

callback.url비어 있으면 콜백 단계 자체를 건너뜁니다. 등록되어 있으면 다음을 호출합니다.

GET {callback.url}?eaibatMsg={check 파일과 동일한 151바이트 문자열}

응답 검증 조건은 세 가지이며, 하나라도 어긋나면 작업 전체가 실패 처리됩니다.

검증 실패 시 메시지
HTTP 상태코드 200 Response code is not 200.(코드)
응답 본문 길이 ≥ 49바이트 BAT return message is too short.
응답 본문 49번째 바이트 = '1' BAT return code is not '1'.(값)

응답 인코딩은 Content-Type 헤더의 charset을 따르고, 없으면 UTF-8로 처리합니다.

알아둘 것: 파일 수신과 저장이 모두 성공해도 콜백 검증에서 실패하면 작업은 오류로 기록됩니다.


11. 의존 라이브러리 취약점 조치 내역

취약점 번호 해당 jar 파일 조치 내용
CVE-2023-46604
CVE-2026-34197
activemq-broker-5.15.13.jar
activemq-client-5.15.13.jar
activemq-console-5.15.13.jar
activemq-spring-5.15.13.jar
5.15.13 → 5.19.11 교체.
이 CVE의 최소 수정 버전은 5.15.16이며,
현재 활성 라인 최신인 5.19.11을 적용했습니다.
CVE-2023-48795 jsch-0.1.55.jar com.jcraft:jsch 0.1.55
→ com.github.mwiede:jsch 2.28.7
교체.
Terrapin은 0.2.15에서
strict KEX 지원으로 수정되었습니다.
CVE-2023-20863 spring-core-5.3.27.jar 등
spring 계열 14건
현재 버전 5.3.27이 바로 이 CVE의 수정 버전입니다.
영향 범위는 5.3.05.3.26 및 6.0.06.0.7이고,
수정 릴리스가 5.3.27 / 6.0.8입니다.
** 조치 불필요. 스캔 도구가 영향 범위 상한을
잘못 잡은 것으로 보이며, 판정 근거 재확인을 요청드립니다
CVE-2026-22740 spring-core-5.3.27.jar 등
spring 계열 14건
이 결함은 WebFlux 서버 애플리케이션 전용입니다
(멀티파트 임시파일 미삭제).
본 애플리케이션은 spring-webmvc 기반이며
spring-webflux jar 자체가 배포물에 없습니다.