# eval is not evil (with LLM)

> 아래의 글을 먼저 읽고 오면 글의 제목과 맥락을 이해하기 쉽습니다.

[Eval is evil - Why we should not use eval in JavaScript](https://dev.to/amitkhonde/eval-is-evil-why-we-should-not-use-eval-in-javascript-1lbh)

## eval()?

 eval() 함수는 다양한 스크립트 언어에서 제공하는 기능으로, 문자열 형태로 작성된 코드를 실행할 수 있는 함수입니다. JavaScript, Python, PHP, Ruby 등에서 공통적으로 존재하며, 문자열을 그대로 가져와서 코드로 실행이 가능한 만큼 강력한 도구이지요. 하지만 그동안 eval은 위의 레퍼런스에서처럼 사악한 사술로 비추어졌습니다.

> python에서는 단일식만 처리가 가능한 eval()과 여러 문의 코드를 처리하는 exec()이 구분됩니다. 하지만, 이 글에서는 같은 맥락의 함수로 여기겠습니다.

## eval() is evil?

이렇게 강력한 도구가 왜 위험한 것을 넘어서, 사용하는 것 자체에도 '사악하다'는 오명이 붙었을까요? 

 가장 큰 문제는 보안 취약점입니다. eval()은 문자열로 전달된 내용을 그대로 실행하는 함수입니다. 그렇기에 입력이 정확하게 통제되어야 안전한 사용임을 전제로 하지요. 이런 함수가 문제가 되는 상황은  어떤 상황일까요? 외부에서 임의로 입력된, 신뢰할 수 없는 데이터를 실행하는 경우입니다. 

 이런 특성은, '사용자를 절대로 믿지말라'고 가르치는 웹 개발에서 특히 문제가 됩니다. 임의의 유저가 어떤 입력을 가져올 지 모르는 일이고, 이를 그대로 믿어서는 안되니깐요. 예를 들어, 사용자가 입력한 문자열을 그대로 eval()로 처리하면, 공격자는 이를 악용해 시스템 명령어를 실행하거나 데이터를 탈취할 수 있습니다. XSS이나 RCE 같은 심각한 공격에 노출될 위험도 있죠.

 문자열로 받은 코드를 실행하기에 생기는 문제도 있습니다. 그대로 실행하기엔 사전에 성능 평가나, 디버깅도 어렵습니다. 문제가 생긴다면, 이를 잘 캐치할 수 없죠. 

 이를 보면, eval() 자체의 문제이기보다는 eval() 함수가 사용되는 환경을 둘러싼 문제에 가깝지 않으신가요? eval() 자체는 강력한 도구이지만, 런타임에서 항상 불안함을 가지고 있던 eval()이었고, 비교적 개방적인 웹 생태계였기에 이런 분위기가 형성된 것이라고 봅니다.

사실 아래와 같은 "eval is evil but not why you may think"와 같은 글에서도 eval 자체의 문제라기 보다는 시스템의 문제이기에 적확하게 사용하면 된다고 하지만, 그럼에도 불구하고 "This is just horrible advice"와 같은 댓글이 달리기도 하죠.

[https://medium.com/mail-online/eval-is-evil-but-not-why-you-may-think-25961f9b01bb](https://medium.com/mail-online/eval-is-evil-but-not-why-you-may-think-25961f9b01bb)

## eval() with llm's output?

그렇다면 왜 글머리의 서두에서는 eval()에 대한 이런 오명을 지워볼려고 했을까요? eval()에 대한 이런 오명을 지워보려는 이유는, eval()이 새로운 가능성을 열어줄 수 있는 강력한 도구이기 때문입니다. LLM과 같이 사용되는 경우, eval()은 특정한 상황에서 강력한 도구로 작용할 수 있습니다.  LLM이 생성한 코드를 실행하는 방식으로, 사용자 입력에 따라 동적으로 로직을 수행하거나, 특정한 목적에 맞는 코드를 실시간으로 생성해 실행할 수 있기 때문입니다. 

기본적으로 코드의 세계는 '인과성'을 기반으로 작동합니다. 선언된 변수가 있고, 데이터가 있으며, 함수들이 이미 있기에 동작하는 방식이지요. 이러한 시스템에서는 실세계의 사람이 요구하는 무한한 요구사항을 동적으로 반영하기 어렵습니다. LLM은 이러한 유저의 '목적성'이 담긴 요청을 처리하기엔 (적어도 지금은) 완벽한 도구이지만, 생성의 결과물이 텍스트라는 단점이 있습니다. 

만약 Prompt engineering으로 위험한 상황에 대해 제약시킨 다음, 안전한 최소권한의 환경에서, 유저의 쿼리를 반영하는 코드를 생성하고 이를 실행할 수 있다면 어떨까요? eval()은 이런 상황을 위한 좋은 인터페이스가 될 수 있으며, 안전하게 통제되는 상황에서 활용될 경우 '인과성'과 '목적성'이 잘 조합되어 운용되는 구현체를 만들 수 있습니다.

아래와 같은, 진짜 심플한 예제처럼 말이지요.

```javascript
Generate a python code to {user_query}

## Constraints
- Don't generate code for anything that could cause fatal or irreversible damage.
- Write code that does exactly that, as long as it doesn't violate any constraints. Don't arbitrarily code 'print()' or add or create code for actions that the user didn't request.

## Output
- Just generate a code content, not other message.

## Output_format
```python
"generated code"
```
```

```javascript
user_query = "ppt 파일을 만들고, 배경을 하늘색으로 꾸미어줘"
prompt = str(prompt).replace("{user_query}", user_query)
inference_result = inference_llm(prompt)

guard_inference_result = guard_inference(inference_result)
if guard_inference_result == "False":
    # Exception 처리 및 응답
    # ...
# 함수 사용 예시
code = remove_python_code_block(inference_result)
exec(code)

```

```javascript
```python
from pptx import Presentation
from pptx.util import Inches
from pptx.dml.color import RGBColor

# Create a presentation object
prs = Presentation()

# Add a slide with a title and content layout
slide_layout = prs.slide_layouts[5]  # Using a blank slide layout
slide = prs.slides.add_slide(slide_layout)

# Set the background color to sky blue
background = slide.background
fill = background.fill
fill.solid()
fill.fore_color.rgb = RGBColor(135, 206, 235)  # Sky blue color

# Save the presentation
prs.save('sky_blue_background_presentation.pptx')
```
```

왜 이것이 가능할까요? 사실 eval()이, 그리고 eval()을 둘러싼 환경이 가지고 있던 단점들을, LLM이 어느정도는 효과적으로 메꿔줄 수 있기 때문입니다. 입력이 불특정한 환경은 생성을 맡은 LLM의 프롬프트로 충분히 그 범위를 한정할 수 있습니다. LLM이 생성하는 코드는 그 목적성에서 불일치할 수는 있어도, 일부러 작동하지 않는 코드를 잘 생성해내지는 않지요. 모듈에 대한 디펜던시도, 어느정도 프롬프트와 생성된 코드에 대한 파싱, 별도의 격리된 가상환경 마련으로 해결이 가능합니다. 이제는 Agent를 통해서 이를 생성하고 실행하고, 실행 이전에 평가도 하여서 복잡도나 보안도 검사하면서 다음 과정으로 넘어갈 수 있지요.

## 그럼에도 남은 과정이 있다면?

 eval()은 강력한 도구지만 항상 실행되는 환경에 대한 강한 통제를 필요로 하며, 예상치 못한 취약점이나 복잡한 사용자 쿼리로 인해 문제가 발생할 가능성이 항상 존재합니다. 따라서 eval()을 사용할 때는 무작정 사용하기보다는 이러한 위험성을 인지하고, 이를 통해 신중하게 관리하며 접근해야 합니다.

1. 

 eval()을 활용하기 위해서는 무엇보다 안전한 환경을 구축하는 것이 필수적입니다. eval()은 실행하는 코드의 위험성을 완전히 제거할 수 없으므로, 문제가 일어나도 전체 시스템에 영향을 주지 않을 수 있는 환경을 구성하는 것이 우선입니다.

1. 

eval()의 보안 우려를 낮추기 위해 Guardrail 설계가 필요합니다. Guardrail은 LLM이 생성한 코드를 실행하기 전에 검증 로직을 추가하는 방식으로 구현할 수 있습니다. 생성하는 프롬프트 자체에서 이를 반영해야 함은 기본중의 기본입니다. 생성 이후에도 LLM 기반 검사, Bandit 라이브러리와 같은 정적검사등을 사용해서 만일 있을지 모르는 위험성을 차단하는 것이 좋습니다.

##  eval()은 좋은 인터페이스가 될 수 있을까?

아직까지는 실험적이지만, 이런 가능성을 염두해두고 저도 eval()을 파이프라인에서 제한적으로 활용해보고 있습니다. eval()을 LLM 파이프라인에서 적용해본 것이 최근에 있어서는 '어떠한 사물도 편견없이 보는' 계기가 되었습니다. 이전에는 사용하기만 해도 욕을 먹어도 마땅한 eval()이, LLM과 함께 사용하면서 외과의사의 메스와 같은 도구로 다가왔습니다. 

지금까지는 커스텀한 API처리, 혹은 non-text 파일을 생성하기 위한 로직 등에 제한적으로 사용하고 있지만, 더 활용해보면서 가능성을 찾아보려고 합니다. 여러분도 LLM과 원하는 결과물 사이에서 구현하기 난감하거나, 인과성이 지배하는 코드에서 다양한 유저의 요구사항을 만족하기 어렵다면, eval()을 이용해서 이를 인터페이스처럼 실험해보시길 권해드립니다.

---

For the site tree, see the [root Markdown](https://slashpage.com/twojay-prompt-engineer.md).
