후배 여러분, 상상해 보세요. 제품 출시를 한 달 앞두고 하드웨어 팀에서 찾아왔습니다. "핀이 부족해서 LED를 MCU 직접 제어가 아니라 I2C GPIO Expander 칩으로 옮겨야 할 것 같아요." 😂 만약 여러분의 main.c 곳곳에 HAL_GPIO_WritePin이 뿌려져 있다면, 그날은 야근 확정입니다. 하지만 오늘 배울 방식으로 설계했다면 단 5분 만에 끝낼 수 있습니다. 어떻게 그게 가능한지 알아봅시다.
1. 나쁜 예: 하드웨어에 종속된 main.c 🛑
보통 처음 펌웨어를 배우면 아래와 같이 코드를 짭니다. CubeIDE가 생성해준 HAL 함수를 비즈니스 로직에서 직접 호출하는 방식이죠. 당장은 잘 돌아가는 것처럼 보이지만, 이것은 하드웨어와 소프트웨어가 강력하게 결합(Tight Coupling)된 위험한 상태입니다.❌ 유지보수가 힘든 코드 (Before)
/* main.c */
while (1) {
if (sensor_error) {
// 하드웨어 제어 함수가 메인 로직에 노출됨
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}
HAL_Delay(500);
}
이 상태에서 LED가 I2C 방식으로 바뀌면 어떻게 될까요? 여러분은 프로젝트 전체 파일을 뒤져서 HAL_GPIO_WritePin을 찾아 I2C_Expander_Write 같은 새로운 함수로 일일이 바꿔야 합니다. 실수가 생길 확률이 100%죠.
2. 좋은 예: OCP를 적용한 추상화 설계 ✅
우리는 OCP(Open-Closed Principle, 개방-폐쇄 원칙)를 적용해야 합니다. "기능 확장에는 열려 있고, 기존 코드 수정에는 닫혀 있어야 한다"는 뜻이죠. 즉, 하드웨어가 바뀌어도 main.c는 수정하지 않도록 만드는 것입니다.
✅ 추상화된 드라이버 레이어 (After)
/* led.h */
void Led_On(void);
/* main.c */
while (1) {
if (sensor_error) {
Led_On(); // 💡 LED가 어떻게 켜지는지 메인은 몰라도 됨!
}
HAL_Delay(500);
}
하드웨어가 바뀌면
led.c 파일 내부만 수정하면 됩니다. main.c는 여전히 Led_On()을 호출하고 있으므로 전혀 손댈 필요가 없습니다.
3. 요구사항 변경 시뮬레이션: I2C Expander로 교체 🧮
실제로 MCU 핀 대신 외부 I2C 칩으로 LED 제어 방식이 바뀌었을 때, 드라이버 파일만 아래와 같이 업데이트하면 끝납니다.
| 기존 (MCU Pin) | 변경 (I2C Expander) |
|---|---|
HAL_GPIO_WritePin(GPIOA, PIN_5, 1); |
HAL_I2C_Master_Transmit(&hi2c1, ...); |
이렇게 구현부를 감추는 것을 정보 은닉(Information Hiding)이라고 부릅니다. 선배들이 귀에 못이 박히도록 강조하는 설계의 기본이죠.
04강 실습 핵심 요약
Led_On() 같은 추상화된 드라이버 함수로 감싸기.자주 묻는 질문 ❓
오늘 배운 내용은 펌웨어 개발자로서 '장인'으로 거듭나기 위한 가장 첫걸음입니다. 지금 바로 여러분의 프로젝트에서 HAL_이라는 단어가 main.c에 몇 개나 있는지 찾아보세요. 그리고 하나씩 지워나가는 즐거움을 느껴보시길 바랍니다! 😊

