infra
Platform

모듈 맵

[Infra Ops] 써드파티 API와 공공 인프라 연계 실무

0 / 52 완료

펼치기
0 / 52 완료0%

인프라 운영 & SRE · 26 / 52

[Infra Ops] 써드파티 API와 공공 인프라 연계 실무

NICE 본인인증, 전자서명, SMS/메일 게이트웨이, 외부 기관 API 연계까지

🚨INCIDENT ALERT
HIGH

운영 서버에서 NICE 본인인증 연동 테스트를 하는데 "연결 거부"가 납니다. 개발 서버에서는 됐는데 운영에서만 안 됩니다. 방화벽 담당자에게 물었더니 "Outbound 오픈 요청서 작성해서 주세요"라고 합니다. 무엇을 적어야 할지, 오픈 후에도 안 된다면 어떻게 점검해야 할지 막막합니다. 또 전자서명 연동에는 JKS 파일이 필요하다고 하는데 이게 뭔지도 모르겠습니다.

이 모듈에서는 외부 기관 API 연계의 전형적인 유형과 사전 점검 순서, JKS/TrustStore 개념, SMS/PG/인증 API 연계 운영 노하우를 다룹니다.

이번 챕터에서 배울 것
  • 1외부 API 연계 전 방화벽 오픈 요청에 필요한 정보를 정확히 파악할 수 있다
  • 2nc, openssl s_client, curl을 순서대로 사용해 외부 연결 문제를 단계별로 진단할 수 있다
  • 3JKS(KeyStore)와 TrustStore의 역할 차이를 설명하고 오류를 해결할 수 있다
  • 4SMS, PG, 본인인증 API 연계 시 반복되는 운영 이슈 패턴을 설명할 수 있다
  • 5Circuit Breaker 개념과 외부 API timeout 설정이 서버 안정성에 미치는 영향을 설명할 수 있다

외부 연계 유형과 사전 준비

💡개념

외부 기관 API 연계의 대표 유형

외부 API 연계는 "우리 코드를 짜는 것"이 아니라 "우리 서버가 외부 시스템과 신뢰를 맺는 것"입니다. 방화벽 오픈, 인증서 교환, IP 화이트리스트 등록처럼 인프라 레이어에서 먼저 해결해야 할 일들이 코드 작성보다 앞서 있습니다. 유형별로 요구사항이 다르기 때문에 어떤 종류의 연계인지 먼저 파악하지 않으면 준비해야 할 것을 빠뜨리게 됩니다.

외부 기관 API 연계 대표 유형 — REST/SOAP 동기 호출, Webhook 콜백, 파일 연계(SFTP), 메시지 큐 등. 유형마다 방화벽 오픈·인증서 교환·IP 화이트리스트 등록 같은 인프라 레이어 준비가 코드 작성보다 선행. "외부 시스템과 신뢰를 맺는" 작업이라 연계 유형별 요구사항을 먼저 파악확대

처음 공공기관 프로젝트에 투입됐을 때 "NICE 연동이 막혔습니다", "SignKorea JKS 파일이 필요하대요", "PG사에서 IP 화이트리스트 등록해달라고 합니다" 같은 요청이 동시에 들어옵니다. 각각 무엇을 해야 하는 작업인지, 사전에 무엇을 준비해야 하는지 모르면 연계 하나하나에서 막힙니다. 유형별로 요구사항이 다르기 때문에 먼저 어떤 종류의 연계인지 파악하는 것이 시작입니다.

공공기관이나 금융 프로젝트에서 자주 만나는 외부 연계 유형입니다. 각각 연계 방식과 준비 사항이 다릅니다. 처음 접하면 "왜 이렇게 복잡한가" 싶지만, 대부분은 보안 요구사항(신원 증명, 암호화, 감사 로그)에서 비롯됩니다.

주요 외부 연계 유형:

