작성일: 2026년 1월 27일
기술 스택: iOS Shortcuts, OpenAI API (gpt-4o-mini), Notion API
1. Project Background (개발 배경)
평소 노션(Notion)에 일정과 프로젝트를 꼼꼼히 기록하는 편입니다. 하지만 기록하는 과정 자체가 꽤 번거로울 때가 많았습니다. 예를 들어 길을 걷다 "다음 주 화요일 저녁 7시, 강남역에서 하준이와 회의"라는 일정이 생각나면, 폰을 켜고 앱을 실행해 페이지를 찾고, 제목과 날짜, 태그를 일일이 지정해야 합니다. 이 과정에 약 20초 정도가 소요되는데, 생각의 흐름이 끊기는 게 아쉬웠습니다. "그냥 말로 대충 던져주면 알아서 정리해 줄 순 없을까?" 이 단순한 필요에서 시작해, 단축어와 API를 활용한 자동화 작업을 시작하게 되었습니다.


2. 설계
단축어와 GPT, 그리고 노션 구조는 간단합니다. [텍스트 입력] -> [GPT-4o (데이터 정제)] -> [Notion API (데이터 입력)] 아이폰의 '단축어(Shortcuts)' 앱은 입력을 전달하는 역할을 하고, 중간에서 GPT가 불분명한 자연어를 노션이 인식할 수 있는 깔끔한 JSON 데이터(제목, 날짜, 태그 등)로 변환해 주는 방식입니다.
준비물 (3가지)
시작하기 전에 딱 3개의 '열쇠'가 필요합니다.
- OpenAI API Key: 여기서 발급 (AI에게 일을 시키기 위해 필요합니다.)
- Notion API Key: 여기서 발급 (새 통합 만들기 -> 시크릿 키 복사)
- Notion 데이터베이스 ID:
- 데이터베이스 페이지를 브라우저로 엽니다.
- 주소창 링크: https://notion.so/아이디/이_부분이_ID입니다?v=...
- 주의: 링크된 뷰(Linked View)가 아니라 원본 데이터베이스의 ID여야 합니다. (제목 옆 ↗ 눌러서 이동한 페이지의 ID)

