- 03 Dec, 2025
전원을 껐다 켜봤나요?
"전원을 껐다 켜봤나요?" 오늘도 전원부터 출근했다. 어제 돌려놓은 테스트. 실패했다. 메일을 확인한다. HW팀 김과장님. "펌웨어 쪽 확인 부탁드립니다." 또다. 보드를 본다. LED가 안 켜진다. 전원을 뺐다 꽂는다. 켜진다. "해결됐습니다." 메일을 보낸다. 5분 걸렸다.이게 우리 일이다. 전원을 끄고 켠다. 하루에 50번. 99%의 좌절 오후 2시. 회의실. "통신이 안 됩니다." PM이 말한다. "리셋 해보셨어요?" 내가 묻는다. "해봤는데 안 돼요." 알았다. 직접 가봐야 한다. 현장에 간다. UART 로그를 본다. 통신은 된다. 그런데 데이터가 이상하다. "전원 껐다 켜보겠습니다." 모두가 본다. 나를. 전원을 끈다. 5초 기다린다. 켠다. 똑같다. 안 된다. "역시 펌웨어 문제네요." 누군가 말한다.99%는 이렇다. 리셋으로 해결 안 된다. 그런데도 우리는 계속한다. 왜냐면 1%가 있으니까. 1%의 기적 작년 겨울이었다. 양산 한 달 전. 필드 테스트 중. 기기가 멈췄다. "재현이 안 됩니다." 테스트팀이 말한다. 현장에 갔다. 한 시간 기다렸다. 멈췄다. JTAG를 연결한다. 스택을 본다. watchdog에 걸렸다. 그런데 원인을 모른다. 코드를 본다. 로그를 본다. 파형을 본다. 3일을 본다. "일단 리셋 루틴 추가하겠습니다." 보고했다. PM이 물었다. "근본 원인은요?" "모릅니다. 그런데 워치독으로 리셋되면 복구는 됩니다." 침묵. "그렇게 하세요." 그게 정답이었다. 양산 후 6개월. 문제없다.리셋이 해결책은 아니다. 그런데 때로는 유일한 대안이다. 리셋의 철학 임베디드 개발자들은 안다. 전원 껐다 켜는 게 얼마나 중요한지. 웹 개발자들은 모른다. 걔네는 서버 재시작하면 된다. 1초면 된다. 우리는 다르다. 리셋에도 종류가 있다. 소프트웨어 리셋. NVIC_SystemReset(). 코드로 리셋한다. 빠르다. 하지만 일부 레지스터는 안 지워진다. 하드웨어 리셋. NRST 핀. 완전히 리셋된다. 그런데 보드를 만져야 한다. 전원 리셋. 플러그를 뺀다. 가장 확실하다. 모든 걸 초기화한다. 프로젝트마다 다르다. 어떤 리셋이 필요한지. 작년 프로젝트. BLE 스택이 꼬였다. 소프트웨어 리셋으론 안 풀렸다. 전원을 완전히 끊어야 했다. 코드에 추가했다. "배터리 분리 후 3초 대기하세요." 사용자 매뉴얼에 들어갔다. 엔지니어가 아닌 사람들은 이해 못 한다. "왜 그냥 리셋 버튼 안 만들어요?" 만들었다. 버튼으로는 안 된다. 전원 분리가 필요하다. "왜요?" 설명했다. 10분 동안. 이해 못 한다. 그냥 했다. 디버깅의 늪 오늘 오전. 새 프로젝트. ESP32로 WiFi 연결. 안 된다. 로그를 본다. "Failed to connect". 당연하다. 그걸 보려고 로그를 보는 게 아닌데. 스펙 문서를 연다. 732페이지. WiFi 챕터. 읽는다. 한 시간. 뭔 소린지 모른다. "일단 리셋해보자." esp_restart(). 코드에 추가한다. 테스트한다. 안 된다. 다시 리셋. 또 안 된다. 10번 반복. 안 된다. 점심시간이다. 점심 먹고 온다. 다시 본다. 로그를 자세히 본다. "SSID not found". 아. WiFi 이름을 잘못 입력했다. 오타. 고친다. 된다. 리셋은 필요 없었다. 그런데 어쨌든 했다. 양산의 공포 양산이 제일 무섭다. 개발할 땐 문제 생기면 고친다. 펌웨어 업데이트하면 된다. 양산 후엔 다르다. 제품이 나간다. 만 대. 십만 대. 문제 생기면? 리콜. 비용이 몇억. 그래서 리셋 루틴을 넣는다. 보험처럼. Watchdog. 무조건 넣는다. 코드가 멈추면 리셋. Brown-out detection. 전압 떨어지면 리셋. Stack overflow check. 스택 터지면 리셋. 예외 처리. Hardfault 나면? 로그 남기고 리셋. 동료가 물었다. "너무 많이 리셋하는 거 아냐?" 아니다. 적당하다. "근본 원인 찾아야 하는 거 아냐?" 찾는다. 당연히. 그런데 못 찾을 수도 있다. 양산 전에 모든 버그를 찾는 건 불가능하다. 그래서 리셋이 필요하다. 사용자는 모른다. 기기가 잠깐 껐다 켜진 걸. 3초면 부팅된다. 우리만 안다. 그게 watchdog 리셋이란 걸. 로그에 남는다. 필드 테스트 데이터를 본다. 한 달에 watchdog 리셋 5번. 괜찮다. 10번 넘어가면 문제다. 코드를 다시 봐야 한다. 중국 칩의 비밀 중국 칩을 쓸 때가 있다. 싸니까. 그런데 스펙이 불친절하다. 영어 번역이 이상하다. "Reset function is to be enabled when power." 무슨 뜻인지 모른다. 구글 번역에 중국어를 넣는다. 더 모른다. 포럼을 뒤진다. 중국 포럼. 번역기 돌린다. 누군가 말한다. "그냥 껐다 켜세요." 중국어로. 해본다. 된다. 왜 되는지 모른다. 그냥 된다. 이게 중국 칩이다. 스펙보다 경험이 중요하다. 작년에 쓴 칩. 레지스터 하나가 리셋 안 됐다. 스펙엔 리셋된다고 나온다. 버그 아니냐고 물었다. 팹리스에. 답장 왔다. "이건 Feature입니다. 의도된 겁니다." 아니다. 버그다. 그냥 인정 안 하는 거다. 코드로 해결했다. 리셋 후 수동으로 레지스터 클리어. 이게 우리 일이다. HW팀과의 전쟁 "펌웨어 문제 아니에요?" HW팀 이과장님. "회로 확인해보셨어요?" 나. "회로는 문제없어요. 시뮬레이션 돌렸어요." "실제 파형 보셨어요?" "그건... 펌웨어가 잘못 설정한 거 아닐까요?" 한숨 나온다. 보드를 가져온다. 오실로스코프 연결한다. 프로브를 댄다. SCL, SDA 라인. I2C 통신. 파형을 본다. 이상하다. 클럭이 흔들린다. "이거 풀업 저항 얼마예요?" "4.7k요." "속도는요?" "400kHz요." 계산한다. 머릿속으로. 안 맞는다. "속도 낮춰보겠습니다." 코드를 고친다. 100kHz로. 테스트한다. 된다. "펌웨어 문제 맞네요." 이과장님이 말한다. 아니다. 하드웨어 문제다. 풀업 저항이 약하다. 그런데 말 안 한다. 이미 PCB 제작했다. 고치려면 재작업이다. 펌웨어로 우회한다. 속도 낮추면 된다. 성능은 좀 떨어진다. 괜찮다. 이게 임베디드다. 완벽한 하드웨어는 없다. 펌웨어로 커버한다. 리셋할 수 없는 것들 가끔 생각한다. 인생도 리셋할 수 있으면. 5년 전으로. 대학 졸업할 때로. 웹 개발 갈까, 임베디드 갈까 고민하던 때. 임베디드를 선택했다. 하드웨어가 재밌어 보였다. 지금은? 후회는 안 한다. 그런데 가끔 힘들다. 야근이 많다. 장비가 회사에 있어서 재택이 안 된다. 연봉은 나쁘지 않다. 5800만원. 야근 수당 더하면 6천 넘는다. 그런데 시급으로 계산하면? 별로다. 웹 개발자 친구. 연봉 7천. 재택 주 3일. 칼퇴. 부럽다. 솔직히. 그런데 걔는 모른다. 내가 아는 걸. 하드웨어가 돌아갈 때의 쾌감. LED가 처음 켜질 때. 센서 데이터가 들어올 때. 그건 웹에서 못 느낀다. 리셋 버튼을 누른다. 기기가 꺼진다. 다시 켜진다. 부팅 로그가 올라온다. UART로. "System initialized." 이 문장을 볼 때. 3일 밤샌 끝에. 그때는 행복하다. 내일도 리셋 퇴근 전이다. 9시. 오늘 작업한 코드. 커밋한다. 메시지를 쓴다. "Add watchdog reset handling." 푸시한다. 내일 출근하면 볼 것들. 테스트 결과. 실패했을 것이다. 또. 메일. "펌웨어 확인 부탁드립니다." 또. 회의. 결론 없을 것이다. 또. 그리고 나는 물을 것이다. "전원 껐다 켜보셨나요?" 이게 우리 일이다.내일도 전원을 끌 것이다. 그리고 켤 것이다. 50번쯤.
- 03 Dec, 2025
금요일 오후 3시, 라이브 제품에서 버그 리포트가 들어왔다
금요일 오후 3시, 라이브 제품에서 버그 리포트가 들어왔다 금요일 오후 3시 17분 슬랙에 빨간 점이 떴다. CS팀 채널. "펌웨어팀 @김펌웨어 님, 고객사에서 제품 이상 동작 리포트 들어왔습니다. 확인 부탁드립니다." 첨부된 영상을 봤다. 우리 제품이 멈춰있다. 화면에 아무것도 안 뜬다. LED만 깜빡인다. 양산 나간 지 3개월 된 제품이다. 지금까지 문제없었다. 갑자기 왜. "재현되나요?" 물었다. "고객사에서는 자주 발생한다고 합니다. 저희 테스트에서는 재현 안 됩니다." 제일 싫은 유형이다.일단 로그부터 고객사에 요청했다. "디버그 로그 남아있나요?" 30분 후 답장. "로그 기능 꺼져있었습니다." 당연하지. 양산 버전은 로그 비활성화했다. 플래시 용량 아끼려고. "언제부터 발생했나요?" "정확히는 모르겠습니다. 이번 주에 여러 번 발생했다고 합니다." 증상만 봐서는 모른다. 리셋인지, 하드폴트인지, 워치독인지. 우리 테스트 환경에서는 3시간째 돌려도 멀쩡하다. 고객 환경이 뭐가 다른 거지. 팀장한테 보고했다. "양산 제품 이슈 들어왔습니다. 재현은 안 됩니다." "심각한가?" "고객사에서 자주 발생한다고 합니다." "주말에 대응 가능한가?" 알았다는 뜻이다. 환경 차이 고객사 환경을 조사했다. CS팀이 정리해준 내용.24시간 연속 동작 온도: 실내, 에어컨 있음 전원: 5V 어댑터 네트워크: WiFi, 공유기는 TP-Link 펌웨어 버전: v1.2.3 (최신)우리 테스트 환경이랑 똑같다. 뭐가 다르지. HW팀 민수한테 물었다. "하드웨어 이슈 가능성?" "양산 전에 다 검증했는데요. EMC 테스트도 통과했고." "혹시 로트 문제?" "같은 로트 제품 다른 데서는 문제없어요." 소프트웨어 문제다. 그것도 특정 조건에서만.금요일 오후 5시 팀원들 다 퇴근했다. 팀장만 남아있다. "일단 집에 가. 주말에 보자." 나도 가고 싶다. 근데 못 간다. 고객사는 월요일까지 답변 원한다. 일요일 밤까지 원인 찾아야 한다. 회사 냉장고에서 레드불 두 개 꺼냈다. 오늘 밤샐 것 같다. 일단 코드 리뷰부터 시작했다. v1.2.3 릴리즈 이후 변경점. 아무것도 없다. v1.2.3이 최신이고, 그 이후 수정사항 없다. 그럼 v1.2.3 자체에 버그가 있다는 건데. 3개월 동안 왜 안 나왔지. 테스트 시나리오를 다시 봤다. 우리가 놓친 케이스가 있다. 24시간 고객사는 24시간 연속 동작이라고 했다. 우리 테스트는 최대 8시간. 보통 8시간이면 충분하다. 메모리 릭도 잡히고, 타이밍 이슈도 나온다. 근데 24시간 이상 돌려야 나오는 버그도 있다. 타이머 오버플로우 같은 거. 코드에서 타이머 쓰는 부분을 찾았다. 여러 개다. WiFi 재연결 타이머, 센서 폴링 타이머, 워치독 타이머, RTC... 한 개씩 체크했다. 변수 타입, 오버플로우 조건, 래핑 처리. 있다. WiFi 재연결 로직에서 uint32_t 타이머를 밀리초로 쓴다. 49일마다 오버플로우. 근데 비교 로직이 단순 대소비교다. 오버플로우 케이스를 안 본다. if (current_time > reconnect_time + timeout) { wifi_reconnect(); }49일 넘어가면 current_time이 0으로 돌아간다. reconnect_time은 큰 값. 이 조건이 영원히 참이 된다. WiFi 재연결을 무한 반복한다. 찾았다.금요일 밤 11시 재현 시나리오를 짰다. 타이머를 강제로 49일 근처 값으로 세팅. 보드에 올렸다. 돌렸다. 10분 후 멈췄다. 똑같은 증상. LED만 깜빡이고, 응답 없다. 로그 출력도 멈췄다. 원인 확정이다. 수정은 간단하다. 타이머 비교 로직을 래핑 세이프하게. if ((int32_t)(current_time - reconnect_time) > timeout) { wifi_reconnect(); }부호 있는 정수로 캐스팅하면 오버플로우 케이스도 올바르게 동작한다. 수정했다. 다시 테스트. 이번엔 안 멈춘다. 1시간 돌렸다. 문제없다. 패치 버전을 만들었다. v1.2.4. 빌드하고, 테스트 보드에 올리고, 검증했다. 새벽 2시다. 문서 작업 버그 리포트를 썼다. 원인: WiFi 재연결 타이머 오버플로우 처리 누락발생 조건: 연속 동작 49일 경과 시영향: 제품 응답 없음, LED만 동작수정: 타이머 비교 로직 오버플로우 세이프하게 수정패치 버전: v1.2.4 CS팀에 전달할 자료도 만들었다. "고객사에 전달 부탁드립니다. 월요일 오전까지 v1.2.4 펌웨어로 업데이트하면 해결됩니다." 양산 제품 패치 계획도 세웠다. 이미 나간 제품들은 OTA로 업데이트. 다행히 우리 제품은 OTA 지원한다. 안 했으면 리콜이다. 새벽 3시. 집에 갔다. 토요일 오전 11시에 일어났다. 슬랙 확인. 팀장: "고생했다. 월요일은 오후 출근해라." 고맙다. CS팀: "고객사에 전달했습니다. 월요일 업데이트 예정입니다." 됐다. 침대에 누웠다. 천장을 봤다. 49일. 1180시간. 누가 그렇게 오래 테스트하나. 우리 테스트 시나리오는 8시간이 최대다. 비용 때문에. 시간 때문에. 양산 전에 못 잡은 버그다. 고객이 먼저 발견했다. Low 레벨 개발은 이런 거다. 한 줄 실수가 49일 뒤에 터진다. 웹이면 고쳐서 배포하면 된다. 5분이면 된다. 펌웨어는 OTA 있어도 조마조마하다. 업데이트 중에 전원 나가면 브릭이다. 그래도 찾아서 다행이다. 교훈 같은 건 없다 타이머 오버플로우는 기본 중의 기본이다. 알고 있었다. 그런데 놓쳤다. 코드 리뷰 때 못 봤다. 테스트로도 못 잡았다. 완벽한 코드는 없다. 완벽한 테스트도 없다. 고객이 발견하기 전에 찾으면 좋은 거고, 못 찾으면 이렇게 된다. 금요일 오후 3시에 리포트 들어오고, 주말 날리고, 새벽에 고치고. 이게 펌웨어 개발이다. 월요일에 팀 회의 때 공유할 것이다. "타이머 비교 로직 체크리스트에 추가하자." 다들 "아 그거" 할 거다. 알면서 놓친 거니까. 그래도 체크리스트에 넣는다. 다음에 또 놓칠 테니까. Low 레벨은 이렇다. 같은 실수를 반복하지 않으려고 체크리스트 만들고, 그래도 놓친다. 그럼 또 추가한다. 체크리스트가 100개 넘어간다. 읽는 사람은 없다. 너무 길어서. 일요일 푹 잤다. 아무것도 안 했다. 월요일 오후 출근 예정이다. 고객사 업데이트 결과 확인해야 한다. 문제없이 되면 좋겠다. OTA 실패하면 또 야근이다. 침대에 누워서 생각했다. 웹 개발자들은 지금 뭐 하고 있을까. 카페에서 맥북 켜고 코딩하고 있을까. 배포 버튼 누르면 끝일까. 부럽다. 나는 보드 없으면 일 못 한다. 회사 와야 한다. 오실로스코프 봐야 한다. 디버거 연결하고, 플래시 지우고, 펌웨어 올리고, 리셋하고, 로그 보고. 49일 버그는 집에서 못 잡는다. 장비가 없어서. 그래도 찾았다. 그걸로 됐다.금요일 오후의 빨간 알림은 늘 시작이다. 주말이 끝나고 월요일이 온다.
- 02 Dec, 2025
웹개발자는 롤백하고, 나는?
웹개발자는 롤백하고, 나는? 지난주 팀 술자리에서 웹개발 하는 대학 후배가 했던 말이 자꾸만 떠올라. "어제 배포 망쳤는데 1분 만에 롤백했어. 문제 없음." 그러곤 웃었다. 맥주 한 잔 마시더니 말이다. 나는 그 말을 듣고 어떤 표정을 했는지 모르겠다. 웃었을 수도 있고, 그냥 마셨을 수도 있고. 기억이 안 난다. 그냥 '아, 그렇구나' 했던 것 같다. 펌웨어는 그렇지 않다. 한 번 나가면 끝 양산이 나가기 전, 문제가 발견되는 게 가장 좋다. 그럼 다시 테스트하고, 버그 고치고, 다시 나간다. 시간은 걸리지만 그래도 시간이면 된다. 양산이 나간 후, 문제가 발견되는 건 다르다. 시리즈 50만 개가 판매되고 있다고 치자. 그 중 200개 정도에서 특정 상황에서만 먹통이 되는 버그가 있다고 하자. 그럼 어떻게 되나? 회사는 리콜을 결정한다. 50만 개를 다 회수해야 할까? 아니다. 200개만이라도 빼서 수리하는 데 생기는 비용. 택배비. 검수 비용. 인건비. 시간. 신뢰도 하락. 그 숫자가 억 단위가 된다. 수십억일 수도 있다. 그게 내 잘못일 수도 있다. 출근하면 테스트 결과부터 어제 밤 10시 50분에 보드에 올린 펌웨어의 테스트 결과가 10시간 돌려졌다. 실패다. 또다시 실패다. "UART로 로그 떴어?" 아니. 부팅도 안 돼. 하드웨어 이슈가 아닐까. HW팀에 연락했다. "그쪽 회로 정상 맞나요?" 이건 내가 이미 3번째 하는 질문이다. "펌웨어가 Initialize 못 하는 거 아니야?" 둘 다 맞을 수도 있다. 하드웨어 설계 스펙이 펌웨어 예상과 다를 수 있다. Reference Manual은 2년 전 버전이고, 실제 칩은 새로 나온 패키지다. 스펙이 바뀌었을 수도 있다. 누군가는 문서를 수정해야 했는데, 그 누군가가 일을 안 했을 수도 있다. 한국 회사지만, 칩은 중국 제조사다. 중국 매뉴얼을 영어로 번역한 거다. 한국어 번역은 없다. 그 스펙 매뉴얼이 1000페이지다. 찾는 게 뭔지를 정확히 모르면서 1000페이지를 찾는다. 마치 영어 사전에서 스펠링 모르는 단어를 찾는 것처럼. 웹개발자의 세계 회의실에서 웹팀 리드가 웃으면서 말했다. "저희는 버그 발견되면 바로 핫픽스하고 배포하면 돼요. 2시간이면 사용자가 문제 없다고 느껴요." 부러운 게 아니다. 그게 상식이기 때문에 더 부럽다. 생각해보니, 웹은 내가 배포한 버전이 뭐가 됐든 상관없다. 사용자는 항상 최신 버전을 본다. 내가 만든 거 못 봐. 버그도 못 봐. 내가 고쳤으면 끝이다. 앱도 마찬가지다. 물론 앱 스토어 심사 시간이 있지만, 긴급 업데이트는 몇 시간 안에 나간다. 사용자가 다운받으면 끝. 과거 버전은 사라진다. 우리는 다르다. 양산이 나가는 그날 양산이 나가는 날은 회의가 많다. "완전히 테스트했나?" "네, 온도 테스트, 습도 테스트, ESD 테스트, 수명 테스트 다 했습니다." "예상 이슈는?" "진동 테스트에서 한 번 부팅이 느렸는데, 재현이 안 돼서 일단 기록만 해뒀습니다." 그 말을 하는 순간부터 신경 쓴다. "한 번 부팅이 느렸다"는 게 뭔가. 왜 그랬나. 다시 재현되면? 그때는 뭐 할 건가. ECN 절차가 있다. 양산 후 발견된 버그를 고쳐서 나머지 로트에 반영하는 절차. 시간도 걸리고 비용도 든다. 1-2개 로트면 괜찮은데, 5-6개 로트까지 나갔을 땐 점점 비용이 커진다. 가장 최악의 경우는 리콜이다. 밤새는 일상 지난 화요일, 새벽 1시에 보드를 보고 있었다. 오실로스코프에서 SPI 신호가 이상했다. 클록이 들쑥날쑥했다. 펌웨어 버그일까, 하드웨어일까. HW팀 설계자에게 슬랙 메시지를 보냈다. "아직 안 주무셨나요?" 새벽 2시였다. 설계자도 깨어있었다. "어? 너도 봤어?" 그럼 다 같이 본다는 뜻이다. 보드를 들었다 놨다를 반복했다. 회로도를 봤다. 매뉴얼을 봤다. 코드를 봤다. 결론은 저항값 하나였다. Pull-up 저항이 약한 거였다. 나는 펌웨어가 잘못된 줄 알고 2시간을 낭비했다. "이거 저 고치고 다시 보내면 될 거 같은데?" HW팀이 말했다. "알겠습니다." 나는 왜 펌웨어만 의심했을까. 아마도 하드웨어는 기판이 나가면 고칠 수 없다는 생각 때문이었을 것 같다. 펌웨어는 다시 올릴 수 있으니까. 하지만 기판도 다시 나갈 수 있다. 다만 비용이 비싸고, 시간이 오래 걸릴 뿐이다. 돌이킬 수 없는 결정 웹개발자들이 부러운 이유는 뭘까. 아마도 피드백 루프가 빠르다는 것 때문일 것 같다. 내가 코드를 짰다. 1시간 뒤에 사용자가 버그를 발견했다. 1시간 뒤에 나는 버그를 알았다. 1시간 뒤에 내가 고쳤다. 1시간 뒤에 사용자가 고쳐진 버전을 봤다. 4시간 사이클. 그 안에 내가 배웠고, 고쳤고, 다시 배웠다. 우리는 다르다. 내가 코드를 짰다. 2주일을 테스트했다. 배포했다. 3개월 뒤에 버그가 발견됐다. 그 버그는 극단적인 상황에서만 나타난다. 사용자는 그 상황을 만났고, 우리는 그 상황을 못 만들었다. 이제 어떻게 해야 할까. 하드웨어를 회수하고, 펌웨어를 업데이트하고, 다시 배포한다. 3개월이 지난 버전을 수정해야 한다. 그 사이에 나는 다른 프로젝트를 했을 것이다. 코드를 까먹었을 것이다. 그 로직이 왜 그렇게 짜여있었는지 모를 것이다. 그래도 고쳐야 한다. 롤백은 불가능한 말 "혹시 이전 버전으로 롤백할 수 있나?" 그 질문은 펌웨어에서 가능할 수도, 불가능할 수도 있다. OTA(Over The Air) 업데이트를 지원하는 기기라면 가능할 수도 있다. 이전 버전의 바이너리를 기기에 내장시켜놓으면 된다. 하지만 그러려면 플래시 용량이 필요하다. 용량이 크면 가격이 올라간다. 가격이 올라가면 경쟁력이 떨어진다. OTA를 안 지원하는 기기라면 불가능하다. 그 기기를 들고 회사에 와서, 기사가 프로그래머에 연결해서, 플래시를 지우고, 다시 굽는다. 손으로. 유선으로. 사용자가 5000명이라면? 5000번을 해야 한다. 아니면 회수해서 한다. 회수 비용이 더 싸다. 5천만 원 정도. "저희는 롤백 정책이 없습니다. 대신 처음부터 완벽하게 만듭니다." 내 팀장이 지난 입사 면접에서 한 말이다. 완벽하다는 게 뭘까. 테스트? 배포 전에 할 수 있는 모든 테스트를 했다고 해도, 실제 환경에서는 뭐가 나타날지 모른다. 온도가 55도를 넘어가는 환경에서만 문제가 생기는 버그가 있다고 하자. 우리 테스트 챔버는 50도까지만 간다. 누가 이 문제를 찾을까. 사용자다. 밤 10시의 선택 어제도 밤 10시까지 있었다. 내일도 있을 것 같다. 마감이 일주일밖에 안 남았고, 발견된 버그가 3개다. 그 중 하나는 HW 이슈고, 하나는 내가 고쳐야 할 이슈고, 하나는 뭔지 모르는 이슈다. 회의실에서 팀장이 말했다. "정해진 마감까지 모든 버그를 고칠 수 없으면, 가장 중요한 버그만 고쳐서 나가는 것도 옵션입니다." 그 말은 뭔가. 남은 버그는 그냥 양산에 넣는다는 뜻이다. 나중에 ECN으로 고친다는 뜻이다. 아무도 반대하지 않았다. 모두가 알고 있다. 일정이 진짜 타이트하다는 걸. 그리고 완벽함은 환상이라는 걸. 그 순간, 웹개발자 후배가 떠올랐다. "어제 배포 망쳤는데 1분 만에 롤백했어." 1분. 1분이면 내 팀은 한 줄의 코드 리뷰도 못 한다. 모두가 아는 그 불안감 펌웨어 팀의 공통된 불안감이 있다. 그건 배포 바로 다음 날이다. 첫 번째 보고가 오기를 기다린다. 첫 번째 불량이 나오는 건 피할 수 없다. 전체 50만 개 중에 1개가 이상할 확률이 0이 될 수 없다. 하지만 그 1개가 우리 펌웨어 때문인지, 하드웨어 때문인지, 조립 공정 때문인지는 다르다. 만약 우리 펌웨어 때문이라면? 그 말은 50만 개가 모두 같은 문제를 가지고 있을 수도 있다는 뜻이다. 첫 번째 보고가 나오지 않으면, 다음 날을 기다린다. 1주일을 기다린다. 1개월을 기다린다. 기다리는 동안 내 머리는 자동으로 최악의 시나리오를 그린다. "혹시 특정 환경에서만 터지는 버그 있나?" "혹시 메모리 리크 있나?" "혹시 초기화 루틴을 빠뜨린 게 있나?" 밤에는 더 심하다. 근데 왜 계속하는가 질문 자체가 이상하다. 왜냐하면 이미 답을 알고 있으니까. 첫째, 다른 회사 갈 돈이 없다. 전직 시 정착금이 별로 없고, 새 회사에서 연봉 올려줄 가능성도 낮다. 경력 5년이면 중간이다. 주니어도 아니고 시니어도 아니다. 둘째, 이 일에 익숙해졌다. 다른 일을 하려면 또 배워야 한다. SI를 간 후배들 보니 나은 게 별로 없는 것 같았다. 그냥 다른 고통일 뿐이다. 셋째, 그리고 이게 가장 솔직한 이유인데, 나는 이 일이 싫지 않다. 디버깅할 때의 그 쾌감이 있다. 오실로스코프에서 파형을 보고, "아, 이거 타이밍 문제다"라고 깨닫는 그 순간. 아무도 안 돌던 코드가 돌기 시작했을 때의 그 희열. 웹개발자들이 이걸 경험하나? 모르겠다. 아마 비슷한 뭔가가 있을 것 같긴 한데. 다만 내가 만드는 건 서버에서 사라지지 않는다. 하드웨어에 박혀있다. 10년 뒤에도 누군가 쓸 것이다. 내 코드가. 내 버그도. 그게 또 다른 종류의 두려움이면서, 또 다른 종류의 자부심이다. 그래도 롤백은 아니다 웹개발자들을 부러워하는 건 여전하다. 하지만 달라진 게 하나 있다. 이제는 그들의 롤백 가능성이 부럽기보다, 내 돌이킬 수 없음이 당연해 보인다. 펌웨어는 그런 거다. 한 번 나가면 끝이다. 그 대신 뭔가를 얻는다. 책임감. 긴장감. 그리고 가끔씩 오는 성취감. 회사 물건이 고장 안 나고 계속 잘 돌아가는 그걸 보며 느끼는, 말로 설명하기 어려운 안정감. 내일도 9시에 출근한다. 어제 돌린 테스트가 실패했을 것 같다. 또 디버깅을 해야 할 것 같다. 또 밤 10시까지 있을 것 같다. 또 밤에 몰래 코딩을 할 것 같다. 그리고 이 모든 게 반복될 것 같다. 웹개발자는 롤백한다. 나는 그냥 계속 나간다.한 번 나가면 끝. 웹은 롤백. 펌웨어는 앞으로만 간다.
- 02 Dec, 2025
하드웨어 문제? 소프트웨어 문제? 중간에 몇 달이 간다
하드웨어 문제? 소프트웨어 문제? 중간에 몇 달이 간다 누가 진범인가 월요일 아침. 회의실 문이 닫힌다. "펌웨어팀, 이 제품 버그 좀 봐주시겠어요?" 하드웨어팀 팀장이 말한다. 목소리는 차분하지만 눈빛은 이미 비난하고 있다. 나는 버그 리포트를 받아든다. A4 한 장. 한글과 한문 섞인 번역체로 쓰여 있다. "디바이스 켜졌다가 5분 뒤 꺼져요." 이게 전부다. 내 머리가 이미 회전하기 시작한다. 소프트웨어 워치독이 있나? 배터리 문제는? 전원 관리 로직은? 아니면 실제로 하드웨어가 재부팅되는 건가? 펌웨어 크래시로 보이지만 사실 회로에서 전류가 튀는 건 아닌가? "일단 확인해보겠습니다." 내 대답. 항상 이 말부터다.오실로스코프 앞에서의 3시간 보드를 받아온다. 오실로스코프 프로브를 잡는다. 손이 익숙하다. 너무 익숙해서 가끔 전봇대 사진 봐도 신경이 쓰인다. VCC를 먼저 본다. 깨끗하다. 정말 깨끗하다. 5V가 흔들리지 않는다. 아, 잠깐. 순간 0.2V 드롭이 보인다. 아니다. 프로브 접지가 문제인가? 한 시간이 지난다. 파형을 다시 본다. 신호는 여전히 깨끗하다. 그럼 왜 꺼져? 혼잣말이 나온다. "클럭은? 클럭이 멈춰?" 오실로스코프 채널을 바꾼다. 48MHz 클럭이 보인다. 주기는 20.8ns. 정확하다. 그럼 뭐가 문제지? 한 시간 더. 커피를 마신다. 찬 커피다. 아침에 받은 거라서. "혹시 리셋 신호?" 리셋 핀을 본다. 3.3V에 안정적이다. 눌리지 않는다. 결론: 파형상으로는 아무 문제 없다. 그럼 소프트웨어다. 아니다. 기다려. 정말 그럴까?그 사이에서 뭔가가 발생한다 코드를 본다. 초기화 루틴부터. 1000줄이 있다. PLL 설정. 타이머 설정. UART 설정. I2C 설정. SPI 설정. GPIO 설정. "이게 순서가 맞나?" 레퍼런스 매뉴얼을 찾는다. STM32H745. 영어 1500페이지. 목차를 본다. 시스템 아키텍처. 전원 관리. 클럭 트리. RCC 레지스터. 2시간이 지난다. 혹시 스택 오버플로우? 혹시 메모리 부족? 혹시 인터럽트 우선순위 충돌? 전부 아니다. 슬랙으로 하드웨어팀에 메시지를 보낸다. "혹시 회로 다시 한 번 확인 가능할까요? 디커플링 캡이나..." 5분이 지난다. 하드웨어팀 팀장이 회의실 문을 열고 들어온다. "펌웨어 문제 아니면요? 우리 회로는 검증 다 했거든요." "저도 파형상으로는 문제가 없는데요. 혹시..." "펌웨어팀에서 먼저 확인해줄 수 있을까요?" 문이 닫힌다. 또 1시간. 또 다른 1시간. 또 다른 1시간. 며칠이 지난다. 상태 리포트를 쓴다. "원인 파악 중. 하드웨어와 소프트웨어의 경계 영역 검토 필요." 이 문장은 내가 아무것도 모른다는 뜻이다. 혹은 그 반대 "좌표를 확인해봤나요?" 하드웨어팀 신입 사원이 묻는다. 좌표? "이 IC, 데이터시트 봤어요? 핀 배치가..." 데이터시트를 본다. 맞다. 맞는데. 잠깐. 핀 번호가. "어? 43번이 GND인데 여기는 NC로 되어 있네요." "아 그건 한국 대리점에서 수정한 버전 문서예요." "...뭐?" 그 IC에서 나가는 신호가 5번인 줄 알았는데 사실은 그게 아니었다. 다른 핀이었다. 그 핀은 시뮬래이션상으로는 출력이 없는 핀이었다. 그래서 소프트웨어에서는 그 신호를 버려버렸다. 마이크로초 단위의 타이밍 오류가 발생했다. 그게 쌓여서 5분에 버퍼 오버플로우가 된 거다. 그런데 버퍼 오버플로우가 스택을 침범했다. 스택에는 뭐가 있었나. 핸들러 포인터가 있었다. 그게 깨졌다. 다음 인터럽트가 들어왔을 때 CPU가 엉뚱한 주소로 점프했다. 워치독이 감지했다. 리셋됐다. 결론: 하드웨어다. 아니다. 결론: 펌웨어 버퍼 사이즈다. 아니다. 결론: 스택 배치다. 아니다. 결론: 데이터시트가 잘못됐다.진짜 보상은 결론이 나왔다. 메일을 쓴다. "원인: 데이터시트 버전 불일치로 인한 IC 핀맵 오류. 영향: 신호 누락으로 인한 버퍼 오버플로우. 해결책: 데이터시트 최신 버전 확인 (영문) 펌웨어 버퍼 사이즈 1.5배 증량 스택 가드 추가"보낸다. 팀장이 본다. "좋아. 수정하지." 며칠이 지난다. 새 펌웨어가 나온다. 테스트한다. 24시간. 문제 없다. 48시간. 문제 없다. 72시간. 문제 없다. 처음으로 웃음이 나온다. 이게 펌웨어 개발의 쾌감이다. 파형을 봤을 때는 답이 없었다. 코드를 봤을 때도 답이 없었다. 그런데 세상에는 많은 문제가 경계에 있다. 하드웨어와 소프트웨어의 경계. 설계와 구현의 경계. 명세와 현실의 경계. 그 경계를 헤매다가 찾는 답. 그 답은 오실로스코프로도 보이지 않고 디버거로도 잘 안 보이고 머리로만 떠올라야 한다. 웹 개발자들은 이런 게 없겠지. 배포 잘못했으면 롤백하면 되니까. 우리는 한 달에 50만 개를 양산한다. 한 개가 재발할 확률이 0.0001%라도 50개가 나간다. 그 50개를 회수하려면 기차 2대가 필요하다. 그래서 우리는 며칠이고 기다린다. 오실로스코프 앞에서. 데이터시트 앞에서. 그리고 마침내 찾았을 때. 아, 그 느낌. 그게 이 일을 계속하는 이유다. 돈이 아니라. 밤새는 게 가치 있다고 느끼는 그 순간.내일 또 다른 버그 리포트가 올 거다. "어떤 제품 꺼져요." 그럼 또 시작한다.
- 02 Dec, 2025
1000페이지 영어 스펙 문서, 번역은 구글 번역기
1000페이지 영어 스펙 문서, 번역은 구글 번역기 아침 9시, 스펙 문서와의 싸움이 시작된다 출근한다. 피곤하다. 어제 테스트는 또 실패했다. 화면을 켠다. 메일함에 스펙 문서 링크가 떠 있다. Reference Manual. 1047페이지. PDF 파일명에는 'v2.3_EN'이라고 적혀 있다. 한국어 버전? 없다. 당연하지, 있을 리가. 팀장이 어제 말했다. "이 칩셋 쓰려고 하니까 이거 한 번 읽어봐. 핵심만." 핵심만. 1047페이지 중 핵심이 뭔데. 일단 열어본다. Chapter 1. Overview. Chapter 2. Pin Configuration. Chapter 3. Electrical Characteristics. 여기까진 괜찮다. 그림도 있고 테이블도 있고. 그런데 Chapter 5. Register Description부터 지옥이 시작된다. "Register 0x24: CTRL_REG_A (Control Register A)" 좋다. "Bit 7-5: Reserved. The bit must be set to 0." 확인했다. "Bit 4: FS1. Full-scale selection. Setting this bit will enable the full-scale mode operation as specified in the electrical characteristics section." 음. 여기부터 애매한데. 번역기를 켠다. "비트 4: FS1. 풀 스케일 선택. 이 비트를 설정하면 전기 특성 섹션에 지정된 대로 풀 스케일 모드 작동을 활성화합니다." 뭔 소린데. "Full-scale mode operation as specified in the electrical characteristics section." 여기서 'as specified'가 뭔지 알려면 electrical characteristics를 다시 봐야 한다. 그걸 번역기로 돌리면 또 다른 용어가 나온다. 그 용어를 다시 찾아봐야 한다. 악순환. 10시, 일단 구글 번역기를 믿어본다 신입 때는 달랐다. 신입 때는 영어를 열심히 공부했다. 회사에서 영어 교육도 해줬고. 비즈니스 영어. Technical English. 근데 배운 게 다였다. '설정하다' '활성화하다' 이런 건 번역기도 한다. 문제는 칩 제조사 엔지니어들의 영어다. 그들은 데이터시트를 쓸 때 최대한 간결하게 쓴다. 한 문장에 정보를 때려 넣는다. 중괄호도 많고, 약자도 많고, 암묵적인 가정도 많다. "The register must be read back after programming to ensure proper synchronization." 번역기: "프로그래밍 후 레지스터를 읽어 적절한 동기화를 보장해야 합니다." 그런데 이게 뭔 뜻인가. '레지스터를 읽어'는 뭐다. SPI로 읽어야 한다는 건가. 아니면 메모리 주소로 읽어야 한다는 건가. '동기화'는 뭔가. 칩 내부의 state machine과 host processor 사이의 동기화인가. 아니면 내부 clock과의 동기화인가. 영어 원문을 다시 읽어도 애매하다. 그럼 이제 뭘 한다. Slack에 올린다. "이거 뭔 뜻일까요?" 팀원 이준호가 답한다. 20분 뒤에. "아 이건 한 번 쓰고 나서 SPI로 읽어서 값이 제대로 들어갔는지 확인하라는 뜻 같은데요. 옆 회사 때 본 데이터시트도 이 칩이었는데 그렇게 했어요." '옆 회사 때 본 데이터시트'. 이게 임베디드 개발자의 성장이다. 데이터시트를 읽을 때 영어 능력이 아니라 경험치를 쓴다. 예전에 본 칩 중에 비슷한 게 있었나. 그렇다면 그건 뭐였나. 그리고 그건 왜 그렇게 동작했나. 구글 번역기의 한계다. 11시 30분, 번역기를 버린다 일단 정해진 시간이 있다. 점심 12시. 그 전에 끝낼 게 있는 데 아직 Register Description 20%밖에 안 봤다. 빠르게 스캔한다. Bit 3, Bit 2, Bit 1, Bit 0까지 모두 같은 방식이다. 각 비트마다 "Setting this bit will..."이 반복된다. 이제 번역기를 켜지 않는다. 그냥 원문을 읽는다. 한국어가 없다면, 영어를 바로 이해하는 수밖에 없다. 이건 새로운 기술이 아니다. 적응이다. 3년 차 때부터 시작했다. Reference Manual을 펼칠 때 한국어 번역을 찾지 않는다. 원문에서 바로 구조를 찾는다. Bit [7:5]라고 쓰여 있으면 7번 비트부터 5번 비트까지라는 건 번역이 필요 없다. R/W라고 쓰여 있으면 Read/Write다. 한국어 대사전보다는 기술 용어 사전이 필요해진다. 예: "LSB First"는 "Least Significant Bit First"다. 번역하면 '최하위 비트 우선'. 근데 이건 번역하는 순간 더 복잡해진다. 그냥 LSB라고 부른다. 같은 팀의 송미영 개발자는 어제 말했다. "한국어 번역본을 기다리기보다 영어 원문에 익숙해지는 게 빠르더라고요. 처음엔 힘들지만." 그 말이 맞다.12시 30분, 한국 자료를 찾아본 지 6개월이 지났다 점심을 먹으며 유튜브를 본다. 'STM32 Reference Manual 한국어' 검색 결과는 없다. 대신 '우리 회사 선배가 쓴 코드 예제'라는 수동적 학습 방법에 의존한다. 하드드라이브에 있는 폴더: /Legacy_Project/2019_Smart_Lock/firmware/src 여기엔 내가 입사하기 전 개발된 코드다. // Written by Park_JH (2019-08-14) // Reference: STM32L152 Reference Manual // Page 287: RTC_CR Register Description uint32_t rtc_init(void) { // Enable PWR clock RCC->APB1ENR |= RCC_APB1ENR_PWREN; PWR->CR |= PWR_CR_DBP; // This is critical: must read back after write // See page 312 of RM uint32_t dummy = PWR->CR; (void)dummy; return 0; }주석이 있다. "Page 312 of RM" 이 사람은 이 코드를 쓸 때 Reference Manual 312페이지를 봤다는 뜻이다. 6년 전에. 그리고 지금 그 페이지가 어디 있는지 몰라도, 이 코드를 복사+붙여넣기 하면 작동한다. 한국어 자료가 없는 대신 '선배 개발자의 코드'가 한국어 자료다. 이게 임베디드 회사의 생태계다. 문서화는 없다. 대신 '이미 작동하는 코드'가 있다. 14시, 구글 번역기의 두 번째 쓸모 그렇다고 구글 번역기가 완전히 쓸모없는 건 아니다. 쓸모 있게 사용하는 방법이 있다. 1단계: 원문을 3번 읽는다 "The FIFO buffer can be configured to generate an interrupt when the data count exceeds the programmable threshold level specified in the FIFO_THR register." 1차 읽음. 뭔 소린지 모름. 다시 읽음. FIFO. 버퍼. 임계값. 뭔가 관련이 있는 것 같다. 다시 읽음. 아, FIFO가 어느 정도 찼을 때 인터럽트를 날린다는 뜻인가? 2단계: 그 다음에 번역기를 킨다 번역기: "FIFO 버퍼는 데이터 개수가 FIFO_THR 레지스터에 지정된 프로그래밍 가능한 임계값을 초과할 때 인터럽트를 생성하도록 구성할 수 있습니다." 내 이해: FIFO가 FIFO_THR 이상으로 차면 인터럽트 발생. 이 값은 프로그래밍으로 설정 가능. 3단계: 코드로 검증한다 // FIFO threshold = 16 bytes CHIP->FIFO_THR = 16;// Enable FIFO interrupt CHIP->INTR_ENABLE |= INTR_FIFO_FULL;작동했다. 내 이해가 맞았다. 이게 올바른 방법이다. 번역기에만 의존하면 틀린다. 원문을 이해해야 번역기를 제대로 쓸 수 있다. 아이러니다. 15시, 중국산 칩의 악몽 어제 하드웨어 팀에서 메시지가 왔다. "이번에 BOM cost 깎으려고 중국산 센서 쓰기로 결정했습니다. 사용 가능한지 펌웨어로 확인해주세요." 첨부 파일: XC1234_Datasheet_ZH_V1.2.pdf 확장자는 PDF인데 전부 중국어다. 구글 번역기를 켜본다. 중국어 → 영어: "50% 확률로 맞음. 50% 확률로 뭔 소린지 몰라." 중국어 → 한국어: "70% 확률로 틀림. 문법이 산산조각." 그럼 이제 뭘 한다. 사진을 찍어서 온라인 이미지 번역기에 올린다. "寄存器 0x12: 控制寄存器" 이미지 번역기: "Register 0x12: Control Register" 그리고 영어 번역기로 다시 돌린다. "Register 0x12: Control Register" 번역기: "레지스터 0x12: 제어 레지스터" 원점이다. 팀장에게 메일을 보낸다. "중국산 센서 사용이 가능하지만, 데이터시트가 중국어만 있어서 약 2주 정도 더 필요할 것 같습니다." 회신: "알겠습니다. 그래도 빨리 부탁합니다. BOM cost가 30% 줄어듭니다." 30%. 2주 vs 30% cost reduction. 회사가 뭘 선택할지는 뻔하다.16시, 그래도 살아가는 방법 Slack에서 임베디드 커뮤니티 링크를 찾는다. Reddit의 r/embedded EEVblog의 Electronics Design Forum STM32 공식 포럼 이 곳들엔 같은 고민을 하는 사람들이 있다. "Has anyone used the XC1234 sensor? The datasheet is only in Chinese..." 21분 뒤에 답이 온다. "Yeah, that's the old model. Use the XC1234A instead. English datasheet available on their official site." 구글 번역기보다 빠르다. 이제 이게 내 방법이다.영어 원문 읽기 구글 번역기로 검증 해석이 안 되면 온라인 커뮤니티에 물어보기 코드로 테스트하기 안 되면 오실로스코프로 파형 확인이 과정은 길다. 어떨 때는 하나의 레지스터 설정이 3시간이 걸린다. 그런데 이제 익숙하다. 펌웨어 개발자는 본래 이렇게 산다. 혼자 영어 문서와 싸우면서. 아무도 도와주지 않는다. 마케팅 팀은 한국어로 된 기획안을 준다. 하드웨어 팀은 한국어로 설명한다. 펌웨어 팀은 영어로 된 데이터시트를 받는다. 그리고 혼자 이해한다. 17시, 결론 대신 내일 계획 오늘도 Register Description 40%까지만 봤다. 내일 또 이어서 본다. 내일도 구글 번역기를 키고 닫는다. 계속해서 원문을 읽는다. 그리고 6개월 뒤쯤이면 이 칩의 모든 레지스터를 외우지 않아도 직관적으로 이해할 것이다. 이게 경험이다. 한국 대학교 임베디드 강의에선 안 배운다. 교재는 모두 한국어고, 한국 교수도 영어 문서를 피한다. 그래서 졸업생은 회사 와서 깜짝 놀란다. "아, 우리 이 칩 쓰는데 이 영어 문서 한 번 보고 시작해." 그 순간부터가 진짜 펌웨어 공부다. 불행인가. 그냥 일이다.내일도 출근해서 1000페이지를 펼칠 것이다. 구글 번역기는 여전히 50%만 맞출 것이다. 근데 괜찮다. 나머지 50%는 경험과 코드와 오실로스코프로 채운다.