마키나락스
경력근거: 다양한 AI 모델 및 AI Agent 를 사용자가 쉽게 활용할 수 있도록 하는 AI 애플리케이션의 백엔드를 구현
마키나락스 FDE 본부는 산업 현장의 실제 데이터로 AI 문제를 정의하고 그 경험을 솔루션으로 쌓는 조직이며, 이 포지션은 AI 애플리케이션 백엔드를 REST API·DB 스키마로 설계해 Kubernetes 플랫폼에 배포·운영하는 일을 맡습니다. 현장에서 받은 점검 엑셀을 규칙으로 정규화해 테이블과 조회 API 로 세우고 컨테이너로 넘기는 이 CLI는 그 사슬을 자격요건 그대로 밟습니다. 우대사항인 Keycloak OIDC 인증 통합과 GitHub Actions 배포도 3주차·2주차에 실제로 붙입니다. 다만 이 포트폴리오가 보여주는 것은 지저분한 업무 문서를 규칙으로 정규화해 백엔드로 세우는 기술이며, 설비 보전·제조 공정 도메인 전문성은 주장하지 않습니다.
AI 모델·에이전트가 쓸 애플리케이션 백엔드를 구현하고 REST API 와 DB 스키마를 직접 설계합니다. 점검 엑셀에서 테이블과 조회 엔드포인트를 세우는 이 도구가 그 일과 직접 맞물립니다.
Kubernetes 기반 플랫폼에서 서비스 배포·운영을 지원하고 CI/CD 로 고객에게 서비스를 넘깁니다. 이 도구는 컨테이너와 Helm 차트로 고객사 사내망에 그대로 넘어가는 형태로 만듭니다.
외부 IdP 와의 인증 흐름을 백엔드에 통합하는 역량을 우대합니다. 조회 API 를 Keycloak OIDC 뒤에 두고 부서별 설비 범위를 토큰으로 제한하는 3주차가 여기에 대응합니다.
고객사 설비 점검 엑셀(월별 시트·병합 셀·반복 머리글·소계 행·단위가 섞인 값)을 명령 한 번으로 읽어, 머리글 별칭 규칙표와 단위·날짜 파서로 한 행 한 기록으로 정규화합니다. 정규화 결과에서 테이블 DDL 을 만들어 적재하고 설비·기간으로 조회하는 REST 엔드포인트와 OpenAPI 문서를 함께 내보냅니다. 규칙이 못 맞춘 머리글은 버리지 않고 mapping.yml 에 미결로 남겨, 고객사 담당자가 확정한 뒤 다시 돌려야 적재됩니다.
마키나락스와 AI 도입 계약을 맺은 제조 고객사의 생산기술팀 담당자입니다. 설비 점검 일지를 10년째 엑셀로 관리해 왔고, 새로 붙는 AI 애플리케이션이 그 기록을 읽게 하려고 매달 사람이 손으로 시트를 옮겨 적던 사람입니다.
점검 엑셀의 지저분함이 머리글 별칭·단위·날짜·행 종류라는 네 갈래로 나뉜다면, (a) 규칙만으로 대부분의 셀을 원본 셀 주소를 달아 정규화하고, (b) 정규화 결과에서 테이블과 조회 엔드포인트를 바로 세우며, (c) 규칙이 못 맞춘 머리글만 사람이 확정하는 왕복으로 좁힙니다.
점검 엑셀 경로를 인자로 주면 정규화 CSV·테이블 DDL·조회 REST API·변환 리포트가 나오는 CLI입니다. 입력은 고객사 양식을 본뜬 더미 엑셀로 시작합니다.
파서와 조회 API 를 컨테이너로 묶고 Helm 차트로 폐쇄망 가정 Kubernetes 에 올린 뒤, GitHub Actions 로 이미지 빌드·배포가 도는 상태입니다.
조회 API 를 Keycloak OIDC 뒤에 두고 토큰 클레임으로 부서별 설비 범위를 제한하며, 다른 고객사 양식 한 벌을 규칙표 교체만으로 통과시킨 결과입니다.
고객사 양식을 본뜬 3개월치 점검 엑셀을 한 명령에 넣으면 정규화 CSV·테이블 DDL·조회 API·변환 리포트가 몇 초 안에 나오는 걸 터미널에서 시연합니다
일부러 만든 지저분한 시트 30종(병합 셀·반복 머리글·소계 행·단위 혼용·미상 머리글)을 넣어도 파서가 멈추지 않고, 못 맞춘 머리글은 버리지 않고 미결로 남깁니다
더미에 심은 정답 200셀 중 정규화 결과가 일치한 게 186셀(약 93%)입니다. 정답과 데이터를 모두 작성자가 만든 합성 기준이라 '성능 검증'이 아니라 '데모 조건에서의 동작'임을 명시합니다
진짜 검증은 여기서 멈춥니다. 위 수치는 전부 작성자가 만든 더미 엑셀 자가채점이며, 실제 정확도는 고객사의 실제 점검 파일과 그 회사 담당자의 확정이 있어야 측정 가능합니다. 이 도구는 그 검증을 현장에서 바로 돌릴 수 있는 상태로 만듭니다
이건 고객사·실데이터 없이 혼자 만든 포트폴리오입니다. 모든 수치는 합성 엑셀 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다
이 포트폴리오가 보여주는 것은 지저분한 업무 문서를 규칙으로 정규화해 테이블과 REST API 로 세우는 백엔드 엔지니어링이며, 설비 보전·제조 공정 도메인 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 병합 셀·반복 머리글·소계 행·미상 머리글 같은 경계 케이스를 더미에 일부러 섞어, 깨끗한 시트만 푸는 게 아님을 보여줍니다
머리글을 LLM 후보로 자동 확정하면 틀린 컬럼이 조용히 테이블에 들어갑니다. 미결 머리글은 mapping.yml 에 남기고 사람이 확정해 다시 돌려야 적재되도록 막았습니다
고객사마다 양식과 점검 기준이 다릅니다. 실제 도입에서는 별칭 규칙표와 판정 기준을 그 회사 파일로 다시 맞춰야 하고, 사내망 DB 접속 정보와 계정 발급은 고객사 승인이 필요하므로 도입 단계 과제로 분리합니다
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사 설비 점검 엑셀을 셀 단위로 읽어놓은 원시 기록입니다. 파서와 규칙표가 가장 먼저 다루는 입력이며, 병합 셀·반복 머리글·소계 행·단위 혼용·미상 머리글을 일부러 섞고 expected_field/expected_value 로 정답을 심어 자가채점합니다.
| row_id | src_sheet | src_cell | row_kind | col_header_raw | value_raw | hard_case | expected_field | expected_value |
|---|---|---|---|---|---|---|---|---|
| rs_001 | 2026-03 점검 | A5 | data | 설비번호 | EQ-101 | none | equipment_id | EQ-101 |
| rs_002 | 2026-03 점검 | B5 | data | 점검일자 | 3/4 | date_shorthand | checked_at | 2026-03-04 |
| rs_003 | 2026-03 점검 | C5 | data | 회전수 | 1,250 rpm | unit_in_value | rotation_rpm | 1250 |
| rs_004 | 2026-03 점검 | D5 | data | 표면온도(℃) | 85℃ | unit_in_value | temperature_c | 85 |
| rs_005 | 2026-03 점검 | E5 | data | 판정 | 정상 | none | status | ok |
아래 Zod 스키마에 맞는 설비 점검 엑셀 원시 셀 기록을 80행 JSON 배열로 생성합니다. 시트 2~3개(월별), 설비 5~7대, 컬럼은 설비번호·일자·회전수·온도·판정·비고 계열로 구성합니다. expected_field/expected_value 는 규칙대로 정직하게 채웁니다. 소계 행·반복 머리글·미상 머리글은 expected_field 를 null 로 둡니다.
{"type":"object","properties":{"row_id":{"type":"string"},"src_sheet":{"type":"string"},"src_cell":{"type":"string"},"row_kind":{"type":"string","enum":["header","data","subtotal","note"]},"col_header_raw":{"type":"string"},"value_raw":{"type":"string"},"hard_case":{"type":"string","enum":["none","repeated_header","merged_cell","subtotal_row","unit_in_value","date_shorthand","unknown_header","empty_mark"]},"expected_field":{"type":["string","null"]},"expected_value":{"type":["string","null"]}},"required":["row_id","src_sheet","src_cell","row_kind","col_header_raw","value_raw","hard_case","expected_field","expected_value"]}
쉬운 시트만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라(깨끗한 시트만 푸는 함정 회피): (1) 병합 셀로 설비번호가 빈 행 5건 이상, (2) 시트 중간에 다시 나오는 머리글 행 2건, (3) 소계·합계 행 3건, (4) 단위가 값에 붙은 셀('1,250 rpm'·'85℃'·'0.8 mm/s') 6건 이상, (5) 날짜 표기 흔들림('3/4'·'2026.4.2'·'26-04-02') 각 2건, (6) 규칙표에 없는 머리글 2건, (7) 결측 표기('-'·'해당없음') 3건. JSON 배열만.{"type":"object","properties":{"row_id":{"type":"string"},"src_sheet":{"type":"string"},"src_cell":{"type":"string"},"row_kind":{"type":"string","enum":["header","data","subtotal","note"]},"col_header_raw":{"type":"string"},"value_raw":{"type":"string"},"hard_case":{"type":"string","enum":["none","repeated_header","merged_cell","subtotal_row","unit_in_value","date_shorthand","unknown_header","empty_mark"]},"expected_field":{"type":["string","null"]},"expected_value":{"type":["string","null"]}},"required":["row_id","src_sheet","src_cell","row_kind","col_header_raw","value_raw","hard_case","expected_field","expected_value"]}도구가 내장한 머리글 별칭 규칙표입니다. 업계 표준을 제정하는 표가 아니라 이 고객사 양식 한 벌을 읽기 위한 도구 내부 규칙이며, 다른 고객사에 가면 통째로 갈아끼웁니다. confirmed_by 가 customer 인 행은 미결로 남았다가 담당자가 확정해 들어온 행입니다.
| alias_raw | std_field | value_kind | unit_hint | confirmed_by |
|---|---|---|---|---|
| 설비번호 | equipment_id | id | — | rule |
| 설비 No | equipment_id | id | — | rule |
| 호기 | equipment_id | id | — | customer |
| 점검일자 | checked_at | date | — | rule |
| 일자 | checked_at | date | — | rule |
아래 Zod 스키마에 맞는 머리글 별칭 규칙표를 24행 JSON 배열로 생성합니다. 표준 필드는 equipment_id·checked_at·rotation_rpm·temperature_c·vibration_mm_s·status·note 로 제한하고, 한 표준 필드에 별칭을 2~4개씩 붙입니다.
{"type":"object","properties":{"alias_raw":{"type":"string"},"std_field":{"type":"string"},"value_kind":{"type":"string","enum":["id","date","number","enum","text"]},"unit_hint":{"type":["string","null"]},"confirmed_by":{"type":"string","enum":["rule","customer"]}},"required":["alias_raw","std_field","value_kind","unit_hint","confirmed_by"]}
실제 현장 표기를 반영해라: 공백·괄호·단위가 붙은 표기('표면온도(℃)'·'설비 No'), 한자·영문 혼용('號機'·'EQ No'), 오타 섞인 표기 2건, 담당자가 확정해 들어온 행(confirmed_by=customer) 4~5건. 같은 alias_raw 를 두 표준 필드에 중복 매핑하지 마라. JSON 배열만.{"type":"object","properties":{"alias_raw":{"type":"string"},"std_field":{"type":"string"},"value_kind":{"type":"string","enum":["id","date","number","enum","text"]},"unit_hint":{"type":["string","null"]},"confirmed_by":{"type":"string","enum":["rule","customer"]}},"required":["alias_raw","std_field","value_kind","unit_hint","confirmed_by"]}| Python | 엑셀 파서·정규화 규칙·조회 API 의 구현 언어입니다 |
| REST API | 설비·기간으로 점검 기록을 조회하는 엔드포인트 설계 기준입니다 |
| PostgreSQL | 정규화 행을 담을 테이블 DDL 과 조회 인덱스를 설계합니다 |
| Docker | 파서와 조회 API 를 고객사에 넘길 컨테이너 이미지로 묶습니다 |
| Kubernetes | 고객사 플랫폼에 올릴 Helm 차트 배포 대상입니다 |
| Keycloak | 3주차에 조회 API 를 OIDC 로 보호하는 외부 IdP 입니다 |
| GitHub Actions | 이미지 빌드와 배포를 도는 CI/CD 파이프라인입니다 |
| Pydantic | 파싱 입력과 LLM 매핑 제안의 런타임 형식 검증입니다 |
| Claude | 규칙이 못 맞춘 머리글의 표준 필드 후보를 제안하는 LLM 입니다 |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 2주차(고객사 사내망 배포)로 진행합니다.
✓ pass → 여기서 멈춰도 완결입니다. 더 키우면 3주차(Keycloak SSO 보호와 두 번째 양식 적용)로 진행합니다.
정규화된 점검 기록 한 줄입니다. 어느 셀에서 어떤 규칙으로 나왔는지와 조회 API 에서 어떤 경로로 읽히는지를 한 스키마에 담아 CSV·테이블·변환 리포트가 같은 형태를 씁니다.
{"type":"object","properties":{"src_sheet":{"type":"string"},"src_cell":{"type":"string"},"equipment_id":{"type":["string","null"]},"checked_at":{"type":["string","null"]},"std_field":{"type":["string","null"]},"value_num":{"type":["number","null"]},"value_text":{"type":["string","null"]},"unit":{"type":["string","null"]},"applied_rules":{"type":"array","items":{"type":"string","enum":["alias_map","merged_inherit","unit_strip","date_parse","enum_map","row_dropped","unmapped_header"]}},"api_path":{"type":["string","null"]},"note":{"type":["string","null"]}},"required":["src_sheet","src_cell","equipment_id","checked_at","std_field","value_num","value_text","unit","applied_rules","api_path","note"]}[
{
"input_ref": "rs_003 (회전수 '1,250 rpm')",
"output": {
"src_sheet": "2026-03 점검",
"src_cell": "C5",
"equipment_id": "EQ-101",
"checked_at": "2026-03-04",
"std_field": "rotation_rpm",
"value_num": 1250,
"value_text": null,
"unit": "rpm",
"applied_rules": [
"alias_map",
"unit_strip",
"date_parse"
],
"api_path": "/equipments/EQ-101/inspections?date=2026-03-04",
"note": null
}
},
{
"input_ref": "rs_006 (병합 셀로 비어 있는 설비번호)",
"output": {
"src_sheet": "2026-03 점검",
"src_cell": "A6",
"equipment_id": "EQ-101",
"checked_at": "2026-03-04",
"std_field": "equipment_id",
"value_num": null,
"value_text": "EQ-101",
"unit": null,
"applied_rules": [
"merged_inherit",
"alias_map"
],
"api_path": "/equipments/EQ-101/inspections?date=2026-03-04",
"note": "병합 셀이라 위 행 값을 상속했습니다"
}
},
{
"input_ref": "rs_011 (규칙표에 없는 머리글 '특이사항2')",
"output": {
"src_sheet": "2026-03 점검",
"src_cell": "F21",
"equipment_id": "EQ-104",
"checked_at": "2026-03-11",
"std_field": null,
"value_num": null,
"value_text": "베어링 교체 예정",
"unit": null,
"applied_rules": [
"unmapped_header"
],
"api_path": null,
"note": "규칙표에 없는 머리글이라 적재하지 않고 mapping.yml 에 미결로 남겼습니다"
}
}
][자동 검증] 1. Pydantic 으로 출력 형식(필드·enum)이 스키마를 통과하는지 확인합니다. 2. std_field·value_num·unit 을 더미에 심은 expected_field/expected_value 와 셀 단위로 대조해 일치 셀 수를 셉니다. 3. row_kind 가 subtotal·header 인 행이 적재 대상에서 빠졌는지 확인합니다. 4. std_field 가 null 이면 api_path 도 null 이고 mapping.yml 미결 목록에 같은 머리글이 있는지 봅니다. 5. 같은 파일을 두 번 돌려도 테이블 행 수가 늘지 않는지(멱등 적재) 확인합니다. [수동 검증] 1. 고객사 담당자 역할 2명이 미결 머리글 목록을 각자 확정해, 확정 결과가 갈리는 머리글을 세어 규칙표에 반영합니다. 2. 정규화 행의 5% 를 원본 셀 주소로 되짚어 사람이 직접 대조합니다.