패턴 명리와 스토리 북

- 패턴 명리와 story 북 (융복합 Mapping Matrix) 입니다 -

오행패턴.오온심리 (융합 매트릭스)

사주 궁합 리포트 자동화 서비스 (앱 코드) (GPT 5)###

손비담 2025. 8. 9. 10:27
반응형

 

사주 궁합 리포트 자동화 서비스 (앱 코드)

다음은 GPT‑5(OpenAI)의 Responses API를 사용해 사주(연주 기반 간단판)와 궁합 리포트를 자동 생성하는 예제 앱입니다.

전체 예제는 빠르게 시작할 수 있는 프론트엔드(React) + 백엔드(Node/Express) 구조로 제공됩니다. 실제 배포/정밀 사주 계산(월주/일주/시주 정확 계산)은 천문학적 계산이 필요하므로 문서 하단에 개선 포인트와 참고자료를 적어두었습니다.


프로젝트 구조 (요약)

sa-ju-gung-hap/
├─ backend/
│  ├─ package.json
│  ├─ index.js        # Express 서버, OpenAI 호출
│  └─ saju.js         # 간단한 사주 계산기 (연주 중심, 개선 필요)
├─ frontend/
│  ├─ package.json
│  └─ src/
│     ├─ App.jsx
│     └─ api.js
└─ .env.example

.env.example

OPENAI_API_KEY=sk-...    # OpenAI API Key
OPENAI_API_BASE=https://api.openai.com
PORT=3000

backend/package.json

{
  "name": "saju-backend",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "node-fetch": "^3.4.0",
    "cors": "^2.8.5",
    "body-parser": "^1.20.1",
    "dayjs": "^1.11.9",
    "dayjs-plugin-utc": "^1.0.0"
  }
}

backend/saju.js

주의: 이 파일은 간단화된 연주(연도 기반) 사주 계산을 제공합니다. 월/일/시의 정확한 간지(천간/지지) 계산은 윤달/태음태양력 보정과 천문적 일간 자오시 계산이 필요합니다. 아래는 연주(연간 간지) 계산 알고리즘의 예시입니다.

// saju.js
const stems = ['갑','을','병','정','무','기','경','신','임','계'];
const branches = ['자','축','인','묘','진','사','오','미','신','유','술','해'];

function yearPillarFromYear(year) {
  // 천간: (year - 4) % 10, 지지: (year - 4) % 12  (서력 기준)
  const g = (year - 4) % 10;
  const z = (year - 4) % 12;
  return {
    year,
    stem: stems[(g+10)%10],
    branch: branches[(z+12)%12],
    ganzhi: stems[(g+10)%10] + branches[(z+12)%12]
  };
}

module.exports = { yearPillarFromYear };

backend/index.js

// index.js
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const fetch = (...args) => import('node-fetch').then(({default: f})=>f(...args));
const { yearPillarFromYear } = require('./saju');

const app = express();
app.use(cors());
app.use(bodyParser.json());

const OPENAI_KEY = process.env.OPENAI_API_KEY;
const OPENAI_BASE = process.env.OPENAI_API_BASE || 'https://api.openai.com';

if (!OPENAI_KEY) {
  console.warn('OPENAI_API_KEY가 설정되어 있지 않습니다. .env 파일을 확인하세요.');
}

// 1) 간단 사주 계산 (연주)
app.post('/api/compute', (req, res) => {
  try {
    const { name, birthDate /* ISO 문자열 e.g. 1990-05-03 */ } = req.body;
    const d = new Date(birthDate);
    if (isNaN(d.getTime())) return res.status(400).json({ error: 'birthDate invalid' });

    const year = d.getUTCFullYear();
    const pillar = yearPillarFromYear(year);

    return res.json({ name, birthDate, pillar });
  } catch (e) {
    console.error(e);
    return res.status(500).json({ error: e.message });
  }
});

