네이버 이웃 추가 / GitHub Profile / 카카오톡 채널 추가 / 방명록 / 이용 안내

makefile 만들고 사용하는 방법

수성컴 | 2023. 01. 07.
makefile 만들고 사용하는 방법

수성컴전자방입니다. C언어나 C++로 코딩을 하고 빌드할 때 Visual Studio를 못 쓴다면 보통 gcc나 g++를 많이 쓰실 것입니다. 그런데 파일이 여러 개로 나뉘어 있는 경우에는 명령어 하나 하나 입력해 가면서 컴파일 하고 링킹 하기가 어렵습니다. 그래서 Make라는 것을 이용합니다. 오늘을 Make 사용법을 알려드리겠습니다.

목차

1. gcc, g++, Make 설치
2. 예제 소스코드
3. makefile 작성(기초적인 방법)
4. makefile에서 변수 사용하기
5. makefile wildcard 함수(*)
6. makefile patsubst 함수(%)
7. makefile pattern rule
8. GCC 의존성 옵션(-MMD, -MP)
9. makefile에서 wildcard(*)와 pattern rule(%) 사용하여 C언어 빌드(종합)
10. make clean 구현하기(리눅스 한정)
11. 빌드 후 *.o 자동 삭제 구현하기(리눅스 한정)

12. Visual Studio Code에서 makefile 사용하기
12.1. Windows
12.2. Linux

13. 글 마무리
14. 참고 자료

* (2026. 09. 12. 수정)목차 번호가 바뀌었으니 참고하시기 바랍니다.

1장과 3~12장은 Ubuntu 22.04가 설치된 가상환경에서 진행하고, 12장은 Windows 11 PC와 WSL에서 각각 진행하겠습니다. 2번 문단은 완전 공용입니다. 착오 없으시길 바랍니다. 운영체제별 차이점에 대해서 따로 설명 드리겠습니다.

