이 예제는 JSON 요청과 응답을 처리하는 ESP32에서 REST API 서버를 만드는 방법을 보여주며, 현대적인 웹 애플리케이션과 모바일 앱 백엔드에 적합합니다.
REST API 엔드포인트 JSON 요청/응답 처리를 포함
POST 요청 처리 JSON 데이터 파싱을 포함
GET 엔드포인트 데이터 조회를 위한
전문적인 JSON 응답 적절한 HTTP 상태 코드와 함께
오류 처리 적절한 JSON 오류 메시지와 함께
CORS 지원 교차 출처 요청을 위한
| 1 | × | ESP32 ESP-WROOM-32 개발 모듈 | 쿠팡 | 아마존 | |
| 1 | × | USB 케이블 타입-A to 타입-C (USB-A PC용) | 쿠팡 | 아마존 | |
| 1 | × | USB 케이블 타입-C to 타입-C (USB-C PC용) | 아마존 | |
| 1 | × | (추천) ESP32용 스크루 터미널 확장 보드 | 쿠팡 | 아마존 | |
| 1 | × | (추천) Breakout Expansion Board for ESP32 | 쿠팡 | 아마존 | |
| 1 | × | (추천) ESP32용 전원 분배기 | 쿠팡 | 아마존 | |
공개: 이 포스팅 에 제공된 일부 링크는 아마존 제휴 링크입니다. 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
JSON 형식으로 타임스탬프가 포함된 성공 메시지를 반환합니다.
응답:
{
"status": "success",
"message": "GET request received",
"timestamp": 12345
}
타임스탬프 값은 ESP32가 시작된 이후 경과한 밀리초를 나타냅니다(millis() 함수에서 얻은 값).
JSON 데이터를 수신하고 수신된 키 값을 반환합니다.
요청:
응답:
{
"status": "success",
"message": "Data received",
"received_key": "your_value"
}
WebServerJson.ino 파일에서 WiFi 자격 증명을 편집하세요:
const char WIFI_SSID[] = "YOUR_WIFI_SSID";
const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";
ESP32를 컴퓨터에 연결하십시오
Arduino IDE에서 올바른 보드와 포트를 선택하십시오
WebServerJson.ino 스케치를 업로드하십시오
시리얼 모니터를 열고 (9600bps)
WiFi 연결이 완료될 때까지 기다리십시오
표시된 IP 주소를 기록해 두십시오
시리얼 모니터에 IP 주소가 표시되지 않는 경우 ESP32 보드의 리셋 버튼을 누르십시오
your-esp32-ip를 시리얼 모니터에 표시되는 실제 IP 주소로 바꿉니다.
curl -X GET http://your-esp32-ip/api/data
예상 출력:
C:\Users\youruser>curl -X GET http://your-esp32-ip/api/data
{
"status": "success",
"message": "GET request received",
"timestamp": 12345
}
타임스탬프는 ESP32가 시작된 이후 경과한 밀리초를 나타냅니다.
curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test_value\"}"
출력 예:
C:\Users\youruser>curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test_value\"}"
{
"status": "success",
"message": "Data received",
"received_key": "test_value"
}
curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"hello_world\"}"
예상 출력:
C:\Users\youruser>curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"hello_world\"}"
{
"status": "success",
"message": "Data received",
"received_key": "hello_world"
}
curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{invalid json}"
예상 출력:
C:\Users\youruser>curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{invalid json}"
{
"status": "error",
"message": "Invalid JSON"
}
curl -X POST http://your-esp32-ip/api/data
예상 출력:
C:\Users\youruser>curl -X POST http://your-esp32-ip/api/data
{
"status": "error",
"message": "No JSON data received"
}
curl -X PUT http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test\"}"
예상 출력:
C:\Users\youruser>curl -X PUT http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test\"}"
{
"status": "error",
"message": "Method not allowed"
}
curl -X GET http://your-esp32-ip/api/nonexistent
예상 출력:
C:\Users\youruser>curl -X GET http://your-esp32-ip/api/nonexistent
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>404 Not Found</title>
</head>
<body>
<h1>404 - Page Not Found</h1>
<p>Method: GET</p>
<p>Sorry, we couldn't find that page!</p>
<a href="/">Return to Home</a>
</body>
</html>
새로운 GET 요청을 만듭니다
URL을 http://your-esp32-ip/api/data로 설정합니다
요청을 보냅니다
응답에 상태, 메시지 및 타임스탬프가 포함되어 있는지 확인합니다
새로운 POST 요청을 만듭니다
URL을 http://your-esp32-ip/api/data로 설정합니다
헤더 추가: Content-Type: application/json
JSON 본문 추가: {"key": "test_value"}
요청을 전송합니다
응답에 수신된 키 값이 표시되는지 확인합니다
예제 코드에 나와 있는 대로 잘못된 요청을 보내 오류 처리를 테스트하세요:
curl -X POST http://your-esp32-ip/api/data
예상: {"status": "오류","message": "JSON 데이터가 수신되지 않았습니다"}
curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{invalid json}"
예상: {"status": "error","message": "잘못된 JSON"}
curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"other_field\": \"value\"}"
예상: 키는 예제 코드에 따라 기본값으로 "none"을 가지게 됩니다: doc["key"] | "none"
curl -X DELETE http://your-esp32-ip/api/data
예상: {"status": "오류","message": "허용되지 않는 메서드"}
server.addRoute("/api/data", handleApiData);
모든 핸들러 함수는 이 시그니처를 따라야 한다:
void handleApiData(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) {
}
void handleApiData(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) {
Serial.print("[API] ");
Serial.print(method);
Serial.print(" request received");
if (method == "POST") {
if (jsonData.length() == 0) {
Serial.println("Error: No JSON data received");
client.println("HTTP/1.1 400 Bad Request");
client.println("Content-Type: application/json");
client.println("Connection: close");
client.println();
client.print("{\"status\": \"error\",\"message\": \"No JSON data received\"}");
return;
}
StaticJsonDocument<200> doc;
DeserializationError error = deserializeJson(doc, jsonData);
if (!error) {
const char* key = doc["key"] | "none";
String response = JSON_RESPONSE;
response.replace("%KEY%", key);
server.sendResponse(client, response.c_str(), "application/json");
} else {
client.println("HTTP/1.1 400 Bad Request");
client.println("Content-Type: application/json");
client.println("Connection: close");
client.println();
client.print("{\"status\": \"error\",\"message\": \"Invalid JSON\"}");
}
} else if (method == "GET") {
String response = JSON_GET_RESPONSE;
response.replace("%TIMESTAMP%", String(millis()));
server.sendResponse(client, response.c_str(), "application/json");
} else {
client.println("HTTP/1.1 405 Method Not Allowed");
client.println("Content-Type: application/json");
client.println("Connection: close");
client.println();
client.print("{\"status\":\"error\",\"message\":\"Method not allowed\"}");
}
}
async function controlLED(action) {
try {
const response = await fetch('http://your-esp32-ip/api/led', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: action })
});
const result = await response.json();
console.log('LED control result:', result);
} catch (error) {
console.error('Error:', error);
}
}
async function getSensorData() {
try {
const response = await fetch('http://your-esp32-ip/api/sensor');
const data = await response.json();
console.log('Sensor data:', data);
} catch (error) {
console.error('Error:', error);
}
}
import requests
import json
def control_led(action):
url = "http://your-esp32-ip/api/led"
data = {"action": action}
response = requests.post(url, json=data)
return response.json()
def get_status():
url = "http://your-esp32-ip/api/status"
response = requests.get(url)
return response.json()
result = control_led("on")
print(result)
status = get_status()
print(status)
실제 예제 코드에 따라 서로 다른 오류가 특정 메시지를 반환합니다:
{
"status": "error",
"message": "No JSON data received"
}
다음의 경우 반환됩니다: JSON 본문이 없는 POST 요청이 전송될 때
{
"status": "error",
"message": "Invalid JSON"
}
다음과 같은 경우에 반환됩니다: JSON 데이터를 구문 분석할 수 없을 때(구문 오류)
{
"status": "error",
"message": "Method not allowed"
}
다음과 같은 경우에 반환됩니다: 지원되지 않는 HTTP 메서드(PUT, DELETE, PATCH 등)를 사용할 때
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>404 Not Found</title>
</head>
<body>
<h1>404 - Page Not Found</h1>
<p>Method: [HTTP_METHOD]</p>
<p>Sorry, we couldn't find that page!</p>
<a href="/">Return to Home</a>
</body>
</html>
다음 경우에 반환됩니다: 존재하지 않는 엔드포인트에 접근할 때
void handleApiStatus(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) {
if (method == "GET") {
String json = "{\"status\":\"online\",\"uptime\":" + String(millis() / 1000) + "}";
server.sendResponse(client, json.c_str(), "application/json");
} else {
client.println("HTTP/1.1 405 Method Not Allowed");
client.println("Content-Type: application/json");
client.println("Connection: close");
client.println();
client.print("{\"status\":\"error\",\"message\":\"Method not allowed\"}");
}
}
server.addRoute("/api/status", handleApiStatus);
예제는 일관된 JSON 형식을 위해 템플릿 문자열을 사용합니다:
const char JSON_RESPONSE[] PROGMEM = R"rawliteral(
{
"status": "success",
"message": "Data received",
"received_key": "%KEY%"
}
)rawliteral";
String response = JSON_RESPONSE;
response.replace("%KEY%", extractedValue);
server.sendResponse(client, response.c_str(), "application/json");
연결 문제
API 엔드포인트에 연결할 수 없으면:
ESP32가 WiFi에 연결되어 있는지 확인하세요(시리얼 모니터에서 확인)
클라이언트와 ESP32가 동일한 네트워크에 있는지 확인하세요
IP 주소가 정확한지 확인하세요
ESP32가 리셋되지 않았는지 확인하세요(리셋되면 IP가 변경됩니다)
JSON 구문 분석 오류
유효하지 않은 JSON 응답을 받으면:
Content-Type 헤더가 application/json으로 설정되어 있는지 확인하십시오
JSON 구문이 유효한지 확인하십시오
특수 문자가 올바르게 이스케이프되었는지 확인하십시오
JSON 페이로드가 너무 크지 않은지 확인하십시오(현재 제한: 200바이트)
POST 요청 이슈
POST 요청이 "No JSON data received"를 반환하면:
요청에 JSON 본문을 보내고 있는지 확인합니다.
Content-Length 헤더가 올바르게 설정되었는지 확인합니다.
HTTP 메서드가 실제로 POST인지 확인합니다.
간단한 JSON 예시로 {"key": "test"}를 사용해 테스트합니다.
메모리 문제
ESP32가 응답하지 않으면:
메모리 사용량 모니터링 - StaticJsonDocument는 200바이트를 사용합니다.
필요하면 JSON_RESPONSE 템플릿 크기를 줄이십시오.
가변 크기의 데이터에는 DynamicJsonDocument 사용을 고려하십시오.
사용자 정의 핸들러 함수의 메모리 누수를 확인하십시오.
JSON 처리 최적화
StaticJsonDocument<200> doc;
StaticJsonDocument<1024> doc;
String response;
response.reserve(256);
response = JSON_RESPONSE;
response.replace("%KEY%", value);
효율적인 응답 처리
client.print(F("{\"status\":\"ok\",\"value\":"));
client.print(sensorValue);
client.print(F("}"));
const char LARGE_RESPONSE[] PROGMEM = R"({
"status": "success",
"data": { ... }
})";
URL 매개변수 처리를 위해 WebServerQueryStrings.ino를 살펴보세요
실시간 통신을 위해 WebServerWithWebSocket.ino를 시도해 보세요
데이터베이스나 클라우드 서비스와의 통합을 고려해 보세요