AstroWay/api v2.295.7 · ko
모든 시스템 정상 작동 중

아스트로로지 애플리케이션 구축 가이드: 개발자를 위한 완전한 가이드

아스트로로지 애플리케이션을 처음부터 만드는 단계별 가이드 - API 선택, 아키텍처, 첫 번째 내태아 도표, 시나스트리 및 트랜짓 추가, 배포

🕓 2026-04-14에 기록, 2026-05-09에 업데이트됨. TS / Python / PHP SDK가 이제 public registry에 올라와 있어 - @astroway/sdk (npm), astroway (PyPI), astroway/sdk (Packagist). 자세한 내용은 changelog에서 확인해.

점성술 앱을 만들고 싶어. 어디서부터 시작할까? 이 가이드에서는 API 선택부터 실제 작동하는 제품 배포까지, 결정 사항과 코드를 다뤄.

코드를 작성하기 전에 정해야 할 것: 계산을 클라이언트에서 할까, 서버에서 할까?

클라이언트 (예: 브라우저의 Swiss Ephemeris WASM):

  • 장점: 요청당 비용 없음, 오프라인 동작, 완전한 제어
  • 단점: 사용자마다 ~2MB WASM, 복잡한 라이선스 (Swiss Ephemeris의 상업적 사용에는 제한이 있음), 계산 코드를 직접 유지보수해야 함

서버 API (AstroWay, Prokerala 등):

  • 장점: 클라이언트 의존성 없음, 작은 앱 bundle, 모바일에서 빠름, 라이선스 골칫거리 없음
  • 단점: 요청당 크레딧 비용, “콜드” 요청 시 지연 시간

대부분의 앱에서는 서버 API가 이긴다. 모바일 사용자는 2MB WASM을 기다려주지 않고, 라이선스 미묘한 문제와 씨름할 필요도 없으며, 최신 API는 동일한 요청을 무료로 캐시해 (AstroWay는 X-Cache: HIT header와 함께 5분 캐시를 제공해).

중요한 것들:

  • 정확도: “내부적으로” Swiss Ephemeris를 쓰는지 찾아봐 (±1 각초). API가 이를 명시하지 않으면 물어봐.
  • 커버리지: 네이탈 + 시나스트리 + 트랜짓이 최소한. 프로그레션과 리턴은 다음 단계. 희귀한 기법 (렉티피케이션, Human Design)은 차별화 요소야.
  • SDK 품질: 타입이 지정된 TypeScript와 Python SDK는 몇 시간을 절약해줘.
  • 요금 모델: credit-based는 혼합 워크로드에서 per-request보다 확장성이 좋아.

이 튜토리얼에서는 AstroWay API를 사용할 거야. 네 가지 모두를 갖추고 있거든 (777개 endpoint, Swiss Ephemeris, 타입 지정 SDK, credit-based).

브라우저에서 점성술 API를 직접 호출하지 마 - API 키가 유출돼. 항상 자체 backend를 통해 호출해.

Terminal window
mkdir my-astrology-app
cd my-astrology-app
npm init -y
npm install express @astroway/sdk zod
npm install -D typescript @types/express @types/node tsx

src/server.ts를 만들어:

import express from 'express';
import { Astroway } from '@astroway/sdk';
import { z } from 'zod';
const app = express();
app.use(express.json());
const client = new Astroway({
apiKey: process.env.ASTROWAY_API_KEY!,
});
const BirthInput = z.object({
date: z.string(),
time: z.string(),
timezoneOffset: z.number(),
latitude: z.number(),
longitude: z.number(),
});
app.post('/api/chart', async (req, res) => {
const parsed = BirthInput.safeParse(req.body);
if (!parsed.success) return res.status(400).json({ error: parsed.error });
try {
const chart = await client.chart.compute({
...parsed.data,
houseSystem: 'P',
});
res.json(chart);
} catch (err) {
res.status(500).json({ error: String(err) });
}
});
app.listen(3000, () => console.log('http://localhost:3000'));

실행:

Terminal window
ASTROWAY_API_KEY=aw_test_... npx tsx src/server.ts

