화해글로벌
경력근거: 자사 서비스의 주요 도메인을 RDB로 데이터 모델링해보신 분
공고가 자격요건에 '자사 서비스의 주요 도메인을 RDB로 데이터 모델링해보신 분' 을 적었고, 주요업무에는 '제품의 사용성을 저하시키는 성능 이슈나 잠재적인 문제를 스스로 찾아' 를 두었습니다. 성분 정보를 다루는 서비스에서 표기가 안 붙은 줄은 검색 결과에서 제품이 통째로 빠지는 잠재 문제이고, 이 포트폴리오는 그 자리를 등록 시점에 잡습니다. 'AI 기반 개발 워크플로우에서 도메인 지식을 효율적으로 축적하고 하네스 엔지니어링' 도 별칭 목록이라는 실물로 축적되는 자리를 가리킵니다. 이 포트폴리오가 보여주는 것은 표준 성분표를 데이터로 두고 표기와 맞춰 같은 성분인지 가리는 기술이며, 화장품 성분의 안전성 판단이나 배합 한도 해석은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
'자사 서비스의 주요 도메인을 RDB로 데이터 모델링해보신 분'. 성분은 그 도메인의 뿌리입니다.
'AI 기반 개발 워크플로우에서 도메인 지식을 효율적으로 축적하고 하네스 엔지니어링'. 별칭 사전이 그 축적의 실물입니다.
'제품의 사용성을 저하시키는 성능 이슈나 잠재적인 문제를 스스로 찾아'. 검색에서 빠지는 제품이 그 잠재 문제입니다.
제품에 붙어 온 전성분 표기가 표준 성분 어느 줄과 같은 것인지 가려 화면에 사유별로 묶어 보여줍니다. 표준 성분표에 대표 이름과 국제 표기, 별칭 목록, 기능, 알레르기 여부를 두고 들어온 표기와 맞춰, 대표 이름과 같으면 그대로 붙이고 별칭과 같으면 별칭 매칭으로 붙입니다. 그렇지 않으면 미등록으로 남기고, 한 제품에 같은 성분이 두 번 올라온 줄과 원천마다 순번이 어긋나는 줄, 순번이 안 읽힌 줄을 각각 다른 사유로 가릅니다. 전성분 목록은 실 제조사 자료 대신 지원자가 공개된 화장품 성분 표기 규격으로 만든 것을 가정합니다.
제조사가 보내온 전성분 목록을 제품에 붙이는 데이터 담당자입니다. 등록 화면에서 줄을 훑어보며 표기가 낯선 것을 골라내다가, 안 붙은 줄 때문에 그 제품이 성분 검색에서 통째로 빠진 것을 문의로 알게 되던 자리에 있습니다.
표준 성분마다 대표 이름과 별칭을 목록으로 두고 들어온 표기와 맞추면, 붙는 줄과 안 붙는 줄이 등록 시점에 갈리고 안 붙는 이유가 근거로 남는다는 가정입니다. 표준 성분표에 없는 이름은 미등록으로 빠지므로 아직 등록하지 않은 성분이 그대로 드러납니다.
전성분 목록과 표준 성분표를 넣으면 줄마다 매칭·미매칭을 판정하고, 미매칭이면 미등록·순번 없음·제품 내 중복·원천 충돌 중 어느 쪽인지 붙여 사유별로 묶어 보여주는 화면이 도는 완결품입니다. 줄을 펼치면 걸린 표준 성분 행과 근거 값이 나옵니다.
공개 규격으로 만든 전성분 3000줄에 손으로 붙인 정답 매칭과 대조해 일치율을 내고, 안 붙은 줄 때문에 성분 검색과 알레르기 필터에서 빠지는 제품 수를 함께 냅니다.
미등록 이름을 글자 겹침으로 표준 성분과 견줘 별칭 후보를 뽑고, 사람이 승인하면 별칭 목록에 넣는 흐름을 만드는 단계입니다.
데이터 담당자가 전성분 목록과 표준 성분표를 넣으면 줄마다 매칭 여부와 미매칭 사유가 사유별로 묶여 화면에 뜨는 과정을 시연합니다.
공개 규격으로 만든 전성분 3000줄과 손으로 붙인 정답 매칭을 대조해 일치율을 냅니다. 단, 정답과 목록을 모두 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
순번이 비었거나 표기가 흔들리거나 표준 성분표에 없는 이름이 섞인 입력 30종을 넣어도 화면이 안 깨지고 사유별로 빠지는 비율을 보여줍니다.
별칭을 추가했을 때 안 붙던 줄이 몇 줄 붙는지, 그래서 검색에 다시 걸리는 제품이 몇 개 느는지 회귀로 확인합니다.
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 작성자가 만든 목록의 자가채점이며, 실제 검색 누락률은 화해글로벌의 실 제품 데이터와 실 검색 기록이 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아니므로, 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 붙였으니 잘 맞는 게 당연하므로, 순번 결측과 원천 충돌, 표에 없는 신규 성분을 일부러 섞어 쉬운 줄만 푸는 함정을 피합니다.
이 포트폴리오가 보여주는 것은 표준 성분표를 데이터로 두고 표기와 맞춰 같은 성분인지 가리는 기술이며, 화장품 성분의 안전성 판단이나 배합 한도 해석은 주장하지 않습니다. 판정은 이름 대조가 하고 LLM 은 담당자가 읽을 사유 문장만 맡습니다.
글자가 비슷하다고 같은 성분이라는 보장이 없어서, 미등록 이름은 자동으로 붙이지 않고 사람이 승인하는 별칭 후보로만 올립니다.
실제 도입 때는 신규 원료가 계속 들어오므로, 표준 성분표와 별칭 목록을 이어서 채우는 일을 운영 과제로 둡니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
제품에 붙어 온 전성분 목록을 줄 단위로 편 것입니다. 줄 번호와 제품 번호, 브랜드, 성분 표기, 순번, 원천, 받은 날을 한 행에 둡니다. 표기는 원천에 따라 '나이아신아마이드'와 '나이아신 아마이드', 수입 라벨의 '精製水'처럼 흔들립니다. 순번이 안 읽힌 행, 한 제품에 같은 성분이 두 번 올라온 행, 원천마다 순번이 다른 행, 표준 성분표에 없는 신규 원료를 일부러 섞어 두어 사유가 갈리게 둡니다.
| line_no | product_no | brand_raw | name_raw | position | source_raw | received_at |
|---|---|---|---|---|---|---|
| IL-001 | PD-101 | 라라랩 | 정제수 | 1 | 제조사 제출 | 2026-08-18 |
| IL-002 | PD-101 | 라라랩 | 부틸렌글라이콜 | 2 | 제조사 제출 | 2026-08-18 |
| IL-003 | PD-101 | 라라랩 | 나이아신아마이드 | 3 | 제조사 제출 | 2026-08-18 |
| IL-004 | PD-102 | 라라랩 | 精製水 | 1 | 수입 라벨 | 2026-08-19 |
| IL-005 | PD-102 | 라라랩 | 나이아신 아마이드 | 2 | 수입 라벨 | 2026-08-19 |
아래 JSON Schema 를 그대로 만족하는 전성분 줄을 3000개 더 만들어 주세요. 제품을 이백 개쯤 두고 제품마다 성분을 열에서 서른 줄 붙이며, 순번은 1부터 늘립니다. 표기는 몇 줄에서 띄어쓰기를 넣거나 한자·영문으로 적어 흔들리게 두고, 원천을 제조사 제출·수입 라벨·라벨 판독으로 섞습니다. 2할은 순번을 비우고, 몇 제품은 같은 성분을 두 줄 넣으며 원천마다 순번을 다르게 두세요. 표준 성분표에 없는 이름도 몇 줄 넣습니다. 매칭 결과는 넣지 마세요.
{"type": "object", "properties": {"line_no": {"type": "string"}, "product_no": {"type": "string"}, "brand_raw": {"type": "string"}, "name_raw": {"type": "string"}, "position": {"type": ["integer", "null"]}, "source_raw": {"type": "string"}, "received_at": {"type": "string"}}, "required": ["line_no", "product_no", "brand_raw", "name_raw", "position", "source_raw", "received_at"]}{"type": "object", "properties": {"line_no": {"type": "string"}, "product_no": {"type": "string"}, "brand_raw": {"type": "string"}, "name_raw": {"type": "string"}, "position": {"type": ["integer", "null"]}, "source_raw": {"type": "string"}, "received_at": {"type": "string"}}, "required": ["line_no", "product_no", "brand_raw", "name_raw", "position", "source_raw", "received_at"]}우리가 쓰는 표준 성분표입니다. 성분 번호와 대표 이름, 국제 표기, 별칭 목록, 기능, 알레르기 유발 여부, 담당을 한 행에 둡니다. 별칭이 있어야 흔들린 표기가 같은 성분으로 붙고, 알레르기 여부가 있어야 회피 필터가 그 줄을 잡습니다. 전성분 목록에 나오는 이름 중 일부는 일부러 넣지 않아 미등록이 도출되게 둡니다.
| std_no | std_name | inci_name | aliases | function_raw | allergen_flag | owner_name |
|---|---|---|---|---|---|---|
| ST-01 | 정제수 | Water | ["精製水","물","water"] | 용제 | false | 성분데이터 |
| ST-02 | 부틸렌글라이콜 | Butylene Glycol | ["부틸렌 글라이콜","BG"] | 보습 | false | 성분데이터 |
| ST-03 | 나이아신아마이드 | Niacinamide | ["나이아신 아마이드","니아신아마이드","비타민B3"] | 미백 | false | 성분데이터 |
| ST-04 | 리모넨 | Limonene | ["리모넨(향료)","d-리모넨"] | 착향 | true | 성분데이터 |
| ST-05 | 세라마이드엔피 | Ceramide NP | ["세라마이드 NP","세라마이드3"] | 보습 | false | 성분데이터 |
아래 JSON Schema 를 그대로 만족하는 표준 성분 행을 200개 더 만들어 주세요. 성분마다 대표 이름과 국제 표기를 두고 별칭을 0개에서 3개까지 붙입니다. 기능을 보습·미백·착향·산화방지·각질제거 등으로 섞고 착향 계열 몇 개는 알레르기 유발을 참으로 둡니다. 전성분 목록에 나오는 이름 중 일부는 일부러 넣지 마세요 — 미등록이 있어야 성분표의 구멍이 드러납니다. 매칭 결과는 넣지 마세요.
{"type": "object", "properties": {"std_no": {"type": "string"}, "std_name": {"type": "string"}, "inci_name": {"type": "string"}, "aliases": {"type": "array", "items": {"type": "string"}}, "function_raw": {"type": "string"}, "allergen_flag": {"type": "boolean"}, "owner_name": {"type": "string"}}, "required": ["std_no", "std_name", "inci_name", "aliases", "function_raw", "allergen_flag", "owner_name"]}{"type": "object", "properties": {"std_no": {"type": "string"}, "std_name": {"type": "string"}, "inci_name": {"type": "string"}, "aliases": {"type": "array", "items": {"type": "string"}}, "function_raw": {"type": "string"}, "allergen_flag": {"type": "boolean"}, "owner_name": {"type": "string"}}, "required": ["std_no", "std_name", "inci_name", "aliases", "function_raw", "allergen_flag", "owner_name"]}| Python | 전성분 표기를 접어 표준 성분표와 맞추고 순번·중복을 세는 매칭기를 맡습니다. |
| PostgreSQL | 표준 성분표와 별칭 목록, 판정 결과를 담습니다. |
| FastAPI | 등록 단계에서 판정을 돌려 화면에 넘기는 API 를 맡습니다. |
| React | 사유별로 묶은 성분 줄과 근거 값을 보여주는 화면을 맡습니다. |
✗ fail → 미등록이 절반을 넘으면 성분표를 먼저 채웁니다.
✓ pass → 2주차 손해 계산으로 넘어갑니다.
✗ fail → 어긋난 표기를 모아 접는 규칙을 다시 봅니다.
✓ pass → 3주차 별칭 후보로 넘어갑니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
match_code사유 라벨 · line_no + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
matched표준 이름 일치alias_matched별칭 일치name_unmapped미등록 성분position_missing순번 없음duplicated_in_product제품 내 중복source_conflict원천 충돌전성분 한 줄과 표준 성분이 만나는 자리마다 같은 성분인지 정합니다. 안 붙으면 미등록·순번 없음·제품 내 중복·원천 충돌 중 어느 쪽인지 싣습니다. 리포트는 이 사유 코드로 묶습니다.
{"type": "object", "properties": {"verdict_no": {"type": "string"}, "line_no": {"type": "string"}, "product_no": {"type": "string"}, "name_raw": {"type": "string"}, "std_no": {"type": ["string", "null"]}, "match_code": {"type": "string", "enum": ["matched", "alias_matched", "name_unmapped", "position_missing", "duplicated_in_product", "source_conflict"]}, "std_name": {"type": ["string", "null"]}, "allergen_flag": {"type": ["boolean", "null"]}, "owner_name": {"type": ["string", "null"]}, "evidence": {"type": "array", "items": {"type": "object", "properties": {"field": {"type": "string"}, "agg": {"type": ["string", "null"]}, "value": {}}, "required": ["field", "value"]}}, "summary": {"type": "string"}, "hedge": {"type": ["string", "null"]}}, "required": ["verdict_no", "line_no", "product_no", "name_raw", "match_code", "evidence", "summary"]}[
{
"verdict_no": "MV-001",
"line_no": "IL-001",
"product_no": "PD-101",
"name_raw": "정제수",
"std_no": "ST-01",
"match_code": "matched",
"std_name": "정제수",
"allergen_flag": false,
"owner_name": "성분데이터",
"evidence": [
{
"field": "name_raw",
"agg": null,
"value": "정제수"
},
{
"field": "std_name",
"agg": null,
"value": "정제수"
}
],
"summary": "표준 이름과 그대로 같아 성분 검색에 바로 걸립니다.",
"hedge": null
},
{
"verdict_no": "MV-002",
"line_no": "IL-004",
"product_no": "PD-102",
"name_raw": "精製水",
"std_no": "ST-01",
"match_code": "alias_matched",
"std_name": "정제수",
"allergen_flag": false,
"owner_name": "성분데이터",
"evidence": [
{
"field": "name_raw",
"agg": null,
"value": "精製水"
},
{
"field": "aliases",
"agg": null,
"value": [
"精製水",
"물",
"water"
]
}
],
"summary": "수입 라벨의 한자 표기라 별칭으로 붙였고 검색에는 같은 성분으로 걸립니다.",
"hedge": null
},
{
"verdict_no": "MV-003",
"line_no": "IL-005",
"product_no": "PD-102",
"name_raw": "나이아신 아마이드",
"std_no": "ST-03",
"match_code": "matched",
"std_name": "나이아신아마이드",
"allergen_flag": false,
"owner_name": "성분데이터",
"evidence": [
{
"field": "name_raw",
"agg": null,
"value": "나이아신 아마이드"
},
{
"field": "std_name",
"agg": null,
"value": "나이아신아마이드"
}
],
"summary": "띄어쓰기만 다른 표기라 접으면 대표 이름과 같습니다. 안 접으면 미백 성분 검색에서 이 제품이 빠집니다.",
"hedge": null
},
{
"verdict_no": "MV-004",
"line_no": "IL-006",
"product_no": "PD-102",
"name_raw": "리모넨",
"std_no": "ST-04",
"match_code": "matched",
"std_name": "리모넨",
"allergen_flag": true,
"owner_name": "성분데이터",
"evidence": [
{
"field": "allergen_flag",
"agg": null,
"value": true
},
{
"field": "function_raw",
"agg": null,
"value": "착향"
}
],
"summary": "알레르기 유발 착향 성분이라 표기가 어긋나면 회피 필터가 새 나갑니다.",
"hedge": null
},
{
"verdict_no": "MV-005",
"line_no": "IL-008",
"product_no": "PD-103",
"name_raw": "세라마이드 NP",
"std_no": "ST-05",
"match_code": "duplicated_in_product",
"std_name": "세라마이드엔피",
"allergen_flag": false,
"owner_name": "성분데이터",
"evidence": [
{
"field": "std_no",
"agg": "count",
"value": 2
},
{
"field": "position",
"agg": null,
"value": 5
}
],
"summary": "같은 제품에 같은 성분이 두 번 올라와 순번이 서로 다릅니다.",
"hedge": null
},
{
"verdict_no": "MV-006",
"line_no": "IL-007",
"product_no": "PD-103",
"name_raw": "세라마이드엔피",
"std_no": "ST-05",
"match_code": "source_conflict",
"std_name": "세라마이드엔피",
"allergen_flag": false,
"owner_name": "성분데이터",
"evidence": [
{
"field": "source_raw",
"agg": null,
"value": "라벨 판독"
},
{
"field": "position",
"agg": null,
"value": 2
}
],
"summary": "라벨 판독은 2번, 제조사 제출은 5번으로 적어 함량 순서가 어긋납니다.",
"hedge": "판독 오류일 수 있어 원본 라벨 이미지를 함께 봅니다."
},
{
"verdict_no": "MV-007",
"line_no": "IL-009",
"product_no": "PD-104",
"name_raw": "글리세린",
"std_no": "ST-06",
"match_code": "position_missing",
"std_name": "글리세린",
"allergen_flag": false,
"owner_name": "성분데이터",
"evidence": [
{
"field": "position",
"agg": null,
"value": null
},
{
"field": "source_raw",
"agg": null,
"value": "라벨 판독"
}
],
"summary": "순번이 안 읽혀 함량 순서를 세울 수 없습니다.",
"hedge": null
},
{
"verdict_no": "MV-008",
"line_no": "IL-011",
"product_no": "PD-105",
"name_raw": "바쿠치올",
"std_no": null,
"match_code": "name_unmapped",
"std_name": null,
"allergen_flag": null,
"owner_name": null,
"evidence": [
{
"field": "name_raw",
"agg": null,
"value": "바쿠치올"
},
{
"field": "std_no",
"agg": "count",
"value": 12
}
],
"summary": "표준 성분 열두 행에 없는 이름이라 검색에도 필터에도 걸리지 않습니다.",
"hedge": null
}
][자동 검증 · 코드가 판정합니다] 1. match_code 가 enum 여섯 값 중 하나인지 확인합니다. 2. verdict_no·line_no·product_no·name_raw·match_code·evidence·summary 가 모두 채워졌는지 확인합니다. 3. line_no 가 전성분 목록에 실재하는 번호인지 대조합니다. 4. std_no 가 있으면 표준 성분표에 실재하는 행 번호인지 대조합니다. 5. std_no 가 있으면 std_name·allergen_flag·owner_name 이 그 성분 행의 값과 같은지 대조합니다. 6. 아래 순서로 판정합니다 — 먼저 9·10·11 을 보고 걸리지 않을 때만 6·7·8 의 이름 매칭 결과를 코드로 씁니다. name_raw 를 띄어쓰기와 대소문자를 접어 std_name 과 견주고, 같으면 match_code 가 matched 인지 확인합니다. 7. 대표 이름과 다르고 aliases 안의 값과 같으면 match_code 가 alias_matched 인지 확인합니다. 8. 대표 이름과도 별칭과도 맞지 않으면 match_code 가 name_unmapped 인지 확인합니다. 9. position 이 비어 있으면 match_code 가 position_missing 인지 확인합니다. 10. 같은 product_no 안에 같은 std_no 가 둘이면 뒤 줄의 match_code 가 duplicated_in_product 인지 다시 셉니다. 11. 같은 product_no·std_no 인데 source_raw 가 다르고 position 이 서로 다르면 match_code 가 source_conflict 인지 확인합니다. 12. allergen_flag 가 참인 성분은 match_code 가 matched 나 alias_matched 인지 반드시 확인합니다 — 안 붙으면 회피 필터가 새 나갑니다. 13. evidence 의 field 이름이 전성분 목록과 표준 성분표에 실재하는 컬럼인지 대조합니다. 14. agg 가 없는 evidence 의 value 가 그 컬럼에 수록된 행 값과 같은지 확인합니다. 15. summary 가 존댓말로 끝나는지 확인합니다. [평가 러너 · 2주차] 공개 규격으로 만든 전성분 3000줄에 손으로 붙인 정답 매칭과 대조해 일치율과 사유 코드별 분포를 내고, 안 붙은 줄 때문에 성분 검색과 알레르기 필터에서 빠지는 제품 수를 함께 냅니다. [수동 검증 · 사람이 봅니다] 1. 미등록 이름을 어느 표준 성분의 별칭으로 넣을지 성분 담당이 승인합니다. 2. 원천끼리 순번이 어긋난 제품은 원본 라벨을 보고 어느 쪽을 쓸지 정합니다.