Atom?
atom 은 이름이 그대로 값인 상수이다. 타 언어의 Symbol 과 유사한 개념이다.
# 기본적인 형태 :merong :foo_bar # 콜론없이 사용되는 특수한 케이스 true # :true false # :false nil # :nil # 공백이나 특수문자가 있는 경우 :"hello world" :"status:ok" # 모듈명 MyApp.MyModule
위 값들은 전부 atom 이다.
atom 은 특히 아래와 같이 map 의 키값으로 자주 사용된다.
config = %{ host: "localhost", port: 4000, ssl: true }
만약 key 를 string 으로 하려면 아래와 같은 문법으로 작성해야 한다.
config = %{ "host" => "localhost", "port" => 4000, "ssl" => true }
atom 을 키값으로 사용하면 쌍따옴표를 생략할 수 있고, 성능 및 컴파일 시 에러를 발견할 수 있고 또 . 연산자로 value 에 접근이 가능하다는 점 등등의 여러가지 이유로 가능하면 map 사용 시 atom 을 키값으로 사용한다.
하지만 이와같은 이점이 있음에도 Phoenix framework 를 사용하면 presentation layer 의 input (client 에서 전달된 값이 json object 인 경우) 이 전부 string key 를 가지는 map 으로 반환된다. 사실 이는 atom 의 특성 상 어쩔 수 없는 제약사항이다.
그럼 어떤 특성이 외부 input 을 string key based map 으로 받게하는지 알아보자.
Atom 의 특성
- atom 은 GC 대상에서 제외된다. 즉 VM 종료시까지 메모리에 남는다.
- atom 의 최대 갯수는 1,048,576 으로 제한된다. Erlang VM 구동 시 기본 값.
- atom 은 전역적으로 공유된다.
GC 대상이 아니면서 최대로 생성될 수 있는 수가 제한되어 있다는 점이 중요하다.
만약 controller 의 input 처럼 서버 외부에서 전달되는 값을 atom 으로 변환해서 map 을 생성하게 된다면 input 을 무작위로 만들어서 요청을 많이 해버리면 금방 atom table 의 최대치를 넘겨서 erlang VM 을 다운시킬 수 있다.
그렇다면 정말로 외부에서 받는 입력을 atom 으로 변환해야 하는 경우 어떻게 해야하는가?
이럴 땐 String.to_existing_atom 함수를 사용해야 한다.
String.to_existing_atom/1
String 을 Atom 으로 변환하는 함수에는 to_atom / to_existing_atom 이 있다.
to_atom 함수는 검증없이 바로 atom 으로 변환하기 때문에 위험하다.
정 필요한 경우 변환하려는 string 이 이미 atom table 에 존재하는지 확인하고 존재하는 경우에만 변환하고 아닌경우 에러를 발생시키는 to_existing_atom 을 사용해야 한다.
iex> String.to_atom("dynamic_#{:rand.uniform(1000)}") :"dynamic_123" # 매번 새로운 atom 생성 iex> String.to_existing_atom("known_atom") # 존재하지 않으면 ArgumentError ** (ArgumentError) atom :known_atom does not exist