curl로 확인:

Terminal window
curl -X POST http://localhost:3000/api/chart \
-H "Content-Type: application/json" \
-d '{"date":"1990-07-14","time":"14:30:00","timezoneOffset":3,"latitude":50.4501,"longitude":30.5234}'

완전한 JSON 네이탈 차트를 받게 돼.

어떤 프레임워크든 괜찮아. 예시로 간단한 폼이 있는 React를 써볼게:

import { useState } from 'react';
type Chart = {
planets: { name: string; longitude: number; isRetrograde: boolean }[];
aspects: { planet1: string; planet2: string; type: { name: string }; orb: number }[];
};
// Planets come with ecliptic longitude, not a sign: the sign is that longitude
// divided by 30. Work it out on your side, it is cheaper than another call.
const SIGNS = ['Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo',
'Libra', 'Scorpio', 'Sagittarius', 'Capricorn', 'Aquarius', 'Pisces'];
const signOf = (longitude: number) => SIGNS[Math.floor(longitude / 30) % 12];
export function NatalForm() {
const [chart, setChart] = useState<Chart | null>(null);
async function handleSubmit(formData: FormData) {
const body = Object.fromEntries(formData.entries());
const res = await fetch('/api/chart', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
date: body.date,
time: body.time + ':00',
timezoneOffset: Number(body.tz),
latitude: Number(body.lat),
longitude: Number(body.lng),
}),
});
setChart(await res.json());
}
return (
<div>
<form action={handleSubmit}>
<input name="date" type="date" required />
<input name="time" type="time" required />
<input name="tz" type="number" placeholder="Timezone offset (e.g. 3)" required />
<input name="lat" type="number" step="0.0001" placeholder="Latitude" required />
<input name="lng" type="number" step="0.0001" placeholder="Longitude" required />
<button>Build chart</button>
</form>
{chart && (
<div>
<h2>Planets</h2>
<ul>
{chart.planets.map(p => (
<li key={p.name}>{p.name}: {signOf(p.longitude)} {(p.longitude % 30).toFixed(2)}°{p.isRetrograde ? ' R' : ''}</li>
))}
</ul>
<h2>Aspects</h2>
<ul>
{chart.aspects.map((a, i) => (
<li key={i}>{a.planet1} {a.type.name} {a.planet2} (orb {a.orb.toFixed(2)}°)</li>
))}
</ul>
</div>
)}
</div>
);
}

궁합은 다음으로 자연스러운 기능이야. src/server.ts에 추가해:

const SynastryInput = z.object({
chart1: BirthInput,
chart2: BirthInput,
});
app.post('/api/synastry', async (req, res) => {
const parsed = SynastryInput.safeParse(req.body);
if (!parsed.success) return res.status(400).json({ error: parsed.error });
const result = await client.synastry.compute(parsed.data);
res.json({
score: result.compatibility.score,
label: result.compatibility.label,
aspects: result.crossAspects,
});
});

끝. endpoint 하나, 호출당 50 크레딧, 0–100 점수와 cross-aspects를 반환해.

콘텐츠 engagement를 위해 데일리 운세는 필수야. AstroWay는 실제 트랜짓 데이터로 이를 생성해:

// The daily horoscope is keyed by sign and date, not by the birth chart.
const DailyInput = z.object({
sign: z.enum(['aries', 'taurus', 'gemini', 'cancer', 'leo', 'virgo',
'libra', 'scorpio', 'sagittarius', 'capricorn', 'aquarius', 'pisces']),
date: z.string(),
});
app.post('/api/horoscope/daily', async (req, res) => {
const parsed = DailyInput.safeParse(req.body);
if (!parsed.success) return res.status(400).json({ error: parsed.error });
const horoscope = await client.horoscope.daily({ ...parsed.data, language: 'uk' });
res.json({
text: horoscope.horoscope,
disclaimer: horoscope.disclaimer, // must be shown in the UI!
});
});

중요: AI 생성 콘텐츠에는 사용자에게 반드시 보여줘야 하는 disclaimer가 포함돼 있어 (Terms of Service에 따라). 제거하지 마.

