Makefile 의 .PHONY 직접 써보면서 이해하기

Oct 18, 2025

2 min
💬

왜 Makefile은 파일 이름이 타깃과 같으면 명령을 실행하지 않을까요? .PHONY 의 역할을 직접 테스트하며 정리했습니다.

1. 개요

.PHONYMakefile에서 특정 target이 실제 파일이 아니라 실행용 이름임을 make에 알려주는 선언입니다. clean, all, install 같은 명령형 target은 파일을 생성하지 않는 경우가 많기 때문에, .PHONY를 붙이지 않으면 같은 이름의 파일과 충돌하거나 불필요한 탐색이 발생할 수 있습니다.

clean 이라는 이름의 파일이 이미 존재하면, make 는 그걸 최신 파일로 인식해 clean 명령을 실행하지 않습니다.

이 문제를 해결하는 것이 .PHONY 입니다. 핵심 확인 포인트는 다음입니다.

  • .PHONY 가 왜 필요한지, 어떤 문제를 해결하는지
  • .PHONY 유무에 따른 make 의 동작 차이
  • 실제 프로젝트에서 .PHONY 를 활용하는 패턴

2. Phony target 이란? (.PHONY)

Makefile에서 target은 어떤 파일을 만들기 위한 규칙입니다.

MAKEFILE
hello: hello.c
	cc hello.c -o hello

여기서 hello 는 실제로 만들어지는 파일 이름입니다. make 는 이 파일이 없거나 source(hello.c)가 더 최신이면 cc 를 실행합니다. 그런데 "파일을 만드는 게 아닌 명령"을 넣으면 문제가 생깁니다.

MAKEFILE
clean:
	rm *.o

디렉터리에 우연히 clean 파일이 있으면, make 는 "이미 clean 이 최신"이라 판단하고 rm *.o 를 수행하지 않습니다. 이때 쓰는 것이 .PHONY 입니다.

MAKEFILE
.PHONY: clean
clean:
	rm *.o

이렇게 선언하면 clean 은 파일이 아니라 명령 이름(label)으로 인식되어 항상 실행됩니다.

NOTE

.PHONYmake 에게 "이 target 은 실제 파일이 아니라 명령용"이라고 알려주는 선언입니다.

3. .PHONY 테스트

테스트를 위해 두 파일을 만듭니다.

hello.c

C
#include <stdio.h>
 
int main(void) {
    puts("Hello, .PHONY!");
    return 0;
}

Makefile

테스트용 전체 Makefile 보기
MAKEFILE
CC      ?= cc
CFLAGS  ?= -O2 -Wall
 
ifeq ($(USE_PHONY),1)
.PHONY: all run clean
endif
 
all: hello
 
hello: hello.o
	$(CC) $(CFLAGS) -o $@ $^
 
hello.o: hello.c
	$(CC) $(CFLAGS) -c $< -o $@
 
run: hello
	@./hello
 
clean:
	@echo "[clean] removing artifacts..."
	@rm -f hello hello.o
 
## 'clean'이라는 실제 파일을 만들어 충돌 테스트
touch-clean-file:
	@echo "This is a file named 'clean'" > clean

3.1. 기본 동작 테스트

아래 명령들은 정상 동작합니다.

Bash
make clean
## → [clean] removing artifacts...
 
make all
## → cc -O2 -Wall -c hello.c -o hello.o
## → cc -O2 -Wall -o hello hello.o
 
make run
## → Hello, .PHONY!

3.2. .PHONY 없이 충돌하는 상황

Bash
## 'clean' 파일 생성
make touch-clean-file
 
make clean
## → make: 'clean'은(는) 이미 업데이트되었습니다.

make cleanhello, hello.o 를 지워야 하는데 삭제되지 않습니다.

3.3. .PHONY 활성화 후 실행

Bash
make USE_PHONY=1 clean
## → [clean] removing artifacts...

.PHONY 가 활성화되면 clean 파일이 있어도 make clean항상 실행됩니다.

TIP

이 테스트로 .PHONYmake 에게 "이 target 은 실제 파일이 아닌 명령용"임을 알려준다는 점을 직접 확인했습니다.

4. .PHONY 사용이 유용한 경우

여러 디렉터리를 동시에 빌드하는 병렬 build 환경을 가정해봅시다.

MAKEFILE
SUBDIRS = foo bar baz
 
subdirs:
	for dir in $(SUBDIRS); do \
		$(MAKE) -C $$dir; \
	done

이 경우 for 루프가 한 셸에서 순차 실행되어 병렬 빌드(make -j)가 불가능하고, 일부 디렉터리에서 에러가 나도 전체 빌드가 계속됩니다. .PHONY 로 개선할 수 있습니다.

MAKEFILE
SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
 
subdirs: $(SUBDIRS)
 
$(SUBDIRS):
	$(MAKE) -C $@

핵심은 $(SUBDIRS)subdirs의존성(prerequisite)으로 분리한 것입니다. 그러면 make 가 각 디렉터리를 독립 target 으로 인식해 make -j 병렬 빌드가 가능해집니다. 여기서 .PHONYfoo, bar, baz 라는 이름의 디렉터리가 있어도 "이미 최신"으로 판단하지 않도록 보장합니다.

5. 마무리

.PHONYMakefile 에서 다음 역할을 합니다.

역할설명
파일 이름 충돌 방지같은 이름의 파일이 있어도 항상 명령이 실행됨
명령 전용 타깃 지원clean, install, test, run 등 실제 파일이 없는 명령용 target 작성
성능 향상암시적 규칙 탐색을 건너뛰어 불필요한 검사 감소

TIP

실제 파일을 생성하지 않는 target 에는 .PHONY 를 습관적으로 선언하는 것이 좋습니다. 충돌 방지뿐 아니라 make 의 불필요한 탐색도 줄여줍니다.

6. 참고문헌