user@intzzzero:~/blog$ls -la[17:31]
$cat "온톨로지와 프롬프트 엔지니어링, 개발자의 새로운 무기.md"
3877 bytes[AI]2025.07.18.
═══════════════════════════════════════════════════════════

철학 용어인 줄만 알았던 온톨로지가 AI 시대에 강력한 무기가 될 줄이야. 우리가 매일같이 하던 데이터 모델링, 클래스 설계가 바로 세상을 정의하는 온톨로지 구축 행위였다. 이제는 잘 정의된 우리 시스템의 세계를 프롬프트에 담아 AI에게 명확히 이해시켜야 예측 가능한 시스템을 만들 수 있다.

architect

본문

온톨로지(Ontology)는 원래 존재의 본질을 파고드는 철학 용어다. 하지만 IT에서는 이 단어를 좀 더 실용적인 의미로 사용한다. 바로 우리가 만들려는 시스템 속 세상의 사물, 개념, 그리고 그것들 사이의 관계를 명확하게 정의하는 약속이나 설계도를 뜻한다.

최근 '온톨로지'라는 단어를 다시 마주했다. 솔직히 예전 같았으면 '어려운 철학 용어' 정도로 치부하고 넘어갔을 거다. 존재, 실재, 범주... 당장 코드 한 줄 더 짜야 하는 개발자에게 이런 단어는 사치처럼 느껴졌으니까. 정확히는 AI가 코드를 대신 짜주기 전까지는 말이다. 이제는 Copilot, Cursor, Claude Code 같은 도구 덕분에 단순 코딩보다 설계와 구조에 대해 깊이 생각하는 시간이 늘었다. 바로 지금, 온톨로지라는 개념이 우리에게 꼭 필요한 이유다.

생각해 보자. 우리가 쇼핑몰 백엔드를 만든다고 할 때, Product, Customer, Order 같은 데이터베이스 테이블을 설계한다. Product 테이블에는 product_id, name, price 같은 컬럼을 넣고, Order 테이블은 customer_id를 외래 키로 참조하며 관계를 맺는다. 이게 바로 우리 쇼핑몰 세계의 규칙, 즉 온톨로지다.

객체지향 프로그래밍(OOP)의 classinstance 개념은 온톨로지의 '보편자(universals)'와 '개별자(particulars)'와 놀랍도록 닮아있다. User라는 class는 사용자의 보편적인 개념(속성과 메서드)을 정의한 것이고, const newUser = new User('홍길동')으로 생성된 newUser 객체는 실존하는 개별 사용자를 나타낸다. 우리는 코드를 통해 우리 시스템의 세계에 존재하는 것들이 무엇이고, 그것들이 어떤 모습이어야 하는지 끊임없이 정의하고 있었던 셈이다.

이 개념을 프롬프트 엔지니어링으로 가져오면 그림이 더 명확해진다. LLM이라는 강력한 도구가 생겼지만, 이 녀석에게 일을 시키려면 우리가 구축한 세계를 제대로 알려줘야 한다. 그냥 "알아서 잘해줘"라고 말하는 건 소용없다.

최근에 본 아티클(Stop Building AI Agents)에서는 LLM에게 모든 자율성을 부여하는 복잡한 에이전트는 디버깅도 어렵고 예측 불가능하며, 대부분의 문제는 더 간단한 워크플로우로 해결할 수 있다는 주장을 한다. 개인적으로 이 주장에 깊이 공감했다. 그리고 그 해결책의 중심에 바로 '온톨로지'가 있다고 생각한다.

ai chat

AI 에이전트가 실패하는 가장 큰 이유는 자신이 무엇을 해야 하는지, 어떤 도구를 사용해야 하는지, 그 결과가 어떤 모습이어야 하는지에 대한 명확한 이해, 즉 온톨로지가 없기 때문이다. 반면, 프롬프트 체이닝이나 라우팅 같은 명확한 워크플로우는 개발자가 시스템의 온톨로지를 완벽하게 통제한다. 각 단계에서 AI가 해야 할 일과 결과물의 형태를 명확히 정의하기 때문에 예측 가능하고 안정적인 시스템을 만들 수 있다.

예를 들어보자. AI에게 사용자 데이터를 요청할 때, 단순히 "사용자 데이터 좀 줘"라고 말하는 것과 아래처럼 구체적인 스키마를 함께 제공하는 것은 하늘과 땅 차이다.

"다음 JSON 스키마에 맞춰서 사용자 데이터를 반환해줘. User 객체는 id, name, email 프로퍼티를 가져야 하고, isActive 필드는 반드시 boolean이어야 해. { \"type\": \"object\", \"properties\": { \"id\": { \"type\": \"number\" }, \"name\": { \"type\": \"string\" }, \"email\": { \"type\": \"string\", \"format\": \"email\" }, \"isActive\": { \"type\": \"boolean\" } }, \"required\": [\"id\", \"name\", \"email\", \"isActive\"] }"

두 번째 프롬프트는 단순히 형식을 지정한 게 아니다. '우리 시스템에서 사용자는 이런 모습으로 존재해야 한다'는 명확한 온톨로지를 LLM에게 전달한 것이다. 이렇게 하면 LLM은 우리가 정의한 세계 안에서 안정적으로 결과를 생성하고, 환각(hallucination)을 일으킬 확률도 극적으로 줄어든다.

외부 시스템과 연동하기 위한 API 명세를 작성하는 것도 마찬가지다. RESTful API의 엔드포인트, 요청(Request)과 응답(Response)의 구조를 정의하는 것은 우리 시스템의 온톨로지를 외부에 공개하고 상호작용의 규칙을 정하는 행위다. 잘 설계된 API 문서는 그 자체로 훌륭한 온톨로지 설명서인 셈이다.

결국 프롬프트 엔지니어링이란, 단순히 말을 예쁘게 다듬는 글짓기 기술이 아니다. 내가 설계한 데이터와 시스템의 세계관(온톨로지)을 AI에게 정확하게 전달하는 능력이다. 현실 세계의 문제를 디지털 세계의 존재로 번역하는 '디지털 시대의 온톨로지스트', 그게 바로 우리 개발자의 역할이 아닐까. 이제는 그 존재를 AI에게 설명하는 능력까지 갖춰야 할 때다. 유행처럼 번지는 AI 에이전트에 휩쓸리기 전에, 내가 만들고 있는 시스템의 온톨로지부터 명확히 정의하고 있는지 돌아볼 일이다.