본문 바로가기
자동차 소프트웨어

OTA(Over-The-Air) 업데이트 완벽 정리: 자동차 펌웨어 무선 업데이트 실무 가이드

by 버그없는토마토 2026. 1. 9.

OTA(Over-The-Air) 업데이트 완벽 정리: 자동차 펌웨어 무선 업데이트 실무 가이드

OTA란?


 

들어가며

 

2010년대 자동차 업데이트 방식:

고장 발생 → 정비소 방문 → 진단기 연결 → 수동 업데이트 → 떠남
          (시간 낭비)   (수동 작업)     (불편함)

2020년대 자동차 업데이트 방식:

고장 발생 → 인터넷 연결 → 자동 다운로드 → 무선 업데이트 → 끝
          (집에서)      (백그라운드)   (OTA 자동)

실무자라면 OTA에 대해 많이 들어봤을거예요
아니? 매일 듣고 매일 평가할 수도 있죠
그만큼 중요하고, 그만큼 친숙합니다


테슬라 오너라면 또 친숙하실거예요
주기적으로 소프트웨어가 새로 나오고 자동차에서 원격으로 OTA업데이트를 통해 차량 소프트웨어를 업데이트하죠
ISO 업데이트와 비슷하다고 생각하시면 편해요.
하지만? 차량 특성상 ISO와는 극명한 차이도 존재합니다
왜냐하면 차량은 생명이 달려있기 떄문이죠.


그래서 OTA에 대해 완벽히 알고계신가요?
알고계신다면 이번 게시물을 통해 한번 짚고 넘어갑시다

 

모른다고요?
그렇다면 이번 기회에 제대로 숙제해 보자구요


Tesla는 2010년부터 OTA를 시작했고, 이제 BMW, Audi, Mercedes, Hyundai 같은 전통 제조사들도

모두 OTA를 도입하고 있습니다.
자동차 소프트웨어 개발자라면 OTA는 더 이상 선택이 아니라 필수인거죠.

그런데 OTA는 단순한 "파일 업로드"가 아니예요.


무선 네트워크의 불안정성, 업데이트 중 전원 차단, 보안 위협, 롤백 불가능성 등 자동차 특성상 고려해야 할 것이 매우 많은 집합체입니다.


이 글에서는 OTA의 원리, 아키텍처, 보안, 그리고 실무 구현법을 정리해보겠습니다


1. OTA(Over-The-Air)란?

1.1 정의

OTA = 무선 네트워크를 통해 원격으로 차량 펌웨어를 업데이트하는 기술

구성:

┌──────────────────┐
│  자동차 제조사   │
│  (서버/클라우드) │
└────────┬─────────┘
         │ (인터넷)
         │ LTE, 5G, WiFi
         ▼
┌──────────────────────────────────────┐
│  사용자의 차량 (OTA 클라이언트)       │
│  ┌────────────────────────────────┐  │
│  │ 진단기 (DCM)                   │  │
│  │ OTA 매니저                     │  │
│  │ Bootloader                     │  │
│  │ Flash 메모리                   │  │
│  └────────────────────────────────┘  │
└──────────────────────────────────────┘

1.2 OTA vs 수동 업데이트 비교

항목 수동 업데이트 OTA 업데이트
방식 정비소 방문 (케이블 연결) 무선 네트워크
시간 1~2시간 (정비소 방문 포함) 10~30분 (자동)
비용 사용자 (정비소) + 제조사 제조사만 (스케일)
사용 편의성 낮음 (방문 필요) 높음 (자동)
긴급 보안 패치 느림 (며칠 소요) 빠름 (즉시)
확산 속도 느림 (차량마다 수동) 빠름 (동시 배포 가능)
롤백 가능 가능 신중하게 설계해야 함

1.3 자동차 OTA의 특수성

자동차 OTA는 PC/스마트폰 OTA와 완전히 다르다.

PC 업데이트 (예: Windows):
✓ 실패 → 재부팅해서 재시도
✓ 용량이 크면? 저녁에 다운로드
✓ 불안정한 네트워크? 무선 전환
✓ 중간 중단 → 다시 시작