// 2) GPT-5로 궁합 리포트 생성
app.post('/api/report', async (req, res) => {
  try {
    const { personA, personB, style = '친절하고 전문적인 한국어' } = req.body;
    if (!personA || !personB) return res.status(400).json({ error: 'personA/personB required' });

    // 예시로 필요한 정보:
    // personA = { name, birthDate, pillar: { year, stem, branch, ganzhi } }

    // 프롬프트(시스템/사용자 메시지 구성)
    const system = `당신은 한국어에 능통한 사주 전문가입니다. 사용자가 제공한 사주(간지)를 바탕으로 두 사람의 궁합 리포트를 작성하세요. 리포트는 요약(강점/주의사항), 세부 해석(연주/월주/일주/시주 가능한 항목 포함), 연애/결혼/커리어 관점 조언, 마지막으로 한 문장의 종합 조언을 포함해야 합니다. 감성적 어조와 전문성을 적절히 섞어 친절하게 작성하세요.`;

    const userContent = `Person A:\n${JSON.stringify(personA, null, 2)}\n\nPerson B:\n${JSON.stringify(personB, null, 2)}\n\n원하는 스타일: ${style}`;

    const payload = {
      model: 'gpt-5',
      messages: [
        { role: 'system', content: system },
        { role: 'user', content: userContent }
      ],
      // 추가 옵션: max_tokens, temperature 등
      max_tokens: 800,
      temperature: 0.4
    };

    const r = await fetch(`${OPENAI_BASE}/v1/responses`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${OPENAI_KEY}`
      },
      body: JSON.stringify(payload)
    });

    if (!r.ok) {
      const text = await r.text();
      return res.status(502).json({ error: 'OpenAI API error', details: text });
    }

    const data = await r.json();

    // Responses API는 다양한 형태로 응답을 줄 수 있으니, 안전하게 텍스트를 추출
    const answer = (data.output && data.output[0] && data.output[0].content) || data;

    return res.json({ raw: data, answer });
  } catch (e) {
    console.error(e);
    return res.status(500).json({ error: e.message });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on ${PORT}`));

frontend/package.json

{
  "name": "saju-frontend",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "axios": "^1.4.0"
  }
}

frontend/src/api.js

import axios from 'axios';
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3000';

export async function computeSaju(payload) {
  return axios.post(`${API_BASE}/api/compute`, payload).then(r=>r.data);
}

export async function generateReport(payload) {
  return axios.post(`${API_BASE}/api/report`, payload).then(r=>r.data);
}

frontend/src/App.jsx

import React, { useState } from 'react';
import { computeSaju, generateReport } from './api';

export default function App(){
  const [a, setA] = useState({ name:'', birthDate:'' });
  const [b, setB] = useState({ name:'', birthDate:'' });
  const [report, setReport] = useState(null);
  const [loading, setLoading] = useState(false);

  async function handleCreate(){
    setLoading(true);
    try {
      const pa = await computeSaju(a);
      const pb = await computeSaju(b);

      const resp = await generateReport({ personA: pa, personB: pb, style: '친절하고 전문적인 한국어' });
      setReport(resp.answer || resp.raw);
    } catch (e) {
      console.error(e);
      alert(e.message || '오류');
    } finally {
      setLoading(false);
    }
  }

  return (
    <div style={{ padding:20, fontFamily:'system-ui' }}>
      <h1>사주 궁합 리포트 자동화</h1>
      <div style={{ display:'flex', gap:20 }}>
        <div>
          <h3>사람 A</h3>
          <input placeholder="이름" value={a.name} onChange={e=>setA({...a,name:e.target.value})} /> <br/>
          <input placeholder="생년-월-일 (YYYY-MM-DD)" value={a.birthDate} onChange={e=>setA({...a,birthDate:e.target.value})} />
        </div>
        <div>
          <h3>사람 B</h3>
          <input placeholder="이름" value={b.name} onChange={e=>setB({...b,name:e.target.value})} /> <br/>
          <input placeholder="생년-월-일 (YYYY-MM-DD)" value={b.birthDate} onChange={e=>setB({...b,birthDate:e.target.value})} />
        </div>
      </div>

      <div style={{ marginTop:20 }}>
        <button onClick={handleCreate} disabled={loading}>{loading? '생성중...':'궁합 리포트 생성'}</button>
      </div>

      {report && (
        <div style={{ marginTop:20 }}>
          <h2>리포트</h2>
          <pre style={{ whiteSpace:'pre-wrap' }}>{JSON.stringify(report, null, 2)}</pre>
        </div>
      )}
    </div>
  );
}

배포 / 실행 방법 (로컬)

  1. 루트 폴더에 .env 생성 후 OPENAI_API_KEY 입력
  2. backend 폴더에서 npm installnpm start
  3. frontend 폴더에서 npm install 후 개발 서버 실행(예: npm start) 또는 정적 빌드

개선 포인트 & 참고

  1. 정밀 사주 계산: 월주/일주/시주를 정확히 계산하려면 태양력/음력 변환, 윤달 처리, 표준시/표준자시(자시) 계산과 같은 천문 계산이 필요합니다. 기존의 검증된 라이브러리(예: 천문학 라이브러리 또는 전문 한중력력 라이브러리)를 연동하세요. 정확도가 중요하다면 한국 천문연구원 자료나 검증된 오픈소스 라이브러리를 사용 권장.
  2. 민감도와 윤리: 사주·운세는 민감한 개인정보(생년월일 등)를 다룹니다. 개인정보 보호정책, 데이터 최소수집, 암호화(전송·저장), 로그 최소화, 사용자 동의 등을 반드시 구현하세요.
  3. GPT 프롬프트 튜닝: 리포트 톤, 길이, 섹션 포맷(요약/상세/조언/한 줄 요약 등)을 고정된 프롬프트로 만들어 일관된 출력 얻기. 또한 function calling이나 structured outputs를 사용해 JSON 포맷으로 리포트를 받는 것이 파싱·저장에 유리합니다. OpenAI Responses API의 predicted_outputs 또는 response_format 가이드를 활용하세요.
  4. 요금/속도 최적화: 많은 사용자 요청을 처리하려면 배치 처리(또는 백그라운드 큐), 토큰 길이 제한, 캐싱 전략(같은 사주 조합 재사용 시) 등을 고려하세요.

원하시면, 지금 이 코드를 월/일/시까지 정확히 계산하는 버전으로 확장하거나, 리포트 출력 형식을 JSON 스키마로 고정해 데이터베이스에 저장 가능한 형태로 바꿔 드릴게요. 어떤 부분을 우선적으로 개선할까요?

 

 

반응형

 

 

사주 궁합 리포트 자동화 서비스 (앱 코드)

다음은 GPT‑5(OpenAI)의 Responses API를 사용해 사주(연주 기반 간단판)와 궁합 리포트를 자동 생성하는 예제 앱입니다. 전체 예제는 빠르게 시작할 수 있는 프론트엔드(React) + 백엔드(Node/Express) 구조로 제공됩니다. 실제 배포/정밀 사주 계산(월주/일주/시주 정확 계산)은 천문학적 계산이 필요하므로 문서 하단에 개선 포인트와 참고자료를 적어두었습니다.


프로젝트 구조 (요약)

sa-ju-gung-hap/
├─ backend/
│  ├─ package.json
│  ├─ index.js        # Express 서버, OpenAI 호출
│  └─ saju.js         # 간단한 사주 계산기 (연주 중심, 개선 필요)
├─ frontend/
│  ├─ package.json
│  └─ src/
│     ├─ App.jsx
│     └─ api.js
└─ .env.example

.env.example

OPENAI_API_KEY=sk-...    # OpenAI API Key
OPENAI_API_BASE=https://api.openai.com
PORT=3000

backend/package.json

{
  "name": "saju-backend",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "node-fetch": "^3.4.0",
    "cors": "^2.8.5",
    "body-parser": "^1.20.1",
    "dayjs": "^1.11.9",
    "dayjs-plugin-utc": "^1.0.0",
    "luxon": "^3.4.0",
    "lunar-typescript-pro": "^1.7.6"
  }
}

