모듈

모듈 (Modules)

루아 5.4에서 모듈(module)은 일반 테이블이나 함수로 구현돼요. require 함수가 모듈을 로드하고, package 테이블이 모듈을 찾고 로드하는 매커니즘을 관리해요. 모듈은 코드를 캡슐화(capsuleize)하고 재사용하는 기본 단위예요.

require

require (modname)

모듈 modname을 로드해요. 동작은 다음과 같아요.

  1. package.loaded[modname]를 확인해 이미 로드된 모듈이 있으면 그 값을 반환해요 (중복 로드 방지).
  2. package.preload[modname]에 로더(loader)가 있으면 그것을 호출해 모듈을 얻어요.
  3. 그렇지 않으면 package.searchers의 각 검색기(searcher)를 순서대로 실행해 modname에 대응하는 로더(파일 경로 검색 등)를 찾아요.
  4. 찾은 로더를 호출해 모듈 값을 얻고, package.loaded[modname]에 저장한 뒤 반환해요.
  5. 로더가 값을 반환하지 않으면 package.loaded[modname]true로 설정돼요.

require는 사용자 모듈을 로드하기 위한 표준 진입점이에요. 모듈이 로드되면 보통 그 모듈 테이블이 반환돼요.

local mymod = require("mymod")

package 테이블과 로딩 경로

package 테이블은 모듈 로딩을 제어하는 함수와 변수를 가져요.

package.path

package.path

모듈을 찾는 데 쓰이는 경로(루아 소스) 패턴 목록의 문자열이에요. 세미콜론(;)으로 구분된 패턴으로, 각 패턴의 ?modname의 점(.)을 디렉터리 구분자(/ 또는 \)로 바꾼 이름으로 치환돼요. 예: "./?.lua;/usr/local/share/lua/5.4/?.lua".

package.path는 환경 변수 LUA_PATH로 덮어쓸 수 있어요.

package.cpath

package.cpath

C 라이브러리(네이티브 모듈)를 찾는 경로 패턴 목록이에요. package.path와 유사하지만 C 공유 라이브러리(.dll, .so, .dylib)용이에요. LUA_CPATH 환경 변수로 설정 가능해요.

package.preload

package.preload

모듈 이름을 키로, 로더 함수를 값으로 갖는 테이블이에요. require는 이 테이블을 먼저 확인해요. 특별한 모듈을 미리 등록할 때 사용해요.

package.loaded

package.loaded

이미 로드된 모듈을 담는 테이블이에요. require는 여기서 모듈을 찾아 반환해요. package.loaded[modname]nil로 만들면 모듈을 다시 로드할 수 있어요.

package.searchers

package.searchers

require가 사용하는 검색기(searcher) 함수들의 리스트예요. 각 검색기는 modname을 받아 (로더, 추가 데이터)를 반환하거나 nil을 반환해요. 기본 검색기는 다음 순서예요.

  1. package.preload에서 로더 검색.
  2. 루아 파일로 검색 (package.path 사용).
  3. C 라이브러리로 검색 (package.cpath 사용).
  4. 루아 파일에서 C 라이브러리로 (모듈명을 기반으로 한 보조 검색).

package.searchpath

package.searchpath (name, path [, sep [, rep]])

검색 경로 path에서 모듈 name을 찾아 실제 파일 경로를 반환해요. 그 패턴들에서 ?name으로 치환하고, 첫 번째로 존재하는 파일을 반환해요. sep는 이름에서 구분자(기본 .)를, rep는 그 구분자를 치환할 디렉터리 구분자(기본 /)를 지정해요. 못 찾으면 nil과 오류 메시지(모든 시도 경로)를 반환해요.

package.loadlib

package.loadlib (libname, funcname)

C 라이브러리 libname에서 funcname이라는 C 함수를 찾아 반환해요. require처럼 자동으로 모듈을 만들지 않고, 로우 레벨에서 C 함수를 직접 얻을 때 사용해요. 못 찾으면 nil과 오류 메시지를 반환해요.

package.config

package.config

루아 빌드 설정을 담는 문자열이에요. 첫 문자는 디렉터리 구분자(/ 또는 \), 두 번째는 경로 구분자(;), 세 번째는 치환 문자(?) 등을 담고 있어요. 라이브러리 작성자가 환경에 맞춰 경로를 조작할 때 씁니다.

모듈 작성 관례

모듈 파일은 보통 모듈 테이블을 만들어 필드로 함수를 담고, 마지막에 그 테이블을 반환해요.

-- mymod.lua
local M = {}

function M.greet(name)
  return "안녕, " .. name .. "!"
end

return M

사용하는 쪽:

local mymod = require("mymod")
print(mymod.greet("세상"))   --> 안녕, 세상!

로드 과정 세부

require는 모듈을 로드한 뒤 적절한 오류 처리를 해요. 모듈을 찾지 못하면 다음과 같은 오류 메시지를 내요.

module 'xyz' not found:
  no field package.preload['xyz']
  no file './xyz.lua'
  ...

package.loaded[modname]가 이미 모듈 테이블을 갖고 있으면, require는 그 모듈을 다시 실행하지 않고 즉시 반환해요. 이것이 루아 모듈이 "한 번만 실행"되는 보장이에요.

출처: 모듈 (Modules)

본문

require의 중복 방지

requirepackage.loaded를 먼저 확인하므로, 같은 모듈을 여러 번 호출해도 본문은 한 번만 실행돼요. 모듈이 초기화 상태를 유지해야 할 때 이 특성이 중요해요.

경로 변수

package.path·package.cpath는 사용자가 환경 변수(LUA_PATH, LUA_CPATH)로 확장할 수 있고, 각각 루아 소스 모듈과 C 확장 모듈의 검색 위치를 정해요.

확장과 커스터마이즈

package.searchers에 검색기를 추가하면 require의 동작을 확장할 수 있어요. package.preload에 로더를 직접 넣으면 사전 등록 모듈을 만들 수 있어요.

더 알아보기