VEDA 주간 학습 정리
[8주차] 리눅스 개발도구 정리
이번 주 학습을 한 문장으로 정리하면: GCC로 소스를 실행 파일로 만들고, make로 빌드를 자동화하며, GDB·Docker·Git으로 개발 흐름을 관리하는 방법을 정리한 주간
작성일: 2026.05.15
이번 주 학습 목표
- GCC가 소스 파일을 실행 파일로 만드는 전체 단계 이해하기
- GDB로 디버깅하는 방법 정리하기
- Makefile을 이용해 반복 빌드와 의존성 관리를 자동화하기
- 정적·공유·동적 라이브러리의 차이와 작성 흐름 익히기
- Docker와 Git을 함께 사용해 개발 환경과 변경 이력을 관리하기
1. 리눅스 개발도구 전체 흐름
이번 주는 리눅스에서 C/C++ 프로그램을 개발할 때 사용하는 도구들을 하나의 흐름으로 연결해 보았다. 소스 코드를 작성한 뒤, 개발 환경을 맞추고, 컴파일과 링크를 거쳐 실행 파일을 만들고, 오류를 추적하고, 결과물을 재사용하거나 버전 관리하는 과정까지 정리했다.
소스 작성
-> Docker로 개발 환경 구성
-> GCC로 컴파일/링크
-> make로 반복 빌드 자동화
-> 라이브러리로 코드 재사용
-> GDB로 오류 추적
-> Git으로 변경 이력 관리
| 도구 | 역할 | 이번 주 정리 포인트 |
|---|---|---|
| GCC | 소스 코드를 실행 파일로 변환한다 | 전처리, 컴파일, 어셈블, 링크 단계 구분 |
| make | 반복되는 빌드를 자동화한다 | 의존 관계와 변경된 파일 기준으로 필요한 부분만 재빌드 |
| GDB | 프로그램 오류를 추적한다 | 브레이크포인트, 변수 확인, 호출 스택, core 분석 |
| Docker | 개발 환경을 이미지와 컨테이너로 고정한다 | 다른 시스템에서도 같은 환경으로 빌드와 실행 |
| Git | 변경 이력을 관리한다 | add, commit, branch, remote 흐름 이해 |
2. GCC로 실행 파일 만들기
GCC는 단순히 C 파일을 실행 파일로 바꾸는 명령어처럼 보이지만, 실제로는 전처리기, 컴파일러, 어셈블러, 링커를 순서대로 호출하는 드라이버 역할을 한다. 이번 주에는 각 단계에서 어떤 중간 결과물이 만들어지는지 확인했다.
hello.c
-> 전처리: hello.i
-> 컴파일: hello.s
-> 어셈블: hello.o
-> 링크: hello
| 단계 | 명령 예시 | 의미 |
|---|---|---|
| 전처리 | gcc -E hello.c -o hello.i |
#include, #define 같은 지시문 처리 |
| 컴파일 | gcc -S hello.i -o hello.s |
전처리된 코드를 어셈블리 코드로 변환 |
| 어셈블 | gcc -c hello.s -o hello.o |
기계어 형태의 오브젝트 파일 생성 |
| 링크 | gcc -o hello hello.o |
오브젝트 파일과 라이브러리를 묶어 실행 파일 생성 |
GCC 옵션 메모
-E, -S, -c는 각 단계까지만 실행할 때 사용한다. 디버깅할 때는 보통 -g -O0 조합이 읽기 좋고, 일반 빌드에서는 -Wall로 경고를 확인하는 습관이 중요하다.
3. GDB로 오류 추적하기
GDB는 프로그램이 실행되는 중간 상태를 직접 확인할 수 있게 해 주는 디버거다. 논리 오류, 세그멘테이션 폴트, 잘못된 변수 값, 호출 스택 문제를 확인할 때 사용한다. 디버깅 정보를 포함하려면 컴파일할 때 -g 옵션을 붙인다.
gcc -g test.c -o test
gdb ./test
| 명령어 | 역할 | 사용 상황 |
|---|---|---|
break |
브레이크포인트 설정 | 특정 줄이나 함수에서 멈추고 싶을 때 |
run |
프로그램 실행 | 디버깅 시작 |
next / step |
한 줄씩 실행 | 함수 내부 진입 여부를 나눠서 추적 |
print |
변수 값 출력 | 현재 값이 예상과 같은지 확인 |
bt |
호출 스택 확인 | 어떤 함수 흐름에서 문제가 났는지 추적 |
프로그램이 비정상 종료될 때 core 파일을 남기면, 종료 당시의 메모리 상태와 호출 스택을 나중에 분석할 수 있다. core 분석에서는 보통 bt로 충돌 지점을 먼저 확인하는 흐름이 유용하다.
4. make와 Makefile
소스 파일이 늘어나면 매번 긴 gcc 명령을 직접 입력하기 어렵다. 어떤 파일이 바뀌었는지 판단해 필요한 부분만 다시 빌드하는 일도 사람이 직접 처리하면 실수하기 쉽다. 이때 make는 Makefile에 적힌 의존 관계를 보고 반복 빌드를 자동화한다.
target: dependency ...
command
target은 만들 결과 파일이거나clean같은 작업 이름이다.dependency는 타깃을 만들기 위해 필요한 파일이다.command는 실제로 실행할 쉘 명령이며 반드시 TAB 문자로 시작해야 한다.- 인수 없이
make를 실행하면 첫 번째 타깃이 기본 타깃으로 실행된다.
| 자동 변수 | 의미 | 활용 포인트 |
|---|---|---|
$@ |
현재 타깃 이름 | 출력 파일명을 반복해서 쓰지 않아도 된다 |
$< |
첫 번째 의존 파일 | %.o: %.c 규칙에서 자주 사용한다 |
$? |
타깃보다 최근에 수정된 의존 파일 목록 | 변경된 파일만 처리할 때 유용하다 |
$* |
확장자를 제외한 현재 타깃 이름 | 파일명 패턴을 다룰 때 사용할 수 있다 |
5. 라이브러리 작성 기법
라이브러리는 재사용 가능한 함수와 코드를 묶어 둔 결과물이다. 이번 주에는 정적 라이브러리, 공유 라이브러리, 동적 라이브러리를 구분하고 각각 어떤 시점에 로드되는지 정리했다.
| 구분 | 확장자 | 로딩 시점 | 특징 |
|---|---|---|---|
| 정적 라이브러리 | .a |
링크 시점 | 필요한 코드가 실행 파일에 포함된다 |
| 공유 라이브러리 | .so |
프로그램 시작 시점 | 여러 프로그램이 같은 라이브러리를 공유할 수 있다 |
| 동적 라이브러리 | .so |
실행 중 필요한 시점 | dlopen(), dlsym()으로 직접 로드한다 |
# 정적 라이브러리 생성과 링크
gcc -c add.c sub.c mul.c div.c
ar -rv libcalc.a add.o sub.o mul.o div.o
gcc -o calc calc.o -L. -lcalc
# 공유 라이브러리 생성과 링크
gcc -fPIC -c add.c sub.c mul.c div.c
gcc -shared -o libcalc.so add.o sub.o mul.o div.o
gcc -o calc calc.o -L. -lcalc
6. Docker와 Git으로 개발 흐름 관리
Docker는 애플리케이션 실행에 필요한 파일, 라이브러리, 설정을 이미지로 묶고 컨테이너로 실행한다. C 개발에서는 컴파일러, make, GDB 같은 도구가 들어 있는 개발 환경을 고정해 두면 다른 시스템에서도 같은 방식으로 빌드할 수 있다.
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y \
build-essential \
gdb \
make \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /workspace
docker build -t my-c-app .로 이미지를 빌드한다.docker run --rm -it ubuntu:24.04 bash로 컨테이너에 진입한다.--mount type=bind를 사용하면 호스트 소스 코드를 컨테이너 안에서 그대로 빌드할 수 있다.- 여러 컨테이너를 함께 관리해야 할 때는
compose.yaml과docker compose를 사용한다.
Git은 작업 내용을 저장하고 되돌아볼 수 있게 해 주는 분산 버전 관리 도구다. C 프로젝트에서는 소스 파일과 Makefile은 관리하고, *.o, *.a, *.so, 실행 파일, core 파일 같은 빌드 결과물은 보통 .gitignore로 제외한다.
git status
git add main.c Makefile
git commit -m "Add initial build files"
git log --oneline
7. 도구를 함께 쓰는 실습 흐름
마지막으로 각 도구를 따로 외우는 것이 아니라 실제 개발 순서 안에서 함께 사용하는 흐름을 정리했다. 예를 들어 계산기 라이브러리를 만든다면 소스 파일과 헤더를 작성하고, Makefile로 정적 라이브러리를 빌드하고, GDB로 실행을 확인한 뒤, Git으로 변경 이력을 남기고, Docker에서 같은 환경으로 다시 빌드해 볼 수 있다.
calc.h
add.c
sub.c
mul.c
div.c
calc.c
Makefile
이번 주 핵심 정리
- GCC는 전처리, 컴파일, 어셈블, 링크 과정을 관리한다.
- make는 의존 관계를 보고 필요한 부분만 다시 빌드한다.
- GDB는 변수 값, 실행 흐름, 호출 스택, core 파일을 분석한다.
- 라이브러리는 코드 재사용을 위한 묶음이며 로딩 시점에 따라 방식이 나뉜다.
- Docker는 개발 환경을 고정하고, Git은 작업 이력을 남긴다.
마무리
이번 주는 리눅스 개발도구를 각각 따로 배우기보다, 하나의 개발 과정 안에서 어떻게 연결되는지 보는 시간이었다. GCC가 실행 파일을 만드는 내부 단계를 이해하고 나니, Makefile이 왜 필요한지와 라이브러리를 왜 따로 묶는지도 더 자연스럽게 이어졌다.
또한 GDB로 문제를 추적하고, Docker로 개발 환경을 고정하고, Git으로 변경 이력을 관리하는 흐름까지 함께 정리하면서 실제 프로젝트에서 쓰이는 기본 도구 체계를 한 번에 훑어볼 수 있었다.