🔨 개발자가 반드시 알아야 할 Compile, Linking, Build, Rebuild, GenerateAll의 완벽한 차이
들어가며
안녕하세요 버그없는토마토입니다
여러분은 혹시 컴파일과 빌드의 차이를 아시나요?
그렇다면 링킹과 빌드의 차이는요?
어떨때 빌드를 쓰고 어떨때 리빌드를 쓸까요?
신입이 물어본다면, 위 개념을 구분해서 설명해줄 수 있나요?
저는 신입때 이 구분이 명확하지 않아서 혼동해서 쓰기도 하고 얼버무려 대답한 적도 있답니다
그러다가 그당시 팀장님이 물어본 질문에 추상적으로만 어버버했던 기억이 있어요..

오늘은 제가 컴파일부터 링킹, 빌드, 리빌드, GenarateAll까지 모두 마스터 시켜드릴게요
여러분이 빌드를 위해 빌드프로그램이든, 모빌진이든, 빌드서버든 어떤 프로젝트를 열었어요.
IDE (Integrated Development Environment):
├─ Compile 버튼 클릭
├─ Linking 버튼 클릭?
├─ Build 버튼 클릭
├─ Rebuild 버튼 클릭
├─ GenerateAll 버튼 클릭
└─ Clean 버튼 클릭?
어?! 이게 다 뭐야?
뭘 눌러야 하는 거야?
Linking은 또 뭐고?
차이가 뭐야?
일하다가:
신입: "연구원님!! Linking이 뭐예요?"
선배: "음... Compile 후에 하는 건데..."
신입: "?"
선배: "그냥 Build 버튼 누르면 알아서 다 돼! 모르면 지피티한테 물어봐......"
결과: 개념은 모르고, 아몰라 지피티한테 물어봐... 😭
이 혼동을 끝내자구요!
이제부터는 Compile, Linking, Build, Rebuild, GenerateAll의
정확한 정의, 순서, 차이, 그리고 언제 뭘 써야 하는지 명확하게 설명해볼게요!!
📌 3분 요약
바쁜 당신을 위한 핵심:
📝 Compile = 소스 코드 → 머신 코드 (한 파일)
🔗 Linking = 여러 Object 파일 → 실행 파일
🔨 Build = Compile + Linking (변경된 파일만)
🔄 Rebuild = Compile + Linking (모든 파일 새로)
⚙️ GenerateAll = 설정 파일 → 코드 자동 생성
Compile은 각 부품, Linking은 조립, Build는 최종 완성!

