feat: scaffold v2 project structure with Docker, FastAPI, and config

동작하는 최소 코드 수준의 v2 스캐폴딩:

- docker-compose.yml: postgres, fastapi, kordoc, frontend, caddy
- app/: FastAPI 백엔드 (main, core, models, ai, prompts)
- services/kordoc/: Node.js 문서 파싱 마이크로서비스
- gpu-server/: AI Gateway + GPU docker-compose
- frontend/: SvelteKit 기본 구조
- migrations/: PostgreSQL 초기 스키마 (documents, tasks, processing_queue)
- tests/: pytest conftest 기본 설정
- config.yaml, Caddyfile, credentials.env.example 갱신

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-02 10:20:15 +09:00
parent b338e6e424
commit 131dbd7b7c
37 changed files with 1122 additions and 19 deletions

View File

@@ -0,0 +1,12 @@
FROM node:20-slim
WORKDIR /app
COPY package.json .
RUN npm install --production
COPY server.js .
EXPOSE 3100
CMD ["node", "server.js"]

View File

@@ -0,0 +1,13 @@
{
"name": "kordoc-service",
"version": "1.0.0",
"description": "HWP/HWPX/PDF 문서 파싱 마이크로서비스",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.0",
"kordoc": "^1.7.0"
}
}

57
services/kordoc/server.js Normal file
View File

@@ -0,0 +1,57 @@
/**
* kordoc 마이크로서비스 — HWP/HWPX/PDF → Markdown 변환 API
*/
const express = require('express');
const app = express();
const PORT = 3100;
app.use(express.json({ limit: '500mb' }));
// 헬스체크
app.get('/health', (req, res) => {
res.json({ status: 'ok', service: 'kordoc' });
});
// 문서 파싱
app.post('/parse', async (req, res) => {
try {
const { filePath } = req.body;
if (!filePath) {
return res.status(400).json({ error: 'filePath is required' });
}
// TODO: kordoc 라이브러리 연동 (Phase 1에서 구현)
// const kordoc = require('kordoc');
// const result = await kordoc.parse(filePath);
// return res.json(result);
return res.json({
markdown: '',
metadata: {},
format: 'unknown',
message: 'kordoc 파싱은 Phase 1에서 구현 예정'
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// 문서 비교
app.post('/compare', async (req, res) => {
try {
const { filePathA, filePathB } = req.body;
if (!filePathA || !filePathB) {
return res.status(400).json({ error: 'filePathA and filePathB are required' });
}
// TODO: kordoc compare 구현 (Phase 2)
return res.json({ diffs: [], message: 'compare는 Phase 2에서 구현 예정' });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.listen(PORT, () => {
console.log(`kordoc-service listening on port ${PORT}`);
});