자동차 OTA (위험 요소들):
✗ 주행 중 다운로드 → 안정성 영향?
✗ 업데이트 중 전원 차단 → 차량 시동 불가?
✗ 실패 → 정비소 방문 필수?
✗ 악의적 접근 → 차량 해킹?
✗ 이전 버전으로 돌릴 수 없음?

2. OTA의 필요성

2.1 자동차 산업의 변화

과거:

펌웨어 버그 발견
    ↓
3개월 뒤 신모델 출시
    ↓
기존 차량: 버그 유지
    ↓
정비소에서만 수정 가능 (선택적)

현재:

펌웨어 버그 발견
    ↓
24시간 뒤 OTA 배포
    ↓
모든 차량 자동 업데이트
    ↓
사용자는 무시 (백그라운드에서 처리)

2.2 OTA 사용 사례

Case 1: 버그 수정

2024년 BMW iX 소프트웨어 버그:
- 배터리 충전 불가능한 경우 발견
- 긴급 OTA 배포
- 24시간 내 300만 대 업데이트 완료
- 정비소 방문 0건

Case 2: 기능 추가

Tesla Cybertruck 예시:
- 새로운 자율주행 기능 → OTA 배포
- 게임 추가 → OTA 배포
- 사용자: 별도 설치 불필요

Case 3: 보안 패치

Hyundai BlueLink 해킹 위협:
- 보안 취약점 발견
- 긴급 OTA 배포
- 전국 차량 원격 보안 강화

3. OTA 아키텍처

3.1 계층 구조

┌─────────────────────────────────────────────────────┐
│         Application Software Layer (ASW)             │
│    - Engine Control, Body Control, Infotainment    │
└────────────────────┬────────────────────────────────┘
                     │
┌────────────────────▼────────────────────────────────┐
│     OTA Management Layer                            │
│     - OTA Manager (FOTA, SOTA)                     │
│     - DCM (Diagnostic Communication Manager)       │
│     - Security Manager (암호화, 서명)               │
└────────────────────┬────────────────────────────────┘
                     │
┌────────────────────▼────────────────────────────────┐
│     Bootloader & Flash Manager                     │
│     - Bootloader (1차 부트로더)                     │
│     - Flash Programmer                             │
│     - CRC/검증                                     │
└────────────────────┬────────────────────────────────┘
                     │
┌────────────────────▼────────────────────────────────┐
│     하드웨어 드라이버 계층                            │
│     - Fls (Flash 드라이버)                         │
│     - Com (통신 드라이버)                          │
└──────────────────────────────────────────────────────┘

3.2 주요 컴포넌트

자동차 OTA 시스템:

제조사 서버 (Cloud)
    ↓
┌──────────────────────────────────────────┐
│ OTA Server                                │
│ - 펌웨어 저장소                           │
│ - 버전 관리                               │
│ - 배포 스케줄링                           │
└──────────────────────────────────────────┘
         │
     (인터넷)
         │
┌──────────────────────────────────────────┐
│ 차량 (클라이언트)                         │
│                                          │
│ ┌──────────────────────────────────────┐ │
│ │ DCM (Diagnostic Communication Mgr)  │ │
│ │ - 연결 관리                          │ │
│ │ - 버전 보고                          │ │
│ │ - 업데이트 스케줄 조정              │ │
│ └──────────────────────────────────────┘ │
│                                          │
│ ┌──────────────────────────────────────┐ │
│ │ OTA Manager                          │ │
│ │ - 다운로드 조정                      │ │
│ │ - 크래시 백업                        │ │
│ │ - 진행 상황 추적                     │ │
│ └──────────────────────────────────────┘ │
│                                          │
│ ┌──────────────────────────────────────┐ │
│ │ Bootloader                           │ │
│ │ - 플래시 작성                        │ │
│ │ - 검증                               │ │
│ │ - 재부팅 관리                        │ │
│ └──────────────────────────────────────┘ │
│                                          │
│ ┌──────────────────────────────────────┐ │
│ │ Flash Memory                         │ │
│ │ (Old Firmware + New Firmware)        │ │
│ └──────────────────────────────────────┘ │
└──────────────────────────────────────────┘

4. OTA 프로세스

4.1 전체 흐름

