파이썬 기초 강좌 #20 모듈과 패키지 – 2편 (Module and Package – vol.2)

11 분 소요

안녕하세요. 스쿨오브웹의 이상희입니다. 지난 강좌에서 모듈의 사용 방법에 대해서 알아보았는데요, 이번 강좌에는 패키지에 대해서 공부해 보도록 하겠습니다. 지난 강좌를 안 읽으신 분들은 아래의 링크를 클릭하셔서 꼭 지난 강좌를 먼저 읽으시고 이 강좌를 읽어주시기 바랍니다.

파이썬 기초 강좌 #20 모듈과 패키지 – 1편 (Module and Package – vol.1)

모듈이 비슷한 기능을 가진 함수들을 한 파일에 모아놓은 거라면, 패키지는 비슷한 기능을 가진 모듈들을 한 폴더 안에 모아놓은 거라고 생각하시면 됩니다. 파이썬에서의 패키지는 닷(.) 노테이션을 사용해서 모듈을 컴퓨터의 폴더와 같이 계층화하는 방법입니다. 윈도우즈에서는 백슬러시()를 사용해서 폴더의 경로를 표시하고, 리눅스에서는 슬러시(/)를 사용하는 것처럼, 파이썬에서는 패키지의 경로를 닷 기호를 사용해서 표기합니다.

패키지를 사용하는 가장 중요한 이유는 프로그램을 배포하기 위해서인데요, 내가 만든 모듈들을 다른 사람에게 줄 때 그냥 여러개의 파이썬 파일을 주기보다는 폴더 안에 넣어서 예쁘게 이름을 붙여서 주면, 다른 패키지에, 같은 이름의 모듈이 있다고 해도, 같은 이름을 가진 모듈끼리의 충돌을 피할 수 있게 됩니다. 모듈을 사용할 때 모듈 이름 뒤에 점을 붙이고 모듈 안의 함수나 변수에 액세스 하듯이, 패키지도 마찬가지로 패키지 이름 뒤에 점을 붙이고 그 뒤에 사용하고 싶은 모듈 이름을 붙여서 모듈에 액세스를 합니다. 사용자가 설치하는 패키지는 Lib 폴더 아래의 site packges 라는 폴더 안에 설치가 됩니다. 그리고 설치되는 패키지는 패키지의 이름이 붙은 폴더 안에 저장이 되게 됩니다.

pip 명령어로 numpy 패키지를 설치해보겠습니다. numpy라는 패키지가 폴더로 생성되는 것을 볼 수 있습니다. 최상위 폴더인 패키지 폴더안에는 하위 폴더를 만들어 기능별로 모듈을 분류하여 보관할 수가 있는데요, 이 하위 폴더들을 서브 패키지라고 부릅니다.

그럼 지금부터 간단한 패키지를 만들어 보겠습니다. 지난번 강좌와 비슷한 게임 프로그램을 만들기 위해서 다음과 같이 공격 기능을 가진 함수들을 모아서 attack.py 라는 모듈을 만들었습니다.

attack.py
def arrow():
print('[매직: 화살] [데미지: 30]')
 
def fireball():
print('[매직: 파이어볼] [데미지: 50]')
 
def goblin():
print('[유닛: 고블린] [데미지: 20]')
 
def giant():
print('[유닛: 자이언트] [데미지: 60]')

먼저 게임이라는 패키지를 만들어서 어택 모듈을 그 안에 포함시키겠습니다. 파이참에서 패키지를 만들면 자동으로 패키지 폴더와 함께 __init__.py 라는 파일이 생성되는 것을 볼 수 있습니다. 파이썬 인터프레터는 폴더안에 __init__.py 파일을 찾아서 이 폴더가 일반 폴더인지 패키지 폴더인지를 구분합니다. 파이썬 버전 3.3 이상에서 부터는 패키지를 만들 때 __init__.py 파일이 없이도 패키지를 생성할 수 있지만, __init__.py 파일은 패키지를 이니셜라이징할 때 많이 쓰여지기 때문에, 대부분의 패키지에 __init__.py 파일이 있는 것을 볼 수 있으실겁니다. 만약에 게임의 어택 기능이 점점 늘어나면 모듈도 커지게 되니까 공격을 기능의 종류별로 분류하여 다른 모듈로 세분화 해야합니다. 이런 경우에 분류된 모듈들을 다시 서브 패키지라는 폴더안에 모아서 분류합니다.

