diff --git a/base-commit b/base-commit new file mode 100644 index 0000000000000..2053492b28030 --- /dev/null +++ b/base-commit @@ -0,0 +1,2 @@ +8447f8d +// https://github.com/rails/rails/tree/8447f8d01c3722593ccd345fd7e88be8039ecbf1/guides diff --git a/guides/source/ko/2_2_release_notes.md b/guides/source/ko/2_2_release_notes.md index 802455f612e68..40059f47bc140 100644 --- a/guides/source/ko/2_2_release_notes.md +++ b/guides/source/ko/2_2_release_notes.md @@ -1,7 +1,7 @@ Ruby on Rails 2.2 Release Notes =============================== -Rails 2.2 delivers a number of new and improved features. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/master) in the main Rails repository on GitHub. +Rails 2.2 delivers a number of new and improved features. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](https://github.com/rails/rails/commits/master) in the main Rails repository on GitHub. Along with Rails, 2.2 marks the launch of the [Ruby on Rails Guides](http://guides.rubyonrails.org/), the first results of the ongoing [Rails Guides hackfest](http://hackfest.rubyonrails.org/guide). This site will deliver high-quality documentation of the major features of Rails. @@ -19,8 +19,8 @@ Rails 2.2 supplies an easy system for internationalization (or i18n, for those o * Lead Contributors: Rails i18 Team * More information : * [Official Rails i18 website](http://rails-i18n.org) - * [Finally. Ruby on Rails gets internationalized](http://www.artweb-design.de/2008/7/18/finally-ruby-on-rails-gets-internationalized) - * [Localizing Rails : Demo application](http://github.com/clemens/i18n_demo_app) + * [Finally. Ruby on Rails gets internationalized](https://web.archive.org/web/20140407075019/http://www.artweb-design.de/2008/7/18/finally-ruby-on-rails-gets-internationalized) + * [Localizing Rails : Demo application](https://github.com/clemens/i18n_demo_app) ### Compatibility with Ruby 1.9 and JRuby @@ -32,7 +32,7 @@ Documentation The internal documentation of Rails, in the form of code comments, has been improved in numerous places. In addition, the [Ruby on Rails Guides](http://guides.rubyonrails.org/) project is the definitive source for information on major Rails components. In its first official release, the Guides page includes: * [Getting Started with Rails](getting_started.html) -* [Rails Database Migrations](migrations.html) +* [Rails Database Migrations](active_record_migrations.html) * [Active Record Associations](association_basics.html) * [Active Record Query Interface](active_record_querying.html) * [Layouts and Rendering in Rails](layouts_and_rendering.html) @@ -43,7 +43,6 @@ The internal documentation of Rails, in the form of code comments, has been impr * [A Guide to Testing Rails Applications](testing.html) * [Securing Rails Applications](security.html) * [Debugging Rails Applications](debugging_rails_applications.html) -* [Performance Testing Rails Applications](performance_testing.html) * [The Basics of Creating Rails Plugins](plugins.html) All told, the Guides provide tens of thousands of words of guidance for beginning and intermediate Rails developers. @@ -57,7 +56,7 @@ rake doc:guides This will put the guides inside `Rails.root/doc/guides` and you may start surfing straight away by opening `Rails.root/doc/guides/index.html` in your favourite browser. * Lead Contributors: [Rails Documentation Team](credits.html) -* Major contributions from [Xavier Noria":http://advogato.org/person/fxn/diary.html and "Hongli Lai](http://izumi.plan99.net/blog/.) +* Major contributions from [Xavier Noria](http://advogato.org/person/fxn/diary.html) and [Hongli Lai](http://izumi.plan99.net/blog/). * More information: * [Rails Guides hackfest](http://hackfest.rubyonrails.org/guide) * [Help improve Rails documentation on Git branch](http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch) @@ -144,7 +143,7 @@ development: * Lead Contributor: [Nick Sieger](http://blog.nicksieger.com/) * More information: - * [What's New in Edge Rails: Connection Pools](http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-connection-pools) + * [What's New in Edge Rails: Connection Pools](http://archives.ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-connection-pools) ### Hashes for Join Table Conditions @@ -164,7 +163,7 @@ Product.all(:joins => :photos, :conditions => { :photos => { :copyright => false ``` * More information: - * [What's New in Edge Rails: Easy Join Table Conditions](http://ryandaigle.com/articles/2008/7/7/what-s-new-in-edge-rails-easy-join-table-conditions) + * [What's New in Edge Rails: Easy Join Table Conditions](http://archives.ryandaigle.com/articles/2008/7/7/what-s-new-in-edge-rails-easy-join-table-conditions) ### New Dynamic Finders @@ -237,7 +236,7 @@ This will enable recognition of (among others) these routes: * Lead Contributor: [S. Brent Faulkner](http://www.unwwwired.net/) * More information: * [Rails Routing from the Outside In](routing.html#nested-resources) - * [What's New in Edge Rails: Shallow Routes](http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-shallow-routes) + * [What's New in Edge Rails: Shallow Routes](http://archives.ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-shallow-routes) ### Method Arrays for Member or Collection Routes @@ -285,7 +284,7 @@ Action Mailer Action Mailer now supports mailer layouts. You can make your HTML emails as pretty as your in-browser views by supplying an appropriately-named layout - for example, the `CustomerMailer` class expects to use `layouts/customer_mailer.html.erb`. * More information: - * [What's New in Edge Rails: Mailer Layouts](http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-mailer-layouts) + * [What's New in Edge Rails: Mailer Layouts](http://archives.ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-mailer-layouts) Action Mailer now offers built-in support for GMail's SMTP servers, by turning on STARTTLS automatically. This requires Ruby 1.8.7 to be installed. @@ -319,7 +318,7 @@ Other features of memoization include `unmemoize`, `unmemoize_all`, and `memoize * Lead Contributor: [Josh Peek](http://joshpeek.com/) * More information: - * [What's New in Edge Rails: Easy Memoization](http://ryandaigle.com/articles/2008/7/16/what-s-new-in-edge-rails-memoization) + * [What's New in Edge Rails: Easy Memoization](http://archives.ryandaigle.com/articles/2008/7/16/what-s-new-in-edge-rails-memoization) * [Memo-what? A Guide to Memoization](http://www.railway.at/articles/2008/09/20/a-guide-to-memoization) ### each_with_object @@ -387,9 +386,9 @@ To avoid deployment issues and make Rails applications more self-contained, it's You can unpack or install a single gem by specifying `GEM=_gem_name_` on the command line. -* Lead Contributor: [Matt Jones](http://github.com/al2o3cr) +* Lead Contributor: [Matt Jones](https://github.com/al2o3cr) * More information: - * [What's New in Edge Rails: Gem Dependencies](http://ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies) + * [What's New in Edge Rails: Gem Dependencies](http://archives.ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies) * [Rails 2.1.2 and 2.2RC1: Update Your RubyGems](http://afreshcup.com/2008/10/25/rails-212-and-22rc1-update-your-rubygems/) * [Detailed discussion on Lighthouse](http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1128) @@ -409,7 +408,7 @@ Deprecated A few pieces of older code are deprecated in this release: * `Rails::SecretKeyGenerator` has been replaced by `ActiveSupport::SecureRandom` -* `render_component` is deprecated. There's a [render_components plugin](http://github.com/rails/render_component/tree/master) available if you need this functionality. +* `render_component` is deprecated. There's a [render_components plugin](https://github.com/rails/render_component/tree/master) available if you need this functionality. * Implicit local assignments when rendering partials has been deprecated. ```ruby diff --git a/guides/source/ko/2_3_release_notes.md b/guides/source/ko/2_3_release_notes.md index 7aef566e40bc5..d1266e86b5d12 100644 --- a/guides/source/ko/2_3_release_notes.md +++ b/guides/source/ko/2_3_release_notes.md @@ -1,7 +1,7 @@ Ruby on Rails 2.3 Release Notes =============================== -Rails 2.3 delivers a variety of new and improved features, including pervasive Rack integration, refreshed support for Rails Engines, nested transactions for Active Record, dynamic and default scopes, unified rendering, more efficient routing, application templates, and quiet backtraces. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/master) in the main Rails repository on GitHub or review the `CHANGELOG` files for the individual Rails components. +Rails 2.3 delivers a variety of new and improved features, including pervasive Rack integration, refreshed support for Rails Engines, nested transactions for Active Record, dynamic and default scopes, unified rendering, more efficient routing, application templates, and quiet backtraces. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](https://github.com/rails/rails/commits/master) in the main Rails repository on GitHub or review the `CHANGELOG` files for the individual Rails components. -------------------------------------------------------------------------------- @@ -52,7 +52,7 @@ Documentation The [Ruby on Rails guides](http://guides.rubyonrails.org/) project has published several additional guides for Rails 2.3. In addition, a [separate site](http://edgeguides.rubyonrails.org/) maintains updated copies of the Guides for Edge Rails. Other documentation efforts include a relaunch of the [Rails wiki](http://newwiki.rubyonrails.org/) and early planning for a Rails Book. -* More Information: [Rails Documentation Projects](http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects.) +* More Information: [Rails Documentation Projects](http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects) Ruby 1.9.1 Support ------------------ @@ -123,14 +123,14 @@ Order.scoped_by_customer_id(12).scoped_by_status("open") There's nothing to define to use dynamic scopes: they just work. * Lead Contributor: [Yaroslav Markin](http://evilmartians.com/) -* More Information: [What's New in Edge Rails: Dynamic Scope Methods](http://ryandaigle.com/articles/2008/12/29/what-s-new-in-edge-rails-dynamic-scope-methods.) +* More Information: [What's New in Edge Rails: Dynamic Scope Methods](http://archives.ryandaigle.com/articles/2008/12/29/what-s-new-in-edge-rails-dynamic-scope-methods) ### Default Scopes Rails 2.3 will introduce the notion of _default scopes_ similar to named scopes, but applying to all named scopes or find methods within the model. For example, you can write `default_scope :order => 'name ASC'` and any time you retrieve records from that model they'll come out sorted by name (unless you override the option, of course). * Lead Contributor: Paweł Kondzior -* More Information: [What's New in Edge Rails: Default Scoping](http://ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping) +* More Information: [What's New in Edge Rails: Default Scoping](http://archives.ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping) ### Batch Processing @@ -156,7 +156,7 @@ Note that you should only use this method for batch processing: for small number * More Information (at that point the convenience method was called just `each`): * [Rails 2.3: Batch Finding](http://afreshcup.com/2009/02/23/rails-23-batch-finding/) - * [What's New in Edge Rails: Batched Find](http://ryandaigle.com/articles/2009/2/23/what-s-new-in-edge-rails-batched-find) + * [What's New in Edge Rails: Batched Find](http://archives.ryandaigle.com/articles/2009/2/23/what-s-new-in-edge-rails-batched-find) ### Multiple Conditions for Callbacks @@ -177,7 +177,7 @@ developers = Developer.find(:all, :group => "salary", :having => "sum(salary) > 10000", :select => "salary") ``` -* Lead Contributor: [Emilio Tagua](http://github.com/miloops) +* Lead Contributor: [Emilio Tagua](https://github.com/miloops) ### Reconnecting MySQL Connections @@ -233,7 +233,7 @@ If you're one of the people who has always been bothered by the special-case nam * More Information: * [The Death of Application.rb](http://afreshcup.com/2008/11/17/rails-2x-the-death-of-applicationrb/) - * [What's New in Edge Rails: Application.rb Duality is no More](http://ryandaigle.com/articles/2008/11/19/what-s-new-in-edge-rails-application-rb-duality-is-no-more) + * [What's New in Edge Rails: Application.rb Duality is no More](http://archives.ryandaigle.com/articles/2008/11/19/what-s-new-in-edge-rails-application-rb-duality-is-no-more) ### HTTP Digest Authentication Support @@ -259,7 +259,7 @@ end ``` * Lead Contributor: [Gregg Kellogg](http://www.kellogg-assoc.com/) -* More Information: [What's New in Edge Rails: HTTP Digest Authentication](http://ryandaigle.com/articles/2009/1/30/what-s-new-in-edge-rails-http-digest-authentication) +* More Information: [What's New in Edge Rails: HTTP Digest Authentication](http://archives.ryandaigle.com/articles/2009/1/30/what-s-new-in-edge-rails-http-digest-authentication) ### More Efficient Routing @@ -375,8 +375,8 @@ You can write this view in Rails 2.3: * Lead Contributor: [Eloy Duran](http://superalloy.nl/) * More Information: * [Nested Model Forms](http://weblog.rubyonrails.org/2009/1/26/nested-model-forms) - * [complex-form-examples](http://github.com/alloy/complex-form-examples) - * [What's New in Edge Rails: Nested Object Forms](http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes) + * [complex-form-examples](https://github.com/alloy/complex-form-examples) + * [What's New in Edge Rails: Nested Object Forms](http://archives.ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes) ### Smart Rendering of Partials @@ -392,7 +392,7 @@ render @article render @articles ``` -* More Information: [What's New in Edge Rails: render Stops Being High-Maintenance](http://ryandaigle.com/articles/2008/11/20/what-s-new-in-edge-rails-render-stops-being-high-maintenance) +* More Information: [What's New in Edge Rails: render Stops Being High-Maintenance](http://archives.ryandaigle.com/articles/2008/11/20/what-s-new-in-edge-rails-render-stops-being-high-maintenance) ### Prompts for Date Select Helpers @@ -419,7 +419,7 @@ You're likely familiar with Rails' practice of adding timestamps to static asset Asset hosts get more flexible in edge Rails with the ability to declare an asset host as a specific object that responds to a call. This allows you to implement any complex logic you need in your asset hosting. -* More Information: [asset-hosting-with-minimum-ssl](http://github.com/dhh/asset-hosting-with-minimum-ssl/tree/master) +* More Information: [asset-hosting-with-minimum-ssl](https://github.com/dhh/asset-hosting-with-minimum-ssl/tree/master) ### grouped_options_for_select Helper Method @@ -496,7 +496,7 @@ Active Support has a few interesting changes, including the introduction of `Obj A lot of folks have adopted the notion of using try() to attempt operations on objects. It's especially helpful in views where you can avoid nil-checking by writing code like `<%= @person.try(:name) %>`. Well, now it's baked right into Rails. As implemented in Rails, it raises `NoMethodError` on private methods and always returns `nil` if the object is nil. -* More Information: [try()](http://ozmm.org/posts/try.html.) +* More Information: [try()](http://ozmm.org/posts/try.html) ### Object#tap Backport @@ -531,7 +531,7 @@ If you look up the spec on the "json.org" site, you'll discover that all keys in ### Other Active Support Changes * You can use `Enumerable#none?` to check that none of the elements match the supplied block. -* If you're using Active Support [delegates](http://afreshcup.com/2008/10/19/coming-in-rails-22-delegate-prefixes/,) the new `:allow_nil` option lets you return `nil` instead of raising an exception when the target object is nil. +* If you're using Active Support [delegates](http://afreshcup.com/2008/10/19/coming-in-rails-22-delegate-prefixes/) the new `:allow_nil` option lets you return `nil` instead of raising an exception when the target object is nil. * `ActiveSupport::OrderedHash`: now implements `each_key` and `each_value`. * `ActiveSupport::MessageEncryptor` provides a simple way to encrypt information for storage in an untrusted location (like cookies). * Active Support's `from_xml` no longer depends on XmlSimple. Instead, Rails now includes its own XmlMini implementation, with just the functionality that it requires. This lets Rails dispense with the bundled copy of XmlSimple that it's been carting around. @@ -557,7 +557,7 @@ Rails Metal is a new mechanism that provides superfast endpoints inside of your ### Application Templates -Rails 2.3 incorporates Jeremy McAnally's [rg](http://github.com/jeremymcanally/rg/tree/master) application generator. What this means is that we now have template-based application generation built right into Rails; if you have a set of plugins you include in every application (among many other use cases), you can just set up a template once and use it over and over again when you run the `rails` command. There's also a rake task to apply a template to an existing application: +Rails 2.3 incorporates Jeremy McAnally's [rg](https://github.com/jm/rg) application generator. What this means is that we now have template-based application generation built right into Rails; if you have a set of plugins you include in every application (among many other use cases), you can just set up a template once and use it over and over again when you run the `rails` command. There's also a rake task to apply a template to an existing application: ``` rake rails:template LOCATION=~/template.rb @@ -570,7 +570,7 @@ This will layer the changes from the template on top of whatever code the projec ### Quieter Backtraces -Building on Thoughtbot's [Quiet Backtrace](https://github.com/thoughtbot/quietbacktrace) plugin, which allows you to selectively remove lines from `Test::Unit` backtraces, Rails 2.3 implements `ActiveSupport::BacktraceCleaner` and `Rails::BacktraceCleaner` in core. This supports both filters (to perform regex-based substitutions on backtrace lines) and silencers (to remove backtrace lines entirely). Rails automatically adds silencers to get rid of the most common noise in a new application, and builds a `config/backtrace_silencers.rb` file to hold your own additions. This feature also enables prettier printing from any gem in the backtrace. +Building on thoughtbot's [Quiet Backtrace](https://github.com/thoughtbot/quietbacktrace) plugin, which allows you to selectively remove lines from `Test::Unit` backtraces, Rails 2.3 implements `ActiveSupport::BacktraceCleaner` and `Rails::BacktraceCleaner` in core. This supports both filters (to perform regex-based substitutions on backtrace lines) and silencers (to remove backtrace lines entirely). Rails automatically adds silencers to get rid of the most common noise in a new application, and builds a `config/backtrace_silencers.rb` file to hold your own additions. This feature also enables prettier printing from any gem in the backtrace. ### Faster Boot Time in Development Mode with Lazy Loading/Autoload @@ -603,8 +603,8 @@ Deprecated A few pieces of older code are deprecated in this release: -* If you're one of the (fairly rare) Rails developers who deploys in a fashion that depends on the inspector, reaper, and spawner scripts, you'll need to know that those scripts are no longer included in core Rails. If you need them, you'll be able to pick up copies via the [irs_process_scripts](http://github.com/rails/irs_process_scripts/tree) plugin. -* `render_component` goes from "deprecated" to "nonexistent" in Rails 2.3. If you still need it, you can install the [render_component plugin](http://github.com/rails/render_component/tree/master.) +* If you're one of the (fairly rare) Rails developers who deploys in a fashion that depends on the inspector, reaper, and spawner scripts, you'll need to know that those scripts are no longer included in core Rails. If you need them, you'll be able to pick up copies via the [irs_process_scripts](https://github.com/rails/irs_process_scripts/tree) plugin. +* `render_component` goes from "deprecated" to "nonexistent" in Rails 2.3. If you still need it, you can install the [render_component plugin](https://github.com/rails/render_component/tree/master.) * Support for Rails components has been removed. * If you were one of the people who got used to running `script/performance/request` to look at performance based on integration tests, you need to learn a new trick: that script has been removed from core Rails now. There’s a new request_profiler plugin that you can install to get the exact same functionality back. * `ActionController::Base#session_enabled?` is deprecated because sessions are lazy-loaded now. @@ -612,7 +612,7 @@ A few pieces of older code are deprecated in this release: * Some integration test helpers have been removed. `response.headers["Status"]` and `headers["Status"]` will no longer return anything. Rack does not allow "Status" in its return headers. However you can still use the `status` and `status_message` helpers. `response.headers["cookie"]` and `headers["cookie"]` will no longer return any CGI cookies. You can inspect `headers["Set-Cookie"]` to see the raw cookie header or use the `cookies` helper to get a hash of the cookies sent to the client. * `formatted_polymorphic_url` is deprecated. Use `polymorphic_url` with `:format` instead. * The `:http_only` option in `ActionController::Response#set_cookie` has been renamed to `:httponly`. -* The `:connector` and `:skip_last_comma` options of `to_sentence` have been replaced by `:words_connnector`, `:two_words_connector`, and `:last_word_connector` options. +* The `:connector` and `:skip_last_comma` options of `to_sentence` have been replaced by `:words_connector`, `:two_words_connector`, and `:last_word_connector` options. * Posting a multipart form with an empty `file_field` control used to submit an empty string to the controller. Now it submits a nil, due to differences between Rack's multipart parser and the old Rails one. Credits diff --git a/guides/source/ko/3_0_release_notes.md b/guides/source/ko/3_0_release_notes.md index 6cb6f738e0d36..44b0406bb3e7a 100644 --- a/guides/source/ko/3_0_release_notes.md +++ b/guides/source/ko/3_0_release_notes.md @@ -15,7 +15,7 @@ Even if you don't give a hoot about any of our internal cleanups, Rails 3.0 is g On top of all that, we've tried our best to deprecate the old APIs with nice warnings. That means that you can move your existing application to Rails 3 without immediately rewriting all your old code to the latest best practices. -These release notes cover the major upgrades, but don't include every little bug fix and change. Rails 3.0 consists of almost 4,000 commits by more than 250 authors! If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/master) in the main Rails repository on GitHub. +These release notes cover the major upgrades, but don't include every little bug fix and change. Rails 3.0 consists of almost 4,000 commits by more than 250 authors! If you want to see everything, check out the [list of commits](https://github.com/rails/rails/commits/master) in the main Rails repository on GitHub. -------------------------------------------------------------------------------- @@ -61,7 +61,7 @@ The `config.gem` method is gone and has been replaced by using `bundler` and a ` ### Upgrade Process -To help with the upgrade process, a plugin named [Rails Upgrade](http://github.com/rails/rails_upgrade) has been created to automate part of it. +To help with the upgrade process, a plugin named [Rails Upgrade](https://github.com/rails/rails_upgrade) has been created to automate part of it. Simply install the plugin, then run `rake rails:upgrade:check` to check your app for pieces that need to be updated (with links to information on how to update them). It also offers a task to generate a `Gemfile` based on your current `config.gem` calls and a task to generate a new routes file from your current one. To get the plugin, simply run the following: @@ -86,7 +86,7 @@ $ cd myapp ### Vendoring Gems -Rails now uses a `Gemfile` in the application root to determine the gems you require for your application to start. This `Gemfile` is processed by the [Bundler](http://github.com/carlhuda/bundler,) which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn't depend on the system gems. +Rails now uses a `Gemfile` in the application root to determine the gems you require for your application to start. This `Gemfile` is processed by the [Bundler](https://github.com/carlhuda/bundler,) which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn't depend on the system gems. More information: - [bundler homepage](http://gembundler.com) @@ -138,14 +138,14 @@ More Information: - [Rails Edge Architecture](http://yehudakatz.com/2009/06/11/r ### Arel Integration -[Arel](http://github.com/brynary/arel) (or Active Relation) has been taken on as the underpinnings of Active Record and is now required for Rails. Arel provides an SQL abstraction that simplifies out Active Record and provides the underpinnings for the relation functionality in Active Record. +[Arel](https://github.com/brynary/arel) (or Active Relation) has been taken on as the underpinnings of Active Record and is now required for Rails. Arel provides an SQL abstraction that simplifies out Active Record and provides the underpinnings for the relation functionality in Active Record. More information: - [Why I wrote Arel](http://magicscalingsprinkles.wordpress.com/2010/01/28/why-i-wrote-arel/.) ### Mail Extraction -Action Mailer ever since its beginnings has had monkey patches, pre parsers and even delivery and receiver agents, all in addition to having TMail vendored in the source tree. Version 3 changes that with all email message related functionality abstracted out to the [Mail](http://github.com/mikel/mail) gem. This again reduces code duplication and helps create definable boundaries between Action Mailer and the email parser. +Action Mailer ever since its beginnings has had monkey patches, pre parsers and even delivery and receiver agents, all in addition to having TMail vendored in the source tree. Version 3 changes that with all email message related functionality abstracted out to the [Mail](https://github.com/mikel/mail) gem. This again reduces code duplication and helps create definable boundaries between Action Mailer and the email parser. More information: - [New Action Mailer API in Rails 3](http://lindsaar.net/2010/1/26/new-actionmailer-api-in-rails-3) @@ -155,13 +155,13 @@ Documentation The documentation in the Rails tree is being updated with all the API changes, additionally, the [Rails Edge Guides](http://edgeguides.rubyonrails.org/) are being updated one by one to reflect the changes in Rails 3.0. The guides at [guides.rubyonrails.org](http://guides.rubyonrails.org/) however will continue to contain only the stable version of Rails (at this point, version 2.3.5, until 3.0 is released). -More Information: - [Rails Documentation Projects](http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects.) +More Information: - [Rails Documentation Projects](http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects) Internationalization -------------------- -A large amount of work has been done with I18n support in Rails 3, including the latest [I18n](http://github.com/svenfuchs/i18n) gem supplying many speed improvements. +A large amount of work has been done with I18n support in Rails 3, including the latest [I18n](https://github.com/svenfuchs/i18n) gem supplying many speed improvements. * I18n for any object - I18n behavior can be added to any object by including `ActiveModel::Translation` and `ActiveModel::Validations`. There is also an `errors.messages` fallback for translations. * Attributes can have default translations. @@ -213,7 +213,6 @@ Railties now deprecates: More information: * [Discovering Rails 3 generators](http://blog.plataformatec.com.br/2010/01/discovering-rails-3-generators) -* [Making Generators for Rails 3 with Thor](http://caffeinedd.com/guides/331-making-generators-for-rails-3-with-thor) * [The Rails Module (in Rails 3)](http://litanyagainstfear.com/blog/2010/02/03/the-rails-module/) Action Pack @@ -250,7 +249,7 @@ Deprecations: More Information: -* [Render Options in Rails 3](http://www.engineyard.com/blog/2010/render-options-in-rails-3/) +* [Render Options in Rails 3](https://blog.engineyard.com/2010/render-options-in-rails-3) * [Three reasons to love ActionController::Responder](http://weblog.rubyonrails.org/2009/8/31/three-reasons-love-responder) @@ -310,7 +309,7 @@ More Information: Major re-write was done in the Action View helpers, implementing Unobtrusive JavaScript (UJS) hooks and removing the old inline AJAX commands. This enables Rails to use any compliant UJS driver to implement the UJS hooks in the helpers. -What this means is that all previous `remote_` helpers have been removed from Rails core and put into the [Prototype Legacy Helper](http://github.com/rails/prototype_legacy_helper.) To get UJS hooks into your HTML, you now pass `:remote => true` instead. For example: +What this means is that all previous `remote_` helpers have been removed from Rails core and put into the [Prototype Legacy Helper](https://github.com/rails/prototype_legacy_helper.) To get UJS hooks into your HTML, you now pass `:remote => true` instead. For example: ```ruby form_for @post, :remote => true @@ -576,7 +575,7 @@ The following methods have been removed because they are no longer used in the f Action Mailer ------------- -Action Mailer has been given a new API with TMail being replaced out with the new [Mail](http://github.com/mikel/mail) as the Email library. Action Mailer itself has been given an almost complete re-write with pretty much every line of code touched. The result is that Action Mailer now simply inherits from Abstract Controller and wraps the Mail gem in a Rails DSL. This reduces the amount of code and duplication of other libraries in Action Mailer considerably. +Action Mailer has been given a new API with TMail being replaced out with the new [Mail](https://github.com/mikel/mail) as the Email library. Action Mailer itself has been given an almost complete re-write with pretty much every line of code touched. The result is that Action Mailer now simply inherits from Abstract Controller and wraps the Mail gem in a Rails DSL. This reduces the amount of code and duplication of other libraries in Action Mailer considerably. * All mailers are now in `app/mailers` by default. * Can now send email using new API with three methods: `attachments`, `headers` and `mail`. diff --git a/guides/source/ko/3_1_release_notes.md b/guides/source/ko/3_1_release_notes.md index 5c99892e39c14..ab6cbc7574058 100644 --- a/guides/source/ko/3_1_release_notes.md +++ b/guides/source/ko/3_1_release_notes.md @@ -94,7 +94,7 @@ gem 'jquery-rails' # config.assets.manifest = YOUR_PATH # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) - # config.assets.precompile `= %w( search.js ) + # config.assets.precompile `= %w( admin.js admin.css ) # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. @@ -553,4 +553,4 @@ Credits See the [full list of contributors to Rails](http://contributors.rubyonrails.org/) for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. -Rails 3.1 Release Notes were compiled by [Vijay Dev](https://github.com/vijaydev.) +Rails 3.1 Release Notes were compiled by [Vijay Dev](https://github.com/vijaydev) diff --git a/guides/source/ko/4_0_release_notes.md b/guides/source/ko/4_0_release_notes.md index d9ecc1e902ba1..0bb5ab669a8ae 100644 --- a/guides/source/ko/4_0_release_notes.md +++ b/guides/source/ko/4_0_release_notes.md @@ -106,7 +106,7 @@ Rails 4.0에서는 많은 기능이 분리되어 gem이 되었습니다. 분리 * Active Record 모델에 대량할당 보호([GitHub](https://github.com/rails/protected_attributes), [Pull Request](https://github.com/rails/rails/pull/7251)) * ActiveRecord::SessionStore([GitHub](https://github.com/rails/activerecord-session_store), [Pull Request](https://github.com/rails/rails/pull/7436)) * Active Record Observer 패턴([GitHub](https://github.com/rails/rails-observers)、[Commit](https://github.com/rails/rails/commit/39e85b3b90c58449164673909a6f1893cba290b2)) -* Active Resource([GitHub](https://github.com/rails/activeresource), [Pull Request](https://github.com/rails/rails/pull/572), [블로그 포스팅](http://yetimedia.tumblr.com/post/35233051627/activeresource-is-dead-long-live-activeresource)) +* Active Resource([GitHub](https://github.com/rails/activeresource), [Pull Request](https://github.com/rails/rails/pull/572), [블로그 포스팅](http://yetimedia-blog-blog.tumblr.com/post/35233051627/activeresource-is-dead-long-live-activeresource)) * 액션 캐시([GitHub](https://github.com/rails/actionpack-action_caching), [Pull Request](https://github.com/rails/rails/pull/7833)) * 페이지 캐시([GitHub](https://github.com/rails/actionpack-page_caching), [Pull Request](https://github.com/rails/rails/pull/7833)) * Sprockets([GitHub](https://github.com/rails/sprockets-rails)) diff --git a/guides/source/ko/4_2_release_notes.md b/guides/source/ko/4_2_release_notes.md index 2c8add32cceaf..a893a424c8311 100644 --- a/guides/source/ko/4_2_release_notes.md +++ b/guides/source/ko/4_2_release_notes.md @@ -294,7 +294,7 @@ development: url: http://localhost:3001 namespace: my_app_development - # config/production.rb + # config/environments/production.rb Rails.application.configure do config.middleware.use ExceptionNotifier, config_for(:exception_notification) end @@ -673,7 +673,7 @@ Active Support * `Concern#class_methods`가 도입되었습니다. `Kernel#concern`와 동일하게 `module ClassMethods`를 대신하는 것이며, `module Foo; extend ActiveSupport::Concern; end`와 같은 코드를 피하기 위한 것입니다. ([Commit](https://github.com/rails/rails/commit/b16c36e688970df2f96f793a759365b248b582ad)) -* 자동 로딩이나 리로딩에 대한 [새로운 가이드](constant_autoloading_and_reloading.html)가 추가되었습니다. +* 자동 로딩이나 리로딩에 대한 [새로운 가이드](autoloading_and_reloading_constants.html)가 추가되었습니다. 크레딧 표기 ------- diff --git a/guides/source/ko/5_0_release_notes.md b/guides/source/ko/5_0_release_notes.md index fa640ba09c944..362df9904d4c8 100644 --- a/guides/source/ko/5_0_release_notes.md +++ b/guides/source/ko/5_0_release_notes.md @@ -3,40 +3,50 @@ Ruby on Rails 5.0 릴리스 노트 Rails 5.0에서 주목할 점 -* 액션케이블 +* 액션 케이블 * Rails API -* 액션레코드 속성 API +* 액션 레코드 속성 API * 테스트 러너 * Rake 명령을 `rails` 명령으로 통일 * Sprockets 3 * Turbolinks 5 * 루비 2.2.2 이상의 버전을 요구 -이 릴리스에서는 주요 변경점에 대해서만 설명합니다. 수정된 버그 및 변경점에 대해서는 Github Rails 저장소에 있는 [커밋 목록](https://github.com/rails/rails/commits/5-0-stable)의 changelog를 참고해주세요. +이 릴리스에서는 주요 변경점에 대해서만 설명합니다. 수정된 버그 및 변경점에 대해서는 Github Rails +저장소에 있는 changelog나 [커밋 목록](https://github.com/rails/rails/commits/5-0-stable)을 +참고해주세요. -------------------------------------------------------------------------------- Rails 5.0로 업그레이드하기 ---------------------- -기존 애플리케이션을 업그레이드한다면 그 전에 충분한 테스트 커버리지를 확보하는 것이 좋습니다. 애플리케이션이 Rails 4.2로 업그레이드되지 않았다면 우선 이를 우선하고, 애플리케이션이 정상적으로 동작하는지 충분히 확인한 뒤에 Rails 5.0을 올려주세요. 업그레이드 시의 주의점에 대해서는 [Ruby on Rails 업그레이드 가이드](upgrading_ruby_on_rails.html#rails-4-2에서-Rails-5-0로-업그레이드)를 참고해주세요. +기존 애플리케이션을 업그레이드한다면 그 전에 충분한 테스트 커버리지를 확보하는 것이 좋습니다. +애플리케이션이 Rails 4.2로 업그레이드되지 않았다면 우선 이를 우선하고, 애플리케이션이 정상적으로 +동작하는지 충분히 확인한 뒤에 Rails 5.0을 올려주세요. 업그레이드 시의 주의점에 대해서는 +[Ruby on Rails 업그레이드 가이드](upgrading_ruby_on_rails.html#rails-4-2에서-rails-5-0로-업그레이드)를 참고해주세요. 주요 변경점 -------------- -### 액션케이블 +### 액션 케이블 -액션케이블은 레일스 5에서 새롭게 도입된 프레임워크로 레일스 애플리케이션에서 [웹 소켓](https://en.wikipedia.org/wiki/WebSocket)과 관련된 부분을 부드럽게 통합합니다. +액션 케이블은 Rails 5에서 새롭게 도입된 프레임워크로 Rails 애플리케이션에서 +[웹 소켓](https://en.wikipedia.org/wiki/WebSocket)과 관련된 부분을 부드럽게 통합합니다. -액션 케이블을 도입하면, 레일스 애플리케이션의 좋은 효율과 확장 가능성을 유지하며 기존의 레일스 애플리케이션과 동일한 스타일, 방법으로 실시간 기능을 루비로 작성할 수 있습니다. 액션케이블은 클라이언트 쪽의 자바 스크립트 프레임워크와 서버 쪽의 루비 프레임워크를 동시에 제공합니다. 액션레코드와 같은 ORM으로 작성된 모든 도메인 모델에 접근할 수 있습니다. +액션 케이블을 도입하면, Rails 애플리케이션의 좋은 생산성과 확장 가능성을 유지하며 기존의 Rails +애플리케이션과 동일한 스타일, 방법으로 실시간 기능을 루비로 작성할 수 있습니다. 액션 케이블은 클라이언트 +쪽의 자바 스크립트 프레임워크와 서버 쪽의 루비 프레임워크를 동시에 제공합니다. 액션 레코드와 같은 +ORM으로 작성된 모든 도메인 모델에 접근할 수 있습니다. -자세한 설명은 [액션케이블의 개요](action_cable_overview.html)를 참조해주세요. +자세한 설명은 [액션 케이블의 개요](action_cable_overview.html)를 참조해주세요. ### API 애플리케이션 -API만을 제공하는 간단한 애플리케이션을 레일스를 사용해 생성할 수 있게 되었습니다. -[Twitter](https://dev.twitter.com) API나 [GitHub](http://developer.github.com) API와 같은 공용 API 서버는 물론, 그 외의 애플리케이션을 위한 API 서버를 작성할 때에도 편리합니다. +API만을 제공하는 간단한 애플리케이션을 Rails를 사용해 생성할 수 있게 되었습니다. +[Twitter](https://dev.twitter.com) API나 [GitHub](http://developer.github.com) +API와 같은 공용 API 서버는 물론, 그 외의 애플리케이션을 위한 API 서버를 작성할 때에도 편리합니다. API Rails 애플리케이션을 생성하려면 다음의 명령어를 사용합니다. @@ -46,24 +56,29 @@ $ rails new my_api --api 이 명령은 다음 3개의 동작을 실행합니다. -- 사용하는 미들웨어를 일반적인 상황보다 적게 사용하여 서버를 실행하도록 설정합니다. 특히 브라우저용 애플리케이션에서 유용한 미들웨어(쿠키에 대한 지원 등)를 일체 사용할 수 없게 됩니다. -- `ApplicationController`는 기존의 `ActionController::Base` 대신에 `ActionController::API`를 상속합니다. 미들웨어와 마찬가지로 액션컨트롤러 모듈에서 브라우저용 애플리케이션에서만 사용되는 모듈을 모두 제외합니다. +- 사용하는 미들웨어를 일반적인 상황보다 적게 사용하여 서버를 실행하도록 설정합니다. + 특히 브라우저용 애플리케이션에서 유용한 미들웨어(쿠키에 대한 지원 등)를 일체 사용할 수 없게 됩니다. +- `ApplicationController`는 기존의 `ActionController::Base` 대신에 + `ActionController::API`를 상속합니다. 미들웨어와 마찬가지로 액션컨트롤러 모듈에서 브라우저용 + 애플리케이션에서만 사용되는 모듈을 모두 제외합니다. - 제너레이터가 뷰, 헬퍼, 애셋을 생성하지 않습니다. -생성된 API 애플리케이션은 API 제공하기 위한 기본이 되며, 필요에 따라서 [기능을 추가](api_app.html) 할 수 있게 됩니다. +생성된 API 애플리케이션은 API 제공하기 위한 토대가 되며, 필요에 따라서 +[기능을 추가](api_app.html) 할 수 있습니다. -자세한 설명은 [레일스에서 API 전용 애플리케이션을 만들기](api_app.html)를 참고하세요. +자세한 설명은 [Rails에서 API 전용 애플리케이션을 만들기](api_app.html)를 참고하세요. -### 액티브레코드 속성 API +### 액티브 레코드 속성 API 모델에 type 속성을 정의합니다. 필요하다면 기존의 속성을 덮어써도 좋습니다. 이를 사용하여 모델의 속성을 SQL로 어떻게 상호변환할지를 제어할 수 있습니다. 또한 `ActiveRecord::Base.where`에 넘겨진 값의 동작을 변경할 수도 있습니다. -이를 통하여 구현의 상세나 몽키 패치에 의존하지 않고 액티브레코드의 대부분에서 도메인 객체를 사용할 수 있게 됩니다. +이를 통하여 구현의 세부나 몽키 패치에 의존하지 않고 액티브 레코드의 대부분에서 도메인 +객체를 사용할 수 있게 됩니다. 다음과 같이 사용할 수도 있습니다. -* 액티브레코드에서 검출된 타입을 덮어쓸 수 있습니다. +* 액티브 레코드에서 검출된 타입을 덮어쓸 수 있습니다. * 기본 동작을 지정할 수 있습니다. * 속성은 데이터베이스 컬럼을 요구하지 않습니다. @@ -97,18 +112,20 @@ store_listing.price_in_cents # => 10 StoreListing.new.my_string # => "new default" StoreListing.new.my_default_proc # => 2015-05-30 11:04:48 -0600 model = StoreListing.new(field_without_db_column: ["1", "2", "3"]) -model.attributes #=> {field_without_db_column: [1, 2, 3]} +model.attributes # => {field_without_db_column: [1, 2, 3]} ``` **커스텀 타입 만들기:** 독자적인 타입을 정의할 수 있으며, 이는 값의 타입으로 정의된 메소드에 응답하는 경우에 한해서만 가능합니다. -`deserialize` 메소드나 `cast` 메소드는 작성한 타입 객체로 호출되어 데이터베이스나 컨트롤러로부터의 받은 실제 입력을 인자로 사용합니다. +`deserialize` 메소드나 `cast` 메소드는 작성한 타입 객체로 호출되어 데이터베이스나 컨트롤러에게 +받은 실제 입력을 인자로 사용합니다. 이는 통화 변환처럼 직접 별도의 변환을 해야하는 경우에 유용합니다. **쿼리하기:** -`ActiveRecord::Base.where`이 호출되면 모델 클래스에 정의된 타입을 사용하여 값을 SQL로 변환하고, 그 값의 객체로 `serialize`를 호출합니다. +`ActiveRecord::Base.where`이 호출되면 모델 클래스에 정의된 타입을 사용하여 값을 SQL로 변환하고, +그 값의 객체로 `serialize`를 호출합니다. 이를 통해서 SQL 쿼리를 실행할 때에 객체를 어떻게 변환할지를 지정할 수 있게 됩니다. @@ -121,19 +138,20 @@ model.attributes #=> {field_without_db_column: [1, 2, 3]} ### 테스트 러너 -새로운 테스트 러너가 도입되어, 레일스에서의 테스트 실행 기능이 강화되었습니다. +새로운 테스트 러너가 도입되어, Rails에서의 테스트 실행 기능이 강화되었습니다. `bin/rails test`로 명령하면 테스트 러너를 사용할 수 있습니다. -테스트 러너는 `RSpec`, `minitest-reporters`, `maxitest`로부터 영감을 얻었습니다. +테스트 러너는 `RSpec`, `minitest-reporters`, `maxitest` 등으로부터 영감을 얻었습니다. 다음과 같은 많은 개선이 이루어졌습니다. -- 테스트의 줄번호를 지정하여 한 테스트만을 실행. -- 테스트의 줄번호를 지정하여 여러 테스트를 실행. +- 테스트의 줄번호를 지정하여 한 테스트만을 실행합니다. +- 테스트의 줄번호를 지정하여 여러 테스트를 실행합니다. - 실패한 경우에 보여주는 메시지가 개선되어, 실패한 테스트를 곧장 재실행할 수 있게 되었습니다. - `-f` 옵션을 사용하면 실패했을 때에 곧바로 테스트를 정지할 수 있습니다. - `-d` 옵션을 사용하면 테스트가 완료될때까지 메시지 출력을 미룰 수 있습니다. - `-b` 옵션을 사용하면 예외에 대한 전체 백트레이스를 얻을 수 있습니다. -- `Minitest`와 통합되어 `-s`로 시드 데이터를 지정, `-n`으로 특정 테스트명을 지정, `-v`로 자세한 메시지 출력을 활성화 하는 등 다양한 옵션을 사용할 수 있게 되었습니다. +- `Minitest`와 통합되어 `-s`로 시드 데이터를 지정, `-n`으로 특정 테스트명을 지정, + `-v`로 자세한 메시지 출력을 활성화 하는 등 다양한 옵션을 사용할 수 있게 되었습니다. - 테스트 출력에 색깔이 추가되었습니다. Railties @@ -143,7 +161,7 @@ Railties ### 제거된 것들 -* `debugger`를 지원하지 않습니다. `debugger`는 루비 2.2에서는 지원되지 않으므로 앞으로는 byebug를 사용할 것. +* `debugger`를 지원하지 않습니다. `debugger`는 루비 2.2에서는 지원되지 않으므로 앞으로는 byebug를 사용. ([commit](https://github.com/rails/rails/commit/93559da4826546d07014f8cfa399b64b4a143127)) * 제거 예정이었던 `test:all` 태스크와 `test:all:db` 태스크를 제거. @@ -185,10 +203,10 @@ Railties ([commit](https://github.com/rails/rails/commit/89a12c931b1f00b90e74afffcdc2fc21f14ca663), [Pull Request](https://github.com/rails/rails/pull/22068)) -* 레일스 애플리케이션을 touch `tmp/restart.txt`로 재기동하는 `bin/rails restart` 태스크가 추가됨. +* Rails 애플리케이션을 touch `tmp/restart.txt`로 재기동하는 `bin/rails restart` 태스크가 추가됨. ([Pull Request](https://github.com/rails/rails/pull/18965)) -* 모든 정의된 이니셜라이져를 레일스가 실행하는 순서대로 출력하는 `bin/rails initializers` 태스크가 추가됨. +* 모든 정의된 이니셜라이져를 Rails가 실행하는 순서대로 출력하는 `bin/rails initializers` 태스크가 추가됨. ([Pull Request](https://github.com/rails/rails/pull/19323)) * development 모드에서 캐시의 활성화 여부를 지정하는 `bin/rails dev:cache`가 추가됨. @@ -345,10 +363,10 @@ Action Pack * `protect_from_forgery`의 prepend의 기본값이 `false`로 변경됨. ([commit](https://github.com/rails/rails/commit/39794037817703575c35a75f1961b01b83791191)) -* `ActionController::TestCase`는 레일스 5.1에서 gem으로 추출될 예정. 앞으로는 `ActionDispatch::IntegrationTest`를 사용. +* `ActionController::TestCase`는 Rails 5.1에서 gem으로 추출될 예정. 앞으로는 `ActionDispatch::IntegrationTest`를 사용. ([commit](https://github.com/rails/rails/commit/4414c5d1795e815b102571425974a8b1d46d932d)) -* 레일스에서 생성하는 ETag이 '강한' 방식에서 '약한' 방식으로 변경됨. +* Rails에서 생성하는 ETag이 '강한' 방식에서 '약한' 방식으로 변경됨. ([Pull Request](https://github.com/rails/rails/pull/17573)) * 컨트롤러 액션에서 `render`가 명시적으로 호출되지 않고, 대응하는 템플릿도 없는 경우, 에러 대신에 `head :no_content`를 암묵적으로 호출하게 됨. @@ -360,10 +378,6 @@ Action Pack * 요청의 인코딩과 응답을 해석하는 부분이 통합 테스트에 추가됨. ([Pull Request](https://github.com/rails/rails/pull/21671)) -* 컨트롤러 액션에서 응답이 명시적으로 지정되지 않은 경우의 기본 랜더링 방식이 변경됨. - ([Pull Request](https://github.com/rails/rails/pull/23827)) - - * 컨트롤러 레벨에서 뷰 컨텍스트에 접근하는 `ActionController#helpers`가 추가됨. ([Pull Request](https://github.com/rails/rails/pull/24866)) @@ -502,7 +516,7 @@ Active Record * 오래된 `mysql` 데이터베이스 어댑터에 대한 지원이 제거됨. 앞으로는 `mysql2`를 사용. 오래된 어댑터에 대한 유지 보수 담당자가 정해지면 해당 어댑터는 별도의 gem으로 분리될 예정. - ([Pull Request 1](https://github.com/rails/rails/pull/22642)], [Pull Request 2](https://github.com/rails/rails/pull/22715)) + ([Pull Request 1](https://github.com/rails/rails/pull/22642), [Pull Request 2](https://github.com/rails/rails/pull/22715)) * `protected_attributes` 잼 지원이 종료됨. ([commit](https://github.com/rails/rails/commit/f4fbc0301021f13ae05c8e941c8efc4ae351fdf9)) @@ -513,6 +527,9 @@ Active Record * `activerecord-deprecated_finders` 잼 지원이 종료됨. ([commit](https://github.com/rails/rails/commit/78dab2a8569408658542e462a957ea5a35aa4679)) +* `ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES` 상수가 제거됨. + ([commit](https://github.com/rails/rails/commit/a502703c3d2151d4d3b421b29fefdac5ad05df61)) + ### 제거 예정 * 쿼리로 클래스를 값으로 넘기는 기능이 제거 예정. 사용자는 문자열을 넘길 것. @@ -604,9 +621,6 @@ Active Record * `ActiveRecord::Relation`에 `#or` 메소드를 추가. WHERE절이나 HAVING절을 결합. ([commit](https://github.com/rails/rails/commit/b0b37942d729b6bdcd2e3178eda7fa1de203b3d0)) -* `#touch`에 `:time` 옵션을 추가. - ([Pull Request](https://github.com/rails/rails/pull/18956)) - * `ActiveRecord::Base.suppress`을 추가. 지정 블럭을 실행 중에 수신자가 저장되지 않도록 함. ([Pull Request](https://github.com/rails/rails/pull/18910)) @@ -692,6 +706,15 @@ Active Record * `touch` 메소드에 `:time` 옵션을 추가. 레코드에 현재 시각 이외의 시각을 지정할 수 있음. ([Pull Request](https://github.com/rails/rails/pull/18956)) +* 얕은 에러가 발생하지 않도록 트랜잭션 콜백을 변경. + 이전에는 트랜잭션 콜백 내부에서 발생한 어떤 에러든 (새로 제거 예정이 된) + `raise_in_transactional_callbacks = true`를 + 사용하지 않더라도 처리했음. + + 더이상 이 에러들은 자동으로 처리되지 않으며, 다른 콜백들과 마찬가지로 해당하는 에러를 처리해줄 + 때까지 거슬러 올라게 됨. + ([commit](https://github.com/rails/rails/commit/07d3d402341e81ada0214f2cb2be1da69eadfe72)) + Active Model ------------ diff --git a/guides/source/ko/action_cable_overview.md b/guides/source/ko/action_cable_overview.md index c2bb3efaf45b1..58541fe58fdf7 100644 --- a/guides/source/ko/action_cable_overview.md +++ b/guides/source/ko/action_cable_overview.md @@ -1,7 +1,8 @@ 액션케이블 개요 ===================== -이 가이드에서는 액션케이블의 구조와 웹소켓을 레일스 애플리케이션에 도입하여 실시간 기능을 구현하는 방법에 관해서 설명합니다. +이 가이드에서는 액션케이블의 구조와 웹소켓을 레일스 애플리케이션에 도입하여 +실시간 기능을 구현하는 방법에 관해서 설명합니다. 이 가이드의 내용: @@ -15,20 +16,31 @@ 들어가면서 ------------ -액션케이블은 [웹소켓](https://en.wikipedia.org/wiki/WebSocket)과 레일스의 다른 부분들을 매끄럽게 통합합니다. 액션케이블을 도입함으로써 레일스 애플리케이션의 고효율성과 확장성에 영향을 주는 일이 없이 일반 레일스 애플리케이션과 같은 스타일/방법으로 실시간 기능을 루비로 작성할 수 있습니다. 이는 클라이언트의 자바스크립트 프레임워크와 서버의 루비 프레임워크를 동시에 제공하는 풀스택 프레임워크입니다. 그러므로 액티브레코드 등의 ORM으로 작성된 모든 도메인 모델에 접근할 수 있습니다. +액션케이블은 [웹소켓](https://en.wikipedia.org/wiki/WebSocket)과 레일스의 다른 부분을 +매끄럽게 통합합니다. 액션케이블을 도입함으로써 레일스 애플리케이션의 고효율성과 확장성에 영향을 주는 일 없이 +일반 레일스 애플리케이션과 같은 스타일/방법으로 실시간 기능을 루비로 작성할 수 있습니다. +이는 클라이언트의 자바스크립트 프레임워크와 서버의 루비 프레임워크를 동시에 제공하는 풀스택 프레임워크입니다. +그러므로 액티브레코드 등의 ORM으로 작성된 모든 도메인 모델에 접근할 수 있습니다. Pub/Sub에 대해서 --------------- -[Pub/Sub](https://ko.wikipedia.org/wiki/%EB%B0%9C%ED%96%89-%EA%B5%AC%EB%8F%85_%EB%AA%A8%EB%8D%B8)은 발행/구독이라고도 불리는 메시지 큐 패러다임입니다. 발신자는 수신자들이 누구인지 알지 못하는 상태로 수신자의 추상 클래스에 정보를 전송합니다. 액션케이블에서는 이 방식을 사용하여 서버와 여러 클라이언트 간의 통신을 구현합니다. +[Pub/Sub](https://ko.wikipedia.org/wiki/%EB%B0%9C%ED%96%89-%EA%B5%AC%EB%8F%85_%EB%AA%A8%EB%8D%B8)은 +발행/구독이라고도 불리는 메시지 큐 패러다임입니다. 발신자는 수신자들이 누구인지 알지 못하는 상태로 +수신자의 추상 클래스에 정보를 전송합니다. 액션케이블에서는 이 접근 방식을 통해 서버와 여러 클라이언트 간의 +통신을 구현합니다. ## 서버 컴포넌트 ### 커넥션 -*커넥션*(connection)은 클라이언트와 서버 간의 관계의 기반이 됩니다. 서버에서 웹소켓이 요청을 받을 때마다 커넥션 객체가 생성됩니다. 이 객체는 앞으로 생성되는 모든 *채널 구독*의 부모가 됩니다. 이 커넥션 자체는 인증이나 허가 후에는 애플리케이션 로직을 다루지 않습니다. 웹소켓 커넥션 클라이언트는 *소비자*라고도 불립니다. 사용자가 여는 브라우저 탭, 윈도우, 기기마다 소비자-커넥션 쌍이 하나씩 생성됩니다. +*커넥션*(connection)은 클라이언트와 서버 간의 관계의 기반이 됩니다. 서버에서 웹소켓이 요청을 받을 +때마다 커넥션 객체가 생성됩니다. 이 객체는 앞으로 생성되는 모든 *채널 구독*의 부모가 됩니다. +이 커넥션 자체는 인증이나 권한 등의 애플리케이션 로직을 다루지 않습니다. 웹소켓의 커넥션 클라이언트는 +*소비자*라고도 불립니다. 사용자가 여는 브라우저 탭, 윈도우, 기기마다 소비자-커넥션 쌍이 하나씩 생성됩니다. -커넥션은 `ApplicationCable::Connection`의 인스턴스입니다. 이 클래스에서는 요청받은 커넥션을 승인하고 사용자를 확인한 경우에 커넥션을 확립합니다. +커넥션은 `ApplicationCable::Connection`의 인스턴스입니다. 이 클래스에서는 요청받은 커넥션을 +승인하고 사용자를 인증한 경우에 커넥션을 확립합니다. #### 커넥션 설정 @@ -54,15 +66,20 @@ module ApplicationCable end ``` -`identified_by`는 커넥션 ID이며, 나중에 특정 커넥션을 탐색하는 경우에도 사용할 수 있습니다. ID로 선언된 정보는 그 커넥션 이외에도 생성된 모든 채널 인스턴스에 같은 이름이 자동으로 위임됩니다. +`identified_by`는 커넥션 ID이며, 나중에 특정 커넥션을 탐색하는 경우에도 사용할 수 있습니다. +ID로 선언된 정보는 그 커넥션 이외에도 생성된 모든 채널 인스턴스에 같은 이름이 자동으로 위임됩니다. -이 예제에서는 애플리케이션의 다른 곳에서 이미 사용자의 인증을 다루고 있으며, 인증에 성공하면 사용자 ID에 서명이 완료된 쿠키가 설정되어있다는 전제를 두고 있습니다. +이 예제에서는 애플리케이션의 다른 곳에서 이미 사용자의 인증을 다루고 있으며, +인증에 성공하면 사용자 ID에 서명이 완료된 쿠키가 설정되어 있다는 전제로 다루고 있습니다. -그러면 새 커넥션이 시도될 때 쿠키는 자동으로 커넥션 인스턴스로 전송되고, `current_user`를 설정하게 됩니다. 현재 사용자와 같은 커넥션이라고 판명되면 그 사용자가 열어둔 모든 커넥션을 가져오거나, 또는 사용자가 삭제되어 인증이 불가능한 경우에 커넥션을 종료할 수도 있습니다. +그러면 새 커넥션이 시도될 때 쿠키는 자동으로 커넥션 인스턴스로 전송되고, 이를 통해 `current_user`를 +설정하게 됩니다. 현재 사용자와 같은 커넥션이라고 판명되면 그 사용자가 열어둔 모든 커넥션을 가져오거나, +또는 사용자가 삭제되어 인증이 불가능한 경우에 커넥션을 종료할 수도 있습니다. ### 채널 -*채널*은 일반적인 MVC에서라면 컨트롤러가 하는 일과 비슷하게, 작업을 논리적인 단위로 캡슐화합니다. 레일스는 기본으로 채널 간에 공유되는 로직을 둘 수 있는 `ApplicationCable::Channel`이라는 부모 클래스를 생성합니다. +*채널*은 일반적인 MVC에서 컨트롤러가 하는 일과 마찬가지로, 작업을 논리적인 단위로 캡슐화합니다. +레일스는 기본으로 채널 간에 공유되는 로직을 위한 `ApplicationCable::Channel`이라는 부모 클래스를 생성합니다. #### 부모 채널 설정 @@ -97,7 +114,7 @@ end ```ruby # app/channels/chat_channel.rb class ChatChannel < ApplicationCable::Channel - # 소비자가 이 채널의 구독자가 되면 이 코드가 호출됨 + # 소비자가 이 채널의 구독자가 되면 이 코드가 호출됨. def subscribed end end @@ -107,7 +124,8 @@ end ### 커넥션 -소비자 쪽에서도 커넥션 인스턴스는 필요합니다. 이 커넥션은 레일스가 생성하는 다음의 자바스크립트 코드에 의해서 확립됩니다. +소비자 쪽에서도 커넥션 인스턴스는 필요합니다. +이 커넥션은 레일스가 생성하는 자바스크립트 코드로 확립됩니다. #### 소비자 연결하기 @@ -124,7 +142,8 @@ end }).call(this); ``` -이를 통해서 서버의 `/cable`에 접속하는 소비자가 준비되었습니다. 단, 관심이 있는 채널을 적어도 하나 이상 구독하기 전까지 커넥션이 확립되지 않습니다. +이를 통해서 서버의 `/cable`에 접속하는 소비자가 준비되었습니다. +단, 채널을 적어도 하나 이상 구독하기 전까지 커넥션이 확립되지 않습니다. #### 구독자 @@ -138,9 +157,10 @@ App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" } App.cable.subscriptions.create { channel: "AppearanceChannel" } ``` -이 코드로 구독할 수 있습니다. 수신한 데이터에 응답하는 기능에 대해서는 나중에 설명합니다. +이 코드로 채널을 구독할 수 있으며, 수신한 데이터에 응답하는 기능에 대해서는 나중에 설명합니다. -소비자는 지정한 채널에 대한 구독자로서 몇 번이고 행동할 수 있습니다. 예를 들자면, 소비자는 여러 채팅방을 동시에 구독할 수 있습니다. +소비자는 특정 채널에 대한 구독자로서 몇 번이고 행동할 수 있습니다. +예를 들자면, 소비자는 여러 채팅방을 동시에 구독할 수 있습니다. ```coffeescript App.cable.subscriptions.create { channel: "ChatChannel", room: "1st Room" } @@ -174,7 +194,7 @@ class CommentsChannel < ApplicationCable::Channel end ``` -이것으로 이 채널에 다음과 같은 브로드캐스트를 할 수 있게 됩니다. +이것으로 이 채널에 다음과 같이 브로드캐스트를 할 수 있게 됩니다. ```ruby CommentsChannel.broadcast_to(@post, @comment) @@ -182,9 +202,11 @@ CommentsChannel.broadcast_to(@post, @comment) ### 브로드캐스트 -*브로드캐스트*(broadcasting)는 발행자가 채널의 구독자들에게 어떤 것이든 전송할 수 있는 pub/sub 연결입니다. 각 채널은 여러 개의 브로드캐스트를 스트리밍할 수 있습니다. +*브로드캐스트*(broadcasting)는 발행자가 채널의 구독자들에게 어떤 것이든 전송할 수 있는 pub/sub 연결입니다. +각 채널은 여러 개의 브로드캐스트를 스트리밍할 수 있습니다. -브로드캐스트는 순수한 온라인 큐이며, 시간에 의존합니다. 스트리밍(한 채널에 대한 구독)하고 있지 않은 소비자는 나중에 접속할 경우 브로드캐스트를 얻을 수 없습니다. +브로드캐스트는 순수한 온라인 큐이며, 시간에 의존합니다. 스트리밍(한 채널에 대한 구독)하고 있지 않은 소비자는 +나중에 접속할 경우 브로드캐스트를 얻을 수 없습니다. 브로드캐스트는 레일스 애플리케이션의 다른 장소에서도 호출할 수 있습니다. @@ -196,17 +218,22 @@ WebNotificationsChannel.broadcast_to( ) ``` -`WebNotificationsChannel.broadcast_to` 호출에서는 사용자마다 다른 브로드캐스트 이름으로 현재 구독 어댑터(기본값은 Redis)의 pubsub 큐에 메시지를 설정합니다. ID가 1인 사용자라면 브로드캐스트의 이름은 `web_notifications_1`처럼 됩니다. +`WebNotificationsChannel.broadcast_to` 호출에서는 사용자마다 다른 브로드캐스트 이름으로 +현재 구독 어댑터(production에서의 기본값은 `redis`이며, 개발환경과 테스트 환경에서는 `async`입니다) +pubsub 큐에 메시지를 저장합니다. ID가 1인 사용자라면 브로드캐스트의 이름은 +`web_notifications:1`이 사용됩니다. -`received` 콜백을 호출하면 이 채널은 `web_notifications_1`이 수신하는 모든 것들을 클라이언트에 직접 스트리밍하게 됩니다. +`received` 콜백을 호출하면 이 채널은 `web_notifications:1`이 수신하는 모든 것을 +클라이언트에 직접 스트리밍하게 됩니다. ### 구독 -채널을 구독한 사용자는 구독자처럼 행동합니다. 이 커넥션은 구독이라 불립니다. 메시지를 받으면 사용자가 전송한 ID에 기반하여 이러한 채널 구독들로 라우트됩니다. +채널을 구독한 사용자는 구독자처럼 행동합니다. 이 커넥션은 구독이라 불립니다. +메시지를 받으면 사용자가 전송한 ID에 기반하여 이러한 채널로 전송합니다. ```coffeescript # app/assets/javascripts/cable/subscriptions/chat.coffee -# 통지를 수신할 수 있는 권한을 서버에게 요청했다고 가정 +# 통지를 수신할 수 있는 권한을 서버에게 받았다고 가정 App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" }, received: (data) -> @appendLine(data) @@ -224,9 +251,9 @@ App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" }, """ ``` -### 채널에 인수를 넘기기 +### 채널에 매개 변수 넘기기 -구독을 생성할 때에 클라이언트의 인수를 서버에 넘길 수 있습니다. 다음의 예제를 보시죠. +구독을 생성할 때 클라이언트의 매개 변수를 서버에 넘길 수 있습니다. 다음의 예제를 보시죠. ```ruby # app/channels/chat_channel.rb @@ -237,7 +264,8 @@ class ChatChannel < ApplicationCable::Channel end ``` -`subscriptions.create`에 인자로 넘겨진 객체는 params 해시가 됩니다. `channel` 키워드는 생략할 수 없습니다. +`subscriptions.create`에 첫번째 인자로 넘겨진 객체는 params 해시가 됩니다. +`channel` 키워드는 생략할 수 없습니다. ```coffeescript # app/assets/javascripts/cable/subscriptions/chat.coffee @@ -259,9 +287,9 @@ App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" }, ``` ```ruby -# 이 코드는 애플리케이션 어딘가에서 호출된다 +# 이 코드는 애플리케이션 어딘가에서 호출된다. # ex: NewCommentJob -ChatChannel.broadcast_to( +ActionCable.server.broadcast( "chat_#{room}", sent_by: 'Paul', body: 'This is a cool chat app.' @@ -270,7 +298,7 @@ ChatChannel.broadcast_to( ### 메시지를 다시 브로드캐스트하기 -한 클라이언트로부터 받은 메시지를 접속하고 있는 다른 클라이언트에게 메시지를 *다시 브로드캐스트*하는 경우가 많습니다. +한 클라이언트로부터 받은 메시지를 접속하고 있는 다른 클라이언트에게 *재전송*하는 경우가 많습니다. ```ruby # app/channels/chat_channel.rb @@ -280,7 +308,7 @@ class ChatChannel < ApplicationCable::Channel end def receive(data) - ChatChannel.broadcast_to("chat_#{params[:room]}", data) + ActionCable.server.broadcast("chat_#{params[:room]}", data) end end ``` @@ -294,7 +322,8 @@ App.chatChannel = App.cable.subscriptions.create { channel: "ChatChannel", room: App.chatChannel.send({ sent_by: "Paul", body: "This is a cool chat app." }) ``` -다시 브로드캐스트를 하게 되면 접속 중인 모든 클라이언트에게 전송합니다. 전송을 요청한 클라이언트 자신도 _예외가 아닙니다_. 사용한 params는 채널에 구독할 때와 같습니다. +다시 브로드캐스트를 하게 되면 접속 중인 모든 클라이언트에게 전송합니다. 전송을 요청한 클라이언트 자신도 +_예외가 아닙니다_. 사용하는 매개 변수들은 채널에 구독할 때와 같습니다. ## 풀스택 예시 @@ -306,9 +335,10 @@ App.chatChannel.send({ sent_by: "Paul", body: "This is a cool chat app." }) ### 예제 1: 사용자 접속을 표시하기 -이는 사용자가 온라인인지 아닌지, 사용자가 어떤 페이지를 열었는지와 같은 정보를 추적하기 위한 간단한 예제입니다. (이는 사용자들이 접속 중일 때에 그 사람 이름의 옆에 녹색 점을 표시할 때에 유용합니다) +이는 사용자가 온라인인지 아닌지, 사용자가 어떤 페이지를 보고 있는지 추적하는 간단한 예제입니다. +(이는 사용자들이 접속 중일 때에 그 사람 이름의 옆에 녹색 점을 표시하는 기능 등을 구현할 때에 유용합니다) -서버 채널은 다음과 같이 만듭니다. +서버 채널을 다음과 같이 만듭니다. ```ruby # app/channels/appearance_channel.rb @@ -331,9 +361,10 @@ class AppearanceChannel < ApplicationCable::Channel end ``` -구독이 시작되면 `subscribed` 콜백이 실행되어, 그 사용자가 온라인이라고 표시됩니다. 이 표시 API를 Redis나 데이터베이스 등과 연동할 수도 있습니다. +구독이 시작되면 `subscribed` 콜백이 실행되어, 그 사용자가 온라인이라고 표시됩니다. +이 표시 API를 Redis나 데이터베이스 등과 연동할 수 있습니다. -클라이언트의 표시 채널을 만듭시다. +클라이언트의 채널을 만듭시다. ```coffeescript # app/assets/javascripts/cable/subscriptions/appearance.coffee @@ -379,20 +410,29 @@ App.cable.subscriptions.create "AppearanceChannel", ##### 클라이언트-서버 간의 동작 -1. **클라이언트**는 **서버**에 `App.cable = ActionCable.createConsumer("ws://cable.example.com")` 경유로 접속합니다. (`cable.js`) **서버**는 이 접속을 `current_user`로 인증합니다. +1. **클라이언트**는 **서버**에 `App.cable = ActionCable.createConsumer("ws://cable.example.com")` +경유로 접속합니다. (`cable.js`) **서버**는 이 접속을 `current_user`로 확인합니다. -2. **클라이언트**는 표시 채널에 `App.cable.subscriptions.create(channel: "AppearanceChannel")`를 거쳐서 접속합니다. (`appearance.coffee`) +2. **클라이언트**는 채널에 `App.cable.subscriptions.create(channel: "AppearanceChannel")`를 +거쳐서 접속합니다. (`appearance.coffee`) -3. **서버**는 표시 채널에 새 구독이 시작된 것을 인식하고 서버의 `subscribed` 콜백을 호출하여 `current_user`의 `appear` 메소드를 호출합니다. (`appearance_channel.rb`) +3. **서버**는 표시 채널에 새 구독이 시작된 것을 인식하고 서버의 `subscribed` 콜백을 통해 +`current_user`의 `appear` 메소드를 호출합니다. (`appearance_channel.rb`) -4. **클라이언트**는 구독이 확립된 것을 확인하고 `connected` (`appearance.coffee`)를 호출합니다. 이를 통해 `@install`과 `@appear`가 호출됩니다. `@appear`는 서버의 `AppearanceChannel#appear(data)`를 통해서 데이터 해시 `{ appearing_on: $("main").data("appearing-on") }`를 넘겨줍니다. 서버의 클래스에 선언되어 있는 (콜백을 제외한) 모든 퍼블릭 메소드가 자동적으로 노출되기 때문에 가능합니다. 공개된 퍼블릭 메소드는 `perform` 메소드를 사용하여 원격 프로시저로써 사용할 수 있습니다. +4. **클라이언트**는 구독이 확립된 것을 확인하고 `connected` (`appearance.coffee`)를 호출합니다. +이를 통해 `@install`과 `@appear`가 호출됩니다. `@appear`는 서버의 `AppearanceChannel#appear(data)`를 +통해서 데이터 해시 `{ appearing_on: $("main").data("appearing-on") }`를 넘겨줍니다. +서버의 클래스에 선언되어 있는 (콜백을 제외한) 모든 퍼블릭 메소드가 자동적으로 노출되기 때문에 가능합니다. +공개된 퍼블릭 메소드는 `perform` 메소드를 사용하여 원격 프로시저로서 사용할 수 있습니다. -5. **서버**는 `current_user`에서 인식한 커넥션의 표시 채널에서 `appear` 액션에 대한 요청을 수신합니다. (`appearance_channel.rb`) **서버**는 데이터 해시에서 `:appearing_on` 키를 사용하여 값을 꺼내어 +5. **서버**는 `current_user`로 확인한 커넥션의 채널에서 `appear` 액션에 대한 요청을 수신합니다. +(`appearance_channel.rb`) **서버**는 데이터 해시에서 `:appearing_on` 키를 사용하여 값을 꺼내어 `current_user.appear`에 넘겨진 `:on`키의 값으로 설정합니다. ### 예제 2: 새로운 알림을 수신하기 -이 예제에서는 웹소켓을 사용하여 서버로부터 클라이언트의 기능을 원격으로 실행하는 동작을 다룹니다. 그런데 웹소켓의 멋진 점은 양방향 통신이라는 것입니다. 그러니 이번에는 서버에서 클라이언트의 액션을 호출해봅니다. +이 예제에서는 웹소켓을 사용하여 서버로부터 클라이언트의 기능을 원격으로 실행하는 동작을 다룹니다. +그런데 웹소켓의 멋진 점은 양방향 통신이라는 것입니다. 이번에는 서버에서 클라이언트의 액션을 호출해봅시다. 이 알림 채널은 올바른 스트림에 브로드캐스트를 할 때 클라이언트에 알림을 표시합니다. @@ -407,17 +447,17 @@ class WebNotificationsChannel < ApplicationCable::Channel end ``` -클라이언트의 알림 채널을 만듭니다. +구독을 위한 클라이언트의 알림 채널을 만듭니다. ```coffeescript # app/assets/javascripts/cable/subscriptions/web_notifications.coffee -# 클라이언트에서는 알림을 보낼 수 있는 권리를 이미 요청했다고 가정 +# 클라이언트에서는 알림을 보낼 수 있는 권한을 가지고 있다고 가정. App.cable.subscriptions.create "WebNotificationsChannel", received: (data) -> new Notification data["title"], body: data["body"] ``` -알림 채널로 어떤 내용을 브로드캐스트하는 것은 애플리케이션의 어디에서라도 가능합니다. +알림 채널 인스턴스로 어떤 내용을 브로드캐스트하는 것은 애플리케이션의 어디에서라도 가능합니다. ```ruby # 이 코드는 애플리케이션의 어딘가(ex: NewCommentJob)에서 호출됨 @@ -428,13 +468,19 @@ WebNotificationsChannel.broadcast_to( ) ``` -`WebNotificationsChannel.broadcast_to` 호출에서는 현재 구독 어댑터의 pubsub큐에 메시지를 추가합니다. 사용자마다 서로 다른 브로드캐스트 이름이 사용됩니다. ID가 1인 사용자라면 브로드캐스트의 이름은 `web_notifications_1`과 같습니다. +`WebNotificationsChannel.broadcast_to` 호출에서는 현재 구독 어댑터의 pubsub 큐에 메시지를 +추가합니다. 이 때 사용자마다 서로 다른 브로드캐스트 이름이 사용됩니다. ID가 1인 사용자라면 +브로드캐스트의 이름은 `web_notifications:1`이 됩니다. -`received` 콜백이 호출되면, 이 채널은 `web_notifications_1`에 도착한 것들을 모두 클라이언트에게 전송합니다. 인자로서 넘겨진 데이터는 서버의 브로드캐스트 호출의 두번째 인수로 넘겨지는 해시입니다. 이 해시는 JSON으로 인코딩되어서 `received`로 수신할 때에 데이터 인자로부터 복원됩니다. +`received` 콜백이 호출되면, 이 채널은 `web_notifications:1`에 도착한 것을 모두 클라이언트에게 +전송합니다. 인자로서 넘겨진 데이터는 서버의 브로드캐스트 호출의 두번째 인수로 넘겨지는 해시입니다. +이 해시는 JSON으로 인코딩되어 전송되며, `received`로 수신할 때 데이터 인자로부터 복원됩니다. ### 더 자세한 예시 -레일스 애플리케이션에 액션 케이블을 설정하는 방법이나 채널을 추가하는 방법에 대해서는 [rails/actioncable-examples](https://github.com/rails/actioncable-examples)에서 전체 예시를 볼 수 있습니다. +레일스 애플리케이션에 액션 케이블을 설정하는 방법이나 채널을 추가하는 방법에 대해서는 +[rails/actioncable-examples](https://github.com/rails/actioncable-examples)에서 +전체 예시를 볼 수 있습니다. ## 설정 @@ -442,7 +488,8 @@ WebNotificationsChannel.broadcast_to( ### 구독 어댑터 -액션케이블은 `config/cable.yml` 설정 파일을 사용합니다. 레일스 환경마다 어댑터와 URL을 하나씩 설정해야 합니다. 어댑터에 대해서는 [의존성](#의존성)를 참고해주세요. +액션케이블은 `config/cable.yml` 설정 파일을 사용합니다. 레일스 환경마다 어댑터와 URL을 하나씩 +설정해야 합니다. 어댑터에 대해서는 [의존성](#의존성)를 참고해주세요. ```yaml development: @@ -458,27 +505,30 @@ production: ### 허가된 요청 호스트 -액션케이블은 허가되지 않은 곳으로부터의 요청을 받지 않습니다. 발송 가능 호스트 목록은 배열의 형태로 서버 설정에 넘깁니다. 각 호스트는 문자열을 사용하거나 정규 표현식 매칭을 사용할 수도 있습니다. +액션케이블은 허가된 곳으로부터의 요청만을 받습니다. 이 호스트 목록은 배열의 형태로 서버 설정에 넘깁니다. +각 호스트는 문자열이나 정규 표현식을 사용할 수 있습니다. ```ruby config.action_cable.allowed_request_origins = ['http://rubyonrails.com', %r{http://ruby.*}] ``` -모든 요청 호스트를 허가 또는 거부하려면 다음을 설정하세요. +모든 요청을 허가하려면 다음을 설정하세요. ```ruby config.action_cable.disable_request_forgery_protection = true ``` -development 환경에서 실행 중일 때, 액션케이블은 localhost:3000로부터의 요청을 모두 허가합니다. +development 환경에서 실행 중일 때, 액션케이블은 localhost:3000 로부터의 요청을 모두 허가합니다. ### 소비자 설정 -URL을 설정하려면 HTML 레이아웃의 HEAD에 `action_cable_meta_tag` 호출을 추가합니다. 보통 여기에서 사용하는 URL은 각 환경의 설정 파일에서 `config.action_cable.url`로 설정합니다. +URL을 설정하려면 HTML 레이아웃의 HEAD에 `action_cable_meta_tag` 호출을 추가합니다. +보통 여기에서 사용하는 URL은 각 환경의 설정 파일에서 `config.action_cable.url`로 설정합니다. ### 기타 설정 -이외에도 커넥션마다 로거에 태그를 설정할 수도 있습니다. 다음 예제에서는 사용자 계정 ID가 있는 경우에는 이를 사용하고, 없는 경우에는 'no-account'라는 태그를 사용합니다. +이외에도 커넥션마다 로거에 태그를 설정할 수도 있습니다. 다음 예제에서는 사용자 계정 ID가 있는 +경우에는 이를 사용하고, 없는 경우에는 "no-account"라는 태그를 사용합니다. ```ruby config.action_cable.log_tags = [ @@ -488,15 +538,19 @@ config.action_cable.log_tags = [ ] ``` -이용 가능한 모든 설정 옵션은 `ActionCable::Server::Configuration` 클래스를 확인해주세요. +사용 가능한 모든 옵션은 `ActionCable::Server::Configuration` 클래스를 확인해주세요. -또한 서버가 제공하는 데이터베이스 커넥션의 갯수는 적어도 워커의 숫자보다 많아야합니다. 기본 워커 풀의 크기는 4이므로, 데이터베이스 커넥션도 4개를 준비해야 합니다. 이 값은 `config/database.yml`의 `pool`에서 변경할 수 있습니다. +또한 서버가 제공하는 데이터베이스 커넥션의 갯수는 적어도 워커의 숫자보다 많아야 합니다. +기본 워커 풀의 크기는 4이므로, 데이터베이스 커넥션도 4개 이상을 준비해야 합니다. +이 값은 `config/database.yml`의 `pool`에서 변경할 수 있습니다. ## 액션케이블 전용 서버를 실행하기 ### 애플리케이션에서 실행하기 -액션케이블은 레일스 애플리케이션과 함께 실행할 수 있습니다. 예를 들어, `/websocket`에서 웹소켓 요청을 수신하는 경우에는 `config.action_cable.mount_path`로 경로를 지정할 수 있습니다. +액션케이블은 레일스 애플리케이션과 함께 실행할 수 있습니다. +예를 들어, `/websocket`에서 웹소켓 요청을 수신하는 경우에는 +`config.action_cable.mount_path`로 경로를 지정할 수 있습니다. ```ruby # config/application.rb @@ -505,14 +559,17 @@ class Application < Rails::Application end ``` -레이아웃에서 `action_cable_meta_tag`를 호출하면 `App.cable = ActionCable.createConsumer()`에서 액션케이블 서버에 접속할 수 있게 됩니다. `createConsumer`의 첫번째 인자에는 커스텀 경로가 지정됩니다(e.g. `App.cable = ActionCable.createConsumer("/websocket")`). +레이아웃에서 `action_cable_meta_tag`를 호출하면 `App.cable = ActionCable.createConsumer()`에서 +액션케이블 서버에 접속할 수 있게 됩니다. `createConsumer`의 첫번째 인자에는 커스텀 경로가 +지정됩니다(e.g. `App.cable = ActionCable.createConsumer("/websocket")`). 생성한 서버의 모든 인스턴스와 서버가 생성한 모든 워커 인스턴스에는 액션케이블의 새로운 인스턴스도 포함됩니다. 커넥션 간의 메시지 동기화는 Redis를 통해서 이루어집니다. -### 독립 서버에서 실행하기 +### 독립된 서버에서 실행하기 -애플리케이션 서버와 액션케이블 서버를 나눌 수도 있습니다. 액션케이블 서버는 Rack 애플리케이션입니다만, 독립된 애플리케이션이기도 합니다. +애플리케이션 서버와 액션케이블 서버를 나눌 수도 있습니다. +액션케이블 서버는 Rack 애플리케이션입니다만, 독립된 애플리케이션이기도 합니다. 추천하는 기본 설정은 다음과 같습니다. ```ruby @@ -534,19 +591,27 @@ bundle exec puma -p 28080 cable/config.ru ### 메모 -웹소켓 서버로부터 세션에 접근할 수 없습니다만, 쿠키에는 접근할 수 있습니다. 이를 사용해서 인증을 처리할 수 있습니다. 이 [글](http://www.rubytutorial.io/actioncable-devise-authentication)에서 Devise와 함께 사용하는 방법을 참고하세요. +웹소켓 서버로부터 세션에 접근할 수 없습니다만, 쿠키에는 접근할 수 있습니다. 이를 사용해서 인증을 처리할 수 있습니다. +이 [글](http://www.rubytutorial.io/actioncable-devise-authentication)에서 Devise와 +함께 사용하는 방법을 확인할 수 있습니다. ## 의존성 -액션케이블은 자신의 pubsub 내부의 프로세스에 구독 어댑터 인터페이스를 제공합니다. -비동기, 인라인, PostgreSQL, Evented Redis, Non-evented Redis 등의 어댑터를 사용할 수 있습니다. 새 레일스 애플리케이션의 기본 어댑터는 비동기(`async`) 어댑터입니다. +액션케이블은 pubsub을 처리하기 위한 구독 어댑터 인터페이스를 제공합니다. +비동기, 인라인, PostgreSQL, Evented Redis, Non-evented Redis 등의 어댑터를 사용할 수 있습니다. +새 레일스 애플리케이션의 기본 어댑터는 비동기(`async`) 어댑터입니다. -루비 코드는 [websocket-driver](https://github.com/faye/websocket-driver-ruby), [nio4r](https://github.com/celluloid/nio4r), [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby) 위에서 구축되어 있습니다. +루비 코드는 [websocket-driver](https://github.com/faye/websocket-driver-ruby), +[nio4r](https://github.com/celluloid/nio4r), +[concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby) 위에서 구축되어 있습니다. ## 배포 -액션케이블을 지지하고 있는 것은 웹소켓과 스레드입니다. 프레임워크 내부의 흐름이나 사용자 지정의 채널 동작은 루비의 기본 스레드에 의해서 처리됩니다. 다시 말해 코드의 스레드 세이프를 유지하는 한, 레일스의 정규 모델을 문제 없이 사용할 수 있다는 의미입니다. +액션케이블을 지지하고 있는 것은 웹소켓과 스레드입니다. 프레임워크 내부의 흐름이나 사용자 지정의 채널 동작은 +루비의 기본 스레드를 통하여 처리됩니다. 다시 말해 스레드에 안전한 코드를 유지하는 한, 레일스의 정규 모델을 +문제 없이 사용할 수 있다는 의미입니다. -액션케이블 서버에는 Rack 소캣을 탈취하는 API가 구현되어 있습니다. 이를 통해서, 애플리케이션 서버의 멀티스레드 여부와 관계없이 내부의 커넥션을 멀티 스레드 패턴으로 관리합니다. +액션케이블 서버에는 Rack 소켓을 탈취하는 API가 구현되어 있습니다. 이를 통해서, 애플리케이션 서버의 +멀티 스레드 사용 여부와 관계없이 내부의 커넥션을 멀티 스레드 패턴으로 관리합니다. -따라서 액션케이블은 Unicorn, Puma, Passenger 등의 유명한 서버들과 문제없이 연동될 수 있습니다. +따라서 액션케이블은 Unicorn, Puma, Passenger 등의 유명한 서버와 문제없이 연동될 수 있습니다. diff --git a/guides/source/ko/action_controller_overview.md b/guides/source/ko/action_controller_overview.md index 37060f37e8b89..597c892a4e60b 100644 --- a/guides/source/ko/action_controller_overview.md +++ b/guides/source/ko/action_controller_overview.md @@ -1,5 +1,5 @@ -Action Controller 개요 +액션 컨트롤러 개요 ========================== 이 가이드에서는 컨트롤러의 동작과 애플리케이션의 요청 사이클에서 컨트롤러가 어떻게 사용되는지 설명합니다. @@ -7,12 +7,12 @@ Action Controller 개요 이 가이드의 내용: * 컨트롤러를 거치는 요청의 흐름을 이해햐기 -* 컨트롤러로 넘기는 파라미터를 제한하는 방법 -* 세션이나 쿠키에 데이터를 저장하는 이유와 그 방법 +* 컨트롤러로 넘기는 매개 변수를 제한하는 방법 +* 세션이나 쿠키에 데이터를 저장하는 이유와 방법 * 요청을 처리하는 도중, 필터를 사용하여 코드를 실행하는 방법 -* Action Controller에 내장되어 있는 HTTP인증 +* 액션 컨트롤러에 내장되어 있는 HTTP인증을 사용하는 방법 * 사용자의 브라우저에 데이터를 직접 스트리밍하는 방법 -* 비밀성이 높은 파라미터를 로그로 출력하지 않도록 하는 방법 +* 민감한 매개 변수를 로그로 출력하지 않는 방법 * 요청 처리중에 발생하는 예외를 다루는 방법 -------------------------------------------------------------------------------- @@ -20,20 +20,32 @@ Action Controller 개요 컨트롤러의 역할 -------------------------- -Action Controller는 MVC모델의 C에 해당합니다. 라우팅 설정에 의해서 요청을 처리할 컨트롤러가 지명되면, 컨트롤러는 요청을 해석하고, 적절한 응답을 돌려줄 책임을 집니다. 다행히 이러한 처리는 대부분 Action Controller가 수행합니다. 나아가, 이러한 처리를 가능한 간단하게 만들기 위해 영리한 방식을 사용합니다. +액션 컨트롤러는 MVC모델의 C에 해당합니다. 라우팅가 요청을 처리할 컨트롤러를 결정하면, 컨트롤러는 요청을 +해석하고, 적절한 응답을 돌려줄 책임을 집니다. 다행히 이러한 처리의 기본적인 부분은 대부분 액션 컨트롤러가 +수행하며, 이러한 처리를 가능한 간단하게 만들기 위해 영리한 방식을 사용합니다. -종래의 일반적인 [RESTful](http://ko.wikipedia.org/wiki/REST)한 애플리케이션에서는, 컨트롤러는 요청을 받아(이 부분은 개발자가 볼 수 없도록 되어 있습니다) 데이터를 모델에서 받아오거나, 저장하는 등의 작업을 수행하고, 마지막으로 뷰를 사용하여 HTML 출력을 생성하는 역할을 가집니다. 본인의 컨트롤러를 만드는 방식이 이것과는 좀 다를 수 있습니다만, 신경 쓸 필요는 없습니다. 이것은 컨트롤러를 사용하는 가장 일반적인 방법을 설명한 것이기 때문입니다. +종래의 일반적인 [RESTful](http://ko.wikipedia.org/wiki/REST)한 애플리케이션에서 컨트롤러는 +요청을 받아(이 부분은 개발자가 볼 수 없게 되어 있습니다) 데이터를 모델에서 받아오거나 저장하는 작업을 수행하고, +마지막으로 뷰를 사용하여 HTML 출력을 생성합니다. 본인의 컨트롤러를 만드는 방식이 이것과는 좀 다를 수 +있습니다만, 신경 쓸 필요는 없습니다. 이것은 컨트롤러를 사용하는 가장 일반적인 방법을 설명한 것이기 때문입니다. -컨트롤러가 모델과 뷰의 사이에서 이 양자를 중개한다는 관점도 있습니다. 컨트롤러는 모델의 데이터를 뷰에서 사용할 수 있도록 가공하여, 데이터를 뷰에서 출력하거나, 사용자로부터 입력받은 데이터로 모델을 갱신하거나 합니다. +컨트롤러가 모델과 뷰의 사이를 중개한다는 관점도 있습니다. 컨트롤러는 모델의 데이터를 뷰에서 사용할 수 있도록 +가공하여, 데이터를 뷰에서 출력하거나, 사용자로부터 입력받은 데이터로 모델을 갱신하거나 합니다. -NOTE: 좀 더 자세한 라우팅 과정에 대해서는 이 가이드의 [Rails 라우팅](routing.html)을 참조해주세요. +NOTE: 좀 더 자세한 라우팅 과정에 대해서는 이 가이드의 [Rails 라우팅](routing.html)을 참조하세요. 컨트롤러의 명명 규칙 ---------------------------- -Rails의 컨트롤러 이름(여기에서는 "Controller"라는 부분을 제외합니다)은 기본적으로 이름의 마지막에 '복수형'을 사용합니다. 단, 이것은 반드시 지켜야 하는 것은 아닙니다(예를 들어 `ApplicationController`는 Application이라는 단수형으로 되어있습니다). 예를 들어 `ClientsController`가 `ClientController`보다는 선호되며, `SiteAdminsController`는 `SiteAdminController`나 `SitesAdminsController`보다 선호된다는 식입니다. +Rails의 컨트롤러 이름은 기본적으로 이름의 마지막에 '복수형'을 사용합니다. 단, 이것은 반드시 지켜야 하는 +것은 아닙니다(e.g. `ApplicationController`). 예를 들어 `ClientsController`가 +`ClientController`보다는 선호되며, `SiteAdminsController`는 `SiteAdminController`나 +`SitesAdminsController`보다 선호되는 식입니다. -그러나 일반적으로 이 규칙을 따를 것을 추천합니다. 그 이유는 `resources`같은 기본 라우팅 생성자를 있는 그대로 사용할 수 있다는 점, 애플리케이션 전체에서 URL이나 패스 헬퍼의 사용법을 일관되게 만들수 있다는 점 등이 있습니다. 컨트롤러 이름의 마지막 부분이 복수형으로 되어있지 않으면, 예를 들어 `resouces`로 한번에 라우팅을 할 수 없기에 `:path`나 `:controller`로 하나하나 지정해야합니다. 자세한 설명은 [레이아웃과 랜더링](layouts_and_rendering.html)에서 확인해주세요. +그러나 일반적으로 이 규칙을 따를 것을 추천합니다. 그 이유는 `resources`같은 기본 라우팅 생성자를 +있는 그대로 사용할 수 있다는 점, 애플리케이션 전체에서 URL이나 패스 헬퍼의 사용법을 일관되게 만들 수 +있다는 장점이 있습니다. 자세한 설명은 [레이아웃과 랜더링](layouts_and_rendering.html)에서 +확인해주세요. NOTE: 모델의 명명 규칙은 '단수형'으로, 컨트롤러의 명명 규칙과는 다릅니다. @@ -41,7 +53,9 @@ NOTE: 모델의 명명 규칙은 '단수형'으로, 컨트롤러의 명명 규 메소드와 액션 ------------------- -Rails의 컨트롤러는 `ApplicationController`를 상속한 Ruby 클래스이며, 다른 클래스와 마찬가지로 메소드를 사용할 수 있습니다. 애플리케이션이 브라우저로부터 요청을 받으면 라우팅에 의해서 컨트롤러와 액션이 지정되고, Rails는 그에 알맞는 컨트롤러의 인스턴스를 생성하여 액션명과 동일한 이름을 가지는 메소드를 실행합니다. +Rails의 컨트롤러는 `ApplicationController`를 상속한 루비 클래스이며, 다른 클래스와 마찬가지로 +메소드를 사용할 수 있습니다. 애플리케이션이 브라우저로부터 요청을 받으면 라우팅에 의해서 실행할 컨트롤러와 +액션이 결정되고, Rails는 그 컨트롤러의 인스턴스를 생성하여 액션명과 동일한 이름을 가지는 메소드를 실행합니다. ```ruby class ClientsController < ApplicationController @@ -50,7 +64,12 @@ class ClientsController < ApplicationController end ``` -예를 들어 클라이언트를 한명 추가하기 위해 브라우저에서 애플리케이션의 `/clients/new`에 접근하게 되면, Rails는 `ClientsController`의 인스턴스를 생성하고 `new`라는 메소드를 실행합니다. 여기서 주목해야할 부분은 `new` 메소드의 내용이 비어있음에도 불구하고 정상적으로 동작한다는 점입니다. 이것은 Rails에서는 `new` 액션에서 따로 지정한 것이 없을 경우, `new.html.erb` 뷰를 랜더링하게끔 되어있기 때문입니다. 뷰에서 `@client` 인스턴스 변수에 접근하기 위해서 `new` 메소드에서 `Client`를 하나 생성하고, `@client`에 저장해보죠. +예를 들어 클라이언트를 한명 추가하기 위해 브라우저에서 `/clients/new`에 접속하게 되면, +Rails는 `ClientsController`의 인스턴스를 생성하고 `new`라는 메소드를 실행합니다. 여기서 +주목해야 할 부분은 `new` 메소드의 내용이 비어있음에도 불구하고 정상적으로 동작한다는 점입니다. +Rails에서는 따로 지정한 것이 없을 경우, `new.html.erb` 뷰를 랜더링하기 때문입니다. +뷰에서 `@client` 인스턴스 변수에 접근하기 위해서 `new` 메소드에서 `Client`를 하나 생성하고, +`@client`에 저장해보죠. ```ruby def new @@ -60,21 +79,30 @@ end 더 자세한 내용은 [레이아웃과 랜더링](layouts_and_rendering.html)에 설명되어 있습니다. -`ApplicationController`는 편리한 메소드가 많이 정의되어있는 `ActionController::Base`를 상속합니다. 이 가이드에서는 그 중 일부에 대해서 설명합니다만, 더 자세히 알고 싶은 경우에는 API 문서나 Rails의 소스 코드를 확인해주세요. +`ApplicationController`는 편리한 메소드가 많이 정의되어 있는 `ActionController::Base`를 +상속합니다. 이 가이드에서는 그 중 일부에 대해서 설명합니다만, 더 자세히 알고 싶은 경우에는 API 문서나 +Rails의 소스 코드를 확인해주세요. -public 메소드일 경우에만 액션으로서 사용할 수 있습니다. 보조 메소드나 필터같은, 액션으로 사용되어서는 안되는 메소드들을 private로 설정하는 것이 일반적입니다. +public 메소드일 경우에만 액션으로써 사용할 수 있습니다. 보조 메소드나 필터처럼 액션으로 사용되지 않는 +메소드를 `private`나 `protected`로 지정하는 것이 일반적입니다. -파라미터 +매개 변수 ---------- -컨트롤러의 액션에서는 사용자로부터 전송된 데이터나 그 이외의 파라미터를 사용하여 어떤 작업을 하는 경우가 많습니다. Rails 뿐만이 아니라, 일반적인 웹 애플리케이션에서는 2종류의 파라미터를 사용할 수 있습니다. 첫번째는 URL의 일부로서 전송되는 파라미터로서, '쿼리 문자열 파라미터'라고 부릅니다. 쿼리 문자열은 URL의 "?"의 뒤에 위치합니다. 두번째는 'POST 데이터'라고 불리는 것입니다. POST 데이터는 보통 사용자가 기입한 HTML 폼으로부터 전송됩니다. 이는 HTTP POST 요청의 일부로 전송되기 때문에 POST 데이터라고 불립니다. Rails에서의 쿼리 문자열 파라미터와 POST 데이터를 다루는 방식에는 차이가 없습니다. 어느 쪽도 컨트롤러 내부에서는 `params`라는 이름의 해시를 통해 접근할 수 있습니다. +컨트롤러의 액션에서는 사용자로부터 전송된 데이터나 그 이외의 매개 변수를 사용하여 어떤 작업을 하는 경우가 +많습니다. 일반적인 웹 애플리케이션에서는 2종류의 매개 변수를 사용할 수 있습니다. 첫번째는 URL의 일부로서 +전송되는 매개 변수로, '쿼리 문자열 매개 변수'라고 부릅니다. 쿼리 문자열은 URL의 "?"의 뒤에 위치합니다. +두번째는 'POST 데이터'라고 불리는 것입니다. POST 데이터는 보통 사용자가 기입한 HTML 폼으로부터 +전송됩니다. 이는 HTTP POST 요청의 일부로 전송되기 때문에 POST 데이터라고 불립니다. Rails는 쿼리 +문자열 매개 변수와 POST 데이터를 동일하게 다루며, 어느 쪽도 컨트롤러 내부에서는 `params`라는 이름의 +해시를 통해 접근할 수 있습니다. ```ruby class ClientsController < ApplicationController - # 이 액션에서는 쿼리 문자열 파라미터가 사용됩니다. + # 이 액션에서는 쿼리 문자열 매개 변수가 사용됩니다. # 전송측에서 HTTP GET 요청을 사용하기 때문입니다. - # 단 파라미터에 접근하는 방법은 아래의 방식과 다르지 않습니다. - # 유효한 고객 목록을 얻기 위해 이 액션의 URL은 다음과 같이 되어있습니다. + # 단 매개 변수에 접근하는 방법은 아래의 방식과 다르지 않습니다. + # 유효한 고객 목록을 얻기 위해 이 액션의 URL은 다음과 같이 되어 있습니다. # clients: /clients?status=activated def index if params[:status] == "activated" @@ -85,7 +113,7 @@ class ClientsController < ApplicationController end # 이 액션에서는 POST 데이터를 사용하고 있습니다. - # 이 파라미터는 일반적으로 사용자가 전송한 HTML 폼으로부터 생성됩니다. + # 이 매개 변수는 일반적으로 사용자가 전송한 HTML 폼으로부터 생성됩니다. # 이것은 RESTful한 접근이며, URL은 "/clients"가 됩니다. # 데이터는 URL이 아닌 요청의 body에 포함되어 전송됩니다. def create @@ -101,19 +129,26 @@ class ClientsController < ApplicationController end ``` -### 해시와 배열 파라미터 +### 해시와 배열 매개 변수 -`params` 해시는 1차원의 키, 값 쌍만 저장할 수 있는 것이 아닙니다. 배열이나, 중첩된 해시를 저장할 수도 있습니다. 값의 배열을 전송하고 싶은 경우에는 아래와 같이, 키의 이름에 빈 대괄호를 추가해주세요. +`params` 해시는 1차원의 키-값 쌍만 저장할 수 있는 것이 아닙니다. 배열이나 중첩된 해시를 가질 수도 +있습니다. 값의 배열을 전송하고 싶은 경우에는 아래와 같이 키의 이름에 빈 대괄호를 추가해주세요. ``` GET /clients?ids[]=1&ids[]=2&ids[]=3 ``` -NOTE: "["와 "]"는 URL에서는 사용불가능한 문자이므로, 이 예시의 실제 URL은 "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5d=3"처럼 구성됩니다. 이는 브라우저가 자동적으로 처리해서 전송해주고, 나아가 Rails는 파라미터를 가져올 때 자동적으로 복원해주므로, 평소에는 신경쓸 필요가 없습니다. 단, 어떤 이유로 서버에 직접 요청을 해야하는 경우에는 이 부분에 주의해야 합니다. +NOTE: "["와 "]"는 URL에서는 사용불가능한 문자이므로, 이 예시의 실제 URL은 +"/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5d=3"처럼 구성됩니다. 이는 브라우저가 자동으로 +변환해주며 나아가 Rails가 매개 변수를 가져올 때 자동으로 복원해주므로, 평소에는 신경쓸 필요가 없습니다. +단, 어떤 이유로 서버에 직접 요청을 해야하는 경우에는 이를 주의해야 합니다. -이것으로 받은 `params[:ids]`의 값은 `["1", "2", "3"]`이 됩니다. 하나 더 기억해두어야 하는 것은 파라미터의 값은 모두 '문자열'이라는 점입니다. Rails는 파라미터의 타입을 추측하지 않으며, 타입 변환도 해주지 않으므로, 필요하다면 직접 타입을 변환해야합니다. 예를 들어, 파라미터의 숫자를 `to_i`를 사용해 정수로 변환하는 코드를 자주 볼 수 있습니다. +받은 `params[:ids]`의 값은 `["1", "2", "3"]`이 됩니다. 매개 변수의 값은 모두 '문자열'이라는 +점을 기억하세요. Rails는 매개 변수의 타입을 추측하지 않으며, 타입 변환도 해주지 않습니다. -NOTE: `params`에 `[]`, `[nil]`, `[nil, nil, ...]` 같은 값이 있으면 모두 자동적으로 `nil`로 변환됩니다. 이 동작은 보안 상의 이유로 기본적으로 발생합니다. 자세한 설명은 [보안 가이드](security.html#안전하지_않은_쿼리_생성)을 참조해주세요. +NOTE: `params`에 `[nil]`, `[nil, nil, ...]` 같은 값이 있으면 모두 자동적으로 +`[]`로 변환됩니다. 이 동작은 보안 상의 이유 때문이며 자세한 설명은 +[보안 가이드](security.html#안전하지_않은_쿼리_생성)을 참조해주세요. 해시를 전송하기 위해서는 대괄호에 키 이름을 넣어 전송하면 됩니다. @@ -126,13 +161,18 @@ NOTE: `params`에 `[]`, `[nil]`, `[nil, nil, ...]` 같은 값이 있으면 모 ``` -이 폼을 전송하면 `params[:client]`의 값은 `{ "name" => "Acme", "phone" => "12345", "address" => { "postcode" => "12345", "city" => "Carrot City" } }`가 됩니다. `params[:client][:address]`처럼 해시가 중첩되어 있는 부분을 주목해주세요. +이 폼을 전송하면 `params[:client]`의 값은 +`{ "name" => "Acme", "phone" => "12345", "address" => { "postcode" => "12345", "city" => "Carrot City" } }`가 +됩니다. `params[:client][:address]`처럼 해시가 중첩되어 있는 부분을 주목해주세요. -이 `params` 해시는 사실 `ActiveSupport::HashWithIndifferentAccess`의 인스턴스입니다. 이는 해시인 것 처럼 동작합니다만, 키로 심볼이나 문자열, 어느 쪽을 사용해도 좋다는 점이 다릅니다. +이 `params` 객체는 해시처럼 동작합니다만, 키로 심볼이나 문자열, 어느 쪽을 사용할 수 있다는 점이 다릅니다. -### JSON 파라미터 +### JSON 매개 변수 -Web서비스 애플리케이션을 개발하다 보면, 파라미터를 JSON 형식으로 수신하면 편리할텐데, 라고 생각할 때가 종종 있습니다. Rails에서는 요청의 "Content-Type"에 "application/json"가 지정되어 있으면, 자동적으로 파라미터를 `params` 해시로 변환해줍니다. 그 이후로는 일반적인 `params` 해시를 조작하듯 사용하면 됩니다. +웹 애플리케이션을 개발하다 보면, 매개 변수를 JSON 형식으로 받는다면 편리할텐데, 라고 생각할 때가 종종 +있습니다. Rails에서는 요청의 "Content-Type"에 "application/json"가 지정되어 있으면, +자동적으로 매개 변수를 `params` 해시로 변환해줍니다. 그 이후로는 일반적인 `params` 해시를 조작하듯 +사용할 수 있습니다. 예를 들어 아래의 JSON 데이터를 전송한다고 가정합시다. @@ -140,37 +180,52 @@ Web서비스 애플리케이션을 개발하다 보면, 파라미터를 JSON 형 { "company": { "name": "acme", "address": "123 Carrot Street" } } ``` -`params[:company]`가 넘겨받는 값은 `{ "name" => "acme", "address" => "123 Carrot Street" }`가 됩니다. +`params[:company]`가 넘겨받는 값은 +`{ "name" => "acme", "address" => "123 Carrot Street" }`가 됩니다. -마찬가지로 initializer에서 `config.wrap_parameters`를 활성화 했거나, 컨트롤러에서 `wrap_parameters`를 호출했을 경우, JSON 파라미터의 루트 요소를 안전하게 제거할 수 있습니다. 이 파라미터는 기본적으로 컨트롤러의 이름에 대응하여 복사한 후, 감싸지게 됩니다. 따라서 위의 파라미터는 아래와 같이 사용할 수 있습니다. +마찬가지로 initializer에서 `config.wrap_parameters`를 활성화했거나, 컨트롤러에서 +`wrap_parameters`를 호출했을 경우, JSON 매개 변수의 루트 요소를 안전하게 제거할 수 있습니다. +이 경우, 매개 변수는 복사되고, 컨트롤러의 이름에 맞는 키로 감싸지게 됩니다. +따라서 위의 JSON 요청은 아래와 같이 처리됩니다. ```json { "name": "acme", "address": "123 Carrot Street" } ``` -데이터를 전송한 곳이 `CompaniesController`라고 가정하면, 아래와 같이 `:company`라는 키로 감싸집니다. +데이터를 전송한 곳이 `CompaniesController`라고 가정하면, +아래와 같이 `:company`라는 키로 감싸집니다. ```ruby { name: "acme", address: "123 Carrot Street", company: { name: "acme", address: "123 Carrot Street" } } ``` -키의 이름을 커스터마이즈 하거나, 특정 파라미터를 감싸고 싶은 경우에는 [API 문서](http://api.rubyonrails.org/classes/ActionController/ParamsWrapper.html)를 참조해주세요. +키의 이름을 변경하거나, 특정 매개 변수를 감싸고 싶은 경우에는 +[API 문서](http://api.rubyonrails.org/classes/ActionController/ParamsWrapper.html)를 +참조해주세요. -NOTE: 이전의 XML 파라미터 해석을 도와주던 코드는 `actionpack-xml_parser`라는 gem으로 분리되었습니다. +NOTE: XML 매개 변수 해석을 도와주던 코드는 `actionpack-xml_parser`라는 잼으로 분리되었습니다. -### 라우팅 파라미터 +### 라우팅 매개 변수 -`params` 해시에는 `:controller`와 `:action`가 반드시 포함됩니다. 단, 이 값에는 직접 접근할 수 없으며, `controller_name`과 `action_name`이라는 전용 메소드를 사용해주세요. 라우팅에 정의된 다른 값(`id` 등)에도 접근할 수 있습니다. 예를 들어, '유효' 또는 '무효'로 표기되는 고객 리스트를 생각해봅시다. '보기 좋은' URL에 포함되는 `:status` 파라미터를 가져오기 위해 다음과 같은 라우트를 하나 추가합시다. +`params` 해시는 `:controller`와 `:action`를 반드시 포함합니다. 단, 이 값에는 직접 사용하는 대신, +`controller_name`과 `action_name`이라는 전용 메소드를 사용해주세요. 라우팅에 정의된 +다른 값(`id` 등)에도 접근할 수 있습니다. 예를 들어, "유효" 또는 "무효"로 표기되는 고객 리스트를 +생각해봅시다. "보기 좋은" URL에 포함되는 `:status` 매개 변수를 가져오기 위해 다음과 같은 라우트를 +하나 추가합시다. ```ruby get '/clients/:status' => 'clients#index', foo: 'bar' ``` -이 경우, 브라우저에서 `/clients/active`라는 URL에 접근하면, `params[:status]`가 "active"(유효)로 설정됩니다. 이 라우팅을 사용하면 넘겨진 쿼리 문자열은 당연히 `params[:foo]`에 "bar"로 설정됩니다. 마찬가지로 `params[:action]`에는 "index"가 포함됩니다. +이 경우, 브라우저에서 `/clients/active`라는 URL에 접근하면, `params[:status]`가 +"active"(유효)로 설정됩니다. 이 라우팅을 사용하면 넘겨진 쿼리 문자열은 당연히 `params[:foo]`에 +"bar"로 설정됩니다. 마찬가지로 `params[:action]`에는 "index"가, +그리고 `params[:controller]`에는 "clients"가 포함됩니다. ### `default_url_options` -컨트롤러에서 `default_url_options`라는 이름의 메소드를 정의하면, URL 생성용 전역 기본 파라미터를 설정할 수 있습니다. 이러한 메소드는 필요한 기본값을 가지는 해시를 반환해야하며, 키값으로 심볼을 사용해야만 합니다. +컨트롤러에서 `default_url_options`라는 이름의 메소드를 정의하면, URL 생성용 전역 기본 매개 변수를 +설정할 수 있습니다. 이러한 메소드는 필요한 기본값을 가지는 해시를 반환해야 하며, 키로 심볼을 사용해야 합니다. ```ruby class ApplicationController < ActionController::Base @@ -180,25 +235,35 @@ class ApplicationController < ActionController::Base end ``` -이런 옵션은 URL 생성의 시작점으로 사용할 수 있으며, `url_for` 호출에 넘겨지는 옵션으로 덮어쓸 수 있습니다. +이런 옵션은 URL 생성의 시작점으로 사용할 수 있으며, `url_for` 호출에 넘겨지는 옵션으로 +덮어쓸 수 있습니다. -`ApplicationController`에서 `default_url_options`을 정의하면 위의 예시에서 볼 수 있듯 모든 URL 생성시에 사용되게 됩니다. 이 메소드를 특정 컨트롤러에서 정의하면 그 컨트롤러에서 생성되는 URL에만 영향을 미치게 됩니다. +`ApplicationController`에서 `default_url_options`을 정의하면 위의 예시에서 볼 수 있듯 +모든 URL 생성시에 사용하게 됩니다. 이 메소드를 특정 컨트롤러에서 정의하면 그 컨트롤러에서 생성되는 +URL에만 영향을 미치게 됩니다. + +주어진 요청에서 이 메소드는 생성된 모든 URL에서 각각 호출되지는 않습니다. 성능 상의 이유로 반환되는 해시는 +캐싱되어 있으며, 요청당 많아야 한번 호출됩니다. ### Strong Parameters -Strong parameters를 사용하면, Action Controller가 받은 파라미터를 화이트리스트로 검증하기 전 까지는 바로 Active Model에 넘길 수 없게 됩니다. 이것은 여러 속성을 한번에 갱신하고 싶을 때에 어떤 속성의 갱신을 허가하고, 또다른 속성의 갱신을 금지할 지 명시적으로 결정해야 한다는 것을 의미합니다. 이는 적당히 모든 속성의 갱신을 허락하게 되면 외부에 공개할 필요가 없는 속성까지 실수로 공개하게 될 가능성이 발생하므로, 그러한 상황을 미연에 예방하기 위함입니다. +Strong parameters를 사용하면 액션 컨트롤러가 받은 매개 변수를 화이트리스트로 검증하기 전에는 +Active Model에 통째로 넘길 수 없게 됩니다. 이것은 여러 속성을 한번에 갱신하고 싶을 때에 어떤 속성의 +갱신을 허가하고, 또다른 속성의 갱신을 금지할 지 명시적으로 결정해야 한다는 의미입니다. 이는 사용자가 +보안 상의 이유로 변경해서는 안되는 속성을 실수로 변경할 수 없게끔 방지하기 위한 방책입니다. -나아가 파라미터의 속성에는 '필수(required)'를 지정할 수 있으며, 사전에 정의해둔 raise/rescue 를 실행하여 400 Bad Request로 요청에 응답을 돌려줄 수도 있습니다. +나아가 매개 변수의 속성에는 '필수(required)'를 지정할 수 있으며, 사전에 정의해둔 raise/rescue를 +실행하여 400 Bad Request를 돌려줄 수도 있습니다. ```ruby class PeopleController < ActionController::Base - # 이 코드는 ActiveModel::ForbiddenAttributes 예외를 던집니다. - # 명시적으로 검증을 하지 않고 파라미터를 그냥 통째로 넘기고 있기 때문입니다. + # 이 코드는 ActiveModel::ForbiddenAttributesError 예외를 던집니다. + # 명시적으로 검증을 하지 않고 매개 변수를 그냥 통째로 넘기고 있기 때문입니다. def create Person.create(params[:person]) end - # 이 코드는 파라미터에 person이라는 키가 존재하는 경우에만 성공합니다. + # 이 코드는 매개 변수에 person이라는 키가 존재하는 경우에만 성공합니다. # person이라는 키가 없는 경우에는 ActionController::ParameterMissing 예외를 던집니다. # 이 예외는 ActionController::Base가 잡아 400 Bad Request로 반환합니다. def update @@ -208,8 +273,8 @@ class PeopleController < ActionController::Base end private - # private 메소드를 사용해서 파라미터 검증을 캡슐화합니다. - # 이를 통해 create와 update에서 같은 검증을 쉽게 사용할 수 있습니다. + # private 메소드를 사용해서 매개 변수 검증을 캡슐화합니다. + # 이를 통해 create와 update에서 같은 검증을 쉽게 재사용할 수 있습니다. # 또한 허가할 속성을 사용자마다 다르게 만들 수도 있습니다. def person_params params.require(:person).permit(:name, :age) @@ -219,33 +284,39 @@ end #### 허가된 값 -다음의 예제에서는 +다음의 예제에서는, ```ruby params.permit(:id) ``` -`:id` 키가 `params`에 있으며, 거기에 허가된 형식의 값이 들어있다면 화이트리스트 검증을 통과할 수 있습니다. 그렇지 않으면 그 값은 필터에 의해 제외됩니다. 따라서, 해시나 그이외의 객체를 외부에서 주입할 수 없게 됩니다. +`:id` 키가 `params`에 들어있으며 허가된 형식의 값이 들어있다면 화이트리스트 검증을 통과할 수 +있습니다. 그렇지 않으면 그 값은 필터에 의해 제거됩니다. 따라서 배열이나 해시, 그 이외의 객체를 +외부에서 주입할 수 없게 됩니다. -허가된 형식은 `String`, `Symbol`, `NilClass`, `Numeric`, `TrueClass`, `FalseClass`, `Date`, `Time`, `DateTime`, `StringIO`, `IO`, `ActionDispatch::Http::UploadedFile`, `Rack::Test::UploadedFile`입니다. +허가된 형식은 `String`, `Symbol`, `NilClass`, `Numeric`, `TrueClass`, +`FalseClass`, `Date`, `Time`, `DateTime`, `StringIO`, `IO`, +`ActionDispatch::Http::UploadedFile`, `Rack::Test::UploadedFile`입니다. -`params`의 값이 허가된 형식의 배열이어야만 한다고 선언하려면, 아래와 같이 빈 배열을 매핑하면 됩니다. +`params`의 값이 허가된 형식의 배열이어야 한다고 선언하려면, 아래와 같이 빈 배열을 매핑하면 됩니다. ```ruby params.permit(id: []) ``` -파라미터 해시 전체를 화이트리스트로 만들고 싶은 경우에는 `permit!`메소드를 사용할 수 있습니다. +매개 변수 해시 전체를 화이트리스트로 만들고 싶은 경우에는 `permit!` 메소드를 사용할 수 있습니다. ```ruby params.require(:log_entry).permit! ``` -이렇게 작성하면, `:log_entry` 파라미터 해시와 그 내부의 모든 값들을 허가하게 됩니다. 단, `permit!`은 속성을 한번에 모두 허가하게 되므로, 신중하게 사용해주세요. 현재 모델은 물론, 나중에 속성이 추가되었을 때 일괄 할당으로 취약점이 발생할 가능성이 있기 때문입니다. +이렇게 작성하면, `:log_entry` 매개 변수 해시와 그 내부의 모든 값들을 허가하게 됩니다. 단, +`permit!`은 가진 속성을 모두 허가하게 되므로, 신중하게 사용해주세요. 현재 모델은 물론, 나중에 +속성이 추가되더라도 일괄 할당되기 때문입니다. -#### 중첩된 파라미터 +#### 중첩된 매개 변수 -중첩된 파라미터에 대해서도 아래와 같이 검증할 수 있습니다. +중첩된 매개 변수에 대해서도 아래와 같이 검증할 수 있습니다. ```ruby params.permit(:name, { emails: [] }, @@ -253,11 +324,15 @@ params.permit(:name, { emails: [] }, { family: [ :name ], hobbies: [] }]) ``` -이 선언에서는 `name`, `emails`, `friends` 속성이 화이트리스트에 포함됩니다. 여기에서는 `emails`는 허가된 형식들을 포함하는 배열이길 요구하며, `friends`는 특정 속성을 가지는 리소스의 배열이길 요구하고, 어느쪽이든 `name` 속성(사용 가능한 형식일 경우에만)을 가져야합니다. 또한 `hobbies`와 `family` 속승을 가질 것을 요구합니다. +이 선언에서는 `name`, `emails`, `friends` 속성이 화이트리스트에 포함됩니다. 여기에서는 +`emails`는 허가된 형식들을 포함하는 배열이기를, `friends`는 특정 속성을 가지는 리소스의 +배열이길 요구하고, 어느 쪽이든 `name` 속성(사용 가능한 형식일 경우에만)을 가져야 합니다. 또한 +`hobbies`와 `family`를 요구합니다. #### 추가 예제 -이번에는 `new` 액션에서 검증된 속성을 사용해봅시다. 하지만 `new`를 호출하는 시점에서는 기점이 되는 객체가 존재하지 않으므로 `require`를 사용할 대상이 없다는 문제가 있습니다. +이번에는 `new` 액션에서 검증된 속성을 사용해봅시다. 하지만 `new`를 호출하는 시점에서는 이를 +사용할 객체가 존재하지 않으므로 `require`를 사용할 대상이 없다는 문제가 있습니다. ```ruby # `fetch`를 사용해서 기본 값을 제공하여 @@ -265,14 +340,17 @@ params.permit(:name, { emails: [] }, params.fetch(:blog, {}).permit(:title, :author) ``` -`accepts_nested_attributes_for` 메소드를 사용하면, 관계가 맺어진 레코드를 갱신하거나 삭제할 수 있습니다. 이 동작은 `id`와 `_destroy` 파라미터에 기반합니다. +`accepts_nested_attributes_for` 메소드를 사용하면, 관계가 맺어진 레코드를 갱신하거나 삭제할 +수 있습니다. 이 동작은 `id`와 `_destroy` 매개 변수를 사용합니다. ```ruby # :id와 :_destroy를 허가합니다. params.require(:author).permit(:name, books_attributes: [:title, :id, :_destroy]) ``` -정수 키를 가지는 해시는 다른 방식으로 처리됩니다. 이것들은 자식 객체를 가지고 있는 것처럼 속성을 선언할 수 있습니다. 이러한 종류의 파라미터는 `has_many` 관계와 함께 `accepts_nested_attributes_for` 메소드를 사용하여 얻어올 수 있습니다. +정수 키를 가지는 해시는 다른 방식으로 처리됩니다. 이것들은 자식 객체를 가지고 있는 것처럼 선언할 +수 있습니다. 이러한 종류의 매개 변수는 `has_many` 관계와 함께 +`accepts_nested_attributes_for` 메소드를 사용할 때 가져올 수 있습니다. ```ruby # 아래의 데이터를 화이트리스트로 만들기 @@ -283,11 +361,16 @@ params.require(:author).permit(:name, books_attributes: [:title, :id, :_destroy] params.require(:book).permit(:title, chapters_attributes: [:title]) ``` -#### Strong Parameters의 스코프 바깥 +#### Strong Parameters의 스코프 외부 -strong parameter API는 가장 일반적인 사용 상황을 고려하여 설계되어 있습니다. 다시 말해, 화이트리스트를 사용하는 모든 문제를 다룰 수 있을 정도로 만능은 아니라는 의미입니다. 그러나 이 API를 자신의 코드에 사용하는 것으로 상황에 대응하기 쉬워질 수는 있을 것입니다. +strong parameter API는 가장 일반적인 사용 상황을 고려하여 설계되어 있습니다. 다시 말해, +화이트리스트를 사용하는 모든 문제를 다룰 수 있을 정도로 만능은 아니라는 의미입니다. 그러나 +이 API를 사용하여 상황에 대응하기 쉬워질 수는 있을 것입니다. -다음과 같은 상황을 가정해봅시다. 제품명과 그 제품명에 관련된 임의의 데이터를 표현하는 파라미터가 있으며, 그 모두를 화이트리스트로 만들고 싶습니다. strong parameters API는 임의의 키를 가지는 중첩된 해시 전체를 직접 화이트리스트로 만들 수는 없습니다만, 중첩된 해시의 키를 사용해서 화이트리스트로 만들 대상을 선언할 수 있습니다. +다음과 같은 상황을 가정해봅시다. 제품명과 그 제품명에 관련된 임의의 데이터를 표현하는 매개 변수가 있으며, +그 모두를 화이트리스트로 만들고 싶습니다. strong parameter API는 임의의 키를 가지는 중첩된 해시 +전체를 직접 화이트리스트로 만들 수는 없습니다만, 중첩된 해시의 키를 사용해서 화이트리스트로 만들 대상을 +선언할 수 있습니다. ```ruby def product_params @@ -295,27 +378,41 @@ def product_params end ``` -세션(Session) +세션 ------- -Rails 애플리케이션에서는 사용자마다 세션을 설정합니다. 이전 요청의 정보를 다음의 요청에서도 사용하기 위해서 세션에 소량의 데이터를 저장합니다. 세션은 컨트롤러와 뷰에서만 사용 가능하며 아래처럼 복수의 저장소 중에서 하나를 선택하여 사용할 수 있습니다. +Rails 애플리케이션에서는 사용자마다 이전 요청의 정보를 다음의 요청에서도 사용하기 위해서 세션에 소량의 데이터를 저장합니다. 세션은 컨트롤러와 뷰에서만 사용 가능하며 아래처럼 복수의 저장소 중에서 하나를 선택하여 사용할 수 있습니다. * `ActionDispatch::Session::CookieStore` - 모든 세션을 클라이언트 측의 쿠키에 저장 -* `ActionDispatch::Session::CacheStore` - 데이터를 Rails의 캐시에 보존 -* `ActionDispatch::Session::ActiveRecordStore` - Active Record를 사용해서 데이터 베이스에 보존(`activerecord-session_store` gem이 필요) -* `ActionDispatch::Session::MemCacheStore` - 데이터를 memcached 클러스터에 보존(이 방식은 오래되었으므로 이보다는 CacheStore를 검토하길 권장) - -모든 방식은, 세션마다 식별자를 쿠키에 보존합니다(주의: Rails에서는 보안상의 위험이 있으므로 세션ID를 URL로 넘기는 행동을 허락하지 않습니다. 세션 ID는 쿠키로 넘겨야합니다). - -많은 세션 저장소에서는 이 ID는 단순히 서버의 세션 데이터(데이터베이스 테이블 등)을 검색하기 위해서 사용됩니다. 단 CookieStore는 예외적으로 쿠키에 모든 세션 정보를 저장합니다(세션 ID도 필요하다면 사용할 수 있습니다). 그리고 Rails에서는 CookieStore가 기본으로 사용되며, 또한 이것이 Rails에서 추천하는 저장소이기도 합니다. CookieStore의 이점은, 무척 가볍다는 점과 새로운 웹 애플리케이션에서 세션을 사용할 때에 추가 요구사항이 없다는 점입니다. 쿠키 데이터는 변경 방지를 위해 암호화 서명이 추가되어 있습니다. 또한 쿠키 자신도 암호화 되어있으므로 다른 사람이 읽을 수 없도록 되어있습니다(쿠키가 외부에 의해서 변경될 경우 Rails는 그 쿠키를 거부합니다). - -CookieStore에는 약 4KB의 데이터를 저장할 수 있습니다. 다른 세션 저장소에 비해서는 작습니다만, 보통 이것으로 충분합니다. 세션에 대량의 데이터를 저장한다는 것은 저장소의 종류에 관계없이 권장하지 않습니다. 특히, 세션에 복잡한 객체(모델 인스턴스 등의 기본 루비 객체가 아닌 것들)을 저장하는 것을 권장하지 않습니다. 이러한 것을 저장하는 경우, 서버가 리퀘스트마다 세션을 재구성하지 못하고 에러를 발생시키는 경우가 있습니다. - -사용자 세션에 중요한 정보가 포함되어있지 않은 경우, 또는 사용자 세션을 장기보존해야할 필요가 없는 경우(flash 메시지를 저장하기 위한 용도로만 사용할 경우)는 `ActionDispatch::Session::CacheStore`를 검토해주세요. 이 방식은 웹 애플리케이션에 설정되어있는 캐시 저장소를 이용하여 세션을 저장합니다. 이 방법의 좋은 점은 기존에 존재하는 환경을 그대로 사용하여 세션을 저장할 수 있다는 점과 관리용 설정을 추가할 필요가 없다는 점입니다. 반면, 이 방법의 단점은 세션의 수명이 짧아질 수 있다는 점입니다. 세션이 언제라도 사라질 가능성이 생깁니다. +* `ActionDispatch::Session::CacheStore` - 데이터를 Rails의 캐시에 저장 +* `ActionDispatch::Session::ActiveRecordStore` - 액티브 레코드를 사용해서 데이터베이스에 저장(`activerecord-session_store` gem이 필요) +* `ActionDispatch::Session::MemCacheStore` - 데이터를 memcached 클러스터에 저장(이 방식은 오래되었으므로 이보다는 CacheStore를 검토하길 권장) + +모든 방식은, 세션의 식별자를 쿠키에 보존합니다(주의: Rails에서는 보안상의 위험이 있으므로 세션ID를 URL로 넘기는 행동을 허락하지 않습니다. 세션 ID는 쿠키로 넘겨야합니다). + +많은 세션 저장소에서는 이 ID는 단순히 서버의 세션 데이터(데이터베이스 테이블 등)을 검색하기 위해서 +사용됩니다. 단 CookieStore는 예외적으로 쿠키에 모든 세션 정보를 저장합니다(세션 ID도 필요하다면 +사용할 수 있습니다). 그리고 Rails에서는 CookieStore가 기본으로 사용되며, 또한 이것이 Rails에서 +추천하는 저장소이기도 합니다. CookieStore의 이점은 무척 가볍다는 점과 새로운 웹 애플리케이션에서 +세션을 사용할 때에 추가 요구사항이 없다는 점입니다. 쿠키 데이터는 변경 방지를 위해 암호화 서명이 추가되어 +있습니다. 또한 쿠키 자신도 암호화 되어있으므로 다른 사람이 읽을 수 없도록 되어있습니다(쿠키가 외부에 의해서 변경될 경우 Rails는 그 쿠키를 거부합니다). + +CookieStore에는 약 4KB의 데이터를 저장할 수 있습니다. 다른 세션 저장소에 비해서는 작습니다만, +보통 이것으로 충분합니다. 세션에 대량의 데이터를 저장하는 방법은 저장소의 종류에 관계없이 권장하지 +않습니다. 특히, 세션에 복잡한 객체(모델 인스턴스 등 기본 루비 객체가 아닌 것들)을 저장하는 것도 +권장하지 않습니다. 이러한 객체를 저장하는 경우, 서버가 리퀘스트마다 세션을 재구성하지 못하고 에러를 +발생시키는 경우가 있습니다. + +사용자 세션에 중요한 정보가 포함되어 있지 않은 경우, 또는 사용자 세션을 장기 저장해야할 필요가 없는 +경우(flash 메시지를 저장하기 위한 용도로만 사용할 경우)는 +`ActionDispatch::Session::CacheStore`를 검토해주세요. 이 방식은 웹 애플리케이션에 +설정되어있는 캐시 저장소를 이용하여 세션을 저장합니다. 이 방법의 좋은 점은 기존에 존재하는 환경을 +그대로 사용하여 세션을 저장할 수 있다는 점과 관리용 설정을 추가할 필요가 없다는 점입니다. 반면, +이 방법의 단점은 세션의 수명이 짧아질 수 있다는 점입니다. 세션이 언제라도 사라질 가능성이 생깁니다. 세션 저장소에 대한 더 자세한 설명은 [보안 가이드](security.html)를 참조해주세요. -다른 세션 메커니즘이 필요한 경우에는 `config/initializers/session_store.rb` 파일을 통해 변경할 수 있습니다. +다른 세션 저장 방식이 필요한 경우에는 initializer를 통해 변경할 수 있습니다. ```ruby # 기본으로 사용하는 쿠키 기반 세션 대신에 데이터베이스 세션을 사용하는 경우에는, @@ -324,7 +421,7 @@ CookieStore에는 약 4KB의 데이터를 저장할 수 있습니다. 다른 세 # Rails.application.config.session_store :active_record_store ``` -Rails는 세션 데이터에 서명할 때에 세션 키(쿠키의 이름)를 생성합니다. 이 동작도 `config/initializers/session_store.rb`에서 변경 가능합니다. +Rails는 세션 데이터에 서명할 때에 세션 키(쿠키의 이름)를 생성합니다. 이 동작도 initializer에서 변경 가능합니다. ```ruby # 이 파일을 수정한 뒤에는 서버를 재시작해주세요. @@ -338,7 +435,8 @@ Rails.application.config.session_store :cookie_store, key: '_your_app_session' Rails.application.config.session_store :cookie_store, key: '_your_app_session', domain: ".example.com" ``` -Rails는 세션 데이터에 서명으로 사용할 비밀키를 (CookieStore용으로) 설정합니다.이 비밀키는 `config/secrets.yml`에서 변경 가능합니다. +Rails는 세션 데이터에 서명으로 사용할 비밀키를 (CookieStore용으로) 설정합니다.이 비밀키는 +`config/secrets.yml`에서 변경 가능합니다. ```ruby # Be sure to restart your server when you modify this file. @@ -348,7 +446,7 @@ Rails는 세션 데이터에 서명으로 사용할 비밀키를 (CookieStore용 # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. -# You can use `rake secret` to generate a secure secret key. +# You can use `rails secret` to generate a secure secret key. # Make sure the secrets in this file are kept private # if you're sharing your code publicly. @@ -369,11 +467,12 @@ NOTE: `CookieStore`를 사용중에 비밀키를 변경하면 기존의 세션 ### 세션에 접근하기 -컨트롤러에서는 `session` 메소드를 사용해서 세션을 사용할 수 있습니다. +컨트롤러에서는 `session` 메소드를 사용해서 세션에 접근할 수 있습니다. -NOTE: 세션은 지연 로딩됩니다. 액션에서 세션을 사용하지 않았을 경우, 세션은 불려지지 않습니다. 따라서 접근하지 않았다면 세션을 무효화할 필요가 없습니다. 사용하지 않으면 이미 세션은 무효가 되어있습니다. +NOTE: 세션은 지연 로딩됩니다. 액션에서 세션을 사용하지 않았을 경우, 세션은 로드되지 않습니다. +따라서 접근하지 않았다면 세션을 무효화할 필요가 없습니다. 그저 사용하지 않으면 됩니다. -세션의 값은 해시와 비슷하게 키/값 쌍을 사용해서 저장됩니다. +세션의 값은 해시와 비슷하게 키-값 쌍을 사용해서 저장합니다. ```ruby class ApplicationController < ActionController::Base @@ -397,7 +496,7 @@ class LoginsController < ApplicationController # "Create" a login, aka "log the user in" def create if user = User.authenticate(params[:username], params[:password]) - # 세션에 user.id를 저장하여, 다음 요청에서 사용할 수 있게 합니다. + # 세션에 사용자 ID를 저장하여, 다음 요청에서 사용할 수 있게 합니다. session[:current_user_id] = user.id redirect_to root_url end @@ -422,11 +521,14 @@ end ### Flash -flash는 세션의 특수형으로, 요청마다 내용물이 삭제 됩니다. 이 특징때문에 flash는 '직후의 요청'에서만 참조 가능합니다. 이것은 에러 메시지를 건네는 경우에 특히 편리합니다. +flash는 세션의 특수한 형태로, 요청마다 내용물이 삭제 됩니다. 이 특징때문에 flash는 직후의 요청에서만 +참조 가능합니다. 이것은 에러 메시지를 건네는 경우에 특히 편리합니다. -flash를 사용하는 방법은 세션과 거의 동일하며, 해시라고 생각하고 사용할 수 있습니다(이것을 [FlashHash](http://api.rubyonrails.org/classes/ActionDispatch/Flash/FlashHash.html) 인스턴스라고 부릅니다). +flash를 사용하는 방법은 세션과 거의 동일하며, 해시라고 생각하고 사용할 수 +있습니다([FlashHash](http://api.rubyonrails.org/classes/ActionDispatch/Flash/FlashHash.html) 인스턴스입니다). -예를 들어 로그아웃하는 동작을 구현한다고 생각해보죠. 컨트롤러에서 flash를 사용하여 다음 요청에서 표시할 메시지를 전송할 수 있습니다. +예를 들어 로그아웃하는 동작을 구현한다고 생각해보죠. 컨트롤러에서 flash를 사용하여 다음 요청에서 +표시할 메시지를 전송할 수 있습니다. ```ruby class LoginsController < ApplicationController @@ -438,7 +540,8 @@ class LoginsController < ApplicationController end ``` -flash메시지는 리다이렉션에도 사용할 수 있다는 점에 주목해주세요. 옵션으로 `:notice`, `:alert` 이외에도 일반적인 `:flash`를 사용할 수도 있습니다. +flash메시지는 리다이렉션에도 사용할 수 있다는 점에 주목해주세요. 옵션으로 `:notice`, `:alert` +이외에도 일반적인 `:flash`를 사용할 수도 있습니다. ```ruby redirect_to root_url, notice: "You have successfully logged out." @@ -446,7 +549,10 @@ redirect_to root_url, alert: "You're stuck here!" redirect_to root_url, flash: { referral_code: 1234 } ``` -이 `destroy` 엑션에서는 애플리케이션의 `root_url`로 리다이렉션 되며, 거기에서 메시지를 표시합니다. flash 메시지는 직전의 액션에서 어떠한 메시지가 저장되어 있는지에 관계없이 다음에 이루어지는 액션에 대해서만 사용된다는 점을 주의해주세요. Rails 애플리케이션의 레이아웃에서는 flash를 사용해서 경고나 안내문을 표시하는 것이 일반적입니다. +이 `destroy` 액션에서는 애플리케이션의 `root_url`로 리다이렉션 되며, 거기에서 메시지를 표시합니다. +flash 메시지는 직전의 액션에서 어떠한 메시지가 저장되어 있는지에 관계없이 다음에 이루어지는 액션에 대해서만 +사용된다는 점을 주의해주세요. Rails 애플리케이션의 레이아웃에서는 flash를 사용해서 경고나 안내문을 +표시하는 것이 일반적입니다. ```erb @@ -461,7 +567,7 @@ redirect_to root_url, flash: { referral_code: 1234 } ``` -이와 같이, 액션에서 통지(notice)나 알림(alert) 메시지를 넘겨주면 레이아웃 쪽에서 자동적으로 메시지를 표시합니다. +이와 같이, 액션에서 통지나 알림 메시지를 넘겨주면 레이아웃 쪽에서 자동적으로 메시지를 표시합니다. flash에는 통지나 경고 문자열 뿐만 아니라, 세션에서 보존 가능한 것이라면 무엇이든 저장 가능합니다. @@ -493,7 +599,11 @@ end #### `flash.now` -기본적으로 flash에 값을 추가하면 그 다음 요청에서 그 값을 사용할 수 있습니다만 상황에 따라서 다음 요청을 받기도 전에 같은 요청 내에서 이 flash 값을 참조하고 싶을 때가 있습니다. 예를 들어 `create` 액션에 실패해서 리소스가 저장되지 않았을 경우에, `new` 템플릿을 직접 랜더링하게 됩니다. 이 때 새로운 요청은 발생하지 않습니다만, 이러한 경우에도 flash를 사용해서 메시지를 넘기고 싶을 수 있습니다. 이러한 경우에는 `flash.now`를 사용하면 `flash`와 같은 요령으로 메시지를 사용할 수 있습니다. +기본적으로 flash에 값을 추가하면 그 다음 요청에서 그 값을 사용할 수 있습니다만 상황에 따라서 다음 요청 +전에 같은 요청 내에서 이 flash 값을 참조하고 싶을 때가 있습니다. 예를 들어 `create` 액션에 실패해서 +리소스가 저장되지 않았을 경우에, `new` 템플릿을 직접 랜더링하게 됩니다. 이 때 새로운 요청은 발생하지 +않습니다만, 이러한 경우에도 flash를 사용해서 메시지를 넘기고 싶을 수 있습니다. 이러한 경우에는 +`flash.now`를 사용하면 `flash`와 같은 요령으로 메시지를 사용할 수 있습니다. ```ruby class ClientsController < ApplicationController @@ -512,7 +622,10 @@ end Cookies ------- -Web 애플리케이션에서는 cookie라고 불리는 소량의 데이터를 클라이언트의 브라우저에 저장할 수 있습니다. HTTP에서는 기본적으로 요청과 요청 사이에 아무런 관련이 없습니다만, cookie를 사용하는 것으로 요청간에 (또는 세션 간에) 데이터를 유지할 수 있습니다. Rails에서는 `cookies` 메소드를 사용해서 cookie에 간단하게 접근할 수 있습니다. 접근 방법은 세션과 무척 비슷해서, 해시처럼 동작합니다. +웹 애플리케이션에서는 cookie라고 불리는 소량의 데이터를 클라이언트의 브라우저에 저장할 수 있습니다. +HTTP에서는 기본적으로 요청과 요청 사이에 아무런 관련이 없습니다만, cookie를 사용하는 것으로 요청 간에 +(또는 세션 간에) 데이터를 유지할 수 있습니다. Rails에서는 `cookies` 메소드를 사용해서 cookie에 +간단하게 접근할 수 있습니다. 접근 방법은 세션과 무척 비슷해서, 해시처럼 동작합니다. ```ruby class CommentsController < ApplicationController @@ -540,11 +653,16 @@ class CommentsController < ApplicationController end ``` -세션을 삭제할 경우에는 키에 `nil`을 대입했습니다만, cookie를 삭제하는 경우에는 `cookies.delete(:key)`를 사용해주세요. +세션을 삭제할 때에는 키에 `nil`을 대입했습니다만, cookie를 삭제하는 경우에는 +`cookies.delete(:key)`를 사용해주세요. -Rails에서는 비밀 데이터를 저장하기 위해서 서명이 된 cookie jar와 암호화 cookie jar를 사용할 수도 있습니다. 서명이 된 cookie jar에서는 암호회한 서명을 cookie값에 추가하는 것으로 cookie의 변조를 막습니다. 암호화 cookie jar에서는 서명을 추가하면서 값 자체를 암호화하여 사용자들이 읽을 수 없도록 만듭니다. 더 자세한 설명은 [API 문서](http://api.rubyonrails.org/classes/ActionDispatch/Cookies.html)를 참조해주세요. +Rails에서는 비밀 데이터를 저장하기 위해서 서명이 된 cookie jar와 암호화 cookie jar를 사용할 수 +있습니다. 서명이 된 cookie jar에서는 암호화한 서명을 cookie값에 추가하는 것으로 cookie의 변조를 +막습니다. 암호화 cookie jar에서는 서명을 추가할 뿐 아니라 값 자체를 암호화하여 사용자들이 읽을 수 없도록 +만듭니다. 더 자세한 설명은 [API 문서](http://api.rubyonrails.org/classes/ActionDispatch/Cookies.html)를 참조해주세요. -이런 특수한 cookie에서는 시리얼라이저를 사용해 값을 문자열로 직렬화하여 저장하고, 읽어들일 때 다시 역변환을 수행하여 루비 객체로 되돌립니다. +이런 특수한 cookie는 시리얼라이저를 사용해 값을 문자열로 직렬화하여 저장하고, 읽어들일 때 다시 역직렬화를 +수행하여 루비 객체로 되돌립니다. 사용할 시리얼라이저를 지정할 수도 있습니다. @@ -552,17 +670,22 @@ Rails에서는 비밀 데이터를 저장하기 위해서 서명이 된 cookie j Rails.application.config.action_dispatch.cookies_serializer = :json ``` -새로운 애플리케이션의 기본 시리얼라이저는 `:json`입니다. 기존의 cookie가 남아있는 옛 애플리케이션과의 호환성을 위해 `serializer` 옵션으로 아무것도 지정되지 않은 경우에는 `:marshal`을 사용합니다. +새 애플리케이션의 기본 시리얼라이저는 `:json`입니다. 기존의 cookie가 남아있는 옛 애플리케이션과의 +호환성을 위해 `serializer` 옵션으로 아무것도 지정되지 않은 경우에는 `:marshal`을 사용합니다. -시리얼라이저의 옵션으로 `:hybrid`를 지정할 수도 있습니다. 이 값을 지정하면 `Marshal`로 직렬화된 기존의 cookie를 읽어 들이고, 저장할 때에는 `JSON` 형식으로 저장합니다. 이것은 기존 애플리케이션에서 `:json` 시리얼라이저로 넘어갈 때 유용합니다. +시리얼라이저의 옵션으로 `:hybrid`를 지정할 수도 있습니다. 이 값을 지정하면 기존의 cookie를 읽을 때에는 +`Marshal`로 역직렬화를 하며, 저장할 때에는 `JSON` 형식을 사용합니다. 이것은 기존 애플리케이션에서 +`:json` 시리얼라이저로 넘어갈 때 유용합니다. -`load` 메소드와 `dump` 메소드에 응답하는 커스텀 시리얼라이저를 지정할 수도 있습니다. +또는 `load` 메소드와 `dump` 메소드에 응답하는 커스텀 시리얼라이저를 지정할 수도 있습니다. ```ruby Rails.application.config.action_dispatch.cookies_serializer = MyCustomSerializer ``` -`:json` 또는 `:hybrid` 시리얼라이저를 사용하는 경우, 일부 Ruby 객체가 JSON으로 직렬화 되지 않을 수 있다는 점을 주의해주세요. 예를 들어 `Date` 객체나 `Time` 객체는 문자열로 직렬화되며, `Hash`의 키 값은 string으로 변환됩니다. +`:json` 또는 `:hybrid` 시리얼라이저를 사용하는 경우, 일부 루비 객체는 JSON으로 직렬화할 수 없다는 +점을 주의해주세요. 예를 들어, `Date` 객체나 `Time` 객체는 문자열로 직렬화되며 `Hash`는 키만을 +문자열로 변환합니다. ```ruby class CookiesController < ApplicationController @@ -577,15 +700,17 @@ class CookiesController < ApplicationController end ``` -cookie에는 문자열이나 숫자등의 단순한 데이터만을 저장하는 것을 권장합니다. -cookie에 복잡한 객체를 저장해야하는 경우에는 이후 요청에서 cookie로부터 값을 읽어들일 때에 역변환 과정에 직접 관여를 해야할 필요가 있습니다. +cookie에는 문자열이나 숫자 등의 단순한 데이터만을 저장하는 것을 권장합니다. +cookie에 복잡한 객체를 저장해야하는 경우에는 이후 요청에서 cookie로부터 값을 읽어들일 때에 역직렬화 +과정에 직접 관여를 해야 합니다. -cookie 세션 저장소를 사용하는 경우 `session`나 `flash`의 경우도 마찬가지 입니다. +cookie 세션 저장소를 사용하는 경우 `session`나 `flash`에도 적용됩니다.셔 XML과 JSON 데이터를 랜더링하기 --------------------------- -ActionController 덕분에 `XML` 데이터나 `JSON` 데이터의 출력(랜더링)을 무척 간단하게 처리할 수 있습니다. scaffold를 사용해서 생성된 컨트롤러는 아래와 같이 되어 있을 것입니다. +ActionController 덕분에 `XML` 데이터나 `JSON` 데이터의 출력(랜더링)을 무척 간단하게 처리할 수 +있습니다. scaffold를 사용해서 생성된 컨트롤러는 아래와 같이 되어 있을 것입니다. ```ruby class UsersController < ApplicationController @@ -600,7 +725,8 @@ class UsersController < ApplicationController end ``` -이 코드에서는 `render xml: @users.to_xml`이 아닌 `render xml: @users`처럼 되어있다는 점에 주목해주세요. Rails는 객체가 문자열 형식이 아닌 경우에 자동적으로 `to_xml`을 호출해줍니다. +이 코드에서는 `render xml: @users.to_xml`이 아닌 `render xml: @users`처럼 되어있다는 점에 +주목해주세요. Rails는 객체가 문자열 형식이 아닌 경우에 자동적으로 `to_xml`을 호출해줍니다. 필터 ------- @@ -609,7 +735,9 @@ end 필터는 상속이 가능하기 때문에 `ApplicationController`에 필터를 설정하면 애플리케이션의 모든 컨트롤러에 적용됩니다. -"Before" 필터는 요청 처리를 도중에 중지시킬 수 있으므로 주의해주세요. 자주 사용되는 "Before" 필터의 사용법으로 사용자가 액션을 실행하기 전에 로그인을 요구하는 것이 있습니다. 이 필터 메소드는 아래와 같이 될 겁니다. +"before" 필터는 요청 처리를 도중에 중지시킬 수 있으므로 주의해주세요. 자주 사용되는 "before" 필터의 +사용법으로 사용자가 액션을 실행하기 전에 로그인을 요구하는 것이 있습니다. 이 필터 메소드는 아래와 같이 될 +겁니다. ```ruby class ApplicationController < ActionController::Base @@ -620,15 +748,21 @@ class ApplicationController < ActionController::Base def require_login unless logged_in? flash[:error] = "You must be logged in to access this section" - redirect_to new_login_url # halts request cycle + redirect_to new_login_url # 처리를 중지한다 end end end ``` -이 메소드는 에러 메시지를 flash에 저장하고 사용자가 로그인하지 않았을 경우에 로그인 페이지로 리다이렉션하는 간단한 코드입니다. "before" 필터에 의해서 출력 또는 리다이렉션이 발생하면, 이 액션은 실행되지 않습니다. 필터의 실행 후에 실행될 예정이었던 다른 필터가 있었던 경우, 이 역시 실행이 취소됩니다. +이 메소드는 에러 메시지를 flash에 저장하고 사용자가 로그인하지 않았을 경우에 로그인 페이지로 돌려 보내는 +간단한 코드입니다. "before" 필터에 의해서 출력 또는 리다이렉션이 발생하면, 이 액션은 실행되지 않습니다. +필터의 실행 후에 실행될 예정이었던 다른 필터가 있다면, 이 역시 실행이 취소됩니다. -이 예시에서는 필터를 `ApplicationController`에 추가했으므로, 이를 상속하는 모든 컨트롤러에 영향을 주게 됩니다. 다시 말해, 애플리케이션의 모든 기능에 대해서 로그인을 요구하게 됩니다. 당연하지만 애플리케이션의 모든 화면에서 인증을 요구하게 되면, 인증에 필요한 로그인 화면까지 출력할 수 없게 되는 곤란한 상황이 됩니다. 따라서 이렇게 모든 컨트롤러나 액션에 대해서 로그인을 요구해서는 안됩니다. `skip_before_action`을 사용하면 특정 액션에서 필터의 사용을 막을 수 있습니다. +이 예시에서는 필터를 `ApplicationController`에 추가했으므로, 이를 상속하는 모든 컨트롤러에 영향을 +주게 됩니다. 다시 말해, 애플리케이션의 모든 기능에 대해서 로그인을 요구하게 됩니다. 당연하지만 +애플리케이션의 모든 화면에서 인증을 요구하게 되면, 인증에 필요한 로그인 화면까지 출력할 수 없게 되는 +곤란한 상황이 됩니다. 따라서 이렇게 모든 컨트롤러나 액션에 대해서 로그인을 요구해서는 안됩니다. +`skip_before_action`을 사용하면 특정 액션에서 필터의 사용을 막을 수 있습니다. ```ruby class LoginsController < ApplicationController @@ -636,17 +770,25 @@ class LoginsController < ApplicationController end ``` -이렇게 작성하는 것으로 `LoginsController`의 `new` 액션과 `create` 액션을 지금까지처럼 인증을 요구하지 않도록 만들 수 있습니다. 특정 액션에서만 필터를 무효화하고 싶은 경우에는 `:only` 옵션을 사용하세요. 반대로 특정 액션에서만 필터를 무효화하고 싶지 않은 경우에는 `:except` 옵션을 사용합니다. 이러한 옵션은 필터를 추가할 때에도 사용할 수 있으므로, 처음 선언할 때에 선택된 액션에 대해서만 필터가 실행되도록 만들 수도 있습니다. +이렇게 작성하는 것으로 `LoginsController`의 `new` 액션과 `create` 액션을 지금까지처럼 인증을 +요구하지 않도록 만들 수 있습니다. 특정 액션에서만 필터를 무효화하고 싶은 경우에는 `:only` 옵션을 +사용하세요. 반대로 특정 액션에서만 필터를 쓰고 싶은 경우에는 `:except` 옵션을 사용합니다. +이러한 옵션은 필터를 추가할 때에도 사용할 수 있으므로, 처음 선언할 때에 선택된 액션에 대해서만 필터가 +실행되도록 만들 수도 있습니다. ### after 필터와 around 필터 -"before" 필터 이외에도 액션 실행후에 실행되는 필터나, 실행 전후 모두에 실행되는 필터를 사용할 수 있습니다. +"before" 필터 이외에도 액션 실행후에 실행되는 필터나 실행 전후 모두에 실행되는 필터를 사용할 수 있습니다. -"after" 필터는 "before" 필터와 비슷합니다만, "after" 필터의 경우에는 액션이 이미 실행된 상태이며, 클라이언트에 전송할 데이터에 접근할 수 있다는 점이 다릅니다. 당연하지만 "after" 필터를 어떻게 작성하더라도 액션의 실행을 중단할 수는 없습니다. +"after" 필터는 "before" 필터와 비슷합니다만, "after" 필터의 경우에는 액션이 이미 실행된 상태이며, +클라이언트에 전송할 데이터에 접근할 수 있다는 점이 다릅니다. 당연하지만 "after" 필터를 어떻게 +작성하더라도 액션의 실행을 중단할 수는 없습니다. -"around" 필터를 사용하는 경우에는 필터 내부의 어딘가에서 반드시 `yield` 를 실행해서 액션을 실행시켜줘야할 의무가 있습니다. 이것은 Rack 미들웨어의 동작과 비슷합니다. +"around" 필터를 사용하는 경우에는 필터 내부의 어딘가에서 반드시 `yield` 를 실행해서 액션을 +실행시켜줘야할 의무가 있습니다. 이것은 Rack 미들웨어의 동작과 비슷합니다. -예를 들어 어떤 변경에 대해서 승인 처리를 하는 웹사이트를 생각해 봅시다. 관리자는 이 변경 내용을 간단하게 확인하고, 트랜잭션 내에서 승인처리를 한다고 합시다. +예를 들어 어떤 변경에 대해서 승인 처리를 하는 웹사이트를 생각해 봅시다. 관리자는 간단하게 이 변경 내용을 +미리 확인하고, 트랜잭션 내에서 승인처리를 한다고 합시다. ```ruby class ChangesController < ApplicationController @@ -666,15 +808,18 @@ class ChangesController < ApplicationController end ``` -"around" 필터의 경우 화면 출력(랜더링)도 yield에 포함된다는 점에 주의해주세요. 특히 위의 예시로 말하자면 뷰 자신이 데이터베이스로부터 (스코프 등을 사용해서) 읽는 작업을 하게 되면, 그 작업 역시 트랜잭션에 포함되므로 데이터는 프리뷰에서 볼 수 있게 됩니다. +"around" 필터의 경우 화면 출력(랜더링)도 yield에 포함된다는 점에 주의해주세요. 특히 위의 예시로 +말하자면 뷰 자신이 데이터베이스로부터 (스코프 등을 통해) 읽기 작업을 하게 되면, 그 작업 역시 트랜잭션에 +포함되므로 프리뷰에서 볼 수 있게 됩니다. 일부러 yield를 실행하지 않고 직접 응답을 생성한다는 방법도 존재합니다. 이 경우 액션은 실행되지 않습니다. ### 그 이외의 필터 사용법 -가장 일반적인 필터 사용 방법은 private 메소드를 작성하고, *_action을 사용해서 그 메소드를 추가하는 것입니다만, 같은 결과를 얻을 수 있는 방법이 2가지 더 존재합니다. +가장 일반적인 필터 사용 방법은 private 메소드를 작성하고, *_action을 사용해서 그 메소드를 추가하는 +것입니다만, 같은 결과를 얻을 수 있는 방법이 2가지 더 존재합니다. -첫번째는 *_action 메소드에 직접 블록을 넘겨주는 방법입니다. 이 블록은 컨트롤러를 가인수로 가지며, 위편의 `require_login` 필터의 내용물을 작성하게 됩니다. +첫번째는 *_action 메소드에 직접 블록을 넘겨주는 방법입니다. 이 블록은 컨트롤러를 가인수로 가집니다. 위의 `require_login` 필터를 재작성해보죠. ```ruby class ApplicationController < ActionController::Base @@ -687,9 +832,14 @@ class ApplicationController < ActionController::Base end ``` -필터에서 `send` 메소드를 사용하고 있는 점에 주목해주세요. `logged_in?` 메소드는 private이기 때문에 컨트롤러의 스코프에서는 필터가 동작하지 않기 때문입니다(역주: `send` 메소드를 사용하면 private 메소드를 호출할 수 있습니다). 이 방법은 특정 필터를 구현하는 방법으로서는 권장되지 않습니다만, 간결하게 작성하고 싶은 경우에는 도움이 될 수도 있습니다. +필터에서 `send` 메소드를 사용하고 있는 점에 주목해주세요. `logged_in?` 메소드는 private이기 때문에 +컨트롤러의 스코프에서는 필터가 동작하지 않기 때문입니다(역주: `send` 메소드를 사용하면 private 메소드를 +호출할 수 있습니다). 이 방법은 특정 필터를 구현하는 방법으로서는 권장되지 않습니다만, 간결하게 작성하고 +싶은 경우에는 도움이 될 수도 있습니다. -두번째 방법은 클래스를 사용해서 필터를 구현하는 것입니다(실제로는 특정 메소드에 올바르게 응답하는 객체라면 무엇이든 괜찮습니다). 다른 두가지 방법으로 구현하면 읽기도 어렵고, 재사용하기도 힘든 경우에 유용합니다. 예를 들어 로그인 필터를 클래스를 사용하는 방식으로 변경해봅시다. +두번째 방법은 클래스를 사용해서 필터를 구현하는 것입니다(실제로는 특정 메소드에 올바르게 응답하는 객체라면 +무엇이든 괜찮습니다). 다른 두가지 방법으로 구현하면 읽기도 어렵고, 재사용하기도 힘든 경우에 유용합니다. +예를 들어 로그인 필터를 클래스를 사용하는 방식으로 변경해봅시다. ```ruby class ApplicationController < ActionController::Base @@ -706,16 +856,26 @@ class LoginFilter end ``` -반복하지만, 이 예시는 필터로서는 이상적인 구현이 아닙니다. 왜냐하면 이 필터는 컨트롤러의 스코프에서 동작하지 않고, 컨트롤러가 인자로서 넘겨지기 때문입니다. 이 필터 클래스에는 필터와 같은 이름의 메소드가 구현될 필요가 있습니다. 따라서 `before_action` 필터의 경우, 클래스에 `before` 메소드를 구현할 필요가 있습니다. `around` 메소드에서는 `yield`를 호출해서 액션을 실행할 필요가 있다는 것도 잊지 마세요. +반복하지만, 이 예시는 필터로서는 이상적인 구현이 아닙니다. 왜냐하면 이 필터는 컨트롤러가 인자로서 넘겨받을 +뿐 아니라, 컨트롤러의 스코프에서 동작하지 않습니다. 이 필터 클래스에는 필터와 같은 이름의 메소드가 구현될 +필요가 있습니다. 따라서 `before_action` 필터의 경우, 클래스에 `before` 메소드를 구현할 필요가 +있습니다. `around` 메소드에서는 `yield`로 액션을 실행해야 한다는 것도 잊지 마세요. Request Forgery 방어 -------------------------- -CSRF(Cross Site Request Forgery)는 공격방법 중 한가지 입니다. 악의있는 웹사이트가 사용자를 속이고, 공격 목표 웹사이트에 위험한 요청을 몰래 전송하는 것입니다. 공격자는 대상에 대한 지식이나 권한을 가지고 있지 않더라도, 목표 사이트에 대해서 데이터를 추가/변경/삭제를 할 수 있습니다. +CSRF(Cross Site Request Forgery)는 악의있는 웹사이트가 사용자를 속이고, 공격 목표 웹사이트에 +위험한 요청을 몰래 전송하는 공격방법 중 한가지 입니다. 공격자는 대상에 대한 지식이나 권한을 가지고 있지 +않더라도, 목표 사이트에 대해서 데이터를 추가/변경/삭제를 할 수 있습니다. -이 공격을 방어하기 위해서 필요한 첫번째 방법은, 절대로 GET 요청을 통해서 'create/update/destroy'와 같은 파괴적인 조작을 할 수 없게 하는 것입니다. 웹 애플리케이션이 RESTful 규칙을 따르고 있다면 큰 문제는 없을 것입니다. 그러나 악의적인 웹사이트는 GET이외의 요청을 목표 사이트에 전송하는 것도 간단히 해낼 수 있습니다. Request Forgery 방어는 바로 그것을 막기 위한 것입니다. 말 그대로 요청을 위조(forgery)로 부터 보호합니다. +이 공격을 방어하기 위해서 필요한 첫번째 방법은, 절대로 GET 요청을 통해서 +'create/update/destroy'와 같은 파괴적인 조작을 하지 않는 것입니다. 웹 애플리케이션이 +RESTful 규칙을 따르고 있다면 이미 이 기준을 통과합니다. 그러나 악의적인 웹사이트는 GET 이외의 요청을 +목표 사이트에 전송하는 것도 간단히 해낼 수 있습니다. Request Forgery 방어는 바로 그것을 막기 위한 +것입니다. 말 그대로 요청을 위조(forgery)로 부터 보호합니다. -구체적으로는 추측 불가능한 토큰을 모든 요청에 추가합니다. 이 토큰은 서버만이 알고 있으므로 요청이 포함하고 있는 토큰이 잘못되었다면, 접근을 거부합니다. +구체적으로는 추측 불가능한 토큰을 서버에 들어오는 모든 요청에 추가합니다. 요청이 포함하고 있는 토큰이 +올바르지 않다면 접근을 거부합니다. 아래와 같은 폼을 생성해보죠. @@ -733,65 +893,87 @@ CSRF(Cross Site Request Forgery)는 공격방법 중 한가지 입니다. 악의 - + ``` -Rails에서는 [Form헬퍼](form_helpers.html)를 사용해서 생성된 모든 폼에 토큰을 추가합니다. Form헬퍼를 사용하지 않고 직접 작성한 경우나, 다른 이유로 토큰이 필요한 경우에는 `form_authenticity_token` 메소드를 통해 토큰을 생성할 수 있습니다. +Rails에서는 [Form 헬퍼](form_helpers.html)를 사용해서 생성된 모든 폼에 토큰을 추가합니다. +Form 헬퍼를 사용하지 않고 직접 작성한 경우나, 다른 이유로 토큰이 필요한 경우에는 +`form_authenticity_token` 메소드를 통해 토큰을 생성할 수 있습니다. -`form_authenticity_token`메소드는 유효한 인증 토큰을 생성합니다. 이 메소드는 커스텀 Ajax 호출 등, Rails가 자동으로 토큰을 생성해주지 않는 장소에서 사용할 때에 유용합니다. +`form_authenticity_token`메소드는 유효한 인증 토큰을 생성합니다. 이 메소드는 커스텀 Ajax 호출 +등, Rails가 자동으로 토큰을 생성해주지 않는 장소에서 사용할 때에 유용합니다. -이 가이드의 [보안 가이드](security.html)에서는 이 주제를 포함한 많은 보안 문제에 대해서 언급하고 있으며, 그 모두는 웹 애플리케이션 개발할 때에 반드시 읽어야 할 것들입니다. +이 가이드의 [보안 가이드](security.html)에서는 이 주제를 포함한 많은 보안 문제에 대해서 언급하고 +있으며, 그 모두는 웹 애플리케이션 개발할 때에 반드시 읽어야 하는 것입니다. 요청 객체와 응답 객체 -------------------------------- -모든 컨트롤러에는 현재 실행중인 요청과 관련된 요청 객체와 응답 객체를 가리키는 2개의 접근 메소드가 있습니다. `request` 메소드는 `AbstractRequest`클래스의 인스턴스를 포함하고, `response` 메소드는 현재 클라이언트에게 돌려줄 내용을 가지고 있는 응답 객체를 돌려줍니다. +모든 컨트롤러에는 현재 실행중인 요청과 관련된 요청 객체와 응답 객체를 가리키는 2개의 접근 메소드가 +있습니다. `request` 메소드는 `ActionDispatch::Request` 클래스의 인스턴스를 포함하고, +`response` 메소드는 현재 클라이언트에게 돌려줄 내용을 가지고 있는 응답 객체를 돌려줍니다. ### `request` 객체 -요청 객체에는 브라우저로부터 전송된 요청에 대한 유용한 정보가 다수 포함되어 있습니다. 이용가능한 메소드를 모두 알고 싶은 경우에는 [API문서](http://api.rubyonrails.org/classes/ActionDispatch/Request.html)를 참조해주세요. 여기에서는 그 중에서 몇가지를 소개합니다. +요청 객체에는 브라우저로부터 전송된 요청에 대한 유용한 정보가 다수 포함되어 있습니다. 사용 가능한 메소드를 +모두 알고 싶은 경우에는 [Rails API 문서](http://api.rubyonrails.org/classes/ActionDispatch/Request.html)와 +[Rack 문서](http://www.rubydoc.info/github/rack/rack/Rack/Request)를 +참조해주세요. 여기에서는 그 중에서 몇 가지를 소개합니다. -| `request`의 속성 | 목적 | +| `request`의 속성 | 목적 | | ----------------------------------------- | -------------------------------------------------------------------------------- | -| host | 요청에 사용된 호스트 이름 | -| domain(n=2) | 호스트의 이름(TLD)의 우측으로부터 `n`번째 세그먼트 | -| format | 클라이언트로부터 요청받은 Content-Type | -| method | 요청에서 사용된 HTTP 메소드 | -| get?, post?, patch?, put?, delete?, head? | HTTP메소드가 GET/POST/PATCH/PUT/DELETE/HEAD 중 각각 맞는 메소드에 해당하는 경우 true를 돌려줌 | -| headers | 요청에 포함되어있는 헤더를 포함하는 해시를 돌려줌 | -| port | 요청에 사용된 포트 번호(정수) | -| protocol | 사용된 프로토콜을 포함한 주소 문자열을 돌려줌(예를 들어, "http://....." 이런 형태) | -| query_string | URL에서 사용된 쿼리 문자열("?" 뒷 부분) | -| remote_ip | 클라이언트의 ip 주소 | -| url | 요청에서 사용된 URL 전체 | +| host | 요청에 사용된 호스트 이름 | +| domain(n=2) | 호스트의 이름(TLD)의 우측으로부터 `n`번째 세그먼트 | +| format | 클라이언트로부터 요청받은 Content-Type | +| method | 요청에서 사용된 HTTP 메소드 | +| get?, post?, patch?, put?, delete?, head? | HTTP메소드가 GET/POST/PATCH/PUT/DELETE/HEAD 중 각각 맞는 메소드에 해당하는 경우 true를 돌려줌 | +| headers | 요청에 포함되어있는 헤더를 포함하는 해시를 돌려줌 | +| port | 요청에 사용된 포트 번호(정수) | +| protocol | 사용된 프로토콜을 포함한 주소 문자열을 돌려줌(예를 들어, "http://....." 이런 형태) | +| query_string | URL에서 사용된 쿼리 문자열("?" 뒷 부분) | +| remote_ip | 클라이언트의 ip 주소 | +| url | 요청에서 사용된 URL 전체 | #### `path_parameters`, `query_parameters`, `request_parameters` -Rails는 요청 시에 받은 쿼리 문자열, 또는 POST로 받은 값 등을 모두 `params` 해시에 모아줍니다. Request 객체에는 3개의 접근자가 있으며 파라미터의 출처에 따라 접근할 수도 있습니다. `query_parameters` 해시에는 쿼리 문자열로 전송된 파라미터가 포함됩니다. `request_parameters` 해시에는 POST 본문에 포함된 파라미터가 들어있습니다. `path_parameters`에는 라우팅에 따라 특정 컨트롤러와 액션에 대한 경로로 인식된 파라미터가 포함됩니다. +Rails는 요청 시에 받은 쿼리 문자열, 또는 POST로 받은 값 등을 모두 `params` 해시에 모아줍니다. +Request 객체에는 3개의 접근자가 있으며 매개 변수의 출처에 따라 접근할 수도 있습니다. +`query_parameters` 해시에는 쿼리 문자열로 전송된 매개 변수가 포함됩니다. `request_parameters` +해시에는 POST 본문에 포함된 매개 변수가 들어있습니다. `path_parameters`에는 라우팅에 따라 특정 +컨트롤러와 액션에 대한 경로로 인식된 매개 변수가 포함됩니다. ### `response` 객체 -response 객체는 액션이 실행 될 때에 생성되며, 클라이언트에 돌려줄 데이터를 랜더링하기 위한 것이므로, response 객체를 직접 사용할 일은 그다지 없습니다. 하지만 때때로 (예를 들자면, after filter에서) response 객체를 직접 조작할 수 있다면 편리할 겁니다. response 객체의 접근 메소드들은 세터(setter)도 가지고 있으므로, 이를 사용해서 response 객체의 값들을 직접 변경할 수 있습니다. - -| `response`의 속성 | 목적 | -| ---------------------- | --------------------------------------------------------------------------------------------------- | -| body | 클라이언트에 돌려줄 데이터의 문자열. 대부분의 경우 HTML | -| status | 응답의 HTTP 상태 코드(200 OK, 404 file not found 등)| -| location | 리다이렉션을 할 URL | -| content_type | 응답의 Content-Type | -| charset | 응답에 사용될 문자셋. 기본은 "utf-8" | -| headers | 응답에 사용될 헤더들 | +response 객체는 액션이 실행 될 때에 생성되며, 클라이언트에 돌려줄 데이터를 랜더링하기 위한 것이므로, +response 객체를 직접 사용할 일은 그다지 없습니다. 하지만 때때로 (예를 들자면, after filter에서) +response 객체를 직접 조작할 수 있다면 편리할 겁니다. response 객체의 접근 메소드들은 +세터(setter)도 가지고 있으므로, 이를 사용해서 response 객체의 값들을 직접 변경할 수 있습니다. +사용 가능한 전체 메소드 목록을 확인하고 싶다면 [Rails API 문서](http://api.rubyonrails.org/classes/ActionDispatch/Response.html)와 +[Rack 문서](http://www.rubydoc.info/github/rack/rack/Rack/Response)를 +참조해주세요. + +| `response`의 속성 | 목적 | +| ---------------------- | ------------------------------------------------ | +| body | 클라이언트에 돌려줄 데이터의 문자열. 대부분의 경우 HTML | +| status | 응답의 HTTP 상태 코드(200 OK, 404 file not found 등) | +| location | 리다이렉션을 할 URL | +| content_type | 응답의 Content-Type | +| charset | 응답에 사용될 문자셋. 기본은 "utf-8" | +| headers | 응답에 사용될 헤더들 | #### 커스텀 헤더 설정하기 -응답에서 커스텀 헤더를 사용하고 싶은 경우라면 `response.headers`를 사용할 수 있습니다. 이 헤더 속성은 해시이며, 헤더명과 값이 그 내부에 들어있으며, 이미 몇몇 값들이 Rails에 의해 자동적으로 저장되어 있습니다. 헤더를 추가, 변경하고 싶은 경우에는 아래와 같이 `response.headers`에 할당하면 됩니다. +응답에서 커스텀 헤더를 사용하고 싶은 경우에는 `response.headers`를 사용할 수 있습니다. +이 헤더 속성은 해시이며, 헤더명과 값이 그 내부에 들어있으며, 이미 몇몇 값들이 Rails에 의해 자동으로 +저장되어 있습니다. 헤더를 추가, 변경하고 싶은 경우에는 아래와 같이 `response.headers`에 할당하면 +됩니다. ```ruby response.headers["Content-Type"] = "application/pdf" ``` -NOTE: 이렇게 하고 싶은 경우에는 `content_type` 세터를 직접 사용하는 것이 읽기 좋습니다. +NOTE: 이렇게 하고 싶은 경우에는 `content_type` 세터를 직접 사용하는 것이 바람직합니다. HTTP 인증 -------------------- @@ -803,7 +985,10 @@ Rails에는 2가지의 HTTP인증 기능이 내장되어 있습니다. ### HTTP BASIC 인증 -HTTP BASIC인증은 인증 스킴의 일종으로, 많은 브라우저 및 HTTP 클라이언트에서 지원되고 있습니다. 예를 들어 웹 애플리케이션에는 관리 화면이 있고, 브라우저의 HTTP BASIC 인증 창에서 사용자의 이름과 비밀번호를 입력하지 않으면 접근할 수 없도록 만들고 싶다고 해봅시다. 이 내장 인증 기능은 무척 간단하게 사용할 수 있습니다. 필요한 것은 `http_basic_authenticate_with` 뿐입니다. +HTTP BASIC 인증은 인증 방식의 일종으로, 많은 브라우저 및 HTTP 클라이언트에서 지원되고 있습니다. +예를 들어 웹 애플리케이션에는 관리 화면이 있고, 브라우저의 HTTP BASIC 인증 창에서 사용자의 이름과 +비밀번호를 입력하지 않으면 접근할 수 없도록 만들고 싶다고 해봅시다. 이 내장 인증 기능은 무척 간단하게 +사용할 수 있습니다. 필요한 것은 `http_basic_authenticate_with` 뿐입니다. ```ruby class AdminsController < ApplicationController @@ -811,11 +996,15 @@ class AdminsController < ApplicationController end ``` -이 때 `AdminsController`를 상속한 컨트롤러를 만들어도 좋습니다. 이 필터는 해당하는 컨트롤러의 모든 액션에서 실행되므로, 그 HTTP BASIC인증을 통해 보호할 수 있습니다. +이 때 `AdminsController`를 상속한 컨트롤러를 만들어도 좋습니다. 이 필터는 해당하는 컨트롤러의 +모든 액션에서 실행되므로, 그 HTTP BASIC인증을 통해 보호할 수 있습니다. ### HTTP 다이제스트 인증 -HTTP 다이제스트 인증은 BASIC 인증 보다도 고도의 인증 시스템으로 암호회지 않은 평문 패스워드를 네트워크를 통해 전송하지 않아도 된다는 장점이 있습니다(BASIC인증도 HTTPS를 통하면 안전해집니다). Rails에서는 다이제스트 인증 역시 간단하게 사용할 수 있습니다. `authenticate_or_request_with_http_digest` 메소드를 사용하세요. +HTTP 다이제스트 인증은 BASIC 인증 보다도 고도의 인증 시스템으로 암호화지 않은 평문 패스워드를 +네트워크를 통해 전송하지 않아도 된다는 장점이 있습니다(BASIC인증도 HTTPS를 통하면 안전해집니다). +Rails에서는 다이제스트 인증 역시 간단하게 사용할 수 있습니다. +authenticate_or_request_with_http_digest` 메소드를 사용하세요. ```ruby class AdminsController < ApplicationController @@ -833,12 +1022,18 @@ class AdminsController < ApplicationController end ``` -위의 예제에서 볼 수 있듯, `authenticate_or_request_with_http_digest`의 블록 내에서는 가인수를 하나(사용자 이름)밖에 받을 수가 없습니다. 그리고 블록에서는 패스워드가 반환됩니다. `authenticate_or_request_with_http_digest`에서 `nil` 또는 `false`가 반환된 경우에는 인증이 실패합니다. +위의 예제에서 볼 수 있듯, `authenticate_or_request_with_http_digest`의 블록 내에서는 +가인수를 하나(사용자 이름)밖에 받을 수가 없습니다. 그리고 블록에서는 패스워드가 반환됩니다. +`authenticate_or_request_with_http_digest`에서 `nil` 또는 `false`가 반환된 경우에는 +인증이 실패합니다. 스트리밍과 파일 다운로드 ---------------------------- -HTML을 출력하지 않고, 사용자에게 파일을 직접 전송하고 싶은 경우가 있습니다. `send_data` 메소드와 `send_file` 메소드는 Rails의 모든 컨트롤러에서 사용 가능하며, 둘 다 스트림 데이터를 클라이언트에게 전송하기 위해서 사용됩니다. `send_file`은 디스크 상의 파일명을 얻거나, 파일의 내용을 스트리밍하는 등 편리한 메소드입니다. +HTML을 출력하지 않고, 사용자에게 파일을 직접 전송하고 싶은 경우가 있습니다. `send_data` 메소드와 +`send_file` 메소드는 Rails의 모든 컨트롤러에서 사용 가능하며, 둘 다 스트림 데이터를 클라이언트에게 +전송하기 위해서 사용됩니다. `send_file`은 디스크 상의 파일명을 얻거나, 파일의 내용을 스트리밍하는 등 +편리한 메소드입니다. 클라이언트에 데이터를 전송하고 싶은 경우에는 `send_data`를 사용합니다. @@ -866,7 +1061,13 @@ class ClientsController < ApplicationController end ``` -위의 예제에서 `download_pdf` 엑션에서 private 메소드가 호출되어 실제 PDF 생성은 private 메소드에서 실행됩니다. PDF는 문자열의 형태로 반환됩니다. 이어서, 이 문자열은 클라이언트에 대해서 파일 다운로드 형태로 전송됩니다. 이때 저장용 파일명 역시 클라이언트에 표시됩니다. 스트리밍 전송할 파일을 클라이언트에 파일로서 다운로드하지 못하게 하고 싶은(파일로 저장하지 못하게 하고 싶은) 경우가 있습니다. 때때로 HTML 페이지에 삽입 가능한 이미지 파일을 촬영했다고 가정합시다. 이때 브라우저에 대해서 이 파일이 저장용이 아니라는 것을 알리기 위해서 `:disposition` 옵션에 "inline"을 지정합니다. 반대의 옵션은 "attachment"로 이것은 전송시의 기본 설정입니다. +위의 예제에서 `download_pdf` 액션에서 private 메소드가 호출되어 실제 PDF 생성은 private +메소드에서 실행됩니다. PDF는 문자열의 형태로 반환됩니다. 이어서, 이 문자열은 클라이언트에 대해서 파일 +다운로드 형태로 전송됩니다. 이때 저장용 파일명 역시 클라이언트에 표시됩니다. 스트리밍 전송할 파일을 +클라이언트에 파일로서 다운로드하지 못하게 하고 싶은(파일로 저장하지 못하게 하고 싶은) 경우가 있습니다. +때때로 HTML 페이지에 삽입 가능한 이미지 파일을 촬영했다고 가정합시다. 이때 브라우저에 대해서 이 파일이 +저장용이 아니라는 것을 알리기 위해서 `:disposition` 옵션에 "inline"을 지정합니다. 반대의 옵션은 +"attachment"로 이것은 전송시의 기본 설정입니다. ### 파일 전송하기 @@ -884,17 +1085,29 @@ class ClientsController < ApplicationController end ``` -파일은 4KB씩 나누어서 스트리밍으로 전송됩니다. 이것은 커다란 파일을 한번에 메모리에 읽지 않게 하기 위함입니다. 이 나눠 읽기는 `:stream` 옵션에서 끌 수 있습니다. `:buffer_size` 옵션에 이 블럭 사이즈를 지정할 수도 있습니다. +파일은 4KB씩 나누어서 스트리밍으로 전송됩니다. 이것은 커다란 파일을 한번에 메모리에 읽지 않게 하기 +위함입니다. 이 나눠 읽기는 `:stream` 옵션에서 끌 수 있습니다. `:buffer_size` 옵션에 이 블럭 +사이즈를 지정할 수도 있습니다. -`:type` 옵션이 미지정인 경우 `:filename`으로 넘겨받은 파일을 보고 추측합니다. 확장자에 해당하는 Content-Type이 Rails에 등록되어있지 않은 경우, `application/octet-stream`가 사용됩니다. +`:type` 옵션이 미지정인 경우 `:filename`으로 넘겨받은 파일을 보고 추측합니다. 확장자에 해당하는 +Content-Type이 Rails에 등록되어있지 않은 경우, `application/octet-stream`가 사용됩니다. -WARNING: (params나 cookie 등의) 클라이언트에서 전송된 데이터를 사용해 서버에 있는 파일을 지정할 경우, 충분한 주의해주세요. 클라이언트에서 악의있는 파일 경로를 넘겨받아 개발자가 의도하지 않은 파일에 접근하여 보안 상의 위험을 초래할 수 있다는 것을 염두해주세요. +WARNING: (params나 cookie 등의) 클라이언트에서 전송된 데이터를 사용해 서버에 있는 파일을 지정할 +경우, 충분한 주의해주세요. 클라이언트에서 악의있는 파일 경로를 넘겨받아 개발자가 의도하지 않은 파일에 +접근하여 보안 상의 위험을 초래할 수 있다는 것을 염두해주세요. -TIP: 정적으로 제공되는 파일을 일부러 Rails를 통해서 전송하는 것은 권장하지 않습니다. 대부분의 경우, 웹서버의 public 폴더에 두고, 다운로드하도록 하면 됩니다. Rails를 경유해서 다운로드하는 것 보다도 Apache 등의 웹서버로부터 직접 다운로드하도록 두는 것이 훨씬 효율적이며, 나아가 Rails 전체를 경유하는 불필요한 요청을 받지 않아도 되기 때문입니다. +TIP: 정적으로 제공되는 파일을 일부러 Rails를 통해서 전송하는 것은 권장하지 않습니다. 대부분의 경우, +웹서버의 public 폴더에 두고, 다운로드하도록 하면 됩니다. Rails를 경유해서 다운로드하는 것보다도 +Apache 등의 웹서버로부터 직접 다운로드하도록 두는 것이 훨씬 효율적이며, 나아가 Rails 전체를 경유하는 +불필요한 요청을 받지 않아도 되기 때문입니다. ### RESTful한 다운로드 -`send_data`만으로도 문제없이 사용할 수 있습니다만, 제대로 된 RESTful의 애플리케이션을 만들고 싶다면, 파일 다운로드용 액션을 추가할 필요는 없습니다. REST라는 용어에는 위의 예제에서 사용된 PDF 파일와 같은 것들은 클라이언트 리소스를 다른 형태로 표현했을 뿐으로 보기 때문입니다. Rails에는 이에 기반한 "RESTful 다운로드"를 간단하게 실현하기 위해서 세련된 방법을 준비해두고 있습니다. PDF 다운로드를 스트리밍으로 다루지 않고, `show` 액션의 일부로 다루도록 하면 됩니다. +`send_data`만으로도 문제없이 사용할 수 있습니다만, 제대로 된 RESTful의 애플리케이션을 만들고 싶다면, +파일 다운로드용 액션을 추가할 필요는 없습니다. REST라는 용어에는 위의 예제에서 사용된 PDF 파일와 같은 +것들은 클라이언트 리소스를 다른 형태로 표현했을 뿐으로 보기 때문입니다. Rails에는 이에 기반한 +"RESTful 다운로드"를 간단하게 실현하기 위해서 세련된 방법을 준비해두고 있습니다. PDF 다운로드를 +스트리밍으로 다루지 않고, `show` 액션의 일부로 다루도록 하면 됩니다. ```ruby class ClientsController < ApplicationController @@ -910,13 +1123,15 @@ class ClientsController < ApplicationController end ``` -또한 이 예제가 실제로 동작하기 위해서는 Rails의 MIME type에 PDF를 추가해야 합니다. 이를 위해서는 `config/initializers/mime_types.rb`에 다음의 코드를 추가합니다. +또한 이 예제가 실제로 동작하기 위해서는 Rails의 MIME type에 PDF를 추가해야 합니다. 이를 위해서는 +`config/initializers/mime_types.rb`에 다음의 코드를 추가합니다. ```ruby Mime::Type.register "application/pdf", :pdf ``` -NOTE: Rails의 설정 파일은 처음 기동할 때에만 읽힙니다('app/' 이하의 파일들처럼 요청때마다 다시 읽히지 않습니다). 추가한 설정을 반영하기 위해서는 서버를 다시 시작할 필요가 있습니다. +NOTE: Rails의 설정 파일은 처음 기동할 때에만 읽힙니다('app/' 이하의 파일들처럼 요청때마다 다시 +읽히지 않습니다). 추가한 설정을 반영하기 위해서는 서버를 다시 시작할 필요가 있습니다. 이것으로 아래와 같이 URL에 ".pdf"를 추가하는 것으로 PDF 파일을 다운로드 받을 수 있습니다. @@ -926,11 +1141,14 @@ GET /clients/1.pdf ### 임의의 데이터를 라이브 스트리밍하기 -Rails는 파일 이외의 것을 전송할 수도 있습니다. 실제로 response 객체에 포함 가능한 것이라면 무엇이든 전송할 수 있습니다. `ActionController::Live` 모듈을 사용하면, 브라우저와 영속적인 연결을 생성할 수 있습니다. 이를 통해 언제라도 원하는 타이밍에 임의의 데이터를 브라우저에 전송할 수 있습니다. +Rails는 파일 이외의 것을 전송할 수도 있습니다. 실제로 response 객체에 포함 가능한 것이라면 무엇이든 +전송할 수 있습니다. `ActionController::Live` 모듈을 사용하면, 브라우저와 영속적인 연결을 생성할 +수 있습니다. 이를 통해 언제라도 원하는 타이밍에 임의의 데이터를 브라우저에 전송할 수 있습니다. #### 라이브 스트리밍을 사용하기 -컨트롤러 클래스에 `ActionController::Live`를 추가하면, 그 컨트롤러의 모든 액션에서 데이터를 스트리밍할 수 있게 됩니다. 이 모듈을 아래와 같이 믹스인합니다. +컨트롤러 클래스에 `ActionController::Live`를 추가하면, 그 컨트롤러의 모든 액션에서 데이터를 +스트리밍할 수 있게 됩니다. 이 모듈을 아래와 같이 믹스인합니다. ```ruby class MyController < ActionController::Base @@ -950,13 +1168,20 @@ end 이 코드에서는 브라우저와에 영속적인 연결을 확립하고, 1초마다 `"hello world\n"`를 100번 전송합니다. -단, 주의해야할 점이 몇가지 있습니다. 응답은 스트림이 확실히 닫히도록 해야합니다. 스트림을 닫지 않게 되면, 소켓이 영원히 열려있는 상태로 방치됩니다. 응답 스트림에 데이터를 전송하기 전에 Content-Type을 `text/event-stream`으로 설정할 필요가 있습니다. 그 이유는 응답을 확정해버리면(`response.committed`이 true를 돌려줄 때), 이후에 헤더 값을 변경할 수 없기 때문입니다. 이것은 응답 스트림에 대해서 `write` 또는 `commit`을 호출했을 경우에 발생합니다. +단, 주의해야할 점이 몇가지 있습니다. 응답은 스트림이 확실히 닫히도록 해야합니다. 스트림을 닫지 않게 되면, +소켓이 영원히 열려있는 상태로 방치됩니다. 응답 스트림에 데이터를 전송하기 전에 Content-Type을 +`text/event-stream`으로 설정할 필요가 있습니다. 그 이유는 응답을 확정해버리면(`response.committed`이 +true를 돌려줄 때), 이후에 헤더 값을 변경할 수 없기 때문입니다. 이것은 응답 스트림에 대해서 `write` +또는 `commit`을 호출했을 경우에 발생합니다. #### 사용 예시 -지금 당신은 노래방 기계를 개발중입니다. 사용자는 곡의 가사를 보고 싶어합니다. 각각의 `Song`에는 몇몇의 행(行)이 있으며, 각 행마다 '곡이 끝날때까지 몇 박자가 남았는가'를 가리키는 `num_beats`가 저장되어 있습니다. +지금 당신은 노래방 기계를 개발중입니다. 사용자는 곡의 가사를 보고 싶어합니다. 각각의 `Song`에는 몇몇의 +행(行)이 있으며, 각 행마다 '곡이 끝날때까지 몇 박자가 남았는가'를 가리키는 `num_beats`가 저장되어 +있습니다. -가사를 '노래방 스타일'로 사용자에게 보여주고 싶기 때문에, 직전의 가사를 다 부르고 난 뒤에 다음 가사를 보여주어야 합니다. 이 때 아래와 같이 `ActionController::Live`를 사용할 수 있습니다. +가사를 '노래방 스타일'로 사용자에게 보여주고 싶기 때문에, 직전의 가사를 다 부르고 난 뒤에 다음 가사를 +보여주어야 합니다. 이 때 아래와 같이 `ActionController::Live`를 사용할 수 있습니다. ```ruby class LyricsController < ActionController::Base @@ -980,7 +1205,8 @@ end #### 스트리밍을 하는 경우 고려해야할 부분 -임의의 데이터를 스트리밍할 수 전송할 수 있다는 것은 무척 강력한 도구입니다. 지금까지 예제에서 소개했듯이, 필요할 때에 필요한 응답을 스트림을 통해 전송할 수 있습니다. 단, 아래의 항목들을 조심해주세요. +임의의 데이터를 스트리밍할 수 전송할 수 있다는 것은 무척 강력한 도구입니다. 지금까지 예제에서 소개했듯이, +필요할 때에 필요한 응답을 스트림을 통해 전송할 수 있습니다. 단, 아래의 항목들을 조심해주세요. * 응답 스트림을 하나 만들 때마다 새로운 스레드가 생성되고, 원래의 스레드로부터 스레드 지역 변수가 복사됩니다. 스레드 지역 변수가 증가하게 되면 성능에 영향을 미칠 수 있습니다. 또한 스레드 자체가 너무 많아도 마찬가지로 성능 저하의 요인이 될 수 있습니다. * 응답 스트림을 닫지 못하면, 대응하는 소켓을 영원히 열어둔 채로 방치되게 됩니다. 응다 스트림을 사용하는 경우에는 반드시 `close`를 호출해주세요. @@ -989,19 +1215,29 @@ end 로그 필터링 ------------- -Rails의 로그파일은 `log` 폴더 밑에 환경마다 하나씩 생성됩니다. 디버그 시에 애플리케이션에서 무슨 일이 발생하기 있는지 확인할 때에 무척 편리합니다만, 실제 애플리케이션에서 고객의 비밀번호와 같은 중요한 정보를 로그 파일에 출력하고 싶지 않을 때도 있습니다. +Rails의 로그파일은 `log` 폴더 밑에 환경마다 하나씩 생성됩니다. 디버그 시에 애플리케이션에서 무슨 일이 +발생하기 있는지 확인할 때에 무척 편리합니다만, 실제 애플리케이션에서 고객의 비밀번호와 같은 중요한 정보를 +로그 파일에 출력하고 싶지 않을 때도 있습니다. -### 파라미터를 필터링하기 +### 매개 변수를 필터링하기 -Rails 애플리케이션의 설정 파일 config.filter_parameters을 통해 특정 요청 파라미터의 값을 로그에 저장하지 않도록 설정할 수 있습니다. 필터링된 파라미터는 로그에서 [FILTERED]라는 글자로 변환됩니다. +Rails 애플리케이션의 설정 파일 config.filter_parameters을 통해 특정 요청 매개 변수의 값을 +로그에 저장하지 않도록 설정할 수 있습니다. 필터링된 매개 변수는 로그에서 [FILTERED]라는 문자열로 +변환됩니다. ```ruby config.filter_parameters << :password ``` +NOTE: 제공된 매개 변수들은 부분 매칭 정규 식으로 처리됩니다. Rails는 기본으로 `:password`를 적당한 + initializer(`initializers/filter_parameter_logging.rb`)에서 제공하여 일반적인 + 애플리케이션에서 많이 사용하는 `password`와 `password_confirmation`를 처리해줍니다. + ### 리다이렉션을 필터링하기 -애플리케이선에서 발생하는 리다이렉션 URL중 몇가지는 상황에 따라 로그를 출력하지 않는 것이 좋을 경우도 있습니다. 이럴 때는 설정의 `config.filter_redirect` 옵션을 사용해서 리다이렉션 정보를 로그에 출력하지 않도록 만들 수 있습니다. +애플리케이선에서 발생하는 리다이렉션 URL중 몇가지는 상황에 따라 로그를 출력하지 않는 것이 좋을 경우도 +있습니다. 이럴 때는 설정의 `config.filter_redirect` 옵션을 사용해서 리다이렉션 정보를 로그에 +출력하지 않도록 만들 수 있습니다. ```ruby config.filter_redirect << 's3.amazonaws.com' @@ -1018,21 +1254,36 @@ config.filter_redirect.concat ['s3.amazonaws.com', /private_path/] Rescue ------ -어떤 애플리케이션에도 어딘가에 버그가 존재하며, 이를 적절한 처리를 통해 에러를 던질 필요가 있습니다. 예를 들어 사용자가 데이터베이스에 이미 존재하지 않은 리소스에 접근하는 경우, Active Record는 `ActiveRecord::RecordNotFound` 예외를 던집니다. +어떤 애플리케이션에도 어딘가에 버그가 존재하며, 이를 적절한 처리를 통해 에러를 던질 필요저장 있습니다. +예를 들어 사용자가 데이터베이스에 이미 존재하지 않은 리소스에 접근하는 경우, 액티브 레코드는 +`ActiveRecord::RecordNotFound` 예외를 던집니다. -Rails의 기본 예외 처리에서는 예외의 종류에 관계없이 "500 Server Error"를 표시합니다. 요청이 로컬 환경의 브라우저에서 이루어진 경우에는 상세한 추적 정보가 표시되므로 문제를 파악하고, 대응할 수 있게 됩니다. 요청이 원격 브라우저에서 왔을 경우 Rails는 "500 Server Error"라는 메시지만을 사용자에게 필요하고, 라우팅이나 코드가 없는 경우 "404 Not Found"를 표시하거나 합니다. 이대로라면 너무 매정한 느낌이 들기 때문에 에러를 잡고, 사용자에게 보여주는 방법을 커스터마이즈 하고 싶습니다. Rails 애플리케이션에서는 예외 처리를 다양한 레벨에서 할 수 있습니다. +Rails의 기본 예외 처리에서는 예외의 종류에 관계없이 "500 Server Error"를 표시합니다. 요청이 +로컬 환경의 브라우저에서 이루어진 경우에는 상세한 추적 정보가 표시되므로 문제를 파악하고, 대응할 수 있게 +됩니다. 요청이 원격 브라우저에서 왔을 경우 Rails는 "500 Server Error"라는 메시지만을 사용자에게 +필요하고, 라우팅이나 코드가 없는 경우 "404 Not Found"를 표시하거나 합니다. 이대로라면 너무 매정한 +느낌이 들기 때문에 에러를 잡고, 사용자에게 보여주는 방법을 커스터마이즈 하고 싶습니다. Rails +애플리케이션에서는 예외 처리를 다양한 레벨에서 할 수 있습니다. ### 기본 500, 404 템플릿 -배포 환경의 Rails 애플리케이션은 기본으로 404 또는 500 에러 메시지를 출력합니다. 이 메시지는 `public` 폴더에 존재하는 HTML 파일입니다. 각각 `404.html`와 `500.html`라는 이름입니다. 이러한 파일을 커스터마이즈해서 정보를 추가하거나, 레이아웃을 변경할 수 있습니다. 단, 이것은 어디까지나 정적인 HTML파일이므로 RHTML이나 ERB는 사용할 수 없습니다. +배포 환경의 Rails 애플리케이션은 기본으로 404 또는 500 에러 메시지를 출력합니다. 이 메시지는 +`public` 폴더에 존재하는 HTML 파일입니다. 각각 `404.html`와 `500.html`라는 이름입니다. +이러한 파일을 커스터마이즈해서 정보를 추가하거나, 레이아웃을 변경할 수 있습니다. 단, 이것은 어디까지나 +정적인 HTML파일이므로 RHTML이나 ERB는 사용할 수 없습니다. ### `rescue_from` -에러를 처리하는 동작을 좀 더 세련되게 만들고 싶은 경우에는 `rescue_from`를 사용할 수 있습니다. 이것은 특정 종류의, 또는 여러 종류의 예외를 하나의 컨트롤러 전체 또는 그 자식 클래스에서 다룰 수 있도록 해줍니다. +에러를 처리하는 동작을 좀 더 세련되게 만들고 싶은 경우에는 `rescue_from`를 사용할 수 있습니다. +이것은 특정 종류의, 또는 여러 종류의 예외를 하나의 컨트롤러 전체 또는 그 자식 클래스에서 다룰 수 있도록 +해줍니다. -`rescue_from`로 잡을 수 있는 예외가 발생하면, 핸들러에 예외 객체를 넘길 수 있습니다. 이 핸들러는 메소드나, `:with` 옵션을 사용해 Proc 객체를 직접 넘길 수 있습니다. Proc 객체 대신에 블럭을 직접 넘길 수도 있습니다. +`rescue_from`로 잡을 수 있는 예외가 발생하면, 핸들러에 예외 객체를 넘길 수 있습니다. 이 핸들러는 +메소드나, `:with` 옵션을 사용해 Proc 객체를 직접 넘길 수 있습니다. Proc 객체 대신에 블럭을 직접 +넘길 수도 있습니다. -`rescue_from`를 사용하여 모든 `ActiveRecord::RecordNotFound` 에러를 잡아 처리를 하는 예제를 아래에 소개합니다. +`rescue_from`를 사용하여 모든 `ActiveRecord::RecordNotFound` 에러를 잡아 처리를 하는 예제를 +아래에 소개합니다. ```ruby class ApplicationController < ActionController::Base @@ -1046,7 +1297,10 @@ class ApplicationController < ActionController::Base end ``` -이전보다 조금더 구조적이 되었습니다만, 이대로라면 기본 에러 처리보다 나아진 점이 없습니다. 하지만 이런식으로 모든 예외를 잡아서 처리하게 된다면 원하는 대로 커스터마이즈할 수 있게 됩니다. 예를 들어 커스텀 예외 클래스를 선언하고, 사용자가 접속 권한을 가지고 있지 않은 컨트롤러에 접근하려고 했을 경우에 예외를 던질 수도 있습니다. +이전보다 조금 더 구조적이 되었습니다만, 이대로라면 기본 에러 처리보다 나아진 점이 없습니다. 하지만 +이런식으로 모든 예외를 잡아서 처리하게 된다면 원하는 대로 커스터마이즈할 수 있게 됩니다. 예를 들어 +커스텀 예외 클래스를 선언하고, 사용자가 접속 권한을 가지고 있지 않은 컨트롤러에 접근하려고 했을 경우에 +예외를 던질 수도 있습니다. ```ruby class ApplicationController < ActionController::Base @@ -1078,12 +1332,21 @@ class ClientsController < ApplicationController end ``` -NOTE: `ApplicationController` 클레스서만 처리 가능한 예외가 몇가지 있습니다. 이는 컨트롤러가 초기화되어 액션이 실행되기 전에 발생하는 예외가 있기 때문입니다. 자세한 설명은 Pratik Naik의 [글](http://m.onkey.org/2008/7/20/rescue-from-dispatching)을 참고해주세요. +WARNING: 특별한 이유 없이 `rescue_from Exception`나 `rescue_from StandardError`를 + 사용해서는 안됩니다. 이는 커다란 영향을 미치기 때문입니다(e.g. 개발 환경에서 자세한 에러 + 정보를 볼 수 없게 됩니다). + +NOTE: Production환경에서 모든 `ActiveRecord::RecordNotFound` 에러는 404 에러 페이지를 + 랜더링합니다. 별도의 동작을 실행하고 싶은 것이 아니라면 굳이 처리하지 않아도 됩니다. + +NOTE: `ApplicationController` 클래스에서만 처리 가능한 예외가 몇 가지 있습니다. 이는 컨트롤러가 +초기화되어 액션이 실행되기 전에 발생하는 예외가 있기 때문입니다. HTTPS 프로토콜을 강제하기 -------------------- -보안상의 이유로, 특정 컨트롤러에 대해서 HTTPS 접속만 사용하도록 강제하고 싶을 때가 있습니다. 컨트롤러에서 `force_ssl` 메소드를 사용하는 것으로 SSL을 강제할 수 있습니다. +보안상의 이유로, 특정 컨트롤러에 대해서 HTTPS 접속만 사용하도록 강제하고 싶을 때가 있습니다. +컨트롤러에서 `force_ssl` 메소드를 사용하는 것으로 SSL을 강제할 수 있습니다. ```ruby class DinnerController @@ -1091,7 +1354,8 @@ class DinnerController end ``` -필터와 마찬가지로 `:only` 옵션이나 `:except` 옵션을 사용해서 컨트롤러 내의 특정 액션에만 보안 접속을 강제할 수 있습니다. +필터와 마찬가지로 `:only` 옵션이나 `:except` 옵션을 사용해서 컨트롤러 내의 특정 액션에만 보안 접속을 +강제할 수 있습니다. ```ruby class DinnerController @@ -1101,6 +1365,5 @@ class DinnerController end ``` -`force_ssl`을 여러 컨트롤러에서 사용하고 싶다면, 애플리케이션 전체에서 HTTPS 접속을 요구하는 편이 좋습니다. 이를 위해서는 환경 파일에서 `config.force_ssl`을 설정하세요. - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. +`force_ssl`을 여러 컨트롤러에서 사용하고 싶다면, 애플리케이션 전체에서 HTTPS 접속을 요구하는 편이 +좋습니다. 이를 위해서는 환경 파일에서 `config.force_ssl`을 설정하세요. diff --git a/guides/source/ko/action_mailer_basics.md b/guides/source/ko/action_mailer_basics.md index 7244db752754c..d3ce4e0cdcbeb 100644 --- a/guides/source/ko/action_mailer_basics.md +++ b/guides/source/ko/action_mailer_basics.md @@ -2,7 +2,9 @@ Action Mailer 기초 ==================== -여기에서는 애플리케이션에서 메일을 주고 받기 위해서 필요한 모든 것들과 Action Mailer에 대해 다양한 설명을 제공합니다. 또한 메일러를 테스트하기 위한 방법에 대해서도 설명합니다. +여기에서는 애플리케이션에서 메일을 주고 받기 위해서 필요한 모든 것들과 Action +Mailer에 대해 다양한 설명을 제공합니다. 또한 메일러를 테스트하기 위한 방법에 +대해서도 설명합니다. 이 가이드의 내용: @@ -16,7 +18,10 @@ Action Mailer 기초 시작하면서 ------------ -Action Mailer를 사용하여 애플리케이션의 메일러 클래스나 뷰에서 메일을 전송할 수 있습니다. 메일러의 동작은 컨트롤러와 유사한 부분이 많습니다. 메일러는 `ActionMailer::Base`를 상속하고 `app/mailers`에 위치하며 `app/views`에 있는 뷰와 연결됩니다. +Action Mailer를 사용하여 애플리케이션의 메일러 클래스나 뷰에서 메일을 전송할 +수 있습니다. 메일러의 동작은 컨트롤러와 유사한 부분이 많습니다. 메일러는 +`ActionMailer::Base`를 상속하고 `app/mailers`에 위치하며 `app/views`에 있는 +뷰와 연결됩니다. 메일을 보내기 -------------- @@ -145,9 +150,13 @@ $ bin/rails generate scaffold user name email login $ bin/rails db:migrate ``` -사용자 모델을 생성했으므로 이어서 `app/controllers/users_controller.rb`를 편집하고, 새 사용자가 생성된 직후에 `UserMailer`의 `UserMailer.welcome_email`을 사용하여 그 사용자에게 메일이 전송되도록 합시다. +사용자 모델을 생성했으므로 이어서 `app/controllers/users_controller.rb`를 +편집하고, 새 사용자가 생성된 직후에 `UserMailer`의 `UserMailer.welcome_email`을 +사용하여 그 사용자에게 메일이 전송되도록 합시다. -Action Mailer는 Active Job과 잘 결합되어 있으므로, Web의 요청/응답 흐름의 바깥에서 비동기로 메일을 전송합니다. 이 덕분에 사용자는 메일 전송이 끝나기를 기다릴 필요가 없습니다. +Action Mailer는 Active Job과 잘 결합되어 있으므로, Web의 요청/응답 흐름의 +바깥에서 비동기로 메일을 전송합니다. 이 덕분에 사용자는 메일 전송이 끝나기를 +기다릴 필요가 없습니다. ```ruby class UsersController < ApplicationController @@ -172,9 +181,17 @@ class UsersController < ApplicationController end ``` -NOTE: Active Job은 기본으로 작업을 ':inline'으로 실행합니다. 따라서 이 시점에서 `deliver_later`를 사용해서 메일을 전송할 수 있습니다. 또한, 메일을 나중에 백그라운드로 전송하고 싶은 경우에는 Sidekiq이나 Resque등의 백엔드 큐 시스템을 사용하도록 Active Job을 설정하면 됩니다. +NOTE: Active Job은 기본으로 작업을 `:async` 어댑터로 실행합니다. 따라서 이 +시점에서 `deliver_later`를 사용해서 메일을 전송할 수 있습니다. Active Job의 +기본 어댑터는 프로세스 내의 스레드 풀을 사용하여 동작합니다. 이는 별도의 +인프라를 요구하지 않으므로 개발/테스트 환경에는 잘 맞습니다만, 재시작할 때마다 +쌓여있는 작업을 버리기 때문에 Production 환경에서는 그다지 적절하지 않습니다. +영속적인 백엔드가 필요하다면 Sidekiq이나 Resque 등의 백엔드 큐 시스템을 +사용하도록 Active Job을 설정하면 됩니다. -메일을 cronjob 등에서 지금 바로 보내고 싶은 경우에는 `deliver_now`를 호출하면 됩니다. +메일을 cronjob 등에서 지금 바로 보내고 싶은 경우에는 `deliver_now`를 +호출하면 +됩니다. ```ruby class SendWeeklySummary @@ -186,13 +203,19 @@ class SendWeeklySummary end ``` -이 `welcome_email` 메소드는 `ActionMailer::MessageDelivery` 객체를 하나 반환합니다. 이 객체는 그 메일 자신이 전송 대상임을 `deliver_now`나 `deliver_later`에 알립니다. `ActionMailer::MessageDelivery` 객체는 `Mail::Message`를 감싸고 있습니다. 내부의 `Mail::Message` 객체를 꺼내거나 변경하고 싶은 경우에는 `ActionMailer::MessageDelivery` 객체의 `message` 메소드를 통해서 접근할 수 있습니다. +이 `welcome_email` 메소드는 `ActionMailer::MessageDelivery` 객체를 하나 +반환합니다. 이 객체는 그 메일 자신이 전송 대상임을 `deliver_now`나 +`deliver_later`에 알립니다. `ActionMailer::MessageDelivery` 객체는 +`Mail::Message`를 감싸고 있습니다. 내부의 `Mail::Message` 객체를 꺼내거나 +변경하고 싶은 경우에는 `ActionMailer::MessageDelivery` 객체의 `message` +메소드를 통해서 접근할 수 있습니다. ### 헤더의 값을 자동으로 인코딩하기 Action Mailer는 메일의 헤더나 본문의 멀티바이트 문자를 자동적으로 인코딩합니다. -다른 문자셋을 정의하고 싶을 때나, 사전에 직접 다른 인코딩 변환을 해두고 싶을 때에는 [Mail](https://github.com/mikel/mail) 라이브러리를 참조해주세요. +다른 문자셋을 정의하고 싶을 때나, 사전에 직접 다른 인코딩 변환을 해두고 싶을 +때에는 [Mail](https://github.com/mikel/mail) 라이브러리를 참조해주세요. ### Action Mailer의 모든 메소드 @@ -212,28 +235,37 @@ Action Mailer에서는 파일을 간단하게 첨부할 수 있습니다. attachments['filename.jpg'] = File.read('/path/to/filename.jpg') ``` - `mail` 메소드를 호출하면, Multipart 형식의 메일이 전송됩니다. 전송되는 메일은 Top level이 `multipart/mixed`이고, 첫번째 부분이 `multipart/alternative`라는 올바른 형식으로 중첩되어 있는 일반 텍스트 메일 또는 HTML 메일입니다. + `mail` 메소드를 호출하면, Multipart 형식의 메일이 전송됩니다. 전송되는 메일은 +Top level이 `multipart/mixed`이고, 첫번째 부분이 `multipart/alternative`라는 +올바른 형식으로 중첩되어 있는 일반 텍스트 메일 또는 HTML 메일입니다. -NOTE: 메일에 첨부된 파일은 자동적으로 Base64로 인코딩됩니다. 다른 인코딩을 사용하고 싶은 경우에는 사전에 원하는 인코딩을 적용한 내용물을 `Hash`로 감싸서 `attachments`로 넘겨주세요. +NOTE: 메일에 첨부된 파일은 자동적으로 Base64로 인코딩됩니다. 다른 인코딩을 +사용하고 싶은 경우에는 사전에 원하는 인코딩을 적용한 내용물을 `Hash`로 감싸서 +`attachments`로 넘겨주세요. -* Action Mailer와 Mail는 헤더와 컨텐츠를 지정하여 파일명을 넘겨주면 그것들을 사용합니다. +* Action Mailer와 Mail는 헤더와 컨텐츠를 지정하여 파일명을 넘겨주면 그것들을 +사용합니다. ```ruby encoded_content = SpecialEncode(File.read('/path/to/filename.jpg')) attachments['filename.jpg'] = { - mime_type: 'application/x-gzip', + mime_type: 'application/gzip', encoding: 'SpecialEncoding', content: encoded_content } ``` -NOTE: 인코딩의 종류를 지정하면 Mail은 내용물이 이미 인코딩 되었다고 생각하고 Base64로 인코딩을 시도하지 않습니다. +NOTE: 인코딩의 종류를 지정하면 Mail은 내용물이 이미 인코딩 되었다고 생각하고 +Base64로 인코딩을 시도하지 않습니다. #### 파일을 인라인으로 첨부하기 -Action Mailer 3.0부터 파일을 인라인으로 첨부할 수 있습니다. 이 기능은 3.0보다 이전에 이루어졌던 여러 트릭들을 바탕으로 최대한 이상적이고, 단순하게 구현한 것입니다. +Action Mailer 3.0부터 파일을 인라인으로 첨부할 수 있습니다. 이 기능은 3.0보다 +이전에 이루어졌던 여러 트릭들을 바탕으로 최대한 이상적이고, 단순하게 구현한 +것입니다. -* Mail에 인라인 첨부를 사용하도록 지시하려면 Mailer의 attachments 메소드에 `#inline`을 호출하기만 하면 됩니다. +* Mail에 인라인 첨부를 사용하도록 지시하려면 Mailer의 attachments 메소드에 +`#inline`을 호출하기만 하면 됩니다. ```ruby def welcome @@ -330,6 +362,23 @@ end 이 코드는 HTML 형식을 'another_template.html.erb' 템플릿을 사용하여 랜더링하며, 텍스트일 경우에는 `:text`로 랜더링합니다. 랜더링 명령은 Action Controller에서 사용하고 있는 것과 동일하므로 `:text`, `:inline` 등의 옵션을 동일하게 사용할 수 있습니다. +#### 메일러 뷰 캐싱하기 + +애플리케이션 뷰처럼 메일러 뷰에서도 `cache` 메소드를 사용하며 캐싱을 할 수 +있습니다. + +``` +<% cache do %> + <%= @company.name %> +<% end %> +``` + +이 기능을 사용하기 위해서는 애플리케이션에 다음의 설정을 추가해주세요. + +``` + config.action_mailer.perform_caching = true +``` + ### Action Mailer의 레이아웃 메일러도 컨트롤러의 뷰와 동일한 방법을 통해 레이아웃을 사용할 수 있습니다. 메일러에서 사용하는 레이아웃 이름은 메일러와 동일한 이름일 필요가 있습니다. 예를 들자면, `user_mailer.html.erb`나 `user_mailer.text.erb`라는 레이아웃은 메일러의 레이아웃으로 인식됩니다. @@ -553,7 +602,9 @@ Action Mailer를 설정하기 |`deliveries`|`delivery_method :test`를 사용해서 Action Mailer로부터 전송된 메일 배열을 저장합니다. 유닛 테스트나 기능테스트에서 유용합니다.| |`default_options`|`mail` 메소드 옵션(`:from`, `:reply_to` 등)의 기본값을 설정합니다.| -가능한 모든 설정 옵션을 보기 위해서는 'Rails 애플리케이션을 설정하기'의 [Action Mailer를 설정하기](configuring.html#action-mailer를-설정하기)를 참조해주세요. +가능한 모든 설정 옵션을 보기 위해서는 'Rails 애플리케이션을 설정하기'의 +[Action Mailer를 설정하기](configuring.html#action-mailer를-설정하기)를 +참조해주세요. ### Action Mailer의 설정 예시 @@ -564,7 +615,7 @@ config.action_mailer.delivery_method = :sendmail # Defaults to: # config.action_mailer.sendmail_settings = { # location: '/usr/sbin/sendmail', -# arguments: '-i -t' +# arguments: '-i' # } config.action_mailer.perform_deliveries = true config.action_mailer.raise_delivery_errors = true @@ -573,7 +624,9 @@ config.action_mailer.default_options = {from: 'no-reply@example.com'} ### Gmail을 위한 Action Mailer 설정 -Action Mailer에 [Mail gem](https://github.com/mikel/mail)이 도입되었으므로 `config/environments/$RAILS_ENV.rb` 파일에 아래와 같이 무척 간단해진 설정을 추가해주세요. +Action Mailer에 [Mail gem](https://github.com/mikel/mail)이 도입되었으므로 +`config/environments/$RAILS_ENV.rb` 파일에 아래와 같이 무척 간단해진 설정을 +추가해주세요. ```ruby config.action_mailer.delivery_method = :smtp @@ -590,12 +643,16 @@ config.action_mailer.smtp_settings = { 메일러 테스트 -------------- -메일러의 테스트 방법은 테스트 가이드의 [메일러 테스트하기](testing.html#메일러-테스트하기)를 참조해주세요. +메일러의 테스트 방법은 테스트 가이드의 +[메일러 테스트하기](testing.html#메일러-테스트하기)를 참조해주세요. 메일을 전송 직전에 변경하기 ------------------- -메일을 전송하기 전에 약간의 수정을 하고 싶은 경우가 있습니다. 다행히 Action Mailer는 모든 메일을 전송하기 전에 추가작업을 하기 위한 방법을 제공합니다. 이것을 사용하여 메일이 최종적으로 전송 에이전트에게 넘기기 직전에 메일의 내용을 수정하기 위한 코드를 등록할 수 있습니다. +메일을 전송하기 전에 약간의 수정을 하고 싶은 경우가 있습니다. 다행히 Action +Mailer는 모든 메일을 전송하기 전에 추가작업을 하기 위한 방법을 제공합니다. +이것을 사용하여 메일이 최종적으로 전송 에이전트에게 넘기기 직전에 메일의 내용을 +수정하기 위한 코드를 등록할 수 있습니다. ```ruby class SandboxEmailInterceptor @@ -605,12 +662,15 @@ class SandboxEmailInterceptor end ``` -인터셉터가 동작하기 위해서는 Action Mailer 프레임워크에 등록할 필요가 있습니다. 이 코드는 다음과 같이 initializer의 `config/initializers/sandbox_email_interceptor.rb` 파일에서 처리할 수 있습니다. +인터셉터가 동작하기 위해서는 Action Mailer 프레임워크에 등록할 필요가 있습니다. +이 코드는 다음과 같이 initializer의 +`config/initializers/sandbox_email_interceptor.rb` 파일에서 처리할 수 있습니다. ```ruby ActionMailer::Base.register_interceptor(SandboxEmailInterceptor) if Rails.env.staging? ``` -NOTE: 이 예제에서는 "staging"라는 환경을 사용하고 있습니다. 이것은 실제 환경(production환경)과 같은 상태에서 테스트를 하기 위한 환경입니다. Rails의 커스텀 환경에 대해서는 [Rails 환경을 생성하기](configuring.html#rails-환경을-생성하기)를 참조해주세요. - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. +NOTE: 이 예제에서는 "staging"라는 환경을 사용하고 있습니다. 이것은 실제 +환경(production환경)과 같은 상태에서 테스트를 하기 위한 환경입니다. Rails의 +커스텀 환경에 대해서는 +[Rails 환경을 생성하기](configuring.html#rails-환경을-생성하기)를 참조해주세요. diff --git a/guides/source/ko/active_job_basics.md b/guides/source/ko/active_job_basics.md index 836f3bc5cda9d..af9ed77e69654 100644 --- a/guides/source/ko/active_job_basics.md +++ b/guides/source/ko/active_job_basics.md @@ -17,13 +17,24 @@ Active Job 기초 시작하기 전에 ------------ -Active Job은 잡을 선언하고, 이를 이용하여 큐를 사용하는 백엔드에서 다양한 방법으로 이를 처리하는 프레임워크입니다. 여기서 잡이란 정기적으로 정기적으로 실행되는 작업이나, 인보이스 발행이나 메일 전송 등, 어떤 것이라도 가능합니다. 이 작업들을 좀 더 작은 단위로 분할해서 병렬 실행할 수도 있습니다. +Active Job은 잡을 선언하고, 이를 이용하여 큐를 사용하는 백엔드에서 다양한 +방법으로 이를 처리하는 프레임워크입니다. 여기서 잡이란 정기적으로 정기적으로 +실행되는 작업이나, 인보이스 발행이나 메일 전송 등, 어떤 것이라도 가능합니다. +이 작업들을 좀 더 작은 단위로 분할해서 병렬 실행할 수도 있습니다. Active Job의 목적 ----------------------------- -Active Job의 주 목적은 Rails 애플리케이션이 곧바로 실행하는 작업일지라도, 자신만의 잡 관리 인프라를 가질 수 있도록 하는 것입니다. 이를 통해서 Delayed Job과 Resque와 같은, 다양한 잡마다의 실행 방식의 차이를 신경쓰지 않고 잡 프레임워크의 기능이나 그 이외의 gem을 탑재할 수 있게 됩니다. 백엔드에서 큐를 관리할 때에는 조작 이외에는 신경을 쓸 필요가 없게 됩니다. 또한 잡 관리 프레임워크를 변경하더라도 잡을 새로 작성할 필요가 없다는 장점도 있습니다. +Active Job의 주 목적은 Rails 애플리케이션이 곧바로 실행하는 작업일지라도, +자신만의 잡 관리 인프라를 가질 수 있도록 하는 것입니다. 이를 통해서 +Delayed Job과 Resque와 같은, 다양한 잡마다의 실행 방식의 차이를 신경쓰지 않고 +잡 프레임워크의 기능이나 그 이외의 gem을 탑재할 수 있게 됩니다. 백엔드에서 +큐를 관리할 때에는 조작 이외에는 신경을 쓸 필요가 없게 됩니다. 또한 잡 관리 +프레임워크를 변경하더라도 잡을 새로 작성할 필요가 없다는 장점도 있습니다. +NOTE: Rails는 기본으로 프로세스 내의 스레드 풀을 통해 비동기 큐를 동작시킵니다. +이를 통해서 잡이 비동기적으로 동작합니다만, 큐에 들어있는 잡들은 재시작과 함께 +초기화됩니다. 잡 만들기 -------------- @@ -32,7 +43,8 @@ Active Job의 주 목적은 Rails 애플리케이션이 곧바로 실행하는 ### 잡 생성하기 -Active Job은 잡 생성을 위한 Rails 제너레이터를 제공합니다. 이를 실행하면 `app/jobs`에 잡이 하나 생성됩니다. +Active Job은 잡 생성을 위한 Rails 제너레이터를 제공합니다. 이를 실행하면 +`app/jobs`에 잡이 하나 생성됩니다. ```bash $ bin/rails generate job guests_cleanup @@ -48,14 +60,16 @@ $ bin/rails generate job guests_cleanup --queue urgent create app/jobs/guests_cleanup_job.rb ``` -이처럼, Rails에서 다른 제너레이터를 사용할 때와 완전히 동일한 방법으로 잡을 생성할 수 있습니다. +이처럼, Rails에서 다른 제너레이터를 사용할 때와 완전히 동일한 방법으로 잡을 +생성할 수 있습니다. -제너레이터를 사용하고 싶지 않다면, `app/jobs`의 아래에 자신의 잡 파일을 직접 생성할 수도 있습니다. 이런 경우에는 반드시 `ActiveJob::Base`를 상속해주세요. +제너레이터를 사용하고 싶지 않다면, `app/jobs`의 아래에 자신의 잡 파일을 직접 +생성할 수도 있습니다. 이런 경우에는 반드시 `ApplicationJob`를 상속해주세요. 생성된 잡은 아래와 같습니다. ```ruby -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob queue_as :default def perform(*args) @@ -90,32 +104,66 @@ MyJob.set(wait: 1.week).perform_later(record) # 일주일 뒤에 실행하고 ### 백엔드 -Active Job에는 Sidekiq, Resque, Delayed Job 등의 다양한 큐용 백엔드에 접속 가능한 어댑터가 내장되어 있습니다. 사용가능한 최신 어댑터 리스트에 대해서는 API 문서의 [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html)를 참조해주세요. +Active Job에는 Sidekiq, Resque, Delayed Job 등의 다양한 큐용 백엔드에 접속 +가능한 어댑터가 내장되어 있습니다. 사용가능한 최신 어댑터 리스트에 대해서는 +API 문서의 [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html)를 참조해주세요. ### 백엔드 설정하기 사용하는 큐는 자유롭게 설정하고 변경할 수 있습니다. ```ruby -# 반드시 어댑터 잼을 Gemfile에 추가하고 -# 어댑터마다 반드시 인스톨과 배포하는 것을 잊지 말아주세요. -Rails.application.config.active_job.queue_adapter = :sidekiq +# config/application.rb +module YourApp + class Application < Rails::Application + # 반드시 어댑터 잼을 Gemfile에 추가하고 + # 어댑터마다 반드시 인스톨과 배포하는 것을 잊지 말아주세요. + config.active_job.queue_adapter = :sidekiq + end +end +``` + +또는 잡 별로 백엔드를 다르게 설정할 수도 있습니다. + +```ruby +class GuestsCleanupJob < ApplicationJob + self.queue_adapter = :resque + # ... +end + +# `config.active_job.queue_adapter`에 어떤 백엔드를 설정했는지에 관계없이 +# 이 잡은 `resque`를 백엔드로 사용할 것입니다. ``` +### 백엔드 시작하기 + +잡은 Rails 애플리케이션에서 대해서 병렬로 실행되기 때문에 많은 큐 라이브러리는 +잡을 처리하기 위한 라이브러리의 큐 서비스를 별도로 실행하기를 요구합니다. +큐 백엔드를 시작하는데에 필요한 설명은 각 라이브러리의 문서를 +참조하세요. + +각 라이브러리들의 문서 목록입니다. + +- [Sidekiq](https://github.com/mperham/sidekiq/wiki/Active-Job) +- [Resque](https://github.com/resque/resque/wiki/ActiveJob) +- [Sucker Punch](https://github.com/brandonhilkert/sucker_punch#active-job) +- [Queue Classic](https://github.com/QueueClassic/queue_classic#active-job) 큐 ------ -대부분의 어댑터에서는 다수의 큐를 사용할 수 있습니다. Active Job을 사용하는 것으로 특정 큐에 잡의 일정을 추가할 수 있습니다. +대부분의 어댑터에서는 다수의 큐를 사용할 수 있습니다. Active Job을 사용하면 +특정 큐에 잡을 추가할 수 있습니다. ```ruby -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob queue_as :low_priority # .... end ``` -`application.rb`에서 아래와 같이 `config.active_job.queue_name_prefix`를 사용해서 큐 이름에 특정 접두어를 추가할 수 있습니다. +`application.rb`에서 아래와 같이 `config.active_job.queue_name_prefix`를 +사용해서 큐 이름에 특정 접두어를 추가할 수 있습니다. ```ruby # config/application.rb @@ -126,25 +174,52 @@ module YourApp end # app/jobs/guests_cleanup.rb -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob queue_as :low_priority #.... end -# 이상으로 production 환경에서 production_low_priority 라는 큐에서 잡이 실행되게 되며, beta 환경에서는 beta_low_priority라는 큐에서 잡이 실행되게 됩니다. -# +# 이상으로 production 환경에서 production_low_priority 라는 큐에서 잡이 +# 실행되게 되며, staging 환경에서는 staging_low_priority라는 큐에서 잡이 +# 실행되게 됩니다. ``` -잡을 추가하는 시점에서 큐를 제어하고 싶은 경우에는 #set에 `:queue` 옵션을 추가하면 됩니다. +기본 큐 이름 구분 기호는 '\_'입니다.이는 `application.rb`의 +`config.active_job.queue_name_delimiter`를 통해서 변경할 수 있습니다. + +```ruby +# config/application.rb +module YourApp + class Application < Rails::Application + config.active_job.queue_name_prefix = Rails.env + config.active_job.queue_name_delimiter = '.' + end +end + +# app/jobs/guests_cleanup_job.rb +class GuestsCleanupJob < ApplicationJob + queue_as :low_priority + #.... +end + +# 이제 잡은 production 환경이라면 production.low_priority 큐에, +# staging 환경이라면 staging.low_priority 큐에 추가됩니다. +``` + +잡을 추가하는 시점에서 큐를 제어하고 싶은 경우에는 #set에 `:queue` 옵션을 +추가하면 됩니다. ```ruby MyJob.set(queue: :another_queue).perform_later(record) ``` -잡을 동작하는 시점에서 큐를 제어하기 위해서 #queue_as에 블록을 넘겨줄 수도 있습니다. 넘겨진 블록은 그 잡의 컨텍스트 내에서 실행됩니다(따라서 self.arguments에도 접근할 수 있습니다). 그리고 이 블록에서는 큐의 이름을 반환해야 합니다. +잡을 동작하는 시점에서 큐를 제어하기 위해서 `#queue_as`에 블록을 넘겨줄 수도 +있습니다. 넘겨진 블록은 그 잡의 컨텍스트 내에서 실행됩니다(따라서 +self.arguments에도 접근할 수 있습니다). 그리고 이 블록에서는 큐의 이름을 +반환해야 합니다. ```ruby -class ProcessVideoJob < ActiveJob::Base +class ProcessVideoJob < ApplicationJob queue_as do video = self.arguments.first if video.owner.premium? @@ -162,13 +237,16 @@ end ProcessVideoJob.perform_later(Video.last) ``` -NOTE: 설정한 큐의 이름을 큐를 관리하는 백엔드에서 '이해할 수' 있도록 해주세요. 일부 백엔드에서는 넘겨받는 큐의 이름을 지정해야 할 필요가 있습니다. +NOTE: 설정한 큐의 이름을 큐를 관리하는 백엔드에서 '이해할 수' 있도록 해주세요. +일부 백엔드에서는 넘겨받는 큐의 이름을 지정해야 할 필요가 있습니다. 콜백 --------- -Active Job은 잡의 생애 주기에 따른 훅을 제공합니다. 이를 이용해서 콜백을 사용할 수 있으므로 잡의 생애 주기의 특정 시점에 원하는 이벤트를 호출할 수 있습니다. +Active Job은 잡의 생애 주기에 따른 훅을 제공합니다. 이를 이용해서 콜백을 +사용할 수 있으므로 잡의 생애 주기의 특정 시점에 원하는 이벤트를 호출할 수 +있습니다. ### 사용 가능한 콜백 @@ -182,7 +260,7 @@ Active Job은 잡의 생애 주기에 따른 훅을 제공합니다. 이를 이 ### 사용법 ```ruby -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob queue_as :default before_enqueue do |job| @@ -205,7 +283,10 @@ end ActionMailer ------------ -최근 웹 애플리케이션에서 자주 쓰는 잡 중의 한가지는 요청-응답 주기 밖에서 발생하는 메일 전송일 것입니다. 이를 잡으로 처리하는 것으로 사용자가 메일 송신을 기다릴 필요가 없어집니다. Active Job은 Action Mailer와 통합되어 있으므로 비동기 메일 전송도 간단하게 처리할 수 있습니다. +최근 웹 애플리케이션에서 자주 쓰는 잡 중의 한 가지는 요청-응답 주기 밖에서 +발생하는 메일 전송일 것입니다. 이를 잡으로 처리하는 것으로 사용자가 메일 +송신을 기다릴 필요가 없어집니다. Active Job은 Action Mailer와 통합되어 +있으므로 비동기 메일 전송도 간단하게 처리할 수 있습니다. ```ruby # 곧바로 전송하고 싶은 경우에는 #deliver_now를 사용 @@ -215,10 +296,26 @@ UserMailer.welcome(@user).deliver_now UserMailer.welcome(@user).deliver_later ``` +국제화 +-------- + +각 잡은 생성될 때의 `I18n.locale`설정을 사용합니다. 메일을 비동기로 전송하는 +경우에 유용합니다. + +```ruby +I18n.locale = :eo + +UserMailer.welcome(@user).deliver_later # Email will be localized to Esperanto. +``` + GlobalID -------- -Active Job에서는 GlobalID를 파라미터로 사용할 수 있습니다. GlobalID를 사용하면, 동작 중인 Active Record 객체를 잡에 넘겨줄 때에 클래스와 id를 지정할 필요가 없어집니다. 클래스와 id를 지정하는 이전의 방법은 나중에 명시적으로 역직렬화(deserialize)를 할 필요가 있었습니다. 이전대로라면 이런 식으로 작성하던 코드를, +Active Job에서는 GlobalID를 파라미터로 사용할 수 있습니다. GlobalID를 사용하면, +동작 중인 Active Record 객체를 잡에 넘겨줄 때에 클래스와 id를 지정할 필요가 +없어집니다. 클래스와 id를 지정하는 이전의 방법은 나중에 명시적으로 +역직렬화(deserialize)를 할 필요가 있었습니다. 이전대로라면 이런 식으로 +작성하던 코드를, ```ruby class TrashableCleanupJob @@ -239,7 +336,8 @@ class TrashableCleanupJob end ``` -이 코드는 `ActiveModel::GlobalIdentification`을 믹스인해둔 모든 클래스에서 동작하며, 이 모듈은 Active Model 클래스에 기본적으로 믹스인되어 있습니다. +이 코드는 `GlobalID::Identification`을 믹스인해둔 모든 클래스에서 동작하며, +이 모듈은 Active Model 클래스에 기본적으로 믹스인되어 있습니다. 예외 @@ -249,7 +347,7 @@ Active Job에서는 잡 실행시에 발생하는 예외를 처리하기 위한 ```ruby -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob queue_as :default rescue_from(ActiveRecord::RecordNotFound) do |exception| @@ -262,4 +360,15 @@ class GuestsCleanupJob < ActiveJob::Base end ``` -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. +### 역직렬화(Deserialization) + +GlobalID는 `#perform`을 통해서 넘겨진 Active Record 객체를 직렬화합니다. + +만약 잡이 큐에 등록된 이후에 `#perform` 메소드라 실행되기 전에 레코드가 +삭제되면, Active Job은 `ActiveJob::DeserializationError` 예외를 던집니다. + +잡 테스트하기 +-------------- + +잡을 테스트하는 방법에 대해서는 [테스팅 가이드](testing.html#잡-테스트하기)에서 +자세한 설명을 확인할 수 있습니다. diff --git a/guides/source/ko/active_model_basics.md b/guides/source/ko/active_model_basics.md index 778e309ea3aa1..84875f12721eb 100644 --- a/guides/source/ko/active_model_basics.md +++ b/guides/source/ko/active_model_basics.md @@ -1,19 +1,31 @@ 액티브 모델 =================== -이 가이드에서는 모델 클래스를 사용하는 경우에 필요한 모든 것에 대해 설명합니다. 액션 팩 헬퍼는 액티브 모델 덕분에 일반 루비 객체와도 사용할 수 있습니다. 액티브 모댈을 사용하면 커스텀 ORM을 만들어 레일스 프레임워크 외부에서 사용할 수도 있습니다. +이 가이드에서는 모델 클래스를 사용하는 경우에 필요한 모든 것에 대해 설명합니다. +액티브 모델은 액션 팩 헬퍼 덕분에 일반 루비 객체와도 사용할 수 있습니다. +액티브 모델을 통해 커스텀 ORM을 만들어 레일스 프레임워크 외부에서 사용할 수도 +있습니다. +이 가이드의 내용: + +* 액티브 모델의 동작. +* 콜백과 검증의 동작. +* 직렬화의 동작 방식. +* 액티브 모델과 Rails 국제화(i18n) 프레임워크의 동작 -------------------------------------------------------------------------------- 들어가기 전에 ------------ -액티브 모델은 액티브 레코드에서 사용되는 많은 모듈을 포함하는 라이브러리입니다. 몇몇 모듈에 대해서는 아래에서 설명합니다. +액티브 모델은 액티브 레코드에서 사용되는 많은 모듈을 포함하는 라이브러리입니다. +몇몇 모듈에 대해서는 아래에서 설명합니다. ### Attribute Methods -`ActiveModel::AttributeMethods` 모듈은 클래스의 메소드에 접두사, 또는 접미사를 추가할 수 있습니다. 이 모듈을 사용하려면 접두사, 또는 접미사를 정의하고 이를 적용할 메소드를 지정하세요. +`ActiveModel::AttributeMethods` 모듈은 클래스의 메소드에 접두사, 또는 접미사를 +추가할 수 있습니다. 이 모듈을 사용하려면 접두사, 또는 접미사를 정의하고 이를 +적용할 메소드를 지정하세요. ```ruby class Person @@ -416,14 +428,15 @@ Finished in 0.024899s, 240.9735 runs/s, 1204.8677 assertions/s. 6 runs, 30 assertions, 0 failures, 0 errors, 0 skips ``` -객체가 액션 팩을 사용하기 위해서 모든 API를 구현해야할 필요는 없습니다. 이 모듈은 모든 기능을 구현하고 싶은 경우에 -가이드라인을 제공하는 것이 목적입니다. +객체가 액션 팩을 사용하기 위해서 모든 API를 구현해야할 필요는 없습니다. 이 +모듈은 모든 기능을 구현하고 싶은 경우에 가이드라인을 제공하는 것이 목적입니다. ### SecurePassword -`ActiveModel::SecurePassword` 모듈은 암호화된 폼에서 비밀번호를 안전하게 저장하기 위한 방법을 제공합니다. -이 모듈을 사용하면 `has_secure_password` 클래스 메소드가 추가되며, 이 메소드는 `password`라는 접근자와 -필요한 검증 기능을 추가합니다. +`ActiveModel::SecurePassword` 모듈은 암호화된 폼에서 비밀번호를 안전하게 +저장하기 위한 방법을 제공합니다. +이 모듈을 사용하면 `has_secure_password` 클래스 메소드가 추가되며, 이 메소드는 +`password`라는 접근자와 필요한 검증 기능을 추가합니다. #### 요구조건 @@ -433,7 +446,7 @@ Finished in 0.024899s, 240.9735 runs/s, 1204.8677 assertions/s. `has_secure_password`는 `password` 접근자에 다음과 같은 검증을 추가합니다. 1. 비밀번호는 반드시 존재해야 합니다. -2. 비밀번호는 반드시 확인용 비밀번호와 동일해야 합니다. +2. 비밀번호는 반드시 확인용 비밀번호와 동일해야 합니다(+password_confirmation이 제공됩니다+). 3. 길이는 72자 이하여야 합니다. #### 예제 diff --git a/guides/source/ko/active_record_basics.md b/guides/source/ko/active_record_basics.md index 65a09a750c64e..0d6ea855ba9a3 100644 --- a/guides/source/ko/active_record_basics.md +++ b/guides/source/ko/active_record_basics.md @@ -17,34 +17,64 @@ Active Record 기초 Active Record에 대해서 ---------------------- -Active Record란, [MVC](https://ko.wikipedia.org/wiki/모델-뷰-컨트롤러)에서 말하는 M, 다시 말해 모델에 해당하는 것으로, 비지니스 데이터와 비지니스 로직을 표현하기 위한 계층입니다. Active Record는 데이터베이스에 항구적으로 보존될 필요가 있는 비지니스 객체를 편하게 생성하고 사용할 수 있게 해줍니다. Active Record는 ORM(Object Relational Mapping) 시스템에 기술되어 있는 'Active Record 패턴'을 구현한 것으로 이와 같은 이름이 붙혀져 있습니다. +Active Record란 [MVC](https://ko.wikipedia.org/wiki/모델-뷰-컨트롤러)에서 +말하는 M, 다시 말해 모델에 해당하는 것으로, 비지니스 데이터와 비지니스 로직을 +표현하기 위한 계층입니다. Active Record는 데이터베이스에 항구적으로 보존될 +필요가 있는 비지니스 객체를 편하게 생성하고 사용할 수 있게 해줍니다. +Active Record는 ORM(Object Relational Mapping) 시스템에 기술되어 있는 +'Active Record 패턴'을 구현한 것으로 이와 같은 이름이 붙혀져 있습니다. ### Active Record 패턴 -[Active Record는 Martin Fowler가 쓴](http://www.martinfowler.com/eaaCatalog/activeRecord.html) _Patterns of Enterprise Application Architecture_ 라는 서적에서 언급되었습니다. Active Record에 있어서 객체란 영속적인 데이터로, 그 데이터와 데이터의 행동에 대한 것이기도 합니다. Active Record에서는 데이터에 접근하는 방식을 확실히 하는 것은, 그 객체의 사용자에게 데이터베이스에 읽고 쓰는 방법을 가르치는 것의 일부이다, 라는 의견을 채용하고 있습니다. +[Active Record는 Martin Fowler가 쓴](http://www.martinfowler.com/eaaCatalog/activeRecord.html) +_Patterns of Enterprise Application Architecture_ 라는 서적에서 언급되었습니다. +Active Record에 있어서 객체란 영속적인 데이터로, 그 데이터와 데이터의 행동에 +대한 것이기도 합니다. Active Record에서는 데이터에 접근하는 방식을 확실히 하는 +것은, 그 객체의 사용자에게 데이터베이스에 읽고 쓰는 방법을 가르치는 것의 +일부이다, 라는 의견을 채용하고 있습니다. ### O/R 매핑 -객체 관계 매핑(O/R매핑이나 ORM이라고 줄여쓰기도 합니다)이란 애플리케이션이 가진 다양한 객체를 관계형 데이터베이스(RDBMS)의 테이블에 연결하는 것입니다. ORM을 사용하는 것으로 SQL문을 직접 작성하는 대신 적은 코드를 작성하는 것으로 애플리케이션의 객체의 속성이나 관계를 데이터베이스에 저장하거나 읽어올 수 있게 됩니다. +객체 관계 매핑(O/R매핑이나 ORM이라고 줄여쓰기도 합니다)이란 애플리케이션이 +가진 다양한 객체를 관계형 데이터베이스(RDBMS)의 테이블에 연결하는 것입니다. +ORM을 사용하는 것으로 SQL문을 직접 작성하는 대신 적은 코드를 작성하는 것으로 +애플리케이션의 객체의 속성이나 관계를 데이터베이스에 저장하거나 읽어올 수 +있게 됩니다. ### ORM 프레임워크로서의 Active Record -Active Record에는 다양한 기능이 구현되어있으며, 그 중에서 아래에 언급한 것들이 특히 중요합니다. +Active Record에는 다양한 기능이 구현되어있으며, 그 중에서 아래에 언급한 것들이 +특히 중요합니다. -* 모델과 그의 데이터를 표현한다 -* 모델간의 관계(Association)를 표현한다 -* 관련된 모델을 이용해서 상속 계층을 표현한다 -* 데이터가 데이터베이스에 저장되기 전에 검증을 수행한다 -* 객체 지향적인 방법으로 데이터베이스를 조작한다 +* 모델과 그의 데이터를 표현한다. +* 모델간의 관계(Association)를 표현한다. +* 관련된 모델을 이용해서 상속 계층을 표현한다. +* 데이터가 데이터베이스에 저장되기 전에 검증을 수행한다. +* 객체 지향적인 방법으로 데이터베이스를 조작한다. Active Record에 있어서의 CoC(Convention over Configuration) ---------------------------------------------- -다른 프로그래밍 언어나 프레임워크를 사용해서 애플리케이션을 개발하면, 설정을 위한 코드를 대량으로 작성하는 경우가 자주 발생합니다. 일반적인 ORM 애플리케이션에서 특히 이런 경향이 있습니다. 하지만 Rails에 적합한 규칙에 따르게 되면, Active Record 모델을 만들때 설정에 관련된 코드는 최소한으로 줄일 수 있습니다. 상황에 따라서는 설정을 위한 코드가 전혀 필요 없는 경우도 있습니다. 이것은 애플리케이션의 설정이 대부분 동일하다면, 그것을 기본값으로 설정해야한다는 생각에 기초하고 있습니다. 다시 말해, 명시적인 설정이 필요한 경우는 표준 규칙만으로는 부족한 경우 뿐입니다. +다른 프로그래밍 언어나 프레임워크를 사용해서 애플리케이션을 개발하면, 설정을 +위한 코드를 대량으로 작성하는 경우가 자주 발생합니다. 일반적인 +ORM 애플리케이션에서 특히 이런 경향이 있습니다. 하지만 Rails에 적합한 규칙에 +따르게 되면, Active Record 모델을 만들때 설정에 관련된 코드는 최소한으로 줄일 +수 있습니다. 상황에 따라서는 설정을 위한 코드가 전혀 필요 없는 경우도 있습니다. +이것은 애플리케이션의 설정이 대부분 동일하다면, 그것을 기본값으로 설정해야 +한다는 생각에 기초하고 있습니다. 다시 말해, 명시적인 설정이 필요한 경우는 +표준 규칙만으로는 부족한 경우 뿐입니다. ### 명명 규칙 -Active Record에는 모델과 데이터베이스의 테이블을 매핑할 때에 지켜야할 규칙이 몇가지 있습니다. Rails에서는 데이터베이스의 테이블 이름을 찾을 때에 모델의 클래스명의 복수형을 사용합니다. 다시 말해 `Book`이라는 모델 클래스가 있을 경우, 여기에 대응하는 데이터베이스의 테이블은 복수형인 **books**이 됩니다. Rails의 복수형화 알고리즘은 무척 영리해서, 불규칙 변형일 경우에도 복수형으로 변환하거나, 단수형으로 변환할 수 있습니다(person <-> people 등). 모델의 클래스명이 2단어 이상의 복합어일 경우, Ruby의 관습인 CamelCase에 따라주세요. 그리고 테이블의 이름은 소문자에 밑줄(CamelCase에 대응하는 테이블 명은 camel_cases가 됩니다)을 이용해야 합니다. 이하의 예제를 참조해주세요. +Active Record에는 모델과 데이터베이스의 테이블을 매핑할 때에 지켜야할 규칙이 +몇 가지 있습니다. Rails에서는 데이터베이스의 테이블 이름을 찾을 때에 모델의 +클래스명의 복수형을 사용합니다. 다시 말해 `Book`이라는 모델 클래스가 있을 경우, +여기에 대응하는 데이터베이스의 테이블은 복수형인 **books**이 됩니다. Rails의 +복수형화 알고리즘은 무척 영리해서 불규칙 변형일 경우에도 복수형으로 변환하거나, +단수형으로 변환할 수 있습니다(person <-> people 등). 모델의 클래스명이 2단어 +이상의 복합어일 경우, Ruby의 관습인 CamelCase에 따라주세요. 그리고 테이블의 +이름은 소문자에 밑줄(CamelCase에 대응하는 테이블 명은 camel_cases가 됩니다)을 +이용해야 합니다. 이하의 예제를 참조해주세요. * 데이터베이스 테이블명 - 복수형, 단어는 밑줄로 구분한다. (ex: `book_clubs`) * 모델 클래스명 - 단수형, 단어는 대문자로 시작한다. (es: `BookClub`) @@ -60,11 +90,17 @@ Active Record에는 모델과 데이터베이스의 테이블을 매핑할 때 ### 스키마 규칙 -Active Record에서는 데이터베이스의 테이블에 사용하는 컬럼명에 대해서도 사용 목적에 따른 규칙이 존재합니다. +Active Record에서는 데이터베이스의 테이블에 사용하는 컬럼명에 대해서도 사용 +목적에 따른 규칙이 존재합니다. -* **Foreign keys** - 이 컬럼은 `테이블명의 단수형_id` 로 명명합니다. (ex: `item_id`, `order_id`) 이 컬럼들은 Active Record가 모델간의 관계를 설정할 때 참조합니다. +* **Foreign keys** - 이 컬럼은 `테이블명의 단수형_id` 로 명명합니다. +(ex: `item_id`, `order_id`) 이 컬럼들은 Active Record가 모델간의 관계를 +설정할 때 참조합니다. -* **Primary Keys** - 기본값으로 `id`라는 이름을 가지는 integer 형의 컬럼을 테이블의 기본키로 사용합니다. 이 컬럼은 [Active Record Migrations](migrations.html)을 사용해서 테이블을 작성할 때에 자동으로 생성됩니다. +* **Primary Keys** - 기본값으로 `id`라는 이름을 가지는 integer 형의 컬럼을 +테이블의 기본키로 사용합니다. 이 컬럼은 +[Active Record Migrations](active_record_migrations.html)을 사용해서 +테이블을 작성할 때에 자동으로 생성됩니다. 이외에도, Active Record 인스턴스에 기능을 추가하는 컬럼이 더 있습니다. @@ -75,19 +111,27 @@ Active Record에서는 데이터베이스의 테이블에 사용하는 컬럼명 * `관계명_type` - [Polymorphic Associations](association_basics.html#polymorphic-associations)의 종류를 저장합니다. * `테이블명_count` - 관계 설정에 있어서 속해있는 객체 숫자를 캐싱하기 위해서 사용됩니다. 예를 들어 `Post` 클래스에 `comments_count`라는 컬럼이 있고, 거기에 `Comment` 인스턴스가 다수 존재한다면, 각각의 Post마다 Comment의 숫자가 캐싱됩니다. -NOTE: 이 컬럼명들은 필수는 아니지만, Active Record에 의해서 예약되어 있습니다. 어쩔 수 없는 경우가 아니라면 이 예약된 컬럼명을 사용하는 것은 피해주세요. 예를 들어 `type`라는 단어는 테이블에서 Single Table Inheritance(STI)를 지정하기 위해서 예약되어 있습니다. STI를 사용하지 않는 경우라도 예약어보다는 "context"같은, 데이터를 적절히 표현할 수 있는 단어를 검토해주세요. +NOTE: 이 컬럼명들은 필수는 아니지만, Active Record에 의해서 예약되어 있습니다. +어쩔 수 없는 경우가 아니라면 이 예약된 컬럼명을 사용하는 것은 피해주세요. +예를 들어 `type`라는 단어는 테이블에서 Single Table Inheritance(STI)를 +지정하기 위해서 예약되어 있습니다. STI를 사용하지 않는 경우라도 예약어보다는 +"context"같은, 데이터를 적절히 표현할 수 있는 단어를 검토해주세요. Active Record 모델 만들기 ----------------------------- -Active Record 모델을 만드는 것은 무척 간단합니다. 다음과 같이 `ActiveRecord::Base` 클래스의 자식 클래스를 만들기만 하면 끝입니다. +Active Record 모델을 만드는 것은 무척 간단합니다. 다음과 같이 +`ActiveRecord::Base` 클래스의 자식 클래스를 만들기만 하면 끝입니다. ```ruby class Product < ActiveRecord::Base end ``` -위의 코드는 `Product` 모델을 만들고, 데이터베이스의 `products` 테이블에 매핑됩니다. 거기에 테이블에 포함되어있는 각 컬럼을 만들어진 모델의 인스턴스의 속성으로 매핑합니다. 이하의 SQL문에서 `products`라는 테이블을 생성했다고 해봅시다. +위의 코드는 `Product` 모델을 만들고, 데이터베이스의 `products` 테이블에 +매핑됩니다. 거기에 테이블에 포함되어있는 각 컬럼을 만들어진 모델의 인스턴스의 +속성으로 매핑합니다. 이하의 SQL문에서 `products`라는 테이블을 생성했다고 +해봅시다. ```sql CREATE TABLE products ( @@ -108,9 +152,13 @@ puts p.name # "Some Book" 명명 규칙을 덮어쓰기 --------------------------------- -Rails 애플리케이션에서 별도의 명명 규칙을 사용하지 않으면 안되는, 예를 들어 레거시 데이터베이스를 사용해서 Rails 애플리케이션을 작성하지 않으면 곤란한 경우에는 어떻게 하면 될까요. 그런 상황에는 기본 명명 규칙을 간단하게 덮어쓸 수 있습니다. +Rails 애플리케이션에서 별도의 명명 규칙을 사용하지 않으면 안되는, 예를 들어 +레거시 데이터베이스를 사용해서 Rails 애플리케이션을 작성하지 않으면 곤란한 +경우에는 어떻게 하면 될까요. 그런 상황에는 기본 명명 규칙을 간단하게 덮어쓸 +수 있습니다. -`ActiveRecord::Base.table_name=` 메소드를 사용해서 사용할 테이블명을 명시적으로 지정할 수 있습니다. +`ActiveRecord::Base.table_name=` 메소드를 사용해서 사용할 테이블명을 명시적으로 +지정할 수 있습니다. ```ruby class Product < ActiveRecord::Base @@ -118,7 +166,8 @@ class Product < ActiveRecord::Base end ``` -테이블명을 지정했을 경우, 테스트 정의에서 `set_fixture_class` 메소드를 사용해 픽스쳐(클래스명.yml)에 대응하는 클래스명을 별도로 정의할 필요가 있습니다. +테이블명을 지정했을 경우, 테스트 정의에서 `set_fixture_class` 메소드를 사용해 +픽스쳐(클래스명.yml)에 대응하는 클래스명을 별도로 정의할 필요가 있습니다. ```ruby class FunnyJoke < ActiveSupport::TestCase @@ -128,7 +177,8 @@ class FunnyJoke < ActiveSupport::TestCase end ``` -`ActiveRecord::Base.primary_key=` 메소드를 사용해서 테이블의 기본키로 사용할 컬럼명도 덮어쓸 수 있습니다. +`ActiveRecord::Base.primary_key=` 메소드를 사용해서 테이블의 기본키로 사용할 +컬럼명도 덮어쓸 수 있습니다. ```ruby class Product < ActiveRecord::Base @@ -139,19 +189,28 @@ end CRUD: 데이터 읽고 쓰기 ------------------------------ -CRUD란 4개의 데이터베이스 조작을 가리키는 **C** reate、 **R** ead、 **U** pdate、 **D** elete의 첫글자입니다. Active Record는 각각에 대한 메소드를 자동적으로 생성하고, 이를 이용해서 애플리케이션은 테이블에 저장되어 있는 데이터를 조작할 수 있습니다. +CRUD란 4개의 데이터베이스 조작을 가리키는 **C** reate, **R** ead, **U** pdate, +**D** elete의 첫글자입니다. Active Record는 각각에 대한 메소드를 자동적으로 +생성하고, 이를 이용해서 애플리케이션은 테이블에 저장되어 있는 데이터를 조작할 +수 있습니다. ### Create -Active Record의 객체는 해시나 블록을 이용해서 생성할 수 있습니다. 또한 생성 후에는 속성을 수동으로 추가할 수도 있습니다. `new` 메소드를 실행하면 새로운 객체가 반환됩니다만, `create`를 실행하면 새로운 객체가 반환되고, 나아가 데이터베이스에 저장도 수행합니다. +Active Record의 객체는 해시나 블록을 이용해서 생성할 수 있습니다. 또한 생성 +후에는 속성을 수동으로 추가할 수도 있습니다. `new` 메소드를 실행하면 새로운 +객체가 반환됩니다만, `create`를 실행하면 새로운 객체가 반환되고, 나아가 +데이터베이스에 저장도 수행합니다. -예를 들어 `User`라는 모델에 `name`과 `occupation`라는 속성이 있다고 하면, `create` 메소드를 실행하면 새로운 레코드가 하나 생성되고, 데이터베이스에 저장됩니다. +예를 들어 `User`라는 모델에 `name`과 `occupation`라는 속성이 있다고 하면, +`create` 메소드를 실행하면 새로운 레코드가 하나 생성되고, 데이터베이스에 +저장됩니다. ```ruby user = User.create(name: "David", occupation: "Code Artist") ``` -`new` 메소드를 사용한 경우에는 객체는 저장되지 않고 인스턴스로 반환되기만 합니다. +`new` 메소드를 사용한 경우에는 객체는 저장되지 않고 인스턴스로 반환되기만 +합니다. ```ruby user = User.new @@ -161,7 +220,8 @@ user.occupation = "Code Artist" 이 경우, `user.save`를 실행해야만 데이터베이스에 레코드가 저장됩니다. -마지막으로 `create`나 `new`에 블록을 건네주면, 새로운 객체는 초기화를 위해서 블록을 사용합니다. +마지막으로 `create`나 `new`에 블록을 건네주면, 새로운 객체는 초기화를 위해서 +블록을 사용합니다. ```ruby user = User.new do |u| @@ -172,7 +232,9 @@ end ### Read -Active Record는 데이터베이스의 데이터에 접근하기 위해 풍부한 API를 제공합니다. 아래는 Active Record에 의해서 제공되는 다양한 데이터 접근 메소드 중의 몇몇 예시입니다. +Active Record는 데이터베이스의 데이터에 접근하기 위해 풍부한 API를 제공합니다. +아래는 Active Record에 의해서 제공되는 다양한 데이터 접근 메소드 중의 몇몇 +예시입니다. ```ruby # 모든 사용자 컬렉션을 반환한다 @@ -190,15 +252,19 @@ david = User.find_by(name: 'David') ``` ```ruby -# 이름이 David이고, 직업이 코드 아티스트인 사용자를 모두 반환하고, created_at 컬럼을 기준으로 내림차순으로 정렬한다. +# 이름이 David이고, 직업이 코드 아티스트인 사용자를 모두 반환하고, +# created_at 컬럼을 기준으로 내림차순으로 정렬한다. users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC') ``` -Active Record 모델에 쿼리를 실행하는 것은 [Active Record Query Interface](active_record_querying.html)에서 자세하게 설명합니다. +Active Record 모델에 쿼리를 실행하는 것은 +[Active Record Query Interface](active_record_querying.html)에서 자세하게 +설명합니다. ### Update -Active Record 객체를 받으면, 객체의 속성을 변경하고 데이터베이스에 저장할 수 있습니다. +Active Record 객체를 받으면 객체의 속성을 변경하고 데이터베이스에 저장할 수 +있습니다. ```ruby user = User.find_by(name: 'David') @@ -206,14 +272,17 @@ user.name = 'Dave' user.save ``` -위의 코드를 더 짧게 하려면, 속성명과설정할 값을 매핑하는 해시를 사용해서 다음과 같이 작성할 수 있습니다. +위의 코드를 더 짧게 하려면, 속성명과 설정할 값을 매핑하는 해시를 사용해서 +다음과 같이 작성할 수 있습니다. ```ruby user = User.find_by(name: 'David') user.update(name: 'Dave') ``` -이것은 여러 속성을 한번에 변경하고 싶을 때에 편리합니다. 나아가서 여러 개의 레코드를 한번에 변경하고 싶다면 `update_all`이라는 클래스 메소드를 사용할 수 있습니다. +이것은 여러 속성을 한번에 변경하고 싶을 때에 편리합니다. 나아가서 여러 개의 +레코드를 한 번에 변경하고 싶다면 `update_all`이라는 클래스 메소드를 사용할 수 +있습니다. ```ruby User.update_all "max_login_attempts = 3, must_change_password = 'true'" @@ -221,7 +290,8 @@ User.update_all "max_login_attempts = 3, must_change_password = 'true'" ### Delete -다른 메소드와 마찬가지로 Active Record 객체를 얻으면, 그 객체를 destroy하는 것으로 데이터베이스에서 삭제할 수 있습니다. +다른 메소드와 마찬가지로 Active Record 객체를 얻으면, 그 객체를 destroy하는 +것으로 데이터베이스에서 삭제할 수 있습니다. ```ruby user = User.find_by(name: 'David') @@ -231,9 +301,18 @@ user.destroy 유효성 검사(validation) ----------- -Active Record를 사용해서 모델이 데이터베이스에 저장되기 전에 모델의 상태를 검증할 수 있습니다. 모델을 검사하기 위해서 다양한 메소드들의 준비되어 있습니다. 속성이 존재하는지, 유일한 값인지, 특정한 형식에 따르고 있는지, 등을 검사할 수 있습니다. +Active Record를 사용해서 모델이 데이터베이스에 저장되기 전에 모델의 상태를 +검증할 수 있습니다. 모델을 검사하기 위해서 다양한 메소드들의 준비되어 있습니다. +속성이 존재하는지, 유일한 값인지, 특정한 형식에 따르고 있는지 등을 검사할 수 +있습니다. + +유효성 검사는 데이터베이스를 영속화하는 데에 무척 중요합니다. 이 때문에 +`create`, `save`, `update` 메소드는 검증이 실패하는 경우 `false`를 반환합니다. +이런 경우에는 데이터베이스에 아무런 변경도 발생하지 않습니다. 이 3개의 +메소드에는 각각 파괴적인 버전(`create!`, `save!`, `update!`)가 있으며, 검증에 +실패한 경우에는 좀 더 엄격한 대응, 다시 말해서 `ActiveRecord::RecordInvalid` +예외를 발생시킵니다. -유효성 검사는 데이터베이스를 영속화하는 데에 무척 중요합니다. 이 때문에 `create`, `save`, `update` 메소드는 검증이 실패하는 경우 `false`를 반환합니다. 이런 경우에는 데이터베이스에 아무런 변경도 발생하지 않습니다. 이 3개의 메소드에는 각각 파괴적인 버전(`create!`, `save!`, `update!`)가 있으며, 검증에 실패한 경우에는 좀 더 엄격한 대응, 다시 말해서 `ActiveRecord::RecordInvalid` 예외를 발생시킵니다. 아래의 예로 간단하게 설명하겠습니다. ```ruby @@ -245,17 +324,25 @@ User.create # => false User.create! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank ``` -유효성 검사에 대한 자세한 설명은 [Active Record Validations](active_record_validations.html)를 참조해주세요. +유효성 검사에 대한 자세한 설명은 +[Active Record Validations](active_record_validations.html)를 참조해주세요. 콜백 --------- -Active Record 콜백을 사용하는 것으로 모델의 라이프 사이클 중 특정 이벤트가 발생했을 때에 원하는 코드를 실행할 수 있습니다. 레코드의 생성, 갱신, 삭제 등 다양한 이벤트에 대해서 콜백을 설정할 수 있습니다. 자세한 설명은 [Active Record Callbacks](active_record_callbacks.html)를 참조해주세요. +Active Record 콜백을 사용하는 것으로 모델의 라이프 사이클 중 특정 이벤트가 +발생했을 때에 원하는 코드를 실행할 수 있습니다. 레코드의 생성, 갱신, 삭제 등 +다양한 이벤트에 대해서 콜백을 설정할 수 있습니다. 자세한 설명은 +[Active Record Callbacks](active_record_callbacks.html)를 참조해주세요. 마이그레이션 ---------- -Rails에는 데이터베이스 스키마를 관리하기 위한 도메인 특화 언어(DSL: Domain Specific Language)가 있으며, 마이그레이션(migration)이라고도 불립니다. 마이그레이션은 파일로 저장됩니다. `bin/rails`를 통해 Active Record가 지원하는 다양한 데이터베이스에 대한 마이그레이션을 수행할 수 있습니다. 아래는 테이블을 생성하는 마이그레이션입니다. +Rails에는 데이터베이스 스키마를 관리하기 위한 도메인 특화 +언어(DSL: Domain Specific Language)가 있으며, 마이그레이션(migration)이라고도 +불립니다. 마이그레이션은 파일로 저장됩니다. `bin/rails`를 통해 Active Record가 +지원하는 다양한 데이터베이스에 대한 마이그레이션을 수행할 수 있습니다. +아래는 테이블을 생성하는 마이그레이션입니다. ```ruby class CreatePublications < ActiveRecord::Migration @@ -275,8 +362,13 @@ class CreatePublications < ActiveRecord::Migration end ``` -Rails는 어떤 마이그레이션 파일이 데이터베이스에 반영되어있는지 파악하고 있어서, 그 정보를 활용해 롤백 기능도 제공하고 있습니다. 테이블을 실제로 생성하기 위해서는 `bin/rails db:migrate`를 실행합니다. 롤백하기 위해서는 `bin/rails db:rollback`을 실행하면 됩니다. +Rails는 어떤 마이그레이션 파일이 데이터베이스에 반영되어있는지 파악하고 있어서, +그 정보를 활용해 롤백 기능도 제공하고 있습니다. 테이블을 실제로 생성하기 +위해서는 `bin/rails db:migrate`를 실행합니다. 롤백하기 위해서는 +`bin/rails db:rollback`을 실행하면 됩니다. -위의 마이그레이션 코드는 데이터베이스에 의존하지 않는다는 점에 주목해주세요. MySQL, PostgreSQL, Oracle 등, 다수의 데이터베이스에 대해서 실행할 수 있습니다. 마이그레이션에 대한 자세한 설명은 [Active Record Migrations](migrations.html)을 참조해주세요. +위의 마이그레이션 코드는 데이터베이스에 의존하지 않는다는 점에 주목해주세요. +MySQL, PostgreSQL, Oracle 등, 다수의 데이터베이스에 대해서 실행할 수 있습니다. +마이그레이션에 대한 자세한 설명은 +[Active Record Migrations](active_record_migrations.html)을 참조해주세요. -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/active_record_callbacks.md b/guides/source/ko/active_record_callbacks.md index 0b507ad5b2ee8..d7620a6de223f 100644 --- a/guides/source/ko/active_record_callbacks.md +++ b/guides/source/ko/active_record_callbacks.md @@ -243,22 +243,36 @@ NOTE: `find_by_*`메소드와 `find_by_*!` 메소드는 속성마다 자동적 * `update_all` * `update_counters` -중요한 비지니스 로직이나 애플리케이션 로직은 콜백을 사용하기 때문에 이 메소드들을 사용하는 경우에는 주의해주세요. 실수로 콜백을 우회하게 되면, 데이터 부정합이 발생할 가능성이 있습니다. +중요한 비지니스 로직이나 애플리케이션 로직은 콜백을 사용하기 때문에 이 +메소드들을 사용하는 경우에는 주의해주세요. 실수로 콜백을 우회하게 되면, +데이터 부정합이 발생할 가능성이 있습니다. 콜백 등록 취소 ----------------- -모델에 새로운 콜백을 등록하면 실행 큐에 삽입됩니다. 이 큐에는 모델에 대한 모든 검증, 등록된 콜백, 실행 대기중인 데이터베이스 조작 등이 들어갑니다. +모델에 새로운 콜백을 등록하면 실행 큐에 삽입됩니다. 이 큐에는 모델에 대한 모든 +검증, 등록된 콜백, 실행 대기중인 데이터베이스 조작 등이 들어갑니다. -콜백 체인은 하나의 트랜잭션에 포함됩니다. _before_ 콜백 중 하나가 `false`를 반환하거나 예외를 발생시키는 경우, 전체가 정지하고 롤백됩니다. 이 경우, _after_ 콜백은 예외를 발생시키는 경우에만 중지됩니다. +콜백 체인은 하나의 트랜잭션에 포함됩니다. _before_ 콜백 중 하나가 `false`를 +반환하거나 예외를 발생시키는 경우, 전체가 정지하고 롤백됩니다. 이 경우, +_after_ 콜백은 예외를 발생시키는 경우에만 중지됩니다. -WARNING: 콜백의 체인 뒤에 발생하는 `ActiveRecord::Rollback` 을 제외한 모든 예외는 Rails에 의해서 다시 발생됩니다. `ActiveRecord::Rollback` 이외의 예외가 발생하면 `save`나 `update_attributes`같은 메소드처럼 예외의 발생을 고려하지 않은 코드(보통 `true`나 `false`가 반환됩니다)의 동작을 망가뜨리게 됩니다. +WARNING: 콜백의 체인 뒤에 발생하는 `ActiveRecord::Rollback`이나 +`ActiveRecord::RecordInvalid`를 제외한 모든 예외는 Rails에 의해서 다시 +발생됩니다. `ActiveRecord::Rollback`나 `ActiveRecord::RecordInvalid` 이외의 +예외가 발생하면 `save`나 `update_attributes`같은 메소드처럼 예외의 발생을 +고려하지 않은 코드(보통 `true`나 `false`가 반환됩니다)의 동작을 망가뜨리게 +됩니다. 관계 콜백 -------------------- -콜백은 모델의 관계를 통해서도 동작할 수 있습니다. 또한 관계를 사용해서 콜백을 정의하는 것도 가능합니다. 한명의 사용자가 여러개의 글을 가지고 있는 경우로 예를 들어보겠습니다. 어떤 사용자가 작성한 글은 그 사용자가 삭제되면 함께 삭제될 필요가 있습니다. `User` 모델에 `depenent`를 추가하고 `Post` 모델에 `after_destroy` 콜백을 추가하면 다음과 같이 동작합니다. +콜백은 모델의 관계를 통해서도 동작할 수 있습니다. 또한 관계를 사용해서 콜백을 +정의하는 것도 가능합니다. 한명의 사용자가 여러개의 글을 가지고 있는 경우로 예를 +들어보겠습니다. 어떤 사용자가 작성한 글은 그 사용자가 삭제되면 함께 삭제될 +필요가 있습니다. `User` 모델에 `depenent`를 추가하고 `Post` 모델에 +`after_destroy` 콜백을 추가하면 다음과 같이 동작합니다. ```ruby class User < ActiveRecord::Base @@ -285,11 +299,20 @@ Post destroyed 조건부 콜백 --------------------- -검증과 마찬가지로 주어진 조건을 만족하는 경우에만 실행되는 콜백 메소드를 작성할 수 있습니다. 이렇게 하기 위해서는 콜백에 `:if` 또는 `:unless` 옵션을 사용하면 됩니다. 이 옵션은 심볼, 문자열, `Proc` 또는 `Array`를 인수로 받습니다. 특정한 상황에서만 콜백이 실행될 필요가 있는 경우에는 `:if` 옵션을 사용합니다. 특성 상황에서 콜백이 실행되어서는 안되는 경우에 `:unless` 옵션을 사용합니다. +검증과 마찬가지로 주어진 조건을 만족하는 경우에만 실행되는 콜백 메소드를 +작성할 수 있습니다. 이렇게 하기 위해서는 콜백에 `:if` 또는 `:unless` 옵션을 +사용하면 됩니다. 이 옵션은 심볼, 문자열, `Proc` 또는 `Array`를 인수로 받습니다. +특정한 상황에서만 콜백이 실행될 필요가 있는 경우에는 `:if` 옵션을 사용합니다. +특성 상황에서 콜백이 실행되어서는 안되는 경우에 `:unless` 옵션을 사용합니다. ### `:if`와 `:unless` 에서 심볼 사용하기 -`:if`와 `:unless` 옵션에 콜백 호출 직전에 호출되는 메소드(true, false 중 하나를 반환해야합니다)의 이름을 나타내는 심볼을 사용할 수 있습니다. `:if`의 경우 메소드가 false를 반환하면 콜백이 실행되지 않습니다. `:unless`를 사용하는 경우 메소드가 true를 반환하는 경우에 콜백이 실행되지 않습니다. 이것은 콜백에서 가장 많이 사용되는 방법입니다. 이렇게 여러개의 메소드를 등록하는 것으로 콜백을 호출하는 시점을 점검할 수 있습니다. +`:if`와 `:unless` 옵션에 콜백 호출 직전에 호출되는 메소드(true, false 중 하나를 +반환해야합니다)의 이름을 나타내는 심볼을 사용할 수 있습니다. `:if`의 경우 +메소드가 false를 반환하면 콜백이 실행되지 않습니다. `:unless`를 사용하는 경우 +메소드가 true를 반환하는 경우에 콜백이 실행되지 않습니다. 이것은 콜백에서 가장 +많이 사용되는 방법입니다. 이렇게 여러개의 메소드를 등록하는 것으로 콜백을 +호출하는 시점을 점검할 수 있습니다. ```ruby class Order < ActiveRecord::Base @@ -299,7 +322,9 @@ end ### `:if`와 `:unless` 에서 문자열 사용하기 -문자열을 사용할 수도 있습니다. 이 문자열은 나중에 `evel`로 평가되기 때문에 실행 가능한 올바른 Ruby 코드를 포함해야 합니다. 문자열이 포함된 조건이 충분히 짧은 경우에만 사용해주세요. +문자열을 사용할 수도 있습니다. 이 문자열은 나중에 `evel`로 평가되기 때문에 +실행 가능한 올바른 Ruby 코드를 포함해야 합니다. 문자열이 포함된 조건이 충분히 +짧은 경우에만 사용해주세요. ```ruby class Order < ActiveRecord::Base @@ -309,7 +334,8 @@ end ### `:if`와 `:unless`에서 `Proc`를 사용하기 -마지막으로 `:if`와 `:unless`에서 `Proc` 객체를 사용할 수도 있습니다. 이 옵션은 한줄 정도로 작성 가능한 함수를 검증에 사용하는 경우에 쓸만합니다. +마지막으로 `:if`와 `:unless`에서 `Proc` 객체를 사용할 수도 있습니다. 이 옵션은 +한줄 정도로 작성 가능한 함수를 검증에 사용하는 경우에 쓸만합니다. ```ruby class Order < ActiveRecord::Base @@ -394,7 +420,7 @@ end ```ruby class PictureFile < ActiveRecord::Base - after_commit :delete_picture_file_from_disk, on: [:destroy] + after_commit :delete_picture_file_from_disk, on: :destroy def delete_picture_file_from_disk if File.exist?(filepath) @@ -404,8 +430,12 @@ class PictureFile < ActiveRecord::Base end ``` -NOTE: 여기서 `:on` 옵션은 콜백이 호출되는 조건을 지정합니다. `:on` 옵션을 지정하지 않으면 모든 액션에서 콜백이 호출되게 됩니다. +NOTE: 여기서 `:on` 옵션은 콜백이 호출되는 조건을 지정합니다. `:on` 옵션을 +지정하지 않으면 모든 액션에서 콜백이 호출되게 됩니다. -WARNING: `after_commit` 콜백과 `after_rollback` 콜백은 1개의 트랜잭션에서 발생한 어떤 모델의 생성, 갱신, 삭제 뒤에 호출되는 것이 보증됩니다. 이 콜백들중 어떤 것 하나라도 예외가 발생시키더라도 다른 콜백에 영향을 주지 않습니다. 따라서 만약 직접 만든 콜백이 예외를 발생시킬 가능성이 있는 경우에는 자신의 콜백 내에서 rescue를 해서 적절한 예외 처리를 해야할 필요가 있습니다. +WARNING: `after_commit` 콜백과 `after_rollback` 콜백은 1개의 트랜잭션에서 +발생한 어떤 모델의 생성, 갱신, 삭제 뒤에 호출됩니다. 이 콜백들 중 어떤 것 +하나라도 예외를 발생시키면, 실행되지 않은 나머지 콜백들은 실행되지 않습니다. +따라서 만약 직접 만든 콜백이 예외를 발생시킬 가능성이 있는 경우에는 자신의 +콜백 내에서 rescue를 해서 적절한 예외 처리를 해야할 필요가 있습니다. -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/active_record_migrations.md b/guides/source/ko/active_record_migrations.md index ebe1712263ac4..4b7dcc4c934f4 100644 --- a/guides/source/ko/active_record_migrations.md +++ b/guides/source/ko/active_record_migrations.md @@ -8,7 +8,7 @@ Active Record Migrations * 마이그레이션 작성에 사용하는 제너레이터 * Active Record가 제공하는 데이터베이스 조작용 메소드 설명 -* 마이그레이션의 실행과 스키마 갱신용 Rake task 설명 +* 마이그레이션의 실행과 스키마 갱신용 bin/rails task 설명 * 마이그레이션과 스키마 파일(`schema.rb`)의 관계 -------------------------------------------------------------------------------- @@ -16,14 +16,22 @@ Active Record Migrations 마이그레이션의 개요 ------------------ -마이그레이션은 [데이터베이스 스키마의 계속적인 변경](http://en.wikipedia.org/wiki/Schema_migration) (영어)을 통합적이고 간단하게 수행하기 위한 방법입니다. 마이그레이션에서 Ruby의 DSL을 사용하고 있으므로 SQL문을 직접 작성할 필요가 없으며, 스키마와 스키마의 변경을 데이터베이스의 종류에 의존하지 않을 수 있습니다. +마이그레이션은 [데이터베이스 스키마의 계속적인 변경](http://en.wikipedia.org/wiki/Schema_migration)(영어)을 +통합적이고 간단하게 수행하기 위한 방법입니다. 마이그레이션에서 Ruby의 DSL을 +사용하고 있으므로 SQL문을 직접 작성할 필요가 없으며, 스키마와 스키마의 변경을 +데이터베이스의 종류에 의존하지 않을 수 있습니다. -하나 하나의 마이그레이션은 데이터베이스의 새로운 'version'이라고 볼 수 있습니다. 스키마는 처음 아무것도 없는 상태에서 시작해서, 마이그레이션에 의한 변경이 이루어질때마다 테이블,컬럼, 엔트리가 추가 또는 삭제됩니다. Active Record는 시간순에 따라서 스키마를 변경하는 방법을 알고 있으므로, 어느 시점으로부터든 최신 버전의 스키마로 갱신할 수 있습니다. Active Record는 `db/schema.rb` 파일을 갱신하고, 데이터베이스의 최신 구조와 일치하도록 만듭니다. +하나 하나의 마이그레이션은 데이터베이스의 새로운 'version'이라고 볼 수 +있습니다. 스키마는 처음 아무것도 없는 상태에서 시작해서, 마이그레이션에 의한 +변경이 이루어질때마다 테이블, 컬럼, 엔트리가 추가 또는 삭제됩니다. +Active Record는 시간순에 따라서 스키마를 변경하는 방법을 알고 있으므로, +어느 시점으로부터든 최신 버전의 스키마로 갱신할 수 있습니다. Active Record는 +`db/schema.rb` 파일을 갱신하고, 데이터베이스의 최신 구조와 일치하도록 만듭니다. 마이그레이션의 예를 하나 들어보겠습니다. ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name @@ -35,18 +43,30 @@ class CreateProducts < ActiveRecord::Migration end ``` -위의 마이그레이션을 실행하면 `products`라는 이름의 테이블이 추가됩니다. 이 안에는 `name`이라는 string 타입의 컬럼과 `description`이라는 text 타입의 컬럼이 포함되어 있습니다. 기본키는 `id`라는 이름으로 암묵적으로 추가됩니다. `id`는 Active Record 모델에서의 기본으로 설정된 기본키 이름입니다. `timestamps` 매크로는 `created_at`와 `updated_at`이라는 컬럼을 추가합니다. 이런 특수한 컬럼이 존재하는 경우, Active Record에 의해서 자동적으로 관리됩니다. +위의 마이그레이션을 실행하면 `products`라는 이름의 테이블이 추가됩니다. +이 안에는 `name`이라는 string 타입의 컬럼과 `description`이라는 text 타입의 +컬럼이 포함되어 있습니다. 기본키는 `id`라는 이름으로 암묵적으로 추가됩니다. +`id`는 Active Record 모델에서의 기본으로 설정된 기본키 이름입니다. +`timestamps` 매크로는 `created_at`와 `updated_at`이라는 컬럼을 추가합니다. +이런 특수한 컬럼이 존재하는 경우, Active Record에 의해서 자동적으로 관리됩니다. -마이그레이션이 새 버전에서 어떻게 변할지에 대한 동작을 정의하고 있다는 점에 주목해주세요. 마이그레이션을 실행하기 전에는 테이블이 존재하지 않습니다. 마이그레이션을 실행하면 테이블이 생성됩니다. Active Record는 이 마이그레이션의 진행을 역전시킬 방법을 알고 있습니다. 그래서 마이그레이션을 롤백하면 테이블이 삭제됩니다. +마이그레이션이 새 버전에서 어떻게 변할지에 대한 동작을 정의하고 있다는 점에 +주목해주세요. 마이그레이션을 실행하기 전에는 테이블이 존재하지 않습니다. +마이그레이션을 실행하면 테이블이 생성됩니다. Active Record는 이 마이그레이션의 +진행을 역전시킬 방법을 알고 있습니다. 그래서 마이그레이션을 롤백하면 테이블이 +삭제됩니다. -스키마 변경에 대한 명령에 대해서 데이터베이스 레벨에서 트랜잭션을 지원하는 경우, 마이그레이션은 트랜잭션의 내부에서 실행됩니다. 만약 지원되지 않는 경우, 마이그레이션 중에 일부가 실패한 경우 롤백할 수 없습니다. 그 경우에는 변경사항을 수동으로 롤백해야할 필요가 있습니다. +스키마 변경에 대한 명령에 대해서 데이터베이스 레벨에서 트랜잭션을 지원하는 +경우, 마이그레이션은 트랜잭션의 내부에서 실행됩니다. 만약 지원되지 않는 경우, +마이그레이션 중에 일부가 실패한 경우 롤백할 수 없습니다. 그 경우에는 변경사항을 +수동으로 롤백해야할 필요가 있습니다. NOTE: 몇몇 쿼리는 트랜잭션 하에서 실행할 수 없는 경우가 있습니다. 어댑터가 DDL 트랜잭션을 지원하고 있는 경우에는 `disable_ddl_transaction!`을 사용해서 단일 마이그레이션에서 트랜잭션을 무효화할 수 있습니다. Active Record가 되돌리는 방법을 알 수 없는 마이그레이션을 실행하고 싶은 경우에는 `reversible`을 사용할 수 있습니다. ```ruby -class ChangeProductsPrice < ActiveRecord::Migration +class ChangeProductsPrice < ActiveRecord::Migration[5.0] def change reversible do |dir| change_table :products do |t| @@ -61,7 +81,7 @@ end `change` 대신에 `up`과 `down`을 사용할 수도 있습니다. ```ruby -class ChangeProductsPrice < ActiveRecord::Migration +class ChangeProductsPrice < ActiveRecord::Migration[5.0] def up change_table :products do |t| t.change :price, :string @@ -92,7 +112,7 @@ $ bin/rails generate migration AddPartNumberToProducts 이 명령으로 생성되는 마이그레이션에는 실제 코드는 존재하지 않지만 적당한 이름은 붙여져 있습니다. ```ruby -class AddPartNumberToProducts < ActiveRecord::Migration +class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change end end @@ -107,7 +127,7 @@ $ bin/rails generate migration AddPartNumberToProducts part_number:string 위를 실행하면 다음과 같이 생성됩니다. ```ruby -class AddPartNumberToProducts < ActiveRecord::Migration +class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string end @@ -123,7 +143,7 @@ $ bin/rails generate migration AddPartNumberToProducts part_number:string:index 실행하면 아래의 마이그레이션이 생성됩니다. ```ruby -class AddPartNumberToProducts < ActiveRecord::Migration +class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string add_index :products, :part_number @@ -140,7 +160,7 @@ $ bin/rails generate migration RemovePartNumberFromProducts part_number:string 실행하면 다음처럼 생성됩니다. ```ruby -class RemovePartNumberFromProducts < ActiveRecord::Migration +class RemovePartNumberFromProducts < ActiveRecord::Migration[5.0] def change remove_column :products, :part_number, :string end @@ -156,7 +176,7 @@ $ bin/rails generate migration AddDetailsToProducts part_number:string price:dec 생성된 마이그레이션은 다음과 같습니다. ```ruby -class AddDetailsToProducts < ActiveRecord::Migration +class AddDetailsToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string add_column :products, :price, :decimal @@ -173,7 +193,7 @@ $ bin/rails generate migration CreateProducts name:string part_number:string 이를 실행하면 아래와 같은 마이그레이션이 생성됩니다. ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name @@ -194,14 +214,16 @@ $ bin/rails generate migration AddUserRefToProducts user:references 이를 실행하면 다음과 같은 마이그레이션이 생성됩니다. ```ruby -class AddUserRefToProducts < ActiveRecord::Migration +class AddUserRefToProducts < ActiveRecord::Migration[5.0] def change - add_reference :products, :user, index: true + add_reference :products, :user, foreign_key: true end end ``` -이 마이그레이션을 실행하면 `user_id` 컬럼이 추가되고, 적절한 인덱스가 추가됩니다. +이 마이그레이션을 실행하면 `user_id` 컬럼이 추가되고, 적절한 인덱스가 +추가됩니다. +`add_reference`의 다른 옵션에 대해서는 [API 문서](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_reference)를 참조해주세요. 이름의 일부에 `JoinTable`이 포함되어 있으면 테이블 조인을 생성할 수도 있습니다. @@ -212,7 +234,7 @@ $ bin/rails g migration CreateJoinTableCustomerProduct customer product 이를 실행하면 다음과 같은 마이그레이션을 생성합니다. ```ruby -class CreateJoinTableCustomerProduct < ActiveRecord::Migration +class CreateJoinTableCustomerProduct < ActiveRecord::Migration[5.0] def change create_join_table :customers, :products do |t| # t.index [:customer_id, :product_id] @@ -233,7 +255,7 @@ $ bin/rails generate model Product name:string description:text 다음과 같은 마이그레이션이 생성됩니다. ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name @@ -260,10 +282,10 @@ $ bin/rails generate migration AddDetailsToProducts 'price:decimal{5,2}' supplie 실행하면 아래와 같은 마이그레이션이 생성됩니다. ```ruby -class AddDetailsToProducts < ActiveRecord::Migration +class AddDetailsToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :price, :decimal, precision: 5, scale: 2 - add_reference :products, :supplier, polymorphic: true, index: true + add_reference :products, :supplier, polymorphic: true end end ``` @@ -295,17 +317,30 @@ create_table :products, options: "ENGINE=BLACKHOLE" do |t| end ``` -위의 마이그레이션에서는 테이블을 생성하는 SQL문에 `ENGINE=BLACKHOLE` 옵션을 추가하고 있습니다(MySQL을 사용하는 경우 기본값은 `ENGINE=InnoDB`입니다). +위의 마이그레이션에서는 테이블을 생성하는 SQL문에 `ENGINE=BLACKHOLE` 옵션을 +추가하고 있습니다(MySQL이나 MariaDB를 사용하는 경우 기본값은 +`ENGINE=InnoDB`입니다). + +그리고 `:comment` 옵션에 테이블에 대한 설명을 추가하고, 이를 데이터베이스에 +반영할 수 있습니다. 이는 Mysql Workbench나 PgAdmin III와 같은 데이터베이스 +관리 도구를 통해서 확인할 수 있습니다. 커다란 데이터베이스를 사용하는 +애플리케이션 마이그레이션에서 설명을 작성하기를 추천합니다. 이는 데이터 모델을 +이해하기 쉽게 만들 뿐 아니라 문서를 생성할 수도 있습니다. +현재 MySQL과 PostgreSQL 어댑터가 이 기능을 지원합니다. ### 테이블 조인을 추가하기 -마이그레이션의 `create_join_table` 메소드는 has_and_belongs_to_many(HABTM) 조인을 생성합니다. 일반적으로 아래와 같이 작성합니다. +마이그레이션의 `create_join_table` 메소드는 has_and_belongs_to_many(HABTM) +조인을 생성합니다. 일반적으로 아래와 같이 작성합니다. ```ruby create_join_table :products, :categories ``` -이에 의해서 `categories_products` 라는 테이블이 생성되며, 그 내부에 `category_id` 컬럼과 `product_id` 컬럼이 생성됩니다. 이 컬럼들에는 `:null` 옵션이 기본적으로 포함되어 있으며, 기본값은 `false`입니다. `column_options` 옵션을 사용하는 것으로 이 값을 덮어쓸 수 있습니다. +이에 의해서 `categories_products` 라는 테이블이 생성되며, 그 내부에 +`category_id` 컬럼과 `product_id` 컬럼이 생성됩니다. 이 컬럼들에는 `:null` +옵션이 기본적으로 포함되어 있으며, 기본값은 `false`입니다. `column_options` +옵션을 사용하는 것으로 이 값을 덮어쓸 수 있습니다. ```ruby create_join_table :products, :categories, column_options: {null: true} @@ -377,8 +412,12 @@ TIP: `change_column_null`는 `change_column`(그리고 `change_column_default`) * `null`은 컬럼에서 `NULL`의 사용을 허가, 또는 금지합니다. * `default`는 컬럼의 기본값을 정의할 수 있도록 합니다. date처럼 동적인 값을 사용하는 경우, 기본값은 초기값(마이그레이션이 실행된 날짜)으로 처리된다는 점에 주의해주세요. * `index` 는 컬럼에 인덱스를 추가합니다. +* `comment` 는 컬럼에 대한 설명을 추가합니다. + +몇몇 어댑터에서는 이외에도 사용가능한 옵션들이 존재합니다. 자세한 설명이 +필요하시면 각 어댑터의 API 문서를 참조해주세요. -몇몇 어댑터에서는 이외에도 사용가능한 옵션들이 존재합니다. 자세한 설명이 필요하시면 각 어댑터의 API 문서를 참조해주세요. +NOTE: `null`과 `default` 옵션은 커맨드 라인 명령을 통해서 추가할 수 없습니다. ### 외래키 @@ -447,7 +486,7 @@ Product.connection.execute('UPDATE `products` SET `price`=`free` WHERE 1') 마이그레이션이 복잡해지면, Active record가 마이그레이션을 롤백할 수 없는 경우가 생깁니다. `reversible` 메소드를 사용하는 것으로 마이그레이션을 적용할 때의 동작과, 롤백할 때의 동작을 지정할 수 있습니다. 예를 들자면 다음과 같은 식입니다. ```ruby -class ExampleMigration < ActiveRecord::Migration +class ExampleMigration < ActiveRecord::Migration[5.0] def change create_table :distributors do |t| t.string :zipcode @@ -476,16 +515,30 @@ class ExampleMigration < ActiveRecord::Migration end ``` -`reversible` 메소드를 사용하는 것으로, 각 명령을 올바른 순서대로 실행할 수 있게됩니다. 위의 마이그레이션 예제에서 롤백을 수행하는 경우 `down`블록은 반드시 `home_page_url` 컬럼이 삭제된 이후, 그리고 `distributors` 테이블이 삭제되기 이전에 실행됩니다. +`reversible` 메소드를 사용하는 것으로, 각 명령을 올바른 순서대로 실행할 수 있게 +됩니다. 위의 마이그레이션 예제에서 롤백을 수행하는 경우 `down`블록은 반드시 +`home_page_url` 컬럼이 삭제된 이후, 그리고 `distributors` 테이블이 삭제되기 +이전에 실행됩니다. -직접 생성한 마이그레이션에서 롤백을 해서는 안되는 경우임에도 불구하고, 롤백을 실행해서 데이터의 일부가 소실될 수도 있습니다. 그러한 경우에는 `down` 블록에서 `ActiveRecord::IrreversibleMigration`를 발생시키면 됩니다. 이렇게 하는 것으로 누군가가 나중에 마이그레이션 롤백을 호출한 경우, 에러를 통해 롤백을 실행할 수 없다는 것을 알려줄 수 있습니다. +직접 생성한 마이그레이션에서 롤백을 해서는 안되는 경우임에도 불구하고, +롤백을 실행해서 데이터의 일부가 소실될 수도 있습니다. 그러한 경우에는 `down` +블록에서 `ActiveRecord::IrreversibleMigration`를 발생시키면 됩니다. 이렇게 하는 +것으로 누군가가 나중에 마이그레이션 롤백을 호출한 경우, 에러를 통해 롤백을 +실행할 수 없다는 것을 알려줄 수 있습니다. ### `up`/`down` 메소드 사용하기 -`change` 대신에 종래의 `up`과 `down`을 사용할 수 있습니다. 이 때에는 `up` 메소드에는 어떻게 스키마를 변경할 지를 기술하고, `down` 메소드에는 `up` 메소드에 의해서 발생한 변경사항을 취소하는 방법을 기술할 필요가 있습니다. 다시 말해, `up` 뒤에 `down`을 실행하는 경우, 스키마가 이전과 동일한 상태를 유지할 수 있도록 해야합니다. 예를 들어, `up` 메소드에서 테이블을 추가했다면 `down` 메소드에서는 테이블을 삭제하면 됩니다. `down` 메소드에서 이루어지는 작업 순서는 `up` 메소드 내에서 이루어진 변경 순서의 정반대로 만드는 것이 좋습니다. 아까의 `reversible` 섹션의 예는 다음처럼 작성할 수 있습니다. +`change` 대신에 종래의 `up`과 `down`을 사용할 수 있습니다. 이 때에는 `up` +메소드에는 어떻게 스키마를 변경할 지를 기술하고, `down` 메소드에는 `up` +메소드에 의해서 발생한 변경사항을 취소하는 방법을 기술할 필요가 있습니다. +다시 말해, `up` 뒤에 `down`을 실행하는 경우, 스키마가 이전과 동일한 상태를 +유지할 수 있도록 해야합니다. 예를 들어, `up` 메소드에서 테이블을 추가했다면 +`down` 메소드에서는 테이블을 삭제하면 됩니다. `down` 메소드에서 이루어지는 작업 +순서는 `up` 메소드 내에서 이루어진 변경 순서의 정반대로 만드는 것이 좋습니다. +아까의 `reversible` 섹션의 예는 다음처럼 작성할 수 있습니다. ```ruby -class ExampleMigration < ActiveRecord::Migration +class ExampleMigration < ActiveRecord::Migration[5.0] def up create_table :distributors do |t| t.string :zipcode @@ -516,16 +569,20 @@ class ExampleMigration < ActiveRecord::Migration end ``` -마이그레이션에서 롤백이 불가능한 경우, `down` 메소드에는 `ActiveRecord::IrreversibleMigration`를 추가해둘 필요가 있습니다. 이렇게 해두는 것으로, 나중에 누군가가 마이그레이션을 롤백하는 경우에 실행불가능하다는 것을 알려줄 수 있습니다. +마이그레이션에서 롤백이 불가능한 경우, `down` 메소드에는 +`ActiveRecord::IrreversibleMigration`를 추가해둘 필요가 있습니다. 이렇게 해두는 +것으로, 나중에 누군가가 마이그레이션을 롤백하는 경우에 실행불가능하다는 것을 +알려줄 수 있습니다. ### 이전 마이그레이션을 롤백하기 -`revert` 메소드를 사용하는 것으로 Active Record의 마이그레이션 롤백 기능을 사용할 수 있습니다. +`revert` 메소드를 사용하는 것으로 Active Record의 마이그레이션 롤백 기능을 +사용할 수 있습니다. ```ruby require_relative '2012121212_example_migration' -class FixupExampleMigration < ActiveRecord::Migration +class FixupExampleMigration < ActiveRecord::Migration[5.0] def change revert ExampleMigration @@ -539,7 +596,7 @@ end `revert`는 블록도 받을 수 있습니다. 블록에서는 롤백을 위한 명령어 목록을 추가할 수 있습니다. 이것은 이전에 사용한 마이그레이션의 일부만을 롤백하고 싶을때에 유용합니다. 예를 들어서 `ExampleMigration`이 이미 적용되어있으며, 나중이 되어서야 우편번호를 검증하는 작업은 `CHECK` 제약보다 Active Record의 유효성검사를 먼저 하는 편이 좋다는 것을 발견한 상황이라고 가정해봅시다. ```ruby -class DontUseConstraintForZipcodeValidationMigration < ActiveRecord::Migration +class DontUseConstraintForZipcodeValidationMigration < ActiveRecord::Migration[5.0] def change revert do # copy-pasted code from ExampleMigration @@ -566,15 +623,22 @@ class DontUseConstraintForZipcodeValidationMigration < ActiveRecord::Migration end ``` -`revert`를 사용하지 않고 기존의 방식대로 직접 작성할 수도 있습니다만, 그 만큼 불필요한 노력이 더 들어갑니다(`create_table`와 `reversible`의 위치를 바꾸고, `create_table`을 `drop_table`로 변경하고, 마지막으로 `up`과 `down`을 바꿔야 합니다). +`revert`를 사용하지 않고 기존의 방식대로 직접 작성할 수도 있습니다만, 그만큼 +불필요한 노력이 더 들어갑니다(`create_table`와 `reversible`의 위치를 바꾸고, +`create_table`을 `drop_table`로 변경하고, 마지막으로 `up`과 `down`을 바꿔야 +합니다). `revert`는 이러한 작업을 간편하게 만들어줍니다. 마이그레이션을 실행하기 ------------------ -Rails에는 마이그레이션을 실행하기 위한 Rake task가 존재합니다. +Rails에는 마이그레이션을 실행하기 위한 bin/rails task가 존재합니다. -가장 편하게 마이그레이션을 실행하기 위한 Rake task는 대부분의 경우 `bin/rails db:migrate`일겁니다. 이 Rake 명령은 기본적으로 지금까지 실행된 적이 없는 `change` 또는 `up` 메소드를 실행합니다. 실행되지 않은 마이그레이션이 없는 경우에는 아무것도 하지 않고 종료합니다. 마이그레이션의 실행 순서는 마이그레이션의 타임스탬프에 의존합니다. +가장 편하게 마이그레이션을 실행하기 위한 bin/rails task는 대부분의 경우 +`bin/rails db:migrate`일 겁니다. 이 명령은 기본적으로 지금까지 실행된 적이 +없는 `change` 또는 `up` 메소드를 실행합니다. 실행되지 않은 마이그레이션이 없는 +경우에는 아무것도 하지 않고 종료합니다. 마이그레이션의 실행 순서는 +마이그레이션의 타임스탬프에 의존합니다. `db:migrate` 명령을 실행하면 `db:schema:dump` 작업도 동시에 호출된다는 점을 주의해주세요. 이 작업은 `db/schema.rb` 스키마 파일을 변경하고, 스키마가 데이터베이스의 구조와 일치하도록 만듭니다. @@ -602,37 +666,56 @@ $ bin/rails db:rollback STEP=3 이렇게 마지막에 실행한 3개의 마이그레이션을 롤백할 수 있습니다. -`db:migrate:redo`는 롤백과 마이그레이션을 동시에 실행할 수 있는 단축 명령입니다. 다수의 버전에 대해서 실행하고 싶은 경우에는 `db:rollback` 때와 마찬가지로 `STEP` 파라미터를 지정하면 됩니다. +`db:migrate:redo`는 롤백과 마이그레이션을 동시에 실행할 수 있는 단축 +명령입니다. 다수의 버전에 대해서 실행하고 싶은 경우에는 `db:rollback` 때와 +마찬가지로 `STEP` 파라미터를 지정하면 됩니다. ```bash $ bin/rails db:migrate:redo STEP=3 ``` -단 `db:migrate`로 실행할 수 없는 작업을 이 명령을 통해 실행할 수는 없습니다. 이는 단순히, 버전을 명시적으로 지정할 필요가 없도록 `db:migrate`를 쓰기 편하게 만든 것이기 때문입니다. +단 `db:migrate`로 실행할 수 없는 작업을 이 명령을 통해 실행할 수는 없습니다. +이는 단순히, 버전을 명시적으로 지정할 필요가 없도록 `db:migrate`를 쓰기 편하게 +만든 것이기 때문입니다. ### 데이터베이스 설정하기 -`bin/rails db:setup`은 데이터베이스의 생성, 스키마 읽기/쓰기, 초기 데이터(seed)를 사용해 데이터베이스의 초기화 등을 수행합니다. +`bin/rails db:setup`은 데이터베이스의 생성, 스키마 읽기/쓰기, 초기 +데이터(seed)를 사용해 데이터베이스의 초기화 등을 수행합니다. ### 데이터베이스 리셋하기 -`bin/rails db:reset`은 데이터베이스를 drop하고 재설정합니다. 이 명령은 `bin/rails db:drop db:setup`과 동등합니다. +`bin/rails db:reset`은 데이터베이스를 drop하고 재설정합니다. 이 명령은 +`bin/rails db:drop db:setup`과 동등합니다. -NOTE: 이 명령은 모든 마이그레이션을 실행하는 것과 동일하지 않습니다. 이 명령은 현재의 `schema.rb`의 내용을 그대로 다시 사용하기 때문입니다. 마이그레이션을 롤백할 수 없는 경우에는 `bin/rails db:reset`를 실행해도 복구할 수 없는 경우가 있습니다. 스키마 덤프에 대해서는 [스키마 덤프의 의의](#스키마_덤프의_의의)를 참조해주세요. +NOTE: 이 명령은 모든 마이그레이션을 실행하는 것과 동일하지 않습니다. 이 명령은 +현재의 `schema.rb`의 내용을 그대로 다시 사용하기 때문입니다. 마이그레이션을 +롤백할 수 없는 경우에는 `bin/rails db:reset`를 실행해도 복구할 수 없는 경우가 +있습니다. 스키마 덤프에 대해서는 [스키마 덤프의 의의](#스키마_덤프의_의의)를 +참조해주세요. ### 특정 마이그레이션만을 실행하기 -특정 마이그레이션의 up 또는 down 을 실행할 필요가 있는 경우에는 `db:migrate:up` 또는 `db:migrate:down`을 사용합니다. 아래에서처럼 적절한 버전 번호를 지정하는 것으로 해당하는 마이그레이션을 포함한 `change`, `up`, `down` 메소드를 호출할 수 있습니다. +특정 마이그레이션의 up 또는 down 을 실행할 필요가 있는 경우에는 `db:migrate:up` +또는 `db:migrate:down`을 사용합니다. 아래에서처럼 적절한 버전 번호를 지정하는 +것으로 해당하는 마이그레이션을 포함한 `change`, `up`, `down` 메소드를 호출할 +수 있습니다. ```bash $ bin/rails db:migrate:up VERSION=20080906120000 ``` -위를 실행하면 버전 번호가 20080906120000인 마이그레이선에 포함되어있는 `change`(또는 `up`)이 실행됩니다. 이 명령은 처음에 해당 마이그레이션이 적용된 상태인지를 체크하고, Active Record에 의해서 이미 실행되었다고 판단되면 아무것도 실행하지 않습니다. +위를 실행하면 버전 번호가 20080906120000인 마이그레이선에 포함되어있는 +`change`(또는 `up`)이 실행됩니다. 이 명령은 처음에 해당 마이그레이션이 적용된 +상태인지를 체크하고, Active Record에 의해서 이미 실행되었다고 판단되면 아무것도 +실행하지 않습니다. ### 다른 환경에서 마이그레이션을 실행하기 -기본적으로 `bin/rails db:migrate`는 `development` 환경에서 실행됩니다. 다른 환경에서 마이그레이션을 실행하고 싶은 경우에는 명령어를 실행할 때 `RAILS_ENV`라는 환경변수를 지정합니다. 예를 들어서 `test` 환경에서 마이그레이션을 실행하고 싶은 경우에는 아래와 같이 명령하면 됩니다. +기본적으로 `bin/rails db:migrate`는 `development` 환경에서 실행됩니다. 다른 +환경에서 마이그레이션을 실행하고 싶은 경우에는 명령어를 실행할 때 +`RAILS_ENV`라는 환경변수를 지정합니다. 예를 들어서 `test` 환경에서 +마이그레이션을 실행하고 싶은 경우에는 아래와 같이 명령하면 됩니다. ```bash $ bin/rails db:migrate RAILS_ENV=test @@ -640,7 +723,9 @@ $ bin/rails db:migrate RAILS_ENV=test ### 마이그레이션 실행 결과 출력값을 변경하기 -기본적으로 마이그레이션을 실행한 후에 실행된 내용과 각각의 소요 시간이 출력됩니다. 예를 들어 테이블 작성과 인덱스를 추가하는 마이그레이션을 실행하면 아래와 같이 출력됩니다. +기본적으로 마이그레이션을 실행한 후에 실행된 내용과 각각의 소요 시간이 +출력됩니다. 예를 들어 테이블 작성과 인덱스를 추가하는 마이그레이션을 실행하면 +아래와 같이 출력됩니다. ```bash == CreateProducts: migrating ================================================= @@ -660,7 +745,7 @@ $ bin/rails db:migrate RAILS_ENV=test 아래의 마이그레이션을 봐주세요. ```ruby -class CreateProducts < ActiveRecord::Migration +class CreateProducts < ActiveRecord::Migration[5.0] def change suppress_messages do create_table :products do |t| @@ -695,35 +780,71 @@ end == CreateProducts: migrated (10.0054s) ======================================= ``` -Active Record에서 아무것도 출력하고 싶지 않은 경우에는 `bin/rails db:migrate VERBOSE=false`를 실행하는 것으로 출력을 완전히 막을 수 있습니다. +Active Record에서 아무것도 출력하고 싶지 않은 경우에는 +`bin/rails db:migrate VERBOSE=false`를 실행하는 것으로 출력을 완전히 막을 수 +있습니다. 기존의 마이그레이션을 변경하기 ---------------------------- -마이그레이션을 직접 작성하다보면, 때때로 실수하는 경우가 있습니다. 이미 마이그레이션을 실행해버린 뒤라면 기존의 마이그레이션을 편집해서 다시 마이그레이션을 실행해도 의미가 없습니다. Rails는 마이그레이션이 이미 적용되었다고 생각하고 있으므로 `bin/rails db:migrate`를 실행해도 아무것도 변경되지 않습니다. 이러한 경우에는 마이그레이션을 일단 롤백(`bin/rails db:rollback` 등을 이용해서)하고 마이그레이션을 수정, 그리고 수정 완료된 버전을 실행하기 위해서 `bin/rails db:migrate`를 실행해야할 필요가 있습니다. - -무엇보다 기존의 마이그레이션을 직접 변경하는 것은 일반적으로 좋은 방법이 아닙니다. 기존의 마이그레이션을 변경하면, 자신 뿐 아니라, 함께 작업하는 사람들에게도 추가 작업을 강요하는 꼴이 되기 때문입니다. 또한 기존의 마이그레이션이 이미 실 배포환경에 적용되어있을 경우, 무척 골치아플 것입니다. 이런 경우에는 기존의 마이그레이션을 직접 수정하지 말고 이를 위한 마이그레이션을 새로 생성하고, 실행하는 것이 올바른 방법입니다. 또는 아직 버전 컨트롤 시스템에 반영되지 않은 마이그레이션을 편집하는 것이 가장 무난한 방법이라고 할 수 있습니다. - -`revert` 메소드는 이전에 마이그레이션 전체 또는 그 일부를 취소하기 위한 마이그레이션을 작성할 때에도 편리합니다(이미 언급한 [이전 마이그레이션을 롤백하기](#이전_마이그레이션을_롤백하기)를 참조하세요). +마이그레이션을 직접 작성하다보면, 때때로 실수하는 경우가 있습니다. 이미 +마이그레이션을 실행해버린 뒤라면 기존의 마이그레이션을 편집해서 다시 +마이그레이션을 실행해도 의미가 없습니다. Rails는 마이그레이션이 이미 +적용되었다고 생각하고 있으므로 `bin/rails db:migrate`를 실행해도 아무것도 +변경되지 않습니다. 이러한 경우에는 마이그레이션을 일단 +롤백(`bin/rails db:rollback` 등을 이용해서)하고 마이그레이션을 수정, 그리고 +수정 완료된 버전을 실행하기 위해서 `bin/rails db:migrate`를 실행해야할 필요가 +있습니다. + +무엇보다 기존의 마이그레이션을 직접 변경하는 것은 일반적으로 좋은 방법이 +아닙니다. 기존의 마이그레이션을 변경하면, 자신 뿐 아니라, 함께 작업하는 +사람들에게도 추가 작업을 강요하는 꼴이 되기 때문입니다. 또한 기존의 +마이그레이션이 이미 실 배포환경에 적용되어있을 경우, 무척 골치아플 것입니다. +이런 경우에는 기존의 마이그레이션을 직접 수정하지 말고 이를 위한 +마이그레이션을 새로 생성하고, 실행하는 것이 올바른 방법입니다. 또는 아직 버전 +컨트롤 시스템에 반영되지 않은 마이그레이션을 편집하는 것이 가장 무난한 +방법이라고 할 수 있습니다. + +`revert` 메소드는 이전에 마이그레이션 전체 또는 그 일부를 취소하기 위한 +마이그레이션을 작성할 때에도 편리합니다(이미 언급한 +[이전 마이그레이션을 롤백하기](#이전-마이그레이션을-롤백하기)를 참조하세요). 스키마 덤프의 의의 ---------------------- ### 스키마 파일의 의미 -Rails의 마이그레이션은 너무 강력해서, 데이터베이스 스키마를 생성하기 위한 믿을 수 있는 정보원으로 사용하기에는 적절치 않습니다. 스키마 정보는 `db.schema.rb`나 Active Record가 데이터베이스를 검사하는 것으로 생성된 SQL파일을 사용하게 됩니다. 이 파일들은 단순히 데이터베이스의 현재 상태를 나타내는 것으로, 개발자가 편집하는 파일이 아닙니다. - -애플리케이션의 새로운 인스턴스를 배포하는 경우에, 방대한 마이그레이션 이력을 모두 재실행할 필요는 없습니다. 오히려 그런 방식을 사용하면 에러가 발생하기 쉬워질 것입니다. 그 대신 현재의 스키마의 상태를 데이터베이스에게 알려주는 것이 간결하고 빠릅니다. - -예를 들어 Rails에서 test 환경용의 데이터베이스를 생성하는 방법을 설명합니다. 현재 development 데이터베이스를 `db/schema.rb`나 `db/structure.sql`로 덤프를 생성하고, 이어서 이 파일을 test 환경용의 데이터베이스에 그대로 적용합니다. - -스키마 파일은 Active Record 객체에 어떤 속성이 있는지 확인하기도 편리합니다. 모델은 스키마 정보를 가지고 있지 않습니다. 스키마 정보는 여러 마이그레이션 파일에 나누어져서 존재하고 있으며, 그대로는 무척 찾기 불편합니다만, 스키마 파일은 이 정보를 모아서 보관하고 있습니다. 또한 [annotate_models](https://github.com/ctran/annotate_models) 잼을 사용하면 모델 파일의 시작 부분에 스키마 정보를 요약해주는 주석을 자동적으로 추가, 갱신되므로 편리합니다. +Rails의 마이그레이션은 너무 강력해서, 데이터베이스 스키마를 생성하기 위한 +믿을 수 있는 정보원으로 사용하기에는 적절치 않습니다. 스키마 정보는 +`db.schema.rb`나 Active Record가 데이터베이스를 검사하는 것으로 생성된 +SQL파일을 사용하게 됩니다. 이 파일들은 단순히 데이터베이스의 현재 상태를 +나타내는 것으로, 개발자가 편집하는 파일이 아닙니다. + +애플리케이션의 새로운 인스턴스를 배포하는 경우에, 방대한 마이그레이션 이력을 +모두 재실행할 필요는 없습니다. 오히려 그런 방식을 사용하면 에러가 발생하기 +쉬워질 것입니다. 그 대신 현재의 스키마의 상태를 데이터베이스에게 알려주는 것이 +간결하고 빠릅니다. + +예를 들어 Rails에서 test 환경용의 데이터베이스를 생성하는 방법을 설명합니다. +현재 development 데이터베이스를 `db/schema.rb`나 `db/structure.sql`로 덤프를 +생성하고, 이어서 이 파일을 test 환경용의 데이터베이스에 그대로 적용합니다. + +스키마 파일은 Active Record 객체에 어떤 속성이 있는지 확인하기도 편리합니다. +모델은 스키마 정보를 가지고 있지 않습니다. 스키마 정보는 여러 마이그레이션 +파일에 나누어져서 존재하고 있으며, 그대로는 무척 찾기 불편합니다만, 스키마 +파일은 이 정보를 모아서 보관하고 있습니다. 또한 +[annotate_models](https://github.com/ctran/annotate_models) 잼을 사용하면 +모델 파일의 시작 부분에 스키마 정보를 요약해주는 주석을 자동적으로 추가, +갱신되므로 편리합니다. ### 스키마 덤프의 종류 -스키마의 덤프 방법으로는 2가지가 있습니다. 덤프 방법은 `config/application.rb`의 `config.active_record.schema_format`에서 `:sql` 또는 `:ruby`로 지정할 수 있습니다. +스키마의 덤프 방법으로는 2가지가 있습니다. 덤프 방법은 +`config/application.rb`의 `config.active_record.schema_format`에서 `:sql` +또는 `:ruby`로 지정할 수 있습니다. -`:ruby`로 지정하면, 스키마는 `db/schema.rb`에 저장됩니다. 이 파일을 열어보면 하나의 커다란 마이그레이션처럼 보일 것입니다. +`:ruby`로 지정하면, 스키마는 `db/schema.rb`에 저장됩니다. 이 파일을 열어보면 +하나의 커다란 마이그레이션처럼 보일 것입니다. ```ruby ActiveRecord::Schema.define(version: 20080906171750) do @@ -743,36 +864,68 @@ ActiveRecord::Schema.define(version: 20080906171750) do end ``` -이 스키마 정보는 보이는 것처럼 스키마의 내용을 단도직입적으로 나타내고 있습니다. 이 파일은 데이터베이스를 상세하게 확인하고 `create_table`이나 `add_index` 등을 이용해서 그 구조를 표현합니다. 이 스키마 정보는 데이터베이스의 종류에 의존하지 않으므로, Active Reord가 지원하는 데이터베이스라면 어떤 내용이라도 포함할 수 있습니다. 이 특성은 여러 종류의 데이터베이스를 실행할 수 있는 애플리케이션을 만들 필요가 있을 때 유용합니다. +이 스키마 정보는 보이는 것처럼 스키마의 내용을 단도직입적으로 나타내고 +있습니다. 이 파일은 데이터베이스를 상세하게 확인하고 `create_table`이나 +`add_index` 등을 이용해서 그 구조를 표현합니다. 이 스키마 정보는 데이터베이스의 +종류에 의존하지 않으므로, Active Record가 지원하는 데이터베이스라면 어떤 +내용이라도 포함할 수 있습니다. 이 특성은 여러 종류의 데이터베이스를 실행할 수 +있는 애플리케이션을 만들 필요가 있을 때 유용합니다. -이런 유용한 특징을 얻는 대신에, 한가지 단점이 있습니다. 당연하지만, `db/schema.rb`에서는 데이터베이스의 고유한 항목(트리거나, 프로시져 등)을 포함할 수 없습니다. 마이그레이션에는 커스텀 SQL을 포함할 수 있습니다만, 스키마를 덤프할 때에는 데이터베이스에서 구조를 재구성할 수 는 없기 때문입니다. 그러므로 데이터베이스 고유의 기능을 사용하려면 스키마의 포맷을 `:sql`로 설정할 필요가 있습니다. +이런 유용한 특징을 얻는 대신에, 한가지 단점이 있습니다. 당연하지만, +`db/schema.rb`에서는 데이터베이스의 고유한 항목(트리거나, 프로시져 등)을 +포함할 수 없습니다. 마이그레이션에는 커스텀 SQL을 포함할 수 있습니다만, +스키마를 덤프할 때에는 데이터베이스에서 구조를 재구성할 수 는 없기 때문입니다. +그러므로 데이터베이스 고유의 기능을 사용하려면 스키마의 포맷을 `:sql`로 설정할 +필요가 있습니다. -이 경우 Active Record의 스키마 덤프를 이용하는 대신, 데이터베이스 고유의 툴을 사용해서 `db/structure.sql`에 덤프합니다(`db:structure:dump` 명령어를 사용). 예를 들어서 PostgreSQL의 경우 `pg_dump` 유틸리티가 있습니다. MySQL의 경우는 `SHOW CREATE TABLE`의 출력 결과가 파일에 포함됩니다. +이 경우 Active Record의 스키마 덤프를 이용하는 대신, 데이터베이스 고유의 툴을 +사용해서 `db/structure.sql`에 덤프합니다(`db:structure:dump` 명령어를 사용). +예를 들어서 PostgreSQL의 경우 `pg_dump` 유틸리티가 있습니다. MySQL이나 +MariaDB의 경우는 `SHOW CREATE TABLE`의 출력 결과가 파일에 포함됩니다. -스키마를 읽어 들일 때에는 거기에 포함되는 SQL문을 실행하기만 합니다. 이에 의해서 데이터베이스의 구조의 완전한 사본을 생성할 수 있습니다. 그 대신, `:sql` 형식을 사용한 경우에는 그 스키마를 작성한 RDBMS 이외에는 사용할 수 없다는 제한사항도 생겨납니다. +스키마를 읽어 들일 때에는 거기에 포함되는 SQL문을 실행하기만 합니다. 이에 +의해서 데이터베이스의 구조의 완전한 사본을 생성할 수 있습니다. 그 대신, +`:sql` 형식을 사용한 경우에는 그 스키마를 작성한 RDBMS 이외에는 사용할 수 +없다는 제한사항도 생겨납니다. ### 스키마 덤프와 소스 코드 관리 -위에서 언급한 대로 스키마 덤프는 데이터베이스 스키마에서 정보를 가져오기 때문에 신뢰할 수 있습니다. 따라서 스키마 파일을 Git 등의 버전 관리 하에 두기를 강하게 추천합니다. +위에서 언급한 대로 스키마 덤프는 데이터베이스 스키마에서 정보를 가져오기 때문에 +신뢰할 수 있습니다. 따라서 스키마 파일을 Git 등의 버전 관리 하에 두기를 강하게 +추천합니다. -`db/schema.rb`에는 데이터베이스의 현재 버전 번호가 포함되어있습니다. 이를 통해 다른 브랜치에서 스키마가 변경되어있었던 경우에도, 양자를 병합할 때 경고가 발생한다는 장점도 있습니다. 충돌이 발생했을 경우에는 수동으로 번호가 큰 버전을 남겨둘 필요가 있습니다. +`db/schema.rb`에는 데이터베이스의 현재 버전 번호가 포함되어있습니다. 이를 통해 +다른 브랜치에서 스키마가 변경되어있었던 경우에도, 양자를 병합할 때 경고가 +발생한다는 장점도 있습니다. 충돌이 발생했을 경우에는 수동으로 번호가 큰 버전을 +남겨둘 필요가 있습니다. Active Record의 참조정합성 --------------------------------------- -Active Record는 영리하게 동작해야하는 것은 모델이지, 데이터베이스가 아니라는 컨셉에 기초하고 있습니다. 그리고 실제로 트리거나 제약 같은 고도의 데이터베이스 기능은 그렇게 많이 사용되지 않습니다. +Active Record는 영리하게 동작해야하는 것은 모델이지, 데이터베이스가 아니라는 +컨셉에 기초하고 있습니다. 그리고 실제로 트리거나 제약 같은 고도의 데이터베이스 +기능은 그렇게 많이 사용되지 않습니다. -`validates :foreign_key, uniqueness: true`같은 데이터베이스 검증기능은 데이터 정합성을 모델이 처리하고 있는 한가지 예시입니다. 모델의 관계 설정시에 `:dependent` 옵션을 지정하면 부모 객체가 삭제되었을 경우에, 자식 객체도 자동적으로 삭제됩니다. 애플리케이션 레벨에서 실행되는 다른 것들과 마찬가지로 이런 모델의 기능만으로 참조정합성을 유지할 수 없기 때문에, 데이터베이스의 [외래키 제약](#외래키)을 사용해서 참조 정합성을 확보하는 개발자도 있습니다. +`validates :foreign_key, uniqueness: true`같은 데이터베이스 검증기능은 데이터 +정합성을 모델이 처리하고 있는 한가지 예시입니다. 모델의 관계 설정시에 +`:dependent` 옵션을 지정하면 부모 객체가 삭제되었을 경우에, 자식 객체도 +자동으로 삭제됩니다. 애플리케이션 레벨에서 실행되는 다른 것들과 마찬가지로 +이런 모델의 기능만으로 참조정합성을 유지할 수 없기 때문에, 데이터베이스의 +[외래키 제약](#외래키)을 사용해서 참조 정합성을 확보하는 개발자도 있습니다. -Active Record만으로 이런 외부 기능을 전부 제공할 수는 없습니다만, `execute` 메소드를 사용해서 임의의 SQL을 실행할 수 있습니다. +Active Record만으로 이런 외부 기능을 전부 제공할 수는 없습니다만, `execute` +메소드를 사용해서 임의의 SQL을 실행할 수 있습니다. 마이그레이션과 Seed 데이터 ------------------------ -데이터베이스에 데이터를 추가할 때에 마이그레이션이 사용되는 경우도 있습니다. +Rails 마이그레이션은 일관적인 스키마 변경 방식을 제공하려는 목적이 있습니다. +마이그레이션은 데이터를 추가하거나 수정할 때에도 사용할 수 있습니다. 이는 +실제 환경의 데이터베이스처럼 삭제하거나 재생성해서는 안되는 데이터베이스에서 +유용하게 사용됩니다. ```ruby -class AddInitialProducts < ActiveRecord::Migration +class AddInitialProducts < ActiveRecord::Migration[5.0] def up 5.times do |i| Product.create(name: "Product ##{i}", description: "A product.") @@ -785,7 +938,9 @@ class AddInitialProducts < ActiveRecord::Migration end ``` -하지만 Rails에는 초기 데이터를 데이터베이스에 주기 위한 Seed 기능이 있습니다. `db/seeds.rb` 파일에 약간의 루비 코드를 추가하고 `bin/rails db:seed`를 실행하기만 하면 됩니다. +그런데 Rails에는 초기 데이터를 데이터베이스에 주기 위한 Seed 기능이 있습니다. +`db/seeds.rb` 파일에 약간의 루비 코드를 추가하고 `bin/rails db:seed`를 +실행하기만 하면 됩니다. ```ruby 5.times do |i| @@ -793,6 +948,6 @@ end end ``` -이 방법이라면, 마이그레이션을 사용하는 것보다 깔끔하게 새 애플리케이션의 데이터베이스를 설정할 수 있습니다. +이 방법이라면, 마이그레이션을 사용하는 것보다 깔끔하게 새 애플리케이션의 +데이터베이스를 설정할 수 있습니다. -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/active_record_querying.md b/guides/source/ko/active_record_querying.md index 62208f770b776..c9e7ab977138f 100644 --- a/guides/source/ko/active_record_querying.md +++ b/guides/source/ko/active_record_querying.md @@ -10,13 +10,17 @@ Active Record Query Interface * 검색된 레코드의 순서, 가져오고 싶은 속성, 그룹 등을 설정하기 * eager loading을 사용해서 데이터를 가져올 때 필요한 쿼리 실행횟수를 줄이기 * 동적 검색 메소드를 사용하기 +* 여러 Active Record 메소드를 사용하기 위해서 메소드를 연결해 사용하기 * 어떤 레코드가 존재하는지 확인하기 * Active Record 모델에서 계산하기 * EXPLAIN 사용하기 -------------------------------------------------------------------------------- -SQL을 사용해서 데이터베이스의 레코드를 검색하는 것에 익숙해진 사람이 Rails를 배우게 되면, 같은 작업을 무척 세련된 방식으로 실현한다는 점을 느낄 수 있을 겁니다. Active Record를 사용하게 되면 SQL을 직접 실행할 필요가 거의 없어집니다. +SQL을 사용해서 데이터베이스의 레코드를 검색하는 것에 익숙해진 사람이 Rails를 +배우게 되면, 같은 작업을 무척 세련된 방식으로 실현한다는 점을 느낄 수 있을 +겁니다. Active Record를 사용하게 되면 SQL을 직접 실행할 필요가 거의 +없어집니다. 이 가이드의 예제에서는 아래의 모델을 사용합니다. @@ -48,16 +52,21 @@ class Role < ActiveRecord::Base end ``` -Active Record는 사용자를 대신해서 데이터베이스에 대한 쿼리를 전송합니다. 전송되는 쿼리는 많은 데이터베이스 시스템(MySQL, PostgreSQL, SQLite 등)과 호환성이 있습니다. Active Record를 이용하면, 사용하고 있는 데이터베이스 시스템의 종류에 관계 없이 같은 방식을 쓸 수 있습니다. +Active Record는 사용자를 대신해서 데이터베이스에 대한 쿼리를 전송합니다. +전송되는 쿼리는 많은 데이터베이스 시스템(MySQL, MariaDB, PostgreSQL, SQLite +등)과 호환성이 있습니다. Active Record를 이용하면, 사용하고 있는 데이터베이스 +시스템의 종류에 관계 없이 같은 방식을 쓸 수 있습니다. 데이터베이스에서 객체 가져오기 ------------------------------------ -Active Record에서는 데이터베이스에서 객체를 가져오기 위해 다양한 검색 메소드를 제공하고 있습니다. 이러한 검색 메소드를 사용해서 SQL을 직접 작성할 필요 없이 데이터베이스에 전송할 쿼리를 만들 수 있습니다. +Active Record에서는 데이터베이스에서 객체를 가져오기 위해 다양한 검색 메소드를 +제공하고 있습니다. 이러한 검색 메소드를 사용해서 SQL을 직접 작성할 필요 없이 +데이터베이스에 전송할 쿼리를 만들 수 있습니다. Active Record에서는 아래의 메소드를 지원합니다. -* `bind` +* `find` * `create_with` * `distinct` * `eager_load` @@ -78,12 +87,13 @@ Active Record에서는 아래의 메소드를 지원합니다. * `reorder` * `reverse_order` * `select` -* `uniq` * `where` -위의 메소드는 전부 `ActiveRecord::Relation` 인스턴스를 반환합니다. +`where`나 `group`과 같은 검색 메소드들은 `ActiveRecord::Relation`의 인스턴스로 +컬렉션을 반환합니다. `find`나 `first`와 같은 단일 개체를 검색하는 메소드는 +모델 객체를 곧장 반환힙니다. -`Model.find(options)`의 동작을 간단하게 요약해보면 아래와 같습니다. +`find(options)`의 동작을 간단하게 요약해보면 아래와 같습니다. * 주어진 옵션을 등가의 SQL 쿼리로 변환합니다. * SQL 쿼리를 실행하고, 결과를 데이터베이스에서 가져옵니다. @@ -94,12 +104,13 @@ Active Record에서는 아래의 메소드를 지원합니다. Active Record에는 하나의 객체를 가져오기 위한 여러가지 방법이 준비되어 있습니다. -#### 기본키를 사용하기 +#### `find` -`Model.find(primary_key)`를 사용하면 주어진 조건에 맞는 _기본키_를 가지는 객체를 가져올 수 있습니다. +`find` 메소드를 사용하면 주어진 조건에 맞는 _기본키_를 가지는 객체를 가져올 수 +있습니다. ```ruby -# Find the client with primary key (id) 10. +# 기본키(id)가 10인 클라이언트를 찾습니다. client = Client.find(10) # => # ``` @@ -110,119 +121,131 @@ client = Client.find(10) SELECT * FROM clients WHERE (clients.id = 10) LIMIT 1 ``` -`Model.find(primary_key)`에서 적당한 레코드를 발견하지 못했을 경우 `ActiveRecord::RecordNotFound` 예외가 발생합니다. +`find`에서 적당한 레코드를 발견하지 못했을 경우 +`ActiveRecord::RecordNotFound` 예외가 발생합니다. -#### `take` - -`Model.take`는 레코드를 하나 가져옵니다. 어떤 레코드를 가져올지는 지정하지 않습니다. +이 메소드로도 여러 객체에 대한 쿼리를 작성할 수 있습니다. 기본키의 배열 객체와 +함께 `find` 메소드를 호출하세요. 요청한 _기본키_에 맞는 객체들의 배열이 +반환됩니다. 예를 들자면, ```ruby -client = Client.take -# => # +# 기본키가 1이거나 10인 클라이언트를 찾는다. +client = Client.find([1, 10]) # Or even Client.find(1, 10) +# => [#, #] ``` 이것과 등가인 SQL은 아래와 같습니다. ```sql -SELECT * FROM clients LIMIT 1 +SELECT * FROM clients WHERE (clients.id IN (1,10)) ``` -모델에 레코드가 하나도 없는 경우에는 `nil`을 반환합니다. 이 때, 예외는 발생하지 않습니다. +WARNING: `find` 메소드는 주어진 기본키 배열 중에서 단 하나도 만족하는 객체를 +발견하지 못할 경우에 `ActiveRecord::RecordNotFound` 에러를 던집니다. -TIP: 이 메소드가 어떤 레코드를 돌려줄지는 사용하는 데이터베이스 엔진에 따라 다를 수 있습니다. - -#### `first` +#### `take` -`Model.first`는 기본키로 오름차순을 하고, 첫번째 레코드를 가져옵니다. +`take` 메소드는 레코드를 하나 가져옵니다. 어떤 레코드를 가져올지는 지정하지 +않습니다. ```ruby -client = Client.first +client = Client.take # => # ``` 이것과 등가인 SQL은 아래와 같습니다. -```sql -SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1 +```sql +SELECT * FROM clients LIMIT 1 ``` -모델에 레코드가 하나도 없는 경우에는 `nil`을 반환합니다. 이 때, 예외는 발생하지 않습니다. +모델에 레코드가 하나도 없는 경우에는 `nil`을 반환합니다. 이 때, 예외는 +발생하지 않습니다. -#### `last` - -`Model.last`는 기본키로 내림차순을 하고, 첫번째 레코드를 가져옵니다. +`take` 메소드에 숫자를 넘겨서 그 숫자만큼의 레코드를 가져올 수도 있습니다. ```ruby -client = Client.last -# => # +client = Client.take(2) +# => [ +# #, +# # +# ] ``` 이것과 등가인 SQL은 아래와 같습니다. ```sql -SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 +SELECT * FROM clients LIMIT 2 ``` -모델에 레코드가 하나도 없는 경우에는 `nil`을 반환합니다. 이 때, 예외는 발생하지 않습니다. +`take!` 메소드는 `take`와 동일하게 동작합니다만, 조건에 만족하는 객체가 +존재하지 않는 경우에 `ActiveRecord::RecordNotFound` 에러를 던진다는 점이 +다릅니다. -#### `find_by` +TIP: 이 메소드가 어떤 레코드를 돌려줄지는 사용하는 데이터베이스 엔진에 따라 +다를 수 있습니다. + +#### `first` -`Model.find_by`는 주어진 조건에 맞는 레코드 중 첫번째를 반환합니다. +`first` 메소드는 기본키로 오름차순을 하고, 첫번째 레코드를 가져옵니다. ```ruby -Client.find_by first_name: 'Lifo' +client = Client.first # => # - -Client.find_by first_name: 'Jon' -# => nil ``` -위의 명령은 아래와 같이 작성할 수도 있습니다. +이것과 등가인 SQL은 아래와 같습니다. -```ruby -Client.where(first_name: 'Lifo').take +```sql +SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1 ``` -#### `take!` +모델에 레코드가 하나도 없는 경우에는 `nil`을 반환합니다. 이 때, 예외는 발생하지 않습니다. + +만약 [기본 스코프](#기본-스코프를-사용하기)가 정렬 메소드를 포함하고 있는 경우, +`first`는 이 순서를 기준으로 첫 번째 레코드를 가져옵니다. -`Model.take!`는 레코드를 하나 가져옵니다. +`first` 메소드에 숫자를 넘겨서 해당하는 갯수만큼의 레코드를 가져올 수도 있습니다. ```ruby -client = Client.take! -# => # +client = Client.first(3) +# => [ +# #, +# #, +# # +# ] ``` 이것과 등가인 SQL은 아래와 같습니다. ```sql -SELECT * FROM clients LIMIT 1 +SELECT * FROM clients ORDER BY clients.id ASC LIMIT 3 ``` -`Model.take!`에서 적당한 레코드를 발견하지 못한 경우, `ActiveRecord::RecordNotFound` 예외가 발생합니다. - -#### `first!` - -`Model.first!`는 기본키로 오름차순을 하고, 첫번째 레코드를 가져옵니다. +`order`를 사용하는 컬렉션에 대해서 `first` 는 해당 순서를 사용하여 첫번째 +레코드를 가져옵니다. ```ruby -client = Client.first! -# => # +client = Client.order(:first_name).first +# => # ``` 이것과 등가인 SQL은 아래와 같습니다. -```sql -SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1 +```sql +SELECT * FROM clients ORDER BY clients.first_name ASC LIMIT 1 ``` -`Model.first!`에서 적당한 레코드를 발견하지 못한 경우, `ActiveRecord::RecordNotFound` 예외가 발생합니다. +`first!` 메소드는 `first` 메소드와 동일하게 동작합니다만, 조건에 만족하는 +레코드를 찾지 못할 경우에 `ActiveRecord::RecordNotFound` 에러를 발생시킨다는 +점이 다릅니다. -#### `last!` +#### `last` -`Model.last!`는 기본키로 내림차순을 하고, 첫번째 레코드를 가져옵니다. +`last` 메소드는 기본키로 내림차순을 하고, 마지막 레코드를 가져옵니다. ```ruby -client = Client.last! +client = Client.last # => # ``` @@ -232,97 +255,87 @@ client = Client.last! SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 ``` -`Model.last!`에서 적당한 레코드를 발견하지 못한 경우, `ActiveRecord::RecordNotFound` 예외가 발생합니다. +모델에 레코드가 하나도 없는 경우에는 `nil`을 반환합니다. 이 때, 예외는 발생하지 않습니다. -#### `find_by!` +만약 [기본 스코프](#기본-스코프를-사용하기)가 정렬 메소드를 포함하고 있는 경우, +`last`는 이 순서를 기준으로 첫 번째 레코드를 가져옵니다. -`Model.find_by!`는 주어진 조건에 맞는 레코드 중 첫번째를 반환합니다. 적당한 레코드를 발견하지 못한 경우 `ActiveRecord::RecordNotFound` 예외가 발생합니다. +`last` 메소드에 숫자를 넘겨서 해당하는 갯수만큼의 레코드를 가져올 수도 있습니다. ```ruby -Client.find_by! first_name: 'Lifo' -# => # - -Client.find_by! first_name: 'Jon' -# => ActiveRecord::RecordNotFound +client = Client.last(3) +# => [ +# #, +# #, +# # +# ] ``` -위의 명령은 아래와 같은 방식으로도 표현할 수 있습니다. +이것과 등가인 SQL은 아래와 같습니다. -```ruby -Client.where(first_name: 'Lifo').take! +```sql +SELECT * FROM clients ORDER BY clients.id DESC LIMIT 3 ``` -### 여러 개의 객체를 가져오기 - -#### 여럭 개의 기본키를 사용하기 - -`Model.find()`는 _기본키_의 배열을 받아서 주어진 _기본키_를 가지는 레코드들의 배열을 돌려줍니다. +`order`를 사용하는 컬렉션에 대해서 `last` 는 해당 순서를 사용하여 첫번째 +레코드를 가져옵니다. ```ruby -# Find the clients with primary keys 1 and 10. -client = Client.find([1, 10]) # Or even Client.find(1, 10) -# => [#, #] +client = Client.order(:first_name).last +# => # ``` 이것과 등가인 SQL은 아래와 같습니다. ```sql -SELECT * FROM clients WHERE (clients.id IN (1,10)) +SELECT * FROM clients ORDER BY clients.first_name DESC LIMIT 1 ``` -WARNING: `Model.find(array_of_primary_key)`는 주어진 기본키 중 하나라도 일치하는 레코드를 발견하지 못하면 `ActiveRecord::RecordNotFound` 예외를 발생시킵니다. +`last!` 메소드는 `last` 메소드와 동일하게 동작합니다만, 조건에 만족하는 +레코드를 찾지 못할 경우에 `ActiveRecord::RecordNotFound` 에러를 발생시킨다는 +점이 다릅니다. -#### take +#### `find_by` -`Model.take(limit)`는 `limit`에 지정된 갯수만큼의 레코드를 반환합니다. 꺼내는 순서는 지정하지 않습니다. +`find_by`는 주어진 조건에 맞는 레코드 중 첫 번째를 반환합니다. ```ruby -Client.take(2) -# => [#, - #] -``` - -이것과 등가인 SQL은 아래와 같습니다. +Client.find_by first_name: 'Lifo' +# => # -```sql -SELECT * FROM clients LIMIT 2 +Client.find_by first_name: 'Jon' +# => nil ``` -#### first - -`Model.first(limit)`는 기본키를 오름차순으로 `limit`에서 지정된 갯수만큼의 레코드를 반환합니다. +위의 명령은 아래와 같이 작성할 수도 있습니다. ```ruby -Client.first(2) -# => [#, - #] +Client.where(first_name: 'Lifo').take ``` 이것과 등가인 SQL은 아래와 같습니다. ```sql -SELECT * FROM clients ORDER BY id ASC LIMIT 2 +SELECT * FROM clients WHERE (clients.first_name = 'Lifo') LIMIT 1 ``` -#### last - -`Model.last(limit)`는 기본키를 내림차순으로 `limit`에서 지정된 갯수만큼의 레코드를 반환합니다. +`find_by!`는 주어진 조건에 맞는 레코드 중 첫번째를 반환합니다. 적당한 레코드를 발견하지 못한 경우 `ActiveRecord::RecordNotFound` 예외가 발생합니다. ```ruby -Client.last(2) -# => [#, - #] +Client.find_by! first_name: 'does not exist' +# => ActiveRecord::RecordNotFound ``` -이것과 등가인 SQL은 아래와 같습니다. +위의 명령은 아래와 같은 방식으로도 표현할 수 있습니다. -```sql -SELECT * FROM clients ORDER BY id DESC LIMIT 2 +```ruby +Client.where(first_name: 'does not exist').take! ``` ### 여러 개의 객체를 배치로 가져오기 -다수의 레코드를 반복 처리 하고 싶은 경우가 있습니다. 예를 들어 많은 유저들에게 뉴스레터를 전송하고 싶거나, 데이터를 내보내거나, 하는 경우입니다. +다수의 레코드를 반복 처리 하고 싶은 경우가 있습니다. 예를 들어 많은 유저들에게 뉴스레터를 전송하고 +싶거나, 데이터를 내보내거나, 하는 경우입니다. 이런 처리를 그대로 구현한다고 하면 아래와 같이 될 겁니다. @@ -333,15 +346,27 @@ User.all.each do |user| end ``` -그러나 위와 같은 처리는 테이블의 크기가 커질수록 현실적이지 않은 코드가 됩니다. `User.all.each`는 Active Record에 대해서 _테이블 전체_를 한번에 꺼내오고, 심지어 매 레코드마다 객체를 생성한 뒤, 그 객체들이 저장된 배열을 메모리에 보관하기 때문입니다. 만약 막대한 양의 레코드에 대해서 이러한 작업을 하려고 하면 코드는 메모리의 용량 부족으로 제대로 동작하지 않을 것입니다. +그러나 위와 같은 처리는 테이블의 크기가 커질수록 현실적이지 않은 코드가 됩니다. `User.all.each`는 +Active Record에 대해서 _테이블 전체_를 한번에 꺼내오고, 심지어 매 레코드마다 객체를 생성한 뒤, +그 객체들이 저장된 배열을 메모리에 보관하기 때문입니다. 만약 막대한 양의 레코드에 대해서 이러한 +작업을 하려고 하면 코드는 메모리의 용량 부족으로 제대로 동작하지 않을 것입니다. -Rails에서는 이러한 작업을 메모리를 압박하지 않는 크기의 배치 작업으로 분할해서 처리하는 방법을 2가지 제공하고 있습니다. 첫번째는 `find_each` 메소드를 사용하는 방법입니다. 이것은 레코드 뭉치를 하나씩 꺼내서 _각_ 레코드를 하나의 모델로 생성하고 넘긴 블록을 yield 합니다. 두번째 방법은 `find_in_batches` 메소드를 사용하는 방법입니다. 레코드 뭉치를 하나씩 꺼내서 _배치 전체_를 모델 배열로 만들어서 블록에 yield합니다. +Rails에서는 이러한 작업을 메모리를 압박하지 않는 크기의 배치 작업으로 분할해서 처리하는 방법을 +2가지 제공하고 있습니다. 첫번째는 `find_each` 메소드를 사용하는 방법입니다. 이것은 레코드 뭉치를 +하나씩 꺼내서 _각_ 레코드를 하나의 모델로 생성하고 넘긴 블록을 yield 합니다. 두번째 방법은 +`find_in_batches` 메소드를 사용하는 방법입니다. 레코드 뭉치를 하나씩 꺼내서 _배치 전체_를 모델 +배열로 만들어서 블록에 yield합니다. -TIP: `find_each` 메소드와 `find_in_batches` 메소드는 한번에 메모리에 올릴 수 없을 정도의 대량의 레코드를 순차 처리하기 위한 방법입니다. 수천개의 레코드에 대해서 단순한 반복 처리를 하는 경우라면 기존의 검색 메소드로도 충분합니다. +TIP: `find_each` 메소드와 `find_in_batches` 메소드는 한번에 메모리에 올릴 수 없을 정도의 +대량의 레코드를 순차 처리하기 위한 방법입니다. 수천 개의 레코드에 대해서 단순한 반복 처리를 하는 +경우라면 기존의 검색 메소드로도 충분합니다. #### `find_each` -`find_each` 메소드는 레코드 뭉치를 하나 꺼내서, _각_ 레코드를 하나씩 객체로 만들어 개별적으로 블록의 yield를 호출합니다. 아래의 예제에서는 `find_each`에서 1000건의 레코드를 꺼냅니다. 이 숫자는 `find_each`와 `find_in_batches`에서 기본적으로 사용되는 값이며, 이어서 각 모델에 대해서 개별적으로 yield를 호출합니다. 이 작업은 모든 레코드가 처리될 때까지 반복됩니다. +`find_each` 메소드는 레코드 뭉치를 하나 꺼내서, _각_ 레코드를 하나씩 객체로 만들어 개별적으로 +블록의 yield를 호출합니다. 아래의 예제에서는 `find_each`에서 1000건의 레코드를 꺼냅니다. +이 숫자는 `find_each`와 `find_in_batches`에서 기본적으로 사용되는 값이며, 이어서 각 모델에 +대해서 개별적으로 yield를 호출합니다. ```ruby User.find_each do |user| @@ -349,11 +374,23 @@ User.find_each do |user| end ``` -##### `find_each`의 옵션 +필요한 데이터를 다 가져와서 처리할 때까지 이 반복 작업이 계속됩니다. -`find_each`는 기존의 `find` 메소드와 같은 옵션을 사용할 수 있습니다. 단, `:order`의 `:limit`는 `find_each` 내부에서 쓰기 위해서 예약되어 있으므로 사용할 수 없습니다. +`find_each`는 위에서 볼 수 있듯 모델 클래스와도 동작하지만, 관계와 함께 사용할 수도 있습니다. -기존의 옵션 이외에도 `:batch_size`와 `:start`도 사용할 수 있습니다. +```ruby +User.where(weekly_subscriber: true).find_each do |user| + NewsMailer.weekly(user).deliver_now +end +``` + +정렬 순서가 없는 한, 이 메소드는 내부적으로 순서를 정하기 위해서 강제적으로 정렬을 수행합니다. + +수신한 객체가 정렬 순서 여부에 따른 처리는 `config.active_record.error_on_ignored_order`에 +따라서 결정됩니다. 만약 true라면 `ArgumentError`가 발생하며, 그렇지 않다면 정렬 순서 정보가 +무시되고 경고가 발생합니다. 이는 아래에서 설명할 `:error_on_ignore` 옵션으로 무시할 수 있습니다. + +##### `find_each`의 옵션 **`:batch_size`** @@ -361,45 +398,79 @@ end ```ruby User.find_each(batch_size: 5000) do |user| - NewsLetter.weekly_deliver(user) + NewsMailer.weekly(user).deliver_now end ``` **`:start`** -기본적으로 레코드는 기본키의 오름차순대로 가져오게 됩니다. 기본키는 정수이어야 합니다. 시작 시점의 몇몇 ID가 필요하지 않은 경우 `:start`를 사용해서 시퀀스의 시작 ID를 지정할 수 있습니다. 이 옵션은 중단된 배치작업을 재개하는 경우 등에 유용합니다. +기본적으로 레코드는 기본키의 오름차순으로 가져오게 됩니다. 기본키는 정수이어야 합니다. 시작 시점의 +몇몇 ID가 필요하지 않은 경우 `:start`를 사용해서 시퀀스의 시작 ID를 지정할 수 있습니다. 이 +옵션은 중단된 배치작업을 재개하는 경우 등에 유용합니다. -예를 들어 1회의 작업에서 5000건을 가져오고, 기본키가 2000 이상인 사용자들에게만 뉴스 레터를 보내고 싶은 경우, 다음과 같이 작성합니다. +예를 들어 기본키가 2000 이상인 사용자들에게만 뉴스 레터를 보내고 싶은 경우, 다음과 같이 작성합니다. ```ruby -User.find_each(start: 2000, batch_size: 5000) do |user| - NewsLetter.weekly_deliver(user) +User.find_each(start: 2000) do |user| + NewsMailer.weekly(user).deliver_now end ``` -이외에도 같은 처리를 여러 곳에서 분산해서 작업하는 경우를 생각할 수 있습니다. `start` 옵션을 적절하게 사용해서, 각 처리 장소에서 10000개의 레코드씩을 처리하도록 만들 수도 있을겁니다. +**`:finish`** + +`:start` 옵션과 비슷하게 `:finish`는 배치로 처리할 마지막 레코드의 ID를 지정합니다. 이는 +`:start`부터 `:finish` 사이에 있는 레코드에 대해서만 배치 처리를 하고 싶은 경우에 유용합니다. + +예를 들어 기본키가 2000부터 10000까지인 사용자들에게만 뉴스 레터를 보내고 싶은 경우, 다음과 같이 +작성합니다. + +```ruby +User.find_each(start: 2000, finish: 10000) do |user| + NewsMailer.weekly(user).deliver_now +end +``` + +이외에도 같은 처리를 여러 곳에서 분산해서 작업하는 경우를 생각할 수 있습니다. `start`와 `:finish` +옵션을 적절하게 사용해서, 각 처리 장소에서 10000개의 레코드씩을 처리하도록 만들 수도 있을 겁니다. + +**`:error_on_ignore`** + +관계에 정렬 순서가 지정되어 있는 경우 에러를 던질지 여부를 결정하는 애플리케이션 설정을 덮어씁니다. + #### `find_in_batches` -`find_in_batches` 메소드는 레코드를 뭉치로 꺼내는 점은 `find_each`와 닮아 있습니다. 다른 점은 `find_in_batches`는 _뭉치_에서 모델을 각각 꺼내서 처리하는 것이 아닌 모델의 배열로서 블록을 yield한다는 점입니다. 아래의 예제에서는 주어진 블록에 대해서 한번에 1000개의 인보이스 배열을 yield합니다. 마지막의 배열에서는 1000건씩 처리하고 남은 인보이스가 포함됩니다. +`find_in_batches` 메소드는 레코드를 뭉치로 꺼내는 점은 `find_each`와 닮아 있습니다. 다른 +점은 `find_in_batches`는 _뭉치_에서 모델을 각각 꺼내서 처리하는 것이 아닌 모델의 배열로서 +블록을 yield한다는 점입니다. 아래의 예제에서는 주어진 블록에 대해서 한번에 1000개의 인보이스 +배열을 yield합니다. 마지막의 배열에서는 1000건씩 처리하고 남은 인보이스가 포함됩니다. ```ruby # 1회에 add_invoices에 인보이스가 1000건이 들어있는 배열을 넘긴다. -Invoice.find_in_batches(include: :invoice_lines) do |invoices| +Invoice.find_in_batches do |invoices| export.add_invoices(invoices) end ``` -NOTE: `:include` 옵션을 사용하면 모델과 함께 가져올 관계 모델을 지정할 수 있습니다. +`find_in_batches`는 위에서 볼 수 있듯 모델 클래스와도 동작하지만, +관계와 함께 사용할 수도 있습니다. + +```ruby +Invoice.pending.find_in_batches do |invoice| + pending_invoices_export.add_invoices(invoices) +end +``` + +정렬 순서가 없는 한, 이 메소드는 내부적으로 순서를 정하기 위해서 강제적으로 정렬을 수행합니다. ##### `find_in_batches`의 옵션 -`find_in_batches`에서는 `find_each`과 마찬가지로 기존의 `find` 메소드에서 사용하던 옵션에 `:batch_size`나 `:start` 옵션을 사용할 수 있습니다. `:order`와 `:limit`는 `find_in_batches` 내부에서 사용하기 위해 예약되어있으므로 사용할 수 없습니다. +`find_in_batches`는 `find_each`와 동일한 옵션을 사용할 수 있습니다. 조건 ---------- -`where`는 반환되는 레코드를 필터링하기 위한 조건을 지정합니다. SQL문에서의 `WHERE`의 부분에 해당합니다. 조건은 문자열, 배열, 해시 중의 하나를 이용해서 지정할 수 있습니다. +`where`는 반환되는 레코드를 필터링하기 위한 조건을 지정합니다. SQL문에서의 `WHERE`의 부분에 해당합니다. 조건은 문자열, 배열, 해시 중 하나를 이용해서 지정할 수 있습니다. ### 문자열만을 사용하기 @@ -409,13 +480,15 @@ WARNING: 조건을 문자열만으로 구성하게 되면 SQL 주입 취약성 ### 배열을 사용하기 -조건에서 사용하는 값이 변경될 가능성이 있는 경우, 인수를 어떻게 넘기면 좋을까요? 이 경우는 아래와 같이 쓸 수 있습니다. +조건에서 사용하는 값이 변경될 가능성이 있는 경우, 인수를 어떻게 넘기면 좋을까요? 이 경우는 아래와 +같이 쓸 수 있습니다. ```ruby Client.where("orders_count = ?", params[:orders]) ``` -Active Record는 첫번째 인자를 확인하고, 그 뒤에 추가 인자가 있다면, 첫번째 인자에 있는 물음표`(?)`를 추가 인자로 대체합니다. +Active Record는 첫번째 인자를 확인하고, 그 뒤에 추가 인자가 있다면, 첫번째 인자에 있는 +물음표`(?)`를 추가 인자로 대체합니다. 여러 개의 조건을 지정하고 싶은 경우에는 아래와 같이 쓰면 됩니다. @@ -423,25 +496,29 @@ Active Record는 첫번째 인자를 확인하고, 그 뒤에 추가 인자가 Client.where("orders_count = ? AND locked = ?", params[:orders], false) ``` -이 예시에서, 첫번째 물음표는 `params[:orders]`로 대체되고, 두번째 물음표는 `false`를 SQL형식으로 변환된 값(변환 방식은 어댑터마다 다릅니다)으로 대체됩니다. +이 예시에서, 첫번째 물음표는 `params[:orders]`로 대체되고, 두번째 물음표는 `false`를 +SQL형식으로 변환된 값(변환 방식은 어댑터마다 다릅니다)으로 대체됩니다. -아래와 같은 작성 방식을 추천합니다. +이와 같은 방법 대신, ```ruby -Client.where("orders_count = ?", params[:orders]) +Client.where("orders_count = #{params[:orders]}") ``` -아래와 같은 방식은 위험하며, 사용하지 않기를 권장합니다. +아래와 같은 작성 방식을 추천합니다. ```ruby -Client.where("orders_count = #{params[:orders]}") +Client.where("orders_count = ?", params[:orders]) ``` -조건 문자열에 변수를 직접 대입하면, 그 변수는 데이터베이스에 **그대로** 넘어가게 됩니다. 이것은 악의가 있는 인물이 필터링되지 않은 위험한 변수를 넘길 수 있게 만듭니다. 나아가서 악의가 있는 인물이 데이터베이스를 마음대로 조작할 수 있게 되어 데이터베이스 전체가 위험에 빠질 수도 있습니다. 그러므로 조건문자열에 변수를 그대로 대입하지 말아주세요. +조건 문자열에 변수를 직접 대입하면, 그 변수는 데이터베이스에 **그대로** 넘어가게 됩니다. 이는 +악의가 있는 인물이 필터링되지 않은 위험한 변수를 넘길 수 있게 만듭니다. 나아가서 악의가 있는 인물이 +데이터베이스를 마음대로 조작할 수 있게 되어 데이터베이스 전체가 위험에 빠질 수도 있습니다. 그러므로 +조건문자열에 변수를 그대로 대입하지 말아주세요. -TIP: SQL 주입 취약성에 대해서는 [Rails 보안 가이드](security.html#sqlインジェクション)를 참조해주세요. +TIP: SQL 주입 취약성에 대해서는 [Rails 보안 가이드](security.html#sql-인젝션)를 참조해주세요. -#### 플레이스홀더를 사용하기 +#### 플레이스홀더 사용하기 물음표`(?)`를 인수로 대체하는 것과 마찬가지로, 배열을 통해 키/값 해시를 지정할 수 있습니다. @@ -454,23 +531,32 @@ Client.where("created_at >= :start_date AND created_at <= :end_date", ### 해시를 사용하기 -Active Record에서는 조건을 해시로 넘길수도 있습니다. 이 방식을 사용하는 것으로 조건부분의 가독성을 향상시킬 수 있습니다. 조건을 해시로 넘기는 경우, 해시의 키에는 조건을 주고 싶은 필드명을, 값에는 그 필드가 어떤 조건을 가지는 지를 지정할 수 있습니다. +Active Record에서는 조건을 해시로 넘길수도 있습니다. 이 방식을 사용하는 것으로 조건 부분의 +가독성을 향상시킬 수 있습니다. 조건을 해시로 넘기는 경우, 해시의 키에는 조건을 주고 싶은 필드명을, +값에는 그 필드가 어떤 조건을 가지는 지를 지정할 수 있습니다. -NOTE: 해시에 의한 조건은 등가, 범위, 서브셋만 사용할 수 있습니다. +NOTE: 해시에 의한 조건은 동치, 범위, 서브셋만 사용할 수 있습니다. -#### 등가 조건 +#### 동치 조건 ```ruby Client.where(locked: true) ``` +이는 다음과 같은 쿼리를 생성합니다. + +```sql +SELECT * FROM clients WHERE (clients.locked = 1) +``` + 필드명은 문자열을 사용할 수도 있습니다. ```ruby Client.where('locked' => true) ``` -belongs_to 관계의 경우, Active Record 객체가 값으로 사용되고 있다면, 외래키를 모델을 식별하기 위한 용도로 사용할 수 있습니다. 이 방법은 다형 관계에서도 사용할 수 있습니다. +belongs_to 관계의 경우, Active Record 객체가 값으로 사용되고 있다면, 외래키를 모델을 +식별하기 위한 용도로 사용할 수 있습니다. 이 방법은 다형 관계에서도 사용할 수 있습니다. ```ruby Post.where(author: author) @@ -491,7 +577,7 @@ Client.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight) SELECT * FROM clients WHERE (clients.created_at BETWEEN '2008-12-21 00:00:00' AND '2008-12-22 00:00:00') ``` -[조건에서 배열을 사용하기](#배열을_사용하기)에서 더 간결한 문법을 소개하고 있습니다. +[조건에서 배열을 사용하기](#배열을-사용하기)에서 더 간결한 문법을 소개하고 있습니다. #### 서브셋 조건 @@ -560,15 +646,19 @@ Client.order("orders_count ASC", "created_at DESC") Client.order("orders_count ASC").order("created_at DESC") # SELECT * FROM clients ORDER BY orders_count ASC, created_at DESC ``` +WARNING: 만약 **MySQL 5.7.5** 이상에서 `order` 메소드와 함께 `select`, `pluck`, +`ids`와 같은 메소드를 사용하고, 정렬시에 사용하는 메소드가 선택 목록에 포함되어 있지 않다면 +`ActiveRecord::StatementInvalid` 에러가 발생할 수 있습니다. 다음 절에서 결과 셋으로부터 필드를 선택하는 방법을 확인하세요. 특정 필드만을 가져오기 ------------------------- -기본적으로 `Model.find`를 실행하면 결과에서 모든 필드를 가져옵니다. 내부적으로는 `select *`이 실행됩니다. +기본적으로 `Model.find`를 실행하면 결과에서 모든 필드를 가져옵니다. 내부적으로는 +`select *`이 실행됩니다. 결과에서 특정 필드만을 가져오고 싶은 경우, `select` 메소드를 사용할 수 있습니다. -예를 들어 `viewable_by`컬럼과 `locked`컬럼만을 가져오고 싶은 경우, 다음처럼 할 수 있습니다. +예를 들어 `viewable_by` 컬럼과 `locked` 컬럼만을 가져오고 싶은 경우 다음처럼 작성합니다. ```ruby Client.select("viewable_by, locked") @@ -580,13 +670,17 @@ Client.select("viewable_by, locked") SELECT viewable_by, locked FROM clients ``` -select를 사용하면 선택된 필드만을 사용해서 모델 객체가 초기화되기 때문에 주의해주세요. 모델 객체가 초기화 될 때에 지정하지 않았던 필드로 접근하려고 하면 아래와 같은 메시지가 나타납니다. +select를 사용하면 선택된 필드만을 사용해서 모델 객체가 초기화되기 때문에 주의해주세요. 모델 +객체가 초기화 될 때에 지정하지 않았던 필드로 접근하려고 하면 아래와 같은 메시지가 나타납니다. ```bash ActiveModel::MissingAttributeError: missing attribute: <속성명> ``` -`<속성명>`은 접근하려고 했던 속성입니다. `id` 메소드는 이 `ActiveRecord::MissingAttributeError`가 발생하지 않습니다. 관계가 정상적으로 동작하기 위해서는 `id` 메소드가 필요하기 때문에, 관계 모델을 사용하는 경우에는 주의해주세요. +`<속성명>`은 접근하려고 했던 속성입니다. +`id` 메소드는 `ActiveRecord::MissingAttributeError`가 발생하지 않습니다. 관계가 +정상적으로 동작하기 위해서는 `id` 메소드가 필요하기 때문에, 관계 모델을 사용하는 경우에는 +주의해주세요. 특정 필드에 대해서 중복이 없는 레코드만을 가져오고 싶은 경우, `distinct`를 사용할 수 있습니다. @@ -613,7 +707,7 @@ query.distinct(false) Limit와 Offset ---------------- -`Model.find`로 실행되는 SQL에 `LIMIT`를 적용하고 싶은 경우 `limit` 메소드와 `offset` 메소드를 사용하는 것으로 `LIMIT`를 지정할 수 있습니다. +`find`로 실행되는 SQL에 `LIMIT`를 적용하고 싶은 경우 `limit` 메소드와 `offset` 메소드를 사용하는 것으로 `LIMIT`를 지정할 수 있습니다. `limit` 메소드는 가져올 레코드 갯수의 상한을 지정합니다. `offset`는 레코드를 반환하기 전에 무시할 레코드의 갯수를 지정합니다. @@ -633,7 +727,7 @@ SELECT * FROM clients LIMIT 5 Client.limit(5).offset(30) ``` -이 코드는 처음 30개의 클라이언트를 무시하고 31번째부터 최대 5명의 클라이언트를 반환합니다. 이 때의 SQL은 아래와 같습니다. +이 코드는 31번째부터 최대 5명의 클라이언트를 반환합니다. 이 때의 SQL은 아래와 같습니다. ```sql SELECT * FROM clients LIMIT 5 OFFSET 30 @@ -642,9 +736,11 @@ SELECT * FROM clients LIMIT 5 OFFSET 30 그룹 ----- -검색 메소드에서 실행되는 SQL에 `GROUP BY`를 추가하고 싶은 경우에는 `group` 메소드를 사용할 수 있습니다. +검색 메소드에서 실행되는 SQL에 `GROUP BY`를 추가하고 싶은 경우에는 `group` 메소드를 사용할 +수 있습니다. -예를 들어, 주문(order)의 생성일별로 분류된 컬렉션을 가져오고 싶은 경우에는 다음과 같이 할 수 있습니다. +예를 들어, 주문(order)의 생성일 별로 분류된 컬렉션을 가져오고 싶은 경우에는 다음과 같이 할 +수 있습니다. ```ruby Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)") @@ -660,10 +756,29 @@ FROM orders GROUP BY date(created_at) ``` +### 묶은 아이템들의 갯수 + +단일 쿼리로 특정 그룹에 있는 아이템의 갯수를 가져오고 싶다면 `group` 뒤에서 `count`를 +호출해주세요. + +```ruby +Order.group(:status).count +# => { 'awaiting_approval' => 7, 'paid' => 12 } +``` + +이 쿼리는 다음과 같은 SQL을 생성합니다. + +```sql +SELECT COUNT (*) AS count_all, status AS status +FROM "orders" +GROUP BY status +``` + Having ------ -SQL에서는 `GROUP BY` 필드에서 조건을 지정할 경우에 `HAVING`을 사용합니다. 검색 메소드에서 `:having`를 사용하면 `Model.find`에서 `HAVING`을 추가할 수 있습니다. +SQL에서는 `GROUP BY` 필드에서 조건을 지정할 경우에 `HAVING`을 사용합니다. 검색 메소드에서 +`:having`를 사용하면 `Model.find`에서 `HAVING`을 추가할 수 있습니다. ```ruby Order.select("date(created_at) as ordered_date, sum(price) as total_price"). @@ -709,7 +824,7 @@ Post.where(id: 10, trashed: false).unscope(where: :id) # SELECT "posts".* FROM "posts" WHERE trashed = 0 ``` -관계에 대해서 `unscope`를 호출하게 되면, 거기에 머지되는 모든 관계에 영향을 줍니다. +관계에 대해서 `unscope`를 호출하게 되면, 거기에 병합되는 모든 관계에 영향을 줍니다. ```ruby Post.order('id asc').merge(Post.unscope(:order)) @@ -740,8 +855,6 @@ SELECT "posts".* FROM "posts" WHERE (id > 10) ORDER BY id desc LIMIT 20 ```ruby class Post < ActiveRecord::Base - .. - .. has_many :comments, -> { order('posted_at DESC') } end @@ -752,12 +865,14 @@ Post.find(10).comments.reorder('name') ```sql SELECT * FROM posts WHERE id = 10 ORDER BY name +SELECT * FROM comments WHERE article_id = 10 ORDER BY name ``` `reorder`를 호출하지 않았을 경우에 실행되는 SQL은 아래와 같습니다. ```sql SELECT * FROM posts WHERE id = 10 ORDER BY posted_at DESC +SELECT * FROM comments WHERE article_id = 10 ORDER BY posted_at DESC ``` ### `reverse_order` @@ -817,7 +932,7 @@ SELECT * FROM posts WHERE `trashed` = 1 AND `trashed` = 0 Null 관계 ------------- -`none` 메소드는 연쇄가 가능한 관계(ActiveRecord::Relation)를 돌려줍니다(레코드를 돌려주지 않습니다). 이 메소드에게 받은 관계에 어떤 조건을 연결하더라도, 항상 빈 관계가 생성됩니다. 이는 메소드나 스코프에 연쇄(chain) 가능한 응답이 필요하고, 결과를 돌려주고 싶지 않은 경우에 편리합니다. +`none` 메소드는 연쇄가 가능한 관계를 돌려줍니다(레코드를 돌려주지 않습니다). 이 메소드에게 받은 관계에 어떤 조건을 연결하더라도, 항상 빈 관계가 생성됩니다. 이는 메소드나 스코프에 연쇄(chain) 가능한 응답이 필요하고, 결과를 돌려주고 싶지 않은 경우에 편리합니다. ```ruby Post.none # 빈 관계를 돌려주며, 쿼리를 생성하지 않습니다. @@ -859,16 +974,21 @@ client.save Active Record에서는 2가지의 잠금 기능이 있습니다. -* 낙관적 잠금 (optimistic) -* 비관적 잠금 (pessimistic) +* 낙관적(optimistic) 잠금 +* 비관적(pessimistic) 잠금 -### 낙관적 잠금 (optimistic) +### 낙관적(optimistic) 잠금 -낙관적 잠금은 여러 명의 사용자가 같은 레코드를 편집할 수 있도록 하고, 데이터의 충돌은 최소한으로 발생한다고 가정합니다. 이 방법에서는 레코드가 공개된 뒤로 변경된 적이 있는지를 확인합니다. 만약 변경이 있었다면 `ActiveRecord::StaleObjectError`를 발생시킵니다. +낙관적 잠금은 여러 명의 사용자가 같은 레코드를 편집할 수 있도록 하고, 데이터의 충돌은 최소한으로 +발생한다고 가정합니다. 이 방법에서는 레코드가 공개된 뒤로 변경된 적이 있는지를 확인합니다. 만약 +변경이 있었다면 `ActiveRecord::StaleObjectError`를 발생시킵니다. **낙관적 잠금 컬럼** -낙관적 잠금을 사용하기 위해서는 테이블에 `lock_version`이라는 이름의 integer형 컬럼이 있어야 합니다. Active Record는 레코드가 변경될 때마다 `lock_version`의 값을 1씩 증가시킵니다. 변경 요청이 발생했을 때의 `lock_version`의 값이 데이터베이스 상의 `lock_version`보다 적은 경우, 변경 요청은 실패하며, `ActiveRecord::StaleObjectError` 에러를 발생시킵니다. 예를 들어, +낙관적 잠금을 사용하기 위해서는 테이블에 `lock_version`이라는 이름의 integer형 컬럼이 있어야 +합니다. Active Record는 레코드가 변경될 때마다 `lock_version`의 값을 1씩 증가시킵니다. 변경 +요청이 발생했을 때의 `lock_version`의 값이 데이터베이스 상의 `lock_version`보다 적은 경우, +변경 요청은 실패하며, `ActiveRecord::StaleObjectError` 에러를 발생시킵니다. 예를 들어, ```ruby c1 = Client.find(1) @@ -878,14 +998,17 @@ c1.first_name = "Michael" c1.save c2.name = "should fail" -c2.save # ActiveRecord::StaleObjectErrorを発生 +c2.save # ActiveRecord::StaleObjectError를 발생 ``` -예외가 발생한 후, 이 예외를 처리하여 충돌을 해결해야 합니다. 충돌의 해결 방법으로는 롤백, 병합, 또는 비지니스 로직에 알맞는 해결 방식 등을 사용해서 처리해주세요. +예외가 발생한 후, 이 예외를 처리하여 충돌을 해결해야 합니다. 충돌의 해결 방법으로는 롤백, 병합, +또는 비지니스 로직에 알맞는 해결 방식 등을 사용해서 처리해주세요. -`ActiveRecord::Base.lock_optimistically = false`을 설정하면 이 잠금을 비활성화할 수 있습니다. +`ActiveRecord::Base.lock_optimistically = false`을 설정하면 이 잠금을 비활성화할 수 +있습니다. -`ActiveRecord::Base`에는 `lock_version` 컬럼명을 명시적으로 지정하기 위한 `locking_column`가 있습니다. +`ActiveRecord::Base`에는 `lock_version` 컬럼명을 명시적으로 지정하기 위한 +`locking_column`가 있습니다. ```ruby class Client < ActiveRecord::Base @@ -893,7 +1016,7 @@ class Client < ActiveRecord::Base end ``` -### 비관적 잠금 +### 비관적(pessimistic) 잠금 비관적 잠금에서는 데이터베이스가 존재하는 잠금 기능을 사용합니다. 관계를 구축할 때에 `lock`을 사용하면, 선택한 행에 대해 배타적 잠금을 수행합니다. `lock`를 사용하는 관계는 데드락 조건을 회피하기 위해서 트랜잭션으로 처리됩니다. 예를 들어, @@ -923,12 +1046,12 @@ Item.transaction do end ``` -모델 인스턴스가 이미 있는 경우, 트랜잭션을 시작하며 그 내부 인스턴스들의 잠금을 일괄저으로 처리합니다. +모델 객체가 이미 있는 경우, 트랜잭션을 시작하며 그 내부 객체들의 잠금을 일괄저으로 처리합니다. ```ruby item = Item.first item.with_lock do - # 이 블록은 트랜잭션 내부에서 호출된다 + # 이 블록은 트랜잭션 내부에서 호출된다. # item은 이미 잠긴 상태 item.increment!(:views) end @@ -937,52 +1060,57 @@ end 테이블 조인하기 -------------- -Active Record에는 SQL에서의 `JOIN`을 사용할 수 있게 해주는 `joins` 메소드가 있습니다. `joins`에는 다양한 사용 방법이 있습니다. +Active Record는 `JOIN`을 사용할 수 있게 해주는 `joins`와 `left_outer_joins` 메소드가 +있습니다. `joins`는 `INNER JOIN`이나 커스텀 쿼리에서 사용되는 반면, `left_outer_joins`는 +`LEFT OUTER JOIN`을 쓰고 싶은 경우에 사용합니다. -### SQL 조각을 사용하기 +### `joins` + +`joins`에는 다양한 사용 방법이 있습니다. + +#### SQL 조각을 사용하기 `joins`의 인수로 SQL을 넘겨서 `JOIN`을 사용할 수 있습니다. ```ruby -Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id = clients.id') +Author.joins("INNER JOIN posts ON posts.author_id = authors.id AND posts.published = 't'") ``` 이 코드는 아래와 같은 SQL을 생성합니다. ```sql -SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id = clients.id +SELECT authors.* FROM authors INNER JOIN posts ON posts.author_id = authors.id AND posts.published = 't' ``` -### 레일즈의 관계 배열/해시를 사용하기 +#### Rails의 관계 배열/해시를 사용하기 -WARNING: 이 메소드는 `INNER JOIN`에서만 사용할 수 있습니다. +Active Record에서는 `joins` 메소드를 사용해서 `JOIN`을 설정할 때에 모델에 정의된 +[관계](association_basics.html)의 이름을 사용할 수 있습니다. -Active Record에서는 `joins` 메소드를 사용해서 `JOIN`을 설정할 때에 모델에 정의된 [관계](association_basics.html)의 이름을 사용할 수 있습니다. - -예를 들어, 아래의 `Category`, `Post`, `Comment`, `Guest`, `Tag` 모델이 있다고 해봅시다. +예를 들어, 아래의 `Category`, `Article`, `Comment`, `Guest`, `Tag` 모델이 있다고 해봅시다. ```ruby -class Category < ActiveRecord::Base - has_many :posts +class Category < ApplicationRecord + has_many :articles end -class Post < ActiveRecord::Base +class Article < ApplicationRecord belongs_to :category has_many :comments has_many :tags end -class Comment < ActiveRecord::Base - belongs_to :post +class Comment < ApplicationRecord + belongs_to :article has_one :guest end -class Guest < ActiveRecord::Base +class Guest < ApplicationRecord belongs_to :comment end -class Tag < ActiveRecord::Base - belongs_to :post +class Tag < ApplicationRecord + belongs_to :article end ``` @@ -991,45 +1119,48 @@ end #### 단일 관계 조인하기 ```ruby -Category.joins(:posts) +Category.joins(:articles) ``` -이는 아래와 같은 SQL을 실행합니다. +이는 아래와 같은 SQL을 생성합니다. ```sql SELECT categories.* FROM categories - INNER JOIN posts ON posts.category_id = categories.id + INNER JOIN articles ON articles.category_id = categories.id ``` -이 SQL을 우리말로 적으면, 'Post에 있는 모든 카테고리를 포함하는 Category 객체를 하나 반환해'가 됩니다. 또한 같은 카테고리에 여러개의 Post가 있는 경우, 카테고리가 중복됩니다. 중복되지 않는 카테고리 목록이 필요한 경우에는 `Category.joins(:posts).uniq`를 사용할 수 있습니다. +이 SQL을 우리말로 적으면, 'Article에 있는 모든 카테고리를 포함하는 Category 객체를 하나 +반환해'가 됩니다. 또한 같은 카테고리에 여러개의 Post가 있는 경우, 카테고리가 중복됩니다. 중복되지 +않는 카테고리 목록이 필요한 경우에는 `Category.joins(:articles).distinct`를 사용할 수 +있습니다. #### 여러 개의 관계를 조인하기 ```ruby -Post.joins(:category, :comments) +Article.joins(:category, :comments) ``` 이 코드에 의해서 아래와 같은 SQL이 실행됩니다. ```sql -SELECT posts.* FROM posts - INNER JOIN categories ON posts.category_id = categories.id - INNER JOIN comments ON comments.post_id = posts.id +SELECT articles.* FROM articles + INNER JOIN categories ON articles.category_id = categories.id + INNER JOIN comments ON comments.article_id = articles.id ``` -이 SQL을 우리말로 적으면, '카테고리가 하나 있고, 덧글이 적어도 하나 존재하는 모든 Post를 반환해'가 됩니다. 이쪽도 덧글이 여러개 있는 경우에는 중복해서 나타나게 됩니다. +이 SQL을 우리말로 적으면, '카테고리가 하나 있고, 덧글이 적어도 하나 존재하는 모든 Article을 반환해'가 됩니다. 이쪽도 덧글이 여러개 있는 경우에는 중복해서 나타나게 됩니다. #### 중첩(Nested)된 관계를 조인하기(한 단계) ```ruby -Post.joins(comments: :guest) +Article.joins(comments: :guest) ``` 이 명령으로 아래와 같은 SQL이 생성됩니다. ```sql -SELECT posts.* FROM posts - INNER JOIN comments ON comments.post_id = posts.id +SELECT articles.* FROM articles + INNER JOIN comments ON comments.article_id = articles.id INNER JOIN guests ON guests.comment_id = comments.id ``` @@ -1038,22 +1169,27 @@ SELECT posts.* FROM posts #### 중첩(Nested)된 관계를 조인하기(여러 단계) ```ruby -Category.joins(posts: [{ comments: :guest }, :tags]) +Category.joins(articles: [{ comments: :guest }, :tags]) ``` 이 코드에 의해서 아래와 같은 SQL이 생성됩니다. ```sql SELECT categories.* FROM categories - INNER JOIN posts ON posts.category_id = categories.id - INNER JOIN comments ON comments.post_id = posts.id + INNER JOIN articles ON articles.category_id = categories.id + INNER JOIN comments ON comments.article_id = articles.id INNER JOIN guests ON guests.comment_id = comments.id - INNER JOIN tags ON tags.post_id = posts.id + INNER JOIN tags ON tags.article_id = articles.id ``` -### 결합시에 조건 지정하기 +우리말로 표현하자면 '태그를 가지고 있고, 손님이 작성한 덧글을 가지고 있는 글의 카테고리 목록을 +반환해'가 됩니다. -[배열](#배열을_사용하기)과 [문자열](#문자열만을_사용하기)조건을 사용해서 조인 테이블에서 조건을 지정할 수 있습니다. [해시](#해시를_사용하기)의 경우, 조인 테이블에서 특수한 조건을 지정하는 경우에 사용합니다. +#### 결합시에 조건 지정하기 + +[배열](#배열을-사용하기)과 [문자열](#문자열만을-사용하기)조건을 사용해서 조인 테이블에서 조건을 +지정할 수 있습니다. [해시](#해시를-사용하기)의 경우, 조인 테이블에서 특수한 조건을 지정하는 경우에 +사용합니다. ```ruby time_range = (Time.now.midnight - 1.day).Time.now.midnight @@ -1067,7 +1203,26 @@ time_range = (Time.now.midnight - 1.day).Time.now.midnight Client.joins(:orders).where(orders: { created_at: time_range }) ``` -이 코드에서는 어제 주문(order)를 신청한 모든 고객을 검색합니다. 여기에서도 SQL의 `BETWEEN`을 사용합니다. +이 코드에서는 어제 주문(order)를 신청한 모든 고객을 검색합니다. 여기에서도 SQL의 `BETWEEN`을 +사용합니다. + +### `left_outer_joins` + +관계가 없는 레코드 집합을 가져오고 싶은 경우에는 `left_outer_joins` 메소드를 사용할 수 있습니다. + +```ruby +Author.left_outer_joins(:posts).distinct.select('authors.*, COUNT(posts.*) AS posts_count').group('authors.id') +``` + +이는 다음과 같은 SQL을 생성합니다. + +```sql +SELECT DISTINCT authors.*, COUNT(posts.*) AS posts_count FROM "authors" +LEFT OUTER JOIN posts ON posts.author_id = authors.id GROUP BY authors.id +``` + +이 명령은 '저자들이 글을 썼든 안썼든 관계 없이, 작성한 글의 갯수와 함께 모든 저자들을 돌려줘.'가 +됩니다. 관계를 Eager loading 하기 @@ -1075,7 +1230,7 @@ Client.joins(:orders).where(orders: { created_at: time_range }) Eager loading이란, `Model.find`에 의해서 반환되는 객체에 연관된 객체를 같이 읽어오기 위한 방식으로, 쿼리의 사용 횟수를 가능한 줄일 수 있습니다. -**N + 1쿼리 문제** +**N + 1 쿼리 문제** 아래의 코드에 대해서 생각해봅시다. 고객을 10명 검색해서 우편번호를 출력합니다. @@ -1087,13 +1242,18 @@ clients.each do |client| end ``` -이 코드는 얼핏 보기에는 아무런 문제가 없어보입니다. 그러나 실행되는 쿼리의 횟수가 너무 많다는 것이 문제입니다. 코드에서는 처음 고객을 10명 검색하는 쿼리를 실행하고, 그 후에 거기에서 주소를 가져오기 위해서 쿼리를 10번 실행하기 때문에 합계 **11**번의 쿼리를 실행합니다. +이 코드는 얼핏 보기에는 아무런 문제가 없어 보입니다. 그러나 실행되는 쿼리의 횟수가 너무 많다는 것이 +문제입니다. 코드에서는 처음 고객을 10명 검색하는 쿼리를 실행하고, 그 후에 거기에서 주소를 가져오기 +위해서 쿼리를 10번 실행하기 때문에 합계 **11**번의 쿼리를 실행합니다. **N + 1 쿼리 문제 해결하기** -Active Record는 읽어야하는 모든 관계를 사전에 지정할 수 있습니다. 이것은 `Model.find`를 호출 할 때에 `includes`를 설정해주면 됩니다. `includes`를 사용하면 Active Record는 지정된 모든 관계들을 최소한의 쿼리 실행으로 읽어들일 수 있게 해줍니다. +Active Record는 읽어야하는 모든 관계를 사전에 지정할 수 있습니다. 이것은 `find`를 호출 할 때에 +`includes`를 설정해주면 됩니다. `includes`를 사용하면 Active Record는 지정된 모든 관계들을 +최소한의 쿼리 실행으로 읽어들일 수 있게 해줍니다. -위의 예시로 설명하자면, `Client.limit(10)`라는 명령을 수정하여 주소까지 한번에 읽어올 수 있도록 할 수 있습니다. +위의 예시로 설명하자면, `Client.limit(10)`라는 명령을 수정하여 주소까지 한번에 읽어올 수 있도록 +할 수 있습니다. ```ruby clients = Client.includes(:address).limit(10) @@ -1113,12 +1273,13 @@ SELECT addresses.* FROM addresses ### 여러 개의 관계를 한번에 읽어오기 -Active Record는 위와 같은 방식으로 1개의 `Model.find`에서 다른 관계를 몇 개라도 읽어올 수 있습니다. `includes`에 배열, 해시 또는 배열과 해시를 중첩시켜서 사용하면 됩니다. +Active Record는 위와 같은 방식으로 1개의 `find`에서 다른 관계를 몇 개라도 읽어올 수 있습니다. +`includes`에 배열, 해시 또는 배열과 해시를 중첩시켜서 사용하면 됩니다. #### 여러 개의 관계의 배열 ```ruby -Post.includes(:category, :comments) +Article.includes(:category, :comments) ``` 이 코드는 글과 관련된 카테고리, 덧글을 모두 가져옵니다. @@ -1126,40 +1287,58 @@ Post.includes(:category, :comments) #### 중첩된 해시 사용하기 ```ruby -Category.includes(posts: [{ comments: :guest }, :tags]).find(1) +Category.includes(articles: [{ comments: :guest }, :tags]).find(1) ``` 이 코드는 id=1인 카테고리를 검색하고, 관련된 모든 글과 태그, 덧글, 덧글을 작성한 손님까지 읽어옵니다. ### 조건을 지정해서 관계를 가져오기 -Active Record에서는 `joins`처럼 가져오는 시점에서 조건을 지정할 수 있습니다만, [joins](#테이블 조인하기)를 사용하길 권장합니다. +Active Record에서는 `joins`처럼 가져오는 시점에서 조건을 지정할 수 있습니다만, +[joins](#테이블-조인하기)를 사용하길 권장합니다. 하지만 이렇게 작성해야만 하는 경우에는 `where`을 사용하면 됩니다. ```ruby -Post.includes(:comments).where("comments.visible" => true) +Article.includes(:comments).where(comments: { visible: true }) ``` -이 코드는 `LEFT OUTER JOIN`을 포함하는 쿼리를 하나 생성합니다. `joins`를 사용하면 `INNER JOIN`을 사용하는 쿼리가 생성될 것입니다. +이 코드는 `LEFT OUTER JOIN`을 포함하는 쿼리를 하나 생성합니다. `joins`를 사용하면 +`INNER JOIN`을 사용하는 쿼리가 생성될 것입니다. ```ruby - SELECT "posts"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE (comments.visible = 1) + SELECT "articles"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "articles" LEFT OUTER JOIN "comments" ON "comments"."article_id" = "articles"."id" WHERE (comments.visible = 1) ``` `where`이 없는 경우라면 2개의 쿼리를 생성합니다. -이 `includes` 쿼리의 경우 덧글의 존재 여부에 관계 없이 모든 글을 읽어들일 것입니다. 반면 `joins` (INNER JOIN)을 사용하는 경우, 결합조건을 **반드시** 만족해야 하므로 덧글이 없는 글은 반환되지 않습니다. +NOTE: 이런 방식으로 `where`을 사용하려면 인수로 해시를 넘겨야 합니다. SQL 조각을 사용하는 경우에는 테이블을 조인하기 위해서 `references`를 사용해야 합니다. + +```ruby +Article.includes(:comments).where("comments.visible = true").references(:comments) +``` + +이 `includes` 쿼리의 경우 덧글의 존재 여부에 관계 없이 모든 글을 읽어들일 것입니다. 반면 +`joins` (INNER JOIN)을 사용하는 경우, 결합조건을 **반드시** 만족해야 하므로 덧글이 없는 글은 +반환되지 않습니다. + +NOTE: 만약 관계 정보가 조인의 일부로 eager load된다면, 불러오는 모델에서는 별도의 선택 절이 +나타나지 않을 것입니다. 왜냐하면 그들이 부모 레코드의 것인지, 자식 레코드의 것인지 모호하기 +때문입니다. 스코프 ------ -스코프를 설정하여 관계 객체나 모델에 대한 메소드 호출등에 참조되는, 자주 사용되는 쿼리를 지정할 수 있습니다. 스코프에는 `where`, `joins`, `includes` 같은 지금까지 등장한 모든 메소드를 사용할 수 있으며, 어떤 스코프 메소드도 언제나 `ActiveRecord::Relation`를 반환합니다. 이 객체에 대해서 별도의 스코프를 포함하는 다른 메소드를 호출할 수도 있습니다. +스코프를 설정하여 관계 객체나 모델에 대한 메소드 호출 등에 참조되는, 자주 사용되는 쿼리를 지정할 수 +있습니다. 스코프에는 `where`, `joins`, `includes` 같은 지금까지 등장한 모든 메소드를 사용할 +수 있으며, 어떤 스코프 메소드도 언제나 `ActiveRecord::Relation`를 반환합니다. 이 객체에 +대해서 별도의 스코프를 포함하는 다른 메소드를 호출할 수도 있습니다. -스코프를 설정하기 위해서는 클래스에서 `scope` 메소드를 통해 스코프가 호출될 때에 실행되어야 할 쿼리를 넘겨주면 됩니다. +스코프를 설정하기 위해서는 클래스에서 `scope` 메소드를 통해 스코프가 호출될 때에 실행되어야 할 +쿼리를 넘겨주면 됩니다. ```ruby -class Post < ActiveRecord::Base +class Article < ApplicationRecord scope :published, -> { where(published: true) } end ``` @@ -1167,7 +1346,7 @@ end 아래에서 알 수 있듯이, 스코프의 설정은 클래스 메소드를 정의하는 방법과 완전히 같기 때문에, 어느 방식을 사용할 지는 취향대로 선택해주세요. ```ruby -class Post < ActiveRecord::Base +class Article < ApplicationRecord def self.published where(published: true) end @@ -1177,7 +1356,7 @@ end 스코프를 스코프 내에서 연쇄(chain)시킬 수도 있습니다. ```ruby -class Post < ActiveRecord::Base +class Article < ApplicationRecord scope :published, -> { where(published: true) } scope :published_and_commented, -> { published.where("comments_count > 0") } end @@ -1186,22 +1365,22 @@ end 이 `published` 스코프를 호출하기 위해서는 클래스에서 이 스코프를 호출하면 됩니다. ```ruby -Post.published # => [published posts] +Article.published # => [published posts] ``` -또는 `Post` 객체로 넘어오는 관계에서도 이 스코프를 호출할 수 있습니다. +또는 `Article` 객체로 넘어오는 관계에서도 이 스코프를 호출할 수 있습니다. ```ruby category = Category.first -category.posts.published # => [published posts belonging to this category] +category.articles.published # => [published articles belonging to this category] ``` -### 인수를 넘기기 +### 인수 넘기기 스코프에는 인수를 넘길 수 있습니다. ```ruby -class Post < ActiveRecord::Base +class Article < ApplicationRecord scope :created_before, ->(time) { where("created_at < ?", time) } end ``` @@ -1209,13 +1388,13 @@ end 인수를 포함하는 스코프를 호출할 경우에는 클래스 메소드와 같은 방식으로 선언할 수 있습니다. ```ruby -Post.created_before(Time.zone.now) +Article.created_before(Time.zone.now) ``` 하지만 이 스코프에서 가능한 기능은, 클래스 메소드에서의 그것과 중복됩니다. ```ruby -class Post < ActiveRecord::Base +class Article < ApplicationRecord def self.created_before(time) where("created_at < ?", time) end @@ -1228,12 +1407,73 @@ end category.posts.created_before(time) ``` +### 조건 사용하기 + +스코프에서도 조건을 사용할 수 있습니다. + +```ruby +class Article < ApplicationRecord + scope :created_before, ->(time) { where("created_at < ?", time) if time.present? } +end +``` + +다른 예제들과 마찬가지로, 이는 클래스 메소드와 유사하게 동작합니다. + +```ruby +class Article < ApplicationRecord + def self.created_before(time) + where("created_at < ?", time) if time.present? + end +end +``` + +그러나 여기에는 하나의 커다란 차이가 있습니다. 스코프는 조건이 `false`로 평가된 경우에도 +`ActiveRecord::Relation` 객체를 반환합니다만, 같은 경우 클래스 메소드는 `nil`을 반환합니다. +이는 메소드 호출을 연쇄하는 도중에 `NoMethodError` 에러를 발생시킬 가능성이 있습니다. + +### 기본 스코프를 사용하기 + +어떤 스코프를 모델의 모든 쿼리에 적용하고 싶은 경우, 모델의 내부에서 `default_scope`라는 메소드를 사용할 수 있습니다. + +```ruby +class Client < ApplicationRecord + default_scope { where("removed_at IS NULL") } +end +``` + +이 모델에 대해서 쿼리를 실행하면 아래와 같은 SQL을 생성합니다. + +```sql +SELECT * FROM clients WHERE removed_at IS NULL +``` + +기본 스코프의 조건이 복잡하다면 스코프를 클래스 메소드로 정의하는 것도 한가지 방법입니다. + +```ruby +class Client < ActiveRecord::Base + def self.default_scope + # ActiveRecord::Relation을 반환해야 합니다. + end +end +``` + +NOTE: `default_scope`는 레코드를 변경할 경우 뿐만 아니라, 레코드를 생성하거나 만들 때에도 적용됩니다. 예를 들면, + +```ruby +class Client < ApplicationRecord + default_scope { where(active: true) } +end + +Client.new # => # +Client.unscoped.new # => # +``` + ### 스코프 병합 `where`과 마찬가지로 `AND` 조건을 사용해서 스코프를 병합할 수 있습니다. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord scope :active, -> { where state: 'active' } scope :inactive, -> { where state: 'inactive' } end @@ -1259,7 +1499,7 @@ User.active.merge(User.inactive) 여기서 하나 주의할 점은 `default_scope`는 `scope`나 `where` 조건보다도 우선된다는 점입니다. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord default_scope { where state: 'pending' } scope :active, -> { where state: 'active' } scope :inactive, -> { where state: 'inactive' } @@ -1277,71 +1517,145 @@ User.where(state: 'inactive') 이 예제에서 알 수 있듯이 `default_scope`가 `scope`와 `where`보다 우선됩니다. -### 기본 스코프를 사용하기 -어떤 스코프를 모델의 모든 쿼리에 적용하고 싶은 경우, 모델의 내부에서 `default_scope`라는 메소드를 사용할 수 있습니다. +### 모든 스코프를 삭제하기 + +어떤 이유로 모든 스코프를 쓰고 싶지 않은 때에는 `unscoped`를 사용할 수 있습니다. 이 메소드는 모델에서 `default_scope`를 사용하고 있지만 특정 쿼리에 한해서 그 스코프를 적용하고 싶지 않은 경우에 유용합니다. ```ruby -class Client < ActiveRecord::Base - default_scope { where("removed_at IS NULL") } -end +Client.unscoped.load ``` -이 모델에 대해서 쿼리를 실행하면 아래와 같은 SQL을 생성합니다. +이 메소드는 스코프를 모두 무시하고, 쿼리를 실행합니다. -```sql -SELECT * FROM clients WHERE removed_at IS NULL +```ruby +Client.unscoped.all +# SELECT "clients".* FROM "clients" + +Client.where(published: false).unscoped.all +# SELECT "clients".* FROM "clients" ``` -기본 스코프의 조건이 복잡하다면 스코프를 클래스 메소드로 정의하는 것도 한가지 방법입니다. +`unscoped`은 블록도 사용할 수 있습니다. ```ruby -class Client < ActiveRecord::Base - def self.default_scope - # ActiveRecord::Relationを返すようにする - end -end +Client.unscoped { + Client.created_before(Time.zone.now) +} ``` -### 모든 스코프를 삭제하기 +동적 파인더 +--------------- -어떤 이유로 모든 스코프를 쓰고 싶지 않은 때에는 `unscoped`를 사용할 수 있습니다. 이 메소드는 모델에서 `default_scope`를 사용하고 있지만 특정 쿼리에 한해서 그 스코프를 적용하고 싶지 않은 경우에 유용합니다. +Active Record는 테이블에 정의된 모든 필드(속성이라고도 불립니다)에 대한 검색 메소드를 자동으로 +제공합니다. 예를 들어서 `Client` 모델에 `first_name`이라는 필드가 있다고 하면, +`find_by_first_name`이라는 메소드가 Active Record에 의해서 자동적으로 생성됩니다. +`Client` 모델에 `locked`라는 필드가 있다면, `find_by_locked`라는 메소드도 사용가능합니다. + +이 동적 검색 메소드의 뒤에 `Client.find_by_name!("Ryan")`과 같은 느낌표(`!`)를 추가하면 +해당하는 레코드가 없는 경우에 `ActiveRecord::RecordNotFound` 에러를 발생시킵니다. + +name과 locked를 모두 사용해서 검색하고 싶은 경우에는 2개의 필드명을 and로 연결해서 호출하면 +됩니다. 이 경우, `Client.find_by_first_name_and_locked("Ryan", true)`처럼 작성할 수 +있습니다. + +Enums +----- + +`enum` 매크로는 정수 컬럼을 다른 값으로 사용할 수 있게끔 해줍니다. ```ruby -Client.unscoped.load +class Book < ApplicationRecord + enum availability: [:available, :unavailable] +end ``` -이 메소드는 스코프를 모두 무시하고, 쿼리를 실행합니다. +이는 자동으로 각각에 대응하는 [스코프](#스코프)를 생성합니다. 상태를 변경하는 메소드나, 현재 상태를 +확인하는 메소드도 함께 추가됩니다. -`unscoped`에 `scope`를 이어서(chain) 사용할 수는 없으므로 주의해주세요. 이러한 경우에는 `unscoped`를 블록 형식으로 사용하는 것을 추천합니다. +```ruby +# 두 예제는 모두 사용 가능한 책들을 가져옵니다. +Book.available +# 또는 +Book.where(availability: :available) + +book = Book.new(availability: :available) +book.available? # => true +book.unavailable! # => true +book.available? # => false +``` + +여기에 대한 전체 문서는 [Rails API 문서](http://api.rubyonrails.org/classes/ActiveRecord/Enum.html)를 확인하세요. + +메소드 체인 이해하기 +--------------------------------- + +Active Record 패턴은 [Method Chaining](http://en.wikipedia.org/wiki/Method_chaining)을 +구현하고 있습니다. 이는 여러 Active Record 메소드를 쉽고 직관적으로 함께 사용할 수 있게 해줍니다. + +`all`, `where`, `joins` 처럼 이전 메소드 호출이 `ActiveRecord::Relation`를 반환한다면 +메소드를 연쇄할 수 있습니다. 메소드 체인의 끝에는 단일 객체를 +반환([단일 객체 돌려받기](#단일-객체-돌려받기)를 참조)합니다. + +아래에 예제가 있습니다. 이 가이드는 모든 가능성 중 일부만을 다룹니다. Active Record 메소드가 +호출되면, 쿼리는 즉시 생성되어 데이터베이스로 전송되지 않으며, 이는 데이터가 실제로 필요한 시점에 +발생합니다. 쿼리를 생성하는 각 예제들을 살펴보세요. + +### 여러 테이블로부터 조건부 데이터를 가져오기 ```ruby -Client.unscoped { - Client.created_before(Time.zone.now) -} +Person + .select('people.id, people.name, comments.text') + .joins(:comments) + .where('comments.created_at > ?', 1.week.ago) ``` -동적 파인더 ---------------- +생성되는 SQL은 이렇습니다. -Active Record는 테이블에 정의된 모든 필드(속성이라고도 불립니다)에 대한 검색 메소드를 자동적으로 제공합니다. 예를 들어서 `Client` 모델에 `first_name`이라는 필드가 있다고 하면, `find_by_first_name`이라는 메소드가 Active Record에 의해서 자동적으로 생성됩니다. `Client` 모델에 `locked`라는 필드가 있다면, `find_by_locked`라는 메소드도 사용가능합니다. +```sql +SELECT people.id, people.name, comments.text +FROM people +INNER JOIN comments + ON comments.person_id = people.id +WHERE comments.created_at = '2015-01-01' +``` -이 동적 검색 메소드의 뒤에 `Client.find_by_name!("Ryan")`과 같은 느낌표(`!`)를 추가하면 해당하는 레코드가 없는 경우에 `ActiveRecord::RecordNotFound` 에러를 발생시킵니다. +### 여러 테이블로 부터 특정 데이터를 가져오기 -name과 locked를 모두 사용해서 검색하고 싶은 경우에는 2개의 필드명을 and로 연결해서 호출하면 됩니다. 이 경우, `Client.find_by_first_name_and_locked("Ryan", true)`처럼 작성할 수 있습니다. +```ruby +Person + .select('people.id, people.name, companies.name') + .joins(:company) + .find_by('people.name' => 'John') # 이 메소드는 반드시 마지막에 와야 합니다. +``` + +생성되는 SQL은 이렇습니다. + +```sql +SELECT people.id, people.name, companies.name +FROM people +INNER JOIN companies + ON companies.person_id = people.id +WHERE people.name = 'John' +LIMIT 1 +``` + +NOTE: 조건에 맞는 레코드가 여러개 있을 경우, `find_by`는 첫번째 레코드만을 가져오고, 나머지를 +무시한다는 것을 기억하세요(위 SQL에서 `LIMIT 1`). 새로운 객체를 검색하거나 만들기 -------------------------- -NOTE: Rails 4.0에서는 일부 동적 검색 메소드가 비권장으로 지정되었습니다. 이것들은 Rails 4.1에서 삭제될 예정입니다. 가장 좋은 방법은 Active Record의 스코프로 대체하는 것입니다. 비권장으로 변경된 파인더 잼은 https://github.com/rails/activerecord-deprecated_finders 에서 찾아보실 수 있습니다. - -레코드를 검색해보고 없다면 생성한다, 라는 것은 꽤 자주 있는 상황입니다. `find_or_create_by`나 `find_or_create_by!`를 사용하면 이러한 작업을 한번에 처리할 수 있습니다. +레코드를 검색해보고 없다면 생성한다, 라는 것은 꽤 자주 있는 상황입니다. `find_or_create_by`나 +`find_or_create_by!`를 사용하면 이러한 작업을 한번에 처리할 수 있습니다. ### `find_or_create_by` -`find_or_create_by` 메소드는 지정된 속성을 가지는 레코드가 존재하는지를 확인합니다. 레코드가 없는 경우에는 `create`가 호출됩니다. 아래의 예시를 봐주세요. +`find_or_create_by` 메소드는 지정된 속성을 가지는 레코드가 존재하는지를 확인합니다. 레코드가 +없는 경우에는 `create`가 호출됩니다. 아래의 예시를 봐주세요. -'Andy'라는 이름의 고객을 찾고, 없다면 새로 생성하고 싶다고 가정합시다. 이러한 경우에는 아래와 같이 실행하면 됩니다. +'Andy'라는 이름의 고객을 찾고, 없다면 새로 생성하고 싶다고 가정합시다. 이러한 경우에는 아래와 같이 +실행하면 됩니다. ```ruby Client.find_or_create_by(first_name: 'Andy') @@ -1357,11 +1671,14 @@ INSERT INTO clients (created_at, first_name, locked, orders_count, updated_at) V COMMIT ``` -`find_or_create_by`는 이미 존재하는 레코드나 그렇지 않으면 새로운 레코드를 반환합니다. 이 경우, Andy라는 이름의 고객이 없었기 때문에 새 레코드를 반환하였습니다. +`find_or_create_by`는 이미 존재하는 레코드나 그렇지 않으면 새로운 레코드를 반환합니다. 이 경우, +Andy라는 이름의 고객이 없었기 때문에 새 레코드를 반환하였습니다. `create`와 마찬가지로 검증에 통과하는가, 아닌가에 따라 새로운 레코드가 데이터베이스에 저장되지 않을 수도 있습니다. -이번에는 새로운 레코드를 작성할 경우에 'locked' 속성을 `false`로 설정하고 싶은데, 그것을 쿼리에는 포함하고 싶지 않다고 가정해봅시다. 거기서 "Andy"라는 이름의 고객을 검색하거나, 그 이름을 가지는 고객이 없는 경우 "Andy"라는 고객을 생성하고, 잠금을 해제하고 싶습니다. +이번에는 새로운 레코드를 작성할 경우에 'locked' 속성을 `false`로 설정하고 싶은데, 그것을 +쿼리에는 포함하고 싶지 않다고 가정해봅시다. 거기서 "Andy"라는 이름의 고객을 검색하거나, 그 이름을 +가지는 고객이 없는 경우 "Andy"라는 고객을 생성하고, 잠금을 해제하고 싶습니다. 이것은 2가지 방법으로 구현할 수 있습니다. 첫번째는 `create_with`를 사용하는 방법입니다. @@ -1381,7 +1698,8 @@ end ### `find_or_create_by!` -`find_or_create_by!`를 사용하면 새로운 레코드가 생성을 시도하고, 실패했을 경우에 예외를 발생시킵니다. 이 가이드에서는 검증에 대해서 설명하지 않습니다만, +`find_or_create_by!`를 사용하면 새로운 레코드가 생성을 시도하고, 실패했을 경우에 예외를 +발생시킵니다. 이 가이드에서는 검증에 대해서 설명하지 않습니다만, ```ruby validates :orders_count, presence: true @@ -1396,7 +1714,10 @@ Client.find_or_create_by!(first_name: 'Andy') ### `find_or_initialize_by` -`find_or_initialize_by` 메소드는 `find_or_create_by`와 같은 방식으로 동작합니다만, `create` 대신에 `new`를 호출한다는 점이 다릅니다. 다시 말해, 모델의 새로운 객체를 생성하지만, 데이터베이스에는 저장하지 않습니다. `find_or_create_by`의 예외를 조금 바꾸어서 설명해보겠습니다. 이번에는 'Nick'이라는 이름의 고객이 필요하다고 합시다. +`find_or_initialize_by` 메소드는 `find_or_create_by`와 같은 방식으로 동작합니다만, +`create` 대신에 `new`를 호출한다는 점이 다릅니다. 다시 말해, 모델의 새로운 객체를 생성하지만, +데이터베이스에는 저장하지 않습니다. `find_or_create_by`의 예제를 조금 바꾸어서 설명합니다. +이번에는 'Nick'이라는 이름의 고객이 필요하다고 합시다. ```ruby nick = Client.find_or_initialize_by(first_name: 'Nick') @@ -1425,36 +1746,42 @@ nick.save SQL로 검색하기 -------------- -직접 SQL을 사용해서 레코드를 검색하고 싶은 경우에 `find_by_sql`를 사용할 수 있습니다. 이 `find_by_sql` 메소드는 객체 배열을 하나 반홚납니다. 쿼리가 레코드를 하나만 찾은 경우에도 배열을 돌려주기 때문에 주의해주세요. 예를 들어서 아래와 같은 쿼리를 실행한다고 가정합시다. +직접 SQL을 사용해서 레코드를 검색하고 싶은 경우에 `find_by_sql`을 사용할 수 있습니다. 이 +`find_by_sql` 메소드는 객체 배열을 하나 반환합니다. 쿼리가 레코드를 하나만 찾은 경우에도 배열을 +돌려주기 때문에 주의해주세요. 예를 들어서 아래와 같은 쿼리를 실행한다고 가정합시다. ```ruby Client.find_by_sql("SELECT * FROM clients INNER JOIN orders ON clients.id = orders.client_id ORDER BY clients.created_at desc") # => [ - #, - #, - # ... -] +# #, +# #, +# ... +# ] ``` -`find_by_sql`는 데이터베이스에서 Active Record에서 제공하지 않는 쿼리를 사용하기 위한 간단한 방법을 제공하며, 인스턴스화 된 객체를 반환합니다. +`find_by_sql`은 데이터베이스에서 Active Record에서 제공하지 않는 쿼리를 사용하기 위한 간단한 +방법을 제공하며, 인스턴스화 된 객체를 반환합니다. ### `select_all` -`find_by_sql`는 `connection#select_all`과 깊은 관계가 있습니다. `select_all`은 `find_by_sql`와 마찬가지로 커스텀 SQL을 사용해서 데이터베이스에서 결과를 가져옵니다만, 가져온 결과를 객체로 만들지 않는다는 점이 다릅니다. 대신, 해시 배열을 돌려주며, 하나의 해시가 하나의 레코드를 나타냅니다. +`find_by_sql`은 `connection#select_all`과 깊은 관계가 있습니다. `select_all`은 +`find_by_sql`와 마찬가지로 커스텀 SQL을 사용해서 데이터베이스에서 결과를 가져옵니다만, 가져온 +결과를 객체로 만들지 않는다는 점이 다릅니다. 대신, 해시 배열을 돌려주며, 하나의 해시가 하나의 +레코드를 나타냅니다. ```ruby -Client.connection.select_all("SELECT * FROM clients WHERE id = '1'") +Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'") # => [ - {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"}, - {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"} -] +# {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"}, +# {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"} +# ] ``` ### `pluck` -`pluck`은 모델에서 사용하는 테이블로부터 1개 또는 그 이상의 컬럼을 가져올때 사용할 수 있습니다. 인수로서 컬럼명 리스트를 받고, 지정된 컬럼 값의 배열을 그에 맞는 데이터 형식으로 반환합니다. +`pluck`은 모델에서 사용하는 테이블로부터 1개 또는 그 이상의 컬럼을 가져올 때 사용할 수 있습니다. 인수로서 컬럼명 리스트를 받고, 지정된 컬럼 값의 배열을 그에 맞는 데이터 형식으로 반환합니다. ```ruby Client.where(active: true).pluck(:id) @@ -1474,29 +1801,32 @@ Client.pluck(:id, :name) ```ruby Client.select(:id).map { |c| c.id } - # 또는 +# 또는 Client.select(:id).map(&:id) - # 또는 +# 또는 Client.select(:id, :name).map { |c| [c.id, c.name] } ``` ```ruby Client.pluck(:id) - # 또는 +# 또는 Client.pluck(:id, :name) ``` -`select`와는 다르게 `pluck`은 데이터베이스에서 받은 결과를 직접 Ruby의 배열로 변환합니다. 이를 위해 `ActiveRecord` 객체를 사전에 준비할 필요가 없습니다. 따라서, 이 메소드는 대규모의 쿼리나 사용 빈도가 높은 쿼리에서 사용하면 퍼포먼스를 향상시킬 수 있습니다. 단, 속성 접근자를 덮어쓰는 모델 메소드는 사용할 수 없습니다. 예를 들어, 다음과 같은 경우입니다. +`select`와는 다르게 `pluck`은 데이터베이스에서 받은 결과를 직접 Ruby의 배열로 변환합니다. 이를 +위해 `ActiveRecord` 객체를 사전에 준비할 필요가 없습니다. 따라서, 이 메소드는 대규모의 쿼리나 +사용 빈도가 높은 쿼리에서 사용하면 퍼포먼스를 향상시킬 수 있습니다. 단, 속성 접근자를 덮어쓰는 모델 +메소드는 사용할 수 없습니다. 예를 들어, 다음과 같은 경우입니다. ```ruby -class Client < ActiveRecord::Base +class Client < ApplicationRecord def name - "저는 #{super}입니다." + "I am #{super}" end end Client.select(:name).map &:name -# => ["저는 David입니다.", "저는 Jeremy입니다.", "저는 Jose입니다."] +# => ["I am David", "I am Jeremy", "I am Jose"] Client.pluck(:name) # => ["David", "Jeremy", "Jose"] @@ -1522,7 +1852,7 @@ Person.ids ``` ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord self.primary_key = "person_id" end @@ -1534,17 +1864,19 @@ Person.ids -------------------- 객체가 존재하는지 아닌지, `exists?`로 확인할 수 있습니다. -이 메소드는 `find`와 같은 쿼리를 전송합니다만, 객체의 컬렉션을 돌려주는 대신 `true`나 `false`를 반환합니다. +이 메소드는 `find`와 같은 쿼리를 전송합니다만, 객체의 컬렉션을 돌려주는 대신 `true`나 `false`를 +반환합니다. ```ruby Client.exists?(1) ``` -`exists?`는 여러개의 인수를 받을 수 있습니다. 단 그 값들 중 하나라도 존재한다면 다른 값이 존재하지 않더라도 `true`를 돌려줍니다. +`exists?`는 여러개의 인수를 받을 수 있습니다. 단 그 값들 중 하나라도 존재한다면 다른 값이 +존재하지 않더라도 `true`를 돌려줍니다. ```ruby Client.exists?(id: [1,2,3]) - # 또는 +# 또는 Client.exists?(name: ['John', 'Sergei']) ``` @@ -1554,7 +1886,8 @@ Client.exists?(name: ['John', 'Sergei']) Client.where(first_name: 'Ryan').exists? ``` -이 예제에서는 `first_name`이 'Ryan'인 고객이 한명이라도 존재하면 `true`를 반환하고, 그 이외의 경우에는 `false`를 반환합니다. +이 예제에서는 `first_name`이 'Ryan'인 고객이 한명이라도 존재하면 `true`를 반환하고, 그 이외의 +경우에는 `false`를 반환합니다. ```ruby Client.exists? @@ -1669,66 +2002,87 @@ EXPLAIN 실행하기 관계(ActiveRecord::Relation)를 통해 실행되는 쿼리에서 EXPLAIN을 실행할 수 있습니다. 아래의 코드에서는, ```ruby -User.where(id: 1).joins(:posts).explain +User.where(id: 1).joins(:articles).explain ``` 아래와 같은 결과가 생성됩니다. ``` -EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `posts` ON `posts`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 -+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ -| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | -+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ -| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | | -| 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where | -+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ +EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `articles` ON `articles`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 ++----+-------------+----------+-------+---------------+ +| id | select_type | table | type | possible_keys | ++----+-------------+----------+-------+---------------+ +| 1 | SIMPLE | users | const | PRIMARY | +| 1 | SIMPLE | articles | ALL | NULL | ++----+-------------+----------+-------+---------------+ ++---------+---------+-------+------+-------------+ +| key | key_len | ref | rows | Extra | ++---------+---------+-------+------+-------------+ +| PRIMARY | 4 | const | 1 | | +| NULL | NULL | NULL | 1 | Using where | ++---------+---------+-------+------+-------------+ + 2 rows in set (0.00 sec) ``` -MySQL일 경우의 결과는 위와 같습니다. +MySQL나 MariaDB일 경우, 결과는 위와 같습니다. -Active Record는 데이터베이스의 쉘에서 볼수 있을법한 정형화된 결과를 출력합니다. PostgreSQL 어댑터를 통해서 같은 쿼리를 실행하면, 다음과 같은 결과를 얻을 수 있습니다. +Active Record는 데이터베이스의 쉘에서 볼 수 있을법한 정형화된 결과를 출력합니다. PostgreSQL +어댑터를 통해서 같은 쿼리를 실행하면, 다음과 같은 결과를 얻을 수 있습니다. ``` -EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE "users"."id" = 1 +EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "articles" ON "articles"."user_id" = "users"."id" WHERE "users"."id" = 1 QUERY PLAN ------------------------------------------------------------------------------ -Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0) - Join Filter: (posts.user_id = users.id) + Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0) + Join Filter: (articles.user_id = users.id) -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4) Index Cond: (id = 1) - -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4) - Filter: (posts.user_id = 1) + -> Seq Scan on articles (cost=0.00..28.88 rows=8 width=4) + Filter: (articles.user_id = 1) (6 rows) ``` Eager loading을 사용하고 있으면 내부에서 복수의 쿼리가 실행되는 경우가 있으며, 일부의 쿼리에서는 직전 쿼리의 결과를 요구하는 경우도 있습니다. 때문에 `explain`은 이 쿼리를 직접 실행하고 그 이후에 쿼리 플랜을 요구합니다. 예를 들어 아래와 같은 코드에서는, ```ruby -User.where(id: 1).includes(:posts).explain +User.where(id: 1).includes(:articles).explain ``` 다음과 같은 결과를 생성합니다. ``` EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 -+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ -| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | -+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ -| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | | -+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ ++----+-------------+-------+-------+---------------+ +| id | select_type | table | type | possible_keys | ++----+-------------+-------+-------+---------------+ +| 1 | SIMPLE | users | const | PRIMARY | ++----+-------------+-------+-------+---------------+ ++---------+---------+-------+------+-------+ +| key | key_len | ref | rows | Extra | ++---------+---------+-------+------+-------+ +| PRIMARY | 4 | const | 1 | | ++---------+---------+-------+------+-------+ + 1 row in set (0.00 sec) -EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1) -+----+-------------+-------+------+---------------+------+---------+------+------+-------------+ -| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | -+----+-------------+-------+------+---------------+------+---------+------+------+-------------+ -| 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where | -+----+-------------+-------+------+---------------+------+---------+------+------+-------------+ +EXPLAIN for: SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` IN (1) ++----+-------------+----------+------+---------------+ +| id | select_type | table | type | possible_keys | ++----+-------------+----------+------+---------------+ +| 1 | SIMPLE | articles | ALL | NULL | ++----+-------------+----------+------+---------------+ ++------+---------+------+------+-------------+ +| key | key_len | ref | rows | Extra | ++------+---------+------+------+-------------+ +| NULL | NULL | NULL | 1 | Using where | ++------+---------+------+------+-------------+ + + 1 row in set (0.00 sec) ``` -이 결과는 MySQL일 경우입니다. +이 결과는 MySQL와 MariaDB일 경우입니다. ### EXPLAIN의 출력 결과를 이해하기 @@ -1738,7 +2092,6 @@ EXPLAIN의 출력 결과에 대한 자세한 설명은 이 가이드의 범위 * MySQL: [EXPLAIN Output Format](http://dev.mysql.com/doc/refman/5.6/en/explain-output.html) (영어) -* PostgreSQL: [EXPLAIN 사용하기](https://www.postgresql.jp/document/9.3/html/using-explain.html) (일본어) - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. +* MariaDB: [EXPLAIN](https://mariadb.com/kb/en/mariadb/explain/) (영어) +* PostgreSQL: [Using EXPLAIN](http://www.postgresql.org/docs/current/static/using-explain.html) (영어) \ No newline at end of file diff --git a/guides/source/ko/active_record_validations.md b/guides/source/ko/active_record_validations.md index a24b6ffbac025..987c3d83d5408 100644 --- a/guides/source/ko/active_record_validations.md +++ b/guides/source/ko/active_record_validations.md @@ -2,7 +2,8 @@ Active Record Validation ========================== -이 가이드에서는 Active Record의 유효성 검사(Validation) 기능을 사용해서 객체가 데이터베이스에 저장되기 전에 자신의 상태를 검증하는 방법에 대해서 설명합니다. +이 가이드에서는 Active Record의 유효성 검사(Validation) 기능을 사용해서 객체가 +데이터베이스에 저장되기 전에 자신의 상태를 검증하는 방법에 대해서 설명합니다. 이 가이드의 내용: @@ -18,7 +19,7 @@ Active Record Validation 간단한 유효성 검사의 예시를 소개합니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -26,9 +27,12 @@ Person.create(name: "John Doe").valid? # => true Person.create(name: nil).valid? # => false ``` -이상에서 알 수 있듯, 이 유효성 검사에서는 `Person`이 `name` 속성이 없는 경우에 유효하지 않은 것으로 판정합니다. 그래서 두번째 `Person`은 데이터베이스에 저장되지 않습니다. +이상에서 알 수 있듯, 이 유효성 검사에서는 `Person`이 `name` 속성이 없는 경우에 +유효하지 않은 것으로 판정합니다. 그래서 두번째 `Person`은 데이터베이스에 +저장되지 않습니다. -유효성 검사를 자세히 설명하기 전에, 유효성 검사가 애플리케이션 전체에서 중요한 이유를 설명합니다. +유효성 검사를 자세히 설명하기 전에, 유효성 검사가 애플리케이션 전체에서 중요한 +이유를 설명합니다. ### 유효성 검사를 하는 이유 @@ -47,7 +51,7 @@ Person.create(name: nil).valid? # => false Active Record의 객체에는 2가지 종류가 있습니다. 객체가 데이터베이스의 레코드(row)에 매핑 된 것과 그렇지 않은 것입니다. 예를 들어 `new` 메소드로 생성된 객체는 아직 데이터베이스에 속해있지 않습니다. `save` 메소드를 호출해야만 적절한 데이터베이스의 테이블에 저장됩니다. Active Record의 `new_record?` 인스턴스 메소드를 사용해서 객채가 데이터베이스에 저장되었는지 확인할 수 있습니다. 다음의 간단한 Active Record 클래스를 보시죠. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord end ``` @@ -96,17 +100,23 @@ CAUTION: 데이터베이스의 객체를 변경하는 방법이 한가지만은 * `update_columns` * `update_counters` -사실 `save`에서 `validate: false`를 인수로 건네주면 `save`에서도 유효성 검사를 무시할 수 있습니다만, 사용하실 때에는 세심의 주의를 기울여주세요. +사실 `save`에서 `validate: false`를 인수로 건네주면 `save`에서도 유효성 검사를 +무시할 수 있습니다만, 사용하실 때에는 세심의 주의를 기울여주세요. * `save(validate: false)` ### `valid?`와 `invalid?` -Rails에서 객체가 유효(valid)한지 아닌지를 검증할 때에는 `valid?`라는 메소드가 사용됩니다. 이 메소드는 단독으로도 사용 가능합니다. `valid?`를 실행하면 유효성 검사가 수행되며 객체에 에러가 없는 경우 true를 돌려주고, 그렇지 않으면 false를 돌려줍니다. +Rails는 Active Record 객체를 저장하기 전에 검증을 실행합니다. +만약 이 검증이 어떤 에러를 돌려준다면 Rails는 객체를 저장하지 않습니다. + +직접 검증을 만들 수 도 있습니다. `valid?`는 만든 검증을 실행하고 객체에서 +에러를 발견하지 못한 경우에 true를 반환하며, 그렇지 않은 경우에 false를 +반환합니다. 아래와 같이 사용할 수 있습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -114,12 +124,17 @@ Person.create(name: "John Doe").valid? # => true Person.create(name: nil).valid? # => false ``` -Active Record에서 유효성 검사가 이루어진 뒤에 `errors.messages` 라는 인스턴스 메소드를 사용하면, 발생한 에러에 접근할 수 있습니다. 이 메소드는 에러 메시지의 컬렉션을 반환합니다. 기본적으로는 유효성 검사를 실한 뒤에 이 컬렉션이 아무것도 가지고 있지 않을 경우에만 객체가 유효하다고 판단합니다. +Active Record에서 유효성 검사가 이루어진 뒤에 `errors.messages` 라는 인스턴스 +메소드를 사용하면, 발생한 에러에 접근할 수 있습니다. 이 메소드는 에러 메시지의 +컬렉션을 반환합니다. 기본적으로는 유효성 검사를 실한 뒤에 이 컬렉션이 아무것도 +가지고 있지 않을 경우에만 객체가 유효하다고 판단합니다. -`new` 를 사용해서 생성된 객체는 유효성에 문제가 있다고 하더라도 에러가 있다고 표시되지 않으므로, 주의해야합니다. `new` 만으로는 유효성 검사가 실행되지 않습니다. +`new` 를 사용해서 생성된 객체는 유효성에 문제가 있다고 하더라도 에러가 있다고 +표시되지 않으므로, 주의해야합니다. `new` 만으로는 유효성 검사가 실행되지 +않습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -131,33 +146,41 @@ end >> p.valid? # => false >> p.errors.messages -# => {name:["공란으로 둘 수 없습니다."]} +# => {name:["can't be blank"]} >> p = Person.create # => # >> p.errors.messages -# => {name:["공란으로 둘 수 없습니다."]} +# => {name:["can't be blank"]} >> p.save # => false >> p.save! -# => ActiveRecord::RecordInvalid: Validation failed: 공란으로 둘 수 없습니다. +# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank >> Person.create! -# => ActiveRecord::RecordInvalid: Validation failed: 공란으로 둘 수 없습니다. +# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank ``` -`invalid?`는 `valid?`의 정 반대의 동작을 합니다. 이 메소드는 유효성 검사를 수행하고 객체에서 에러가 발생한 경우에 true를, 그렇지 않으면 false를 반환합니다. +`invalid?`는 `valid?`의 정 반대의 동작을 합니다. 이 메소드는 유효성 검사를 +수행하고 객체에서 에러가 발생한 경우에 true를, 그렇지 않으면 false를 +반환합니다. ### `errors[]` -`errors[:attribute]`를 사용해서 특정 객체의 속성이 유효한지를 확인합니다. 이 메소드는 `:attribute`의 모든 에러 목록을 반환합니다. 지정된 속성에서 에러가 발생하지 않았을 경우에는 빈 배열을 반환합니다. +`errors[:attribute]`를 사용해서 특정 객체의 속성이 유효한지를 확인합니다. +이 메소드는 `:attribute`의 모든 에러 목록을 반환합니다. 지정된 속성에서 +에러가 발생하지 않았을 경우에는 빈 배열을 반환합니다. -이 메소드는 유효성 검사가 _끝난 뒤에만_ 유용합니다. 이 메소드는 에러 컬렉션을 확인하기만 하고, 유효성 검사 자체를 실행하지는 않기 때문입니다. 이 메소드는 앞에서 이야기했던 `ActiveRecord::Base#invalid?`와는 다르게, 객체 전체의 유효성을 확인하지 않기 때문입니다. 다시 말해, 객체 각각의 속성에 대해서 에러가 있는지 없는지 만을 확인합니다. +이 메소드는 유효성 검사가 _끝난 뒤에만_ 유용합니다. 이 메소드는 에러 컬렉션을 +확인하기만 하고, 유효성 검사 자체를 실행하지는 않기 때문입니다. 이 메소드는 +앞에서 이야기했던 `ApplicationRecord#invalid?`와는 다르게, 객체 전체의 유효성을 +확인하지 않기 때문입니다. 다시 말해, 각각의 속성에 대해서 에러가 있는지 +없는지을 확인합니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true end @@ -165,43 +188,92 @@ end >> Person.create.errors[:name].any? # => true ``` -검증 에러에 대한 자세한 설명은 [검증 에러 다루기](#검증_에러_사용하기)를 참조해주세요. 지금부터는 Rails가 제공하는 내장 검증 헬퍼를 설명합니다. +검증 에러에 대한 자세한 설명은 [검증 에러 다루기](#검증-에러-사용하기)를 +참조해주세요. 지금부터는 Rails가 제공하는 내장 검증 헬퍼를 설명합니다. + +### `errors.details` + +부정한 속성에 대해서 어떤 검증이 실패했는지를 확인하려면 +`errors.details[:attribute]`를 사용하세요. 이는 `:error`라는 키를 통해서 +검증자의 심볼을 얻어올 수 있습니다. + +```ruby +class Person < ApplicationRecord + validates :name, presence: true +end + +>> person = Person.new +>> person.valid? +>> person.errors.details[:name] # => [{error: :blank}] +``` + +검증 에러에 대한 자세한 설명은 [검증 에러 다루기](#검증-에러-사용하기)를 +참조해주세요. 지금부터는 Rails가 제공하는 내장 검증 헬퍼를 설명합니다. 검증 헬퍼 ------------------ -Active Record에서는 클래서 정의에서 직접 사용가능한 검증 헬퍼가 다수 있습니다. 이 헬퍼들은 공통의 검증 규칙을 제공합니다. 검증이 실패할 때마다 객체의 `errors` 컬렉션에 에러메시지가 추가되며, 그 메시지는 검증 대상인 속성과 연관되어 저장됩니다. +Active Record에서는 클래서 정의에서 직접 사용가능한 검증 헬퍼가 다수 있습니다. +이 헬퍼들은 공통의 검증 규칙을 제공합니다. 검증이 실패할 때마다 객체의 `errors` +컬렉션에 에러 메시지가 추가되며, 그 메시지는 검증 대상인 속성과 연관되어 +저장됩니다. -어느 헬퍼도 검증 가능한 속성 갯수에는 제한을 두지 않으므로, 한줄의 코드를 작성하는 것만으로도 많은 속성에 대해서 같은 검증 규칙을 적용할 수 있습니다. +어느 헬퍼도 검증 가능한 속성 갯수에는 제한을 두지 않으므로, 한줄의 코드를 +작성하는 것만으로도 많은 속성에 대해서 같은 검증 규칙을 적용할 수 있습니다. -`:on` 옵션과 `:message` 옵션은 어느 헬퍼에서도 사용할 수 있습니다. 이 옵션들은 각각 유효성 검사가 실행되는 시점과 검증이 실패했을 때에 `errors` 컬렉션에 추가될 메시지를 지정합니다. `:on` 옵션은 `:create`나 `:update`를 값으로 취합니다. 검증 헬퍼에는 각각 기본 에러 메시지가 준비되어 있습니다. `:message` 옵션을 사용하지 않는 경우에는 기본 에러 메시지가 사용됩니다. 사용 가능한 헬퍼를 하나씩 알아봅시다. +`:on` 옵션과 `:message` 옵션은 어느 헬퍼에서도 사용할 수 있습니다. 이 옵션들은 +각각 유효성 검사가 실행되는 시점과 검증이 실패했을 때에 `errors` 컬렉션에 +추가될 메시지를 지정합니다. `:on` 옵션은 `:create`나 `:update`를 값으로 +취합니다. 검증 헬퍼에는 각각 기본 에러 메시지가 준비되어 있습니다. `:message` +옵션을 사용하지 않는 경우에는 기본 에러 메시지가 사용됩니다. 사용 가능한 +헬퍼를 하나씩 알아봅시다. ### `acceptance` -이 메소드는 양식이 전송되었을 때에 유저 인터페이스에서 체크박스에 체크가 되어있는지 아닌지를 확인합니다. 유저가 서비스 이용 약관에 대한 동의, 사용자에게 문서를 읽어야 한다는 것 같은 무언가의 동의를 요구할 때 등에 사용할 수 있습니다. 이 검증은 웹 애플리케이션에서만 볼 수 있는 것으로, `acceptance`는 데이터베이스에 저장할 필요가 없습니다. 저장용의 컬럼이 생성되어있지 않은 경우, 헬퍼는 가상의 속성을 만들어서 사용합니다. +이 메소드는 양식이 전송되었을 때에 유저 인터페이스에서 체크박스에 체크가 +되어있는지 아닌지를 확인합니다. 유저가 서비스 이용 약관에 대한 동의, +사용자에게 문서를 읽어야 한다는 것 같은 무언가의 동의를 요구할 때 등에 사용할 +수 있습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :terms_of_service, acceptance: true end ``` +이 검사는 `terms_of_service`가 `nil`인지만을 확인합니다. 이 헬퍼의 기본 에러 메시지는 _"must be accepted"_ 입니다. +`message` 옵션을 통해서 전용 메시지를 사용할 수도 있습니다. + +```ruby +class Person < ApplicationRecord + validates :terms_of_service, acceptance: { message: 'must be abided' } +end +``` -이 헬퍼에서는 `:accept`옵션을 사용할 수 있습니다. 이 옵션은 `체크됨`을 나타내는 값을 지정할 수 있습니다. 기본값은 "1"입니다만, 간단하게 변경할 수 있습니다. +이 헬퍼에서는 `:accept` 옵션을 사용할 수 있습니다. 이 옵션은 `체크됨`을 +나타내는 값을 지정할 수 있습니다. 기본값은 `["1", true]`입니다만, 간단하게 +변경할 수 있습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :terms_of_service, acceptance: { accept: 'yes' } + validates :eula, acceptance: { accept: ['TRUE', 'accepted'] } end ``` +이 검증은 웹 애플리케이션에 있어서 매우 명확하며, 이 'acceptance'는 +데이터베이스에 저장될 필요가 없습니다. 만약 해당하는 필드를 가지고 있지 않다면 +헬퍼는 가상 속성을 생성합니다. 만약 데이터베이스에 해당하는 속성이 존재하지 +않는다면, `accept` 옵션이 설정되거나, `true`를 넘기지 않으면 검증이 동작하지 +않을 것입니다. + ### `validates_associated` 모델이 다른 모델과 관계가 설정되어있고, 양 쪽의 모델에 대해서 유효성 검사를 실행할 필요가 있는 경우에는 이 헬퍼를 사용합니다. 객체를 저장할때 관계가 설정된 객체마다 `valid?`가 호출됩니다. ```ruby -class Library < ActiveRecord::Base +class Library < ApplicationRecord has_many :books validates_associated :books end @@ -209,16 +281,22 @@ end 이 검증은 어떤 종류의 관계에라도 사용할 수 있습니다. -CAUTION: `validates_associated`은 한 쪽에서만 호출해주세요. 만약 관계가 설정된 두 모델 모두 이 헬퍼를 사용하면 무한루프에 빠집니다. +CAUTION: `validates_associated`은 한쪽에서만 호출해주세요. 만약 관계가 설정된 +두 모델 모두 이 헬퍼를 사용하면 무한루프에 빠집니다. -`validates_associated`의 기본 에러메시지는 _"is invalid"_ 입니다. 관계가 설정된 객체는 자신의 `errors` 컬렉션에 에러를 저장하므로, 검증을 실행한 모델에서 직접 그 에러를 확인할 수는 없습니다. +`validates_associated`의 기본 에러메시지는 _"is invalid"_ 입니다. 관계가 +설정된 객체는 자신의 `errors` 컬렉션에 에러를 저장하므로, 검증을 실행한 +모델에서 직접 그 에러를 확인할 수는 없습니다. ### `confirmation` -이 헬퍼는 2개의 텍스트 필드가 완전히 일치하는 내용을 가져야할 때 사용할 수 있습니다. 예를 들어, 이메일 주소와 이메일 주소 확인 필드를 만든다고 합시다. 이 검증 헬퍼는 가상의 속성을 생성합니다. 그 속성의 이름은 확인하고 싶은 속성명에 "_confirmation"을 추가하면 됩니다. +이 헬퍼는 2개의 텍스트 필드가 완전히 일치하는 내용을 가져야할 때 사용할 수 +있습니다. 예를 들어, 이메일 주소와 이메일 주소 확인 필드를 만든다고 합시다. +이 검증 헬퍼는 가상의 속성을 생성합니다. 그 속성의 이름은 확인하고 싶은 +속성명에 "_confirmation"을 추가하면 됩니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :email, confirmation: true end ``` @@ -233,26 +311,40 @@ end 이 검증은 `email_confirmation`가 `nil`이 아닌 경우에만 수행됩니다. 필수로 확인하기 위해서는 확인용의 속성값을 받아야 한다는 검증을 추가해주세요(아래와 같이 설정하면 됩니다. `presence`에 대해서는 아래에서 설명합니다). ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :email, confirmation: true validates :email_confirmation, presence: true end ``` +`:case_sensitive` 옵션을 사용하여 확인시에 대소문자를 구별할지 아닐지를 +결정할 수 있습니다. 이 옵션의 기본값은 true입니다. + +```ruby +class Person < ApplicationRecord + validates :email, confirmation: { case_sensitive: false } +end +``` + 이 헬퍼의 기본 에러 메시지는 _"doesn't match confirmation"_ 입니다. ### `exclusion` -이 헬퍼는 주어진 집합의 속성의 값이 포함되어있지 '않은지' 검사합니다(블랙 리스트). 집합은 임의의 enumerable한 객체를 사용할 수 있습니다. +이 헬퍼는 주어진 집합의 속성의 값이 포함되어있지 '않은지' 검사합니다(블랙 +리스트). 집합은 임의의 열거 가능한 객체를 사용할 수 있습니다. ```ruby -class Account < ActiveRecord::Base +class Account < ApplicationRecord validates :subdomain, exclusion: { in: %w(www us ca jp), message: "%{value}는 예약어입니다." } end ``` -`exclusion`헬퍼의 `:in` 옵션에는 검증시에 포함되어서는 안되는 값들의 집합을 넘겨줍니다. `:in` 옵션에는 `:within`이라는 동의어도 있으므로, 편의에 맞춰서 어느 쪽이든 사용할 수 있습니다. 위의 예제에서는 `:message` 옵션에서 속성 값을 어떻게 사용하는지를 보여주고 있습니다. +`exclusion` 헬퍼의 `:in` 옵션에는 검증시에 포함하면 안되는 값들의 집합을 +넘겨줍니다. `:in` 옵션에는 `:within`이라는 동의어도 있으므로, 편의에 맞춰서 +어느 쪽이든 사용할 수 있습니다. 위의 예제에서는 `:message` 옵션에서 속성 값을 +어떻게 사용하는지를 보여주고 있습니다. 메시지 인수에 대한 전체 설명은 +[메시지 문서](#message)를 참고하세요. 기본 에러 메시지는 _"is reserved"_ 입니다. @@ -261,27 +353,33 @@ end 이 헬퍼는 `with`옵션으로 주어진 정규표현식과 속성의 값이 매칭되는지 확인합니다. ```ruby -class Product < ActiveRecord::Base +class Product < ApplicationRecord validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/, message: "영문자만 사용할수 있습니다." } end ``` +반대로, 매칭되어서는 안되는 정규 표현식을 `:without` 옵션으로 넘길 수 있습니다. + 기본 에러 메시지는 _"is invalid"_ 입니다. ### `inclusion` 이 헬퍼는 주어진 집합에 속성값이 포함되어있는지 확인합니다. -집합으로서 임의의 enumerable한 객체를 사용할 수 있습니다. +집합으로서 임의의 열거 가능한 객체를 사용할 수 있습니다. ```ruby -class Coffee < ActiveRecord::Base +class Coffee < ApplicationRecord validates :size, inclusion: { in: %w(small medium large), -message: "%{value} 사이즈는 존재하지 않습니다." } + message: "%{value} 사이즈는 존재하지 않습니다." } end ``` -`inclusion` 헬퍼에는 `:in` 옵션이 있으며, 사용 가능한 값들을 지정합니다(화이트 리스트). `:in` 옵션에는 `:within`이라는 동의어가 있으며, 편한 것을 사용하면 됩니다. 위의 예제에서는 `:message` 옵션에서 어떻게 속성 값을 사용하는지를 보여주고 있습니다. +`inclusion` 헬퍼에는 `:in` 옵션이 있으며, 사용 가능한 값들을 지정합니다(화이트 +리스트). `:in` 옵션에는 `:within`이라는 동의어가 있으며, 편한 것을 사용하면 +됩니다. 위의 예제에서는 `:message` 옵션에서 어떻게 속성 값을 사용하는 지를 +보여주고 있습니다. 메시지 인수에 대한 전체 설명은 [메시지 문서](#message)를 +참고하세요. 이 헬퍼의 기본 에러 메시지는 _"is not included in the list"_ 입니다. @@ -290,7 +388,7 @@ end 이 헬퍼는 속성값의 길이를 검사합니다. 여러가지 옵션이 있어서, 다양한 방식으로 길이 제한을 설정할 수 있습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, length: { minimum: 2 } validates :bio, length: { maximum: 500 } validates :password, length: { in: 6..20 } @@ -305,30 +403,23 @@ end * `:in` 또는 `:within` - 속성의 길이는 주어진 구간 내에 존재해야하며, Range 객체를 넘겨주어야 합니다. * `:is` - 속성의 길이는 주어진 값과 동일해야합니다. -기본 에러 메시지는 실행된 검사에 따라서 다릅니다. `:wrong_length`, `:too_long`, `:too_short` 옵션을 사용해서 변경할 수 있으며, `%{count}`를 길이 제한을 나타내는 플레이스 홀더로 사용할 수 있습니다. `:message` 옵션을 사용해서 에러 메시지를 지정할 수도 있습니다. +기본 에러 메시지는 실행된 검사에 따라서 다릅니다. `:wrong_length`, `:too_long`, +`:too_short` 옵션을 사용해서 변경할 수 있으며, `%{count}`를 길이 제한을 +나타내는 플레이스 홀더로 사용할 수 있습니다. `:message` 옵션을 사용해서 +에러 메시지를 지정할 수도 있습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :bio, length: { maximum: 1000, -too_long: "최대 %{count}글자까지 작성할 수 있습니다." } + too_long: "%{count} characters is the maximum allowed" } end ``` -이 헬퍼는 기본적으로 글자 단위의 길이를 검사합니다만, `:tokenizer` 옵션을 사용하면 다른 방식으로 길이를 계산할 수도 있습니다. - -```ruby -class Essay < ActiveRecord::Base - validates :content, length: { - minimum: 300, - maximum: 400, - tokenizer: lambda { |str| str.scan(/\w+/) }, - too_short: "%{count}개 이상의 단어가 필요합니다.", - too_long: "사용 가능한 최대 단어 수는 %{count}입니다." - } -end -``` - -기본 에러 메시지는 복수형으로 표현된다는 점에 주의해주세요(ex: "is too short (minimum is %{count} characters)"). 그러므로 `:minimum`을 1로 설정하는 경우라면 메시지를 커스터마이즈해서 단수형으로 만들거나, 이 헬퍼 대신 `presence: true`를 사용하면 됩니다. `:in` 또는 `:within` 으로 1보다 작은 값을 지정하는 경우, 메시지를 변경하거나, `length` 헬퍼보다 `presence`를 먼저 호출하세요. +기본 에러 메시지가 복수형이라는 점을 주의하세요(e.g. "is too short (minimum +is %{count} characters)"). 이런 이유로 `:minimum`이 1인 경우에는 별도의 +메시지를 넘겨주거나 `presence: true`를 사용하세요. `:in`이나 `:within`이 1보다 +작은 제한을 사용한다면 이 역시 별도의 메시지를 쓰거나 `length`보다 `presence`를 +사용하세요. ### `numericality` @@ -340,12 +431,13 @@ end /\A[+-]?\d+\Z/ ``` -위의 정규표현을 사용해서 속성값을 검증합니다. 그렇지 않은 경우에는 값을 `Float`로 변환해서 검증을 시도합니다. +위의 정규표현을 사용해서 속성값을 검증합니다. 그렇지 않은 경우에는 값을 +`Float`로 변환해서 검증을 시도합니다. -WARNING: 위의 정규표현은 맨 뒤에 개행 기호가 있어도 유효합니다. +WARNING: 위의 정규표현식은 맨 뒤에 개행 기호가 있어도 유효합니다. ```ruby -class Player < ActiveRecord::Base +class Player < ApplicationRecord validates :points, numericality: true validates :games_played, numericality: { only_integer: true } end @@ -358,17 +450,22 @@ end * `:equal_to` - 지정된 값과 검사하는 값이 같아야합니다. 기본 에러 메시지는 _"must be equal to %{count}"_ 입니다. * `:less_than` - 이 옵션으로 넘겨진 값보다 검사하는 값이 작아야 합니다. 기본 에러 메시지는 _"must be less than %{count}"_ 입니다. * `:less_than_or_equal_to` - 검사하는 값의 최대값을 지정합니다. 기본 에러 메시지는 _"must be less than or equal to %{count}"_ 입니다. +* `:other_than` - 이 옵션으로 넘겨진 값이 아니기를 기대합니다. 기본 에러 메시지는 _"must be other than %{count}"_ 입니다. * `:odd` - true로 설정하면, 값이 홀수인지 확인합니다. 기본 에러 메시지는 _"must be odd"_ 입니다. * `:even` - true로 설정하면, 값이 짝수인지 확인합니다. 기본 에러 메시지는 _"must be even"_ 입니다. +NOTE: `numericality`는 `nil`을 허가하지 않습니다. 필요하다면 +`allow_nil: true` 옵션을 사용하세요. + 기본 에러 메시지는 _"is not a number"_ 입니다. ### `presence` -이 헬퍼는 지정된 속성이 비어있는지 확인합니다. 값이 `nil`이나 공백 문자가 아닌 것을 확인 하기 위해서 `blank?` 메소드를 사용합니다. +이 헬퍼는 지정된 속성이 비어있는지 확인합니다. 값이 `nil`이나 공백 문자가 +아닌 것을 확인 하기 위해서 `blank?` 메소드를 사용합니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, :login, :email, presence: true end ``` @@ -376,7 +473,7 @@ end 관계 자체의 존재를 확인하기 위해서는, 관계가 설정된 객체가 존재하는지 확인할 필요가 있습니다. ```ruby -class LineItem < ActiveRecord::Base +class LineItem < ApplicationRecord belongs_to :order validates :order, presence: true end @@ -385,23 +482,30 @@ end 자신에게 속한 객체의 존재가 존재하는지 확인해야 하는 경우, 이를 위해서는 관계 설정 시에 `:inverse_of` 옵션을 지정할 필요가 있습니다. ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord has_many :line_items, inverse_of: :order end ``` 이 헬퍼를 사용해서, `has_one` 또는 `has_many` 관계를 통해서 연관된 객체의 존재를 검증할 때에는 `blank?`와 `marked_for_destruction?` 모두 false를 반환하는지 확인합니다. -`false.blank?`는 언제나 true 이므로 Boolean에 대해서 이 함수를 호출하는 경우에는 올바른 결과를 얻을 수 없습니다. Boolean에 대해 존재 검증을 하고 싶을 경우에는 `validates :field_name, inclusion: { in: [true, false] }`를 사용해야합니다. +`false.blank?`는 언제나 true 이므로 Boolean을 사용하는 값에 대해서 Boolean인지 +검증하려면 다음의 방법을 사용하세요. -기본 에러 메시지는 _"can't be blank"_ 입니다. +```ruby +validates :boolean_field_name, inclusion: { in: [true, false] } +validates :boolean_field_name, exclusion: { in: [nil] } +``` + +위 검증 중 하나를 사용하는 것으로 값이 `nil`이 넘어가서 `NULL` 값이 되는 +경우를 피할 수 있습니다. ### `absence` 이 헬퍼는 지정된 속성이 비어있는지 봅니다. 값이 `nil`이거나 공백문자인지 확인하기 위해서 `present?` 메소드를 사용합니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, :login, :email, absence: true end ``` @@ -409,7 +513,7 @@ end 관계가 없는지를 확인하고 싶은 경우에는, 관계가 있는 객체가 존재하는지를 확인하고 그 객체에 맵핑된 외래키의 존재 여부를 확인해야 합니다. ```ruby -class LineItem < ActiveRecord::Base +class LineItem < ApplicationRecord belongs_to :order validates :order, absence: true end @@ -418,23 +522,31 @@ end 관계된 레코드가 존재해서는 안되는 경우, 이를 검증하기 위해서는 관계 설정시에 `:inverse_of` 옵션을 지정해야할 필요가 있습니다. ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord has_many :line_items, inverse_of: :order end ``` -이 헬퍼를 사용해서 `has_one` 또는 `has_many` 관계에 있는 객체가 존재하는지 검증할 때에는 `presence?`와 `marked_for_destruction?`가 모두 false 를 반환하는지 확인합니다. +이 헬퍼를 사용해서 `has_one` 또는 `has_many` 관계에 있는 객체가 존재하는지 +검증할 때에는 `presence?`와 `marked_for_destruction?`가 모두 false 를 +반환하는지 확인합니다. -`false.present?`는 언제나 false이므로, Boolean에 대해서 이 메소드를 사용하면 올바른 결과를 얻을 수 없습니다. 이러한 경우에는 `validates :field_name, exclusion: { in: [true, false] }`을 사용하면 됩니다. +`false.present?`는 언제나 false이므로, Boolean에 대해서 이 메소드를 사용하면 +올바른 결과를 얻을 수 없습니다. 이러한 경우에는 +`validates :field_name, exclusion: { in: [true, false] }`을 사용하면 됩니다. 기본 에러 메시지는 _"must be blank"_ 입니다. ### `uniqueness` -이 헬퍼는 객체가 저장되기 전에 속성값이 유일한지(unique) 확인합니다. 이 헬퍼는 데이터베이스 자체에 유일성 제약을 추가한 것이 아니기 때문에, 하나의 데이터베이스에 접속한 2개의 접속에서 유일하기를 바라는 어떤 값을 2개 생성하는 상황이 발생할 수 도 있습니다. 이를 피하기 위해서는 데이터베이스에도 유일성 제약을 설정해둘 필요가 있습니다. 복합 인덱스에 대해서는 [MySQL 메뉴얼](http://dev.mysql.com/doc/refman/5.6/en/multiple-column-indexes.html)(영어)을 참조해주세요. +이 헬퍼는 객체가 저장되기 전에 속성값이 유일한지(unique) 확인합니다. 이 헬퍼는 +데이터베이스 자체에 유일성 제약을 추가한 것이 아니기 때문에, 하나의 +데이터베이스에 접속한 2개의 접속에서 유일하기를 바라는 어떤 값을 2개 생성하는 +상황이 발생할 수 도 있습니다. 이를 피하기 위해서는 데이터베이스에도 유일성 +제약을 설정해둘 필요가 있습니다. ```ruby -class Account < ActiveRecord::Base +class Account < ApplicationRecord validates :email, uniqueness: true end ``` @@ -444,16 +556,23 @@ end 이 헬퍼에는 유일성 체크의 기간을 제한하기 위해 사용 가능한 `:scope` 옵션이 있습니다. ```ruby -class Holiday < ActiveRecord::Base +class Holiday < ApplicationRecord validates :name, uniqueness: { scope: :year, message: "년에 1회만 사용할 수 있습니다." } end ``` -이 헬퍼에는 `:case_sensitive`라는 옵션도 있습니다. 이것은 제약조건을 검사할 때에 대소문자를 구분할지를 지정합니다. 이 옵션의 기본값은 true입니다. +`:scope` 옵션을 통해서 발생할 수 있는 잘못된 유일성 제약 처리를 피하기 위해 +데이터베이스 제약을 사용하고 싶은 경우에는 두 컬럼 모두에 유일성 제약을 걸어야 +합니다. [MySQL 매뉴얼](http://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html)의 +다중 컬럼 인덱스나 [PostgreSQL 매뉴얼](http://www.articlegresql.org/docs/current/static/ddl-constraints.html)의 +여러 컬럼에 걸친 유일성 제약에 대한 예제를 참고하세요. + +이 헬퍼에는 `:case_sensitive`라는 옵션도 있습니다. 이것은 제약조건을 검사할 +때에 대소문자를 구분할지를 지정합니다. 이 옵션의 기본값은 true입니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, uniqueness: { case_sensitive: false } end ``` @@ -475,18 +594,24 @@ class GoodnessValidator < ActiveModel::Validator end end -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates_with GoodnessValidator end ``` -NOTE: `record.errors[:base]`에 추가되는 에러는 특정 속성에 대한 것이 아닌, 그 레코드 전체의 상태에 대한 것입니다. +NOTE: `record.errors[:base]`에 추가되는 에러는 특정 속성에 대한 것이 아닌, +그 레코드 전체의 상태에 대한 것입니다. -`validates_with`는 유효성 검사에 사용하는 하나의 클래스, 또는 클래스의 목록을 인수로 받습니다. `validates_with`에는 기본 에러 메시지가 없으며, 필요하다면 넘긴 검증용 클래스에서 레코드의 에러 컬렉션에 직접 추가해야합니다. +`validates_with`는 유효성 검사에 사용하는 하나의 클래스, 또는 클래스의 목록을 +인수로 받습니다. `validates_with`에는 기본 에러 메시지가 없으며, 필요하다면 +넘긴 검증용 클래스에서 레코드의 에러 컬렉션에 직접 추가해야합니다. -검증 메소드를 구현하기 위해서는 `record` 파라미터를 받아야 하며, 이 파라미터를 통해 검증될 레코드가 넘겨지게 됩니다. +검증 메소드를 구현하기 위해서는 `record` 파라미터를 받아야 하며, 이 파라미터를 +통해 검증될 레코드가 넘겨지게 됩니다. -다른 유효성 검사와 마찬가지로 `validates_with` 헬퍼에서도 `:if`, `:unless`, `:on` 옵션을 사용할 수 있습니다. 이외의 옵션을 넘길 경우, 검증용 클래스에 `options` 해시로 넘겨지게 됩니다. +다른 유효성 검사와 마찬가지로 `validates_with` 헬퍼에서도 `:if`, `:unless`, +`:on` 옵션을 사용할 수 있습니다. 이외의 옵션을 넘길 경우, 검증용 클래스에 +`options` 해시로 넘겨지게 됩니다. ```ruby class GoodnessValidator < ActiveModel::Validator @@ -497,17 +622,20 @@ class GoodnessValidator < ActiveModel::Validator end end -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates_with GoodnessValidator, fields: [:first_name, :last_name] end ``` -이 검증용 클래스는 애플리케이션의 생애 주기 내에서 *단 한번만 초기화*된다는 점을 기억해주세요. 검증이 이루어질 때마다 초기화되지 않으므로, 인스턴스 변수를 사용할 경우에는 충분히 주의해주세요. +이 검증용 클래스는 애플리케이션의 생애 주기 내에서 *단 한번만 초기화*된다는 +점을 기억해주세요. 검증이 이루어질 때마다 초기화되지 않으므로, 인스턴스 변수를 +사용할 경우에는 충분히 주의해주세요. -작성한 검증용 클래스가 복잡해져서 인스턴스 변수를 사용하고 싶어질 경우에는, 루비 객체를 그냥 사용할 수도 있습니다. +작성한 검증용 클래스가 복잡해져서 인스턴스 변수를 사용하고 싶어질 경우에는, +루비 객체를 그냥 사용할 수도 있습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validate do |person| GoodnessValidator.new(person).validate end @@ -530,17 +658,22 @@ end ### `validates_each` -이 헬퍼는 1개의 블록에 대해서 속성을 검사합니다. 정의되어있는 검증용 함수는 없으므로 블록을 사용하는 검사를 직접 작성하고, `validates_each`에 넘기는 모든 속성에 대해서 블록을 통해 테스트를 수행합니다. 아래의 예제에서는 성과 이름이 대문자로만 시작하도록 하고 있습니다. +이 헬퍼는 1개의 블록에 대해서 속성을 검사합니다. 정의되어 있는 검증용 함수는 +없으므로 블록을 사용하는 검사를 직접 작성하고, `validates_each`에 넘기는 모든 +속성에 대해서 블록을 통해 테스트를 수행합니다. 아래의 예제에서는 성과 이름이 +대문자로만 시작하도록 하고 있습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates_each :name, :surname do |record, attr, value| record.errors.add(attr, 'must start with upper case') if value =~ /\A[a-z]/ end end ``` -이 블록은 레코드와 속성의 이름, 그리고 속성의 값을 넘겨줍니다. 블록에서 이것들을 사용해 데이터가 올바른지를 체크할 수 있습니다. 검증에 실패한 경우에는 모델에 에러메시지를 추가하여 검증이 무효가 되도록 해주세요. +이 블록은 레코드와 속성의 이름, 그리고 속성의 값을 넘겨줍니다. 블록에서 +이것들을 사용해 데이터가 올바른지를 체크할 수 있습니다. 검증에 실패한 경우에는 +모델에 에러메시지를 추가하여 검증이 무효가 되도록 해주세요. 공통의 검증 옵션 ------------------------- @@ -552,18 +685,22 @@ end `:allow_nil` 옵션은 대상의 값이 `nil`인 경우에 검증을 시도하지 않습니다. ```ruby -class Coffee < ActiveRecord::Base +class Coffee < ApplicationRecord validates :size, inclusion: { in: %w(small medium large), message: "%{value}은(는) 유효한 값이 아닙니다." }, allow_nil: true end ``` +메시지 인수에 대한 전체 설명은 [메시지 문서](#message)를 참고하세요. + ### `:allow_blank` -`:allow_blank` 옵션은 `:allow_nil` 옵션과 비슷합니다. 이 옵션을 사용하면, 속성의 값이 `blank?`에 해당하는 경우 검증을 시도하지 않습니다. `blank?`로 true를 반환하는 값은 `nil`과 공백문자를 포함합니다. +`:allow_blank` 옵션은 `:allow_nil` 옵션과 비슷합니다. 이 옵션을 사용하면, +속성의 값이 `blank?`에 해당하는 경우 검증을 시도하지 않습니다. `blank?`로 +true를 반환하는 값은 `nil`과 공백문자를 포함합니다. ```ruby -class Topic < ActiveRecord::Base +class Topic < ApplicationRecord validates :title, length: { is: 5 }, allow_blank: true end @@ -573,14 +710,46 @@ Topic.create(title: nil).valid? # => true ### `:message` -이전에도 보였듯, 검증에 실패 했을 때 `eroros` 컬렉션에 추가될 에러 메시지를 `:message` 옵션으로 설정할 수 있습니다. 이 옵션을 사용하지 않는 경우, Active Record는 검증 헬퍼의 기본 에러 메시지를 사용합니다. +이전에도 보였듯, 검증에 실패 했을 때 `errors` 컬렉션에 추가될 에러 메시지를 +`:message` 옵션으로 설정할 수 있습니다. 이 옵션을 사용하지 않는 경우, +Active Record는 검증 헬퍼의 기본 에러 메시지를 사용합니다. `:message` 옵션은 +`String`이나 `Proc`을 받습니다. + +문자열 `:message` 값은 `%{value}`, `%{attribute}`, `%{model}`를 받을 수 있으며 +이것들은 검증이 실패했을 때에 자동으로 각각의 값으로 대체됩니다. 이 대체는 +I18n 젬을 통해서 이루어지며, 이 플레이스 홀더들은 공백없이 반드시 일치해야 +합니다. + +`Proc`을 `:message` 값으로 사용하는 경우 내부에 검증되는 객체와 `:model`, +`:attribute`, `:value`가 들어 있는 해시 객체를 넘깁니다. + +```ruby +class Person < ApplicationRecord + # Hard-coded message + validates :name, presence: { message: "must be given please" } + + # 메시지는 동적인 값입니다. %{value}는 속성의 실제 값으로 대체되며, + # %{attribute}와 %{model}도 사용 가능합니다. + validates :age, numericality: { message: "%{value} seems wrong" } + + # Proc + validates :username, + uniqueness: { + # object = person object being validated + # data = { model: "Person", attribute: "Username", value: } + message: ->(object, data) do + "Hey #{object.name}!, #{data[:value]} is taken already! Try again #{Time.zone.tomorrow}" + end + } +end +``` ### `:on` `:on` 옵션은 유효성 검사의 실행 타이밍을 지정합니다. 기본적으로 내장 검증 헬퍼는 저장할 때에 실행됩니다. 이것은 레코드의 생성할 때, 갱신할 때 모두 실행됩니다. 실행 타이밍을 변경하고 싶은 경우 `on: :create`를 지정하면 레코드가 생성될 때만 검증이 수행되며 `on: :update`를 지정하면 레코드를 변경할 때만 검증이 수행됩니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord # 값이 중복되어 있어도 email을 변경할 수 있음 validates :email, uniqueness: true, on: :create @@ -592,40 +761,65 @@ class Person < ActiveRecord::Base end ``` +또는 별도의 컨텍스트를 넘길수도 있습니다. 이를 사용하려면 `valid?`와 +`invalid?`, `save` 호출 시에 명시적으로 해당 컨텍스트 이름을 넘겨야 합니다. + +```ruby +class Person < ApplicationRecord + validates :email, uniqueness: true, on: :account_setup + validates :age, numericality: true, on: :account_setup +end + +person = Person.new +``` + +`person.valid?(:account_setup)`는 모델을 저장하지 않고 위의 두 검증을 +실행합니다. 그리고 `person.save(context: :account_setup)`는 `person`을 +저장하기 전에 `account_setup` 컨텍스트 하에서 검증합니다. 이러한 명시적인 +컨텍스트 지정을 사용하는 경우에는 해당하는 컨텍스트의 검증과 컨텍스트가 +존재하지 않는 검증만을 사용합니다. + 엄격한 유효성 검사 ------------------ -객체가 유효하지 않을 경우에 `ActiveModel::StrictValidationFailed` 가 발생하도록 할 수 있습니다. +객체가 유효하지 않을 경우에 `ActiveModel::StrictValidationFailed`가 +발생하도록 할 수 있습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: { strict: true } end Person.new.valid? # => ActiveModel::StrictValidationFailed: 반드시 이름을 입력해야 합니다. ``` -다른 예외를 `:strict` 옵션으로 추가할 수 도 있습니다. +다른 예외를 `:strict` 옵션으로 추가할 수도 있습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :token, presence: true, uniqueness: true, strict: TokenGenerationException end -Person.new.valid? # => TokenGenerationException: 토큰 란이 공백일 수 없습니다. +Person.new.valid? # => TokenGenerationException: 토큰이 공백일 수 없습니다. ``` 조건부 유효성 검사 ---------------------- -특정 조건을 만족하는 경우에만 검증을 실행하고 싶을 때가 있습니다. `:if` 옵션이나 `:unless` 옵션을 사용하는 것으로 조건을 지정할 수 있습니다. 인수로는 심볼, 문자열, `Proc`이나 `Array`를 사용할 수 있습니다. `:if` 옵션은 특정 조건을 만족하는 경우에 유효성 검사를 **실행해야 하는** 경우에 사용합니다. 특정 조건을 만족하는 경우에 유효성 검사를 **실행하면 안되는** 경우에는 `:unless` 옵션을 사용합니다. +특정 조건을 만족하는 경우에만 검증을 실행하고 싶을 때가 있습니다. +`:if` 옵션이나 `:unless` 옵션을 사용하는 것으로 조건을 지정할 수 있습니다. +인수로는 심볼, 문자열, `Proc`이나 `Array`를 사용할 수 있습니다. `:if` 옵션은 +특정 조건을 만족하는 경우에 유효성 검사를 **실행해야 하는** 경우에 사용합니다. +특정 조건을 만족하는 경우에 유효성 검사를 **실행하면 안되는** 경우에는 +`:unless` 옵션을 사용합니다. ### `:if`나 `:unless`에서 심볼 사용하기 -유효성 검사를 실행하기 직전에 호출될 메소드의 이름을 심볼의 형태로 `:if`나 `:unless`에 지정할 수 있습니다. 이것은 가장 빈번하게 사용되는 방식입니다. +유효성 검사를 실행하기 직전에 호출될 메소드의 이름을 심볼의 형태로 `:if`나 +`:unless`에 지정할 수 있습니다. 이것은 가장 빈번하게 사용되는 방식입니다. ```ruby -class Order < ActiveRecord::Base +class Order < ApplicationRecord validates :card_number, presence: true, if: :paid_with_card? def paid_with_card? @@ -636,20 +830,24 @@ end ### `:if`나 `:unless`에서 문자열 사용하기 -문자열을 사용하는 것도 가능합니다. 이 문자열은 나중에 `eval`을 통해 평가되므로, 실행 가능한 올바른 Ruby 코드를 포함해야 합니다. 이 방법은 문자열이 충분히 짧은 경우에만 사용하는 것이 좋습니다. +문자열을 사용하는 것도 가능합니다. 이 문자열은 나중에 `eval`을 통해 평가되므로, +실행 가능한 올바른 Ruby 코드를 포함해야 합니다. 이 방법은 문자열이 충분히 짧은 +경우에만 사용하는 것이 좋습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :surname, presence: true, if: "name.nil?" end ``` ### `:if`나 `:unless`에서 Proc 사용하기 -호출하고 싶은 `Proc` 객체를 `:if`나 `:unless`에서 사용할 수 있습니다. `Proc` 객체를 사용하면 각각의 메소드를 지정하는 대신, 바로 조건을 적을 수 있다는 장점이 있습니다. 한 줄로 해결 가능한 경우에 많이 사용합니다. +호출하고 싶은 `Proc` 객체를 `:if`나 `:unless`에서 사용할 수 있습니다. +`Proc` 객체를 사용하면 각각의 메소드를 지정하는 대신, 바로 조건을 적을 수 +있다는 장점이 있습니다. 한 줄로 해결 가능한 경우에 많이 사용합니다. ```ruby -class Account < ActiveRecord::Base +class Account < ApplicationRecord validates :password, confirmation: true, unless: Proc.new { |a| a.password.blank? } end @@ -657,10 +855,11 @@ end ### 조건부 유효성 검사를 그룹화하기 -때때로 1개의 조건을 여러 유효성 검사에서 사용할 수 있다면 편리한 경우가 있습니다. 이것은 `with_options` 를 사용하면 간단하게 구현할 수 있습니다. +때때로 1개의 조건을 여러 유효성 검사에서 사용할 수 있다면 편리한 경우가 +있습니다. 이것은 `with_options` 를 사용하면 간단하게 구현할 수 있습니다. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord with_options if: :is_admin? do |admin| admin.validates :password, length: { minimum: 10 } admin.validates :email, presence: true @@ -668,30 +867,34 @@ class User < ActiveRecord::Base end ``` -`with_options` 블록에 있는 모든 유효성 검사는 `if: :is_admin?`라는 조건이 포함됩니다. +`with_options` 블록에 있는 모든 유효성 검사는 `if: :is_admin?`라는 조건이 +포함됩니다. ### 유효성 검사의 조건을 결합하기 -반대로 유효성 검사의 실행 조건을 여러개 정의하고 싶은 경우, `Array`를 사용할 수 있습니다. 동일한 유효성 검사에 대해서 `:if`와 `:unless`를 모두 사용할 수 있습니다. +반대로 유효성 검사의 실행 조건을 여러개 정의하고 싶은 경우, `Array`를 사용할 +수 있습니다. 동일한 유효성 검사에 대해서 `:if`와 `:unless`를 모두 사용할 수 +있습니다. ```ruby -class Computer < ActiveRecord::Base +class Computer < ApplicationRecord validates :mouse, presence: true, if: ["market.retail?", :desktop?] unless: Proc.new { |c| c.trackpad.present? } end ``` -여기에서는 `:if` 조건이 모두 `true`이고, `:unless` 조건이 하나도 `true`가 아닌 경우에만 실행됩니다. +여기에서는 `:if` 조건이 모두 `true`이고, `:unless` 조건이 하나도 `true`가 아닌 +경우에만 실행됩니다. 커스텀 유효성 검사를 실행하기 ----------------------------- -내장 검증 헬퍼만으로는 부족한 경우, 원하는 유효성 검사기나 검증 메소드를 작성할 수 있습니다. +내장 검증 헬퍼만으로는 부족한 경우, 원하는 유효성 검증자나 검증 메소드를 작성할 수 있습니다. -### 커스텀 유효성 검사기 +### 커스텀 유효성 검증자 -커스텀 유효성 검사기(validator)는 `ActiveModel::Validator`을 확장한 클래스입니다. 여기에서는 `validate` 메소드를 구현할 필요가 있습니다. 이 메소드는 레코드를 하나를 인수로 받고, 받은 레코드에 대해서 검증을 수행합니다. 커스텀 유효성 검사기는 `validates_with` 메소드를 이용해서 호출할 수 있습니다. +커스텀 유효성 검증자(validator)는 `ActiveModel::Validator`을 확장한 클래스입니다. 여기에서는 `validate` 메소드를 구현할 필요가 있습니다. 이 메소드는 레코드를 하나를 인수로 받고, 받은 레코드에 대해서 검증을 수행합니다. 커스텀 유효성 검증자는 `validates_with` 메소드를 이용해서 호출할 수 있습니다. ```ruby class MyValidator < ActiveModel::Validator @@ -708,7 +911,11 @@ class Person end ``` -각각의 속성을 검증하기 위한 커스텀 유효성 검사기를 추가하기 위해서는 `ActiveModel::EachValidator`를 사용하는 것이 가장 간단하고 쉽습니다. 이 경우, 커스텀 유효성 검사기는 `validate_each` 메소드를 구현해야할 필요가 있습니다. 이 메소드는 그 인스턴스에 대응하는 레코드, 속성의 이름과 그 값을 인자로 넘겨 받습니다. +각각의 속성을 검증하기 위한 커스텀 유효성 검증자를 추가하기 위해서는 +`ActiveModel::EachValidator`를 사용하는 것이 가장 간단하고 쉽습니다. 이 경우, +커스텀 유효성 검증자는 `validate_each` 메소드를 구현해야할 필요가 있습니다. +이 메소드는 그 인스턴스에 대응하는 레코드, 속성의 이름과 그 값을 인자로 넘겨 +받습니다. ```ruby class EmailValidator < ActiveModel::EachValidator @@ -719,7 +926,7 @@ class EmailValidator < ActiveModel::EachValidator end end -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :email, presence: true, email: true end ``` @@ -728,12 +935,19 @@ end ### 커스텀 메소드 -모델의 상태를 확인하고, 유효하지 않은 경우에 `errors` 컬렉션에 에러 메시지를 추가하는 메소드를 만들 수도 있습니다. 이 메소드를 통해 유효성 검사를 하기 위해서는 `validate` 메소드에 검증 메소드를 가리키는 심볼을 넘길 필요가 있습니다. +모델의 상태를 확인하고, 유효하지 않은 경우에 `errors` 컬렉션에 에러 메시지를 +추가하는 메소드를 만들 수도 있습니다. 이 메소드를 통해 유효성 검사를 하기 +위해서는 `validate`([API](http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate)) +클래스 메소드에 검증 메소드를 가리키는 심볼을 넘길 필요가 있습니다. + +하나의 `validate` 메소드에는 여러 개의 심볼을 넘길 수 있습니다. 이 메소드들은 +등록된 순서대로 실행됩니다. -하나의 `validate` 메소드에는 여러 개의 심볼을 넘길 수 있습니다. 이 메소드들은 등록된 순서대로 실행됩니다. +`valid?` 매소드는 errors 컬렉션이 비어있는지 확인하므로, 커스텀 검증 메소드는 +검증이 실패하기 바라는 경우에 에러를 추가해야 합니다. ```ruby -class Invoice < ActiveRecord::Base +class Invoice < ApplicationRecord validate :expiration_date_cannot_be_in_the_past, :discount_cannot_be_greater_than_total_value @@ -751,10 +965,12 @@ class Invoice < ActiveRecord::Base end ``` -이 검증들은 `valid?`가 호출될 때 실행됩니다. 커스텀 유효성 검사가 실행되는 시점은 `:on` 옵션을 통해서 변경할 수 있습니다. 예를 들어, `validate`에 대해서 `on: :create` 또는 `on: :update`를 설정할 수 있습니다. +이 검증들은 `valid?`가 호출될 때 실행됩니다. 커스텀 유효성 검사가 실행되는 +시점은 `:on` 옵션을 통해서 변경할 수 있습니다. 예를 들어, `validate`에 대해서 +`on: :create` 또는 `on: :update`를 설정할 수 있습니다. ```ruby -class Invoice < ActiveRecord::Base +class Invoice < ApplicationRecord validate :active_customer, on: :create def active_customer @@ -766,23 +982,26 @@ end 검증 에러 사용하기 ------------------------------ -Rails에는 이미 설명한 `valid?`나 `invalid?` 메소드 이외에도, `errors` 컬렉션를 다루는 메소드가 여럿 있습니다. +Rails에는 이미 설명한 `valid?`나 `invalid?` 메소드 이외에도, `errors` 컬렉션을 +다루는 메소드가 여럿 있습니다. -아래는 자주 사용되는 메소드 목록입니다. 사용가능한 모든 메소드 목록에 대해서는 `ActiveModel::Errors` 문서를 참조해주세요. +아래는 자주 사용되는 메소드 목록입니다. 사용가능한 모든 메소드 목록에 대해서는 +`ActiveModel::Errors` 문서를 참조해주세요. ### `errors` -모든 에러를 포함하는 `ActiveModel::Errors` 클래스의 인스턴스를 하나 반환합니다. 키는 속성명, 값은 모든 에러 문자열의 배열입니다. +모든 에러를 포함하는 `ActiveModel::Errors` 클래스의 인스턴스를 하나 반환합니다. +키는 속성명, 값은 모든 에러 문자열의 배열입니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end person = Person.new person.valid? # => false person.errors.messages -# => {:name=>["을 비워둘 수 없습니다.", "이 너무 짧습니다. (최소 3글자)"]} + # => {:name=>["을 비워둘 수 없습니다.", "이 너무 짧습니다. (최소 3글자)"]} person = Person.new(name: "John Doe") person.valid? # => true @@ -791,10 +1010,13 @@ person.errors.messages # => {} ### `errors[]` -`errors[]` 는 어떤 속성에 대한 에러 메시지를 확인하고 싶을 때에 사용하며, 어떤 속성에 대한 모든 에러 메시지를 포함하는 문자열 배열을 반환합니다. 하나의 문자열 당 하나의 에러 메시지입니다. 속성에 관한 에러가 없는 경우, 빈 배열을 반환합니다. +`errors[]` 는 어떤 속성에 대한 에러 메시지를 확인하고 싶을 때에 사용하며, 어떤 +속성에 대한 모든 에러 메시지를 포함하는 문자열 배열을 반환합니다. 하나의 +문자열 당 하나의 에러 메시지입니다. 속성에 관한 에러가 없는 경우, 빈 배열을 +반환합니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end @@ -814,10 +1036,17 @@ person.errors[:name] ### `errors.add` -`add` 메소드를 사용해서, 어떤 속성에 대한 메시지를 직접 추가할 수 있습니다. `errors.full_messages`나 `errors.to_a` 메소드를 사용해서, 사용자가 실제로 보게 될 양식에 에러 메시지를 출력할 수 있습니다. 이 경우, 각각의 메시지에는 각 속성명이 추가되며, 그 첫번째 글자는 대문자로 변경됩니다. `add` 메소드는 에러 메시지를 추가하고 싶은 속성명, 그리고 메시지의 내용을 인자로 넘겨받습니다. +`add` 메소드를 사용해서, 어떤 속성에 대한 메시지를 직접 추가할 수 있습니다. +인수로 속성명과 에러 메시지를 받습니다. + +`errors.full_messages`나 `errors.to_a` 메소드를 사용해서, 사용자가 실제로 보게 +될 양식에 에러 메시지를 출력할 수 있습니다. 이 경우, 각각의 메시지에는 +각 속성명이 추가되며, 그 첫번째 글자는 대문자로 변경됩니다. `add` 메소드는 +에러 메시지를 추가하고 싶은 속성명, 그리고 메시지의 내용을 인자로 +넘겨받습니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.add(:name, "에는 다음의 문자를 포함할 수 없습니다. !@#%*()_-+=") end @@ -832,12 +1061,13 @@ person.errors.full_messages # => ["Name 에는 다음의 문자를 포함할 수 없습니다. !@#%*()_-+="] ``` -`[]=`를 사용해도 같은 결과를 얻을 수 있습니다. +`errors#add` 대신에 `<<`를 사용하여 `errors.messages`에 에러를 추가할 수도 +있습니다. ```ruby -class Person < ActiveRecord::Base + class Person < ApplicationRecord def a_method_used_for_validation_purposes - errors[:name] = "에는 다음의 문자를 포함할 수 없습니다. !@#%*()_-+=" + errors.messages[:name] << "에는 다음의 문자를 포함할 수 없습니다. !@#%*()_-+=" end end @@ -848,14 +1078,54 @@ class Person < ActiveRecord::Base person.errors.to_a # => ["Name 에는 다음의 문자를 포함할 수 없습니다. !@#%*()_-+="] +`` + +### `errors.details` + +`errors.add` 메소드를 사용하여 에러를 반환한 검증자의 정보를 추가할 수 +있습니다. + +```ruby +class Person < ApplicationRecord + def a_method_used_for_validation_purposes + errors.add(:name, :invalid_characters) + end +end + +person = Person.create(name: "!@#") + +person.errors.details[:name] +# => [{error: :invalid_characters}] ``` +`errors.details`에 에러 메시지를 좀 더 자세하게 작성하고 싶다면, `errors.add`에 +추가로 정보를 넘기면 됩니다. + +```ruby +class Person < ApplicationRecord + def a_method_used_for_validation_purposes + errors.add(:name, :invalid_characters, not_allowed: "!@#%*()_-+=") + end +end + +person = Person.create(name: "!@#") + +person.errors.details[:name] +# => [{error: :invalid_characters, not_allowed: "!@#%*()_-+="}] +``` + +모든 Rails의 내장 검증자는 details 해시에 그에 맞는 검증자 형식과 함께 에러를 +추가합니다. + ### `errors[:base]` -개별 속성에 대한 메시지를 추가하는 대신, 객체 자체에 대한 에러 메시지를 추가할 수도 있습니다. 속성의 값에 관계 없이 객체를 유효하지 않다고 판정하고 싶은 경우에는 사용할 수 있습니다. `errors[:base]` 는 베열이므로 여기에 문자열을 추가하기만 하면 됩니다. +개별 속성에 대한 메시지를 추가하는 대신, 객체 자체에 대한 에러 메시지를 추가할 +수도 있습니다. 속성의 값에 관계 없이 객체를 유효하지 않다고 판정하고 싶은 +경우에 사용할 수 있습니다. `errors[:base]` 는 배열이므로 여기에 문자열을 +추가하기만 하면 됩니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord def a_method_used_for_validation_purposes errors[:base] << "이 사람은 다음과 같은 이유로 가입할 수 없습니다. 이하 생략" end @@ -864,10 +1134,15 @@ end ### `errors.clear` -`clear` 메소드는 `errors` 컬렉션에 포함되는 메시지를 모두 삭제하고 싶을 경우에 사용할 수 있습니다. 유효하지 않은 객체에 대해서 `errors.clear`를 호출하더라도 유효한 객체가 되지 않음을 주의해 주세요. `errors`의 값은 없어집니다만, `valid?`나 객체를 저장하는 어떤 메소드(save, update, ...)가 호출 될 경우에 유효성 검사가 재실행되기 때문입니다. 그 때 다시 검증에 실패하면 `errors` 컬렉션에 다시 에러 메시지가 쌓이게 됩니다. +`clear` 메소드는 `errors` 컬렉션에 포함되는 메시지를 모두 삭제하고 싶을 경우에 +사용할 수 있습니다. 유효하지 않은 객체에 대해서 `errors.clear`를 호출하더라도 +유효한 객체가 되지 않음을 주의해 주세요. `errors`의 값은 없어집니다만, +`valid?`나 객체를 저장하는 어떤 메소드(save, update, ...)가 호출 될 경우에 +유효성 검사가 재실행되기 때문입니다. 그 때 다시 검증에 실패하면 `errors` +컬렉션에 다시 에러 메시지가 쌓이게 됩니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end @@ -890,7 +1165,7 @@ p.errors[:name] `size` 메소드는 그 객체가 가지고 있는 에러 메시지의 전체 갯수를 반환합니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end @@ -906,19 +1181,25 @@ person.errors.size # => 0 검증 에러를 뷰에서 출력하기 ------------------------------------- -모델을 만들고, 유효성 검사를 추가한 뒤, 웹페이지에서 양식을 사용해서 그 모델을 생성할 수 있게 되면 그 모델의 검증이 실패했을 때에 에러 메시지를 표시할 수 있길 바랄 겁니다. +모델을 만들고, 유효성 검사를 추가한 뒤, 웹페이지에서 양식을 사용해서 그 모델을 +생성할 수 있게 되면 그 모델의 검증이 실패했을 때에 에러 메시지를 표시할 수 +있길 바랄 겁니다. -에러 메시지의 표시 방법은 애플리케이션마다 다르기 때문에, Rails에서는 이런 메시지를 직접 생성하는 뷰 헬퍼를 제공하지 않습니다. 대신 Rails는 일반적인 검증 메소드가 여럿 제공되므로 커스텀 메소드를 만드는 것도 비교적 간단합니다. 또한, scaffold를 사용해서 뷰를 생성하게 되면, 그 모델의 에러 메시지를 전부 표시할 수 있는 ERB가 `_form.html.erb`에 추가됩니다. +에러 메시지의 표시 방법은 애플리케이션마다 다르기 때문에, Rails에서는 이런 +메시지를 직접 생성하는 뷰 헬퍼를 제공하지 않습니다. 대신 Rails는 일반적인 +검증 메소드가 여럿 제공되므로 커스텀 메소드를 만드는 것도 비교적 간단합니다. +또한, scaffold를 사용해서 뷰를 생성하게 되면, 그 모델의 에러 메시지를 전부 +표시할 수 있는 ERB가 `_form.html.erb`에 추가됩니다. -`@post`라는 이름의 인스턴스 변수에 보존된 모델이 있다고 가정합시다. +`@article`라는 이름의 인스턴스 변수에 보존된 모델이 있다고 가정합시다. ```ruby -<% if @post.errors.any? %> +<% if @article.errors.any? %>
-

<%= pluralize(@post.errors.count, "error") %> 때문에 이 글을 저장할 수 없습니다:

+

<%= pluralize(@article.errors.count, "error") %> 때문에 이 글을 저장할 수 없습니다:

    - <% @post.errors.full_messages.each do |msg| %> + <% @article.errors.full_messages.each do |msg| %>
  • <%= msg %>
  • <% end %>
@@ -926,15 +1207,17 @@ person.errors.size # => 0 <% end %> ``` -그리고 Rails의 양식 헬퍼를 사용해서 양식을 생성하는 경우, 어떤 필드에서 검증 에러가 발생하면 그 필드 를 감싸는 `
` 태그가 자동적으로 추가됩니다. +그리고 Rails의 양식 헬퍼를 사용해서 양식을 생성하는 경우, 어떤 필드에서 +검증 에러가 발생하면 그 필드 를 감싸는 `
` 태그가 자동적으로 추가됩니다. ```
- +
``` -이 div 태그에 원하는 스타일을 적용할 수 있습니다. Rails가 생성하는 scaffold에 의해서 아래와 같은 css 규칙이 추가됩니다. +이 div 태그에 원하는 스타일을 적용할 수 있습니다. Rails가 생성하는 +scaffold에 의해서 아래와 같은 css 규칙이 추가됩니다. ``` .field_with_errors { @@ -944,6 +1227,4 @@ person.errors.size # => 0 } ``` -이 CSS는 에러를 포함하는 항목을 빨간 상자로 둘러쌉니다. - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. +이 CSS는 에러를 포함하는 항목을 2 픽셀의 외각선으로 둘러쌉니다. diff --git a/guides/source/ko/active_support_core_extensions.md b/guides/source/ko/active_support_core_extensions.md index aa7ba1c340562..613dba6691c4e 100644 --- a/guides/source/ko/active_support_core_extensions.md +++ b/guides/source/ko/active_support_core_extensions.md @@ -1,9 +1,11 @@ Active Support 코어 확장 기능 ============================== -Active Support는 Ruby on Rails의 구성 요소중 하나로, Ruby의 확장 기능, 유틸리티, 그 외의 작업 등을 담당하고 있습니다. +Active Support는 Ruby on Rails의 구성 요소중 하나로, Ruby의 확장 기능, +유틸리티, 그 외의 작업 등을 담당하고 있습니다. -Active Support는 언어 레벨에서 다양한 기능을 추가해주며, Rails 애플리케이션의 개발과 Ruby on Rails 자체의 개발을 지원하기 위한 목적으로 만들어졌습니다. +Active Support는 언어 레벨에서 다양한 기능을 추가해주며, Rails 애플리케이션의 +개발과 Ruby on Rails 자체의 개발을 지원하기 위한 목적으로 만들어졌습니다. 이 가이드의 내용: @@ -19,7 +21,11 @@ Active Support는 언어 레벨에서 다양한 기능을 추가해주며, Rails ### 독립적인 Active Support -흔적을 최대한 남기지 않기 위해서, Active Support는 기본적으로 아무것도 읽어들이지 않습니다. Active Support는 자잘하게 분할되어 필요한 확장 기능만 불러올 수 있도록 되어 있습니다. 또한 연관되어 있는 확장기능(상황에 따라서는 모든 확장 기능)도 동시에 불러올 때에 사용할 수 있는 엔트리 포인트도 포함하고 있습니다. +흔적을 최대한 남기지 않기 위해서, Active Support는 기본적으로 아무것도 +읽어들이지 않습니다. Active Support는 자잘하게 분할되어 필요한 확장 기능만 +불러올 수 있도록 되어 있습니다. 또한 연관되어 있는 확장기능(상황에 따라서는 +모든 확장 기능)도 동시에 불러올 때에 사용할 수 있는 엔트리 포인트도 포함하고 +있습니다. 따라서 아래와 같은 require문을 실행하더라도, @@ -27,13 +33,16 @@ Active Support는 언어 레벨에서 다양한 기능을 추가해주며, Rails require 'active_support' ``` -객체는 `blank?`에 응답하지 않습니다(역주: `black?`는 Active Support가 추가해주는 메소드 중 하나임). 이 정의가 어떤 식으로 로드되는지 확인해봅시다. +객체는 `blank?`에 응답하지 않습니다. 이 정의가 어떤 식으로 로드되는지 확인해봅시다. #### 필요한 정의만을 선택 -`blank?` 메소드를 사용하는 가장 '가벼운' 방법은 그 메소드가 정의되어있는 파일만을 선택해서 불러오는 것입니다. +`blank?` 메소드를 사용하는 가장 '가벼운' 방법은 그 메소드가 정의되어 있는 +파일만을 선택해서 불러오는 것입니다. -이 가이드에서는 코어 확장 기능으로 정의되어있는 모든 메소드에 대해서 그 정의 파일이 어디에 위치해 있는지를 적어두었습니다. 예를 들어 `blank?`의 경우, 아래와 같은 메모가 되어 있습니다. +이 가이드에서는 코어 확장 기능으로 정의되어있는 모든 메소드에 대해서 그 정의 +파일이 어디에 위치해 있는지를 적어두었습니다. 예를 들어 `blank?`의 경우, +아래와 같은 메모가 되어 있습니다. NOTE: `active_support/core_ext/object/blank.rb`에 정의되어 있습니다. @@ -44,13 +53,17 @@ require 'active_support' require 'active_support/core_ext/object/blank' ``` -Active Support는 무척 조심스러워서, 어떤 파일을 선택했을 경우 정말로 필요한 파일들만을 동시에 불러옵니다(의존 관계가 있는 경우). +Active Support는 무척 조심스러워서, 어떤 파일을 선택했을 경우 정말로 필요한 +파일들만을 동시에 불러옵니다(의존 관계가 있는 경우). #### 코어 확장 기능을 그룹화하여 불러오기 -다음 단계로 `Object`에 대한 모든 확장 기능을 불러와봅시다. 경험적으로 `SomeClass`라는 클래스가 있다면 `active_support/core_ext/some_class`라는 경로를 지정하면 한번에 읽어올 수 있습니다. +다음 단계로 `Object`에 대한 모든 확장 기능을 불러와봅시다. 경험적으로 +`SomeClass`라는 클래스가 있다면 `active_support/core_ext/some_class`라는 +경로를 지정하면 한번에 읽어올 수 있습니다. -따라서, (`blank?`를 포함하여)`Object`에 대한 모든 확장기능을 불러오기 위해서는 다음과 같이 작성하면 됩니다. +따라서, (`blank?`를 포함하여)`Object`에 대한 모든 확장기능을 불러오기 위해서는 +다음과 같이 작성하면 됩니다. ```ruby require 'active_support' @@ -68,17 +81,24 @@ require 'active_support/core_ext' #### 모든 Active Support를 읽어오기 -마지막으로 사용가능한 Active Support를 모두 불러오고 싶다면 이렇게 할 수 있습니다. +마지막으로 사용가능한 Active Support를 모두 불러오고 싶다면 이렇게 할 수 +있습니다. ```ruby require 'active_support/all' ``` -단, 이 코드를 실행하더라도 Active Support 전체가 메모리 상에 로드되는 것은 아닙니다. 일부는 `autoload`로 설정되어서, 실제로 사용하기 전까지는 로드되지 않습니다. +단, 이 코드를 실행하더라도 Active Support 전체가 메모리 상에 로드되는 것은 +아닙니다. 일부는 `autoload`로 설정되어서, 실제로 사용하기 전까지는 로드되지 +않습니다. ### Ruby on Rails 애플리케이션에서 Active Support를 사용하기 -Ruby on Rails 애플리케이션에서는 기본적으로 모든 Active Support를 불러옵니다. `active_support.bare`를 true로 설정했을 때는 예외입니다. 이 옵션을 true로 설정하면 프레임워크 자체가 필요로 할 때까지 애플리케이션은 확장 기능을 불러오지 않습니다. 또한 불러올 확장 기능은 위에서 이야기했듯이 각 부분별로 그때그때 선택됩니다. +Ruby on Rails 애플리케이션에서는 기본적으로 모든 Active Support를 불러옵니다. +`active_support.bare`를 true로 설정했을 때는 예외입니다. 이 옵션을 true로 +설정하면 프레임워크 자체가 필요로 할 때까지 애플리케이션은 확장 기능을 +불러오지 않습니다. 또한 불러올 확장 기능은 위에서 이야기했듯이 각 부분별로 +그때그때 선택됩니다. 모든 객체에서 사용할 수 있는 확장 기능 ------------------------- @@ -95,11 +115,15 @@ Rails 애플리케이션은 아래의 값을 공백(blank)라고 판단합니다 * 그 외, `empty?` 메소드에 true를 돌려주는 모든 객체를 비어있다고 생각합니다. -INFO: 문자열을 판정하기위해, Unicode에 대응하는 문자 클래스인 `[:space:]`를 사용합니다. 그러므로 예를 들어 U+2029(단락 구분자)역시 공백 문자로 판정됩니다. +INFO: 문자열을 판정하기위해, Unicode에 대응하는 문자 클래스인 `[:space:]`를 +사용합니다. 그러므로 예를 들어 U+2029(단락 구분자)역시 공백 문자로 판정됩니다. -WARNING: 숫자에 대해서는 공백인지 아닌지 판단할 수 없습니다. 특히 0이나 0.0은 **공백이 아니므로** 주의해주세요. +WARNING: 숫자에 대해서는 공백인지 아닌지 판단할 수 없습니다. 특히 0이나 0.0은 +**공백이 아니므로** 주의해주세요. -예를 들어 `ActionController::HttpAuthentication::Token::ControllerMethods`에 있는 아래의 메소드에서는 `blank?`를 사용해서 토큰이 존재하고 있는지를 확인합니다. +예를 들어 `ActionController::HttpAuthentication::Token::ControllerMethods`에 +있는 아래의 메소드에서는 `blank?`를 사용해서 토큰이 존재하고 있는지를 +확인합니다. ```ruby def authenticate(controller, &login_procedure) @@ -110,7 +134,8 @@ def authenticate(controller, &login_procedure) end ``` -`present?` 메소드는 `!blank?` 메소드와 동등합니다. 아래의 예시는 `ActionDispatch::Http::Cache::Response`에서 인용했습니다. +`present?` 메소드는 `!blank?` 메소드와 동등합니다. 아래의 예시는 +`ActionDispatch::Http::Cache::Response`에서 인용했습니다. ```ruby def set_conditional_cache_control! @@ -123,7 +148,8 @@ NOTE: `active_support/core_ext/object/blank.rb`에 정의되어 있습니다. ### `presence` -`presence` 메소드는 `present?`가 true인 경우에는 자기 자신의 리시버를 반환하고, false인 경우에는 `nil`을 반환합니다. 이 메소드는 아래와 같은 경우에 편리합니다. +`presence` 메소드는 `present?`가 true인 경우에는 자기 자신의 리시버를 반환하고, +false인 경우에는 `nil`을 반환합니다. 이 메소드는 아래와 같은 경우에 편리합니다. ```ruby host = config[:host].presence || 'localhost' @@ -227,7 +253,9 @@ end @number.try(:next) ``` -`ActiveRecord::ConnectionAdapters::AbstractAdapter`에 있는 다른 예시를 소개합니다. 여기에서는 `@logger`가 `nil`일 경우가 있습니다. 이 코드에서는 `try`를 사용하는 것으로 불필요한 체크를 하는 수고를 덜 수 있습니다. +`ActiveRecord::ConnectionAdapters::AbstractAdapter`에 있는 다른 예시를 +소개합니다. 여기에서는 `@logger`가 `nil`일 경우가 있습니다. 이 코드에서는 +`try`를 사용하는 것으로 불필요한 체크를 하는 수고를 덜 수 있습니다. ```ruby def log_info(sql, name, ms) @@ -238,12 +266,21 @@ def log_info(sql, name, ms) end ``` -`try` 메소드는 인수 대신 블록과 함께 호출할 수도 있습니다. 이 경우 객체가 `nil`이 아닌 경우에만 블록이 실행됩니다. +`try` 메소드는 인수 대신 블록과 함께 호출할 수도 있습니다. 이 경우 객체가 +`nil`이 아닌 경우에만 블록이 실행됩니다. ```ruby @person.try { |p| "#{p.first_name} #{p.last_name}" } ``` +`try`는 얕은 에러를 사용하므로 nil을 반환합니다. 만약 작성 미스에 따른 문제를 +피하고 싶다면 `try!`를 사용하세요. + +```ruby +@number.try(:nest) # => nil +@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer +``` + NOTE: `active_support/core_ext/object/try.rb`에 정의되어 있습니다. ### `class_eval(*args, &block)` @@ -269,14 +306,19 @@ NOTE: `active_support/core_ext/kernel/singleton_class.rb`에 정의되어 있습 ### `acts_like?(duck)` -`acts_like?` 메소드는, 일부 클래스가 다른 클래스와 같은 방식으로 동작하는지에 대해서 어떤 관례에 따라서 확인합니다. `String` 클래스와 동일한 인터페이스를 제공하는 클래스가 있고, 그 중에서 아래의 메소드를 정의했다고 가정해 봅시다. +`acts_like?` 메소드는, 일부 클래스가 다른 클래스와 같은 방식으로 동작하는 +지에 대해서 어떤 관례에 따라서 확인합니다. `String` 클래스와 동일한 +인터페이스를 제공하는 클래스가 있고, 그 중에서 아래의 메소드를 정의했다고 +가정해 봅시다. ```ruby def acts_like_string? end ``` -이 메소드는 단순한 지표이며, 메소드 자체가 돌려주는 값과 관련은 없습니다. 이에 의해서 클라이언트 코드에서는 이래와 같은 덕 타이핑(duck typing) 체크를 할 수 있게 됩니다. +이 메소드는 단순한 지표이며, 메소드 자체가 돌려주는 값과 관련은 없습니다. +이에 의해서 클라이언트 코드에서는 이래와 같은 덕 타이핑(duck typing) 체크를 +할 수 있게 됩니다. ```ruby some_klass.acts_like?(:string) @@ -288,7 +330,9 @@ NOTE: `active_support/core_ext/object/acts_like.rb`에 정의되어 있습니다 ### `to_param` -Rails의 모든 객체들에 `to_param` 메소드를 사용할 수 있습니다. 이것은 객체를 값으로 표현한 것을 반환한다는 의미입니다. 반환된 값은 쿼리 문자열이나 URL의 일부로 사용할 수 있습니다. +Rails의 모든 객체들에 `to_param` 메소드를 사용할 수 있습니다. 이것은 객체를 +값으로 표현한 것을 반환한다는 의미입니다. 반환된 값은 쿼리 문자열이나 URL의 +일부로 사용할 수 있습니다. 기본으로 `to_param` 메소드는 `to_s` 메소드를 호출하게 됩니다. @@ -304,7 +348,9 @@ Rails의 모든 객체들에 `to_param` 메소드를 사용할 수 있습니다. 이 메소드는 Rails의 많은 클래스에서 재정의됩니다. -예를 들어 `nil`, `true`, `false`의 경우는 자기 자신을 반환합니다. `Array#to_param`를 실행하면 `to_param`이 배열 내의 각 요소에 대해서 실행되며, 결과가 "/"로 join됩니다. +예를 들어 `nil`, `true`, `false`의 경우는 자기 자신을 반환합니다. +`Array#to_param`를 실행하면 `to_param`이 배열 내의 각 요소에 대해서 실행되며, +결과가 "/"로 join됩니다. ```ruby [0, true, String].to_param # => "0/true/String" @@ -332,7 +378,9 @@ NOTE: `active_support/core_ext/object/to_param.rb`에 정의되어 있습니다. ### `to_query` -이 메소드는 이스케이프 되지 않은 `key`를 받으면, 그 키를 `to_param`이 돌려주는 값을 대응시키는 쿼리 문자열의 일부를 생성합니다. 단 해시는 예외입니다(뒤에서 설명). 예를 들자면 다음과 같은 경우, +이 메소드는 이스케이프 되지 않은 `key`를 받으면, 그 키를 `to_param`이 돌려주는 +값을 대응시키는 쿼리 문자열의 일부를 생성합니다. 단 해시는 예외입니다(뒤에서 +설명). 예를 들자면 다음과 같은 경우, ```ruby class User @@ -357,7 +405,8 @@ account.to_query('company[name]') 따라서 이 결과값은 그대로 쿼리 문자열로 사용할 수 있습니다. -배열에 `to_query` 메소드를 사용한 경우 `to_query`를 배열의 각 요소에 호출하여 `_key_[]`를 키로 추가하고, 그 값들을 "&"로 연결한 결과를 반환합니다. +배열에 `to_query` 메소드를 사용한 경우 `to_query`를 배열의 각 요소에 호출하여 +`key[]`를 키로 추가하고, 그 값들을 "&"로 연결한 결과를 반환합니다. ```ruby [3.4, -45.6].to_query('sample') @@ -636,8 +685,6 @@ module ActiveSupport mattr_accessor :load_once_paths mattr_accessor :autoloaded_constants mattr_accessor :explicitly_unloadable_constants - mattr_accessor :logger - mattr_accessor :log_activity mattr_accessor :constant_watch_stack mattr_accessor :constant_watch_stack_mutex end @@ -713,76 +760,6 @@ M.parents # => [X::Y, X, Object] NOTE: `active_support/core_ext/module/introspection.rb`에 정의되어 있습니다. -### 상수 - -`local_constants` 메소드는 리시버 모듈에 정의된 상수를 반환합니다. - -```ruby -module X - X1 = 1 - X2 = 2 - module Y - Y1 = :y1 - X1 = :overrides_X1_above - end -end - -X.local_constants # => [:X1, :X2, :Y] -X::Y.local_constants # => [:Y1, :X1] -``` - -상수명은 심볼로 돌아옵니다. - -NOTE: `active_support/core_ext/module/introspection.rb`에 정의되어 있습니다. - -#### 정규 상수명 - -표준 메소드인 `const_defined?`, `const_get`, `const_set`에서는 순수한 상수명만을 사용할 수 있습니다. Active Support는 이 API를 확장하여 전체 경로에 가까운(qualified) 상수명을 넘길 수 있게 해줍니다. - -이 메소드들은 `qualified_const_defined?`, `qualified_const_get`, `qualified_const_set`입니다. 넘긴 인수는 리시버를 기준으로 경로를 포함한 상수 명일 것이라는 전제를 가집니다. - -```ruby -Object.qualified_const_defined?("Math::PI") # => true -Object.qualified_const_get("Math::PI") # => 3.141592653589793 -Object.qualified_const_set("Math::Phi", 1.618034) # => 1.618034 -``` - -경로를 포함하지 않는 순수한 정수명도 사용할 수 있습니다. - -```ruby -Math.qualified_const_get("E") # => 2.718281828459045 -``` - -이러한 메소드들은 내장된 메소드와 무척 닮아있습니다. 특히 `qualified_constant_defined?` 메소드는 옵션으로 리시버의 부모에서도 해당하는 상수를 검색할 것인지를 지정하는 두번째 인수를 받을 수 있습니다. 이 플래그는 주어진 모든 상수에 대해서 메소드로 경로를 따라 내려가며 적용됩니다. - -아래의 예시를 확인해주세요. - -```ruby -module M - X = 1 -end - -module N - class C - include M - end -end -``` - -`qualified_const_defined?`는 다음과 같이 동작합니다. - -```ruby -N.qualified_const_defined?("C::X", false) # => false -N.qualified_const_defined?("C::X", true) # => true -N.qualified_const_defined?("C::X") # => true -``` - -마지막 예제에서 볼 수 있듯, `const_defined?` 메소드와 마찬가지로 두번째 인수는 기본으로 true로 설정되어 있습니다. - -내장 메소드와 호환성을 위해 상대 경로 이외에는 사용할 수 없습니다. `::Math::PI`와 같은 절대 경로를 사용한 상수명을 사용하면 `NameError`가 발생합니다. - -NOTE: `active_support/core_ext/module/qualified_const.rb`에 정의되어 있습니다. - ### 도달 가능 이름을 가지는 모듈이 대응하는 상수에 저장되어 있는 경우에 도달 가능(reachable)이라고 표현합니다. 이것은 상수를 사용하여 모듈 객체에 접근할 수 있다는 의미입니다. @@ -1030,7 +1007,8 @@ class A class_attribute :x, instance_reader: false end -A.new.x = 1 # NoMethodError +A.new.x = 1 +A.new.x # NoMethodError ``` 편의를 위해서 `class_attribute`는 인스턴스의 reader가 돌려주는 값을 '이중부정'하는 인스턴스 존재 확인 메소드도 정의합니다. 위의 예제로 설명하자면, `x?`가 바로 그것입니다. @@ -1678,19 +1656,6 @@ NOTE: `active_support/core_ext/string/inflections.rb`에 정의되어 있습니 "Admin::Hotel::ReservationUtils".deconstantize # => "Admin::Hotel" ``` -다음의 Active Record 예시에서는 `Module#qualified_const_set`에서 이 메소드를 사용하고 있습니다. - -```ruby -def qualified_const_set(path, value) - QualifiedConstUtils.raise_if_absolute(path) - - const_name = path.demodulize - mod_name = path.deconstantize - mod = mod_name.empty? ? self : qualified_const_get(mod_name) - mod.const_set(const_name, value) -end -``` - NOTE: `active_support/core_ext/string/inflections.rb`에 정의되어 있습니다. #### `parameterize` @@ -1736,7 +1701,8 @@ NOTE: `active_support/core_ext/string/inflections.rb`에 정의되어 있습니 "highrise_production.companies".classify # => "Company" ``` -`classify`가 돌려주는 클래스 이름은 문자열입니다. 얻은 문자열에 대해서 `constantize`를 호출하는 것으로 실제 클래스 객체를 얻을 수 있습니다. +`classify`가 돌려주는 클래스 이름은 문자열입니다. 얻은 문자열에 대해서 +`constantize`를 호출하는 것으로 실제 클래스 객체를 얻을 수 있습니다. NOTE: `active_support/core_ext/string/inflections.rb`에 정의되어 있습니다. @@ -1745,7 +1711,7 @@ NOTE: `active_support/core_ext/string/inflections.rb`에 정의되어 있습니 `constantize` 메소드는 리시버의 값을 참조하여 실제 객체를 반환합니다. ```ruby -"Fixnum".constantize # => Fixnum +"Integer".constantize # => Integer module M X = 1 @@ -1997,12 +1963,14 @@ NOTE: `active_support/core_ext/numeric/bytes.rb`에 정의되어 있습니다. 사람에게 가독성이 좋은 바이트 형식으로 변환할 수 있습니다. ```ruby -123.to_s(:human_size) # => 123 Bytes -1234.to_s(:human_size) # => 1.21 KB -12345.to_s(:human_size) # => 12.1 KB -1234567.to_s(:human_size) # => 1.18 MB -1234567890.to_s(:human_size) # => 1.15 GB -1234567890123.to_s(:human_size) # => 1.12 TB +123.to_s(:human_size) # => 123 Bytes +1234.to_s(:human_size) # => 1.21 KB +12345.to_s(:human_size) # => 12.1 KB +1234567.to_s(:human_size) # => 1.18 MB +1234567890.to_s(:human_size) # => 1.15 GB +1234567890123.to_s(:human_size) # => 1.12 TB +1234567890123456.to_s(:human_size) # => 1.1 PB +1234567890123456789.to_s(:human_size) # => 1.07 EB ``` 사람에게 가독성이 좋은 숫자 단위를 사용할 수 있습니다. @@ -2110,7 +2078,7 @@ BigDecimal.new(5.00, 6).to_formatted_s("e") # => "0.5E1" ```ruby [[1, 2], [2, 3], [3, 4]].sum # => [1, 2, 2, 3, 3, 4] %w(foo bar baz).sum # => "foobarbaz" -{a: 1, b: 2, c: 3}.sum # => [:b, 2, :c, 3, :a, 1] +{a: 1, b: 2, c: 3}.sum # => [:b, 2, :c, 3, :a, 1] ``` 빈 컬렉션은 기본으로 0을 반환합니다만, 이 동작은 바꿀 수 있습니다. @@ -2209,7 +2177,9 @@ Active Support에는 배열에 여러가지 API를 추가하며, 이는 배열 [].from(0) # => [] ``` -`second`, `third`, `fourth`, `fifth`는 대응하는 요소를 반환합니다(`first`는 원래 내장되어 있는 메소드입니다). 재미를 위해 지금은 `forty_two`도 사용할 수 있습니다(역주: [Rails 2.2 이후](https://github.com/rails/rails/commit/9d8cc60ec3845fa3e6f9292a65b119fe4f619f7e)로 사용 가능합니다. '42'에 대해서는 Wikipedia의 [이 문서](https://ko.wikipedia.org/wiki/%EC%9D%80%ED%95%98%EC%88%98%EB%A5%BC_%EC%97%AC%ED%96%89%ED%95%98%EB%8A%94_%ED%9E%88%EC%B9%98%ED%95%98%EC%9D%B4%EC%BB%A4%EB%A5%BC_%EC%9C%84%ED%95%9C_%EC%95%88%EB%82%B4%EC%84%9C_(%EC%86%8C%EC%84%A4))의 줄거리를 참조해주세요.). +`second`, `third`, `fourth`, `fifth`, `second_to_last`, `third_to_last`는 + 대응하는 요소를 반환합니다(`first`, `last`는 원래 내장되어 있는 메소드입니다). +재미를 위해 지금은 `forty_two`도 사용할 수 있습니다(역주: [Rails 2.2 이후](https://github.com/rails/rails/commit/9d8cc60ec3845fa3e6f9292a65b119fe4f619f7e)로 사용 가능합니다. '42'에 대해서는 Wikipedia의 [이 문서](https://ko.wikipedia.org/wiki/%EC%9D%80%ED%95%98%EC%88%98%EB%A5%BC_%EC%97%AC%ED%96%89%ED%95%98%EB%8A%94_%ED%9E%88%EC%B9%98%ED%95%98%EC%9D%B4%EC%BB%A4%EB%A5%BC_%EC%9C%84%ED%95%9C_%EC%95%88%EB%82%B4%EC%84%9C_(%EC%86%8C%EC%84%A4))의 줄거리를 참조해주세요.). ```ruby %w(a b c d).third # => c @@ -2602,8 +2572,7 @@ NOTE: `active_support/core_ext/array/grouping.rb`에 정의되어 있습니다. ```ruby XML_TYPE_NAMES = { "Symbol" => "symbol", - "Fixnum" => "integer", - "Bignum" => "integer", + "Integer" => "integer", "BigDecimal" => "decimal", "Float" => "float", "TrueClass" => "boolean", @@ -2722,7 +2691,7 @@ NOTE: `active_support/core_ext/hash/except.rb`에 정의되어 있습니다. ```ruby {nil => nil, 1 => 1, a: :a}.transform_keys { |key| key.to_s.upcase } -# => {"" => nil, "A" => :a, "1" => 1} +# => {"" => nil, "1" => 1, "A" => :a} ``` 키가 중복되는 경우에는 그중 하나의 값이 우선됩니다. 우선되는 값은 같은 해시가 주어진 경우라도 같은 결과를 준다고 보장하지 않습니다. @@ -2764,7 +2733,7 @@ NOTE: `active_support/core_ext/hash/keys.rb`에 정의되어 있습니다. ```ruby {nil => nil, 1 => 1, a: :a}.stringify_keys -# => {"" => nil, "a" => :a, "1" => 1} +# => {"" => nil, "1" => 1, "a" => :a} ``` 키가 중복되는 경우, 한 쪽의 값이 우선됩니다. 우선되는 값은 같은 해시가 주어진 경우에도 항상 같다고 보장하지 않습니다. @@ -2806,7 +2775,7 @@ NOTE: `active_support/core_ext/hash/keys.rb`에 정의되어 있습니다. ```ruby {nil => nil, 1 => 1, "a" => "a"}.symbolize_keys -# => {1=>1, nil=>nil, :a=>"a"} +# => {nil=>nil, 1=>1, :a=>"a"} ``` WARNING: 이 예제에서는 3개의 키중 마지막 하나만 심볼로 변환되지 않았다는 점에 주목하세요. 숫자와 nil은 심볼로 변환할 수 없습니다. @@ -2884,7 +2853,7 @@ Ruby에는 문자열이나 배열을 나누어 일부를 꺼내는 내장 메소 ```ruby {a: 1, b: 2, c: 3}.slice(:a, :c) -# => {:c=>3, :a=>1} +# => {:a=>1, :c=>3} {a: 1, b: 2, c: 3}.slice(:b, :X) # => {:b=>2} # 존재하지 않는 키는 무시 @@ -2978,6 +2947,25 @@ end NOTE: `active_support/core_ext/regexp.rb`에 정의되어 있습니다. +### `match?` + +Rails는 `Regexp#match?`를 Ruby 2.4에 앞서 구현했습니다. + +```ruby +/oo/.match?('foo') # => true +/oo/.match?('bar') # => false +/oo/.match?('foo', 1) # => true +``` + +백포트는 동일한 인터페이스를 가지고 있으며, `$1`과 같은 값들을 설정하지 +않는다는 차이점이 있습니다만, 성능적인 차이는 없습니다. 이는 Ruby 2.4에 +적응하기 유리한 코드를 작성할 수 있도록 돕기 위함입니다. 예를 들어 Rails는 +내부적으로 이를 이미 사용하고 있습니다. + +Active Support는 `Regexp#match?`가 존재하지 않는 경우에만 정의하며, 따라서 +Ruby 2.4 이후의 코드를 사용하는 경우 본래의 코드를 사용하여 성능 향상을 +얻을 수 있도록 하고 있습니다. + `Range` 확장 --------------------- @@ -3040,13 +3028,29 @@ NOTE: `active_support/core_ext/range/overlaps.rb`에 정의되어 있습니다. NOTE: 다음 메소드들은 모두 같은 파일 `active_support/core_ext/date/calculations.rb`에 위치하고 있습니다. -INFO: 다음 계산 방법들 중의 일부에서는 1582년 10월을 극단적인 예외로서 사용하고 있습니다. 이 달에는 율리우스 력으로부터 그레고리 력으로 변경이 이루어져서 10월 5일부터 10월 14일까지가 존재하지 않습니다. 이 가이드에서는 이 특수한 달에 대해서 길게 이야기하지 않습니다만, 메소드가 이 달에서도 기대대로 동작한다는 점을 설명해두고 싶습니다. 구체적인 예시로는 `Date.new(1582, 10, 4).tomorrow`를 실행하면 `Date.new(1582, 10, 15)`가 반환됩니다. 기대대로 동작한다는 것은 Active Support의 `test/core_ext/date_ext_test.rb`용의 테스트 코드에서 확인하실 수 있습니다. +INFO: 다음 계산 방법들 중의 일부에서는 1582년 10월을 극단적인 예외로서 +사용하고 있습니다. 이 달에는 율리우스 력으로부터 그레고리 력으로 변경이 +이루어져서 10월 5일부터 10월 14일까지가 존재하지 않습니다. 이 가이드에서는 +이 특수한 달에 대해서 길게 이야기하지 않습니다만, 메소드가 이 달에서도 +기대대로 동작한다는 점을 설명해두고 싶습니다. 구체적인 예시로는 +`Date.new(1582, 10, 4).tomorrow`를 실행하면 `Date.new(1582, 10, 15)`가 +반환됩니다. 기대대로 동작한다는 것은 Active Support의 +`test/core_ext/date_ext_test.rb`용의 테스트 코드에서 확인하실 수 있습니다. #### `Date.current` -Active Support에서는 `Date.current`를 정의하고 현재의 시간대에 맞는 '오늘'을 돌려줍니다. 이 메소드는 `Date.today`와 유사합니다만, 사용자가 정의한 시간대이 있는 경우에 그것을 고려한다는 점이 다릅니다. Active Support에서는 `Date.yesterday` 메소드와 `Date.tomorrow`도 정의하고 있습니다. 인스턴스에서는 `past?`, `today?`, `future?`를 사용할 수 있으며, 이들은 모두 `Date.current`를 기준으로 계산됩니다. +Active Support에서는 `Date.current`를 정의하고 현재의 시간대에 맞는 '오늘'을 +돌려줍니다. 이 메소드는 `Date.today`와 유사합니다만, 사용자가 정의한 시간대에 +있는 경우에 그것을 고려한다는 점이 다릅니다. Active Support에서는 +`Date.yesterday` 메소드와 `Date.tomorrow`도 정의하고 있습니다. 인스턴스에서는 +`past?`, `today?`, `future?`, `on_weekday?`, `on_weekend?`를 사용할 수 있으며, +이들은 모두 `Date.current`를 기준으로 계산됩니다. -사용자가 정의한 시간대를 사용하는 메소드를 통해 날짜를 비교하고 싶은 경우 `Date.today` 대신 `Date.current`를 반드시 사용해주세요. 이후에 사용자가 정의한 시간대와 시스템의 시간대를 비교해야하는 상황이 있을 수도 있습니다. 시스템의 시간대에서는 `Date.today`가 사용됩니다. 다시 말해서 `Date.today`가 `Date.yesterday`와 같은 상황도 존재할 수 있습니다. +사용자가 정의한 시간대를 사용하는 메소드를 통해 날짜를 비교하고 싶은 경우 +`Date.today` 대신 `Date.current`를 반드시 사용해주세요. 이후에 사용자가 정의한 +시간대와 시스템의 시간대를 비교해야하는 상황이 있을 수도 있습니다. 시스템의 +시간대에서는 `Date.today`가 사용됩니다. 다시 말해서 `Date.today`가 +`Date.yesterday`와 같은 상황도 존재할 수 있습니다. #### 이름이 있는 날짜 @@ -3429,6 +3433,8 @@ years_ago years_since prev_year (last_year) next_year +on_weekday? +on_weekend? ``` 이하의 메소드는 모두 재정의되기 때문에 이들을 사용하기 위해서 `active_support/core_ext/date/calculations.rb`를 불러올 필요는 **없습니다**. @@ -3615,9 +3621,12 @@ years_ago years_since prev_year (last_year) next_year +on_weekday? +on_weekend? ``` -이들은 동일하게 동작하며, 관련된 문서를 참조하시고, 다음과 같은 차이점에 대해서도 기억해주세요. +이들은 동일하게 동작하며, 관련된 문서를 참조하시고, 다음과 같은 차이점에 +대해서도 기억해주세요. * `change` 메소드에 추가로 `:usec` 옵션을 사용할 수 있습니다. * `Time`은 섬머타임(DST)을 이해합니다. 아래와 같은 DST처리도 올바르게 됩니다. diff --git a/guides/source/ko/api_app.md b/guides/source/ko/api_app.md index f373d313ccf89..d1b977dd94e2d 100644 --- a/guides/source/ko/api_app.md +++ b/guides/source/ko/api_app.md @@ -1,204 +1,150 @@ -**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.** - - -Using Rails for API-only Applications +API 전용 레일스 애플리케이션 만들기 ===================================== -In this guide you will learn: +이 가이드의 내용: -* What Rails provides for API-only applications -* How to configure Rails to start without any browser features -* How to decide which middleware you will want to include -* How to decide which modules to use in your controller +* 레일스가 API 전용 애플리케이션을 위해 제공하는 기능 +* 브라우저 관련 기능을 제외하고 레일스를 실행하기 +* 미들웨어 선택하기 +* 컨트롤러에서 사용할 모듈 선택하기 -------------------------------------------------------------------------------- -What is an API Application? +API 애플리케이션에 대해 --------------------------- -Traditionally, when people said that they used Rails as an "API", they meant -providing a programmatically accessible API alongside their web application. -For example, GitHub provides [an API](http://developer.github.com) that you -can use from your own custom clients. +지금까지 레일스로 API를 사용한다고 하면 프로그램이 사용할 수 있는 API를 +웹 애플리케이션에 추가하는 방식을 의미했습니다. +예를 들어 GitHub이 제공하는 [API](http://developer.github.com)를 직접 만든 +클라이언트에서 사용할 수 있습니다. -With the advent of client-side frameworks, more developers are using Rails to -build a back-end that is shared between their web application and other native -applications. +클라이언트 프레임워크의 등장에 따라, 다른 웹 애플리케이션과 네이티브 +애플리케이션에서 레일스로 만든 백엔드 서버를 사용하는 경우가 늘었습니다. -For example, Twitter uses its [public API](https://dev.twitter.com) in its web -application, which is built as a static site that consumes JSON resources. +Twitter는 자사 웹 애플리케이션에서 [공개 API](https://dev.twitter.com)를 +사용하고 있습니다. +이 웹 애플리케이션은 JSON 리소스만을 사용하는 정적인 사이트입니다. -Instead of using Rails to generate HTML that communicates with the server -through forms and links, many developers are treating their web application as -just an API client delivered as HTML with JavaScript that consumes a JSON API. +많은 개발자가 레일스를 폼이나 링크를 통하는 서버 간의 통신을 위해 HTML을 +생성하는 대신, 웹 애플리케이션을 단순한 API 클라이언트로 정의하고, JSON API를 +사용하는 HTML과 자바스크립트를 제공하는 방식으로 다루게 되었습니다. -This guide covers building a Rails application that serves JSON resources to an -API client, including client-side frameworks. +여기에서는 클라이언트 프레임워크의 설명을 포함해, JSON 리소스를 +API 클라이언트에 제공하는 레일스 애플리케이션을 구축하는 방법에 관해서 +설명합니다. -Why Use Rails for JSON APIs? +JSON API에 레일스를 사용하는 이유 ---------------------------- -The first question a lot of people have when thinking about building a JSON API -using Rails is: "isn't using Rails to spit out some JSON overkill? Shouldn't I -just use something like Sinatra?". - -For very simple APIs, this may be true. However, even in very HTML-heavy -applications, most of an application's logic lives outside of the view -layer. - -The reason most people use Rails is that it provides a set of defaults that -allows developers to get up and running quickly, without having to make a lot of trivial -decisions. - -Let's take a look at some of the things that Rails provides out of the box that are -still applicable to API applications. - -Handled at the middleware layer: - -- Reloading: Rails applications support transparent reloading. This works even if - your application gets big and restarting the server for every request becomes - non-viable. -- Development Mode: Rails applications come with smart defaults for development, - making development pleasant without compromising production-time performance. -- Test Mode: Ditto development mode. -- Logging: Rails applications log every request, with a level of verbosity - appropriate for the current mode. Rails logs in development include information - about the request environment, database queries, and basic performance - information. -- Security: Rails detects and thwarts [IP spoofing - attacks](http://en.wikipedia.org/wiki/IP_address_spoofing) and handles - cryptographic signatures in a [timing - attack](http://en.wikipedia.org/wiki/Timing_attack) aware way. Don't know what - an IP spoofing attack or a timing attack is? Exactly. -- Parameter Parsing: Want to specify your parameters as JSON instead of as a - URL-encoded String? No problem. Rails will decode the JSON for you and make - it available in `params`. Want to use nested URL-encoded parameters? That - works too. -- Conditional GETs: Rails handles conditional `GET` (`ETag` and `Last-Modified`) - processing request headers and returning the correct response headers and status - code. All you need to do is use the - [`stale?`](http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-stale-3F) - check in your controller, and Rails will handle all of the HTTP details for you. -- HEAD requests: Rails will transparently convert `HEAD` requests into `GET` ones, - and return just the headers on the way out. This makes `HEAD` work reliably in - all Rails APIs. - -While you could obviously build these up in terms of existing Rack middleware, -this list demonstrates that the default Rails middleware stack provides a lot -of value, even if you're "just generating JSON". - -Handled at the Action Pack layer: - -- Resourceful Routing: If you're building a RESTful JSON API, you want to be - using the Rails router. Clean and conventional mapping from HTTP to controllers - means not having to spend time thinking about how to model your API in terms - of HTTP. -- URL Generation: The flip side of routing is URL generation. A good API based - on HTTP includes URLs (see [the GitHub Gist API](http://developer.github.com/v3/gists/) - for an example). -- Header and Redirection Responses: `head :no_content` and - `redirect_to user_url(current_user)` come in handy. Sure, you could manually - add the response headers, but why? -- Caching: Rails provides page, action and fragment caching. Fragment caching - is especially helpful when building up a nested JSON object. -- Basic, Digest, and Token Authentication: Rails comes with out-of-the-box support - for three kinds of HTTP authentication. -- Instrumentation: Rails has an instrumentation API that triggers registered - handlers for a variety of events, such as action processing, sending a file or - data, redirection, and database queries. The payload of each event comes with - relevant information (for the action processing event, the payload includes - the controller, action, parameters, request format, request method and the - request's full path). -- Generators: It is often handy to generate a resource and get your model, - controller, test stubs, and routes created for you in a single command for - further tweaking. Same for migrations and others. -- Plugins: Many third-party libraries come with support for Rails that reduce - or eliminate the cost of setting up and gluing together the library and the - web framework. This includes things like overriding default generators, adding - Rake tasks, and honoring Rails choices (like the logger and cache back-end). - -Of course, the Rails boot process also glues together all registered components. -For example, the Rails boot process is what uses your `config/database.yml` file -when configuring Active Record. - -**The short version is**: you may not have thought about which parts of Rails -are still applicable even if you remove the view layer, but the answer turns out -to be most of it. - -The Basic Configuration +레일스로 JSON API를 만드는 것에 대해서 많은 개발자가 가장 먼저 떠올리는 +질문은 이렇습니다. +"레일스로 JSON을 제공하는건 너무 거창하지 않나요? 그냥 Sinatra로 만들면 +어떤가요?" + +단순한 API 서버라면 아마도 그럴 겁니다. 하지만 HTML의 비중이 매우 큰 +애플리케이션이라도, 로직 대부분은 뷰의 바깥에 존재합니다. + +많은 개발자가 레일스를 채용하는 이유는 세세한 설정을 고민하지 않고, +빠르게 애플리케이션을 제공할 수 있기 때문입니다. + +API 애플리케이션 개발에 도움이 되는 레일스의 기능을 몇 가지 소개합니다. + +미들웨어에서 제공하는 기능 목록 + +- 리로딩: 레일스 애플리케이션은 '투명한 리로딩'을 지원합니다. 예를 들어 애플리케이션이 커져 요청마다 서버를 재기동하는 방법을 사용할 수 없더라도 투명한 리로딩이 가능합니다. +- 개발 모드: 레일스 애플리케이션의 개발 모드에는 기본값이 이미 설정되어 있으므로 실제 환경의 성능에 대한 걱정 없이 즐겁게 작업을 진행할 수 있습니다. +- 테스트 모드: 개발 모드와 같습니다. +- 로그 출력: 레일스 애플리케이션은 요청마다 로그를 출력합니다. 또한 현재 모드에 따라서 로그의 레벨이 조정됩니다. 개발 모드의 로그에는 요청 환경, 데이터베이스 질의, 간단한 성능 정보 등이 출력됩니다. +- 보안: [IP 스푸핑 공격](https://en.wikipedia.org/wiki/IP_address_spoofing)을 검출, 방어합니다. 또한 [타이밍 공격](http://en.wikipedia.org/wiki/Timing_attack)에 대응할 수 있는 암호화 서명을 다룹니다. +- 매개변수 분석: URL 인코딩이나 문자열 대신 JSON으로 매개변수를 지정할 수 있습니다. JSON은 레일스에서 해석되어 `params`를 통해 접근할 수 있습니다. 물론 중첩된 URL 인코딩 매개변수도 다룰 수 있습니다. +- 조건부 GET: 레일스에서는 `ETag`나 `Last-Modified`를 사용한 조건부 GET을 사용합니다. 이는 요청 헤더를 처리하고, 올바른 응답 헤더와 상태 코드를 돌려줍니다. 컨트롤러에 [`stale?`](http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-stale-3F)을 추가하면 HTTP의 구체적인 동작은 레일스가 처리합니다. +- HEAD 요청: 레일스에서는 `HEAD` 요청을 투명하게 `GET` 요청으로 변환하고, 헤더만을 반환합니다. 이를 통해서 모든 레일스 API에서 `HEAD` 요청을 사용할 수 있습니다. + +Rack 미들웨어의 이런 기능들을 직접 구현할 수도 있습니다만, 레일스의 기본 미들웨어를 "JSON 생성용"으로 +쓰더라도 많은 이점을 얻을 수 있습니다. + +액션 팩에서 제공하는 기능 + +- 리소스 기반 라우팅: RESTful JSON API를 개발한다면 레일스의 라우터도 사용하고 싶을 것입니다. HTTP로부터 컨트롤러로 명확하게 연결할 수 있으므로 HTTP에 대해서 API를 어떻게 구성할지 고민할 필요가 없습니다. +- URL 생성: 라우팅은 URL을 생성할 때에도 편리합니다. 잘 구성된 HTTP 기반의 API에는 URL도 포함됩니다([GitHub Gist API](http://developer.github.com/v3/gists/)가 좋은 예시입니다). +- 헤더 응답이나 리다이렉션 응답: `head :no_content`나 `redirect_to user_url(current_user)` 등을 사용할 수 있습니다. 헤더 응답을 직접 생성하지 않아도 됩니다. +- 캐시: 레일스는 페이지 캐싱, 액션 캐싱, 조각 캐싱을 사용할 수 있습니다. 특히 조각 캐싱은 중첩된 JSON 객체를 만들 때 유용합니다. +- 기본 인증, 다이제스트 인증, 토큰 인증: 3종류의 HTTP 인증을 간단하게 도입할 수 있습니다. +- 계측(Instrumentation): 레일스의 계측 API는 등록한 다양한 이벤트 핸들러를 실행합니다. 액션 처리, 파일이나 데이터 전송, 리다이렉트, 데이터베이스 질의 등을 다룹니다. 각 이벤트의 페이로드에 다양한 정보가 포함되어 있습니다. 예를 들어 이벤트를 처리하는 액션의 경우, 페이로드에는 컨트롤러, 액션, 매개변수, 요청 형식, 요청 경로등이 포함됩니다. +- 제너레이터: 명령 하나로 리소스를 간단하게 생성하고, API에 맞는 모델, 컨트롤러, 테스트 스텁, 라우팅을 바로 사용할 수 있습니다. 마이그레이션 등의 작업도 명령으로 실행할 수 있습니다. +- 플러그인: 수많은 서드파티 라이브러리를 사용할 수 있습니다. 라이브러리 설정이나 웹 프레임워크 등의 연동도 간단하므로, 비용을 줄일 수 있습니다. 플러그인을 통해 기존 제너레이터를 덮어쓰거나 Rake 태스크를 추가, 또는 레일스의 동작을 변경할 수도 있습니다(로거나 캐시 백엔드 등). + +물론 레일스의 실행 프로세스에서는 등록된 컴포넌트를 모두 읽어서 연동합니다. 예를 들어 실행할 때에 `config/database.yml` 파일을 통하여 액티브 레코드를 설정합니다. + +**한줄 요약**: 레일스에서 뷰와 관련된 동작을 제외하면 어떤 기능을 사용할 수 있을까요? 기능 대부분을 사용할 수 있습니다. + +기본 설정 ----------------------- -If you're building a Rails application that will be an API server first and -foremost, you can start with a more limited subset of Rails and add in features -as needed. +레일스 애플리케이션을 API 서버로 구축하고 싶다면, 기능을 제한한 레일스 하위 셋을 사용하여 필요한 기능을 +추가하는 것이 좋을 겁니다. -### Creating a new application +### 애플리케이션을 새로 생성하기 -You can generate a new api Rails app: +API 레일스 애플리케이션을 생성하려면 다음의 명령을 사용합니다. ```bash $ rails new my_api --api ``` -This will do three main things for you: +이 명령은 다음 3개의 동작을 실행합니다. -- Configure your application to start with a more limited set of middleware - than normal. Specifically, it will not include any middleware primarily useful - for browser applications (like cookies support) by default. -- Make `ApplicationController` inherit from `ActionController::API` instead of - `ActionController::Base`. As with middleware, this will leave out any Action - Controller modules that provide functionalities primarily used by browser - applications. -- Configure the generators to skip generating views, helpers and assets when - you generate a new resource. +- 사용하는 미들웨어를 기존보다 적게끔 설정합니다. 특히 브라우저용 애플리케이션에서 유용한 미들웨어(쿠키 등의 지원)를 완전히 사용할 수 없게 됩니다. +- `ApplicationController`을 `ActionController::Base`가 아닌 `ActionController::API`에서 상속받습니다. 미들웨어와 마찬가지로 액션 컨트롤러 모듈에서 브라우저용 애플리케이션에서만 사용되는 부분을 모두 제외합니다. +- 제너레이터가 뷰, 헬퍼, 어셋을 생성하지 않도록 합니다. -### Changing an existing application +### 기존의 애플리케이션을 변경하기 -If you want to take an existing application and make it an API one, read the -following steps. +기존의 애플리케이션을 API 전용으로 만들려면 다음 순서를 따라주세요. -In `config/application.rb` add the following line at the top of the `Application` -class definition: +`config/application.rb`의 `Application` 클래스에 다음을 추가합니다. ```ruby config.api_only = true ``` -In `config/environments/development.rb`, set `config.debug_exception_response_format` -to configure the format used in responses when errors occur in development mode. +`config/environments/development.rb`에서 +`config.debug_exception_response_format`을 통해 개발 모드에서 에러가 발생할 +경우에 응답에서 사용할 형식을 지정하세요. -To render an HTML page with debugging information, use the value `:default`. +`:default`는 HTML 페이지로 디버깅 정보를 제공합니다. ```ruby config.debug_exception_response_format = :default ``` -To render debugging information preserving the response format, use the value `:api`. +`:api`는 응답 형식을 유지한 채로 디버깅 정보를 제공합니다. ```ruby config.debug_exception_response_format = :api ``` -By default, `config.debug_exception_response_format` is set to `:api`, when `config.api_only` is set to true. +`config.api_only`를 true로 설정하면 `config.debug_exception_response_format`의 +기본값이 `:api`로 설정됩니다. -Finally, inside `app/controllers/application_controller.rb`, instead of: +마지막으로 `app/controllers/application_controller.rb`를, ```ruby class ApplicationController < ActionController::Base -end +end ``` -do: +다음과 같이 변경합니다. ```ruby class ApplicationController < ActionController::API -end +end ``` -Choosing Middleware +미들웨어 선택하기 -------------------- -An API application comes with the following middleware by default: +API 애플리케이션에서는 기본으로 다음의 미들웨어를 사용합니다. - `Rack::Sendfile` - `ActionDispatch::Static` @@ -217,26 +163,25 @@ An API application comes with the following middleware by default: - `Rack::ConditionalGet` - `Rack::ETag` -See the [internal middleware](rails_on_rack.html#internal-middleware-stack) -section of the Rack guide for further information on them. +자세한 설명은 Rack 가이드의 [내부 미들웨어](rails_on_rack.html#미들웨어-스택의-내용)에서 확인하세요. -Other plugins, including Active Record, may add additional middleware. In -general, these middleware are agnostic to the type of application you are -building, and make sense in an API-only Rails application. +미들웨어는 액티브 레코드 등의 플러그인에 의해서 추가되는 경우도 있습니다. +일반적으로 구축할 애플리케이션의 종류와 미들웨어는 관련이 없습니다만, +API 전용 레일스 애플리케이션에서는 의미가 있습니다. -You can get a list of all middleware in your application via: +애플리케이션의 모든 미들웨어를 확인하려면 다음 명령을 실행하세요. ```bash $ rails middleware ``` -### Using the Cache Middleware +### 캐시 미들웨어를 사용하기 -By default, Rails will add a middleware that provides a cache store based on -the configuration of your application (memcache by default). This means that -the built-in HTTP cache will rely on it. +레일스는 애플리케이션의 설정에 따라 캐시 저장소(기본값은 memcache)를 제공하는 +미들웨어를 추가합니다. 다시 말해, 레일스에 포함된 HTTP 캐시는 이 캐시 저장소에 +의존합니다. -For instance, using the `stale?` method: +예를 들자면, 다음과 같이 `stale?` 메소드를 호출한다고 가정합시다. ```ruby def show @@ -248,14 +193,13 @@ def show end ``` -The call to `stale?` will compare the `If-Modified-Since` header in the request -with `@post.updated_at`. If the header is newer than the last modified, this -action will return a "304 Not Modified" response. Otherwise, it will render the -response and include a `Last-Modified` header in it. +`stale?` 호출은 `@post.updated_at`과 요청에 있는 `If-Modified-Since` 헤더를 +비교합니다. 헤더가 마지막 변경 시점보다 새로운 경우 "304 Not Modified"를 +반환하거나 `Last-Modified` 헤더를 포함하여 응답을 랜더링합니다. -Normally, this mechanism is used on a per-client basis. The cache middleware -allows us to share this caching mechanism across clients. We can enable -cross-client caching in the call to `stale?`: +일반적으로 이 동작은 클라이언트마다 이루어집니다만, 캐시 미들웨어가 있다면 +클라이언트 간에 이 캐시를 공유할 수도 있습니다. 클라이언트 캐시 공유는 +`stale?` 호출 시점에 지정할 수 있습니다. ```ruby def show @@ -263,56 +207,51 @@ def show if stale?(last_modified: @post.updated_at, public: true) render json: @post - end + end end ``` -This means that the cache middleware will store off the `Last-Modified` value -for a URL in the Rails cache, and add an `If-Modified-Since` header to any -subsequent inbound requests for the same URL. +캐시 미들웨어는 URL에 대응하는 `Last-Modified` 값을 레일스 캐시에 저장하고 +이후 같은 URL 요청을 수신할 경우 `If-Modified-Since` 헤더를 추가합니다. -Think of it as page caching using HTTP semantics. +이는 HTTP를 사용하는 페이지 캐싱이라고 생각할 수도 있을 겁니다. -### Using Rack::Sendfile +### Rack::Sendfile 사용하기 -When you use the `send_file` method inside a Rails controller, it sets the -`X-Sendfile` header. `Rack::Sendfile` is responsible for actually sending the -file. +레일스 컨트롤러에서 `send_file` 메소드가 실행되면 `X-Sendfile` 헤더가 +추가됩니다. +`Rack::Sendfile`은 실제 파일 전송을 책임집니다. -If your front-end server supports accelerated file sending, `Rack::Sendfile` -will offload the actual file sending work to the front-end server. +빠른 파일 전송(accelerated file sending)을 지원하는 프론트엔드 서버는 +`Rack::Sendfile` 대신에 실제 파일을 전송합니다. -You can configure the name of the header that your front-end server uses for -this purpose using `config.action_dispatch.x_sendfile_header` in the appropriate -environment's configuration file. +프론트엔드 서버에서 파일 전송에 사용하는 헤더의 이름은 해당하는 환경 설정 +파일의 `config.action_dispatch.x_sendfile_header`에서 지정할 수 있습니다. -You can learn more about how to use `Rack::Sendfile` with popular -front-ends in [the Rack::Sendfile -documentation](http://rubydoc.info/github/rack/rack/master/Rack/Sendfile). +[Rack::Sendfile 문서](http://rubydoc.info/github/rack/rack/master/Rack/Sendfile)에서 +인기있는 프론트엔드와 함께 `Rack::Sendfile`을 사용하는 방법을 확인하세요. -Here are some values for this header for some popular servers, once these servers are configured to support -accelerated file sending: +빠른 파일 전송을 사용하려면 헤더에 다음과 같은 값을 설정하세요. ```ruby -# Apache and lighttpd +# Apache, lighttpd config.action_dispatch.x_sendfile_header = "X-Sendfile" # Nginx config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" ``` -Make sure to configure your server to support these options following the -instructions in the `Rack::Sendfile` documentation. +이 옵션을 사용하려면 `Rack::Sendfile` 문서를 따라서 서버를 설정해주세요. -### Using ActionDispatch::Request +### ActionDispatch::Request 사용하기 -`ActionDispatch::Request#params` will take parameters from the client in the JSON -format and make them available in your controller inside `params`. +`ActionDispatch::Request#params`는 클라이언트로부터 매개변수를 JSON 형식으로 받아 컨트롤러의 +`params`로 접근할 수 있게 해줍니다. -To use this, your client will need to make a request with JSON-encoded parameters -and specify the `Content-Type` as `application/json`. +이 기능을 사용하려면 JSON으로 인코딩된 매개변수를 클라이언트에서 보내고, 이 때 `Content-Type`가 +`application/json`이어야 합니다. -Here's an example in jQuery: +jQuery 예제는 다음과 같습니다. ```javascript jQuery.ajax({ @@ -325,71 +264,62 @@ jQuery.ajax({ }); ``` -`ActionDispatch::Request` will see the `Content-Type` and your parameters -will be: +`ActionDispatch::Request`에서는 이 `Content-Type`으로 다음 인자를 받습니다. ```ruby { :person => { :firstName => "Yehuda", :lastName => "Katz" } } ``` -### Other Middleware +### 그 이외의 미들웨어 -Rails ships with a number of other middleware that you might want to use in an -API application, especially if one of your API clients is the browser: +레일스에서는 이외에도 API 애플리케이션을 위한 여러 미들웨어를 사용할 수 있습니다. 특히 브라우저가 +API 클라이언트가 되는 경우에 다음의 미들웨어들이 유용합니다. - `Rack::MethodOverride` - `ActionDispatch::Cookies` - `ActionDispatch::Flash` -- For session management +- 세션 관리용 * `ActionDispatch::Session::CacheStore` * `ActionDispatch::Session::CookieStore` * `ActionDispatch::Session::MemCacheStore` -Any of these middleware can be added via: +이 미들웨어들은 다음과 같이 추가할 수 있습니다. ```ruby config.middleware.use Rack::MethodOverride ``` -### Removing Middleware +### 미들웨어 제거하기 -If you don't want to use a middleware that is included by default in the API-only -middleware set, you can remove it with: +API 전용 미들웨어에 포함하고 싶지 않은 미들웨어는 다음과 같이 삭제할 수 있습니다. ```ruby config.middleware.delete ::Rack::Sendfile ``` -Keep in mind that removing these middleware will remove support for certain -features in Action Controller. +이 미들웨어를 삭제하면 액션 컨트롤러의 일부 기능을 사용할 수 없게 되므로 조심하세요. -Choosing Controller Modules +컨트롤러에서 사용할 모듈 선택하기 --------------------------- -An API application (using `ActionController::API`) comes with the following -controller modules by default: - -- `ActionController::UrlFor`: Makes `url_for` and similar helpers available. -- `ActionController::Redirecting`: Support for `redirect_to`. -- `AbstractController::Rendering` and `ActionController::ApiRendering`: Basic support for rendering. -- `ActionController::Renderers::All`: Support for `render :json` and friends. -- `ActionController::ConditionalGet`: Support for `stale?`. -- `ActionController::BasicImplicitRender`: Makes sure to return an empty response, if there isn't an explicit one. -- `ActionController::StrongParameters`: Support for parameters white-listing in combination with Active Model mass assignment. -- `ActionController::ForceSSL`: Support for `force_ssl`. -- `ActionController::DataStreaming`: Support for `send_file` and `send_data`. -- `AbstractController::Callbacks`: Support for `before_action` and - similar helpers. -- `ActionController::Rescue`: Support for `rescue_from`. -- `ActionController::Instrumentation`: Support for the instrumentation - hooks defined by Action Controller (see [the instrumentation - guide](active_support_instrumentation.html#action-controller) for -more information regarding this). -- `ActionController::ParamsWrapper`: Wraps the parameters hash into a nested hash, - so that you don't have to specify root elements sending POST requests for instance. - -Other plugins may add additional modules. You can get a list of all modules -included into `ActionController::API` in the rails console: +API 애플리케이션(`ActionController::API`를 사용)에는 다음과 같은 컨트롤러 모듈이 포함됩니다. + +- `ActionController::UrlFor`: `url_for` 등의 헬퍼 제공 +- `ActionController::Redirecting`: `redirect_to` 제공 +- `AbstractController::Rendering`와 `ActionController::ApiRendering`: 기본적인 랜더링을 제공 +- `ActionController::Renderers::All`: `render :json` 등을 제공 +- `ActionController::ConditionalGet`: `stale?`을 제공 +- `ActionController::BasicImplicitRender`: 명시적인 응답이 없으면 빈 응답을 반환 +- `ActionController::StrongParameters`: 매개변수를 위한 화이트리스트를 제공(액티브 모델의 대량 할당과 함께 동작) +- `ActionController::ForceSSL`: `force_ssl`을 제공 +- `ActionController::DataStreaming`: `send_file`이나 `send_data`를 제공 +- `AbstractController::Callbacks`: `before_action` 등의 헬퍼를 제공 +- `ActionController::Rescue`: `rescue_from`을 제공 +- `ActionController::Instrumentation`: 액션 컨트롤러에서 정의하는 계측 훅을 제공([계측 가이드](active_support_instrumentation.html#action-controller)를 참조) +- `ActionController::ParamsWrapper`: 매개변수 해시를 감싸서 중첩된 해시로 만듦. 이를 통해서 POST 요청을 전송하는 경우에도 최상위 요소를 지정하지 않도록 해줌 + +다른 플러그인을 통해 모듈이 추가되는 경우도 있습니다. +`ActionController::API`의 모든 모듈 목록은 다음 명령으로 확인할 수 있습니다. ```bash $ bin/rails c @@ -403,22 +333,19 @@ $ bin/rails c ActionView::ViewPaths] ``` -### Adding Other Modules +### 그 외의 모듈 추가하기 + +액션 컨트롤러의 어떤 모듈도 자신이 의존하는 모듈을 파악하고 있으므로 자유롭게 +컨트롤러에 모듈을 추가할 수 있습니다. -All Action Controller modules know about their dependent modules, so you can feel -free to include any modules into your controllers, and all dependencies will be -included and set up as well. +자주 사용되는 것은 다음과 같습니다. -Some common modules you might want to add: +- `AbstractController::Translation`: 지역화용 `l`과 번역용 `t` 메소드를 제공 +- `ActionController::HttpAuthentication::Basic`(그리고 `Digest`, `Token`): HTTP의 기본 인증, 다이제스트 인증, 토큰 인증을 제공 +- `ActionView::Layouts`: 레이아웃을 제공 +- `ActionController::MimeResponds`: `respond_to`을 제공 +- `ActionController::Cookies`: 서명과 암호화를 포함한 `cookies`를 제공. 쿠키 미들웨어가 필요. -- `AbstractController::Translation`: Support for the `l` and `t` localization - and translation methods. -- `ActionController::HttpAuthentication::Basic` (or `Digest` or `Token`): Support - for basic, digest or token HTTP authentication. -- `ActionView::Layouts`: Support for layouts when rendering. -- `ActionController::MimeResponds`: Support for `respond_to`. -- `ActionController::Cookies`: Support for `cookies`, which includes - support for signed and encrypted cookies. This requires the cookies middleware. +모듈은 `ApplicationController`에 추가하는 것이 가장 좋습니다만, 각각의 +컨트롤러에 추가해도 괜찮습니다. -The best place to add a module is in your `ApplicationController`, but you can -also add modules to individual controllers. diff --git a/guides/source/ko/api_documentation_guidelines.md b/guides/source/ko/api_documentation_guidelines.md index ec943d74478da..71c5bbf92896d 100644 --- a/guides/source/ko/api_documentation_guidelines.md +++ b/guides/source/ko/api_documentation_guidelines.md @@ -106,7 +106,7 @@ If `return` is needed it is recommended to explicitly define a method. 식의 실행 결과를 함께 적는 경우, 앞 부분에 "# => "를 추가해서 횡으로 정렬하세요. ```ruby -# For checking if a fixnum is even or odd. +# For checking if an integer is even or odd. # # 1.even? # => false # 1.odd? # => true diff --git a/guides/source/ko/asset_pipeline.md b/guides/source/ko/asset_pipeline.md index 2b4c464f7469c..6ed4c32796f4c 100644 --- a/guides/source/ko/asset_pipeline.md +++ b/guides/source/ko/asset_pipeline.md @@ -17,19 +17,24 @@ Asset Pipeline 애셋 파이프라인에 대해서 --------------------------- -애셋 파이프라인이란 JavaScript나 CSS 애셋을 최소화(minify: 공백이나 개행 등을 제거) 또는 압축하여 연결하는 프레임워크입니다. 애셋 파이프라인에서는 CoffeeScript나 SASS, ERB 등의 다른 언어로 기술된 애셋을 생성하는 기능을 추가할 수도 있습니다. - -기술적으로는 애셋 파이프라인은 이미 Rails 4의 핵심 기능이 아닙니다. 프레임워크로부터 분리되어 [sprockets-rails](https://github.com/rails/sprockets-rails)라는 gem으로 제공되고 있습니다. - -Rails에서는 기본적으로 애셋 파이프라인을 사용하고 있습니다. - -Rails 애플리케이션을 새로 생성하는 경우에 애셋 파이프라인을 비활성화하고 싶은 경우에는 아래와 같이 `--skip-sprockets` 를 넘겨주세요. +애셋 파이프라인이란 JavaScript나 CSS 애셋을 최소화(minify: 공백이나 개행 등을 +제거) 또는 압축하여 연결하는 프레임워크입니다. 애셋 파이프라인에서는 +CoffeeScript나 SASS, ERB 등의 다른 언어로 기술된 애셋을 생성하는 기능을 추가할 +수도 있습니다. +거기에 다른 젬들로부터 애셋을 자동으로 애플리케이션에 결합해주기도 합니다. +예를 들어, jquery-rails는 jquery.js의 사본을 가지고 있으며 Rails에서 AJAX +기능을 사용할 수 있게 해줍니다. + +애셋 파이프라인은 [sprockets-rails](https://github.com/rails/sprockets-rails) +잼으로 구현되어 있으며, 새 앱을 만들때 기본으로 포함됩니다. 이를 비활성화하고 +싶은 경우에는 아래와 같이 `--skip-sprockets` 를 넘겨주세요. ```bash rails new appname --skip-sprockets ``` -Rails 4에서는 `sass-rails`, `coffee-rails`, `uglifier` gem이 자동적으로 Gemfile에 추가됩니다. Sprokets는 애셋을 압축할 때 이 gem들을 사용합니다. +Rails에서는 `sass-rails`, `coffee-rails`, `uglifier` gem이 자동적으로 Gemfile에 +추가됩니다. Sprokets는 애셋을 압축할 때 이 gem들을 사용합니다. ```ruby gem 'sass-rails' @@ -37,31 +42,56 @@ gem 'uglifier' gem 'coffee-rails' ``` -`--skip-sprockets` 옵션을 사용하면 Rails 4에서 `sass-rails`와 `uglifier`가 Gemfile에 추가되지 않습니다. 그러므로 애셋 파이프라인을 나중에 활성화하고 싶은 경우에는 이 gem들을 Gemfile에 다시 추가해주어야 합니다. 마찬가지로 애플리케이션을 새로 생성할 경우에 `--skip-sprockets` 옵션을 사용하면 `config/application.rb` 파일의 내용물이 약간 변경됩니다. 구체적으로는 sproket railtie에서 필요로하는 내용들이 주석처리됩니다. 애셋 파이프라인을 수동으로 활성화하는 경우에는 이러한 주석처리 된 코드들을 찾아서 주석처리를 해제해주어야 합니다. +`--skip-sprockets` 옵션을 사용하면 Rails는 이러한 젬들을 Gemfile에 추가하지 +않습니다. 그러므로 애셋 파이프라인을 나중에 활성화하고 싶은 경우에는 이 젬들을 +Gemfile에 다시 추가해주어야 합니다. 마찬가지로 애플리케이션을 새로 생성할 경우 +`--skip-sprockets` 옵션을 사용하면 `config/application.rb` 파일의 내용물이 약간 +변경됩니다. 구체적으로는 sproket railtie에서 필요로하는 내용들이 +주석처리됩니다. 애셋 파이프라인을 수동으로 활성화하는 경우에는 이러한 +주석처리 된 코드들을 찾아서 주석처리를 해제해주어야 합니다. ```ruby # require "sprockets/railtie" ``` -애셋 압축 방식을 지정하려면 `production.rb`에 존재하는 해당하는 설정을 찾습니다. `config.assets.css_compressor`로는 CSS 압축 방식, `config.assets.js_compressor`로는 JavaScript의 압축 방식을 각각 지정할 수 있습니다. +애셋 압축 방식을 지정하려면 `production.rb`에 존재하는 해당하는 설정을 +찾습니다. `config.assets.css_compressor`로는 CSS 압축 방식, +`config.assets.js_compressor`로는 JavaScript의 압축 방식을 각각 지정할 수 +있습니다. ```ruby config.assets.css_compressor = :yui config.assets.js_compressor = :uglifier ``` -NOTE: `sass-rails` gem이 Gemfile에 포함되어 있다면 자동적으로 CSS 압축에 사용됩니다. 이러한 경우 `config.assets.css_compressor` 옵션은 지정되지 않습니다. +NOTE: `sass-rails` gem이 Gemfile에 포함되어 있다면 자동적으로 CSS 압축에 +사용됩니다. 이러한 경우 `config.assets.css_compressor` 옵션은 지정되지 +않습니다. ### 주요한 기능 -애셋 파이프라인의 첫번째 기능은 애셋을 연결하는 것입니다. 이를 통해 브라우저가 웹페이지를 랜더링하기 위해서 서버에 보내야하는 HTTP 요청의 숫자를 줄일 수 있습니다. 웹 브라우저가 동시에 처리할 수 있는 요청 수에는 제한이 있으므로, 동시 요청수를 줄일 수 있다면 그만큼 페이지를 로딩하는 속도가 빨라집니다. +애셋 파이프라인의 첫번째 기능은 애셋을 연결하는 것입니다. 이를 통해 브라우저가 +웹페이지를 랜더링하기 위해서 서버에 보내야하는 HTTP 요청의 숫자를 줄일 수 +있습니다. 웹 브라우저가 동시에 처리할 수 있는 요청 수에는 제한이 있으므로, +동시 요청수를 줄일 수 있다면 그만큼 페이지를 로딩하는 속도가 빨라집니다. -Sprockets은 모든 JavaScript 파일을 하나의 마스터 `.js` 파일로 연결하고, 모든 CSS 파일을 하나의 마스터 `.css` 파일로 연결합니다. 이 가이드에서 나중에 설명하겠지만, 애셋 파일을 그룹으로 묶는 방법은 자유롭게 변경할 수 있습니다. production 환경에서는 애셋 파일명에 MD5 핑거프린트를 삽입하여 애셋 파일이 웹 브라우저에서 캐싱되도록 합니다. 핑거프린트의 변경은 애셋 파일의 내용물이 변경되었을 때에 자동적으로 이루어집니다. +Sprockets은 모든 JavaScript 파일을 하나의 마스터 `.js` 파일로 연결하고, 모든 +CSS 파일을 하나의 마스터 `.css` 파일로 연결합니다. 이 가이드에서 나중에 +설명하겠지만, 애셋 파일을 그룹으로 묶는 방법은 자유롭게 변경할 수 있습니다. +production 환경에서는 애셋 파일명에 MD5 핑거프린트를 삽입하여 애셋 파일이 +웹 브라우저에서 캐싱되도록 합니다. 핑거프린트의 변경은 애셋 파일의 내용물이 +변경되었을 때에 자동적으로 이루어집니다. -애셋 파이프라인의 또 다른 기능은 애셋의 최소화(압축)입니다. CSS파일의 최소화는 공백과 주석을 제거하는 것으로 이루어집니다. JavaScript의 압축 프로세스는 이보다 더 복잡합니다. 이때에 사용할 방법은 내장된 옵션으로부터 선택하거나 다른 것을 지정할 수 있습니다. +애셋 파이프라인의 또 다른 기능은 애셋의 최소화(압축)입니다. CSS파일의 최소화는 +공백과 주석을 제거하는 것으로 이루어집니다. JavaScript의 압축 프로세스는 이보다 +더 복잡합니다. 이 때 사용할 방법은 내장된 옵션으로부터 선택하거나 다른 것을 +지정할 수 있습니다. -애셋 파이프라인의 3번재 기능은 보다 고도의 언어를 사용한 코딩을 지원하는 것입니다. 이러한 언어로 작성된 코드는 전처리되어 시렞의 애셋이 됩니다. 기본으로 지원되는 언어는 CSS로 변환되는 SASS, JavaScript로 변환되는 CoffeeScript, CSS/JavaScript로 변환되는 ERB입니다. +애셋 파이프라인의 3번째 기능은 보다 고도의 언어를 사용한 코딩을 지원하는 +것입니다. 이러한 언어로 작성된 코드는 전처리되어 시렞의 애셋이 됩니다. +기본으로 지원되는 언어는 CSS로 변환되는 SASS, JavaScript로 변환되는 +CoffeeScript, CSS/JavaScript로 변환되는 ERB입니다. ### 핑거프린트와 주의점 @@ -150,7 +180,7 @@ end * `vendor/assets`는 JavaScript 플러그인이나 CSS 프레임워크 등, 외부의 단체 등이 관리하는 애셋을 저장합니다. -WARNING: Rails 3으로부터 업그레이드를 하는 경우에는 `lib/assets`과 `vendor/assets`에 저장되어 있는 애셋이 Rails 4에서는 애플리케이션의 매니페스트에 의해서 포함되어 사용가능하다는 점, 단 미리 컴파일될 파일 목록에는 포함되지 않게 되었다는 점을 주의해주세요. 더 자세한 안내는 [애셋을 미리 컴파일하기](#애셋을-미리-컴파일하기)를 참조해주세요. +WARNING: Rails 3으로부터 업그레이드를 하는 경우에는 `lib/assets`과 `vendor/assets`에 저장되어 있는 애셋이 Rails에서는 애플리케이션의 매니페스트에 의해서 포함되어 사용가능하다는 점, 단 미리 컴파일될 파일 목록에는 포함되지 않게 되었다는 점을 주의해주세요. 더 자세한 안내는 [애셋을 미리 컴파일하기](#애셋을-미리-컴파일하기)를 참조해주세요. #### 경로 탐색 @@ -223,11 +253,11 @@ Sprockets은 애셋을 사용하기 위한 메소드를 추가해주지 않습 <%= javascript_include_tag "application" %> ``` -Rails 4부터 포함되는 turbolinks gem를 사용하고 있는 경우, 'data-turbolinks-track' 옵션을 사용할 수 있습니다. 이것은 애셋이 갱신되어 페이지에 로딩되었는지 아닌지 turbolinks가 확인합니다. +Rails부터 포함되는 turbolinks gem를 사용하고 있는 경우, 'data-turbolinks-track' 옵션을 사용할 수 있습니다. 이것은 애셋이 갱신되어 페이지에 로딩되었는지 아닌지 turbolinks가 확인합니다. ```erb -<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> -<%= javascript_include_tag "application", "data-turbolinks-track" => true %> +<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => "reload" %> +<%= javascript_include_tag "application", "data-turbolinks-track" => "reload" %> ``` 일반적인 뷰에서는 아래와 같은 방법으로 `public/assets/images` 폴더의 이미지에 접근할 수 있습니다. @@ -303,7 +333,7 @@ $('#logo').attr src: "<%= asset_path('logo.png') %>" Sprockets에서는 어떤 애셋을 가져와서 지원할지를 지정할 때에 매니페스트 파일을 사용합니다. 매니페스트 파일에는 _디렉티브(directive)_가 포함되어 있습니다. 디렉티브를 사용하여 필요한 파일을 지정하고, 거기에 기반하여 최종적인 단일 CSS나 JavaScript 파일이 생성됩니다. Sprockets는 디렉티브로 지정된 파일을 읽어와, 필요에 따라서 처리를 하고 연결하여 단일 파일을 생성하여 압축해줍니다(`Rails.application.config.assets.compress`가 true인 경우). 파일을 연결하여 하나로 만드는 것으로 브라우저로부터 서버에 대한 요청 횟수를 줄일 수 있으며, 압축을 통해서 파일 사이즈도 줄여서 페이지를 읽어오는데 걸리는 시간을 대폭 단축시킵니다. -새로 생성한 Rails 4애플리케이션에서는 기본으로 `app/assets/javascripts/application.js` 파일에 다음과 같은 내용이 포함되어 있습니다. +새로 생성한 Rails 애플리케이션에서는 기본으로 `app/assets/javascripts/application.js` 파일에 다음과 같은 내용이 포함되어 있습니다. ```js // ... @@ -327,7 +357,7 @@ Rails는 아래의 내용을 포함하는 `app/assets/stylesheets/application.cs */ ``` -Rails 4는 `app/assets/javascripts/application.js`와 `app/assets/stylesheets/application.css` 파일을 둘 다 생성합니다. 이것은 Rails 애플리케이션을 새로 생성할 때의 `--skip-sprockets`와는 관계 없이 실행됩니다. 이에 따라 필요에 따라 손쉽게 애셋 파이프라인을 추가할 수도 있습니다. +Rails는 `app/assets/javascripts/application.js`와 `app/assets/stylesheets/application.css` 파일을 둘 다 생성합니다. 이것은 Rails 애플리케이션을 새로 생성할 때의 `--skip-sprockets`와는 관계 없이 실행됩니다. 이에 따라 필요에 따라 손쉽게 애셋 파이프라인을 추가할 수도 있습니다. JavaScript에서 사용할 수 있는 디렉티브는 스타일시트에서도 사용할 수 있습니다(그러나 JavaScript와 다르게 스타일시트는 명시적으로 포함된다는 점이 다릅니다). CSS 매니페스트에서의 `require_tree` 디렉티브의 동작은 JavaScript의 경우와 마찬가지로 지정된 폴더에 있는 모든 스타일시트를 require합니다. @@ -392,6 +422,19 @@ config.assets.raise_runtime_errors = false 이 옵션이 true라면 애플리케이션의 애셋이 `config.assets.precompile`에 기술되어 있는 순서대로 모두 불러오는지를 확인합니다. `config.assets.digest`도 true인 경우, 애셋에 대한 요청에서는 다이제스트를 반드시 포함해야합니다. +### 애셋을 찾을 수 없을 때 에러를 던지기 + +sprockets-rails >= 3.2.0를 사용하고 있다면 애셋을 요청받고, 발견하지 못했을 때 +어떤 행동을 할지 설정할 수 있습니다. "asset fallback"을 끄고 있다면 애셋을 +발견하지 못했을 때 에러를 던집니다. + +```ruby +config.assets.unknown_asset_fallback = false +``` + +만약 "asset fallback"이 활성화되어 있다면 애셋의 경로를 찾지 못했다는 메시지가 +에러를 던지는 대신 반환됩니다. 이 동작은 기본으로 활성화되어 있습니다. + ### 다이제스트를 비활성화하기 `config/environments/development.rb`를 다음과 같이 고쳐서 다이제스트를 비활성화할 수 있습니다. @@ -416,9 +459,13 @@ config.assets.debug = false ``` -애셋은 서버 기동 후에 첫번째 리퀘스트를 받은 시점에서 컴파일과 캐시가 실행됩니다. Sprockets는 `must-revalidate`라는 Cache-Control HTTP 헤더를 설정하여 이후의 요청에 대한 오버헤드를 줄입니다. 이 경우 브라우저는 304(Not Modified) 응답을 받게 됩니다. +애셋은 서버 기동 후에 첫번째 리퀘스트를 받은 시점에서 컴파일과 캐시가 +실행됩니다. Sprockets는 `must-revalidate`라는 Cache-Control HTTP 헤더를 +설정하여 이후의 요청에 대한 오버헤드를 줄입니다. 이 경우 브라우저는 +304(Not Modified) 응답을 받게 됩니다. -요청과 요청 사이에 매니페스트에 지정되어 있는 파일 중 하나에서 변경이 있었을 경우, Rails 서버는 새로 컴파일 된 파일을 응답으로 돌려줍니다. +요청과 요청 사이에 매니페스트에 지정되어 있는 파일 중 하나에서 변경이 있었을 +경우, Rails 서버는 새로 컴파일 된 파일을 응답으로 돌려줍니다. Rails의 헬퍼 메소드를 사용하여 디버그 모드를 켤 수도 있습니다. @@ -429,14 +476,18 @@ Rails의 헬퍼 메소드를 사용하여 디버그 모드를 켤 수도 있습 디버그 모드가 이미 켜져있는 경우, `:debug` 옵션은 의미가 없습니다. -development 환경에서 건전성을 확인하기 위한 일환으로 압축을 활성화하거나, 디버그의 필요성에 따라 그때그때 켜고 끌 수 있습니다. +development 환경에서 건전성을 확인하기 위한 일환으로 압축을 활성화하거나, +디버그의 필요성에 따라 그때그때 켜고 끌 수 있습니다. production 환경의 경우 ------------- -Sprockets은 production 환경에서는 위에서 말한 핑거프린트에 의한 스킴을 사용합니다. 기본으로 Rails의 애셋은 전처리된 정적인 애셋으로 웹서버에서 제공됩니다. +Sprockets은 production 환경에서는 위에서 말한 핑거프린트에 의한 스킴을 +사용합니다. 기본으로 Rails의 애셋은 전처리된 정적인 애셋으로 웹서버에서 +제공됩니다. -MD5는 컴파일된 파일의 내용을 기반으로 전처리중에 생성되며, 파일명에 추가되어 저장됩니다. 매니페스트의 이름은 Rails 헬퍼가 핑거프린트를 추가하여 사용합니다. +MD5는 컴파일된 파일의 내용을 기반으로 전처리중에 생성되며, 파일명에 추가되어 +저장됩니다. 매니페스트의 이름은 Rails 헬퍼가 핑거프린트를 추가하여 사용합니다. 다음은 예시입니다. @@ -452,19 +503,27 @@ MD5는 컴파일된 파일의 내용을 기반으로 전처리중에 생성되 ``` -NOTE: 애셋 파이프라인의 `:cache` 옵션과 `:concat`옵션은 폐기되었습니다. 이러한 옵션은 `javascript_include_tag`와 `stylesheet_link_tag`에서 삭제해주세요. +NOTE: 애셋 파이프라인의 `:cache` 옵션과 `:concat`옵션은 폐기되었습니다. +이러한 옵션은 `javascript_include_tag`와 `stylesheet_link_tag`에서 +삭제해주세요. -핑거프린트의 동작에 대해서는 `config.assets.digest` 초기화 옵션에서 제어할 수 있습니다. production 환경에서는 기본으로 `true`이며, 그 이외에서는 `false`입니다. +핑거프린트의 동작에 대해서는 `config.assets.digest` 초기화 옵션에서 제어할 수 +있습니다. 기본값은 `true`입니다. -NOTE: `config.assets.digest` 옵션은 가급적 변경하지 말아주세요. 파일명에 다이제스트가 포함되지 않으면 먼 미레에 헤더가 설정되었을 때에 클라이언트가 파일의 내용이 변경된 것을 검출하지 못하게 될 수 있습니다. +NOTE: `config.assets.digest` 옵션은 가급적 변경하지 말아주세요. 파일명에 +다이제스트가 포함되지 않으면 먼 미레에 헤더가 설정되었을 때에 클라이언트가 +파일의 내용이 변경된 것을 검출하지 못하게 될 수 있습니다. ### 애셋을 전처리하기 -Rails에는 파이프라인에 애셋 매니페스트 파일을 수동으로 컴파일하기 위한 태스크가 포함되어 있습니다. +Rails에는 파이프라인에 애셋 매니페스트 파일을 수동으로 컴파일하기 위한 +태스크가 포함되어 있습니다. -컴파일 된 애셋은 `config.assets.prefix`에서 지정한 위치에 저장됩니다. 이 위치의 기본값은 `/assets` 폴더 입니다. +컴파일 된 애셋은 `config.assets.prefix`에서 지정한 위치에 저장됩니다. 이 +위치의 기본값은 `/assets` 폴더 입니다. -배포시에 이 태스크를 서버 상에서 실행하면, 컴파일된 애셋이 서버 상에 직접 생성됩니다. 로컬 환경에서 컴파일 하는 방법에 대해서는 다음 절을 참고해주세요. +배포시에 이 태스크를 서버 상에서 실행하면, 컴파일된 애셋이 서버 상에 직접 +생성됩니다. 로컬 환경에서 컴파일 하는 방법에 대해서는 다음 절을 참고해주세요. 다음이 그 태스크입니다. @@ -472,7 +531,8 @@ Rails에는 파이프라인에 애셋 매니페스트 파일을 수동으로 컴 $ RAILS_ENV=production bin/rails assets:precompile ``` -Capistrano (v2.15.1 이후)에는 배포중에 이 태스크를 사용하는 레시피가 포함되어 있습니다. `Capfile`에 다음을 추가합니다. +Capistrano (v2.15.1 이후)에는 배포중에 이 태스크를 사용하는 레시피가 포함되어 +있습니다. `Capfile`에 다음을 추가합니다. ```ruby load 'deploy/assets' @@ -481,42 +541,34 @@ load 'deploy/assets' 이를 통해 `config.assets.prefix`로 지정된 폴더가 `shared/assets`에 링크됩니다. 이미 이 공유 폴더를 사용하고 있다면 별도의 배포용 태스크를 작성해야합니다. -이 폴더는 복수의 배포에 걸쳐 공유된다는 점이 중요합니다. 이는 서버 이외의 다른 장소에서 캐시되어있는 패이지가 오래된 컴파일된 애셋을 참조하고 있는 경우에도, 캐시된 페이지의 수명이 되어 삭제될 때 까지는 그 오래된 페이지의 참조가 유효하도록 만들기 때문입니다. +이 폴더는 복수의 배포에 걸쳐 공유된다는 점이 중요합니다. 이는 서버 이외의 다른 +장소에서 캐시되어있는 패이지가 오래된 컴파일된 애셋을 참조하고 있는 경우에도, +캐시된 페이지의 수명이 되어 삭제될 때까지는 그 오래된 페이지의 참조가 +유효하도록 만들기 때문입니다. -파일을 컴파일 할 때에 기본 매쳐에 의해서 `app/assets` 폴더에 있는 `application.js`, `application.css`, 그리고 모든 비JS/CSS 파일(이를 통해 모든 이미지 파일도 자동적으로 포함됩니다)가 포함됩니다. `app/assets` 폴더에 있는 gem도 포함됩니다. +파일을 컴파일 할 때에 기본 매쳐에 의해서 `app/assets` 폴더에 있는 +`application.js`, `application.css`, 그리고 모든 비JS/CSS 파일(이를 통해 모든 +이미지 파일도 자동적으로 포함됩니다)가 포함됩니다. `app/assets` 폴더에 있는 +젬도 포함됩니다. ```ruby [ Proc.new { |filename, path| path =~ /app\/assets/ && !%w(.js .css).include?(File.extname(filename)) }, /application.(css|js)$/ ] ``` -NOTE: 이 매쳐(그리고 뒤에서 설명할 precompile 배열의 다른 멤버)가 적용되는 것은 컴파일 전이나 컴파일 중의 파일명이 아닌, 컴파일 후의 최종적인 파일명이라는 점을 주의해주세요. 이것은 컴파일 되어서 JavaScript나 CSS로 변환되는 중간 과정인 파일은(순수한 JavaScript/CSS와 마찬가지로) 매쳐의 대상에서 모두 제외된다는 의미입니다. 예를 들자면 `.coffee`와 `.scss` 파일은 컴파일 후에는 각각 JavaScript와 CSS로 변환되므로, 이들은 자동적으로 포함되지 않습니다. +NOTE: 이 매쳐(그리고 뒤에서 설명할 precompile 배열의 다른 멤버)가 적용되는 +것은 컴파일 전이나 컴파일 중의 파일명이 아닌, 컴파일 후의 최종적인 +파일명이라는 점을 주의해주세요. 이것은 컴파일 되어서 JavaScript나 CSS로 +변환되는 중간 과정인 파일은(순수한 JavaScript/CSS와 마찬가지로) 매쳐의 +대상에서 모두 제외된다는 의미입니다. 예를 들자면 `.coffee`와 `.scss` 파일은 +컴파일 후에는 각각 JavaScript와 CSS로 변환되므로, 이들은 자동적으로 포함되지 +않습니다. -다른 매니페스트나, 그 외의 스타일시트/JavaScript 파일을 포함하고 싶은 경우에는 `config/initializers/assets.rb`의 `precompile`라는 배열을 사용하세요. +다른 매니페스트나, 그 외의 스타일시트/JavaScript 파일을 포함하고 싶은 경우에는 +`config/initializers/assets.rb`의 `precompile`라는 배열을 사용하세요. ```ruby -Rails.application.config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js'] -``` - -또는 아래와 같이 모든 애셋을 미리 컴파일할 수도 있습니다. - -```ruby -# config/initializers/assets.rb -Rails.application.config.assets.precompile << Proc.new do |path| - if path =~ /\.(css|js)\z/ - full_path = Rails.application.assets.resolve(path).to_path - app_assets_path = Rails.root.join('app', 'assets').to_path - if full_path.starts_with? app_assets_path - logger.info "including asset: " + full_path - true - else - logger.info "excluding asset: " + full_path - false - end - else - false - end -end +Rails.application.config.assets.precompile += %w( admin.js admin.css ) ``` NOTE: precompile 배열에 Sass나 CoffeeScript 파일등을 추가할 경우에도 반드시 `.js`, `.css`로 끝나는 파일명(다시 말해 컴파일이 끝난 시점의 파일명)으로 지정해주세요. @@ -536,9 +588,11 @@ NOTE: precompile 배열에 Sass나 CoffeeScript 파일등을 추가할 경우에 "my_image-231a680f23887d9dd70710ea5efd3c62.png"}} ``` -매니페스트 위치의 기본값은 `config.assets.prefix`로 지정된 장소의 최상위 폴더(기본값은 '/assets')입니다. +매니페스트 위치의 기본값은 `config.assets.prefix`로 지정된 장소의 최상위 +폴더(기본값은 '/assets')입니다. -NOTE: production 환경에서 발견되지 않는 컴파일 후의 파일이 있다면, 찾을 수 없는 파일명을 에러 메시지에 포함하고 있는 `Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError`가 발생합니다. +NOTE: production 환경에서 발견되지 않는 컴파일 후의 파일이 있다면, 찾을 수 없는 +파일명을 에러 메시지에 포함하고 있는 `Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError`가 발생합니다. #### 먼 미래에 유효기간이 끝나는 헤더 @@ -567,19 +621,20 @@ location ~ ^/assets/ { add_header Cache-Control public; add_header ETag ""; - break; } ``` ### 로컬에서 미리 컴파일하기 -애셋을 로컬에서 미리 컴파일하는 이유는 몇가지를 생각해볼 수 있습니다. 예를 들자면 다음과 같은 이유입니다. +애셋을 로컬에서 미리 컴파일하는 이유는 몇가지를 생각해볼 수 있습니다. 예를 +들자면 다음과 같은 이유입니다. * production 환경의 파일 시스템에 쓰기 권한이 없음 * 배포를 여러곳에 해야해서 같은 작업을 반복하고 싶지 않음 * 애셋을 변경하지 않는 배포를 빈번하게 함 -로컬에서 컴파일하여 컴파일 후의 애셋 파일을 Git 등에 의해 소스 관리 대상으로 포함하고, 다른 파일과 함께 배포되도록 만들 수 있습니다. +로컬에서 컴파일하여 컴파일 후의 애셋 파일을 Git 등에 의해 소스 관리 대상으로 +포함하고, 다른 파일과 함께 배포되도록 만들 수 있습니다. 단, 주의할 부분이 있습니다. @@ -593,13 +648,21 @@ location ~ ^/assets/ { config.assets.prefix = "/dev-assets" ``` -`prefix`를 변경하면 Sprockets은 development 환경에서 다른 URL을 사용해서 애셋을 제공하며, 모든 요청이 Sprockets에 넘겨지게 됩니다. production환경의 접두어는 `/assets`를 그대로 사용합니다. 이 변경이 이루어지지 않으면 애플리케이션은 development 환경에서도 production 환경과 동일한 애셋을 제공합니다. 이 경우, 애셋을 다시 컴파일하지 않으면 작업 중의 변경사항이 반영되지 않습니다. +`prefix`를 변경하면 Sprockets은 development 환경에서 다른 URL을 사용해서 애셋을 +제공하며, 모든 요청이 Sprockets에 넘겨지게 됩니다. production환경의 접두어는 +`/assets`를 그대로 사용합니다. 이 변경이 이루어지지 않으면 애플리케이션은 +development 환경에서도 production 환경과 동일한 애셋을 제공합니다. 이 경우, +애셋을 다시 컴파일하지 않으면 작업 중의 변경사항이 반영되지 않습니다. -실제로는, 이를 통해 로컬에서 컴파일을 할 수 있게 되므로, 필요에 따라 그 파일들을 소스 관리 시스템에 커밋할 수 있게 됩니다. development 환경은 기대한 대로 동작을 하게 됩니다. +실제로는, 이를 통해 로컬에서 컴파일을 할 수 있게 되므로, 필요에 따라 그 +파일들을 소스 관리 시스템에 커밋할 수 있게 됩니다. development 환경은 기대한 +대로 동작을 하게 됩니다. ### 동적인 컴파일 -상황에 따라서는 동적으로 컴파일(live compilation)을 사용하고 싶은 경우도 있을 겁니다. 이 상황에서는 파이프라인의 애셋에 대한 요청이 직접 Sprockets을 통해 처리됩니다. +상황에 따라서는 동적으로 컴파일(live compilation)을 사용하고 싶은 경우도 있을 +겁니다. 이 상황에서는 파이프라인의 애셋에 대한 요청이 직접 Sprockets을 통해 +처리됩니다. 이 옵션을 활성화하려면 아래와 같이 설정합니다. @@ -607,13 +670,21 @@ config.assets.prefix = "/dev-assets" config.assets.compile = true ``` -최초의 요청을 받으면 애셋은 위의 development 환경의 부분에서 설명했듯 컴파일과 캐싱 작업이 이루어 집니다. 헬퍼에서 사용되는 매니페스트 이름은 MD5 해시가 포함됩니다. +최초의 요청을 받으면 애셋은 위의 development 환경의 부분에서 설명했듯 컴파일과 +캐싱 작업이 이루어 집니다. 헬퍼에서 사용되는 매니페스트 이름은 MD5 해시가 +포함됩니다. -또한 Sprockets는 `Cache-Control` HTTP 헤더를 `max-age=31536000`로 변경합니다. 이 헤더는 서버와 클라이언트의 사이에 존재하는 모든 캐시(프록시 등)에 대해서 서버가 제공하는 컨텐츠는 1년간 캐시해도 좋다고 알립니다. 이에 의해서 그 서버의 애셋에 대한 요청 수를 줄일 수 있으며, 애셋을 브라우저에서 직접 캐시하거나, 그 중간에서 캐시로 대체할 수 있는 기회가 주어집니다. +또한 Sprockets는 `Cache-Control` HTTP 헤더를 `max-age=31536000`로 변경합니다. +이 헤더는 서버와 클라이언트의 사이에 존재하는 모든 캐시(프록시 등)에 대해서 +서버가 제공하는 컨텐츠는 1년간 캐시해도 좋다고 알립니다. 이에 의해서 그 서버의 +애셋에 대한 요청 수를 줄일 수 있으며, 애셋을 브라우저에서 직접 캐시하거나, +그 중간에서 캐시로 대체할 수 있는 기회가 주어집니다. -이 기능은 메모리를 추가로 사용하며, 성능에 영향을 줄 수 있므로 권장하지 않습니다. +이 기능은 메모리를 추가로 사용하며, 성능에 영향을 줄 수 있므로 권장하지 +않습니다. -실제 애플리케이션의 배포시스템에 JavaScript 런타임이 없는 경우에는 다음을 Gemfile에 추가하세요. +실제 애플리케이션의 배포시스템에 JavaScript 런타임이 없는 경우에는 다음을 +Gemfile에 추가하세요. ```ruby group :production do @@ -623,21 +694,52 @@ end ### CDN -CDN([컨텐츠 전송 네트워크](http://ko.wikipedia.org/wiki/%EC%BD%98%ED%85%90%EC%B8%A0_%EC%A0%84%EC%86%A1_%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC))는 전세계를 대상으로 애셋을 캐싱하는 것을 주목적으로 설계됩니다. 이를 통해 브라우저에서 애셋을 요청하게 되면, 네트워크 상에서 가장 가까운 캐시의 사본이 사용됩니다. production 환경의 Rails 서버로부터 (중간 캐시를 사용하지 않고) 직접 애셋을 제공하고 있다면, 애플리케이션과 브라우저의 사이에서 CDN을 사용하는 것이 가장 좋습니다. - -CDN의 일반적인 사용법은 production 서버를 "origin" 서버로 설정하는 것입니다. 다시 말해, 브라우저가 CDN 상의 애셋을 요청하여, 캐시가 발견되지 않았을 경우 즉시 원 서버로부터 애셋 파일을 가져와서 캐싱하는 식입니다. 예를 들자면, Rails 애플리케이션을 `example.com`이라는 도메인으로 운영하고 있고, `mycdnsubdomain.fictional-cdn.com`라는 CDN이 설정되어 있다고 가정합시다. `mycdnsubdomain.fictional-cdn.com/assets/smile.png`이 요청되면, CDN은 일단 기존 서버의 `example.com/assets/smile.png`에 접근하여 이 요청을 캐싱합니다. CDN에 같은 요청이 다시 발생하면 캐시된 사본을 사용하게 됩니다. CDN이 애셋을 직접 제공하는 경우, 브라우저로부터 요청이 직접 Rails 서버에 넘어가는 경우는 없습니다. CDN이 제공하는 애셋은 네트워크 상에서 브라우저와 가까운 위치에 존재하므로 요청이 빠르게 처리됩니다. 또한, 서버는 애셋 전송에 사용할 시간을 절약할 수 있으므로 애플리케이션의 코드를 좀 더 빠르게 제공할 수 있게 됩니다. +CDN([컨텐츠 전송 네트워크](http://ko.wikipedia.org/wiki/%EC%BD%98%ED%85%90%EC%B8%A0_%EC%A0%84%EC%86%A1_%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC))는 전세계를 +대상으로 애셋을 캐싱하는 것을 주목적으로 설계됩니다. 이를 통해 브라우저에서 +애셋을 요청하게 되면, 네트워크 상에서 가장 가까운 캐시의 사본이 사용됩니다. +production 환경의 Rails 서버로부터 (중간 캐시를 사용하지 않고) 직접 애셋을 +제공하고 있다면, 애플리케이션과 브라우저의 사이에서 CDN을 사용하는 것이 +가장 좋습니다. + +CDN의 일반적인 사용법은 production 서버를 "origin" 서버로 설정하는 것입니다. +다시 말해, 브라우저가 CDN 상의 애셋을 요청하여, 캐시가 발견되지 않았을 경우 +즉시 원 서버로부터 애셋 파일을 가져와서 캐싱하는 식입니다. 예를 들자면, +Rails 애플리케이션을 `example.com`이라는 도메인으로 운영하고 있고, +`mycdnsubdomain.fictional-cdn.com`라는 CDN이 설정되어 있다고 가정합시다. +`mycdnsubdomain.fictional-cdn.com/assets/smile.png`이 요청되면, CDN은 일단 +기존 서버의 `example.com/assets/smile.png`에 접근하여 이 요청을 캐싱합니다. +CDN에 같은 요청이 다시 발생하면 캐시된 사본을 사용하게 됩니다. CDN이 애셋을 +직접 제공하는 경우, 브라우저로부터 요청이 직접 Rails 서버에 넘어가는 경우는 +없습니다. CDN이 제공하는 애셋은 네트워크 상에서 브라우저와 가까운 위치에 +존재하므로 요청이 빠르게 처리됩니다. 또한, 서버는 애셋 전송에 사용할 시간을 +절약할 수 있으므로 애플리케이션의 코드를 좀 더 빠르게 제공할 수 있게 됩니다. #### CDN에서 정적인 애셋을 제공하기 -CDN을 설정하려면, Rails 애플리케이션이 인터넷 상에서 production 환경으로 동작하고 있어야하며, `example.com`처럼 누구라도 접근할 수 있는 URL이 존재해야 합니다. 이어서 클라우드 호스팅 제공자가 제공하는 CDN 서비스와 계약할 필요도 있습니다. 이 경우, CDN의 "origin" 설정을 Rails 애플리케이션의 웹사이트 `example.com`로 설정해야압니다. "origin" 서버의 설정 방법에 대해서는 각 제공자에게 문의해주세요. - -서비스에서 사용하는 CDN으로부터 애플리케이션에서 사용하기 위한 커스텀 서브 도메인(ex: `mycdnsubdomain.fictional-cdn.com`)도 얻어야 합니다. 여기까지로 CDN 서버의 설정이 완료되므로 이번에는 브라우저에 대해서 Rails 서버에 직접 접근하는 것이 아닌 CDN으로부터 애셋을 가져오도록 알려줄 필요가 있습니다. 이를 위해서는 본래 사용하던 상대경로 대신에 CDN을 애셋의 호스트 서버로 사용하도록 Rails를 변경합니다. Rails의 애셋 호스트를 설정하려면 `config/production.rb`의 `config.action_controller.asset_host`를 다음과 같이 설정하세요. +CDN을 설정하려면, Rails 애플리케이션이 인터넷 상에서 production 환경으로 +동작하고 있어야하며, `example.com`처럼 누구라도 접근할 수 있는 URL이 존재해야 +합니다. 이어서 클라우드 호스팅 제공자가 제공하는 CDN 서비스와 계약할 필요도 +있습니다. 이 경우, CDN의 "origin" 설정을 Rails 애플리케이션의 웹사이트 +`example.com`로 설정해야압니다. "origin" 서버의 설정 방법에 대해서는 각 +제공자에게 문의해주세요. + +서비스에서 사용하는 CDN으로부터 애플리케이션에서 사용하기 위한 커스텀 서브 +도메인(ex: `mycdnsubdomain.fictional-cdn.com`)도 얻어야 합니다. 여기까지로 +CDN 서버의 설정이 완료되므로 이번에는 브라우저에 대해서 Rails 서버에 직접 +접근하는 것이 아닌 CDN으로부터 애셋을 가져오도록 알려줄 필요가 있습니다. +이를 위해서는 본래 사용하던 상대경로 대신에 CDN을 애셋의 호스트 서버로 +사용하도록 Rails를 변경합니다. Rails의 애셋 호스트를 설정하려면 +`config/environments/production.rb`의 `config.action_controller.asset_host`를 다음과 같이 +설정하세요. ```ruby config.action_controller.asset_host = 'mycdnsubdomain.fictional-cdn.com' ``` -NOTE: 여기에 적는 것은 "호스트명"(서브 도메인과 루트 도메인을 합친 것)뿐입니다. `http://`나 `https://` 같은 프로토콜 스킴을 적을 필요는 없습니다. 애셋에 대한 링크에서 사용되는 프로토콜 스킴은 웹페이지에 대한 요청이 발생했을 때, 그 페이지에 대한 기본 접근 방법에 따라서 적절하게 생성됩니다. +NOTE: 여기에 적는 것은 "호스트명"(서브 도메인과 루트 도메인을 합친 +것)뿐입니다. `http://`나 `https://` 같은 프로토콜 스킴을 적을 필요는 없습니다. +애셋에 대한 링크에서 사용되는 프로토콜 스킴은 웹페이지에 대한 요청이 발생했을 +때, 그 페이지에 대한 기본 접근 방법에 따라서 적절하게 생성됩니다. 이 값은 [환경변수](http://ko.wikipedia.org/wiki/%ED%99%98%EA%B2%BD_%EB%B3%80%EC%88%98)로 설정할 수도 있습니다. 이를 사용하면 스테이징 서버를 실행하는 작업이 편해집니다. @@ -645,9 +747,11 @@ NOTE: 여기에 적는 것은 "호스트명"(서브 도메인과 루트 도메 config.action_controller.asset_host = ENV['CDN_HOST'] ``` -NOTE: 이 설정을 유효하게 만들려면 서버의 `CDN_HOST` 환경 변수에 값(이 경우라면, `mycdnsubdomain.fictional-cdn.com`)을 설정해두어야 합니다. +NOTE: 이 설정을 유효하게 만들려면 서버의 `CDN_HOST` 환경 변수에 값(이 경우라면, +`mycdnsubdomain.fictional-cdn.com`)을 설정해두어야 합니다. -서버와 CDN의 설정을 완료한 후, 다음의 애셋을 가지고 있는 웹 페이지에 접근했다고 가정합니다. +서버와 CDN의 설정을 완료한 후, 다음의 애셋을 가지고 있는 웹 페이지에 +접근했다고 가정합니다. ```erb <%= asset_path('smile.png') %> @@ -775,11 +879,21 @@ config.assets.js_compressor = :uglifier NOTE: `uglifier`를 사용하려면 [ExecJS](https://github.com/sstephenson/execjs#readme)가 지원하는 JavaScript 런타임이 필요합니다. Mac OS X나 Windows를 사용하고 있는 경우에는 OS에 JavaScript 런타임을 설치해주세요. -NOTE: CSS나 JavaScript의 압축을 활성화하는 `config.assets.compress` 옵션은 Rails 4에서 제거되었습니다. 현재는 이 옵션을 설정하더라도 아무 영향도 주지 않습니다. CSS 및 JavaScript 애셋 압축을 제어하기 위해서는 `config.assets.css_compressor`와 `config.assets.js_compressor`를 사용하세요. +### GZip으로 압축한 애셋 제공하기 + +기본으로 gzip으로 압축된 애셋이 압축되지 않은 애셋과 함께 생성됩니다. 압축된 +애셋을 사용하면 전송량을 줄일 수 있습니다. 이는 `gzip` 플래그를 통해 변경할 +수 있습니다. + +```ruby +config.assets.gzip = false # 압축된 애셋 생성하지 않기 +``` ### 다른 압축 방법을 사용하기 -CSS나 JavaScript의 압축 설정에는 다른 객체를 설정할 수도 있습니다. 설정에 넘길 객체에는 `compress` 메소드가 구현되어 있어야 합니다. 이 메소드는 문자열을 인수로 받아서 압축 결과를 문자열의 형태로 반환하면 됩니다. +CSS나 JavaScript의 압축 설정에는 다른 객체를 설정할 수도 있습니다. 설정에 넘길 +객체에는 `compress` 메소드가 구현되어 있어야 합니다. 이 메소드는 문자열을 +인수로 받아서 압축 결과를 문자열의 형태로 반환하면 됩니다. ```ruby class Transformer @@ -795,7 +909,6 @@ end config.assets.css_compressor = Transformer.new ``` - ### _애셋_의 경로를 변경하기 Sprockets가 사용하는 애셋의 경로는 기본값으로 `/assets`입니다. @@ -828,18 +941,13 @@ TIP: 자세한 설명은 production 환경용의 웹서버 문서를 참고해 애셋의 캐시 저장소 ------------------ -Rails의 캐시 저장소는 Sprockets를 사용해서 development 환경과 production 환경의 애셋을 캐싱할 때 사용됩니다. 캐시 저장소의 설정은 `config.assets.cache_store`에서 변경할 수 있습니다. - -```ruby -config.assets.cache_store = :memory_store -``` +기본으로 Sprockets 캐시는 development 환경과 production 환경에서 +`tmp/cache/assets`를 사용합니다. 이 경로는 다음과 같이 변경할 수 있습니다. -애셋 캐시 스토어에서 사용할 수 있는 옵션은 애플리케이션의 캐시 스토어와 동일합니다. - - -```ruby -config.assets.cache_store = :memory_store, { size: 32.megabytes } -``` +config.assets.configure do |env| + env.cache = ActiveSupport::Cache.lookup_store(:memory_store, + { size: 32.megabytes }) +end 애셋 캐시 스토어를 비활성화 하려면 다음의 코드를 추가합니다. @@ -910,20 +1018,26 @@ config.assets.debug = true `production.rb`의 경우. ```ruby -# 압축 기능을 사용하려면 config.assets.js_compressor = 를 사용하세요 -# :uglifier config.assets.css_compressor = :yui +# 사용하고 싶은 전처리기를 선택하세요 +config.assets.js_compressor = :uglifier +# config.assets.css_compressor = :yui # 컴파일된 애셋이 발견되지 않는 경우에 애셋 파이프라인으로 돌아가지 않기 config.assets.compile = false -# 애셋 URL의 다이제스트를 생성하기(Deprecated될 가능성 있음) +# 애셋 URL의 다이제스트를 생성하기 config.assets.digest = true # 추가 애셋을 미리 컴파일하기 (application.js, application.css, 그리고 모든 -# 비JS/CSS 파일이 추가되어 있음) config.assets.precompile += %w( search.js ) +# 비JS/CSS 파일이 추가되어 있음) +# config.assets.precompile += %w( admin.js admin.css ) ``` -Rails 4는 Sprockets의 기본 설정값을 test 환경을 위한 `test.rb`에서 설정하지 않도록 변경되었습니다. 따라서 `test.rb`에서 Sprockets의 설정을 추가할 필요가 있습니다. test환경에서의 이전 기본값은 `config.assets.compile = true`, `config.assets.compress = false`, `config.assets.debug = false`, `config.assets.digest = false`입니다. +Rails는 Sprockets의 기본 설정값을 test 환경을 위한 `test.rb`에서 설정하지 +않도록 변경되었습니다. 따라서 `test.rb`에서 Sprockets의 설정을 추가할 필요가 +있습니다. test환경에서의 이전 기본값은 `config.assets.compile = true`, +`config.assets.compress = false`, `config.assets.debug = false`, +`config.assets.digest = false`입니다. 다음을 `Gemfile`에 추가해야합니다. @@ -932,5 +1046,3 @@ gem 'sass-rails', "~> 3.2.3" gem 'coffee-rails', "~> 3.2.1" gem 'uglifier' ``` - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/association_basics.md b/guides/source/ko/association_basics.md index 2d12c6b4111bc..c02b5ae693d48 100644 --- a/guides/source/ko/association_basics.md +++ b/guides/source/ko/association_basics.md @@ -15,63 +15,74 @@ Active Record Associations 관계를 선언하는 이유 ----------------- -모델과 모델 사이에는 관계를 선언할 필요가 있습니다만, 그 이유를 알고 계시나요? 관계를 선언하면 그를 이용해서 필요한 조작들을 무척 간단하게 할 수 있기 때문입니다. 간단한 Rails 애플리케이션을 예로 들어서 설명해보겠습니다. 이 애플리케이션에는 고객용 모델(Customer)과 주문용 모델(Order)가 있다고 합시다. 한명의 고객은 여러 주문을 할 수 있습니다. 관계를 선언하지 않은 상태에서는 아래와 같이 모델을 정의합니다. +모델과 모델 사이에는 관계를 선언할 필요가 있습니다만, 그 이유를 알고 계시나요? +관계를 선언하면 그를 이용해서 필요한 조작들을 무척 간단하게 할 수 있기 +때문입니다. 간단한 Rails 애플리케이션을 예로 들어서 설명해보겠습니다. 이 +애플리케이션에는 저자 모델(Author)과 책 모델(Book)이 있다고 합시다. +관계를 사용하지 않는다면 다음과 같이 선언할 수 있습니다. ```ruby -class Customer < ActiveRecord::Base +class Author < ApplicationRecord end -class Order < ActiveRecord::Base +class Book < ApplicationRecord end ``` -여기서 기존의 고객이 새로운 주문을 하나 추가한다고 합시다. 이 경우, 아래와 같은 코드를 실행해야합니다. +여기서 기존의 저자에 새 책을 하나 추가한다고 합시다. 이 경우, 아래와 같은 +코드를 실행해야합니다. ```ruby -@order = Order.create(order_date: Time.now, customer_id: @customer.id) +@book = Book.create(published_at: Time.now, author_id: @author.id) ``` -이번에는 고객을 삭제하는 경우를 생각해봅시다. 고객을 삭제하는 경우, 아래와 같이 고객의 주문도 남김없이 삭제해야합니다. +이번에는 저자를 삭제하는 경우를 생각해봅시다. 저자를 삭제하는 경우, 아래와 +같이 저자의 책도 남김없이 삭제해야합니다. ```ruby -@orders = Order.where(customer_id: @customer.id) -@orders.each do |order| - order.destroy +@books = Book.where(author_id: @author.id) +@books.each do |book| + book.destroy end -@customer.destroy +@author.destroy ``` -Active Record의 관계 선언 기능을 사용하면, 2개의 모델 간에 연결이 있다는 것을 Rails에 명시적으로 선언할 수 있습니다. 고객과 주문의 모델을 다음과 같이 변경하세요. +Active Record의 관계 선언 기능을 사용하면, 2개의 모델 간에 연결이 있다는 것을 +Rails에 명시적으로 선언할 수 있습니다. 저자와 책 모델을 다음과 같이 +변경하세요. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, dependent: :destroy +class Author < ApplicationRecord + has_many :books, dependent: :destroy end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end ``` -이와 같이 관계를 선언하는 것으로 고객의 새로운 주문을 하나 추가할 경우에 해야하는 작업이 아래와 같이 한줄로 간단하게 처리 가능합니다. +이와 같이 관계를 선언하는 것으로 저자의 새로운 책을 하나 추가할 경우에 +해야하는 작업이 아래와 같이 한 줄로 줄어듭니다. ```ruby -@order = @customer.orders.create(order_date: Time.now) +@book = @author.books.create(published_at: Time.now) ``` -고객과 고객의 주문을 한번에 삭제하는 작업은 더 간단합니다. +저자와 저자의 책을 한번에 삭제하는 작업은 더 간단합니다. ```ruby -@customer.destroy +@author.destroy ``` -다른 관계를 선언하는 방법에 대해서는 다음 장을 읽어주세요. 그 뒤에는 관계 선언에 필요한 다양한 팁, 활용 방법과 Rails의 관계 선언 메소드와 옵션을 상세하게 소개합니다. +다른 관계를 선언하는 방법에 대해서는 다음 장을 읽어주세요. 그 뒤에는 관계 +선언에 필요한 다양한 팁, 활용 방법과 Rails의 관계 선언 메소드와 옵션을 +상세하게 소개합니다. 관계 선언의 종류 ------------------------- -Rails에서 '관계(association)'란 2개의 Active Record 모델간의 관계를 의미합니다. 관계 선언은 일종의 매크로적인 선언 형식으로 구현되어있으며 이에 따라서 모델간의 관계 선언을 선언적으로 추가할 수 있습니다. 예를 들어, 어떤 모델이 다른 모델에 종속되어 있음(`belongs_to`)을 선언하면 2개의 모델의 각각 인스턴스 사이에 '기본키 - 외래키' 정보를 유지하도록 Rails에게 지시할 수 있습니다. Rails에서 지원되는 관계는 아래의 6종류입니다. +Rails는 다음의 6가지의 관계를 지원합니다. * `belongs_to` * `has_one` @@ -80,35 +91,50 @@ Rails에서 '관계(association)'란 2개의 Active Record 모델간의 관계 * `has_one :through` * `has_and_belongs_to_many` +관계 선언은 매크로 형식으로 구현되어있으며 이를 통해 모델간의 관계를 +선언적으로 추가할 수 있습니다. 예를 들어, 어떤 모델이 다른 모델에 종속되어 +있음(`belongs_to`)을 선언하면 2개의 모델의 각각 인스턴스 사이에 '[기본키](https://ko.wikipedia.org/wiki/기본_키) - [외래키](https://ko.wikipedia.org/wiki/외래_키)' +정보를 유지하도록 Rails에게 지시할 수 있으며, 다양한 편의 메소드들도 추가할 +수 있습니다. + 이 가이드에서는 각각의 관계의 선언방법과 사용방법에 대해서 자세히 설명합니다. 그 전에 각각의 관계가 어떤 상황에서 적절한지에 대해서 간단히 소개합니다. ### `belongs_to` -어떤 모델에서 `belongs_to` 관계를 선언하면 다른 편의 모델간에 '1대1' 관계가 설정됩니다. 이 때 선언한 모델의 모든 인스턴스는 다른 편의 모델의 인스턴스에 '종속(belongs to)'됩니다. 예를 들어 Rails 애플리케이션에 고객(customer)와 주문(order) 정보가 포함되며, 1개의 주문에 대해 정확히 1명의 고객만이 존재할 수 있다고 한다면, Order 모델은 다음과 같이 선언할 수 있습니다. +어떤 모델에서 `belongs_to` 관계를 선언하면 다른 편의 모델간에 '1대1' 관계가 +설정됩니다. 이 때 선언한 모델의 모든 인스턴스는 다른 편의 모델의 인스턴스에 +'종속(belongs to)'됩니다. 예를 들어 Rails 애플리케이션에 저자(author)와 +책(book) 정보가 포함되며, 1개의 책에 대해 정확히 1명의 저자만이 존재할 수 +있다고 한다면, Book 모델은 다음과 같이 선언할 수 있습니다. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end ``` ![belongs_to 관계](images/belongs_to.png) -NOTE: `belongs_to` 관계를 지정하는 모델 명은 '단수형'이어야 합니다. 예제의 경우 `Order` 모델에서 관계 선언을 `customer`의 복수형인 `customers`로 하게 되면 "uninitialized constant Order::Customers" 에러가 발생합니다. Rails에는 관계로 선언된 이름에서 자동적으로 모델의 클래스 명을 추측합니다. 관계 선언이 'customer'으로 되어 있다면, `Customer`라고 추측합니다. 따라서 관계 선언을 복수형으로 잘못하게 된다면, 잘못된 클래스명을 추측하게 됩니다. +NOTE: `belongs_to` 관계를 지정하는 모델 명은 '단수형'이어야 합니다. 예제의 +경우 `Book` 모델에서 관계 선언을 `author`의 복수형인 `authors`로 하게 되면 +"uninitialized constant Book::Authors" 에러가 발생합니다. Rails에는 관계로 +선언된 이름에서 자동적으로 모델의 클래스 명을 추측합니다. 관계 선언이 +'author'으로 되어 있다면, `Author`라고 추측합니다. 따라서 관계 선언을 +복수형으로 잘못하게 된다면, 잘못된 클래스명을 추측하게 됩니다. 위의 선언에 대응하는 마이그레이션은 아래와 같은 모습이 됩니다. ```ruby -class CreateOrders < ActiveRecord::Migration +class CreateBooks < ActiveRecord::Migration[5.0] def change - create_table :customers do |t| + create_table :authors do |t| t.string :name t.timestamps end - create_table :orders do |t| - t.belongs_to :customer - t.datetime :order_date + create_table :books do |t| + t.belongs_to :author, index: true + t.datetime :published_at t.timestamps end end @@ -117,10 +143,15 @@ end ### `has_one` -`has_one` 관계도 반대편의 모델과 1대1 관계를 설정합니다. 그러나, 그 의미와 결과는 `belongs_to`와는 약간 다릅니다. `has_one` 관계를 선언하는 경우, 그 선언이 있었던 모델의 인스턴스가 반대편의 모델의 인스턴스를 '통째로 포함'하거나 또는 '소유'하고 있다는 것을 의미합니다. 예를 들자면 공급자(supplier) 한 명 마다 계정을 하나씩 가질 수 있다는 관계가 있다고 한다면, 아래와 같이 선언할 수 있습니다. +`has_one` 관계도 반대편의 모델과 1대1 관계를 설정합니다. 그러나, 그 의미와 +결과는 `belongs_to`와는 약간 다릅니다. `has_one` 관계를 선언하는 경우, +그 선언이 있었던 모델의 인스턴스가 반대편의 모델의 인스턴스를 '통째로 +포함'하거나 또는 '소유'하고 있다는 것을 의미합니다. 예를 들자면 +공급자(supplier) 한 명 마다 계정을 하나씩 가질 수 있다는 관계가 있다고 한다면, +아래와 같이 선언할 수 있습니다. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account end ``` @@ -130,7 +161,7 @@ end 위의 관계에 대응하는 마이그레이션은 다음과 같습니다. ```ruby -class CreateSuppliers < ActiveRecord::Migration +class CreateSuppliers < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name @@ -138,7 +169,7 @@ class CreateSuppliers < ActiveRecord::Migration end create_table :accounts do |t| - t.belongs_to :supplier + t.belongs_to :supplier, index: true t.string :account_number t.timestamps end @@ -146,13 +177,27 @@ class CreateSuppliers < ActiveRecord::Migration end ``` +사용 방식에 따라서는 유일한 인덱스를 만들거나 계정 테이블에 공급자의 외래키를 +만들어야 할 수도 있습니다. 이런 경우 컬럼 정의는 아래처럼 작성할 수 있습니다. + +```ruby +create_table :accounts do |t| + t.belongs_to :supplier, index: true, unique: true, foreign_key: true + # ... +end +``` + ### `has_many` -`has_many` 관계는 '일대다'의 관계를 나타냅니다. `has_many` 관계가 사용되는 경우, 반대편의 모델에서는 `belongs_to`가 사용되는 경우가 많습니다. 선언된 모델의 인스턴스는 피 선언된 모델의 '0개 이상의' 인스턴스를 소유합니다. 예를 들어 고객(customer)와 주문(order)를 포함하는 Rails 애플리케이션에서는 고객의 모델을 아래와 같이 선언할 수 있습니다. +`has_many` 관계는 '일대다'의 관계를 나타냅니다. `has_many` 관계가 사용되는 +경우, 반대편의 모델에서는 `belongs_to`가 사용되는 경우가 많습니다. 선언된 +모델의 인스턴스는 피 선언된 모델의 '0개 이상의' 인스턴스를 소유합니다. +예를 들어 저자(author)와 책(book)을 포함하는 Rails 애플리케이션에서는 저자 +모델을 아래와 같이 선언할 수 있습니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` @@ -163,16 +208,16 @@ NOTE: `has_many` 관계를 선언하는 경우, 상대의 모델명을 '복수 이 관계에 대응하는 마이그레이션은 다음과 같습니다. ```ruby -class CreateCustomers < ActiveRecord::Migration +class CreateAuthors < ActiveRecord::Migration[5.0] def change - create_table :customers do |t| + create_table :authors do |t| t.string :name t.timestamps end - create_table :orders do |t| - t.belongs_to :customer - t.datetime :order_date + create_table :books do |t| + t.belongs_to :author, index: true + t.datetime :published_at t.timestamps end end @@ -181,20 +226,24 @@ end ### `has_many :through` -`has_many :through` 관계는 다른 모델과 '다대다' 관계를 설정하는 경우에 사용됩니다. 이 관계 선언은 2개의 모델 사이에 '제3의 모델'이 사용되는 것이 특징입니다. 이를 이용해 상대 모델의 '0개 이상'의 인스턴스와 연결됩니다. 예를 들어 환자(patient)가 의사(physician) 사이에 진찰예약(appointment)를 신청하는 경우를 생각해봅시다. 이 경우, 관계 선언은 다음과 같이 할 수 있습니다. +`has_many :through` 관계는 다른 모델과 '다대다' 관계를 설정하는 경우에 +사용됩니다. 이 관계 선언은 2개의 모델 사이에 '제3의 모델'이 사용되는 것이 +특징입니다. 이를 이용해 상대 모델의 '0개 이상'의 인스턴스와 연결됩니다. +예를 들어 환자(patient)가 의사(physician) 사이에 진찰예약(appointment)을 +신청하는 경우를 생각해봅시다. 이 경우, 관계 선언은 다음과 같이 할 수 있습니다. ```ruby -class Physician < ActiveRecord::Base +class Physician < ApplicationRecord has_many :appointments has_many :patients, through: :appointments end -class Appointment < ActiveRecord::Base +class Appointment < ApplicationRecord belongs_to :physician belongs_to :patient end -class Patient < ActiveRecord::Base +class Patient < ApplicationRecord has_many :appointments has_many :physicians, through: :appointments end @@ -205,7 +254,7 @@ end 이 관계에 대응하는 마이그레이션은 아래와 같습니다. ```ruby -class CreateAppointments < ActiveRecord::Migration +class CreateAppointments < ActiveRecord::Migration[5.0] def change create_table :physicians do |t| t.string :name @@ -218,8 +267,8 @@ class CreateAppointments < ActiveRecord::Migration end create_table :appointments do |t| - t.belongs_to :physician - t.belongs_to :patient + t.belongs_to :physician, index: true + t.belongs_to :patient, index: true t.datetime :appointment_date t.timestamps end @@ -227,35 +276,43 @@ class CreateAppointments < ActiveRecord::Migration end ``` -조인 모델의 컬렉션은 API를 통해서 처리할 수 있습니다. 예를 들자면 다음과 같이 대입한다고 합시다. +조인 모델의 컬렉션은 API를 통해서 처리할 수 있습니다. 예를 들자면 다음과 +같이 대입한다고 합시다. ```ruby physician.patients = patients ``` -이때, 새롭게 관계가 선언된 객체에 대해서 새로운 조인 모델이 생성됩니다. 그 중 문제가 있는 객체의 경우는 삭제되며, 조인 모델 컬렉션에 포함되지 않습니다. +이때, 새롭게 관계가 선언된 객체에 대해서 새로운 조인 모델이 생성됩니다. 그 중 +문제가 있는 객체의 경우는 삭제되며, 조인 모델 컬렉션에 포함되지 않습니다. -WARNING: 문제가 있는 경우의 자동 삭제는 바로 이루어집니다. 이 경우 삭제 관련 콜백은 호출되지 않으므로 주의해주세요. +WARNING: 문제가 있는 경우의 자동 삭제는 바로 이루어집니다. 이 경우 삭제 관련 +콜백은 호출되지 않으므로 주의해주세요. -`has_many :through` 관련은 중첩된 `has_many` 관계를 통해서 지름길을 설정하는 경우에도 편리합니다. 예를 들어 1개의 문서에 많은 장(section)이 있고, 1개의 장에는 많은 단락(paragraph)가 있는 상태에서, 장을 통하지 않고 문서에 존재하는 모든 단락을 포함하는 콜렉션이 필요하다고 가정해봅시다. 이 경우 아래와 같이 정의할 수 있습니다. +`has_many :through` 관련은 중첩된 `has_many` 관계를 통해서 지름길을 설정하는 +경우에도 편리합니다. 예를 들어 1개의 문서에 많은 장(section)이 있고, 1개의 +장에는 많은 단락(paragraph)가 있는 상태에서, 장을 통하지 않고 문서에 존재하는 +모든 단락을 포함하는 콜렉션이 필요하다고 가정해봅시다. 이 경우 아래와 같이 +정의할 수 있습니다. ```ruby -class Document < ActiveRecord::Base +class Document < ApplicationRecord has_many :sections has_many :paragraphs, through: :sections end -class Section < ActiveRecord::Base +class Section < ApplicationRecord belongs_to :document has_many :paragraphs end -class Paragraph < ActiveRecord::Base +class Paragraph < ApplicationRecord belongs_to :section end ``` -Rails는 `through: :sections`를 선언하는 것으로 다음과 같은 명령을 이해할 수 있게 됩니다. +Rails는 `through: :sections`를 선언하는 것으로 다음과 같은 명령을 이해할 수 +있게 됩니다. ```ruby @document.paragraphs @@ -263,20 +320,24 @@ Rails는 `through: :sections`를 선언하는 것으로 다음과 같은 명령 ### `has_one :through` -`has_one :through` 관계는 다른 모델과 일대일 관계를 설정합니다. 이 관계는 2개의 모델 사이에 '제3의 모델'을 사용하는 점이 특징입니다. 이에 따라, 상대 모델을 1개의 인스턴스를 매칭합니다. 예를 들어 1명의 공급자(supplier)가 1개의 계정을 가지며, 1개의 계정은 1개의 계정 이력(account_history)을 가지는 경우, 공급자 모델은 아래와 같이 선언될 수 있습니다. +`has_one :through` 관계는 다른 모델과 일대일 관계를 설정합니다. 이 관계는 +2개의 모델 사이에 '제3의 모델'을 사용하는 점이 특징입니다. 이에 따라, 상대 +모델을 1개의 인스턴스를 매칭합니다. 예를 들어 1명의 공급자(supplier)가 1개의 +계정을 가지며, 1개의 계정은 1개의 계정 이력(account_history)을 가지는 경우, +공급자 모델은 아래와 같이 선언될 수 있습니다. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account has_one :account_history, through: :account end -class Account < ActiveRecord::Base +class Account < ApplicationRecord belongs_to :supplier has_one :account_history end -class AccountHistory < ActiveRecord::Base +class AccountHistory < ApplicationRecord belongs_to :account end ``` @@ -286,7 +347,7 @@ end 이 관계에 대응하는 마이그레이션은 다음과 같습니다. ```ruby -class CreateAccountHistories < ActiveRecord::Migration +class CreateAccountHistories < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name @@ -294,13 +355,13 @@ class CreateAccountHistories < ActiveRecord::Migration end create_table :accounts do |t| - t.belongs_to :supplier + t.belongs_to :supplier, index: true t.string :account_number t.timestamps end create_table :account_histories do |t| - t.belongs_to :account + t.belongs_to :account, index: true t.integer :credit_rating t.timestamps end @@ -310,14 +371,19 @@ end ### `has_and_belongs_to_many` -`has_and_belongs_to_many` 관계는 다른 모델과 '다대다'의 관계를 생성합니다만, `through:`를 지정하는 경우와는 다르게, 제 3의 모델을 사용하지 않습니다(역주: 아래에서 설명하지만, 조인용 테이블은 필요합니다). 예를 들어 애플리케이션에 완성품(assembly)과 부품(part)가 있고, 1개의 완성품에 여러 부품을 사용하고, 반대로 1개의 부품에도 여러 개의 완성품이 대응되는 경우, 모델은 다음과 같이 선언할 수 있습니다. +`has_and_belongs_to_many` 관계는 다른 모델과 '다대다'의 관계를 생성합니다만, +`through:`를 지정하는 경우와는 다르게, 제 3의 모델을 사용하지 않습니다(역주: +아래에서 설명하지만, 조인용 테이블은 필요합니다). 예를 들어 애플리케이션에 +완성품(assembly)과 부품(part)이 있고, 1개의 완성품에 여러 부품을 사용하고, +반대로 1개의 부품에도 여러 개의 완성품이 대응되는 경우, 모델은 다음과 같이 +선언할 수 있습니다. ```ruby -class Assembly < ActiveRecord::Base +class Assembly < ApplicationRecord has_and_belongs_to_many :parts end -class Part < ActiveRecord::Base +class Part < ApplicationRecord has_and_belongs_to_many :assemblies end ``` @@ -327,7 +393,7 @@ end 이 관계에 대응하는 마이그레이션은 다음과 같습니다. ```ruby -class CreateAssembliesAndParts < ActiveRecord::Migration +class CreateAssembliesAndParts < ActiveRecord::Migration[5.0] def change create_table :assemblies do |t| t.string :name @@ -340,8 +406,8 @@ class CreateAssembliesAndParts < ActiveRecord::Migration end create_table :assemblies_parts, id: false do |t| - t.belongs_to :assembly - t.belongs_to :part + t.belongs_to :assembly, index: true + t.belongs_to :part, index: true end end end @@ -349,16 +415,25 @@ end ### `belongs_to`와 `has_one` 중 어느 것을 사용해야 하는가 -2개의 모델 간에 1대1 관계를 생성하고 싶은 경우, 어느 한쪽에 `belongs_to`을 추가히고 반대편의 모델에 `has_one`을 추가해야 합니다. 어느 관계를 어느 모델에 두어야 할까요. +2개의 모델 간에 1대1 관계를 생성하고 싶은 경우, 어느 한쪽에 `belongs_to`을 +추가히고 반대편의 모델에 `has_one`을 추가해야 합니다. 어느 관계를 어느 모델에 +두어야 할까요. -구별의 기준이 되는 것은 외래키(foreign key)를 어느쪽에 두는가, 입니다(외래키는 `belongs_to`를 추가한 쪽의 모델의 테이블에 추가됩니다). 물론 이것만으로는 결정할 수 없습니다. 데이터의 실제 의미에 대해서도 생각해 볼 필요가 있습니다. `has_one`이라는 관계는 주어가 목적어를 소유하고 있다, 라는 것을 표현하고 있습니다. 그리고 목적어는 주어에게 소유당하고 있다는 것을 나타냅니다. 예를 들어, '공급자가 계정을 가지고 있다'고 보는 것이 '계정이 공급자를 가지고 있다'보다 자연스럽습니다. 다시 말해, 이 경우 올바른 관계는 다음과 같습니다. +구별의 기준이 되는 것은 외래키(foreign key)를 어느쪽에 두는가, 입니다(외래키는 +`belongs_to`를 추가한 쪽의 모델의 테이블에 추가됩니다). 물론 이것만으로는 +결정할 수 없습니다. 데이터의 실제 의미에 대해서도 생각해 볼 필요가 있습니다. +`has_one`이라는 관계는 주어가 목적어를 소유하고 있다, 라는 것을 표현하고 +있습니다. 그리고 목적어는 주어에게 소유당하고 있다는 것을 나타냅니다. 예를 +들어, '공급자가 계정을 가지고 있다'고 보는 것이 '계정이 공급자를 +가지고 있다'보다 자연스럽습니다. 다시 말해, 이 경우 올바른 관계는 다음과 +같습니다. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account end -class Account < ActiveRecord::Base +class Account < ApplicationRecord belongs_to :supplier end ``` @@ -366,7 +441,7 @@ end 이 관계에 대응하는 마이그레이션은 아래와 같습니다. ```ruby -class CreateSuppliers < ActiveRecord::Migration +class CreateSuppliers < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name @@ -382,71 +457,92 @@ class CreateSuppliers < ActiveRecord::Migration end ``` -NOTE: 마이그레이션에서 `t.integer :supplier_id`처럼 '소문자 모델명_id'라고 적는 것으로 외래키를 명시적으로 지정할 수 있습니다. 현재 버전의 Rails에서는 `t.references :supplier`라는 식으로 자세한 구현을 추상화하여 숨기는 방식을 사용할 수 있습니다. +NOTE: 마이그레이션에서 `t.integer :supplier_id`처럼 '소문자 모델명_id'라고 +적는 것으로 외래키를 명시적으로 지정할 수 있습니다. 현재 버전의 Rails에서는 +`t.references :supplier`라는 식으로 자세한 구현을 추상화하여 숨기는 방식을 +사용할 수 있습니다. ### `has_many :through`와 `has_and_belongs_to_many` 중 어느 것을 사용해야 하는가 -Rails에서는 모델간의 다대다 관계를 선언할때 2가지 방법이 사용가능합니다. 간단한 것은 `has_and_belongs_to_many`를 사용하는 방법입니다. 이 방법으로는 관계를 직접적으로 지정할 수 있습니다. +Rails에서는 모델간의 다대다 관계를 선언할때 2가지 방법이 사용가능합니다. +간단한 것은 `has_and_belongs_to_many`를 사용하는 방법입니다. 이 방법으로는 +관계를 직접적으로 지정할 수 있습니다. ```ruby -class Assembly < ActiveRecord::Base +class Assembly < ApplicationRecord has_and_belongs_to_many :parts end -class Part < ActiveRecord::Base +class Part < ApplicationRecord has_and_belongs_to_many :assemblies end ``` -다대다 관계를 선언하는 다른 방법으로는 `has_many :through`가 있습니다. 이 경우에는 조인 모델을 사용한 간접적인 관계 선언이 사용됩니다. +다대다 관계를 선언하는 다른 방법으로는 `has_many :through`가 있습니다. +이 경우에는 조인 모델을 사용한 간접적인 관계 선언이 사용됩니다. ```ruby -class Assembly < ActiveRecord::Base +class Assembly < ApplicationRecord has_many :manifests has_many :parts, through: :manifests end -class Manifest < ActiveRecord::Base +class Manifest < ApplicationRecord belongs_to :assembly belongs_to :part end -class Part < ActiveRecord::Base +class Part < ApplicationRecord has_many :manifests has_many :assemblies, through: :manifests end ``` -각 모델 자체를 독립적으로 다루고 싶은 경우(관계 자체에 대한 처리를 하고 싶은 경우)에는 중간에 조인 모델을 사용하는 `has_many :through`를 선택하는 것이 가장 간단합니다. 관계 사이에 전혀 처리를 할 필요가 없다면 조인 모델을 준비할 필요가 없는 `has_and_belongs_to_many`를 사용하는 것이 간단합니다(단, 이 경우에는 조인 모델이 필요 없지만, 조인용 테이블은 별도로 테이블에 작성해야 한다는 점을 잊지 마세요). +각 모델 자체를 독립적으로 다루고 싶은 경우(관계 자체에 대한 처리를 하고 싶은 +경우)에는 중간에 조인 모델을 사용하는 `has_many :through`를 선택하는 것이 +가장 간단합니다. 관계 사이에 전혀 처리를 할 필요가 없다면 조인 모델을 준비할 +필요가 없는 `has_and_belongs_to_many`를 사용하는 것이 간단합니다(단, +이 경우에는 조인 모델이 필요 없지만, 조인용 테이블은 별도로 작성해야 한다는 +점을 잊지 마세요). -조인 모델에서 유효성 검사, 콜백, 추가 속성 등이 필요한 경우에는 `has_many :through`를 사용하세요. +조인 모델에서 유효성 검사, 콜백, 추가 속성 등이 필요한 경우에는 +`has_many :through`를 사용하세요. ### Polymorphic Assosiation -_다형 관계_는 관계 선언을 응용한 것입니다. 다형 관계를 사용하면 어떤 모델이 여러 개의 모델에 속해 있다는 사실을 하나의 관계 선언으로 표현할 수 있습니다. 예를 들어 사진(picture) 모델이 있고, 그 모델을 종업원(employee) 모델과 제품(product) 모델 모두에게 종속시키고 싶다고 해봅시다. 이 경우 아래와 같이 선언합니다. +_다형 관계_는 관계 선언을 응용한 것입니다. 다형 관계를 사용하면 어떤 모델이 +여러 개의 모델에 속해 있다는 사실을 하나의 관계 선언으로 표현할 수 있습니다. +예를 들어 사진(picture) 모델이 있고, 그 모델을 종업원(employee) 모델과 +제품(product) 모델 모두에게 종속시키고 싶다고 해봅시다. 이 경우 아래와 같이 +선언합니다. ```ruby -class Picture < ActiveRecord::Base +class Picture < ApplicationRecord belongs_to :imageable, polymorphic: true end -class Employee < ActiveRecord::Base +class Employee < ApplicationRecord has_many :pictures, as: :imageable end -class Product < ActiveRecord::Base +class Product < ApplicationRecord has_many :pictures, as: :imageable end ``` -다형적인 `belongs_to`는 다른 어떤 모델에서도 사용 가능한 인터페이스를 선언하는 것이라고 생각할 수 있습니다. `@employee.pictures`라고 호출하면 사진 컬렉션을 `Employee` 모델의 인스턴스에서 얻을 수 있습니다. +다형적인 `belongs_to`는 다른 어떤 모델에서도 사용 가능한 인터페이스를 +선언하는 것이라고 생각할 수 있습니다. `@employee.pictures`라고 호출하면 +사진 컬렉션을 `Employee` 모델의 인스턴스에서 얻을 수 있습니다. -마찬가지로 `@product.pictures`라고 호출하면 `Product` 모델의 인스턴스에서 사진 컬렉션을 얻을 수 있습니다. +마찬가지로 `@product.pictures`라고 호출하면 `Product` 모델의 인스턴스에서 사진 +컬렉션을 얻을 수 있습니다. -`Picture` 모델의 인스턴스가 있다면, `@picture.imageable`이라고 호출하는 것으로 부모 객체를 얻을 수 있습니다. 이를 위해서는 다형적인 인터페이스를 사용하는 모델에서 외래키 컬럼과 형식을 저장하는 컬럼을 선언해야 합니다. +`Picture` 모델의 인스턴스가 있다면, `@picture.imageable`이라고 호출하는 것으로 +부모 객체를 얻을 수 있습니다. 이를 위해서는 다형적인 인터페이스를 사용하는 +모델에서 외래키 컬럼과 형식을 저장하는 컬럼을 선언해야 합니다. ```ruby -class CreatePictures < ActiveRecord::Migration +class CreatePictures < ActiveRecord::Migration[5.0] def change create_table :pictures do |t| t.string :name @@ -461,11 +557,11 @@ end `t.references` 를 사용하면 좀 더 간단하게 작성할 수 있습니다. ```ruby -class CreatePictures < ActiveRecord::Migration +class CreatePictures < ActiveRecord::Migration[5.0] def change create_table :pictures do |t| t.string :name - t.references :imageable, polymorphic: true + t.references :imageable, polymorphic: true, index: true t.timestamps end end @@ -476,10 +572,14 @@ end ### Self Joins -데이터 모델을 설계하다보면 때때로 자기자신에 관계를 선언해야할 필요가 있는 모델을 사용하게 됩니다. 예를 들어 하나의 데이터베이스 모델에 모든 종업원 정보를 저장하고 싶은데, 매니저와 일반직원(subordinate)의 관계를 추가하고 싶은 경우 등이 있을겁니다. 이런 상황에서는 자체 조인 관계를 사용해서 모델로 추상화할 수 있습니다. +데이터 모델을 설계하다보면 때때로 자기자신에 관계를 선언해야할 필요가 있는 +모델을 사용하게 됩니다. 예를 들어 하나의 데이터베이스 모델에 모든 종업원 +정보를 저장하고 싶은데, 매니저와 일반직원(subordinate)의 관계를 추가하고 +싶은 경우 등이 있을겁니다. 이런 상황에서는 자체 조인 관계를 사용해서 모델로 +추상화할 수 있습니다. ```ruby -class Employee < ActiveRecord::Base +class Employee < ApplicationRecord has_many :subordinates, class_name: "Employee", foreign_key: "manager_id" @@ -492,7 +592,7 @@ end 마이그레이션과 스키마에는 모델 자신에게 references 컬럼을 추가하면 됩니다. ```ruby -class CreateEmployees < ActiveRecord::Migration +class CreateEmployees < ActiveRecord::Migration[5.0] def change create_table :employees do |t| t.references :manager @@ -505,7 +605,8 @@ end 팁과 주의사항 -------------------------- -Rails 애플리케이션에서 Active Record의 관계 선언을 효율적으로 사용하기 위해서는 아래와 같은 것들을 알아두어야 합니다. +Rails 애플리케이션에서 Active Record의 관계 선언을 효율적으로 사용하기 +위해서는 아래와 같은 것들을 알아두어야 합니다. * 캐시 제어 * 중복된 이름 사용 피하기 @@ -515,138 +616,191 @@ Rails 애플리케이션에서 Active Record의 관계 선언을 효율적으로 ### 캐시 제어 -관계 메소드는 모두 캐시를 이용해서 구축되어 있습니다. 마지막으로 실행한 쿼리의 결과를 캐시에 저장해두고, 그 이후의 조작에서 사용합니다. 이 캐시는 메소드간에도 공유된다는 점에 주의해주세요. 예를 들자면, +관계 메소드는 모두 캐시를 이용해서 구축되어 있습니다. 마지막으로 실행한 쿼리의 +결과를 캐시에 저장해두고, 그 이후의 조작에서 사용합니다. 이 캐시는 +메소드간에도 공유된다는 점에 주의해주세요. 예를 들자면, ```ruby -customer.orders # 데이터베이스에서 orders를 얻어온다 -customer.orders.size # orders 캐시가 사용된다 -customer.orders.empty? # orders 캐시가 사용된다 +author.books # 데이터베이스에서 books를 얻어온다 +author.books.size # books 캐시가 사용된다 +author.books.empty? # books 캐시가 사용된다 ``` -데이터가 애플리케이션의 다른 부분에 의해서 변경될 가능성을 고려하여, 데이터를 새로 읽어오고 싶은 경우에는 어떻게 해야할까요? 이 경우에는 관계 메소드를 호출할 때에 `true`를 넘겨주는 것으로 캐시를 비우고, 다시 불러오게 됩니다. +데이터가 애플리케이션의 다른 부분에 의해서 변경될 가능성을 고려하여, 데이터를 +새로 읽어오고 싶은 경우에는 어떻게 해야할까요? 이 경우에는 `reload` 메소드를 +호출하세요. ```ruby -customer.orders # 데이터베이스에서 orders를 얻어온다 -customer.orders.size # orders 캐시가 사용된다 -customer.orders(true).empty? # orders 캐시를 삭제하고 데이터베이스에서 다시 읽어온다 +author.books # 데이터베이스에서 books를 얻어온다 +author.books.size # books 캐시가 사용된다 +author.books.reload.empty? # books 캐시를 삭제하고 데이터베이스에서 다시 읽어온다 ``` ### 중복된 이름 사용 피하기 -관계 선언에 모든 이름을 사용할 수 있는 것은 아닙니다. 관계 선언시에는 관계 이름과 같은 이름의 메소드가 추가됩니다. 따라서 `ActiveRecord::Base`의 인스턴스에서 이미 사용되고 있는 이름을 관계 선언시에 사용해서는 안됩니다. 중복되는 이름을 사용하게 되면 기존에 선언되어있던 메소드들을 덮어쓰게 됩니다. 예를 들어, `attributes`나 `connection`은 관계 선언시에 써서는 안되는 이름 중 하나입니다. +관계 선언에 모든 이름을 사용할 수 있는 것은 아닙니다. 관계 선언시에는 관계 +이름과 같은 이름의 메소드가 추가됩니다. 따라서 `ApplicationRecord`의 +인스턴스에서 이미 사용되고 있는 이름을 관계 선언시에 사용해서는 안됩니다. +중복되는 이름을 사용하게 되면 기존에 선언되어있던 메소드들을 덮어쓰게 됩니다. +예를 들어, `attributes`나 `connection`은 관계 선언시에 써서는 안되는 이름 중 +하나입니다. ### 스키마 갱신하기 -관계 선언은 무척 편리합니다만 아쉽게도 모든 것을 알아서 해주는 마법은 아닙니다. 관계 선언을 사용게 되면, 이에 맞게끔 데이터베이스 스키마를 갱신해야합니다. 선언한 관계에 따라 다르겠습니다만, 구체적으로 아래의 2개의 작업이 필요합니다. 1. `belongs_to`를 사용하는 경우에는 외래키를 추가해야합니다. 2. `has_and_belongs_to_many`를 사용하는 경우에는 적절한 조인 테이블을 추가해야합니다. +관계 선언은 무척 편리합니다만 아쉽게도 모든 것을 알아서 해주는 마법은 아닙니다. +관계 선언을 사용게 되면, 이에 맞게끔 데이터베이스 스키마를 갱신해야합니다. +선언한 관계에 따라 다르겠습니다만, 구체적으로 아래의 2개의 작업이 필요합니다. 1. `belongs_to`를 사용하는 경우에는 외래키를 추가해야합니다. 2. `has_and_belongs_to_many`를 사용하는 경우에는 적절한 조인 테이블을 추가해야합니다. #### `belongs_to`에 대응하는 외래키 추가하기 `belongs_to`를 선언하면 이에 맞는 외래키를 추가해야합니다. 아래의 모델을 예로 들겠습니다. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end ``` -이 선언은 orders 테이블에 아래와 같이 외래키를 추가해야할 필요가 있습니다. +이 선언은 books 테이블에 아래와 같이 외래키를 추가해야할 필요가 있습니다. ```ruby -class CreateOrders < ActiveRecord::Migration +class CreateBooks < ActiveRecord::Migration[5.0] def change - create_table :orders do |t| - t.datetime :order_date - t.string :order_number - t.integer :customer_id + create_table :books do |t| + t.datetime :published_at + t.string :book_number + t.integer :author_id end + + add_index :books, :author_id end end ``` -모델을 먼저 만들고, 그 이후에 관계를 선언하는 경우에는 `add_column` 마이그레이션을 작성해서 대상 테이블에 필요한 외래키를 추가해주세요. +모델을 먼저 만들고, 그 이후에 관계를 선언하는 경우에는 `add_column` +마이그레이션을 작성해서 대상 테이블에 필요한 외래키를 추가해주세요. #### `has_and_belongs_to_many` 관계를 위한 조인 테이블 추가하기 -`has_and_belongs_to_many`를 선언한 경우에는 이를 처리하기 위한 조인 테이블을 명시적으로 추가할 필요가 있습니다. `:join_table` 옵션을 사용해서 조인 테이블의 이름이 명시적으로 지정되지 않은 경우, Active Record는 2개의 클래스명을 사전순으로 늘어놓고 연결한 뒤, 적당한 조인 테이블 명을 추측합니다. 예를 들어 Customer 모델과 Order 모델이 있다면, c가 o보다 사전에서 먼저 나오기 때문에 "customers_orders"라는 기본 조인 테이블 명이 사용됩니다. +`has_and_belongs_to_many`를 선언한 경우에는 이를 처리하기 위한 조인 테이블을 +명시적으로 추가할 필요가 있습니다. `:join_table` 옵션을 사용해서 조인 테이블의 +이름이 명시적으로 지정되지 않은 경우, Active Record는 2개의 클래스명을 +사전순으로 늘어놓고 연결한 뒤, 적당한 조인 테이블 명을 추측합니다. 예를 들어 +Author 모델과 Book 모델이 있다면, "a"가 "b"보다 사전에서 먼저 나오기 때문에 +"authors_books"라는 기본 조인 테이블 명이 사용됩니다. -WARNING: 모델의 이름 순서는 `String` 클래스의 `<`연산자를 사용해서 계산됩니다. 이것은 두 문자열의 길이가 다르고, 짧은 쪽이 긴 쪽의 앞부분에 완전히 일치한 경우, 긴 쪽의 문자열은 짧은 쪽의 문자열보다 사전순이 뒤쪽이 됩니다. 예를 들어 "paper\_boxes" 테이블과 "papers" 테이블이 있는 경우, 이 테이블명을 합치면 "papers\_paper\_boxes"가 되는 것처럼 보입니다. "paper\_boxes"가 길기 때문에 상식적으로는 뒤에 위치할 것으로 추측되기 때문입니다. 그러나 실제로 Rails가 예측한 결합 테이블 이름은 "paper\_boxes\_papers"입니다. 이는 언더스코어 '\_' 가 's'보다 우선순위가 높기 때문입니다. +WARNING: 모델의 이름 순서는 `String` 클래스의 `<` 연산자를 사용해서 계산됩니다. +이것은 두 문자열의 길이가 다르고, 짧은 쪽이 긴 쪽의 앞부분에 완전히 일치한 +경우, 긴 쪽의 문자열은 짧은 쪽의 문자열보다 사전순이 뒤쪽이 됩니다. 예를 들어 +"paper\_boxes" 테이블과 "papers" 테이블이 있는 경우, 이 테이블명을 합치면 +"papers\_paper\_boxes"가 되는 것처럼 보입니다. "paper\_boxes"가 길기 때문에 +상식적으로는 뒤에 위치할 것으로 추측되기 때문입니다. 그러나 실제로 Rails가 +예측한 결합 테이블 이름은 "paper\_boxes\_papers"입니다. 이는 언더스코어 +'\_' 가 's'보다 우선순위가 높기 때문입니다. -생성된 이름이 어떻든, 적절한 마이그레이션을 실행해서 조인 테이블을 생성해야합니다. 다음을 생각해봅시다 +생성된 이름이 어떻든, 적절한 마이그레이션을 실행해서 조인 테이블을 생성해야 +합니다. 다음을 생각해봅시다. ```ruby -class Assembly < ActiveRecord::Base +class Assembly < ApplicationRecord has_and_belongs_to_many :parts end -class Part < ActiveRecord::Base +class Part < ApplicationRecord has_and_belongs_to_many :assemblies end ``` -이 관계에 대응하는 `assemblies_parts` 테이블을 마이그레이션으로 추가해야합니다. 이 테이블에는 기본키를 설정하지 말아주세요. +이 관계에 대응하는 `assemblies_parts` 테이블을 마이그레이션으로 추가해야 +합니다. 이 테이블에는 기본키를 설정하지 말아주세요. ```ruby -class CreateAssembliesPartsJoinTable < ActiveRecord::Migration +class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0] def change create_table :assemblies_parts, id: false do |t| t.integer :assembly_id t.integer :part_id end + + add_index :assemblies_parts, :assembly_id + add_index :assemblies_parts, :part_id end end ``` -이 테이블은 모델을 가지지 않으므로 `create_table`에 `id: false`를 넘겨줍니다. 이렇게 하지 않으면 이 조인 테이블은 정상적으로 동작하지 않습니다. 모델의 ID가 비정상적이거나, 사용중에 예외가 발생하는 등 `has_and_belongs_to_many`의 동작이 수상한 경우에는 이 설정이 제대로 되어있는지 확인해보세요. +이 테이블은 모델을 가지지 않으므로 `create_table`에 `id: false`를 넘겨줍니다. +이렇게 하지 않으면 이 조인 테이블은 정상적으로 동작하지 않습니다. 모델의 ID가 +비정상적이거나, 사용중에 예외가 발생하는 등 `has_and_belongs_to_many`의 동작이 +수상한 경우에는 이 설정이 제대로 되어있는지 확인해보세요. + +아니면 `create_join_table` 메소드를 사용할 수도 있습니다. + +```ruby +class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0] + def change + create_join_table :assemblies, :parts do |t| + t.index :assembly_id + t.index :part_id + end + end +end +``` ### 관계의 스코프 제어 -기본적으로 관계 선언에 의해서는 현재 모듈의 범위 내에 존재하는 객체만이 검색됩니다. Active Record 모델을 모듈 내에서 선언한 경우라면 이를 주의할 필요가 있습니다. 예를 들어, +기본적으로 관계 선언에 의해서는 현재 모듈의 범위 내에 존재하는 객체만이 +검색됩니다. Active Record 모델을 모듈 내에서 선언한 경우라면 이를 주의할 +필요가 있습니다. 예를 들어, ```ruby module MyApplication module Business - class Supplier < ActiveRecord::Base + class Supplier < ApplicationRecord has_one :account end - class Account < ActiveRecord::Base + class Account < ApplicationRecord belongs_to :supplier end end end ``` -이 코드는 정상적으로 동작합니다. 이것은 `Supplier` 클래스와 `Account` 클래스가 같은 모듈 내에서 정의되어 있기 때문입니다. 반대로 아래의 코드는 동작하지 않습ㄴ디ㅏ. `Supplier` 클래스와 `Account` 클래스가 서로 다른 스코프에서 정의되어 있기 때문입니다. +이 코드는 정상적으로 동작합니다. 이것은 `Supplier` 클래스와 `Account` 클래스가 +같은 모듈 내에서 정의되어 있기 때문입니다. 반대로 아래의 코드는 동작하지 +않습니다. `Supplier` 클래스와 `Account` 클래스가 서로 다른 스코프에서 정의되어 +있기 때문입니다. ```ruby module MyApplication module Business - class Supplier < ActiveRecord::Base + class Supplier < ApplicationRecord has_one :account end end module Billing - class Account < ActiveRecord::Base + class Account < ApplicationRecord belongs_to :supplier end end end ``` -다른 스코프에 있는 모델을 이용해 관계를 선언하려면 관계 선언시에 정확한 클래스 명을 지정하면 됩니다. +다른 스코프에 있는 모델을 이용해 관계를 선언하려면 관계 선언시에 정확한 +클래스 명을 지정하면 됩니다. ```ruby module MyApplication module Business - class Supplier < ActiveRecord::Base + class Supplier < ApplicationRecord has_one :account, class_name: "MyApplication::Billing::Account" end end module Billing - class Account < ActiveRecord::Base + class Account < ApplicationRecord belongs_to :supplier, class_name: "MyApplication::Business::Supplier" end @@ -659,55 +813,64 @@ end 관계는 일반적으로 양쪽에 모두 선언됩니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end ``` -Active Record는 이 양방향 관계 선언에 대해 별도의 처리를 하지 않습니다. 이에 따라서 아래처럼 객체 2개의 사본 간에 내용이 일치하지 않는 경우가 생깁니다. +Active Record는 이 양방향 관계 선언에 대해 별도의 처리를 하지 않습니다. +이에 따라서 아래처럼 객체 2개의 사본 간에 내용이 일치하지 않는 경우가 생깁니다. ```ruby -c = Customer.first -o = c.orders.first -c.first_name == o.customer.first_name # => true -c.first_name = 'Manny' -c.first_name == o.customer.first_name # => false +a = Author.first +b = c.books.first +a.first_name == b.author.first_name # => true +a.first_name = 'Manny' +a.first_name == b.author.first_name # => false ``` -이런 문제가 발생하는 이유는 c와 o.customer라는 같은 데이터임에도 메모리 상에서는 다른 것으로 처리되고 있으며, 한쪽이 갱신되더라도 다른쪽이 자동적으로 변경되지 않기 때문입니다. Active Record의 `:inverse_of` 를 사용하면 이런 문제를 해결할 수 있습니다. +이런 문제가 발생하는 이유는 a와 b.author라는 같은 데이터임에도 메모리 상에서는 +다른 것으로 처리되고 있으며, 한쪽이 갱신되더라도 다른쪽이 자동적으로 변경되지 +않기 때문입니다. Active Record의 `:inverse_of` 를 사용하면 이런 문제를 해결할 +수 있습니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, inverse_of: :customer +class Author < ApplicationRecord + has_many :books, inverse_of: :author end -class Order < ActiveRecord::Base - belongs_to :customer, inverse_of: :orders +class Book < ApplicationRecord + belongs_to :author, inverse_of: :books end ``` -이와 같이 변경하는 것으로 Active Record는 customer 객체의 사본을 하나만 읽어들이게 되며, 부정합이 발생할 가능성을 방지하고, 동시에 애플리케이션의 효율을 증가시킵니다. +이와 같이 변경하는 것으로 Active Record는 author 객체의 사본을 하나만 +읽어들이게 되며, 부정합이 발생할 가능성을 방지하고, 동시에 애플리케이션의 +효율을 증가시킵니다. ```ruby -c = Customer.first -o = c.orders.first -c.first_name == o.customer.first_name # => true -c.first_name = 'Manny' -c.first_name == o.customer.first_name # => true +a = Author.first +b = a.books.first +a.first_name == b.author.first_name # => true +a.first_name = 'Manny' +a.first_name == b.author.first_name # => true ``` -그러나 `inverse_of`를 사용할 때에는 몇가지 제한사항이 있습니다. +그러나 `inverse_of`를 사용할 때에는 몇 가지 제한사항이 있습니다. * `:through`와 함께 사용할 수 없습니다. * `:polymorphic`와 함께 사용할 수 없습니다. * `:as`와 함께 사용할 수 없습니다. * `belongs_to`의 경우, `has_many`의 역관계는 무시됩니다. -관계 선언에서는 항상 역관계를 검출하려고 합니다. 그때 `:inverse_of`를 휴리스틱하게 설정합니다. 일반적인 이름이라면 대부분의 관계 선언에서 역관계가 지원됩니다. 단, 아래의 옵션을 사용하는 경우에는 역관계가 자동적으로 설정되지 않습니다. +관계 선언에서는 항상 역관계를 검출하려고 합니다. 그때 `:inverse_of`를 +휴리스틱하게 설정합니다. 일반적인 이름이라면 대부분의 관계 선언에서 역관계가 +지원됩니다. 단, 아래의 옵션을 사용하는 경우에는 역관계가 자동적으로 설정되지 +않습니다. * :conditions * :through @@ -727,57 +890,72 @@ c.first_name == o.customer.first_name # => true `belongs_to`가 선언된 클래스에는 아래에 있는 5개의 메소드가 사용가능해집니다. -* `association(force_reload = false)` +* `association` * `association=(associate)` * `build_association(attributes = {})` * `create_association(attributes = {})` * `create_association!(attributes = {})` -이 메소드 중, `association` 부분은 플레이스홀더로, `belongs_to`의 첫 인수로 받은 관계명이 사용됩니다. 아래의 예시에서는 customer가 선언되어 있습니다. +이 메소드 중, `association` 부분은 플레이스홀더로, `belongs_to`의 첫 인수로 +받은 관계명이 사용됩니다. 아래의 예시에서는 author가 선언되어 있습니다. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end ``` -이에 따라서 Order 모델의 인스턴스에서는 아래의 메소드가 사용가능해집니다. +이에 따라서 Book 모델의 인스턴스에서는 아래의 메소드가 사용가능해집니다. ```ruby -customer -customer= -build_customer -create_customer -create_customer! +author +author= +build_author +create_author +create_author! ``` -NOTE: 새로 추가한 `has_one`이나 `belongs_to`를 초기화하는 경우에는 `has_many`나 `ahs_and_belongs_to_many`에서 사용되는 `association.build` 대신, `build_`로 시작하는 메소드를 사용해 주세요. 바로 생성을 하고 싶은 경우에는 `create_`로 시작되는 메소드를 사용해주세요. +NOTE: 새로 추가한 `has_one`이나 `belongs_to`를 초기화하는 경우에는 +`has_many`나 `ahs_and_belongs_to_many`에서 사용되는 `association.build` 대신, +`build_`로 시작하는 메소드를 사용해 주세요. 바로 생성을 하고 싶은 경우에는 +`create_`로 시작되는 메소드를 사용해주세요. -##### `association(force_reload = false)` +##### `association` -`association` 메소드는 관계 선언된 객체를 반환합니다. 없는 경우에는 `nil`을 돌려줍니다. +`association` 메소드는 관계 선언된 객체를 반환합니다. 없는 경우에는 `nil`을 +돌려줍니다. ```ruby -@customer = @order.customer +@author = @book.author ``` -관계가 선언된 객체가 데이터베이스에서 검색된 적이 있는 경우에는 캐시를 사용합니다. 캐시를 사용하지 않고 데이터베이스에서 직접 읽어오고 싶을 경우에는 `force_reload`의 인수로 `true`를 넘겨주면 됩니다. +관계가 선언된 객체가 데이터베이스에서 검색된 적이 있는 경우에는 캐시를 +사용합니다. 이 동작을 무시하고, 직접 데이터베이스에서 값을 읽어오고 싶은 +경우에는 부모 객체에 `#reload`를 호출해주세요. + +```ruby +@customer = @order.reload.customer +@author = @book.reload.author +``` ##### `association=(associate)` -`association=` 메소드는 인수로 받은 객체를 관계로 연결합니다. 정확히는 넘겨받은 객체에서 기본키를 찾아, 외래키로 저장합니다. +`association=` 메소드는 인수로 받은 객체를 관계로 연결합니다. 정확히는 +넘겨받은 객체에서 기본키를 찾아, 외래키로 저장합니다. ```ruby -@order.customer = @customer +@book.author = @author ``` ##### `build_association(attributes = {})` -`build_association` 메소드는 관계가 선언된 클래스의 새 객체를 반환합니다. 반환된 객체는 넘긴 속성값으로 초기화되어 외래키를 통해 연결됩니다. 단, 반환된 시점에는 아직 저장되지 _않았다_는 점에 주의해주세요. +`build_association` 메소드는 관계가 선언된 클래스의 새 객체를 반환합니다. +반환된 객체는 넘긴 속성값으로 초기화되어 외래키를 통해 연결됩니다. 단, 반환된 +시점에는 아직 저장되지 _않았다_는 점에 주의해주세요. ```ruby -@customer = @order.build_customer(customer_number: 123, - customer_name: "John Doe") +@author = @book.build_author(author_number: 123, + author_name: "John Doe") ``` ##### `create_association(attributes = {})` @@ -785,8 +963,8 @@ NOTE: 새로 추가한 `has_one`이나 `belongs_to`를 초기화하는 경우에 `create_association` 메소드는 관계가 선언된 클래스의 새 객체를 반환합니다. 이 객체는 넘겨진 속성값을 이용하여 초기화되며, 그 객체의 외래키를 통해 연결됩니다. 그리고 해당 모델에서 지정되어있는 모든 검증을 통과했다면 저장됩니다. ```ruby -@customer = @order.create_customer(customer_number: 123, - customer_name: "John Doe") +@author = @book.create_author(author_number: 123, + author_name: "John Doe") ``` ##### `create_association!(attributes = {})` @@ -799,8 +977,8 @@ NOTE: 새로 추가한 `has_one`이나 `belongs_to`를 초기화하는 경우에 Rails의 `belongs_to` 관계는 일반적으로 커스터마이즈할 필요가 없습니다만, 때때로 필요할 때가 있을 수 있습니다. 이럴 때에는 선언 시에 넘기는 옵션과 스코프 블록을 통해 간단히 커스터마이즈 할 수 있습니다. 예를 들어, 아래와 같은 옵션을 추가할 수 있습니다. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, dependent: :destroy, +class Book < ApplicationRecord + belongs_to :author, dependent: :destroy, counter_cache: true end ``` @@ -812,10 +990,12 @@ end * `:counter_cache` * `:dependent` * `:foreign_key` +* `:primary_key` * `:inverse_of` * `:polymorphic` * `:touch` * `:validate` +* `:optional` ##### `:autosave` @@ -823,11 +1003,12 @@ end ##### `:class_name` -관계 이름에서 상대의 객체명을 추측할 수 없는 경우, `:class_name` 옵션을 사용해서 모델명을 지정할 수 있습니다. 예를 들어, 주문(order)가 고객(customer)에 종속되어 있고, 실제 고객 모델명이 `Patron`일 경우에는 다음과 같이 지정합니다. +관계 이름에서 상대의 객체명을 추측할 수 없는 경우, `:class_name` 옵션을 +사용해서 모델명을 지정할 수 있습니다. 예를 들어, 책(book)이 저자(author)에 종속되어 있고, 실제 저자 모델명이 `Patron`일 경우에는 다음과 같이 지정합니다. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, class_name: "Patron" +class Book < ApplicationRecord + belongs_to :author, class_name: "Patron" end ``` @@ -836,45 +1017,59 @@ end `:counter_cache`는 종속되어있는 객체의 갯수 검색 효율을 향상시킵니다. 아래의 모델을 통해 설명하겠습니다. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` -이 선언대로라면 `@customer.orders.size`의 값을 알기 위해서 데이터베이스에 `COUNT(*)` 쿼리를 실행할 필요가 있습니다. 이 호출을 피하기 위해서 '종속되어있는 쪽의 모델(`belongs_to`가 선언되어 있는 모델)'에 카운터 캐시를 추가합니다. +이 선언대로라면 `@author.books.size`의 값을 알기 위해서 데이터베이스에 `COUNT(*)` 쿼리를 실행할 필요가 있습니다. 이 호출을 피하기 위해서 '종속되어있는 쪽의 모델(`belongs_to`가 선언되어 있는 모델)'에 카운터 캐시를 추가합니다. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, counter_cache: true +class Book < ApplicationRecord + belongs_to :author, counter_cache: true end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` 이와 같이 선언하면, 캐시값이 최신의 상태로 유지되며, 다음에 `size` 메소드가 호출되었을 때 저 값이 반환됩니다. -여기서 한가지 주의할 점이 있습니다. `:counter_cache`는 `belongs_to` 선언에서 지정합니다만, 실제로 숫자를 세고 싶은 컬럼은 종속을 요구하는 모델에 추가할 필요가 있습니다. 예제에서라면 `Customer` 모델에 `orders_count` 컬럼을 추가할 필요가 있습니다. 필요하다면 기본 컬럼명을 덮어쓸 수 있습니다. +여기서 한가지 주의할 점이 있습니다. `:counter_cache`는 `belongs_to` 선언에서 +지정합니다만, 실제로 숫자를 세고 싶은 컬럼은 종속을 요구하는 모델에 추가할 +필요가 있습니다. 예제에서라면 `Author` 모델에 `books_count` 컬럼을 추가해야 +합니다. + +필요하다면 `counter_cache`에 `true`를 넘기는 대신 기본 컬럼명을 덮어쓸 이름을 +넘길 수 있습니다. 예를 들어, `books_count` 대신에 `count_of_books`를 사용하려면 +다음과 같이 선언하세요. + + ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, counter_cache: :count_of_orders +class Book < ApplicationRecord + belongs_to :author, counter_cache: :count_of_books end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` +NOTE: :counter_cache 옵션은 `belongs_to`를 선언하는 쪽에만 추가하면 됩니다. + 카운터 캐시용의 컬럼은 모델에 `attr_readonly`에 의해서 읽기 전용 속성으로 추가됩니다. ##### `:dependent` -`:dependent`의 동작은 아래와 같이 대상에 따라서 다릅니다. +소유 객체가 삭제되었을 때 관계로 연결된 객체를 어떻게 다룰지를 결정합니다. -* `:destroy` -- 객체가 삭제되면 종속되어있는 객체에 `destroy`를 호출합니다. -* `:delete` -- 객체가 삭제되면, 종속되어있는 객체를 직접 삭제합니다. 이 때 객체의 `destroy` 메소드는 호출되지 않습니다. +* `:destroy`는 종속되어 있는 객체도 삭제합니다. +* `:delete`는 종속되어 있는 객체를 데이터베이스에서 직접 삭제합니다. 이 때 객체의 콜백은 실행되지 않습니다. +* `:nullify`는 외래키를 `NULL`로 설정하며, 콜백은 실행되지 않습니다. +* `:restrict_with_exception`은 관계를 가지는 레코드가 있는 경우 에러를 던집니다. +* `:restrict_with_error`는 관계를 가지는 객체가 있는 경우 소유자에게 에러 메시지를 추가합니다. WARNING: 다른 클래스와 `has_many` 관계가 있는 `belongs_to`에 이 옵션을 사용해서는 안됩니다. 부모를 잃은 레코드가 데이터베이스에 남겨질 가능성이 있습니다. @@ -883,51 +1078,75 @@ WARNING: 다른 클래스와 `has_many` 관계가 있는 `belongs_to`에 이 옵 Rails의 관례로는 상대 모델을 지정하는 외래키를 저장하는 조인 테이블상의 컬럼명으로 모델명에 `_id`를 붙인 이름을 사용합니다. `:foreign_key`를 사용하면 외래키의 이름을 직접 지정할 수 있습니다. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, class_name: "Patron", - foreign_key: "patron_id" +class Book < ApplicationRecord + belongs_to :author, class_name: "Patron", + foreign_key: "patron_id" end ``` TIP: Rails는 외래키 컬럼을 자동적으로 생성하지 않습니다. 외래키를 사용하는 경우, 마이그레이션을 명시적으로 정의할 필요가 있습니다. +##### `:primary_key` + +관습적으로 레일스는 `id` 컬럼이 테이블의 기본키라고 추측합니다. `:primary_key` +옵션을 사용하여 이를 다른 컬럼으로 지정할 수 있습니다. + +예를 들어 `users` 테이블의 `guid`를 기본키로 사용하고 싶다고 합시다. 그리고 +`todos` 테이블에서는 `user_id`라는 외래키를 `guid`컬럼으로 연결하고 있는 +경우에는 다음과 같이 선언하면 됩니다. + +```ruby +class User < ApplicationRecord + self.primary_key = 'guid' # 기본키는 id가 아닌 guid가 된다 +end + +class Todo < ApplicationRecord + belongs_to :user, primary_key: 'guid' +end +``` + +`@user.todos.create`를 실행하면 `@todo`는 `user_id`에 `@user`의 `guid`를 +가지게 됩니다. + ##### `:inverse_of` `:inverse_of`는 그 관계와 역관계가 되는 `has_many`나 `has_one`의 대상을 지정합니다. `:polymorphic`와 함께 사용하는 경우에는 무효가 됩니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, inverse_of: :customer +class Author < ApplicationRecord + has_many :books, inverse_of: :author end -class Order < ActiveRecord::Base - belongs_to :customer, inverse_of: :orders +class Book < ApplicationRecord + belongs_to :author, inverse_of: :books end ``` ##### `:polymorphic` -`:polymorphic`에 `true`를 넘기면 다형 관계를 지정할 수 있습니다. 다형관계의 상세에 대해서는 [다형 관계](#Polymorphic_Assosiation)를 참조해주세요. +`:polymorphic`에 `true`를 넘기면 다형 관계를 지정할 수 있습니다. 다형관계의 +상세에 대해서는 [다형 관계](#다형-관계)를 참조해주세요. ##### `:touch` -`:touch`를 `:true`로 지정하면 관계가 선언된 객체가 저장, 또는 삭제될 때마다 그 객채의 `updated_at`과 `updated_on` 타임스탬프에 현재 시각이 저장됩니다. +`:touch`를 `:true`로 지정하면 관계가 선언된 객체가 저장, 또는 삭제될 때마다 +그 객체의 `updated_at`과 `updated_on` 타임스탬프에 현재 시각이 저장됩니다. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, touch: true +class Book < ApplicationRecord + belongs_to :author, touch: true end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` -여기에서 Order 클래스는 저장, 또는 삭제될 때에 관계가 선언된 Customer의 타임스탬프를 갱신합니다. 특정 타임스탬프 속성을 지정할 수도 있습니다. +여기에서 Book 클래스는 저장, 또는 삭제될 때에 관계가 선언된 Author의 타임스탬프를 갱신합니다. 특정 타임스탬프 속성을 지정할 수도 있습니다. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, touch: :orders_updated_at +class Book < ApplicationRecord + belongs_to :author, touch: :books_updated_at end ``` @@ -935,14 +1154,19 @@ end `:validate`를 `true`로 지정하면 객체가 저장될 때마다, 관계가 선언된 객체에서도 검증이 수행됩니다. 기본값은 `false`이며, 이 경우 객체가 저장될 때에는, 관계가 설정된 객체를 검증되지 않습니다. +##### `:optional` + +만약 `:optional` 옵션을 `true`로 설정했다면 관계가 설정된 객체가 존재하는지 +여부를 확인하지 않습니다. 이 옵션의 기본값은 `false`입니다. + #### `belongs_to`의 스코프 상황에 따라서는 `belongs_to`에서 사용되는 쿼리를 커스터마이즈 하고 싶을 경우도 있습니다. 스코프 블록을 사용해서 이러한 경우를 커스터마이즈할 수 있습니다. 예를 들어, ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, -> { where active: true }, - dependent: :destroy +class Book < ApplicationRecord + belongs_to :author, -> { where active: true }, + dependent: :destroy end ``` @@ -958,8 +1182,8 @@ end `where`는 관계된 객체가 만족시켜야하는 조건을 지정합니다. ```ruby -class Order < ActiveRecord::Base - belongs_to :customer, -> { where active: true } +class Book < ApplicationRecord + belongs_to :author, -> { where active: true } end ``` @@ -968,38 +1192,40 @@ end `includes` 메소드를 사용하면 관계가 쓰이는 경우 eager-load 해두고 싶은 제2관계를 지정할 수 있습니다. 아래의 모델을 예로 들어보겠습니다. ```ruby -class LineItem < ActiveRecord::Base - belongs_to :order +class LineItem < ApplicationRecord + belongs_to :book end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author has_many :line_items end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` -LineItem에서 고객(Customer)을 `@line_item.order.customer`처럼 직접 가져오는 경우가 빈번하다면 LineItem과 Order의 관계를 선언할 때에 Customer를 포함시켜서 쿼리의 낭비를 줄이고, 효율성을 높일 수 있습니다. +LineItem에서 저자(Author)을 `@line_item.book.author`처럼 직접 가져오는 경우가 빈번하다면 LineItem과 Book의 관계를 선언할 때에 Author를 포함시켜서 쿼리의 낭비를 줄이고, 효율성을 높일 수 있습니다. ```ruby -class LineItem < ActiveRecord::Base - belongs_to :order, -> { includes :customer } +class LineItem < ApplicationRecord + belongs_to :book, -> { includes :author } end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author has_many :line_items end -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` -NOTE: 직접 관계된 경우에는 `include`를 사용할 필요가 없습니다. `Order belongs_to :customer` 같은 관계 선언에서는 필요에 따라서 자동적으로 eager-load 됩니다. +NOTE: 직접 관계된 경우에는 `includes`를 사용할 필요가 없습니다. +`Book belongs_to :author` 같은 관계 선언에서는 필요에 따라서 자동적으로 +eager-load 됩니다. ##### `readonly` @@ -1016,8 +1242,8 @@ TIP: `select`를 `belongs_to`와 사용하는 경우, 올바른 결과를 얻기 `association.nil?` 메소드를 사용해서 관계된 객체가 존재하는지 확인할 수 있습니다. ```ruby -if @order.customer.nil? - @msg = "No customer found for this order" +if @book.author.nil? + @msg = "No author found for this book" end ``` @@ -1034,7 +1260,7 @@ end `has_one`을 선언한 클래스에서는 아래 5개의 메소드가 자동적으로 추가됩니다. -* `association(force_reload = false)` +* `association` * `association=(associate)` * `build_association(attributes = {})` * `create_association(attributes = {})` @@ -1043,7 +1269,7 @@ end 여기에서 `association`는 플레이스홀더이며, `has_one`의 첫번째 인수로 받은 이름으로 대체됩니다. 예를 들어 다음과 같이 선언했다고 합시다. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account end ``` @@ -1060,7 +1286,7 @@ create_account! NOTE: 새롭게 추가된 `has_one`이나 `belongs_to`를 초기화하기 위해서는 `build_`로 시작하는 메소드를 사용해야 합니다. 이 때, `has_many`나 `has_and_belongs_to_many`에서 사용되는 `association.build`를 사용하지 마세요. 바로 저장까지 하고 싶은 경우에는 `create_`로 시작하는 메소드를 사용해주세요. -##### `association(force_reload = false)` +##### `association` `association`는 관계가 맺어진 객체를 반환합니다. 해당하는 객체가 없는 경우에는 `nil`을 반환합니다. @@ -1068,7 +1294,12 @@ NOTE: 새롭게 추가된 `has_one`이나 `belongs_to`를 초기화하기 위해 @account = @supplier.account ``` -관계가 맺어진 객체가 데이터베이스에서 검색된 이력이 있는 경우에는 캐시된 객체가 반환됩니다. 캐시를 읽지 않고 데이터베이스에서 직접 읽어오고 싶은 경우에는 `force_reload` 인수에 `true`를 넘기세요. +관계가 맺어진 객체가 데이터베이스에서 검색된 이력이 있는 경우에는 캐시된 +객체가 반환됩니다. 캐시를 읽지 않고 데이터베이스에서 직접 읽어오고 싶은 경우에는 `#reload`를 호출해주세요. + +```ruby +@account = @supplier.reload.account +``` ##### `association=(associate)` @@ -1103,7 +1334,7 @@ NOTE: 새롭게 추가된 `has_one`이나 `belongs_to`를 초기화하기 위해 Rails의 `has_one`은 대부분의 경우 커스터마이즈할 필요가 없습니다만, 때때로 필요한 때가 있을 수 있습니다. 이럴 때에는 선언시에 넘기는 옵션으로 간단하게 변경할 수 있습니다. 예를 들자면 아래와 같은 방식으로 옵션을 추가할 수 있습니다. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, class_name: "Billing", dependent: :nullify end ``` @@ -1135,7 +1366,7 @@ end 관계 이름에서 상대의 객체명을 추측할 수 없는 경우, `:class_name` 옵션을 사용해서 모델명을 지정할 수 있습니다. 예를 들어 공급자(Supplier)는 계정을 하나씩 가지며, 계정을 나타내는 모델의 실제 이름이 `Account`가 아닌 `Billing`인 경우 아래와 같이 모델명을 지정할 수 있습니다. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, class_name: "Billing" end ``` @@ -1157,7 +1388,7 @@ end Rails의 관례로는 상대 모델을 지정하는 외래키를 저장하는 조인 테이블상의 컬럼명으로 모델명에 `_id`를 붙인 이름을 사용합니다. `:foreign_key`를 사용하면 외래키의 이름을 직접 지정할 수 있습니다. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, foreign_key: "supp_id" end ``` @@ -1169,11 +1400,11 @@ TIP: Rails는 외래키 컬럼을 자동적으로 생성하지 않습니다. 외 `:inverse_of`는 그 관계와 역관계가 되는 `belongs_to`의 대상을 지정합니다. `:through`나 `:as` 옵션과 함께 사용하는 경우에는 동작하지 않습니다. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, inverse_of: :supplier end -class Account < ActiveRecord::Base +class Account < ApplicationRecord belongs_to :supplier, inverse_of: :account end ``` @@ -1203,7 +1434,7 @@ Rails는 관례로서 모델의 기본키가 `id`에 저장되어 있기를 기 상황에 따라서는 `has_one`에서 사용되는 쿼리를 커스터마이즈하고 싶을 때도 있습니다. 스코프 블록을 사용해서 이런 경우를 해결할 수 있습니다. 예를 들어, ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, -> { where active: true } end ``` @@ -1220,7 +1451,7 @@ end `where`는 관계된 객체가 만족시켜야하는 조건을 지정합니다. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, -> { where "confirmed = 1" } end ``` @@ -1230,16 +1461,16 @@ end `includes` 메소드를 사용하면 관계가 쓰이는 경우 eager-load 해두고 싶은 제2관계를 지정할 수 있습니다. 아래의 모델을 예로 들어보겠습니다. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account end -class Account < ActiveRecord::Base +class Account < ApplicationRecord belongs_to :supplier belongs_to :representative end -class Representative < ActiveRecord::Base +class Representative < ApplicationRecord has_many :accounts end ``` @@ -1247,16 +1478,16 @@ end 여기에서 공급자에서 대표(Representative)를 `@supplier.account.representative`처럼 가져오는 경우가 자주 발생한다면, 공급자에서 계정 모델에 관계를 선언할 때에 대표 모델을 포함시키는 것으로 쿼리의 낭비를 줄이고, 효율성을 높일 수 있습니다. ```ruby -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_one :account, -> { includes :representative } end -class Account < ActiveRecord::Base +class Account < ApplicationRecord belongs_to :supplier belongs_to :representative end -class Representative < ActiveRecord::Base +class Representative < ApplicationRecord has_many :accounts end ``` @@ -1297,13 +1528,13 @@ end `has_many`를 선언한 클래스에서는 아래 16개의 메소드가 자동적으로 추가됩니다. -* `collection(force_reload = false)` +* `collection` * `collection<<(object, ...)` * `collection.delete(object, ...)` * `collection.destroy(object, ...)` -* `collection=objects` +* `collection=(objects)` * `collection_singular_ids` -* `collection_singular_ids=ids` +* `collection_singular_ids=(ids)` * `collection.clear` * `collection.empty?` * `collection.size` @@ -1317,38 +1548,38 @@ end 여기에서 `collection` 부분은 플레이스홀더이며, 실제로는 `has_many` 선언시에 넘긴 첫번째 인수명으로 대체됩니다. 또한 `collection_singular`부분은 단수형으로 변경됩니다. 아래와 같은 선언을 보시죠. ```ruby -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end ``` -이에 의해서 `Customer` 모델의 인스턴스는 다음과 같은 메소드를 사용할 수 있게 됩니다. +이에 의해서 `Author` 모델의 인스턴스는 다음과 같은 메소드를 사용할 수 있게 됩니다. ```ruby -orders(force_reload = false) -orders<<(object, ...) -orders.delete(object, ...) -orders.destroy(object, ...) -orders=objects -order_ids -order_ids=ids -orders.clear -orders.empty? -orders.size -orders.find(...) -orders.where(...) -orders.exists?(...) -orders.build(attributes = {}, ...) -orders.create(attributes = {}) -orders.create!(attributes = {})` +books +books<<(object, ...) +books.delete(object, ...) +books.destroy(object, ...) +books=(objects) +book_ids +book_ids=(ids) +books.clear +books.empty? +books.size +books.find(...) +books.where(...) +books.exists?(...) +books.build(attributes = {}, ...) +books.create(attributes = {}) +books.create!(attributes = {})` ``` -##### `collection(force_reload = false)` +##### `collection` `collection`은 관계가 맺어진 모든 객체의 배열을 반환합니다. 해당하는 객체가 없는 경우에는 빈 배열을 반환합니다. ```ruby -@orders = @customer.orders +@books = @author.books ``` ##### `collection<<(object, ...)` @@ -1356,7 +1587,7 @@ orders.create!(attributes = {})` `collection<<`는 1개 이상의 객체를 컬렉션에 추가합니다. 이 때 추가되는 객체의 외래키는 호출하는 쪽의 기본키로 설정됩니다. ```ruby -@customer.orders << @order1 +@author.books << @book1 ``` ##### `collection.delete(object, ...)` @@ -1364,7 +1595,7 @@ orders.create!(attributes = {})` `collection.delete`는 외래키를 `NULL`로 변경하는 것으로, 컬렉션에 있는 1개 이상의 객체를 제거합니다. ```ruby -@customer.orders.delete(@order1) +@author.books.delete(@book1) ``` WARNING: `dependent: :destroy`가 선언되어있는 경우에는 삭제됩니다만, `dependent: :delete_all`가 선언되어 있는 경우에는 콜백의 호출 없이 삭제 된다는 점을 주의해주세요. @@ -1374,12 +1605,12 @@ WARNING: `dependent: :destroy`가 선언되어있는 경우에는 삭제됩니 `collection.destroy`는 컬렉션에 들어있는 객체들에 대해서 `destroy`를 호출하고, 컬렉션에 들어있는 1개 이상의 객체를 삭제합니다. ```ruby -@customer.orders.destroy(@order1) +@author.books.destroy(@book1) ``` WARNING: 이 경우 객체는 _무조건_ 데이터베이스에서 삭제됩니다. 이 때, `:dependent` 옵션이 어떻게 설정되어 있든, 이를 무시하게 됩니다. -##### `collection=objects` +##### `collection=(objects)` `collection=`는 지정한 객체로 컬렉션을 교체합니다. 원래 있던 객체들은 삭제됩니다. @@ -1388,24 +1619,31 @@ WARNING: 이 경우 객체는 _무조건_ 데이터베이스에서 삭제됩니 `collection_singular_ids`는 그 컬렉션에 포함된 객체들의 id를 포함하는 배열을 반환합니다. ```ruby -@order_ids = @customer.order_ids +@book_ids = @author.book_ids ``` -##### `collection_singular_ids=ids` +##### `collection_singular_ids=(ids)` -`collection_singular_ids=`는 지정된 기본키를 가지는 객체들의 집합으로 컬렉션을 대체합니다. 원래의 컬렉션은 삭제됩니다. +`collection_singular_ids=`는 지정된 기본키를 가지는 객체들의 집합으로 컬렉션을 +대체합니다. 원래의 컬렉션은 삭제됩니다. ##### `collection.clear` `collection.clear`는 컬렉션의 모든 객체를 제거합니다. `dependent: :destroy`로 선언된 객체의 경우, 삭제시에 콜백이 호출되며, `dependent: :delete_all`는 데이터베이스에서 직접 호츨되므로 콜백이 호출되지 않습니다. 그 이외의 경우에는 외래키가 `NULL`로 변경됩니다. +```ruby +@author.books.clear +``` + +WARNING: 만약 `dependent: :destroy`가 설정되어 있다면 객체는 `dependent: :delete_all` 인것 처럼 모두 삭제 됩니다. + ##### `collection.empty?` `collection.empty?`는 컬렉션에 객체가 하나도 없는 경우에 `true`를 반환합니다. ```erb -<% if @customer.orders.empty? %> - 주문이 없습니다. +<% if @author.books.empty? %> + 책이 없습니다. <% end %> ``` @@ -1414,15 +1652,15 @@ WARNING: 이 경우 객체는 _무조건_ 데이터베이스에서 삭제됩니 `collection.size`는 컬렉션에 포함된 객체의 갯수를 반환합니다. ```ruby -@order_count = @customer.orders.size +@book_count = @author.books.size ``` ##### `collection.find(...)` -`collection.find`는 컬렉션에 포함된 객체들을 검색합니다. 이 메소드는 `ActiveRecord::Base.find`와 같은 방법으로 사용하면 됩니다. +`collection.find`는 컬렉션에 포함된 객체들을 검색합니다. 이 메소드는 `ApplicationRecord.find`와 같은 방법으로 사용하면 됩니다. ```ruby -@open_orders = @customer.orders.find(1) +@available_books = @author.books.find(1) ``` ##### `collection.where(...)` @@ -1430,21 +1668,28 @@ WARNING: 이 경우 객체는 _무조건_ 데이터베이스에서 삭제됩니 `collection.where`는 컬렉션에서 메소드에서 지정한 조건에 맞는 객체를 검색합니다. 이 메소드를 사용했을 때 객체는 lazy load된다는 점을 주의해주세요. 다시 말해서, 객체를 사용하려는 시점에 데이터베이스에 쿼리를 전송하게 됩니다. ```ruby -@open_orders = @customer.orders.where(open: true) # 이 시점에서 쿼리는 실행되지 않습니다 -@open_order = @open_orders.first # 이 시점에서 쿼리가 실제로 실행됩니다 +@available_books = @author.books.where(available: true) # 이 시점에서 쿼리는 실행되지 않습니다 +@available_book = @available_books.first # 이 시점에서 쿼리가 실제로 실행됩니다 ``` ##### `collection.exists?(...)` -`collection.exists?`는 지정된 조건에 맞는 객체가 컬렉션에 존재하는지를 확인합니다. 이 메소드는 `ActiveRecord::Base.exists?`와 같은 방식으로 사용할 수 있습니다. +`collection.exists?`는 지정된 조건에 맞는 객체가 컬렉션에 존재하는지 +확인합니다. 이 메소드는 [`ApplicationRecord.exists?`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F)와 +같은 방식으로 사용할 수 있습니다. ##### `collection.build(attributes = {}, ...)` `collection.build`는 관계된 클래스의 객체를 1개 이상 반환합니다. 반환된 객체는 넘겨진 속성을 이용해 초기화 되고, 저장되는 시점에 외래키를 추가합니다. 단, 반환되는 시점에서는 아직 _저장되지 않았음_을 유의해주세요. ```ruby -@order = @customer.orders.build(order_date: Time.now, - order_number: "A12345") +@book = @author.books.build(published_at: Time.now, + book_number: "A12345") + +@books = @author.books.build([ + { published_at: Time.now, book_number: "A12346" }, + { published_at: Time.now, book_number: "A12347" } +]) ``` ##### `collection.create(attributes = {})` @@ -1452,8 +1697,13 @@ WARNING: 이 경우 객체는 _무조건_ 데이터베이스에서 삭제됩니 `collection.create`는 관계된 클래스의 객체를 하나 반환합니다. 반환된 객체는 넘겨진 속성을 이용해 초기화 되고, 저장되는 시점에 외래키를 추가합니다. 그리고 유효성 검사를 통과하는 경우, 객체가 저장됩니다. ```ruby -@order = @customer.orders.create(order_date: Time.now, - order_number: "A12345") +@book = @author.books.create(published_at: Time.now, + book_number: "A12345") + +@books = @author.books.create([ + { published_at: Time.now, book_number: "A12346" }, + { published_at: Time.now, book_number: "A12347" } +]) ``` ##### `collection.create!(attributes = {})` @@ -1465,8 +1715,8 @@ WARNING: 이 경우 객체는 _무조건_ 데이터베이스에서 삭제됩니 Rails의 `has_many`는 대부분의 경우 커스터마이즈할 필요가 없습니다만, 때때로 필요한 때가 있을 수 있습니다. 이럴 때에는 선언시에 넘기는 옵션으로 간단하게 변경할 수 있습니다. 예를 들자면 아래와 같은 방식으로 옵션을 추가할 수 있습니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, dependent: :delete_all, validate: :false +class Author < ApplicationRecord + has_many :books, dependent: :delete_all, validate: :false end ``` @@ -1475,6 +1725,7 @@ end * `:as` * `:autosave` * `:class_name` +* `:counter_cache` * `:dependent` * `:foreign_key` * `:inverse_of` @@ -1494,14 +1745,20 @@ end ##### `:class_name` -관계 이름에서 상대의 객체명을 추측할 수 없는 경우, `:class_name` 옵션을 사용해서 모델명을 지정할 수 있습니다. 예를 들어 1명의 고객이 여러 주문(order)을 가지고 있고, 실제 주문 모델의 이름이 `Transaction`인 경우 아래와 같이 선언할 수 있습니다. +관계 이름에서 상대의 객체명을 추측할 수 없는 경우, `:class_name` 옵션을 사용해서 모델명을 지정할 수 있습니다. 예를 들어 1명의 저자가 여러 책(book)을 가지고 있고, 실제 책 모델의 이름이 `Transaction`인 경우 아래와 같이 선언할 수 있습니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, class_name: "Transaction" +class Author < ApplicationRecord + has_many :books, class_name: "Transaction" end ``` +##### `:counter_cache` + +이 옵션은 `:counter_cache`의 컬럼명을 변경하기 위해서 사용할 수 있습니다. +[belongs_to 관계](#belongs-to의-옵션)에서 정의한 `:counter_cache`의 이름을 +바꾸고 싶을 때만 사용할 수 있습니다. + ##### `:dependent` 부모 객체가 삭제될 때에 그 객체와 관계가 선언된 객체를 어떻게 할지 결정합니다. @@ -1517,8 +1774,8 @@ end Rails의 관례로는 상대 모델을 지정하는 외래키를 저장하는 조인 테이블상의 컬럼명으로 모델명에 `_id`를 붙인 이름을 사용합니다. `:foreign_key`를 사용하면 외래키의 이름을 직접 지정할 수 있습니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, foreign_key: "cust_id" +class Author < ApplicationRecord + has_many :books, foreign_key: "cust_id" end ``` @@ -1529,12 +1786,12 @@ TIP: Rails는 외래키 컬럼을 자동적으로 생성하지 않습니다. 외 `:inverse_of`는 그 관계와 역관계가 되는 `belongs_to`의 대상을 지정합니다. `:through`나 `:as` 옵션과 함께 사용하는 경우에는 동작하지 않습니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, inverse_of: :customer +class Author < ApplicationRecord + has_many :books, inverse_of: :author end -class Order < ActiveRecord::Base - belongs_to :customer, inverse_of: :orders +class Book < ApplicationRecord + belongs_to :author, inverse_of: :books end ``` @@ -1545,7 +1802,7 @@ Rails는 관례로서 모델의 기본키가 `id`에 저장되어 있기를 기 `users`테이블에 기본키로 `id` 컬럼이 있고, 그 이외에도 `guid` 라는 컬럼이 있다고 합시다. 그리고 `todos`라는 테이블에서는 `users` 테이블의 `id` 컬럼이 아닌 `guid` 컬럼을 사용하고 싶다면, 아래와 같이 할 수 있습니다. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord has_many :todos, primary_key: :guid end ``` @@ -1574,8 +1831,8 @@ end 상황에 따라서는 `has_many`에서 사용되는 쿼리를 커스터마이즈하고 싶을 때도 있습니다. 스코프 블록을 사용해서 이런 경우를 해결할 수 있습니다. 예를 들어, ```ruby -class Customer < ActiveRecord::Base - has_many :orders, -> { where processed: true } +class Author < ApplicationRecord + has_many :books, -> { where processed: true } end ``` @@ -1590,42 +1847,42 @@ end * `order` * `readonly` * `select` -* `uniq` +* `distinct` ##### `where` `where`는 관계된 객체가 만족시켜야하는 조건을 지정합니다. ```ruby -class Customer < ActiveRecord::Base - has_many :confirmed_orders, -> { where "confirmed = 1" }, - class_name: "Order" +class Author < ApplicationRecord + has_many :confirmed_books, -> { where "confirmed = 1" }, + class_name: "Book" end ``` 조건은 해시를 통해서 지정할 수도 있습니다. ```ruby -class Customer < ActiveRecord::Base - has_many :confirmed_orders, -> { where confirmed: true }, - class_name: "Order" +class Author < ApplicationRecord + has_many :confirmed_books, -> { where confirmed: true }, + class_name: "Book" end ``` -`where`에서 해시를 사용한 경우, 여기에서 작성된 레코드는 자동적으로 이 해시를 이용한 스코프에 포함되게 됩니다. 이 예시의 경우, `@customer.confirmed_orders.create`나 `@customer.confirmed_orders.build`를 실행하면 confirmed 컬럼이 `true`로 생성됩니다. +`where`에서 해시를 사용한 경우, 여기에서 작성된 레코드는 자동적으로 이 해시를 이용한 스코프에 포함되게 됩니다. 이 예시의 경우, `@author.confirmed_books.create`나 `@author.confirmed_books.build`를 실행하면 confirmed 컬럼이 `true`로 생성됩니다. ##### `extending` -`extending`는 관계 프록시를 확장하기 위한 모듈을 지정할 수 있습니다. 관계 확장에 대해서는 [뒤에서 설명합니다](#관계_확장) +`extending`는 관계 프록시를 확장하기 위한 모듈을 지정할 수 있습니다. 관계 확장에 대해서는 [뒤에서 설명합니다](#관계-확장) ##### `group` `group`은 결과를 그룹화하기 위한 속성명을 하나 지정합니다. 내부적으로는 SQL의 `GROUP BY`를 사용합니다. ```ruby -class Customer < ActiveRecord::Base - has_many :line_items, -> { group 'orders.id' }, - through: :orders +class Author < ApplicationRecord + has_many :line_items, -> { group 'books.id' }, + through: :books end ``` @@ -1634,34 +1891,34 @@ end `includes` 메소드를 사용하면 관계가 쓰이는 경우 eager-load 해두고 싶은 제2관계를 지정할 수 있습니다. 아래의 모델을 예로 들어보겠습니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders +class Author < ApplicationRecord + has_many :books end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author has_many :line_items end -class LineItem < ActiveRecord::Base - belongs_to :order +class LineItem < ApplicationRecord + belongs_to :book end ``` -고객(Customer)에서 LineItem을 `@customer.orders.line_items`처럼 직접 가져오는 경우가 자주 발생한다면, Customer와 주문의 관계를 선언할 때에 LineItem을 포함시키는 것으로 쿼리의 낭비를 줄이고, 효율성을 높일 수 있습니다. +저자(Author)에서 LineItem을 `@author.books.line_items`처럼 직접 가져오는 경우가 자주 발생한다면, Author와 책의 관계를 선언할 때에 LineItem을 포함시키는 것으로 쿼리의 낭비를 줄이고, 효율성을 높일 수 있습니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, -> { includes :line_items } +class Author < ApplicationRecord + has_many :books, -> { includes :line_items } end -class Order < ActiveRecord::Base - belongs_to :customer +class Book < ApplicationRecord + belongs_to :author has_many :line_items end -class LineItem < ActiveRecord::Base - belongs_to :order +class LineItem < ApplicationRecord + belongs_to :book end ``` @@ -1670,10 +1927,10 @@ end `limit`은 관계를 통해 가져올 수 있는 객체 숫자를 제한할 때 사용합니다. ```ruby -class Customer < ActiveRecord::Base - has_many :recent_orders, - -> { order('order_date desc').limit(100) }, - class_name: "Order", +class Author < ApplicationRecord + has_many :recent_books, + -> { book('published_at desc').limit(100) }, + class_name: "Book", end ``` @@ -1686,8 +1943,8 @@ end `order`는 컬랙션에서의 순서를 지정합니다. 내부적으로는 SQL의 `ORDER BY`를 사용합니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, -> { order "date_confirmed DESC" } +class Author < ApplicationRecord + has_many :books, -> { book "date_confirmed DESC" } end ``` @@ -1706,7 +1963,7 @@ WARNING: `select`를 사용하는 경우에는 상대 모델의 기본키와 외 `distinct`는 컬렉션에서 중복이 발생하지 않도록 합니다. 이 메소드는 `:through`와 함께 사용될 경우 유용합니다. ```ruby -class Person < ActiveRecord::Base +class Person < ApplicationRecord has_many :readings has_many :posts, through: :readings end @@ -1742,7 +1999,17 @@ Reading.all.inspect # => [#, # ActiveRecord::RecordNotUnique ``` 또한 `include?`를 사용해서 유일성을 체크하면 중복이 발생할 수 있으므로 주의할 필요가 있습니다. 관계에서 유일성을 강제하기 위해서 `include?`를 사용하지 말아주세요. 위의 post를 예로 들자면, 아래와 같은 코드는 문제가 발생하기 쉽습니다. 이것은 여러 사용자가 동시에 이 코드를 실행할 가능성이 있기 때문입니다. @@ -1769,13 +2036,13 @@ person.posts << post unless person.posts.include?(post) `has_and_belongs_to_many`를 선언한 클래스에서는 아래 16개의 메소드가 자동적으로 추가됩니다. -* `collection(force_reload = false)` +* `collection` * `collection<<(object, ...)` * `collection.delete(object, ...)` * `collection.destroy(object, ...)` -* `collection=objects` +* `collection=(objects)` * `collection_singular_ids` -* `collection_singular_ids=ids` +* `collection_singular_ids=(ids)` * `collection.clear` * `collection.empty?` * `collection.size` @@ -1789,7 +2056,7 @@ person.posts << post unless person.posts.include?(post) 여기에서 `collection` 부분은 플레이스홀더이며, 실제로는 `has_and_belongs_to_many` 선언시에 넘긴 첫번째 인수명으로 대체됩니다. 또한 `collection_singular`부분은 단수형으로 변경됩니다. 아래와 같은 선언을 보시죠. ```ruby -class Part < ActiveRecord::Base +class Part < ApplicationRecord has_and_belongs_to_many :assemblies end ``` @@ -1797,13 +2064,13 @@ end 이에 의해서 `Part` 모델의 인스턴스는 다음과 같은 메소드를 사용할 수 있게 됩니다. ```ruby -assemblies(force_reload = false) +assemblies assemblies<<(object, ...) assemblies.delete(object, ...) assemblies.destroy(object, ...) -assemblies=objects +assemblies=(objects) assembly_ids -assembly_ids=ids +assembly_ids=(ids) assemblies.clear assemblies.empty? assemblies.size @@ -1821,7 +2088,8 @@ assemblies.create!(attributes = {})` WARNING: `has_and_belongs_to_many`에서 사용하는 조인 테이블에 이런 여분의 컬럼을 추가하는 것은 권장되지 않습니다. 2개의 모델을 다대다로 결합하는 경우에 이런 복잡한 기능이 필요한 경우, `has_and_belongs_to_many`가 아닌 `has_many :through`를 사용해주세요. -##### `collection(force_reload = false)` + +##### `collection` `collection`은 관계가 맺어진 모든 객체의 배열을 반환합니다. 해당하는 객체가 없는 경우에는 빈 배열을 반환합니다. @@ -1857,7 +2125,7 @@ WARNING: 이 메소드가 호출되더라도 조인 레코드의 콜백은 호 @part.assemblies.destroy(@assembly1) ``` -##### `collection=objects` +##### `collection=(objects)` `collection=`는 지정된 객체로 컬렉션을 변경합니다. 원래의 컬렉션은 삭제됩니다. @@ -1869,7 +2137,7 @@ WARNING: 이 메소드가 호출되더라도 조인 레코드의 콜백은 호 @assembly_ids = @part.assembly_ids ``` -##### `collection_singular_ids=ids` +##### `collection_singular_ids=(ids)` `collection_singular_ids=`는 지정된 기본키를 가지는 객체들의 집합으로 컬렉션을 대체합니다. 원래의 컬렉션은 삭제됩니다. @@ -1897,7 +2165,7 @@ WARNING: 이 메소드가 호출되더라도 조인 레코드의 콜백은 호 ##### `collection.find(...)` -`collection.find`는 컬렉션에 포함된 객체들을 검색합니다. 이 메소드는 `ActiveRecord::Base.find`와 같은 방법으로 사용하면 됩니다. +`collection.find`는 컬렉션에 포함된 객체들을 검색합니다. 이 메소드는 `ApplicationRecord.find`와 같은 방법으로 사용하면 됩니다. ```ruby @assembly = @part.assemblies.find(1) @@ -1913,7 +2181,7 @@ WARNING: 이 메소드가 호출되더라도 조인 레코드의 콜백은 호 ##### `collection.exists?(...)` -`collection.exists?`는 지정된 조건에 맞는 객체가 컬렉션에 존재하는지를 확인합니다. 이 메소드는 `ActiveRecord::Base.exists?`와 같은 방식으로 사용할 수 있습니다. +`collection.exists?`는 지정된 조건에 맞는 객체가 컬렉션에 존재하는지를 확인합니다. 이 메소드는 `ApplicationRecord.exists?`와 같은 방식으로 사용할 수 있습니다. ##### `collection.build(attributes = {})` @@ -1940,9 +2208,9 @@ WARNING: 이 메소드가 호출되더라도 조인 레코드의 콜백은 호 Rails의 `has_and_belongs_to_many`는 대부분의 경우 커스터마이즈할 필요가 없습니다만, 때때로 필요한 때가 있을 수 있습니다. 이럴 때에는 선언시에 넘기는 옵션으로 간단하게 변경할 수 있습니다. 예를 들자면 아래와 같은 방식으로 옵션을 추가할 수 있습니다. ```ruby -class Parts < ActiveRecord::Base - has_and_belongs_to_many :assemblies, autosave: true, - readonly: true +class Parts < ApplicationRecord + has_and_belongs_to_many :assemblies, -> { readonly }, + autosave: true end ``` @@ -1963,7 +2231,7 @@ Rails의 관례로는 상대 모델을 지정하는 외래키를 저장하는 TIP: `:foreign_key`와 `:association_foreign_key`는 다대다 자체조인을 사용할 경우에 유용합니다. 예를 들어, 아래와 같이 쓸 수 있습니다. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord has_and_belongs_to_many :friends, class_name: "User", foreign_key: "this_user_id", @@ -1980,7 +2248,7 @@ end 관계 이름에서 상대의 객체명을 추측할 수 없는 경우, `:class_name` 옵션을 사용해서 모델명을 지정할 수 있습니다. 예를 들어, 하나의 부품(Part)은 복수의 제품(assembly)에서 사용되고, 실제 제품 모델의 이름이 `Gadget`인 경우 아래와 같이 선언할 수 있습니다. ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, class_name: "Gadget" end ``` @@ -1990,7 +2258,7 @@ end Rails의 관례로는 그 모델을 지정하는 외래키를 저장하는 조인 테이블상의 컬럼명으로 모델명에 `_id`를 붙인 이름을 사용합니다. `:foreign_key`를 사용하면 외래키의 이름을 직접 지정할 수 있습니다. ```ruby -class User < ActiveRecord::Base +class User < ApplicationRecord has_and_belongs_to_many :friends, class_name: "User", foreign_key: "this_user_id", @@ -2011,7 +2279,7 @@ end 상황에 따라서는 `has_and_belongs_to_many`에서 사용되는 쿼리를 커스터마이즈하고 싶을 때도 있습니다. 스코프 블록을 사용해서 이런 경우를 해결할 수 있습니다. 예를 들어, ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, -> { where active: true } end ``` @@ -2027,14 +2295,14 @@ end * `order` * `readonly` * `select` -* `uniq` +* `distinct` ##### `where` `where`는 관계된 객체가 만족시켜야하는 조건을 지정합니다. ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, -> { where "factory = 'Seattle'" } end @@ -2043,7 +2311,7 @@ end 조건은 해시를 통해서 지정할 수도 있습니다. ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, -> { where factory: 'Seattle' } end @@ -2053,14 +2321,14 @@ end ##### `extending` -`extending`는 관계 프록시를 확장하기 위한 모듈을 지정할 수 있습니다. 관계 확장에 대해서는 [뒤에서 설명합니다](#관계_확장) +`extending`는 관계 프록시를 확장하기 위한 모듈을 지정할 수 있습니다. 관계 확장에 대해서는 [뒤에서 설명합니다](#관계-확장) ##### `group` `group`은 결과를 그룹화하기 위한 속성명을 하나 지정합니다. 내부적으로는 SQL의 `GROUP BY`를 사용합니다. ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, -> { group "factory" } end ``` @@ -2074,9 +2342,9 @@ end `limit`은 관계를 통해 가져올 수 있는 객체 숫자를 제한할 때 사용합니다. ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, - -> { order("created_at DESC").limit(50) } + -> { book("created_at DESC").limit(50) } end ``` @@ -2089,9 +2357,9 @@ end `order`는 컬랙션에서의 순서를 지정합니다. 내부적으로는 SQL의 `ORDER BY`를 사용합니다. ```ruby -class Parts < ActiveRecord::Base +class Parts < ApplicationRecord has_and_belongs_to_many :assemblies, - -> { order "assembly_name ASC" } + -> { book "assembly_name ASC" } end ``` @@ -2103,9 +2371,9 @@ end `select` 메소드를 사용하면, 관계된 객체의 데이터를 얻어올 때 사용하는 SQL의 `SELECT`절을 덮어쓸 수 있습니다. Rails에는 기본적으로 모든 컬럼을 다 가져옵니다. -##### `uniq` +##### `distinct` -`uniq`는 컬렉션 내의 중복을 제거합니다. +`distinct`는 컬렉션 내의 중복을 제거합니다. #### 객체가 저장되는 시점 @@ -2131,10 +2399,10 @@ end 이 옵션을 콜백 선언 시에 추가하는 것으로 사용할 수 있습니다. 예를 들어, 다음과 같이 쓸 수 있습니다. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, before_add: :check_credit_limit +class Author < ApplicationRecord + has_many :books, before_add: :check_credit_limit - def check_credit_limit(order) + def check_credit_limit(book) ... end end @@ -2145,15 +2413,15 @@ Rails는 콜백에 추가된 객체나 삭제된 객체를 넘겨줍니다. 1개의 이벤트에서 여러 개의 콜백을 사용하고 싶은 경우에는 배열을 사용해서 전달해주세요. ```ruby -class Customer < ActiveRecord::Base - has_many :orders, +class Author < ApplicationRecord + has_many :books, before_add: [:check_credit_limit, :calculate_shipping_charges] - def check_credit_limit(order) + def check_credit_limit(book) ... end - def calculate_shipping_charges(order) + def calculate_shipping_charges(book) ... end end @@ -2166,16 +2434,17 @@ end Rails는 자동적으로 관계를 위한 프록시 객체를 만들어 줍니다만, 익명 모듈을 사용해서 이런 객체들을 확장(검색, 생성 등의 메소드를 추가)할 수 있습니다. 다음과 같이 말이죠. ```ruby -class Customer < ActiveRecord::Base - has_many :orders do - def find_by_order_prefix(order_number) - find_by(region_id: order_number[0..2]) +class Author < ApplicationRecord + has_many :books do + def find_by_book_prefix(book_number) + find_by(category_id: book_number[0..2]) end end end ``` -확장된 기능을 여러 모델에 걸쳐서 공유하고 싶은 경우에는 별도의 모듈로 만들어서 쓸 수 있습니다. +확장된 기능을 여러 모델에 걸쳐서 공유하고 싶은 경우에는 별도의 모듈로 만들어서 +쓸 수 있습니다. ```ruby module FindRecentExtension @@ -2184,11 +2453,11 @@ module FindRecentExtension end end -class Customer < ActiveRecord::Base - has_many :orders, -> { extending FindRecentExtension } +class Author < ApplicationRecord + has_many :books, -> { extending FindRecentExtension } end -class Supplier < ActiveRecord::Base +class Supplier < ApplicationRecord has_many :deliveries, -> { extending FindRecentExtension } end ``` @@ -2199,5 +2468,66 @@ end * `proxy_association.reflection`은 관계를 설명하는 리플렉션 객체를 반환합니다. * `proxy_association.target`는 `belongs_to`나 `has_one`로 연결된 객체를 돌려주거나 `has_many`나 `has_and_belongs_to_many`로 연결된 컬렉션을 반환합니다. -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. +단일 테이블 상속(Single Table Inheritance, STI) +------------------------ + +때떄로 어떤 필드나 행동을 다른 모델간에 공유하고 싶은 경우가 있습니다. +예를 들어 차와 오토바이, 자전거 모델을 가지고 있다고 해봅니다. 이 세 모델에서 +`color`와 `price` 컬럼과 이에 관한 메소드들을 공유하고, 고유의 동작과 +컨트롤러 등은 그대로 두고 싶다고 해봅니다. + +Rails는 이를 꽤 쉽게 만들어 줍니다. 우선 탈 것 모델을 만들어보죠. + +```bash +$ rails generate model vehicle type:string color:string price:decimal{10.2} +``` + +"type" 필드를 추가한 것을 확인하셨나요? 모든 모델은 한 테이블에 저장되므로, +Rails는 모델 이름을 이 컬럼에 저장합니다. 이 예제에서는 "Car", "Motorcycle", +"Bicycle"이 저장될 겁니다. STI는 "type" 필드가 없으면 동작하지 않습니다. + +그리고 Vehicle을 상속하는 3개의 모델을 만듭시다. 이를 위해서 `--parent=PARENT` +옵션을 사용합니다. 이는 특정 부모를 가지는 모델을 (이미 데이터를 저장할 +테이블을 가지고 있기 때문에) 마이그레이션 없이 생성할 수 있게 해줍니다. + +예를 들어 Car 모델을 생성하려면 이렇습니다. + +```bash +$ rails generate model car --parent=Vehicle +``` + +생성된 모델은 이렇습니다. + +```ruby +class Car < Vehicle +end +``` + +이는 Vehicle 모델의 관계, 메소드 등의 모든 행동을 Car 모델에서도 사용할 수 +있다는 의미입니다. + +자동차를 만들면 `vehicles` 테이블에 `type`가 "Car"가 들어있는 레코드를 +생성합니다. + +```ruby +Car.create(color: 'Red', price: 10000) +``` + +이 코드는 다음과 같은 SQL을 생성합니다. + +```sql +INSERT INTO "vehicles" ("type", "color", "price") VALUES ('Car', 'Red', 10000) +``` + +모든 자동차를 가져와봅시다. + +```ruby +Car.all +``` + +이는 다음과 같은 SQL을 생성합니다. + +```sql +SELECT "vehicles".* FROM "vehicles" WHERE "vehicles"."type" IN ('Car') +``` diff --git a/guides/source/ko/autoloading_and_reloading_constants.md b/guides/source/ko/autoloading_and_reloading_constants.md index cd75cf2b19101..23a2d974c7e22 100644 --- a/guides/source/ko/autoloading_and_reloading_constants.md +++ b/guides/source/ko/autoloading_and_reloading_constants.md @@ -334,7 +334,7 @@ require 'erb' 그런데, Rails에는 `post.rb`와 같은 파일을 탐색하는 `$LOAD_PATH`와 비슷한 폴더 목록이 있습니다. 이 목록은 `autoload_paths`이라고 불리고 있으며 기본으로는 다음과 같은 것들이 포함되어 있습니다. -* 애플리케이션과 엔진의 `app` 폴더 하의 모든 폴더들. `app/controllers` 등이 대상. `app` 밑에 있는 `app/workers` 등의 폴더들도 모두 `autoload_paths`에 자동적으로 포함되므로, 기본 폴더로 지정할 필요는 없습니다. +* 실행 시점에 존재하는 애플리케이션과 엔진의 `app` 폴더 하의 모든 폴더들. `app/controllers` 등이 대상. `app` 밑에 있는 `app/workers` 등의 폴더들도 모두 `autoload_paths`에 자동적으로 포함되므로, 기본 폴더로 지정할 필요는 없습니다. * 애플리케이션과 엔진의 모든 `app/*/concerns` 제2의 하위 폴더. diff --git a/guides/source/ko/caching_with_rails.md b/guides/source/ko/caching_with_rails.md index e25a9e5f98335..cf366c207ed01 100644 --- a/guides/source/ko/caching_with_rails.md +++ b/guides/source/ko/caching_with_rails.md @@ -103,16 +103,18 @@ TIP: Memcached 등의 캐시 저장소에서는 오래된 캐시 파일을 자 #### 컬렉션 캐싱 -`render` 헬퍼는 컬렉션의 개별 템플릿을 그릴 때도 캐싱을 사용할 수 있습니다. 위의 예제에서 `each` -를 사용한 코드에서 각각을 가져오는 대신 한 번에 모든 캐시 템플릿을 가져올 수도 있습니다. -콜렉션을 랜더링 하는 경우에 `cached: true`를 지정해주세요. +`render` 헬퍼는 컬렉션의 개별 템플릿을 그릴 때도 캐싱을 사용할 수 있습니다. +위의 예제에서 `each`를 사용한 코드에서 각각을 가져오는 대신 한 번에 모든 캐시 +템플릿을 가져올 수도 있습니다. 콜렉션을 랜더링 하는 경우에 `cached: true`를 +지정해주세요. ```erb <%= render partial: 'products/product', collection: @products, cached: true %> ``` -이 코드는 이전까지 사용되었던 모든 캐시 템플릿이 한 번에 가져오며, 극적으로 속도가 향상됩니다. -나아가 지금까지 캐시되지 않았던 템플릿도 캐시에 추가되어 다음 랜더링 시에 한 번에 읽어올 수 있게 됩니다. +이 코드는 이전까지 사용되었던 모든 캐시 템플릿이 한 번에 가져오며, 극적으로 +속도가 향상됩니다. 나아가 지금까지 캐시되지 않았던 템플릿도 캐시에 추가되어 +다음 랜더링 시에 한 번에 읽어올 수 있게 됩니다. ### 러시아 인형 캐싱 @@ -167,8 +169,9 @@ end #### 암묵적인 의존성 -대부분의 경우, 템플릿의 의존성은 템플릿 자신이 호출하는 `render`에 의해서 발생합니다. -다음 예제에서는 디코딩하는 방법을 다루는 `ActionView::Digestor`를 사용하는 `render` 호출 예제입니다. +대부분의 경우, 템플릿의 의존성은 템플릿 자신이 호출하는 `render`에 의해서 +발생합니다. 다음 예제에서는 디코딩하는 방법을 다루는 `ActionView::Digestor`를 +사용하는 `render` 호출 예제입니다. ```ruby render partial: "comments/comment", collection: commentable.comments @@ -176,11 +179,11 @@ render "comments/comments" render "comments/comments" render("comments/comments") -render "header" => render("comments/header") +render "header" 는 render("comments/header") 가 됩니다. -render(@topic) => render("topics/topic") -render(topics) => render("topics/topic") -render(message.topics) => render("topics/topic") +render(@topic) 는 render("topics/topic") 가 됩니다. +render(topics) 는 render("topics/topic") 가 됩니다. +render(message.topics) 는 render("topics/topic") 가 됩니다. ``` 한편 일부는 호출할 때에 캐시가 적절하게 동작하도록 변경해야 합니다. @@ -243,17 +246,19 @@ render partial: "documents/document", collection: @project.documents.where(publi ### 저레벨 캐시 -뷰의 조각을 캐싱하는 것이 아니라 특정 값이나 쿼리의 결과만을 캐싱하고 싶은 경우가 있습니다. -레일스의 캐싱 기법으로는 어떤 정보라도 캐시에 저장할 수 있습니다. +뷰의 조각을 캐싱하는 것이 아니라 특정 값이나 쿼리의 결과만을 캐싱하고 싶은 +경우가 있습니다. 레일스의 캐싱 기법으로는 어떤 정보라도 캐시에 저장할 수 +있습니다. -저레벨 캐시의 가장 효과적인 구현 방법은 `Rails.cache.fetch` 메소드를 사용하는 것입니다. -이 메소드는 캐시 저장/읽기 모두에 대응합니다. 인수가 하나일 경우 키를 사용해 캐시로부터 값을 반환합니다. -블록을 인수로 넘기면 해당 키의 캐시가 없는 경우, 블록을 실행하고 결과를 캐싱한 뒤 그 값을 반환합니다. +저레벨 캐시의 가장 효과적인 구현 방법은 `Rails.cache.fetch` 메소드를 사용하는 +것입니다. 이 메소드는 캐시 저장/읽기 모두에 대응합니다. 인수가 하나일 경우 +키를 사용해 캐시로부터 값을 반환합니다. 블록을 인수로 넘기면 해당 키의 캐시가 +없는 경우, 블록을 실행합니다. 그리고 블록의 실행 결과를 주어진 키에 저장합니다. 해당하는 키의 캐시가 있는 경우, 블록은 실행되지 않습니다. -다음 예시를 보죠. 애플리케이션에 `Product` 모델이 있고, 여러 웹사이트의 제품 가격을 검색하는 인스턴스 -메소드가 구현되어 있다고 합시다. 저레벨 캐시를 사용하는 경우 이 메소드로부터 완벽한 데이터를 반환할 수 -있습니다. +다음 예시를 보죠. 애플리케이션에 `Product` 모델이 있고, 여러 웹사이트의 제품 +가격을 검색하는 인스턴스 메소드가 구현되어 있다고 합시다. 저레벨 캐시를 +사용하는 경우 이 메소드로부터 완벽한 데이터를 반환할 수 있습니다. ```ruby class Product < ApplicationRecord @@ -357,7 +362,7 @@ initializer에 `:size` 옵션을 지정합니다(기본값은 32MB). 캐시가 config.cache_store = :memory_store, { size: 64.megabytes } ``` -레일스 서버 프로세스를 복수 실행한다면(mongrel_cluster나 Phusion Passenger를 사용하는 경우), +레일스 서버 프로세스를 복수 실행한다면(Phusion Passenger나 puma 클러스터를 사용하는 경우), 캐시 데이터는 프로세스 간에 공유되지 않습니다. 이 캐시 저장소는 대규모로 배포되는 애플리케이션에는 적당하지 않습니다. 단, 작은 규모의 트래픽이 적은 사이트에서 서버 프로세스를 몇 개 정도만 사용한다면 문제 없이 동작합니다. 물론 개발 환경이나 테스트 환경에서도 동작합니다. @@ -495,10 +500,35 @@ class ProductsController < ApplicationController end ``` +때때로 캐시된 응답이 필요할 때가 있습니다. 예를 들어 정적인 페이지는 유효기간이 +필요하지 않습니다. 이를 위해서 `http_cache_forever` 헬퍼를 사용하여 프록시가 +이를 무기한으로 캐싱하도록 만들 수 있습니다. + +캐싱된 응답은 기본으로 공개되지 않으며, 사용자의 웹 브라우저에서만 캐시가 +유지됩니다. 프록시가 응답을 캐싱하길 원한다면 `public: true`를 통해 모든 +사용자에게 캐시된 응답을 넘겨주세요. + +이 헬퍼를 사용하면 `last_modified` 헤더가 `Time.new(2011, 1, 1).utc`로, +`expires` 헤더가 100년으로 설정됩니다. + +WARNING: 브라우저 캐시를 강제로 무효화하지 않는 이상 이 캐시는 사라지지 않기 때문에 이 메소드는 주의해서 사용해주세요. + +```ruby +class HomeController < ApplicationController + def index + http_cache_forever(public: true) do + render + end + end +end +``` + ### 강한 ETag와 약한 ETag -레일스에서는 기본으로 약한 ETag를 사용합니다. 약한 ETag에서는 응답의 본문이 미묘하게 다른 경우에도 같은 ETag를 부여하므로, -사실상 같은 응답인 것처럼 다룹니다. 응답 본문의 극히 일부가 변경된 경우, 페이지의 재생성을 피하고 싶은 경우에 편리합니다. +레일스에서는 기본으로 약한 ETag를 사용합니다. 약한 ETag에서는 응답의 본문이 +미묘하게 다른 경우에도 같은 ETag를 부여하므로, 사실상 같은 응답인 것처럼 +다룹니다. 응답 본문의 극히 일부가 변경된 경우, 페이지의 재생성을 피하고 싶은 +경우에 편리합니다. 약한 Etag에는 `W/`가 앞에 추가되며, 이를 통해 강한 ETag와 구별할 수 있습니다. @@ -507,9 +537,10 @@ end "618bbc92e2d35ea1945008b42799b0e7" → 강한 ETag ``` -강한 ETag는 약한 ETag와는 다르게 바이트 레벨에서 응답이 완전히 일치할 것을 요구합니다. -큰 영상이나 PDF 파일 내부에서 Range 요청을 하는 경우에 편리합니다. Akamai 등 일부 CDN에서는 -강한 ETag만을 지원하고 있습니다. 강한 ETag가 필요한 경우에는 다음과 같이 설정해주세요. +강한 ETag는 약한 ETag와는 다르게 바이트 레벨에서 응답이 완전히 일치할 것을 +요구합니다. 큰 영상이나 PDF 파일 내부에서 Range 요청을 하는 경우에 편리합니다. +Akamai 등 일부 CDN에서는 강한 ETag만을 지원하고 있습니다. 강한 ETag가 필요한 +경우에는 다음과 같이 설정해주세요. ```ruby class ProductsController < ApplicationController diff --git a/guides/source/ko/command_line.md b/guides/source/ko/command_line.md index 29bee6e1447a6..141b3d8b1ac74 100644 --- a/guides/source/ko/command_line.md +++ b/guides/source/ko/command_line.md @@ -37,7 +37,7 @@ INFO: 아직 Rails를 설치하지 않은 경우에는 `gem install rails`를 ```bash $ rails new commandsapp create - create README.rdoc + create README.md create Rakefile create config.ru create .gitignore @@ -208,7 +208,7 @@ Description: Create rails files for model generator. ``` -NOTE: 사용가능한 필드 타입(field types)에 대해서는 [API 문서](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_column)에 있는 `SchemaStatements` 모듈의 add_column 메소드를 참조해주세요. +NOTE: 사용가능한 필드 타입(field types)에 대해서는 [API 문서](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_column)에 있는 `SchemaStatements` 모듈의 add_column 메소드를 참조해주세요. `index` 파라미터는 컬럼에 대응하는 인덱스를 생성합니다. 여기에서는 직접 모델을 만드는 대신에(모델을 생성하는 방법은 나중에 설명하겠습니다), scaffold를 생성해봅시다. Rails에서의 **scaffold**란 모델, 모델을 위한 마이그레이션, 모델을 조작하기 위한 컨트롤러, 모델을 조작, 표시하기 위한 뷰, 이 모두를 위한 테스트 코드를 포함한 것을 가리킵니다. @@ -443,7 +443,7 @@ Database schema version 20110805173523 ### `db` -Rake의 `db:`라는 네임스페이스에 속해있는 태스크 중에서 가장 많이 사용되는 것은 `migrate`와 `create`입니다. 마이그레이션에 대한 태스크(`up`, `down`, `redo`, `reset`)은 모두 한번씩 실험해보는 것을 추천합니다. `bin/rails db:version`을 사용하면 데이터베이스의 버전을 알 수 있으므로 문제가 생겼을 때에 도움이 됩니다. +`db:`라는 bin/rails의 네임스페이스에 속해있는 태스크 중에서 가장 많이 사용되는 것은 `migrate`와 `create`입니다. 마이그레이션에 대한 태스크(`up`, `down`, `redo`, `reset`)은 모두 한번씩 실험해보는 것을 추천합니다. `bin/rails db:version`을 사용하면 데이터베이스의 버전을 알 수 있으므로 문제가 생겼을 때에 도움이 됩니다. 마이그레이션에 대해서는 [마이그레이션](active_record_migrations.html)에서 좀 더 자세하게 다루고 있습니다. @@ -492,7 +492,7 @@ app/models/article.rb: NOTE: 특정 어노테이션만을 출력할 때나, 독자적인 어노테이션을 출력하는 경우에는 FIXME나 BUG같은 각 어노테이션의 이름은 출력되지 않습니다. -`bin/rails notes` 태스크는 기본으로 `app`, `config`, `lib`, `bin`, `test` 폴더를 대상으로 합니다. 다른 폴더도 탐색하고 싶은 경우에는 `config.annotations.register_directories` 옵션을 사용하여 설정할 수 있습니다. +`rails notes` 태스크는 기본으로 `app`, `config`, `lib`, `bin`, `test` 폴더를 대상으로 합니다. 다른 폴더도 탐색하고 싶은 경우에는 `config.annotations.register_directories` 옵션을 사용하여 설정할 수 있습니다. ```ruby config.annotations.register_directories("spec", "vendor") @@ -512,7 +512,7 @@ spec/models/user_spec.rb: ### `routes` -`bin/rails routes`를 사용하면 정의되어있는 모든 라우팅을 출력할 수 있습니다. 이것은 라우팅에서 생긴 문제를 해결해야하거나, 애플리케이션의 라우팅 전체를 이해해야 할 때에 도움이 됩니다. +`rails routes`를 사용하면 정의되어있는 모든 라우팅을 출력할 수 있습니다. 이것은 라우팅에서 생긴 문제를 해결해야하거나, 애플리케이션의 라우팅 전체를 이해해야 할 때에 도움이 됩니다. ### `test` @@ -648,4 +648,4 @@ development: Rails는 선택한 데이터베이스(PostgreSQL)에 대응하도록 database.yml를 구성합니다. -NOTE: 소스 코드 관리 시스템에 대한 옵션을 사용할 때에는, 우선 애플리케이션의 폴더를 생성하고 소스 코드 관리 시스템을 초기화한 이후에 `rails new` 명령어를 실행해주세요. \ No newline at end of file +NOTE: 소스 코드 관리 시스템에 대한 옵션을 사용할 때에는, 우선 애플리케이션의 폴더를 생성하고 소스 코드 관리 시스템을 초기화한 이후에 `rails new` 명령어를 실행해주세요. diff --git a/guides/source/ko/configuring.md b/guides/source/ko/configuring.md index 295eee0b5f0d3..1e3b0d8683f52 100644 --- a/guides/source/ko/configuring.md +++ b/guides/source/ko/configuring.md @@ -59,21 +59,21 @@ Rails 전체에 걸친 설정을 하기 위해서는 `Rails::Railtie` 객체를 * `config.asset_host`은 애셋을 저장할 호스트를 지정합니다. 이 설정은 애셋을 저장할 장소가 CDN(Contents Delivery Network)일 때나, 다른 도메인을 사용하여 브라우저에서 동시 실행 제한을 피하고 싶은 경우에도 유용합니다. 이 메소드는 `config.action_controller.asset_host`를 줄인 것입니다. -* `config.autoload_once_paths`는 서버가 요청 받을 때마다 초기화되지 않는 상수들을 읽어오기 위한 경로들이 들어있는 배열을 받습니다. `config.cache_classes`가 false 인 경우에 유효하지 않으며, development 모드일 경우에는 기본적으로 false로 동작합니다. `config.cache_classes`가 true인 경우, 모든 `config.autoload_once_paths`는 단 한번만 자동으로 읽어와집니다. `config.autoload_once_paths`의 배열에 포함되는 요소는 다음에 설명할 `autoload_paths`에서도 완전히 동일한 방식으로 호출되어야 합니다. 기본값은 빈 배열입니다. +* `config.autoload_once_paths`는 서버가 요청 받을 때마다 초기화되지 않는 상수들을 읽어오기 위한 경로들이 들어있는 배열을 받습니다. `config.cache_classes`가 `false` 인 경우에 유효하지 않으며, development 모드일 경우에는 기본적으로 `false`로 동작합니다. 그렇지 않으면 모든 자동 로딩은 단 한번만 발생합니다. 기본값은 빈 배열입니다. -* `config.autoload_paths`는 Rails가 상수를 자동으로 읽어올 때에 사용할 경로를 포함하는 배열을 인수로 받습니다. `config.autoload_paths`의 기본 값은 `app` 에 존재하는 모든 폴더 입니다(역주: Rails3부터는 autoload_path의 기본값이 빈 배열로 설정되어 있습니다). +* `config.autoload_paths`는 Rails가 상수를 자동으로 읽어올 때에 사용할 경로를 포함하는 배열을 인수로 받습니다. `config.autoload_paths`의 기본 값은 `app` 에 존재하는 모든 폴더입니다. -* `config.cache_classes`는 애플리케이션의 클래스나 모듈을 요청할 때에 다시 읽어올지(=캐싱되어 있는지 아닌지)를 결정합니다. `config.cache_classes`의 기본값은 development 모드에서는 false이므로 코드의 변경사항이 바로 반영되며, test와 production 모드에서는 true로 설정되어, 동작하는 속도를 빠르게 만듭니다. +* `config.cache_classes`는 애플리케이션의 클래스나 모듈을 요청할 때에 다시 읽어올지(=캐싱되어 있는지 아닌지)를 결정합니다. `config.cache_classes`의 기본값은 개발 환경에서는 `false`이며, 테스트, 실제 환경에서는 `true`입니다. * `config.action_view.cache_template_loading`는 요청마다 뷰 템플릿을 다시 읽어올지 아닐지를 결정합니다. 기본값은 `config.cashe_classes`와 같습니다. * `config.beginning_of_week`는 애플리케이션에서의 일주일의 첫번째 날을 지정합니다. 인수로는 요일을 가리키는 올바른 심볼을 넘겨주세요(`:monday` 등). -* `config.cache_store`는 Rails에서의 캐시 처리에 사용할 캐시 저장소를 결정합니다. 지정 가능한 옵션으로는 `:memory_store`, `:file_store`, `:mem_cache_store`, `:null_store`중 하나이며, 또는 캐시 API를 구현하고 있는 객체입니다. `tmp/cache` 폴더가 존재하는 경우 기본값으로 `:file_store`로 설정되며, 그 이외의 경우에는 `:memory_store`로 설정됩니다. +* `config.cache_store`는 Rails에서의 캐시 처리에 사용할 캐시 저장소를 결정합니다. 지정 가능한 옵션으로는 `:memory_store`, `:file_store`, `:mem_cache_store`, `:null_store`중 하나이며, 또는 캐시 API를 구현하고 있는 객체입니다. 기본값은 `:file_store`입니다. -* `config.colorize_logging`는 출력할 로그 정보에 ANSI 색상 정보를 추가할지 말지를 결정합니다. 기본은 true입니다. +* `config.colorize_logging`는 출력할 로그 정보에 ANSI 색상 정보를 추가할지 말지를 결정합니다. 기본은 `true`입니다. -* `config.consider_all_requests_local`는 플래그입니다. 이 플래그가 true인 경우, 어떤 에러가 발생한 경우에도 상세한 디버그 정보가 HTTP 응답으로 출력되며, `/rails/info/properties`안의 애플리케이션 실행시의 컨텍스트가 `Rails::Info` 컨트롤러에 의해서 출력됩니다. 이 플래그는 development 모드와 test 모드에서는 true, production 모드에서는 false로 설정됩니다. 좀 더 세밀하게 제어하고 싶은 경우에는 이 플래그를 false로 지정한 후, 컨트롤러에서 `local_request?` 메소드를 구현하고, 에러가 발생했을 경우에 어떤 디버그 정보를 출력할지를 지정해주세요. +* `config.consider_all_requests_local`는 플래그입니다. 이 플래그가 `true`인 경우, 어떤 에러가 발생한 경우에도 상세한 디버그 정보가 HTTP 응답으로 출력되며, `/rails/info/properties`안의 애플리케이션 실행시의 컨텍스트가 `Rails::Info` 컨트롤러에 의해서 출력됩니다. 이 플래그는 development 모드와 test 모드에서는 `true`, production 모드에서는 `false`로 설정됩니다. 좀 더 세밀하게 제어하고 싶은 경우에는 이 플래그를 `false`로 지정한 후, 컨트롤러에서 `local_request?` 메소드를 구현하고, 에러가 발생했을 경우에 어떤 디버그 정보를 출력할지를 지정해주세요. * `config.console`를 사용하면 콘솔에서 `rails console`를 실행했을 때에 사용될 클래스를 커스터마이즈 할 수 있습니다. 이 메소드는 `console` 블럭과 함께 사용할 때가 편리합니다. @@ -86,79 +86,98 @@ Rails 전체에 걸친 설정을 하기 위해서는 `Rails::Railtie` 객체를 end ``` -* `config.dependency_loading`를 false로 설정하면 상수의 자동 불러오기 설정이 꺼집니다. 이 옵션이 유효한 경우는 `config.cache_classes`가 true인 경우 뿐입니다(`config.cache_classes`는 production 모드에서 기본으로 true입니다). `config.threadsafe!`를 사용하면 이 플래그는 false가 됩니다. +* `config.eager_load`를 `true`로 설정하면 `config.eager_load_namespaces`에 등록되어 있는 사전에 불러오기로 정의되어 있는 네임스페이스를 모두 불러 옵니다. 여기에는 애플리케이션, 엔진, Rails 프레임워크를 포함하는 모든 등록된 네임스페이스가 포함됩니다. -* `config.eager_load`를 true로 설정하면 `config.eager_load_namespaces`에 등록되어 있는 사전에 불러오기로 정의되어 있는 네임스페이스를 모두 불러 옵니다. 여기에는 애플리케이션, 엔진, Rails 프레임워크를 포함하는 모든 등록된 네임스페이스가 포함됩니다. - -* `config.eager_load_namespaces`를 사용하여 등록한 이름은 `config.eager_load`가 true일 때에 불러와집니다. 등록된 네임스페이스는 반드시 `eager_load!` 메소드에 반응해야합니다. +* `config.eager_load_namespaces`를 사용하여 등록한 이름은 `config.eager_load`가 `true`일 때에 불러와집니다. 등록된 네임스페이스는 반드시 `eager_load!` 메소드에 반응해야합니다. * `config.eager_load_paths`는 경로의 배열을 인수로 받습니다. Rails는 cache_classes가 활성화 되어 있을 경우에 이 경로들을 미리 읽어오게(eager load)됩니다. 기본값으로 애플리케이션의 app 폴더에 존재하는 모든 폴더들이 여기에 포함됩니다. +* `config.enable_dependency_loading`: 참일때, 애플리케이션이 eager load를 사용하고 `config.cache_classes`이 참이라 하더라도 자동로딩을 활성화합니다. 기본값은 `false`입니다. + * `config.encoding`은 애플리케이션 전체에서 적용할 인코딩을 지정합니다. 기본 값은 UTF-8입니다. * `config.exceptions_app`는 예외가 발생한 경우에 ShowException 미들웨어에 의해서 호출되는 애플리케이션 예외를 지정합니다. 기본값은 `ActionDispatch::PublicExceptions.new(Rails.public_path)`입니다. -* `config.file_watcher`는 `config.reload_classes_only_on_change`가 true인 경우에 파일 시스템 상에서 파일 갱신이 있는지를 확인할 때 사용할 클래스를 지정합니다. 이 클래스는 `ActiveSupport::FileUpdateChecker` API에 따를 필요가 있습니다. +* `config.debug_exception_response_format`은 개발 모드에서 에러가 발생했을 때 응답에서 사용할 양식을 지정합니다. 기본값은 API 전용일때 `:api`, 그 이외에는 `:default`입니다. + +* `config.file_watcher`는 `config.reload_classes_only_on_change`가 `true`인 경우에 파일 시스템 상에서 파일 갱신이 있는지를 확인할 때 사용할 클래스를 지정합니다. Rails는 `ActiveSupport::FileUpdateChecker`를 기본 값으로 사용하며, 그리고 `ActiveSupport::EventedFileUpdateChecker`([listen](https://github.com/guard/listen) 젬에 의존합니다)도 기본으로 제공합니다. 별도의 클래스를 사용하는 경우에는 `ActiveSupport::FileUpdateChecker`의 API에 따를 필요가 있습니다. + +* `config.filter_parameters`는 비밀번호나 신용카드번호 등 로그에 출력하고 싶지 않은 파라미터 값을 필터링으로 제외하기 위해서 사용합니다. Rails는 기본으로 비밀번호를 제외시키기 위해 `config/initializers/filter_parameter_logging.rb`에 `Rails.application.config.filter_parameters += [:password]`를 추가합니다. 파라미터 필터는 부분 일치 정규 표현식을 사용합니다. -* `config.filter_parameters`는 비밀번호나 신용카드번호 등, 로그에 출력하고 싶지 않은 파라미터 값을 필터링으로 제외하기 위해서 사용합니다. 비밀번호를 제외하는 애플리케이션 필터를 추가하기 위해서는 `config/initializers/filter_parameter_logging.rb`에 `config.filter_parameters+=[:password]`를 추가합니다. +* `config.force_ssl`는 `ActionDispatch::SSL` 미들웨어를 사용해서 모든 요청을 HTTPS 프로토콜로 처리하도록 하며, `config.action_mailer.default_url_options`의 값을 `{ protocol: 'https' }`로 만듭니다. 이는 `config.ssl_options`를 통해 변경할 수 있습니다. 자세한 설명은 [ActionDispatch::SSL 문서](http://edgeapi.rubyonrails.org/classes/ActionDispatch/SSL.html)를 참고하세요. -* `config.force_ssl`는 `ActionDispatch::SSL` 미들웨어를 사용해서 모든 요청을 HTTPS 프로토콜로 처리하도록 강제합니다. +* `config.log_formatter`는 Rails 로거의 형식을 정의합니다. 이 옵션의 기본값은 `ActiveSupport::Logger::SimpleFormatter`의 인스턴스입니다. `config.logger`를 따로 설정한다면, `ActiveSupport::TaggedLogging`로 감싸지기 전에 로거에 포매터를 직접 넘겨주어야 합니다. Rails는 이 작업은 대신해주지 않습니다. -* `config.log_formatter`는 Rails 로거의 형식을 정의합니다. 이 옵션의 기본값은 `ActiveSupport::Logger::SimpleFormatter`의 인스턴스를 사용합니다. 단, production 모드에서만 `Logger::Formatter`를 기본으로 사용합니다. +* `config.log_level`은 Rails에서 로그 출력을 얼마나 자세하게 내보낼지를 지정합니다. 기본값은 `:debug`입니다. 사용 가능한 로그 레벨로는 `:debug`, +`:info`, `:warn`, `:error`, `:fatal`, `:unknown`이 있습니다. -* `config.log_level`은 Rails에서 로그 출력을 얼마나 자세하게 내보낼지를 지정합니다. 기본값은 `:debug`로 지정되어 있으며, production 모드에서만 기본값으로 `:info`를 사용합니다. +* `config.log_tags`는 `request` 객체가 응답하는 메소드의 목록을 인수로 받습니다. 이것은 로그에 디버깅 정보를 태그로 붙일때 편리합니다. 예를 들자면, 서브도메인이나 요청 id를 지정하여 실제 환경에서 디버깅할 때 유용합니다. -* `config.log_tags`는 `request` 객체가 응답하는 메소드의 목록을 인수로 받습니다. 이것은 로그에 디버깅 정보를 태그로 붙일때 편리합니다. 예를 들자면, 서브도메인이나 요청 id를 지정하여 production 모드인 애플리케이션을 디버깅할 때 유용합니다. +* `config.logger`는 `Rails.logger`로, 그리고 `ActiveRecord::Base.logger`와 같은 Rails 로깅에서 사용될 로거를 지정합니다. 기본값은 `log/`폴더에 로그를 출력하는 `ActiveSupport::Logger`의 인스턴스를 감싸고 있는 `ActiveSupport::TaggedLogging`의 인스턴스입니다. 별도의 로거를 사용할 수도 있으며, 이 경우에는 모든 기능을 사용하려면 다음의 가이드라인을지켜주세요. + * 포매터를 지원하려면 `config.log_formatter`에 포매터를 직접 넘겨야합니다. + * 태깅된 로그를 지원하려면 로그 인스턴스는 ActiveSupport::TaggedLogging`로 감싸져 있어야 합니다. + * 출력 무시하기를 지원하려면 로거는 반드시 `LoggerSilence`와 `ActiveSupport::LoggerThreadSafeLevel` 모듈을 포함해야 합니다. `ActiveSupport::Logger` 클래스는 이미 이 모듈들을 포함하고 있습니다. -* `config.logger`는 사용할 로거를 지정합니다. 지정되는 로거는 Log4r 또는 Ruby의 기본 `Logger` 클래스의 인스턴스와 동일한 인터페이스를 가지고 있어야 합니다. 기본값으로는 `ActiveSupport::Logger`의 인스턴스가 지정되어 있습니다. + ```ruby + class MyLogger < ::Logger + include ActiveSupport::LoggerThreadSafeLevel + include LoggerSilence + end + mylogger = MyLogger.new(STDOUT) + mylogger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(mylogger) + ``` + * `config.middleware`는 애플리케이션에서 사용할 미들웨어를 커스터마이즈할 수 있습니다. 자세한 설명은 [미들웨어 설정하기](#미들웨어-설정하기)를 참조해주세요. -* `config.reload_classes_only_on_change`는 감시하고 있는 파일이 변경되었을 경우에만 클래스를 다시 불러올지 아닐지를 지정합니다. 기본으로는 autoload_path에 지정되어있는 모든 파일이 감시 대상이며, 기본값으로는 true로 설정되어 있습니다. `config.cache_classes`가 on인 경우에는 이 옵션이 무시됩니다. +* `config.reload_classes_only_on_change`는 감시하고 있는 파일이 변경되었을 경우에만 클래스를 다시 불러올지 아닐지를 지정합니다. 기본으로는 autoload_path에 지정되어있는 모든 파일이 감시 대상이며, 기본값으로는 `true`로 설정되어 있습니다. `config.cache_classes`가 `true`인 경우에는 이 옵션이 무시됩니다. -`secrets.secret_key_base` 메소드는 변조 방지를 위해서 애플리케이션의 세션에 기존의 비밀키와 비교하기 위한 키를 지정할 때에 사용합니다. 애플리케이션은 `secrets.secret_key_base`를 사용하여 `config/secrets.yml` 등에 저장되어 있는 키를 사용해 초기화합니다. +* `secrets.secret_key_base`는 변조 방지를 위해서 애플리케이션의 세션에 기존의 비밀키와 비교하기 위한 키를 지정할 때에 사용합니다. 애플리케이션은 `secrets.secret_key_base`를 사용하여 `config/secrets.yml` 등에 저장되어 있는 키를 사용해 초기화합니다. -* `config.serve_static_assets`는 정적인 애셋을 다룰지 아닐지를 지정합니다. 기본값으로는 true로 지정되어 있습니다만, production 환경에서는 애플리케이션을 실행하는 Nginx나 Apache등의 서버가 정적인 애셋을 다룰 필요가 있으므로 비활성화됩니다. 기본의 설정과는 다르게 WEBrick을 사용해서 애플리케이션을 production 모드로 실행하거나(절대로 이렇게 사용하지 말아 주세요) 테스트를 하는 경우에는 true로 설정됩니다. 그렇지 않으면 페이지 캐시가 유효하지 않게 되며 public 폴더에 저장되어 있는 정적인 애셋 파일들에 대한 요청은 매번 Rails 애플리케이션을 통하여 처리됩니다. +* `config.public_file_server.enabled`는 public 폴더에서 정적인 파일을 제공할지 여부를 지정합니다. 이 옵션의 기본값은 `true`이지만, 실제 환경에서는 서버 소프트웨어(e.g. NGINX나 Apache)가 정적인 애셋을 대신 다루는 경우가 많기 때문에 `false`로 설정됩니다. 애플리케이션을 WEBrick를 사용해서 실행(이는 권장되지 않습니다)하거나 테스트를 하는 경우에는 이 옵션을 `true`로 지정해야 합니다. 그렇지 않으면 페이지 캐싱을 사용할 수 없으며, public 폴더 밑에 있는 파일들의 요청을 처리할 수 없게 됩니다. -* `config.session_store`는 일반적으로 `config/initializers/session_store.rb`에서 정의되어 있으며, 세션을 저장할 클래스를 지정합니다. 지정 가능한 값은 `:cookie_store`(기본값), `:mem_cache_store`, `:disabled`입니다. `:disabled`를 지정하면, Rails에서 세션을 사용할 수 없게 됩니다. 커스텀 세션 저장소를 지정할 수도 있습니다. +* `config.session_store`는 세션을 저장할 클래스를 지정합니다. 지정 가능한 값은 `:cookie_store`(기본값), `:mem_cache_store`, `:disabled`입니다. `:disabled`를 지정하면, Rails에서 세션을 사용할 수 없게 됩니다. 쿠키 저장소에서 세션 키의 기본값은 애플리케이션의 이름입니다. 커스텀 세션 저장소를 지정할 수도 있습니다. ```ruby config.session_store :my_custom_store ``` -커스텀 스토어는 `ActionDispatch::Session::MyCustomStore`로 정의할 필요가 있습니다. + 이 저장소는 `ActionDispatch::Session::MyCustomStore`로 정의됩니다. * `config.time_zone`은 애플리케이션의 기본 시간대를 설정하여 Active Record에서 인식할 수 있도록 해줍니다. ### 애셋 설정하기 -* `config.assets.enabled`는 애셋 파이프라인을 사용할지 아닐지를 지정합니다. 기본값은 true입니다. +* `config.assets.enabled`는 애셋 파이프라인을 사용할지 아닐지를 지정합니다. 기본값은 `true`입니다. * `config.assets.raise_runtime_errors`를 `true`로 지정하면, 런타임 에러 체크가 활성화됩니다. 이 옵션은 `production` 환경에서 사용하면 배포시에 생각치않은 동작을 발생시킬 가능성이 있으므로 development 환경(`config/environments/development.rb`)에서만 사용하기를 추천합니다. -* `config.assets.compress`는 컴파일된 애셋을 압축할지 아닐지를 지정하는 플래그입니다. `config/environments/production.rb`에서는 명시적으로 true로 지정되어 있습니다. - * `config.assets.css_compressor`는 CSS 압축시에 사용할 프로그램을 지정합니다. 이 옵션은 기본으로 `sass-rails`를 사용하도록 지정되어 있습니다. `:yui`라는 일견 특이해보이는 옵션도 지정할 수 있으며, 이 옵션은 `yui-compressor` gem을 의미합니다. * `config.assets.js_compressor`는 JavaScript 압축을 수행할 프로그램을 지정합니다. 지정 가능한 값으로는 `:closure`, `:uglifier`, `:yui`입니다. 각각 `closure-compiler`, `uglifier`, `yui-compressor` gem에 대응합니다. +* `config.assets.gzip`는 gzip으로 압축된 애셋을 압축되지 않은 애셋과 함께 제공할지 여부를 지정합니다. 기본값은 `true`입니다. + * `config.assets.paths`에는 애셋 검색 시에 사용할 경로를 지정합니다. 이 설정 옵션을 경로에 추가하면, 애셋을 검색할때에 찾을 경로 목록에 추가됩니다. * `config.assets.precompile`은 `application.css`와 `application.js` 이외에 추가하고 싶은 애셋이 있는 경우에 지정합니다. 이것들은 `bin/rails assets:precompile`을 실행할 때에 함께 컴파일 됩니다. -* `config.assets.prefix`는 애셋을 저장할 폴더를 지정합니다. 기본은 `/assets`입니다. +* `config.assets.unknown_asset_fallback`는 sprockets-rails의 버전이 3.2.0 이상일 경우 애셋 파이프라인이 필요한 애셋을 발견하지 못했을 때 어떤 동작을 할지를 지정합니다. 기본값은 `true`입니다. -* `config.assets.digest`는 애셋 이름을 이용하는 MD5 핑거프린트를 사용할지 말지를 지정합니다. `production.rb`에서는 기본으로 `true`로 설정되어 있습니다. +* `config.assets.prefix`는 애셋을 저장할 폴더를 지정합니다. 기본은 `/assets`입니다. -* `config.assets.debug`는 애셋의 연결 및 압축을 무효화할지를 지정합니다. `development.rb`에서는 기본으로 `true`로 지정됩니다. +* `config.assets.manifest`는 애셋 처리의 manifest 파일의 전체 경로를 지정합니다. 기본값은 public 밑의 `config.assets.prefix` 폴더에 `manifest-.json`입니다. -* `config.assets.cache_store`는 Sprockets에서 사용하는 캐시 저장소를 정의합니다. 기본으로는 Rails의 파일 저장소를 사용합니다. +* `config.assets.digest`는 애셋 이름을 이용하는 MD5 핑거프린트를 사용할지 말지를 지정합니다. 기본값은 `true`입니다. -* `config.assets.version`는 MD5 해시 생성에 사용되는 옵션 문자열입니다. 이 값을 변경하면 모든 애셋 파일이 강제적으로 다시 컴파일 됩니다. +* `config.assets.debug`는 애셋의 연결 및 압축을 무효화할지를 지정합니다. `development.rb`에서는 기본값은 `true`입니다. * `config.assets.compile`는 production 환경에서 동적인 Sprockets 컴파일을 할지 말지를 true/false로 지정합니다. -* `config.assets.logger`는 로거를 인수로 받습니다. 이 로거는 Log4의 인터페이스나 Ruby의 `Logger` 클래스의 인터페이스를 따라야 합니다. 기본으로 `config.logger`와 동일한 설정이 사용됩니다. `config.assets.logger`를 false로 사용하면 애셋의 로그 출력을 하지 않게 됩니다. +* `config.assets.logger`는 로거를 인수로 받습니다. 이 로거는 Log4r의 인터페이스나 Ruby의 `Logger` 클래스의 인터페이스를 따라야 합니다. 기본으로 `config.logger`와 동일한 설정이 사용됩니다. `config.assets.logger`를 `false`로 사용하면 애셋의 로그 출력을 하지 않게 됩니다. + +* `config.assets.quiet`는 애셋 요청에 대한 로깅을 비활성화합니다. `development.rb`에서 `true`로 지정되어 있습니다. ### 제너레이터 설정하기 @@ -181,19 +200,21 @@ end * `javascript_engine`은 애셋 생성시에(coffee 등에서) 사용할 엔진을 지정합니다. 기본값은 `nil`입니다. * `orm`은 사용할 ORM(Object Relational Mapping)을 지정합니다. 기본값은 `false`이며, 이 경우 Active Record를 사용합니다. * `resource_controller`는 `rails generate resource`를 실행했을 때에 어떤 제너레이터를 사용하여 컨트롤러를 생성할지 지정합니다. 기본값은 `:controller`입니다. +* `resource_route`는 리소스에 대한 라우팅을 자동으로 생성할지를 지정합니다. 기본값은 `true`입니다. * `scaffold_controller`는 `resource_controller`와 동일하지 않습니다. `scaffold_controller`는 _scaffold_시에 어떤 제너레이터를 사용하여 컨트롤러를 생성할 지(`rails generate scaffold`를 실행했을 때)를 지정합니다. 기본값은 `:scaffold_controller`입니다. * `stylesheets`는 제너레이터에서 스타일시트 생성시에 훅을 사용할지 아닐지를 지정합니다. 이 설정은 `scaffold` 제너레이터 실행시에 사용됩니다만, 다른 제너레이터를 실행할 때에도 사용됩니다. 기본값은 `true`입니다. * `stylesheet_engine`는 애셋 생성시에 사용할, sass같은 스타일시트 엔진을 지정합니다. 기본값은 `:css`입니다. -* `test_framework`는 사용할 테스트용 프레임워크를 지정합니다. 기본값은 `false`이며, 이 경우 Test::Unit이 사용됩니다. +* `scaffold_stylesheet`는 scaffold시에 `scaffold.css`를 생성할지 지정합니다. 기본값은 `true`입니다. +* `test_framework`는 사용할 테스트용 프레임워크를 지정합니다. 기본값은 `false`이며, 이 경우 Minitest가 사용됩니다. * `template_engine`은 뷰 템플릿 엔진(ERB나 Haml 등)을 지정합니다. 기본값은 `:erb`입니다. ### 미들웨어 설정하기 어떤 Rails 애플리케이션이든 그 뒤에는 몇개의 표준적인 미들웨어가 동작하고 있습니다. development 환경에서는 다음과 같은 순서대로 미들웨어를 사용합니다. -* `ActionDispatch::SSL`는 모든 요청에게 HTTPS 프로토콜을 사용할 것을 강요합니다. 이것은 `config.force_ssl`를 `true`로 설정했을 경우에만 유효합니다. 넘길 옵션 값들은 `config.ssl_options`에서 설정할 수 있습니다. -* `ActionDispatch::Static`는 정적 애셋을 처리합니다. `config.serve_static_assets`를 `false`로 하면 사용하지 않습니다. -* `Rack::Lock`는 애플리케이션을 뮤텍스로 감싸서 1번에 하나의 스레드만 호출되도록 만듭니다. 이 미들웨어는 `config.cache_classes`가 `false`로 설정되어 있을 경우에만 유효합니다. +* `ActionDispatch::SSL`는 모든 요청에게 HTTPS 프로토콜을 강제로 적용합니다. 이것은 `config.force_ssl`를 `true`로 설정했을 경우에만 유효합니다. 넘길 옵션 값들은 `config.ssl_options`에서 설정할 수 있습니다. +* `ActionDispatch::Static`는 정적 애셋을 처리합니다. `config.public_file_server.enabled`이 `false`라면 사용되지 않습니다. `index`라는 이름이 아닌 디렉토리 인덱스 파일을 제공하고 싶다면 `config.public_file_server.index_name`를 지정해주세요. 예를 들어 폴더 요청에 대해서 `index.html` 대신에 `main.html` 파일을 사용하고 싶다면, `config.public_file_server.index_name`를 `"main"`라고 지정하면 됩니다. +* `ActionDispatch::Executor`는 스레드 안전한 코드 리로딩을 허용합니다. `config.allow_concurrency`가 `false`라면 비활성화되며, `Rack::Lock`을 로드하게 됩니다. `Rack::Lock`는 애플리케이션을 뮤택스로 감싸서 싱글 스레드에서만 호출되도록 만듭니다. * `ActiveSupport::Cache::Strategy::LocalCache`는 기본적인 메모리 백업 방식의 캐시로 기능합니다. 이 캐시는 스레드간에 안전하지 않으며, 단일 스레드용의 일시적인 메모리 캐시로서만 동작하도록 설계되었다는 점을 주의해주세요. * `Rack::Runtime`는 `X-Runtime` 헤더를 설정합니다. 이 헤더에는 요청을 처리하는데 얼마나 시간이 걸렸는지(초)가 포함됩니다. * `Rails::Rack::Logger`는 요청 처리가 시작되었음을 로그에 알립니다. 요청이 끝나면 모든 로그를 파일에 출력합니다. @@ -202,14 +223,11 @@ end * `ActionDispatch::RemoteIp`는 IP 스푸핑 공격이 있었던 것은 아닌지 확인하고, 요청 해더에서 올바른 `client_ip`를 가져옵니다. 이 설정은 `config.action_dispatch.ip_spoofing_check` 옵션과 `config.action_dispatch.trusted_proxies` 옵션에서 변경 가능합니다. * `Rack::Sendfile`은 body가 하나의 파일로부터 생성된 응답을 가로채서 서버에 설정되어 있는 X-Sendfile 헤더로 변경하고 전송합니다. 이 동작은 `config.action_dispatch.x_sendfile_header`에서 변경가능합니다. * `ActionDispatch::Callbacks`은 요청에 응답하기 전에 정의되어 있는 콜백을 실행합니다. -* `ActiveRecord::ConnectionAdapters::ConnectionManagement`는 `rack.test`의 값이 `true`인 경우가 아니라면, 요청마다 살아있는 커넥션을 종료시킵니다. -* `ActiveRecord::QueryCache`는 요청에 따라 생성된 모든 SELECT 쿼리를 캐싱합니다. INSERT 또는 UPDATE가 발생하면 캐시가 비워집니다. * `ActionDispatch::Cookies`는 요청에 대응하는 cookie를 저장합니다. * `ActionDispatch::Session::CookieStore`는 세션을 cookie에 저장하는 역할을 담당합니다. `config.action_controller.session_store`의 값이 변경되면 다른 미들웨어를 사용할 수 있습니다. 여기에 넘기는 옵션은 `config.action_controller.session_options`에서 변경할 수 있습니다. * `ActionDispatch::Flash`는 `flash` 값을 지정합니다. 이는 `config.action_controller.session_store`에 값이 설정되어 있을 때에만 유효합니다. -* `ActionDispatch::ParamsParser`는 요청으로부터 파라미터를 추출하여 `params`에 저장합니다. * `Rack::MethodOverride`는 `params[:_method]`가 설정되어 있는 경우에 메소드를 재정의합니다. 이는 HTTP에서 PATCH, PUT, DELETE 메소드를 덮어쓰기 위한 미들웨어입니다. -* `ActionDispatch::Head`는 HEAD 요청을 GET 요청으로 변환하여 HEAT 요청이 정상적으로 동작하도록 해줍니다. +* `Rack::Head`는 HEAD 요청을 GET 요청으로 변환하여 HEAT 요청이 정상적으로 동작하도록 해줍니다. `config.middleware.use` 메소드를 사용하면, 위에서 언급한 것 이외의 미들웨어를 추가할 수 있습니다. @@ -220,13 +238,19 @@ config.middleware.use Magical::Unicorns 이를 통해 `Magical::Unicorns` 미들웨어가 실행 스택의 가장 마지막에 추가됩니다. 특정 미들웨어의 앞에 다른 미들웨어를 추가하고 싶은 경우에는 `insert_before`를 사용하세요. ```ruby -config.middleware.insert_before ActionDispatch::Head, Magical::Unicorns +config.middleware.insert_before Rack::Head, Magical::Unicorns ``` -어떤 미들웨어의 뒤에 다른 미들웨어를 추가하고 싶은 경우에는 `insert_after`를 사용하세요. +또는 인덱스를 지정하여 특정 위치에 미들웨어를 추가할 수도 있습니다. 예를 들어, `Magical::Unicorns`를 미들웨어 스택의 맨 위에 추가하고 싶다면, 다음과 같이 지정하세요. ```ruby -config.middleware.insert_after ActionDispatch::Head, Magical::Unicorns +config.middleware.insert_before 0, Magical::Unicorns +``` + +`insert_after`를 사용하여 특정 미들웨어의 뒤에 추가할 수도 있습니다. + +```ruby +config.middleware.insert_after Rack::Head, Magical::Unicorns ``` 아예 다른 것으로 바꿀수도 있습니다. @@ -238,7 +262,7 @@ config.middleware.swap ActionController::Failsafe, Lifo::Failsafe 마찬가지로 미들웨어를 스택에서 제거할 수도 있습니다. ```ruby -config.middleware.delete "Rack::MethodOverride" +config.middleware.delete Rack::MethodOverride ``` ### i18n 설정하기 @@ -253,15 +277,37 @@ config.middleware.delete "Rack::MethodOverride" * `config.i18n.load_path`는 로케일 파일의 검색 경로를 지정합니다. 기본값은 `config/locales/*.{yml,rb}`입니다. +* `config.i18n.fallbacks`는 해당하는 번역이 존재하지 않을 때의 동작을 지정합니다. 3개의 예제를 살펴봅시다. + + * `true`를 설정하면 기본 로케일을 사용합니다. + + ```ruby + config.i18n.fallbacks = true + ``` + + * 아니면 사용할 로케일의 목록을 넘겨줄 수도 있습니다. + + ```ruby + config.i18n.fallbacks = [:tr, :en] + ``` + + * 마지막으로 로케일에 따라 다른 목록을 사용할 수도 있습니다. 예를 들어, `:az`와 `:de`일 때에는 `:tr`을, `:da`일 경우에는 `:en`을 사용해봅시다. + + ```ruby + config.i18n.fallbacks = { az: :tr, da: [:de, :en] } + # 또는 + config.i18n.fallbacks.map = { az: :tr, da: [:de, :en] } + ``` + ### Active Record 설정하기 `config.active_record`에는 많은 옵션이 포함되어 있습니다. * `config.active_record.logger`는 Log4r의 인터페이스 또는 Ruby Logger 클래스를 따르는 로거를 인수로 받습니다. 이 로거는 이후 생성되는 모든 새로운 데이터베이스 커넥션에서 사용됩니다. Active Record 모델 클래스 또는 모델 인스턴스에 대해서 `logger` 메소드를 호출하면 이 로거를 받아올 수 있습니다. 로그 출력을 무효화하기 위해서는 `nil`로 설정합니다. -* `config.active_record.primary_key_prefix_type`는 기본키 컬럼의 명명법을 변경할 때에 사용합니다. Rails에서는 기본키 컬럼의 이름의 기본값으로 `id`를 사용합니다(또한 `id`을 사용하고 싶은 경우에는 별도로 설정할 필요가 없습니다). `id` 이외로는 다음의 두가지를 선택할 수 있습니다. -** `:table_name`를 지정하면, 예를 들어, Customer 클래스의 기본키는 `customerid`가 됩니다. -** `:table_name_with_underscore`를 지정하면, 예를 들어, Customer 클래스의 기본키는 `customer_id`가 됩니다. +* `config.active_record.primary_key_prefix_type`는 기본키 컬럼의 명명법을 변경할 때에 사용합니다. Rails에서는 기본키 컬럼의 이름의 기본값으로 `id`를 사용합니다(또한 `id`을 사용하고 싶은 경우에는 별도로 설정할 필요가 없습니다). `id` 이외로는 다음의 두가지를 선택할 수 있습니다. + * `:table_name`를 지정하면, 예를 들어, Customer 클래스의 기본키는 `customerid`가 됩니다. + * `:table_name_with_underscore`를 지정하면, 예를 들어, Customer 클래스의 기본키는 `customer_id`가 됩니다. * `config.active_record.table_name_prefix`는 테이블 이름의 앞에 전역으로 추가할 문자열을 지정할 수 있습니다. 예를 들어, `northwest_`를 지정하면 Customer 클래스는 `northwest_customers` 테이블을 검색합니다. 기본값은 빈 문자열입니다. @@ -269,15 +315,17 @@ config.middleware.delete "Rack::MethodOverride" * `config.active_record.schema_migrations_table_name`는 스키마 마이그레이션 테이블의 이름으로 사용할 문자열을 지정할 수 있습니다. -* `config.active_record.pluralize_table_names`는 Rails가 찾는 데이터베이스 테이블 이름을 단수형으로 할지, 복수형으로 할지를 지정할 수 있습니다. true로 지정하면, Customer 클래스가 사용하는 테이블 이름은 복수형인 `customers`가 됩니다(기본값). false로 설정하면 Customer 클래스가 사용하는 테이블 이름은 단수형인 `customer`가 됩니다. +* `config.active_record.pluralize_table_names`는 Rails가 찾는 데이터베이스 테이블 이름을 단수형으로 할지, 복수형으로 할지를 지정할 수 있습니다. `true`로 지정하면, Customer 클래스가 사용하는 테이블 이름은 복수형인 `customers`가 됩니다(기본값). `false`로 설정하면 Customer 클래스가 사용하는 테이블 이름은 단수형인 `customer`가 됩니다. * `config.active_record.default_timezone`은 데이터베이스로부터 날짜/시각을 가져왔을때 시간대를 `Time.local`(`:local`을 지정했을 경우)와 `Time.utc`(`:utc`를 지정했을 경우)중 어느 것을 쓸지 지정합니다. 기본값은 `:utc`입니다. * `config.active_record.schema_format`은 데이터베이스 스키마를 파일로 내보낼 때에 사용할 형식을 지정합니다. 기본값은 `:ruby`로 데이터베이스에 의존하지 않고, 마이그레이션에 의존합니다. `:sql`로 지정하면 SQL문으로 내보냅니다만, 이 경우 잠재적으로 데이터베이스에 의존할 가능성이 있습니다. -* `config.active_record.timestamped_migrations`는 마이그레이션 파일의 이름에 시리얼 번호와 타임스탬프 중 어느것을 사용할지를 지정합니다. 기본값은 true로 타임스탬프가 사용됩니다. 개발자가 여러 명인 경우에는 타임스탬프를 추천합니다. +* `config.active_record.error_on_ignored_order`는 배치 쿼리를 실행하는 중에 쿼리의 정럴 순서를 무시하게 되었을 때 에러를 던질지 여부를 지정합니다. `true`일 경우 에러를 던지며, `false`일 경우에 경고를 출력합니다. 기본값은 `false`입니다. + +* `config.active_record.timestamped_migrations`는 마이그레이션 파일의 이름에 시리얼 번호와 타임스탬프 중 어느것을 사용할지를 지정합니다. 기본값은 `true`로 타임스탬프가 사용됩니다. 개발자가 여러 명인 경우에는 타임스탬프를 추천합니다. -* `config.active_record.lock_optimistically`는 Active Record에서 낙관적 잠금을 사용할지를 지정합니다. 기본값은 true(사용함)입니다. +* `config.active_record.lock_optimistically`는 Active Record에서 낙관적 잠금을 사용할지를 지정합니다. 기본값은 `true`입니다. * `config.active_record.cache_timestamp_format`는 캐시 키에 포함되는 타임스탬프 값의 형식을 지정합니다. 기본값은 `:number`입니다. @@ -287,11 +335,20 @@ config.middleware.delete "Rack::MethodOverride" * `config.active_record.maintain_test_schema`는 테스트 실행시에 Active Record가 테스트용 데이터베이스 스키마를 `db/schema.rb`(또는 `db/structure.sql`)에 기초해 최신 상태를 사용할지 아닐지를 지정합니다. 기본값은 `true`입니다. -* `config.active_record.dump_schema_after_migration`는 마이그레이션 실행시에 스키마 덤프(`db/schema.rb`또는 `db/structure.sql`)를 할지 안할지를 지정합니다. 이 옵션은 Rails가 생성하는 `config/environments/production.rb`에서는 false로 설정되어 있습니다. 이 옵션이 지정되어 있지 않은 경우에는 기본값으로 true를 사용합니다. +* `config.active_record.dump_schema_after_migration`는 마이그레이션 실행시에 스키마 덤프(`db/schema.rb`또는 `db/structure.sql`)를 할지 안할지를 지정합니다. 이 옵션은 Rails가 생성하는 `config/environments/production.rb`에서는 `false`로 설정되어 있습니다. 이 옵션이 지정되어 있지 않은 경우에는 기본값으로 `true`를 사용합니다. + +* `config.active_record.dump_schemas`는 db:structure:dump를 호출했을 때에 어떤 데이터 스키머를 덤프할지를 결정합니다. + `:schema_search_path`(기본값)은 schema_search_path에 지정된 모든 스키마를 덤프하며, `:all`는 schema_search_path나 쉼표로 구분된 스키마들의 문자열 무시하고 모든 스키마를 덤프합니다. + +* `config.active_record.belongs_to_required_by_default`는 `belongs_to` 관계의 존재 검증을 기본으로 수행할지 아닐지를 지정하는 boolean값입니다. + +* `config.active_record.warn_on_records_fetched_greater_than`은 쿼리 결과의 크기에 따른 경고를 설정할 수 있게 해줍니다. 쿼리의 결과로 반환된 레코드 셋이 지정된 기준점을 넘어서면 경고가 출력됩니다. 이는 메모리 문제를 야기시킬 수 있는 쿼리를 특정할 때에 사용될 수 있습니다. + +* `config.active_record.index_nested_attribute_errors`는 중첩된 has_many 관계에서 에러가 발생한 경우 에러와 함께 인덱스를 표시하도록 합니다. 기본값은 `false`입니다. MySQL 어댑터를 사용하면 아래의 옵션이 하나 추가됩니다. -* `ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans`는 Active Record가 MySQL 데이터베이스의 모든 `tinyint(1)` 형식의 컬럼을 기본으로 boolean으로 취급할지 아닐지를 지정합니다. 기본값은 true입니다. +* `ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans`는 Active Record가 MySQL 데이터베이스의 모든 `tinyint(1)` 형식의 컬럼을 기본으로 boolean으로 취급할지 아닐지를 지정합니다. 기본값은 `true`입니다. 스키마 덤퍼(Schema Dumper)는 아래의 옵션을 추가합니다. @@ -303,11 +360,11 @@ MySQL 어댑터를 사용하면 아래의 옵션이 하나 추가됩니다. * `config.action_controller.asset_host`는 애셋을 저장할 호스트를 지정합니다. 이것은 애셋을 호스팅하는 장소로, 애플리케이션 서버 대신 CDN(Content Delivery Networks)을 사용하는 경우에 편리합니다. -* `config.action_controller.perform_caching`은 애플리케이션에서 캐싱을 사용할지 안할지를 지정합니다. development 모드에서는 false, production 모드에서는 true로 설정합니다. +* `config.action_controller.perform_caching`은 애플리케이션에서 캐싱을 사용할지 안할지를 지정합니다. development 모드에서는 `false`, production 모드에서는 `true`로 설정합니다. * `config.action_controller.default_static_extension`은 캐시된 페이지에 부여할 확장자를 지정합니다. 기본값은 `.html`입니다. -* `config.action_controller.default_charset`은 모든 화면 출력에서 사용할 기본 문자셋을 지정합니다. 기본값은 "utf-8"입니다. +* `config.action_controller.include_all_helpers`는 모든 뷰 헬퍼를 어디서든 사용할 수 있도록 할지, 대응하는 컨트롤러에서만 사용할 수 있도록 할지를 지정합니다 만약 `false`라면 `UsersHelper`는 `UsersController`에서 랜더링하는 뷰에서만 사용할 수 있습니다. `true`라면 `UsersHelper`의 메소드는 어디에서든 사용할 수 있습니다. 모든 뷰 헬퍼는 각각의 컨트롤러에서만 사용가능한 것이 기본 동작(명시적으로 `true`나 `false`가 지정되지 않았을 경우)입니다. * `config.action_controller.logger`는 Log4r 인터페이스나 기본 Ruby Logger 클래스에 따르는 로거를 인수로 받습니다. 이 로거는 Action Controller로부터 정보를 내보내기 위해서 사용합니다. 로그 출력을 하지 않고싶은 경우에는 `nil`로 설정하세요. @@ -315,6 +372,10 @@ MySQL 어댑터를 사용하면 아래의 옵션이 하나 추가됩니다. * `config.action_controller.allow_forgery_protection`은 CSRF 보호 기능을 켤지를 지정합니다. test 모드의 기본값은 `false`이며, 그 이외에는 `true`로 설정됩니다. +* `config.action_controller.forgery_protection_origin_check`는 HTTP `Origin` 헤더를 추가로 확인할지를 지정합니다. + +* `config.action_controller.per_form_csrf_tokens`는 CSRF 토큰을 생성된 메소드/액션에서만 유효하게 만들지를 지정합니다. + * `config.action_controller.relative_url_root`는 [하위 폴더에 배포하기](configuring.html#하위-폴더에-배포하기-상대url경로-사용하기)를 할 것이라고 Rails에게 알리기 위해서 사용합니다. 기본값은 `ENV['RAILS_RELATIVE_URL_ROOT']`입니다. * `config.action_controller.permit_all_parameters`는 일괄 할당(Mass Assignment)되는 모든 파라미터를 허가하도록 합니다. 기본값은 `false`입니다. @@ -323,6 +384,21 @@ MySQL 어댑터를 사용하면 아래의 옵션이 하나 추가됩니다. * `config.action_controller.always_permitted_parameters`는 파라미터 필터링시에 사용할 화이트 리스트을 지정합니다. 기본값은 `['controller', 'action']`입니다. +* `config.action_controller.enable_fragment_cache_logging`는 조각 캐시 읽기과 쓰기에 대한 출력을 좀 더 자세하게 할 지를 결정합니다. + + ``` + Read fragment views/v1/2914079/v1/2914079/recordings/70182313-20160225015037000000/d0bdf2974e1ef6d31685c3b392ad0b74 (0.6ms) + Rendered messages/_message.html.erb in 1.2 ms [cache hit] + Write fragment views/v1/2914079/v1/2914079/recordings/70182313-20160225015037000000/3b4e249ac9d168c617e32e84b99218b5 (1.1ms) + Rendered recordings/threads/_thread.html.erb in 1.5 ms [cache miss] + ``` + + 기본값은 `false`이며 다음과 같은 출력을 내보냅니다. + + ``` + Rendered messages/_message.html.erb in 1.2 ms [cache hit] + Rendered recordings/threads/_thread.html.erb in 1.5 ms [cache miss] + ``` ### Action Dispatch 설정하기 * `config.action_dispatch.session_store`는 세션 데이터 저장소의 이름을 지정합니다. 기본값은 `:cookie_store`입니다. 그 외에도 `:active_record_store`, `:mem_cache_store`, 또는 커스텀 클래스 이름을 사용할 수 있습니다. @@ -337,8 +413,14 @@ MySQL 어댑터를 사용하면 아래의 옵션이 하나 추가됩니다. } ``` +* `config.action_dispatch.default_charset`는 모든 랜더링에서 사용할 기본 문자열 형식을 지정합니다. 기본값은 `nil`입니다. + * `config.action_dispatch.tld_length`는 애플리케이션에서 사용하는 탑 레벨 도메인(TLD)의 길이를 지정합니다. 기본값은 `1`입니다. +* `config.action_dispatch.ignore_accept_header`는 요청에 포함된 accept 헤더를 무시할지 여부를 지정합니다. 기본값은 `false`입니다. + +* `config.action_dispatch.x_sendfile_header`는 서버 전용 X-Sendfile 헤더를 지정합니다. 이는 서버로부터의 파일 전송을 빠르게 만들 때 유용합니다. 예를 들어 Apache를 위해서는 'X-Sendfile'을 지정할 수 있습니다. + * `config.action_dispatch.http_auth_salt`는 HTTP Auth의 salt값(역주: 해시의 안전성을 강화하기 위해서 추가되는 임의의 값)을 설정합니다. 기본값은 `'http authentication'`입니다. * `config.action_dispatch.signed_cookie_salt`는 cookie 서명시에 사용할 salt값을 설정합니다. 기본값은 `'signed cookie'`입니다. @@ -351,25 +433,27 @@ MySQL 어댑터를 사용하면 아래의 옵션이 하나 추가됩니다. * `config.action_dispatch.rescue_responses`는 각 예외에 대해서 어떤 HTTP 상태 코드를 돌려주어야 하는지를 정의합니다. 에러와 상태 코드를 키/값 쌍으로 가지는 해시를 사용할 수 있으며, 기본값은 다음과 같습니다. - ```ruby - config.action_dispatch.rescue_responses = { - 'ActionController::RoutingError' => :not_found, - 'AbstractController::ActionNotFound' => :not_found, - 'ActionController::MethodNotAllowed' => :method_not_allowed, - 'ActionController::UnknownHttpMethod' => :method_not_allowed, - 'ActionController::NotImplemented' => :not_implemented, - 'ActionController::UnknownFormat' => :not_acceptable, - 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity, - 'ActionController::InvalidCrossOriginRequest' => :unprocessable_entity, - 'ActionDispatch::ParamsParser::ParseError' => :bad_request, - 'ActionController::BadRequest' => :bad_request, - 'ActionController::ParameterMissing' => :bad_request, - 'ActiveRecord::RecordNotFound' => :not_found, - 'ActiveRecord::StaleObjectError' => :conflict, - 'ActiveRecord::RecordInvalid' => :unprocessable_entity, - 'ActiveRecord::RecordNotSaved' => :unprocessable_entity - } - ``` + ```ruby + config.action_dispatch.rescue_responses = { + 'ActionController::RoutingError' => :not_found, + 'AbstractController::ActionNotFound' => :not_found, + 'ActionController::MethodNotAllowed' => :method_not_allowed, + 'ActionController::UnknownHttpMethod' => :method_not_allowed, + 'ActionController::NotImplemented' => :not_implemented, + 'ActionController::UnknownFormat' => :not_acceptable, + 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity, + 'ActionController::InvalidCrossOriginRequest' => :unprocessable_entity, + 'ActionDispatch::Http::Parameters::ParseError' => :bad_request, + 'ActionController::BadRequest' => :bad_request, + 'ActionController::ParameterMissing' => :bad_request, + 'Rack::QueryParser::ParameterTypeError' => :bad_request, + 'Rack::QueryParser::InvalidParameterError' => :bad_request, + 'ActiveRecord::RecordNotFound' => :not_found, + 'ActiveRecord::StaleObjectError' => :conflict, + 'ActiveRecord::RecordInvalid' => :unprocessable_entity, + 'ActiveRecord::RecordNotSaved' => :unprocessable_entity + } + ``` 여기에 명시되지 않은 모든 예외는 500 Internal Server Error로 처리됩니다. @@ -395,20 +479,45 @@ MySQL 어댑터를 사용하면 아래의 옵션이 하나 추가됩니다. * `config.action_view.logger`는 Log4r 또는 기본 Ruby Logger 클래스의 인터페이스를 따르는 로거를 인수로 사용합니다. 이 로거는 Action View로부터 생성된 정보를 로그에 출력할 때에 사용됩니다. 로그 출력을 하고 싶지 않은 경우에는 `nil`을 사용하세요. -* `config.action_view.erb_trim_mode`는 ERB에서 사용할 트림 모드를 지정합니다. 기본은 `'-'`로, `<%= -%>` 또는 `<%= =%>`의 경우 어미에 띄어쓰기 문자를 제거하고 개행합니다. 자세한 설명은 [Erubis 문서](http://www.kuwata-lab.com/erubis/users-guide.06.html#topics-trimspaces)를 참ㅗ하세요. +* `config.action_view.erb_trim_mode`는 ERB에서 사용할 트림 모드를 지정합니다. 기본은 `'-'`로, `<%= -%>` 또는 `<%= =%>`의 경우 어미에 띄어쓰기 문자를 제거하고 개행합니다. 자세한 설명은 [Erubis 문서](http://www.kuwata-lab.com/erubis/users-guide.06.html#topics-trimspaces)를 참고하세요. * `config.action_view.embed_authenticity_token_in_remote_forms`는 폼에서 `:remote => true`를 사용한 경우에 `authenticity_token`의 기본 동작을 정의합니다. 기본값은 `false`이며, 이 경우 리모트 폼에는 `authenticity_token`가 포함되지 않습니다. 이는 폼에서 Fragment 캐시를 사용하고 있는 경우에 편리합니다. 리모트 폼은 `meta` 태그로부터 인증을 받으므로, JavaScript가 동작하지 않는 브라우저를 지원해야하는 것이 아니라면, 폼 안에 삽입할 필요는 없습니다. 그러한 경우에는 `:authenticity_token => true`를 폼 옵션으로 넘기거나, 이 옵션을 `true`로 설정해주세요. -* `config.action_view.prefix_partial_path_with_controller_namespace`는 네임스페이스화된 컨트롤러로부터 출력된 템플릿에 어떤 하위 폴더의 파셜(부분 템플릿)을 탐색할지 말지를 지정합니다. 예를 들어, `Admin::PostsController`라는 컨트롤러가 있고 아래와 같은 템플릿을 랜더링한다고 해봅시다. + ```ruby + config.action_dispatch.rescue_responses = { + 'ActionController::RoutingError' => :not_found, + 'AbstractController::ActionNotFound' => :not_found, + 'ActionController::MethodNotAllowed' => :method_not_allowed, + 'ActionController::UnknownHttpMethod' => :method_not_allowed, + 'ActionController::NotImplemented' => :not_implemented, + 'ActionController::UnknownFormat' => :not_acceptable, + 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity, + 'ActionController::InvalidCrossOriginRequest' => :unprocessable_entity, + 'ActionDispatch::Http::Parameters::ParseError' => :bad_request, + 'ActionController::BadRequest' => :bad_request, + 'ActionController::ParameterMissing' => :bad_request, + 'Rack::QueryParser::ParameterTypeError' => :bad_request, + 'Rack::QueryParser::InvalidParameterError' => :bad_request, + 'ActiveRecord::RecordNotFound' => :not_found, + 'ActiveRecord::StaleObjectError' => :conflict, + 'ActiveRecord::RecordInvalid' => :unprocessable_entity, + 'ActiveRecord::RecordNotSaved' => :unprocessable_entity + } + ``` +* `config.action_view.prefix_partial_path_with_controller_namespace`는 네임스페이스화된 컨트롤러로부터 출력된 템플릿에 어떤 하위 폴더의 파셜(부분 템플릿)을 탐색할지 말지를 지정합니다. 예를 들어, `Admin::ArticlesController`라는 컨트롤러가 있고 아래와 같은 템플릿을 랜더링한다고 해봅시다. ```erb - <%= render @post %> + <%= render @article %> ``` -이 설정의 기본값은 `true`이며, `/admin/posts/_post.erb`에 있는 파셜을 사용하게 됩니다. 만약 `false`로 변경하게 되면 `/posts/_post.erb`를 사용하여 랜더링합니다. 이 동작은 `PostsController` 등에 네임스페이스화되지 않은 컨트롤러를 랜더링할 때와 동일한 것입니다. +이 설정의 기본값은 `true`이며, `/admin/articles/_article.erb`에 있는 파셜을 사용하게 됩니다. 만약 `false`로 변경하게 되면 `/articles/_article.erb`를 사용하여 랜더링합니다. 이 동작은 `ArticlesController` 등에 네임스페이스화되지 않은 컨트롤러를 랜더링할 때와 동일한 것입니다. * `config.action_view.raise_on_missing_translations`은, i18n에서 번역이 존재하지 않은 경우에 에러를 발생시킬지 아닐지를 지정합니다. +* `config.action_view.automatically_disable_submit_tag`는 submit_tag가 클릭하면 자동으로 비활성화될지를 지정하며, 기본값은 `true`입니다. + +* `config.action_view.debug_missing_translation`는 찾을 수 없는 번역 키를 `` 태그로 감쌀지 아닐지를 지정합니다. 기본값은 `true`입니다. + ### Action Mailer 설정하기 `config.action_mailer`에는 다양한 옵션이 존재합니다. @@ -422,6 +531,9 @@ MySQL 어댑터를 사용하면 아래의 옵션이 하나 추가됩니다. * `:user_name` - 메일 서버에서 인증이 요구되는 경우, 여기서 사용자 이름을 설정합니다. * `:password` - 메일 서버에서 인증이 요구되는 경우, 여기서 비밀번호를 설정합니다. * `:authentication` - 메일 서버에서 인증이 요구되는 경우, 여기서 그 종류를 지정합니다. `:plain`, `:login`, `:cram_md5` 중 하나를 사용할 수 있습니다. + * `:enable_starttls_auto` - STARTTLS이 SMTP 서버에서 사용중인지를 탐지하고, 이를 사용합니다. 기본값은 `true`입니다. + * `:openssl_verify_mode` - TLS를 사용할 때 OpenSSL이 어떻게 인증서를 확인할지를 지정할 수 있습니다. 이는 자기 서명하거나 와일드카드 인증서를 검증해야할 때에 유용합니다. `:none`나 `:peer`와 같은 OpenSSL 검증 상수를 사용하거나 직접 `OpenSSL::SSL::VERIFY_NONE`나 `OpenSSL::SSL::VERIFY_PEER`를 사용할 수 있습니다. + * `:ssl/:tls` - SMPT 연결이 SMTP/TLS(SMTPS: TLS로 SMTP를 사용하기)를 사용하도록 활성화합니다. * `config.action_mailer.sendmail_settings`를 통해 `:sendmail` 방식에 대해서 세밀하게 설정을 할 수 있습니다. 해시를 인수로 받으며, 아래의 옵션을 포함할 수 있습니다. * `:location` - sendmail 실행 파일의 위치. 기본값은 `/usr/sbin/sendmail`입니다. @@ -442,7 +554,7 @@ MySQL 어댑터를 사용하면 아래의 옵션이 하나 추가됩니다. parts_order: ["text/plain", "text/enriched", "text/html"] ``` - 해시를 하나 지정해서 옵션을 추가할 수도 있습니다. + 해시를 하나 지정해서 옵션을 추가할 수도 있습니다. ```ruby config.action_mailer.default_options = { @@ -474,18 +586,26 @@ MySQL 어댑터를 사용하면 아래의 옵션이 하나 추가됩니다. config.action_mailer.show_previews = false ``` +* `config.action_mailer.deliver_later_queue_name`은 메일러가 사용할 큐 이름을 지정합니다. 기본값은 `mailer`입니다. + +* `config.action_mailer.perform_caching`은 메일러 템플릿에서 조각 캐싱을 사용할지 여부를 지정합니다. 기본값은 `false`입니다. + ### Active Support 설정하기 Active Support에도 몇가지 설정이 있습니다. * `config.active_support.bare`는 Rails를 실행할 때에 `active_support/all`를 불러올지 말지를 지정합니다. 기본값은 `nil`이며, 이 경우 `active_support/all`를 읽어옵니다. +* `config.active_support.test_order`는 테스트를 실행할 순서를 지정합니다. `:random`과 `:sorted`를 사용할 수 있으며, 기본값은 `:random`입니다. + * `config.active_support.escape_html_entities_in_json`는 JSON 직렬화에 포함되는 HTML 코드를 이스케이프할지를 지정합니다. 기본값은 `false`입니다. * `config.active_support.use_standard_json_time_format`는 ISO 8601 형식에 따른 날짜의 직렬화를 처리할지 말지를 지정합니다. 기본값은 `true`입니다. * `config.active_support.time_precision`는 JSON 인코딩된 시간값의 정밀도를 지정합니다. 기본값은 `3`입니다. +* `ActiveSupport.halt_callback_chains_on_return_false`는 Active Record와 Active Model 콜백 체인이 `before` 콜백에서 `false`를 반환하는 경우에 종료될 지를 지정합니다. `false`로 지정하면, 콜백 체인은 `throw(:abort)`를 명시적으로 호출한 경우에만 종료됩니다. `true`로 지정하면 콜백 체인은 `false`를 호출하는 경우에도 종료되며, 제거 예정 경고를 출력합니다(Rails 5 이전의 동작). 졔거 예정 경고 기간에는 `true`가 기본값입니다. 새 Rails 5 애플리케이션은 `new_framework_defaults.rb`라는 initializer 팡리을 생성하며, 여기에는 `false`로 지정되어 있습니다. 이 파일은 `rails app:update`를 실행한 경우에는 추가되지 *않습니다*. 그러므로 `false`를 반환하는 것은 Rails 5로 업그레이드한 경우에는 여전히 동작하며, 코드를 변경할 수 있도록 제거 예정 경고를 출력합니다. + * `ActiveSupport::Logger.silencer`를 `false`로 지정하면, 블럭 내에서 로그 출력을 제어하는 기능이 비활성화됩니다. 기본값은 `true`입니다. * `ActiveSupport::Cache::Store.logger`는 캐시 저장소 조작에서 사용할 로거를 지정합니다. @@ -496,6 +616,63 @@ Active Support에도 몇가지 설정이 있습니다. * `ActiveSupport::Deprecation.silenced`는 Deprecated 메시지를 표시할지 말지를 지정합니다. +### Active Job 설정하기 + +`config.active_job`는 아래의 설정을 제공합니다. + +* `config.active_job.queue_adapter`는 백엔드 큐로 사용할 어댑터를 지정합니다. 기본 어댑터는 `:async`입니다. 내장된 어댑터의 최신 목록을 확인하려면 [ActiveJob::QueueAdapters API 문서](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html)를 참고하세요. + + ```ruby + # Be sure to have the adapter's gem in your Gemfile + # and follow the adapter's specific installation + # and deployment instructions. + config.active_job.queue_adapter = :sidekiq + ``` + +* `config.active_job.default_queue_name`는 기본 큐의 이름을 지정합니다. 기본값은 `"default"`입니다. + + ```ruby + config.active_job.default_queue_name = :medium_priority + ``` + +* `config.active_job.queue_name_prefix`는 옵션으로 사용하며 공백이 될 수 없는, 모든 잡에서 사용될 접두사를 지정할 수 있습니다. 기본값은 공백이며 사용되지 않습니다. + + 다음 설정은 실제 환경에서 잡을 `production_high_priority` 큐에 추가합니다. + + ```ruby + config.active_job.queue_name_prefix = Rails.env + ``` + + ```ruby + class GuestsCleanupJob < ActiveJob::Base + queue_as :high_priority + #.... + end + ``` + +* `config.active_job.queue_name_delimiter`의 기본값은 `'_'`입니다. `queue_name_prefix`가 설정되면 `queue_name_prefix`는 접두사와 기본 큐 이름을 연결합니다. + + 다음 설정은 잡을 video_server.low_priority` 큐에 추가합니다. + + ```ruby + # delimiter를 사용하려면 접두사를 지정해야 합니다. + config.active_job.queue_name_prefix = 'video_server' + config.active_job.queue_name_delimiter = '.' + ``` + + ```ruby + class EncoderJob < ActiveJob::Base + queue_as :low_priority + #.... + end + ``` + +* `config.active_job.logger`는 Log4r이나 기본 Ruby Logger 클래스의 인터페이스를 사용하는 로거를 받으며, Active Job의 로그 정보를 출력할 때에 사용됩니다. 이 로거는 Active Job 클래스나 인스턴스에서 `logger`를 호출하여 가져올 수 있습니다. 로깅을 사용하지 않으려면 `nil`로 설정하세요. + +### Action Cable 설정하기 + +* `config.action_cable.url`는 Action Cable 서버를 제공할 URL 문자열을 받습니다. 주 애플리케이션과 분리된 환경에서 운영하는 경우에 사용하세요. +* `config.action_cable.mount_path`는 Action Cable을 주 서버 프로세스에서 제공할 때 어느 경로에서 제공할지를 지정합니다. 기본값은 `/cable`입니다. Rails 서버에서 Action Cable를 제공하지 않는다면 nil로 설정하면 됩니다. ### 데이터베이스 설정하기 @@ -640,9 +817,9 @@ development: NOTE: Railsdp서 데이터 저장용으로 SQLite3 데이터베이스를 채용하고 있는 이유는 설정 없이도 쉽게 사용할 수 있기 때문입니다. Rails에서는 SQLite 대신에 MySQL이나 PostgrreSQL 등을 사용할 수도 있습니다. 또한, 데이터베이스 접속용으로 많은 플러그인이 존재합니다. production 환경에서 어떤 데이터베이스를 사용하는 경우, 그를 위한 어뎁터는 대부분 금방 찾을 수 있습니다. -#### MySQL 데이터베이스 설정하기 +#### MySQL이나 MariaDB 설정하기 -Rails同梱のSQLite3に代えてMySQLを採用した場合、`config/database.yml`の記述方法を少し変更します。developmentセクションの記述は以下のようになります。 +기본으로 제공되는 SQLite3 대신에 MySQL이나 MariaDB를 사용하고 싶다면, `config/database.yml`은 조금 다른 모습이 됩니다. 개발 환경의 설정은 이렇습니다. ```yaml development: @@ -655,7 +832,7 @@ development: socket: /tmp/mysql.sock ``` -開発環境のコンピュータにMySQLがインストールされており、ユーザー名root、パスワードなしで接続できるのであれば、上の設定で接続できるようになるはずです。接続できない場合は、`development`セクションのユーザー名またはパスワードを適切なものに変更してください。 +만약 개발용 데이터베이스의 root 사용자의 비밀번호가 없다면 이 설정은 동작해야 합니다. 그렇지 않다면 사용자 이름과 비밀번호를 적절한 값으로 변경해주세요. #### PostgreSQL 데이터베이스 설정하기 @@ -697,7 +874,7 @@ development: database: db/development.sqlite3 ``` -#### JRuby 플랫폼에서 MySQL 데이터베이스 설정하기 +#### JRuby 플랫폼에서 MySQL이나 MariaDB 데이터베이스 설정하기 JRuby 환경에서 MySQL을 사용하는 경우 `config/database.yml`의 작성방법이 조금 다릅니다. development는 다음과 같이 작성합니다. @@ -789,16 +966,7 @@ server { } ``` -반드시 최신 정보는 NGINX 문서를 참조하여주세요. - -#### 하위 폴더에 배포하는 경우의 검토사항 - -실제 환경에서 Rails를 하위 폴더에 배호가게 되면 Rails의 많은 부분에 영향이 발생합니다. - -* 개발 환경 -* 테스트 환경 -* 정적 애셋 제공하기 -* 애셋 파이프라인 +반드시 최신 정보는 [NGINX 문서](http://nginx.org/en/docs/)를 참조하여주세요. Rails 환경 설정 -------------------------- @@ -891,7 +1059,7 @@ WARNING: initializer가 실행되는 순서는 논리적으로 모순이 발생 * `set_clear_dependencies_hook`은 `active_record.set_dispatch_hooks`에 대한 훅을 제공합니다. 이 initializer보다 먼저 실행됩니다. 이 initializer는 `cache_classes`가 `false`인 경우에만 실행됩니다. 그리고 이 initializer는 `ActionDispatch::Callbacks.after`를 사용하여 객체 공간으로부터 요청에서 참조된 상수를 제거하여 이후 요청에서 다시 로드하도록 만듭니다. -* `initialize_dependency_mechanism`는 `config.cache_classes`이 true인 경우 `ActiveSupport::Dependencies.mechanism`에 의존성을 (`load`가 아닌) `require`로 설정합니다. +* `initialize_dependency_mechanism`는 `config.cache_classes`이 `true`인 경우 `ActiveSupport::Dependencies.mechanism`에 의존성을 (`load`가 아닌) `require`로 설정합니다. * `bootstrap_hook`은 모든 정의된 `before_initialize` 블럭을 실행합니다. @@ -903,33 +1071,49 @@ WARNING: initializer가 실행되는 순서는 논리적으로 모순이 발생 * `active_support.initialize_beginning_of_week`는 `config.beginning_of_week`의 값을 기반으로 애플리케이션의 기본 주의 시작요일을 설정합니다. 기본값은 `:monday`입니다. +* `active_support.set_configs`: `config.active_support`의 설정값을 `ActiveSupport`에 메소드 이름을 전송하여 값을 넘겨서 Active Support를 설정합니다. + * `action_dispatch.configure`는 `ActionDispatch::Http::URL.tld_length`를 `config.action_dispatch.tld_length`의 값(탑 레벨 도메인의 길이)으로 설정합니다. * `action_view.set_configs`는 `config.action_view`의 설정값을 `ActionView::Base`에 메소드 이름을 전송하여 값을 넘겨서 Action View를 설정합니다. -* `action_controller.logger`는 `Rails.logger`에 대한 설정이 없는 경우에 `ActionController::Base.logger`를 설정합니다. +* `action_controller.assets_config`는 명시적으로 설정되지 않으면 `config.actions_controller.assets_dir`를 public 폴더로 지정합니다. -* `action_controller.initialize_framework_caches`는 `Rails.cache`에 대한 설정이 없는 경우에 `ActionController::Base.cache_store`를 설정합니다. +* `action_controller.set_helpers_path`는 Action Controller의 `helpers_path`를 애플리케이션의 `helpers_path`로 설정합니다. + +* `action_controller.parameters_config`는 `ActionController::Parameters`를 위해 Strong parameter 옵션을 설정합니다. * `action_controller.set_configs`는 `config.action_controller`의 설정값을 `ActionController::Base`에 메소드 이름을 전송하여 값을 넘겨서 Action Controller를 설정합니다. * `action_controller.compile_config_methods`는 지정한 설정용 메소드를 초기화하고, 좀 더 빠르게 접근할 수 있게 해줍니다. -* `active_record.initialize_timezone`는 `ActiveRecord::Base.time_zone_aware_attributes`를 true로 설정하고 `ActiveRecord::Base.default_timezone`을 UTC로 설정합니다. 속성을 데이터베이스로부터 가져온 경우, 그 속성은 `Time.zone`에서 지정한 시간대로 변환됩니다. +* `active_record.initialize_timezone`는 `ActiveRecord::Base.time_zone_aware_attributes`를 `true`로 설정하고 `ActiveRecord::Base.default_timezone`을 UTC로 설정합니다. 속성을 데이터베이스로부터 가져온 경우, 그 속성은 `Time.zone`에서 지정한 시간대로 변환됩니다. * `active_record.logger`가 설정되어 있지 않은 경우에 `ActiveRecord::Base.logger`를 `Rails.logger`를 설정합니다. +* `active_record.migration_error`는 실행되지 않은 마이그레이션이 있는지 확인하는 미들웨어를 설정합니다. + +* `active_record.check_schema_cache_dump`는 스키마 캐시 덤프가 설정되어 있고, 사용할 수 있다면 이를 불러옵니다. + +* `active_record.warn_on_records_fetched_greater_than`는 쿼리가 대량의 레코드를 반환할 때 출력할 경고를 활성화합니다. + * `active_record.set_configs`는 `config.active_record`의 설정값을 `ActiveRecord::Base`에 메소드 이름을 전송하여 값을 넘겨서 Action Record를 설정합니다. * `active_record.initialize_database`는 데이터베이스 설정을 `config/database.yml`(기본값을 불러오는 곳)으로부터 불러오며, 현재의 환경에서의 접속을 확립합니다. * `active_record.log_runtime`는 요청에서 Active Record를 호출하는 시간을 로거에 전달하는 역할을 하는 `ActiveRecord::Railties::ControllerRuntime`를 포함합니다. -* `active_record.set_dispatch_hooks`는 `config.cache_classes`가 `false`로 설정되어 있는 경우, 다시 읽어올 수 있는 데이터베이스 접속을 모두 초기화시킵니다. +* `active_record.set_reloader_hooks`는 `config.cache_classes`가 `false`로 설정되어 있는 경우, 다시 읽어올 수 있는 데이터베이스 접속을 모두 초기화시킵니다. + +* `active_record.add_watchable_files`는 `schema.rb`와 `structure.sql` 파일을 감시 목록에 추가합니다. + +* `active_job.logger`는 `ActiveJob::Base.logger`가 설정되어 있지 않다면 `Rails.logger`로 설정합니다. + +* `active_job.set_configs`는 `config.active_job`의 설정값을 `ActiveJob::Base`에 메소드 이름을 전송하여 값을 넘기고 Active Job을 설정합니다. * `action_mailer.logger`는 설정이 되어 있지 않은 경우에 `ActionMailer::Base.logger`를 `Rails.logger`로 설정합니다. -* `action_mailer.set_configs`는 `config.action_mailer`의 설정값을 `ActiveMailer::Base`에 메소드 이름을 전송하여 값을 넘겨서 Action Mailer를 설정합니다. 설정을 사용해서 Action Mailer를 설정합니다. +* `action_mailer.set_configs`는 `config.action_mailer`의 설정값을 `ActiveMailer::Base`에 메소드 이름을 전송하여 값을 넘겨서 Action Mailer를 설정합니다. * `action_mailer.compile_config_methods`는 지정된 설정용 메소드를 초기화하고, 좀 더 빠르게 사용할 수 있도록 만듭니다. @@ -945,8 +1129,6 @@ WARNING: initializer가 실행되는 순서는 논리적으로 모순이 발생 * `load_environment_config`는 현재 환경의 `config/environments`를 읽어옵니다. -* `append_asset_paths` 애플리케이션과 거기에 추가되어 있는 railties에 포함되어 있는 애셋 경로를 찾고, `config.static_asset_paths`에서 지정되어 있는 폴더를 감시합니다. - * `prepend_helpers_path`는 애플리케이션이나 railties, 엔진에 포함되는 `app/helpers` 폴더를 헬퍼의 참조 경로에 추가합니다. * `load_config_initializers` 애플리케이션이나 railties, 엔진에 포함되는 `config/initializers`에 있는 Ruby 파일을 모두 읽어옵니다. 이 폴더에 포함되어 있는 파일은, 프레임워크 로딩이 모두 끝난 이후에 처리하고 싶은 설정입니다. @@ -963,13 +1145,13 @@ WARNING: initializer가 실행되는 순서는 논리적으로 모순이 발생 * `build_middleware_stack`는 애플리케이션의 미들웨어 스택을 구성하고, 요청에 대한 Rack 환경 객체를 인수로 받는 `call` 메소드를 가지는 객체를 반환합니다. -* `eager_load!`는 `config.eager_load`가 true인 경우 `config.before_eager_load` 훅을 실행하고, 이어서 `eager_load!`를 호출하여 모든 `config.eager_load_namespaces`가 불러옵니다. +* `eager_load!`는 `config.eager_load`가 `true`인 경우 `config.before_eager_load` 훅을 실행하고, 이어서 `eager_load!`를 호출하여 모든 `config.eager_load_namespaces`가 불러옵니다. * `finisher_hook`은 애플리케이션 초기화 프로세스가 완료된 이후에 실행되는 훅을 제공하며, 애플리케이션이나 railties, 엔진의 `config.after_initialize` 블록을 모두 실행합니다. * `set_routes_reloader`는 `ActionDispatch::Callbacks.to_prepare`을 사용하여 라우팅을 다시 읽어오기 위한 Action Dispatch를 구성합니다. -* `disable_dependency_loading`는 `config.eager_load`가 true인 경우에 자동 의존성 로딩(automatic dependency loading)이 무효화됩니다. +* `disable_dependency_loading`는 `config.eager_load`가 `true`인 경우에 자동 의존성 로딩(automatic dependency loading)이 무효화됩니다. 데이터베이스 커넥션 풀 ---------------- @@ -991,11 +1173,89 @@ development: 이용가능한 숫자보다 많은 커넥션을 사용하려고 시도하면 Active Record는 이를 막고 풀이 넘겨주는 커넥션을 기다립니다. 커넥션을 얻을 수 없는 경우에는 아래와 같은 타임 아웃 에러를 던집니다. ```ruby -ActiveRecord::ConnectionTimeoutError - could not obtain a database connection within 5 seconds. The max pool size is currently 5; consider increasing it: +ActiveRecord::ConnectionTimeoutError - could not obtain a database connection within 5.000 seconds (waited 5.000 seconds) ``` 이 에러가 발생하는 경우에는 `database.yml`의 `pool` 옵션에 설정되어 있는 숫자를 더 큰 숫자로 변경하여 접속 풀의 크기를 키워 문제의 해결을 노려볼 수 있습니다. NOTE: 애플리케이션을 멀티 스레드 환경에서 실행하고 있는 경우에는 많은 스레드가 동시에 접속할 가능성이 있습니다. 현 시점의 요청에 걸린 부하에 따라서 제한된 요청 가능 숫자를 여러 스레드가 경쟁적으로 요구할 가능성이 있습니다. -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. +커스텀 설정 +------------------ + +Rails 설정 객체에서 `config.x` 이름 공간이나 `config`를 직접 사용하여 애플리케이션의 설정을 할 수 있습니다. 중첩된 설정을 정의하는 경우에는 `config.x` 형식을 사용해주세요(ex: `config.x.nested.nested.hi`). 또는 그냥 값이 필요한 경우에는 `config`를 사용하세요(ex: `config.hello`). + + ```ruby + config.x.payment_processing.schedule = :daily + config.x.payment_processing.retries = 3 + config.super_debugger = true + ``` + +이 설정 방식의 장점은 설정 객체를 통해서 이 정보들에 접근할 수 있다는 점입니다. + + ```ruby + Rails.configuration.x.payment_processing.schedule # => :daily + Rails.configuration.x.payment_processing.retries # => 3 + Rails.configuration.x.payment_processing.not_set # => nil + Rails.configuration.super_debugger # => true + ``` + +`Rails::Application.config_for`를 사용하여 설정 파일 전체를 불러올 수도 있습니다. + + ```ruby + # config/payment.yml: + production: + environment: production + merchant_id: production_merchant_id + public_key: production_public_key + private_key: production_private_key + development: + environment: sandbox + merchant_id: development_merchant_id + public_key: development_public_key + private_key: development_private_key + + # config/application.rb + module MyApp + class Application < Rails::Application + config.payment = config_for(:payment) + end + end + ``` + + ```ruby + Rails.configuration.payment['merchant_id'] # => production_merchant_id or development_merchant_id + ``` + +검색 엔진 색인 +---------------------- + +때때로 애플리케이션의 몇몇 페이지들을 Google, Bing, Yahoo, Duck Duck Go와 같은 검색 엔진에서 보이지 않았으면 할 때가 있습니다. 이러한 사이트들의 로봇은 `http://your-site.com/robots.txt`를 확인하여 어떤 페이지에 대한 색인이 허가되어 있는지를 확인합니다. + +Rails는 이 파일을 `/public`에 생성합니다. 기본값은 검색 엔진이 애플리케이션의 모든 페이지를 색인할 수 있도록 허가합닏. 만약 애플리케이션의 모든 페이지 색인을 막고 싶다면 다음을 사용하세요. + +``` +User-agent: * +Disallow: / +``` + +특정 페이지만을 막고 싶은 경우에는 좀 더 복잡한 문법을 사용해야 합니다. [공식 문서](http://www.robotstxt.org/robotstxt.html)에서 이를 배워보세요. + +파일 시스템 변경 감시 +----------------------- + +Rails에 [listen 젬](https://github.com/guard/listen)이 로드되어 있다면 `config.cache_classes`이 `false`인 경우에 파일 시스템 감시를 위해 이를 사용할 수 있습니다. + +```ruby +group :development do + gem 'listen', '>= 3.0.5', '< 3.2' +end +``` + +그렇지 않다면 모든 요청마다 Rails가 애플리케이션 트리를 돌며 변경사항이 없는지 확인합니다. + +Linux나 Mac OS X는 추가로 젬을 필요로 하지 않습니다만, [*BSD](https://github.com/guard/listen#on-bsd)이거나 [윈도우](https://github.com/guard/listen#on-windows)인 경우에는 필요합니다. + +[몇몇 설정에서는 지원되지 않는](https://github.com/guard/listen#issues--limitations) 점을 기억하세요. + + diff --git a/guides/source/ko/contributing_to_ruby_on_rails.md b/guides/source/ko/contributing_to_ruby_on_rails.md index d0e1c8adac498..50dde0e5dc5ed 100644 --- a/guides/source/ko/contributing_to_ruby_on_rails.md +++ b/guides/source/ko/contributing_to_ruby_on_rails.md @@ -38,7 +38,9 @@ issue 보고에는 적어도 제목과 issue에 대한 명쾌한 설명이 필 자신의 issue를 재현하는 방법을 준비하는 것은 다른 개발자가 issue를 확인, 조사, 그리고 수정할 때에 무척 도움이 됩니다. 이를 위한 방법으로, 실행 가능한 테스트 케이스를 제공하는 법이 있습니다. 이 작업을 조금이라도 간단하게 만들기 위해서 Rails 팀은 버그 레포트의 템플릿을 여러가지 준비해두고 있습니다. 이를 바탕으로 작업을 시작할 수 있습니다. * Active Record (모델, 데이터베이스) issue용 템플릿: [gem](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_gem.rb) / [master](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_master.rb) +* Active Record (마이그레이션) issue용 템플릿: [gem](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_migrations_gem.rb) / [master](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_migrations_master.rb) * Action Pack (컨트롤러, 라우팅) issue용 템플릿: [gem](https://github.com/rails/rails/blob/master/guides/bug_report_templates/action_controller_gem.rb) / [master](https://github.com/rails/rails/blob/master/guides/bug_report_templates/action_controller_master.rb) +* Active Job issue용 템플릿: [gem](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_job_gem.rb) / [master](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_job_master.rb) * 그 외의 일반적인 issue용 템플릿: [gem](https://github.com/rails/rails/blob/master/guides/bug_report_templates/generic_gem.rb) / [master](https://github.com/rails/rails/blob/master/guides/bug_report_templates/generic_master.rb) 템플릿에는 '보일러플레이트(boilerplate)'라고 불리는 일종의 기본 코드가 포함되어 있으며, 이를 사용해서 Rails의 릴리스 버전(`*_gem.rb`)이나 edge Rails(`*_master.rb`)에 대한 테스트 케이스를 설정할 수 있습니다. @@ -86,7 +88,7 @@ $ git checkout -b testing_branch 이어서 원격 브런치를 사용해서 로컬의 코드를 갱신합니다. 예를 들어 GitHub 사용자인 JohnSmith가 fork하여 https://github.com/JohnSmith/rails의 "orange"라는 토픽 브랜치에 push를 했다고 가정합시다. ```bash -$ git remote add JohnSmith git://github.com/JohnSmith/rails.git +$ git remote add JohnSmith https://github.com/JohnSmith/rails.git $ git pull JohnSmith orange ``` @@ -110,8 +112,8 @@ Ruby on Rails에는 2 종류의 문서가 있습니다. 하나는 이 가이드 누구라도 Rails 가이드에 기여할 수 있습니다. Rails 가이드에 요구되는 개선 사항은 '일관될 것', '모순이 없을 것', '읽기 쉬울 것', '정보의 추가', '사실과 다른 부분을 수정', '오타 수정', '최신 edge Rails를 반영할것' 등입니다. -[Rails](http://github.com/rails/rails)에 풀 리퀘스트를 보내거나 정기적으로 기여를 하고 싶다면 [Rails 코어 팀](http://rubyonrails.org/core)에게 -docrails에 대한 커밋 권한을 요청해도 좋습니다. 단 docrails에 직접 풀 리퀘스트를 보내지 말아주세요. 자신이 작성한 변경사항에 의견을 묻고 싶은 경우에는 [Rails](http://github.com/rails/rails)에서 부탁드립니다. +[Rails](https://github.com/rails/rails)에 풀 리퀘스트를 보내거나 정기적으로 기여를 하고 싶다면 [Rails 코어 팀](http://rubyonrails.org/community/#core)에게 +docrails에 대한 커밋 권한을 요청해도 좋습니다. 단 docrails에 직접 풀 리퀘스트를 보내지 말아주세요. 자신이 작성한 변경사항에 의견을 묻고 싶은 경우에는 [Rails](https://github.com/rails/rails)에서 부탁드립니다. docrails는 정기적으로 master에 병합되므로 Ruby on Rails 문서의 편집을 효율적으로 수행할 수 있습니다. @@ -151,8 +153,8 @@ NOTE: 이 설명은 Rails 4 이상을 위한 것입니다. 그리고 Redcarpet g * **Italian**: [https://github.com/rixlabs/docrails](https://github.com/rixlabs/docrails) * **Spanish**: [http://wiki.github.com/gramos/docrails](http://wiki.github.com/gramos/docrails) -* **Polish**: [http://github.com/apohllo/docrails/tree/master](http://github.com/apohllo/docrails/tree/master) -* **French** : [http://github.com/railsfrance/docrails](http://github.com/railsfrance/docrails) +* **Polish**: [https://github.com/apohllo/docrails/tree/master](https://github.com/apohllo/docrails/tree/master) +* **French** : [https://github.com/railsfrance/docrails](https://github.com/railsfrance/docrails) * **Czech** : [https://github.com/rubyonrails-cz/docrails/tree/czech](https://github.com/rubyonrails-cz/docrails/tree/czech) * **Turkish** : [https://github.com/ujk/docrails/tree/master](https://github.com/ujk/docrails/tree/master) * **Korean** : [https://github.com/rorlakr/rails-guides](https://github.com/rorlakr/rails-guides) @@ -181,7 +183,7 @@ Rails development box를 사용할 수 없는 상황이라면 Rails 가이드의 코드에 공헌하기 위해서는 우선 Rails 저장소를 복사하는 것부터 시작해야합니다. ```bash -$ git clone git://github.com/rails/rails.git +$ git clone https://github.com/rails/rails.git ``` 이어서 별도의 브랜치를 만듭니다. @@ -244,35 +246,27 @@ Rails에서 코딩을 하는 경우에는 다음의 간단한 스타일 가이 ### 벤치마킹하기 -자신이 작성한 코드에 의해서 Rails의 성능이 저하된다면, 비교를 위해서 [benchmark-ips](https://github.com/evanphx/benchmark-ips) gem을 사용해서 벤치마크 결과를 추가해주세요. +성능에 영향이 있는 변경인 경우에는 코드를 벤치마킹하고 그 영향을 측정해주세요. +결과와 함께 벤치마크에 사용한 스크립트를 공유해주세요. 미래의 기여자들이 쉽게 +이에 대한 정보를 얻을 수 있도록 커밋 메시지에는 이 정보를 반드시 포함해주세요. +(예를 들어, 미래의 Ruby VM 최적화 덕분에 해당 최적화가 없어질지도 모릅니다.) -benchmark-ips의 실행 예시는 다음과 같습니다. +생각하고 있는 특정 시나리오에 대한 성능을 향상시키는 최적화를 만드는 것은 +무척 쉽지만, 일반적인 경우에 대해서는 그렇지 않을 수 있습니다. 그러므로 +대표적인 시나리오들에 대해서 테스트를 해야합니다. 이상적으로는 실제 환경에서 +있었던 실제 시나리오를 기반으로 하는 것이 좋습니다. -```ruby -require 'benchmark/ips' - -Benchmark.ips do |x| - x.report('addition') { 1 + 2 } - x.report('addition with send') { 1.send(:+, 2) } -end -``` - -이 코드에 의해서 다음의 정보를 포함한 레포트를 생성할 수 있습니다. - -``` -Calculating ------------------------------------- - addition 132.013k i/100ms - addition with send 125.413k i/100ms -------------------------------------------------- - addition 9.677M (± 1.7%) i/s - 48.449M - addition with send 6.794M (± 1.1%) i/s - 33.987M -``` - -자세한 설명은 benchmark/ips의 [README](https://github.com/evanphx/benchmark-ips/blob/master/README.md)를 참조해주세요. +[벤치마칭 템플릿](https://github.com/rails/rails/blob/master/guides/bug_report_templates/benchmark.rb)으로부터 +시작하는 것도 좋습니다. 이는 [benchmark-ips](https://github.com/evanphx/benchmark-ips) 젬을 +사용하는 벤치마킹 코드의 간단한 구현이 포함되어 있습니다. 이 템플릿은 +스크립트에 변경 사항을 추가하여 테스트할 수 있도록 디자인되어 있습니다. ### 테스트 실행하기 -Rails에서는 변경을 올릴 때마다 모든 테스트를 전부 실행해야한다는 관례가 있지는 않습니다. 추천하는 작업 순서는 [rails-dev-box](https://github.com/rails/rails-dev-box)에서 설명했듯이 railites의 테스트가 특히 시간이 걸리며, 소스코드를 `/vagrant`에 마운트 시키면 더욱 그렇습니다. +Rails에서는 변경을 올릴 때마다 모든 테스트를 전부 실행해야한다는 관례가 있는 +것은 아닙니다. 추천하는 작업 순서는 [rails-dev-box](https://github.com/rails/rails-dev-box)에서 +설명했듯이 railites의 테스트가 특히 시간이 걸리며, 소스코드를 `/vagrant`에 +마운트 시키면 더욱 그렇습니다. 현실적인 타협안으로서 작성한 코드에 의해 영향이 발생하는지 아닌지를 테스트해주세요. 그리고 변경이 railties에서 발생한 것이 아니라면, 영향을 받는 컴포넌트의 모든 테스트를 실행해주세요. 테스트를 모두 통과한다면 이 패치를 제안할 준비가 완료됩니다. Rails에서는 다른 장소에서 발생할지 모를 예상외의 에러가 발생하는 것을 검출하기 위해 [Travis CI](https://travis-ci.org/rails/rails)를 사용하고 있습니다. @@ -320,7 +314,6 @@ $ bundle exec rake test:sqlite3 이것으로 `sqlite3`에서 테스트를 실행할 수 있게 됩니다. 각각에 대한 태스크는 다음과 같습니다. ```bash -test:mysql test:mysql2 test:postgresql ``` @@ -331,7 +324,7 @@ test:postgresql $ bundle exec rake test ``` -이걸로 4개가 순서대로 실행됩니다. +이걸로 3개가 순서대로 실행됩니다. 단일 테스트를 각각 실행할 수도 있습니다. @@ -455,7 +448,7 @@ Rails [GitHub 저장소](https://github.com/rails/rails)를 열어서 우측 상 로컬 PC상의 저장소에 새로운 원격 저장소를 추가합니다. ```bash -$ git remote add mine git@github.com:<자신의 사용자명>/rails.git +$ git remote add mine https://github.com:<자신의 사용자명>/rails.git ``` 리모트에 변경사항을 올립니다. @@ -469,7 +462,7 @@ Fork한 저장소를 로컬에 복사하고, Rails의 원 저장소를 원격 Fork를 복사한 폴더에서 다음을 실행합니다. ```bash -$ git remote add rails git://github.com/rails/rails.git +$ git remote add rails https://github.com/rails/rails.git ``` Rails의 공식 저장소로부터 새 커밋과 브랜치를 가져옵니다. @@ -594,7 +587,7 @@ $ git format-patch master --stdout > ~/my_changes.patch 대상 브랜치로 넘어가서 변경사항을 적용합니다. ```bash -$ git checkout -b my_backport_branch 3-2-stable +$ git checkout -b my_backport_branch 4-2-stable $ git apply ~/my_changes.patch ``` @@ -609,4 +602,3 @@ Rails 기여자 master나 docrails에의 기여가 인정된 분들을 [Rails 기여자](http://contributors.rubyonrails.org)에 그 이름을 올리고 있습니다. -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/credits.html.erb b/guides/source/ko/credits.html.erb index 34be07ef818aa..5adbd12ac072f 100644 --- a/guides/source/ko/credits.html.erb +++ b/guides/source/ko/credits.html.erb @@ -22,17 +22,17 @@ Ruby on Rails Guides: Credits

Rails Guides Designers

<%= author('Jason Zimdars', 'jz') do %> - Jason Zimdars is an experienced creative director and web designer who has lead UI and UX design for numerous websites and web applications. You can see more of his design and writing at Thinkcage.com or follow him on Twitter. + Jason Zimdars is an experienced creative director and web designer who has lead UI and UX design for numerous websites and web applications. You can see more of his design and writing at Thinkcage.com or follow him on Twitter. <% end %>

Rails Guides Authors

<%= author('Ryan Bigg', 'radar', 'radar.png') do %> - Ryan Bigg works as the Community Manager at Spree Commerce and has been working with Rails since 2006. He's the author of Multi Tenancy With Rails and co-author of Rails 4 in Action. He's written many gems which can be seen on his GitHub page and he also tweets prolifically as @ryanbigg. + Ryan Bigg works as a Rails developer at Marketplacer and has been working with Rails since 2006. He's the author of Multi Tenancy With Rails and co-author of Rails 4 in Action. He's written many gems which can be seen on his GitHub page and he also tweets prolifically as @ryanbigg. <% end %> <%= author('Oscar Del Ben', 'oscardelben', 'oscardelben.jpg') do %> -Oscar Del Ben is a software engineer at Wildfire. He's a regular open source contributor (Github account) and tweets regularly at @oscardelben. +Oscar Del Ben is a software engineer at Wildfire. He's a regular open source contributor (GitHub account) and tweets regularly at @oscardelben. <% end %> <%= author('Frederick Cheung', 'fcheung') do %> @@ -40,7 +40,7 @@ Oscar Del Ben is a software engineer at Wi <% end %> <%= author('Tore Darell', 'toretore') do %> - Tore Darell is an independent developer based in Menton, France who specialises in cruft-free web applications using Ruby, Rails and unobtrusive JavaScript. His home on the internet is his blog Sneaky Abstractions. + Tore Darell is an independent developer based in Menton, France who specialises in cruft-free web applications using Ruby, Rails and unobtrusive JavaScript. You can follow him on Twitter. <% end %> <%= author('Jeff Dean', 'zilkey') do %> @@ -64,7 +64,7 @@ Oscar Del Ben is a software engineer at Wi <% end %> <%= author('Pratik Naik', 'lifo') do %> - Pratik Naik is a Ruby on Rails developer at 37signals and also a member of the Rails core team. He maintains a blog at has_many :bugs, :through => :rails and has a semi-active twitter account. + Pratik Naik is a Ruby on Rails developer at Basecamp and maintains a blog at has_many :bugs, :through => :rails. He also has a semi-active twitter account. <% end %> <%= author('Emilio Tagua', 'miloops') do %> @@ -74,3 +74,7 @@ Oscar Del Ben is a software engineer at Wi <%= author('Heiko Webers', 'hawe') do %> Heiko Webers is the founder of bauland42, a German web application security consulting and development company focused on Ruby on Rails. He blogs at the Ruby on Rails Security Project. After 10 years of desktop application development, Heiko has rarely looked back. <% end %> + +<%= author('Akshay Surve', 'startupjockey', 'akshaysurve.jpg') do %> + Akshay Surve is the Founder at DeltaX, hackathon specialist, a midnight code junkie and occasionally writes prose. You can connect with him on Twitter, Linkedin, Personal Blog or Quora. +<% end %> diff --git a/guides/source/ko/debugging_rails_applications.md b/guides/source/ko/debugging_rails_applications.md index 8d0bc862c1981..5e575ff8ee97b 100644 --- a/guides/source/ko/debugging_rails_applications.md +++ b/guides/source/ko/debugging_rails_applications.md @@ -108,9 +108,10 @@ Title: Rails debugging guide ### 로거란? -Rails는 `ActiveSupport::Logger` 클래스를 사용하여 로그 정보를 출력합니다. 필요에 따라, `Log4r` 등의 다른 로거로 변경해도 좋습니다. +Rails는 `ActiveSupport::Logger` 클래스를 사용하여 로그 정보를 출력합니다. +필요에 따라, `Log4r` 등의 다른 로거로 변경해도 좋습니다. -다른 로거의 설정은 `environment.rb` 또는 각 환경의 설정파일에서 할 수 있습니다. +다른 로거의 설정은 `config/application.rb` 또는 각 환경의 설정파일에서 할 수 있습니다. ```ruby Rails.logger = Logger.new(STDOUT) @@ -128,22 +129,28 @@ TIP: 로그의 저장 위치는 기본으로 `Rails.root/log/`로 되어 있습 ### 로그의 출력 레벨 -메시지의 로그 레벨이 설정되어 있는 최소 로그 레벨 이상이 되었을 경우에만 로그 파일에 그 메시지를 출력합니다. 현재 로그 레벨을 알고 싶은 경우에는 `Rails.logger.level` 메소드를 호출하세요. +메시지의 로그 레벨이 설정되어 있는 최소 로그 레벨 이상이 되었을 경우에만 로그 +파일에 그 메시지를 출력합니다. 현재 로그 레벨을 알고 싶은 경우에는 +`Rails.logger.level` 메소드를 호출하세요. -지정가능한 로그 레벨은 `:debug`, `:info`, `:warn`, `:error`, `:fatal`, `:unknown`의 6가지가 있으며, 각각 0부터 5까지의 숫자에 대응합니다. 기본 로그 레벨을 변경하려면 아래와 같이 추가하세요. +지정가능한 로그 레벨은 `:debug`, `:info`, `:warn`, `:error`, `:fatal`, +`:unknown`의 6가지가 있으며, 각각 0부터 5까지의 숫자에 대응합니다. 기본 로그 +레벨을 변경하려면 아래와 같이 추가하세요. ```ruby -config.log_level = :warn # 환경마다 initializer에서 사용 가능 +config.log_level = :warn # 환경마다, 또는 initializer에서 사용 가능 Rails.logger.level = 0 # 언제라도 사용 가능 ``` -이것은 development 환경이나 staging 환경에서는 로그를 출력하고, production 환경에서는 필요 없는 정보를 로그에 출력하고 싶지 않을 경우 등에 유용합니다. +이것은 development 환경이나 staging 환경에서는 로그를 출력하고, 실제 환경에서는 +필요 없는 정보를 로그에 출력하고 싶지 않을 경우 등에 유용합니다. TIP: Rails의 기본 로그 레벨은 모든 환경에서 `debug`입니다. ### 메시지 전송 -컨트롤러, 모델, 메일러에서 로그를 남기고 싶은 경우에는 `logger.(debug|info|warn|error|fatal)`를 사용합니다. +컨트롤러, 모델, 메일러에서 로그를 남기고 싶은 경우에는 +`logger.(debug|info|warn|error|fatal)`를 사용합니다. ```ruby logger.debug "Person attributes hash: #{@person.attributes.inspect}" @@ -176,7 +183,7 @@ end ``` 이 컨트롤러의 액션을 실행하면 아래와 같은 로그가 생성됩니다. -ㄴ + ``` Processing ArticlesController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST] Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGl @@ -227,7 +234,7 @@ logger.debug {"Person attributes hash: #{@person.attributes.inspect}"} 넘긴 블럭의 내용(여기에서는 문자열의 식전개)은 debug가 유효한 경우에만 처리됩니다. 이 방법을 통해서 얻어지는 성능의 개선은 대량의 로그를 출력하는 경우가 아니라면 크게 실감이 되지 않을 수 있습니다만, 그렇다 하더라도 채용할 만한 가치는 있습니다. -`byebug` gem을 사용해서 디버깅하기 +`byebug` 젬을 사용해서 디버깅하기 --------------------------------- 코드가 기대한대로 동작하지 않는 경우에는 로그나 콘솔에 출력해서 문제를 진단할 필요가 있습니다. 하지만 이 방법으로는 에러 추적을 몇번이고 반복해야 하므로, 근본적인 원인을 찾기에는 그다지 효율이 좋다고 말할 수 없습니다. 실행중인 코드의 상황을 확인할 필요가 있는 경우에 가장 의지할 만한 것은 역시 디버거입니다. @@ -236,7 +243,7 @@ logger.debug {"Person attributes hash: #{@person.attributes.inspect}"} ### 설치 -`byebug` gem을 사용하면, Rails 코드에 중단점을 지정하여 단계별로 실행할 수 있습니다. 다음을 실행하는 것으로 gem을 설치할 수 있습니다. +`byebug` 젬을 사용하면, Rails 코드에 중단점을 지정하여 단계별로 실행할 수 있습니다. 다음을 실행하는 것으로 젬을 설치할 수 있습니다. ```bash $ gem install byebug @@ -280,16 +287,15 @@ end 다음은 예시입니다. ```bash -=> Booting WEBrick +=> Booting Puma => Rails 5.0.0 application starting in development on http://0.0.0.0:3000 => Run `rails server -h` for more startup options -=> Notice: server is listening on all interfaces (0.0.0.0). Consider using 127.0.0.1 (--binding option) -=> Ctrl-C to shutdown server -[2014-04-11 13:11:47] INFO WEBrick 1.3.1 -[2014-04-11 13:11:47] INFO ruby 2.1.1 (2014-02-24) [i686-linux] -[2014-04-11 13:11:47] INFO WEBrick::HTTPServer#start: pid=6370 port=3000 - - +Puma starting in single mode... +* Version 3.4.0 (ruby 2.3.1-p112), codename: Owl Bowl Brawl +* Min threads: 5, max threads: 5 +* Environment: development +* Listening on tcp://localhost:3000 +Use Ctrl-C to stop Started GET "/" for 127.0.0.1 at 2014-04-11 13:11:48 +0200 ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations".* FROM "schema_migrations" Processing by ArticlesController#index as HTML @@ -305,30 +311,57 @@ Processing by ArticlesController#index as HTML 10: respond_to do |format| 11: format.html # index.html.erb 12: format.json { render json: @articles } - (byebug) ``` -그러면 애플리케이션의 깊은 곳으로 들어가봅시다. 우선 디버거의 헬프를 확인해보는 것이 좋을 것입니다. `help`를 입력해보세요. +그러면 애플리케이션의 깊은 곳으로 들어가봅시다. 우선 디버거의 헬프를 확인해 +보는 것이 좋습니다. `help`를 입력해보세요. -``` +``` (byebug) help -byebug 2.7.0 - -Type 'help ' for help on a specific command + break -- Sets breakpoints in the source code + catch -- Handles exception catchpoints + condition -- Sets conditions on breakpoints + continue -- Runs until program ends, hits a breakpoint or reaches a line + debug -- Spawns a subdebugger + delete -- Deletes breakpoints + disable -- Disables breakpoints or displays + display -- Evaluates expressions every time the debugger stops + down -- Moves to a lower frame in the stack trace + edit -- Edits source files + enable -- Enables breakpoints or displays + finish -- Runs the program until frame returns + frame -- Moves to a frame in the call stack + help -- Helps you using byebug + history -- Shows byebug's history of commands + info -- Shows several informations about the program being debugged + interrupt -- Interrupts the program + irb -- Starts an IRB session + kill -- Sends a signal to the current process + list -- Lists lines of source code + method -- Shows methods of an object, class or module + next -- Runs one or more lines of code + pry -- Starts a Pry session + quit -- Exits byebug + restart -- Restarts the debugged program + save -- Saves current byebug session to a file + set -- Modifies byebug settings + show -- Shows byebug settings + source -- Restores a previously saved byebug session + step -- Steps into blocks or methods one or more times + thread -- Commands to manipulate threads + tracevar -- Enables tracing of a global variable + undisplay -- Stops displaying all or some expressions when program stops + untracevar -- Stops tracing a global variable + up -- Moves to a higher frame in the stack trace + var -- Shows variables and its values + where -- Displays the backtrace -Available commands: -backtrace delete enable help list pry next restart source up -break disable eval info method ps save step var -catch display exit interrupt next putl set thread -condition down finish irb p quit show trace -continue edit frame kill pp reload skip undisplay +(byebug) ``` -TIP: 각각의 명령어의 헬프를 보기 위해서는 디버거의 프롬프트에서 `help <명령어>`라고 입력합니다(예시: _`help list`_). 디버그용 명령은 다른 명령과 구별 가능한 범위 내에서 단축시켜서 사용할 수 있습니다. 예를 들자면, `list` 명령 대신에 `l`이라고 입력할 수도 있습니다. - -이전의 10행을 출력하기 위해서는 `list-`(또는 `l-`)라고 입력합니다. +이전의 10줄을 확인하고 싶다면 `list-`(또는 `l-`)를 입력하세요. ``` (byebug) l- @@ -344,10 +377,9 @@ TIP: 각각의 명령어의 헬프를 보기 위해서는 디버거의 프롬프 8 @articles = Article.find_recent 9 10 respond_to do |format| - ``` -이에서 알 수 있듯, 해당하는 파일에 이동하여 `byebug` 호출을 추가한 행의 앞 부분을 확인할 수 있습니다. 마지막으로 `list=`라고 입력하여 현재 위치로 돌아가봅시다. +이를 통해서 파일 내부에서 `byebug`를 호출한 라인의 윗부분에 어떤 코드가 있는지를 확인할 수 있습니다. 마지막으로, 원래 있었던 곳으로 돌아가려면 `list=`를 입력하면 됩니다. ``` (byebug) list= @@ -363,7 +395,6 @@ TIP: 각각의 명령어의 헬프를 보기 위해서는 디버거의 프롬프 10: respond_to do |format| 11: format.html # index.html.erb 12: format.json { render json: @articles } - (byebug) ``` @@ -378,13 +409,13 @@ TIP: 각각의 명령어의 헬프를 보기 위해서는 디버거의 프롬프 ``` (byebug) where --> #0 ArticlesController.index - at /PathTo/project/test_app/app/controllers/articles_controller.rb:8 - #1 ActionController::ImplicitRender.send_action(method#String, *args#Array) - at /PathToGems/actionpack-5.0.0/lib/action_controller/metal/implicit_render.rb:4 + at /PathToProject/app/controllers/articles_controller.rb:8 + #1 ActionController::BasicImplicitRender.send_action(method#String, *args#Array) + at /PathToGems/actionpack-5.0.0/lib/action_controller/metal/basic_implicit_render.rb:4 #2 AbstractController::Base.process_action(action#NilClass, *args#Array) - at /PathToGems/actionpack-5.0.0/lib/abstract_controller/base.rb:189 - #3 ActionController::Rendering.process_action(action#NilClass, *args#NilClass) - at /PathToGems/actionpack-5.0.0/lib/action_controller/metal/rendering.rb:10 + at /PathToGems/actionpack-5.0.0/lib/abstract_controller/base.rb:181 + #3 ActionController::Rendering.process_action(action, *args) + at /PathToGems/actionpack-5.0.0/lib/action_controller/metal/rendering.rb:30 ... ``` @@ -393,18 +424,17 @@ TIP: 각각의 명령어의 헬프를 보기 위해서는 디버거의 프롬프 ``` (byebug) frame 2 -[184, 193] in /PathToGems/actionpack-5.0.0/lib/abstract_controller/base.rb - 184: # is the intended way to override action dispatching. - 185: # - 186: # Notice that the first argument is the method to be dispatched - 187: # which is *not* necessarily the same as the action name. - 188: def process_action(method_name, *args) -=> 189: send_action(method_name, *args) - 190: end - 191: - 192: # Actually call the method associated with the action. Override - 193: # this method if you wish to change how action methods are called, - +[176, 185] in /PathToGems/actionpack-5.0.0/lib/abstract_controller/base.rb + 176: # is the intended way to override action dispatching. + 177: # + 178: # Notice that the first argument is the method to be dispatched + 179: # which is *not* necessarily the same as the action name. + 180: def process_action(method_name, *args) +=> 181: send_action(method_name, *args) + 182: end + 183: + 184: # Actually call the method associated with the action. Override + 185: # this method if you wish to change how action methods are called, (byebug) ``` @@ -444,7 +474,9 @@ TIP: 각각의 명령어의 헬프를 보기 위해서는 디버거의 프롬프 12: format.json { render json: @articles } (byebug) instance_variables -[:@_action_has_layout, :@_routes, :@_headers, :@_status, :@_request, :@_response, :@_env, :@_prefixes, :@_lookup_context, :@_action_name, :@_response_body, :@marked_for_same_origin_verification, :@_config] +[:@_action_has_layout, :@_routes, :@_request, :@_response, :@_lookup_context, + :@_action_name, :@_response_body, :@marked_for_same_origin_verification, + :@_config] ``` 이와 같이, 컨트롤러에서 접근할 수 있는 모든 변수가 출력됩니다. 출력된 변수 목록은 코드의 실행과 함께 동적으로 갱신됩니다. 예를 들어, `next` 명령으로 한 라인을 실행했다고 해봅시다(이 명령에 대해서는 다음에 설명합니다). @@ -469,8 +501,10 @@ TIP: 각각의 명령어의 헬프를 보기 위해서는 디버거의 프롬프 그러면 instance_variables을 다시 한번 확인해보죠. ``` -(byebug) instance_variables.include? "@articles" -true +(byebug) instance_variables +[:@_action_has_layout, :@_routes, :@_request, :@_response, :@_lookup_context, + :@_action_name, :@_response_body, :@marked_for_same_origin_verification, + :@_config, :@articles] ``` 정의 부분이 실행된 것으로, 이번에는 `@articles`도 인스턴스 변수 목록에 포함되어 있습니다. @@ -482,11 +516,19 @@ TIP: `irb` 명령을 사용하는 것으로, **irb** 모드로 실행할 수도 ``` (byebug) help var -v[ar] cl[ass] show class variables of self -v[ar] const show constants of object -v[ar] g[lobal] show global variables -v[ar] i[nstance] show instance variables of object -v[ar] l[ocal] show local variables + + [v]ar + + Shows variables and its values + + + var all -- Shows local, global and instance variables of self. + var args -- Information about arguments of the current scope + var const -- Shows constants of an object. + var global -- Shows global variables. + var instance -- Shows instance variables of self or a specific object. + var local -- Shows local variables in current scope. + ``` 이 메소드는 현재 컨텍스트에서 변수의 값을 검사할 때에 유용한 방법입니다. 예를 들자면, 현 시점에서 지역 변수가 아무것도 정의되지 않은 상태인지 확인해봅시다. @@ -503,14 +545,16 @@ v[ar] l[ocal] show local variables @_start_transaction_state = {} @aggregation_cache = {} @association_cache = {} -@attributes = {"id"=>nil, "created_at"=>nil, "updated_at"=>nil} -@attributes_cache = {} -@changed_attributes = nil -... +@attributes = ## 5: where('created_at > ?', 1.week.ago).limit(limit) - 6: end - 7: - 8: end +[1, 6] in /PathToProject/app/models/article.rb + 1: class Article < ApplicationRecord + 2: def self.find_recent(limit = 10) + 3: byebug +=> 4: where('created_at > ?', 1.week.ago).limit(limit) + 5: end + 6: end (byebug) ``` @@ -555,9 +592,7 @@ Processing by ArticlesController#index as HTML ``` (byebug) next -앞의 프레임의 실행이 완료되었으므로, Next에 의해서 1개 위의 프레임으로 이동합니다. - -[4, 13] in /PathTo/project/test_app/app/controllers/articles_controller.rb +[4, 13] in /PathToProject/app/controllers/articles_controller.rb 4: # GET /articles 5: # GET /articles.json 6: def index @@ -592,7 +627,9 @@ Processing by ArticlesController#index as HTML (byebug) ``` -이것은 자신의 코드의, 나아가서 Ruby on Rails의 버그를 찾기 위한 무척 좋은 방법입니다. +이것은 자신의 코드의 버그를 찾기 위한 무척 좋은 방법입니다. + +TIP: `step n`이나 `next n`을 사용하여 `n`번 만큼 한번에 진행할 수 있습니다. ### 중단점 @@ -600,14 +637,14 @@ Processing by ArticlesController#index as HTML `break`(또는 `b`) 명령을 사용해서 중단점을 동적으로 추가할 수도 있습니다. 직접 중단점을 추가할 수 있는 방법은 아래의 3가지가 있습니다. -* `break line`: 현재 소스 파일의 _line_이 가리키는 줄에 중단점을 설정합니다. -* `break file:line [if expression]`: _file_의 _line_번째 줄에 중단점을 설정합니다. _expression_이 주어진 경우, 그 식이 _true_를 반환하는 경우에만 디버거가 실행됩니다. -* `break class(.|\#)method [if expression]`: _class_에 정의되어 있는 _method_에 중단점을 설정합니다('.'과 '\#'는 각각 클래스와 인스턴스 메소드를 가리킵니다). _expression_의 동작은 file:line의 경우와 같습니다. +* `break n`: 현재 소스 파일의 숫자 _n_이 가리키는 줄에 중단점을 설정합니다. +* `break file:line [if expression]`: _file_의 _n_번째 줄에 중단점을 설정합니다. _expression_이 주어진 경우, 그 식이 _true_를 반환하는 경우에만 디버거가 실행됩니다. +* `break class(.|\#)method [if expression]`: _class_에 정의되어 있는 _method_에 중단점을 설정합니다('.'과 '\#'는 각각 클래스와 인스턴스 메소드를 가리킵니다). _expression_의 동작은 file:n의 경우와 같습니다. 아까와 같은 상황으로 예시를 들어보겠습니다. ``` -[4, 13] in /PathTo/project/app/controllers/articles_controller.rb +[4, 13] in /PathToProject/app/controllers/articles_controller.rb 4: # GET /articles 5: # GET /articles.json 6: def index @@ -620,11 +657,11 @@ Processing by ArticlesController#index as HTML 13: end (byebug) break 11 -Created breakpoint 1 at /PathTo/project/app/controllers/articles_controller.rb:11 +Successfully created breakpoint with id 1 ``` -중단점을 목록으로 확인하기 위해서는 `info breakpoints `_n_이나 `info break `_n_을 사용합니다. 번호를 지정하면, 그 번호의 중단점을 목록 형태로 나타냅니다. 번호를 지정하지 않은 경우에는 모든 중단점을 보여줍니다. +`info breakpoints`를 사용하여 중단점 목록을 확인하세요. 숫자를 넘기면 그 번호의 중단점을 보여줍니다. 그렇지 않으면 모든 중단점을 보여줍니다. ``` (byebug) info breakpoints @@ -642,8 +679,8 @@ No breakpoints. 중단점을 활성화하거나, 무효로 만들 수도 있습니다. -* `enable breakpoints`: _breakpoints_로 지정한 중단점의 목록(지정하지 않은 경우는 모든 중단점)을 활성화합니다. 중단점은 활성화 상태로 생성됩니다. -* `disable breakpoints`: _breakpoints_로 지정한 중단점이 비활성화 되어, 해당 지점에서 디버거가 멈추지 않게 됩니다. +* `enable breakpoints [n [m [...]]]`: _breakpoints_로 지정한 중단점의 목록(지정하지 않은 경우는 모든 중단점)을 활성화합니다. 중단점은 활성화 상태로 생성됩니다. +* `disable breakpoints [n [m [...]]]`: _breakpoints_로 지정한 중단점이 비활성화 되어, 해당 지점에서 디버거가 멈추지 않게 됩니다. ### 예외 잡기 @@ -655,18 +692,18 @@ No breakpoints. 디버거로 정지된 애플리케이션을 재개하는 방법은 두 가지가 있습니다. -* `continue` [line-specification] \(또는 `c`): 스크립트가 직전에 정지되어 있던 주소로부터 프로그램의 실행을 재개합니다. 이 경우, 그때까지 설정되어 있던 중단점이 모두 무시됩니다. 옵션으로 특정 줄 번호를 한번만 유효한 중단점으로 설정할 수 있습니다.で指定できます。このワンタイムブレークポイントに達するとブレークポイントは削除されます。 -* `finish` [frame-number] \(또는 `fin`): 지정한 스택 프레임이 돌아올 때까지 계속해서 실행합니다. frame-number가 지정되어 있지 않은 경우에는 현재 선택되어 있는 프레임이 돌아올 때까지 실행합니다. 프레임의 위치가 지정되어 있지 않은(up이나 down, 또는 프레임 번호가 지정되어 있지 않은) 경우에는 현재 위치로부터 가장 가까운 프레임 또는 0프레임부터 시작합니다. 프레임 번호를 지정하면, 그 프레임이 돌아올 때까지 계속 실행합니다. +* `continue [n]`: 스크립트가 직전에 정지되어 있던 주소로부터 프로그램의 실행을 재개합니다. 이 경우, 그때까지 설정되어 있던 중단점이 모두 무시됩니다. 옵션으로 특정 줄 번호를 한번만 유효한 중단점으로 설정할 수 있습니다. +* `finish [n]`: 지정한 스택 프레임이 돌아올 때까지 계속해서 실행합니다. frame-number가 지정되어 있지 않은 경우에는 현재 선택되어 있는 프레임이 돌아올 때까지 실행합니다. 프레임의 위치가 지정되어 있지 않은(up이나 down, 또는 프레임 번호가 지정되어 있지 않은) 경우에는 현재 위치로부터 가장 가까운 프레임 또는 0프레임부터 시작합니다. 프레임 번호를 지정하면, 그 프레임이 돌아올 때까지 계속 실행합니다. ### 편집 디버거 상의 코드를 에디터에서 열기 위한 명령어는 두 가지가 있습니다. -* `edit [file:line]`: _file_을 에디터로 엽니다. 에디터는 EDITOR 환경 변수에 지정되어 있는 것을 사용합니다. _line_으로 몇번째 줄인지를 지정할 수도 있습니다. +* `edit [file:n]`: _file_을 에디터로 엽니다. 에디터는 EDITOR 환경 변수에 지정되어 있는 것을 사용합니다. _n_으로 몇번째 줄인지를 지정할 수 있습니다. ### 종료 -디버깅을 종료할 때에는 `quit` 명령(단축형은 `q`) 또는 별칭인 `exit`을 사용하세요.ㄴ +디버깅을 종료할 때에는 `quit` 명령(단축형은 `q`) 또는 별칭인 `exit`을 사용하세요. 아니면 `q!`를 입력하여 `Really quit? (y/n)`를 무시할 수 있습니다. quit을 실행하면 사실상 모든 스레드가 종료됩니다. 결과적으로 서버도 종료되므로, 재기동시킬 필요가 있습니다. @@ -674,20 +711,81 @@ quit을 실행하면 사실상 모든 스레드가 종료됩니다. 결과적으 `byebug`의 동작을 변경하기 위한 옵션이 몇가지 존재합니다. -* `set autoreload`: 소스 코드가 변경되면 다시 읽어옵니다(기본값: true). -* `set autolist`: 모든 중단점에서 `list` 명령을 실행합니다(기본값: true). -* `set listsize _n_`: 목록 표시의 줄수를 _n_개로 변경합니다(기본값: 10). -* `set forcestep`: `next`나 `step` 명령을 실행하면 항상 새로운 줄로 이동하게 됩니다. +``` +(byebug) help set + + set -모든 옵션을 확인하려면 `help set`를 실행하세요. 특정 `set` 명령을 확인하기 위해서는 `help set `_subcommand_를 실행하세요. + Modifies byebug settings + + Boolean values take "on", "off", "true", "false", "1" or "0". If you + don't specify a value, the boolean setting will be enabled. Conversely, + you can use "set no" to disable them. + + You can see these environment settings with the "show" command. + + List of supported settings: + + autosave -- Automatically save command history record on exit + autolist -- Invoke list command on every stop + width -- Number of characters per line in byebug's output + autoirb -- Invoke IRB on every stop + basename -- : information after every stop uses short paths + linetrace -- Enable line execution tracing + autopry -- Invoke Pry on every stop + stack_on_error -- Display stack trace when `eval` raises an exception + fullpath -- Display full file names in backtraces + histfile -- File where cmd history is saved to. Default: ./.byebug_history + listsize -- Set number of source lines to list by default + post_mortem -- Enable/disable post-mortem mode + callstyle -- Set how you want method call parameters to be displayed + histsize -- Maximum number of commands that can be stored in byebug history + savefile -- File where settings are saved to. Default: ~/.byebug_save +``` TIP: 이 설정들은 홈 폴더의 `.byebugrc` 파일에 저장해둘 수도 있습니다. 디버거가 실행되면, 이 설정이 전역으로 적용됩니다. 다음은 예시입니다. ```bash -set forcestep +set callstyle short set listsize 25 ``` +`web-console` 젬으로 디버깅하기 +----------------------------------- + +웹 콘솔은 `byebug`와 비슷하지만 브라우저에서 동작한다는 점이 다릅니다. 개발중인 페이지에서 뷰나 컨트롤러의 컨텍스트에 존재하는 콘솔을 요청할 수 있습니다. 콘솔은 HTML 요소들의 뒤에 랜더링됩니다. + +### 콘솔 + +컨트롤러의 액션이나 뷰에서 `console` 메소드로 콘솔을 호출할 수 있습니다. + +예를 들어, 컨트롤러라면, + +```ruby +class PostsController < ApplicationController + def new + console + @post = Post.new + end +end +``` + +뷰라면, + +```html+erb +<% console %> + +

New Post

+``` + +와 같이 호출할 수 있습니다. + +이는 뷰에 콘솔을 랜더링합니다. `console`의 호출 위치에 대해서 신경쓸 필요는 없습니다. 콘솔은 콘솔이 호출된 위치에 관계없이 HTML 요소들의 가장 마지막에 랜더링됩니다. + +콘솔은 순수한 Ruby 코드로 실행됩니다. 클래스를 선언하거나 초기화할 수 있고, 새 모델을 만들거나, 변수들을 검사할 수도 있습니다. + +NOTE: 요청 당 단 하나의 콘솔만을 랜더링할 수 있습니다. 그렇지 않으면 `web-console`이 두번째 `console` 호출시에 에러를 던질 것입니다. + 메모리 누수 디버깅 ------------------------------------ @@ -714,18 +812,10 @@ Valgrind의 설치 방법과 Ruby에서의 사용 방법에 대해서는 [Valgri * [Exception Notifier](https://github.com/smartinez87/exception_notification/tree/master): Rails 애플리케이션에서의 에러 발생시의 메일러 객체와 메일 통지 전송 템플릿의 기본 값을 제공합니다. * [Better Errors](https://github.com/charliesome/better_errors): Rails 표준 에러 페이지를 소스 코드나 변수 조사에 편리한 컨텍스트 정보를 추가하여 보여줍니다. * [RailsPanel](https://github.com/dejan/rails_panel): Rails 개발용의 Chrome 확장 기능입니다. 이것이 있으면 development.log에서 tail 명령을 실행할 필요가 없어집니다. Rails 애플리케이션의 요청에 대한 모든 정보를 브라우저 상(Developer Tools 패널)에서 볼 수 있습니다. db 시간, 랜더링 시간, 총 시간, 파라미터 리스트, 출력한 뷰 등을 볼 수 있습니다. +* [Pry](https://github.com/pry/pry): IRB를 대체할 수 있는 구현체입니다. 참고자료 ---------- -* [ruby-debug 홈페이지](http://bashdb.sourceforge.net/ruby-debug/home-page.html)(영어) -* [debugger 홈페이지](https://github.com/cldwalker/debugger)(영어) * [byebug 홈페이지](https://github.com/deivid-rodriguez/byebug)(영어) * [web-console 홈페이지](https://github.com/rails/web-console)(영어) -* [글: ruby-debug에서 Rails 애플리케이션을 디버깅하기](http://www.sitepoint.com/debug-rails-app-ruby-debug/)(영어) -* [Ryan Bates의 스크린 캐스트: Ruby 디버깅(개정판)](http://railscasts.com/episodes/54-debugging-ruby-revised)(영어) -* [Ryan Bates의 스크린 캐스트: 스택 트레이스](http://railscasts.com/episodes/24-the-stack-trace)(영어) -* [Ryan Bates의 스크린 캐스트: 로거](http://railscasts.com/episodes/56-the-logger)(영어) -* [ruby-debug를 사용한 디버깅](http://bashdb.sourceforge.net/ruby-debug.html)(영어) - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/development_dependencies_install.md b/guides/source/ko/development_dependencies_install.md index 33e552131ad63..48996d3f76952 100644 --- a/guides/source/ko/development_dependencies_install.md +++ b/guides/source/ko/development_dependencies_install.md @@ -1,153 +1,225 @@ -[Development Dependencies Install] 개발 의존성 설치하기 +개발 의존성 설치하기 ================================ -본 가이드는 루비 온 레일스 코어 개발을 위한 환경을 설정하는 방법을 다룹니다. [[[This guide covers how to setup an environment for Ruby on Rails core development.]]] +이 가이드는 Ruby on Rails 개발을 위한 환겨을 설치하는 방법을 설명합니다. -본 가이드를 읽은 후, 다음을 알게 됩니다.:[[[After reading this guide, you will know:]]] +이 가이드에서 다루는 내용: + +* Rails 개발 환경 설정하기 +* Rails 테스트에서 특정 그룹의 유닛 테스트를 실행하기 +* Rails 테스트의 Active Record의 일부를 실행하기 -------------------------------------------------------------------------------- -[The Easy Way] 쉬운 방법 +쉬운 방법 ------------ -개발 환경을 준비하기 위한 가장 손쉽고 추천되는 방법은 [Rails development box](https://github.com/rails/rails-dev-box)를 사용하는 것입니다. [[[The easiest and recommended way to get a development environment ready to hack is to use the [Rails development box](https://github.com/rails/rails-dev-box).]]] +개발 환경을 설정하는 가장 쉬운 방법은 [Rails development box](https://github.com/rails/rails-dev-box)를 사용하는 것입니다. -[The Hard Way] 어려운 방법 +어려운 방법 ------------ -위에서 본 것과 같이 레일스 개발 박스를 사용할 수 없디면, 루비 온 레일스 코어 개발을 위한 개발 박스를 수동으로 구축하는 단계가 다음에 있습니다. [[[In case you can't use the Rails development box, see section above, these are the steps to manually build a development box for Ruby on Rails core development.]]] - -### [Install Git] Git 설치 - -루비 온 레일스는 소스코드 제어를 위해 Git을 사용합니다. [Git homepage](http://git-scm.com/)에 설치 지침이 있습니다. Git에 익숙해지는 것을 도울 다양한 리소스들이 네트워크상에 있습니다. [[[Ruby on Rails uses Git for source code control. The [Git homepage](http://git-scm.com/) has installation instructions. There are a variety of resources on the net that will help you get familiar with Git:]]] - -* [Try Git course](http://try.github.io/)는 기초를 가르쳐주는 인터렉티브 코스입니다. [[[[Try Git course](http://try.github.io/) is an interactive course that will teach you the basics.]]] - -* [official Documentation](http://git-scm.com/documentation)는 매우 포괄적이고 또한 Git의 기초에 대한 약간의 동영상을 포함하고 있습니다. [[[The [official Documentation](http://git-scm.com/documentation) is pretty comprehensive and also contains some videos with the basics of Git]]] - -* [Everyday Git](http://schacon.github.io/git/everyday.html)은 Git을 사용할 때 충분한 내용을 가르쳐줄 것입니다. [[[[Everyday Git](http://schacon.github.io/git/everyday.html) will teach you just enough about Git to get by.]]] +Rails development box를 사용할 수 없는 경우라면, 아래에서 Ruby on Rails 개발 환경을 직접 만드는 방법을 확인하세요. -* Git의 [PeepCode screencast](https://peepcode.com/products/git) ($12)는 따라하기 쉽습니다. [[[The [PeepCode screencast](https://peepcode.com/products/git) on Git ($12) is easier to follow.]]] +### Git 설치하기 -* [GitHub](http://help.github.com)는 Git 리소스의 다양한 링크를 제공합니다. [[[[GitHub](http://help.github.com) offers links to a variety of Git resources.]]] +Ruby on Rails는 소스 코드 관리를 위해서 Git을 사용하고 있습니다. [Git 홈페이지](http://git-scm.com/)에서 설치하는 방법을 확인하세요. 인터넷 상에는 Git에 익숙해지는데 도움을 줄 수 있는 다양한 자료가 있습니다. -* [Pro Git](http://git-scm.com/book)은 크리에이티브 커먼스 라이센스로 제공되는 Git에 대한 온전한 책입니다. [[[[Pro Git](http://git-scm.com/book) is an entire book about Git with a Creative Commons license.]]] +* [Try Git 코스](http://try.github.io/)는 기초적인 사용법을 인터랙티브하게 알려줍니다. +* [공식 문서](http://git-scm.com/documentation)는 포괄적이며 Git의 기초를 설명하는 몇몇 동영상을 포함하고 있습니다. +* [Everyday Git](http://schacon.github.io/git/everyday.html)는 Git을 사용하기에 충분한 내용을 가르쳐줍니다. +* [GitHub](http://help.github.com)는 다양한 Git 자로에 대한 링크 목록을 제공합니다. +* [Pro Git](http://git-scm.com/book)는 Creative Commons license로 Git에 대한 책 전체를 제공합니다. -### [Clone the Ruby on Rails Repository] 루비 온 레일스 저장소 복제 +### Ruby on Rails 저장소 복제하기 -루비 온 레일스 소스 코드를 위치시키고자 하는 폴더로 이동하여(이 작업은 `rails` 서브디렉터리를 만듭니다) 다음을 실행합니다.: [[[Navigate to the folder where you want the Ruby on Rails source code (it will create its own `rails` subdirectory) and run:]]] +Ruby on Rails 소스 코드를 저장하고 싶은 폴더로 이동하세요(그 폴더에 `rails`라는 폴더를 생성할 것입니다). 그리고 다음을 실행하세요. ```bash $ git clone git://github.com/rails/rails.git $ cd rails ``` -### [Set up and Run the Tests] 설정 및 테스트 실행 +### 설정과 테스트 실행하기 -테스트 스위트는 모든 서브밋된 코드를 통과해야 합니다. 새로운 패치를 작성하였건, 다른 사람의 것을 평가하건 상관없이, 테스트를 수행할 수 있어야 합니다. [[[The test suite must pass with any submitted code. No matter whether you are writing a new patch, or evaluating someone else's, you need to be able to run the tests.]]] +모든 제출된 코드는 테스트를 통과해야 합니다. 새 패치를 추가하든, 다른 사람의 코드를 평가하든, 테스트를 실행할 수 있어야 합니다. -먼저 Nokogiri에 대한 libxml2와 libxslt를 개발용 파일과 함께 설치합니다. 우분투의 경우는 [[[Install first libxml2 and libxslt together with their development files for Nokogiri. In Ubuntu that's]]] +우선 SQLite3과 `sqlite3` 젬을 위한 개발용 파일을 설치하세요. Max OS X 사용자라면 다음을 실행하세요. ```bash -$ sudo apt-get install libxml2 libxml2-dev libxslt1-dev +$ brew install sqlite3 ``` -만약 Fedora나 CentOS를 사용하고 있다면, 다음을 실행할 수 있습니다. [[[If you are on Fedora or CentOS, you can run]]] +Ubuntu 사용자라면 apt-get을 사용하세요. ```bash -$ sudo yum install libxml2 libxml2-devel libxslt libxslt-devel +$ sudo apt-get install sqlite3 libsqlite3-dev ``` -만약 이 라이브러리들을 설치하는데 문제가 있다면, 소스코드를 수동으로 컴파일하여 이 라이브러리들을 설치해야 합니다. [Red Hat/CentOS section of the Nokogiri tutorials](http://nokogiri.org/tutorials/installing_nokogiri.html#red_hat__centos)에 있는 지침을 따라 하십시오. [[[If you have any problems with these libraries, you should install them manually compiling the source code. Just follow the instructions at the [Red Hat/CentOS section of the Nokogiri tutorials](http://nokogiri.org/tutorials/installing_nokogiri.html#red_hat__centos) .]]] +Fedora나 CentOS 사용자라면 yum을 사용하세요. + +```bash +$ sudo yum install sqlite3 sqlite3-devel +``` -또한, `sqlite3-ruby` 젬을 위한 SQLite3와 개발용 파일들을, 우분투의 경우 다음과 같이 하면 됩니다. [[[Also, SQLite3 and its development files for the `sqlite3-ruby` gem — in Ubuntu you're done with just]]] +Arch Linux 사용자라면 다음을 실행하세요. ```bash -$ sudo apt-get install sqlite3 libsqlite3-dev +$ sudo pacman -S sqlite ``` -만약 Fedora나 CentOS를 사용하고 있다면, 다음과 같이 합니다. [[[And if you are on Fedora or CentOS, you're done with]]] +FreeBSD 사용자라면 다음을 실행하세요. ```bash -$ sudo yum install sqlite3 sqlite3-devel +# pkg install sqlite3 ``` -[Bundler](http://gembundler.com/)의 최신 버전을 얻습니다. [[[Get a recent version of [Bundler](http://gembundler.com/)]]] +또는 `databases/sqlite3` 포트를 컴파일하세요. + +[Bundler](http://bundler.io/)의 최신 버전을 설치하세요. ```bash $ gem install bundler $ gem update bundler ``` -그리고 다음을 실행합니다. [[[and run:]]] +그리고 다음을 실행하세요. ```bash $ bundle install --without db ``` -이 명령은 MySQL과 PostgreSQL 루비 드라이버를 제외한 모든 의존성을 설치합니다. 이것은 곧 다를 것입니다. 의존성 설치를 마치면, 다음과 같이 테스트 스위트를 실행할 수 있습니다. [[[This command will install all dependencies except the MySQL and PostgreSQL Ruby drivers. We will come back to these soon. With dependencies installed, you can run the test suite with:]]] +이 명령을 통해서 MySQL과 PostgreSQL 드라이버를 제외한 모든 의존성을 설치합니다. + +NOTE: memcached를 사용하는 테스트를 실행하고 싶다면 미리 이를 설치해야 합니다. + +OS X 사용자는 [Homebrew](http://brew.sh/)를 사용하여 memcached를 설치할 수 있습니다. + +```bash +$ brew install memcached +``` + +Ubuntu 사용자라면 apt-get을 사용하세요. + +```bash +$ sudo apt-get install memcached +``` + +Fadora나 CentOS라면 yum을 사용하세요. + +```bash +$ sudo yum install memcached +``` + +Arch Linux 사용자라면 다음을 실행하세요. + +```bash +$ sudo pacman -S memcached +``` + +FreeBSD 사용자라면 다음을 실행하세요. + +```bash +# pkg install memcached +``` + +또는 `databases/memcached` 포트를 컴파일하세요. + +이제 의존성을 모두 설치하였으니 다음의 명령으로 테스트를 실행할 수 있습니다. ```bash $ bundle exec rake test ``` -물론 Action Pack처럼, 특정 컴포넌트를 위한 테스트를 실행할 수도 있습니다. 컴포넌트의 디렉터리로 들어가서 동일한 명령을 수행합니다. [[[You can also run tests for a specific component, like Action Pack, by going into its directory and executing the same command:]]] +Action Pack과 같은 특정 컴포넌트의 테스트만을 실행할 수도 있습니다. 해당 폴더로 이동하여 같은 명령을 실행하세요. ```bash $ cd actionpack $ bundle exec rake test ``` -만약 특정 디렉터리에 위치한 테스트를 실행하고자 한다면 `TEST_DIR` 환경 변수를 사용하십시오. 예를 들어, 다음 명령은 `railties/test/generators`의 테스트만 실행할 것입니다. [[[If you want to run the tests located in a specific directory use the `TEST_DIR` environment variable. For example, this will run the tests of the `railties/test/generators` directory only:]]] +`TEST_DIR` 환경 변수를 사용하여 특정 폴더에 있는 테스트만을 실행하고 싶을 수도 있습니다. 예를 들어 다음 명령은 `railties/test/generators` 폴더에 있는 테스트만을 실행합니다. ```bash $ cd railties $ TEST_DIR=generators bundle exec rake test ``` -개별적으로 특정 단일 테스트만 수행할 수도 있습니다. [[[You can run any single test separately too:]]] +다음처럼 특정 파일만을 실행할 수도 있습니다. ```bash $ cd actionpack $ bundle exec ruby -Itest test/template/form_helper_test.rb ``` -### [Active Record Setup] 액티브 레코드 설정 +또는 특정 파일의 한 테스트만을 실행할 수도 있습니다. -액티브 레코드의 테스트 스위트는 네번 실행을 시도합니다. 한번은 SQLite3, 한번은 두개의 MySQL 젬들(`mysql`과 `mysql2`), 그리고 한번은 PostgreSQL에 대한 실행입니다. 이제 그들을 위한 환경을 설정하는 방법을 보겠습니다. [[[The test suite of Active Record attempts to run four times: once for SQLite3, once for each of the two MySQL gems (`mysql` and `mysql2`), and once for PostgreSQL. We are going to see now how to set up the environment for them.]]] +```bash +$ cd actionpack +$ bundle exec ruby -Itest path/to/test.rb -n test_name +``` + +### Active Record 설정하기 + +Active Record의 테스트는 3번 동작합니다. 한 번은 SQLite3으로, 또 한 번은 MySQL로, 나머지 한번은 PostgreSQL로 말이죠. 이들을 위한 환경을 구성하는 방법에 대해서 알아보겠습니다. -WARNING: 만약 액티브 레코드 코드로 작업하려면, 적어도 MySQL, PostgreSQL, 그리고 SQLite3을 위한 테스트를 통과하는지 _반드시_ 확인해야 합니다. MySQL로만 테스트했을 때 문제 없었던 많은 패치를 거부한 배후에는 다양한 어댑터간의 미묘한 차이가 있습니다. [[[WARNING: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, and SQLite3. Subtle differences between the various adapters have been behind the rejection of many patches that looked OK when tested only against MySQL.]]] +WARNING: 만약 Active Record 코드 상에서 작업을 하고 있다면 _반드시_ 적어도 MySQL, PostgreSQL, SQLite3 환경에서 테스트가 성공해야 합니다. 다양한 어댑터간의 사소한 차이가 MySQL에서만 테스트되었던 많은 패치들이 거절된 이유입니다. -#### [Database Configuration] 데이터베이스 구성 +#### 데이터베이스 설정하기 -액티브 레코드 테스트 스위트는 사용자 정의 구성 파일 `activerecord/test/config.yml`이 필요합니다. 예제는 `activerecord/test/config.example.yml` 내에 제공되는데, 이를 복사하여 자신의 환경에 맞게 사용할 수 있습니다. [[[The Active Record test suite requires a custom config file: `activerecord/test/config.yml`. An example is provided in `activerecord/test/config.example.yml` which can be copied and used as needed for your environment.]]] +Active Record 테스트는 커스텀 설정 파일(`activerecord/test/config.yml`)을 요구합니다. `activerecord/test/config.example.yml`에 샘플이 제공되고 있으므로 환경에 맞게 변경하여 사용하세요. -#### [MySQL and PostgreSQL] MySQL과 PostgreSQL +#### MySQL과 PostgreSQL -MySQL과 PostgreSQL을 위한 스위트를 실행할 수 있게 하려면 이들을 위한 젬이 필요합니다. 먼저 서버를 설치하고, 클라이언트 라이브러리들을 설치하고, 개발용 파일들을 설치합니다. 우분투에서는 다음과 같이 실행합니다. [[[To be able to run the suite for MySQL and PostgreSQL we need their gems. Install first the servers, their client libraries, and their development files. In Ubuntu just run]]] +MySQL과 PostgreSQL에서 테스트를 실행하려면 필요한 젬을 설치해야 합니다. 서버와 클라이언트 라이브러리, 그리고 개발용 파일들을 설치하세요. + +OS X 사용자는 다음을 실행하세요. ```bash -$ sudo apt-get install mysql-server libmysqlclient15-dev +$ brew install mysql +$ brew install postgresql +``` + +그리고 Homebrew가 제공하는 설명을 따라가세요. + +Ubuntu 사용자라면 다음을 실행하세요. + +```bash +$ sudo apt-get install mysql-server libmysqlclient-dev $ sudo apt-get install postgresql postgresql-client postgresql-contrib libpq-dev ``` -Fedora나 CentOS에서는 다음을 실행합니다. [[[On Fedora or CentOS, just run:]]] +Fedora나 CentOS 사용자라면 다음을 실행하세요. ```bash $ sudo yum install mysql-server mysql-devel $ sudo yum install postgresql-server postgresql-devel ``` -그 후, 다음을 실행합니다. [[[After that run:]]] +만약 Arch Linux 사용자라면 MySQL이 더 이상 지원되지 않으며, MariaDB를 사용해야한다는 점을 알려드립니다([이 공지](https://www.archlinux.org/news/mariadb-replaces-mysql-in-repositories/)를 참고하세요). + +```bash +$ sudo pacman -S mariadb libmariadbclient mariadb-clients +$ sudo pacman -S postgresql postgresql-libs +``` + +FreeBSD 사용자라면 다음을 실행하세요. + +```bash +# pkg install mysql56-client mysql56-server +# pkg install postgresql94-client postgresql94-server +``` + +그리고 다음을 실행하세요. ```bash $ rm .bundle/config $ bundle install ``` -먼저 `.bundle/config`를 삭제할 필요가 있는데, 그 이유는 "db" 그룹(아니면 파일에서 수정할 수 있는)을 설치하고자 하지 않았던 파일들을 번들러가 기억하고 있기 때문입니다. [[[We need first to delete `.bundle/config` because Bundler remembers in that file that we didn't want to install the "db" group (alternatively you can edit the file).]]] +우선 Bundler가 "db" 그룹을 설치하지 않았다는 사실을 기억하고 있기 때문에 `.bundle/config`를 지웁니다(또는 변경해도 좋습니다). -MySQL에 대한 테스트 스위트를 실행할 수 있게 하려면 테스트 데이터베이스에 권한을 가진 `rails`라는 이름의 사용자를 생성할 필요가 있습니다. [[[In order to be able to run the test suite against MySQL you need to create a user named `rails` with privileges on the test databases:]]] +MySQL에 대한 테스트를 실행하려면 `rails`라는 사용자를 만들고 테스트 데이터베이스에 접근할 수 있는 권한을 부여해야 합니다. ```bash $ mysql -uroot -p @@ -157,44 +229,95 @@ mysql> GRANT ALL PRIVILEGES ON activerecord_unittest.* to 'rails'@'localhost'; mysql> GRANT ALL PRIVILEGES ON activerecord_unittest2.* to 'rails'@'localhost'; +mysql> GRANT ALL PRIVILEGES ON inexistent_activerecord_unittest.* + to 'rails'@'localhost'; ``` -그리고 테스트 데이터베이스를 생성합니다.: [[[and create the test databases:]]] +그리고 테스트 데이터베이스를 생성합니다. ```bash $ cd activerecord -$ bundle exec rake mysql:build_databases +$ bundle exec rake db:mysql:build ``` -PostgreSQL의 인증은 다르게 작동합니다. 예제를 위한 개발 환경을 설정하는 쉬운 방법은 개발 계정으로 실행하는 것입니다. [[[PostgreSQL's authentication works differently. A simple way to set up the development environment for example is to run with your development account]]] +PostgreSQL의 인증은 조금 다르게 동작합니다. Linux나 BSD일 경우에 개발용 계정과 개발용 환경을 설정하려면 다음을 실행하세요. ```bash $ sudo -u postgres createuser --superuser $USER ``` -그리고나서 다음과 같이 테스트 데이터베이스를 생성합니다. [[[and then create the test databases with]]] +OS X 사용자는 다음을 실행하세요. + +```bash +$ createuser --superuser $USER +``` + +그리고 테스트 데이터베이스를 생성하세요. ```bash $ cd activerecord -$ bundle exec rake postgresql:build_databases +$ bundle exec rake db:postgresql:build ``` -PostgreSQL과 MySQL 모두를 위한 데이터베이스를 만들 수도 있습니다. [[[It is possible to build databases for both PostgreSQL and MySQL with]]] +그러면 이제 PostgreSQL과 MySQL에서 데이터베이스를 구성할 수 있습니다. ```bash $ cd activerecord $ bundle exec rake db:create ``` -다음과 같이 데이터베이스를 정리할 수 있습니다. [[[You can cleanup the databases using]]] +다음을 통해서 데이터베이스를 제거할 수도 있습니다. ```bash $ cd activerecord $ bundle exec rake db:drop ``` -NOTE: 테스트 데이터베이스를 생성하기 위해 rake 태스크를 사용하는 것은 올바른 캐릭터셋과 정렬(collation)을 보장합니다. [[[NOTE: Using the rake task to create the test databases ensures they have the correct character set and collation.]]] +NOTE: rake 명령을 사용하여 테스트 데이터베이스를 만들면, 올바른 문자 형식과 collation으로 생성할 수 있습니다. + +NOTE: PostgreSQL 9.1.x 이하에서 HStore 익스텐션을 활성화하는 도중에 "WARNING: => is deprecated as an operator"와 같은 경고를 볼 수 있습니다. + +만약 다른 데이터베이스를 사용하고 있다면 `activerecord/test/config.yml`나 `activerecord/test/config.example.yml`를 확인하고 기본 연결 설정에 대해서 확인하세요. 필요하다면 컴퓨터에 있는 별도의 자격 인증을 `activerecord/test/config.yml`에 추가해도 좋습니다. 하지만 이러한 변경사항들을 Rails에 커밋해서는 안됩니다. + +### Action Cable 설정하기 + +Action Cable은 Redis를 기본 구독 어댑터([더 읽기](action_cable_overview.html#브로드캐스트))로 사용합니다. 그러므로 Action Cable 테스트를 위해서는 Redis를 설치하고 실행중인 상태이어야 합니다. + +#### Redis를 소스로부터 설치하기 -NOTE: PostgreSQL 9.1.x 혹은 그 이전 버전에서는 HStore 확장을 활성화하는 동안 다음과 같은 경고(혹은 지역화된 경고)를 볼 것입니다: "WARNING: => is deprecated as an operator" [[[NOTE: You'll see the following warning (or localized warning) during activating HStore extension in PostgreSQL 9.1.x or earlier: "WARNING: => is deprecated as an operator".]]] +Redis 문서에서는 버전이 자주 갱신되기 때문에 패키지 매니저를 통한 설치를 권장하지 않습니다. 소스로부터 설치하고 이를 서버에 적용하는 방법에 대해서는 [Redis 설치하기 문서](http://redis.io/download#installation)에 잘 기술되어 있습니다. -만약 다른 데이터베이스를 사용한다면, 기본 접속 정보를 위해 `activerecord/test/config.yml` 혹은 `activerecord/test/config.example.yml` 파일을 살펴 보십시오. 만약 머신에 반드시 다른 종류의 자격증명을 제공해야만 한다면, `activerecord/test/config.yml`를 수정할 수 있습니다. 하지만 그러한 변경의 어떠한 부분이라도 Rails로 푸시하지 않아야 합니다. [[[If you’re using another database, check the file `activerecord/test/config.yml` or `activerecord/test/config.example.yml` for default connection information. You can edit `activerecord/test/config.yml` to provide different credentials on your machine if you must, but obviously you should not push any such changes back to Rails.]]] +#### Redis를 패키지 매니저를 통해 설치하기 + +OS X 사용자라면 다음을 실행하세요. + +```bash +$ brew install redis +``` + +그리고 Homebrew가 제공하는 지시를 따르세요. + +Ubuntu 사용자라면 다음을 실행하세요. + +```bash +$ sudo apt-get install redis-server +``` + +Fedora나 CentOS(EPEL이 활성화되어 있어야 합니다) 사용자는 다음을 실행하세요. + +```bash +$ sudo yum install redis +``` + +Arch Linux 사용자라면 다음을 실행하세요. + +```bash +$ sudo pacman -S redis +$ sudo systemctl start redis +``` + +FreeBSD 사용자라면 다음을 실행하세요. + +```bash +# portmaster databases/redis +``` diff --git a/guides/source/ko/documents.yaml b/guides/source/ko/documents.yaml index 19030c1dccb01..1f0b4f8e42eef 100644 --- a/guides/source/ko/documents.yaml +++ b/guides/source/ko/documents.yaml @@ -34,9 +34,10 @@ url: active_record_querying.html description: Active Record가 제공하는 모든 데이터베이스 쿼리 인터페이스에 대해서 설명합니다. - - name: Active Model + name: Active Model Basics url: active_model_basics.html description: Active Record를 사용하지 않고 모델 클래스를 다루는 방법에 대해서 설명합니다. + work_in_progress: true - name: View documents: @@ -53,28 +54,6 @@ name: Action View Form Helper url: form_helpers.html description: 내장되어있는 폼 헬퍼에 대해서 설명합니다. - - - name: "레일스에서 캐싱하기" - url: caching_with_rails.html - description: 캐시를 사용하여 레일스 애플리케이션을 빠르게 만드는 방법에 대해서 설명합니다. - - - name: Active Support Instrumentation - work_in_progress: true - url: active_support_instrumentation.html - description: 액티브서포트를 통해 레일스나 다른 루비 코드에서 이벤트를 계측하는 방법에 대해서 설명합니다. - - - name: 레일스 애플리케이션 프로파일링 - work_in_progress: true - url: profiling.html - description: 성능을 향상시키기 위해 레일스 애플리케이션을 분석하는 방법에 대해서 설명합니다. - - - name: 레일스로 API 전용 애플리케이션 만들기 - url: api_app.html - description: 레일스를 통해 효율적으로 JSON API 애플리케이션을 만드는 방법에 대해서 설명합니다. - - - name: Action Cable 살펴보기 - url: action_cable_overview.html - description: 실시간 기능을 구현하기 위해 액션케이블이 어떻게 동작하는지, 웹소켓을 어떻게 사용하는지에 대해서 설명합니다. - name: Controller documents: @@ -98,11 +77,11 @@ url: i18n.html description: 레일스 애플리케이션에서 국제화를 지원하는 방법에 대해서 설명합니다. 이를 이용하면 애플리케이션을 서로 다른 언어로 번역할 수 있으며, 단수형/복수형에 대한 규칙을 변경하거나, 각 나라에 맞는 날짜 형식을 설정할 수 있습니다. - - name: Action Mailer + name: Action Mailer Basics url: action_mailer_basics.html description: Action Mailer를 사용해서 메일을 송수신하는 방법에 대해 설명합니다. - - name: Active Job + name: Active Job Basics url: active_job_basics.html description: 백그라운드에서 동작하는 잡(Job)을 만들고, 큐에 전송하고 실행하는데에 있어 필요한 모든 것을 설명합니다. - @@ -142,6 +121,29 @@ name: 상수 자동 읽기와 다시 읽기 url: autoloading_and_reloading_constants.html description: 상수 자동 읽기와 다시 읽기가 어떻게 이루어지는지 설명합니다. + - + name: "레일스에서 캐싱하기" + url: caching_with_rails.html + description: 캐시를 사용하여 레일스 애플리케이션을 빠르게 만드는 방법에 대해서 설명합니다. + - + name: Active Support Instrumentation + work_in_progress: true + url: active_support_instrumentation.html + description: 액티브서포트를 통해 레일스나 다른 루비 코드에서 이벤트를 계측하는 방법에 대해서 설명합니다. + - + name: 레일스 애플리케이션 프로파일링 + work_in_progress: true + url: profiling.html + description: 성능을 향상시키기 위해 레일스 애플리케이션을 분석하는 방법에 대해서 설명합니다. + - + name: 레일스로 API 전용 애플리케이션 만들기 + url: api_app.html + description: 레일스를 통해 효율적으로 JSON API 애플리케이션을 만드는 방법에 대해서 설명합니다. + - + name: Action Cable 살펴보기 + url: action_cable_overview.html + description: 실시간 기능을 구현하기 위해 액션케이블이 어떻게 동작하는지, 웹소켓을 어떻게 사용하는지에 대해서 설명합니다. + - name: 레일스 확장하기 documents: @@ -170,10 +172,6 @@ name: Ruby on Rails에 기여하는 방법 url: contributing_to_ruby_on_rails.html description: Rails는 '어떤 다른 사람의 프레임워크'가 아닙니다. 현재도 계속되고 있는 Rails 프레임워크 개발에 기여하는 다양한 방법에 대해서 설명합니다. - - - name: 레일스 코어 개발 환경 구축 방법 - url: development_dependencies_install.html - description: Ruby on Rails 자체의 개발 환경을 구축하는 방법에 대해서 설명합니다. - name: API 문서 작성 가이드라인 url: api_documentation_guidelines.html diff --git a/guides/source/ko/form_helpers.md b/guides/source/ko/form_helpers.md index 5a0ecc0a67f48..f57e6e08ee943 100644 --- a/guides/source/ko/form_helpers.md +++ b/guides/source/ko/form_helpers.md @@ -33,16 +33,16 @@ NOTE: 이 가이드에서는 폼 헬퍼와 그 인수에 대한 모든 것을 이 코드처럼 인수 없이 호출하게 되면 `
` 태그를 생성합니다. 이 폼의 목적지는 현재 페이지로, HTTP POST가 사용됩니다. 예를 들어, 현재 페이지가 `/home/index`인 경우 아래와 같은 HTML이 생성됩니다(읽기 쉽게끔 개행을 추가 했습니다). ```html - -
- - -
+ + + Form contents
``` -이 폼을 잘 보면 이상한 부분이 있다는 것을 눈치채셨나요? `div` 태그 내부에 2개의 hidden input이 있습니다. 이 div는 생략할 수 없습니다. 이것이 없으면 폼이 정상적으로 전송할 수 없습니다. 처음의 `utf8` hidden input은 브라우저에게 폼에서 해당하는 문자 인코딩을 사용할 것을 강제합니다. 이것은 액션이 "GET"과 "POST"의 어느쪽이라도 모두 생성됩니다. 두번째의 hidden input인 `authenticity_token`는 **cross-site fequest forgery protection**를 위한 보안기능입니다. 이 요소는 GET을 사용하지 않는 모든 폼에서 생성됩니다(보안 기능이 활성화 되어있는 경우). 자세한 설명은 [보안 가이드](security.html#Cross_Site_Request_Forgery_csrf)를 참조해주세요. +이 폼을 잘 보면 이상한 부분이 있다는 것을 눈치채셨나요? `div` 태그 내부에 2개의 hidden input이 있습니다. 이 div는 생략할 수 없으며, 없으면 폼이 정상적으로 전송할 수 없습니다. 처음의 `utf8` hidden input은 브라우저에게 폼에서 해당하는 문자 인코딩을 사용할 것을 강제합니다. 이것은 액션이 "GET"과 "POST"의 어느쪽이라도 모두 생성됩니다. + +두번째의 hidden input인 `authenticity_token`는 **cross-site fequest forgery protection**를 위한 보안기능입니다. 이 요소는 GET을 사용하지 않는 모든 폼에서 생성됩니다(보안 기능이 활성화 되어있는 경우). 자세한 설명은 [보안 가이드](security.html#cross-site-request-forgery-csrf)를 참조해주세요. ### 일반적인 검색 폼 @@ -67,7 +67,8 @@ NOTE: 이 가이드에서는 폼 헬퍼와 그 인수에 대한 모든 것을 이 코드로부터 아래의 HTML이 생성됩니다. ```html -
+ + @@ -161,7 +162,6 @@ NOTE: 체크 박스와 라디오 버튼에는 반드시 label 태그를 함께 <%= search_field(:user, :name) %> <%= telephone_field(:user, :phone) %> <%= date_field(:user, :born_on) %> -<%= datetime_field(:user, :meeting_time) %> <%= datetime_local_field(:user, :graduation_day) %> <%= month_field(:user, :birthday_month) %> <%= week_field(:user, :birthday_week) %> @@ -182,7 +182,6 @@ NOTE: 체크 박스와 라디오 버튼에는 반드시 label 태그를 함께 - @@ -264,7 +263,12 @@ end
``` -`form_for`에 넘기는 이름은 `params`를 사용해서 넘어온 폼의 정보값이 들어있는 키 이름에 영향을 줍니다. 예를 들어, 이 이름이 `article`이라면 모든 input 태그는 `article[속성명]`이라는 폼 name 속성을 가지게 됩니다. 따라서 `create` 액션에서는 `:title` 키와 `:body` 키를 가지는 하나의 해시가 `params[:article]`에 포함됩니다. input의 name 속성의 중요성에 대해서는 [파라미터의 명명 규칙 이해하기](#파라미터의 명명 규칙 이해하기)를 참조해주세요. +`form_for`에 넘기는 이름은 `params`를 사용해서 넘어온 폼의 정보값이 들어있는 +키 이름에 영향을 줍니다. 예를 들어, 이 이름이 `article`이라면 모든 input 태그는 +`article[속성명]`이라는 폼 name 속성을 가지게 됩니다. 따라서 `create` +액션에서는 `:title` 키와 `:body` 키를 가지는 하나의 해시가 `params[:article]`에 +포함됩니다. input의 name 속성의 중요성에 대해서는 +[파라미터의 명명 규칙 이해하기](#파라미터의-명명-규칙-이해하기)를 참조해주세요. 폼 빌더 변수에 대해서 호출되는 헬퍼 메소드는 모델 객체의 헬퍼 메소드와 같습니다. 단, 폼의 경우는 대상이 되는 객체가 이미 폼 빌더에 의해서 관리되고 있기 때문에, 어떤 객체에 대해서 생성할지 지정할 필요가 없다는 점이 다릅니다. @@ -336,8 +340,7 @@ form_for [:admin, @article] form_for [:admin, :management, @article] ``` -Rails의 라우팅 시스템의 자세한 설명과 관련된 규칙에 대해서는 [Rails 라우팅](routing.html)을 참조해주세요. - +Rails의 라우팅 시스템의 자세한 설명과 관련된 규칙에 대해서는 [라우팅 가이드](routing.html)을 참조해주세요. ### 폼에서의 PATCH, PUT, DELETE 메소드 동작 @@ -430,9 +433,14 @@ WARNING: `:include_blank`나 `:prompt`가 지정되어 있지 않을 때, 선택 해시를 사용하여 임의의 값을 추가할 수도 있습니다. ```html+erb -<%= options_for_select([['Lisbon', 1, {'data-size' => '2.8 million'}], ['Madrid', 2, {'data-size' => '3.2 million'}]], 2) %> +<%= options_for_select( + [ + ['Lisbon', 1, { 'data-size' => '2.8 million' }], + ['Madrid', 2, { 'data-size' => '3.2 million' }] + ], 2 +) %> -이 코드로부터는 아래와 같은 결과를 얻을 수 있습니다. +output: @@ -547,7 +555,8 @@ Date.civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, pa ### 모델 객체 헬퍼 -`select_date` 헬퍼는 Active Record 객체를 변경/생성하는 폼에서는 사용하기 어렵게 되어있습니다. Active Record는 `param` 해시에 포함되는 요소가 각각 1개의 속성에 대응될 것을 전제로 하고 있기 때문입니다. 날짜/시각용의 모델 객체 헬퍼는 특별한 이름을 사용해서 값을 전송합니다. Active Record는 이 특별한 이름을 발견하면 다른 파라미터를 위한 값이라고 추측하고, 컬럼의 종류에 맞는 생성자가 있을 것이라고 생각합니다. 예를 들어, +`select_date` 헬퍼는 Active Record 객체를 변경/생성하는 폼에서는 사용하기 어렵게 되어있습니다. Active Record는 `param` 해시에 포함되는 요소가 각각 1개의 속성에 대응될 것을 전제로 하고 있기 때문입니다. +날짜/시각용의 모델 객체 헬퍼는 특별한 이름을 사용해서 값을 전송합니다. Active Record는 이 특별한 이름을 발견하면 다른 파라미터를 위한 값이라고 추측하고, 컬럼의 종류에 맞는 생성자가 있을 것이라고 생각합니다. 예를 들어, ```erb <%= date_select :person, :birth_date %> @@ -624,7 +633,7 @@ end ``` 파일이 업로드된 이후에도 해야할 일이 잔뜩 있습니다. 파일을 어디에 저장(웹서버, Amazon S3 등)할지, -모델과의 관계 설정, 이미지라면 크기 변경이나 섬네일 생성 작업 등이 필요합니다. 이러한 처리들은 이 가이드의 설명 범위를 벗어나므로 다루지 않습니다만, 이러한 처리를 도와주기 위한 라이브러리가 있다는 정도는 알아두면 좋을 겁니다. 그 중에서도 [CarrierWave](https://github.com/jnicklas/carrierwave)와 [Paperclip](http://www.thoughtbot.com/projects/paperclip)이 유명합니다. +모델과의 관계 설정, 이미지라면 크기 변경이나 섬네일 생성 작업 등이 필요합니다. 이러한 처리들은 이 가이드의 설명 범위를 벗어나므로 다루지 않습니다만, 이러한 처리를 도와주기 위한 라이브러리가 있다는 정도는 알아두면 좋을 겁니다. 그 중에서도 [CarrierWave](https://github.com/jnicklas/carrierwave)와 [Paperclip](https://github.com/thoughtbot/paperclip)이 유명합니다. NOTE: 사용자가 파일을 선택하지 않고 업로드를 하게 되면 빈 문자열이 파라미터로 넘어오게 됩니다. @@ -663,6 +672,13 @@ end 이 클래스를 자주 사용한다면 `labeled_form_for` 헬퍼를 정의하고 `builder: LabellingFormBuilder` 옵션을 포함하도록 해두면 편할 겁니다. +```ruby +def labeled_form_for(record, options = {}, &block) + options.merge! builder: LabellingFormBuilder + form_for record, options, &block +end +``` + 여기서 사용되는 폼 빌더는 아래의 코드가 실행된 순간의 동작도 결정합니다. ```erb @@ -678,13 +694,6 @@ end 원칙적으로 HTML 폼은 어떤 형태의 구조화라도 상관하지 않습니다. 폼이 생성하는 것은 모두 이름과 이에 맞는 값의 쌍이며, 이것들은 단순한 문자열입니다. 이 데이터들을 애플리케이션 쪽에서 참조할 때에 배열이나 해시의 형태인 것은 Rails에서 사용하고 있는 파라미터 명명 규칙 덕분입니다. -TIP: Rack의 파라미터 파서를 콘솔에서 직접 호출해서 이 절의 예제를 좀 더 쉽게 해볼 수 있습니다. 예를 들어, 다음처럼 쓸 수 있습니다. - -```ruby -Rack::Utils.parse_query "name=fred&phone=0123456789" -# => {"name"=>"fred", "phone"=>"0123456789"} -``` - ### 기본 구조 배열과 해시는 기본이 되는 2대 구조입니다. 해시는 `params`의 값에 접근할 때 사용되는 문법에서 사용됩니다. 예를 들어 폼에 다음과 같은 것들이 포함되어있다고 해봅시다. @@ -804,7 +813,7 @@ Rails의 일반적인 규칙 중에는 최종적인 입력값은 `fields_for`나 외부 리소스로 임의의 데이터를 전송하고 싶은 경우에도 Rails의 폼 헬퍼를 사용해서 폼을 생성하는 것이 편리합니다. 다만 이 때, 외부 리소스에 대해서 `authenticity_token`를 지정해야하는 경우에는 어떻게 해야할까요? 이것은 `form_tag`에 `authenticity_token: 'your_external_token'`를 주는 것으로 간단하게 설정할 수 있습니다. ```erb -<%= form_tag 'http://farfar.away/form', authenticity_token: 'external_token') do %> +<%= form_tag 'http://farfar.away/form', authenticity_token: 'external_token' do %> Form contents <% end %> ``` @@ -812,7 +821,7 @@ Rails의 일반적인 규칙 중에는 최종적인 입력값은 `fields_for`나 결제 게이트웨이 등의 외부 리소스로 데이터를 전송해야하는 경우, 폼에서 사용가능한 필드는 외부 API에 따라 제한을 받습니다. 그런 경우처럼 `authenticity_token`를 위한 숨김 필드를 생성하지 않으려면 `:authenticity_token`을 `false`로 지정하면 됩니다. ```erb -<%= form_tag 'http://farfar.away/form', authenticity_token: false) do %> +<%= form_tag 'http://farfar.away/form', authenticity_token: false do %> Form contents <% end %> ``` @@ -877,6 +886,7 @@ end <% end %> ``` + 폼에서 중첩된 속성이 사용되면, `fields_for` 헬퍼는 그 관계로 연결된 모든 요소를 하나씩 출력합니다. 특히 Person에 주소가 등록되어 있지 않은 경우에는 아무것도 출력하지 않습니다. 필드의 세트가 적어도 하나 출력되도록 컨트롤러에서 1개 이상의 공백 문자를 사용하는 것은 자주 사용되는 패턴입니다. 아래의 예제에서는 Person 폼을 새로 생성할 경우에 2개의 주소 필드가 표시되도록 합니다. ```ruby @@ -980,5 +990,3 @@ end ### 동적으로 필드 추가하기 필드들을 미리 생성하지 않고 [새로운 주소를 추가] 버튼을 눌렀을 경우에만 이 필드를 생성할 수 있도록 하고 싶을 때가 있습니다. 안타깝게도 Rails에서는 이를 위한 방법이 지원되지 않습니다. 필드를 직접 생성하는 경우에는, 관련된 배열의 키가 중복되지 않도록 해야한다는 점을 주의해주세요. JavaScript에서 현재 시각을 사용해 유일한 식별자를 생성하는 것이 자주 사용되는 방법입니다. - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/getting_started.md b/guides/source/ko/getting_started.md index 6883579e9ccc6..ab2281e692fd5 100644 --- a/guides/source/ko/getting_started.md +++ b/guides/source/ko/getting_started.md @@ -116,7 +116,7 @@ $ cd blog | 파일/폴더 | 목적 | | ----------- | ------- | -|app/|여기에는 애플리케이션의 컨트롤러, 모델, 뷰, 헬퍼, 메일러, 그리고 애셋이 위치하고 있습니다. 이후, 가이드에서는 기본적으로 이 폴더를 중심으로 설명을 진행합니다.| +|app/|여기에는 애플리케이션의 컨트롤러, 모델, 뷰, 헬퍼, 메일러, 채널, 잡, 애셋이 위치하고 있습니다. 이후, 가이드에서는 기본적으로 이 폴더를 중심으로 설명을 진행합니다.| |bin/|여기에는 애플리케이션을 기동하거나, 배포하기 위한 레일스 스크립트 등의 스크립트 파일들이 포함되어 있습니다.| |config/|애플리케이션의 설정 파일(라우팅, 데이터베이스 등)이 위치하고 있습니다. 자세한 내용은[레일스 애플리케이션 설정하기](configuring.html) 를 참조해주세요.| |config.ru|애플리케이션 기동시에 필요한 Rack 기반 서버 용 설정 파일입니다.| @@ -226,7 +226,12 @@ Rails.application.routes.draw do end ``` -`root 'welcome#index'`라고 작성하는 것으로, 레일스에게 애플리케이션의 기본 URL에 대한 접속 요청을 welcome 컨트롤러의 index 액션으로 보내라고 지시할 수 있습니다. 이와 같이 `get 'welcome/index'`는 라는 요청을 welcome 컨트롤러의 index 액션으로 할당합니다. 후자는 위에서 컨트롤러 제너레이터(`bin/rails generate controller welcome index`를 실행했을 때, 자동적으로 생성되어 있습니다. +`root 'welcome#index'`라고 작성하는 것으로, 레일스에게 애플리케이션의 기본 +URL에 대한 접속 요청을 welcome 컨트롤러의 index 액션으로 보내라고 지시할 수 +있습니다. 이와 같이 `get 'welcome/index'`는 +라는 요청을 welcome 컨트롤러의 +index 액션으로 할당합니다. 후자는 위에서 컨트롤러 제너레이터(`bin/rails +generate controller Welcome index`를 실행했을 때, 자동적으로 생성됩니다. 브라우저에서 를 출력해보죠(제너레이터를 실행하기 위해서 레일스 웹서버를 정지하고 있었다면 `bin/rails server`를 재실행해주세요). `app/views/welcome/index.html.erb`에 작성했던 "Hello, Rails!"라는 문제가 브라우저 상에 표시될 것입니다. `WelcomeController`의 `index`액션으로 라우팅이 수행되어 뷰가 정상적으로 출력되는 것을 확인할 수 있습니다. @@ -1143,6 +1148,9 @@ end ``` Comment 모델의 내용은 이전에 보았던 `Article` 모델과 무척 닮아 있습니다. 다른 점이라고 한다면 액티브레코드의 _관계(Association)_를 설정하기 위한 `belongs_to :article`라는 줄이 있다는 부분 뿐입니다. 관계에 대해서는 다음 절에서 설명합니다. +명령에서 사용된 `:reference` 키워드는 모델을 위한 특별한 데이터 형식입니다. +이는 데이터베이스 테이블에 주어진 모델 이름에 `_id`를 붙인 키를 추가하고 정수 값을 받을 수 있게 합니다. 다음 `db/schema.rb`를 읽고 나면 좀 더 잘 이해할 수 있을겁니다. + 모델 파일 외에도 마이그레이션 파일도 생성되어 있습니다. 마이그레이션 파일은 모델에 대응하는 데이터베이스 테이블을 생성하는 내용을 담고 있습니다. ```ruby diff --git a/guides/source/ko/i18n.md b/guides/source/ko/i18n.md index a3d38d50ffc64..5538c52b464de 100644 --- a/guides/source/ko/i18n.md +++ b/guides/source/ko/i18n.md @@ -24,7 +24,7 @@ Rails 애플리케이션을 _지역화_하는 경우, 아마 다음과 같은 3 * Ruby on Rails이 사용하는 I18n의 구조 * RESTful한 애플리에키션을 올바르게 국제화하는 방법을 소개 -* I18n 기능을 사용해서 ActiveRecord의 에러나 ActionMailer의 메일 제목을 번역하는 방법 +* I18n 기능을 사용해서 Active Record의 에러나 Action Mailer의 메일 제목을 번역하는 방법 * 그 외의 애플리케이션의 번역에 사용 가능한 툴 소개 -------------------------------------------------------------------------------- @@ -108,12 +108,11 @@ NOTE: [몇몇 논의](http://groups.google.com/group/rails-i18n/browse_thread/th NOTE: I18의 백엔드는 번역문을 사용할 때가 되어야 해당 파일들을 읽어드립니다. 이에 따라서 번역문이 이미 공개된 이후에도 백엔드를 다른 것으로 바꿀 수 있습니다. -기본의 `application.rb` 파일에는 다른 폴더에 존재하는 로케일을 추가하는 방법이나, 기본으로 사용할 로케일을 변경할 수 있는 방법이 안내되어 있습니다. 필요한 줄들의 주석을 지우고 수정하여 사용하시면 됩니다. +또한 `config/application.rb` 파일을 통해서 기본 로케일ㄹ을 변경하여 불러올 번역을 설정할 수 있습니다. ```ruby -# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. -# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] -# config.i18n.default_locale = :de + config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] + config.i18n.default_locale = :de ``` ### 옵션: I18n 설정을 변경하기 @@ -770,7 +769,7 @@ errors.messages.blank #### 에러 메시지에서의 식 전개 -번역된 모델명, 속성명, 값은 언제든지 식전개에서 사용할 수 있습니다. +번역된 모델명, 속성명, 값은 언제든지 식전개에서 `model`, `attribute`, `value`로 사용할 수 있습니다. 따라서 `"cannot be blank"`라는 기본 에러메시지 대신에 `"Please fill in your %{attribute}"`처럼 속성명을 사용할 수도 있습니다. @@ -792,6 +791,7 @@ errors.messages.blank | inclusion | - | :inclusion | - | | exclusion | - | :exclusion | - | | associated | - | :invalid | - | +| non-optional association | - | :required | - | | numericality | - | :not_a_number | - | | numericality | :greater_than | :greater_than | count | | numericality | :greater_than_or_equal_to | :greater_than_or_equal_to | count | @@ -993,7 +993,7 @@ I18n.t :foo, raise: true # always re-raises exceptions from the backend 여기까지 설명을 읽는 것으로 Ruby on Rails에서 지원하고 있는 I18n의 개요를 파악하고, 애플리케이션을 번역할 준비가 되셨을 것이라고 생각합니다. -이 가이드에서 부족한 부분이나 잘못된 부분을 발견한 경우에는 [이슈 트래커](http://i18n.lighthouseapp.com/projects/14948-rails-i18n/overview)에서 티켓을 생성해주세요. 논의에 참가하고 싶거나, 질문이 있는 경우에는 저희들의 [메일링 리스트](http://groups.google.com/group/rails-i18n)에 등록해주세요. +어떤 부분에 대하여 논의를 원하거나 질문이 있는 경우에는 [메일링 리스트](http://groups.google.com/group/rails-i18n)에 참가해주세요. Rails I18n에 공헌하기 @@ -1001,19 +1001,17 @@ Rails I18n에 공헌하기 Ruby on Rails 2.2에서 도입된 I18n 지원 기능은 지금도 계속 진화하고 있습니다. I18n 프로젝트는 Ruby on Rails의 뛰어난 개발 관습에 의해서 진행되고 있습니다. 그 말은 기능을 갑자기 코어에 도입하지 않고, 처음에는 플러그인으로 개발하며 애플리케이션에서의 사용성을 테스트하고, 그 후에 가장 일반적으로 사용 가능한 기능만을 추출하여 Rails에 포함한다는 의미입니다. -그러므로 Rails 팀은 모든 분들에게 플러그인등의 라이브러리에 추가된 새로운 아이디어나 기능을 사용해보고, 그 결과를 커뮤니티에서 활용할 수 있기를 바랍니다(부디 [메일링 리스트](http://groups.google.com/group/rails-i18n!)에서도 이야기해주세요). +그러므로 Rails 팀은 모든 분들에게 플러그인등의 라이브러리에 추가된 새로운 아이디어나 기능을 사용해보고, 그 결과를 커뮤니티에서 활용할 수 있기를 바랍니다(부디 [메일링 리스트](http://groups.google.com/group/rails-i18n)에서도 이야기해주세요!). -원하는 로케일이 Ruby on Rails의 [번역 예시](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) 저장소에 없는 경우, 저장소를 [_fork_](https://github.com/guides/fork-a-project-and-submit-your-modifications)하여 번역문을 추가하신 후 [pull request](https://github.com/guides/pull-requests)를 보내주세요. +원하는 로케일이 Ruby on Rails의 [번역 예시](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) 저장소에 없는 경우, 저장소를 [_fork_](https://github.com/guides/fork-a-project-and-submit-your-modifications)하여 번역문을 추가하신 후 [pull request](https://help.github.com/articles/about-pull-requests/)를 보내주세요. 리소스 --------- * [Google group: rails-i18n](http://groups.google.com/group/rails-i18n) - I18n 프로젝트의 메일링 리스트입니다. -* [GitHub: rails-i18n](https://github.com/svenfuchs/rails-i18n/tree/master) - I18n 프로젝트의 코드 저장소입니다. Rails 용의 번역문은 [번역 예시](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale)에서 볼 수 있습니다. 이것들은 많은 애플리케이션에서도 도움이 될 것입니다. -* [GitHub: i18n](https://github.com/svenfuchs/i18n/tree/master) - I18n gem의 코드 저장소입니다. -* [Lighthouse: rails-i18n](http://i18n.lighthouseapp.com/projects/14948-rails-i18n/overview) - rails-i18n의 이슈 트래커입니다. -* [Lighthouse: i18n](http://i18n.lighthouseapp.com/projects/14947-ruby-i18n/overview) - i18n gem의 이슈 트래커입니다. +* [GitHub: rails-i18n](https://github.com/svenfuchs/rails-i18n) - I18n 프로젝트의 코드 저장소입니다. Rails 용의 번역문은 [번역 예시](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale)에서 볼 수 있습니다. 이것들은 많은 애플리케이션에서도 도움이 될 것입니다. +* [GitHub: i18n](https://github.com/svenfuchs/i18n) - I18n gem의 코드 저장소입니다. 작성자 @@ -1029,4 +1027,4 @@ Ruby on Rails 2.2에서 도입된 I18n 지원 기능은 지금도 계속 진화 [^(2]:) 이외의 백엔드에서는 다른 형식을 사용하거나, 또는 필수일 수도 있습니다. 예를 들자면 GetText 백엔드는 GetText 파일을 읽어올 것입니다. -[^(3]:) 이유 중 하나는 I18n 기능을 요구하지 않는 애플리케이션에서 불필요한 읽기/쓰기를 수행하고 싶지 않아서입니다. 이를 위해서 저희 팀에서는 I18n을 최대한 간단하게 유지하고, 영어에만 집중하고 있습니다. 또 다른 이유로는 기존에 존재하는 수 많은 언어에서 발생할 수 있는 문제를 한번에 해결하는 만능 솔루션을 구현하는 것은 불가능하기 때문입니다. 결국 저희같은 I18n 개발자들이 할 수 있는 것은 구현 전체를 언제라도 간단하게 교체 가능하도록 만드는 정도입니다. I18n을 교체 가능하게 만드는 것으로 테스트나나 확장이 용이해진다는 장점도 얻을 수 있습니다. \ No newline at end of file +[^(3]:) 이유 중 하나는 I18n 기능을 요구하지 않는 애플리케이션에서 불필요한 읽기/쓰기를 수행하고 싶지 않아서입니다. 이를 위해서 저희 팀에서는 I18n을 최대한 간단하게 유지하고, 영어에만 집중하고 있습니다. 또 다른 이유로는 기존에 존재하는 수 많은 언어에서 발생할 수 있는 문제를 한번에 해결하는 만능 솔루션을 구현하는 것은 불가능하기 때문입니다. 결국 저희같은 I18n 개발자들이 할 수 있는 것은 구현 전체를 언제라도 간단하게 교체 가능하도록 만드는 정도입니다. I18n을 교체 가능하게 만드는 것으로 테스트나나 확장이 용이해진다는 장점도 얻을 수 있습니다. diff --git a/guides/source/ko/layouts_and_rendering.md b/guides/source/ko/layouts_and_rendering.md index 8b0b115ba6065..22f13b85afb50 100644 --- a/guides/source/ko/layouts_and_rendering.md +++ b/guides/source/ko/layouts_and_rendering.md @@ -176,21 +176,18 @@ render template: "products/show" #### 별도의 파일을 사용하기 -`render` 메소드로 지정할 수 있는 뷰는 현재 애플리케이션의 폴더 바깥에 있어도 상관 없습니다(예를 들어, 2개의 Rails 애플리케이션이 같은 뷰 템플릿을 공유하고 있는 경우). - -```ruby -render "/u/apps/warehouse_app/current/app/views/products/show" -``` - -Rails는 경로가 `/`로 시작되는 경우 별도의 파일을 사용한 랜더링이라고 인식합니다. 이러한 경우에 좀 더 명확하게 표현하고 싶은 경우에는 아래처럼 `:file` 옵션을 사용할 수 있습니다(Rails 2.2 이전에서는 해당 옵션을 반드시 사용해야 했습니다). +`render` 메소드로 지정할 수 있는 뷰는 현재 애플리케이션의 폴더 바깥에 있어도 상관 없습니다. ```ruby render file: "/u/apps/warehouse_app/current/app/views/products/show" ``` -`:file` 옵션으로 주어진 경로는 파일 시스템을 기준으로 하는 절대 경로입니다. 당연하지만 해당 파일에 대한 접근 권한이 부여되어있어야 합니다. +`:file` 옵션으로 주어진 경로는 파일 시스템을 기준으로 하는 절대 경로입니다. +당연하지만 해당 파일에 대한 접근 권한이 부여되어있어야 합니다. + +NOTE: `:file` 옵션을 사용자 입력과 함께 사용하는 경우 중대한 보안 결함을 만들 수 있습니다. 공격자가 이 기능으로 보안에 영향을 미치는 파일에 접근하려고 시도할 수 있기 때문입니다. -NOTE: 파일을 사용하는 경우, 기본적으로 현재 레이아웃이 무시됩니다. 이 랜더링 작업을 현재 레이아웃 내부에서 수행하고 싶은 경우에는 `layout: true` 옵션을 추가할 필요가 있습니다. +NOTE: 파일을 사용하는 경우, 현재의 레이아웃을 사용합니다. TIP: Microsoft Windows에서 Rails를 실행하는 경우, 파일을 랜더링하는 경우에 `:file` 옵션을 생략할 수 없습니다. Windows의 파일명 형식이 Unix와 같지 않기 때문입니다. @@ -257,7 +254,7 @@ render html: "Not Found".html_safe TIP: 이 방법은 무척 적은 양의 HTML 코드를 랜더링하고 싶을 때에 편리합니다. 이렇게 랜더링하던 코드가 복잡해지는 경우, 뷰 템플릿 사용을 검토해주세요. -NOTE: 이 옵션을 사용하면 문자열이 'HTML safe'하지 않은 경우에 이스케이프를 수행합니다. +NOTE: `html:` 옵션을 사용하면 HTML 객체들은 `html_safe` 메소드로 HTML 안전하다고 표시되지 않은 경우에 전부 이스케이프를 수행합니다. #### JSON 랜더링하기 @@ -588,12 +585,14 @@ HTTP 요청에 응답을 돌려주는 또다른 방법으로는 `redirect_to`를 redirect_to photos_url ``` -`redirect_to`의 인수로는 어떤 값도 지정할 수 있습니다만, `link_to`나 `url_for`를 사용하는 것이 일반적입니다. 유저를 직전 페이지로 되돌려보내는, 특수한 리다이렉트도 가능합니다. +`redirect_back`을 사용하여 사용자가 직전에 있었던 페이지로 돌려보낼 수도 있습니다. 이 위치는 `HTTP_REFERER` 헤더를 사용하며, 브라우저에 따라서 지원되지 않는 경우도 있으므로 반드시 `fallback_location`을 지정해주어야 합니다. ```ruby -redirect_to :back +redirect_back(fallback_location: root_path) ``` +NOTE: `redirect_to`와 `redirect_back`은 메소드 실행 중에 즉시 반환되거나 종료되지 않고, 그저 HTTP 응답을 설정하기만 합니다. 메소드에서 이들 뒤에 있는 코드들은 모두 실행됩니다. 필요하다면 명시적으로 `return`을 호출하거나 다른 종료 방식을 제공할 수 있습니다. + #### 리다이렉트 상태 코드를 변경하기 `redirect_to`를 호출하면 일시적인 페이지 이동을 의미하는 HTTP 상태 코드인 302가 브라우저로 전송되며, 브라우저는 그 정보를 바탕으로 페이지 이동을 합니다. 다른 상태 코드(301: 영구적인 재전송이 자주 쓰입니다)로 변경하기 위해서는 `:status` 옵션을 사용하세요. @@ -663,7 +662,7 @@ end ### `head`로 본문이 없는 응답 생성하기 -`head` 메소드를 사용하면 브라우저에게 본문(body)이 없는 응답을 전송할 수 있습니다. 이 메소드의 이름은 `render :nothing`보다도 동작을 명확히 표현하고 있습니다. `head` 메소드에는 인수로 HTTP 상태 코드를 표현하는 심볼을 넘길 수 있습니다([참조 테이블](#status) 참조). 옵션의 인수는 헤더명과 값을 쌍으로 하는 해시값이라고 해석됩니다. 예를 들어 아래의 코드는 응답으로 에러 헤더만을 전송합니다. +`head` 메소드를 사용하면 브라우저에게 본문(body)이 없는 응답을 전송할 수 있습니다. `head` 메소드에는 인수로 HTTP 상태 코드를 표현하는 심볼을 넘길 수 있습니다([참조 테이블](#status) 참조). 옵션의 인수는 헤더명과 값을 쌍으로 하는 해시값이라고 해석됩니다. 예를 들어 아래의 코드는 응답으로 에러 헤더만을 전송합니다. ```ruby head :bad_request @@ -1011,6 +1010,42 @@ WARNING: 이미지 파일의 확장자는 생략할 수 없습니다. 이 코드에서 `_ad_banner.html.erb` 파셜과 `_footer.html.erb` 파셜은 많은 페이지에서 재활용 가능한 컨텐츠를 포함할 수 있습니다. 이렇게 하면, 어떤 페이지를 개발중일때 상세한 부분에 대해서는 신경쓰지 않아도 됩니다. +이 가이드의 앞 절에서 살펴 보았듯, `yield`는 레이아웃을 깔끔하게 관리할 수 있는 무척 강력한 도구입니다. 이는 순수한 Ruby로 동작하며 어디서든 사용할 수 있다는 점을 기억하세요. 예를 들어, 유사한 리소스들의 레리아웃 정의를 DRY하게 만들 수 있습니다. + +* `users/index.html.erb` + + ```html+erb + <%= render "shared/search_filters", search: @q do |f| %> +

+ Name contains: <%= f.text_field :name_contains %> +

+ <% end %> + ``` + +* `roles/index.html.erb` + + ```html+erb + <%= render "shared/search_filters", search: @q do |f| %> +

+ Title contains: <%= f.text_field :title_contains %> +

+ <% end %> + ``` + +* `shared/_search_filters.html.erb` + + ```html+erb + <%= form_for(@q) do |f| %> +

Search form:

+
+ <%= yield f %> +
+

+ <%= f.submit "Search" %> +

+ <% end %> + ``` + TIP: 모든 페이지에서 공유되는 컨텐츠라면 파셜을 레이아웃에서 직접 사용해도 좋습니다. #### 파셜 레이아웃 @@ -1059,6 +1094,34 @@ TIP: 모든 페이지에서 공유되는 컨텐츠라면 파셜을 레이아웃 위 2개의 뷰에서는 같은 파셜을 랜더링합니다만 ActionView의 submit 헬퍼는 new 액션인 경우에는 "Create Zone"을 반환하고, edit 액션에서는 "Update Zone"을 반환합니다. +몇몇 경우에만 지역변수를 넘기고 싶다면 `local_assigns`를 사용할 수 있습니다. + +* `index.html.erb` + + ```erb + <%= render user.articles %> + ``` + +* `show.html.erb` + + ```erb + <%= render article, full: true %> + ``` + +* `_articles.html.erb` + + ```erb +

<%= article.title %>

+ + <% if local_assigns[:full] %> + <%= simple_format article.body %> + <% else %> + <%= truncate article.body %> + <% end %> + ``` + +이 방법을 통해서 모든 지역 변수를 선언하지 않고 정말 필요한 것들만 사용할 수 있습니다. + 모든 파셜은 언더스코어를 제외한 파셜명과 동일한 이름의 지역 변수를 가집니다. `:object` 옵션을 사용해서 이 지역 변수에 객체를 넘겨줄 수 있습니다. ```erb @@ -1214,5 +1277,3 @@ TIP: 컬렉션에 따라, 파셜 내부에서 카운터 변수가 사용되는 이렇게 하면 됩니다. News 뷰에서 새로운 레이아웃을 사용하게 되며, 상단 메뉴가 숨겨지고 "content" div 태그에 있는 우측 메뉴가 새롭게 추가 됩니다. 이와 같은 결과를 얻을 수 있는 서브 템플릿의 사용법은 이외에도 여러가지가 있습니다. 중첩 횟수에는 제한이 없다는 점을 기억하세요. 예를 들어, News 레이아웃에서 새로운 레이아웃을 사용하기 위해 `render template: 'layouts/news'`를 사용해 `ActionView::render` 메소드를 사용할 수도 있습니다. `News` 레이아웃을 서브 템플릿으로 만들고 싶지 않다면 `content_for?(:news_content) ? yield(:news_content) : yield`를 `yield`로 바꾸기만 하면 됩니다. - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/maintenance_policy.md b/guides/source/ko/maintenance_policy.md index b3c5601f60dab..beee8ce15135a 100644 --- a/guides/source/ko/maintenance_policy.md +++ b/guides/source/ko/maintenance_policy.md @@ -33,7 +33,7 @@ Rails의 버전명은 [semver](http://semver.org/)의 Semantic Versioning를 약 특별한 경우로서 코어 팀 멤버 중 한명이 지원할 대상 시리즈를 늘리는 것에 찬성하는 경우에는 지원 대상 시리즈가 추가될 수 있습니다. -**현재 지원 대상인 시리즈:** `4.2.Z`, `4.1.Z`(지원 담당: Rafael França). +**현재 지원 대상인 시리즈:** `5.0.Z`, `4.2.Z`. 보안 문제 --------------- @@ -42,18 +42,16 @@ Rails의 버전명은 [semver](http://semver.org/)의 Semantic Versioning를 약 이 릴리스는 직전에 릴리스된 버전에 보안 패치를 적용하여 릴리스 됩니다. 이어서 패치는 x-y-stable(안정판) 브랜치의 마지막에 적용됩니다. 예를 들어 1.2.3이라는 보안 릴리스가 있었다고 하면, 이 릴리스는 1.2.2를 기반으로 1-2-stable의 마지막에 추가됩니다. 다시 말해 최신 Rails를 사용하고 있다면 보안 릴리스의 업데이트를 간단하게 수행할 수 있습니다. -**현재 지원 대상인 시리즈:** `4.2.Z`, `4.1.Z`. +**현재 지원 대상인 시리즈:** `5.0.Z`, `4.2.Z`. 중대한 보안 문제 ---------------------- 중대한 보안 문제인 경우에는 위의 버전과 동일하게 새 버전을 제공하며 최신의 메이저 릴리스 시리즈에 대해서도 보안 패치와 새 버전을 제공합니다. 보안 문제가 어느정도 큰지에 대한 판단은 코어팀에 의해서 이루어지고 있습니다. -**현재 지원 대상인 시리즈:** `4.2.Z`, `4.1.Z`, `3.2.Z`. +**현재 지원 대상인 시리즈:** `5.0.Z`, `4.2.Z`. 지원 대상 외인 릴리스 시리즈 -------------------------- 어떤 릴리스 시리즈가 지원 대상 외로 변경된 경우, 버그 수정과 보안 문제의 대응은 각자의 책임이 됩니다. 경우에 따라서는 수정을 위한 백포트를 git에 공개하는 경우도 있습니다만, 이후 새 버전이 릴리스되는 일은 없습니다. 애플리케이션에서 사용하고 있는 버전을 유지하는데에 어려움을 느끼는 경우에는 지원 대상 버전까지 업그레이드를 해주세요. - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/rails_application_templates.md b/guides/source/ko/rails_application_templates.md index 10a77eb1111bd..6cf34f69989d1 100644 --- a/guides/source/ko/rails_application_templates.md +++ b/guides/source/ko/rails_application_templates.md @@ -89,6 +89,14 @@ end add_source "http://code.whytheluckystiff.net" ``` +블록을 넘기면 블록에 들어있는 젬 목록이 해당 소스 그룹에 포함됩니다. + +```ruby +add_source "http://gems.github.com/" do + gem "rspec-rails" +end +``` + ### environment/application(data=nil, options={}, &block) `config/application.rb` 파일의 `Application` 클래스에 지정된 라인을 추가합니다. @@ -168,18 +176,24 @@ generate(:scaffold, "person", "name:string", "address:text", "age:number") run "rm README.rdoc" ``` -### rake(command, options = {}) +### rails_command(command, options = {}) -Rails 애플리케이션에 있는 rake 태스크를 지정하여 실행합니다. 예를 들어, 데이터베이스의 마이그레이션을 실행하려면 다음과 같이 작성하면 됩니다. +Rails 애플리케이션에 있는 태스크를 실행합니다. 예를 들어, 데이터베이스의 마이그레이션을 실행하려면 다음과 같이 작성하면 됩니다. ```ruby -rake "db:migrate" +rails_command "db:migrate" ``` -Rails의 환경을 지정하여 rake 태스크를 실행할 수도 있습니다. +Rails의 환경을 지정하여 태스크를 실행할 수도 있습니다. ```ruby -rake "db:migrate", env: 'production' +rails_command "db:migrate", env: 'production' +``` + +슈퍼 유저 권한으로 태스크를 실행할 수도 있습니다. + +```ruby +rails_command "log:clear", sudo: true ``` ### route(routing_code) @@ -259,5 +273,3 @@ def source_paths [File.expand_path(File.dirname(__FILE__))] end ``` - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/rails_on_rack.md b/guides/source/ko/rails_on_rack.md index 7e30ad7493c98..c3bd12cc38edf 100644 --- a/guides/source/ko/rails_on_rack.md +++ b/guides/source/ko/rails_on_rack.md @@ -30,7 +30,7 @@ Rails와 Rack `Rails.application`은 Rails 애플리케이션을 Rack 애플리케이션으로 구현한 것입니다. Rack에 준거한 웹서버로, Rails 애플리케이션을 제공하기 위해서는 `Rails.application`객체를 사용해야합니다. -### `rails server` 명령 +### `rails server` `rails server` 명령은 `Rack::Server`의 객체를 생성하고, 웹 서버를 실행합니다. @@ -55,34 +55,13 @@ class Server < ::Rack::Server end ``` -또한 아래와 같이, 미들웨어를 불러옵니다. - -```ruby -def middleware - middlewares = [] - middlewares << [Rails::Rack::Debugger] if options[:debugger] - middlewares << [::Rack::ContentLength] - Hash.new(middlewares) -end -``` - -`Rails::Rack::Debugger`는 주로 development 환경에서 유용합니다. 불러온 미들웨어의 역할은 다음과 같습니다. - -| 미들웨어 | 역할 | -| ----------------------- | --------------------------------------------------------------------------------- | -| `Rails::Rack::Debugger` | 디버거를 실행한다 | -| `Rack::ContentLength` | 응답의 (바이트) 길이를 계산하고, HTTP Content-Length 헤더에 값을 저장한다 | - -### `rackup` 명령 +### `rackup` Rails의 `rails server` 명령 대신에 `rackup` 명령을 사용할 때에는 아래의 내용을 `config.ru`를 작성해서 Rails 애플리케이션의 최상위 폴더에 저장합니다. ```ruby # Rails.root/config.ru -require ::File.expand_path('../config/environment', __FILE__) - -use Rails::Rack::Debugger -use Rack::ContentLength +require_relative 'config/environment' run Rails.application ``` @@ -98,6 +77,10 @@ $ rackup config.ru $ rackup --help ``` +### 개발과 자동 로딩 + +미들웨어는 한번 불러오고나면 변경되더라도 다시 불러와지지 않습니다. 변경사항을 반영하고 싶은 경우에는 애플리케이션을 재기동해야 합니다. + Action Dispatcher의 미들웨어 스택 ---------------------------------- @@ -115,27 +98,25 @@ $ bin/rails middleware 막 생성한 Rails 애플리케이션에서는 다음과 같이 출력될 겁니다. - ```ruby +```ruby use Rack::Sendfile use ActionDispatch::Static -use Rack::Lock -use # +use ActionDispatch::Executor +use ActiveSupport::Cache::Strategy::LocalCache::Middleware use Rack::Runtime use Rack::MethodOverride use ActionDispatch::RequestId use Rails::Rack::Logger use ActionDispatch::ShowExceptions +use WebConsole::Middleware use ActionDispatch::DebugExceptions use ActionDispatch::RemoteIp use ActionDispatch::Reloader use ActionDispatch::Callbacks use ActiveRecord::Migration::CheckPending -use ActiveRecord::ConnectionAdapters::ConnectionManagement -use ActiveRecord::QueryCache use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore use ActionDispatch::Flash -use ActionDispatch::ParamsParser use Rack::Head use Rack::ConditionalGet use Rack::ETag @@ -164,9 +145,9 @@ run Rails.application.routes # Rack::BounceFavicon를 가장 마지막에 추가한다 config.middleware.use Rack::BounceFavicon -# ActiveRecord::QueryCache의 뒤에 Lifo::Cache를 추가한다 +# ActiveRecord::Executor의 뒤에 Lifo::Cache를 추가한다 # 그리고 Lifo::Cache에 { page_cache: false }를 넘긴다 -config.middleware.insert_after ActiveRecord::QueryCache, Lifo::Cache, page_cache: false +config.middleware.insert_after ActionDispatch::Executor, Lifo::Cache, page_cache: false ``` #### 미들웨어를 교체하기 @@ -186,10 +167,10 @@ config.middleware.swap ActionDispatch::ShowExceptions, Lifo::ShowExceptions ```ruby # config/application.rb -config.middleware.delete "Rack::Lock" +config.middleware.delete Rack::Runtime ``` -미들웨어 스택을 확인하면 `Rack::Lock`가 없어졌다는 것을 확인할 수 있습니다. +미들웨어 스택을 확인하면 `Rack::Runtime`가 없어졌다는 것을 확인할 수 있습니다. ```bash $ bin/rails middleware @@ -233,6 +214,10 @@ Action Controller의 기능의 대부분은 미들웨어로서 구현되어 있 * `env["rack.multithread"]`를 `false`로 설정하여 애플리케이션을 Mutex로 감쌉니다. +**`ActionDispatch::Executor`** + +* 개발 중에 스레드 안전한 코드 리로딩을 위해서 사용됩니다. + **`ActiveSupport::Cache::Strategy::LocalCache::Middleware`** * 메모리를 사용한 캐싱을 위해서 사용합니다. 이 캐시는 스레드 안전(thread-safe)하지 않습니다. @@ -277,14 +262,6 @@ Action Controller의 기능의 대부분은 미들웨어로서 구현되어 있 * 적용되지 않은 마이그레이션이 있는지 확인합니다. 미실행된 것이 있으면 `ActiveRecord::PendingMigrationError`를 발생시킵니다. -**`ActiveRecord::ConnectionAdapters::ConnectionManagement`** - -* 요청을 처리할 때마다 데이터베이스 커넥션을 커넥션 풀에 반환합니다. `env['rack.test']`가 `true`가 아닌 경우에만 반환을 합니다. - -**`ActiveRecord::QueryCache`** - -* Active Record의 쿼리 캐시 기능을 활성화합니다. - **`ActionDispatch::Cookies`** * 쿠키 기능을 제공합니다. @@ -297,10 +274,6 @@ Action Controller의 기능의 대부분은 미들웨어로서 구현되어 있 * flash 기능을 제공합니다(flash란 연속된 요청간에 값을 공유하는 기능입니다). 이것은 `config.action_controller.session_store`에 값이 설정되어 있을 경우에만 유효합니다. -**`ActionDispatch::ParamsParser`** - -* 요청에 포함된 파라미터를 분석해서 `params`에 넣어줍니다. - **`Rack::Head`** * HEAD 요청을 `GET`으로 변환하여 처리합니다. @@ -322,11 +295,7 @@ TIP: 이러한 미들웨어는 모두 Rack의 미들웨어 스택에서도 사 * [Rack 공식 사이트](http://rack.github.io) * [Rack 입문](http://chneukirchen.org/blog/archive/2007/02/introducing-rack.html) -* [Ruby on Rack #1 - Hello Rack!](http://m.onkey.org/ruby-on-rack-1-hello-rack) -* [Ruby on Rack #2 - The Builder](http://m.onkey.org/ruby-on-rack-2-the-builder) ### 미들웨어를 이해하기 * [Railscast on Rack Middlewares](http://railscasts.com/episodes/151-rack-middleware) - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/routing.md b/guides/source/ko/routing.md index 24c7b835b5955..7545b97353982 100644 --- a/guides/source/ko/routing.md +++ b/guides/source/ko/routing.md @@ -7,16 +7,16 @@ Rails 라우팅 * routes.rb를 읽는 방법 * 직접 라우팅을 작성하는 방법(리소스 베이스의 라우팅을 추천합니다만, match 메소드를 사용한 라우팅도 가능합니다) -* 액션에서 받는 파라미터 +* 컨트롤러의 액션에 넘길 라우트 매개변수를 선언하는 방법 * 라우트 헬퍼를 사용해서 경로나 URL을 자동생성하는 방법 -* 제한을 추가하거나 Rack 엔드포인트 생성하는 방법 +* 제한을 추가하거나 Rack 엔드포인트 추가하는 방법 -------------------------------------------------------------------------------- Rails 라우터의 목적 ------------------------------- -Rails의 라우터는 요청받은 URL을 인식하고 적절한 컨트롤러의 액션을 매칭합니다. 라우터는 뷰에서 이러한 경로나 URL을 직접 하드 코딩하는 것을 피하기 위한 경로나 URL도 제공합니다. +Rails의 라우터는 요청받은 URL을 인식하고 적절한 컨트롤러의 액션이나 Rack 애플리케이션에 매칭합니다. 라우터는 뷰에서 이러한 경로나 URL을 직접 하드 코딩하는 것을 피하기 위한 경로나 URL도 제공합니다. ### URL을 실제 코드와 연결하기 @@ -385,7 +385,7 @@ end | PATCH/PUT | /comments/:id(.:format) | comments#update | sekret_comment_path | | DELETE | /comments/:id(.:format) | comments#destroy | sekret_comment_path | -### 라우팅의 'concern' 기능 +### 라우팅의 'concern' concern을 사용하여, 다른 리소스나 라우팅의 내부에서 사용할 수 있는 공통의 라우팅을 선언할 수 있습니다. concern은 아래와 같이 정의합니다. @@ -545,29 +545,23 @@ Rails에서는 리소스 라우팅을 사용할 때에 임의의 URL을 액션 ### 파라미터 나누기 -일반적인 라우팅을 설정한다면, Rails가 브라우저로부터 받은 HTTP 요청을 라우팅에 매칭하기 위한 심볼을 몇 개 넘깁니다. 이 심볼 중 `:controller`와 `:action`은 애플리케이션의 컨트롤러와 액션에 각각 매칭됩니다. 아래의 예제를 보시죠. +일반적인 라우팅을 설정하는 경우라면, Rails가 받은 HTTP 요청을 라우팅에 매칭하기 위한 심볼을 몇 개 넘깁니다. 아래의 예제를 보시죠. ```ruby -get ':controller(/:action(/:id))' +get 'photos(/:id)', to: :display ``` -브라우저에서 보낸 `/photos/show/1` 요청은 위의 라우팅으로 처리하게 된다면, `Photos` 컨트롤러의 `show` 액션이 호출되며, URL의 마지막에 있는 `"1"`은 `params[:id]`를 통해 접근할 수 있습니다. `:action`과 `:id`가 필수가 아니라는 점을 ()로 표현하고 있으므로, 이 라우팅은 `/photos`를 `PhotosController#index`로 넘겨줄 수도 있습니다. +브라우저에서 보낸 `/photos/1` 요청은 위의 (이전에 이에 매칭되는 라우트가 없었기 때문에) 라우팅으로 처리하게 되며, `Photos` 컨트롤러의 `display` 액션이 호출됩니다. 그리고 URL의 마지막에 있는 `"1"`은 `params[:id]`를 통해 접근할 수 있습니다. `:id`가 필수가 아니라는 점을 ()로 표현하고 있으므로, 이 라우팅은 `/photos`를 `PhotosController#display`로 넘겨줄 수도 있습니다. ### 동적인 세그먼트 -일반 라우팅의 일부로서, 문자열을 고정하지 않는 동적인 세그먼트를 자유롭게 사용할 수 있습니다. `:controller`나 `:action`을 제외한 어떤 것이라도 `params`에 포함시켜 액션에 건네줄 수 있습니다. 아래와 같은 라우팅을 선언했다고 가정합시다. +일반 라우팅의 일부로서, 문자열을 고정하지 않는 동적인 세그먼트를 자유롭게 사용할 수 있습니다. 어떤 것이라도 `params`에 포함시켜 액션에 건네줄 수 있습니다. 아래와 같은 라우팅을 선언했다고 가정합시다. ```ruby -get ':controller/:action/:id/:user_id' +get 'photos/:id/:user_id', to: 'photos#show' ``` -브라우저에서의 `/photos/show/1/2` 경로는 `Photos` 컨트롤러의 `show` 액션에 매칭됩니다. 이 경우에는 `params[:id]`에는 `"1"`, `params[:user_id]`에는 `"2"`가 저장됩니다. - -NOTE: `:controller` 경로 세그먼트를 사용하는 경우 `:namespace`나 `:module`를 함께 사용할 수 없습니다. 반드시 사용하고 싶다면, 아래와 같이 필요한 네임스페이스에서만 :controller`에 제약을 추가합니다. - -```ruby -get ':controller(/:action(/:id))', controller: /admin\/[^\/]+/ -``` +브라우저에서의 `/photos/1/2` 요청은 `Photos` 컨트롤러의 `show` 액션에 매칭됩니다. 이 경우에는 `params[:id]`에는 `"1"`, `params[:user_id]`에는 `"2"`가 저장됩니다. TIP: 동적인 세그먼트 분할에서는 기본적으로 마침표(`.`)을 사용할 수 없습니다. 이는 마침표가 라우팅에서 포맷을 구분하기 위한 용도로 사용되고 있기 때문입니다. 반드시 동적 세그먼트 내에서 마침표를 쓰고 싶은 때에는 기본 설정을 덮어써야합니다. 예를 들어 `id: /[^\/]+/`라고 사용한다면 슬래시 이외의 모든 문자를 사용할 수 있습니다. @@ -576,31 +570,23 @@ TIP: 동적인 세그먼트 분할에서는 기본적으로 마침표(`.`)을 라우트 선언시에 콜론을 사용하지 않은 경우, 정적인 세그먼트가 되어 고정 문자열을 사용하게 됩니다. ```ruby -get ':controller/:action/:id/with_user/:user_id' +get 'photos/:id/with_user/:user_id', to: 'photos#show' ``` -이 라우팅에서는 `/photos/show/1/with_user/2`와 같은 경로가 매칭됩니다. `with_user`는 그대로 사용되고 있습니다. 이 때 액션에서 사용할 수 있는 `params`는 `{ controller: 'photos', action: 'show', id: '1', user_id: '2' }`가 됩니다. +이 라우팅에서는 `/photos/1/with_user/2`와 같은 경로가 매칭됩니다. `with_user`는 그대로 사용되고 있습니다. 이 때 액션에서 사용할 수 있는 `params`는 `{ controller: 'photos', action: 'show', id: '1', user_id: '2' }`가 됩니다. ### 쿼리 문자열 쿼리 문자열으로 지정되어있는 파라미터도 모두 `params`에 포함됩니다. 아래의 라우팅으로 예를 들어 보겠습니다. ```ruby -get ':controller/:action/:id' +get 'photos/:id', to: 'photos#show' ``` -브라우저에서 `/photos/show/1?user_id=2`라는 경로를 요청받으면 `Photos` 컨트롤러의 `show` 액션에 매칭됩니다. 이 때 `params`는 `{ controller: 'photos', action: 'show', id: '1', user_id: '2' }`가 됩니다. +브라우저에서 `/photos/1?user_id=2`라는 경로를 요청받으면 `Photos` 컨트롤러의 `show` 액션에 매칭됩니다. 이 때 `params`는 `{ controller: 'photos', action: 'show', id: '1', user_id: '2' }`가 됩니다. ### 기본 설정을 정의하기 -`:controller` 심볼이나 `:action` 심볼은 라우팅 내에서 명시적으로 지정할 필요가 없습니다. 이 들은 아래와 같이 기본으로 지정할 수 있습니다. - -```ruby -get 'photos/:id', to: 'photos#show' -``` - -이 라우팅은 `/photos/12`라는 경로에 매칭되며, `Photos` 컨트롤러의 `show` 액션에 할당됩니다. - `:defaults` 옵션에 해시를 넘기는 것으로 추가 기본 설정을 정의할 수도 있습니다. 이 정의는 동적 세그먼트로서 지정하지 않은 파라미터에 대해서도 적용됩니다. 예를 들면, ```ruby @@ -693,6 +679,8 @@ end NOTE: 요청 기반의 조건은 Request 객체에 대해 지정된 메소드를 호출합니다. 메소드를 호출할 때에는 호출시에 넘긴 해시의 키와 동일한 이름의 메소드를 Request 객체로 호출하며, 반환된 값과 해시의 값을 비교합니다. 따라서, 조건에 사용된 값의 타입이 대응하는 Request 객체 메소드의 결과의 타입과 일치해야합니다. 예를 들어 `constraints: { subdomain: 'api' }`라는 조건은 `api` 서브 도메인을 정상적으로 매칭할 수 있습니다만, `constraints: { subdomain: :api }`와 같이 심볼을 사용한 경우에는 `api`와 정상적으로 비교되지 않습니다. `request.subdomain`가 돌려주는 `'api'`는 문자열이기 때문입니다. +NOTE: `format` 조건에는 예외가 하나 있습니다. 요청 객체의 메소드에는 모든 경로에서 내부적으로 사용하는 조건부 파라미터가 존재합니다. 세그먼트 조건이 선행하며 `format` 조건은 해시를 통해서 강제되었을 경우에만 적용됩니다. 예를 들어 `get 'foo', constraints: { format: 'json' }`는 format이 조건부이기 때문에 `GET /foo`를 매칭합니다. 하지만 [람다](#복잡한-조건)를 사용하여 `get 'foo', constraints: lambda { |req| req.format == :json }`와 같이 정의한다면 명시적으로 JSON 요청만을 처리하게 됩니다. + ### 복잡한 조건 좀 더 복잡한 조건을 사용하고 싶은 경우에는, Rails에서 요구하는 `matches?`에 알맞은 객체를 넘기면 됩니다. 예를 들어 블랙리스트에 등록되어있는 모든 사용자를 `BlacklistController`로 라우팅하고 싶다고 가정해봅시다. 이러한 경우에는 다음과 같이 정의하면 됩니다. @@ -793,10 +781,10 @@ Rails는 호스트(`http://www.example.com` 등)가 URL에 지정되어있지 `Post` 컨트롤러의 `index` 액션에 대응하는 `'posts#index'`같은 문자열 대신에 임의의 Rack 애플리케이션을 매쳐의 엔드 포인트로 지정할 수 있습니다. ```ruby -match '/application.js', to: Sprockets, via: :all +match '/application.js', to: MyRackApp, via: :all ``` -Rails 라우터의 입장에서 보면 `Sprockets`은 `call`에 응답해서 `[status, headers, body]`를 돌려주기만 하면 라우팅이 된 장소가 Rack 애플리케이션이든 액션이든 관계가 없습니다. 이것은 Rack 애플리케이션이 모든 HTTP 메서드를 적절하게 다루기를 원할 수 있으므료, `via: :all`의 적절한 사용 예시가 될 수 있습니다. +Rails 라우터의 입장에서 보면 `MyRackApp`은 `call`에 응답해서 `[status, headers, body]`를 돌려주기만 하면 라우팅이 된 장소가 Rack 애플리케이션이든 액션이든 관계가 없습니다. 이것은 Rack 애플리케이션이 모든 HTTP 메서드를 적절하게 다루기를 원할 수 있으므료, `via: :all`의 적절한 사용 예시가 될 수 있습니다. NOTE: 참고로 `'posts#index'`는 `PostsController.action(:index)`라는 형태로 변환됩니다. 이는 올바른 Rack 애플리케이션을 반환합니다. @@ -1043,7 +1031,7 @@ Rails에는 라우팅을 확인하는 기능과 테스트를 하기 위한 기 ### 기존의 룰을 한번에 확인하기 -현재 애플리케이션에서 사용가능한 라우팅을 모두 보기 위해서는 서버가 **development** 환경에서 동작하고 있는 상태로 브라우저에서 `http://localhost:3000/rails/info/routes`에 접속합니다. 터미널에서 `bin/rails routes`를 실행해도 같은 결과를 얻을 수 있습니다. +현재 애플리케이션에서 사용가능한 라우팅을 모두 보기 위해서는 서버가 **development** 환경에서 동작하고 있는 상태로 브라우저에서 `http://localhost:3000/rails/info/routes`에 접속합니다. 터미널에서 `rails routes`를 실행해도 같은 결과를 얻을 수 있습니다. 어떤 방법을 사용하더라도 `routes.rb` 파일에 기록된 순서대로 라우팅이 표시됩니다. 하나의 라우팅에 에 대해 다음과 같은 정보를 보여줍니다. @@ -1052,7 +1040,7 @@ Rails에는 라우팅을 확인하는 기능과 테스트를 하기 위한 기 * 매칭되는 URL 패턴 * 그 라우팅에서 사용되는 파라미터 -아래는 어떤 RESTful한 라우팅에 대해서 `bin/rails routes`을 실행한 결과를 발췌한 것입니다. +아래는 어떤 RESTful한 라우팅에 대해서 `rails routes`을 실행한 결과를 발췌한 것입니다. ``` users GET /users(.:format) users#index @@ -1061,13 +1049,24 @@ new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit ``` -`CONTROLLER` 환경 변수를 넘겨서 특정 컨트롤러의 라우팅 목록만을 볼 수도 있습니다. +grep 옵션(-g)을 사용하여 라우팅 목록을 확인할 수도 있습니다. URL 헬퍼 메소드 이름, HTTP 동사, 또는 URL 경로에 부분적으로일치하는 모든 라우팅을 출력합니다. -```bash -$ CONTROLLER=users bin/rails routes +``` +$ bin/rails routes -g new_comment +$ bin/rails routes -g POST +$ bin/rails routes -g admin ``` -TIP: 라우팅 목록이 너무 길지 않다면 `bin/rails routes` 쪽이 읽기 편할 것입니다. +-c 옵션을 사용해서 특정 컨트롤러의 라우팅 목록만을 볼 수도 있습니다. + +``` +$ bin/rails routes -c users +$ bin/rails routes -c admin/users +$ bin/rails routes -c Comments +$ bin/rails routes -c Articles::CommentsController +``` + +TIP: 라우팅 목록이 너무 길지 않다면 `rails routes` 쪽이 읽기 편할 것입니다. ### 라우팅 테스트하기 @@ -1107,5 +1106,3 @@ assert_recognizes({ controller: 'photos', action: 'create' }, { path: 'photos', ```ruby assert_routing({ path: 'photos', method: :post }, { controller: 'photos', action: 'create' }) ``` - -TIP: 이 가이드는 [Rails Guilde 일본어판](http://railsguides.jp)으로부터 번역되었습니다. diff --git a/guides/source/ko/ruby_on_rails_guides_guidelines.md b/guides/source/ko/ruby_on_rails_guides_guidelines.md index 2d4a9e72e0aaa..aabe5f7074077 100644 --- a/guides/source/ko/ruby_on_rails_guides_guidelines.md +++ b/guides/source/ko/ruby_on_rails_guides_guidelines.md @@ -14,7 +14,7 @@ Rails 가이드의 가이드라인 마크다운(Markdown) ------- -가이드는 [GitHub Flavored Markdown](http://github.github.com/github-flavored-markdown/)으로 작성되어 있습니다. 참고자료로 정리된 [Markdown 문서](http://daringfireball.net/projects/markdown/syntax), [치트 시트](http://daringfireball.net/projects/markdown/basics), 일반 마크다운과의 차이점에 관한 [문서](http://github.github.com/github-flavored-markdown/)가 있습니다. +가이드는 [GitHub Flavored Markdown](https://github.github.com/github-flavored-markdown/)으로 작성되어 있습니다. 참고자료로 정리된 [Markdown 문서](http://daringfireball.net/projects/markdown/syntax), [치트 시트](http://daringfireball.net/projects/markdown/basics), 일반 마크다운과의 차이점에 관한 [문서](https://github.github.com/github-flavored-markdown/)가 있습니다. Prologue -------- diff --git a/guides/source/ko/security.md b/guides/source/ko/security.md index 22eed518d3592..39dfe56bee18e 100644 --- a/guides/source/ko/security.md +++ b/guides/source/ko/security.md @@ -17,15 +17,40 @@ Rails 보안 가이드 들어가면서 ------------ -웹 애플리케이션 프레임워크는 웹 애플리케이션을 손쉽게 개발할 수 있도록 만들어졌습니다. 그 중에서 보안을 비교적 유지하기 쉬운 프레임워크도 있습니다. 하지만, 어떤 프레임워크가 다른 것들보다도 안전하다고 말하기 어렵기도 합니다. 올바르게 사용한다면, 대부분의 프레임워크는 어느 정도 안전한 웹 애플리케이션을 만들 수 있습니다(반대로 말하자면, 올바르게 사용하지 않으면, 어떤 웹 애플리케이션도 안전하다고 할 수 없습니다). Ruby on Rails에는 이러한 문제가 큰 보안 문제를 발생시키지 않도록 편리한 헬퍼 메소드(SQL 인젝션을 위한 것 등)를 몇가지 제공하고 있습니다. 필자가 지금까지 점검했던 Rails 애플리케이션들은 대부분 보안 기준을 통과하고 있어서 무척 기쁠 따름입니다. - -일반적으로 도입하는 것만으로 보안을 용이하게 도입할 수 있는 편리한 도구는 없습니다. 보안은 프레임워크를 사용하는 사람에게 의존합니다. 상황에 따라서는 개발방법 역시 보안에 영향을 미치는 경우도 있습니다. 보안은 웹 애플리케이션을 구성하는 여러 계층(서버측 저장소, 웹서버, 웹 애플리케이션 자신 등)에 의존합니다. 어느 한 계층에 문제가 있다면 다른 계층이 안전하다고 하더라도 전체의 보안 수준은 문제가 있는 계층의 수준으로 떨어지고 맙니다. - -Gartner Group은 공격의 75%가 웹 애플리케이션 층에 대해서 이루어지고 있다고 보고했으며, 점검을 받은 300개의 웹사이트중 97%가 취약성을 가지고 있었다는 결과를 얻었습니다. 이것은 일반인이라도 이해하고 조작할 수 있을 정도로 웹 애플리케이션이 간단하여 비교적 공격이 쉽기 때문입니다. - -웹 애플리케이션에 대한 공격에는 사용자 계정을 탈취, 접근 권한 확인을 우회, 비밀 데이터를 훔치기, 정당하지 않은 컨텐츠 접근 등의 다양한 방법이 존재합니다. 나아가 공격자가 돈벌기, 또는 기업 재산에 손을 대어, 기업의 이미지를 훼손하기 위한 목적으로 트로이 목마 프로그램이나 스팸 메일 자동 송신 프로그램을 설치할 수도 있습니다. 이러한 공격을 받기 쉬운 지점을 제거하여 공격을 예방하고, 영향을 최소화하려면 적들의 공격방법을 완전히 이해해둘 필요가 있습니다. 그렇지 않으면 알맞은 대책을 세울 수 없습니다. - -안전한 웹 애플리케이션을 개발하기 위해서 필요한 것은 모든 계층을 최신 버전으로 유지할 것, 그리고 적을 알아 두는 것입니다. 최신 버전을 유지하라는 것은 보안 메일링 리스트를 구독하고, 보안 관련 블로그를 읽으며, 최신 프로그램을 사용하여 보안 체크하는 습관을 가지라는 의미입니다(추가자료를 참조해주세요). 필자는 이러한 것들을 손으로 직접 하고 있습니다만, 이것은 일부러 직접 하는 것으로 귀찮은 논리상의 보안 문제를 발견하기 위한 방법이 될 수 있기 때문입니다. +웹 애플리케이션 프레임워크는 웹 애플리케이션을 손쉽게 개발할 수 있도록 +만들어졌습니다. 그 중에서 보안을 비교적 유지하기 쉬운 프레임워크도 있습니다. +하지만 어떤 프레임워크가 다른 것들보다도 안전하다고 말하기 어렵기도 합니다. +올바르게 사용한다면, 대부분의 프레임워크는 어느 정도 안전한 웹 애플리케이션을 +만들 수 있습니다. Ruby on Rails에는 이러한 문제가 큰 보안 문제를 발생시키지 +않도록 편리한 헬퍼 메소드(SQL 인젝션을 위한 것 등)를 몇가지 제공하고 있습니다. + +일반적으로 도입하는 것만으로 보안을 용이하게 도입할 수 있는 편리한 도구는 +없습니다. 보안은 프레임워크를 사용하는 사람에게 의존합니다. 상황에 따라서는 +개발방법 역시 보안에 영향을 미치는 경우도 있습니다. 보안은 웹 애플리케이션을 +구성하는 여러 계층(서버측 저장소, 웹서버, 웹 애플리케이션 자신 등)에 +의존합니다. 어느 한 계층에 문제가 있다면 다른 계층이 안전하다고 하더라도 +전체의 보안 수준은 문제가 있는 계층의 수준으로 떨어지고 맙니다. + +Gartner Group은 공격의 75%가 웹 애플리케이션 층에 대해서 이루어지고 있다고 +보고했으며, 점검을 받은 300개의 웹사이트중 97%가 취약성을 가지고 있었다는 +결과를 얻었습니다. 이것은 일반인이라도 이해하고 조작할 수 있을 정도로 +웹 애플리케이션이 간단하여 비교적 공격이 쉽기 때문입니다. + +웹 애플리케이션에 대한 공격에는 사용자 계정을 탈취, 접근 권한 확인을 우회, +비밀 데이터를 훔치기, 정당하지 않은 컨텐츠 접근 등의 다양한 방법이 존재합니다. +나아가 공격자가 돈벌기, 또는 기업 재산에 손을 대어, 기업의 이미지를 훼손하기 +위한 목적으로 트로이 목마 프로그램이나 스팸 메일 자동 송신 프로그램을 설치할 +수도 있습니다. 이러한 공격을 받기 쉬운 지점을 제거하여 공격을 예방하고, 영향을 +최소화하려면 적들의 공격방법을 완전히 이해해둘 필요가 있습니다. 그렇지 않으면 +알맞은 대책을 세울 수 없습니다. + +안전한 웹 애플리케이션을 개발하기 위해서 필요한 것은 모든 계층을 최신 버전으로 +유지할 것, 그리고 적을 알아 두는 것입니다. 최신 버전을 유지하라는 것은 보안 +메일링 리스트를 구독하고, 보안 관련 블로그를 읽으며, 최신 프로그램을 사용하여 +보안 체크하는 습관을 가지라는 의미입니다(추가자료를 +참조해주세요). 필자는 이러한 것들을 손으로 직접 하고 있습니다만, 이것은 일부러 +직접 하는 것으로 귀찮은 논리상의 보안 문제를 발견하기 위한 방법이 될 수 있기 +때문입니다. 세션 -------- @@ -38,24 +63,24 @@ NOTE: _HTTP는 상태를 유지하지 않는(Stateless) 프로토콜입니다. 많은 애플리케이션에서는 어떤 사용자가 어떤 상태인지 추적할 필요가 있습니다. 쇼핑몰에서의 장바구니 기능이나, 현재 로그인해있는 사용자의 아이디 등이 이에 해당합니다. 세션이라는 개념이 없다면, 사용자를 식별, 인증 작업을 요청이 발생할때마다 해야할 것입니다. Rails는 사용자가 애플리케이션에 처음 접근했을 때에 자동적으로 세션을 생성합니다. 사용자가 이미 애플리케이션을 사용중이라면 기존의 세션을 가져옵니다. -일반적으로 세션을 구성하는 요소는 값이 들어있는 해시와 세션 id입니다. 세션 id는 32문자의 문자열로, 해시를 찾기 위해서 사용합니다. 클라이언트 브라우저에 전송되는 Cookie에는 항상 세션 id가 포함됩니다. 다른 관점에서 보자면, 브라우저는 클라이언트로부터 요청을 받을때마다 Cookie를 전송합니다. Rails에서는 세션 메소드를 사용해서 값을 읽고 쓸 수 있습니다. +일반적으로 세션을 구성하는 요소는 값이 들어있는 해시와 세션 ID입니다. 세션 ID는 32문자의 문자열로, 해시를 찾기 위해서 사용합니다. 클라이언트 브라우저에 전송되는 Cookie에는 항상 세션 ID가 포함됩니다. 다른 관점에서 보자면, 브라우저는 클라이언트로부터 요청을 받을때마다 Cookie를 전송합니다. Rails에서는 세션 메소드를 사용해서 값을 읽고 쓸 수 있습니다. ```ruby session[:user_id] = @current_user.id User.find(session[:user_id]) ``` -### 세션 id +### 세션 ID -NOTE: _세션 id는 32바이트의 MD5해시값입니다._ +NOTE: _세션 ID는 32바이트의 MD5해시값입니다._ -1개의 세션 id는 임의의 문자열의 해시값으로 있습니다. 이 임의의 문자열은 현재 시각, 0에서 1사이의 난수, Ruby 인터프리터의 프로세스id(이것도 기본적으로 난수입니다), 그리고 약간의 문자열입니다. 현시점에는 Rails의 세션 id에 무차별 대입 공격(Brute force attack)을 하는 것은 불가능합니다. MD5는 현재까지 깨지지는 않았습니다만, 충돌을 임의발생시킬 수 있는 문제가 있으므로, 같은 해시값을 다른 입력 텍스트로부터 생성하는 것은 '이론적으로' 불가능하지 않습니다. 다만 이것이 보안상의 문제가 된 적은 아직 없습니다. +세션 ID는 플랫폼에서 사용 가능한 방법(OpenSSL, /dev/urandeom 또는 Win32)을 통해 임의의 hex 문자열을 생성하는 `SecureRandom.hex`로부터 생성됩니다. 현재로서는 Rails의 세션 ID를 무차별 대입 공격하는 것은 어렵습니다. ### 세션 탈취(Session Hijacking) -WARNING: _사용자의 세션 id가 노출되면, 공격자는 그 사용자인척하며 웹 애플리케이션을 사용할 수 있습니다._ +WARNING: _사용자의 세션 ID가 노출되면, 공격자는 그 사용자인척하며 웹 애플리케이션을 사용할 수 있습니다._ -많은 웹 애플리케이션에서는 어떤 식으로든 인증 시스템을 가지고 있습니다. 사용자가 사용자 이름과 비밀번호를 입력하면, 웹 애플리케이션에서는 그것을 확인하고 그에 대응하는 사용자id를 세션 해시에 저장합니다. 그때부터 세션이 유효하게 됩니다. 요청이 발생할때마다, 웹 애플리케이션은 세션에 존재하는 사용자id를 가지는 사용자를 가져옵니다. 그 때에 다시 인증을 할 필요는 없습니다. 세션은 cookie내의 세션 id를 통해서 식별할 수 있습니다. +많은 웹 애플리케이션에서는 어떤 식으로든 인증 시스템을 가지고 있습니다. 사용자가 사용자 이름과 비밀번호를 입력하면, 웹 애플리케이션에서는 그것을 확인하고 그에 대응하는 사용자 id를 세션 해시에 저장합니다. 그때부터 세션이 유효하게 됩니다. 요청이 발생할때마다, 웹 애플리케이션은 세션에 존재하는 사용자 id를 가지는 사용자를 가져옵니다. 그 때에 다시 인증을 할 필요는 없습니다. 세션은 cookie내의 세션 ID를 통해서 식별할 수 있습니다. 이처럼 cookie는 웹 애플리케이션에서 일시적인 인증 기능을 제공합니다. 다른 사람의 cookie를 훔쳐올 수 있다면 그 사용자의 권한으로 웹 애플리케이션을 사용할 수 있게 됩니다. 이를 통해 심각한 문제가 발생할 가능성이 있습니다. 세션 탈취를 막기 위한 방법을 몇가지 소개합니다. @@ -67,9 +92,9 @@ WARNING: _사용자의 세션 id가 노출되면, 공격자는 그 사용자인 * 공용 컴퓨터에서 작업을 한 이후에 cookie를 제거하는 사용자는 거의 없습니다. 마지막 사용자가 웹 애플리케이션에서 로그아웃하는 것을 잊어버리고 자리를 뜨게 되면, 다음 사용자는 그 웹 애플리케이션을 그대로 쓸 수 있습니다. 사용자에게는 _로그아웃 버튼_을 반드시 제공해야 합니다. 그것도 _눈에 잘 띄는_것으로요. -* 크로스 사이트 스크립팅(XSS) 공격은 많은 경우에 사용자의 cookie를 손에 넣는 것이 목적입니다. XSS를 참고해주세요. +* 크로스 사이트 스크립팅(XSS) 공격은 많은 경우에 사용자의 cookie를 손에 넣는 것이 목적입니다. [XSS](#크로스-사이트-스크립팅-xss)를 참고해주세요. -* 공격자가 자신이 모르는 cookie를 훔치는 대신 자신이 알고 있는 cookie의 세션 id를 고정시키는 공격 방법도 존재합니다. 자세한 설명은 이후에 설명할 세션 고정에 대한 설명을 참조해주세요. +* 공격자가 자신이 모르는 cookie를 훔치는 대신 자신이 알고 있는 cookie의 세션 ID를 고정시키는 공격 방법도 존재합니다. 자세한 설명은 이후에 설명할 세션 고정에 대한 설명을 참조해주세요. 대부분의 경우 공격자의 목적은 돈입니다. [Symantec Global Internet Security Threat Report](http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf)에 의하면 훔친 은행 계좌의 거래 가격은 사용가능한 자금에 따라 다릅니다만 대체로 $10부터 $1000 정도, 신용카드번호가 $0.40부터 $20 정도, 온라인 경매 사이트의 계정이 $1부터 $8 정도, 이메일의 비밀번호가 $4부터 $30 정도라고 합니다. @@ -90,7 +115,13 @@ Rails 2에서 CookieStore라는 새로운 기본 세션 저장소가 도입되 * Cookie의 크기는 4 KB로 정해져 있습니다. _일반적으로 현재 사용자의 데이터베이스id를 세션에 저장하는 데에는 아무런 문제가 없습니다._ -* Cookie에 저장되어 있는 것은 일반 텍스트(실제로는 Base64로 인코딩되어 있습니다만, 암호화되어 있지는 않습니다)이므로, 세션에 저장되어 있는 정보는 마음만 먹는다면 클라이언트 쪽에서 바로 확인할 수 있습니다. 여기에서도 알 수 있듯, _어떤 비밀 정보도 cookie에 저장해서는 안됩니다_. 서버 쪽에서는 세션이 변경되는 것을 막기 위해서 서버상의 비밀키를 사용하여 세션의 다이제스트를 계산하여, 그것을 cookie의 마지막에 추가합니다. +* Cookie에 저장되어 있는 것은 일반 텍스트(실제로는 Base64로 인코딩되어 있습니다만, 암호화되어 있지는 않습니다)이므로, 세션에 저장되어 있는 정보는 마음만 먹는다면 클라이언트 쪽에서 바로 확인할 수 있습니다. 여기에서도 알 수 있듯, _어떤 비밀 정보도 cookie에 저장해서는 안됩니다_. 서버 쪽에서는 세션이 변경되는 것을 막기 위해서 서버 상의 비밀키(`secrets.secret_token`)를 사용하여 세션의 다이제스트를 계산하여, 그것을 cookie의 마지막에 추가합니다. + +그러나 Rails 4부터 기본 저장소가 EncryptedCookieStore로 변경되었습니다. +EncryptedCookieStore를 사용하면 세션은 cookie에 저장되기 전에 암호화됩니다. +이는 사용자가 쿠키에 접근하고 변경하는 것을 막을 수 있습니다. 그러므로 세션은 +좀 더 데이터를 저장하기 안전한 장소가 되었습니다. 암호화에는 +`config/secrets.yml`에 저장되어 있는 `secrets.secret_key_base`를 사용합니다. 다시 말해, cookie 저장소의 (변경방지용) 보안은 이 서버상의 비밀키(또한 다이제스트 생성 알고리즘 -- 호환성을 위해서 기본으로 SHA1을 사용)에 걸려 있습니다. 따라서 _비밀키를 쉬운 문자열(사전에서 가져온 단어나, 30글자 이하의 짧은 문자열)을 사용해서는 안됩니다_. @@ -238,6 +269,8 @@ _POST 요청도 (의도에 반해서) 자동적으로 전송되는 경우가 있 `