Elixir 에서 모듈이 외부의 다른 모듈 또는 함수를 참조하는 키워드로 alias 와 import 가 있다.
defmodule MyApp.Context do
alias MyApp.Repo
alias MyApp.Users.User
import Ecto.Query, only: [from: 2, where: 3]
def list_active_users do
from(u in User, where: u.active == true)
|> Repo.all()
end
end
둘 다 외부 모듈을 참조할수 있게 하는 키워드인 것 같은데 언제 alias 를 쓰고 언제 import 를 쓸까?
alias?
alias 는 이름과 같이 모듈에 별칭을 지어줄 수 있다. 다음은 Elixir School 에서 예로 든 alias 코드이다.
defmodule Sayings.Greetings do
def basic(name), do: "Hi, #{name}"
end
defmodule Example do
alias Sayings.Greetings
def greeting(name), do: Greetings.basic(name)
end
# 별칭을 사용하지 않는 경우
defmodule Example do
def greeting(name), do: Sayings.Greetings.basic(name)
end
별칭을 사용하지 않으면 모듈의 Namespace 를 정확히 지정(Sayings.Greetings)해야 하지만 alias 를 사용하면 Greetings 로도 해당 모듈에 접근할 수 있다.
아래와 같이 아예 다른 별명을 붙여 사용할 수도 있다.
defmodule Example do
alias Sayings.Greetings, as: Hi
def print_message(name), do: Hi.basic(name)
end
import?
import 는 기본적으로 모듈의 모든 함수와 매크로를 불러오지만 일부만 불러올 수 도 있다.
iex> last([1, 2, 3])
** (CompileError) iex:9: undefined function last/1
iex> import List
nil
iex> last([1, 2, 3])
3
iex> import List, only: [last: 1]
iex> first([1, 2, 3])
** (CompileError) iex:13: undefined function first/1
iex> last([1, 2, 3])
3
iex> import List, except: [last: 1]
nil
iex> first([1, 2, 3])
1
iex> last([1, 2, 3])
** (CompileError) iex:3: undefined function last/1
alias 와는 다르게 모듈 이름을 지정하지 않고 함수를 바로 사용할 수 있다.
import 가 모듈 기능을 다 불러올 수 있으면 import 만 쓰면 되지 않나? 하는 의문이 들 수 있다. 그 의문에 대해 Claude Opus 4.1 이 대답 해준 내용이 깔끔해서 일부를 첨부한다.
핵심 차이점
alias: 모듈 이름을 단축하거나 변경
- 참조시 별칭을 붙여 모듈 이름을 바꿀 수 있음
- 함수 호출 시 모듈명 필요 (
A.function())
import: 함수/매크로를 현재 네임스페이스로 가져옴
- 모듈명 없이 직접 함수 호출 가능 (
function()) - 컴파일러가 함수 이름을 원본 모듈로 자동 연결
분리한 이유
1. 네임스페이스 오염 방지
# import를 남용하면
import Module1
import Module2
import Module3
some_function() # 어느 모듈의 함수인지 불명확
2. 코드 가독성과 추적성
# alias 사용 - 출처가 명확
alias MyApp.Users.UserService, as: Users
Users.create_user(params) # 모듈이 명시되어 있음
# import 사용 - 출처가 불명확
import MyApp.Users.UserService
create_user(params) # 어디서 온 함수인지 즉시 알기 어려움
3. 선택적 가져오기의 유연성
# Ecto.Query의 경우 - DSL처럼 사용하고 싶을 때
import Ecto.Query, only: [from: 2, where: 3]
# 일반 모듈은 alias로 충분
alias MyApp.Accounts
Accounts.get_user(id)
4. 충돌 관리
defmodule MyModule do
# 두 모듈에 같은 이름의 함수가 있을 때
alias ModuleA
alias ModuleB
ModuleA.process(data) # 명확함
ModuleB.process(data) # 명확함
# import 하면 충돌 발생
# import ModuleA
# import ModuleB # process/1 충돌!
end
실무 관례
일반적으로:
- alias: 대부분의 모듈 참조에 사용
- import: DSL 스타일 API (Ecto.Query 등)나 자주 사용하는 헬퍼 함수에만 제한적 사용
defmodule MyApp.Context do
# 모듈은 alias
alias MyApp.Repo
alias MyApp.Users.User
# DSL은 import
import Ecto.Query, only: [from: 2, where: 3]
def list_active_users do
from(u in User, where: u.active == true)
|> Repo.all() # Repo는 alias로 참조
end
end
alias 는 사용처에서 이름도 직접 지정할 수 있고 기본적으로 모듈 이름을 명시적으로 사용해야 하는 반면 import 는 참조에 있어서 더 유연한 기능을 제공하지만 모듈 이름을 사용하지 않기 때문에 alias 에 비해 좀 더 암묵적인 특성이 있다. 기본적으로 alias 를 주로 사용하겠지만 상황에 따라 DSL 같이 함수 이름 자체가 특정 모듈을 확정할 수 있다면 import 를 통해 코드 축약을 기대할 수 있을 것 같다.