HTTPS로 배포된 React 애플리케이션에서 HTTP API(서울시 문화행사 정보)를 호출하려고 할 때 Mixed Content 에러가 발생한다.
하필 서울시 open api는 https 프로토콜을 제공하지 않기 때문에
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests
(https로 변환하는 방법) 위 방법도 안먹힌다.
// ❌ 직접 HTTP API를 호출하는 경우
fetch('http://openapi.seoul.go.kr:8088/API_KEY/json/culturalEventInfo/1/5/');
// Mixed Content 에러 발생!
Vercel의 Serverless Function을 프록시로 사용하여 HTTP API 호출을 우회한다.
const axios = require('axios');
module.exports = async (req, res) => {
try {
// 서버 사이드에서 HTTP API 호출
const API_KEY = process.env.REACT_APP_API_KEY;
const response = await axios.get(
`http://openapi.seoul.go.kr:8088/${API_KEY}/json/culturalEventInfo/1/5/`
);
// 응답 데이터 확인 및 반환
if (!response.data.culturalEventInfo) {
throw new Error(response.data.RESULT?.MESSAGE || 'API 호출 실패');
}
res.status(200).json(response.data);
} catch (error) {
console.error('Server Error:', error);
res.status(500).json({
error: true,
message: error.message || 'Internal Server Error'
});
}
};
{
"version": 2,
"rewrites": [
{
"source": "/api/:path*",
"destination": "/api/:path*"
}
]
}
const fetchEvents = async () => {
try {
// ✅ HTTPS로 제공되는 Vercel Function 호출
const response = await fetch('/api/events');
const data = await response.json();
if (data.error) {
throw new Error(data.message);
}
setEvents(data.culturalEventInfo.row || []);
} catch (err) {
console.error('Error details:', err);
setError(err.message);
}
};
import { useState, useEffect } from 'react';
import './App.css';
function App() {
const [events, setEvents] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const fetchEvents = async () => {
try {
const response = await fetch('/api/events');
console.log('Response status:', response.status);
const data = await response.json();
console.log('Response data:', data);
if (data.error) {
throw new Error(data.message);
}
setEvents(data.culturalEventInfo.row || []);
} catch (err) {
console.error('Error details:', err);
setError(err.message);
}
};
fetchEvents();
}, []);
if (error) {
return <div>에러 발생: {error}</div>;
}
return (
<div className="App">
<h1>서울시 문화행사 정보</h1>
{events.length === 0 ? (
<p>로딩중...</p>
) : (
<ul>
{events.map((event, index) => (
<li key={index} style={{marginBottom: '20px', textAlign: 'left'}}>
<h3>{event.TITLE}</h3>
<p>분류: {event.CODENAME}</p>
<p>장소: {event.PLACE}</p>
<p>날짜: {event.DATE}</p>
<p>요금: {event.USE_FEE}</p>
{event.MAIN_IMG && (
<img
src={event.MAIN_IMG}
alt={event.TITLE}
style={{maxWidth: '200px'}}
/>
)}
</li>
))}
</ul>
)}
</div>
);
}
export default App;
[브라우저] → HTTPS → [Vercel Function] → HTTP → [서울시 API]
브라우저: "물건이 왔는데 가져오기 무서워요.. Vercel Function님 대신 가져와주세요ㅎ" (브라우저가 Vercel Function에 HTTPS로 요청)
Vercel Function: 응 알겠어. 나는 브라우저가 아니라 서버라서 http/https 모두 가져올 수 있어. (Vercel이 서버에서 HTTP API 호출)
Vercel Function: 자 받아. 가져왔어. (Vercel이 데이터를 HTTPS로 브라우저에 전달)
/api/events.js
파일 생성 및 서버리스 함수 구현vercel.json
설정으로 API 라우트 경로 매핑.env.local
파일에 환경변수를 설정.이렇게 Vercel Serverless Function을 활용하면 Mixed Content 에러를 해결하면서도 보안을 유지할 수 있다! 이것이 바로 서버리스 아키텍처라고 한다. 엥? vercel 서버를 사용하는데 왜 서버less 지? => 내가 서버 관리안하고 vercel서비스가 서버 관리를 해주므로 서버리스라고 하는 것이다.