backend/saju.js

확장된 버전: 이제 연/월/일/시(시주) 까지 계산합니다. lunar-typescript-pro 라이브러리를 우선 사용하고(음력/절기/간지 지원), 없을 경우 간단한 수학적 대체 알고리즘을 사용합니다. 시(時)는 태국·한국 등 표준시의 시각을 기준으로 2시간 단위로 계산합니다.

// saju.js (expanded)
const { DateTime } = require('luxon');
let LunarLib;
try {
  LunarLib = require('lunar-typescript-pro'); // npm: lunar-typescript-pro (추천)
} catch(e) {
  LunarLib = null;
}

const stems = ['갑','을','병','정','무','기','경','신','임','계'];
const branches = ['자','축','인','묘','진','사','오','미','신','유','술','해'];

// 간단 연주(기존 방식 유지)
function yearPillarFromYear(year) {
  const g = (year - 4) % 10;
  const z = (year - 4) % 12;
  return {
    year,
    stem: stems[(g+10)%10],
    branch: branches[(z+12)%12],
    ganzhi: stems[(g+10)%10] + branches[(z+12)%12]
  };
}

// 시간(시지) -> 지지 매핑 (24시간 기준, 각 2시간: 子 23:00-01:00)
function hourToBranchIndex(hour) {
  // hour: 0-23 (local hour)
  // mapping: 23-0 -> 子(0), 1-2->丑(1), 3-4->寅(2), ...
  if (hour === 23) return 0;
  return Math.floor((hour + 1) / 2) % 12;
}

