AI 기반 서비스

Today
-
Total
-
← 목록으로

Tapo 전원 사이클의 함정 - DHCP가 바꿔버린 IP

이창연•2026년 1월 31일

Tapo 전원 사이클의 함정

아침에 맞이한 텔레그램 알림

2026년 1월 31일 아침. 텔레그램에 익숙한 알림이 와 있었다.

⚡️ Spark 전원 꺼짐 감지
• Tapo P110M 전원 사이클 실행 중...
• 플러그 IP: 192.168.50.77

어제 구축한 Tapo 자동 복구 시스템이 작동했다! 이번엔 진짜 살아났겠지?

8시 20분. CraftBay MCP는 여전히 응답이 없었다.

316번의 실패

Spark Recovery MCP(miniPC)를 통해 상태를 확인했다.

[2026-01-31 08:20:34] WARN: CraftBay MCP 헬스체크 실패 (연속 313 회)
[2026-01-31 08:20:34] WARN: Spark 서버 ping 실패 - 전원 꺼짐 추정
[2026-01-31 08:20:34] INFO: 전원 사이클 이미 실행됨, 부팅 대기 중...

313번 연속 실패. 그런데 "전원 사이클 이미 실행됨"이라고 한다.

state.json을 확인해보니:

{
  "craftbay": {
    "failures": 316,
    "power_cycled": true,
    "last_power_cycle": "2026-01-31T03:08:31+09:00"
  }
}

새벽 3시 8분에 전원 사이클을 실행했고, 이후 계속 부팅 대기 중이었다. 5시간 넘게.

범인은 플러그 상태

Tapo 플러그 상태를 직접 확인했다:

$ python3 tapo-spark.py status
장치명: 서버 플러그
전원상태: OFF

OFF?!

전원 사이클은 OFF → 5초 대기 → ON 순서로 실행되어야 하는데, 플러그가 OFF에서 멈춰있었다.

진짜 원인: DHCP IP 변경

복구 후 원인을 분석해보니, 전날 있었던 인터넷 장애가 문제였다.

  1. 인터넷 장애 발생
  2. 모뎀과 공유기 리셋
  3. DHCP가 각 장비에 새 IP 할당
  4. Tapo 플러그 IP가 변경됨
  5. 복구 스크립트는 하드코딩된 기존 IP로 접속 시도
  6. 연결 실패 → 플러그 OFF 상태로 방치

스크립트를 다시 보자:

PLUG_IP = "192.168.50.77"  # 하드코딩된 IP

async def get_device():
    client = ApiClient(TAPO_EMAIL, TAPO_PASSWORD)
    return await client.p110(PLUG_IP)  # 이 IP가 맞아야 함

공유기 리셋 후 Tapo 플러그가 다른 IP를 받았다면, 이 스크립트는 엉뚱한 IP에 명령을 보내거나 연결 자체가 실패했을 것이다.

원격으로 전원 복구

Claude.ai에서 Spark Recovery MCP를 통해 직접 전원을 켰다:

$ python3 tapo-spark.py on
Tapo P110M: 전원 ON

$ python3 tapo-spark.py status
장치명: 서버 플러그
전원상태: ON

이제 Spark 부팅을 기다린다.

30초... 1분... 2분...

$ ping -c 3 192.168.50.10
From 192.168.50.196 icmp_seq=1 Destination Host Unreachable

여전히 응답이 없다. 그런데 3분쯤 지나니 부팅이 완료됐다.

Welcome to NVIDIA DGX Spark Version 7.3.1 (GNU/Linux 6.14.0-1013-nvidia aarch64)
 System information as of Sat Jan 31 08:28:28 AM KST 2026
  System load:  1.45              Temperature:             82.9 C
  Usage of /:   70.5% of 3.67TB   Processes:               737
  Memory usage: 69%               Users logged in:         0

DGX Spark 부팅 시간이 생각보다 길었을 뿐. 원격 복구 성공!

근본적인 해결: DHCP 예약

같은 일이 반복되지 않도록, 공유기에서 DHCP 예약(IP 고정)을 설정했다.

장비MAC고정 IP
H200-Hub3C:6A:D2:CB:D5:D4192.168.50.8
spark-home4C:BB:47:29:B1:81192.168.50.150
DESKTOP-HOME9C:6B:00:8D:95:70192.168.50.102
miniPC68:1D:EF:4E:2C:AD192.168.50.196
Tapo P110M98:03:8E:D3:24:19192.168.50.77

특히 Tapo 스마트 플러그의 IP를 고정한 것이 핵심이다. 이제 공유기를 리셋해도 플러그 IP는 항상 192.168.50.77이 된다.

개선해야 할 것들

이번 장애에서 드러난 문제점:

1. 하드코딩된 IP

PLUG_IP = "192.168.50.77"  # 이게 바뀌면 끝

개선안: DHCP 예약으로 IP 고정 (완료)

2. 전원 사이클 스크립트에 예외 처리 없음

# 현재 코드 - 에러 시 플러그가 OFF로 남을 수 있음
await device.off()
await asyncio.sleep(delay)
await device.on()  # 여기서 실패하면?

개선안:

try:
    await device.off()
    await asyncio.sleep(delay)
    await device.on()
except Exception as e:
    # 실패 시 무조건 ON 재시도
    try:
        await device.on()
    except:
        pass
    raise

3. 전원 상태 확인 로직 없음

Watchdog이 "전원 사이클 이미 실행됨"만 보고 무한 대기했다. 플러그가 실제로 ON인지 확인하는 로직이 필요하다.

4. 복구 타임아웃 없음

새벽 3시에 전원 사이클 후 5시간 동안 그냥 기다렸다. 일정 시간 후 재시도하는 로직이 필요하다.

교훈

  1. DHCP는 믿을 게 못 된다 - 네트워크 장비 리셋하면 IP가 바뀐다. 서버와 IoT 장비는 반드시 IP 고정.

  2. 하드코딩은 재앙의 씨앗 - 설정 파일로 분리하거나, 최소한 DHCP 예약으로 IP를 고정해야 한다.

  3. 외부 의존성은 불안정하다 - Tapo는 클라우드 인증이 필요하고, 네트워크 상태에 민감하다. 폴백 로직 필수.

  4. "실행했다" ≠ "성공했다" - 실제 상태를 확인해야 한다. 플러그가 OFF인지 ON인지.

이제 진짜 안 죽겠지. 아마도.


추신: 결국 원격으로 해결해서 다행이다. 주말 아침에 서버 전원 누르러 집에 가는 건 좀 그렇잖아.