Wi-Fi 프로비저닝(Provisioning)이란 디스플레이나 입력 버튼이 없는 스마트 홈 기기(IoT)가 인터넷에 연결될 수 있도록 Wi-Fi 네트워크 정보(SSID, 비밀번호)를 기기에 전달하고 설정하는 초기화 과정을 말합니다.
프로비저닝이 필요한 이유는 스마트 전구, 로봇 청소기, 스마트 플러그 같은 기기들은 화면이나 키보드가 없어 사용자가 직접 와이파이 비밀번호를 입력할 수 없습니다. 따라서 스마트폰 앱을 통해 기기를 임시로 네트워크에 연결하고, 구동에 필요한 Wi-Fi 환경을 '준비'시켜 주는 과정이 반드시 필요합니다.
프로비저닝 작동 방식 (주요 3단계)
- 기기 모드 전환: 기기가 스스로 일시적인 임시 무선 공유기(AP, Access Point) 모드로 작동합니다.
- 연결 및 정보 전송: 사용자가 스마트폰을 해당 기기의 임시 Wi-Fi에 연결한 후, 전용 앱을 통해 실제 사용할 집의 Wi-Fi 이름과 비밀번호를 기기에 전송합니다.
- 네트워크 적용: 기기가 임시 모드를 종료하고 전달받은 일반 Wi-Fi 네트워크에 정상적으로 접속합니다.
사용되는 대표적인 방법
- SoftAP 방식: 위에서 설명한 방식으로, 기기를 직접 임시 무선 네트워크(AP)로 만들어 정보를 수신합니다.
- BLE(저전력 블루투스) 방식: 스마트폰과 기기가 블루투스로 먼저 통신하여 Wi-Fi 정보를 넘겨주는 방식입니다. 연결이 더 안정적이고 빠릅니다.
- WPS (Wi-Fi Protected Setup): 공유기의 WPS 버튼과 기기의 버튼을 동시에 눌러 간단하게 연결하는 방식입니다.
여기에서는 SoftAP 프로비저닝을 설명한다.
BLE 프로비저닝은 다음 포스팅을 참고합니다.
동작 방식
처음 부팅 시 (저장된 Wi-Fi 없음)
1. ESP32_Provisioning 이름의 AP가 뜸 (비번: 12345678)
2. 스마트폰/PC로 해당 AP에 접속
3. 브라우저가 자동으로 캡티브 포털 페이지를 열거나, 192.168.4.1 직접 접속
4. SSID + 비밀번호 입력 → "저장 및 연결" 클릭
5. ESP32가 NVS(플래시)에 저장 후 재부팅
이후 부팅 시 (저장된 자격증명 있음)
- 저장된 Wi-Fi로 자동 연결 시도 (10초 대기)
- 성공 → STA 모드로 계속 동작
- 실패 → 다시 AP 모드 진입해서 재설정 가능
사용 방법
1. Arduino IDE에서 ESP32 보드 선택 후 업로드
2. 시리얼 모니터 열기 (115200 baud)
3. ESP32_Provisioning AP에 접속
4. 브라우저에서 192.168.4.1 접속
5. 연결할 Wi-Fi 정보 입력
시리얼 모니터 출력은 아래와 같다.
시리얼 출력 전체
처음 부팅 (저장 없음)
--- SoftAP 프로비저닝 시작 ---
AP IP 주소: 192.168.4.1
AP SSID: ESP32_Provisioning / PW: 12345678
웹 서버 시작됨
사용자가 SSID/PW 입력 후 제출
입력된 SSID: MyHomeWiFi
재부팅...
재부팅 후 — 연결 성공
저장된 자격증명으로 연결 시도: MyHomeWiFi
..........
Wi-Fi 연결 성공!
IP 주소: 192.168.1.42
재부팅 후 — 연결 실패
저장된 자격증명으로 연결 시도: MyHomeWiFi
..........
연결 실패 — 프로비저닝 AP 시작
--- SoftAP 프로비저닝 시작 ---
AP IP 주소: 192.168.4.1
AP SSID: ESP32_Provisioning / PW: 12345678
웹 서버 시작됨
코드
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <Preferences.h>
// AP 설정
const char* apSSID = "ESP32_Provisioning";
const char* apPassword = "12345678"; // 오픈 AP 방지
WebServer server(80);
DNSServer dnsServer;
Preferences preferences;
// 재부팅 예약 플래그 (응답 전송 완료 후 재부팅)
bool shouldRestart = false;
unsigned long restartTime = 0;
// 프로비저닝 HTML 페이지
String SendHTML() {
String ptr = "<!DOCTYPE html><html>\n";
ptr += "<head><meta charset=\"UTF-8\">\n";
ptr += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n";
ptr += "<title>ESP32 Wi-Fi Provisioning</title>\n";
ptr += "<style>\n";
ptr += "body { font-family: Arial; text-align: center; margin: 0px auto; padding-top: 30px;}\n";
ptr += "input { padding: 10px; margin: 10px; width: 80%; }\n";
ptr += "button { padding: 10px; width: 85%; background-color: #4CAF50; color: white; border: none; font-size: 16px; }\n";
ptr += "</style>\n";
ptr += "</head>\n";
ptr += "<body>\n";
ptr += "<h1>Wi-Fi 설정</h1>\n";
ptr += "<form action=\"/connect\" method=\"POST\">\n";
ptr += "<label>SSID:</label><br><input type=\"text\" name=\"ssid\" maxlength=\"32\" placeholder=\"Wi-Fi 이름 입력\"><br>\n";
ptr += "<label>비밀번호:</label><br><input type=\"password\" name=\"pass\" maxlength=\"63\" placeholder=\"비밀번호 입력\"><br>\n";
ptr += "<button type=\"submit\">저장 및 연결</button>\n";
ptr += "</form>\n";
ptr += "</body>\n";
ptr += "</html>\n";
return ptr;
}
void handleRoot() {
server.send(200, "text/html", SendHTML());
}
// 캡티브 포털: 알 수 없는 경로 → 루트로 리디렉션
void handleNotFound() {
server.sendHeader("Location", "/", true);
server.send(302, "text/plain", "");
}
void handleConnect() {
String ssid = server.arg("ssid");
String pass = server.arg("pass");
// 입력값 검증
if (ssid.length() == 0 || ssid.length() > 32) {
server.send(400, "text/html; charset=utf-8",
"<h2>오류: SSID가 비어 있거나 너무 깁니다. (최대 32자)</h2>"
"<a href=\"/\">돌아가기</a>");
return;
}
if (pass.length() > 63) {
server.send(400, "text/html; charset=utf-8",
"<h2>오류: 비밀번호가 너무 깁니다. (최대 63자)</h2>"
"<a href=\"/\">돌아가기</a>");
return;
}
Serial.println("입력된 SSID: " + ssid);
// 비밀번호는 시리얼 출력 생략 (보안)
// NVS에 자격증명 저장
preferences.begin("wifi-creds", false);
preferences.putString("ssid", ssid);
preferences.putString("pass", pass);
preferences.end();
server.send(200, "text/html; charset=utf-8",
"<!DOCTYPE html><html><head><meta charset=\"UTF-8\"></head><body>"
"<h2>Wi-Fi 정보가 저장되었습니다.</h2>"
"<p>잠시 후 기기가 재부팅됩니다...</p>"
"</body></html>");
// 응답 전송 완료 후 재부팅 예약 (1초 후)
shouldRestart = true;
restartTime = millis() + 1000;
}
void startProvisioningAP() {
Serial.println("\n--- SoftAP 프로비저닝 시작 ---");
// AP+STA 모드 명시적 설정
WiFi.mode(WIFI_AP_STA);
WiFi.softAP(apSSID, apPassword);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP 주소: ");
Serial.println(IP);
Serial.print("AP SSID: ");
Serial.print(apSSID);
Serial.print(" / PW: ");
Serial.println(apPassword);
// 캡티브 포털 DNS
dnsServer.start(53, "*", IP);
// 웹 서버 라우팅
server.on("/", handleRoot);
server.on("/connect", HTTP_POST, handleConnect);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("웹 서버 시작됨");
}
void setup() {
Serial.begin(115200);
delay(100);
// 저장된 자격증명 로드
preferences.begin("wifi-creds", true);
String savedSSID = preferences.getString("ssid", "");
String savedPass = preferences.getString("pass", "");
preferences.end();
// 저장된 자격증명이 있으면 STA 연결 먼저 시도
if (savedSSID.length() > 0) {
Serial.println("\n저장된 자격증명으로 연결 시도: " + savedSSID);
WiFi.mode(WIFI_STA);
WiFi.begin(savedSSID.c_str(), savedPass.c_str());
unsigned long start = millis();
while (millis() - start < 10000) {
if (WiFi.status() == WL_CONNECTED) break;
delay(500);
Serial.print(".");
}
Serial.println();
if (WiFi.status() == WL_CONNECTED) {
Serial.println("Wi-Fi 연결 성공!");
Serial.print("IP 주소: ");
Serial.println(WiFi.localIP());
return; // 연결 성공: AP 모드 진입 불필요
}
Serial.println("연결 실패 — 프로비저닝 AP 시작");
}
startProvisioningAP();
}
void loop() {
// STA 연결 상태면 루프에서 처리 없음
if (WiFi.getMode() == WIFI_STA && WiFi.status() == WL_CONNECTED) {
return;
}
server.handleClient();
dnsServer.processNextRequest();
// 재부팅 예약 처리 (응답 전송 완료 보장)
if (shouldRestart && millis() >= restartTime) {
Serial.println("재부팅...");
ESP.restart();
}
}

'ESP32' 카테고리의 다른 글
| 실내에서 센티미터 수준의 정밀도로 모든 것을 추적 (0) | 2026.05.26 |
|---|---|
| 저전력 ESP32 회로 제작기 (0) | 2026.05.25 |
| ESP32를 이용한 엣지 임펄스 기반 오프라인 음성 인식 (0) | 2026.05.25 |
| esp32-s3 보드 정보 출력으로 Flash와 PSRAM 크기 알아내자 (0) | 2026.05.21 |
| ESP32-S3 개발 보드 RGB LED 테스트 (0) | 2026.05.21 |
| ESP32 웹 업데이트 OTA(Over The Air) 프로그래밍 (0) | 2026.05.21 |
| ESP32 기본 OTA(Over The Air) 프로그래밍 (0) | 2026.05.21 |
| ESP32-C3 슈퍼 미니 빠르게 시작하기 가이드 배포 (0) | 2026.05.18 |
취업, 창업의 막막함, 외주 관리, 제품 부재!
당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약,
아이디어는 있지만 구현할 기술이 없는 막막함.
우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.
이제 고민을 멈추고, 캐어랩을 만나세요!
코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.
제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!
귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.
캐어랩