[1단계: 준비 및 점검]
┌─────────────────────────────────────┐
│ 차량이 주차 중인지 확인             │
│ 배터리 전압 충분한지 확인           │
│ 네트워크 신호 충분한지 확인         │
│ 펌웨어 업데이트 가능한지 확인       │
└──────────────┬──────────────────────┘
               ▼
[2단계: 업데이트 알림]
┌─────────────────────────────────────┐
│ 사용자에게 알림                     │
│ "새로운 펌웨어 버전이 준비됨"      │
│ "주차 중 또는 충전 중 업데이트"    │
│ 사용자 승인 대기                    │
└──────────────┬──────────────────────┘
               ▼
[3단계: 다운로드]
┌─────────────────────────────────────┐
│ 펌웨어 파일 다운로드 (보통 100MB~1GB)│
│ 진행 상황 백분율 표시                │
│ 중단 가능 (언제든 중단 후 재시작)    │
│ 네트워크 손실 시 자동 재시도         │
└──────────────┬──────────────────────┘
               ▼ (다운로드: 10~30분)
[4단계: 검증 및 검사]
┌─────────────────────────────────────┐
│ 다운로드한 파일 CRC 검증             │
│ 디지털 서명 확인                     │
│ 버전 호환성 확인                     │
│ 결함 영역 점검                       │
└──────────────┬──────────────────────┘
               ▼
[5단계: 설치]
┌─────────────────────────────────────┐
│ ⚠️  차량 제어 일부 제약              │
│ - 에어컨 제어 불가                  │
│ - 창문 조작 불가                    │
│ - 기어 변경 불가                    │
│ Flash 메모리에 기록 (Bootloader)    │
│ 설치 시간: 5~15분                   │
└──────────────┬──────────────────────┘
               ▼
[6단계: 최종 검증]
┌─────────────────────────────────────┐
│ 설치된 펌웨어 무결성 재확인          │
│ 모든 ECU 정상 작동 확인              │
│ DTC 초기화 (진단 코드 리셋)          │
└──────────────┬──────────────────────┘
               ▼
[7단계: 재부팅]
┌─────────────────────────────────────┐
│ 차량 완전 재부팅                     │
│ 새로운 펌웨어로 부팅                │
│ 새로운 버전 확인                     │
│ 업데이트 완료!                      │
└─────────────────────────────────────┘

전체 시간: 20~60분 (배경에서 진행)

4.2 상세 타이밍

시간    이벤트                상태
────────────────────────────────────────
0min    업데이트 시작         정상
5min    다운로드 진행         네트워크 활성
20min   다운로드 완료         검증 중
22min   검증 완료             설치 준비
23min   설치 시작             제약 적용!
        (에어컨 OFF 등)       (차량 제어 제한)
25min   플래시 쓰기 진행      ⚠️  전원 차단 금지!
35min   플래시 쓰기 완료      설치 완료
36min   최종 검증             검사 중
40min   재부팅                부팅 중...
45min   부팅 완료             업데이트 완료!

위험 구간: 23min ~ 36min
→ 이 동안 전원이 끊기면 차량이 시동 불가!

5. OTA 보안

5.1 보안 위협

자동차 OTA를 노린 공격:

[1] 가짜 펌웨어 주입
공격자가 악의적 펌웨어를 차량으로 보냄
    → 차량 해킹 (엔진 제어 변조)
    → 브레이크 조작
    → 개인정보 탈취

[2] 중간자 공격 (Man-in-the-Middle)
펌웨어 다운로드 중 인터셉트
    → 파일 변조
    → 멀웨어 주입

[3] 버전 롤백 공격
이전 버전 (버그 있음)으로 강제 다운그레이드
    → 알려진 취약점 악용

[4] DoS (Denial of Service)
OTA 서버 공격으로 업데이트 중단
    → 차량 불완전 상태 유지

5.2 보안 메커니즘

메커니즘 1: 암호화

// 펌웨어 다운로드 시 SSL/TLS 사용
HTTPS 프로토콜
    ↓
차량이 제조사 인증서 검증
    ↓
암호화된 채널로 전송
    ↓
차량이 로컬에서만 복호화

결과: 
✓ 누군가 네트워크 감청해도 읽을 수 없음
✓ 중간자 공격 방지

메커니즘 2: 디지털 서명

// 제조사가 펌웨어 서명