유형대표 서비스연계 방식특이사항
본인인증NICE, KCB, KMCHTTP API + 암복호화암호화 키 관리, IP 등록 필요
전자서명SignKorea, CrossCertHTTPS + mTLS (클라이언트 인증서)JKS 인증서 관리
PG(결제)KG이니시스, NHN KCPHTTPS API, WebhookIP 화이트리스트, 테스트/운영 키 분리
SMS/알림톡AWS SNS, 나이스정보통신, 카카오HTTPS REST APIAPI 키, 발신번호 사전 등록
공공 Open API행안부, 건강보험공단 등HTTPS + API Key서비스 신청 후 심사 과정 필요
파일 전송SFTP, AS2, VANTCP 22/4080별도 방화벽 오픈, 인증서 교환

연계 전 체크리스트:

로컬 터미널
# 외부 기관에서 확인해야 할 정보
# - 운영 서버 IP/도메인 및 포트
# - 프로토콜 (HTTP/HTTPS, TCP)
# - 인증 방식 (API Key, 클라이언트 인증서, IP 화이트리스트)
# - 테스트 환경 계정/엔드포인트 별도 제공 여부

# 방화벽 오픈 요청 시 필요한 정보
# - 출발지: 우리 운영 서버 IP (WAS 서버들)
# - 목적지: 외부 API 서버 IP 또는 도메인
# - 목적지 포트: 443 (HTTPS), 80 (HTTP), 22 (SFTP) 등
# - 방향: Outbound (내부 → 외부)
# - 프로토콜: TCP

외부 API 연결 사전 점검 순서

💡개념

nc → openssl → curl — 단계별 진단

외부 API 연결이 안 될 때 무작정 앱 로그만 보면 원인을 찾기 어렵습니다. 네트워크 계층부터 단계적으로 점검하면 어느 지점에서 막히는지 빠르게 특정할 수 있습니다.

nc → openssl → curl — 단계별 진단확대

3단계 점검 순서:

로컬 터미널
# -- 1단계: TCP 연결 가능 여부 (nc) --
# 방화벽이 열렸는지, 서버가 포트를 열고 있는지 확인
nc -zv external-api.example.com 443
# 성공: Connection to external-api.example.com 443 port [tcp/https] succeeded!
# 실패: nc: connect to external-api.example.com port 443 (tcp) failed: Connection refused
#      → 방화벽 미오픈 또는 서버가 포트를 닫은 상태

# 1단계 실패 시 → 방화벽 오픈 요청 또는 외부 기관에 서버 상태 확인
# 1단계 성공 시 → 2단계 진행

# -- 2단계: SSL/TLS 인증서 검증 (openssl s_client) --
# SSL 핸드셰이크가 정상인지, 인증서 체인이 완전한지 확인
openssl s_client -connect external-api.example.com:443 2>/dev/null \
  | grep -E "Verify return code|issuer|subject|expire"
# 정상: Verify return code: 0 (ok)
# 실패 예: Verify return code: 21 (unable to verify the first certificate)
#         → 중간 CA 인증서 누락, truststore 업데이트 필요

# 서버 인증서 만료일 확인
openssl s_client -connect external-api.example.com:443 2>/dev/null \
  | openssl x509 -noout -dates
# notAfter=Nov 30 23:59:59 2026 GMT

# 2단계 실패 시 → SSL 인증서 문제 (아래 TroubleCase 참고)
# 2단계 성공 시 → 3단계 진행

# -- 3단계: HTTP 응답 확인 (curl) --
# 실제 API 응답 코드와 내용 확인
curl -m 10 -s -o /dev/null -w "%{http_code}" https://external-api.example.com/health
# 200: 정상
# 403: IP 화이트리스트 미등록 또는 API Key 오류
# 401: 인증 실패
# 000: 연결 자체 실패 (1단계로 되돌아가기)

# 응답 본문까지 확인
curl -m 10 -s https://external-api.example.com/health
# {"status":"ok"} 또는 에러 메시지

# 응답 시간 측정 (timeout 설정 기준)
curl -m 30 -w "\nDNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTotal: %{time_total}s\n" \
  -s -o /dev/null https://external-api.example.com/health

JKS와 TrustStore — Java SSL 인증서 관리

💡개념

KeyStore vs TrustStore — 헷갈리는 개념 정리

