DEVELOPER GUIDE
토큰을 받아 첫 호출을 성공시키기까지 필요한 절차를 순서대로 정리했습니다. 각 서비스의 정확한 필드 스펙은 Swagger 문서에서 Try it out으로 직접 확인하세요.
모든 호출은 Authorization: Bearer <토큰> 헤더가 필요합니다.
토큰은 JWT가 아니라 opaque 문자열입니다 - 탈취돼도 그 안에 아무 정보도 들어있지 않고,
서버가 저장해 둔 해시와 대조해서만 유효성을 판단합니다.
# Authorize 창에는 Bearer 접두사 없이 토큰 값만 넣습니다. curl -H "Authorization: Bearer <토큰>" \ https://호스트/api/services/AC_ADDRAT_L1/call \ -X POST -H "Content-Type: application/json" \ -d '{"isPatNo":"00012345"}'
헤더가 아예 없으면 401 missing Authorization: Bearer <token> header,
토큰이 틀렸거나 폐기/만료됐으면 401 invalid or revoked API token이 오는데
- 이때 hint 필드에 "URL을 넣은 건 아닌지", "Bearer가 중복된 건 아닌지",
"폐기됐는지 만료됐는지"를 서버가 진단해서 알려줍니다. 값 자체는 절대 되비추지 않으니 hint만 보고
원인을 좁히세요.
이 게이트웨이는 병원 tmax 도메인의 서비스를 이름 그대로 노출합니다 - 서비스명은 업무그룹 머릿글자로 시작합니다(예: 원무업무는 A, 진료업무는 M). 어떤 서비스가 있는지는 세 가지 경로로 찾을 수 있습니다.
GET /docs — Swagger UI. 필드까지 완전히 문서화된 파일럿
서비스는 여기서 바로 Try it out으로 실행해볼 수 있습니다.GET /api/services?q=&group=&status=RDY&limit=&offset=
— 이름/업무그룹/기동상태로 검색·페이징 조회.GET /api/services/{svcName} — 서비스 하나의 상세 스펙
(입출력 필드, requires_tx, accepts_multi_row_input,
has_binary_field)을 반환합니다. 아래 03~06 항목은 이 값들을 보고
판단합니다.모든 서비스 호출은 하나의 엔드포인트를 씁니다.
// body: 별칭(alias) 필드로 채운 평면 JSON 객체 { "isPatNo": "00012345" } // -> 200 응답: 응답도 별칭으로 번역돼 돌아옵니다 { "success": true, "sPatname": "…", "tBirthday": "…", … }
필드명은 원래 tmax FML 이름(예: S_STRING1)이 아니라, Pro*C 소스에서
자동 추출한 읽기 쉬운 별칭(isPatNo)을 씁니다. 어떤 별칭이 있는지는
02의 서비스 스펙 조회로 확인하세요. 값은 문자열·숫자·불리언 같은 평면 스칼라만 가능합니다 - 중첩
객체는 FML로 표현할 수 없어 400 invalid_request_body로 거절됩니다.
언어별 호출 예제 (모두 같은 요청을 보냅니다):
curl -X POST "https://호스트/api/services/AC_ADDRAT_L1/call" \ -H "Authorization: Bearer <토큰>" \ -H "Content-Type: application/json" \ -d '{"isPatNo":"00012345"}'
# pip install requests import requests TOKEN = "<토큰>" BASE_URL = "https://호스트" resp = requests.post( f"{BASE_URL}/api/services/AC_ADDRAT_L1/call", headers={"Authorization": f"Bearer {TOKEN}"}, json={"isPatNo": "00012345"}, timeout=20, ) data = resp.json() if resp.status_code == 200: print("성공:", data) elif resp.status_code == 422: print("업무 실패:", data) # 재시도해도 결과 동일 - 07 참고 else: print(f"오류 {resp.status_code}:", data)
// 외부 라이브러리 불필요 - java.net.http (Java 11+) 사용 import java.net.URI; import java.net.http.*; import java.time.Duration; String token = "<토큰>"; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://호스트/api/services/AC_ADDRAT_L1/call")) .header("Authorization", "Bearer " + token) .header("Content-Type", "application/json") .timeout(Duration.ofSeconds(20)) .POST(HttpRequest.BodyPublishers.ofString("{\"isPatNo\":\"00012345\"}")) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { // 성공 - response.body() 를 파싱 } else if (response.statusCode() == 422) { // 업무 실패 - 재시도해도 결과 동일, 07 참고 }
일부 서비스는 여러 테이블에 걸친 쓰기를 하나의 글로벌 트랜잭션(XA)으로 묶어야 합니다. 그런
서비스는 02의 스펙 조회에서 requires_tx: true로 표시되며, 호출 시
쿼리스트링에 ?tx=true를 붙여야 합니다.
curl ... -X POST ".../api/services/AC_ADDRAT_U1/call?tx=true" -d '{"...": "..."}'
requires_tx: false인 서비스에 tx=true를 붙여도
해가 되진 않지만, 필요 없는 서비스에는 붙이지 않는 편이 트랜잭션 오버헤드가 없어 유리합니다.
accepts_multi_row_input: true인 서비스는 한 번의 호출로 여러 행을
보낼 수 있습니다. 최상위 필드 대신 rows 배열에 행 객체를 나열하세요.
{ "rows": [
{ "isPatNo": "00012345", "sItemCd": "A001" },
{ "isPatNo": "00012345", "sItemCd": "A002" }
] }
모든 행은 정확히 같은 필드 구성이어야 합니다. 행마다 키가 다르면 브릿지가 열을 순차로
쌓는 방식(fbput) 특성상 값이 한 칸씩 밀려버릴 수 있어, 서버는 이 경우를 조용히 처리하는 대신
400으로 명시적으로 거절합니다. 같은 필드를 최상위와 rows
양쪽에 중복 지정해도 마찬가지로 거절됩니다.
has_binary_field: true인 서비스는 이미지·첨부파일 필드를 포함합니다.
값은 base64로 인코딩한 문자열로 보내고 받습니다 - 별도 multipart 업로드는 지원하지 않습니다.
필드당 크기 한도는 약 1MB(1,048,512바이트, 디코딩 후 기준)입니다. 초과하면 tmax까지
가지 않고 그 자리에서 413 payload_too_large로 거절되며, 응답에
어느 필드가 몇 바이트였고 한도가 얼마인지 정확한 수치가 함께 옵니다 - 브릿지 stderr을 뒤질 필요
없이 바로 원인을 알 수 있습니다.
HTTP 상태 코드로 "성공/업무 실패/시스템 오류"를 구분합니다 - 재시도 전략이 코드마다 다릅니다.
| 코드 | 의미 | 대처 |
|---|---|---|
| 200 | 성공 (업무 로직까지 정상 처리) | — |
| 400 | 요청 본문 형식 오류 (중첩 객체, rows 필드 불일치 등) | 재시도 전에 요청 본문을 고쳐야 함 - 그대로 재시도해도 동일하게 실패 |
| 401 | 토큰 없음/무효/만료/폐기 | hint 필드 확인 후 토큰 재확인 또는 관리자에게 재발급 요청 |
| 403 | 토큰의 ROLE에 이 서비스가 포함되지 않음 | 관리자에게 ROLE 할당 요청 |
| 404 | 존재하지 않거나(오탈자) ROLE 밖이라 "없음"으로 응답한 서비스 | 서비스명 재확인 |
| 413 | 바이너리 필드가 한도(약 1MB) 초과 | 압축하거나 분할 - 응답의 size_bytes/limit_bytes 참고 |
| 422 | 업무 실패 (tmax 도메인은 정상 응답했지만 업무 조건 불충족 - 예: 대상 없음, 입력값 오류) | 그대로 재시도해도 결과 동일 - 응답 필드로 원인 확인 |
| 502 | 게이트웨이-tmax 브릿지 오류 (타임아웃 15초 기본값, 브릿지 실행 실패, tmax 도메인 응답 불가 등) | 일시적 장애일 가능성 - 백오프 후 재시도 가능 |
200과 422 둘 다 tmax 도메인이 정상적으로
응답했다는 뜻입니다(둘 다 "요청은 잘 갔다") - 차이는 응답 본문의 success
필드가 가리는 업무 결과입니다. 다만 조회 결과가 0건인 경우는 실패로 보지 않고 서버가
success: true, no_data_found: true로 자동 정규화해 돌려줍니다.
tx=true)을 재시도할 때는 중복 처리 가능성을 염두에 두고 멱등하게
설계하세요 - 게이트웨이 쪽에서 중복 방지를 대신해주지 않습니다.requires_tx/
accepts_multi_row_input/has_binary_field는
서비스가 바뀌지 않는 한 고정값이라, 매 호출 전에 다시 조회할 필요는 없습니다.status가 RDY인지
확인하면, 유지보수로 잠시 내려간 서비스를 부르는 헛수고를 줄일 수 있습니다.