아이투맥스
경력근거: 고객사의 비즈니스 문제를 기술적으로 해석하고
아이투맥스는 고객사의 비즈니스 문제를 기술적으로 해석해 Salesforce Agentforce 와 LLM 으로 솔루션을 설계·구현하고, ERP·그룹웨어 같은 외부 레거시와 API·Webhook·OAuth 로 잇는 고객 밀착형 엔지니어를 찾습니다. 현장에서 실제로 건네받는 자료는 잘 정리된 액션 카탈로그가 아니라 고객사 개발팀이 툴로 뽑아준 Swagger 문서와 담당자가 메신저로 흘려준 운영 메모라, PoC 는 늘 이 문서에서 무엇이 비어 있는지 가려내 되묻는 데서 시작합니다. 이 도구는 그 앞단을 명령 하나로 끝내고, 문서만으로 정해지지 않는 항목은 추측하지 않고 담당자별 질문으로 가려냅니다. 이 포트폴리오가 보여주는 것은 API 명세 문서를 규칙으로 판정해 확인 질문 산출물로 바꾸는 파이프라인 구현 기술이며, Salesforce Agentforce 플랫폼 운영 경험과 고객사 업무 도메인 전문성은 주장하지 않고 면접에서 솔직히 다룹니다.
공고가 고객사의 비즈니스 문제를 기술적으로 해석하고 연동으로 푸는 엔지니어를 찾습니다. 연동의 첫 단추는 고객사가 준 명세 문서에서 무엇이 비어 있는지 가려내 되묻는 일입니다.
ERP·그룹웨어·컨택센터 같은 외부 시스템과 AI 기능을 잇는 일이라, 명세 문서의 인증 방식·파라미터 설명·오류 코드가 갖춰졌는지가 착수 가능 여부를 가릅니다.
고객사 담당자와 기술 미팅으로 요구사항을 정의하는 일이 담당업무에 있습니다. 무엇을 물어야 하는지가 문서로 정리돼 나가면 그 미팅이 짧아집니다.
Salesforce Agentforce 도입 FDE 의 연동 착수 도구입니다. 고객사가 준 OpenAPI 문서와 담당자 운영 메모를 인자로 받아 연동을 막는 결함을 규칙 8종으로 판정하고, 확인 질문 목록 마크다운과 미해결 항목 CSV 를 떨어뜨립니다.
고객사에 파견 나간 아이투맥스 AI Solutions Engineer 입니다. 킥오프 이튿날 고객사 IT 팀에게서 ERP·그룹웨어·컨택센터 Swagger 문서 세 벌과 메신저로 흘러온 운영 메모를 받아 두고, 어느 API 를 Agentforce 액션으로 붙일 수 있는지 답해야 하는데 인증 방식이 안 적힌 엔드포인트와 설명 없는 파라미터를 하나씩 눈으로 세다 첫 주를 보내던 사람으로, 무엇을 되물어야 하는지가 문서 한 장으로 정리돼 나오길 바랍니다.
연동을 막는 결함은 대부분 인증 방식 미기재·파라미터 설명 누락·응답 예시 부재·오류 코드 미정의처럼 명세 문서에서 필드 유무로 판정되는 항목이라, 문서만 읽어도 담당자에게 보낼 질문의 대부분이 자동으로 뽑힙니다.
OpenAPI 문서 한 벌과 담당자 메모 CSV 를 인자로 주면 엔드포인트를 행 단위로 평탄화하고 결함 규칙 8종으로 판정해, 담당자별로 묶은 확인 질문 목록 마크다운과 미해결 항목 CSV 두 파일을 쓰는 CLI입니다. 실제 고객사 API 호출과 Agentforce 연결은 넣지 않고 문서만 읽습니다.
회신이 채워진 질문지를 다시 인자로 받아 닫힌 질문과 남은 질문에 사유를 붙여 가르고, 회신 내용이 명세 문서와 어긋나면 모순으로 따로 표시해 2차 질문지를 내는 판입니다.
질문이 모두 닫힌 엔드포인트를 Agentforce 액션 정의 JSON 과 Node.js 호출 스텁으로 내보내고, 고객사별 스펙 점검을 정기 배치로 다시 돌리는 확장판입니다.
고객사 명세 세 벌(엔드포인트 42건)과 담당자 메모 CSV 를 넣고 명령을 한 번 치면, 확인 질문 목록 마크다운과 미해결 항목 CSV 가 몇 초 안에 떨어지고 착수 가능·확인 필요·차단 건수가 터미널에 요약돼 나오는 걸 시연합니다
판정은 문서 필드의 유무로만 내려가는 결정론이라 같은 입력에 같은 결과가 나옵니다. 규칙 8종마다 위반·비위반 픽스처를 두고 회귀 테스트가 전부 통과하는 걸 보여줍니다
일부러 만든 지저분한 명세 12종(인증 미기재·설명 없는 필수 파라미터·한글 경로와 한글 파라미터명·같은 경로가 두 문서에 중복·개인정보 필드에 마스킹 표기 없음·빈 오류 코드 배열)을 넣어도 출력 형식이 한 번도 안 깨지고, 문서만으로 정해지지 않으면 추측하지 않고 확인 필요로 가려냅니다
2주차에 담당자 회신을 반영하면 1차 질문 42건 가운데 27건(약 64%)이 닫히고 나머지는 사유를 달고 남습니다. 단, 명세·메모·회신을 모두 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건에서의 동작임을 명시합니다
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 전부 작성자가 만든 더미 명세와 더미 회신 자가채점이며, 실제 결함 적중률은 아이투맥스가 들어간 고객사의 실 명세와 실 IT 담당자 회신이 있어야 측정 가능합니다. 이 도구는 그 검증을 문서만 갈아끼워 바로 돌릴 수 있는 상태로 만듭니다
이건 고객·실데이터 없이 혼자 만든 포트폴리오입니다. 모든 수치는 합성 명세·합성 회신 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다
이 포트폴리오가 보여주는 것은 API 명세 문서를 규칙으로 판정해 확인 질문 산출물로 바꾸는 파이프라인 구현 기술과, LLM 을 문안 변환에만 가두고 판정을 규칙에 맡기는 설계입니다. Salesforce Agentforce 플랫폼 운영·Apex·Flow 개발 경험과 고객사 업무 도메인 전문성은 이 포트폴리오로 증명되지 않으므로 주장하지 않으며, 그 깊이는 면접에서 모르면 모른다고 말하는 편이 낫습니다
결함을 심은 사람이 작성자라 잘 잡히는 게 당연합니다. 인증 미기재·한글 경로·중복 경로·설명 없는 필수 파라미터처럼 실제 문서에서 흔한 케이스를 더미에 일부러 섞어, 깨끗한 명세만 통과시키는 게 아님을 보여줍니다
규칙은 필드가 있는지만 봅니다. 설명이 적혀 있어도 내용이 틀린 경우는 잡지 못하므로, 그 판단은 담당자 회신과 실제 호출로 넘기고 도구는 물어야 할 자리만 지목합니다
고객사마다 명세 작성 관행과 필수 항목이 다릅니다. 새 문서 표준을 제정하지 않고 고객사가 이미 쓰는 문서를 그대로 입력으로 받으며, 규칙에 안 걸리는 항목이 많으면 질문지가 얇아져 무엇을 더 받아야 하는지가 드러나게 둡니다
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사 IT 팀이 넘긴 OpenAPI 문서를 엔드포인트 단위로 평탄화한 표입니다. 결함 규칙 판정의 입력입니다.
| ep_id | source | method | path | summary | auth | params | has_response_example | error_codes | pii_fields | rate_limit |
|---|---|---|---|---|---|---|---|---|---|---|
| E001 | erp_swagger.json | GET | /orders/{orderId} | 주문 단건 조회 | oauth2 | [{"name":"orderId","required":true,"desc":"주문 번호입니다"}] | true | ["400","404"] | [] | 60 rpm |
| E002 | erp_swagger.json | GET | /orders | 주문 목록 조회 | oauth2 | [{"name":"from","required":true,"desc":null},{"name":"to","required":true,"desc":null}] | false | [] | ["customerName"] | — |
| E003 | erp_swagger.json | POST | /orders/{orderId}/cancel | — | unspecified | [{"name":"orderId","required":true,"desc":"주문 번호입니다"},{"name":"reasonCode","required":true,"desc":null}] | false | ["409"] | [] | — |
| E004 | groupware_openapi.yaml | GET | /employees | 임직원 목록 조회 | apikey | [{"name":"deptCd","required":false,"desc":"부서 코드입니다"}] | true | ["401"] | ["email","phone"] | — |
| E005 | groupware_openapi.yaml | POST | /approvals | 결재 상신 | apikey | [{"name":"docType","required":true,"desc":null},{"name":"approverId","required":true,"desc":"결재자 사번입니다"}] | false | ["400"] | ["approverId"] | — |
다음 Zod 스키마를 따르는 고객사 엔드포인트 행을 더 생성하라: {"type":"object","properties":{"ep_id":{"type":"string"},"source":{"type":"string"},"method":{"type":"string","enum":["GET","POST","PUT","DELETE"]},"path":{"type":"string"},"summary":{"type":["string","null"]},"auth":{"type":"string","enum":["oauth2","apikey","basic","none","unspecified"]},"params":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"required":{"type":"boolean"},"desc":{"type":["string","null"]}},"required":["name","required","desc"]}},"has_response_example":{"type":"boolean"},"error_codes":{"type":"array","items":{"type":"string"}},"pii_fields":{"type":"array","items":{"type":"string"}},"rate_limit":{"type":["string","null"]}},"required":["ep_id","source","method","path","summary","auth","params","has_response_example","error_codes","pii_fields","rate_limit"]}. 깨끗한 명세만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라: auth 가 'unspecified' 인 인증 미기재, required 가 true 인데 desc 가 null 인 설명 없는 필수 파라미터, error_codes 가 빈 배열, pii_fields 에 값이 있는데 마스킹 표기가 summary·desc 어디에도 없는 건, 한글 path 와 한글 파라미터명, 서로 다른 source 에 같은 path 가 중복으로 나오는 건, summary 가 null 인 건. 추가로 형식 깨뜨리기 시도 3건(method 가 enum 밖이거나 params 가 배열이 아닌 행)을 별도 표시해 파서 가드 테스트에 쓴다.{"type":"object","properties":{"ep_id":{"type":"string"},"source":{"type":"string"},"method":{"type":"string","enum":["GET","POST","PUT","DELETE"]},"path":{"type":"string"},"summary":{"type":["string","null"]},"auth":{"type":"string","enum":["oauth2","apikey","basic","none","unspecified"]},"params":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"required":{"type":"boolean"},"desc":{"type":["string","null"]}},"required":["name","required","desc"]}},"has_response_example":{"type":"boolean"},"error_codes":{"type":"array","items":{"type":"string"}},"pii_fields":{"type":"array","items":{"type":"string"}},"rate_limit":{"type":["string","null"]}},"required":["ep_id","source","method","path","summary","auth","params","has_response_example","error_codes","pii_fields","rate_limit"]}고객사 담당자가 메일·메신저로 흘려준 운영 메모입니다. 결함 일부는 이 메모로 닫히고, 메모가 명세와 어긋나면 모순으로 남습니다.
| note_id | from_role | raw_text | refers_path | kind |
|---|---|---|---|---|
| N001 | 고객사 IT | ERP 는 OAuth2 client_credentials 만 씁니다.… | — | auth |
| N002 | 고객사 IT | 그룹웨어는 운영계 키가 아직 안 나왔고 개발계만 열려 있습니다 | — | env |
| N003 | 고객사 현업 | 주문 목록은 하루치만 뽑아 쓰면 됩니다 | /orders | etc |
| N004 | 고객사 IT | 상담 티켓 생성은 초당 10건을 넘기면 막힙니다 | /tickets | rate |
| N005 | 고객사 IT | 전화번호는 마스킹된 필드가 따로 있는데 이름이 뭔지는 담당자에게 물어… | /tickets | pii |
다음 Zod 스키마를 따르는 담당자 운영 메모를 더 생성하라: {"type":"object","properties":{"note_id":{"type":"string"},"from_role":{"type":"string","enum":["고객사 IT","고객사 현업","영업 담당"]},"raw_text":{"type":"string"},"refers_path":{"type":["string","null"]},"kind":{"type":"string","enum":["auth","rate","env","pii","contact","etc"]}},"required":["note_id","from_role","raw_text","refers_path","kind"]}. 친절한 메모만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라: refers_path 가 null 이라 어느 엔드포인트 이야기인지 모르는 건, '그거 안 씁니다' 처럼 지시어만 있는 건, 명세에는 인증이 안 적혔는데 '문서에 다 나와 있습니다' 라고 답한 모순 건, 답을 주지 않고 다른 담당자를 가리키는 건, 같은 경로에 대해 서로 다른 두 메모가 어긋나는 건. 추가로 refers_path 가 명세에 없는 경로를 가리키는 행 2건을 넣어 매칭 실패 처리를 테스트한다.{"type":"object","properties":{"note_id":{"type":"string"},"from_role":{"type":"string","enum":["고객사 IT","고객사 현업","영업 담당"]},"raw_text":{"type":"string"},"refers_path":{"type":["string","null"]},"kind":{"type":"string","enum":["auth","rate","env","pii","contact","etc"]}},"required":["note_id","from_role","raw_text","refers_path","kind"]}| Python | CLI 본체입니다. OpenAPI 문서 로딩과 엔드포인트 평탄화, 결함 규칙 8종 판정, 마크다운·CSV 산출을 한 명령에 묶습니다. |
| TypeScript | 3주차에 내보내는 Agentforce 액션 정의의 타입 선언을 함께 생성해 스텁과 정의가 어긋나지 않게 잡습니다. |
| JavaScript | 내보낸 호출 스텁을 담당자가 브라우저 콘솔이나 스크립트로 바로 실행해 응답 모양을 확인하는 데 씁니다. |
| Java | 고객사 레거시가 Spring 계열이면 springdoc 이 자동 생성한 OpenAPI 문서가 들어옵니다. 그 산출물에 흔한 빈 description 과 누락된 example 을 규칙 기본값으로 반영합니다. |
| Node.js | 3주차 호출 스텁을 Node.js 실행 파일로 내보내 고객사 담당자가 별도 환경 없이 돌려보게 합니다. |
| AWS | 2주차부터 도구를 Docker 이미지로 굽고 AWS 에서 배치로 돌려 고객사가 명세를 갱신할 때마다 질문 목록을 다시 뽑습니다. |
| LLM | 규칙이 잡아낸 위반 코드를 담당자가 그대로 읽을 한 문장 질문으로 옮깁니다. 판정에는 관여하지 않습니다. |
✓ pass → 여기서 멈춰도 착수 첫날 질문지를 뽑는 도구로 완결됩니다. 더 키우려면 2주차에서 담당자 회신을 되받아 잔여 질문만 남기는 재판정을 붙입니다.
✓ pass → 3주차에서 질문이 모두 닫힌 엔드포인트를 Agentforce 액션 정의와 호출 스텁으로 내보냅니다.
엔드포인트 한 건에 대한 연동 착수 판정과 담당자에게 보낼 확인 질문입니다. 이 행들을 담당자별로 묶어 확인 질문 목록 마크다운을 씁니다.
{"type":"object","properties":{"ep_id":{"type":"string"},"verdict":{"type":"string","enum":["ready","needs_answer","blocked"]},"broken_rules":{"type":"array","items":{"type":"string","enum":["R1_auth_unspecified","R2_param_desc_missing","R3_no_response_example","R4_no_error_codes","R5_pii_unmasked","R6_no_rate_limit","R7_summary_missing","R8_duplicate_path"]}},"resolved_by_note":{"type":"array","items":{"type":"string"}},"conflicts":{"type":"array","items":{"type":"string"}},"questions":{"type":"array","items":{"type":"object","properties":{"rule":{"type":"string"},"ask":{"type":"string"},"owner":{"type":"string","enum":["고객사 IT","고객사 현업","내부 확인"]}},"required":["rule","ask","owner"]}},"hedge":{"type":"string"}},"required":["ep_id","verdict","broken_rules","resolved_by_note","conflicts","questions","hedge"]}[
{
"ep_id": "E001",
"verdict": "ready",
"broken_rules": [],
"resolved_by_note": [
"N001"
],
"conflicts": [],
"questions": [],
"hedge": "문서 필드가 모두 채워져 있어 착수 가능으로 판정했습니다. 실제 호출로 검증하지는 않았습니다"
},
{
"ep_id": "E002",
"verdict": "needs_answer",
"broken_rules": [
"R2_param_desc_missing",
"R3_no_response_example",
"R4_no_error_codes",
"R5_pii_unmasked",
"R6_no_rate_limit"
],
"resolved_by_note": [
"N003"
],
"conflicts": [],
"questions": [
{
"rule": "R2_param_desc_missing",
"ask": "'/orders' 의 필수 파라미터 'from' 과 'to' 는 어떤 형식의 날짜를 받습니까. 예시 값을 하나씩 알려주시면 좋겠습니다",
"owner": "고객사 IT"
},
{
"rule": "R5_pii_unmasked",
"ask": "'/orders' 응답의 'customerName' 은 개인정보로 보이는데 마스킹된 형태로 내려오는지, 아니면 별도 권한이 필요한지 알려주시면 좋겠습니다",
"owner": "고객사 IT"
},
{
"rule": "R4_no_error_codes",
"ask": "'/orders' 호출이 실패할 때 어떤 오류 코드가 내려오는지 목록을 받을 수 있겠습니까",
"owner": "고객사 IT"
}
],
"hedge": "판정은 문서 필드의 유무만 보고 내렸습니다. 문서에 적힌 내용이 실제와 다른 경우는 잡지 못합니다"
},
{
"ep_id": "E010",
"verdict": "blocked",
"broken_rules": [
"R2_param_desc_missing",
"R3_no_response_example",
"R4_no_error_codes",
"R5_pii_unmasked",
"R6_no_rate_limit"
],
"resolved_by_note": [],
"conflicts": [
"N011"
],
"questions": [
{
"rule": "R5_pii_unmasked",
"ask": "'/고객정보조회' 응답에 '주민번호뒤7자리' 가 포함돼 있습니다. 이 필드를 빼고 받을 수 있는 경로가 있는지, 없다면 어떤 승인 절차가 필요한지 알려주시면 좋겠습니다",
"owner": "고객사 IT"
},
{
"rule": "R1_auth_unspecified",
"ask": "'/고객정보조회' 는 인증 없이 열려 있는 것으로 문서에 적혀 있는데, 사내망 전용이라는 메모와 어긋납니다. 실제 접근 방식을 확인해 주시면 좋겠습니다",
"owner": "고객사 IT"
},
{
"rule": "R5_pii_unmasked",
"ask": "이 엔드포인트를 Agentforce 액션으로 붙여도 되는지 내부 개인정보 검토가 먼저 필요합니다",
"owner": "내부 확인"
}
],
"hedge": "개인정보 필드가 마스킹 표기 없이 노출돼 착수 불가로 판정했습니다. 합성 명세 기준입니다"
}
]verdict·broken_rules·owner enum 과 questions 배열 형태는 Zod 로 자동 검증합니다. broken_rules 는 입력 행의 필드 유무에서 결정론으로 계산되므로 규칙 8종마다 위반·비위반 픽스처를 두고 회귀 테스트로 판정 일치를 확인합니다. resolved_by_note·conflicts 의 note_id 는 handover_note 와 조인해 실재 여부를 검사합니다. ask 문장이 원문 경로·파라미터명을 substring 으로 인용했는지는 문자열 매칭으로 검사하고, 실패하면 규칙별 기본 문안으로 되돌립니다.