1. gcc, g++, Make 설치

  • Ubuntu에서 gcc 설치 명령어: sudo apt install gcc
  • Ubuntu에서 g++ 설치 명령어: sudo apt install g++
  • Ubuntu에서 make 설치 명령어: sudo apt install make
  • Windows에서 gcc, g++, make를 사용하려면 MinGW-w64 하나만 설치하시면 됩니다.(관련글(https://sooseongcom.com/post/MinGW-w64-HowToInstall))

1.1. C언어 컴파일을 하려면 gcc가 설치되어 있어야 하고, C++ 컴파일을 하려면 g++가 설치되어 있어야 합니다. 독자 여러분들도 이 정도는 이미 준비하셨겠지만, 여기서 한 번 알려드립니다.
1.2. (리눅스 사용자 한정)Make를 설치합니다.

2. 예제 소스코드

2.1. main.c

#include <stdio.h>
#include "header.h"
 
int main(void)
{
    int a=1, b=2, add;
    double ave;
    point dot={1, 2};
 
    add=sum(a, b);
    printf("Sum is %d\n", add);
 
    ressum(&a, &b, &add);
    printf("Sum is %d\n", add);
 
    ave=average(a, b);
    printf("Average is %f\n", ave);
 
    resaverage(&a, &b, &ave);
    printf("Average is %f\n", ave);
 
    dot.x++; dot.y++;
    printf("점을 x방향으로 1, y방향으로 1 평행이동: (%f, %f)", dot.x, dot.y);
 
    return 0;
}

2.2. func1.c

int sum(int a, int b)
{
    return a+b;
}
 
void ressum(int* a, int* b, int* result)
{
    *result=*a+*b;
    return;
}

2.3. func2.c

double average(int a, int b)
{
    return (double)(a+b)/2;
}
 
void resaverage(int* a, int* b, double* result)
{
    *result=(double)(*a+*b)/2;
    return;
}

2.4. header.h

#ifndef HEADER
    #define HEADER
 
typedef struct _point{
    double x;
    double y;
} point;
 
int sum(int a, int b);
void ressum(int* a, int* b, int* result);
double average(int a, int b);
void resaverage(int* a, int* b, double* result);
 
#endif

makefile 소스코드는 3~7번 문단에서 보여드리겠습니다.

3. makefile 작성(기초적인 방법)

자, 이제 makefile 작성 방법을 차근차근 배워 봅시다.
파일명은 makefile입니다. 확장명은 따로 없습니다.
2번 문단대로 main.c, func1.c, func2.c, header.h가 있는 상황이라고 가정하고 예제 코드를 보여드리겠습니다.

run: main.o func1.o func2.o
	gcc -g -o run main.o func1.o func2.o

main.o: main.c header.h
	gcc -g -c main.c

func1.o: func1.c header.h
	gcc -g -c func1.c

func2.o: func2.c header.h
	gcc -g -c func2.c

(참고: Windows로 진행하실 경우 ‘run’ 대신 ‘run.exe’를 쓰시기 바랍니다. 리눅스에서는 보통 실행파일의 확장명을 쓰지 않습니다.)
하나하나 설명 드리겠습니다.

[Line 1]
run: main.o func1.o func2.o
가장 먼저 등장하는 이름이 run이죠? 그렇기 때문에 일단 Line 1~2를 실행합니다.
Make는 재귀적으로 동작합니다. 콜론(:) 뒤를 보시면 main.o func1.o func2.o가 있죠? 이 파일들은 각각 Line 4, 7, 10의 target입니다(target이 무엇인지는 후술합니다). 그러면 일단 Line 4부터 봅시다.

[Line 4]
main.o: main.c header.h
콜론 뒤에 main.c header.h가 있습니다. 이것은 main.c와 header.h 중에 수정된 파일이 있으면 Line 5를 실행하겠다는 뜻입니다.

[Line 5]
gcc -g -c func1.c
평범한 gcc 명령어입니다. func1.c를 컴파일(-c)하되, 디버깅이 가능한 옵션(-g)을 주었습니다.

Line 4~5와 같은 원리로 Line 7~8, Line 10~11도 작동합니다. 이 모든 것이 진행된 다음 Line 1~2를 다시 보겠습니다. Line 1에서 콜론 뒤에 있는 항목들을 다 확인했으니 Line 2로 갑니다.

[Line 2]
gcc -g -o run main.o func1.o func2.o
역시 다중 파일 컴파일을 해 보셨다면 평범하게 느끼실 명령어입니다. main.o, func1.o, func2.o를 링크하여 run이라는 실행파일을 만듭니다.

이제 make를 실행해 보겠습니다.

make실행전
왼쪽과 같이 파일들이 있는 상황입니다. 이 파일들이 있는 경로로 터미널을 실행한 뒤 make 명령을 실행합니다.

  • Linux에서: make
  • Windows에서: mingw32-make

운영체제 별로 명령어 차이가 있으니 주의하시기 바랍니다. 이 글에서 따로 언급 안 해도 Linux에서는 make이고, Windows에서는 mingw32-make입니다.

make실행후
컴파일과 링킹이 모두 완료되어 run 파일이 생성된 것을 보실 수 있습니다. 터미널에서 ./run (Windows에서는 .\run.exe)을 입력하여 실행하실 수 있습니다.

make재실행
소스 코드 수정 없이 make를 한 번 더 실행하면 빌드가 진행되지 않습니다. 수정 사항이 있어야 빌드가 진행됩니다.

★★★makefile의 기초적인 작성 방법은 아래와 같다고 보시면 됩니다.★★★

<Target>: <prerequisites>
    <Recipe>

이러한 것을 ‘Rule’이라고 합니다.

  • target: 빌드 대상 이름입니다. 보통 이 rule에서 생성되는 파일의 이름(과 확장명)으로 합니다.
  • prerequisite: target을 만들기 위해 input으로 사용되는 파일입니다.
  • recipe: 이 rule이 작동할 때 실행될 명령어들입니다. 위의 예제에서는 한 줄씩만 썼지만, 사실 여러 줄에 걸쳐서 여러 명령어들을 쓸 수 있습니다.

이번 문단에서는 이 정도를 알아 가시면 됩니다.

4. makefile에서 변수 사용하기

이제 본격적으로 make의 기능을 활용해 봅니다. 변수를 정의해서 써 보겠습니다. 일단 예제 코드 드립니다.

CC=gcc
OBJS=main.o func1.o func2.o
FLAG=-g
EXEC=run

$(EXEC): $(OBJS)
	$(CC) $(FLAG) -o $@ $(OBJS)

main.o: main.c header.h
	$(CC) $(FLAG) -c main.c

func1.o: func1.c header.h
	$(CC) $(FLAG) -c func1.c

func2.o: func2.c header.h
	$(CC) $(FLAG) -c func2.c

이 코드는 3번 문단의 코드와 같은 기능을 하는 코드입니다. 차이점은 변수를 사용했다는 것뿐입니다. Line 1~4가 변수 지정 부분이고, Line 6부터 make가 실행된다고 보시면 됩니다.

[Line 1~4 변수 지정]
변수 정의는 평범하게 변수명 = 내용 으로 하시면 됩니다.

[Line 6~16]
변수는 $로 시작합니다. 우리가 직접 정의한 변수를 사용하려면 $(변수명)으로 사용합니다. 예를 들어 예제에서 $(EXEC)을 쓴 것은 run을 쓴 것과 같습니다.

우리가 정의한 것 외에 이미 정의되어 있는 ‘자동 변수(Automatic Variable)’가 있습니다. 몇 가지만 알아보겠습니다.

  • $@: 해당 rule의 target
    • 예를 들어 예제의 Line 7에 있는 $@는 $(EXEC)을 의미하고, 이는 곧 Line 4에 정의한 대로 run을 의미합니다.
  • $<: 해당 rule의 첫 번째 prerequisite
  • $^: 해당 rule의 prerequisites 전체
  • $?: 해당 rule의 prerequisites 전체 중 변경된 것들

makefile변수사용
make를 실행한 모습입니다.

5. makefile wildcard 함수(*)

파일이 여러 개일 때 그 파일명을 모두 makefile에 작성하기 귀찮습니다. 이것을 wildcard로 해결할 수 있습니다. 이름은 거창해 보이지만 사실 *입니다.

(2026. 09. 12. 수정)
makefile을 만들 때 Linux에서 기본 제공하는 wildcard를 사용하면 변수에 저장하거나 컴파일 최적화하는 데 문제가 생깁니다. 따라서 make에서 제공하는 wildcard 함수를 사용하는 것이 좋습니다.

예) $(wildcard *.c)

wildcard비교
위의 비교 표에서 알 수 있듯 makefile의 wildcard 함수를 사용해야 컴파일 시간을 절약할 수 있습니다.

6. makefile patsubst 함수

makefile로 자동화할 때 wildcard 함수만으로는 부족합니다. pattern rule을 함께 사용하면 좋습니다. 우선 patsubst 함수부터 살펴보겠습니다. patsubst 함수는 문자열에서 특정 형태를 일괄적으로 변경하는 함수입니다.

사용 방법) $(patsubst from, to, 원래문자열)
fromto%.o, %.c 같은 형태로 %를 사용하여 작성합니다.

