- 변수와 상수
- 상수와 심볼
- 심볼과 문자열
- 루비온레일스에서의 심볼 사용예
변수와 상수
변수는 특정 데이터의 메모리상의 위치를 말한다. 따라서 아래와 같이 변수에 문자열 "abc"를 할당하게 되면,
var = "abc"
String 클래스가 "abc"값으로 초기화 되어 문자열 객체를 만들게 된다. 이 때 변수 var는 이 객체의 메모리상의 위치를 가리키게 되는 것이다.
우리는 이 변수 var를 참조할 때마다 var가 가리키는 메모리상의 위치에 있는 객체의 값을 반환하게 되는 것이다.
>> var = "abc" #=> "abc" >> var #=> "abc" >> var.class #=> String >> var.object_id #=> 70287904281360 >> var.object_id #=> 70287904281360 >> var.object_id #=> 70287904281360
따라서, var라는 변수는 문자열 "abc"를 가지고 있지만 실제로는 var라는 변수가 가리키는 메모리상의 값을 불러오게 되는 것이다. var 변수를 참조할 때마다 동일한 값을 가져오는 것은 당연한 것이다.
변수는 mutable한 속성을 가지고 있어서 얼마든지 다른 값으로 할당할 수 있다.
변수는 mutable한 속성을 가지고 있어서 얼마든지 다른 값으로 할당할 수 있다.
>> var = "bcd" #=> "bcd" >> var #=> "bcd" >> var.object_id #=> 70287903718960 >> var.object_id #=> 70287903718960 >> var.object_id #=> 70287903718960
이 때 변수 var의 참조값은 변경된다. 즉, var 변수에 다른 값을 할당하는 순간 또 다시 String 클래스가 새로운 값 "bcd"로 초기화되어 문자열 객체가 만들어지고 var 변수 해당 객체의 메모리상의 새로운 위치를 가리키게 되는 것이다. 따라서 변경된 var 변수의 object_id 값은 변경전의 값과 달라지지만 var 변수를 참조할 때마다 동일값을 얻게 되는 것이다.
이제, 변수와 비슷한 상수(constant)를 비교해 보자. 변수와 구분되는 결정적인 차이점은, 첫째, 상수는 알파벳 대문자로 시작하다는 것이다. 둘째, 상수는 다른 값으로 변경할 수 없다는 것이다.
따라서, 변수이름을 정할 때 무심고 첫문자를 대문자로 하면 의도하지 않게 상수가 되어 버린다는 것을 유의해야 한다. 상수값을 실수로 변경하게 되면,
>> Abc = "abc" #=> "abc" >> Abc.class #=> String >> Abc.object_id #=> 70144975225980 >> Abc.object_id #=> 70144975225980 >> Abc.object_id #=> 70144975225980 >> Abc = "bcd" (irb):9: warning: already initialized constant Abc #=> "bcd"
경고 메시지가 보이게 된다. 즉, 상수는 변경할 수 없다는 말이다.
상수와 심볼
값을 변경할 수 없는 상수가 문자열을 가지고 있는 특수한 상황에서, 루비는 symbol 이라는 특별한 클래스를 제공해 주고 있다. 즉, symbol 객체는 문자열 상수를 생각하면 쉽게 이해할 수 있을 것이다. 그러나 상수와는 달리 표기법에서 차이가 있다. 따옴표로 둘러싼 문자열 앞에 콜론(:)을 붙여주면 되는데, 이 때 따옴표는 생략할 수 있다. 따라서, 아래의 예는 모두 symbol을 나타낸다.
>> :abc #=> :abc >> :"abc" #=> :abc
심볼과 문자열
위에서와 같이 문자열을 심볼로 변환해 주는 메소드(string.to_sym 또는 string.intern)를 사용하면 쉽게 변경할 수 있다.
>> "abc".to_sym #=> :abc >> "abc".intern #=> :abc
반대로 symbol을 문자열로 변환할 수도 있다.
>> :abc.to_s #=> "abc"
이상에서와 같이, 문자열과 symbol은 서로 클래스 변경을 쉽게 할 수 있다. 당연한 결과지만 문자열과 symbol에 대한 object_id를 살펴보면 금방 그 의미를 유추해 볼 수 있다.
>> "abc".object_id #=> 70144975069500 >> "abc".object_id #=> 70144975056060 >> "abc".object_id #=> 70144975050760 >> :abc.object_id #=> 459368 >> :abc.object_id #=> 459368 >> :abc.object_id #=> 459368
문자열은 사용할 때마다 object_id 값이 달라진다. 즉, 그때마다 새로운 String 객체가 만들어진다는 말이다. 그러나 symbol은 항상 동일한 object_id 값을 가지게 된다는 것이다. 이것은 무슨 의미일까. symbol은 이름과 값을 동시에 가진다는 것는 것이다. 변수의 경우는 변수명이 있고 그 변수명이 가지는 값은 변수명이 가리키는 메모리상의 위치에 존재하는 값이라는 말이다. 그러나 :abc 라는 심볼의 경우는, abc라는 심볼이름과 "abc"라는 값을 동시에 가지게 된다는 말이다. 따라서 언제든지 :abc와 같이 참조할 수 있다는 말이다. 동일한 값을 얻기 위해 변수에 symbol을 할당할 필요가 없다는 결론이다. 또한 동일한 symbol 참조할 때마다 항상 동일한 메모리상의 위치에 있는 값을 가져오게 되어 문자열과는 달리 heap 메모리를 쓸데 없이 낭비하지 않고 문자열에 비해 40%정도의 처리속도의 향상을 가져오게 된다는 장점이 있다. 그러나 symbol 도 10,000개를 넘어가면 symbol table에 등록이 되지 않는다고 한다. 또한 사용하지 않는 symbol에 대해서도 garbage collection이 자동으로 이루어지 않는다고 한다. 이러한 symbol의 종류와 갯수를 모두 보고 싶은 경우에는 아래와 같이 하면 된다.
>> Symbol.all_symbols => [:"core#set_method_alias", :"core#set_variable_alias", :"core#undef_method", :"core#define_method", :"core#define_singleton_method", :"core#set_postexe", :each, :length, :size, :lambda, :intern, :gets, :succ, :method_missing, :send, :__send__, ... ] >> Symbol.all_symbols.size #=> 2947
루비온레일스에서의 심볼 사용예
그렇다면 루비온레일스에서 symbol은 주로 어떤 용도로 사용할까.
아마도 해시 키로 symbol을 가장 많이 사용하지 않나 생각된다. 또한 키값으로도 문자열 객체나 symbol 객체 중에 어떤 것을 사용해도 되는 경우가 많다.
폼에서 서밋하여 넘겨 받은 값들을 루비온레일스에서는 params 라는 해시로 접근할 수 있다. 이 params 해시는 쿼리스트링으로 넘어오는 변수나 Post 변수로 넘어오는 변수 모두에 대해서 사용할 수 있다. params[:person] 와 같이 컨트롤러 액션에서 사용하면 넘어 온 변수 중에서 :person 키를 가지는 값을 얻을 수 있는데, @person 인스턴스 변수를 이용해서 폼을 작성한 경우 @person 인스턴스 변수로 참조되는 person 객체에 대한 속성은 params[:person][:name] 와 같이 조회할 수 있게 되는 것이다. 물론 이 해시는 중첩 구조를 가지게 된다.
class PersonController < ApplicationController
def new
@person = Person.new
end
def create
@person = Person.new(params[:person])
respond_to do |format|
if @person.save
format.html { redirect_to @person, notice: 'Person was successfully created.' }
format.json { render json: @person, status: :created, location: @person }
else
format.html { render action: "new" }
format.json { render json: @person.errors, status: :unprocessable_entity }
end
end
end
end
루비 1.9부터는 해시 표기법상 단축형이 지원된다. 따라서 아래의 표기법은 동일 한 것이다.
>> h = { :name => "Lucius", :age => "50" }
>> h = { name: "Lucius", age: "50" }
symbol에서 사용하는 특이한 메소드 to_proc를 소개한다. 매우 편리한 메소드이며 흔히 사용하는 것이다. 사용법 아래와 같다.
즉, 메소드명을 심볼형으로 사용하여 블록을 반환해 주는 Symbol의 "&" (= to_proc) 메소드를 사용할 수 있다.
지금까지의 내용을 정리해 보면, symbol은 문자열값을 가지는 상수로 생각할 수 있다. 변수와 같이 항상 메모리상의 같은 위치를 참조하게 되고, 상수와 같이 값을 변경할 수 없고, 변수에 할당하지 않고 사용하여 언제나 동일한 심볼을 사용할 수 있다. 이에 반해 문자열을 재사용할 때는 반드시 변수에 할당하여 사용하지만 할당할 때마다 새로운 문자열 객체가 만들어져 메모리상에 다른 위치를 가리키게 된다는 것이다.
본 내용에 대한 스크린캐스트 동영상도 함께 제작하여 유투브에 공유하였다.
참고문서
>> (1..3).collect { | i | i.to_s }
>> (1..3).collect(&:to_s)
즉, 메소드명을 심볼형으로 사용하여 블록을 반환해 주는 Symbol의 "&" (= to_proc) 메소드를 사용할 수 있다.
지금까지의 내용을 정리해 보면, symbol은 문자열값을 가지는 상수로 생각할 수 있다. 변수와 같이 항상 메모리상의 같은 위치를 참조하게 되고, 상수와 같이 값을 변경할 수 없고, 변수에 할당하지 않고 사용하여 언제나 동일한 심볼을 사용할 수 있다. 이에 반해 문자열을 재사용할 때는 반드시 변수에 할당하여 사용하지만 할당할 때마다 새로운 문자열 객체가 만들어져 메모리상에 다른 위치를 가리키게 된다는 것이다.
본 내용에 대한 스크린캐스트 동영상도 함께 제작하여 유투브에 공유하였다.
참고문서
- The Ruby_Newbie Guide to Symbols
- Ruby Symbils Ultimate Guide
- Ruby's Symbols Explained
- 13 Ways of Looking at a Ruby Symbol





