ESP32 WebServer 라이브러리 참조

개요

DIYables_ESP32_WebServer 라이브러리는 ESP32 보드에서 WebSocket 지원이 포함된 다중 페이지 웹 서버를 구축하기 위한 포괄적인 솔루션을 제공합니다.

준비물

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용 전원 분배기 쿠팡 | 아마존
공개: 이 포스팅 에 제공된 일부 링크는 아마존 제휴 링크입니다. 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

설치

자세한 사용 방법

다음 지시를 단계별로 따라가세요:

  • ESP32를 처음 사용하는 경우, Arduino IDE에서 ESP32 환경 설정에 대한 튜토리얼을 참조하십시오.
  • ESP32 보드를 USB 케이블로 컴퓨터에 연결합니다.
  • 컴퓨터에서 Arduino IDE를 실행합니다.
  • 적절한 ESP32 보드(예: ESP32)와 COM 포트를 선택합니다.
  • Arduino IDE의 왼쪽에 있는 Library Manager 아이콘을 클릭하여 라이브러리 관리자를 엽니다.
  • Web Server for ESP32를 검색하고 DIYables의 mWebSockets를 찾습니다.
  • Install 버튼을 클릭하여 mWebSockets 라이브러리를 추가합니다.
ESP32 웹 서버 라이브러리

웹소켓 지원(내장)

WebSocket 기능이 이제 이 라이브러리에 직접 통합되었습니다!

웹소켓 구현은 Dawid Kurek의 훌륭한 mWebSockets 라이브러리에 기반하며, ESP32에 맞게 특별히 통합되고 최적화되어 사용하기 쉽게 만들어졌습니다:

  • 추가 라이브러리 설치가 필요하지 않습니다 - WebSocket 지원이 내장되어 있습니다
  • ESP32에 최적화되었습니다 - 플랫폼별 구현이 간소화되었습니다
  • WiFi 호환성 - ESP32의 네이티브 WiFi 스택을 사용합니다
  • RFC 6455 준수 - 전체 WebSocket 프로토콜 지원
  • 실시간 통신 - 양방향 데이터 교환

사용법:

  • 모든 것에 대해(웹 서버 + 웹소켓): #include <DIYables_ESP32_WebServer.h>를 사용하세요
  • 그게 다야! - 필요할 때 웹소켓 기능이 자동으로 사용 가능해집니다
  • 메모리 효율적 - 사용되지 않는 웹소켓 코드는 컴파일러에 의해 자동으로 최적화에서 제외됩니다

크레딧: WebSocket 구현은 Dawid Kurek가 만든 mWebSockets 라이브러리에서 가져와(LGPL-2.1 라이선스) 되었으며, ESP32와의 원활한 호환성을 위해 수정 및 통합되었습니다.

라이브러리 클래스

DIYables_ESP32_WebServer 클래스

웹 서버를 생성하고 관리하기 위한 메인 클래스.

생성자

DIYables_ESP32_WebServer(int port = 80)

지정된 포트에서 웹 서버 인스턴스를 생성합니다(기본값: 80).

방법

시작()
void begin() void begin(const char* ssid, const char* password)

웹 서버를 시작하고 들어오는 연결을 수신하기 시작합니다.

오버로딩된 버전들:

  • begin(): 서버만 시작합니다(와이파이는 애플리케이션에서 별도로 연결해야 합니다)
  • begin(ssid, password): 한 번의 호출로 WiFi에 연결하고 서버를 시작합니다(레거시 방식)
setNotFoundHandler()
void setNotFoundHandler(RouteHandler handler)

404 Not Found 응답에 대한 사용자 정의 핸들러를 설정합니다.

와이파이 상태 출력()
void printWifiStatus()

WiFi 연결 상태와 IP 주소를 시리얼 모니터에 출력합니다.

클라이언트 처리()
void handleClient()

들어오는 클라이언트 요청을 처리합니다. 이는 메인 루프에서 반복적으로 호출되어야 합니다.

on()
void on(const String &uri, HTTPMethod method, THandlerFunction fn) void on(const String &uri, THandlerFunction fn) // defaults to GET

특정 URI와 HTTP 메서드에 대한 핸들러 함수를 등록합니다.

