현업에서 펌웨어나 자동차 제어기(ECU) 소프트웨어를 개발하다 보면 이런 경험, 한 번쯤 있으실 겁니다. "센서 하나 추가했을 뿐인데, 기존 코드를 건드렸더니 멀쩡하던 시스템이 다운되네?" 결국 끝없이 늘어나는 if-else문과 switch-case문 속에서 야근을 맞이하게 되죠.

특히 30대~50대 실무진이나 리더급 개발자라면 유지보수가 쉬운 아키텍처의 중요성을 누구보다 뼈저리게 느끼실 텐데요. 오늘은 여러분의 퇴근 시간을 앞당겨 줄 '임베디드 C언어 개방 폐쇄 원칙(OCP) 템플릿 소스코드'를 준비했습니다. 이 글을 꼭 저장(북마크)해 두시고, 다음 프로젝트나 코드 리뷰 시간에 바로 꺼내서 복사/붙여넣기로 활용해 보세요!

임베디드 C언어 개방 폐쇄 원칙 OCP 소스코드 템플릿 메인 썸네일

기존 코드를 건드리지 않는 마법, 임베디드 C언어와 개방 폐쇄 원칙(OCP)

개방 폐쇄 원칙(Open-Closed Principle, OCP)은 객체지향 설계의 핵심입니다. "확장에는 열려 있고(Open), 수정에는 닫혀 있어야(Closed) 한다"는 뜻이죠. 즉, 새로운 기능(예: 온도 센서 추가)을 넣을 때 기존 동작 코드는 단 한 줄도 수정하지 않아야 한다는 의미입니다.

C++이나 Java 같은 객체지향 언어에서는 인터페이스(Interface)를 통해 쉽게 구현하지만, C언어에서는 '함수 포인터(Function Pointer)'와 '구조체(Struct)'를 결합하여 완벽하게 OCP를 흉내 낼 수 있습니다.

C언어 디자인 패턴 중 개방 폐쇄 원칙을 설명하는 모듈화 개념도

실무에 바로 쓰는 C언어 OCP 소스코드 템플릿 3단계

지금부터 보여드리는 3단계 템플릿은 자동차나 임베디드 장비에서 다중 센서 데이터를 수집할 때 바로 적용할 수 있는 강력한 꿀팁입니다.

1단계: 공통 인터페이스(구조체) 정의하기

모든 센서가 공통으로 가져야 할 기능(초기화, 데이터 읽기)을 함수 포인터로 묶어 인터페이스 구조체를 만듭니다. 이 부분이 바로 확장을 가능하게 하는 뼈대입니다.


/* sensor_interface.h */
typedef struct {
    const char* sensorName;
    void (*init)(void);
    int (*readData)(void);
} Sensor_Interface_t;

2단계: 개별 모듈(센서) 구현하기

이제 새로운 센서가 추가될 때마다 기존 코드는 무시하고, 새로운 파일만 만들어서 위에서 만든 인터페이스 규격에 맞춰 구현해 주면 됩니다.


/* temp_sensor.c (온도 센서) */
#include "sensor_interface.h"
#include <stdio.h>

static void Temp_Init(void) {
    printf("온도 센서 초기화 완료\n");
}

static int Temp_Read(void) {
    return 25; /* 가상의 온도 데이터 */
}

/* 외부로 인터페이스 객체만 노출 */
const Sensor_Interface_t TempSensor = {
    .sensorName = "Temperature",
    .init = Temp_Init,
    .readData = Temp_Read
};

새로운 센서 모듈이 추가되는 C언어 OCP 패턴 실제 구현 모습

3단계: 관리자(Manager)에서 인터페이스 배열로 제어하기

메인 실행부에서는 구체적인 센서의 이름이나 함수(Temp_Read 등)를 전혀 알 필요가 없습니다. 단지 인터페이스 배열을 순회하며 함수 포인터를 호출할 뿐입니다.


/* main.c */
#include <stdio.h>
#include "sensor_interface.h"

/* 외부 센서 객체 참조 */
extern const Sensor_Interface_t TempSensor;
extern const Sensor_Interface_t PressureSensor; /* 새로운 센서가 추가되어도 */

/* 센서 목록 배열 (수정은 이 배열의 목록만 추가하면 끝!) */
const Sensor_Interface_t* sensorList[] = {
    &TempSensor,
    &PressureSensor
};

#define SENSOR_COUNT (sizeof(sensorList) / sizeof(sensorList[0]))

int main(void) {
    /* 1. 모든 센서 초기화 */
    for(int i = 0; i < SENSOR_COUNT; i++) {
        sensorList[i]->init();
    }

    /* 2. 모든 센서 데이터 읽기 */
    for(int i = 0; i < SENSOR_COUNT; i++) {
        int data = sensorList[i]->readData();
        printf("[%s] 데이터: %d\n", sensorList[i]->sensorName, data);
    }

    return 0;
}

결론: 유연한 코드가 칼퇴를 만듭니다

위 템플릿을 사용하면, 나중에 '습도 센서'가 추가되더라도 기존의 main.c 핵심 구동 로직이나 다른 센서 코드는 전혀 수정할 필요가 없습니다. 그저 humidity_sensor.c를 새로 만들고 배열에 한 줄만 추가하면 끝입니다.

  • 유지보수성 극대화: 코드 수정으로 인한 사이드 이펙트(Side Effect) 원천 차단
  • 가독성 향상: 끝없는 분기문(if-else) 제거
  • 협업 효율 증가: 각 개발자가 자신의 센서 모듈 파일만 독립적으로 개발 가능

임베디드 시스템 설계의 격을 높여주는 이 OCP 템플릿 코드, 지금 바로 브라우저 북마크에 저장해 두시고 다음 프로젝트에 꼭 써먹어 보시길 바랍니다! 💡

여러분의 현재 프로젝트에서는 어떤 방식으로 센서나 모듈 확장을 처리하고 계신가요?