전자서명 API 연동 중 javax.net.ssl.SSLHandshakeException: PKIX path building failed 오류가 납니다. "TrustStore에 CA를 추가해야 한다"는 말을 들었는데, 이미 JKS 파일을 설정했습니다. KeyStore와 TrustStore가 다른 것인지 처음 접하면 혼란스럽습니다. 두 개념은 방향이 달라서, 어느 쪽이 문제인지 구분하지 못하면 엉뚱한 곳을 고치게 됩니다.

Java에서 SSL 인증서 관련 오류가 나면 KeyStore와 TrustStore 개념 혼동이 원인인 경우가 많습니다. 두 개념은 방향이 다릅니다. KeyStore는 "나의 인증서"(클라이언트가 서버에 제시), TrustStore는 "신뢰하는 CA 목록"(서버 인증서를 검증하기 위한)입니다.

KeyStore vs TrustStore — 방향이 다르다. KeyStore(JKS)는 나의 인증서와 개인키를 보관해 mTLS에서 서버가 클라이언트 인증서를 요구할 때 "이게 나야"라고 제시하는 용도이며 -Djavax.net.ssl.keyStore로 지정하고 기본값이 없다. TrustStore는 신뢰할 CA 인증서 목록으로 자체 서명·사설 CA 서버를 검증하며 -Djavax.net.ssl.trustStore로 지정하고 기본값은 JDK cacerts(공인 CA 포함)다. TLS 핸드셰이크에서 내 앱은 KeyStore로 내 인증서를 제시하고 TrustStore로 서버 인증서를 CA 검증한다. PKIX path building failed 오류는 TrustStore에 상대 CA가 없다는 뜻이지 내 인증서(KeyStore) 문제가 아니므로 keytool -importcert로 CA를 TrustStore에 추가한다. Java는 OS 신뢰 저장소가 아니라 자체 cacerts를 보므로 "브라우저는 되는데 앱만 안 되는" 상황이 자주 생긴다확대

KeyStore vs TrustStore:

항목KeyStore (JKS)TrustStore
역할나의 인증서와 개인키 보관신뢰할 CA 인증서 목록
언제 필요mTLS (클라이언트 인증서 요구)자체 서명 인증서, 사설 CA
Java 옵션-Djavax.net.ssl.keyStore-Djavax.net.ssl.trustStore
기본값없음JDK의 cacerts (공인 CA 기본 포함)

JKS 관련 자주 쓰는 명령어:

로컬 터미널
# JKS 파일 내용 확인 (어떤 인증서가 들어있는지)
keytool -list -keystore client-cert.jks -storepass 비밀번호
# 출력: Alias name, Entry type, Certificate fingerprint

# PEM 인증서를 JKS로 변환 (외부 기관에서 PEM 발급 시)
# 1단계: PEM + 개인키 → PKCS12 변환
openssl pkcs12 -export \
  -in client.crt \
  -inkey client.key \
  -out client.p12 \
  -name myalias \
  -passout pass:임시비밀번호

# 2단계: PKCS12 → JKS 변환
keytool -importkeystore \
  -srckeystore client.p12 \
  -srcstoretype PKCS12 \
  -srcstorepass 임시비밀번호 \
  -destkeystore client.jks \
  -deststoretype JKS \
  -deststorepass jks비밀번호

# TrustStore에 외부 CA 인증서 추가 (사설 CA 또는 중간 CA)
keytool -importcert \
  -keystore truststore.jks \
  -storepass trustpass \
  -alias external-ca \
  -file external-ca.crt \
  -noprompt

# Java 앱 실행 시 JKS 사용 설정
java \
  -Djavax.net.ssl.keyStore=/etc/ssl/client.jks \
  -Djavax.net.ssl.keyStorePassword=비밀번호 \
  -Djavax.net.ssl.trustStore=/etc/ssl/truststore.jks \
  -Djavax.net.ssl.trustStorePassword=trustpass \
  -jar app.jar

외부 API 호출의 생애

💡개념

외부 API를 한 번 호출할 때 벌어지는 일 — 요청 준비부터 장애 격리까지 5단계