제조사의 개인키로 서명 생성:
Signature = Sign_with_PrivateKey(Firmware)

차량이 검증:
if (Verify_with_PublicKey(Firmware, Signature) == OK) {
    // 신뢰할 수 있는 펌웨어
    SetupUpdate(Firmware);
} else {
    // 가짜 펌웨어!
    ErrorOccurred();
}

결과:
✓ 위조된 펌웨어 감지 불가능
✓ 공격자가 서명 생성 불가 (개인키 없음)

메커니즘 3: 버전 관리

허용되는 업데이트 경로:

v1.0 → v1.1 ✓ (정상 업그레이드)
v1.1 → v1.0 ✗ (롤백 차단)
v1.1 → v2.0 ✓ (메이저 업그레이드)
v2.0 → v1.x ✗ (다운그레이드 금지)

이유:
- v1.0에 알려진 버그 있음
- 롤백되면 그 버그 다시 노출
- 공격자가 버그 악용 가능

메커니즘 4: CRC & 해시

// 파일 무결성 검증

다운로드 파일:
┌──────────────────────┐
│ 펌웨어 데이터 (98MB) │
├──────────────────────┤
│ CRC32 (4 bytes)      │
├──────────────────────┤
│ SHA256 (32 bytes)    │
├──────────────────────┤
│ 디지털 서명 (128 bytes)│
└──────────────────────┘

검증:
1. CRC32 계산해서 저장된 값과 비교
2. SHA256 해시 계산해서 저장된 값과 비교
3. 디지털 서명 검증
4. 모두 일치 → 파일 완전함 ✓

6. AUTOSAR UDS (Unified Diagnostic Services)

6.1 OTA 관련 UDS 서비스

UDS (국제표준 ISO 14229):

OTA에 필요한 서비스들:

┌─────────────────────────────────┐
│ 0x10: ECUReset                  │
│ - 재부팅 명령                   │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│ 0x22: ReadDataByIdentifier      │
│ - 현재 버전 읽기                │
├─────────────────────────────────┤
│ 요청: 0x22 F186                 │
│ 응답: v1.2.3 (버전 식별자)      │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│ 0x31: RoutineControl            │
│ - 진단 루틴 (CAN 통신 테스트)   │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│ 0x34: RequestDownload ⭐ OTA    │
│ - 다운로드 세션 시작            │
├─────────────────────────────────┤
│ 요청:                           │
│ 0x34 (RequestDownload)          │
│ 0x01 (데이터 포맷: 압축됨)      │
│ 0x00 (주소: default)            │
│ 길이: 0x10000000 (256MB)        │
│                                 │
│ 응답:                           │
│ 0x74 (긍정 응답)                │
│ 0x00 (예약됨)                   │
│ MaxBlockLength: 0x1000 (4KB)    │
│ → 4KB씩 분할 전송               │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│ 0x36: TransferData ⭐ OTA       │
│ - 실제 데이터 전송              │
├─────────────────────────────────┤
│ 요청:                           │
│ 0x36 (TransferData)             │
│ 0x01 (블록 번호)                │
│ [4KB 펌웨어 데이터]             │
│ CRC                             │
│                                 │
│ 응답:                           │
│ 0x76 (긍정 응답)                │
│ 0x01 (블록 번호 에코)           │
│                                 │
│ 참고: 4KB씩 반복해서 전송       │
│ 1GB = 256 * 4MB = 256,000 블록! │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│ 0x37: RequestTransferExit ⭐ OTA│
│ - 다운로드 세션 종료            │
├─────────────────────────────────┤
│ 요청:                           │
│ 0x37 (RequestTransferExit)      │
│                                 │
│ 응답:                           │
│ 0x77 (긍정 응답)                │
│ [완료 정보]                     │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│ 0x3E: TesterPresent            │
│ - 연결 유지                     │
├─────────────────────────────────┤
│ 요청: 0x3E (계속 보냄)          │
│ 응답: 0x7E (계속 응답)          │
│                                 │
│ 참고: 주기적으로 보내서         │
│ 타임아웃 방지                   │
└─────────────────────────────────┘

6.2 OTA 시퀀스 다이어그램

