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

Oct 18, 2025

2 min
💬

clean이라는 파일이 있으면 make는 clean 명령을 실행하지 않는다. .PHONY가 이 충돌을 어떻게 막는지 직접 Makefile을 만들어 확인해 보았고, 실제 프로젝트에서 쓰는 선언 패턴까지 정리했다.

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)를 충분히 활용할 수 없고(각 하위 makefile 내부 target 은 병렬로 빌드되지만 디렉터리는 한 번에 하나씩만 처리됨), 일부 디렉터리에서 에러가 나도 전체 빌드가 계속된다. .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. 참고문헌