📝 Compile (컴파일)이란?
정의
Compile = 소스 코드를 머신 코드로 변환하는 과정
레고로 예를 들어볼게요
레고블럭이 촤르르 있고, 그걸 하나하나 조립하여 하나의 결과물을 만들어내죠.
컴파일의 과정은 '레고블럭 하나 하나' 라고 생각하시면 됩니다.저는 레고가 비싸서 가지고 놀지도 못했지만요
원본: posco.c (소스 코드)
↓ (컴파일)
결과: posco.o (머신 코드 / Object 파일)
흐름:
┌──────────────┐
│ posco.c │ ← 사람이 읽을 수 있음
│ int x = 5; │
│ x = x + 1; │
└──────────┬───┘
│ (Compile)
↓
┌──────────────────────┐
│ posco.o (Object File)│ ← 컴퓨터만 읽을 수 있음
│ 01001101 01100101... │ ← 기계어
└──────────────────────┘
핵심:
✓ 한 번에 한 파일
✓ 실행은 불가능 (아직)
✓ Object 파일만 생성
컴파일 과정 (상세 - 7 단계)
Source Code (posco.c):
#include <stdio.h>
int main() {
int x = 5;
int y = x + 1;
printf("y = %d", y);
return 0;
}
↓ (Step 1: 전처리 - Preprocessing)
#include <stdio.h> 확장
→ stdio.h 파일의 내용을 포함시킴
결과:
// stdio.h의 모든 내용
printf 함수 선언들...
int main() {
int x = 5;
int y = x + 1;
printf("y = %d", y);
return 0;
}
↓ (Step 2: 구문 분석 - Parsing)
문법이 맞나?
- 세미콜론은 있나?
- 괄호가 맞나?
- 중괄호가 맞나?
결과: ✓ 문법 OK!
↓ (Step 3: 의미 분석 - Semantic Analysis)
타입이 맞나?
- int x = 5? OK
- int y = x + 1? OK (int + int = int)
- printf("...", y)? OK
결과: ✓ 타입 OK!
↓ (Step 4: 중간 코드 생성 - Intermediate Code Generation)
고급 언어 → 중간 표현 (IR)
결과:
LOAD x, 5
ADD x, 1
STORE y
CALL printf
↓ (Step 5: 최적화 - Optimization)
불필요한 코드 제거, 루프 최적화
결과:
LOAD x, 5
ADD x, 1 // 즉시 y에 저장
CALL printf
↓ (Step 6: 코드 생성 - Code Generation)
중간 코드 → 어셈블리 코드
결과:
MOV eax, 5 ; x = 5
ADD eax, 1 ; x + 1
MOV ebx, eax ; y = 결과
CALL printf ; printf 호출
↓ (Step 7: 어셈블 - Assembling)
어셈블리 코드 → 머신 코드
결과: posco.o (Object File)
┌─────────────────────┐
│ 01001101 01100101 │
│ 10110010 11001010 │ (기계어)
│ ... │
└─────────────────────┘
최종: posco.o 생성! ✓
컴파일의 특징
특징:
1️⃣ 한 번에 한 파일 처리
posco.c → posco.o
math.c → math.o
main.c → main.o
(각각 독립적)
2️⃣ Object 파일은 실행 불가
posco.o는 완성된 프로그램이 아님
다른 Object 파일들과 연결 필요
3️⃣ 시간이 짧음
작은 파일: 0.1초
중간 파일: 0.5초
큰 파일: 1~2초
4️⃣ 오류 가능
문법 오류 (Syntax Error): missing ';'
타입 오류 (Type Error): int x = "posco"
컴파일 오류 (Compile Error)
5️⃣ 경고도 가능
컴파일은 되지만 주의
"Unused variable: x"
Compile 오류 예시
❌ 문법 오류:
int main() {
int x = 5 // <- 세미콜론 없음!
return 0;
}
컴파일 결과: error: expected ';'
❌ 타입 오류:
int main() {
int x = "posco"; // <- 문자열을 정수에?
return 0;
}
컴파일 결과: error: incompatible types
❌ 변수 미선언:
int main() {
printf("%d", y); // <- y를 선언 안 함!
return 0;
}
컴파일 결과: error: 'y' undeclared

