파이썬의 eval() 함수는 문자열 형태로 작성된 파이썬 표현식을 파싱한 뒤, 프로그램 내에서 실제로 실행하고 그 결과를 반환하는 내장 함수입니다. 문자열로 된 수식이나 표현식을 동적으로 처리해야 할 때 유용하게 사용됩니다.
eval() 기본 문법
eval() 함수의 일반적인 문법은 다음과 같습니다.
eval(expression, globals=None, locals=None)
매개변수 설명
expression: 필수 매개변수입니다. 실행할 파이썬 표현식이 담긴 문자열을 지정합니다.
globals: 선택 매개변수입니다. 딕셔너리 형태로, eval()에서 사용 가능한 전역 심볼을 지정합니다. 명시하지 않으면 기본 빌트인(builtins)이 그대로 사용되며,
{'__builtins__': None}으로 설정하면 내장 함수 접근을 차단할 수 있습니다.locals: 선택 매개변수입니다. eval() 내부에서 사용할 지역 변수와 함수를 지정합니다.
이제 다양한 예제를 통해 eval() 함수를 더 깊이 이해해 보겠습니다.
가장 간단한 eval() 예제
str1 = 'Hello'
print(eval('str1 == "Hello"'))
print(eval('str1 + ", Python"'))실행 결과
True Hello, Python
첫 번째 줄은 비교 표현식을 평가하여 True를 반환하고, 두 번째 줄은 문자열 연결 결과인 Hello, Python을 출력합니다.
사용자 입력과 함께 eval() 활용하기
eval()은 사용자가 입력한 수학 표현식을 즉시 계산하는 용도로도 활용할 수 있습니다.
from math import *
for i in range(1, 3):
func = input("평가할 수학 함수를 입력하세요:\n")
try:
print(eval(func))
except Exception as ex:
print(ex)
break
print('Done')실행 결과
평가할 수학 함수를 입력하세요: abs(-42) 42 평가할 수학 함수를 입력하세요: max(12, 3, 4, 2, 17) 17 Done
try-except 블록으로 감싸 잘못된 입력이 들어와도 프로그램이 비정상 종료되지 않도록 처리한 점에 주목하세요.
globals와 locals 이해하기
eval()에 어떤 함수와 변수를 넘겨줄지 제대로 이해하려면, 먼저 현재 스코프에 존재하는 전역 및 지역 심볼 목록을 확인하는 것이 좋습니다.
from math import *
def square_root(n):
return sqrt(n)
# 현재 전역 심볼 테이블을 딕셔너리로 출력
print('전역 심볼 목록:\n', globals())
# 현재 지역 심볼 테이블을 딕셔너리로 출력
print('\n지역 심볼 목록:\n', locals())
# 현재 지역 스코프의 이름 목록 출력
print('\n현재 지역 스코프의 이름들:\n', dir())실행 결과
전역 심볼 목록:
{'__name__': '__main__',
'__doc__': None,
'__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__spec__': None, '__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'acos': <built-in function acos>,
'acosh': <built-in function acosh>,
'asin': <built-in function asin>,
'ceil': <built-in function ceil>,
'cos': <built-in function cos>,
'degrees': <built-in function degrees>,
'exp': <built-in function exp>,
'factorial': <built-in function factorial>,
'floor': <built-in function floor>,
'gcd': <built-in function gcd>,
'hypot': <built-in function hypot>,
'log': <built-in function log>,
'log10': <built-in function log10>,
'log2': <built-in function log2>,
'pow': <built-in function pow>,
'sin': <built-in function sin>,
'sqrt': <built-in function sqrt>,
'tan': <built-in function tan>,
'trunc': <built-in function trunc>,
'pi': 3.141592653589793,
'e': 2.718281828459045,
'tau': 6.283185307179586,
'inf': inf,
'nan': nan,
'square_root': <function square_root at 0x055246F0>
}
...(math 모듈의 나머지 함수들도 동일한 방식으로 포함됨)모듈 최상위 레벨에서는 전역 스코프와 지역 스코프가 같기 때문에 globals(), locals(), dir()의 결과가 사실상 동일하게 나타납니다.
globals를 빈 딕셔너리로 지정하기
globals 값을 빈 딕셔너리로 넘기면 eval() 내부에서 접근 가능한 이름이 크게 제한됩니다.
>>> print(eval('dir()', {}))
['__builtins__']빈 딕셔너리를 전달했음에도 __builtins__만 남아 있는 이유는, 파이썬이 globals에 빌트인 정보를 자동으로 삽입하기 때문입니다.
특정 함수만 허용하는 안전한 eval() 사용법
다음 예제는 math 모듈의 함수 중 일부만 선택적으로 노출하여 eval()의 사용 범위를 제어하는 방법을 보여줍니다.
from math import *
for i in range(1, 4):
func = input("평가할 수학 함수를 입력하세요.\n허용된 함수: abs(x), pow(x,y):\n")
try:
print(eval(func, {'abs': abs, 'pow': pow}))
except Exception as ex:
print(ex)
break
print('Done')실행 결과
평가할 수학 함수를 입력하세요. 허용된 함수: abs(x), pow(x,y): abs(-74) 74 평가할 수학 함수를 입력하세요. 허용된 함수: abs(x), pow(x,y): pow(2,7) 128.0 평가할 수학 함수를 입력하세요. 허용된 함수: abs(x), pow(x,y): sqrt(47) name 'sqrt' is not defined Done
globals 딕셔너리에 'sqrt'가 포함되어 있지 않기 때문에 마지막 입력에서는 NameError가 발생하는 것을 확인할 수 있습니다. 이처럼 eval()에 전달하는 globals를 직접 구성하면 실행 환경을 원하는 대로 제한할 수 있습니다.
eval() 사용 시 보안 주의사항
eval()은 문자열 그대로 코드를 실행하기 때문에, 신뢰할 수 없는 외부 입력을 그대로 전달하면 시스템 명령 실행이나 민감한 데이터 유출 등 심각한 보안 취약점으로 이어질 수 있습니다. 예를 들어 eval("__import__('os').system('rm -rf /')")처럼 악성 코드가 실행될 위험이 있습니다.
따라서 실무에서는 다음 원칙을 지키는 것이 좋습니다.
외부 입력에는 eval() 대신
ast.literal_eval()을 우선 고려합니다. 이 함수는 문자열, 숫자, 리스트, 딕셔너리 등 리터럴만 평가하므로 훨씬 안전합니다.eval()을 꼭 사용해야 한다면, 허용할 함수와 변수만 담은 globals/locals를 명시적으로 지정해 접근 범위를 최소화합니다.
사용자 입력을 eval()에 직접 전달하는 구조는 피하고, 불가피하다면 화이트리스트 검증을 반드시 수행합니다.
마무리
eval()은 문자열 표현식을 동적으로 실행할 수 있는 강력한 도구이지만, 그만큼 위험도가 큰 함수입니다. 간단한 계산기나 학습용 스크립트에서는 편리하게 활용할 수 있으며, 운영 환경에서는 반드시 globals/locals를 제어하거나 ast.literal_eval() 같은 안전한 대안을 사용하는 것이 바람직합니다.