차량                          서버
│                              │
├─ RequestDownload ──────────→ │
│  (다운로드 시작)             │
│                              │
│ ←──── RequestDownload_Resp ──┤
│  (OK, MaxBlockLength=4KB)    │
│                              │
├─ TransferData #1 ──────────→ │
│  (블록 1: 4KB)               │
│ ←──── TransferData_Resp ─────┤
│                              │
├─ TransferData #2 ──────────→ │
│  (블록 2: 4KB)               │
│ ←──── TransferData_Resp ─────┤
│                              │
│  ... (256,000번 반복) ...   │
│                              │
├─ RequestTransferExit ──────→ │
│  (다운로드 완료)             │
│                              │
│ ←── RequestTransferExit_Resp─┤
│  (완료 정보)                 │
│                              │
├─ ECUReset ───────────────→ │
│  (재부팅)                    │
│                              │
│ ←──── ECUReset_Resp ────────┤
│                              │
└─ 재부팅 ──────────────────→ │
   (새 버전으로 부팅)          │

7. Bootloader와 이중 뱅크 (Dual Bank)

7.1 플래시 메모리 레이아웃

Old School (위험):
┌────────────────────────────┐ 0x0000
│ Bootloader (Primary)       │ 64KB
├────────────────────────────┤ 0x10000
│ Application Firmware       │ 500KB
│ (교체 중 위험!)            │
│                            │
└────────────────────────────┘ 0x80000
↑
문제: 업데이트 중 전원 끊김 → 차량 시동 불가!


Modern (안전):
┌────────────────────────────┐ 0x0000
│ Bootloader (Primary) [불변]│ 64KB
├────────────────────────────┤ 0x10000
│ Bank A (Old Firmware)      │ 500KB
│ 또는                       │
│ Bank B (New Firmware)      │ 500KB
│ (하나씩 활성)              │
│                            │
├────────────────────────────┤ 0x80000
│ Metadata                   │ 16KB
│ (활성 뱅크 정보)           │
└────────────────────────────┘ 0x84000

특징:
✓ 업데이트는 비활성 뱅크에 수행
✓ 활성 뱅크는 항상 정상 펌웨어
✓ 업데이트 완료 후 메타데이터 변경
✓ 다음 부팅에 새 펌웨어 로드
✓ 실패해도 이전 펌웨어 유지!

7.2 Bootloader 동작

차량 전원 ON
      ↓
┌─────────────────────────────┐
│ Bootloader 실행             │
│ (항상 0x0000에서 시작)       │
└──────────┬──────────────────┘
           │
      ┌────▼─────┐
      │ 메타데이터 읽기
      │ 활성 뱅크 확인
      └────┬─────┘
           │
     ┌─────▼──────────┐
     │ Bank A 활성?   │
     └──┬──────────┬──┘
        YES       NO
        │         │
   Bank A   Bank B 로드
   로드      (병렬 실행)
        │         │
        └─────┬───┘
              │
     ┌────────▼─────────┐
     │ Application 실행  │
     │ (새 또는 이전)    │
     └──────────────────┘

7.3 OTA 업데이트 절차

상황 1: 정상 업데이트

시작:
Bank A (활성, v1.0) ← 현재 실행
Bank B (비활성, 빈 상태)

다운로드 중:
Bank A (활성, v1.0) ← 현재 실행
Bank B (비활성, v2.0 다운로드 중) ← 안전!

검증 완료 후:
Bank A (이전, v1.0)
Bank B (활성, v2.0) ← 메타데이터 변경

재부팅:
Bootloader 확인 → Bank B 활성 → v2.0 실행!

결과: ✓ v1.0 → v2.0 성공!


상황 2: 업데이트 중 전원 차단

다운로드 중 전원 끊김:
Bank A (활성, v1.0) ← 완전하고 정상
Bank B (비활성, v2.0 불완전)

재부팅:
Bootloader 확인 → Bank A 여전히 활성
Bank A 실행 (v1.0 정상 부팅!)

결과: ✓ 차량 시동 가능! (이전 버전)
     사용자가 재시도 가능!

8. 실무 구현 예시

8.1 OTA 다운로드 코드 (C)

#define DOWNLOAD_BLOCK_SIZE 4096  // 4KB
#define MAX_RETRIES 3

typedef struct {
    uint32_t total_size;
    uint32_t downloaded_size;
    uint8_t block_number;
    uint8_t retry_count;
} OTA_Download_Context_t;

