스타트 루비(Start Ruby) #1
이 시간에는 루비 언어의 탄생 배경과 루비 언어의 특징에 대해서 간단히 알아보겠습니다. 루비 언어는 여러 언어의 장점을 취해서 만든만큼 다양한 기능들이 있지만, 사실 세컨드 랭귀지(Second Language)로 사용하는 정도라면 이번 장과 다음 장에서 설명하는 내용만으로도 충분할 것입니다. 자, 그럼 루비의 세계로 한 번 들어가 보겠습니다.
루비(Ruby)?
루비는 마츠모토 유키히로라는 일본 사람이 만들었습니다. 유키히로씨는 쉽게 쓸 수 있으면서 객체지향적인 언어를 만드는 걸 목표로 했고, 그래서 탄생한 것이 바로 루비(Ruby)입니다.
사실, 루비는 루비 온 레일즈(Ruby on Rails) 덕분에 더 널리 알려졌는데요, 레일즈 프레임워크는 스프링 프레임워크처럼 웹 개발을 편하게 해주는 프레임워크의 한 종류랍니다. 지금 보시는 이 사이트도 루비 온 레일즈로 만들어졌습니다.
루비는 다른 스크립트 언어가 갖고 있는 대부분의 기능이 포함되어 있습니다. 물론 거기에는 다양한 확장 라이브러리도 포함해서 말이지요. ;) 여러분이 필요로 하는 대부분의 기능은 이미 다른 라이브러리에 포함되어 있을 겁니다. 잘만 활용한다면 쉽고, 빠르게 개발을 진행할 수 있습니다.
이러한 특징때문에 반복되는 작업을 자동화하는 간단한 프로그램이나, 본격적인 개발 전에 프로토타입을 만들어보는 용으로 사용하면 딱입니다(물론 제 개인적인 소견이에요. ㅠㅠ).
콘솔 출력
루비에서 데이터를 출력하는 방법은 아주 간단합니다. 앞에서 본 것처럼 puts
를 쓰면 대부분의 데이터를 출력할 수 있습니다. 단, 원하는 형태로 데이터를 출력하고 싶다면 조금 고민을 해야하지만, C 언어를 조금 안다면 그리 어렵지 않습니다. C 언어의 printf와 비슷한 기능을 하는 sprintf()
가 있기 때문이죠. C처럼 생긴 sprintf()가 싫다면 "문자열 포맷" %[]
형식의 루비 스타일도 있으니 입맛대로 사용하면 됩니다. ^^;;;;
a = sprintf("제 이름은 %s이고 나이는 %d 입니다.", "루비", 1)
b = "제 이름은 %s이고 나이는 %d 입니다." %["루비", 1]
c = sprintf("PI는 .2f 입니다.", 3.1425);
puts a
puts b
puts c
콘솔 입력
루비에서 콘솔 입력은 한 줄 또는 여러 줄을 입력받을 수 있습니다. 한 줄 입력은 gets() or readline()
함수를 사용하면 되고, 여러 줄 입력은 readlines()
함수를 사용하면 됩니다.
a = gets() # 엔터가 입력될 때까지 저장
b = readline() # 위와 같은 결과
c = readlines() # 여러 줄을 읽을 때 사용
루비 데이터 타입
루비에는 크게 7가지 기본 타입이 있는데요, 숫자와 문자열, 배열(Array)과 해시(Hash), 범위(Range)와 심볼(Symbol), 그리고 정규표현식(Regular Expression)이 그겁니다.
숫자와 문자열, 배열은 기존에 다른 언어와 큰 차이가 없기 때문에 간단히 살펴보고 넘어가겠습니다. 그렇다고 나머지가 엄청 복잡한 건 아니니 너무 걱정안하셔도 됩니다(사실 정규표현식은 좀 낯설긴 해요. ^^;;;;).
숫자, 문자열
아래는 루비에서 숫자와 문자열을 나타낸 예입니다.
a = 5 # 정수 5
b = 10.1 # 실수 10.1
c = "이것은 문자열입니다." # 문자열
puts a, b, c # a,b,c 변수의 값을 차례로 출력
결과:
5
10.1
이것은 문자열입니다.
위의 a, b, c를 변수라고 하며, 변수는 값을 저장하는 일종의 공간입니다. 루비의 변수는 타입이 정해져있지 않고 우변에 대입하는 값에 따라서 변합니다. C나 C++ 언어처럼 선언할 때 변수의 타입을 지정해주는 것과는 다른 방식이지요.
puts
는 파라미터로 넘어온 값을 출력하는 함수로, 숫자 타입부터 배열이나 해시까지 아주 편하게 출력할 수 있습니다. 앞으로 자주 사용할 것이니 puts() 함수를 눈여겨봐두기 바랍니다.
배열, 해시, 범위
배열과 해시 역시 아주 직관적입니다. 아래는 배열과 해시의 예를 나타낸 겁니다.
a = [0, 1, 2, 3, 4, 5] # 0 에서 5까지 정수가 들어있는 1차원 배열
# 사과와 바나나를 키로 사용하고, 그 값으로 빨강, 노랑을 연결한 해시
b = {"사과" => "빨강", "바나나" => "노랑"}
puts a, a.length, b["사과"], c["바나나"]
결과:
0
1
2
3
4
5
6
빨강
노랑
배열은 다른 언어와 마찬가지로 []로 감싸고 그 안에 배열의 요소를 ,로 구분하여 넣으면 됩니다. 잠시 후에 설명하겠지만 루비의 모든 타입은 클래스이기 때문에, 기본적으로 몇가지 함수나 속성을 가지고 있습니다. 위에서 나온 length도 그중에 한 가지며, 배열의 길이를 나타내는 속성입니다.
해시는 키(Key)와 값(Value)로 이루어진 테이블이며, 다른 말로는 맵(Map)이라고도 합니다. 해시는 배열과 달리 키를 사용하여 데이터를 읽고 쓸 수 있으므로, 연속적이지 않은 데이터를 다루는데 유용합니다. 해시에서 키와 값은 => 로 연결하며, 항목이 여러 개일 경우 ,로 구분합니다.
다음 범위와 정규표현식으로 넘어가기 전에 간단히 2차원 배열을 생성하는 방법을 살펴보겠습니다. 2차원 배열은 1차원 배열을 내부에 가지고 있는 배열입니다. 따라서 아래처럼 2차원 배열에 담을 배열을 먼저 생성한 뒤에, 다른 1차원 배열을 생성해서 그 안에 두면 됩니다.
a = [0, 1] # 1차원 배열
b = [2, 3] # 1차원 배열
c = [a, b] # 2차원 배열
puts c
puts c[0][0]
puts c[0][1]
puts c[1][0]
puts c[1][1]
결과:
[[0, 1], [2, 3]]
0
1
2
3
위의 코드는 2차원 배열을 만들고 각 요소에 접근하는 방법을 나타낸 겁니다. 2차원 배열이 "1차원 배열을 요소로 갖고 있는 1차원 배열"이란 것을 생각해보면, 쉽게 예측할 수 있는 코드입니다. ^^)-b
범위는 숫자와 ..
또는 ...
으로 이루어진 조합으로, 시작과 끝에 포함되는 모든 정수를 뜻합니다. 즉 1..10
은 1부터 10까지 모든 정수(1,2,3...10)을 뜻한다는 것이죠. ..
과 ...
의 차이는 ..
는 끝 숫자를 범위에 포함하지만, ...
는 끝 숫자를 범위에 포함하지 않는 것입니다. 아래는 그 사용법과 결과를 나타낸 겁니다.
a = 0..10 # 0부터 10까지 범위의 정수
b = 0...10 # 0부터 9까지 범위의 정수
puts a
puts b
결과:
0..10
0...10
위의 결과를 보면 살짝 당황스러울 겁니다. 0부터 10까지의 숫자가 출력되기를 바랬는데, 우리가 만든 범위를 그대로 출력해주니 말입니다. 여기에 살짝 for 문을 추가하여 실제로 범위에 들어가는 값을 출력해보겠습니다.
a = 0..10
b = 0...10
puts "0..10"
for i in a
puts i
end
puts "0...10"
for i in b
puts i
end
결과:
0..10
0
1
2
3
4
5
6
7
8
9
10
0...10
0
1
2
3
4
5
6
7
8
9
정규표현식(Regular Expression)과 문자열 다루기
루비의 정규표현식은 /
로 구분합니다. 즉 /Ruby/
처럼 사용하면 Ruby
와 일치하는 문자열을 나타내는 정규표현식이 됩니다. 정규 표현식 뒤에는 페턴을 어떻게 일치시킬지를 나타내는 옵션이 들어가고 옵션의 의미는 다음과 같습니다.
* /Ruby/i : 대소문자 구분 안함
* /Ruby/m : 줄바꿈 문자도 일반 문자처럼 다룰 때 사용
* /Ruby/x : 공백문자와 주석을 허용(RE 확장 문법)
정규표현식에서 ()
, []
, {}
, .
, ?
, +
, *
, |
, ^
, $
는 각자 의미가 있습니다. 각 기호의 의미는 말로 설명하는 것보다 직접 사용법을 보는 것이 더 이해가 쉽습니다. 다음은 각 기호의 사용법입니다.
/(R|r)uby/ # Ruby 또는 ruby와 일치
/[Rr]uby/ # Ruby 또는 ruby와 일치
/[0-9]/ # 모든 숫자 한자리와 일치
/[0-9]+/ # 모든 숫자 한자리 이상과 일치
/Ruby!*/ # Ruby 또는 Ruby 뒤에 !가 1개 이상 붙은 문자열과 일치
/Ruby!?/ # Ruby 또는 Ruby!와 일치
/Ruby{3}/ # Ruby가 3번 반복되는 문자열과 일치
/Ruby{1,3}/ # Ruby가 최소 1번부터 최대 3번까지 반복되는 문자열과 일치
/[^0-9]/ # 숫자를 제외한 문자열과 일치
/ruby$/ # ruby로 끝나는 문자열과 일치
기호로 인해 더 쉽게 정규표현식을 만들 수 있게 된 건 좋은데, 이런 기호를 실제로 쓰고 싶으면 어떻게 할까요? 방법은 간단합니다. 앞에서 나온 기호들을 실제 패턴 일치에 사용하고 싶으면 \
를 앞에 붙여주면됩니다. 아래처럼 말이죠 ;)
/Ruby\?/ # Ruby?와 일치
/Ruby\+/ # Ruby+와 일치
+
, *
와 같은 반복 기호 뒤에는 ?
기호를 사용할 수 있는데, 이는 패턴을 일치시킬 때 범위를 어디까지로 한정하는가에 사용됩니다. 아래와 같이 Ruby> 가 있을 때 ?가 있을 때와 없을 때의 차이는 다음과 같습니다.
a = "<ruby>Ruby>"
결과:
a =~ /<.*>/
$~
=> #<MatchData "<ruby>Ruby>">
a =~ /<.*?>/
$~
=> #<MatchData "<ruby>">
위에서 $~
로 나타낸 것은 정규표현식에서 일치한 부분을 나타내는 전역 변수입니다. 객체지향으로 보면 Regexp.last_match
와 같습니다. 사실 Regexp.last_match는 $~의 값을 그대로 반환합니다.
정규표현식에는 문자와 숫자를 의미하거나 공백 또는 모든 문자를 의미하는 표현식이 있습니다. 위의 .
이 바로 그것인데요, 다음은 해당 표현식을 나타낸 것입니다.
* /./ : 줄바꿈을 제외한 모든 문자와 일치
* /./m : 줄바꿈을 포함한 모든 문자와 일치
* /\d/ : 숫자와 일치
* /\D/ : 숫제를 제외한 모든 문자와 일치
* /\s/ : 공백 문자와 일치 \t, \r, \n, \f
* /\S/ : 공백을 제외한 문자와 일치
* /\w/ : 단어 하나와 일치
* /\W/ : 단어가 아닌 문자에 일치
사실, 문자열의 경우 배열의 인덱스에 정규표현식을 넣어서 일치하는 부분만 잘라낼 수 있습니다. 아래처럼 말이죠. slice()
함수를 사용해도 같은 결과를 얻을 수 있고, slice!()를 사용하면 원하는 결과만 문자열에서 꺼내고 기존 문자열은 나머지만 남길수도 있습니다.
a = "<ruby>Ruby>"
a[/<.*>/]
a[/<.*?>/]
a.slice(/<.*>/)
a.slice!(/<.*?>/)
puts a
결과:
<ruby>Ruby> #a[/<.*>/]
<ruby> #a[/<.*?>/]
<ruby>Ruby> # a.slice(/<.*>/)의 결과
<ruby> # a.slice!(/<.*?>/)의 결과
Ruby> # puts a의 결과
그리고, 패턴을 비교하는데 =~
만 쓰란 법도 없습니다. 문자열이라면 .match()
를 사용해서 MatchData
객체를 반환하게 만들수도 있습니다. =~
는 일치하는 인덱스를 반환하거나 nil
을 반환하는 반면 .match()
는 MatchData
를 반환하거나 nil
을 반환하는 것이 다릅니다.
명령어 실행
루비는 언어 내에서 `로 명령어를 실행하는 것을 지원합니다. 예를들어 `ls`를 하면 ls가 실행되어 그 결과가 문자열로 반환이됩니다. 다음은 ls
명령어를 실행하여 현재 디렉터리의 목록을 받아와 출력하는 예제입니다.
a = `ls`
puts a
결과:
posts.html
posts_files
start-ruby-1.md
test.css
test.html
test_files
vim-markdown-preview-설정.md
마무리...
이상으로 루비 언어의 특징에 대해서 간단하게 알아봤습니다. 다음 시간에는 루비 언어를 이용해서 귀찮은 작업을 한방에 처리할 수 있는 방법을 알아보겠습니다.