외부 API 호출은 코드에서 httpClient.post(url, body) 한 줄이지만, 그 안에서 인증 토큰·헤더 준비 → 호출 → 응답 처리(상태코드·페이징) → 재시도·타임아웃·레이트리밋 대응 → 장애 격리가 순서대로 일어납니다. 이 단계를 알면 401·429·타임아웃 같은 증상을 보고 어느 단계 문제인지 바로 짚을 수 있습니다. 앞 절의 ncopensslcurl이 "연결이 되나"를 계층으로 봤다면, 여기서는 "호출 한 번이 어떻게 흘러가다 어디서 깨지나"를 봅니다.

TEXT
[우리 서버]  callExternalApi(payload)
   │
   ① 요청 준비           (URL·메서드·헤더 / 인증 토큰·API 키·서명 부착)
   │
   ② 호출               (connectTimeout 내 TCP·TLS 수립 → 요청 전송)
   │
   ③ 응답 처리           (상태코드 분기 2xx/4xx/5xx, 페이징·부분 결과 조립)
   │    → readTimeout 내 본문 수신
   │
   ④ 재시도·레이트리밋 대응  (5xx·타임아웃만 backoff 재시도, 429는 Retry-After 존중)
   │
   ⑤ 장애 격리           (연속 실패율 임계 초과 → Circuit Breaker OPEN → 즉시 fallback)
   ▼
[우리 서버]  결과 반환 또는 fallback (외부 장애가 우리로 전파되지 않음)

각 단계에서 무슨 일이 일어나고, 막히면 어떤 증상인가:

단계하는 일여기서 막히면
① 요청 준비URL·헤더에 인증 토큰·API 키·서명을 붙임토큰 만료·키 오타 → 호출 시 401 Unauthorized / 서명·권한 부족 → 403
② 호출connectTimeout 안에 연결하고 요청 전송포트·방화벽 미오픈 → Connection refused·연결 타임아웃(Outbound) / IP 화이트리스트 미등록 → 403
③ 응답 처리상태코드로 분기하고 페이징·부분 결과를 조립레이트리밋 → 429 Too Many Requests / readTimeout 초과 → Read timed out / 페이징 누락 → 일부만 처리(부분 실패)
④ 재시도일시 실패(5xx·타임아웃)만 backoff로 재시도4xx까지 재시도 → 같은 실패 반복·자원 낭비 / 재시도 폭주 → 상대 서버에 부하 가중
⑤ 장애 격리연속 실패율이 임계를 넘으면 회로를 열어 호출 차단격리 없으면 외부 지연이 우리 스레드를 잠식 → 의존성 장애가 서비스 전체로 전파

즉 외부 API 오류를 볼 때 상태코드가 곧 단계 지도입니다 — 401·403은 ①(인증·권한·IP), Connection refused·타임아웃은 ②(네트워크·방화벽), 429는 ③(레이트리밋), Read timed out은 ③(응답 지연), 재시도해도 계속 실패면 ④⑤(의존성 장애)입니다. 4xx는 재시도해도 소용없으니 즉시 실패로, 5xx·타임아웃만 backoff 재시도로 갈라야 하고, 외부가 오래 죽어 있으면 ⑤ Circuit Breaker로 우리 자원을 지킵니다. 아래에서 ④⑤의 timeout과 Circuit Breaker를 자세히 봅니다.

Circuit Breaker — 외부 API 장애 격리

💡개념

timeout 설정과 Circuit Breaker의 필요성

외부 API가 갑자기 느려지거나 응답을 하지 않을 때, 우리 서버가 함께 장애로 빠지는 것을 방지해야 합니다. timeout만으로는 부족하고, Circuit Breaker 패턴이 필요합니다.

timeout 설정의 중요성:

로컬 또는 서버
# 잘못된 예: timeout 없음 → 스레드 무기한 대기
curl https://external-api.example.com/payment

# 올바른 예: 적절한 timeout 설정
curl -m 10 https://external-api.example.com/payment
# 10초 내 응답 없으면 포기

# Java HttpClient timeout 설정 예시
# connection-timeout: 3초 (TCP 연결 수립)
# read-timeout: 10초 (응답 본문 수신)

Circuit Breaker 동작 원리:

Circuit Breaker 상태 전이 — CLOSED(정상)→실패율 임계 초과→OPEN(즉시 실패, 외부 호출 차단)→30초 후 HALF-OPEN(일부만 통과)→성공 시 CLOSED·실패 시 OPEN확대

