ESP-IDF: ESP32 웹 서버 - 기본 HTTP 인증(로그인/로그아웃)
이 가이드는 ESP-IDF로 프로그래밍된 ESP32를 사용하여 간단한 HTML 웹 페이지를 제공하는 로컬 웹 서버를 구축하는 방법을 보여줍니다. 이 웹 페이지는 기본 HTTP 인증으로 비밀번호 보호되며, 로컬 네트워크에서 접속할 수 있지만 올바른 사용자 이름과 비밀번호를 입력해야 합니다. 로그아웃하면 올바른 자격 증명을 입력해야만 다시 접속할 수 있습니다.
필수 조건
이 가이드를 따르기 전에 VS Code IDE(Microsoft Visual Studio Code)에 ESP-IDF 확장 프로그램을 설치해야 합니다. 아직 설치하지 않았다면 다음 가이드를 따라 설치하세요.
시작 가이드: VS Code를 사용하여 ESP-IDF로 ESP32 프로그래밍하기
또한 원하는 ESP32 개발 보드 모델이 필요합니다 .
보안 문제
이 프로젝트는 사용자의 로컬 네트워크에서 사용하도록 설계되었으며, 권한이 없는 가족 구성원이나 친구처럼 누구나 ESP IP 주소를 입력하여 웹 서버에 접근하는 것을 방지합니다.
네트워크 보안이 제대로 되어 있다면 대부분의 애플리케이션에는 기본 인증을 사용하는 HTTP 서버만으로도 충분합니다. 하지만 누군가 네트워크를 해킹했다면 HTTP를 사용하든 HTTPS를 사용하든 상관없습니다. 해커는 HTTPS를 우회하여 사용자 이름과 비밀번호를 탈취할 수 있습니다.
프로젝트 개요
우리가 만들 프로젝트의 특징을 간단히 살펴보겠습니다.

ESP-IDF ESP32 웹 서버 기본 HTTP 인증 로그인 및 로그아웃 프로젝트 개요
- 이 튜토리얼에서는 웹 서버에 비밀번호를 설정하여 보호하는 방법을 배우게 됩니다.
- ESP32 IP 주소로 웹 서버 페이지에 접속하려고 하면 사용자 이름과 비밀번호를 묻는 팝업 창이 나타납니다.
- 웹 서버 페이지에 접속하려면 ESP32 스케치에 정의된 올바른 사용자 이름과 비밀번호를 입력해야 합니다.
- 웹 서버에 로그아웃 버튼이 있습니다. 로그아웃 버튼을 클릭하면 로그아웃 페이지로 이동합니다.
- 올바른 자격 증명으로 로그인해야만 웹 서버에 다시 접속할 수 있습니다.
- 다른 기기(로컬 네트워크 내)에서 웹 서버에 접속하려고 하면, 다른 기기에서 로그인에 성공했더라도 올바른 자격 증명으로 로그인해야 합니다.
- 인증 정보는 암호화되지 않습니다.
ESP32용 ESP-IDF 템플릿 앱 프로젝트 생성하기
ESP-IDF 확장 프로그램은 필요한 모든 파일과 구성이 자동으로 생성되는 간편한 방식으로 처음부터 프로젝트를 만들 수 있도록 지원합니다.
VS Code에서 새로운 ESP-IDF 프로젝트를 생성하려면 다음 단계를 따르세요.
- ESP-IDF Espressif 확장 프로그램을 엽니다.
- " 고급 " 메뉴를 펼치세요
- “ 새 프로젝트 마법사 ” 옵션을 클릭하세요.
- 프레임워크 버전을 선택하려면 " ESP-IDF v5.4.1 사용 "을 선택하세요.

ESP-IDF ESP32 새 프로젝트 생성 마법사 메뉴
새 창이 열리면 다음 필드를 입력하세요.
- 프로젝트 이름: 원하는 프로젝트 이름을 입력하세요;
- 프로젝트 디렉토리로 이동: 폴더 아이콘을 클릭하고 프로젝트 파일을 저장할 대상 폴더를 선택하세요. 어떤 디렉토리든 사용할 수 있습니다. 참고: Google Drive/OneDrive/Dropbox 폴더는 사용하지 마세요 . 클라우드 폴더에 저장하면 빌드 과정에서 많은 파일이 생성되어 빌드 속도가 매우 느려질 수 있습니다.
- ESP-IDF 대상: 대상 장치 칩을 선택하세요. 저는 esp32s3 칩이 장착된 ESP32를 사용하고 있습니다.
- ESP-IDF 보드: esp32s3 칩의 경우, 구성도 선택해야 합니다: ESP32-S 칩(내장 USB-JTAG를 통해);
- 시리얼 포트: ESP32 보드를 컴퓨터에 연결한 후, ESP32에 해당하는 올바른 COM 포트 번호를 선택하십시오.
- 템플릿 선택: 파란색 버튼을 클릭하여 템플릿을 사용하여 새 프로젝트를 생성하세요.

