키 받기

기회 API 붙이기 설명서

사주 앱에 계산과 완성된 풀이 글을 붙이는 방법을 처음부터 끝까지 적었습니다. 이 문서 하나로 붙습니다. 버전 v0.3.5 · 풀이 원문 2.3.0

1. 무엇을 드리는지 — 오 분 요약

다른 사주 API는 대개 계산값까지 줍니다. 여덟 글자를 받아도 화면에 쓸 글이 없어서, 결국 그 글을 직접 써야 합니다. 기회는 사람이 읽는 완성 문장까지 줍니다.

드리는 것내용
계산만세력(년·월·일·시주)·24절기·일진·한 달 표·대운·음양력 변환. 1901~2099년
완성 풀이제목·요약·본문 여러 칸·문단마다 근거·한계. 길이 셋, 말투 셋, 이름 부르기
열한 가지 풀이나의 성향·연애·돈재물·직장사업·가족자녀·부모·친구인간관계·친구·배우자·오행·건강
궁합두 사람을 함께 읽는 글
오늘의 기운그날의 일진이 이 사주에 어떻게 닿는지. 매일 열어 볼 거리
붙이기 키트연결 코드·응답 견본·검사. 이 문서의 예시가 전부 그 키트입니다

규칙으로 조립합니다. 생성 모델을 쓰지 않아서 같은 생년월일이면 언제나 같은 글이 나옵니다. 글에 숫자를 쓰지 않고, 길흉과 시기를 정해 말하지 않습니다. 판단마다 근거가 함께 옵니다.

먼저 아셔야 할 것 — 요청에 담긴 생년월일·출생시각은 저장하지 않습니다. 그래서 "지난번 결과를 다시 달라"는 조회가 없습니다. 결과를 다시 보여주려면 앱이 응답을 저장해 두셔야 합니다(11절·14절).

2. 키 받기

요금 페이지에서 결제하시면 그 자리에서 키가 나옵니다. 사람이 확인하고 연락드리는 절차가 없습니다. 심사도 서류도 없습니다.

무엇내용
단가정가 29,900원/월(부가세 별도). 계약일부터 첫 한 달 50% — 기간 행사가 아니라 회사마다 첫 달입니다
전체 키기능 전부가 들어 있는 키
맞춤 키고른 묶음만 들어 있는 키. 예를 들어 연애 풀이와 궁합만. 값은 전체와 같습니다
연장같은 이메일로 다시 결제하면 같은 키가 한 달 늘어납니다. 키를 바꿔 끼울 필요가 없습니다
무료 체험 키없습니다

맞춤 키를 쓰실 때, 키에 없는 기능을 부르면 403 FEATURE_NOT_INCLUDED 가 오고 그 호출은 요금에서 세지 않습니다. 키에 무엇이 들어 있는지는 GET /v1/me 의 features 로 확인하시고, 없는 기능은 화면에서 숨기시면 됩니다.

3. 첫 호출 한 줄

키를 받으셨으면 이것부터 찍어 보십시오. 맛보기 풀이는 호출로 세지 않습니다.

curl -H "x-api-key: sk_live_당신의키"   "https://api.gihoe.co.kr/v1/reading?date=1990-03-15&time=07:20"

돌아오는 봉투는 늘 같은 모양입니다.

{
  "success": true,
  "data": { ... },
  "meta": { "version": "0.3.5", "responseMs": 12, "plan": "basic" }
}

실패도 같은 모양입니다. success:false 와 error{code,message} 가 옵니다. 코드는 10절에 전부 있습니다.

4. 붙이기 키트 설치 — 파일 두 개, 환경변수 두 개

키트는 결제 뒤 발급 페이지 문의로 드립니다. 폴더를 그대로 복사해 넣으시면 됩니다.

kits/gihoe-connect/
  client.mjs      기회 API 부르기 (그대로 씁니다)
  to-app.mjs      응답을 앱 자료형으로 (회사마다 여기만 고칩니다)
  kit.test.mjs    검사 — 서버 없이 견본으로 돕니다
  fixtures/       실제 응답 견본 (가상 인물)