Resilience4j (Spring Boot) 설정 예시:

YAML
# application.yml
resilience4j:
  circuitbreaker:
    instances:
      smsService:
        failure-rate-threshold: 50          # 50% 실패 시 OPEN
        wait-duration-in-open-state: 30s    # 30초 후 HALF-OPEN 시도
        sliding-window-size: 10             # 최근 10개 요청 기준
        permitted-number-of-calls-in-half-open-state: 3

실습 — 외부 API 연결 사전 점검

1외부 API 3단계 연결 점검

TCP 연결부터 단계적으로 점검합니다. 실제 외부 API 서버 주소로 교체하여 사용합니다.

로컬 터미널
# 1단계: TCP 포트 연결 확인
nc -zv external-api.example.com 443

# 2단계: SSL 인증서 검증
openssl s_client -connect external-api.example.com:443 2>/dev/null \
  | grep "Verify return code"

# 3단계: HTTP 응답 확인 (응답 코드 + 시간)
curl -m 10 -s -o /dev/null -w "%{http_code} (%{time_total}s)\n" \
  https://external-api.example.com/health
nc -zv external-api.example.com 443
🔍실행 후 확인할 것
  • nc -zv external-api.example.com 443 결과부터 확인 — "succeeded!"이면 TCP 연결 가능. "Connection refused"는 서비스 미기동, "timed out"은 방화벽 차단(우리 서버 Outbound 미오픈 가능성)
  • openssl s_client 결과에서 "Verify return code: 0 (ok)"이면 TLS 정상 — 번호가 18(self-signed), 19(chain 불완전), 20(인증서 없음) 등이면 JKS TrustStore에 해당 CA 인증서 추가 필요
  • nc 성공하고 openssl도 정상인데 curl 응답이 401이면 인증 헤더 오류, 403이면 IP 허용 목록 미등록, 타임아웃이면 timeout 설정(보통 5~30초)이 외부 API 응답 시간보다 짧은 것
2JKS 인증서 내용 확인

mTLS 연계 시 사용하는 JKS 파일의 인증서 정보를 확인합니다.

로컬 터미널
# JKS 파일 목록 및 만료일 확인
keytool -list -v -keystore /etc/ssl/client.jks -storepass 비밀번호 \
  | grep -E "Alias|Valid|until"

# TrustStore에 등록된 CA 목록 확인
keytool -list -keystore /etc/ssl/truststore.jks -storepass trustpass \
  | grep -E "Alias|trustedCertEntry"

# 인증서 만료 여부 빠른 확인
keytool -list -v -keystore /etc/ssl/client.jks -storepass 비밀번호 \
  | grep "until" | while read line; do
    echo "$line"
  done
keytool -list -keystore /path/to/client.jks -storepass 비밀번호
🔍실행 후 확인할 것
  • keytool -list 출력에서 alias 이름을 먼저 확인 — 연계 기관이 요구하는 alias명과 대소문자까지 정확히 일치해야 함. 다르면 mTLS 핸드셰이크 시 인증서를 찾지 못해 연결 실패
  • "Valid from" ~ "until" 날짜에서 만료일 확인 — 현재 날짜 기준으로 30일 이내면 교체 일정 수립 필요. 만료된 인증서는 SSL handshake failure 발생
  • privateKeyEntry가 있어야 클라이언트 인증서 포함 — trustedCertEntry만 있으면 서버 인증서 검증용(TrustStore)이지 클라이언트 인증서(KeyStore)가 아님. mTLS에서는 두 용도의 JKS를 별도로 관리

트러블슈팅

원인: 우리 서버의 Outbound 방화벽이 막혀 있습니다. 개발 서버는 방화벽이 느슨하고 운영 서버는 엄격한 경우가 많아 개발에서는 됐는데 운영에서는 안 되는 전형적인 패턴입니다.

로컬 터미널
# 우리 서버에서 외부 API 서버로 직접 연결 테스트
nc -zv external-api.example.com 443
# 실패: nc: connect to external-api.example.com port 443 failed: Connection timed out
# → Outbound 방화벽 미오픈 확인 필요

# 방화벽 오픈 요청 정보 수집
dig +short external-api.example.com    # 도메인 → IP 조회
# 여러 IP가 나오면 모두 화이트리스트 등록 요청