ESP-IDF ESP32 새 프로젝트 생성 마법사 메뉴에서 디렉토리 보드 템플릿을 선택하세요.
메뉴에서 " ESP-IDF 템플릿 " 샘플 프로젝트를 선택하고 " 템플릿 샘플 프로젝트를 사용하여 프로젝트 생성 " 버튼을 누르십시오.

ESP-IDF ESP32 IDF 템플릿을 사용하여 새 샘플 프로젝트 생성
VS Code에서 ESP-IDF 프로젝트 열기
몇 초 후 VS Code의 새 창에 알림이 나타납니다. " 프로젝트 열기 "를 클릭하면 새로 생성된 ESP-IDF 샘플 프로젝트 템플릿을 열 수 있습니다.

ESP-IDF ESP32 새 프로젝트 열기 샘플
중요: VS Code에서 ESP-IDF 프로젝트를 자동으로 열 수 있도록 하는 알림을 받지 못한 경우, 다음 지침에 따라 쉽게 설정할 수 있습니다.
파일 > 폴더 열기… 로 이동하세요 .

ESP-IDF ESP32 프로젝트 폴더 열기 VS Code 파일 메뉴
컴퓨터에서 esp-idf-project 폴더 (이전에 정의한 프로젝트 폴더 이름)를 찾아 " 폴더 선택 "을 클릭하세요.

ESP-IDF ESP32 VS Code에서 프로젝트 열기 폴더 선택
이제 완료되었습니다! 새로운 ESP-IDF 프로젝트 템플릿이 성공적으로 생성되어 열렸습니다.
ESP-IDF는 프로젝트에 필요한 여러 파일, 폴더 및 하위 폴더를 생성합니다. 이 가이드에서는 기본 파일을 모두 그대로 두고 main.c 파일만 수정하는 것을 권장합니다.
예제 코드는 main.c 파일에 작성되어 있습니다. 해당 파일을 열려면 다음 지침을 따르세요.
- 왼쪽 사이드바의 첫 번째 아이콘을 클릭하여 프로젝트 탐색기를 엽니다.
- 프로젝트 폴더 이름을 선택하세요. 제 경우에는 " ESP-IDF-PROJECT " 입니다 .
- " 메인 " 폴더를 펼치세요.
- " main.c " 파일을 클릭하세요.
- 기본 main.c 템플릿 파일이 코드 창에 로드됩니다.

VS Code에서 ESP-IDF ESP32 프로젝트를 열고 메인 C 파일로 이동합니다.
Wi-Fi 설정 옵션 파일(Kconfig.projbuild)을 생성합니다.
ESP-IDF의 Kconfig.projbuild 파일 은 SDK 구성 편집기(menuconfig 도구)에서 사용할 사용자 지정 옵션을 정의하기 위해 프로젝트의 메인 폴더에 배치되는 구성 파일입니다. 이를 통해 사용자는 빌드 구성 중에 사용될 문자열이나 값과 같은 특정 설정을 지정할 수 있습니다.
Kconfig.projbuild 파일을 생성하려면 파일 탐색기 왼쪽 사이드바에서 메인 폴더 맨 위를 마우스 오른쪽 버튼으로 클릭한 다음 " 새 파일... " 옵션을 선택합니다.

메인 폴더에 Kconfig projbuild라는 새 파일을 생성합니다.
새 파일의 이름을 정확히 ‘Kconfig.projbuild’로 지정하고, 다음 스크린샷에 표시된 내용을 방금 생성한 파일에 복사하십시오.

Kconfig projbuild Wi-Fi 설정 파일
menu "Wi-Fi Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
help
SSID (network name) you want to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
help
Password of the Wi-Fi network you want to connect to.
endmenu
파일 내용을 저장하세요. 그러면 SDK 구성 편집기에 " Wi-Fi 구성 "이라는 새 메뉴가 생성되고, 여기에 SSID와 비밀번호를 입력하여 ESP가 네트워크에 연결할 수 있습니다.
Kconfig.projbuild 파일 이 준비되면 톱니바퀴 아이콘을 클릭하여 SDK 구성 편집기(menuconfig)를 엽니다.

SDK 구성 편집기 열기 > 메뉴 구성 > Wi-Fi 구성
이제 main.c 파일에 네트워크 자격 증명을 입력하는 대신 menuconfig 도구를 사용하여 구성할 수 있습니다. 다음 단계를 따르세요.
- " 와이파이 "를 검색하세요.
- 해당 필드에 SSID와 비밀번호를 입력하세요.
- 변경 사항을 적용하려면 " 저장 " 버튼을 누르세요.

