메타프로그래밍
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
메타프로그래밍은 프로그램을 작성하는 프로그램 기법으로, 런타임 시스템 내부를 노출하거나, 동적 실행을 통해, 또는 컴파일러와 같은 프로그램 변환 시스템을 사용하여 구현된다. 코드 생성, 코드 계측, 도메인 특화 언어(DSL) 구현에 활용되며, Lisp의 매크로, C++의 템플릿 메타프로그래밍 등이 대표적인 예시이다. 스크립트 언어가 메타프로그래밍에 유리하며, Lisp 매크로는 언어 문법 확장을 통해 강력한 프로그래밍 능력을 제공하지만, 남용 시 코드 가독성을 저해할 수 있다. 메타프로그래밍은 강력하지만, 디버깅의 어려움, 시스템 위험 초래 가능성 등의 위험성을 가지고 있다.
더 읽어볼만한 페이지
- 메타프로그래밍 - 템플릿 (C++)
C++ 템플릿은 다양한 자료형에 대해 동일한 코드를 재사용하는 기능으로, 함수, 클래스, 변수 템플릿을 지원하며 C++11부터는 가변 템플릿을 통해 인수의 개수를 유연하게 처리할 수 있지만, 과도한 사용은 코드 이해를 어렵게 하고 컴파일 시간을 증가시키는 등의 단점이 있다. - 메타프로그래밍 - 위생 매크로
위생 매크로는 변수 섀도잉 문제를 해결하여 매크로 확장의 안전성을 보장하며, Scheme과 같은 언어에서 식별자의 어휘적 범위를 보존하고 우발적인 포착을 방지한다. - 프로그래밍 패러다임 - 지식 표현
지식 표현은 컴퓨터가 인간의 지식을 이해하고 활용하도록 정보를 구조화하는 기술이며, 표현력과 추론 효율성의 균형, 불확실성 처리 등을 핵심 과제로 다양한 기법과 의미 웹 기술을 활용한다. - 프로그래밍 패러다임 - 의도적 프로그래밍
의도적 프로그래밍은 프로그래머의 의도를 명확히 포착하고 활용하여 소프트웨어 개발 생산성을 향상시키기 위한 프로그래밍 패러다임으로, 트리 기반 저장소를 사용해 코드 의미 구조를 보존하고, WYSIWYG 환경에서 도메인 전문가와 협업하며, 코드 상세 수준 조절 및 자동 문서화를 통해 가독성과 유지보수성을 높이는 데 중점을 둔다.
메타프로그래밍 | |
---|---|
개요 | |
정의 | 메타프로그래밍은 코드를 데이터로 취급하는 프로그래밍 기법임. |
특징 | 런타임 또는 컴파일 타임에 프로그램의 구조나 동작을 수정하거나 생성할 수 있음. |
활용 | 코드 생성, 최적화, 추상화 수준 향상 등에 활용됨. |
주요 기법 | |
리플렉션 | 런타임에 프로그램의 구조를 분석하고 수정하는 기능. |
매크로 | 컴파일 타임에 코드를 변환하는 기능. |
템플릿 메타프로그래밍 | 컴파일 타임에 템플릿을 사용하여 코드를 생성하는 기법. |
코드 생성 | 프로그램을 사용하여 코드를 생성하는 기법. |
어노테이션 | 코드에 메타데이터를 추가하여 컴파일러나 런타임 환경에서 처리하도록 하는 기법. |
장점 | |
코드 재사용성 증가 | 공통 코드를 생성하여 코드 중복을 줄임. |
생산성 향상 | 반복적인 코딩 작업을 자동화하여 개발 시간을 단축함. |
유연성 증가 | 런타임에 프로그램의 동작을 변경하여 다양한 요구사항에 대응할 수 있음. |
추상화 수준 향상 | 복잡한 코드를 단순화하고 이해하기 쉽게 만듦. |
최적화 | 컴파일 타임에 코드를 최적화하여 성능을 향상시킴. |
단점 | |
코드 복잡성 증가 | 메타프로그래밍 코드는 일반 코드보다 이해하기 어려울 수 있음. |
디버깅 어려움 | 생성된 코드는 소스 코드와 다를 수 있어 디버깅이 어려울 수 있음. |
컴파일 시간 증가 | 컴파일 타임 메타프로그래밍은 컴파일 시간을 증가시킬 수 있음. |
런타임 성능 저하 | 런타임 메타프로그래밍은 런타임 성능을 저하시킬 수 있음. |
활용 예시 | |
ORM (Object-Relational Mapping) | 데이터베이스 테이블과 객체 간의 매핑을 자동으로 생성. |
AOP (Aspect-Oriented Programming) | 관심사 분리를 통해 코드의 모듈성을 높임. |
DSL (Domain-Specific Language) | 특정 도메인에 특화된 언어를 만들어 생산성을 높임. |
프로그래밍 언어 지원 | |
지원 언어 | C++, C#, Java, Python, Ruby, JavaScript, Lisp, Smalltalk 등 다양한 언어에서 지원. |
관련 용어 | |
관련 용어 | 리플렉션, 매크로, 템플릿 메타프로그래밍, 코드 생성, AOP, DSL |
2. 접근 방식
메타프로그래밍은 일반적으로 다음 세 가지 방식으로 작동한다.[6][7]
# 응용 프로그래밍 인터페이스(API)를 통해 런타임 시스템 (엔진)의 내부를 프로그래밍 코드에 노출한다.
# JavaScript와 같이 인수나 컨텍스트를 사용하여 문자열로 구성된 프로그래밍 명령을 동적 실행한다.
# 언어 밖으로 완전히 벗어난다.
2. 1. API를 통한 런타임 시스템 내부 노출
메타프로그래밍의 한 가지 방식은 .NET 공용 중간 언어(CIL) 이미터와 같은 응용 프로그래밍 인터페이스(API)를 통해 런타임 시스템(엔진)의 내부를 프로그래밍 코드에 노출하는 것이다.[6]2. 2. 동적 실행
JavaScript와 같은 언어는 인수나 컨텍스트를 사용하여 문자열로 구성된 프로그래밍 명령을 동적 실행하는 방식을 지원한다.[6] 이러한 방식을 통해 "프로그램이 프로그램을 작성하는" 것이 가능해진다.2. 3. 언어 외부 접근
언어 설명을 받아 해당 언어에 대한 임의 변환을 수행하는 컴파일러와 같은 범용 프로그램 변환 시스템을 사용하는 방식이다.[7] 이를 통해 해당 대상 언어에 자체적인 메타프로그래밍 기능이 있는지 여부에 관계없이 메타프로그래밍을 사실상 모든 대상 언어에 적용할 수 있다. Scheme이 C에서 직면한 일부 제한 사항을 Scheme 언어의 일부인 구문을 사용하여 C를 확장함으로써 해결하는 방식을 볼 수 있다.[7]3. 활용
일반적으로, 스크립트 언어는 메타프로그래밍에 능하다고 여겨진다. 컴파일 언어는 실행 전에 소스 코드를 일괄 변환한다는 특성상, 번역과 실행을 반복하는 스크립트 언어보다 실행 시의 인터럽트나 변환의 자유도가 낮다.[16]
Lisp의 매크로는 대표적인 메타프로그래밍의 예시이다. Lisp는 데이터와 코드가 모두 S식으로 표현되지만, 매크로를 사용하면 S식이 언어 처리계에 해석되기 전에 다른 S식으로 변환할 수 있다.
예를 들어
```lisp
(defstruct point (x 0) (y 0))
```
라는 코드를 통해
- 구조체 정의 point형
- 생성자 make-point (생략 시 초기값은 0, 0)
- 접근자 point-x point-y
- 복제 copy-point
- 술어 point-p
가 자동으로 생성된다.
이는 "자동 생성이 언어 내장 기능이 아닌 Lisp의 매크로로 기술되어 있으며, 필요하다면 유사한 메커니즘을 프로그래머가 정의할 수 있기" 때문에 메타프로그래밍이라고 불린다. 이는 사실상 언어 문법의 확장과 같으며, 매우 강력한 프로그램 능력을 얻게 된다. 반면, 매크로는 임의의 어휘를 대체할 수 있기 때문에, 전개된 코드가 언어의 문법에서 벗어날 가능성이 있다는 것을 의미한다. 따라서 일반적으로 필요하지 않은 메타프로그래밍은 피해야 한다.
Lisp의 S식이 Lisp 폼이 되기 위해서는 "S식의 첫 번째 요소는 (함수, 매크로, 특수 폼) 중 하나여야 한다."라는 규칙이 있지만,
```lisp
(defmacro backwards (expr) (reverse expr))
```
를 정의하면
```lisp
(backwards ("hello,world" t format))
```
와 같이 작성할 수 있다.
이는 Lisp의 매크로가 언어 사양을 변경하고 독자적인 문법을 만들어냈다고 볼 수 있다. 그러나 이러한 문법은 코드의 가독성을 해칠 우려가 있는 불필요한 메타프로그래밍의 예시이다.
메타프로그래밍의 다른 예로는 C++에서의 "템플릿 메타프로그래밍" 등이 있다.
3. 1. 코드 생성
프로그램이 실행 중에 다른 프로그램이나 코드 조각을 생성하는 방식이다. 예를 들어, 유닉스 셸 스크립트를 사용하여 특정 패턴의 코드를 반복적으로 생성할 수 있다.다음은 생성적 프로그래밍의 예시인 간단한 POSIX 셸 스크립트 메타프로그램이다.
```bash
#!/bin/sh
# 메타프로그램
echo '#!/bin/sh' > program
for i in $(seq 992)
do
echo "echo $i" >> program
done
chmod +x program
```
이 스크립트(또는 프로그램)는 1부터 992까지의 숫자를 출력하는 993줄짜리 새 프로그램을 생성한다. 이것은 코드를 사용하여 더 많은 코드를 작성하는 방법을 보여주는 예시일 뿐이며, 숫자 목록을 출력하는 가장 효율적인 방법은 아니다. 그럼에도 불구하고, 프로그래머는 이 메타프로그램을 1분 이내에 작성하고 실행할 수 있으며, 그 시간 안에 1000줄 이상의 코드를 생성하게 된다.
3. 2. 코드 계측
메타프로그래밍은 동적 프로그램 분석을 수행하기 위해 프로그램을 계측하는 데 사용될 수 있다.3. 3. 도메인 특화 언어 (DSL)
메타프로그래밍은 특정 도메인에 특화된 언어(DSL, Domain-Specific Language)를 생성하여 해당 도메인의 문제를 보다 쉽게 해결할 수 있도록 돕는다. Lex와 Yacc는 DSL을 활용한 대표적인 예시이다.[16]4. 프로그래밍 언어별 활용
여러 프로그래밍 언어에서 매크로 시스템을 제공한다. 대표적인 언어로는 Lisp, Clojure, Common Lisp, Racket, Scheme (위생적 매크로), MacroML, Template Haskell, Scala, Nim, Rust, Haxe, Julia, Elixir 등이 있다.[16]
일반적으로 스크립트 언어는 메타프로그래밍에 능하다고 여겨진다. 컴파일 언어는 실행 전에 소스 코드를 일괄 변환한다는 특성상, 번역과 실행을 반복하는 스크립트 언어보다 실행 시의 인터럽트나 변환의 자유도가 낮다.
C++의 "템플릿 메타프로그래밍"도 메타프로그래밍의 한 예시이다.
Common Lisp[10], 파이썬, NIL, Groovy, 루비, 스몰토크, Lua에서 메타클래스를 제공한다.
C++ 템플릿[11], D, Common Lisp, Scheme와 쿼시인용("백쿼트") 연산자를 사용하는 대부분의 Lisp 방언[12], Nim 등에서 템플릿 메타프로그래밍을 지원한다.
단계적 메타프로그래밍을 지원하는 언어는 다음과 같다.
- MetaML
- MetaOCaml
- Scala https://docs.scala-lang.org/scala3/reference/metaprogramming.html 네이티브 또는 Lightweight Modular Staging Framework 사용[13][14]
- http://terralang.org Terra
종속 타입을 사용하면 생성된 코드가 유효하지 않은 경우가 없음을 증명할 수 있다.[15] 그러나 이 접근 방식은 최첨단 기술이며 연구용 프로그래밍 언어 외에서는 거의 찾아볼 수 없다.
4. 1. 매크로 시스템
Lisp, Clojure, Common Lisp, Racket, Scheme (위생적 매크로), MacroML, Template Haskell, Scala, Nim, Rust, Haxe, Julia, Elixir 등 다양한 언어에서 매크로 시스템을 제공한다.[16]일반적으로 스크립트 언어는 메타프로그래밍에 능하다고 여겨진다. 컴파일 언어는 실행 전에 소스 코드를 일괄 변환한다는 특성상, 번역과 실행을 반복하는 스크립트 언어보다 실행 시의 인터럽트나 변환의 자유도가 낮다.
C++의 "템플릿 메타프로그래밍" 등도 메타프로그래밍의 다른 예시이다.
4. 1. 1. Lisp 매크로
Lisp의 매크로는 대표적인 메타프로그래밍의 예시이다. Lisp에서 데이터와 코드는 모두 S식으로 표현되는데, 매크로를 사용하면 S식이 언어 처리계에 해석되기 전에 다른 S식으로 변환될 수 있다.[16]예를 들어, 다음과 같은 코드를 통해
(defstruct point (x 0) (y 0))
다음 항목들이 자동으로 생성된다.
- 구조체 정의 point형
- 생성자 make-point (생략 시 초기값은 0, 0)
- 접근자 point-x, point-y
- 복제 copy-point
- 술어 point-p
이는 "자동 생성이 언어 내장 기능이 아닌 Lisp 매크로로 기술되어 있으며, 필요하다면 유사한 메커니즘을 프로그래머가 정의할 수 있기" 때문에 메타프로그래밍이라고 불린다. 이는 사실상 언어 문법을 확장하는 것과 같으며, 매우 강력한 프로그램 작성 능력을 제공한다.
하지만, 매크로는 임의의 어휘를 대체할 수 있으므로 전개된 코드가 언어 문법에서 벗어날 가능성이 있다. 따라서 일반적으로 불필요한 메타프로그래밍은 피해야 한다.
예를 들어, 문자열을 정형화하여 출력하는 format 함수는 다음과 같이 사용한다.
(format t "hello,world")
Lisp의 S식이 Lisp 폼이 되기 위해서는 "S식의 첫 번째 요소는 (함수, 매크로, 특수 폼) 중 하나여야 한다." 그러나 다음과 같이 `backwards` 매크로를 정의하면,
(defmacro backwards (expr) (reverse expr))
다음과 같이 작성할 수 있다.
(backwards ("hello,world" t format))
이는 Lisp 매크로가 겹따옴표("")로 묶은 언어 사양을 변경하고 독자적인 문법을 만들어낸 것으로 볼 수 있다. 그러나 이 문법은 통상적인 Lisp에서 기대되는 구성이 아니며, 코드 가독성을 해칠 우려가 있는 불필요한 메타프로그래밍의 예시이다.
4. 2. 매크로 어셈블러
IBM/360 및 파생 기종은 강력한 매크로 어셈블러 기능을 갖추고 있어, 완전한 어셈블리 언어 프로그램 또는 프로그램의 일부(예: 서로 다른 운영 체제용)를 생성하는 데 자주 사용되었다. CICS 트랜잭션 처리 시스템과 함께 제공되는 매크로는 COBOL 문을 전처리 단계로 생성하는 어셈블러 매크로를 갖추고 있었다.MASM과 같은 다른 어셈블러도 매크로를 지원한다.
4. 3. 메타클래스
Common Lisp[10], 파이썬, NIL, Groovy, 루비, 스몰토크, Lua에서 메타클래스를 제공한다.4. 4. 템플릿 메타프로그래밍
C++ 템플릿[11], D, Common Lisp, Scheme와 쿼시인용("백쿼트") 연산자를 사용하는 대부분의 Lisp 방언[12], Nim 등에서 템플릿 메타프로그래밍을 지원한다.일반적으로, 스크립트 언어는 메타프로그래밍에 능하다고 여겨진다. 컴파일 언어는 실행 전에 소스 코드를 일괄 변환한다는 특성상, 번역과 실행을 반복하는 스크립트 언어보다 실행 시의 인터럽트나 변환의 자유도가 낮다.
대표적인 메타프로그래밍의 예는 Lisp의 매크로이다. Lisp는 데이터와 코드가 모두 S식으로 표현되지만, 매크로를 사용하면 S식이 언어 처리계에 해석되기 전에 다른 S식으로 변환할 수 있다. 예를 들어,
```lisp
(defstruct point (x 0) (y 0))
```
라는 코드를 통해
- 구조체 정의 point형
- 생성자 make-point (생략 시 초기값은 0, 0)
- 접근자 point-x, point-y
- 복제 copy-point
- 술어 point-p
가 자동으로 생성된다.
이것이 메타프로그래밍이라고 불리는 이유는 "자동 생성이 언어 내장 기능이 아닌 Lisp의 매크로로 기술되어 있으며, 필요하다면 프로그래머가 유사한 메커니즘을 정의할 수 있기" 때문이다. 이는 사실상 언어 문법의 확장과 같으며, 매우 강력한 프로그램 능력을 얻게 된다. 반면, 매크로는 임의의 어휘를 대체할 수 있기 때문에, 전개된 코드가 언어의 문법에서 벗어날 가능성이 있다는 것을 의미한다. 따라서 일반적으로 필요하지 않은 메타프로그래밍은 피해야 한다.
예를 들어, 문자열을 정형화하여 출력하는 format 함수
```lisp
(format t "hello,world")
```
를 사용한 예[16]가 있다. Lisp의 S식이 Lisp 폼이 되기 위해서는 "S식의 첫 번째 요소는 (함수, 매크로, 특수 폼) 중 하나여야 한다." 그러나
```lisp
(defmacro backwards (expr) (reverse expr))
```
를 정의하면 다음과 같이 쓸 수 있다.
```lisp
(backwards ("hello,world" t format))
```
이것은 Lisp의 매크로가 위의 겹따옴표 「」로 묶은 언어 사양을 변경하고, 독자적인 문법을 만들어냈다고도 생각할 수 있다. 그러나 이 문법은 통상의 Lisp에서 기대되는 구성이 아닌 "기술대로 동작하지 않는 프로그램"의 한 예이며, 코드의 가독성을 해칠 우려가 있는 불필요한 메타프로그래밍이다.
메타프로그래밍의 다른 예로는 C++에서의 "템플릿 메타프로그래밍" 등이 있다.
4. 5. 단계적 메타프로그래밍
다음은 단계적 메타프로그래밍을 지원한다.- MetaML
- MetaOCaml
- Scala https://docs.scala-lang.org/scala3/reference/metaprogramming.html 네이티브 또는 Lightweight Modular Staging Framework 사용[13][14]
- http://terralang.org Terra
4. 6. 종속 타입
종속 타입을 사용하면 생성된 코드가 유효하지 않은 경우가 없음을 증명할 수 있다.[15] 그러나 이 접근 방식은 최첨단 기술이며 연구용 프로그래밍 언어 외에서는 거의 찾아볼 수 없다.5. 위험성 및 과제
일부에서는 메타프로그래밍 기능을 완벽하게 사용하려면 가파른 학습 곡선이 필요하다고 주장한다.[8] 메타프로그래밍은 런타임 시 더 많은 유연성과 설정 가능성을 제공하므로, 메타프로그래밍을 오용하거나 잘못 사용하면 일반 개발자가 디버깅하기 매우 어려운 불필요하고 예상치 못한 오류가 발생할 수 있다. 신중하게 사용하지 않으면 시스템에 위험을 초래하고 취약하게 만들 수 있다. 메타프로그래밍의 잘못된 사용으로 인해 발생할 수 있는 일반적인 문제 중 일부는 컴파일러가 누락된 구성 매개변수를 식별할 수 없거나, 잘못되었거나 부정확한 데이터로 인해 알 수 없는 예외 또는 다른 결과가 발생할 수 있다는 것이다.[9] 이로 인해 일부에서는[8] 숙련된 개발자만이 언어 또는 플랫폼에서 메타프로그래밍을 사용하는 기능을 개발해야 하며, 일반 개발자는 이러한 기능을 관례의 일부로 사용하는 방법을 배워야 한다고 믿는다.
하지만 메타 프로그래밍이 강력한 수단인 만큼 그에 따른 위험성도 이해해야 한다.
다음은 자바스크립트에서의 메타 프로그래밍 예시이다.
```javascript
const add = new Function(..."xy", "return x + y");
add(2, 3); // => 5
```
이 예시에서는 문자열로부터 함수 `add`를 생성한 후, 그 함수를 이용하여 계산을 수행하고 있다.
하지만 이처럼 무분별한 사용을 할 경우, `Function` 생성자에 전달하는 인수를 잘못 입력하는 것만으로도 '''파괴적이고 치명적인 결과'''를 초래할 수 있다.
그래서 자바스크립트에서는 "기존 기능을 확장하는 것"에 주력한 Proxy, Reflect라는 객체를 제공하고 있다. 이처럼 목적에 맞는 수단으로 사용함으로써 위험성을 최소화하면서 강점을 최대화할 수 있다.
참조
[1]
웹사이트
Course on Program Analysis and Transformation
https://handbook.uni[...]
2014-09-18
[2]
서적
Generative Programming
Addison Wesley
2000
[3]
웹사이트
The Art of Metaprogrmming in Java
https://newcircle.co[...]
2014-01-28
[4]
웹사이트
Programming Concepts: Type Introspection and Reflection
https://thesocietea.[...]
2014-09-14
[5]
웹사이트
What Is Metaprogramming? – Part 2/2
https://prateekvjosh[...]
2014-08-14
[6]
웹사이트
Rdoc for Class: BasicObject (Ruby 1.9.3) - instance_eval
http://www.ruby-doc.[...]
2011-12-30
[7]
웹사이트
Art of Metaprogramming
http://www.ibm.com/d[...]
[8]
웹사이트
The challenge of metaprogramming
http://www.ianbickin[...]
2016-09-21
[9]
웹사이트
Beware of Metaprogramming
https://medium.com/@[...]
Medium Corporation
2014-08-21
[10]
문서
Through Common Lisp Object System's "Meta Object Protocol"
[11]
웹사이트
C++ Template Metaprogramming
http://aszt.inf.elte[...]
2022-07-23
[12]
문서
Lisp (programming language) "Self-evaluating forms and quoting", quasi-quote operator.
[13]
웹사이트
LMS: Program Generation and Embedded Compilers in Scala
https://scala-lms.gi[...]
2017-12-06
[14]
간행물
Lightweight Modular Staging: A Pragmatic Approach to Runtime Code Generation and Compiled DSLs
http://infoscience.e[...]
2012-06
[15]
간행물
Ur: statically-typed metaprogramming with type-level record computation
http://adam.chlipala[...]
2012-08-29
[16]
웹사이트
Practical Common Lisp
https://archive.org/[...]
Internet Archive
2005-04-11
[17]
웹인용
Rdoc for Class: BasicObject (Ruby 1.9.3) - instance_eval
http://www.ruby-doc.[...]
2011-12-30
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com