약 먹었는지 매번 까먹는다면? 3만원 물리 버튼으로 해결하기 — 코드 한 줄 안 쓴 설계 주차
이번 주는 아무것도 구현하지 않았습니다. 부품도 아직 안 샀고요.
대신 AI와 계속 대화하면서 "무엇을 만들지"를 좁혀갔는데, 그 과정에서 제가 처음에 세웠던 가정이 여러 번 깨졌습니다. 그 깨지는 과정이 오히려 남길 가치가 있다고 생각해서, 결과물 대신 의사결정 로그를 정리합니다.
1. 배경 — 이미 있는 시스템에 뭘 더 붙이려는 건가
몇 주 전부터 집에 이런 걸 돌리고 있습니다.
텔레그램 (부부 둘) → 맥미니 에이전트 → Supabase → 거실 태블릿
"우유 사왔어", "금요일 3시 병원 예약" 같은 걸 텔레그램에 던지면 맥미니에서 도는 에이전트가 알아서 분류하고, 챙길 게 생기면 거실 태블릿에 카드로 뜹니다.
잘 쓰고 있는데, 약 복용만 유독 기록이 안 됐습니다.
이유가 명확했어요. 약은 먹는 데 3초 걸리는데, 폰을 꺼내서 텔레그램을 열고 "약 먹었어"를 치는 게 10초입니다. 그래서 안 하게 되더라고요. 기록의 가치보다 기록의 비용이 큰 겁니다.
그래서 결론: 누르는 데 0.5초 걸리는 물리 버튼이 필요하다.
---
2. 첫 질문 — "이걸 어떻게 만들죠?"가 아니었다
처음 AI에게 던진 건 설계 문서였습니다. "이런 걸 만들고 싶은데 어떻게 생각해?"가 아니라, 제가 먼저 초안을 쓰고 검증을 요청했습니다.
이 방식이 좋았던 게, 대화가 "뭘 만들까"가 아니라 "이 설계의 약한 곳이 어디냐"로 시작됐습니다. 그 결과 나온 원칙 세 가지가 이번 주 결론의 대부분입니다.
원칙 1: 펌웨어는 최대한 멍청하게
ESP32는 버튼을 읽고, 요청을 보내고, 응답을 화면에 그리는 것까지만 합니다. 하루 경계가 언제인지, 중복인지, 리마인드를 보낼지는 전부 서버가 판단합니다.
이유는 하나입니다. 펌웨어 재플래싱은 귀찮습니다. 로직이 펌웨어에 들어가면 "리마인드를 9시에서 8시로 바꾸자"에도 USB를 꽂아야 합니다. 서버에 있으면 코드 한 줄 고치고 재시작이면 끝입니다.
그래서 화면에 뿌릴 문구조차 서버가 내려줍니다.
{
"status": "recorded",
"display": {
"title": "오늘 완료 · 09:14",
"subtitle": "이번 주 6/7일",
"color": "green"
}
}
ESP32는 이 JSON을 받아서 color대로 배경을 칠하고 title을 그릴 뿐입니다.
원칙 2: 시각의 근거는 age_ms
ESP32에는 RTC가 없어서 전원이 나가면 시계를 잃습니다. 그래서 이벤트에 "몇 시에 눌렀다"가 아니라 "눌린 지 몇 ms 됐다"를 담습니다.
{ "event_id": "med-button-01:7:42", "type": "taken", "age_ms": 1830 }
서버가 실제_시각 = 수신_시각 - age_ms로 복원합니다. NTP가 안 붙었든, 오프라인 큐에 3시간 쌓여 있다가 나중에 전송되든 시각이 정확합니다.
원칙 3: 취소를 DELETE로 처리하지 않는다
이벤트 로그는 append-only입니다. 취소하면 cancelled 이벤트를 추가로 쌓고, 현재 상태는 로그를 재생해서 계산합니다.
med_events (절대 UPDATE/DELETE 안 함)
event_id TEXT PRIMARY KEY -- 멱등성 키
type TEXT -- taken | cancelled
occurred_at TIMESTAMP -- received_at - age_ms
source TEXT -- button | dashboard | agent
이러면 "몇 번 잘못 눌렀다가 취소했는지"까지 남아서 나중에 분석 재료가 됩니다. 그리고 append-only는 멱등성과 궁합이 좋습니다.
한 가지 더 — 하루 경계는 자정이 아니라 새벽 4시로 잡았습니다. 23:55에 먹고 5분 뒤 날짜가 넘어가면 "오늘 안 먹음"이 되는 게 위험해서요.
---
3. 아키텍처
```
[60mm 아케이드 버튼]
│ GPIO / GND 2가닥
[ESP32] │ 버튼 읽고 HTTP 전송, USB-C 전원
│ WiFi
[집 공유기] 같은 랜 · 외부 노출 없음
│ HTTP POST /api/med/event
[맥미니] 에이전트 + HTTP 서버
│ 중복 · 4시 경계 판정
[Supabase] med_events 이벤트 로그
│
[태블릿 · 폰] 대시보드 · 텔레그램
← 응답 JSON (색 · 문구) 은 맥미니가 ESP32로 되돌려줌
```
엔드포인트는 2개면 충분하다는 결론이었습니다.
- POST /api/med/event — 이벤트 기록 (멱등성 키로 중복 제거)
- GET /api/med/state — 5분마다 폴링해서 화면 최신화 (태블릿에서 수동 기록해도 버튼 화면이 따라옴)
버튼과 태블릿 대시보드는 같은 API를 쓰는 대등한 클라이언트입니다. 어느 쪽에서 기록해도 반대쪽에 반영됩니다.
---
4. 부품 고르다 깨진 가정 4개
여기가 이번 주의 진짜 내용입니다. 설계는 30분이면 나왔는데, 부품 고르는 데 몇 시간이 걸렸고 그 과정에서 제 가정이 계속 틀렸습니다.
가정 ①: "RGB 아케이드 버튼을 사면 빨강/초록이 되겠지"
틀렸습니다. 60mm 아케이드 버튼은 시장에 사실상 전부 단색 LED입니다. 구매할 때 빨강이면 빨강만, 초록이면 초록만 켜집니다. 한 버튼이 색을 오가는 제품은 이 크기에 없습니다.
그런데 이게 문제가 안 됐습니다. 제 설계 문서에 이미 답이 있었거든요.
> "멀리서 힐끗 봤을 때 색만으로 판단돼야 하므로, 배경 전체를 칠한다"
색은 버튼이 아니라 화면이 담당하는 설계였습니다. 버튼은 그냥 눌리기만 하면 됐던 겁니다. 스스로 쓴 문서를 다시 읽고 깨달았습니다.
가정 ②: "화면 달린 보드를 사야 한다"
반은 맞고 반은 틀렸습니다. 가격 차이가 컸습니다.
| | ESP32 DevKitC | T-Display-S3 |
|---|---|---|
| 가격 | 12,550원 | 44,950원 |
| 배송 | 내일 | 2주 |
| 화면 | 없음 | 1.9" 컬러 |
화면이 없어도 LED 달린 아케이드 버튼의 켜짐/꺼짐만으로 "오늘 먹었나"는 판단됩니다. 문구까지 보고 싶으면 3천원짜리 OLED를 나중에 붙여도 되고요.
결론은 둘 다 사기로 했습니다. 싼 걸로 내일 바로 왕복을 돌려보고, 비싼 건 도착하면 화면을 붙이는 순서로요. 2주를 기다리면 흐름이 끊긴다는 게 이유였습니다.
가정 ③: "T-Display라고 적힌 건 다 같은 제품이겠지"
쇼핑몰에서 "T-Display"로 검색하니 이런 게 나왔습니다.
- T-ETH ESP32 S3 이더넷 개발보드 T-Display, T-ETH-Lite → 실제로는 이더넷 보드
- LILYGO T-Display-S3 AMOLED → 화면 종류가 다르고 핀 설정도 다름, 68,600원
제가 필요한 건 T-Display-S3, 1.9인치 ST7789 LCD 딱 그것이었는데, 이름이 비슷한 다른 제품이 훨씬 많았습니다. 모델명 전체를 정확히 확인하지 않으면 엉뚱한 걸 사게 됩니다.
가정 ④: "매크로 키패드를 붙이면 확장성이 좋아지겠지"
이게 제일 오래 붙잡은 부분입니다.
3.5인치 LCD 달린 8키 매크로 패드(53,878원)를 발견하고, "지금은 버튼 1개지만 나중에 여러 입력을 받으려면 이게 낫지 않나?" 생각했습니다. ESP32에 USB로 연결하면 되지 않을까 하고요.
두 가지 이유로 기각했습니다.
첫째, 기술적 벽. 키보드를 붙이려면 ESP32가 USB 호스트(PC 역할)를 해야 합니다. 그런데 제가 사려던 ESP32 DevKitC는 USB 호스트를 아예 지원하지 않습니다 — 그 USB 포트는 전원/업로드용 UART 브리지일 뿐 진짜 USB가 아닙니다. ESP32-S3라면 가능하긴 하지만 Arduino 환경에선 예제가 거의 없어 esp-idf 수준으로 파야 하고, ESP32가 키보드에 5V를 공급해줘야 하는 문제도 남습니다.
둘째, 확장성에 대한 오해. 이게 더 중요했습니다.
제가 "확장성 = 한 기기에 버튼을 많이 다는 것"으로 생각했는데, 집안일은 그렇지 않습니다.
```
약 먹음 → 침실/식탁
음쓰 버림 → 주방
고양이 밥 → 베란다
택배 받음 → 현관
```
일이 일어나는 장소에 버튼이 있어야 누릅니다. 매크로패드는 8키가 한 덩어리라 전부 한 자리에 있어야 하고, 주방에서 음쓰 버리고 침실까지 걸어가서 누르는 일 은 일어나지 않습니다.
반면 GPIO 직결은:
- 버튼 하나당 2가닥, ESP32 하나에 20개 이상
- 선 길이만큼 흩뿌릴 수 있음
- 버튼뿐 아니라 아무 센서나 받음 (문 열림, 무게, 온습도, 모션)
그리고 진짜 확장 전략은 ESP32를 여러 대 두는 것이었습니다. 매크로패드 한 대 값(53,878원)이면 ESP32를 4대 집안 곳곳에 뿌릴 수 있습니다. 서버는 device_id만 다르게 받으면 되고, 이건 이미 설계에 들어있던 필드였습니다.
---
5. 그 외 자잘하지만 중요했던 결정들
USB는 전원만, 통신은 WiFi. 처음에 "USB로 맥미니에 연결하나요?"라고 물었는데, 그러면 버튼이 케이블 길이만큼 맥미니에 묶입니다. 약 먹는 자리에 맥미니가 있어야 하는 이상한 구조가 되죠. USB-C는 콘센트에 꽂고 통신은 WiFi로 하는 게 맞습니다.
맥미니 IP 고정은 필수. DHCP라 공유기 재부팅 시 IP가 바뀌면 어느 날 버튼이 조용히 먹통이 됩니다. 공유기에서 MAC 기반 고정 할당을 걸어둬야 합니다.
케이스는 "통 결합 완제품"이 있다. 산업용 푸시버튼 박스(5천원~1.3만원)는 이미 케이스에 버튼이 달려 나오고 선 빼는 구멍까지 있습니다. 다만 Ø22~30mm 버튼용이라 60mm 아케이드는 안 들어갑니다. 60mm를 쓰려면 다이소 통에 구멍을 뚫거나 아크릴 재단을 주문해야 합니다.
LED 아케이드 버튼의 "장착 홀"은 60mm가 아니다. 60mm는 누르는 캡 크기고, 실제 뚫어야 할 구멍은 제품마다 44~59mm로 제각각입니다. 상세페이지에서 mounting hole 치수를 반드시 확인해야 합니다.
---
6. 이번 주에 배운 것
하나. 스스로 쓴 설계 문서를 다시 읽으면 답이 있을 때가 있다.
RGB 버튼이 없다는 걸 알고 당황했는데, 제 문서에 "색은 배경 전체를 칠해서 표현한다"고 이미 써 있었습니다. 부품에 꽂혀서 설계를 잊고 있었던 겁니다.
둘. "확장성"이라는 단어를 쓸 때 그게 무슨 축인지 정의해야 한다.
저는 "입력 개수"로 생각했는데, 실제로 필요한 확장성은 "물리적 분산"이었습니다. 축을 잘못 잡으니 5만원짜리 오답(매크로패드)이 정답처럼 보였습니다.
셋. 부품 이름은 끝까지 읽어야 한다.
"T-Display"까지만 보고 샀으면 이더넷 보드가 왔을 겁니다.
넷. 구현 전에 대화로 태우는 시간이 아깝지 않다.
이번 주 실제 산출물은 설계 문서와 부품 목록뿐입니다. 그런데 이 과정이 없었으면 매크로패드를 사서 USB 호스트와 씨름하다가, 그게 안 되면 프로젝트를 접었을 가능성이 높습니다.
---
7. 다음 주 계획
부품이 도착하면 이 순서로 갑니다.
1. 맥미니에 엔드포인트 2개 추가 — 하드웨어 없이 curl로 먼저 검증. 버튼이 보낼 요청과 curl 요청이 동일하므로, 이 시점에 시스템이 사실상 완성됩니다.
2. 보드 단독 테스트 — 내장 버튼으로 시리얼 출력까지
3. WiFi + HTTP 왕복 — 여기서 이미 쓸 수 있는 물건이 됩니다
4. 견고하게 — 오프라인 큐(NVS), event_id 멱등성, 백오프 재시도, 5분 폴링
5. 아케이드 버튼 + 케이스 조립
특히 1번이 좋은 게, 하드웨어를 기다리는 동안 절반 이상을 만들 수 있다는 점입니다.
---
부품 목록 (총 3만원 내외)
| 항목 | 가격 | 비고 |
|---|---|---|
| ESP32 DevKitC V4 | 12,550원 | WiFi 내장, 화면 없음 |
| 60mm LED 아케이드 버튼 | 약 1만원 | 장착 홀 치수 확인 필수 |
| 악어클립↔듀폰 점퍼 | 3천원 | 납땜 회피 |
| 케이스 (다이소 통 등) | 2천원 | 또는 푸시버튼 박스 5천원 |
(화면까지 원하면 T-Display-S3 44,950원 추가)
---