Flask 객체를 생성할 때 생각 없이 __name__을 넣는다.
다른 글을 쓰다가 __name__을 왜 넣어야 하는지를 몰라서 이번 글을 적으며 조사했다.





__name__ 이란

파이썬으로 코드를 작성하다 보면 __name__이란 bulit-in 변수를 사용할 때가 있다.
내 생각으로는 가장 많이 사용할 때는 처음 시작하는(entry point) 코드에서 사용을 할 때가 가장 많지 않나 싶다.

python으로 직접 실행 시
처음 실행되는 파일의 __name__ 값은 __main__이 되고 나머지 불러온 값들의 __name__ 값은 파일의 이름이 되는 원리를 이용하여서 주로 사용한다.



그 외에도 blueprint 객체를 생성할 때처럼 사용하는 경우를 많이 보았다.
blueprint의 경우 아래와 같이 설명해 놓았다.

blueprint에서 __name__이 들어가는 인자의 설명을 보면 보통 __name__을 넣으며
이 인자는 root_path를 찾는데 도움을 준다고 한다.

The name of the blueprint package, usually '__name__'.
This helps locate the ``root_path`` for the blueprint.


__name__ 을 왜 사용할까


간단한 파일 2개로 테스트를 해보려고 한다.
폴더 구조는 아래와 같다.

name_test/
	main.py
        wrap1/
        	wrap1.py

각각의 코드는 아래와 같다.



스크립트로 실행 시 결과 값을 보면 아래와 같다.


main파일의 __name__은 자주 사용하는 if 문을 통해서 __main__이 나온다는 것을 알고 있었기에 넘어가고,

import 한 모듈(패키지)의 __name__ 의 경우는
python을 실행시킨 경로에서 해당 파일까지의 경로와 같고, import 구문에서의 경로를 나타내는 것과도 같다.
( ./wrap1/wrap1.py, from wrap1.wrap1 import wrap1fun )
__name__이란 것이 root 경로를 기준으로 패키지의 위치를 알려주는 변수 같다.

결론적으로 __name__을 사용하는 이유는 리소스, 모듈(패키지)등을 찾기 쉽게 하기 위해서 인 것 같다.
스크립트로 직접 실행 시 __name__ 값은 __main__을 받고,
__main__에서 import한 파일들(.py)들은 __main__파일에서의 상대 경로의 값을 가지는 것 같다.

Flask 객체 생성

이제 드디어 글의 목적인 Flask 객체 생성에 대해서 시작하려고 한다.
객체 생성하는 것에 앞서 Flask를 실행하는 두 가지 방법에 대해서 알아보려고 한다.

코드는 아래와 같이 작성했다.
실행은 'python3 app.py'으로 실행하였다.



실행 시 아래와 같이 출력된다.

출력문을 보게 되면 'app'이란 이름으로 서버가 실행이 되었다는 것을 알 수 있다.
이제 __name__값을 보기 위해 요청을 해보자.


요청의 결과 값을 보면 __name__값이 __main__으로 나와있다.
직접 app.py을 실행하였으므로 당연한 결과 같다.

약간 다른 시도를 해볼 생각이다.
app.py이란 파일 이름 대신 다른 이름을 주면 Serving Flask app 'app' 부분에서 app 대신 다른 이름이 나올 겉 같다.

예상대로 이름이 바뀌어서 출력이 되었다.
그런데 바뀌면 안 될 것 같은데 바뀌었다.

Flask객체를 생성할 때 우리는 __name__이란 변수를 넣어서 초기화했다.
그렇다면 애초에 'app'이나 'flask_server'로 이름이 되어있으면 않된다.

이유는 Flask 객체 생성(초기화) 코드를 보면 알 수 있다.
소스 링크 auto_find_instance_path: https://github.com/pallets/flask/blob/2.2.2/src/flask/app.py
소스 링크 find_package: https://github.com/pallets/flask/blob/main/src/flask/scaffold.py

Flask 객체가 생성될 때 import_name을 이용하여 auto_find_instance_path을 호출하고
해당 함수는 find_package함수를 호출하여 경로를 찾는데,
_find_package 함수에서 아래와 같은 코드를 찾을 수 있다.

코드에서 확인 가능한 것처럼 import_name이 __main__으로 넘어올 시 현재 경로를 반환하는 것을 확인 가능하고,
주석에서도 interactive 혹은 main모듈로 초기화 시 이 코드가 실행되는 것을 알려주고 있다.


결론

이것저것 조사해 보니 Flask 객체 생성 시 __name__을 이유를 알 것 같다.
애초에 Flask 코드 내부(flask GitHub코드 110번 줄부터 보면 된다.)
인자에 대하여 설명이 잘되어있다.

첫 번째 인자(import_name)의 리소스와 파일 시스템 때문에 사용된다고 한다.
root 경로부터 모듈(파일, 패키지)을 읽고 리소스, 템플릿들을 읽기 편하게 하기 위해서이다.


번외

flask run ( gunicorn )

Flask 튜토리얼(Quick start)에 나와있는 방법으로, 직접적으로 실행시키지 않는 방법이다.

테스트를 위해 아래와 같이 코드를 작성했다.
flask run으로 실행하면 5000번 포트로 열려있는 서버가 동작한다.







위 코드에서 주목해야 할 것은 app을 실행시키는 명령어(app.run())이 따로 없다는 것이다.
즉 해당 코드가 바로 실행이 되는 게 아니라 import 되어서 실행된다는 것이다.
직접 실행시키는 것과 달리 import 되어서 실행되었다면 __name__값은 해당 파일의 경로 값을 가지게 된다.

특이한 점은 flask run으로 실행하기 위해서는 파일 이름이 app.py 혹은 wsgi.py여야 한다는 것이다.
찾아보니 환경변수 FLASK_APP이 없다면 현재 디렉터리에서 app.py 와 wsgi.py를 찾는다고 한다.

export FLASK_APP && flask run

더욱 신기한 것은 FLASK_APP을 설정하고 실행할 때 발생한다.
FLASK_APP에 현재 경로('.')를 넣고 flask run을 사용 시 아래와 같이 에러가 발생한다.

보면 app 이름이 '.'으로 되어있다.
에러 문구를 보면 flask application 혹은 factory가 없다고 나와있다.
찾아보면 FLASK_APP에 입력한 경로에 __init__. py으로 파일 이름을 변경하면 잘 실행된다고 한다.

번외는 나중에 시간이 되면 따로 글을 쓸 예정이다.


# 2022-10-05 수정

'기록 > 궁금증' 카테고리의 다른 글

[Flask] Flask에 middleware을 넣어보자  (0) 2022.08.23
[Javascript] const, let, var  (0) 2022.08.18