// 일간(일주의 천간) 계산을 위해 율리우스/표준 알고리즘 fallback 사용
// 위키피디아의 'Sexagenary cycle' 계산법을 참고해 단순 구현
function dayStemBranchFromYMD(y, m, d) {
  // Algorithm from public references (see doc) — returns indices
  // Convert month numbers as in the algorithm
  let Y = y;
  let M = m;
  if (M <= 2) {
    Y -= 1;
    M += 12;
  }
  const C = Math.floor(Y / 100);
  const yMod = Y % 100;
  // Julian Day Number (transparent and robust)
  const A = Math.floor(Y/100);
  const B = 2 - A + Math.floor(A/4);
  const jd = Math.floor(365.25*(Y+4716)) + Math.floor(30.6001*(M+1)) + d + B - 1524.5;
  // Use JD to compute sexagenary day index relative to known epoch (e.g., 1984-02-02 = 甲子)
  // We'll map by using a known reference: 1984-02-02 is Jia-Zi (甲子) day (index 0)
  const ref = Date.UTC(1984,1,2); // 1984-02-02 UTC
  const daysDiff = Math.floor((Date.UTC(y, m-1, d) - ref) / (24*3600*1000));
  const dayIndex = ((daysDiff % 60) + 60) % 60; // 0..59
  const sIdx = dayIndex % 10;
  const bIdx = dayIndex % 12;
  return { stemIndex: sIdx, branchIndex: bIdx, stem: stems[sIdx], branch: branches[bIdx], ganzhi: stems[sIdx]+branches[bIdx] };
}