환경변수 두 개 — 키는 서버에서만 씁니다. 브라우저·앱 안에 넣지 마십시오.
GIHOE_API_ORIGIN=https://api.gihoe.co.kr
GIHOE_API_KEY=sk_live_...
서버 코드
import { createGihoeConnect } from './kits/gihoe-connect/client.mjs';
import { readingToCopy, readingSections, categoryToCopy, dailyToCopy,
         eventPopups, readingHolds, readingCacheKey } from './kits/gihoe-connect/to-app.mjs';

const gihoe = createGihoeConnect({
  baseUrl: process.env.GIHOE_API_ORIGIN,
  apiKey: process.env.GIHOE_API_KEY,
});

// entry = 앱이 저장한 출생정보 그대로
// { birthDate:'1990-03-15', calendarType:'solar'|'lunar', birthTime:'07:20'|'unknown',
//   isLeapMonth:false, gender:'female'|'male'|'unspecified' }
const { data, quota } = await gihoe.reading(entry, { length: 'normal', name: '지영' });
const copy = readingToCopy(data);        // 화면 글감 여섯 칸
const sections = readingSections(data);  // 제목 있는 단락들
검사 — 서버 없이 견본으로 돕니다. 붙인 뒤 이것부터 돌리십시오.
node --test kits/gihoe-connect/kit.test.mjs

5. 화면 흐름 만들기

실제로 파는 앱이 쓰는 순서입니다. 그대로 따라 하셔도 되고, 필요한 단계만 쓰셔도 됩니다.

단계부르는 것화면셈
1. 들어오기/v1/reading 맛보기(length=short)짧은 풀이를 공짜로 보여 주고 관심을 끕니다0회
2. 결제(앱의 결제)풀이 하나 또는 전체를 파는 자리—
3. 본문/v1/reading?length=normal제목·요약·본문·근거·한계1회
4. 풀이/v1/reading?category=money고른 분야 하나풀이당 1회
5. 매일/v1/daily앱을 다시 열 이유전체 키 0회
6. 둘이서/v1/match궁합2회

맛보기가 0회인 이유는 분명합니다. 앱의 유입 화면이 앱의 비용이 되면 안 되기 때문입니다.

6. 부를 수 있는 것 전부

주소무엇주는 값
GET/POST /v1/reading완성 풀이 글length=short|normal|long, tone=talk|calm|plain, name, category
GET/POST /v1/match궁합 글dateA timeA dateB timeB 또는 본문 {a,b}
GET/POST /v1/daily오늘의 기운 글on=2026-09-20(비우면 오늘)
GET/POST /v1/manse만세력 계산detail=full 이면 지장간·십이운성·신살까지
GET/POST /v1/daeun대운isFemale 필요(순행·역행이 갈립니다)
GET /v1/calendar/convert음양력 변환date, isLunar, isLeapMonth
GET /v1/solar-terms24절기year
GET /v1/me내 키 상태요금제·들어 있는 묶음·이번 달 사용량·사람 수·설정
POST /v1/support문의키 없이도 됩니다. 이메일로 답합니다
GET /v1/openapi.json기계용 명세OpenAPI 3

입력은 쿼리로도 본문으로도 보낼 수 있습니다. 날짜는 YYYY-MM-DD, 시각은 HH:MM. 시각을 모르면 비워 두십시오 — 시주를 만들지 않고 여섯 글자로 계산하며(pillars.hour 가 null), 응답의 assumptions.assumedNoon 으로 밝힙니다. 음력이면 isLunar=true 와 함께 isLeapMonth 를 반드시 주셔야 합니다(윤달을 마음대로 추정하지 않습니다).

7. 응답 모양과 화면에 넣는 법

풀이 글은 어디서나 같은 모양입니다. 이 모양만 알면 화면을 한 번만 만들면 됩니다.

{
  "prose": {
    "title": "제목 한 줄",
    "summary": ["요약 문장", "..."],
    "blocks": [
      { "id": "foundation", "heading": "타고난 바탕",
        "sentences": ["문장", "문장"],
        "basis": ["일간 기(己)", "오행 토"] },
      ...
    ],
    "limits": ["이 풀이가 말하지 않는 것", "..."]
  },
  "minor": { "under14": false, "under19": false },
  "events": [ ... ],
  "readingVersion": "1.0.0", "proseVersion": "1.2.0"
}