예제와 함께 살펴보겠습니다.

SRCS=$(wildcard *.c)
OBJS=$(patsubst %.c, %.o, $(SRCS))

[Line 1]
5.2절에서 살펴본 wildcard 함수입니다. *.c 형태의 파일 목록을 변수(배열?) SRCS에 저장합니다.

[Line 2]
SRCS 배열에서 .c로 끝나는 것들을 .o의 형태로 모두 바꾸어 OBJS에 저장합니다.

7. makefile pattern rule

<Target>: <prerequisites>를 %.o: %.c와 같은 형태로 쓰면 수정된 .c 파일에 대해서만 작업을 수행할 수 있습니다.

pattern rule vs. wildcard
Pattern rule을 사용할 때는 @? 대신 @< 를 사용한다는 점에 유의합니다. @.o와 수정된 @.c가 1:1로 대응되기 때문에 해당 rule의 첫 번째 prerequisite를 가져오는 $<로 접근 가능하다고 생각하시면 될 것 같습니다.

8. GCC 의존성 옵션(-MMD, -MP)

gcc의 옵션으로 -MMD와 -MP 옵션을 사용하여 의존성 파일을 생성할 수 있습니다.

  • -MMD: 컴파일 시 의존성 파일 생성 ⇒ *.d
  • -MP: *.d 파일을 생성할 때 가짜 대상 추가(header 파일이 삭제되었을 때 발생하는 make 에러를 방지)

사용 방법) gcc -MMD -MP -o 파일명.o -c 파일명.c

