https://github.com/onesound71/WebContentIntegration
GitHub - onesound71/WebContentIntegration
Contribute to onesound71/WebContentIntegration development by creating an account on GitHub.
github.com
웹 개발을 하다 보면 여러 서버의 웹 페이지를 하나로 통합해야 하는 상황이 생깁니다. 보통은 iframe을 사용하는 것이 일반적이지만, iframe은 여러 제약사항이 있죠. 스타일링의 어려움, 보안 정책, 반응형 디자인의 한계 등등...
그래서 iframe 없이 서버 A에서 서버 B의 웹 페이지를 완전히 컨트롤할 수 있는 방법을 실험해봤습니다. 완벽한 해결책은 아니지만, 이런 식으로도 구현이 가능하구나 정도로 이해해주시면 됩니다. 더 좋은 방법이 생각나면 다시 공유하겠습니다!
핵심 아이디어: HTML Fetch 통합
기본 아이디어는 간단합니다:
- 서버 A가 서버 B의 완성된 HTML을 통째로 가져온다
- 가져온 HTML을 필요에 따라 수정한다
- 클라이언트에게 마치 서버 A의 페이지인 것처럼 제공한다
- JavaScript API 호출은 절대 URL로 서버 B를 직접 타겟팅한다
@app_a.get("/", response_class=HTMLResponse)
async def read_root():
async with httpx.AsyncClient(timeout=10.0) as client:
# 서버 B의 대시보드 HTML을 통째로 가져오기
response = await client.get(f"{SERVER_B_URL}/dashboard")
if response.status_code == 200:
html_content = response.text
# 필요시 HTML 수정 (스크립트 경로 등)
modified_html = modify_html_for_integration(html_content)
return HTMLResponse(content=modified_html)
프로젝트 구조와 실행
간단한 구조
WebContentIntegrationDemo/
├── server_a.py # 메인 서버 (포트 8000)
├── server_b.py # 데이터 서버 (포트 8001)
├── requirements.txt # FastAPI, uvicorn, httpx 등
└── test_buttons.html # 테스트 페이지
실행 방법
# 터미널 1: 데이터 서버 먼저 실행
python server_b.py
# 터미널 2: 메인 서버 실행
python server_a.py
그러면 http://localhost:8000
에 접속했을 때, 서버 B의 대시보드가 서버 A를 통해 제공됩니다.
주요 구현 포인트
1. JavaScript API 라우팅
가장 중요한 부분입니다. 통합된 페이지에서 API 호출이 올바른 서버로 가도록 절대 URL을 사용합니다:
// ✅ 올바른 방법: 절대 URL로 서버 B 타겟팅
async function refreshWeather() {
const response = await fetch('http://localhost:8001/api/weather', {
method: 'GET',
mode: 'cors',
headers: {'Content-Type': 'application/json'}
});
if (response.ok) {
const data = await response.json();
updateWeatherDisplay(data);
}
}
// ❌ 이렇게 하면 서버 A로 요청이 가버림
// const response = await fetch('/api/weather');
2. CORS 설정
서버 간 통신을 위해 CORS 설정이 필수입니다:
from fastapi.middleware.cors import CORSMiddleware
app_b.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:8000"], # 서버 A 도메인
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
3. 실시간 데이터 통합
통합된 페이지에서 여러 API를 동시에 호출하여 실시간 데이터를 보여줍니다:
async function refreshAllData() {
const startTime = performance.now();
try {
// 병렬로 여러 API 호출
const [weatherResponse, analyticsResponse, usersResponse] =
await Promise.all([
fetch('http://localhost:8001/api/weather'),
fetch('http://localhost:8001/api/analytics'),
fetch('http://localhost:8001/api/users')
]);
// 응답 처리 및 UI 업데이트
const weather = await weatherResponse.json();
const analytics = await analyticsResponse.json();
const users = await usersResponse.json();
updateDashboard(weather, analytics, users);
} catch (error) {
console.error('데이터 로드 실패:', error);
showErrorMessage('일부 데이터를 불러올 수 없습니다.');
}
const totalTime = Math.round(performance.now() - startTime);
console.log(`전체 데이터 로드 시간: ${totalTime}ms`);
}
구현된 주요 기능들
실시간 모니터링 대시보드
- 사용자 통계, 상품 정보, 방문자 현황
- 날씨 정보 및 시스템 상태
- 자동/수동 데이터 새로고침
API 테스트 시스템
- 개별 API 성능 테스트
- 전체 API 일괄 테스트
- 응답 시간 및 데이터 크기 분석
- 실시간 테스트 로그
에러 처리 및 헬스 체크
@app_a.get("/health")
async def health_check():
try:
# 서버 B 상태 확인
async with httpx.AsyncClient(timeout=5.0) as client:
response = await client.get(f"{SERVER_B_URL}/health")
server_b_status = response.status_code == 200
except:
server_b_status = False
return {
"server_a": "healthy",
"server_b": "healthy" if server_b_status else "unreachable",
"integration": "active" if server_b_status else "degraded"
}
장단점 분석
장점
- iframe의 제약 없음: 스타일링 자유도, 반응형 디자인 가능
- 단일 도메인 경험: 사용자 입장에서는 하나의 사이트처럼 느껴짐
- 실시간 통합: 서버 간 데이터를 자유롭게 조합 가능
- 성능 모니터링: 각 API 호출 시간을 정밀하게 측정 가능
단점
- 서버 부하: 모든 요청이 서버 A를 거쳐야 함
- 복잡성: 에러 처리, 라우팅 등이 복잡해짐
- 보안 고려사항: CORS 설정, API 경로 노출 등
- 실시간성 제한: 서버 B의 실시간 업데이트를 완전히 반영하기 어려움
실제 활용 사례
이런 방식은 다음과 같은 상황에서 유용할 수 있습니다:
마이크로서비스 UI 통합
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 사용자 │ │ 주문 서비스 │ │ 재고 서비스 │
│ 대시보드 │◄──►│ UI │ │ UI │
│ (서버 A) │ │ (서버 B) │ │ (서버 C) │
└─────────────┘ └─────────────┘ └─────────────┘
레거시 시스템 통합
기존 시스템의 UI를 큰 수정 없이 새로운 플랫폼에 통합할 때 유용합니다.
개선 가능한 부분들
현재 구현은 실험적 성격이 강합니다. 실제 운영에서는 다음 사항들을 고려해야 합니다:
캐싱 전략
# HTML 응답 캐싱으로 성능 향상
@lru_cache(maxsize=100)
async def get_cached_dashboard():
# 캐시된 HTML 반환
pass
더 나은 라우팅
- API Gateway 패턴 적용
- 로드 밸런싱 고려
- 서비스 디스커버리 연동
보안 강화
- JWT 토큰 기반 인증
- API 키 관리
- Rate Limiting
마치며
iframe 없이 서버 간 웹 페이지를 통합하는 실험을 해봤습니다. 완벽한 해결책은 아니지만, "이런 식으로도 구현이 가능하구나" 정도로 이해해주시면 됩니다.
실제로는 다음과 같은 대안들도 고려해볼 만합니다:
- Module Federation (Webpack 5)
- Single-SPA 같은 마이크로프론트엔드 프레임워크
- Server-Side Includes (SSI)
- Edge Side Includes (ESI)
더 좋은 방법이나 개선 아이디어가 생각나면 다시 공유하겠습니다!
혹시 비슷한 요구사항이 있으시거나 다른 접근 방법을 시도해보신 분이 계시다면 댓글로 공유해주세요. 함께 더 나은 방법을 찾아보면 좋겠습니다! 🚀
'나의 IT 기억' 카테고리의 다른 글
🚀 LangChain 생태계 완벽 가이드: 직장인이 꼭 알아야 할 AI 개발 도구들 (0) | 2025.06.02 |
---|---|
Redis 말고 공유 메모리? 웹서버 여러대에서 증권 시세를 빠르게 공유하는 새로운 방법 (0) | 2025.06.01 |
🔐 SSO(Single Sign-On)란? 하나의 로그인으로 모든 서비스 이용하기 (0) | 2025.05.30 |
💼 업무 자동화 전쟁: n8n vs Langflow vs Make - 당신의 팀에겐 어떤 무기가 필요할까? (6) | 2025.05.29 |
🤖 그냥해보세요! Qwen2.5VL 멀티모달 모델로 이미지 분석하기 (0) | 2025.05.28 |