# 라우팅 경로 확인
traceroute external-api.example.com
# 어느 홉에서 막히는지 확인

# 방화벽 오픈 후 재테스트
nc -zv external-api.example.com 443
# 성공하면 다음 단계(SSL, HTTP) 점검

해결: 보안팀 또는 방화벽 담당자에게 Outbound 오픈 요청서를 제출합니다. 요청서에 포함해야 할 내용: 출발지 서버 IP, 목적지 IP(외부 API IP), 목적지 포트(443), 프로토콜(TCP), 방향(Outbound), 오픈 사유(서비스명 및 연계 목적).

원인: 외부 API 서버의 인증서 체인에서 중간 CA(Intermediate CA) 인증서가 변경됐는데 우리 서버의 TrustStore(cacerts 또는 커스텀 truststore.jks)가 업데이트되지 않았습니다. 외부 기관이 인증서를 갱신하면서 중간 CA가 달라진 경우에 자주 발생합니다.

로컬 터미널
# 현재 외부 서버의 인증서 체인 확인
openssl s_client -connect external-api.example.com:443 \
  -showcerts 2>/dev/null | grep -E "BEGIN CERTIFICATE|subject|issuer"

# 출력에서 중간 CA 인증서 확인
# 체인이 완전하지 않으면: Chain: End Entity → (중간 CA 없음) → Root CA
# 정상 체인: End Entity → Intermediate CA → Root CA

# 중간 CA 인증서 추출 (2번째 인증서가 보통 중간 CA)
openssl s_client -connect external-api.example.com:443 \
  -showcerts 2>/dev/null | awk '/BEGIN CERT/,/END CERT/' | \
  awk 'BEGIN{n=0} /BEGIN CERT/{n++} n==2' > intermediate-ca.crt

# 추출한 중간 CA를 TrustStore에 추가
keytool -importcert \
  -keystore /etc/ssl/truststore.jks \
  -storepass trustpass \
  -alias intermediate-ca-new \
  -file intermediate-ca.crt \
  -noprompt

# Java 기본 cacerts에 추가 (sudo 필요)
sudo keytool -importcert \
  -keystore $JAVA_HOME/jre/lib/security/cacerts \
  -storepass changeit \
  -alias intermediate-ca-new \
  -file intermediate-ca.crt \
  -noprompt

# WAS 재시작 후 연결 테스트
sudo systemctl restart tomcat
curl -v https://external-api.example.com/health 2>&1 | grep -E "verify|Verify"

심화 — 서버 인증서는 멀쩡한데 mTLS만 끊긴다

💡개념

심화: mTLS 핸드셰이크의 안쪽 — 클라이언트 인증서는 언제, 무엇으로 검증되나

앞에서 KeyStore(나의 인증서)와 TrustStore(신뢰하는 상대)를 나눴습니다. 그 둘이 실제 TLS 핸드셰이크의 어느 순간에 쓰이는지를 알면, "서버 인증서는 정상인데 mTLS만 실패"하는 장애를 정확히 짚을 수 있습니다.

  • 핸드셰이크 순서: 클라이언트가 ClientHello를 보내면, 서버는 자신의 인증서와 함께 CertificateRequest(클라이언트 인증서를 내놓으라는 요구)를 보냅니다. 그러면 클라이언트는 자기 인증서(KeyStore의 것)를 제시하고, 개인키로 서명한 CertificateVerify로 "이 인증서의 주인이 나"임을 증명합니다.
  • 검증은 서버 쪽에서 일어난다: 우리가 낸 클라이언트 인증서는 서버가 자기 신뢰 앵커로 검증합니다. 즉 우리 클라이언트 인증서를 발급한 CA를 서버가 신뢰해야 하고, 우리는 리프 인증서만이 아니라 중간 CA까지 이어지는 전체 체인을 제시해야 서버가 앵커까지 경로를 이을 수 있습니다.
  • 여기서 실패하면 HTTP는 시작도 못 한다: 클라 인증서 체인이 불완전하거나, 우리 발급 CA를 서버가 안 믿거나, 인증서가 만료·alias 불일치로 실제 제시되지 않으면 서버는 핸드셰이크 단계에서 handshake_failure·bad_certificate로 연결을 끊습니다. 응답 코드(401/403) 이전, 즉 HTTP에 닿기도 전에 끊기는 것이 서버 인증서 문제(PKIX)와의 결정적 차이입니다.
  • 진단이 갈리는 지점: openssl s_client로 클라 인증서 없이 접속하면 서버 인증서만 검증되어 Verify return code: 0이 나올 수 있습니다. 그것은 "서버 인증서가 정상"일 뿐, 우리 클라 인증서가 받아들여지는지는 별개입니다 — 그래서 mTLS는 클라 인증서를 붙여 따로 재현해야 합니다.

