1. 시작
인스타 릴스를 넘기다 우연히 유니티 관련 흥미로운 책 광고를 보게 됐다.
그래서 앞으로 이 책을 공부하면서 그날그날 배운 내용들을 하나씩 정리해보려고 한다!
LEVELIT 유니티와 OpenAI API로 만드는 인공지능 NPC | 박재환 - 교보문고
LEVELIT 유니티와 OpenAI API로 만드는 인공지능 NPC | AI와 게임이 만나면 NPC가 숨을 쉰다고?유니티에 ChatGPT, Whisper, DALL-E, TTS 같은 최신 생성형 AI를 자연스럽게 얹어, 말을 걸면 반응하고 상황에 맞게
product.kyobobook.co.kr
예전부터 외부 API를 연동해서
내 프로젝트 안에서 실제로 눈에 보이게 동작하는 것들을 만드는 걸 좋아했는데
이번에 Unity를 제대로 배우는 김에
OpenAI API를 붙여서 나만의 인공지능 NPC를 만들어보는 것도 정말 좋은 경험이 될 것 같았다.
2. API 연결 & 동작 원리 확인
1. HTTP POST 요청을 위한 using문
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;
이 중에서 HTTP 통신과 직접적으로 관련 있는 것은 아래다.
[UnityEngine.Networking]
- UnityWebRequest 클래스를 사용하기 위해 필요
- Unity에서 GET / POST / PUT / DELETE 같은 HTTP 요청을 보낼 때 사용
UnityWebRequest request = new UnityWebRequest(apiUrl, "POST");
[System.Text]
- 문자열을 byte 배열로 변환하기 위해 사용
- HTTP 요청의 body는 결국 byte 데이터이기 때문에 JSON 문자열을 그대로 보낼 수 없고, 인코딩 과정이 필요하다.
byte[] postData = Encoding.UTF8.GetBytes(jsonData);
[System.Text.RegularExpressions]
- 서버에서 받은 JSON 응답 중"content" 부분만 추출하기 위해 사용
2. OpenAI API 기본 설정
private const string apiUrl = "https://api.openai.com/v1/chat/completions";
private const string apiKey = "";
public string prompt = "You are a helpful assistant.";
public string content = "Write a haiku about recursion in programming.";
- apiUrl : OpenAI의 Chat Completions API 엔드포인트
- apiKey : 요청을 보낸 사용자를 인증하기 위한 키
- prompt : AI의 역할
- content : 사용자가 모델에 전달할 구체적인 요청 내용
3. HTTP 요청
OpenAI는 JSON 형태의 요청만 받기 때문에
코드 내부에서 JSON 형태로 변환해주는 부분이 필요하다.
string jsonData = @"{
""model"": ""gpt-4o"",
""messages"": [
{
""role"": ""system"",
""content"": """ + prompt + @"""
},
{
""role"": ""user"",
""content"": """ + content + @"""
}
]
}";
byte[] postData = Encoding.UTF8.GetBytes(jsonData);
그리고 API 호출은 네트워크 통신이므로 비동기 방식으로 호출해야한다.
따라서 유니티에선 비동기 처리를 위해 코루틴을 활용해준다.
using (UnityWebRequest request = new UnityWebRequest(apiUrl, "POST"))
{
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("Authorization", "Bearer " + apiKey);
request.uploadHandler = new UploadHandlerRaw(postData);
request.downloadHandler = new DownloadHandlerBuffer();
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
{
Debug.Log("Error : " + request.error);
}
else
{
string responsText = request.downloadHandler.text;
Debug.Log("Response : " + responsText);
string pattern = @"""content"":\s*""(.*?)""";
Match match = Regex.Match(responsText, pattern);
if (match.Success)
{
string assistantMessage = match.Groups[1].Value;
Debug.Log("Assistant Message : " + assistantMessage);
}
else
{
Debug.LogWarning("No valid response found.");
}
}
}
POST 방식으로 호출을 하고
using 블록을 사용하여 요청 종료시 자동 정리되도록 해주었다.
[헤더 설정]
서버가 받은 데이터가 JSON임을 알리기 위하여 Content-Type에 json으로 넣어주었고
API Key를 가진 사용자가 보냄을 알리기 위해 Bearer 키워드를 사용해주었다.. (이게 규정?? 인가요?ㅠ 잘은 모르겠다)
[핸들러]
UploadHandlerRaw : POST로 보낼 데이터
DownloadHandlerBuffer : 서버 응답을 문자열로 받기 위해 사용

OpenAI에게 받은 Json의 형식은 위와 같은데
이 예제에서는 content 부분만 다시 출력하도록 assistant Message를 설정해주었다.
pattern에 content부분만 match해서 출력되도록 해주었으니
두번째 로그를 살펴보면
JSON 파일 내부에 "content"로 작성된 부분만 출력된 것을 확인할 수 있었다.
4. 왜 match를 사용한걸까?
DownloadHandlerBuffer를 통해 전달받은 응답은
OpenAI 서버가 반환한 JSON 전체 문자열이기 때문에 원하는 데이터(content)를 그대로 사용할 수 없다.
따라서 JSON 내부에서 실제 응답 텍스트가 담긴 "content" 필드만 추출하기 위해 match라는 정규식을 이용해준 것이다.
그러니까 즉, request.downloadHandler.text; 로 뽑은 건 그냥 JSON 전체 파일이고
이 중에 content나 뭐 service_tier 등에 해당하는 값을 뽑고싶다면 Match를 사용해주면 된다.
'Unity > OpenAI' 카테고리의 다른 글
| [OpenAI API] 음성 스펙트럼을 분석하여 발음에 따른 애니메이션 만들기 (1) | 2026.03.15 |
|---|---|
| [OpenAI API] 내 질문에 음성으로 대답하는 NPC 만들기 (0) | 2026.03.10 |
| [OpenAI API] 음성 인식 기반 AI NPC 구현하기 (0) | 2026.02.26 |
| [OpenAI API] Whisper API 사용해보기 (0) | 2026.02.24 |
| [OpenAI API] AI와 대화 시스템 만들기 (0) | 2026.02.12 |