매개변수:

  • uri: URI 경로(예: "/", "/led", "/api/data")
  • method: HTTP 메서드(GET, POST, PUT, DELETE 등)
  • fn: 경로에 접근했을 때 실행되는 핸들러 함수

참고: 이 라이브러리는 on() 대신 addRoute() 메서드를 사용합니다. 아래에 올바른 사용법이 나와 있습니다.

경로 추가()
void addRoute(const String &uri, RouteHandler handler)

특정 URI에 대한 핸들러 함수를 등록합니다. 이것이 라이브러리에서 실제로 사용되는 메서드입니다.

RouteHandler 함수 형식:

라우트 핸들러는 이 정확한 시그니처를 따라야 합니다:

void handlerFunction(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData)

매개변수:

  • client: 응답 전송을 위한 WiFiClient 참조
  • method: 문자열로 된 HTTP 메서드 ("GET", "POST", 등)
  • request: 전체 요청 URI
  • params: 쿼리 매개변수 (QueryParams 객체)
  • jsonData: POST 요청의 JSON 페이로드 (GET은 비어 있음)

핸들러 구현 예시:

  1. 기본 GET 핸들러:
void handleHome(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { if (method == "GET") { String response = "<html><body><h1>Hello World</h1></body></html>"; server.sendResponse(client, response.c_str()); } } void setup() { server.addRoute("/", handleHome); }
  1. JSON API 핸들러 (GET 및 POST):
void handleApiData(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { if (method == "POST") { if (jsonData.length() == 0) { 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; } // Process JSON data StaticJsonDocument<200> doc; DeserializationError error = deserializeJson(doc, jsonData); if (error) { 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\"}"); return; } const char* key = doc["key"] | "none"; String response = "{\"status\":\"success\",\"received_key\":\"" + String(key) + "\"}"; server.sendResponse(client, response.c_str(), "application/json"); } else if (method == "GET") { String response = "{\"status\":\"success\",\"message\":\"GET request received\"}"; 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\"}"); } } void setup() { server.addRoute("/api/data", handleApiData); }
  1. 쿼리 매개변수 핸들러:
void handleLedControl(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { if (method == "GET") { // Access query parameters String action = params.getValue("action"); // ?action=on if (action == "on") { digitalWrite(LED_PIN, HIGH); server.sendResponse(client, "LED turned ON"); } else if (action == "off") { digitalWrite(LED_PIN, LOW); server.sendResponse(client, "LED turned OFF"); } else { client.println("HTTP/1.1 400 Bad Request"); client.println("Content-Type: text/plain"); client.println("Connection: close"); client.println(); client.print("Invalid action. Use ?action=on or ?action=off"); } } } void setup() { server.addRoute("/led", handleLedControl); }
응답 보내기()
void sendResponse(WiFiClient& client, const char* content, const char* contentType = "text/html")

클라이언트에게 HTTP 응답을 보낸다.

매개변수:

  • client: 핸들러 함수에서 제공되는 WiFiClient 참조
  • content: 응답 본문 콘텐츠
  • contentType: 응답의 MIME 타입 (기본값: "text/html")

사용 예시:

// Send HTML response server.sendResponse(client, "<h1>Hello World</h1>"); // Send JSON response server.sendResponse(client, "{\"status\":\"ok\"}", "application/json"); // Send plain text server.sendResponse(client, "Success", "text/plain");
인증 방법
인증 활성화()
void enableAuthentication(const char* username, const char* password, const char* realm = "ESP32 Server")

모든 경로에 대해 HTTP 기본 인증을 활성화합니다. 활성화되면 모든 경로에서 인증이 필요합니다.

매개변수:

  • username: 인증에 사용할 사용자 이름(최대 31자)
  • password: 인증에 사용할 비밀번호(최대 31자)
  • realm: 브라우저에 표시되는 인증 영역(최대 63자, 선택 사항)

사용 예시:

// Enable authentication with default realm server.enableAuthentication("admin", "password123"); // Enable authentication with custom realm server.enableAuthentication("admin", "secure456", "My ESP32 Device");
인증 비활성화()
void disableAuthentication()

인증을 비활성화하여 모든 경로를 다시 공개적으로 접근 가능하게 만듭니다.

사용 예시:

server.disableAuthentication();
인증 활성화 여부()
bool isAuthenticationEnabled()

인증이 현재 활성화되어 있으면 true를 반환하고, 그렇지 않으면 false를 반환합니다.

사용 예시:

if (server.isAuthenticationEnabled()) { Serial.println("Authentication is active"); } else { Serial.println("All routes are public"); }
send401()
void send401(WiFiClient& client)

적절한 WWW-Authenticate 헤더를 포함한 401 Unauthorized 응답을 보냅니다. 인증 실패 시 자동으로 호출되지만, 커스텀 핸들러에서 수동으로 사용할 수도 있습니다.

사용 예시:

// Manual 401 response in a custom handler server.send401(client);
수동 응답 전송

HTTP 헤더와 상태 코드를 더 자세히 제어하려면:

void sendCustomResponse(WiFiClient& client) { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: application/json"); client.println("Access-Control-Allow-Origin: *"); client.println("Connection: close"); client.println(); client.print("{\"custom\":\"response\"}"); }

쿼리 매개변수 접근

쿼리 파라미터 구조

The QueryParams 객체에는 URL에서 구문 분석된 쿼리 매개변수가 포함되어 있습니다:

struct QueryParams { int count; // Number of parameters struct { const char* key; // Parameter name const char* value; // Parameter value } params[MAX_PARAMS]; }

쿼리 매개변수에 접근하기

void handleWithParams(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { // Access specific parameter by looping through String unit = "C"; // default value for (int i = 0; i < params.count; i++) { if (String(params.params[i].key) == "unit") { unit = params.params[i].value; break; } } // Use the parameter String response = "Unit selected: " + unit; server.sendResponse(client, response.c_str()); } // URL examples: // /temperature?unit=F // /led?state=on&brightness=50

매개변수 헬퍼 함수

더 쉽게 매개변수에 접근하기 위한 헬퍼 함수 만들기:

String getParam(const QueryParams& params, const String& key, const String& defaultValue = "") { for (int i = 0; i < params.count; i++) { if (String(params.params[i].key) == key) { return String(params.params[i].value); } } return defaultValue; } bool hasParam(const QueryParams& params, const String& key) { for (int i = 0; i < params.count; i++) { if (String(params.params[i].key) == key) { return true; } } return false; } // Usage in handlers: void handleLed(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { String state = getParam(params, "state", "off"); int brightness = getParam(params, "brightness", "100").toInt(); if (state == "on") { digitalWrite(LED_PIN, HIGH); server.sendResponse(client, "LED turned ON with brightness " + String(brightness)); } else { digitalWrite(LED_PIN, LOW); server.sendResponse(client, "LED turned OFF"); } }

웹소켓 클래스(내장)

웹소켓 서버
WebSocketServer wsServer(81); // Port 81 for WebSocket

net::WebSocketServer의 별칭 - 초보자용으로 간소화됨.

웹소켓
WebSocket ws;

net::WebSocket의 별칭 - 웹소켓 연결을 나타냅니다.

웹소켓 메서드
시작()
void begin()

웹소켓 서버를 시작합니다.

루프()
void loop()

WebSocket 이벤트를 처리합니다. 메인 루프에서 이 함수를 호출하십시오.

onConnection()
void onConnection([](WebSocket &ws) { // Handle new WebSocket connection });

새 웹소켓 연결에 대한 콜백을 설정합니다.

온메시지()
void onMessage([](WebSocket &ws, const WebSocket::DataType dataType, const char *message, uint16_t length) { // Handle incoming WebSocket message });

수신되는 웹소켓 메시지에 대한 콜백을 설정합니다.

닫기()
void onClose([](WebSocket &ws, const WebSocket::CloseCode code, const char *reason, uint16_t length) { // Handle WebSocket connection close });

WebSocket 연결 종료 시 호출될 콜백을 설정합니다.

보내다()
void send(const String &message) void send(const char *message, size_t length)

WebSocket을 통해 메시지를 보냅니다.

닫기()
void close()

웹소켓 연결을 닫습니다.

추가 웹소켓 메서드
방송텍스트()
void broadcastTXT(const char* payload) void broadcastTXT(const String& payload)

연결된 모든 웹소켓 클라이언트에게 텍스트 메시지를 전송합니다.

브로드캐스트BIN()
void broadcastBIN(const uint8_t* payload, size_t length)

연결된 모든 WebSocket 클라이언트에 이진 데이터를 브로드캐스트합니다.

연결된 클라이언트들()
size_t connectedClients()

현재 연결되어 있는 WebSocket 클라이언트의 수를 반환합니다.

듣고 있는지
bool isListening()

WebSocket 서버가 연결을 활성적으로 수신 대기 중일 때 true를 반환합니다.

웹소켓 이벤트 유형
데이터 타입 열거형
  • WebSocket::DataType::TEXT - 텍스트 메시지 타입
  • WebSocket::DataType::BINARY - 이진 데이터 타입
CloseCode 열거형

연결 종료 사유에 대한 표준 WebSocket 종료 코드.

고급 WebSocket 사용법

이벤트 핸들러 설정

void setup() { Serial.begin(9600); // Initialize WiFi and servers WiFi.begin(ssid, password); server.begin(); wsServer.begin(); // Set up WebSocket event handlers wsServer.onConnection([](WebSocket &ws) { Serial.print("Client connected from: "); Serial.println(ws.getRemoteIP()); ws.send("{\"type\":\"welcome\",\"message\":\"Connected to ESP32\"}"); }); wsServer.onMessage([](WebSocket &ws, const WebSocket::DataType dataType, const char *message, uint16_t length) { handleWebSocketMessage(ws, message, length); }); wsServer.onClose([](WebSocket &ws, const WebSocket::CloseCode code, const char *reason, uint16_t length) { Serial.println("Client disconnected"); }); }

메시지 처리

void handleWebSocketMessage(WebSocket &ws, const char *message, uint16_t length) { String msg = String(message); Serial.println("Received: " + msg); // JSON message handling if (msg.indexOf("\"type\":\"led\"") >= 0) { if (msg.indexOf("\"action\":\"on\"") >= 0) { digitalWrite(LED_PIN, HIGH); ws.send("{\"type\":\"led_status\",\"status\":\"on\"}"); } else if (msg.indexOf("\"action\":\"off\"") >= 0) { digitalWrite(LED_PIN, LOW); ws.send("{\"type\":\"led_status\",\"status\":\"off\"}"); } } // Echo functionality String response = "Echo: " + msg; ws.send(response.c_str()); }

센서 데이터 방송

void loop() { server.handleClient(); wsServer.loop(); // Broadcast sensor data every 5 seconds static unsigned long lastBroadcast = 0; if (millis() - lastBroadcast > 5000) { if (wsServer.connectedClients() > 0) { float temperature = getTemperature(); String sensorData = "{"; sensorData += "\"type\":\"sensor\","; sensorData += "\"temperature\":" + String(temperature, 1) + ","; sensorData += "\"timestamp\":" + String(millis()); sensorData += "}"; wsServer.broadcastTXT(sensorData); } lastBroadcast = millis(); } }

HTTP 메서드

라이브러리는 표준 HTTP 메서드들을 지원합니다:

  • HTTP_GET (가져오기)
  • HTTP_POST (생성/전송)
  • HTTP_PUT (대체/수정)
  • HTTP_DELETE (삭제)
  • HTTP_PATCH (부분 수정)
  • HTTP_HEAD (헤더 조회)
  • HTTP_OPTIONS (옵션)

클라이언트 측 자바스크립트 통합

기본 웹소켓 연결

// Connect to ESP32 WebSocket server const ws = new WebSocket('ws://your-esp32-ip:81'); ws.onopen = function(event) { console.log('Connected to ESP32 WebSocket'); document.getElementById('status').textContent = 'Connected'; }; ws.onmessage = function(event) { console.log('Received:', event.data); try { const data = JSON.parse(event.data); handleEsp32Message(data); } catch (e) { // Handle plain text messages console.log('Text message:', event.data); } }; ws.onclose = function(event) { console.log('Disconnected from ESP32'); document.getElementById('status').textContent = 'Disconnected'; // Auto-reconnect after 3 seconds setTimeout(connectWebSocket, 3000); }; ws.onerror = function(error) { console.error('WebSocket error:', error); };

명령 보내기

// Send LED control command function controlLED(action) { if (ws.readyState === WebSocket.OPEN) { const command = { type: 'led', action: action, timestamp: Date.now() }; ws.send(JSON.stringify(command)); } } // Send sensor request function requestSensorData() { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({type: 'get_sensor'})); } }

메시지 처리

function handleEsp32Message(data) { switch(data.type) { case 'sensor': updateTemperatureDisplay(data.temperature); break; case 'led_status': updateLEDStatus(data.status); break; case 'welcome': console.log('Welcome message:', data.message); break; default: console.log('Unknown message type:', data); } }

웹소켓 예제

간단한 에코 서버

wsServer.onMessage([](WebSocket &ws, const WebSocket::DataType dataType, const char *message, uint16_t length) { String response = "Echo: " + String(message); ws.send(response.c_str()); });

JSON 명령 처리

void processWebSocketCommand(WebSocket &ws, const String& message) { if (message.indexOf("\"type\":\"led\"") >= 0) { if (message.indexOf("\"action\":\"on\"") >= 0) { digitalWrite(LED_PIN, HIGH); ws.send("{\"type\":\"led_status\",\"status\":\"on\",\"success\":true}"); } else if (message.indexOf("\"action\":\"off\"") >= 0) { digitalWrite(LED_PIN, LOW); ws.send("{\"type\":\"led_status\",\"status\":\"off\",\"success\":true}"); } } else if (message.indexOf("\"type\":\"get_sensor\"") >= 0) { float temp = getTemperature(); String response = "{\"type\":\"sensor\",\"temperature\":" + String(temp, 1) + "}"; ws.send(response.c_str()); } }

하트비트 구현

void setupHeartbeat() { static unsigned long lastHeartbeat = 0; if (millis() - lastHeartbeat > 30000) { // Every 30 seconds if (wsServer.connectedClients() > 0) { String heartbeat = "{\"type\":\"heartbeat\",\"timestamp\":" + String(millis()) + "}"; wsServer.broadcastTXT(heartbeat); } lastHeartbeat = millis(); } }

웹소켓 문제 해결

일반적인 문제

웹소켓 연결 실패

  • 웹소켓 서버 포트(기본값: 81)에 접근 가능한지 확인
  • ESP32의 IP 주소가 올바르고 도달 가능한지 확인
  • 브라우저 개발자 도구를 사용하여 웹소켓 연결 오류를 확인

수신되지 않는 메시지

  • 시리얼 모니터에서 WebSocket 이벤트 로그를 확인
  • JSON 메시지 형식이 올바른지 확인
  • JSON을 사용하기 전에 간단한 텍스트 메시지로 테스트합니다
  • 메시지 길이가 버퍼 한도를 넘지 않는지 확인합니다

메모리 사용량이 높습니다

  • 동시 WebSocket 연결 수를 제한합니다
  • 메시지 버퍼를 정기적으로 비웁니다
  • 효율적인 문자열 처리 사용(자주 문자열을 연결하는 것을 피합니다)
  • 가용 힙 메모리를 모니터링합니다

디버그 도우미

void debugWebSocket() { Serial.println("=== WebSocket Debug Info ==="); Serial.println("Connected clients: " + String(wsServer.connectedClients())); Serial.println("Server listening: " + String(wsServer.isListening() ? "Yes" : "No")); Serial.println("Free memory: " + String(ESP.getFreeHeap()) + " bytes"); Serial.println("Uptime: " + String(millis() / 1000) + " seconds"); Serial.println("============================"); }

성능 모니터링

void monitorPerformance() { static unsigned long lastCheck = 0; static int messageCount = 0; messageCount++; if (millis() - lastCheck > 10000) { // Every 10 seconds Serial.println("Messages/10s: " + String(messageCount)); Serial.println("Clients: " + String(wsServer.connectedClients())); messageCount = 0; lastCheck = millis(); } }

라이브러리는 자리 표시자 대체가 가능한 HTML 템플릿을 지원합니다:

String response = HTML_TEMPLATE; response.replace("%TEMPERATURE%", String(temperature)); response.replace("%LED_STATUS%", ledStatus ? "ON" : "OFF"); server.send(200, "text/html", response);

일반적인 자리 표시자:

  • %TEMPERATURE% - 온도 값
  • %LED_STATUS% - LED 상태
  • %QUERY_PARAM% - 쿼리 매개변수 값들

고급 웹 서버 기능

CORS 지원

웹 애플리케이션에 대한 교차 출처 요청 활성화:

// Handle preflight OPTIONS requests server.on("/api/data", HTTP_OPTIONS, []() { server.sendHeader("Access-Control-Allow-Origin", "*"); server.sendHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); server.sendHeader("Access-Control-Allow-Headers", "Content-Type"); server.send(200); }); // Add CORS headers to all responses server.sendHeader("Access-Control-Allow-Origin", "*"); server.sendHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); server.sendHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");

JSON 응답 헬퍼들

JSON API 개발 간소화:

void sendJsonResponse(int statusCode, const String& json) { server.send(statusCode, "application/json", json); } void sendError(int statusCode, const String& message) { String error = "{\"error\":\"" + message + "\"}"; sendJsonResponse(statusCode, error); } // Usage sendJsonResponse(200, "{\"status\":\"success\",\"data\":\"value\"}"); sendError(400, "Invalid request format");

요청 유효성 검사

강력한 입력 유효성 검사 구현:

bool isValidJsonAction(const String& action) { return (action == "on" || action == "off" || action == "toggle"); } bool validateRequiredFields(const String& jsonData, const String& field) { return (jsonData.indexOf("\"" + field + "\":") >= 0); } server.on("/api/control", HTTP_POST, []() { if (!server.hasArg("plain")) { sendError(400, "JSON body required"); return; } String body = server.arg("plain"); if (!validateRequiredFields(body, "action")) { sendError(400, "Missing required field: action"); return; } // Process valid request... });

향상된 JSON 처리

ArduinoJson 라이브러리로 복잡한 JSON 처리를 위한:

#include <ArduinoJson.h> void handleJsonRequest() { String requestBody = server.arg("plain"); StaticJsonDocument<200> doc; DeserializationError error = deserializeJson(doc, requestBody); if (error) { sendError(400, "Invalid JSON format"); return; } // Extract values safely const char* action = doc["action"] | "none"; int value = doc["value"] | 0; bool enabled = doc["enabled"] | false; // Create response StaticJsonDocument<200> response; response["status"] = "success"; response["received_action"] = action; response["received_value"] = value; String responseString; serializeJson(response, responseString); sendJsonResponse(200, responseString); }

오류 처리

기본 404 핸들러

라이브러리는 기본 404 오류 페이지를 제공합니다. 이를 재정의할 수 있습니다:

server.onNotFound([]() { server.send(404, "text/html", "<h1>Custom 404 Page</h1>"); });

모범 사례

  1. 메모리 관리: 플래시 메모리에 저장된 문자열 리터럴에 대해 F() 매크로를 사용합니다
  2. 비차단 코드: 서버 차단을 피하기 위해 핸들러 함수를 가볍게 유지합니다
  3. 보안: 입력 매개변수를 검증하고 출력을 정화합니다
  4. 성능: 적절한 HTTP 상태 코드와 콘텐츠 타입을 사용합니다
  5. 웹소켓: 연결 상태를 적절히 처리하고 재연결 로직을 구현합니다

디버깅

시리얼 디버깅을 활성화하여 서버 활동을 모니터링하십시오:

void setup() { Serial.begin(9600); // ... rest of setup } void loop() { server.handleClient(); if (Serial.available()) { String command = Serial.readString(); Serial.println("Debug: " + command); } }

호환성

  • ESP32: 완전히 지원됩니다
  • WiFi Library: 필요합니다(Arduino IDE에 포함되어 있음)
  • Memory Requirements: 최소 32KB 플래시, 2KB RAM

제한 사항

웹 서버의 제한

  • 동시 실행 가능한 최대 HTTP 연결 수: 4 (하드웨어 제한)
  • 최대 URL 길이: 256자
  • 템플릿 자리 표시자: 중첩된 치환 없음

웹소켓 제한 사항

  • 최대 WebSocket 메시지 크기: 메시지당 1KB
  • 최대 동시 WebSocket 연결 수: 4-6개(가용 메모리에 따라 다름)
  • 메시지 분할: 자동으로 처리되지만 성능에 영향을 줄 수 있음
  • 바이너리 메시지 크기: 사용 가능한 램에 의해 제한
  • 연결 시간 초과: 기본값 60초(구성 가능)

메모리 제약

  • 필요한 최소 플래시 메모리: 32KB
  • 기본 기능에 필요한 최소 RAM: 2KB
  • WebSocket 오버헤드: 연결당 약 200-500 바이트
  • 활성 연결당 약 1KB의 메시지 버퍼링