정리하면 PKIX path building failed가 "상대(서버) 인증서를 내가 못 믿겠다(TrustStore)"라면, mTLS handshake_failure는 "내(클라) 인증서를 상대가 못 믿겠다(KeyStore 체인·발급 CA)"로 방향이 반대입니다.

상황: nc로 포트는 열려 있고, openssl s_client -connect host:443은 서버 인증서 검증 0 (ok)를 돌려줍니다. 그런데 클라이언트 인증서를 붙여 호출하는 실제 앱만 SSLHandshakeException·handshake_failure로 끊기고, HTTP 응답 코드는 구경도 못 합니다. 서버 인증서는 멀쩡한데 mTLS만 안 됩니다.

원인: 서버가 CertificateRequest로 우리 클라이언트 인증서를 요구하는데, 우리 쪽 제시가 서버 검증을 통과하지 못합니다. 흔한 경우는 (1) KeyStore(JKS)에 리프 인증서만 있고 중간 CA 체인이 빠져 서버가 신뢰 앵커까지 경로를 못 잇거나, (2) 우리 클라 인증서를 발급한 CA가 서버의 신뢰 목록에 없거나, (3) 인증서 만료·alias 불일치로 애초에 인증서가 제시되지 않은 것입니다. 셋 다 서버 쪽 검증 실패라 HTTP 이전 단계에서 끊깁니다.

진단: openssl s_client -connect host:443 -cert client.crt -key client.key -CAfile chain.pem로 클라 인증서를 붙여 재현합니다. peer did not return a certificate면 제시 자체가 안 된 것(alias·경로), alert handshake failure·certificate unknown이면 서버가 우리 발급 CA를 안 믿는 것입니다. keytool -list -v로 KeyStore에 privateKeyEntry와 리프+중간 CA 전체 체인이 있는지, 인증서 유효기간이 남았는지 확인합니다.

해결: KeyStore에 리프와 중간 CA를 모두 포함시킵니다 — PEM에서 만들 때 openssl pkcs12 -export에 전체 체인 파일을 넣어 체인이 딸려 가게 합니다. 인증서가 만료됐으면 갱신하고, 우리 발급 CA를 상대 기관 신뢰 목록에 등록해 달라고 요청합니다. openssl 재현에서 핸드셰이크가 성공하면 그 구성을 앱 JKS에 반영하고 WAS를 재시작합니다.

💼
실무 맥락
현업 패턴

실제 업무에서 이 지식이 쓰이는 상황:

공공기관이나 금융 프로젝트에서 외부 연계 오류는 오픈 전에 가장 많이 발생하는 장애 유형입니다. 대부분은 방화벽 오픈 누락과 SSL 인증서 문제입니다.

새 외부 연계 추가 시 표준 절차:

로컬 터미널
# 1. 외부 기관에서 받아야 할 정보 목록 작성
# - 운영/테스트 엔드포인트 URL
# - IP 화이트리스트 등록 (우리 서버 IP 전달 필요)
# - API Key 또는 클라이언트 인증서 발급
# - 테스트 계정 (SMS 발송 수신번호, 결제 테스트 카드 등)

# 2. 방화벽 오픈 요청 (개발→운영 환경 모두)
# 3. 연결 사전 점검 (3단계: nc → openssl → curl)
nc -zv api.external.com 443
openssl s_client -connect api.external.com:443 2>/dev/null | grep "Verify return code"
curl -m 10 -s -o /dev/null -w "%{http_code}" https://api.external.com/health

