시스템 간 연동 (EAI) · 동기 REST 연동 · 이론
동기 연동에서 실제로 신경 쓸 것들
한 줄 요약
동기 REST 연동의 어려움은 호출이 아니라 기다림에 있다 — 타임아웃을 안 걸면 상대 시스템의 장애가 그대로 우리 시스템의 장애가 된다.
왜 동기 연동의 진짜 위험이 '기다림'인가
REST 로 상대 시스템을 호출하는 것은 쉽다. 어려운 것은 상대가 느릴 때다.
우리 화면이 상대 API 를 동기로 부르는데 상대가 30초를 끈다고 하자.
- 우리 WAS 스레드가 30초 동안 묶인다
- 사용자는 화면 앞에서 기다린다. 대개 새로고침을 누른다
- 새로고침은 요청을 하나 더 만든다. 앞의 요청은 살아 있다
- 스레드가 빠르게 소진된다. 상대 시스템 장애가 우리 시스템 장애가 된다
그래서 동기 연동에는 반드시 타임아웃이 있어야 한다. 두 종류다.
연결 타임아웃(connect) : 상대와 TCP 연결이 맺어질 때까지 → 짧게 (1~3초)응답 타임아웃(read) : 응답을 다 받을 때까지 → 업무에 따라 (5~30초)연결 타임아웃을 짧게 잡는 것이 중요하다. 상대 서버가 죽어 있으면
연결은 즉시 실패해야 한다. 이걸 30초로 잡아 두면 상대가 죽은 동안
우리 스레드가 30초씩 묶인다.
그리고 타임아웃만으로는 부족하다. 상대가 계속 느리면 우리도 계속 묶인다.
그때 필요한 것이 서킷 브레이커다. 실패율이 임계치를 넘으면
일정 시간 동안 아예 호출하지 않고 즉시 실패시킨다.
"빨리 실패하는 것"이 "느리게 성공하기를 기다리는 것"보다 나은 상황이 있다.
타임아웃은 '응답이 없다'는 뜻이 아니다
여기가 가장 중요한 지점이다.
> 타임아웃이 났을 때, 상대가 요청을 못 받은 것인지
> 처리는 했는데 응답만 못 온 것인지 우리는 구별할 수 없다.
주문 전송에서 타임아웃이 났다. 재전송할 것인가?
- 상대가 못 받았다면 재전송해야 한다
- 상대가 처리했다면 재전송은 중복 주문이다
이 문제는 재시도로 풀 수 없다. 멱등성으로 푼다.
송신측이 매 요청에 고유한 키(전문번호, Idempotency-Key)를 붙이고,
수신측이 같은 키를 두 번 받으면 처리하지 않고 최초 응답을 그대로 돌려준다.
그러면 재전송이 안전해진다. 이건 뒤 모듈에서 실습으로 다룬다.
요청 전 검증 — 나가기 전에 막는다
상대에게 보내기 전에 우리가 먼저 검증해야 할 것들이 있다.
- 필수값: 정의서의 필수 항목이 다 있는가
- 길이: 바이트 기준으로 초과하지 않는가
- 타입/형식: 날짜 형식, 숫자 필드에 문자가 없는가
- 코드값: 정의서에 명시된 코드 목록 안에 있는가
이걸 안 하면 상대 시스템의 오류 응답으로 알게 된다. 그러면
- 왕복 시간이 낭비되고
- 상대 시스템 로그에 우리 오류가 쌓이고 (연동 담당자 간 감정 소모)
- 대량 배치라면 수천 건의 오류가 한꺼번에 터진다
"나쁜 데이터는 우리 쪽에서 막는다" 가 연동 개발의 기본 예의다.
연동 로그는 별도로 남긴다
애플리케이션 로그에 섞어 두면 나중에 못 찾는다. 연동 전용 로그를 만들고
한 줄에 한 호출을 남긴다. 필드는 최소한 이 정도.
2026-08-19T14:03:22.145+0900|IF-ORD-001|ORDER-SYS|PARTNER-API|0000|182|a1b2c3d4 시각 인터페이스ID 송신 수신 응답코드 소요ms 추적ID- 추적 ID(trace_id) 가 특히 중요하다. 상대 시스템 로그와 대조할 때
- 소요 시간을 남기면 "요즘 느려졌다"는 말에 근거를 댈 수 있다.
- 응답 코드를 남기면 오류 분포를 집계할 수 있다.
이것 하나로 찾는다. 요청 헤더로 함께 보내고, 상대에게도 남겨 달라고
정의서에 적어 둔다.
그리고 개인정보를 로그에 남기지 않는다. 주민번호, 계좌번호, 카드번호는
마스킹하거나 아예 안 남긴다. 연동 로그는 오래 보관되므로 위험이 크다.
대량 처리는 동기로 하지 않는다
"주문 10만 건을 상대에 전송" 같은 요구사항에 동기 REST 를 쓰면 안 된다.
한 건에 100ms 만 걸려도 10만 건이면 약 2.8시간이다. 그 사이에
한 번이라도 끊기면 어디까지 갔는지 모른다.
대안은 셋이다.
1. 파일 배치 — 한 번에 묶어 보내고 결과를 파일로 받는다
2. 큐 — 비동기로 던지고 결과는 별도 통지
3. 벌크 API — 한 요청에 N 건(보통 100~1000)을 담고, 건별 결과를 받는다
3번을 쓸 때 반드시 정의해야 할 것: 부분 실패를 어떻게 처리할 것인가.
100건 중 3건이 실패하면 전체 롤백인가, 97건은 처리하고 3건만 돌려주는가.
정의서에 없으면 양쪽이 다르게 구현한다. 그리고 이 차이는
"건수가 안 맞는다"는 형태로 며칠 뒤에 발견된다.
현장에서 만나는 모습
이 실패는 늘 같은 모양으로 온다. 상대 API 가 느려지고, 우리 화면이 멈추고, 사용자가 새로고침을 누르고, 그 새로고침이 요청을 하나 더 만든다. 앞의 요청은 아직 살아 있으므로 스레드는 두 배로 묶인다. 몇 분 뒤 우리 WAS 의 스레드가 소진되고, 상대와 아무 상관 없는 화면까지 전부 멈춘다.
이때 모니터링에는 우리 시스템의 CPU 도 메모리도 정상으로 보인다. 스레드가 일을 하는 게 아니라 기다리고 있을 뿐이기 때문이다. 그래서 원인을 상대가 아니라 우리 쪽에서 찾다가 시간을 버린다.
연동 로그를 따로 남겨야 하는 이유가 이것이다. 애플리케이션 로그에 섞어 두면 "상대가 언제부터 느려졌는가" 를 뽑는 데 한참 걸린다. 인터페이스 ID·응답 코드·소요 시간을 고정 형식으로 한 줄씩 남겨 두면 그 답이 1 분이면 나온다.