SDK 구성 편집기 메뉴 구성 Kconfig projbuild Wi-Fi 구성 SSID 비밀번호
코드: ESP-IDF를 사용한 HTTP 인증 기능이 있는 ESP32 웹 서버
다음 코드를 main.c 파일에 복사하세요. 이 코드는 ESP32에서 호스팅되는 기본 HTML 웹 페이지를 제공하며, 기본 HTTP 인증을 통해 비밀번호로 보호됩니다.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
https://RandomNerdTutorials.com/esp-idf-esp32-web-server-http-authentication/
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <esp_log.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_netif.h>
#include <esp_http_server.h>
#include <esp_tls.h>
#include <esp_check.h>
#include <nvs_flash.h>
#include "esp_tls_crypto.h"
#include "sdkconfig.h"
#define MY_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define MY_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
#define BASIC_AUTH_USER "admin"
#define BASIC_AUTH_PASS "password"
typedef struct {
char *username;
char *password;
} basic_auth_info_t;
#define HTTPD_401 "401 UNAUTHORIZED" // HTTP Response 401
static const char *TAG = "web_server";
// HTML web page to serve the root /
static const char *root_page =
"<!DOCTYPE html>"
"<html>"
"<head>"
"<title>ESP-IDF: ESP32 Web Server</title>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
"</head>"
"<body>"
"<h1>ESP-IDF: ESP32 Web Server</h1>"
"<p>Hello from ESP32!</p>"
"<button onclick=\"window.location.href = '/logout';\">Logout</button>"
"</body>"
"</html>";
// HTML web page to serve the /logout (redirects the user to the root page after logout)
static const char *logout_page =
"<!DOCTYPE html>"
"<html>"
"<head>"
"<title>Logout</title>"
"<script>"
"function forceLogout(){"
" var xhr = new XMLHttpRequest();"
" xhr.open('GET', '/', true, 'logout', 'logout');"
" xhr.send();"
" setTimeout(function(){ window.location.href='/'; }, 500);"
"}"
"</script>"
"</head>"
"<body onload='forceLogout()'>"
"<h2>Logging out...</h2>"
"</body>"
"</html>";
// Send 401 + WWW-Authenticate header
static esp_err_t send_401_response(httpd_req_t *req)
{
httpd_resp_set_status(req, HTTPD_401);
httpd_resp_set_type(req, "text/html");
httpd_resp_set_hdr(req, "Connection", "keep-alive");
httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"ESP32 Web Server\"");
httpd_resp_send(req, NULL, 0);
return ESP_OK;
}
// HTTP basic authentication base64 encoding function
static char *http_auth_basic(const char *username, const char *password)
{
size_t out;
char *user_info = NULL;
char *digest = NULL;
size_t n = 0;
int rc = asprintf(&user_info, "%s:%s", username, password);
if (rc < 0) {
ESP_LOGE(TAG, "asprintf() returned: %d", rc);
return NULL;
}
if (!user_info) {
ESP_LOGE(TAG, "No enough memory for user information");
return NULL;
}
esp_crypto_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info));
// 6: The length of the "Basic " string
// n: Number of bytes for a base64 encode format
// 1: Number of bytes for a reserved which be used to fill zero
digest = calloc(1, 6 + n + 1);
if (digest) {
strcpy(digest, "Basic ");
esp_crypto_base64_encode((unsigned char *)digest + 6, n, &out, (const unsigned char *)user_info, strlen(user_info));
}
free(user_info);
return digest;
}
// Check basic authentication
static bool check_basic_auth(httpd_req_t *req, const char *username, const char *password)
{
char *buf = NULL;
size_t buf_len = httpd_req_get_hdr_value_len(req, "Authorization") + 1;
if (buf_len <= 1) {
ESP_LOGE(TAG, "No Authorization header");
return false;
}
buf = calloc(1, buf_len);
if (!buf) {
ESP_LOGE(TAG, "No memory for auth buffer");
return false;
}
if (httpd_req_get_hdr_value_str(req, "Authorization", buf, buf_len) != ESP_OK) {
free(buf);
return false;
}
char *expected = http_auth_basic(username, password);
if (!expected) {
free(buf);
return false;
}
bool authenticated = (strcmp(expected, buf) == 0);
free(expected);
free(buf);
if (authenticated) {
ESP_LOGI(TAG, "Authenticated user: %s", username);
} else {
ESP_LOGE(TAG, "Authentication failed");
}
return authenticated;
}
// GET handler for the /logout
static esp_err_t logout_get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "text/html");
httpd_resp_send(req, logout_page, HTTPD_RESP_USE_STRLEN);
ESP_LOGI(TAG, "Logout page served");
return ESP_OK;
}
// URI registration structure for the /logout
static const httpd_uri_t logout_uri = {
.uri = "/logout",
.method = HTTP_GET,
.handler = logout_get_handler,
.user_ctx = NULL
};
// Root web page GET handler with basic authentication
static esp_err_t basic_auth_get_handler(httpd_req_t *req)
{
basic_auth_info_t *basic_auth_info = req->user_ctx;
if (!check_basic_auth(req, basic_auth_info->username, basic_auth_info->password)) {
return send_401_response(req);
}
httpd_resp_set_type(req, "text/html");
httpd_resp_send(req, root_page, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
// URI registration structure for the root / with basic authentication
static httpd_uri_t basic_auth = {
.uri = "/",
.method = HTTP_GET,
.handler = basic_auth_get_handler,
};
// Register the basic authentication URI handler
static void httpd_register_basic_auth(httpd_handle_t server)
{
basic_auth_info_t *basic_auth_info = calloc(1, sizeof(basic_auth_info_t));
if (basic_auth_info) {
basic_auth_info->username = BASIC_AUTH_USER;
basic_auth_info->password = BASIC_AUTH_PASS;
basic_auth.user_ctx = basic_auth_info;
httpd_register_uri_handler(server, &basic_auth);
}
}
// Start the HTTP server
static httpd_handle_t start_web_server(void)
{
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_handle_t server = NULL;
if (httpd_start(&server, &config) == ESP_OK) {
ESP_LOGI(TAG, "HTTP server started on port %d", config.server_port);
httpd_register_uri_handler(server, &logout_uri);
httpd_register_basic_auth(server);
return server;
}
ESP_LOGE(TAG, "Failed to start HTTP server");
return NULL;
}
// Wi-Fi and IP event handler
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
ESP_LOGI(TAG, "Wi-Fi STA started. Connecting to %s...", MY_ESP_WIFI_SSID);
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGW(TAG, "Wi-Fi disconnected. Retrying connection...");
esp_wifi_connect();
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP Address: " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Web Server ready! Access at http://" IPSTR "/", IP2STR(&event->ip_info.ip));
}
}
void app_main(void)
{
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// Initialize TCP/IP stack and event loop
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Create default Wi-Fi STA interface
esp_netif_create_default_wifi_sta();
// Initialize Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// Register event handlers
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&wifi_event_handler,
NULL, NULL));
// Configure Wi-Fi STA
wifi_config_t wifi_config = {
.sta = {
.ssid = MY_ESP_WIFI_SSID,
.password = MY_ESP_WIFI_PASS,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
// Start the web server (it will be ready once IP is assigned)
httpd_handle_t server = start_web_server();
if (server) {
ESP_LOGI(TAG, "Web Server initialized. Waiting for Wi-Fi connection...");
}
}
코드 작동 방식
이 섹션에서는 주요 코드 부분을 살펴보고 어떻게 작동하는지 알아보겠습니다.
라이브러리
먼저 필요한 라이브러리를 포함시키는 것으로 시작하겠습니다.
- stdio.h– 표준 C 라이브러리가 사용될 것입니다.printf디버깅 정보를 시리얼 모니터에 출력하는 함수;
- 문자열.h- 문자열 조작에 사용되는 표준 C 라이브러리;
- FreeRTOS.h– FreeRTOS의 핵심 유형 및 기능을 제공합니다.
- task.h– 작업 관리 기능을 사용할 수 있습니다.
- esp_log.h– 디버깅을 위해 시리얼 모니터에 로그 메시지를 포맷하는 프레임워크를 제공합니다.
- esp_system.h- 재시작 및 하드웨어 정보와 같은 시스템 수준 기능;
- esp_wifi.h– Wi-Fi 설정 라이브러리(스테이션 및 액세스 포인트 모드, 연결 옵션 포함);
- esp_event.h- Wi-Fi 상태 변경과 같은 이벤트를 처리합니다.
- esp_netif.h– 네트워크 인터페이스를 제공합니다.
- esp_http_server.hHTTP 서버를 생성하고 HTTP 요청/응답을 처리하는 라이브러리입니다.
- esp_tls.h– 안전한 소켓 통신을 위한 ESP-IDF TLS/SSL 라이브러리;
- esp_check.h– 오류 검사를 위한 ESP-IDF 헬퍼 함수;
- nvs_flash.h– 키-값 데이터를 비휘발성 저장 장치(NVS) 메모리에 영구적으로 저장합니다.
- esp_tls_crypto.h– ESP-IDF TLS 암호화 유틸리티(암호, 해시 및 인증서 처리용);
- sdkconfig.h– 프로젝트 구성 파일이 포함되어 있습니다.
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <esp_log.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_netif.h>
#include <esp_http_server.h>
#include <esp_tls.h>
#include <esp_check.h>
#include <nvs_flash.h>
#include "esp_tls_crypto.h"
#include "sdkconfig.h"
Wi-Fi 설정
이러한 변수들은 이전 섹션 " Wi-Fi 구성 옵션 파일 생성 - Kconfig.projbuild "에서 설명한 대로 menuconfig 옵션 에서 가져옵니다 .
#define MY_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define MY_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
HTTP 인증 구성
ESP32 웹 서버에 접속하기 위한 사용자 이름과 비밀번호를 설정할 수 있습니다. 이 예시에서는 사용자 이름을 'admin' 으로 설정했지만 , 원하는 이름으로 변경할 수 있습니다. 비밀번호는 'password' 이지만 , 이 또한 변경 가능합니다.
#define BASIC_AUTH_USER "admin"
#define BASIC_AUTH_PASS "password"
로깅 태그
그만큼“웹_서버”접두사는 모든 디버깅 메시지를 기록하는 데 사용됩니다.
static const char *TAG = "web_server";
HTML – 루트 웹 페이지
이것은 HTTP 인증이 성공적으로 완료된 후 연결된 클라이언트에게 전송될 HTML 웹 페이지입니다.
static const char *root_page =
"<!DOCTYPE html>"
"<html>"
"<head>"
"<title>ESP-IDF: ESP32 Web Server</title>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
"</head>"
"<body>"
"<h1>ESP-IDF: ESP32 Web Server</h1>"
"<p>Hello from ESP32!</p>"
"<button onclick=\"window.location.href = '/logout';\">Logout</button>"
"</body>"
"</html>";
HTML - 로그아웃 웹페이지
이 HTML 웹페이지는 루트 웹페이지에서 로그아웃 버튼을 클릭한 후 일시적으로 리디렉션될 페이지입니다.
static const char *logout_page =
"<!DOCTYPE html>"
"<html>"
"<head>"
"<title>Logout</title>"
"<script>"
"function forceLogout(){"
" var xhr = new XMLHttpRequest();"
" xhr.open('GET', '/', true, 'logout', 'logout');"
" xhr.send();"
" setTimeout(function(){ window.location.href='/'; }, 500);"
"}"
"</script>"
"</head>"
"<body onload='forceLogout()'>"
"<h2>Logging out...</h2>"
"</body>"
"</html>";
http_auth_basic
http_auth_basic 함수는 전달된 사용자 이름과 비밀번호를 “username:password” 형식의 문자열로 연결하여 완전한 HTTP 기본 인증 헤더 값을 생성합니다.
static char *http_auth_basic(const char *username, const char *password)
{
size_t out;
char *user_info = NULL;
char *digest = NULL;
size_t n = 0;
int rc = asprintf(&user_info, "%s:%s", username, password);
if (rc < 0) {
ESP_LOGE(TAG, "asprintf() returned: %d", rc);
return NULL;
}
if (!user_info) {
ESP_LOGE(TAG, "No enough memory for user information");
return NULL;
}
esp_crypto_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info));
// 6: The length of the "Basic " string
// n: Number of bytes for a base64 encode format
// 1: Number of bytes for a reserved which be used to fill zero
digest = calloc(1, 6 + n + 1);
if (digest) {
strcpy(digest, "Basic ");
esp_crypto_base64_encode((unsigned char *)digest + 6, n, &out, (const unsigned char *)user_info, strlen(user_info));
}
free(user_info);
return digest;
}
check_basic_auth
check_basic_auth 함수는 HTTP 요청에서 Authorization 헤더를 추출하여 HTTP 클라이언트의 인증 정보를 확인합니다. 그런 다음 http_auth_basic을 사용하여 올바르게 Base64로 인코딩된 인증 정보를 생성합니다.
마지막으로, 클라이언트가 전송한 인증 정보를 코드에 정의된 예상 사용자 이름/비밀번호와 비교합니다. 인증 정보가 일치하면 true를 반환합니다. 그렇지 않은 경우, 인증 오류의 원인을 로그에 기록합니다.
static bool check_basic_auth(httpd_req_t *req, const char *username, const char *password)
{
char *buf = NULL;
size_t buf_len = httpd_req_get_hdr_value_len(req, "Authorization") + 1;
if (buf_len <= 1) {
ESP_LOGE(TAG, "No Authorization header");
return false;
}
buf = calloc(1, buf_len);
if (!buf) {
ESP_LOGE(TAG, "No memory for auth buffer");
return false;
}
if (httpd_req_get_hdr_value_str(req, "Authorization", buf, buf_len) != ESP_OK) {
free(buf);
return false;
}
char *expected = http_auth_basic(username, password);
if (!expected) {
free(buf);
return false;
}
bool authenticated = (strcmp(expected, buf) == 0);
free(expected);
free(buf);
if (authenticated) {
ESP_LOGI(TAG, "Authenticated user: %s", username);
} else {
ESP_LOGE(TAG, "Authentication failed");
}
return authenticated;
}
HTTP GET - 로그아웃 처리기
logout_get_handler는 응답 콘텐츠 유형을 “text/html”로 설정하고, logout_page HTML 웹 페이지 콘텐츠를 클라이언트에 전송하며, 메시지를 기록하고, ESP_OK를 반환하는 간단한 GET 요청 핸들러입니다.
static esp_err_t logout_get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "text/html");
httpd_resp_send(req, logout_page, HTTPD_RESP_USE_STRLEN);
ESP_LOGI(TAG, "Logout page served");
return ESP_OK;
}
logout_uri 구조체는 이 핸들러를 ESP32 HTTP 서버에 등록하여, /logout URL로 전송되는 모든 GET 요청이 logout_get_handler에 의해 처리되도록 합니다.
static const httpd_uri_t logout_uri = {
.uri = "/logout",
.method = HTTP_GET,
.handler = logout_get_handler,
.user_ctx = NULL
};
HTTP GET - 루트 핸들러
다음 함수들은 httpd 라이브러리를 사용하여 간단한 보호된 웹 서버를 생성합니다. basic_auth_get_handler 함수는 루트 URI /에 대한 GET 요청 핸들러 역할을 합니다. 이 함수는 먼저 인증 정보를 추출한 다음, check_basic_auth를 호출하여 클라이언트가 전송한 Base64 인코딩된 자격 증명을 정의된 BASIC_AUTH_USER 및 BASIC_AUTH_PASS와 대조하여 검증합니다. 마지막으로, 인증에 실패하면 401 Unauthorized 응답을 반환하고, 그렇지 않으면 앞서 정의한 root_page 웹 페이지를 반환합니다.
static esp_err_t basic_auth_get_handler(httpd_req_t *req)
{
basic_auth_info_t *basic_auth_info = req->user_ctx;
if (!check_basic_auth(req, basic_auth_info->username, basic_auth_info->password)) {
return send_401_response(req);
}
httpd_resp_set_type(req, "text/html");
httpd_resp_send(req, root_page, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
static httpd_uri_t basic_auth = {
.uri = "/",
.method = HTTP_GET,
.handler = basic_auth_get_handler,
};
static void httpd_register_basic_auth(httpd_handle_t server)
{
basic_auth_info_t *basic_auth_info = calloc(1, sizeof(basic_auth_info_t));
if (basic_auth_info) {
basic_auth_info->username = BASIC_AUTH_USER;
basic_auth_info->password = BASIC_AUTH_PASS;
basic_auth.user_ctx = basic_auth_info;
httpd_register_uri_handler(server, &basic_auth);
}
}
HTTP 웹 서버를 시작하세요
start_web_server() 함수는 서버를 구성하는 것으로 시작합니다(기본 포트 80을 사용함):
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_handle_t server = NULL;
httpd_start(&server, &config) 함수는 실제 HTTP 웹 서버를 시작하고 포트 80에서 연결을 수신 대기하도록 하여 클라이언트가 연결할 수 있게 합니다. 그런 다음 로그아웃 경로를 등록합니다.
if (httpd_start(&server, &config) == ESP_OK) {
ESP_LOGI(TAG, "HTTP server started on port %d", config.server_port);
httpd_register_uri_handler(server, &logout_uri);
httpd_register_basic_auth(server);
return server;
}
HTTP 웹 서버를 시작하는 데 실패하면 실패 메시지를 출력합니다.
ESP_LOGE(TAG, "Failed to start HTTP server");
Wi-Fi 및 IP 이벤트 핸들러
이 콜백 함수는 Wi-Fi 관련 이벤트가 발생했을 때 실행됩니다.
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
ESP_LOGI(TAG, "Wi-Fi STA started. Connecting to %s...", MY_ESP_WIFI_SSID);
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGW(TAG, "Wi-Fi disconnected. Retrying connection...");
esp_wifi_connect();
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP Address: " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Web Server ready! Access at http://" IPSTR "/", IP2STR(&event->ip_info.ip));
}
}
예를 들어, ESP32에 IP 주소가 할당되면 다음과 같은 동작이 발생합니다.IP_EVENT이 메시지가 시리얼 모니터에 출력됩니다.
ESP_LOGI(TAG, "Got IP Address: " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Web Server ready! Access at http://" IPSTR "/", IP2STR(&event->ip_info.ip));
app_main(void)
ESP-IDF 프로젝트를 생성할 때,앱_메인이 함수는 항상 실행되도록 호출됩니다. ESP-IDF 애플리케이션의 코드를 작성해야 하는 곳이 바로 이 함수이며, 다음과 같은 역할을 합니다.설정()아두이노 프로그래밍에서 ESP32가 부팅될 때 ESP-IDF 프레임워크가 호출됩니다.앱_메인.
void app_main(void)
{
// your code goes here
}
app_main(void) 함수에서는 먼저 NVS(저장소)를 초기화합니다. ESP32는 Wi-Fi 설정을 플래시 메모리에 저장합니다.
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
다음으로, 네트워크 기능 사용에 필요한 TCP/IP 스택을 초기화합니다.
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP32가 라우터에 연결할 수 있도록 Wi-Fi 인터페이스를 스테이션 모드로 시작합니다. 또한 Wi-Fi 기능을 초기화합니다.
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP32가 Wi-Fi 연결을 설정하거나 IP 주소를 할당받을 때 해당 Wi-Fi 이벤트가 실행되도록 Wi-Fi 이벤트를 할당합니다.
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&wifi_event_handler,
NULL, NULL));
다음 몇 줄은 ESP32가 네트워크에 연결할 수 있도록 SSID와 비밀번호를 설정하고, ESP를 스테이션 모드로 설정하고, Wi-Fi를 시작하는 내용입니다.
wifi_config_t wifi_config = {
.sta = {
.ssid = MY_ESP_WIFI_SSID,
.password = MY_ESP_WIFI_PASS,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
마지막으로 웹 서버를 시작하세요.
httpd_handle_t server = start_web_server();
if (server) {
ESP_LOGI(TAG, "Web Server initialized. Waiting for Wi-Fi connection...");
}
ESP32 보드에 코드를 빌드하고 플래싱하세요.
ESP-IDF 코드를 ESP32에 빌드하고 플래싱하려면 항상 다음 절차를 따라야 합니다. 플래싱 방식(UART), COM 포트 번호, 대상 장치(ESP32)를 선택하고, 코드를 빌드한 다음, 마지막으로 보드에 플래싱해야 합니다. 이러한 모든 명령은 VS Code 하단 메뉴 모음에서 사용할 수 있습니다.
모든 옵션이 올바른지 확인하십시오(프로젝트 마법사를 사용한 경우 이미 올바르게 구성되어 있을 수 있습니다).
VS Code ESP-IDF 구성 설정 모두 확인 UART COM 포트 대상 보드
하지만 설정이 올바르지 않은 경우, 다음 지침에 따라 모든 것이 올바르게 설정되었는지 확인하십시오. 먼저 " 별 " 아이콘을 클릭하고 플래시 방식을 UART 로 선택하십시오 .

VS Code ESP-IDF에서 ESP32 플래시 프로그래밍을 위해 UART 옵션을 선택하세요
ESP32 보드가 컴퓨터에 연결된 상태에서 COM 포트(플러그 아이콘)를 클릭하고 ESP32에 해당하는 올바른 포트 번호를 선택하십시오.

VS Code ESP-IDF 프로그래밍 ESP32 보드에서 올바른 COM 포트 번호를 선택하세요
대상 장치도 선택해야 합니다. 하단 바에 있는 칩 아이콘을 클릭하세요. 제 경우에는 esp32s3 칩이 장착된 ESP32를 사용하고 있습니다.

VS Code에서 ESP32 S3 또는 올바른 대상 장치(ESP-IDF)를 선택하세요.
이 보드의 경우, ESP32-S 칩(내장 USB-JTAG 사용) 구성을 선택해야 합니다 .

VS Code ESP-IDF에서 내장 USB JTAG 타겟 장치를 통해 ESP32 S3 칩을 선택하세요.
마지막으로 VS Code 하단의 명령 모음에도 비슷한 옵션이 선택되어 있어야 합니다.

VS Code ESP-IDF 구성 설정 모두 확인 UART COM 포트 대상 보드
이제 아래 이미지에 표시된 대로 렌치 아이콘( 프로젝트 빌드 )을 클릭하여 프로젝트를 빌드할 수 있습니다.

VS Code 프로젝트 빌드 예제 코드 ESP32 ESP-IDF
프로젝트를 처음 빌드할 때는 일반적으로 시간이 조금 더 걸립니다. 빌드가 완료되면 터미널 메뉴에 비슷한 메시지가 출력되고 " 빌드 성공 " 메시지가 표시됩니다.

VS Code에서 ESP32 ESP-IDF 예제 프로젝트 빌드 성공 메시지
이제 마지막 단계입니다. " 플래시 장치 " 버튼(번개 아이콘)을 클릭하여 ESP-IDF 프로젝트를 ESP32에 플래싱할 수 있습니다 .

VS Code 플래시 헬로 월드 코드 프로젝트를 ESP32 ESP-IDF에 출력
사용하는 보드에 따라 ESP32의 BOOT 버튼을 길게 눌러 플래싱 모드로 진입해야 할 수도 있습니다. 플래싱이 완료되면 " 플래시 완료 " 라는 정보 메시지가 표시됩니다 .

VS Code 플래시 헬로 월드 프로젝트를 ESP32 ESP-IDF로 완료 성공 메시지
데모
모든 단계를 제대로 따라했다면 예제가 보드에서 성공적으로 실행될 것입니다. 터미널 창을 열고 화면 아이콘으로 표시된 " 장치 모니터링 " 도구를 클릭하세요.

VS 코드에서 터미널 창을 열고 모니터 장치 ESP32 ESP-IDF를 표시합니다.
ESP32가 Wi-Fi에 연결되면 시리얼 모니터에 IP 주소가 출력됩니다. ESP32 웹 서버에 접속하려면 이 IP 주소가 필요하므로 복사해 두세요.

ESP-IDF ESP32 간단 웹 서버 터미널 데모
참고: 시리얼 모니터에 아무것도 표시되지 않으면 ESP32의 "EN" 버튼(microUSB 포트 옆에 있는 활성화/재설정 버튼)을 누르십시오.
로컬 네트워크에서 웹 서버에 접속하려면 브라우저를 열고 ESP32의 IP 주소를 입력하세요. 그러면 사용자 이름과 비밀번호를 입력하라는 페이지가 나타납니다. 사용자 이름과 비밀번호를 입력하면 웹 서버에 접속할 수 있습니다. 코드를 수정하지 않았다면 사용자 이름은 'admin'이고 비밀번호는 'password' 입니다 .

ESP-IDF ESP32 웹 서버 기본 HTTP 인증 로그인 비밀번호 보호 화면 시연
올바른 사용자 이름과 비밀번호를 입력하면 ESP32에서 제공하는 유사한 웹 페이지가 표시될 것입니다.

ESP-IDF ESP32 웹 서버 기본 HTTP 인증 로그인 데모
웹 서버 페이지에 로그아웃 버튼이 있습니다. 로그아웃 버튼을 클릭하면 로그아웃됩니다.

ESP-IDF ESP32 웹 서버 HTTP 인증 로그아웃 로그아웃 화면
그러면 자동으로 홈페이지로 리디렉션되며, 웹 서버에 접속하려면 HTTP 인증 정보를 다시 입력해야 합니다.

ESP-IDF ESP32 웹 서버 기본 HTTP 인증 로그인 화면 시연
마무리
이 튜토리얼에서는 ESP-IDF ESP32 웹 서버(암호로 보호되는 웹 서버)에 인증 기능을 추가하는 방법을 배웠습니다. 이 튜토리얼에서 배운 내용은 ESP-IDF로 구축된 모든 웹 서버에 적용할 수 있습니다.
고생하셨습니다. 이 튜토리얼의 원본은 다음 링크를 따라가세요. 존중합니다.
'ESP32' 카테고리의 다른 글
| ESP32-C3 슈퍼 미니 빠르게 시작하기 가이드 배포 (0) | 2026.05.18 |
|---|---|
| ESP32 개발 보드에 USB-C 또는 Micro-USB 포트가 두 개 있는 이유 (0) | 2026.05.03 |
| ESP32 아두이노 환경과 ESP-IDF 환경 비교 (0) | 2026.04.30 |
| ESP-Claw 개념 (0) | 2026.04.27 |
| ESP32 SD 카드의 폴더를 PC에서 직접 보이게 (0) | 2026.04.22 |
| ESP32-S3 + E-Paper 완전 사용 가이드 (0) | 2026.04.15 |
| Iridium 9603N 전원 인가 시 BOD 리셋 이슈 (0) | 2026.04.12 |
| Iridium 9603N ESP32-S3 위성 통신 송신 절차 (0) | 2026.04.11 |
취업, 창업의 막막함, 외주 관리, 제품 부재!
당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약,
아이디어는 있지만 구현할 기술이 없는 막막함.
우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.
이제 고민을 멈추고, 캐어랩을 만나세요!
코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.
제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!
귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.
캐어랩