데일리 운세는 같은 생일과 비슷한 네이탈 차트를 가진 모든 사용자에게 동일해. 공격적으로 캐시해:

import { LRUCache } from 'lru-cache';
const cache = new LRUCache<string, any>({ max: 1000, ttl: 86400 * 1000 });
app.post('/api/horoscope/daily', async (req, res) => {
const key = JSON.stringify(req.body);
const cached = cache.get(key);
if (cached) return res.json(cached);
const horoscope = await client.horoscope.daily(req.body);
cache.set(key, horoscope);
res.json(horoscope);
});

AstroWay에도 내장 5분 서버 캐싱 (X-Cache: HIT)이 있지만, 오래 유지되는 콘텐츠에는 자체 캐시가 필요해.

어떤 플랫폼이든 괜찮아. 빠른 시작이라면 Vercel이나 Railway:

Terminal window
# vercel.json
{ "functions": { "src/server.ts": { "runtime": "@vercel/node" } } }

플랫폼 대시보드에서 env ASTROWAY_API_KEY를 설정해.

여기까지 왔다면 - 출시 전에 월간 크레딧 소비량을 추정해봐:

credits/month = DAU × (avg_charts_per_user × 20
+ avg_synastries × 50
+ avg_horoscopes × 20) × 30

100 DAU가 하루에 사용자당 차트 1개 + 운세 1개 + 시나스트리 0.3개를 사용한다면:

100 × (20 + 20 + 0.3 × 50) × 30 = 165,000 credits/month

이건 Starter 플랜으로 $19/월 (200K 크레딧)이야.

성장하면서 추가할 만한 고급 기능들:

  • 트랜짓 오버레이: /v1/transits는 네이탈 차트 위에 현재 행성 위치를 보여줘
  • 프로그레션: /v1/progressions으로 심리적 발달의 아크를 표현
  • Solar return: /v1/solar-return으로 “생일부터 1년 예측”
  • Human Design: Human Design API는 완전히 새로운 기능 영역을 열어줘
  • 아스트로카토그래피: /v1/acg는 사용자가 어디에 살면 좋을지 매핑해줘

각 기능은 처음부터 구현하기엔 무겁지만 - AstroWay에서는 API 호출 한 번이야.

MakSeong · AstroWay

AstroWay API를 만들고 있어: Swiss Ephemeris를 순수 REST로 감싸고, 실제로 중요한 지루한 세부사항을 기록합니다.

// 이걸로 빌드해

Solar Fire에 사용된 것과 동일한 Swiss Ephemeris - 단 4줄의 코드로.

카드 없이 무료 키. 첫 결제 전까지 월 5,000 API 호출.

더 많은 블로그 글 모든 글 보기 →

Ephemeris 2026-07-19

우리가 정확성을 제어하는 방법: CI vs swetest 및 NASA

아스트로-API의 정확성은 하나의 에프메르에디드 리팩토링으로 쉽게 저하됩니다. CI의 보호를 분석합니다: 앱과 API를 위한 하나의 스위스 에프메르에디스 핵심, 수백의 스냅샷이 동결된 표준 맵에, swetest CGI, Kerykeion, Prokerala 및 NASA의 어두움 카탈로그에 대한 각 PR의 삼각화.

Engineering 2026-07-15

세 가지 공식 SDK: 순수 curl 대신 TypeScript, Python, PHP

순수 HTTP로도 동작하지만, 타입이 있는 클라이언트는 몇 시간을 아껴 줍니다: 경로 자동 완성, 요청과 응답 타입, 408/409/429/5xx에 대한 내장 재시도, Stainless 스타일의 오류 계층. 세 가지 공식 SDK인 @astroway/sdk(npm), astroway(PyPI), astroway/sdk(Packagist)를 살펴보고, 이들이 어떻게 하나의 OpenAPI 계약에서 생성되는지 설명합니다.

Industry 2026-06-05

Free Astrology API: Which One Has the Best Free Tier in 2026?

A side-by-side of free tiers across the major astrology APIs - credits, request caps, card requirements - and how much you can actually build for free.