코드가 코드를 이해하고, 바꾸고, 만들고, 실행까지 한다고?
메타 프로그래밍이란?
우리 보통 코드를 짜면 그 코드가 어떤 "기능"을 하잖아요?
근데 메타 프로그래밍은 좀 다릅니다.
“코드를 조작하는 코드를 짜는 것”
즉, 코드를 대상으로 삼는 코드예요.
예를 들어,
- 함수를 분석하고
- 새로운 함수를 만들어내고
- 어떤 클래스의 속성을 수정하고
- 또는 아예 새로운 클래스를 만들어내기도 하고요.
말장난 같다고요?
사실 이런 거예요
// 일반적인 코드
const double = (x) => x * 2;
// 메타적인 접근
const makeMultiplier = (n) => (x) => x * n;
const triple = makeMultiplier(3); // 코드가 코드를 만듦
왜 굳이 그렇게 복잡하게?
메타 프로그래밍이 필요한 이유는 딱 세 가지예요
- 자동화: 반복되는 패턴을 코드로 줄일 수 있음
- 유연함: 상황에 따라 코드 자체를 바꿔서 실행 가능
- 프레임워크 만들기: 데코레이터, 리플렉션 등으로 동작 제어 가능
예를 들어 ORM 같은 라이브러리들은
우리 코드 속 클래스나 필드를 분석해서 SQL 쿼리문을 자동 생성하잖아요?
그게 바로 메타 프로그래밍이에요.
정적 vs 동적 메타 프로그래밍
구분 | 설명 | 예시 언어 |
정적 | 코드 작성 중(컴파일 타임)에 동작 | C++, Rust, Haskell |
동적 | 코드 실행 중(런타임)에 동작 | JavaScript, Python, Ruby |
쉽게 말하면,
- 정적: “미리 알아야 돼” (코드 짤 때 결정)
- 동적: “그때 가봐야 알아” (실행하면서 판단)
어떤 기법이 있는가?
기법 | 예시 | 설명 |
리플렉션 | Object.keys(obj) | 객체나 클래스의 구조를 실행 중 조사 |
매크로 | C에서 #define | 코드 텍스트 자체를 바꾸는 전처리 |
데코레이터 | Python의 @login_required | 함수나 클래스에 기능을 입힘 |
코드 생성 | eval("2 + 2") | 문자열로 코드 만들고 실행 |
동적 타입/클래스 생성 | Python의 type() | 런타임에 새로운 클래스 만들기 |
AST 조작 | Babel, ESLint | 코드의 구조(트리)를 직접 바꿔치기 |
언어별 예시
JavaScript
eval()
,new Function()
,Proxy
,Reflect
- 프론트에서 동적으로 컴포넌트 만들거나, 권한 체크도 메타프로그래밍 덕분
Python
@데코레이터
,type()
으로 클래스 만들기- Flask 같은 프레임워크가 라우팅 해주는 것도 사실 메타프로그래밍
Java
Class.forName()
,Method.invoke()
- 스프링이 DI나 AOP 할 수 있는 이유도 리플렉션 덕분
C++
- 템플릿 메타 프로그래밍, 매크로, 컴파일 타임 코드 생성
- 무겁고 어렵지만 컴파일된 코드는 엄청 빠름
장점 vs 단점
장점
- 반복 줄이기: 코드를 코드로 만들 수 있으니 반복 X
- 동적/자동화된 로직: 설정만 바꾸면 코드가 달라짐
- 프레임워크 구현: 추상화 끝판왕
단점
- 복잡도: 코드가 코드처럼 안 보임
- 디버깅 헬: 어디서 뭐가 생성됐는지 모를 수 있음
- 보안 이슈:
eval()
잘못 쓰면 재앙
언제 쓰면 좋고, 언제 피해야 하나?
써야 할 때
- 코드가 너무 반복적일 때
- 사용자 설정에 따라 로직 바꿔야 할 때
- 프레임워크나 유틸 만들 때
피해야 할 때
- 단순한 기능인데 괜히 복잡하게 만들려 할 때
- 협업 중인데 나만 이해하고 다른 사람 다 못 알아들을 때
- 디버깅이 중요한 시스템 (e.g. 은행, 병원 등)
마무리: 이게 진짜 개발자 스킬업의 문턱
메타 프로그래밍을 이해하고 쓰기 시작하면,
“내가 쓰는 도구”에서
“도구를 만드는 사람” 으로 한 단계 올라선 거예요.
복잡하긴 하지만, 분명히 재밌고 쓸모 있고, 성장의 계단이 될 수 있어요.
이제는 함수나 클래스만 짜지 말고,
"코드를 다루는 코드" 도 한 번쯤 생각해보는 건 어때요?
728x90