Elixir: alias vs import

Elixir 에서 모듈이 외부의 다른 모듈 또는 함수를 참조하는 키워드로 aliasimport 가 있다.

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 를 통해 코드 축약을 기대할 수 있을 것 같다.

댓글 남기기