ESP32의 WiFi 연결이 불안정하다고? 지연시간이 길었나?
- 07 Dec, 2025
ESP32 WiFi 연결이 불안정하다고? 지연시간이 길었나?
오늘도 WiFi가 안 된다
출근했다. 어제 밤 11시까지 ESP32 WiFi 코드 손봤는데 오늘 아침에 테스트하니까 또 안 된다. 연결은 되는데 5분 지나면 끊긴다. 로그 보니까 WIFI_EVENT_STA_DISCONNECTED. 이유 코드는 WIFI_REASON_BEACON_TIMEOUT.
비콘 타임아웃. 즉 공유기한테 신호를 못 받았다는 거다.
“코드 문제 아닌가요?” 팀장이 물었다. 코드는 어제도 돌아갔다. 장소만 바뀌었다. 개발실에서는 되는데 회의실에서는 안 된다.

무선 통신은 이렇다. 같은 코드가 어디서는 완벽하고 어디서는 쓰레기가 된다. 하드웨어 환경에 따라 달라진다. 개발자가 제어할 수 없는 영역이다.
커피 마시고 오실로스코프 켰다.
안테나가 문제일까
ESP32 모듈 두 종류 있다. 하나는 PCB 안테나, 하나는 외부 안테나 커넥터. 우리 제품은 PCB 안테나다. 싸니까.
PCB 안테나는 보드 배치가 중요하다. 안테나 밑에 GND 플레인 있으면 신호 약해진다. 주변에 금속 케이스 있어도 문제다. 우리 케이스는 플라스틱이지만 내부에 EMI 차폐용 금속 테이프 붙어 있다.
HW팀한테 물었다. “안테나 주변 keep-out 영역 지켰어요?”
“네, 5mm 확보했습니다.”
데이터시트에는 10mm 권장이다. 5mm면 부족하다. 하지만 PCB 크기 제약이 있었다. 제품이 작아야 한다고 했다. 그래서 안테나 영역을 줄였다.

“그럼 외부 안테나 버전으로 테스트해보죠.”
외부 안테나 달았다. 연결 안정적이다. 10분 지나도 안 끊긴다. 1시간 돌렸다. 문제없다.
문제는 양산이다. 외부 안테나는 부품 비용이 오르고 조립 공정이 추가된다. 원가 담당자가 싫어한다. “PCB 안테나로 안 되나요?”
된다. 조건만 맞으면.
전파 간섭이라는 늪
회의실에서 안 되는 이유를 찾아야 한다. WiFi 스캔 돌렸다. 주변 AP가 12개다. 다들 2.4GHz 채널 1, 6, 11 쓴다. 우리 공유기는 채널 6. 옆 회사 공유기도 채널 6.
채널 겹치면 간섭 생긴다. 특히 2.4GHz는 채널 폭이 넓어서 1, 6, 11만 겹치지 않는다. 나머지는 다 겹친다.
공유기 설정 들어가서 채널 11로 바꿨다. 다시 테스트. 조금 나아졌다. 그래도 10분 지나면 끊긴다.
RSSI 로그 찍어봤다. 신호 강도다. -70dBm 정도 나온다. 약하다. -50dBm 이상은 되어야 안정적이다. -70dBm이면 패킷 로스 생긴다.