// 전체 사주(연/월/일/시) 계산 함수
function computeFullPillars({ birthISO, timezone }){
  // birthISO: 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:mm' or with seconds
  // timezone: IANA timezone string (e.g. 'Asia/Seoul')
  const dt = timezone ? DateTime.fromISO(birthISO, { zone: timezone }) : DateTime.fromISO(birthISO);
  if (!dt.isValid) throw new Error('Invalid birth date/time');

  const y = dt.year;
  const m = dt.month;
  const day = dt.day;
  const hour = dt.hour;

  // Year pillar: use lunar lib if available to respect Chinese new year/立春 기준
  let yearPillar;
  if (LunarLib && LunarLib.solarToLunar) {
    // lunar-typescript-pro provides干支信息; API details may vary by version
    try {
      const lunar = LunarLib.solarToLunar(y, m, day); // {cyclicalYear, cyclicalMonth, cyclicalDay, ...}
      // assume cyclicalYear like '甲子'
      yearPillar = {
        year: y,
        stem: lunar.ganYear || (lunar.cyclicalYear && lunar.cyclicalYear[0]) || yearPillarFromYear(y).stem,
        branch: lunar.zhiYear || (lunar.cyclicalYear && lunar.cyclicalYear[1]) || yearPillarFromYear(y).branch,
        ganzhi: lunar.cyclicalYear || yearPillarFromYear(y).ganzhi
      };
    } catch(e) {
      yearPillar = yearPillarFromYear(y);
    }
  } else {
    yearPillar = yearPillarFromYear(y);
  }

  // Month pillar: if lunar lib available, use its month ganzhi; else approximate by mapping lunar month
  let monthPillar;
  if (LunarLib && LunarLib.solarToLunar) {
    try {
      const lunar = LunarLib.solarToLunar(y, m, day);
      monthPillar = {
        month: lunar.lunarMonth || m,
        stem: lunar.ganMonth || null,
        branch: lunar.zhiMonth || null,
        ganzhi: lunar.cyclicalMonth || null
      };
    } catch(e){
      monthPillar = { month: m, stem: null, branch: null, ganzhi: null };
    }
  } else {
    monthPillar = { month: m, stem: null, branch: null, ganzhi: null };
  }

  // Day pillar: try lunar lib -> fallback algorithm
  let dayPillar;
  if (LunarLib && LunarLib.solarToLunar) {
    try {
      const lunar = LunarLib.solarToLunar(y, m, day);
      dayPillar = {
        day,
        stem: lunar.ganDay || lunar.cyclicalDay && lunar.cyclicalDay[0],
        branch: lunar.zhiDay || lunar.cyclicalDay && lunar.cyclicalDay[1],
        ganzhi: lunar.cyclicalDay || null
      };
      if (!dayPillar.ganzhi) dayPillar = dayStemBranchFromYMD(y,m,day);
    } catch(e){
      dayPillar = dayStemBranchFromYMD(y,m,day);
    }
  } else {
    dayPillar = dayStemBranchFromYMD(y,m,day);
  }

  // Hour pillar: compute branch from hour, then compute hour stem using day stem index
  const hourBranchIdx = hourToBranchIndex(hour);
  const hourBranch = branches[hourBranchIdx];

  // hour stem calculation: commonly used formula: 时干序号 = (日干序号 * 2 + 时支序号) % 10
  const dayStemIdx = (typeof dayPillar.stemIndex === 'number') ? dayPillar.stemIndex : stems.indexOf(dayPillar.stem);
  const hourStemIdx = ((dayStemIdx * 2) + hourBranchIdx) % 10;
  const hourStem = stems[(hourStemIdx+10)%10];
  const hourGanzhi = hourStem + hourBranch;

  return {
    datetime: dt.toISO(),
    timezone: dt.zoneName,
    yearPillar,
    monthPillar,
    dayPillar,
    hourPillar: { hour, stem: hourStem, branch: hourBranch, ganzhi: hourGanzhi }
  };
}

module.exports = { yearPillarFromYear, computeFullPillars };

Note: 이 확장판은 lunar-typescript-pro 같은 검증된 음력/간지 라이브러리를 우선 사용하도록 설계했습니다. 라이브러리가 없다면 위키피디아 기반의 간단한 알고리즘으로 대체합니다. (정밀도 향상을 위해 한국 천문연구원 자료 또는 Swiss Ephemeris 계열 라이브러리 연동을 권장합니다.)



