|
| 1 | +--- |
| 2 | +layout: news_post |
| 3 | +title: "Ruby 3.0.0 릴리스" |
| 4 | +author: "naruse" |
| 5 | +translator: "yous" |
| 6 | +date: 2020-12-25 00:00:00 +0000 |
| 7 | +lang: ko |
| 8 | +--- |
| 9 | + |
| 10 | +Ruby 3.0.0 릴리스를 알리게 되어 기쁩니다. 우리는 2015년부터 성능, 동시성, 타입 기능을 목표로 한 Ruby 3을 위해 열심히 개발해 왔습니다. 특히 성능 부분에서, Matz 씨는 "Ruby3는 Ruby2보다 3배 더 빠를 것"이라고 해 [Ruby 3x3](https://blog.heroku.com/ruby-3-by-3)으로 알려지기도 했습니다. |
| 11 | + |
| 12 | +{% assign release = site.data.releases | where: "version", "3.0.0" | first %} |
| 13 | + |
| 14 | +<img src='https://cache.ruby-lang.org/pub/media/ruby3x3.png' alt='Optcarrot 3000 frames' width='100%' /> |
| 15 | + |
| 16 | +NES의 게임 에뮬레이션 작업을 통해 싱글 스레드 성능을 측정한 [optcarrot 벤치마크](https://github.com/mame/optcarrot)를 보면, Ruby 2.0보다 3배 더 빠른 성능을 달성했습니다! <details>이 벤치마크는 [benchmark-driver.github.io/hardware.html](https://benchmark-driver.github.io/hardware.html)에 표기된 환경에서 측정되었습니다. [8c510e4095 커밋](https://github.com/ruby/ruby/commit/8c510e4095)이 Ruby 3.0 코드로 사용되었습니다. 환경과 벤치마크에 따라 3배 빠르지 않을 수도 있습니다.</details> |
| 17 | + |
| 18 | +Ruby 3.0.0의 목표는 다음 항목을 통해 다루고 있습니다. |
| 19 | +* 성능 |
| 20 | + * MJIT |
| 21 | +* 동시성 |
| 22 | + * Ractor |
| 23 | + * Fiber 스케줄러 |
| 24 | +* 타입 (정적 분석) |
| 25 | + * RBS |
| 26 | + * TypeProf |
| 27 | + |
| 28 | +위의 성능 향상과 함께, Ruby 3.0에 몇 가지 새 기능이 도입됩니다. |
| 29 | + |
| 30 | +## 성능 |
| 31 | + |
| 32 | +> 제가 콘퍼런스 키노트에서 처음 "Ruby3x3"을 선언했을 때, 코어 팀 구성원을 포함한 많은 사람들은 "Matz 씨는 허풍쟁이"라고 느꼈습니다. 사실 저도 그렇게 느꼈어요. 하지만 우리는 해냈습니다. (일부 벤치마크에서) Ruby3.0을 Ruby2.0보다 세 배 빠르게 만드는 데 성공한 코어 팀이 자랑스럽습니다. -- Matz |
| 33 | +
|
| 34 | +### MJIT |
| 35 | + |
| 36 | +MJIT의 많은 개선이 추가되었습니다. 자세한 내용은 NEWS를 확인하세요. |
| 37 | + |
| 38 | +Ruby 3.0에서 JIT는 게임([Optcarrot](https://benchmark-driver.github.io/benchmarks/optcarrot/commits.html#chart-1)), AI([Rubykon](https://benchmark-driver.github.io/benchmarks/rubykon/commits.html)), 적은 메서드를 여러 번 호출하는 데 대부분의 시간을 사용하는 애플리케이션 등의 제한적인 작업 성능을 향상시킵니다. |
| 39 | + |
| 40 | +Ruby 3.0이 [JIT이 적용된 코드 크기를 현저히 줄였지만](https://twitter.com/k0kubun/status/1256142302608650244), Rails와 같은 작업에 대해서는 아직 준비되지 않았습니다. 굉장히 많은 메서드에 시간을 사용하는 Rails의 경우 JIT으로 인해 명령어 캐시 미스가 더 악화됩니다. 이 문제에 대한 추후 개선은 Ruby 3.1 소식을 기다려주세요. |
| 41 | + |
| 42 | +## 동시성 / 병렬성 |
| 43 | + |
| 44 | +> 지금은 멀티코어 시대입니다. 동시성은 아주 중요합니다. Ruby는 Ractor, Async Fiber와 함께 진정한 동시성 언어가 될 것입니다. --- Matz |
| 45 | +
|
| 46 | +### Ractor (실험적) |
| 47 | + |
| 48 | +Ractor는 스레드 안전에 대한 걱정이 없는 병렬 실행을 제공하기 위해 설계된 액터 모델과 비슷한 동시 실행 추상화 모델입니다. |
| 49 | + |
| 50 | +여러 개의 Ractor를 만들고 병렬로 실행할 수 있습니다. Ractor는 일반 객체를 공유할 수 없기 때문에 스레드 안전한 병렬 프로그램을 만들 수 있습니다. Ractor 간의 통신은 메시지 넘기기를 통해서 지원됩니다. |
| 51 | + |
| 52 | +객체 공유를 제한하기 위해, Ractor는 Ruby 문법에 여러 제한을 추가했습니다(여러 개의 Ractor를 사용하지 않는다면 제한은 없습니다). |
| 53 | + |
| 54 | +명세와 구현은 아직 완성되지 않았으므로 앞으로 변경될 수 있습니다. 그러므로 이 기능은 실험적으로 제공되며 처음 `Ractor.new`를 실행하면 "실험적 기능"이라는 경고를 표시합니다. |
| 55 | + |
| 56 | +다음은 유명한 벤치마크 tak 함수([Tak (function) - Wikipedia](https://en.wikipedia.org/wiki/Tak_(function)))의 실행 시간을 측정하는 작은 프로그램입니다. 4번 순차적으로 실행하거나, Ractor를 통해 4번 병렬로 실행합니다. |
| 57 | + |
| 58 | +``` ruby |
| 59 | +def tarai(x, y, z) = |
| 60 | + x <= y ? y : tarai(tarai(x-1, y, z), |
| 61 | + tarai(y-1, z, x), |
| 62 | + tarai(z-1, x, y)) |
| 63 | +require 'benchmark' |
| 64 | +Benchmark.bm do |x| |
| 65 | + # 순차적 버전 |
| 66 | + x.report('seq'){ 4.times{ tarai(14, 7, 0) } } |
| 67 | + |
| 68 | + # 병렬 버전 |
| 69 | + x.report('par'){ |
| 70 | + 4.times.map do |
| 71 | + Ractor.new { tarai(14, 7, 0) } |
| 72 | + end.each(&:take) |
| 73 | + } |
| 74 | +end |
| 75 | +``` |
| 76 | + |
| 77 | +``` |
| 78 | +Benchmark result: |
| 79 | + user system total real |
| 80 | +seq 64.560736 0.001101 64.561837 ( 64.562194) |
| 81 | +par 66.422010 0.015999 66.438009 ( 16.685797) |
| 82 | +``` |
| 83 | + |
| 84 | +이 결과는 Ubuntu 20.04, Intel(R) Core(TM) i7-6700(4 코어, 8 하드웨어 스레드)에서 측정되었습니다. 이는 병렬 버전이 순차적 버전보다 3.87배 빠르다는 것을 보여줍니다. |
| 85 | + |
| 86 | +더 자세한 내용은 [doc/ractor.md](https://docs.ruby-lang.org/en/3.0.0/doc/ractor_md.html)를 확인하세요. |
| 87 | + |
| 88 | +### Fiber 스케줄러 |
| 89 | + |
| 90 | +블로킹 작업에 끼어들 수 있는 `Fiber#scheduler`가 도입됩니다. 이는 기존에 존재하는 코드를 변경하지 않고 가벼운 동시성을 지원할 수 있게 합니다. 어떻게 동작하는지 궁금하다면 ["Don't Wait For Me, Scalable Concurrency for Ruby 3"](https://www.youtube.com/watch?v=Y29SSOS4UOc)을 보세요. |
| 91 | + |
| 92 | +현재 지원되는 클래스와 메서드는 다음과 같습니다. |
| 93 | + |
| 94 | +- `Mutex#lock`, `Mutex#unlock`, `Mutex#sleep` |
| 95 | +- `ConditionVariable#wait` |
| 96 | +- `Queue#pop`, `SizedQueue#push` |
| 97 | +- `Thread#join` |
| 98 | +- `Kernel#sleep` |
| 99 | +- `Process.wait` |
| 100 | +- `IO#wait`, `IO#read`, `IO#write`와 관련 메서드(예: `#wait_readable`, `#gets`, `#puts` 등). |
| 101 | +- `IO#select`는 *지원되지 않습니다*. |
| 102 | + |
| 103 | +이 예제 프로그램은 HTTP 요청 몇 개를 동시에 수행합니다. |
| 104 | + |
| 105 | +``` ruby |
| 106 | +require 'async' |
| 107 | +require 'net/http' |
| 108 | +require 'uri' |
| 109 | + |
| 110 | +Async do |
| 111 | + ["ruby", "rails", "async"].each do |topic| |
| 112 | + Async do |
| 113 | + Net::HTTP.get(URI "https://www.google.com/search?q=#{topic}") |
| 114 | + end |
| 115 | + end |
| 116 | +end |
| 117 | +``` |
| 118 | + |
| 119 | +이 코드는 이벤트 루프를 지원하는 [async](https://github.com/socketry/async)를 사용합니다. 이 이벤트 루프는 `Net::HTTP`를 논블로킹 작업으로 만들기 위해 `Fiber#scheduler` 훅을 사용합니다. Ruby의 논블로킹 실행을 제공하기 위해 다른 gem도 이 인터페이스를 사용할 수 있습니다. 그리고 이러한 gem은 논블로킹 훅을 지원하는 Ruby의 다른 구현체(예: JRuby, TruffleRuby)와도 호환됩니다. |
| 120 | + |
| 121 | +## 정적 분석 |
| 122 | + |
| 123 | +> 2010년대는 정적 타입 프로그래밍 언어의 시대였습니다. Ruby는 추상 해석을 통해, 타입 선언 없이 정적 타입 체크를 하여 미래를 추구합니다. RBS와 TypeProf는 미래를 향한 첫걸음입니다. 다음 단계도 기대해주세요. --- Matz |
| 124 | +
|
| 125 | +### RBS |
| 126 | + |
| 127 | +RBS는 Ruby 프로그램의 타입을 기술하기 위한 언어입니다. |
| 128 | + |
| 129 | +TypeProf와 다른 도구들을 포함해서, RBS를 지원하는 타입 검사기는 RBS 정의를 통해 Ruby 프로그램을 더 잘 이해합니다. |
| 130 | + |
| 131 | +클래스나 모듈에 정의된 메서드, 인스턴스 변수와 타입, 상속이나 믹스인 관계에 대한 정의를 작성할 수 있습니다. |
| 132 | + |
| 133 | +RBS의 목표는 Ruby 프로그램에서 흔히 보이는 패턴을 지원하는 것과 유니언 타입, 메서드 오버로딩, 제네릭을 포함하는 고급 타입을 작성할 수 있도록 하는 것입니다. 또한 _인터페이스 타입_을 이용해 덕타이핑을 지원합니다. |
| 134 | + |
| 135 | +Ruby 3.0은 RBS로 작성된 타입 정의를 해석하고 처리하는 `rbs` gem을 포함합니다. |
| 136 | +다음은 클래스, 모듈, 상수 정의를 포함하는 RBS의 작은 예시입니다. |
| 137 | + |
| 138 | +``` rbs |
| 139 | +module ChatApp |
| 140 | + VERSION: String |
| 141 | + class Channel |
| 142 | + attr_reader name: String |
| 143 | + attr_reader messages: Array[Message] |
| 144 | + attr_reader users: Array[User | Bot] # `|`는 유니언 타입을 의미합니다. 여기에서는 `User` 또는 `Bot`이라는 의미입니다. |
| 145 | + def initialize: (String) -> void |
| 146 | + def post: (String, from: User | Bot) -> Message # 메서드 오버로딩을 지원합니다. |
| 147 | + | (File, from: User | Bot) -> Message |
| 148 | + end |
| 149 | +end |
| 150 | +``` |
| 151 | + |
| 152 | +더 자세한 내용은 [rbs gem의 README](https://github.com/ruby/rbs)를 확인하세요. |
| 153 | + |
| 154 | +### TypeProf |
| 155 | + |
| 156 | +TypeProf는 Ruby 패키지에 포함된 타입 분석 도구입니다. |
| 157 | + |
| 158 | +지금의 TypeProf는 타입 추론 도구 역할을 합니다. |
| 159 | + |
| 160 | +TypeProf는 타입 어노테이션이 없는 일반적인 Ruby 코드를 읽어서, 어떤 메서드가 정의되어 있고 어떻게 사용되는지 분석하고, RBS 형식으로 타입 시그니처의 프로토타입을 생성합니다. |
| 161 | + |
| 162 | +다음은 TypeProf의 간단한 데모입니다. |
| 163 | + |
| 164 | +예제 입력입니다. |
| 165 | + |
| 166 | +``` ruby |
| 167 | +# test.rb |
| 168 | +class User |
| 169 | + def initialize(name:, age:) |
| 170 | + @name, @age = name, age |
| 171 | + end |
| 172 | + attr_reader :name, :age |
| 173 | +end |
| 174 | +User.new(name: "John", age: 20) |
| 175 | +``` |
| 176 | + |
| 177 | +예제 출력입니다. |
| 178 | + |
| 179 | +``` |
| 180 | +$ typeprof test.rb |
| 181 | +# Classes |
| 182 | +class User |
| 183 | + attr_reader name : String |
| 184 | + attr_reader age : Integer |
| 185 | + def initialize : (name: String, age: Integer) -> [String, Integer] |
| 186 | +end |
| 187 | +``` |
| 188 | + |
| 189 | +입력을 "test.rb"에 저장하고 "typeprof test.rb" 커맨드를 통해 TypeProf를 실행할 수 있습니다. |
| 190 | + |
| 191 | +[TypeProf를 온라인에서 사용](https://mame.github.io/typeprof-playground/#rb=%23+test.rb%0Aclass+User%0A++def+initialize%28name%3A%2C+age%3A%29%0A++++%40name%2C+%40age+%3D+name%2C+age%0A++end%0A++%0A++attr_reader+%3Aname%2C+%3Aage%0Aend%0A%0AUser.new%28name%3A+%22John%22%2C+age%3A+20%29&rbs=)해볼 수도 있습니다. (TypeProf를 서버 쪽에서 실행하는 거라서, 잠시 동작하지 않을 수도 있습니다!) |
| 192 | + |
| 193 | +더 자세한 내용은 [TypeProf 문서](https://github.com/ruby/typeprof/blob/master/doc/doc.md)와 [데모](https://github.com/ruby/typeprof/blob/master/doc/demo.md)를 확인하세요. |
| 194 | + |
| 195 | +TypeProf는 실험적이고 아직 완성되지 않았습니다. Ruby 언어의 일부만 지원되고, 타입 오류 감지 기능은 제한적입니다. 하지만 언어 기능의 지원 범위, 분석 성능, 사용성이 빠르게 개선되고 있는 중입니다. 어떤 종류의 피드백이든 환영합니다. |
| 196 | + |
| 197 | +## 그 이외의 주목할 만한 기능 |
| 198 | + |
| 199 | +* 한 줄 패턴 매칭을 재설계했습니다. (실험적) |
| 200 | + |
| 201 | + * `=>`가 추가됩니다. 오른 방향 대입처럼 사용할 수 있습니다. |
| 202 | + |
| 203 | + ```ruby |
| 204 | + 0 => a |
| 205 | + p a #=> 0 |
| 206 | + |
| 207 | + {b: 0, c: 1} => {b:} |
| 208 | + p b #=> 0 |
| 209 | + ``` |
| 210 | + |
| 211 | + * `in`이 `true` 또는 `false`를 반환하도록 변경됩니다. |
| 212 | + |
| 213 | + ```ruby |
| 214 | + # 버전 3.0 |
| 215 | + 0 in 1 #=> false |
| 216 | +
|
| 217 | + # 버전 2.7 |
| 218 | + 0 in 1 #=> raise NoMatchingPatternError |
| 219 | + ``` |
| 220 | + |
| 221 | +* 검색 패턴이 추가됩니다. (실험적) |
| 222 | + |
| 223 | + ``` ruby |
| 224 | + case ["a", 1, "b", "c", 2, "d", "e", "f", 3] |
| 225 | + in [*pre, String => x, String => y, *post] |
| 226 | + p pre #=> ["a", 1] |
| 227 | + p x #=> "b" |
| 228 | + p y #=> "c" |
| 229 | + p post #=> [2, "d", "e", "f", 3] |
| 230 | + end |
| 231 | + ``` |
| 232 | + |
| 233 | +* end 없는 메서드 정의가 추가됩니다. |
| 234 | + |
| 235 | + ``` ruby |
| 236 | + def square(x) = x * x |
| 237 | + ``` |
| 238 | + |
| 239 | +* `Hash#except`가 내장됩니다. |
| 240 | + |
| 241 | + ``` ruby |
| 242 | + h = { a: 1, b: 2, c: 3 } |
| 243 | + p h.except(:a) #=> {:b=>2, :c=>3} |
| 244 | + ``` |
| 245 | + |
| 246 | +* 메모리 뷰가 실험적인 기능으로 추가됩니다. |
| 247 | + |
| 248 | + * 이는 숫자 배열이나 비트맵 이미지와 같은 메모리 공간을 확장 라이브러리 간에 교환하기 위한 새로운 C-API 집합입니다. 확장 라이브러리는 모양, 요소의 형식 등으로 구성된 메모리 공간의 메타데이터를 공유할 수 있습니다. 이러한 메타데이터를 사용하여 확장 라이브러리는 다차원 배열을 적절하게 공유할 수 있습니다. 이 기능은 Python의 버퍼 프로토콜을 참고하여 설계되었습니다. |
| 249 | + |
| 250 | +## 성능 향상 |
| 251 | + |
| 252 | +* IRB에 긴 코드를 붙여 넣는 속도가 Ruby 2.7.0에 포함된 버전보다 53배 빨라졌습니다. 예를 들어, [이 샘플 코드](https://gist.github.com/aycabta/30ab96334275bced5796f118c9220b0b)를 붙여 넣는 데 드는 시간이 11.7초에서 0.22초로 줄어들었습니다. |
| 253 | + |
| 254 | + |
| 255 | +<video autoplay="autoplay" controls="controls" muted="muted" width="764" height="510" poster="https://cache.ruby-lang.org/pub/media/ruby-3.0-irb-highspeed.png"> |
| 256 | + <source src="https://cache.ruby-lang.org/pub/media/ruby-3.0-irb-highspeed.mp4" type="video/mp4"> |
| 257 | +</video> |
| 258 | + |
| 259 | + |
| 260 | +* IRB에 `measure` 커맨드가 추가되었습니다. 간단한 실행 시간 측정이 가능합니다. |
| 261 | + |
| 262 | + ``` |
| 263 | + irb(main):001:0> 3 |
| 264 | + => 3 |
| 265 | + irb(main):002:0> measure |
| 266 | + TIME is added. |
| 267 | + => nil |
| 268 | + irb(main):003:0> 3 |
| 269 | + processing time: 0.000058s |
| 270 | + => 3 |
| 271 | + irb(main):004:0> measure :off |
| 272 | + => nil |
| 273 | + irb(main):005:0> 3 |
| 274 | + => 3 |
| 275 | + ``` |
| 276 | +
|
| 277 | +## 그 이외의 2.7 이후로 주목할 만한 변경 |
| 278 | +
|
| 279 | +* 키워드 인자가 다른 인자들로부터 분리됩니다. |
| 280 | + * 원칙적으로 Ruby 2.7에서 경고를 출력하는 코드는 동작하지 않습니다. 자세한 내용은 [이 문서](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/)를 확인하세요. |
| 281 | + * 한편, 인자를 전달할 때 앞쪽 인자를 사용할 수 있습니다. |
| 282 | +
|
| 283 | + ``` ruby |
| 284 | + def method_missing(meth, ...) |
| 285 | + send(:"do_#{ meth }", ...) |
| 286 | + end |
| 287 | + ``` |
| 288 | +
|
| 289 | +* 패턴 매칭(`case`/`in`)은 이제 실험적이지 않습니다. |
| 290 | + * 더 자세한 내용은 [패턴 매칭 문서](https://docs.ruby-lang.org/en/3.0.0/doc/syntax/pattern_matching_rdoc.html)를 확인하세요. |
| 291 | +* `$SAFE` 기능이 완전히 제거됩니다. 이 값은 이제 일반 전역 변수입니다. |
| 292 | +* Ruby 2.5에서 백트레이스의 순서가 역순이 되었습니다만, 이를 취소합니다. 이제 백트레이스는 Ruby 2.4처럼 동작합니다. 예외가 발생한 곳의 오류 메시지와 줄 번호가 가장 먼저 출력되며, 이를 호출한 곳의 정보가 그 뒤에 출력됩니다. |
| 293 | +* 표준 라이브러리를 업데이트했습니다. |
| 294 | + * RubyGems 3.2.3 |
| 295 | + * Bundler 2.2.3 |
| 296 | + * IRB 1.3.0 |
| 297 | + * Reline 0.2.0 |
| 298 | + * Psych 3.3.0 |
| 299 | + * JSON 2.5.1 |
| 300 | + * BigDecimal 3.0.0 |
| 301 | + * CSV 3.1.9 |
| 302 | + * Date 3.1.0 |
| 303 | + * Digest 3.0.0 |
| 304 | + * Fiddle 1.0.6 |
| 305 | + * StringIO 3.0.0 |
| 306 | + * StringScanner 3.0.0 |
| 307 | + * 등 |
| 308 | +* 다음 라이브러리는 이제 내장 gem이나 표준 라이브러리가 아닙니다. |
| 309 | + 각 기능이 필요한 경우에는 해당하는 gem을 설치해주세요. |
| 310 | + * sdbm |
| 311 | + * webrick |
| 312 | + * net-telnet |
| 313 | + * xmlrpc |
| 314 | +* 다음 기본 gem은 이제 내장 gem이 됩니다. |
| 315 | + * rexml |
| 316 | + * rss |
| 317 | +* 다음 표준 라이브러리가 기본 gem이 되고, rubygems.org에 배포됩니다. |
| 318 | + * English |
| 319 | + * abbrev |
| 320 | + * base64 |
| 321 | + * drb |
| 322 | + * debug |
| 323 | + * erb |
| 324 | + * find |
| 325 | + * net-ftp |
| 326 | + * net-http |
| 327 | + * net-imap |
| 328 | + * net-protocol |
| 329 | + * open-uri |
| 330 | + * optparse |
| 331 | + * pp |
| 332 | + * prettyprint |
| 333 | + * resolv-replace |
| 334 | + * resolv |
| 335 | + * rinda |
| 336 | + * set |
| 337 | + * securerandom |
| 338 | + * shellwords |
| 339 | + * tempfile |
| 340 | + * tmpdir |
| 341 | + * time |
| 342 | + * tsort |
| 343 | + * un |
| 344 | + * weakref |
| 345 | + * digest |
| 346 | + * io-nonblock |
| 347 | + * io-wait |
| 348 | + * nkf |
| 349 | + * pathname |
| 350 | + * syslog |
| 351 | + * win32ole |
| 352 | +
|
| 353 | +더 자세한 내용은 [NEWS](https://github.com/ruby/ruby/blob/{{ release.tag }}/NEWS.md)나 |
| 354 | +[커밋 로그](https://github.com/ruby/ruby/compare/v2_7_0...{{ release.tag }})를 |
| 355 | +확인해주세요. |
| 356 | +
|
| 357 | +이러한 변경사항에 따라, Ruby 2.7.0 이후로 [파일 {{ release.stats.files_changed }}개 수정, {{ release.stats.insertions }}줄 추가(+), {{ release.stats.deletions }}줄 삭제(-)](https://github.com/ruby/ruby/compare/v2_7_0...{{ release.tag }}#file_bucket)가 |
| 358 | +이루어졌습니다! |
| 359 | +
|
| 360 | +> Ruby3.0은 마일스톤입니다. 언어는 진화했고, 호환성은 유지했습니다. 이게 끝이 아닙니다. Ruby는 계속 진보하고, 더 대단해질 것입니다. 기대해주세요! --- Matz |
| 361 | +
|
| 362 | +메리 크리스마스, 해피 홀리데이, Ruby 3.0과 함께 프로그래밍을 즐겨보세요! |
| 363 | +
|
| 364 | +## 다운로드 |
| 365 | +
|
| 366 | +* <{{ release.url.gz }}> |
| 367 | +
|
| 368 | + SIZE: {{ release.size.gz }} |
| 369 | + SHA1: {{ release.sha1.gz }} |
| 370 | + SHA256: {{ release.sha256.gz }} |
| 371 | + SHA512: {{ release.sha512.gz }} |
| 372 | +
|
| 373 | +* <{{ release.url.xz }}> |
| 374 | +
|
| 375 | + SIZE: {{ release.size.xz }} |
| 376 | + SHA1: {{ release.sha1.xz }} |
| 377 | + SHA256: {{ release.sha256.xz }} |
| 378 | + SHA512: {{ release.sha512.xz }} |
| 379 | +
|
| 380 | +* <{{ release.url.zip }}> |
| 381 | +
|
| 382 | + SIZE: {{ release.size.zip }} |
| 383 | + SHA1: {{ release.sha1.zip }} |
| 384 | + SHA256: {{ release.sha256.zip }} |
| 385 | + SHA512: {{ release.sha512.zip }} |
| 386 | +
|
| 387 | +## Ruby는 |
| 388 | +
|
| 389 | +Ruby는 1993년에 Matz(마츠모토 유키히로) 씨가 처음 개발했고, |
| 390 | +현재는 오픈 소스로서 개발되고 있습니다. 여러 플랫폼에서 동작하며, |
| 391 | +특히 웹 개발에서 전 세계적으로 이용되고 있습니다. |
0 commit comments