키트의 readingToCopy(data) 를 쓰면 화면 글감 여섯 칸으로 바뀝니다.

칸무엇을 넣나
heading큰 제목
core먼저 보이는 요약
scenarios[]펼쳐 보는 본문들
reflection돌아볼 것
action살릴 것
limit이 풀이가 말하지 않는 것 — 화면에서 지우지 마십시오
근거와 한계를 함께 보여 주십시오. 문단마다 붙는 basis 와 limits 는 "왜 그렇게 읽었는지"와 "무엇을 말하지 않는지"입니다. 이것을 지우면 단정하는 글로 보이고, 환불과 항의가 거기서 생깁니다. 접어 두셔도 좋으니 지우지는 마십시오.

8. 열한 가지 풀이

/v1/reading 에 category 를 더하면 그 분야 풀이가 data.category 로 함께 옵니다. 모양은 전부 같습니다 — 요약 + 네 문단 + 문단마다 근거 + 금지선.

id이름무엇을 읽나
overview나의 성향타고난 바탕·기운의 세기·균형·선택의 결
love연애다가가는 방식·곁 자리·살릴 것·흔들리기 쉬운 때
money돈·재물들어오는 길·버는 것과 남는 것·지킬 것·위험
career직장·사업일하는 결·조직 안의 자리·살릴 것·어긋나는 자리
children가족·자녀기운이 실린 자리·돌보는 방식·살릴 것·부딪히는 자리
parents부모부모 자리(월주)·받은 결(인성)·살릴 것·어긋나기 쉬운 자리
friends친구끌리는 친구·우정 속 내 자리·서운해지는 순간·오래 가는 사이 (말투 '~해요')
relationships친구·인간관계사람을 대하는 결·둘레·살릴 것·어긋나는 때
partner배우자배우자 별·배우자 자리·함께 사는 방식·어긋나기 쉬운 때
elements오행다섯 기운의 셈·몰린 기운·빈 기운과 살릴 기운·계절
health건강힘을 쓰는 결·무리하기 쉬운 자리·채우는 방식·오래 가게

배우자는 isFemale 을 주시면 명리 관행대로 별을 잡고(여성은 관성, 남성은 재성), 안 주시면 추정하지 않고 두 별을 나란히 읽습니다. 결혼 시기·상대의 정체·이혼은 말하지 않습니다.

건강은 병명·증상·진단·치료·처방·몸의 부위·수명을 말하지 않습니다. 기운을 쓰고 채우는 결만 읽고, 의학 정보가 아니라는 문장을 글 안에 넣어 보냅니다. 그 문장도 화면에 그대로 두십시오.

부모는 태어난 달의 기둥을 부모 자리로, 받쳐 주는 기운을 받은 결로 읽습니다. 부모가 계신지 안 계신지·어떤 분인지·건강·수명은 말하지 않고, '부모덕이 있다 없다' 같은 판정도 하지 않습니다. 가족·자녀(children)와는 다른 글입니다 — 가족은 가족 전체를, 부모는 위에서 내려온 자리 하나만 읽습니다. 그래서 두 풀이를 서로 대신 쓸 수 없습니다.

친구(friends)는 나란한 자리(비겁) 하나만 읽습니다. relationships(친구·인간관계)가 사람 전반을 대하는 결을 읽는 것과 달라 서로 대신 쓸 수 없습니다 — 같은 사람에게서 두 글은 한 문장도 겹치지 않습니다. 말투가 '~해요'이고 분량이 1,100~1,300자로 고정입니다.

목록에 없는 아이디를 보내시면 400 VALIDATION_ERROR 로 옵니다.

이름은 바꿔 쓰셔도 됩니다. 화면에서 '부모'를 '부모운'이라 부르고 싶으시면 categoryToCopy(c, { labels: { parents: '부모운' } }) 처럼 주십시오. 글은 한 글자도 바뀌지 않고 이름표만 바뀝니다. 아이디(categoryId)는 그대로라 저장해 두신 결과와도 어긋나지 않습니다.

9. 궁합 · 오늘의 기운

궁합

const { data } = await gihoe.match(a, b, { length: 'normal' });
const copy = matchToCopy(data);    // 같은 글감 모양
const table = matchTable(data);    // 숫자 없이 막대만