공유기를 가까이 가져왔다. 1m 거리. RSSI -40dBm. 연결 안정적이다. 30분 테스트, 문제없다.
거리가 문제다. 그리고 벽이 문제다. 회의실과 개발실 사이에 콘크리트 벽 하나 있다. 2.4GHz는 벽 투과 잘 되지만 신호는 약해진다.
팀장한테 보고했다. “안테나 성능 부족입니다. 거리 3m 이내에서는 안정적인데 벽 넘어가면 불안정합니다.”
“고객 환경은 더 안 좋을 텐데요.”
맞다. 고객은 공유기를 어디 둘지 모른다. 10m 떨어질 수도 있다. 벽 두 개 넘어갈 수도 있다.
코드로 버틸 수 있는 것들
하드웨어 못 바꾸면 펌웨어로 버텨야 한다. 할 수 있는 게 몇 가지 있다.
첫째, WiFi 파워 세이빙 끄기. ESP32는 기본적으로 DTIM 주기마다 슬립 들어간다. 전력 아끼려고. 하지만 슬립 들어가면 비콘 놓칠 수 있다. 신호 약할 때는 치명적이다.
esp_wifi_set_ps(WIFI_PS_NONE);
이거 하나로 연결 안정성 올라간다. 대신 전류 소모 20mA 증가. 배터리 제품이면 고민해야 한다. 우리는 DC 전원이라 상관없다.
둘째, 재연결 로직 강화. 연결 끊기면 자동으로 재연결 시도한다. ESP-IDF 기본 동작이다. 하지만 재연결 시도 간격이 짧으면 공유기가 부담스러워한다. 너무 길면 서비스 중단 시간이 길어진다.
나는 5초 간격으로 설정했다. 3번 실패하면 10초로 늘린다. 10번 실패하면 리부팅. 완벽하지 않지만 최선이다.
셋째, TCP Keepalive. TCP 연결 유지용 패킷이다. 일정 시간마다 상대한테 ‘살아있냐’ 물어본다. 대답 없으면 연결 끊는다.
int keepalive = 1;
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(int));
int keepidle = 30; // 30초 idle 후 시작
int keepintvl = 5; // 5초 간격
int keepcnt = 3; // 3번 실패하면 끊기
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(int));
이것도 도움 된다. 연결 끊겼는지 빨리 알 수 있다.
지연시간은 또 다른 문제
연결 안정성 해결했다고 끝이 아니다. 지연시간 문제가 남았다.
ping 찍어봤다. 평균 50ms. 나쁘지 않다. 그런데 가끔 500ms 튄다. 1초 넘을 때도 있다. 이게 문제다.
우리 제품은 실시간 제어 장치다. 명령 내리면 100ms 안에 반응해야 한다. 지연시간 500ms면 사용자는 ‘고장 났나’ 생각한다.
원인을 찾아야 한다. Wireshark 켰다. 패킷 캡처 시작.
보니까 재전송이 많다. TCP 패킷이 안 가고 다시 보낸다. WiFi 환경이 불안정하니까 패킷 로스 생긴다. 로스 생기면 재전송하고, 재전송하면 지연 증가한다.
해결 방법? 근본적으로는 WiFi 신호 강도 올려야 한다. 안테나 개선이 답이다.
펌웨어로 할 수 있는 건 UDP 쓰기. TCP는 신뢰성 보장하려고 재전송한다. UDP는 안 한다. 대신 데이터 유실 가능하다.
실시간성이 중요하면 UDP가 낫다. 제어 명령은 최신 상태가 중요하지 과거 명령은 의미 없다. 100ms 전 명령어 1초 후에 도착하면 쓸모없다.
“UDP로 바꿔볼까요?” 제안했다.
“데이터 유실되면요?” 팀장이 물었다.
“중요한 건 ACK 받고, 나머지는 재전송 안 하는 걸로요.”
Application 레벨에서 ACK 구현하기로 했다. 중요한 명령만 확인 응답 받는다. 나머지는 그냥 보낸다. 유실되면 다음 거 보낸다.
코드 수정 일주일 걸렸다. 테스트해보니 지연시간 평균 30ms. 튀어도 150ms. 개선됐다.
양산 전 마지막 테스트
양산 전에 환경 테스트 한다. 다양한 조건에서 돌려본다.
- 거리 테스트: 1m, 3m, 5m, 10m
- 장애물 테스트: 벽 없음, 벽 1개, 벽 2개
- 간섭 테스트: AP 1개, AP 5개, AP 10개
- 장시간 테스트: 24시간, 72시간
거리 테스트. 10m에서 RSSI -75dBm. 연결 유지되지만 패킷 로스 5%. 사용 가능하지만 권장하지 않는다. 매뉴얼에 ‘공유기와 5m 이내 설치 권장’ 쓰기로 했다.
장애물 테스트. 벽 2개 넘어가면 연결 불안정. 이건 어쩔 수 없다. 안테나 성능 한계다. 매뉴얼에 ‘장애물 최소화’ 추가.
간섭 테스트. AP 10개 환경에서 연결 유지된다. 하지만 지연시간 증가. 평균 80ms. 허용 범위다.
장시간 테스트. 72시간 돌렸다. 중간에 두 번 끊겼다. 자동 재연결 됐다. 괜찮다.
테스트 보고서 작성했다. 팀장한테 올렸다. “양산 가능합니다. 단, 안테나 개선 버전 2차 개발 건의합니다.”
“1차는 이대로 가고 2차에서 개선하죠.”
그렇게 결정됐다.
결국 하드웨어다
펌웨어 개발자지만 인정한다. WiFi 안정성은 결국 하드웨어다.
안테나 배치, 케이스 설계, PCB 레이아웃. 이게 90%다. 펌웨어는 나머지 10% 보완하는 거다.
개발실에서 완벽해도 현장에서 안 되면 소용없다. 고객 환경은 통제 불가능하다. 공유기 위치, 주변 간섭, 벽 재질, 전부 다르다.
그래서 마진을 남겨야 한다. 신호 강도 -50dBm 이상 목표로 설계해야 현장에서 -70dBm 나와도 버틴다.
우리는 -70dBm 목표로 설계했다. 그래서 현장에서 불안정하다. 다음 버전에서는 외부 안테나 쓰기로 했다. 원가 오르지만 안정성이 중요하다.
고객 클레임 처리 비용이 안테나 비용보다 비싸다. 한 번 출장 가면 20만원이다. 클레임 10건이면 200만원. 안테나는 개당 500원 차이다. 1000개 팔아도 50만원 차이.
계산하면 답 나온다.
퇴근하면서 생각했다. 무선 통신 제품은 어렵다. 유선은 꽂으면 된다. 무선은 환경 따라 달라진다.
다음 프로젝트는 유선으로 하고 싶다. 하지만 IoT 시대에 유선 제품은 없다. 다들 무선 원한다. 편하니까.
편한 건 사용자고 고생은 개발자다. 뭐 어쩌겠나. 월급 받는 일이다.
내일은 BLE 디버깅이다. WiFi보다 더 까다롭다고 들었다. 미리 두통약 챙겨야겠다.