OTA_Download_Context_t ota_ctx = {0};

// Step 1: 다운로드 세션 시작 (UDS 0x34)
Std_ReturnType OTA_RequestDownload(uint32_t firmware_size) {
    uint8_t request[8] = {
        0x34,                  // UDS Service: RequestDownload
        0x01,                  // dataFormatIdentifier (압축)
        0x00,                  // addressAndLengthFormatIdentifier
        // addressAndLength (실제로는 더 복잡함)
    };

    // ECU에 요청 전송
    uint8_t response[8];
    if (SendUDSRequest(request, 8, response, 8) != OK) {
        return ERROR;
    }

    // 응답 파싱: 최대 블록 크기 추출
    uint32_t max_block_length = (response[2] << 8) | response[3];

    if (max_block_length < DOWNLOAD_BLOCK_SIZE) {
        DOWNLOAD_BLOCK_SIZE = max_block_length;
    }

    ota_ctx.total_size = firmware_size;
    ota_ctx.downloaded_size = 0;
    ota_ctx.block_number = 1;
    ota_ctx.retry_count = 0;

    return OK;
}

// Step 2: 데이터 전송 (UDS 0x36)
Std_ReturnType OTA_TransferData(uint8_t* data_buffer, uint32_t data_length) {
    uint8_t request[DOWNLOAD_BLOCK_SIZE + 4];
    request[0] = 0x36;                      // UDS Service: TransferData
    request[1] = ota_ctx.block_number;      // blockSequenceCounter

    // 데이터 복사
    memcpy(&request[2], data_buffer, data_length);

    // CRC 계산 및 추가
    uint32_t crc = CalculateCRC32(data_buffer, data_length);
    request[data_length + 2] = (crc >> 24) & 0xFF;
    request[data_length + 3] = (crc >> 16) & 0xFF;
    request[data_length + 4] = (crc >> 8) & 0xFF;
    request[data_length + 5] = crc & 0xFF;

    // 전송 시도 (재시도 로직 포함)
    uint8_t response[4];
    uint8_t retry = 0;

    while (retry < MAX_RETRIES) {
        if (SendUDSRequest(request, data_length + 6, response, 4) == OK) {
            if (response[0] == 0x76) {  // 긍정 응답
                ota_ctx.downloaded_size += data_length;
                ota_ctx.block_number++;
                ota_ctx.retry_count = 0;
                return OK;
            }
        }

        retry++;
        ota_ctx.retry_count++;

        if (ota_ctx.retry_count > 10) {
            return ERROR;  // 너무 많은 재시도
        }

        // 재시도 전 대기
        Delay_ms(100);
    }

    return ERROR;
}

// Step 3: 다운로드 종료 (UDS 0x37)
Std_ReturnType OTA_RequestTransferExit(void) {
    uint8_t request[1] = {0x37};  // UDS Service: RequestTransferExit
    uint8_t response[4];

    if (SendUDSRequest(request, 1, response, 4) != OK) {
        return ERROR;
    }

    if (response[0] == 0x77) {  // 긍정 응답
        return OK;
    }

    return ERROR;
}

// Step 4: 재부팅 (UDS 0x10)
Std_ReturnType OTA_ECUReset(void) {
    uint8_t request[2] = {0x10, 0x01};  // RequestReset with enableRapidPowerShutDown
    uint8_t response[2];

    if (SendUDSRequest(request, 2, response, 2) != OK) {
        return ERROR;
    }

    if (response[0] == 0x50) {  // 긍정 응답
        // 재부팅 대기
        Delay_ms(100);
        return OK;
    }

    return ERROR;
}