backend/index.js

// index.js (updated to support full pillars - 연/월/일/시)
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const fetch = (...args) => import('node-fetch').then(({default: f})=>f(...args));
const { computeFullPillars } = require('./saju');

const app = express();
app.use(cors());
app.use(bodyParser.json());

const OPENAI_KEY = process.env.OPENAI_API_KEY;
const OPENAI_BASE = process.env.OPENAI_API_BASE || 'https://api.openai.com';

if (!OPENAI_KEY) {
  console.warn('OPENAI_API_KEY가 설정되어 있지 않습니다. .env 파일을 확인하세요.');
}

// 1) 정밀 사주 계산 (연/월/일/시). 입력은 birthDate (YYYY-MM-DD) 또는 birthDateTime (ISO) 및 timezone(optional)
app.post('/api/compute', (req, res) => {
  try {
    const { name, birthDate /* e.g. '1990-05-03' */, birthDateTime /* e.g. '1990-05-03T14:30' */, timezone } = req.body;
    const input = birthDateTime || birthDate;
    if (!input) return res.status(400).json({ error: 'birthDate or birthDateTime required' });

    const result = computeFullPillars({ birthISO: input, timezone });
    return res.json({ name, input, pillars: result });
  } catch (e) {
    console.error(e);
    return res.status(500).json({ error: e.message });
  }
});