두 사람의 기운이 어떻게 만나는지를 글로 드립니다. 점수로 등수를 매기지 않습니다. 호출은 2회로 셉니다(두 사람이니까요).

오늘의 기운

const { data } = await gihoe.daily(entry);          // 오늘
const { data: d20 } = await gihoe.daily(entry, { on: '2026-09-20' });
const today = dailyToCopy(data);   // { date, dayGanji, heading, core, sections[], action, limit }
30일 묶음으로 파실 때 — 오늘의 기운은 전체 키에서 호출 0회입니다. 풀이 하나(1,053자)에 오늘의 기운 30일(하루 400자)을 묶으면 한 상품이 13,000자가 되는데 추가 비용이 0원입니다. 만료일은 앱이 주문에 저장하시고, 지난 뒤에는 부르지 않으시면 됩니다. 우리 쪽 잠금은 없습니다.

그날의 일진이 이 사주에 어떻게 닿는지를 세 문단으로 드립니다. 하루를 좋다·나쁘다로 판정하지 않고, 무엇을 하라·피하라 지시하지 않고, 행운의 색·숫자·방향을 말하지 않습니다. 같은 사람 같은 날이면 언제나 같은 글이라 화면에 캐시하기 좋습니다. 전체 키에서는 호출로 세지 않습니다 — 매일 여는 거리이기 때문입니다.

10. 호출 셈과 한도

부르는 것셈
맛보기 풀이(short, 풀이 없음)0회
보통·긴 풀이1회
풀이 하나1회
궁합2회
오늘의 기운0회(전체 키)
계산(만세력·대운·음양력·절기)0회
키에 없는 묶음 · 연령 기준에 걸린 요청세지 않음

남은 횟수는 응답 헤더 X-Monthly-Remaining 으로 옵니다. 기본 한도는 월 2,000건, 분당 30회입니다. 한도를 넘으면 추가 요금 대신 차단됩니다 — 예상 못 한 청구서가 나올 일이 없습니다. 분당 한도에 걸리면 429가 오고 다음 분에 저절로 풀립니다.

11. 오류가 왔을 때 앱이 할 일

상태코드뜻앱이 할 일
400VALIDATION_ERROR입력이 잘못됨(없는 날짜, 모르는 풀이 아이디 등)입력 화면으로 돌려보냅니다. 음력이면 윤달 여부를 물으십시오
401API_KEY_REQUIRED / INVALID_API_KEY키가 없거나 틀림서버 환경변수를 확인하십시오
401KEY_EXPIRED이용 기간이 지남같은 이메일로 결제하면 같은 키가 연장됩니다
402QUOTA_EXHAUSTED이번 달 한도를 다 씀다음 달까지 기다리거나 한도를 올리십시오. 화면에는 "잠시 뒤 다시"로
403FEATURE_NOT_INCLUDED이 키에 없는 묶음그 기능은 화면에서 숨기십시오. 호출은 안 셉니다
403AGE_RESTRICTED회사가 정한 연령 기준 미만보호자 확인 화면으로. 호출은 안 셉니다
429RATE_LIMITED분당 한도잠깐 쉬었다 다시. 다음 분에 풀립니다
500INTERNAL_ERROR우리 잘못한 번만 다시 불러 보시고, 계속되면 문의 주십시오

키트의 client.mjs 는 이 오류들을 앱이 화면에 그대로 쓸 수 있는 말로 바꿔 줍니다. 요금제·키 이야기 같은 운영자용 설명은 따로 operatorHint 에 담아 두어 고객에게 보이지 않습니다.

12. 회사별 설정 — 연령 기준, 이벤트 칸

연령 기준

모든 응답에 minor{under14,under19} 표시가 함께 옵니다(숫자 없이 참·거짓). 아예 막고 싶으시면 한 번만 정해 두십시오.

POST /v1/me/policy   { "minAge": 19 }   // 0(막지 않음·기본) · 14 · 19

기준 미만이면 403 AGE_RESTRICTED 가 오고 호출도 세지 않습니다. 기본이 0인 이유는 부모가 자녀 사주를 보는 경우 때문입니다.

이벤트 칸 — 매달 새 기능