🔗 Linking (링킹)이란?
정의
Linking = 여러 Object 파일과 라이브러리를 연결해서 최종 실행 파일을 만드는 과정
링킹의 단계는 레고들을 하나하나 조립하는 과정입니다.
링킹 전:
posco.o (Object 파일 1)
math.o (Object 파일 2)
util.o (Object 파일 3)
libc.a (표준 라이브러리)
↓ (Linking)
링킹 후:
posco (최종 실행 파일)
링킹의 역할 (4가지)
1️⃣ Object 파일들을 하나로 합치기
┌─────────┐
│posco.o │
└─────┬───┘
│
┌─────▼───┐
│ math.o │ ← 결합
└─────┬───┘
│
┌─────▼───┐
│ util.o │ ← 결합
└─────┬───┘
│
결합 완료! ✓
2️⃣ 라이브러리 추가
printf, malloc, strcpy 같은
표준 함수들을 포함
예: libc.a (C 표준 라이브러리)
3️⃣ 심볼 해결 (Symbol Resolution)
어떤 함수를 어디서 찾을까?
예: main.o에서 "printf 호출"
→ printf를 libc.a에서 찾음
→ 연결!
4️⃣ 메모리 주소 할당
각 코드와 데이터에 주소 할당
예:
main 함수: 0x08048000
printf 함수: 0x08049000
x 변수: 0x0804a000
링킹 상세 흐름
┌────────────────────────────────┐
│ Step 1: Object 파일 읽기 │
│ posco.o, math.o, util.o 로드 │
└────────┬───────────────────────┘
│
┌────────▼───────────────────────┐
│ Step 2: 심볼 테이블 수집 │
│ 모든 함수, 변수의 이름 수집 │
│ │
│ 예: printf (외부 참조) │
│ calculate_sum (내부 정의) │
└────────┬───────────────────────┘
│
┌────────▼───────────────────────┐
│ Step 3: 심볼 해결 │
│ "printf는 어디에 있나?" │
│ → libc.a에서 찾음 │
│ → 주소 결정 │
└────────┬───────────────────────┘
│
┌────────▼───────────────────────┐
│ Step 4: 메모리 배치 │
│ .text (코드) → 0x08048000 │
│ .data (초기화 데이터) → ... │
│ .bss (미초기화 데이터) → ... │
└────────┬───────────────────────┘
│
┌────────▼───────────────────────┐
│ Step 5: 재배치 (Relocation) │
│ 상대 주소를 절대 주소로 변환 │
└────────┬───────────────────────┘
│
┌────────▼───────────────────────┐
│ 결과: posco (실행 파일) │
│ ✓ 실행 가능! │
└────────────────────────────────┘
링킹 오류 (Symbol 문제)
❌ 링킹 오류 1: 함수 정의 없음
posco.c:
int main() {
calculateSum(1, 2); // 함수 호출
return 0;
}
문제: calculateSum 함수의 구현이 없다!
링킹 결과:
undefined reference to 'calculateSum'
해결:
math.c 파일을 함께 컴파일해야 함:
gcc posco.c math.c -o posco
❌ 링킹 오류 2: 중복 정의
posco.c:
void greeting() {
printf("posco!");
}
main.c:
void greeting() {
printf("Hi!");
}
문제: greeting 함수가 두 군데 있다!
링킹 결과:
multiple definition of 'greeting'
해결:
함수를 한 군데만 정의하고,
다른 곳에서는 extern으로 선언:
posco.c:
void greeting() { ... }
main.c:
extern void greeting(); // 선언만
int main() {
greeting();
return 0;
}
❌ 링킹 오류 3: 라이브러리 누락
posco.c:
#include <math.h>
int main() {
double result = sqrt(16);
return 0;
}
문제: sqrt 함수가 libm (수학 라이브러리)에 있는데 링크 안 함
링킹 결과:
undefined reference to 'sqrt'
해결:
수학 라이브러리를 명시적으로 링크:
gcc posco.c -o posco -lm
(^ -lm = link math library)
Compile vs Linking
Compile:
- 소스 코드 → Object 파일
- 한 파일씩 독립적 처리
- Object 파일은 실행 불가
- 오류: 문법, 타입
- 빠름 (0.1~2초)
Linking:
- Object + Library → 실행 파일
- 모든 파일을 함께 처리
- 최종 실행 파일 생성
- 오류: 심볼, 중복 정의
- 조금 더 오래 걸림 (0.5~5초)
비유가 또 빠질 수 없죠:
Compile = 레고 블록 하나하나 만들기
Linking = 레고 블록들을 연결해서 집 만들기