// Main OTA 함수
Std_ReturnType OTA_UpdateFirmware(uint8_t* firmware_data, uint32_t firmware_size) {
    // 1. 다운로드 요청
    if (OTA_RequestDownload(firmware_size) != OK) {
        return ERROR;
    }

    // 2. 데이터 전송 (블록 단위)
    uint32_t offset = 0;
    while (offset < firmware_size) {
        uint32_t block_size = (firmware_size - offset > DOWNLOAD_BLOCK_SIZE) ?
                              DOWNLOAD_BLOCK_SIZE : (firmware_size - offset);

        if (OTA_TransferData(&firmware_data[offset], block_size) != OK) {
            return ERROR;
        }

        offset += block_size;

        // 진행 상황 보고
        uint8_t progress = (offset * 100) / firmware_size;
        ReportProgress(progress);
    }

    // 3. 다운로드 종료
    if (OTA_RequestTransferExit() != OK) {
        return ERROR;
    }

    // 4. 검증 및 재부팅
    if (OTA_VerifyFirmware() != OK) {
        return ERROR;  // 검증 실패, 롤백
    }

    // 5. 재부팅
    if (OTA_ECUReset() != OK) {
        return ERROR;
    }

    return OK;
}

8.2 메타데이터 및 Bootloader

// 플래시 메타데이터 구조
typedef struct {
    uint32_t active_bank;      // 0: Bank A, 1: Bank B
    uint32_t firmware_version; // v1.2.3 형식
    uint32_t crc32;            // 펌웨어 무결성
    uint32_t signature_valid;  // 서명 검증 결과
    uint32_t reserved[4];      // 예약된 공간
} OTA_Metadata_t;

#define METADATA_ADDRESS 0x80000  // 플래시 주소
#define BANK_A_ADDRESS   0x10000
#define BANK_B_ADDRESS   0x50000

// Bootloader: 활성 뱅크 로드
void Bootloader_Main(void) {
    // 메타데이터 읽기
    OTA_Metadata_t* metadata = (OTA_Metadata_t*)METADATA_ADDRESS;

    uint32_t active_address;

    if (metadata->active_bank == 0) {
        active_address = BANK_A_ADDRESS;  // Bank A
    } else {
        active_address = BANK_B_ADDRESS;  // Bank B
    }

    // 활성 뱅크 무결성 검증
    uint32_t calculated_crc = CalculateCRC32(
        (uint8_t*)active_address,
        0x40000  // 256KB 펌웨어 크기
    );

    if (calculated_crc != metadata->crc32) {
        // CRC 실패! 다른 뱅크로 폴백
        if (metadata->active_bank == 0) {
            active_address = BANK_B_ADDRESS;
        } else {
            active_address = BANK_A_ADDRESS;
        }
    }

    // 활성 뱅크로 점프
    JumpToAddress(active_address);
}

// OTA 업데이트 후 메타데이터 변경
Std_ReturnType OTA_CommitUpdate(uint32_t new_bank) {
    OTA_Metadata_t new_metadata;

    // 새 뱅크에서 메타데이터 읽기
    uint32_t fw_address = (new_bank == 0) ? BANK_A_ADDRESS : BANK_B_ADDRESS;

    new_metadata.active_bank = new_bank;
    new_metadata.firmware_version = GetFirmwareVersion();
    new_metadata.crc32 = CalculateCRC32(
        (uint8_t*)fw_address,
        0x40000
    );
    new_metadata.signature_valid = VerifySignature(
        (uint8_t*)fw_address,
        0x40000
    );

    // 메타데이터 플래시에 기록
    if (FlashProgram(METADATA_ADDRESS, (uint8_t*)&new_metadata, sizeof(OTA_Metadata_t)) != OK) {
        return ERROR;
    }

    return OK;
}

9. 자주 하는 실수

9.1 검증 생략

- 잘못된 예:
// 다운로드만 하고 검증 안 함
OTA_DownloadFirmware();
OTA_ECUReset();  // 바로 재부팅!

문제:
- 손상된 파일 설치 가능
- 차량 시동 불가
- 정비소 방문 필수

- 올바른 예:
OTA_DownloadFirmware();
if (OTA_VerifyFirmware() != OK) {  // 검증 필수!
    OTA_Rollback();
    return ERROR;
}
OTA_ECUReset();

9.2 롤백 방법 미제공

- 잘못된 예:
// 이중 뱅크 없음
Bank (유일한 공간)
    ↓ 업데이트 중 전원 차단
    ↓ 펌웨어 손상
    ↓ 차량 시동 불가!
    ↓ 정비소 방문 필수

- 올바른 예:
Bank A (활성, v1.0)
Bank B (대기)
    ↓ 다운로드 중 전원 차단
    ↓ Bank A 여전히 정상
    ↓ 재부팅 후 v1.0 실행
    ↓ 재시도 가능!