응답의 events[] 에 그 달의 거리가 담겨 옵니다(예: 추석에 "올해 남은 넉 달"). 우리가 배포하면 붙어 있는 모든 앱이 같은 날 함께 받습니다. 앱은 eventPopups(data.events) 로 띄우기만 하면 되고, 원하지 않으시면 한 번에 끕니다.

POST /v1/me/events   { "enabled": false }

13. 몇 사람이 썼는지 보기

GET /v1/me  →  data.usage = { month, count, byFeature, people }

계약 단위는 '호출'입니다. usage.count 하나만 보시면 됩니다 — 여러 풀이를 /v1/readings 로 묶어 부르면 풀이가 셋이든 다섯이든 호출 1회입니다. 묶음 하나가 곧 호출 하나입니다. 받으신 묶음을 나눠 쓰시든 쪼개 쓰시든 그건 쓰시는 쪽 몫이고, 저희 셈은 늘 1입니다. usage.byFeature 와 usage.people 은 늘 0입니다 — 계약이 호출 단위라 따로 세실 것이 없습니다. 아래는 그 전 설명입니다. 생년월일은 저장하지 않고, 비밀값과 그 키의 지문을 섞은 되돌릴 수 없는 표식으로만 세기 때문에 원래 값을 복원할 수 없고, 회사가 다르면 같은 사람도 다른 표식이라 회사끼리 맞춰 볼 수 없습니다. 달이 바뀌면 새로 셉니다.

14. 저장·캐시·재시도

우리는 저장하지 않습니다. 생년월일도, 만든 글도 남기지 않습니다. 그래서 "아까 그 결과 다시" 같은 조회 주소가 없습니다. 결과를 다시 보여주려면 앱이 응답을 저장하십시오.

같은 사람·같은 옵션이면 언제나 같은 글이 나오므로, 캐시하기 아주 쉽습니다. 키트의 readingCacheKey(entry, options) 가 저장 열쇠를 만들어 줍니다.

const key = readingCacheKey(entry, { length: 'normal', name });
const saved = await db.get(key);
const { data } = saved ?? await gihoe.reading(entry, { length: 'normal', name });

재시도는 500과 429에만 하십시오. 400·401·403은 다시 불러도 같은 답이 옵니다. 시간 제한은 키트가 기본 10초로 잡아 둡니다.

출생 시각을 모르는 손님에게는 readingHolds(data, entry) 가 HOLD_BIRTH_TIME_UNKNOWN 같은 표시를 돌려줍니다. 모르는 것은 모른다고 화면에 적는 편이 신뢰를 남깁니다.

15. 버전과 호환 약속

칸무엇
meta.versionAPI 버전
data.readingVersion계산·판정 규칙
data.proseVersion기본 풀이 문장
data.category.version풀이 문장
data.matchVersion · data.dailyVersion궁합·오늘의 기운 문장

필드를 말없이 지우지 않습니다. 모양을 바꿔야 하면 60일 전에 알려 드립니다. 문장이 좋아지면 위 번호가 올라갑니다 — 번호가 같으면 글도 같습니다. 새 기능은 늘 더하기로 들어오므로, 붙여 두신 코드가 새 기능 때문에 깨지지 않습니다.

16. 붙이기 마무리 점검표

이 일곱 개가 되면 붙은 것입니다.

  1. 서버 환경변수 두 개가 들어갔고, 키가 브라우저·앱 안에는 없다
  2. 맛보기 풀이가 화면에 뜬다(호출이 안 세지는 것도 /v1/me 로 확인)
  3. 본문 풀이에 근거와 한계가 함께 보인다
  4. 풀이 하나가 열리고, 키에 없는 풀이는 화면에서 숨겨진다
  5. 없는 날짜·윤달 미지정 같은 잘못된 입력이 입력 화면으로 돌아간다
  6. 결과를 앱이 저장해 두었다가 다시 보여 준다
  7. node --test kits/gihoe-connect/kit.test.mjs 가 전부 통과한다
더 깊은 내용: 주소별 문서 · OpenAPI · llms.txt · 요금 · 문의
막히시면 POST /v1/support 로 보내 주십시오. 키 없이도 됩니다. 영업일 기준 하루 안에 이메일로 답합니다.
참고용 계산입니다. 길흉과 시기를 단정하지 않습니다.