🔨 Build (빌드)란?
정의
Build = Compile + Linking + 기타 작업의 전체 과정 (변경된 파일만)
빌드의 개념은 레고를 조립하는 과정이라고 보시면 돼요.
하지만 뒤에 변경된 파일만이라는 말이 붙었죠?
원래 만들었던 자동차 레고에 사람만 다른 친구를 태우고 싶을 때 빌드를 사용합니다
기존 자동차 레고는 그대로 두고 사람만 바꿔 끼는거죠.
장점은? 사람만 바꿔 끼면 되기때문에 실행 시간이 빠르다는 장점이죠.
다시 처음부터 만들지 않아도 되니까요!
Build의 전체 흐름:
┌─────────────────────────────────────┐
│ 1️⃣ 변경된 파일 확인 (Dependency) │
│ "어떤 파일이 변경됐나?" │
│ │
│ Modified Files: │
│ ├─ posco.c (변경됨!) ← 컴파일 │
│ ├─ math.c (안 변경) ← 유지 │
│ └─ util.c (안 변경) ← 유지 │
└────────┬────────────────────────────┘
│
┌────────▼────────────────────────────┐
│ 2️⃣ 변경된 파일만 컴파일 │
│ posco.c → posco.o (새로 만듦) │
│ math.o, util.o는 기존 유지 │
└────────┬────────────────────────────┘
│
┌────────▼────────────────────────────┐
│ 3️⃣ 링킹 │
│ posco.o + math.o + util.o │
│ + 라이브러리들 → posco (실행) │
└────────┬────────────────────────────┘
│
┌────────▼────────────────────────────┐
│ 4️⃣ 검증 │
│ 오류 체크, 경고 확인 │
└────────┬────────────────────────────┘
│
┌────────▼────────────────────────────┐
│ 결과: posco (실행 파일) │
│ ✓ 실행 가능! │
└─────────────────────────────────────┘
핵심 포인트: "변경된 것만 다시!"
Build의 특징
특징:
1️⃣ 스마트한 처리
변경된 파일만 컴파일
안 변경된 파일은 이전 Object 파일 사용
2️⃣ 시간이 짧음
일부만 컴파일하니까
전체 Rebuild보다 훨씬 빠름
예:
posco.c만 변경 → 0.5초
모든 파일 다시 → 3초
(6배 차이!)
3️⃣ 실행 파일 생성
Compile + Linking까지 완료
바로 실행 가능!
4️⃣ 일상 사용
개발하면서 계속 사용
"저장 → Build" 반복
5️⃣ 의존성 추적
헤더 파일 변경 감지
예: common.h 변경
→ common.h를 #include한 모든 파일 재컴파일
Build 예시
프로젝트 구조:
posco.c ──┐
├─> Makefile
math.c ───┤
│
util.c ───┘
Makefile:
CC = gcc
SRCS = posco.c math.c util.c
OBJS = $(SRCS:.c=.o)
TARGET = posco
$(TARGET): $(OBJS)
$(CC) $(OBJS) -o $(TARGET)
%.o: %.c
$(CC) -c $< -o $@
상황 1: 처음 빌드
$ make
gcc -c posco.c -o posco.o
gcc -c math.c -o math.o
gcc -c util.c -o util.o
gcc posco.o math.o util.o -o posco
결과: 모든 파일 컴파일 + 링킹 ✓
상황 2: posco.c만 수정 후 빌드
$ make
gcc -c posco.c -o posco.o (← 이것만!)
gcc posco.o math.o util.o -o posco
결과: posco.c만 재컴파일 ✓
상황 3: 아무 파일도 수정 안 함
$ make
make: 'posco' is up to date.
결과: 아무것도 안 함 ✓ (이미 최신)

🔄 Rebuild (리빌드)란?
정의
Rebuild = 모든 파일을 처음부터 다시 컴파일하고 링킹
리빌드의 개념을 설명하기 위해 빌드에서 변경된 파일만 이라는 주석을 굳이 추가했던 것인데요
리빌드란? 자동차 레고를 다시 처음부터 조립하는 거예요
다시 처음부터 조립하기 때문에 실행시간이 오래 걸려요.
그럼 왜 리빌드를 사용하냐고요?
어디선가 오류가 생겼는데 추적이 어려울경우에 리빌드를 통해 리셋하는 개념입니다.
오류의 여지조차 주기 싫을때 리빌드로 가능성을 배제시켜버리는거죠.
Rebuild의 흐름:
┌─────────────────────────────────────┐
│ 1️⃣ 모든 Object 파일 삭제 │
│ posco.o, math.o, util.o 모두 삭제│
│ (변경 여부 상관없음) │
└────────┬────────────────────────────┘
│
┌────────▼────────────────────────────┐
│ 2️⃣ 모든 파일 다시 컴파일 │
│ posco.c → posco.o (새로 만듦) │
│ math.c → math.o (새로 만듦) │
│ util.c → util.o (새로 만듦) │
│ (모든 파일 강제 컴파일) │
└────────┬────────────────────────────┘
│
┌────────▼────────────────────────────┐
│ 3️⃣ 링킹 │
│ 모든 새 Object 파일 합치기 │
└────────┬────────────────────────────┘
│
┌────────▼────────────────────────────┐
│ 결과: posco (새로 만든 실행 파일) │
│ ✓ 완전히 새로운 빌드! │
└─────────────────────────────────────┘
핵심: "싹 다 새로 만들어!"
Build vs Rebuild 시간 비교
프로젝트: 10개 파일 (각각 1초 컴파일)
상황: posco.c만 변경
Build:
├─ posco.c 컴파일: 1초
├─ math.o~util.o 유지: 0초 (이미 있음)
├─ 링킹: 0.5초
└─ 총: 1.5초 ⚡
Rebuild:
├─ posco.c 컴파일: 1초
├─ math.c 컴파일: 1초
├─ util.c 컴파일: 1초
├─ ... (모든 파일)
├─ 링킹: 0.5초
└─ 총: 10.5초 🐌
차이: 7배! (1.5초 vs 10.5초)
실무 예시:
Build 자주 (매 10분마다)
Rebuild 가끔 (필요할 때만)
= 개발 속도 대폭 향상! ⚡
Rebuild가 필요한 경우
1️⃣ 컴파일러 옵션 변경
-O2 (최적화 off) → -O3 (최적화 on)
문제: 다른 최적화 레벨로 모든 파일이 다르게 컴파일됨
→ 모든 파일 영향
→ Rebuild 필요! 🔄
2️⃣ 헤더 파일 변경
common.h 수정
common.h를 #include하는 파일들:
├─ posco.c
├─ math.c
└─ util.c
→ 여러 파일 영향
→ Rebuild 필요! 🔄
3️⃣ 매크로 정의 변경
#define BUFFER_SIZE 256 → 512
문제: 이 매크로를 사용하는 모든 파일에 영향
→ Rebuild 필요! 🔄
4️⃣ 의존성 문제
Build했는데 이상한 오류 발생:
"undefined reference to 'xyz'"
원인: 의존성 추적 실패?
해결: "그냥 Rebuild 해봐!" ← 실무 조언
→ Rebuild 필요! 🔄
5️⃣ 깨끗한 빌드 원할 때
"처음부터 깔끔하게 시작하고 싶어"
→ Rebuild 필요! 🔄