이 옵션을 사용하면 파일명.d 파일이 생성되며, 이 파일은 makefile 문법으로 작성되어 있습니다. 따라서 makefile에서 -include 파일명.d로 불러올 수 있습니다.

9. makefile에서 wildcard(*)와 pattern rule(%) 사용하여 C언어 빌드(종합)

5~8장의 내용을 종합하여 만들어 본 만능 makefile입니다.
(물론 C언어 말고 C++ 빌드를 하실 것이라면 C++에 맞게 수정하셔야 합니다.^^)

CC=gcc
SRCS=$(wildcard *.c)
OBJS=$(patsubst %.c, %.o, $(SRCS))
DEPS = $(patsubst %.c, %.d, $(SRCS))
EXEC=run
FLAG=-g -MMD -MP

all: $(EXEC)
-include $(DEPS)

$(EXEC): $(OBJS)
	$(CC) $(FLAG) -o $@ $^

%.o: %.c
	$(CC) $(FLAG) -c $<

[Line 1]
CC=gcc
C언어 컴파일러로 gcc를 사용합니다.

[Line 2]
SRCS=$(wildcard *.c)
*.c 형태의 파일 목록을 변수(배열?) SRCS에 저장합니다.

[Line 3]
OBJS=$(patsubst %.c, %.o, $(SRCS))
SRCS 배열에서 .c로 끝나는 것들을 .o의 형태로 모두 바꾸어 OBJS에 저장합니다.
OBJS에 Wildcard를 사용할 경우 첫 컴파일 때 *.o 파일들이 없으면 어떤 파일들로 linking을 해야 할지 make tool이 인식을 못 합니다. 따라서 본 예제처럼 OBJSpatsubst 함수를 사용하여 설정합니다.

[Line 4]
DEPS = $(patsubst %.c, %.d, $(SRCS))
SRCS 배열에서 .c로 끝나는 것들을 .d의 형태로 모두 바꾸어 DEPS에 저장합니다.

[Line 5]
EXEC=run
실행파일명은 “run”으로 하겠습니다.

[Line 6]
FLAG=-g -MMD -MP
gdb 디버거를 사용하기 위한 flag인 -g를 추가했습니다.
-MMD-MP는 의존성 파일 생성을 위해 추가했습니다.

[Line 8] 첫 규칙(make 실행 시 동작)
all: $(EXEC)
make 명령을 실행하면 맨 처음 규칙부터 실행하게 되어 있습니다. 그러나 저는 9행에서 -include를 먼저 할 것이기 때문에 $(EXEC)으로 넘겨 주었습니다.

[Line 9]
-include $(DEPS)
DEPS에 해당하는 파일들의 내용이 9행 지점에 삽입됩니다.
맨 앞의 -DEPS에 해당하는 파일들 중 없는 파일을 무시하라는 뜻입니다.

[Line 11]
$(EXEC): $(OBJS)
OBJS들을 사용하여 linking하는 과정을 Line 11~12에 작성할 것입니다.

[Line 12]
$(CC) $(FLAG) -o $^
Linking할 때는 모든 object file(*.o)들을 사용해야 하므로 $^을 사용합니다.

[Line 14]
%.o: %.c
C code(.c) 중 생성/수정된 파일에 한해서 작업을 수행합니다.

[Line 15]
$(CC) $(FLAG) -c $<
C code(.c) 중 생성/수정된 파일을 컴파일합니다. $?를 쓰지 않았지만 이미 C code(.c) 중 생성/수정된 파일이 prerequisite으로 들어왔기 때문에 $<를 써도 됩니다.

빌드 예시
C code(*.c)의 경우 컴파일이 진행되지 않은 파일, 생성/수정된 파일에 대해서만 컴파일을 진행하고, header.h 수정 시 해당 헤더 파일을 include하는 C code만 재컴파일하는 것을 볼 수 있습니다.

10. make clean 구현하기(리눅스 한정)

빌드한 과정에서 생성된 object code들(*.o)과 결과물(run)을 삭제하는 기능을 구현해 보겠습니다. 리눅스 명령어 rm을 사용합니다.

CC=gcc
SRCS=$(wildcard *.c)
OBJS=$(patsubst %.c, %.o, $(SRCS))
DEPS = $(patsubst %.c, %.d, $(SRCS))
EXEC=run
FLAG=-g -MMD -MP

all: $(EXEC)
-include $(DEPS)

