This repository has been archived by the owner on Jan 7, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmigrations.html
781 lines (713 loc) · 52.1 KB
/
migrations.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
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Ruby on Rails Guides: 마이그레이션</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print" />
<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shCore.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shThemeRailsGuides.css" />
</head>
<body class="guide">
<div id="topNav">
<div class="wrapper">
<strong>More at <a href="http://rubyonrails.org/">rubyonrails.org:</a> </strong>
<a href="http://rubyonrails.org/">Overview</a> |
<a href="http://rubyonrails.org/download">Download</a> |
<a href="http://rubyonrails.org/deploy">Deploy</a> |
<a href="http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/overview">Code</a> |
<a href="http://rubyonrails.org/screencasts">Screencasts</a> |
<a href="http://rubyonrails.org/documentation">Documentation</a> |
<a href="http://rubyonrails.org/ecosystem">Ecosystem</a> |
<a href="http://rubyonrails.org/community">Community</a> |
<a href="http://weblog.rubyonrails.org/">Blog</a>
</div>
</div>
<div id="header">
<div class="wrapper clearfix">
<h1><a href="index.html" title="Return to home page">Guides.rubyonrails.org</a></h1>
<p class="hide"><a href="#mainCol">Skip navigation</a>.</p>
<ul class="nav">
<li><a href="index.html">홈</a></li>
<li class="index"><a href="index.html" onclick="guideMenu(); return false;" id="guidesMenu">목차</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<dl class="L">
<dt>시작</dt>
<dd><a href="getting_started.html">레일즈 시작하기</a></dd>
<dt>모델(Models)</dt>
<dd><a href="migrations.html">레일즈 데이터베이스 마이그레이션</a></dd>
<dd><a href="active_record_validations_callbacks.html">액티브 레코드 데이터 검증(Validation)과 Callback(콜백)</a></dd>
<dd><a href="association_basics.html">액티브 레코드 Association(관계)</a></dd>
<dd><a href="active_record_querying.html">액티브 레코드 쿼리 인터페이스</a></dd>
<dt>뷰(Views)</dt>
<dd><a href="layouts_and_rendering.html">레이아웃(Layouts)과 렌더링(Rendering)</a></dd>
<dd><a href="form_helpers.html">액션 뷰 폼 핼퍼(Action View Form Helpers)</a></dd>
<dt>컨트롤러(Controllers)</dt>
<dd><a href="action_controller_overview.html">액션 컨트롤러 둘러보기</a></dd>
<dd><a href="routing.html">외부 요청에 대한 레일즈 라우팅</a></dd>
</dl>
<dl class="R">
<dt>심화내용</dt>
<dd><a href="active_support_core_extensions.html">액티브 서포트(Active Support) 확장(Core Extensions)</a></dd>
<dd><a href="i18n.html">레일즈 국제화I(nternationalization) API</a></dd>
<dd><a href="action_mailer_basics.html">액션 메일러의 기본</a></dd>
<dd><a href="testing.html">레일즈 어플리케이션 테스트하기</a></dd>
<dd><a href="security.html">레일즈 어플리케이션의 보안</a></dd>
<dd><a href="debugging_rails_applications.html">레일즈 어플리케이션 디버깅</a></dd>
<dd><a href="performance_testing.html">레일즈 어플리케이션 성능 테스트하기</a></dd>
<dd><a href="configuring.html">레일즈 어플리케이션 설정</a></dd>
<dd><a href="command_line.html">레일즈 커멘드라인 도구와 Rake 테스크</a></dd>
<dd><a href="caching_with_rails.html">레일즈를 이용한 캐싱</a></dd>
<dt>레일즈 확장하기(Extending Rails)</dt>
<dd><a href="plugins.html">레일즈 플러그인 작성의 기본</a></dd>
<dd><a href="rails_on_rack.html">렉 위의 레일즈(Rails on Rack)</a></dd>
<dd><a href="generators.html">레일즈 제너레이터(Generator) 제작과 수정</a></dd>
<dt>루비 온 레이즈에 기여하기</dt>
<dd><a href="contributing_to_ruby_on_rails.html">루비 온 레이즈에 기여하기</a></dd>
<dd><a href="api_documentation_guidelines.html">API 문서 가이드라인</a></dd>
<dd><a href="ruby_on_rails_guides_guidelines.html">루비 온 레일즈 가이드에 대한 가이드라인</a></dd>
<dt>Release Notes</dt>
<dd><a href="3_0_release_notes.html">Ruby on Rails 3.0 Release Notes</a></dd>
<dd><a href="2_3_release_notes.html">Ruby on Rails 2.3 Release Notes</a></dd>
<dd><a href="2_2_release_notes.html">Ruby on Rails 2.2 Release Notes</a></dd>
</dl>
</div>
</li>
<li><a href="contribute.html">기여하기</a></li>
<li><a href="credits.html">수고하신 분들</a></li>
</ul>
</div>
</div>
<hr class="hide" />
<div id="feature">
<div class="wrapper">
<h2>마이그레이션</h2>
<p>마이그레이션은 체계적이고 유기적인 수단으로 데이터베이스를 변경할 수 있는 편리한 방법입니다. 여러분은 손수 SQL을 수정할 수 있습니다. 그러나 수정후에 다른 개발자들에게 수정 사항을 알려줘야할 책임을 가지게 될겁니다. 그리고 다음에 제품을 배포 할때도 변경 사항이 실행되는지 이를 지켜봐야만 합니다.</p>
<p>액티브 레코드는 이미 실행된 마이그레이션을 추적합니다. 그래서 여러분이 해야할 일은 소스를 업데이트 하고 <tt>rake db:migrate</tt> 명령을 실행하는 일 뿐입니다. 액티브 레코드는 실행해야 할 마이그레이션만 작업을 진행합니다. 그리고 여러분의 데이터베이스의 구조에 부합하는 <tt>db/schema.rb</tt> 파일도 갱신합니다.</p>
<p>마이그레이션은 루비를 이용해서 이러한 변경 사항을 기술합니다. 이것의 굉장한 점은, 대부분의 액티브 레코드의 기능처럼 데이터베이스에 독립적이라는 것 입니다.: <tt>SELECT *</tt> 의 다양한 변화에 비한다면야, <tt>CREATE TABLE</tt>의 정확한 문법에 대해서 크게 걱정할 필요가 없습니다. (데이터베이스의 특별한 기능들을 위해 기존 SQL도 사용할 수 있습니다.) 예를 들어, 개발에서는 SQLite3 를 사용하지만, 제품에서는 MySQL을 사용할 수 있죠.</p>
<p>여러분은 다음 내용의 마이그레이션 관한 모든걸 배울 수 있습니다.:</p>
<ul>
<li>제너레이터(generators) 사용하고 만들기</li>
<li>액티브 레코드가 제공하는 데이터베이스 관리 메소드</li>
<li>마이그레이션을 관리하는 Rake 태스크</li>
<li>마이그레이션과 <tt>schema.rb</tt> 의 관계</li>
</ul>
<div id="subCol">
<h3 class="chapter"><img src="images/chapters_icon.gif" alt="" />Chapters</h3>
<ol class="chapters">
<li><a href="#Anatomy-of-a-Migration">마이그레이션 분석</a><ul><li><a href="#migrations-are-classes">마이그레이션은 클래스이다.</a></li> <li><a href="#whats-in-a-name">이름의 의미</a></li> <li><a href="#changing-migrations">마이그레이션 변경하기</a></li></ul></li><li><a href="#creating-a-migration">마이그레이션 만들기</a><ul><li><a href="#creating-a-model">모델 만들기</a></li> <li><a href="#creating-a-standalone-migration">독립적인(Standalone) 마이그레이션 만들기</a></li></ul></li><li><a href="#writing-a-migration">마이그레이션 작성하기</a><ul><li><a href="#creating-a-table">테이블 만들기</a></li> <li><a href="#changing-tables">테이블 변경</a></li> <li><a href="#special-helpers">특별한 헬퍼</a></li> <li><a href="#writing-your-down-method"><tt>down</tt> 메소드 작성하기</a></li></ul></li><li><a href="#running-migrations">마이그레이션 실행하기</a><ul><li><a href="#rolling-back">롤백 하기</a></li> <li><a href="#being-specific">지정 하기</a></li> <li><a href="#being-talkative">결과 출력하기</a></li></ul></li><li><a href="#using-models-in-your-migrations">여러분의 마이그레이션에서 모델 사용하기</a><ul><li><a href="#dealing-with-changing-models">모델 변경 처리하기</a></li></ul></li><li><a href="#schema-dumping-and-you">스키마 덤프와 여러분</a><ul><li><a href="#what-are-schema-files-for">스키마 파일은 무엇을 위해 존재하는가?</a></li> <li><a href="#types-of-schema-dumps">스키마 덤프의 유형</a></li> <li><a href="#schema-dumps-and-source-control">스키마 덤프와 소스 컨트롤</a></li></ul></li><li><a href="#active-record-and-referential-integrity">액티브 레코드와 참조 무결성</a></li><li><a href="#changelog">Changelog</a></li><li><a href="#changelog-for-korean-translation">Changelog for Korean Translation</a></li></ol></div>
</div>
</div>
<div id="container">
<div class="wrapper">
<div id="mainCol">
<div class='warning'><p>이 가이드의 원본은 영문 버전인 <a href="http://guides.rubyonrails.org/migrations.html">Rails Database Migrations</a> 입니다. 번역 과정에서 원본과 차이가 발생할 수 있습니다. 번역본은 원본의 참고로 생각해 주세요.</p></div>
<h3 id="Anatomy-of-a-Migration">1 마이그레이션 분석</h3>
<p>마이그레이션의 자세한 부분을 다루기 앞서, 여러분이 수행할 수 있는 몇 가지 예제가 있습니다.:</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class CreateProducts < ActiveRecord::Migration
def self.up
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end
def self.down
drop_table :products
end
end
</pre>
</div>
</notextile>
<p>이 마이그레이션은 문자열 컬럼 <tt>name</tt>과 텍스트 컬럼 <tt>description</tt>으로 구성된 <tt>products</tt> 테이블을 추가합니다. 기본키(primary key) 컬럼 <tt>id</tt>도 역시 추가됩니다. 그렇지만 기본키에 관한 세팅은 기본 설정입니다. 액티브 레코드가 자동으로 생성하는 타임스탬프 컬럼인 <tt>created_at</tt> 과 <tt>updated_at</tt>도 추가됩니다. 이 마이그레이션은 간단하게 테이블을 삭제하는 것으로 되돌릴 수 있습니다.</p>
<p>마이그레이션은 스키마의 변경에만 그치는게 아닙니다. 마이그레이션을 데이터베이스내의 잘못된 데이터를 고치거나, 새로운 필드를 만드는데도 사용할 수 있습니다.:</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class AddReceiveNewsletterToUsers < ActiveRecord::Migration
def self.up
change_table :users do |t|
t.boolean :receive_newsletter, :default => false
end
User.update_all ["receive_newsletter = ?", true]
end
def self.down
remove_column :users, :receive_newsletter
end
end
</pre>
</div>
</notextile>
<p>이 마이그레이션은 <tt>receive_newsletter</tt> 컬럼을 <tt>user</tt> 테이블에 추가합니다. 우리는 새로운 사용자에 대해서는 기본 값을 <tt>false</tt>로 하기를 원하지만, 기존 사용자에 대해서도 옵션을 생각해야 합니다. 그래서 User 모델을 기존 사용자들에 대해 해당 플래그를 <tt>true</tt>로 세팅하는데 사용합니다.</p>
<div class='note'><p>마이그레이션 중에서 모델을 사용하는데 몇가지 <a href="#using-models-in-your-migrations">주의사항</a> 을 참고하세요.</p></div>
<h4 id="migrations-are-classes">1.1 마이그레이션은 클래스이다.</h4>
<p>마이그레이션은 <tt>up</tt>(필요한 변경 사항을 수행합니다.)과 <tt>down</tt>(변경 사항을 되돌립니다.)을 구현한 <tt>ActiveRecord::Migration</tt>의 서브 클래스 입니다.</p>
<p>액티브 레코드는 데이터베이스 독립적인 방법으로 일반 데이터 정의 태스크를 수행하는 메소드들을 제공합니다. (여러분은 나중에 이에 관해서 자세히 배울 것입니다.):</p>
<ul>
<li><tt>create_table</tt></li>
<li><tt>change_table</tt></li>
<li><tt>drop_table</tt></li>
<li><tt>add_column</tt></li>
<li><tt>change_column</tt></li>
<li><tt>rename_column</tt></li>
<li><tt>remove_column</tt></li>
<li><tt>add_index</tt></li>
<li><tt>remove_index</tt></li>
</ul>
<p>만약, 여러분의 데이터베이스에 특화된 일을 수행할 필요가 있다면 (예컨데 <a href="#active-record-and-referential-integrity" title="foreign key">외래키</a> 제약사항 만들기), <tt>execute</tt> 함수로 임의의 SQL을 실행할 수 있습니다. 단지 마이그레이션은 보통 루비 클래스 입니다. 그래서 이런 함수에 대한 제약 사항은 없습니다. 예를 들어, 컬럼을 추가한 후에 이미 존재하는 레코드를 위해서 추가된 컬럼에 값을 세팅하는 코드를 작성할 수 있습니다. (필요시 여러분의 모델을 사용해야 합니다.)</p>
<p>데이터베이스가 스키마 변경을 위한 트랜젝션을 제공하면(가령 PostgreSQL 혹은 SQLite3), 마이그레이션은 트랜젝션으로 작업을 감쌉니다. 만약 이를 지원하지 않으면 (가령 MySQL), 마이그레이션이 실패 했을때 변경이 적용된 부분들이 되돌아가지(roll back) 못할 것입니다. 이 상황에서 변경된 부분을 손수 되돌려야 할 것입니다.</p>
<h4 id="whats-in-a-name">1.2 이름의 의미</h4>
<p>마이그레이션은 각 마이그레이션 클래스당 하나의 파일로 <tt>db/migrate</tt>에 저장됩니다. 파일의 이름은 <tt>YYYYMMDDHHMMSS_create_products.rb</tt>한 형태의 포맷입니다. 이 이름은 <span class="caps">UTC</span> 타임스탬프 마이그레이션 식별자와 언더스코어(_ 모양의 글자)로 연결된 마이그레이션의 이름으로 구성됩니다. 마이그레이션 클래스의 이름( CamelCase 버전)은 반드시 파일 이름의 글자 부분과 맞아야합니다. 예를들어서 <tt>20080906120000_create_products.rb</tt>는 반드시 <tt>CreateProducts</tt>을 정의해야 하고, <tt>20080906120001_add_details_to_products.rb</tt>는 반드시 <tt>AddDetailsToProducts</tt>을 정의해야 합니다. 만약 파일의 이름을 변경하려면, 반드시 파일 내부의 클래스의 이름도 <em>변경해야 합니다.</em> 그렇지 않으면, 레일즈는 클래스를 찾지 못해서 에러를 보고 할 것입니다.</p>
<p>레일즈는 내부적으로 오직 마이그레이션의 번호(타임스탬프를 의미)를 마이그레이션 식별에 사용합니다. 레일즈 2.1 이전에 마이그레이션 번호는 1부터 시작해서 마이그레이션이 생성될때 마다 증가하는 값을 취했습니다. 여러명의 개발자들이 함께 개발할때, 이 규칙은 소규모의 충돌 상황에서도 변경을 되돌리고나서 여러 마이그레이션의 번호를 다시 부여하는 상황을 만들었습니다. 레일즈 2.1에서 마이그레이션의 생성 시각을 식별하는데 사용해서 이러한 문제 상황을 대부분 피할 수 있게 되었습니다. <tt>config/application.rb</tt>에 다음 라인을 추가시키면 예전 방식의 번호 매기는 방식으로 돌아갈 수 있습니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
config.active_record.timestamped_migrations = false
</pre>
</div>
</notextile>
<p>타임스탬프와 마이그레이션을 기록하는 조합은 레일즈가 여러 개발자들이 마이그레이션에 관여하면서 생기는 일반적인 상황을 다룰 수 있게 해줍니다.</p>
<p>예를들어서 앨리스가 마이그레이션 <tt>20080906120000</tt>과 <tt>20080906123000</tt>을 추가하고 밥이 <tt>20080906124500</tt>를 추가시킨 후 이를 실행합니다. 앨리스는 그녀의 마이그레이션에서 변경을 완료하고 점검합니다. 그리고 밥은 자신의 소스에 최근 변경 사항을 내려 받습니다. 레일즈는 앨리스의 두개의 마이그레이션을 아직 실행하지 않았다는걸 알 수있습니다. 그래서 밥이 실행하는 <tt>rake db:migrate</tt>은 실행되지 않은 앨리스의 마이그레이션을 실행합니다. (더 이후의 타임스템프 값을 가진 밥의 마이그레이션을 실행하는데도 말이죠.) 그리고 이 과정과 비슷하게 마이그레이션 다운(down)시에도 실행되지 않은 앨리스의 <tt>down</tt> 메소드를 실행하지 않습니다.</p>
<p>물론, 이는 팀간의 대화를 대체하는건 아닙니다. 예를 들어서, 만약 앨리스의 마이그레이션이 밥의 마이그레이션 실행에 필요한 테이블을 삭제하면, 문제는 확실히 발생합니다.</p>
<h4 id="changing-migrations">1.3 마이그레이션 변경하기</h4>
<p>때로, 마이그레이션을 작성할때 여러분은 실수를 하게 됩니다. 이미 마이그레이션을 실행한 후에는, 단순히 마이그레이션을 수정하고 다시 실행하는 것으로 원하는 변경을 반영할 수는 없습니다.:레일즈는 이미 실행된 마이그레이션이라면, <tt>rake db:migrate</tt>를 수행할때 아무 것도 실행하지 않습니다. 여러분은 반드시 마이그레이션을 되돌(rollback)리고 (예를 들어, <tt>rake db:rollback</tt>를 사용), 마이그레이션을 수정하고 정확한 버전에 대하여 <tt>rake db:migrate</tt>를 실행해야 합니다.</p>
<p>일반적으로 마이그레이션 수정은 좋은 생각이 아닙니다.:여러분은 자신과 동료들을 위해서 추가적인 일을 만들어내고, 만약 제품에 이미 적용된 마이그레이션을 수정하면 골치 아픈 상황을 만들어 낼겁니다. 대신에 필요한 변경을 수행하는 새로운 마이그레이션을 만드세요. 소스 관리 도구에 커밋되지 않은(혹은 좀 더 일반적으로 표현하자면 여러분의 개발 머신 이외에 퍼지지 않은) 새로운 마이그레이션을 수정하는 편이 상대적으로 무해합니다. 이런건 그냥 상식이죠.</p>
<h3 id="creating-a-migration">2 마이그레이션 만들기</h3>
<h4 id="creating-a-model">2.1 모델 만들기</h4>
<p>모델과 발판(scaffold) 제너레이터는 새로운 모델을 추가를 위한 적합한 마이그레이션을 만들 것입니다. 이 마이그레이션은 이미 관련된 테이블 생성에 대한 코드를 담고 있을 것입니다. 만약 레일즈에게 여러분이 원하는 컬럼들을 알려주면 이 역시 함께 생성됩니다. 예를들어서 다음과 같이 실행하면</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
rails generate model Product name:string description:text
</pre>
</div>
</notextile>
<p>다음과 같은 마이그레이션이 생성됩니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class CreateProducts < ActiveRecord::Migration
def self.up
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end
def self.down
drop_table :products
end
end
</pre>
</div>
</notextile>
<p>여러분이 원하는 컬럼을 얼마든지 name/type 형태로 추가할 수 있습니다. 기본값으로, <tt>t.timestamps</tt>가 추가되어 있습니다.(이 구문으로 생성된 <tt>updated_at</tt>과 <tt>created_at</tt> 컬럼은 액티브 레코드에 의해서 자동으로 채워집니다.)</p>
<h4 id="creating-a-standalone-migration">2.2 독립적인(Standalone) 마이그레이션 만들기</h4>
<p>만약 다른 목적으로 (가령, 이미 존재하는 테이블에 컬럼을 추가하는 경우) 마이그레이션을 만들려면, 마이그레이션 제너레이터를 사용할 수 있습니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
rails generate migration AddPartNumberToProducts
</pre>
</div>
</notextile>
<p>이 명령은 비어있지만, 정확한 이름을 가진 마이그레이션을 만듭니다.:</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class AddPartNumberToProducts < ActiveRecord::Migration
def self.up
end
def self.down
end
end
</pre>
</div>
</notextile>
<p>만약 마이그레이션 이름이 “AddXXXToYYY” 혹은 “RemoveXXXFromYYY” 이고 이후에 컬럼 이름과 타입을 입력하면 정확한 <tt>add_column</tt> 과 <tt>remove_column</tt> 구문이 생성될 것입니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
rails generate migration AddPartNumberToProducts part_number:string
</pre>
</div>
</notextile>
<p>이 명령은 다음을 생성하죠.</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class AddPartNumberToProducts < ActiveRecord::Migration
def self.up
add_column :products, :part_number, :string
end
def self.down
remove_column :products, :part_number
end
end
</pre>
</div>
</notextile>
<p>비슷하게,</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
rails generate migration RemovePartNumberFromProducts part_number:string
</pre>
</div>
</notextile>
<p>다음의 내용을 생성합니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class RemovePartNumberFromProducts < ActiveRecord::Migration
def self.up
remove_column :products, :part_number
end
def self.down
add_column :products, :part_number, :string
end
end
</pre>
</div>
</notextile>
<p>이런 마술 같은 컬럼 만들기는 하나만 할 수 있는게 아닙니다. 예를들어서</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
rails generate migration AddDetailsToProducts part_number:string price:decimal
</pre>
</div>
</notextile>
<p>명령은 다음을 생성합니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class AddDetailsToProducts < ActiveRecord::Migration
def self.up
add_column :products, :part_number, :string
add_column :products, :price, :decimal
end
def self.down
remove_column :products, :price
remove_column :products, :part_number
end
end
</pre>
</div>
</notextile>
<p>늘, 시작점이 될만한 것을 만들 수 있습니다. 여기에서 부터 여러분에 입맛에 맞게 추가하고 삭제하면 됩니다.</p>
<h3 id="writing-a-migration">3 마이그레이션 작성하기</h3>
<p>이제 제너레이터를 이용해서 여러분의 마이그레이션을 만들어 볼때 입니다!</p>
<h4 id="creating-a-table">3.1 테이블 만들기</h4>
<p>마이그레이션 메소드인 <tt>create_table</tt>은 여러분의 편리한 도구 중 하나입니다. 보통 이렇게 사용합니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
create_table :products do |t|
t.string :name
end
</pre>
</div>
</notextile>
<p>이 코드는 <tt>name</tt>이란 이름의 컬럼을 가진 <tt>products</tt> 테이블을 만듭니다. (그리고 아래의 논의에서 처럼, 암시적인 <tt>id</tt> 컬럼도 생성합니다.)</p>
<p>이 객체는 테이블에 만들 컬럼 정보를 블록 형태로 실행합니다. 이를 수행하는 방법은 두가지가 있죠.:첫번째 (관습적인) 폼은 다음과 같습니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
create_table :products do |t|
t.column :name, :string, :null => false
end
</pre>
</div>
</notextile>
<p>두번째 폼은 “섹시(sexy)” 마이그레이션이라 불리며, 중복된 <tt>column</tt> 메소드를 날려버린 방법이죠. 대신에 <tt>string</tt>, <tt>integer</tt> 등의 메소드는 해당 타입의 컬럼을 생성합니다. 이후 파라미터는 동일합니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
create_table :products do |t|
t.string :name, :null => false
end
</pre>
</div>
</notextile>
<p>기본적으로, <tt>create_table</tt>은 <tt>id</tt>라는 기본키(primary key)를 생성합니다. <tt>:primary_key</tt> 옵션을 이용해서 기본키(primary key)의 이름을 변경하거나, 기본키(primary key)를 원하지 않으면, (예를들어 <span class="caps">HABTM</span> 조인 테이블 같이요.) <tt>:id => false</tt> 를 넘기세요. 만약, 데이터베이스에 특화된 옵션을 넘기려면, <tt>:options</tt>에 <span class="caps">SQL</span> 조각을 위치 시킬 수 있습니다. 예를 들어서</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
create_table :products, :options => "ENGINE=BLACKHOLE" do |t|
t.string :name, :null => false
end
</pre>
</div>
</notextile>
<p>이 코드는 <tt>ENGINE=BLACKHOLE</tt>을 테이블 생성하는 <span class="caps">SQL</span> 구문에 추가할 것입니다. (MySQL은 기본 값은 <tt>ENGIN=InnoDB</tt> 입니다.)</p>
<p>액티브 레코드가 지원하는 타입은 <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>, <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt> 입니다.</p>
<p>이들은 각 데이터베이스에 적합한 타입으로 짝지어 집니다. 예를들어서 MySQL을 사용할때 <tt>:string</tt>은 <tt>VARCHAR(255)</tt>와 짝지어 집니다. 섹시하지 않은(non-sexy) 문법을 사용하면, 액티브 레코드가 지원하지 않는 타입의 컬럼도 만들수 있습니다. 예를들어</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
create_table :products do |t|
t.column :name, 'polygon', :null => false
end
</pre>
</div>
</notextile>
<p>그렇지만, 아마 이런 코드는 데이터베이스 이식성이 떨어지게 되겠죠.</p>
<h4 id="changing-tables">3.2 테이블 변경</h4>
<p><tt>create_table</tt>과 가까운 메소드는 존재하는 테이블을 변경에 사용되는 <tt>change_table</tt>입니다. 이건 <tt>create_table</tt>과 비슷한 모습으로 사용되지만 객체가 실행하는 블록에 더 많은 트릭이 있습니다. 예를들어</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
change_table :products do |t|
t.remove :description, :name
t.string :part_number
t.index :part_number
t.rename :upccode, :upc_code
end
</pre>
</div>
</notextile>
<p>이 코드는 <tt>description</tt>과 <tt>name</tt>을 삭제, <tt>part_number</tt> 컬럼을 만들고 인덱스를 부여합니다. 마지막으로 <tt>upccode</tt> 컬럼을 <tt>upc_code</tt>로 이름을 변경합니다. 이건 다음의 코드와 동일합니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
remove_column :products, :description
remove_column :products, :name
add_column :products, :part_number, :string
add_index :products, :part_number
rename_column :products, :upccode, :upc_code
</pre>
</div>
</notextile>
<p>테이블 이름을 반복하지 말고, 수정할 테이블과 관련된 구문을 한곳에 모으세요. 각 변환 코드 역시 줄어듭니다. 예를들어, <tt>remove_column</tt> 는 <tt>remove</tt>로 그리고 <tt>add_index</tt>는 <tt>index</tt>가 됩니다.</p>
<h4 id="special-helpers">3.3 특별한 헬퍼</h4>
<p>액티브 레코드는 공통의 기능에 대해 몇가지 간단한 방법을 제공합니다. 가장 자주 쓰이는 예가 <tt>created_at</tt>과 <tt>updated_at</tt> 컬럼을 함께 추가할때, 이를 메소드로 수행 할 수 있습니다.:</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
create_table :products do |t|
t.timestamps
end
</pre>
</div>
</notextile>
<p>이 코드는 방금 언급한 두 컬럼(거기에 <tt>id</tt>컬럼 까지)을 가진 새로운 products 테이블을 만듭니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
change_table :products do |t|
t.timestamps
end
</pre>
</div>
</notextile>
<p>이렇게 기존 테이블에 두 컬럼을 추가할 수도 있죠.</p>
<p><tt>references</tt>로 불리는 다른 헬퍼도 있습니다. (<tt>belongs_to</tt> 로도 가능합니다.) 가장 간단한 형태로 예를 들어본다면,</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
create_table :products do |t|
t.references :category
end
</pre>
</div>
</notextile>
<p>이 코드는 정확한 타입을 가진 <tt>category_id</tt> 컬럼을 만듭니다. 컬럼의 이름이 아니라 모델의 이름을 넘기는 부분에 주목하세요. 액티브 레코드는 여러분을 위해 <tt>id</tt>를 이름에 추가합니다. 만약, 여러 형과 <tt>belongs_to</tt> 관계를 가지고 있다면, <tt>references</tt>에 필요한 컬럼 정보도 추가해야 합니다.:</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
create_table :products do |t|
t.references :attachment, :polymorphic => {:default => 'Photo'}
end
</pre>
</div>
</notextile>
<p>이 코드는 <tt>attachment_id</tt> 컬럼과 문자열이면서 기본값이 ’Photo’인 <tt>attachment_type</tt> 컬럼을 추가할 것입니다.</p>
<div class='note'><p><tt>references</tt> 헬퍼는 외래키(foreign key) 제약사항을 추가하지는 않습니다. 이를 위해서 <tt>execute</tt>를 이용하거나, <a href="#active-record-and-referential-integrity">외래키(foreign key) 지원</a> 을 추가하는 플러그인을 사용하세요.</p></div>
<p>액티브 레코드가 제공하는 헬퍼가 만족스럽지 않으면 <tt>execute</tt> 함수로 원하는 SQL을 실행하세요.</p>
<p>개별 메소드에 대한 더 자세한 내용과 예제는 <span class="caps">API</span> 문서를 참고하세요. 상세한 문서들은 다음과 같습니다. <a href="http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html"><tt>ActiveRecord::ConnectionAdapters::SchemaStatements</tt></a> (이 문서는 <tt>up</tt>과 <tt>down</tt> 내에서 사용할수 있는 메소드 정보를 제공합니다.), <a href="http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html"><tt>ActiveRecord::ConnectionAdapters::TableDefinition</tt></a> (이 문서는 <tt>creat_table</tt>을 사용할때 객체가 실행하는 블록내에서 사용할 수 있는 메소드 정보를 제공합니다.), 그리고 <a href="http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html"><tt>ActiveRecord::ConnectionAdapters::Table</tt></a> (이 문서는 <tt>change_table</tt>을 사용할때 객체가 실행하는 블록에서 사용할 수 있는 메소드 정보를 제공합니다.).</p>
<h4 id="writing-your-down-method">3.4 <tt>down</tt> 메소드 작성하기</h4>
<p>마이그레이션의 <tt>down</tt> 메소드는 <tt>up</tt> 메소드가 수행한 변경을 되돌립니다. 다른 말로하자면, <tt>up</tt>을 통해서 변경된 데이터베이스 스키마는 <tt>down</tt>을 통해서 되돌려 집니다. 예를들어, 만약 여러분이 <tt>up</tt> 메소드에서 테이블을 만들었다면, 여러분은 <tt>down</tt> 메소드에서 이를 제거해야 합니다. 현명하게 <tt>up</tt> 메소드의 반대 순서로 만드세요. 예를들면</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class ExampleMigration < ActiveRecord::Migration
def self.up
create_table :products do |t|
t.references :category
end
#add a foreign key
execute <<-SQL
ALTER TABLE products
ADD CONSTRAINT fk_products_categories
FOREIGN KEY (category_id)
REFERENCES categories(id)
SQL
add_column :users, :home_page_url, :string
rename_column :users, :email, :email_address
end
def self.down
rename_column :users, :email_address, :email
remove_column :users, :home_page_url
execute "ALTER TABLE products DROP FOREIGN KEY fk_products_categories"
drop_table :products
end
end
</pre>
</div>
</notextile>
<p>때로, 마이그레이션을 되돌릴 수 없게 될 수 있습니다. 가령, 데이터를 삭제하는 경우 말이죠. 이렇게 마이그레이션을 되돌릴 수 없을때는 <tt>down</tt> 메소드에서 <tt>IrreversibleMigration</tt>을 발생 시키세요. 만약에 다른이가 여러분의 마이그레이션을 되돌리려고 시도하면, 에러 메세지가 불가능 하다는 사실을 안내해 줄 것입니다.</p>
<h3 id="running-migrations">4 마이그레이션 실행하기</h3>
<p>레일즈는 마이그레이션 작업을 위한 rake 태스크 조합을 제공합니다. 최초의 마이그레이션은 아마도 여러분이 사용하게될 <tt>db:migrate</tt>와 관련있습니다. 가장 기본적인 동작은 아직 실행되지 않은 모든 마이그레이션의 <tt>up</tt> 메소드를 실행하는 것 입니다. 만약 마이그레이션이 없더라도, 일을 무사히 마칩니다.</p>
<p><tt>db:migrate</tt> 실행하면 데이터베이스 구조에 부합하는 db/schema.rb 파일을 업데이트하는 <tt>db:schema:dump</tt> 태스크도 실행하는 걸 기억하세요.</p>
<p>원하는 버전을 지정하면, 액티브 레코드는 필요한 마이그레이션(up 혹은 down)을 정해진 버전까지만 실행합니다. 버전은 마이그레이션 파일 앞 부분의 숫자 접두어 입니다. 예를 들어서 버전 20080906120000을 실행하려 이렇게 합니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
rake db:migrate VERSION=20080906120000
</pre>
</div>
</notextile>
<p>현재 버전보다 더 크면, 20080906120000를 포함해서 해당 버전까지 모든 마이그레이션을 실행합니다.(다른 말로, ‘마이그레이션 올리기’라고 합니다.) 만약, 마이그레이션 내리기(롤백을 의미)를 수행하면 20080906120000까지 모든 마이그레이션의 <tt>down</tt> 메소드를 실행하지만 20080906120000을 포함하지는 않습니다.</p>
<h4 id="rolling-back">4.1 롤백 하기</h4>
<p>흔한 테스크는 마지막 마이그레이션을 되돌리는 일 입니다. 예컨데, 실수를 한 후 이를 고치기를 원할때죠. 롤백을 위해 관련된 버전 숫자를 따라가는 것보다 이렇게 하는게 더 쉽죠.</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
rake db:rollback
</pre>
</div>
</notextile>
<p>이 명령은 최근 마이그레이션의 <tt>down</tt>을 실행합니다. 만약 여러번의 undo가 필요하면 <tt>STEP</tt> 파라미터를 이용할 수 있습니다.:</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
rake db:rollback STEP=3
</pre>
</div>
</notextile>
<p>이 명령은 최근 마이그레이션 3개의 <tt>down</tt> 메소드를 실행합니다.</p>
<p><tt>db:migrate:redo</tt> 태스크는 롤백을 하고 다시 마이그레이션을 수행하는데 사용하는 단축 명령입니다. 만약 더 많은 버전을 한꺼번에 되돌아가고 싶다면, <tt>db:rollback</tt> 태스크에서 여러분이 <tt>STEP</tt> 파라미터를 사용한 것 처럼 사용할 수 있습니다. 예를들면 다음과 같습니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
rake db:migrate:redo STEP=3
</pre>
</div>
</notextile>
<p>이 레이크 태스크들은 <tt>db:migrate</tt>와 함께하지 않습니다. 그들은 단순히 여러분이 특정한 버전으로 마이그레이그션을 필요 없을때까지 좀더 편한 방법을 제공할 뿐입니다.</p>
<p>마지막으로, <tt>db:reset</tt> 태스크는 모든 데이터베이스를 삭제하고 현재의 스키마를 재생성합니다.</p>
<div class='note'><p><tt>db:reset</tt>은 모든 마이그레이션을 실행하는 것과 동일하지는 않습니다. – <a href="#schema-dumping-and-you">schema.rb</a> 을 참고하세요.</p></div>
<h4 id="being-specific">4.2 지정 하기</h4>
<p>만약, 원하는 버전의 마이그레이션만 올리거나(up) 내릴(down) 필요할때, <tt>db:migrate:up</tt> 과 <tt>db:migrate:down</tt>을 이용할 수 있습니다. 적당한 버전을 지정해주면, 관련된 마이그레이션 해당 버전의 <tt>up</tt> 혹은 <tt>down</tt> 메소드를 호출 합니다. 예를들어</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
rake db:migrate:up VERSION=20080906120000
</pre>
</div>
</notextile>
<p>이 명령은 20080906120000 마이그레이션의 <tt>up</tt> 메소드를 실행합니다. 이 태스크는 마이그레이션이 이미 실행되었는지 여부도 검사합니다. 그래서 예컨데, <tt>db:migrate:up VERSION=20080906120000</tt> 명령은 액티브 레코드가 이미 20080906120000 버전을 실행했다고 생각되면 아무 것도 수행하지 않습니다.</p>
<h4 id="being-talkative">4.3 결과 출력하기</h4>
<p>기본적으로, 마이그레이션은 여러분에게 정확이 어떤 일이 진행되고 있고, 얼마나 시간이 걸렸는지 보고합니다. 테이블을 만들고, 인덱스를 추가하는 마이그레이션 중 출력되는 결과는 다음과 같습니다.:</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
20080906170109 CreateProducts: migrating
-- create_table(:products)
-> 0.0021s
-- add_index(:products, :name)
-> 0.0026s
20080906170109 CreateProducts: migrated (0.0059s)
</pre>
</div>
</notextile>
<p>몇가지 메소드는 이러한 모든 출력을 제어하는 기능을 제공합니다.</p>
<ul>
<li><tt>suppress_messages</tt>는 메소드의 블록에서 만들어지는 어떠한 결과도 출력하지 않습니다.</li>
<li><tt>say</tt>는 텍스트를 출력합니다. (두번째 인자는 들여쓰기를 할 것인지 여부를 결정합니다.)</li>
<li><tt>say_with_time</tt>는 메소드에 주어진 블록의 수행 시간과 함께 텍스트를 출력합니다. 만약, 그 블록이 정수를 반환하면, 그 숫자를 열(row)로 생각해서 출력 결과에 반영합니다.</li>
</ul>
<p>예를들어서, 이 마이그레이션은</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class CreateProducts < ActiveRecord::Migration
def self.up
suppress_messages do
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end
say "Created a table"
suppress_messages {add_index :products, :name}
say "and an index!", true
say_with_time 'Waiting for a while' do
sleep 10
250
end
end
def self.down
drop_table :products
end
end
</pre>
</div>
</notextile>
<p>다음과 같은 결과를 출력합니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
20080906170109 CreateProducts: migrating
Created a table
-> and an index!
Waiting for a while
-> 10.0001s
-> 250 rows
20080906170109 CreateProducts: migrated (10.0097s)
</pre>
</div>
</notextile>
<p>만약, 액티브 레코드 실행중에 출력을 원하지 않을때, <tt>rake db:migrate VERBOSE=false</tt> 명령어로 실행하면 어떠한 출력도 하지 않을 겁니다.</p>
<h3 id="using-models-in-your-migrations">5 여러분의 마이그레이션에서 모델 사용하기</h3>
<p>마이그레이션에서 데이터를 만들거나 갱신할때, 자주 모델 중에 하나를 사용하고 싶어지죠. 아무튼, 모델은 관련있는 데이터를 쉽게 접근하는 방법을 제공하니까요. 사용 할 수는 있습니다만, 몇가지 주의 사항에 주목해 주세요.</p>
<p>예를들어, <tt>Product</tt> 모델을 이용해서 관련 테이블의 한 열을 갱신한다고 생각해 보죠. 앨리스는 <tt>Product</tt> 모델을 업데이트하고, 새로운 컬럼과 유요성 검증을 추가 했습니다. 밥이 휴가에서 돌아와서, 자신의 소스를 업데이트하고 <tt>rake db:migrate</tt>를 이용해서 마이그레이션을 실행합니다. 밥이 마이그레이션을 실행하는 시점에서 <tt>Product</tt> 모델에 앨리스가 추가한 데이터 검증이 추가되어 있는 상황이죠. 그런데 데이터베이스는 아직 옛날 데이터이고 컬럼을 가지고 있지 않아요. 데이터 검증에 필요한 컬럼을 가지고 있지 않아서 에러가 발생합니다.</p>
<p>저는 직접 SQL을 이용하지 않고, 데이터베이스의 자료를 그냥 갱신하기를 원하는 상황이 자주 발생헤요.:어떤 특정한 모델에 국한되지 않습니다. 이를 해결하기 위한 한가지 패턴은 모델의 사본을 마이그레이 내부에 정의하는 겁니다. 예를들면:</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class AddPartNumberToProducts < ActiveRecord::Migration
class Product < ActiveRecord::Base
end
def self.up
...
end
def self.down
...
end
end
</pre>
</div>
</notextile>
<p>일런 패턴의 마이그레이션은 <tt>Product</tt> 모델의 최소의 사본을 가지며 작업하고, 어플리케이션에 정의된 <tt>Product</tt>에 대한 걱정을 할 필요가 없습니다.</p>
<h4 id="dealing-with-changing-models">5.1 모델 변경 처리하기</h4>
<p>성능 때문에 컬럼에 관한 정보는 객체에 임시 저장(Cached)됩니다. 예를들어, 여러분이 테이블에 컬럼을 추가하고 나서 연관된 모델을 이용해서 새로운 데이터를 입력하려고 하면 모델은 과거 컬럼 정보를 이용하려 합니다. <tt>reset_column_information</tt> 메소드를 이용하면 액티브 레코드에게 강제로 컬럼 정보를 다시 읽으라고 명령할 수 있습니다. 예를 들면 다음과 같습니다.:</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class AddPartNumberToProducts < ActiveRecord::Migration
class Product < ActiveRecord::Base
end
def self.up
add_column :product, :part_number, :string
Product.reset_column_information
...
end
def self.down
...
end
end
</pre>
</div>
</notextile>
<h3 id="schema-dumping-and-you">6 스키마 덤프와 여러분</h3>
<h4 id="what-are-schema-files-for">6.1 스키마 파일은 무엇을 위해 존재하는가?</h4>
<p>마이그레이션은 아마도 데이터베이스를 위한 믿을만한 소스가 되지는 못합니다. 이 부분은 <tt>db/schema.rb</tt> 나 액티브 레코드가 데이터베이스 조사해서 생성한 <span class="caps">SQL</span> 파일의 역할이죠. 이 파일은 사용자가 손수 수정 하도록 의도된 파일이 아닙니다. 단지 현재 데이터베이스의 상태를 표현합니다.</p>
<p>어플리케이션의 새로운 인스턴스를 배포할때 마이그레이션의 전체 과정을 재현할 필요는 없습니다. (그리고 이건 에러가 발생하기도 쉽죠) 현재 스키마의 기술된 내용을 데이터베이스에 적재하는 편이 훨씬 단순하고 빠릅니다.</p>
<p>예를 들어, 테스트 데이터베이스를 만드는 방법을 생각해보죠.:현재 개발 데이터베이스를 덤프하고(<tt>db/schema.rb</tt> 든 <tt>db/development.sql</tt> 이든) 나서 테스트 데이터베이스에 이것을 적재합니다.</p>
<p>스키마 파일은 액티브 레코드가 어떠한 속성(attribute)를 가지고 있는지 한눈에 보기에도 유용합니다. 이 정보는 모델의 코드에 들어 있지 않고, 흔히 여러 마이그레이션에 흩어 집니다. 그러나, 스키마 파일안에는 모든 정보가 담겨있죠. <a href="http://agilewebdevelopment.com/plugins/annotate_models">annotate_models</a> 플러그인은 이런 정보를 각 모델의 상단에 요약해서 주석으로 붙여주는데, 이것도 흥미 있을 겁니다.</p>
<h4 id="types-of-schema-dumps">6.2 스키마 덤프의 유형</h4>
<p>스키마 덤프에는 두가지 방법이 있습니다. <tt>config.active_record.schema_format</tt> 값으로 <tt>config/application.rb</tt>안에 세팅됩니다. <tt>:sql</tt> 이나 <tt>:ruby</tt>를 가질 수 있습니다.</p>
<p>만약 <tt>:ruby</tt>를 스키마로 선택했다면, <tt>db/schema.rb</tt>에 저장됩니다. 이 파일을 보면, 매우 거대한 마이그레이션 하나를 발견할 겁니다.</p>
<notextile>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
ActiveRecord::Schema.define(:version => 20080906171750) do
create_table "authors", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "products", :force => true do |t|
t.string "name"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
t.string "part_number"
end
end
</pre>
</div>
</notextile>
<p>많은 방법 중 이것이 가장 정확합니다. 데이터베이스를 조사하고 <tt>create_table</tt>, <tt>add_index</tt> 등을 이용하여 구조화된 표현으로 이 파일이 만들어 집니다. 이 파일은 데이터베이스 독립적이기 때문에 액티브 레코드를 지원하는 어떤 데이터베이스에도 적재할 수 있습니다. 만약 여러분이 어플리케이션을 배포할때 다양한 데이터베이스를 대상으로 한다면 이는 매우 유용합니다.</p>
<p>그렇지만, 교환 조건(trade-off)가 있죠.:<tt>db/schema.rb</tt>는 데이터베이스에 특화된 표현을 할수 없습니다. 가령 외래키(foreign key) 제약 사항, 트리거(trigger)나 저장 프로시저(stored procedure) 같은거 말이죠. 마이그레이션 중에 여러분은 맞춤형 <span class="caps">SQL</span> 구문을 실행수 있지만, 스키마 덤프 엔진은 데이터베이스에서 그러한 구문을 만들어 낼 수 없습니다. 만약 이런 기능을 만드시 사용해야한다면 스키마 포맷의 세팅을 <tt>:sql</tt>로 하세요.</p>
<p>액티브 레코드의 스키마 덤프 엔진 대신에, 해당 데이터베이스에 특화된 도구(<tt>db:structure:dump</tt> Rake 태스크를 통해서)를 사용해서 <tt>db/#{Rails.env}_structure.sql</tt>에 데이터베이스의 구조를 덤프 할 수 있습니다. 예를들어, PostgresSQL을 위해서는 <tt>pg_dump</tt> 유틸리티를 사용하고, MySQL을 위해서는 다양한 테이블에 대한 <tt>SHOW CREATE TABLE</tt>을 결과를 파일에 저장하면 됩니다. 스키마를 데이터베이스에 적재하는 방법은 내부에 포함된 <span class="caps">SQL</span> 구문 실행으로 간단하게 수행할 수 있습니다.</p>
<p>당연히, 이는 데이터베이스의 구조를 완벽하게 복제할 수 있는 방법이지만, 이 방법은 데이터베이스 이외의 저장 수단에는 스키마 정보를 로딩할 수 없게됩니다.</p>
<p>By definition this will be a perfect copy of the database’s structure but this will usually prevent loading the schema into a database other than the one used to create it.</p>
<h4 id="schema-dumps-and-source-control">6.3 스키마 덤프와 소스 컨트롤</h4>
<p>스키마 덤프는 여러분의 데이터베이스의 스키마를 위한 믿을 만한 소스입니다. 그래서 이를 소스 컨트롤에 포함 시키는걸 강력히 추천합니다.</p>
<h3 id="active-record-and-referential-integrity">7 액티브 레코드와 참조 무결성</h3>
<p>액티브 레코드의 방법은 데이터베이스보다 여러분의 모델을 더 영리하게 만들기를 주장합니다. 가령, 트리거(trigger)나 외래키 제약 사항(constraint) 같은 요소 같이 영리함(intelligence)을 데이터베이스로 맡기는 요소를 과도하게 사용하는 방법을 권장하지 않습니다.</p>
<p><tt>validates :foreign_key, :uniqueness => true</tt>과 같은 데이터 검증(validation)은 모델에서 데이터의 무결성을 지킬수 있는 방법입니다. 관계(association)에서 <tt>:dependent</tt> 옵션은 부모 모델이 삭제되면 자동으로 자식 모델도 삭제합니다. 어플리케이션 레벨의 이러한 동작만으로 완전히 참조 무결성을 보장할 수는 없습니다. 그래서 몇몇 분들은 외래키 제약 사항을 더 많이 사용합니다.</p>
<p>액티브 레코드는 그러한 요소에 대해서 직접적인 어떤 도구도 지원하지는 않지만, <tt>execute</tt> 메소드로 SQL을 실행할 수 있습니다. 이와 관련한 다양한 플러그도 있습니다. 가령 <a href="http://github.com/harukizaemon/redhillonrails/tree/master/foreign_key_migrations/">foreign_key_migrations</a> 플러그인은 외래키 추가 기능을 액티브 레코드에 제공합니다. (<tt>db/schema.rb</tt>에 외래키 정보를 덤프하는 것도 포함합니다.)</p>
<h3 id="changelog">8 Changelog</h3>
<ul>
<li>July 15, 2010: minor typos corrected by <a href="http://jaimeiniesta.com">Jaime Iniesta</a></li>
<li>September 14, 2008: initial version by <a href="credits.html#fcheung">Frederick Cheung</a></li>
</ul>
<h3 id="changelog-for-korean-translation">9 Changelog for Korean Translation</h3>
<ul>
<li>2011년 2월 15일 초벌 번역 완료 by <a href="http://neocoin.net">Sangmin Ryu</a></li>
</ul>
<h3>Feedback</h3>
<p>
You're encouraged to help in keeping the quality of this guide.
</p>
<p>
If you see any typos or factual errors you are confident to
patch, please clone <a href="https://github.com/lifo/docrails">docrails</a>
and push the change yourself. That branch of Rails has public write access.
Commits are still reviewed, but that happens after you've submitted your
contribution. <a href="https://github.com/lifo/docrails">docrails</a> is
cross-merged with master periodically.
</p>
<p>
You may also find incomplete content, or stuff that is not up to date.
Please do add any missing documentation for master. Check the
<a href="ruby_on_rails_guides_guidelines.html">Ruby on Rails Guides Guidelines</a>
for style and conventions.
</p>
<p>
Issues may also be reported in <a href="https://github.com/lifo/docrails/issues">Github</a>.
</p>
<p>And last but not least, any kind of discussion regarding Ruby on Rails
documentation is very welcome in the <a href="http://groups.google.com/group/rubyonrails-docs">rubyonrails-docs mailing list</a>.
</p>
</div>
</div>
</div>
<hr class="hide" />
<div id="footer">
<div class="wrapper">
<p>This work is licensed under a <a href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-Share Alike 3.0</a> License</p>
<p>"Rails", "Ruby on Rails", and the Rails logo are trademarks of David Heinemeier Hansson. All rights reserved.</p>
</div>
</div>
<script type="text/javascript" src="javascripts/guides.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shCore.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushRuby.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushXml.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushSql.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushPlain.js"></script>
<script type="text/javascript">
SyntaxHighlighter.all()
</script>
</body>
</html>