⚙️ GenerateAll (제너레이트올)이란?
정의
GenerateAll = AUTOSAR 설정 파일(.arxml)에서 코드를 자동 생성
모빌진 프로젝트를 진행하다보면 Generate폴더와 Debug 폴더가 나옵니다
GenerateAll로 모빌진 수정점을 반영하여 생성파일을 생성하는 과정입니다.
그리고 그 Generate 파일을 통해 컴파일을 진행하죠.
GenerateAll의 흐름:
┌─────────────────────────────────────┐
│ AUTOSAR 설정 파일들 │
│ ├─ EngineControl.arxml │
│ ├─ Network.arxml │
│ ├─ SystemDescription.arxml │
│ └─ AUTOSAR 정의들... │
└────────┬────────────────────────────┘
│ (Generator 도구)
│ (Davinci Configurator, SystemDesk 등)
▼
┌─────────────────────────────────────┐
│ 자동 생성된 코드 │
│ ├─ Rte.h / Rte.c (RTE 코드) │
│ ├─ Rte_Type.h (타입 정의) │
│ ├─ Rte_SWC_*.h (SWC 헤더) │
│ ├─ Com_PBcfg.c (통신 설정) │
│ ├─ Dem_PbCfg.c (진단 설정) │
│ └─ ... (많은 파일들) │
└────────┬────────────────────────────┘
│
┌────────▼────────────────────────────┐
│ Build 시작 │
│ 이 생성된 코드들을 컴파일 │
└─────────────────────────────────────┘
핵심: "설정 → 코드 자동 생성"
GenerateAll의 상세 작업
ARXML 파일 (설정):
<AUTOSAR>
<AR-PACKAGES>
<AR-PACKAGE>
<SHORT-NAME>ServiceInterface</SHORT-NAME>
<ELEMENTS>
<SERVICE-INTERFACE>
<SHORT-NAME>EngineControlI</SHORT-NAME>
<OPERATIONS>
<OPERATION-PROTOTYPE>
<SHORT-NAME>GetRPM</SHORT-NAME>
<OUTPUT>RPM (uint16)</OUTPUT>
</OPERATION-PROTOTYPE>
<OPERATION-PROTOTYPE>
<SHORT-NAME>SetTorque</SHORT-NAME>
<INPUT>Torque (uint16)</INPUT>
</OPERATION-PROTOTYPE>
</OPERATIONS>
</SERVICE-INTERFACE>
</ELEMENTS>
</AR-PACKAGE>
</AR-PACKAGES>
</AUTOSAR>
↓ (GenerateAll 도구 실행)
자동 생성된 Rte.h:
#ifndef RTE_H
#define RTE_H
#include "Std_Types.h"
// GetRPM 함수 원형
Std_ReturnType Rte_Call_EngineControl_GetRPM(
uint16* rpm
);
// SetTorque 함수 원형
Std_ReturnType Rte_Call_EngineControl_SetTorque(
uint16 torque
);
#endif // RTE_H
자동 생성된 Rte.c:
#include "Rte.h"
#include "EngineControl_Impl.h"
Std_ReturnType Rte_Call_EngineControl_GetRPM(
uint16* rpm
) {
// RTE가 실제 구현 함수 호출
return EngineControl_GetRPM_Impl(rpm);
}
Std_ReturnType Rte_Call_EngineControl_SetTorque(
uint16 torque
) {
// RTE가 실제 구현 함수 호출
return EngineControl_SetTorque_Impl(torque);
}
자동 생성된 Rte_Type.h:
#ifndef RTE_TYPE_H
#define RTE_TYPE_H
// 신호 타입 정의
typedef struct {
uint16 RPM;
uint16 Torque;
uint8 EngineStatus;
} Rte_Type_EngineData;
#endif
GenerateAll의 특징
특징:
1️⃣ 설정 기반
.arxml, .dbc 설정 파일에서 생성
코드 직접 수정 ❌
설정만 수정하고 재생성 ✓
2️⃣ AUTOSAR 전용
AUTOSAR 프로젝트에서만 사용
일반 C/C++ 프로젝트에는 없음
3️⃣ 시간이 빠름
생성 자체는 빠름 (1~2초)
하지만 이후 Build까지 고려하면 시간 걸림
4️⃣ 수동 수정 금지!
생성된 파일 직접 수정하면?
다시 GenerateAll하면 덮어씌워짐! 💥
예:
Rte.c에서 수동 수정 후
다시 GenerateAll 실행
→ 수정사항 모두 사라짐! ❌
5️⃣ 재생성 필수
설정 변경할 때마다 다시 생성
"GenerateAll → Build" 순서!
주의: 손으로 쓴 코드는 어디에?
구조:
┌────────────────────────────┐
│ 생성된 코드 (자동) ❌ 수정 금지│
├────────────────────────────┤
│ Rte.h (자동 생성) │
│ typedef struct { │
│ uint16 RPM; │
│ } Rte_Type_EngineData; │
├────────────────────────────┤
│ Rte.c (자동 생성) │
│ Std_ReturnType Rte_Call... │
└────────────────────────────┘
↓ (포함/호출)
┌────────────────────────────┐
│ 손으로 쓴 코드 ✓ 자유 │
├────────────────────────────┤
│ EngineControl_Impl.c │
│ Std_ReturnType │
│ EngineControl_GetRPM_Impl() {│
│ uint16 rpm = 3000; │
│ // 나의 로직! │
│ return E_OK; │
│ } │
└────────────────────────────┘
규칙:
- Rte.h, Rte.c: 자동 생성, 수정 금지
- EngineControl_Impl.c: 손으로 작성, 자유
- common.h: 손으로 작성, 자유
차이:
생성 파일: GenerateAll할 때마다 덮어씌워짐
구현 파일: 손으로 쓴 것 유지
📊 5가지 명령의 완벽 비교
| 명령 | 정의 | 대상 | 소요 시간 | 사용 시점 | 특징 |
|------|------|------|---------|---------|------|
| **Compile** | 소스→Object | 한 파일 | 0.1~2초 | 드물게 | 실행불가 |
| **Linking** | Object+Lib→실행 | 모든파일 | 0.5~5초 | 자동 | 최종실행 |
| **Build** | Compile+Link | 변경파일 | 0.5초 | 매일 | 빠름! |
| **Rebuild** | Compile+Link | 모든파일 | 3~10초 | 필요시 | 느림 |
| **GenerateAll** | ARXML→코드 | 설정 | 1~2초 | ARXML변경 | AUTOSAR전용 |
흐름:
GenerateAll (필요시, AUTOSAR 설정 변경)
↓
Build (매일, 코드 수정할 때마다)
↓
오류 발생?
↓
Rebuild (해결 안 되면)
↓
Clean (완전 초기화, 최후의 수단)
🔌 실무 시나리오
시나리오 1: 일반적인 개발
[아침 출근]
1️⃣ 코드 수정
EngineControl.c 파일 수정
2️⃣ Build 클릭
변경된 EngineControl.c만 Compile
전체 Linking
시간: 0.5초 ⚡
3️⃣ 오류 없음!
계속 개발
[점심 시간 전]
1️⃣ 또 수정
TransmissionControl.c 파일 수정
2️⃣ Build 클릭
변경된 파일만 Compile
시간: 0.5초 ⚡
결론: Build만 써도 충분! ✓
시나리오 2: 설정 변경 (AUTOSAR)
[오전]
ARXML 파일에서 신호 추가
└─ EngineControl 신호 2개 추가
[IDE에서]
1️⃣ GenerateAll 클릭!
작업 순서:
└─ Rte.h 다시 생성
└─ Rte.c 다시 생성 (새로운 신호 함수 추가)
└─ Rte_Type.h 다시 생성 (새로운 타입)
시간: 2초
2️⃣ Build 클릭
작업:
└─ 생성된 Rte.c Compile
└─ 전체 Linking
시간: 1초
결론: GenerateAll → Build 순서! ✓
주의: GenerateAll 빼먹으면?
→ 설정은 바뀌었는데 코드는 안 바뀜
→ 버그! 🐛
시나리오 3: 이상한 오류
[오후 2시]
빌드 실패!
에러 메시지:
"undefined reference to 'EngineControl_GetRPM_Impl'"
뭐가 잘못된 걸까?
- 함수 선언은 있는데?
- EngineControl_Impl.c도 있는데?
- 왜 안 찾지?
원인 불명...
[선배의 조언]
"음... 그냥 Rebuild 해봐!"
1️⃣ Rebuild 클릭
작업:
├─ 모든 .o 파일 삭제
├─ 모든 .c 파일 Compile (새로)
│ ├─ EngineControl_Impl.c
│ ├─ Rte.c
│ └─ 기타 모든 파일
└─ 링킹 (다시)
시간: 5초
2️⃣ 성공! ✓
결론: 원인 모를 오류? Rebuild! 🔄
이유:
아마도 Object 파일 중 하나가 손상되었거나,
의존성 추적이 잘못되었을 수 있음
→ Rebuild로 모두 다시 해결!
시나리오 4: 매크로 변경
[오후 3시]
common.h 파일에서 중요한 매크로 변경:
#define BUFFER_SIZE 256 → 512
#define MAX_CHANNELS 4 → 8
이 매크로를 사용하는 파일들:
├─ EngineControl.c
├─ TransmissionControl.c
├─ DiagnosticServer.c
└─ Communication.c
[개발자: Build만 했어]
결과: 일부 파일은 256, 일부는 512?
→ 버퍼 크기 일관성 없음
→ 메모리 오류! 💥
[해결책]
Rebuild 클릭!
작업:
├─ 모든 파일 강제 재컴파일
│ (common.h 변경 감지 여부 관계없이)
├─ 모든 파일이 새로운 매크로값 사용
└─ 일관성 보장 ✓
결론: 전역 설정 변경? Rebuild! 🔄
💡 실무 팁 & Best Practices
1️⃣ 언제 뭘 사용할까?
일반 코드 수정:
코드 수정 → Build → 테스트
AUTOSAR 설정 수정:
ARXML 수정 → GenerateAll → Build → 테스트
전역 설정/매크로 수정:
설정 변경 → Rebuild → 테스트
이상한 오류:
① Build 다시
② GenerateAll + Build
③ Rebuild
④ Clean + Rebuild (최후)
2️⃣ 빠른 빌드를 위한 팁
❌ 항상 Rebuild
시간 낭비: 매번 3~10초
✓ 상황에 맞게 선택
├─ 일반 코드 수정: Build (0.5초)
├─ ARXML 수정: GenerateAll (2초)
├─ 매크로 수정: Rebuild (5초)
└─ 불명 오류: Rebuild (5초)
효과:
단순 계산:
- 매일 Build 10번 (코드 수정)
- 주 1회 GenerateAll (설정 수정)
- 월 2회 Rebuild (오류 해결)
Build만 쓸 때: 0.5×10 = 5초/일
Rebuild 남발: 5×10 = 50초/일 (10배!)
월간:
Build: 5초 × 20일 = 100초 = 1.7분
Rebuild 남발: 50초 × 20일 = 1000초 = 16.7분
= 월 15분 시간 절약! ⚡
3️⃣ CI/CD에서의 활용
자동 빌드 파이프라인:
1️⃣코드 커밋
└─ Git에 푸시
2️⃣ CI 서버 (Jenkins, GitLab CI)
├─ Clean (완전 초기화)
├─ GenerateAll (모든 ARXML 재생성)
├─ Rebuild (모든 파일 컴파일)
└─ Test 실행
3️⃣ 결과 보고
├─ 빌드 성공/실패
├─ 테스트 결과
└─ 배포
CI에서는?
→ 항상 Clean + GenerateAll + Rebuild
→ 일관성, 신뢰성 최우선
→ 속도는 그 다음
🎯 면접 예상 질문
Q1: "Compile과 Linking의 차이는?"
A: "Compile은 한 개 소스 파일을 Object 파일로 변환하는 과정이고,
Linking은 여러 Object 파일과 라이브러리를 연결해서
최종 실행 파일을 만드는 과정입니다.
Compile 없이는 Linking도 불가능해요."
Q2: "Build와 Compile의 차이는?"
A: "Compile은 소스 → Object 파일만 하는 과정이고,
Build는 Compile에 더해 Linking과 최적화 과정을 포함한
전체 과정입니다. 또한 Build는 변경된 파일만 컴파일하지만,
Compile 명령을 직접 쓸 때는 보통 한 파일씩 해야 합니다."
Q3: "Rebuild는 언제 써요?"
A: "Build로 오류가 안 나는데 이상한 결과가 나올 때,
또는 매크로나 헤더 파일, 컴파일러 옵션을 변경했을 때 씁니다.
모든 파일을 처음부터 다시 컴파일하니까요."
Q4: "GenerateAll은?"
A: "AUTOSAR 프로젝트에서 .arxml 설정 파일에서
코드를 자동 생성하는 과정입니다.
ARXML를 수정하면 GenerateAll → Build 순서로 진행합니다.
생성된 파일은 수정하면 안 되고, 구현은 별도 파일에서 합니다."
Q5: "생성된 파일을 직접 수정해도 돼?"
A: "안 됩니다. GenerateAll하면 다시 생성되면서
제 수정사항이 다 없어집니다.
Rte.c 같은 생성 파일은 건드리지 말고,
EngineControl_Impl.c 같은 별도의 구현 파일에서
손으로 코드를 작성해야 합니다."
📌 핵심 정리
| 명령 | 정의 | 실행 대상 | 시간 | 일상성 |
|---|---|---|---|---|
| Compile | 소스→Object | 한 파일 | ⚡ | 드물 |
| Linking | Object+Lib→실행 | 모든 파일 | ⚡ | 자동 |
| Build | Compile+Link | 변경 파일만 | ⚡ | 매일 |
| Rebuild | Compile+Link | 모든 파일 | 🐌 | 필요시 |
| GenerateAll | ARXML→코드 | 설정 | ⚡ | ARXML수정시 |
마치며
이 개념들 정말 중요합니다.
기본 중의 기본이고 전공 수업에서도 가장 기본으로 다루는 내용일거예요
근데 그만큼 기본으로 다루기 때문에 쉽게 넘기도 쉬워요
왜냐하면 대충은 알거든요
하지만 ' 대충 아는 것'과 '정확히 아는 것'은 다릅니다
둘다 '안다'이지만 누군가에게 그것을 설명할 때는 정확히 알아야하기 때문이죠
혼자 개발할 때라면 형상만 기억해도 문제가 안되지만, 면접 대비하시는 분도 계실 것이며
주니어에게 업무를 가르쳐 주는 실무자도 있을 거라고 생각해요.
정확히 알고 멋진 선배, 멋진 면접자가 되자고요.
'임베디드 기초' 카테고리의 다른 글
| 자동차 개발자가 반드시 알아야 할 ROM, RAM, Flash, EEPROM, NVM의 완벽한 차이 (1) | 2025.12.22 |
|---|---|
| 실무자가 반드시 알아야 할 Static, Extern, Global 변수의 완벽한 차이 (3) | 2025.12.19 |
| 🔗 링크(Link)란 무엇인가? (0) | 2025.12.14 |
| 컴파일(Compile)이란 무엇인가? (0) | 2025.12.14 |
| RTOS와 Kernel은 무엇이 다를까? (0) | 2025.12.13 |