# 4. 앱 연동 테스트 (테스트 환경 → 운영 환경 순서)
# 5. Circuit Breaker 및 timeout 설정 검토

SMS 발송 실패 시 빠른 점검:

로컬 또는 서버
# API 직접 호출로 응답 코드/메시지 확인
curl -X POST https://sms-api.example.com/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"01012345678","message":"테스트"}'
# 응답: {"result":"error","code":"E401","message":"Invalid API Key"}
# → API Key 만료 또는 IP 화이트리스트 미등록 확인

외부 API 연계는 우리가 통제할 수 없는 외부 시스템에 의존하는 구간입니다. Circuit Breaker와 적절한 timeout으로 외부 장애가 내부 시스템으로 전파되는 것을 막는 것이 운영 안정성의 핵심입니다.


명령어·단축키 빠른 참조

외부 기관 API 연계에서 nc→openssl→curl 3단계 진단과 JKS/TrustStore 관리에 쓴 명령을 모았습니다. "예" 열의 조합을 그대로 써도 됩니다.

명령어/단축키용도자주 쓰는 예
nc -zv1단계 — L4 포트·방화벽 도달 확인nc -zv external-api.example.com 443
openssl s_client -connect2단계 — TLS 핸드셰이크·인증서 체인 검증openssl s_client -connect host:443 2>/dev/null | grep "Verify return code"
openssl x509 -noout -dates서버 인증서 만료일 확인openssl s_client -connect host:443 2>/dev/null | openssl x509 -noout -dates
openssl s_client -showcerts중간 CA 포함 인증서 체인 추출openssl s_client -connect host:443 -showcerts
curl -m -w "%{http_code}"3단계 — HTTP 응답코드·시간 확인curl -m 10 -s -o /dev/null -w "%{http_code} (%{time_total}s)\n" URL
keytool -list -vJKS·TrustStore 내용·만료·alias 확인keytool -list -v -keystore client.jks -storepass PW | grep -E "Alias|until"
keytool -importcertTrustStore에 외부·중간 CA 추가(PKIX 해결)keytool -importcert -keystore truststore.jks -alias ca -file ca.crt
openssl pkcs12 -exportPEM+개인키 → PKCS12로 묶기openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12
keytool -importkeystorePKCS12 → JKS 변환-srckeystore client.p12 -srcstoretype PKCS12 -destkeystore client.jks
dig +short방화벽 등록용 목적지 IP 조회dig +short external-api.example.com
traceroute어느 홉에서 막히는지 경로 확인traceroute external-api.example.com

관련 모듈로 더 깊이:

다음 모듈에서는 SAML과 OAuth 기반 SSO 연계 구성과 장애 분석을 다룹니다.

지식 확인

퀴즈 — 8문제

Q1

외부 API 연계 전 방화벽 오픈 요청 시 반드시 확인해야 할 사항이 아닌 것은?

Q2

Java KeyStore(JKS) 인증서 파일이 외부 API 연계에 필요한 이유는?

Q3

SMS 발송이 갑자기 실패할 때 가장 먼저 확인해야 할 로그는?

Q4

외부 API timeout을 너무 길게(예: 60초) 설정하면 우리 서버에 생기는 문제는?

Q5

외부 기관 API 연동에서 KeyStore와 TrustStore는 역할이 다르다. 어떻게 구분되나?

Q6

외부 HTTPS API 호출이 실패한다. 원인을 빠르게 좁히려고 nc → openssl s_client → curl 순서로 진단하는 이유는?

Q7

[심화] mTLS 연동에서 서버 인증서 검증은 정상인데(openssl s_client가 Verify return code 0) 클라이언트 인증서 제시 단계에서 handshake_failure가 난다. 이 실패의 위치와 원인으로 옳은 것은?

Q8

[심화] 서버 인증서는 정상인데 앱의 mTLS만 handshake_failure로 끊긴다. 원인을 격리하기 위한 가장 적절한 첫 조치는?

0 / 8 답변

🧪 실습으로 확인하기

Nginx 설치 및 기동

초급

Linux 서버에 Nginx를 설치하고 systemd 서비스로 등록하여 80포트에서 응답하는 상태까지 만든다.

40📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요