$(EXEC): $(OBJS)
	$(CC) $(FLAG) -o $@ $^

%.o: %.c
	$(CC) $(FLAG) -c $<

clean:
	rm *.o *.d
	rm $(EXEC)

9장의 소스 코드에서 Line 17~19만 추가되었습니다.
make 명령을 실행하면 맨 처음 target인 $(EXEC), 즉 run부터 보게 됩니다. 이는 *.o를 호출합니다. 그런데 *.o는 clean을 호출하지 않습니다. 그러므로 clean이 실행되지 않습니다! 그러면 어떻게 clean을 사용할 수 있을까요?

바로 make 명령어를 입력할 때 make clean으로 실행하는 것입니다!!

make clean 실행 전
make clean을 실행하기 전입니다.

make clean 실행 후
make clean을 실행한 모습입니다. object code들과 실행파일이 모두 삭제되었습니다.

사실 Windows Powershell도 rm 명령어가 있긴 한데 Windows Powershell은 rm 명령어에 *을 사용할 수가 없어서 이 문단을 적용할 수 없습니다.

11. 빌드 후 *.o 자동 삭제 구현하기(리눅스 한정)

빌드하면 실행파일만 생성되도록 해 보겠습니다. 구현 방법은 빌드 후에 object code를 모두 삭제하는 것입니다. 그런데 이 기능을 쓰면 보기에는 예쁠지 몰라도 코딩 과정에서 make를 실행할 때마다 처음부터 컴파일 해야 하므로 추천하지는 않습니다.(본인 상황에 맞게 사용하세요.)

CC=gcc
EXEC=run
FLAG=-g

$(EXEC): *.o
	$(CC) $(FLAG) -o $@ *.o
	rm *.o

*.o: *.c *.h
	$(CC) $(FLAG) -c *.c

[Line 5]
$(EXEC): *.o
콜론 뒤의 *.o를 호출합니다.

[Line 9]
*.o: *.c *.h
콜론 뒤의 *.c *.h를 호출합니다. makefile에 *.c, *.h가 target인 rule이 없으므로 폴더 내에서 *.c, *.h 중 수정된 파일이 있으면 Line 10을 실행합니다.

[Line 10]
$(CC) $(FLAG) -c *.c
이 부분은 실행했을 때 gcc -g -c *.c로 동작합니다.

[Line 6]
$(CC) $(FLAG) -o $@ *.o
이 부분은 실행했을 때 gcc -g -o run *.o로 동작합니다.

[Line 7]
rm *.o
이 명령어가 그대로 실행됩니다. 해당 폴더 내에서 확장명이 .o인 파일이 모두 삭제됩니다.

make 자동삭제 실행 전
make 실행 전 모습입니다.

make 자동삭제 실행 후
make를 실행한 모습입니다. 실행파일만 생겼습니다.

12. Visual Studio Code에서 makefile 사용하기

12.1. Windows

F5누름
12.1.1. 소스코드가 있는 폴더에 makefile을 넣습니다.
12.1.2. C code를 연 상태에서 F5를 누릅니다. 12.1.3. C++ (GDB/LLDB)를 클릭합니다.

구성선택
12.1.4. C/C++: gcc.exe 활성 파일 빌드 및 디버그를 클릭합니다.

오류발생
12.1.5. 오류가 발생합니다. 중단을 클릭합니다.

.vscode 폴더
12.1.6. .vscode 폴더 안에 tasks.json이 생겼습니다. 이 파일을 수정합니다.

{
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: gcc.exe 활성 파일 빌드",
            "command": "mingw32-make",
            "args": [
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "디버거에서 생성된 작업입니다."
        }
    ],
    "version": "2.0.0"
}

12.1.7. .vscode 폴더 안에 아래의 내용으로 launch.json을 만듭니다.

