-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgetting_started.html
1641 lines (1448 loc) · 123 KB
/
getting_started.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>레일스로 시작하기 — Ruby on Rails Guides</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" data-turbolinks-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print">
<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shCore.css" data-turbolinks-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shThemeRailsGuides.css" data-turbolinks-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/fixes.css" data-turbolinks-track="reload">
<link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<script src="javascripts/syntaxhighlighter.js" data-turbolinks-track="reload"></script>
<script src="javascripts/turbolinks.js" data-turbolinks-track="reload"></script>
<script src="javascripts/guides.js" data-turbolinks-track="reload"></script>
<script src="javascripts/responsive-tables.js" data-turbolinks-track="reload"></script>
<meta property="og:title" content="레일스로 시작하기 — Ruby on Rails Guides" />
<meta name="description" content="레일스로 시작하기본 가이드 내용에서는 루비온레일스(이하 레일스)를 준비하고 실행하는 것에 대해서 다룬다.본 가이드를 읽은 후에는 다음의 내용을 알게 된다. 레일스를 설치하는 방법, 레일스 애플리케이션을 생성하는 방법, 애플리케이션을 데이터베이스로 연결하는 방법 레일스 애플리케이션의 일반적인 레이아웃 MVC(Model, View, Controller)와 RESTful 디자인에 대한 기본 원리 레일스 애플리케이션의 시작부분을 신속하게 생성하는 방법" />
<meta property="og:description" content="레일스로 시작하기본 가이드 내용에서는 루비온레일스(이하 레일스)를 준비하고 실행하는 것에 대해서 다룬다.본 가이드를 읽은 후에는 다음의 내용을 알게 된다. 레일스를 설치하는 방법, 레일스 애플리케이션을 생성하는 방법, 애플리케이션을 데이터베이스로 연결하는 방법 레일스 애플리케이션의 일반적인 레이아웃 MVC(Model, View, Controller)와 RESTful 디자인에 대한 기본 원리 레일스 애플리케이션의 시작부분을 신속하게 생성하는 방법" />
<meta property="og:locale" content="en_US" />
<meta property="og:site_name" content="Ruby on Rails Guides" />
<meta property="og:image" content="https://avatars.githubusercontent.com/u/4223" />
<meta property="og:type" content="website" />
</head>
<body class="guide">
<div id="topNav">
<div class="wrapper">
<strong class="more-info-label">공식 웹사이트 <a href="https://rubyonrails.org/">rubyonrails.org:</a> </strong>
<span class="red-button more-info-button">
루비온레일스 웹사이트
</span>
<ul class="more-info-links s-hidden">
<li class="more-info"><a href="https://weblog.rubyonrails.org/">블로그</a></li>
<li class="more-info"><a href="https://guides.rubyonrails.org/">영문가이드</a></li>
<li class="more-info"><a href="https://api.rubyonrails.org/">레일스API</a></li>
<li class="more-info"><a href="https://stackoverflow.com/questions/tagged/ruby-on-rails">질문하기</a></li>
<li class="more-info"><a href="https://github.com/rails/rails">GitHub에서 기여하기</a></li>
</ul>
</div>
</div>
<div id="header">
<div class="wrapper clearfix">
<h1><a href="index.html" title="Return to home page">Guides.rubyonrails.org</a></h1>
<ul class="nav">
<li><a class="nav-item" href="index.html">홈</a></li>
<li class="guides-index guides-index-large">
<a href="index.html" id="guidesMenu" class="guides-index-item nav-item">가이드 인덱스</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<div class="guides-section-container">
<div class="guides-section">
<dt>시작하면서</dt>
<dd><a href="getting_started.html">레일스로 시작하기</a></dd>
</div>
<div class="guides-section">
<dt>모델</dt>
<dd><a href="active_record_basics.html">액티브 레코드 기본</a></dd>
<dd><a href="active_record_migrations.html">액티브 레코드 마이그레이션</a></dd>
<dd><a href="active_record_validations.html">액티브 레코드 유효성 검증</a></dd>
<dd><a href="active_record_callbacks.html">액티브 레코드 콜백</a></dd>
<dd><a href="association_basics.html">Active Record Associations</a></dd>
<dd><a href="active_record_querying.html">Active Record Query Interface</a></dd>
</div>
<div class="guides-section">
<dt>Views</dt>
<dd><a href="layouts_and_rendering.html">Layouts and Rendering in Rails</a></dd>
<dd><a href="form_helpers.html">Action View Form Helpers</a></dd>
</div>
<div class="guides-section">
<dt>Controllers</dt>
<dd><a href="action_controller_overview.html">Action Controller Overview</a></dd>
<dd><a href="routing.html">Rails Routing from the Outside In</a></dd>
</div>
<div class="guides-section">
<dt>Other Components</dt>
<dd><a href="active_support_core_extensions.html">Active Support Core Extensions</a></dd>
<dd><a href="action_mailer_basics.html">Action Mailer Basics</a></dd>
<dd><a href="active_job_basics.html">Active Job Basics</a></dd>
<dd><a href="active_storage_overview.html">Active Storage Overview</a></dd>
<dd><a href="action_cable_overview.html">Action Cable Overview</a></dd>
</div>
<div class="guides-section">
<dt>Digging Deeper</dt>
<dd><a href="i18n.html">Rails Internationalization (I18n) API</a></dd>
<dd><a href="testing.html">Testing Rails Applications</a></dd>
<dd><a href="security.html">Securing Rails Applications</a></dd>
<dd><a href="debugging_rails_applications.html">Debugging Rails Applications</a></dd>
<dd><a href="configuring.html">Configuring Rails Applications</a></dd>
<dd><a href="command_line.html">The Rails Command Line</a></dd>
<dd><a href="asset_pipeline.html">The Asset Pipeline</a></dd>
<dd><a href="working_with_javascript_in_rails.html">Working with JavaScript in Rails</a></dd>
<dd><a href="autoloading_and_reloading_constants.html">Autoloading and Reloading Constants (Zeitwerk Mode)</a></dd>
<dd><a href="autoloading_and_reloading_constants_classic_mode.html">Autoloading and Reloading Constants (Classic Mode)</a></dd>
<dd><a href="caching_with_rails.html">Caching with Rails: An Overview</a></dd>
<dd><a href="api_app.html">Using Rails for API-only Applications</a></dd>
</div>
<div class="guides-section">
<dt>Extending Rails</dt>
<dd><a href="rails_on_rack.html">Rails on Rack</a></dd>
<dd><a href="generators.html">Creating and Customizing Rails Generators & Templates</a></dd>
</div>
<div class="guides-section">
<dt>Contributions</dt>
<dd><a href="contributing_to_ruby_on_rails.html">Contributing to Ruby on Rails</a></dd>
<dd><a href="api_documentation_guidelines.html">API Documentation Guidelines</a></dd>
<dd><a href="ruby_on_rails_guides_guidelines.html">Guides Guidelines</a></dd>
</div>
<div class="guides-section">
<dt>Policies</dt>
<dd><a href="maintenance_policy.html">Maintenance Policy</a></dd>
</div>
<div class="guides-section">
<dt>Release Notes</dt>
<dd><a href="upgrading_ruby_on_rails.html">Upgrading Ruby on Rails</a></dd>
<dd><a href="6_0_release_notes.html">Version 6.0 - August 2019</a></dd>
<dd><a href="5_2_release_notes.html">Version 5.2 - April 2018</a></dd>
<dd><a href="5_1_release_notes.html">Version 5.1 - April 2017</a></dd>
<dd><a href="5_0_release_notes.html">Version 5.0 - June 2016</a></dd>
<dd><a href="4_2_release_notes.html">Version 4.2 - December 2014</a></dd>
<dd><a href="4_1_release_notes.html">Version 4.1 - April 2014</a></dd>
<dd><a href="4_0_release_notes.html">Version 4.0 - June 2013</a></dd>
<dd><a href="3_2_release_notes.html">Version 3.2 - January 2012</a></dd>
<dd><a href="3_1_release_notes.html">Version 3.1 - August 2011</a></dd>
<dd><a href="3_0_release_notes.html">Version 3.0 - August 2010</a></dd>
<dd><a href="2_3_release_notes.html">Version 2.3 - March 2009</a></dd>
<dd><a href="2_2_release_notes.html">Version 2.2 - November 2008</a></dd>
</div>
</div>
</div>
</li>
<li><a class="nav-item" href="contributing_to_ruby_on_rails.html">기여하기</a></li>
<li class="guides-index guides-index-small">
<select class="guides-index-item nav-item">
<option value="index.html">가이드 인덱스</option>
<optgroup label="시작하면서">
<option value="getting_started.html">레일스로 시작하기</option>
</optgroup>
<optgroup label="모델">
<option value="active_record_basics.html">액티브 레코드 기본</option>
<option value="active_record_migrations.html">액티브 레코드 마이그레이션</option>
<option value="active_record_validations.html">액티브 레코드 유효성 검증</option>
<option value="active_record_callbacks.html">액티브 레코드 콜백</option>
<option value="association_basics.html">Active Record Associations</option>
<option value="active_record_querying.html">Active Record Query Interface</option>
</optgroup>
<optgroup label="Views">
<option value="layouts_and_rendering.html">Layouts and Rendering in Rails</option>
<option value="form_helpers.html">Action View Form Helpers</option>
</optgroup>
<optgroup label="Controllers">
<option value="action_controller_overview.html">Action Controller Overview</option>
<option value="routing.html">Rails Routing from the Outside In</option>
</optgroup>
<optgroup label="Other Components">
<option value="active_support_core_extensions.html">Active Support Core Extensions</option>
<option value="action_mailer_basics.html">Action Mailer Basics</option>
<option value="active_job_basics.html">Active Job Basics</option>
<option value="active_storage_overview.html">Active Storage Overview</option>
<option value="action_cable_overview.html">Action Cable Overview</option>
</optgroup>
<optgroup label="Digging Deeper">
<option value="i18n.html">Rails Internationalization (I18n) API</option>
<option value="testing.html">Testing Rails Applications</option>
<option value="security.html">Securing Rails Applications</option>
<option value="debugging_rails_applications.html">Debugging Rails Applications</option>
<option value="configuring.html">Configuring Rails Applications</option>
<option value="command_line.html">The Rails Command Line</option>
<option value="asset_pipeline.html">The Asset Pipeline</option>
<option value="working_with_javascript_in_rails.html">Working with JavaScript in Rails</option>
<option value="autoloading_and_reloading_constants.html">Autoloading and Reloading Constants (Zeitwerk Mode)</option>
<option value="autoloading_and_reloading_constants_classic_mode.html">Autoloading and Reloading Constants (Classic Mode)</option>
<option value="caching_with_rails.html">Caching with Rails: An Overview</option>
<option value="api_app.html">Using Rails for API-only Applications</option>
</optgroup>
<optgroup label="Extending Rails">
<option value="rails_on_rack.html">Rails on Rack</option>
<option value="generators.html">Creating and Customizing Rails Generators & Templates</option>
</optgroup>
<optgroup label="Contributions">
<option value="contributing_to_ruby_on_rails.html">Contributing to Ruby on Rails</option>
<option value="api_documentation_guidelines.html">API Documentation Guidelines</option>
<option value="ruby_on_rails_guides_guidelines.html">Guides Guidelines</option>
</optgroup>
<optgroup label="Policies">
<option value="maintenance_policy.html">Maintenance Policy</option>
</optgroup>
<optgroup label="Release Notes">
<option value="upgrading_ruby_on_rails.html">Upgrading Ruby on Rails</option>
<option value="6_0_release_notes.html">Version 6.0 - August 2019</option>
<option value="5_2_release_notes.html">Version 5.2 - April 2018</option>
<option value="5_1_release_notes.html">Version 5.1 - April 2017</option>
<option value="5_0_release_notes.html">Version 5.0 - June 2016</option>
<option value="4_2_release_notes.html">Version 4.2 - December 2014</option>
<option value="4_1_release_notes.html">Version 4.1 - April 2014</option>
<option value="4_0_release_notes.html">Version 4.0 - June 2013</option>
<option value="3_2_release_notes.html">Version 3.2 - January 2012</option>
<option value="3_1_release_notes.html">Version 3.1 - August 2011</option>
<option value="3_0_release_notes.html">Version 3.0 - August 2010</option>
<option value="2_3_release_notes.html">Version 2.3 - March 2009</option>
<option value="2_2_release_notes.html">Version 2.2 - November 2008</option>
</optgroup>
</select>
</li>
</ul>
</div>
</div>
<hr class="hide" />
<div id="feature">
<div class="wrapper">
<h2 id="getting-started-with-rails">레일스로 시작하기</h2><p>본 가이드 내용에서는 루비온레일스(이하 레일스)를 준비하고 실행하는 것에 대해서 다룬다.</p><p>본 가이드를 읽은 후에는 다음의 내용을 알게 된다.</p>
<ul>
<li>레일스를 설치하는 방법, 레일스 애플리케이션을 생성하는 방법, 애플리케이션을 데이터베이스로 연결하는 방법</li>
<li>레일스 애플리케이션의 일반적인 레이아웃</li>
<li>MVC(Model, View, Controller)와 RESTful 디자인에 대한 기본 원리</li>
<li>레일스 애플리케이션의 시작부분을 신속하게 생성하는 방법</li>
</ul>
<div id="subCol">
<h3 class="chapter"><img src="images/chapters_icon.gif" alt="" />Chapters</h3>
<ol class="chapters">
<li><a href="#guide-assumptions">가이드에 대한 전제조건</a></li>
<li><a href="#what-is-rails">레일스란 무엇인가</a></li>
<li>
<a href="#creating-a-new-rails-project">레일스 프로젝트 생성하기</a>
<ul>
<li><a href="#installing-rails">레일스 설치하기</a></li>
<li><a href="#creating-the-blog-application">Blog 애플리케이션 생성하기</a></li>
</ul>
</li>
<li>
<a href="#hello-rails-bang">Hello, Rails!</a>
<ul>
<li><a href="#starting-up-the-web-server">웹서버 시작하기</a></li>
<li><a href="#say-hello-rails">"Hello, Rails" 표시하기</a></li>
<li><a href="#setting-the-application-home-page">애플리케이션 홈 페이지 설정하기</a></li>
</ul>
</li>
<li>
<a href="#getting-up-and-running">작동하기</a>
<ul>
<li><a href="#laying-down-the-groundwork">기본틀 잡기</a></li>
<li><a href="#the-first-form">첫번째 폼</a></li>
<li><a href="#creating-articles">기사 작성하기</a></li>
<li><a href="#creating-the-article-model">Article 모델 생성하기</a></li>
<li><a href="#running-a-migration">마이그레이션 작업 수행하기</a></li>
<li><a href="#saving-data-in-the-controller">컨트롤러에서 데이터 저장하기</a></li>
<li><a href="#showing-articles">기사 보여주기</a></li>
<li><a href="#listing-all-articles">모든 기사 목록 보기</a></li>
<li><a href="#adding-links">링크 추가하기</a></li>
<li><a href="#adding-some-validation">몇가지 유효성 검사 추가하기</a></li>
<li><a href="#updating-articles">기사 업데이트하기</a></li>
<li><a href="#using-partials-to-clean-up-duplication-in-views">파셜을 이용하여 뷰의 중복 코드 정리하기</a></li>
<li><a href="#deleting-articles">기사 삭제하기</a></li>
</ul>
</li>
<li>
<a href="#adding-a-second-model">두번째 모델 추가하기</a>
<ul>
<li><a href="#generating-a-model">모델 생성하기</a></li>
<li><a href="#associating-models">모델 관계 선언하기</a></li>
<li><a href="#adding-a-route-for-comments">댓글에 대한 라우트 추가하기</a></li>
<li><a href="#generating-a-controller">컨트롤러 생성하기</a></li>
</ul>
</li>
<li>
<a href="#refactoring">리팩토링하기</a>
<ul>
<li><a href="#rendering-partial-collections">파셜 컬렉션 렌더링하기</a></li>
<li><a href="#rendering-a-partial-form">파셜 폼 렌더링하기</a></li>
</ul>
</li>
<li>
<a href="#deleting-comments">댓글 삭제하기</a>
<ul>
<li><a href="#deleting-associated-objects">관련 객체 삭제하기</a></li>
</ul>
</li>
<li>
<a href="#security">보안</a>
<ul>
<li><a href="#basic-authentication">기본 인증</a></li>
<li><a href="#other-security-considerations">다른 보안 고려사항</a></li>
</ul>
</li>
<li><a href="#whats-next">향후 계획</a></li>
<li><a href="#configuration-gotchas">설정시 유의사항</a></li>
</ol>
</div>
</div>
</div>
<div id="container">
<div class="wrapper">
<div id="mainCol">
<h3 id="guide-assumptions"><a class="anchorlink" href="#guide-assumptions">1 가이드에 대한 전제조건</a></h3><p>본 가이드 내용은 레일스 애플리케이션을 만들어 본 경험이 없는 초보자를 대상으로 작성되었다. 반드시 레일스에 대한 경험을 필요로 하지 않는다.</p><p>레일스는 루비 언어로 만들어진 웹애플리케이션 프레임워크이다.
루비 언어에 대한 사전 지식이 없이 바로 레일스로 작업을 하게 되면 매우 경사진 학습곡선을 경험하게 될 것이다. 아래에 루비 언어를 배우기 위한 온라인 자원에 대한 몇가지 목록을 모아 놓았다.</p>
<ul>
<li><a href="https://www.ruby-lang.org/en/documentation/">루비언어 공식 웹사이트</a></li>
<li><a href="https://github.com/EbookFoundation/free-programming-books/blob/master/free-programming-books.md#ruby">무료 프로그래밍 서적 목록</a></li>
</ul>
<p>주의할 것은 위에서 소개한 서적 중에는, 그 내용이 아주 훌륭한 것이지만, 오래된 버전인 루비 1.6과 주로 1.8 버전에 대한 것들이 있어서 레일스로 개발할 때 주로 접하게 되는 루비 문법들이 포함되지 않을 수 있다는 것이다.</p><h3 id="what-is-rails"><a class="anchorlink" href="#what-is-rails">2 레일스란 무엇인가</a></h3><p>레일스란 루비 언어로 작성된 웹애플리케이션 개발 프레임워크이다.
모든 개발자들이 작업을 시작할 때 필요로 하는 것들이 사전에 준비된 것으로 가정하여 웹애플리케이션을 보다 쉽게 개발할 수 있도록 만들어졌다.
다수의 언어와 프레임워크보다 더 작은 량의 코드를 작성하여 더 많은 것을 구현할 수 있게 해준다.
고급 레일스 개발자들 역시 레일스가 웹애플리케이션 개발을 더 재밋게 해 준다고 말한다.</p><p>레일스는 독단적인 측면을 가지는 소프트웨어다. 즉 최선의 방식이 있다고 가정하여 그 방법을 사용하도록 권하지만 경우에 따라서는 다른 대안을 사용하지 않도록 한다. 소위 "레일스 방식"을 배우게 되면 생산성에 있어서 어마어마한 향상을 가져오게 되는 것을 알게 될 것이다. 다른 언어를 사용할 때 익혔던 습관을 레일스 개발시에 버리지 못하거나 다른 곳에서 배웠던 패턴을 그대로 사용하려고 할 경우는 레일스를 사용하므로써 얻게 되는 즐거움을 덜 느끼게 될 것이다.</p><p>레일스 철학은 두 개의 중요한 가이드 원칙을 포함한다.</p>
<ul>
<li>
<strong>Don't Repeat Yourself(같은 내용의 정보를 반복하지 말 것):</strong> DRY란 하나의 소프트웨어 개발 원칙으로 "모든 지식은 하나의 시스템 내에서 유일해야 하고 모호성이 없어야 하며 권위를 가져야 한다"는 내용을 의미한다. 같은 내용의 정보를 반복해서 작성하지 않으므로써 코드를 더 잘 유지할 수 있고 더 많은 확장성을 부여할 수 있으며 버그를 줄일 수 있게 되는 것이다.</li>
<li>
<strong>Convention Over Configuration(설정보다는 관례를 우선시 함):</strong> 레일스는 웹애플리케이션에서 발생할 수 있는 다양한 작업들에 대한 최선의 방법을 알고 있기 때문에, 한없이 이어지는 설정파일들을 사용해서 상세한 설정 내용을 명시하는 대신에 이런 것들에 대한 일련의 사전 정의된 설정을 기본 규칙으로 지정해 준다.</li>
</ul>
<h3 id="creating-a-new-rails-project"><a class="anchorlink" href="#creating-a-new-rails-project">3 레일스 프로젝트 생성하기</a></h3><p>본 가이드를 읽어가는 최선의 방식은 단계별로 따라서 해 보는 것이다. 모든 단계는 예제 애플리케이션을 실행하는데 필수이며 어떠한 코드나 단계도 추가적으로 필요하지 않다. </p><p>본 가이드를 따라하면 <code>blog</code>라는 간단한 웹브로그 프로젝트를 만들게 될 것이다. 프로젝트를 진행하기 전에 각자의 시스템에 레일스가 설치되어 있어야 한다.</p><div class="info"><p>아래의 예제에서 사용하는 <code>$</code> 문자는 유닉스계열의 운영체제에서 터미널 프롬프트로 사용하는 것인데 설정에 따라 다르게 보일 수 있다. 윈도우를 사용할 경우에는 <code>C:\sourc_code</code>와 같이 보일 것이다.</p></div><h4 id="installing-rails"><a class="anchorlink" href="#installing-rails">3.1 레일스 설치하기</a></h4><p>레일스를 설치하기 전에 각자의 시스템에 레일스 프레이워크에서 사용하는 연관 언어나 프로그램이 설치되어 있는지 확인해야 하는데, 여기에는 루비와 SQLite3 등이 포함된다.</p><p>우선 터미널 프로그램을 실행한 후 커맨드라인 프롬프트를 연다. macOS에서는 Terminal.app 프로그램을 실행하고, 윈도에서는 시작 메뉴로부터 "실행(Run)" 명령을 선택한 후 'cmd.exe'라고 입력하여 엔터키를 입력한다. 달러 표시 <code>$</code> 문자 뒤에 오는 명령은 커맨드라인에서 실행해야 한다. 이어서 이설치된 루비 버전을 확인한다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ ruby -v
ruby 2.5.0
</pre>
</div>
<p>레일스는 루비 버전이 최소한 2.5.0 이상이어야 한다. 따라서, 이전 버전으로 확인될 경우는 최신버전으로 설치할 필요가 있다.</p><div class="info"><p>윈도우 시스템에 레일스를 신속하게 설치하기 위해서는 <a href="http://railsinstaller.org">Rails Installer</a>를 사용할 수 있다. 대부분의 운영체제에서 더 많은 설치 방법을 찾아 보길 원한다면 <a href="https://www.ruby-lang.org/en/documentation/installation/">ruby-lang.org</a>를 살펴보기 바란다.</p></div><p>윈도우에서 작업을 할 경우는, <a href="https://rubyinstaller.org/downloads/">Ruby Installer Development Kit</a>를 추가로 설치해야 한다.</p><p>또한 SQLite3 데이터베이스를 설치할 필요가 있다.
많은 사람들이 사용하는 다수의 유닉스계열의 운영체제는 SQLite3 가용 버전이 미리 설치되어 있다.
윈도우에서는 Rails Installer를 이용하여 설치한 경우, 이미 SQLite가 설치되어 있다. 기타 다른 경우에는 <a href="https://www.sqlite.org">SQLite3 웹사이트</a>를 방문하여 설치 안내문을 참고할 수 있다.
제대로 설치되었을 경우 PATH에 경로가 추가되었는지 확인해 보기 바란다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ sqlite3 --version
</pre>
</div>
<p>위의 명령을 실행할 경우 버전이 표시되어야 한다.</p><p>레일스를 설치하기 위해서는 RubyGems에서 제공하는 <code>gem install</code> 명령을 실행한다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ gem install rails
</pre>
</div>
<p>이상의 모든 것이 제대로 설치되었다는 것을 확인하기 위해서 아래의 명령을 실행할 수 있어야 한다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails --version
</pre>
</div>
<p>"Rails 6.0.0"과 같이 표시된다면 이제 시작할 준비가 된 것이다.</p><h4 id="creating-the-blog-application"><a class="anchorlink" href="#creating-the-blog-application">3.2 Blog 애플리케이션 생성하기</a></h4><p>레일스에서 기본으로 제공되는 많은 생성자 스크립트를 이용하면 특정 작업에 필요한 모든 것을 자동으로 생성해 주기 때문에 개발을 보다 쉽게 할 수 있다. 이 중에 하나는 애플리케이션을 만들어 주는 생성자 스크립트인데 레일스 애플리케이션의 기본 골격구조를 제공해 주기 때문에 직접 코드를 작성할 필요가 없다. Creating the blog application</p><p>이 생성자를 사용하기 위해서는 터미널을 열고 파일을 생성할 권한이 있는 적당한 디렉토리로 이동한 후 아래와 같이 입력한다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails new blog
</pre>
</div>
<p>이로써 <code>blog</code> 디렉토리에 Blog라는 레일스 애플리케이션이 생성되고 <code>bundle install</code> 명령으로 <code>Gemfile</code>에 명시된 젬들이 설치될 것이다.</p><div class="note"><p>Windows Subsystem for Linux(WSL)를 사용할 경우에는 현재 파일 시스템 알림 기능상에 제약점이 발견되어 <code>rails new blog --skip-spring --skin-listen</code> 와 같이 옵션을 추가하여 명령을 실행하여 <code>spring</code>과 <code>listen</code> 젬의 기능을 중단해야 한다.</p></div><div class="info"><p><code>rails new -h</code> 명령을 실행하면 레일스 애플리케이션 빌더가 사용할 수 있는 모든 커맨드라인 옵션들을 볼 수 있다.</p></div><p>blog 애플리케이션을 생성한 후에는 해당 폴더로 이동한다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ cd blog
</pre>
</div>
<p><code>blog</code> 디렉토리에는 다수의 자동생성된 파일과 폴더가 존재하는데 레일스 애플리케이션의 구조를 반영한 것이다. 본 튜토리얼상의 대부분의 작업은 <code>app</code> 폴더에서 진행할 것이지만 레일스에서 디폴트로 생성한 파일과 폴더의 기능들에 대한 기본 설명을 아래에 기술해 놓았다.</p>
<table>
<thead>
<tr>
<th>파일/폴더</th>
<th>용도</th>
</tr>
</thead>
<tbody>
<tr>
<td>app/</td>
<td>애플리케이션의 컨트롤러, 모델, 뷰, 헬퍼, 메일러, 채널, 작업 및 애셋을 포함한다. 본 가이드의 나머지 부분에서는 이 폴더에 중점을 둘 것이다.</td>
</tr>
<tr>
<td>bin/</td>
<td>앱을 시작하는 레일스 스크립트를 포함하며 애플리케이션 설정, 업데이트, 배포 또는 실행하는 데 사용하는 스크립트를 포함 할 수 있다.</td>
</tr>
<tr>
<td>config/</td>
<td>애플리케이션의 라우트, 데이터베이스 등을 구성한다. <a href="configuring.html">Configuring Rails Applications</a>에 자세히 설명되어 있다.</td>
</tr>
<tr>
<td>config.ru</td>
<td>애플리케이션을 시작하는 데 사용되는 랙(Rack) 기반 서버의 랙 구성. 랙에 대한 자세한 내용은 <a href="https://rack.github.io/">Rack 웹 사이트</a>를 참조한다.</td>
</tr>
<tr>
<td>db/</td>
<td>현재 데이터베이스 스키마와 데이터베이스 마이그레이션이 포함되어 있다.</td>
</tr>
<tr>
<td>Gemfile<br>Gemfile.lock</td>
<td>이 파일을 사용하면 레일스 애플리케이션에 필요한 젬(gem) 의존성을 지정할 수 있다. 이 파일은 Bundler 젬에서 사용한다. Bundler에 대한 자세한 내용은 <a href="https://bundler.io">Bundler 웹 사이트</a>를 참조한다.</td>
</tr>
<tr>
<td>lib/</td>
<td>애플리케이션을 위한 확장 모듈.</td>
</tr>
<tr>
<td>log/</td>
<td>애플리케이션 로그 파일</td>
</tr>
<tr>
<td>package.json</td>
<td>이 파일을 사용하면 레일스 애플리케이션에 필요한 npm 종속성을 지정할 수 있다. 이 파일은 Yarn에서 사용한다. Yarn에 대한 자세한 내용은 <a href="https://yarnpkg.com/lang/en/">Yarn 웹 사이트</a>를 참조한다.</td>
</tr>
<tr>
<td>public/</td>
<td>누구라도 접급할 수 있는 유일한 폴더이다. 정적 파일 및 컴파일 된 애셋을 포함한다.</td>
</tr>
<tr>
<td>Rakefile</td>
<td>이 파일은 커맨드 라인에서 실행할 수 있는 태스크(task)를 찾아서 로드한다. 태스크 정의는 레일스의 구성 요소 전체에 걸쳐 정의된다. <code>Rakefile</code>을 변경하는 대신 애플리케이션의 <code>lib/tasks</code> 디렉토리에 파일을 추가한 후 자신의 태스크를 추가해야 한다.</td>
</tr>
<tr>
<td>README.md</td>
<td>애플리케이션에 대한 간단한 사용 설명서이다. 이 파일을 편집하여 다른 사용자에게 애플리케이션의 기능, 설정 방법 등을 알려 주어야 한다.</td>
</tr>
<tr>
<td>storage/</td>
<td>디스크 서비스용 액티브 스토리지 파일. 이에 대해서는 <a href="active_storage_overview.html">Active Storage Overview</a>에서 다룬다.</td>
</tr>
<tr>
<td>test/</td>
<td>유닛 테스트, 픽스쳐(fixtures, 테스트 데이터) 및 기타 테스트 장치. 이것들은 <a href="testing.html">Testing Rails Applications</a>에서 다룬다.</td>
</tr>
<tr>
<td>tmp/</td>
<td>임시 파일 (캐시(cache)와 pid 파일).</td>
</tr>
<tr>
<td>vendor/</td>
<td>모든 벤더(타사) 코드를 위한 장소이다. 전형적인 레일스 애플리케이션에서는 벤더에서 제공하는 젬을 여기에 포함한다.</td>
</tr>
<tr>
<td>.gitignore</td>
<td>이 파일은 git에게 무시해야 할 파일 (또는 패턴)을 알려준다. 파일 무시에 대한 자세한 내용은 <a href="https://help.github.com/articles/ignoring-files">GitHub - Ignoring files</a>를 참조한다.</td>
</tr>
<tr>
<td>.ruby-version</td>
<td>이 파일에는 기본 루비 버전이 포함되어 있다.</td>
</tr>
</tbody>
</table>
<h3 id="hello-rails-bang"><a class="anchorlink" href="#hello-rails-bang">4 Hello, Rails!</a></h3><p>먼저, 스크린 상에 어떤 문자들이 보이도록 해 보자. 이를 위해서 레일스 애플리케이션 서버를 실행시켜야 한다.</p><h4 id="starting-up-the-web-server"><a class="anchorlink" href="#starting-up-the-web-server">4.1 웹서버 시작하기</a></h4><p>레일스 애플리케이션은 사실 이미 정상적으로 동작이 가능한 상태이다. 이를 확인하려면 각자의 개발 머신에서 웹서버를 시작할 필요가 있다. <code>blog</code> 디렉토리에서 아래의 명령을 실행하여 서버를 시작할 수 있다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails server
</pre>
</div>
<div class="info"><p>윈도우를 사용할 경우에는 <code>bin</code> 폴더에 있는 스크립트를, 예: <code>ruby bin\rails server</code>, 루비 인터프리터로 직접 넘겨 주어야 한다.</p></div><div class="info"><p>자바스크립트 애셋을 압축하기 위해서는 시스템에 자바스크립트 런타임이 설치되어 있어야 하는데, 만약 그렇지 못할 경우에는 <code>execjs</code> 에러가 발생할 것이다. 대부분의 경우 macOS와 윈도우에는 자바스크립트 런타임이 이미 설치되어 있다. <code>therubyrhino</code>는 JRuby 사용자들을 위한 권장되는 런타임이며 JRuby 하에 생성된 앱의 <code>Gemfile</code>에 기본으로 추가된다. <a href="https://github.com/rails/execjs#readme">ExecJS</a>에서 모든 사용가능한 런타임을 찾아 볼 수 있다.</p></div><p>이로써 레일스에서 디폴트로 배포하는 웹서버인 Puma를 시동하게 될 것이다. 작업 중인 애플리케이션이 동작하는 것을 확인하기 위해서 브라우저를 연 후 <a href="http://localhost:3000">http://localhost:3000</a>로 이동했을 때 아래와 같은 레일스 디폴트 정보 페이지를 볼 수 있어야 한다.</p><p><img src="images/getting_started/rails_welcome.png" alt="Welcome aboard screenshot"></p><div class="info"><p>웹서버를 중단하기 위해서는 서버가 실행 중인 터미널 윈도우에서 Ctrl+C를 누른다. 서버가 중단된 것을 확인하기 위해서는 커맨드 프롬프트를 다시 볼 수 있어야 한다. macOS를 포함해서 대부분의 유닉스계열의 시스템에서는 커맨드 프롬프트가 달러 문자 <code>$</code>로 표시될 것이다. 개발 모드에서는 변경된 내용이 서버에 자동으로 반영되기 때문에 일반적으로 서버를 재시동할 필요가 없다.</p></div><p>"Welcome aboard" 페이지는 레일스 애플리케이션이 제대로 생성되었는지를 알 수 있는 일종의 <em>smoke test</em> 의 의미를 가진다. 즉, 소프트웨어가 제대로 설정되어 페이지를 서비스할 수 있음을 확인하는 것이다.</p><h4 id="say-hello-rails"><a class="anchorlink" href="#say-hello-rails">4.2 "Hello, Rails" 표시하기</a></h4><p>"Hello" 문자를 표시하기 위해서는 최소한 하나의 <em>controller(컨트롤러)</em> 와 하나의 <em>view(뷰)</em> 를 생성해야 한다.</p><p>컨트롤러는 애플리케이션에 대한 특정 요청를 받는 역할을 한다.
<em>Routing(라우팅)</em> 은 어떤 컨트롤러가 어떤 요청을 받을 것인가를 결정한다. 종종 하나의 컨트롤러가 하나 이상의 라우트로 연결되기도 하는데 이 때 특정 컨트롤러의 라우트들은 각기 다른 <em>actions(액션)</em> 을 호출하여 서비스한다. 액션은 정보를 모아서 뷰에 제공하는 역할을 수행한다.</p><p>뷰는 이러한 정보를 사람이 읽을 수 있는 형태로 표시하는데 중요한 차이점은 정보를 수집하는 곳이 뷰가 아니고 <em>컨트롤러</em> 라는 점이다. 뷰는 바로 이러한 정보를 단지 표시만해야 한다. 보통 뷰 템플릿은 eRuby(Embeded Ruby)로 작성하는데 사용자들에게 보내지기 전에 레일스 엔진이 요청 주기에 따라 처리하게 된다.</p><p>컨트롤러를 새로이 추가할 때는 "controlller" 생성자를 실행해야 하는데 이 때 "Welcome"이라는 컨트롤러와 "index"라는 액션을 아래와 같이 알려 주어야 한다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails generate controller Welcome index
</pre>
</div>
<p>이로써 다수의 파일과 하나의 라우트가 생성될 것이다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
create app/controllers/welcome_controller.rb
route get 'welcome/index'
invoke erb
create app/views/welcome
create app/views/welcome/index.html.erb
invoke test_unit
create test/controllers/welcome_controller_test.rb
invoke helper
create app/helpers/welcome_helper.rb
invoke test_unit
invoke assets
invoke scss
create app/assets/stylesheets/welcome.scss
</pre>
</div>
<p>이 중에서 가장 중요한 것은 <code>app/controllers/welcome_controller.rb</code>에 위치한 컨트롤러 파일과 <code>app/views/welcome/index.html.erb</code>에 위치한 뷰 파일이다.</p><p>텍스트 에디터 상에서 <code>app/views/welcome/index.html.erb</code> 파일을 열고 기존 코드를 모두 삭제한 후 아래의 코드로 대체한다.</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<h1>Hello, Rails!</h1>
</pre>
</div>
<h4 id="setting-the-application-home-page"><a class="anchorlink" href="#setting-the-application-home-page">4.3 애플리케이션 홈 페이지 설정하기</a></h4><p>컨트롤러와 뷰를 작성했기 때문에 이제 레일스에게 "Hello, Rails!"라는 글을 보여줄 시점을 알려 주어야 한다. 여기서는 루트 URL <a href="http://localhost:3000">http://localhost:3000</a>로 이동할 때 보여 주고자 한다. 이 순간 바로 "Welcom aboard" 라는 글을 보게 될 것이다.</p><p>다음으로는 실제 홈 페이지의 위치를 지정해 주어야 한다.</p><p>에디터 상에서 <code>config/routes.rb</code> 파일을 열면 아래와 같은 내용이 보인다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
Rails.application.routes.draw do
get 'welcome/index'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
</pre>
</div>
<p>이것은 특별한 <a href="https://en.wikipedia.org/wiki/Domain-specific_language">DSL (domain-specific language)</a>로 작성된 라우트 항목들을 포함하는 애플리케이션의 <em>routing file(라우팅 파일)</em> 이며 이 파일을 통해서 레일스는 서버로 들어오는 요청을 어떤 컨트롤러와 액션으로 연결할지 알게 된다. 이 파일에 <code>root 'welcome#index'</code> 코드라인을 추가하면 아래와 같이 보이게 된다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
Rails.application.routes.draw do
get 'welcome/index'
root 'welcome#index'
end
</pre>
</div>
<p><code>root 'welcome#index'</code>는 레일스에게 애플리케이션 루트로 들어오는 요청을 welcome 컨트롤러의 index 액션으로 매핑하도록 알려 주며 <code>get 'welcome/index'</code>는 <a href="http://localhost:3000/welcome/index">http://localhost:3000/welcome/index</a>로 들어오는 요청을 welcome 컨트롤러의 index 액션으로 매핑하도록 알려 준다. 이것은 이전에 컨트롤러 생성자(<code>rails generate controller Welcome index</code>)를 실행했을 때 이미 만들어졌던 것이다.</p><p>컨트롤러를 만들기 위해서 중단한 경우에는 웹서버를 시작한 후 브라우저 상에서 <a href="http://localhost:3000">http://localhost:3000</a>로 이동한다. 이 때 <code>app/views/welcome/index.html.erb</code> 파일에 추가했던 "Hello, Rails!" 메시지를 보게 된다면 새로 추가한 라우트가 <code>WelcomeController</code>의 <code>index</code> 액션으로 제대로 이동하여 뷰를 정확하게 렌더링하고 있다는 것을 간접적으로 시사하는 것이다.</p><div class="info"><p>라우팅에 대한 더 자세한 내용은 <a href="routing.html">Rails Routing from the Outside In</a>를 참고하기 바란다.</p></div><h3 id="getting-up-and-running"><a class="anchorlink" href="#getting-up-and-running">5 작동하기</a></h3><p>컨트롤러, 액션, 뷰 작성법을 알게 되었으니 이제 좀 더 실질적인 것을 만들어 보도록 하자.</p><p>Blog 애플리케이션에서 새로운 <em>resource(리소스)</em> 를 하나 추가할 것이다. 리소스란 기사(읽은거리), 사람, 동물 등과 같이 비슷한 객체들을 일컫는 말한다. 특정 리소스에 대한 항목을 생성하고, 읽고, 업데이트하고, 삭제할 수 있고 이러한 작업을 <em>CRUD</em> 작업으로 말하기도 한다.</p><p>레일스에서 제공하는 <code>resources</code> 메소드는 표준 REST 리소스를 선언하는데 사용할 수 있다. 따라서 <code>config/routes.rb</code> 파일에 <em>article resource</em> 를 추가할 필요가 있으며 그 파일 내용은 아래와 같다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
Rails.application.routes.draw do
get 'welcome/index'
resources :articles
root 'welcome#index'
end
</pre>
</div>
<p><code>rails routes</code> 명령을 실행하면 모든 표준 RESTful 액션에 대한 라우트 정의를 볼 수 있을 것이다. prefix 열과 다른 열의 의미는 나중에 보게 될 것이지만 지금 당장은 레일스가 단수형 <code>article</code>을 추론한 후 각 라우트를 구분하기 위해 의미있게 사용하는 것을 주목한다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails routes
Prefix Verb URI Pattern Controller#Action
welcome_index GET /welcome/index(.:format) welcome#index
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
root GET / welcome#index
</pre>
</div>
<p>다음 섹션에서 새 기사를 생성한 후 결과를 볼 수 있도록 기능을 추가할 것이다. 이것은 CRUD에서 "C"와 "R"에 해당하는 것으로 create(생성하기)와 read(읽기)를 의미한다. 이러한 작업을 하는 폼 형태는 아래와 같이 보일 것이다.</p><p><img src="images/getting_started/new_article.png" alt="The new article form"></p><p>현재 상태는 기본 형태로 보이지만 작동하는데 문제가 없다. 이후에 스타일을 좋게 만드는 작업을 보게 될 것이다.</p><h4 id="laying-down-the-groundwork"><a class="anchorlink" href="#laying-down-the-groundwork">5.1 기본틀 잡기</a></h4><p>먼저, 새로운 기사를 작성할 장소가 필요하다. 이를 위한 적당한 위치는 <code>/articles/new</code>가 될 것이다. 이미 정의된 바 있는 라우트를 따라 외부로부터의 요청은 <code>/articles/new</code>로 이어질 것이다. <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a>로 이동하면 라우팅 에러가 발생할 것이다. </p><p><img src="images/getting_started/routing_error_no_controller.png" alt="Another routing error, uninitialized constant ArticlesController"></p><p>이 에러는 해당 라우트의 요청을 처리하기 위해서는 하나의 컨트롤러가 정의되어 있어야 하기 때문에 발생한다.
이러한 문제를 해결하는 방법은 간단하다. <code>ArticlesController</code>라는 컨트롤러를 생성하는 것이다. 아래와 같은 명령을 실행하면 이러한 작업을 수행할 수 있다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails generate controller Articles
</pre>
</div>
<p>방금 생성된 <code>app/controllers/articles_controller.rb</code> 파일을 열면 빈 컨트롤러를 보게 될 것이다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class ArticlesController < ApplicationController
end
</pre>
</div>
<p>이 컨트롤러는 <code>ApplicationController</code>로부터 상속받는 단지 하나의 클래스에 불과하다.
이 클래스 내에 컨트롤러 액션으로 동작하는 메소드를 정의하게 된다. 이러한 액션들은 기사들에 대한 CRUD 작업을 수행하게 된다.</p><div class="note"><p>루비에는 <code>public</code>, <code>private</code>, <code>protected</code> 메소드가 있지만 <code>public</code> 메소드만이 컨트롤러 액션으로 작업을 수행하게 된다. 더 자세한 내용은 <a href="http://www.ruby-doc.org/docs/ProgrammingRuby/">Programming Ruby</a>을 확인해 보기 바란다.</p></div><p>이제 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a>를 다시 보기하면 새로운 에러를 보게 될 것이다.</p><p><img src="images/getting_started/unknown_action_new_for_articles.png" alt="Unknown action new for ArticlesController!"></p><p>이러한 에러는 방금 전에 생성한 <code>ArticlesController</code> 내에 <code>new</code> 액션이 정의되어 있지 않다는 것을 알려 준다. 이것은 컨트롤러의 생성과정에서 원하는 액션을 명시적으로 추가하지 않는 한 레일스가 컨트롤러를 비어 있는 상태로 생성하기 때문이다.</p><p>컨트롤러 내에 액션을 수작업으로 정의하기 위해서는 단지 해당 컨트롤러 내에 새로운 메소드를 정의하면 된다. <code>app/controllers/articles_controller.rb</code> 파일을 열고 <code>ArticlesController</code> 클래스 내에 <code>new</code> 메소드를 정의하면 아래와 같이 보이게 된다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class ArticlesController < ApplicationController
def new
end
end
</pre>
</div>
<p>이 상태에서 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a>를 새로 보기하면 또 다른 에러를 보게 될 것이다.</p><p><img src="images/getting_started/template_is_missing_articles_new.png" alt="Template is missing for articles/new"></p><p>레일스는 이와 같은 단순한 액션이 자신의 정보를 표시하기 위해 이와 연과되는 뷰를 가지는 것으로 기대하기 때문에 이런 에러가 발생하는 것이다. 뷰가 없다면 레일스는 예외를 발생할 것이다.</p><p>다시 전체 에러 메시지를 살펴 보자.</p>
<blockquote>
<p>ArticlesController#new 액션은 다음의 요청 포맷으로 작성된 템플릿 파일이 누락되어 있다: text/html</p>
<p>NOTE!
별도로 이름을 명시하지 않는 한, 레일스는 컨트롤러 이름과 동일한 폴더 내에 액션 이름과 동일한 템플릿 파일을 만들어 줄 것으로 기대한다. 이 컨트롤러가 204(컨텐츠 없음) 응답 상태를 보내는 API일 경우에는 이러한 템플릿 파일이 필요없지만, 브라우저 상에서 이 컨트롤러에 접근할 때는 HTML 템플릿이 요구되기 때문에 이런 에러 메시지를 보이게 된다. 그런 경우라면 계속해서 작업을 진행하면 된다.</p>
</blockquote>
<p>이 메시지는 어떤 템플릿 파일이 누락되었는지 알려 준다. 이 경우는 <code>articles/new</code> 템플릿 파일이 해당된다. 레일스는 먼저 이 템플릿 파일을 찾게 되고 해당 위치에 없을 경우, <code>ArticlesController</code>가 <code>AppllicationController</code>로부터 상속을 받기 때문에 <code>application/new</code> 템플릿 파일을 로드하려고 시도할 것이다.</p><p>다음으로 메시지 내용 중에는 <code>request.formats</code>가 포함되어 있는데, 이것은 응답으로 내보낼 템플릿 파일의 포맷을 명시하는 것이다. 브라우저를 통해서 이 페이지를 요청했기 때문에 <code>text/html</code>로 설정되어 있다. 따라서 레일스는 HTML 포맷의 템플릿 파일을 찾게 된다.</p><p>이 경우에 동작하게 될 가장 단순한 템플릿 파일은 <code>app/views/articles/new.html.erb</code>에 위치하게 될 것이다. 이 파일명의 확장자명이 중요한데, 첫번째 확장자(.html)는 템플릿의 <em>포맷</em> 이고 두번째 확장자는 템플릿을 최종적으로 작성할 때 사용하는 <em>핸들러</em> 를 의미한다. 레일스는 애플리케이션의 <code>app/views</code> 폴더 내 <code>articles/new</code> 템플릿 파일을 찾게 된다. 이 템플릿의 포맷은 <code>html</code>이고 HTML 포맷에 대한 기본 핸들러는 <code>erb</code>이라고 해석하게 된다. 다른 포맷에 대해서 다른 핸들러를 사용하게 된다. <code>builder</code> 핸들러는 XML 템플릿을 빌드하고 <code>coffee</code> 핸들러는 자바스크립트 템플릿을 빌드하는데 사용할 수 있다. HTML 폼을 새로 만들기 원하기 때문에 HTML 문서에 루비 언어를 임베드하기 위해 만들어진 <code>ERB</code> 언어를 사용할 것이다.</p><p>따라서, 파일명은 <code>articles/new.html.erb</code> 이어야 하고 애플리케이션의 <code>app/views</code> 디렉토리 내에 위치해야 한다.</p><p>이제 <code>app/views/articles/new.html.erb</code> 위치에 새로운 파일을 생성하고 아래와 같이 작성한다.</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<h1>New Article</h1>
</pre>
</div>
<p><a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a>를 새로 보기하면 하나의 타이틀을 포함하는 페이지를 보게 될 것이다. 라우트, 컨트롤러, 액션, 뷰가 조화롭게 잘 동작하고 있는 것이다. 새로운 기사를 작성할 폼을 생성할 시점이 되었다.</p><h4 id="the-first-form"><a class="anchorlink" href="#the-first-form">5.2 첫번째 폼</a></h4><p>이 템플릿 파일에 폼을 생성하기 위해 <em>폼 빌더</em> 를 사용할 것이다. 레일스에서 사용하는 기본 폼 빌더는 <code>form_with</code> 헬퍼메소드가 제공해 준다. 이 메소드를 사용하기 위해서는 아래의 코드를 <code>app/views/articles/new.html.erb</code> 파일에 추가해 준다.</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= form_with scope: :article, local: true do |form| %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
</pre>
</div>
<p>이제 이 페이지를 다시 보기하면 위의 예제에서 보았던 것과 동일한 폼을 보게 될 것이다.
레일스에서 폼을 빌드하는 것은 정말 이렇게도 쉽다!</p><p><code>form_with</code> 메소드를 호출할 때 이 폼에 대한 식별용 스코프를 지정한다. 이 경우에는 심볼 형태인 <code>:article</code>로 지정한다. 이것은 <code>form_with</code> 헬퍼에게 이 폼의 용도를 알려 주는 것이다. 이 메소드의 블록 내에서는 <code>form</code> 블록변수로 표시되는 <code>FormBuilder</code> 객체를 이용하여 기사 title과 text 용으로 두 개의 라벨과 두 개의 텍스트 필드를 빌드한다. 최종적으로 <code>form</code> 객체에 대해서 <code>submit</code> 메소드를 호출하여 폼에서 사용하게 되는 서밋 버튼을 생성하게 된다.</p><p>그러나 이 폼에서는 한가지 문제가 있다. 페이지의 소스보기에서 헬프메소드로 생성된 HTML을 조사해 보면 form 태그의 <code>action</code> 속성이 <code>articles/new</code>로 지정된 것을 알 수 있다. 이 라우트가 현재 폼이 위치하는 바로 그 페이지를 가리키기 때문이다. 이 라우트는 새로운 기사를 입력하기 위한 폼을 표시하기 위해서만 사용되어야 한다.</p><p>이 폼이 또 다른 곳으로 이동하기 위해서는 다른 URL을 사용해야 한다. 이 작업은 <code>form_with</code> 메소드에 <code>:url</code> 옵션을 지정함으로써 바로 해결할 수 있다. 레일스에서 보통, 이와 같이 새로운 폼 데이터를 서밋할 때 사용하는 액션을 "create"라고 부르는데 폼은 바로 이 액션으로 서밋되도록 해야 한다.</p><p>app/views/articles/new.html.erb<code>파일에 있는</code>form_with`를 아래와 같이 보이도록 수정한다.</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
</pre>
</div>
<p>이 예문에서는 <code>:url</code> 옵션으로 <code>articles_path</code> 헬퍼를 넘겨준다. 이러한 작업으로 초래되는 결과를 보기 위해서 <code>rails routes</code>의 결과를 다시 보도록 한다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails routes
Prefix Verb URI Pattern Controller#Action
welcome_index GET /welcome/index(.:format) welcome#index
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
root GET / welcome#index
</pre>
</div>
<p><code>articles_path</code> 헬퍼는 폼이 <code>articles</code> 접두어(prefix)와 연관되는 URI 패턴을 가리키도록 하는데 이 때 폼은 기본상태에서 해당 라우트로 <code>POST</code> 요청을 보내게 된다. 이 라우트는 <code>ArticlesController</code> 컨트롤러의 <code>create</code> 액션과 연결된다.</p><p>폼과 라우트가 지정된 상태에서 폼에 데이터를 입력한 후 서밋 버튼을 클릭하면 새 기사를 생성하는 과정을 시작하게 된다. 언급한 바와 같이 폼을 서밋하면 익숙한 에러 메시지를 보게 된다.</p><p><img src="images/getting_started/unknown_action_create_for_articles.png" alt="Unknown action create for ArticlesController"></p><p>이제 이것이 제대로 동작하도록 하려면 <code>ArticlesController</code> 내에 <code>create</code> 액션을 작성해야 한다.</p><div class="note"><p>보통은 <code>form_with</code> 헬퍼는 Ajax로 폼을 서밋하게 되므로 전체 페이지 리디렉션이 발생하지 않는다. 현재는 이 가이드를 보다 쉽게 이해할 수 있도록 <code>local: true</code>로 옵션을 지정하여 이 기능을 사용하지 않도록 했다.</p></div><h4 id="creating-articles"><a class="anchorlink" href="#creating-articles">5.3 기사 작성하기</a></h4><p>"Unknow action" 에러 메시지가 사라지게 하려면, 아래와 같이 <code>app/controllers/articles_controller.rb</code> 파일 내의 <code>ArticlesController</code> 클래스에서, <code>new</code> 액션 바로 아래에, <code>create</code> 액션을 정의한다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class ArticlesController < ApplicationController
def new
end
def create
end
end
</pre>
</div>
<p>이제 다시 폼 서밋하면 페이지상에 아무런 변화를 볼 수 없을 것이다. 잘 못 된 것은 아니므로 걱정할 필요 없다. 응답으로 보낼 내용이 없는 경우 레일스는 보통 <code>204 No Content(내용 없음)</code> 응답을 보내기 때문이다. 단지 <code>create</code> 액션만 추가한 후 응답으로 보낼 내용을 작성하지 않았다. 이 경우에는, <code>create</code> 액션은 데이터베이스로 새로 작성한 기사를 저장하도록 해야 한다.</p><p>폼이 서밋될 때, 폼 필드는 <em>매개변수(parameters)</em> 로써 레일스로 보내진다. 이 매개변수들은 어떤 특정 일을 수행하기 위해 컨트롤러 액션에서 참조할 수 있다. 이 매개변수들의 상태를 보기 위해 아래와 같이 <code>create</code> 액션을 수정한다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def create
render plain: params[:article].inspect
end
</pre>
</div>
<p>여기서 <code>render</code> 메소드는 <code>:plain</code> 키와 <code>params[:article].inspect</code> 값을 가지는 매우 단순한 해시를 인수로 받는다. <code>params</code> 메소드는 폼에서부터 넘어오는 매개변수(또는 폼 필드)를 나타내는 객체이다. <code>params</code> 메소드는 <code>ActionController::Parameters</code> 객체를 반환하는데, 이 객체로서 문자열이나 심볼을 이용하여 해시 키에 접근할 수 있도록 한다. 이 경우에 중요한 매개변수들은 폼으로부터 넘어 오는 것들 뿐이다.</p><div class="info"><p><code>params</code> 메소드를 꽤나 일상적으로 사용할 것이기 때문에 확실하게 파악해 둘 필요가 있다. <strong><a href="http://www.example.com/?username=dhh&[email protected]">http://www.example.com/?username=dhh&[email protected]</a></strong> 와 같은 URL을 예로 들어 보자. 이 URL에서 <code>params[:username]</code>은 "dhh"가 될 것이고 <code>params[:email]</code>은 "<a href="mailto:[email protected]">[email protected]</a>"가 될 것이다.</p></div><p>한번 더 폼을 다시 서밋하면 아래와 같은 것을 보게 될 것이다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
<ActionController::Parameters {"title"=>"First Article!", "text"=>"This is my first article."} permitted: false>
</pre>
</div>
<p>이 액션은 폼으로부터 넘어 오는 해당 기사에 대한 매개변수를 보여 준다. 그러나 이것은 실제로 그렇게 유용하지 못하다. 그렇다. 매개변수들을 볼 수 있지만 그것들을 이용하여 특별히 작업한 것이 전혀 없다.</p><h4 id="creating-the-article-model"><a class="anchorlink" href="#creating-the-article-model">5.4 Article 모델 생성하기</a></h4><p>레일스에서는 모델 이름을 단수형으로 사용하고 해당 데이터베이스 테이블명으로는 복수형을 사용한다. 레일스는 모델을 생성하는 생성자 스크립트를 제공하는데 대부분의 레일스 개발자들은 새로운 모델을 작성할 때 이것을 사용하려고 한다. 새로운 모델을 작성할 때는 터미널에서 아래의 명령을 실행한다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails generate model Article title:string text:text
</pre>
</div>
<p>이 명령으로써 문자형의 <em>title</em> 속성과 텍스트 속성의 <em>text</em> 속성을 가지는 <code>Article</code> 모델을 원한다고 레일스에게 알려주게 된다. 이 속성들은 자동으로 데이터베이스 <code>articles</code> 테이블에 추가되어 <code>Article</code> 모델로 매핑된다.</p><p>레일스 다수의 파일들을 생성하므로써 응답을 보이게 된다. 현재로서는 <code>app/models/article.rb</code> 와 <code>db/migrate/20140120191729_create_articles.rb</code> 파일(각자 파일명에 차이가 있을 수 있음)에만 집중하도록 한다. 후자는 데이터베이스 구조를 정의하는 것을 담당하는데 이것은 다음에 살펴 보도록 한다.</p><div class="info"><p>액티브 레코드는 매우 스마트해서 자동으로 컬럼명을 모델 속성으로 매핑해 주는데, 이것은 액티브 레코드가 자동으로 해 주기 때문에 레일스 모델 내에서 속성들을 따로 선언해 줄 필요가 없다는 것을 뜻한다.</p></div><h4 id="running-a-migration"><a class="anchorlink" href="#running-a-migration">5.5 마이그레이션 작업 수행하기</a></h4><p>방금 보았듯이 <code>rails generate model</code> 명령으로 <code>db/migrate</code> 디렉토리 내에 <em>데이터베이스 마이그레이션</em> 파일이 생성되었다. 마이그레이션을 데이터베이스 테이블을 생성하고 변경하는 작업을 쉽게 해 주기 위해 작성된 루비 클래스이다. 레일스는 rake 명령을 사용하여 마이그레이션 작업을 수행하며 데이터베이스에 적용이 완료된 후에도 마이그레이션을 취소할 수도 있다. 마이그레이션 파일명은 타임스탬프를 포함하는데 생성된 순서대로 마이그레이션으로 처리하기 위한 것이다.</p><p><code>db/migrate/YYYYMMDDHHMMSS_create_articles.rb</code> 파일(각자의 파일명이 다를 수 있음)의 내용은 아래와 같을 것이다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class CreateArticles < ActiveRecord::Migration[6.0]
def change
create_table :articles do |t|
t.string :title
t.text :text
t.timestamps
end
end
end
</pre>
</div>
<p>위의 마이그레이션에서 작성되는 <code>change</code> 메소드는 마이그레이션 작업이 수행될 때 호출된다. 이 메소드에서 정의된 작업 또한 가역적이며 이것은 나중에 되돌리기를 원할 경우 레일스가 마이그레이션으로 변경된 내용을 되돌릴 수 있는 방법을 알고 있다는 것을 의미한다. 이 마이그레이션을 수행하면 하나의 문자열 컬럼과 텍스트 컬럼을 가지는 <code>articles</code> 테이블을 생성하게 될 것이다. 또한 두 개의 타임스탬프 필드도 생성하는데 레일스가 기사를 생성하고 업데이트하는 시간을 추적하는데 사용된다.</p><div class="info"><p>마이그레이션에 대한 더 많은 정보를 원할 경우 <a href="active_record_migrations.html">Active Record Migrations</a>를 참고한다.</p></div><p>이 지점에서 아래와 같이 레일스 명령을 사용하여 마이그레이션 작업을 수행할 수 있다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails db:migrate
</pre>
</div>
<p>레일스는 이 마이그레이션 명령을 실행한 후 Articles 테이블이 생성되었음을 알려 줄 것이다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
== CreateArticles: migrating ==================================================
-- create_table(:articles)
-> 0.0019s
== CreateArticles: migrated (0.0020s) =========================================
</pre>
</div>
<div class="note"><p>보통 개발 환경에서 작업을 할 것이기 때문에 <code>config/database.yml</code> 파일의 <code>development</code> 섹션에 정의된 데이터베이스에 마이그레이션 작업이 적용될 것이다. 다른 환경에서 마이그레이션을 실행하고자 할 경우, 예를 들어 운영 환경에서, <code>rails db:migrate RAILS_ENV=production</code>와 같이 명령을 호출할 때 명시적으로 지정해 주어야 한다.</p></div><h4 id="saving-data-in-the-controller"><a class="anchorlink" href="#saving-data-in-the-controller">5.6 컨트롤러에서 데이터 저장하기</a></h4><p>새로 생성한 <code>Article</code> 모델을 이용하여 데이터베이스로 데이터를 저장하기 위해서는 <code>ArticlesController</code>로 돌아가서 <code>create</code> 액션을 변경할 필요가 있다. <code>app/controllers/articles_controller.rb</code> 파일을 열고 아래와 같이 <code>create</code> 액션을 변경한다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def create
@article = Article.new(params[:article])
@article.save
redirect_to @article
end
</pre>
</div>
<p>위에서 일어난 상황을 설명하면 다음과 같다. 모든 레일스 모델은 각각의 데이터베이스 테이블 컬럼으로 자동으로 매핑되는 각각의 속성으로 초기화될 수 있다. 첫번째 코드라인에서 이런 작업을 하게 된다(<code>params[:article]</code>에는 관심있는 속성들이 포함되어 있다는 것을 기억한다). 다음으로 <code>@article.save</code>은 모델을 데이터베이스에 저장한다. 마지막으로 사용자를 <code>show</code> 액션으로 리디렉션하는데 이것은 나중에 정의할 것이다.</p><div class="info"><p>이 가이드에서 기사들에 대한 대부분의 다른 참조는 소문자를 사용했던 반면, <code>Article.new</code>에서는 <code>A</code>를 대문자로 표시한 점에 대해서 궁금할 수 있다. 여기서는 <code>app/models/article.rb</code>에 정의 된 <code>Article</code> 클래스를 참조한다. 루비의 클래스 이름은 대문자로 시작해야 한다.</p></div><div class="info"><p>나중에 알게 되겠지만 <code>@article.save</code>는 기사가 저장되었는지 여부를 부울 값(true/false)으로 반환한다.</p></div><p><a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a>로 이동하면 기사를 <em>거의</em> 생성할 수 있게 될 것이다. 이와 같이 시도할 경우 아래와 같은 에러 메시지를 보게 될 것이다.</p><p><img src="images/getting_started/forbidden_attributes_for_new_article.png" alt="Forbidden attributes for new article"></p><p>레일스에는 보안상 안전한 프로그램을 작성하는데 도움이되는 몇 가지 보안 기능이 있으며 이제 그 중 하나를 사용할 것이다. 이것은 <a href="action_controller_overview.html#strong-parameters">strong parameters</a>라고 하는 것인데 컨트롤러 동작에 어떤 파라미터가 허용되는지 레일스에게 정확히 알려 준다.</p><p>왜 이렇게 귀찮은 과정을 거쳐야 할까? 모든 컨트롤러 파라미터를 한꺼번에 취합해서 모델로 자동 할당할 수 있다면 프로그래밍 작업을 더 쉽게 할 수 있겠지만 이 간편함은 역시 악의적인 용도로 사용될 수 있다. 서버에 대한 요청이 새로운 기사를 폼 서밋하는 것처럼 보이도록 조작되고 애플리케이션의 무결성을 위반하는 값을 가진 별도의 필드가 포함되었다면 어떻게 될까? 그것들은 기사 속성들과 함께 모델에서 데이터베이스로 '대량 할당' 되어 애플리케이션을 손상 시키거나 악화시킬 수 있을 것이다.</p><p>잘못된 대량 할당을 방지하기 위해서 허용되는 컨트롤러 매개변수를 정의해야 한다. 여기서는 <code>create</code>를 유효하게 사용하기 위해 <code>title</code> 및 <code>text</code> 매개 변수를 허용하고 필수항목으로 지정한다. 이를 위한 문법으로 <code>require</code>와 <code>permit</code>을 도입한다. <code>create</code> 액션에 아래와 같이 한 줄을 포함할 것이다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
@article = Article.new(params.require(:article).permit(:title, :text))
</pre>
</div>
<p>이것은 종종 별도의 메소드로 분리되어 동일한 컨트롤러 내의 여러 액션(예 :<code>create</code> 및<code>update</code>)에서 재사용 되기도 한다. 이 메소드는 대량 할당 문제를 해결할 뿐만 아니라 종종 의도된 경우 외에는 호출될 수 없도록 <code>private</code>로 선언한다. 아래에 그 결과를 보여 준다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def create
@article = Article.new(article_params)
@article.save
redirect_to @article
end
private
def article_params
params.require(:article).permit(:title, :text)
end
</pre>
</div>
<div class="info"><p>자세한 내용은 위의 레퍼런스 및 <a href="ttps://weblog.rubyonrails.org/2012/3/21/strong-parameters/">Strong Paramters에 대한 이 블로그의 관련 기사</a>를 참고한다.</p></div><h4 id="showing-articles"><a class="anchorlink" href="#showing-articles">5.7 기사 보여주기</a></h4><p>지금 폼을 다시 서밋하면 레일스는 <code>show</code> 액션를 찾지 못한다고 불평할 것이다. 그다지 유용하지는 않더라도 계속하기 전에 <code>show</code> 액션을 추가하도록 한다.</p><p><code>rails routes</code>의 결과에서 보았 듯이 <code>show</code> 액션의 라우트는 다음과 같다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
article GET /articles/:id(.:format) articles#show
</pre>
</div>
<p>특수한 문법인 <code>:id</code> 는 이 라우트가 <code>:id</code> 매개 변수를 필요로 한다는 것을 레일스에 알려 주는데 이 경우에는 기사의 ID가 된다.</p><p>이전과 마찬가지로 <code>app/controllers/articles_controller.rb</code>의 <code>show</code> 액션과 해당 뷰를 추가해야 한다.</p><div class="note"><p>자주 사용하는 방법은 표준 CRUD 작업을 각 컨트롤러에 <code>index</code>, <code>show</code>, <code>new</code>, <code>edit</code>, <code>create</code>, <code>update</code> 및 <code>destroy</code> 순서로 배치하는 것이다. 각자가 임의로 순서를 변경할 수 있지만 이 메소드들은 public 메소드라는 점을 기억해 둔다. 이 가이드의 앞부분에서 언급했듯이 컨트롤러 상에 <code>private</code>를 선언하기 전에 배치해야 한다.</p></div><p>이를 감안하여 아래와 같이<code>show</code> 액션을 추가하자.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class ArticlesController < ApplicationController
def show
@article = Article.find(params[:id])
end
def new
end
# snippet for brevity
</pre>
</div>
<p>몇 가지 유의할 사항. <code>Article.find</code>를 사용하여 요청에서 <code>:id</code> 매개 변수를 얻기 위해 <code>params[:id]</code>를 전달하여 관심있는 기사를 찾는다. 또한 기사 객체에 대한 참조를 유지하기 위해 인스턴스 변수(<code>@</code> 접두사)를 사용한다. 레일스가 모든 인스턴스 변수를 뷰에 전달하기 때문에 이 작업을 수행한다.</p><p>이제 다음 내용으로 새 파일 <code>app/views/articles/show.html.erb</code>를 작성한다.</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
</pre>
</div>
<p>이와 같이 변경한 후 비로서 기사를 새로 작성할 수 있게 되는 것이다.
이제 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a>를 방문하여 직접 사용해 보기 바란다!</p><p><img src="images/getting_started/show_action_for_articles.png" alt="Show action for articles"></p><h4 id="listing-all-articles"><a class="anchorlink" href="#listing-all-articles">5.8 모든 기사 목록 보기</a></h4><p>모든 기사를 나열할 방법도 필요하므로 함께 진행하도록 하자.
<code>rails routes</code>의 결과에 따른 라우트는 아래와 같다.</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
articles GET /articles(.:format) articles#index
</pre>
</div>
<p><code>app/controllers/articles_controller.rb</code> 파일의 <code>ArticlesController</code> 내에 해당 라우트로 연결되는 <code>index</code> 액션을 추가한다. <code>index</code> 액션을 작성할 때는 습관적으로 컨트롤러 내에 첫 번째 메소드로 배치한다. 아래와 같이 작성한다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def new
end
# snippet for brevity
</pre>
</div>
<p>마지막으로 이 액션에 대한 뷰를 추가한다. 이 뷰 파일은 <code>app/views/articles/index.html.erb</code>에 위치한다.</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<h1>Listing articles</h1>
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th></th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
</tr>
<% end %>
</table>
</pre>
</div>
<p>이제 <a href="http://localhost:3000/articles">http://localhost:3000/articles</a>로 이동하면 지금까지 작성한 모든 기사 목록이 표시될 것이다.</p><h4 id="adding-links"><a class="anchorlink" href="#adding-links">5.9 링크 추가하기</a></h4><p>이제 기사를 작성하고 보여주고 목록을 나열할 수 있게 되었다. 다음으로 페이지간의 이동을 위한 몇가지 링크를 추가해 보도록 하자.</p><p><code>app/views/welcome/index.html.erb</code> 파일을 열고 아래와 같이 변경한다.</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<h1>Hello, Rails!</h1>
<%= link_to 'My Blog', controller: 'articles' %>
</pre>
</div>
<p><code>link_to</code> 메소드는 레일스의 내장 뷰 헬퍼 중 하나이다. 표시할 텍스트와 이동 위치(이 경우 기사 목록에 대한 경로)를 기반으로 하이퍼링크를 생성한다.</p><p>이 "New Article" 링크를 <code>app/views/articles/index.html.erb</code>에 추가하여 다른 뷰에 대한 링크를 추가하고 <code><table></code> 태그 위에 배치해 보자.</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= link_to 'New article', new_article_path %>
</pre>
</div>
<p>이 링크를 사용하면 새 기사를 작성할 수 있는 폼을 불러올 수 있다.</p><p>이제 <code>app/views/articles/new.html.erb</code> 파일 내의 폼 아래에 또 다른 링크를 추가하여 <code>index</code> 액션으로 돌아 갈 수 있도록 한다.</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
...
<% end %>
<%= link_to 'Back', articles_path %>
</pre>
</div>
<p>마지막으로, <code>app/views/articles/show.html.erb</code> 템플릿에 링크를 추가하여 <code>index</code> 액션으로 돌아갈 수 있도록 하면 단일 기사를 보는 상태에서 되돌아 가서 다시 전체 목록을 볼 수 있게 된다.</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<%= link_to 'Back', articles_path %>
</pre>
</div>
<div class="info"><p>레일스는 기본적으로 현재 컨트롤러를 사용하기 때문에 동일한 컨트롤러에서 액션에 연결하고자 할 경우 <code>:controller</code> 옵션을 지정할 필요가 없다.</p></div><div class="info"><p>개발 모드(기본적으로 작업중인 모드)에서 레일스는 모든 브라우저 요청에 따라 애플리케이션을 다시 로드하므로 소스 코드의 변경시 웹 서버를 중지했다가 다시 시작할 필요가 없다.</p></div><h4 id="adding-some-validation"><a class="anchorlink" href="#adding-some-validation">5.10 몇가지 유효성 검사 추가하기</a></h4><p><code>app/models/article.rb</code> 모델 파일은 아래와 같이 간단하다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class Article < ApplicationRecord
end
</pre>
</div>
<p>이 파일에는 그다지 많은 내용이 있지 않지만 <code>Article</code> 클래스가 <code>ApplicationRecord</code>로부터 상속 받는다는 것에 주목한다. <code>ApplicationRecord</code>는, 기본 데이터베이스 CRUD (Create, Read, Update, Destroy) 작업, 데이터 유효성 검사, 정교한 검색 지원 및 여러 모델을 서로 연관시키는 기능을 포함하여, 레일스 모델에 많은 기능을 무료로 제공하는 <code>ActiveRecord::Base</code>로부터 상속받는다.</p><p>레일스에는 모델로 보내는 데이터의 유효성 검사에 도움이 되는 메소드가 포함되어 있다.
<code>app/models/article.rb</code> 파일을 열고 아래와 같이 변경한다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class Article < ApplicationRecord
validates :title, presence: true,
length: { minimum: 5 }
end
</pre>
</div>
<p>이로써 모든 기사의 제목은 5자 이상이어야 한다. 레일스는 컬럼의 유무와 형식, 그리고 관련 객체의 존재 등 모델의 다양한 조건을 검증 할 수 있다. 유효성 검사는 <a href="active_record_validations.html">Active Record Validations</a>에 자세히 설명되어 있다.</p><p>유효성 검사가 설정된 상태에서 유효하지 않은 기사에 대해서 <code>@article.save</code>를 호출하면 <code>false</code>를 반환할 것이다. <code>app/controllers/articles_controller.rb</code>를 다시 열면 <code>create</code> 액션 내에서 <code>@article.save</code>를 호출 한 결과를 확인하지 않는다는 것을 알게 될 것이다.
이 상태에서 <code>@article.save</code>가 실패하면 사용자에게 폼을 다시 보여줘야 한다. 이를 위해서는 <code>app/controllers/articles_controller.rb</code>의 <code>new</code> 및 <code>create</code> 액션을 아래와 같이 변경한다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
</pre>
</div>
<p><code>new</code> 액션은 이제 인스턴스 변수 <code>@article</code>을 새로 생성하게 되는데, 잠시 후에 그 이유를 알게 될 것이다.</p><p><code>create</code> 액션 내에서 <code>save</code>가 <code>false</code>를 반환 할 때 <code>redirect_to</code> 대신 <code>render</code>를 사용하는 것을 주목한다. 렌더링될 때 <code>@article</code> 객체를 <code>new</code> 템플릿으로 다시 전달하기 위해 <code>render</code> 메소드를 사용하는 것이다. 이 렌더링은 폼 서밋과 동일한 요청 내에서 수행되지만 <code>redirect_to</code>는 브라우저에 다른 요청을 하도록 한다.</p><p><a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a>를 다시 로드하고 title 없이 기사를 저장하려고 하면 검증 오류로 인하여 다시 폼이 렌더링 되지만 이에 대한 유용한 정보를 제공해 주지 못한다. 사용자에게 무언가 잘못되었다고 알려 주어야 한다. 이를 위해 <code>app/views/articles/new.html.erb</code>를 수정하여 오류 메시지가 발생한 경우 표시하도록 한다.</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
<% if @article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(@article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
</pre>
</div>
<p>여기서 처리되는 과정을 보면, <code>@article.errors.any?</code>를 호출하여 오류가 있는지 확인하고, 이 경우 <code>@article.errors.full_messages</code>로 모든 오류 목록을 표시하도록 한다.</p><p><code>pluralize</code>는 숫자와 문자열을 인수로 받는 레일스 헬퍼 메소드이다. 숫자가 1보다 크면 문자열이 복수형으로 자동변환된다.</p><p><code>ArticlesController</code>에 <code>@article = Article.new</code>를 추가한 이유는 그렇지 않을 경우 뷰 상에서 인스턴스 변수 <code>@article</code>의 값은 <code>nil</code>이 되고 이 때<code>@article.errors.any?</code>를 호출하면 에러가 발생하기 때문이다.</p><div class="info"><p>레일스는 <code>field_with_errors</code> 클래스가 지정되어 있는 div 태그로 에러를 포함하는 필드를 자동으로 래핑한다. CSS 규칙을 정의하여 이러한 필드를 두드러지게 보이게 할 수 있다.</p></div><p>이제 새 기사 폼 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a>에서 title을 지정하지 않고 기사를 저장할 때 오류 메시지가 멋있게 표시될 것이다.</p><p><img src="images/getting_started/form_with_errors.png" alt="Form With Errors"></p><h4 id="updating-articles"><a class="anchorlink" href="#updating-articles">5.11 기사 업데이트하기</a></h4><p>지금까지 CRUD의 "CR"부분을 다뤘다. 이제 기사를 업데이트하면서 "U"부분에 초점을 맞추어 보자.</p><p>첫 번째 단계는 아래와 같이 <code>edit</code> 액션을 <code>ArticlesController</code>에 추가하는 것이다. 일반적으로 <code>new</code>와 <code>create</code> 액션 사이에 위치한다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def new
@article = Article.new
end
def edit
@article = Article.find(params[:id])
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
</pre>
</div>
<p>뷰에는 기사를 새로 작성할 때 사용한 것과 유사한 폼이 포함된다. <code>app/views/articles/edit.html.erb</code>라는 파일을 생성한 후 아래와 같이 추가한다.</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<h1>Edit article</h1>
<%= form_with(model: @article, local: true) do |form| %>
<% if @article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(@article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
</pre>
</div>
<p>이번에는 폼이 <code>update</code> 액션을 가리키도록 하는데, 아직 정의되지 않았지만 곧 될 것이다.</p><p><code>form_with</code> 메소드에 기사 객체를 전달하면 편집 된 기사 폼을 서밋하기 위한 URL이 자동으로 설정된다. 이 옵션을 사용하면 <code>PATCH</code> HTTP 메소드로 이 폼을 서밋할 수 있으며 이 메소드는 REST 프로토콜에 따라 리소스를 <strong>업데이트</strong>하는 데 사용하는 HTTP 메소드이다.</p><p>또한, 위의 편집 뷰에서 <code>model: @article</code>과 같이 <code>form_with</code>에 모델 객체를 전달하면 폼 헬퍼가 객체의 해당 값으로 폼 필드를 채우게 된다. new 뷰에서와 같이 <code>scope: :article</code>과 같은 심볼 스코프를 전달하면 빈 폼 필드 만 생성된다. 자세한 내용은 <a href="https://api.rubyonrails.org/6-0-stable/classes/ActionView/Helpers/FormHelper.html#method-i-form_with">form_with documentation</a>에서 찾아 볼 수 있다.</p><p>다음으로 <code>app/controllers/articles_controller.rb</code>에 <code>update</code> 액션을 만들어야 한다. <code>create</code> 액션과 <code>private</code> 메소드 사이에 추가한다.</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article
else
render 'edit'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
</pre>
</div>
<p>새로 작성하는 <code>update</code> 메소드는 이미 존재하는 레코드를 업데이트할 때 사용되며 업데이트하려는 속성을 포함하는 해시를 이용한다. 이전과 마찬가지로 기사를 업데이트하는 동안 오류가 발생하면 폼을 사용자에게 다시 보여 주도록 한다.</p><p>앞서 create 액션을 위해 정의했던 <code>article_params</code> 메소드를 재사용한다.</p><div class="info"><p>모든 속성을 <code>update</code>에 전달할 필요는 없다. 예를 들어, <code>@article.update(title: 'A new title')</code>이 호출되면 레일스는 <code>title</code> 속성 만 업데이트하고 다른 모든 속성은 변경하지 않는다.</p></div><p>마지막으로, 모든 기사 목록에서 <code>edit</code> 액션에 대한 링크를 보여 주기 위해서, 이제 <code>app/views/articles/index.html.erb</code>에 이 링크를 추가하여 "Show" 링크 옆에 보이도록 한다.</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<table>
<tr>