9.3 전력 부족 고려 안 함

- 잘못된 예:
// 배터리 전압 확인 안 함
OTA_StartUpdate();
// 업데이트 중 배터리 방전
// 플래시 쓰기 중단!

- 올바른 예:
if (GetBatteryVoltage() < 11.0V) {
    return ERROR;  // 배터리 충전 필요
}

if (IsBatteryCharging() == FALSE) {
    // 충전 중일 때만 업데이트
    return ERROR;
}

OTA_StartUpdate();

9.4 네트워크 중단 미처리

- 잘못된 예:
// 다운로드 중 네트워크 끊김
OTA_DownloadFirmware();  // WiFi 끊김!
// 불완전한 파일 설치됨

- 올바른 예:
#define MAX_RETRIES 5

Std_ReturnType status = OTA_DownloadFirmware();

if (status == NETWORK_ERROR) {
    for (int retry = 0; retry < MAX_RETRIES; retry++) {
        Delay_ms(5000);  // 5초 대기
        status = OTA_DownloadFirmware();

        if (status == OK) {
            break;
        }
    }

    if (status != OK) {
        return ERROR;  // 재시도 실패
    }
}

10. OTA 성능 분석

10.1 다운로드 시간

펌웨어 크기: 1GB
네트워크: LTE (평균 10Mbps)

계산:
1GB = 8,000 Mb
시간 = 8,000 Mb / 10 Mbps = 800초 = 13분

실제:
- 오버헤드 (UDS 프로토콜): +5%
- 재시도 (약한 신호): +20%
- 패킷 손실: +10%
─────────────────────────
실제 시간: 20~30분

요소별 시간:
- 다운로드: 15~20분
- 검증: 2~3분
- 설치: 5~10분
- 재부팅: 1~2분
─────────────────────────
총 시간: 23~35분

10.2 전력 소비

OTA 프로세스 중 전력 소비:

대기: 100W (백그라운드)
다운로드: 200W (모뎀 활성)
설치: 150W (플래시 쓰기)
재부팅: 500W (부팅 프로세스)

총 에너지:
(20분 × 200W) + (3분 × 150W) + (2분 × 500W)
= 4000Wh + 450Wh + 1000Wh
= 5450Wh

배터리 용량: 60kWh (일반적인 EV)
OTA로 소비: 5450Wh / 60000Wh = 9%

결론: ✓ 배터리 충전 중이면 무시할 수준

10.3 네트워크 대역폭

단일 차량: 1GB / 20분 = 50Mbps (피크)
100만 대 동시: 50 × 1,000,000 = 50PB/s (불가능!)

해결책:

[1] Staged Rollout (단계적 배포)
Day 1: 100,000대 (5%) 
Day 2: 300,000대 (15%)
Day 3: 1,000,000대 (50%)
Day 4: 나머지

→ 네트워크 부하 분산


[2] 대역폭 제한
차량이 최대 20Mbps로 제한
→ 서버 부하 감소


[3] CDN (Content Delivery Network)
서버 → 지역 서버 → 차량
→ 전송 최적화

11. 핵심 정리

OTA(Over-The-Air):
- 무선 네트워크로 차량 펌웨어 원격 업데이트
- 현대 자동차 필수 기술
- 단순 파일 업로드가 아님

필요성:
- 버그 수정 빠름 (며칠 → 즉시)
- 보안 패치 효율적
- 사용자 편의성 증대
- 정비소 방문 감소

핵심 기술:
- 이중 뱅크 (Dual Bank): 안전성
- 디지털 서명: 보안
- UDS 프로토콜: 표준
- CRC/해시: 무결성

위험 요소:
- 전원 차단 (이중 뱅크로 해결)
- 네트워크 중단 (재시도 로직)
- 악의적 공격 (암호화 + 서명)
- 버전 롤백 (버전 관리)

프로세스:
1. 준비 및 점검
2. 알림 및 승인
3. 다운로드 (20~30분)
4. 검증 (2~3분)
5. 설치 (5~10분)
6. 재부팅 (1~2분)

성능:
- 전체 시간: 30~45분
- 전력 소비: 배터리의 ~10%
- 네트워크: 50Mbps 개별 차량