- Interface (Client): 접근성이 가장 뛰어난 iOS/macOS의 기본 기능인 '단축어(Shortcuts)'를 활용해 단축키로 즉시 실행하도록 구성했습니다.
- Intelligence (Brain):
gpt-4o-mini모델을 사용하여 자연어 입력을 분석하고, 노션 API가 요구하는 JSON 스키마로 변환하는 역할을 수행했습니다. - Storage (Database): Notion API를 통해 실제 데이터베이스에 레코드를 생성(Create Page)했습니다.
[Prompt Engineering 전략]
AI가 환각(Hallucination)을 일으키지 않고 정확한 규격을 지키도록 시스템 프롬프트를 엄격하게 제어했습니다.
- Strict Constraint: 우선순위나 태그는 반드시 지정된 리스트(
['우선순위 1', '우선순위 2'...]) 내에서만 선택하도록 강제했습니다. - Context Awareness: "집중해서 해야 함" 같은 문맥을 파악해 특정 아이콘(
집중🔥)을 태깅하도록 지시했습니다.
{
"model": "gpt-4o-mini",
"messages": [
{
"role": "system",
"content": "You are a specialized Notion Task Manager. Analyze input to extract: Title, Date, Category, Priority, Focus, and People.\n\n1. **Category**: Choose strictly from [만남, 할일, 공부, 운동, 미팅 기록, ...]. Default '일반'.\n2. **Priority**: Choose strictly from ['우선순위 1', '우선순위 2', '추후 Task', '기록']. Default '우선순위 2'.\n3. **Focus**: Choose strictly from ['Task', '집중🔥']. Default 'Task'.\n4. **People**: Extract names. IMPORTANT: Return as a STRINGIFIED JSON array (string), e.g., \"[{\\\"name\\\": \\\"Kim\\\"}]\". If none, return \"[]\".\n5. **Date**: Return ISO 8601 format with +09:00. If no date is mentioned, use the Current Date.\n\nOutput strictly valid JSON:\n{\n \"title\": \"...\",\n \"date\": \"YYYY-MM-DDTHH:mm:00+09:00\",\n \"category\": \"...\",\n \"priority\": \"...\",\n \"focus\": \"...\",\n \"people_data\": \"...\"\n}"
},
{
"role": "user",
"content": "Current Date: 현재 날짜\nInput: 입력 요청"
}
]
}
3. Key Troubleshooting (핵심 문제 해결)
API 통신 과정에서 발생한 5가지의 주요 이슈를 해결하며 시스템 안정성을 확보했습니다.
① API Payload 전송 형식 오류 (Text vs File)
gpt-4o-mini에 보낼 JSON Body를 작성하는 과정에서 단축어 앱이 텍스트를 URL로 오인하여 요청이 실패했습니다. 이를 해결하기 위해 JSON 데이터를 텍스트 변수에 담은 뒤, API 요청 블록의 입력 타입을 '파일'로 설정하여 텍스트를 파일 스트림처럼 전송하는 트릭을 사용하여 우회했습니다.
② 데이터베이스 식별 오류 (Linked vs Source DB)
API에 올바른 DB ID를 입력했음에도 404 object_not_found 에러가 발생했습니다. 확인 결과, 대시보드에 있는 '링크된 데이터베이스(Linked Database)'의 ID를 사용한 것이 원인이었습니다. API는 원본 소스(Source) DB의 ID만 인식하므로, 원본 페이지로 이동하여 URL에 포함된 고유 32자리 ID를 추출해 적용했습니다.
③ 봇 권한 스코프 설정 (Authorization)
올바른 ID를 사용했음에도 접근 권한 에러가 지속되었습니다. Notion API는 통합(Integration)을 생성하는 것만으로는 권한이 부여되지 않으며, 타겟 데이터베이스 페이지별로 해당 봇을 '초대(Connection)'해야 함을 확인하고 수동 연결을 수행했습니다.
④ 시차(Timezone) 문제 해결 (UTC vs KST)
"저녁 7시"로 입력한 일정이 노션 캘린더에는 "새벽 4시"로 등록되는 문제가 발생했습니다. Notion 서버는 UTC(협정 세계시)를 기준시로 사용하기 때문이었습니다. 이를 해결하기 위해 시스템 프롬프트 단계에서 ISO 8601 포맷 생성 시 한국 표준시 오프셋(+09:00)을 강제로 붙이도록 지시하여(YYYY-MM-DDTHH:mm:00+09:00) 로컬 타임존 정합성을 맞췄습니다.
⑤ JSON 형 변환 및 유효성 검사 (Type Mismatch)
GPT가 반환한 태그 리스트(String)를 노션 API의 multi_select 속성(Array Object)에 매핑하는 과정에서 validation_error가 발생했습니다. 단축어 앱 내에서 문자열로 된 JSON 응답을 '딕셔너리'로 파싱(Parsing)하는 전처리 과정을 추가하여, 노션이 인식할 수 있는 객체 형태로 데이터를 변환해 전송했습니다.
4. Result & Insight (결과 및 회고)
자동화된 일정 등록
이제 맥북이나 아이폰에서 단축어를 실행하고 "다음 주 금요일 저녁 8시 친구들과 파티, 중요함"이라고 입력하면, 자동으로 노션 캘린더에 다음과 같이 등록됩니다.
- 제목: 파티
- 날짜: 2026-02-xx 20:00
- 태그: 만남, 우선순위 1 (키워드 기반 자동 분류)
날짜를 계산하거나 태그를 일일이 누를 필요 없이, 문장 입력만으로 정리가 끝납니다.
핵심 코드 (프롬프트 및 JSON) 구현에 필요한 핵심 설정은 다음과 같습니다.
[GPT 시스템 프롬프트] GPT가 노션 API 규격에 맞는 JSON을 출력하도록 설정합니다.
{
"model": "gpt-4o-mini",
"messages": [
{
"role": "system",
"content": "You are a Notion Task Manager. Analyze input strictly.\n1. Category: Choose from [만남, 할일, 공부, 운동]. Default '일반'.\n2. Priority: Choose from ['우선순위 1', '우선순위 2', '추후 Task'].\n3. People: Return as STRINGIFIED JSON array (e.g., \"[{\\\"name\\\":\\\"Kim\\\"}]\").\n4. Date: Return ISO 8601 format with +09:00 (KST).\n\nOutput strictly valid JSON:\n{\"title\": \"...\", \"date\": \"...\", \"category\": \"...\", \"priority\": \"...\", \"people_data\": \"...\"}"
},
{ "role": "user", "content": "Current Date: [현재날짜]\nInput: [입력값]" }
]
}
[노션 API 요청 데이터] GPT가 반환한 값을 변수로 받아 전송합니다.
{
"parent": { "database_id": "데이터베이스_ID" },
"properties": {
"이름": { "title": [ { "text": { "content": "[제목_변수]" } } ] },
"분류": { "select": { "name": "[분류_변수]" } },
"우선순위": { "select": { "name": "[우선순위_변수]" } },
"사람": { "multi_select": [사람_리스트_변수] },
"날짜": { "date": { "start": "[날짜_변수]" } }
}
}
[정량적 성과]
- 데이터 정제: AI가 문맥을 파악해 자동으로 분류와 태그를 지정해 줌으로써 데이터 관리 비용을 최소화했습니다.
[Insight]
단순한 API 연동 프로젝트처럼 보였으나, 실제로는 '자연어(비정형 데이터)를 시스템(정형 데이터)에 맞게 변환하는 파이프라인'을 구축하는 과정이었습니다. 특히 LLM을 활용한 개발에서는 프롬프트를 통해 데이터 타입을 강제하는 것(Structured Output)이 시스템 안정성의 핵심임을 배웠습니다. 매번 반복되는 작은 불편함을 기술로 직접 해결했다는 점에서 의미가 있었습니다. 이제는 일정 입력에 대한 부담이 사라졌고, 시스템이 잘 작동하는 것을 보는 소소한 즐거움도 생겼습니다.
'Chlidevknowledge' 카테고리의 다른 글
| 맥os 환경에서 aws ec2 ssh 서버에 로컬 파일 전송하기 (0) | 2025.11.18 |
|---|---|
| Next.js Authentication : 사용자 인증을 안전하게 구현하는 방법 (0) | 2025.01.26 |
| Next.js Server Action : 코드 간소화와 최적화된 사용자 경험 (1) | 2025.01.25 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!