{
    // IntelliSense를 사용하여 가능한 특성에 대해 알아보세요.
    // 기존 특성에 대한 설명을 보려면 가리킵니다.
    // 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요.
    "version": "0.2.0",
    "configurations": [
        {
            "name": "gcc.exe - 활성 파일 빌드 및 디버그",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}\\run.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "miDebuggerPath": "C:\\Program Files\\mingw64\\bin\\gdb.exe",
            "setupCommands": [
                {
                    "description": "gdb에 자동 서식 지정 사용",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "C/C++: gcc.exe 활성 파일 빌드"
        }
    ]
}

VSCodeBuild
12.1.8. F5를 누르면 빌드가 잘 진행됩니다.
(출력 결과가 안 보인다면 터미널 탭을 클릭하세요.)

VSCodeDebugging
VS Code에서 Breakpoint를 설정했다면 디버깅도 가능합니다.

12.2. Linux

(2026. 09. 12. 수정. 참고로 스크린샷은 WSL에서 찍었습니다.)

F5누름
12.2.1. 소스코드가 있는 폴더에 makefile을 넣습니다.
12.2.2. C code를 연 상태에서 F5를 누릅니다.

구성선택
12.2.3. C/C++: gcc 활성 파일 빌드 및 디버그를 클릭합니다.

오류발생
12.2.5. 오류가 발생합니다. 중단을 클릭합니다.

.vscode 폴더
12.2.6. .vscode 폴더 안에 tasks.json이 생겼습니다. 이 파일을 수정합니다.

{
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: gcc 활성 파일 빌드",
            "command": "make",
            "args": [
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "디버거에서 생성된 작업입니다."
        }
    ],
    "version": "2.0.0"
}

기본 생성된 파일에서 6행을 “make”로 바꾸고, 7행 쯤 있는 args들을 지워 준 것입니다.

12.2.7. .vscode 폴더 안에 아래의 내용으로 launch.json을 만듭니다.

{
    // IntelliSense를 사용하여 가능한 특성에 대해 알아보세요.
    // 기존 특성에 대한 설명을 보려면 가리킵니다.
    // 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요.
    "version": "0.2.0",
    "configurations": [
        {
            "name": "gcc.exe - 활성 파일 빌드 및 디버그",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}/run",   //Linux는 "${fileDirname}/run"
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "miDebuggerPath": "/usr/bin/gdb",
            "setupCommands": [
                {
                    "description": "gdb에 자동 서식 지정 사용",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "C/C++: gcc 활성 파일 빌드"
        }
    ]
}

VSCodeBuild
12.2.8. F5를 누르면 빌드가 잘 진행됩니다.
(출력 결과가 안 보인다면 터미널 탭을 클릭하세요.)

VSCodeDebugging
VS Code에서 Breakpoint를 설정했다면 디버깅도 가능합니다.

13. 글 마무리

제 글을 읽어 주셔서 감사합니다. 다음에 만나요!

14. 참고 자료

1) 이승준. 2018. “[Make 튜토리얼] Makefil 예제와 작성 방법 및 기본 패턴”, TUWLAB. (2023. 01. 06. 방문). https://www.tuwlab.com/ece/27193
2) 슬기야놀자. 2021. “[VSCode / C / C++] VSCode에서 Makefile 사용하기”, 슬기야 코딩하자. (2023. 01. 06. 방문). https://urakasumi.tistory.com/281
3) GNU. n. d. “GNU make 4.4, 10.5.3, 10.5.4”, GNU make 문서. (2023. 01. 06. 방문). https://www.gnu.org/software/make/manual/html_node/index.html#SEC_Contents
4) Herdsman 외 1명. 2020. “why my makefile works with asterisk as wildcard, but do not with percentage?”, stackoverflow. (2023. 01. 06. 방문). https://stackoverflow.com/questions/59804167/why-my-makefile-works-with-asterisk-as-wildcard-but-do-not-with-percentage
5) 윤정도. 2022. “[Linux/Makefile] 2. 패턴 규칙”, 무과금 액티브. (2023. 01. 06. 방문). https://blog.naver.com/reversing_joa/222840966671
6) 즐거운 개발자. 2023. “Makefile 작성 - 함수 사용(wildcard, patsubst..)”, IT 개발자의 낙서장. (2026. 09. 12. 방문). https://blog.naver.com/jckim1988/223278345764
7) quokkalover. 2026. “[Makefile] %(percent symbol)의 의미”, 쿼카러버의 기술 블로그. (2026. 09. 12. 방문). https://etloveguitar.tistory.com/48
8) minjungk. 2022. “[Makefile] Makefile 의존성 처리”, 팔만코딩경. (2026. 09. 12. 방문). https://80000coding.oopy.io/1c295f7b-cfbc-41df-8e8a-23a66f6266d3
9)