어택 기능을 종류별로 magicunit이라는 모듈안에 나눠보겠습니다.

magic.py
def arrow():
print('[매직: 화살] [데미지: 30]')
 
def fireball():
print('[매직: 파이어볼] [데미지: 50]')
unit.py
def goblin():
print('[유닛: 고블린] [데미지: 20]')
 
def giant():
print('[유닛: 자이언트] [데미지: 60]')

보통 게임에는 공격 기능만 있는게 아니라, 방어 기능도 있으니, 방어 기능을 가지는 서브 패키지를 만들겠습니다. 물론, 디펜스 서브패키지에 여러가지 모듈이 들어가야 겠지만, 시간 관계상 디펜스 패키지의 모듈은 생성하지 않겠습니다.

지금부터 사용자가 게임이라는 패키지를 자신의 프로그램에 임포트하여 사용하는 예를 보여드리겠습니다. 사용자는 엔트리포인트가 되는 파이썬 파일을 만들고 그 파일 안에서 게임 패키지를 임포트하고 사용하게 되겠죠. 패키지 안의 모듈을 임포트 하려면 앞에서 설명드린 것과 같이 패키지.서브패키지.모듈의 형식을 사용하시면 됩니다.

main.py
import game.attack.magic
 
game.attack.magic.arrow()
콘솔 출력
[매직: 화살] [데미지: 30]

그런데 이렇게 임포트를 하시면 매번 magic 모듈을 사용하기위해 패키지 이름 부터 입력해야 하니, 다음과 같이 as 키워드를 사용하시거나 from 키워드를 사용하시는게 좋습니다.

main.py
# as 키워드 사용
import game.attack.magic as magic
 
magic.arrow()
main.py
# from 키워드 사용
from game.attack import magic
 
magic.arrow()

패키지가 더 복잡해지면, 개발자는 사용자가 패키지안의 모듈을 더 편리하게 액세스 할 수 있게, __init__.py 파일에서 자동으로 임포트하게 해줍니다. 패키지를 임포트하면 __init__.py 파일은 자동으로 실행됩니다. 게임 패키지의 __init__.py 파일에 코드를 추가하고 실행해 해보겠습니다. 게임 패키지를 임포트하니 __init__.py 파일안의 프린트 함수가 출력된 것을 확인했습니다. __init__.py 파일에 다음과 같은 임포트 구문을 넣어 놓으면 모듈이 자동으로 게임 네임스페이스에 로딩되고, 사용자는 어택 서브 패키지 이름을 사용하지 않고도, 게임 패키지 이름을 통해서 매직 모듈과 유닛 모듈에 액세스 할 수 있게 됩니다.

__init__.py
from .attack import magic, unit
main.py
from game import magic, unit
 
magic.arrow()
unit.giant()
콘솔 출력
[매직: 화살] [데미지: 30]
[유닛: 자이언트] [데미지: 60]

함수의 이름만으로 바로 함수에 액세스 할 수 있도록 다음과 같은 코드를 사용할 수도 있습니다.

__init__.py
from .attack.magic import *
from .attack.unit import *
main.py
from game import *
 
arrow()
giant()
콘솔 출력
[매직: 화살] [데미지: 30]
[유닛: 자이언트] [데미지: 60]

이렇게 만들어진 패키지는 사용자에게 배포하기 위해서 setuptools와 같은 라이브러리를 사용해서 패키징을 합니다. setuptools의 사용은 초급 과정에서는 조금 어려우니, 기초과정에서는 여기까지만 이해하셔도 충분하다고 봅니다. setuptools의 사용 방법에 대해서는 고급과정에서 자세히 다루도록 하겠습니다.

지금까지 파이썬 기초강좌 시리즈를 애독해주셔서 대단히 감사합니다. 🚀