// 2) GPT-5로 궁합 리포트 생성 (unchanged)
app.post('/api/report', async (req, res) => {
  try {
    const { personA, personB, style = '친절하고 전문적인 한국어' } = req.body;
    if (!personA || !personB) return res.status(400).json({ error: 'personA/personB required' });

    const system = `당신은 한국어에 능통한 사주 전문가입니다. 사용자가 제공한 사주(간지)를 바탕으로 두 사람의 궁합 리포트를 작성하세요. 리포트는 요약(강점/주의사항), 세부 해석(연주/월주/일주/시주 가능한 항목 포함), 연애/결혼/커리어 관점 조언, 마지막으로 한 문장의 종합 조언을 포함해야 합니다. 감성적 어조와 전문성을 적절히 섞어 친절하게 작성하세요.`;

    const userContent = `Person A:
${JSON.stringify(personA, null, 2)}

Person B:
${JSON.stringify(personB, null, 2)}

원하는 스타일: ${style}`;

    const payload = {
      model: 'gpt-5',
      messages: [
        { role: 'system', content: system },
        { role: 'user', content: userContent }
      ],
      max_tokens: 800,
      temperature: 0.4
    };

    const r = await fetch(`${OPENAI_BASE}/v1/responses`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${OPENAI_KEY}`
      },
      body: JSON.stringify(payload)
    });

    if (!r.ok) {
      const text = await r.text();
      return res.status(502).json({ error: 'OpenAI API error', details: text });
    }

    const data = await r.json();
    const answer = (data.output && data.output[0] && data.output[0].content) || data;
    return res.json({ raw: data, answer });
  } catch (e) {
    console.error(e);
    return res.status(500).json({ error: e.message });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on ${PORT}`));

---

## frontend/package.json

```json
{
  "name": "saju-frontend",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "axios": "^1.4.0"
  }
}

frontend/src/api.js

import axios from 'axios';
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3000';

export async function computeSaju(payload) {
  return axios.post(`${API_BASE}/api/compute`, payload).then(r=>r.data);
}

export async function generateReport(payload) {
  return axios.post(`${API_BASE}/api/report`, payload).then(r=>r.data);
}

frontend/src/App.jsx

import React, { useState } from 'react';
import { computeSaju, generateReport } from './api';

export default function App(){
  const [a, setA] = useState({ name:'', birthDate:'' });
  const [b, setB] = useState({ name:'', birthDate:'' });
  const [report, setReport] = useState(null);
  const [loading, setLoading] = useState(false);

  async function handleCreate(){
    setLoading(true);
    try {
      const pa = await computeSaju(a);
      const pb = await c

 

 

캔버스 문서(사주 궁합 리포트 자동화 앱 — 코드 및 설치 가이드)에 요청하신 월/일/시까지 계산 가능한 확장 버전 코드를 추가·갱신해 두었습니다.

 

변경 요약

  • backend/package.json: luxon(시간대/로컬타임 처리)과 lunar-typescript-pro(음력·간지 계산 권장 라이브러리)를 의존성에 추가했습니다.
  • backend/saju.js: computeFullPillars() 함수를 추가 — 입력(ISO 문자열 + 선택적 IANA timezone)을 받아 연주/월주/일주/시주(가능한 경우 라이브러리 기반 간지, 없으면 보정된 알고리즘)를 반환합니다.
    • 시간대 처리는 luxon으로 안정적으로 처리합니다.
    • 음력·간지 변환은 lunar-typescript-pro 우선 사용하며, 해당 라이브러리 없을 경우 위키피디아 기반의 일간(日柱) 계산 등으로 폴백합니다.
    • 시주(時柱)는 2시간 단위 지지(子丑寅...)로 계산하고, 시의 천간(時干) 은 통상적으로 쓰이는 공식 时干序号 = (日干序号 * 2 + 时支序号) % 10을 사용했습니다(실무 자료/참고문헌에 따라 표기법이 약간씩 다릅니다).
  • backend/index.js: /api/compute 엔드포인트를 업데이트 — birthDate(YYYY-MM-DD) 혹은 birthDateTime(ISO) + timezone(예: Asia/Seoul)을 받아 computeFullPillars() 결과를 반환합니다.
  • 문서에 설명/주의사항(정확도, 권장 라이브러리 연동, 한국 표준시·立春 처리 등)도 추가했습니다.

 

중요한 참고자료 (특히 정확도/알고리즘 근거)

  • Sexagenary cycle(육십갑자) — 연/월/일 계산의 기본 개념 및 알고리즘적 접근(참고용). 위키백과
  • 음력/간지 라이브러리(예: lunar-typescript-pro) — npm 패키지로 음력·절기·간지 정보를 제공하는 라이브러리들이 존재합니다. 실무에서는 이런 검증된 라이브러리를 연동해 월주/일주의 정확도를 확보하세요. jsDelivrnpm
  • 시주(時柱)·시干 계산 방법 설명(예시 자료) — 시간(2시간 단위) 매핑 및 시간의 천간을 결정하는 실무 표준 설명 참고. (웹 가이드/위키 등) 위키북스소후

다음 추천 작업 (원하시면 바로 진행)

  1. 라이브러리 API 정확화 & 테스트 — lunar-typescript-pro의 실제 메서드/필드명을 코드에 맞게 정밀 조정 및 단위 테스트(샘플 생년월일/시각으로 검증). (권장)
  2. 한국 표준(立春 기준) 처리 — 농력(음력) 기준과 더불어 立春 기준으로 연주를 따르는 방식(전통 명리학에서 자주 쓰임)을 옵션으로 구현.
  3. 일관된 JSON 스키마 — 프론트/백엔드가 바로 저장·검색 가능한 구조(JSON 스키마)로 결과 포맷 고정.
  4. 자동화 테스트 셋 — 다양한 케이스(윤달, 섬머타임, 시간 미기입 등)를 포함한 테스트 케이스 작성.

 

원하시면 지금 (A) 라이브러리 메서드 이름을 정확히 맞춰 실행 가능한 상태로 수정하고, 예시 입력(실제 날짜·시간, 예: 1986-07-17T02:30, Asia/Seoul)으로 확인해 드리거나, (B) 立春 기준 옵션을 포함해 다시 코드 보강해 드릴게요. 어느 쪽으로 진행할까요? 😊

 

 

728x90
반응형