SOY NV AI commited on
Commit
ff774af
ยท
1 Parent(s): f40b5cd

Add DB schema check and migration scripts for tags column

Browse files
check_tags_table_schema.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """uploaded_file ํ…Œ์ด๋ธ”์˜ tags ์ปฌ๋Ÿผ ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ ๋ฐ ์ˆ˜์ •"""
4
+ import sys
5
+ import os
6
+ from pathlib import Path
7
+
8
+ # ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ๋ฅผ Python ๊ฒฝ๋กœ์— ์ถ”๊ฐ€
9
+ project_root = Path(__file__).parent
10
+ sys.path.insert(0, str(project_root))
11
+
12
+ from app import create_app
13
+ from app.database import db
14
+ from sqlalchemy import inspect, text
15
+
16
+ def check_and_fix_tags_column():
17
+ """uploaded_file ํ…Œ์ด๋ธ”์— tags ์ปฌ๋Ÿผ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์—†์œผ๋ฉด ์ถ”๊ฐ€"""
18
+ app = create_app()
19
+
20
+ with app.app_context():
21
+ try:
22
+ print("\n" + "="*80)
23
+ print("๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์ •๋ณด:")
24
+ print("="*80)
25
+ print(f"DB URI: {app.config.get('SQLALCHEMY_DATABASE_URI', 'N/A')}")
26
+
27
+ # DB ์—”์ง„ ๊ฐ€์ ธ์˜ค๊ธฐ
28
+ engine = db.engine
29
+
30
+ # Inspector๋กœ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ ํ™•์ธ
31
+ inspector = inspect(engine)
32
+
33
+ # uploaded_file ํ…Œ์ด๋ธ”์ด ์žˆ๋Š”์ง€ ํ™•์ธ
34
+ tables = inspector.get_table_names()
35
+ print(f"\n๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ” ๋ชฉ๋ก ({len(tables)}๊ฐœ):")
36
+ for table in sorted(tables):
37
+ print(f" - {table}")
38
+
39
+ if 'uploaded_file' not in tables:
40
+ print("\nโŒ uploaded_file ํ…Œ์ด๋ธ”์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!")
41
+ print(" ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค...")
42
+ db.create_all()
43
+ print("โœ… ํ…Œ์ด๋ธ” ์ƒ์„ฑ ์™„๋ฃŒ")
44
+ return
45
+
46
+ # uploaded_file ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ ํ™•์ธ
47
+ columns = inspector.get_columns('uploaded_file')
48
+ column_names = [col['name'] for col in columns]
49
+
50
+ print(f"\n{'='*80}")
51
+ print(f"uploaded_file ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ๋ชฉ๋ก ({len(columns)}๊ฐœ):")
52
+ print('='*80)
53
+ for col in columns:
54
+ nullable = col.get('nullable', True)
55
+ nullable_str = "NULL ํ—ˆ์šฉ" if nullable else "NOT NULL"
56
+ print(f" - {col['name']}: {col['type']} ({nullable_str})")
57
+
58
+ # tags ์ปฌ๋Ÿผ ํ™•์ธ
59
+ if 'tags' in column_names:
60
+ print(f"\nโœ… tags ์ปฌ๋Ÿผ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค!")
61
+
62
+ # tags ์ปฌ๋Ÿผ์˜ ์ƒ์„ธ ์ •๋ณด
63
+ tags_col = next((col for col in columns if col['name'] == 'tags'), None)
64
+ if tags_col:
65
+ print(f" ํƒ€์ž…: {tags_col['type']}")
66
+ print(f" Nullable: {tags_col.get('nullable', True)}")
67
+
68
+ # ์‹ค์ œ ๋ฐ์ดํ„ฐ ํ™•์ธ
69
+ print(f"\n{'='*80}")
70
+ print("ํƒœ๊ทธ ๋ฐ์ดํ„ฐ ํ™•์ธ:")
71
+ print('='*80)
72
+
73
+ result = db.session.execute(text("SELECT COUNT(*) FROM uploaded_file"))
74
+ total_files = result.scalar()
75
+ print(f"์ „์ฒด ํŒŒ์ผ ์ˆ˜: {total_files}")
76
+
77
+ result = db.session.execute(text("""
78
+ SELECT COUNT(*) FROM uploaded_file
79
+ WHERE tags IS NOT NULL AND tags != ''
80
+ """))
81
+ files_with_tags = result.scalar()
82
+ print(f"ํƒœ๊ทธ๊ฐ€ ์žˆ๋Š” ํŒŒ์ผ ์ˆ˜: {files_with_tags}")
83
+
84
+ if files_with_tags > 0:
85
+ result = db.session.execute(text("""
86
+ SELECT id, original_filename,
87
+ LENGTH(tags) as tag_length,
88
+ SUBSTR(tags, 1, 100) as tag_preview
89
+ FROM uploaded_file
90
+ WHERE tags IS NOT NULL AND tags != ''
91
+ ORDER BY id DESC
92
+ LIMIT 5
93
+ """))
94
+
95
+ print(f"\n์ตœ๊ทผ ํƒœ๊ทธ๊ฐ€ ์žˆ๋Š” ํŒŒ์ผ (์ตœ๋Œ€ 5๊ฐœ):")
96
+ for row in result:
97
+ print(f" - ID: {row[0]}, ํŒŒ์ผ๋ช…: {row[1]}")
98
+ print(f" ํƒœ๊ทธ ๊ธธ์ด: {row[2]} ๋ฌธ์ž")
99
+ print(f" ํƒœ๊ทธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ: {row[3]}...")
100
+ else:
101
+ print("\nโš ๏ธ ํƒœ๊ทธ๊ฐ€ ์žˆ๋Š” ํŒŒ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค.")
102
+ print(" '์ผ๋ฐ˜ ํƒœ๊ทธ ์ƒ์„ฑ' ๋˜๋Š” '์ƒ์„ธ ํƒœ๊ทธ ์ƒ์„ฑ'์„ ์‹คํ–‰ํ•œ ํ›„ ๋‹ค์‹œ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")
103
+
104
+ else:
105
+ print(f"\nโŒ tags ์ปฌ๋Ÿผ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!")
106
+ print(" ์ปฌ๋Ÿผ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค...")
107
+
108
+ # SQLite์ธ ๊ฒฝ์šฐ
109
+ if 'sqlite' in str(engine.url):
110
+ print(" SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ์ง€")
111
+ db.session.execute(text("ALTER TABLE uploaded_file ADD COLUMN tags TEXT"))
112
+ db.session.commit()
113
+ print("โœ… tags ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ์™„๋ฃŒ (SQLite)")
114
+ else:
115
+ # PostgreSQL, MySQL ๋“ฑ ๋‹ค๋ฅธ DB
116
+ print(f" {engine.url.drivername} ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ์ง€")
117
+ try:
118
+ db.session.execute(text("ALTER TABLE uploaded_file ADD COLUMN tags TEXT"))
119
+ db.session.commit()
120
+ print("โœ… tags ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ์™„๋ฃŒ")
121
+ except Exception as e:
122
+ print(f"โŒ ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ์‹คํŒจ: {e}")
123
+ db.session.rollback()
124
+ raise
125
+
126
+ # ๋‹ค์‹œ ํ™•์ธ
127
+ inspector = inspect(engine)
128
+ columns = inspector.get_columns('uploaded_file')
129
+ column_names = [col['name'] for col in columns]
130
+ if 'tags' in column_names:
131
+ print("โœ… ํ™•์ธ: tags ์ปฌ๋Ÿผ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")
132
+ else:
133
+ print("โŒ ๊ฒฝ๊ณ : ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ํ›„์—๋„ ํ™•์ธ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!")
134
+
135
+ print("\n" + "="*80)
136
+ print("ํ™•์ธ ์™„๋ฃŒ")
137
+ print("="*80)
138
+
139
+ except Exception as e:
140
+ print(f"\nโŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
141
+ import traceback
142
+ traceback.print_exc()
143
+ db.session.rollback()
144
+ sys.exit(1)
145
+
146
+ if __name__ == '__main__':
147
+ print("="*80)
148
+ print("uploaded_file ํ…Œ์ด๋ธ” tags ์ปฌ๋Ÿผ ํ™•์ธ ๋ฐ ์ˆ˜์ •")
149
+ print("="*80)
150
+ check_and_fix_tags_column()
151
+
migrate_add_tags_column.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ uploaded_file ํ…Œ์ด๋ธ”์— tags ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์Šคํฌ๋ฆฝํŠธ
5
+ ์‹ค์„œ๋ฒ„์—์„œ ์‹คํ–‰ํ•˜์—ฌ tags ์ปฌ๋Ÿผ์ด ์—†์œผ๋ฉด ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
6
+ """
7
+ import sys
8
+ import os
9
+ from pathlib import Path
10
+
11
+ # ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ๋ฅผ Python ๊ฒฝ๋กœ์— ์ถ”๊ฐ€
12
+ project_root = Path(__file__).parent
13
+ sys.path.insert(0, str(project_root))
14
+
15
+ from app import create_app
16
+ from app.database import db
17
+ from sqlalchemy import inspect, text
18
+
19
+ def migrate_add_tags_column():
20
+ """uploaded_file ํ…Œ์ด๋ธ”์— tags ์ปฌ๋Ÿผ ์ถ”๊ฐ€"""
21
+ app = create_app()
22
+
23
+ with app.app_context():
24
+ try:
25
+ print("="*80)
26
+ print("uploaded_file ํ…Œ์ด๋ธ” tags ์ปฌ๋Ÿผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜")
27
+ print("="*80)
28
+
29
+ # DB ์—”์ง„ ๊ฐ€์ ธ์˜ค๊ธฐ
30
+ engine = db.engine
31
+ print(f"\n๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค: {engine.url}")
32
+
33
+ # Inspector๋กœ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ ํ™•์ธ
34
+ inspector = inspect(engine)
35
+
36
+ # uploaded_file ํ…Œ์ด๋ธ”์ด ์žˆ๋Š”์ง€ ํ™•์ธ
37
+ tables = inspector.get_table_names()
38
+ if 'uploaded_file' not in tables:
39
+ print("โŒ uploaded_file ํ…Œ์ด๋ธ”์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!")
40
+ print(" ๋จผ์ € ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜์—ฌ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜์„ธ์š”.")
41
+ return False
42
+
43
+ # uploaded_file ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ ํ™•์ธ
44
+ columns = inspector.get_columns('uploaded_file')
45
+ column_names = [col['name'] for col in columns]
46
+
47
+ print(f"\nํ˜„์žฌ ์ปฌ๋Ÿผ ๋ชฉ๋ก: {', '.join(column_names)}")
48
+
49
+ # tags ์ปฌ๋Ÿผ ํ™•์ธ
50
+ if 'tags' in column_names:
51
+ print("\nโœ… tags ์ปฌ๋Ÿผ์ด ์ด๋ฏธ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค!")
52
+ print(" ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
53
+ return True
54
+
55
+ # tags ์ปฌ๋Ÿผ ์ถ”๊ฐ€
56
+ print("\nโš ๏ธ tags ์ปฌ๋Ÿผ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค...")
57
+
58
+ # SQLite์ธ ๊ฒฝ์šฐ
59
+ if 'sqlite' in str(engine.url):
60
+ print(" SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ์ง€")
61
+ db.session.execute(text("ALTER TABLE uploaded_file ADD COLUMN tags TEXT"))
62
+ db.session.commit()
63
+ print("โœ… tags ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ์™„๋ฃŒ (SQLite)")
64
+ else:
65
+ # PostgreSQL, MySQL ๋“ฑ ๋‹ค๋ฅธ DB
66
+ db_type = engine.url.drivername
67
+ print(f" {db_type} ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ์ง€")
68
+ try:
69
+ if 'postgresql' in db_type or 'postgres' in db_type:
70
+ # PostgreSQL
71
+ db.session.execute(text("ALTER TABLE uploaded_file ADD COLUMN IF NOT EXISTS tags TEXT"))
72
+ elif 'mysql' in db_type:
73
+ # MySQL
74
+ db.session.execute(text("ALTER TABLE uploaded_file ADD COLUMN tags TEXT"))
75
+ else:
76
+ # ๊ธฐํƒ€
77
+ db.session.execute(text("ALTER TABLE uploaded_file ADD COLUMN tags TEXT"))
78
+
79
+ db.session.commit()
80
+ print("โœ… tags ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ์™„๋ฃŒ")
81
+ except Exception as e:
82
+ print(f"โŒ ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ์‹คํŒจ: {e}")
83
+ db.session.rollback()
84
+ import traceback
85
+ traceback.print_exc()
86
+ return False
87
+
88
+ # ๋‹ค์‹œ ํ™•์ธ
89
+ inspector = inspect(engine)
90
+ columns = inspector.get_columns('uploaded_file')
91
+ column_names = [col['name'] for col in columns]
92
+
93
+ if 'tags' in column_names:
94
+ print("\nโœ… ํ™•์ธ: tags ์ปฌ๋Ÿผ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")
95
+ print(" ์ด์ œ '์ผ๋ฐ˜ ํƒœ๊ทธ ์ƒ์„ฑ'๊ณผ '์ƒ์„ธ ํƒœ๊ทธ ์ƒ์„ฑ' ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
96
+ return True
97
+ else:
98
+ print("\nโŒ ๊ฒฝ๊ณ : ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ํ›„์—๋„ ํ™•์ธ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!")
99
+ return False
100
+
101
+ except Exception as e:
102
+ print(f"\nโŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
103
+ import traceback
104
+ traceback.print_exc()
105
+ db.session.rollback()
106
+ return False
107
+
108
+ if __name__ == '__main__':
109
+ success = migrate_add_tags_column()
110
+ sys.exit(0 if success else 1)
111
+
ํƒœ๊ทธ_์ƒ์„ฑ_DB_์ €์žฅ_๋ฌธ์ œ_ํ•ด๊ฒฐ.md ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ํƒœ๊ทธ ์ƒ์„ฑ DB ์ €์žฅ ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ฐ€์ด๋“œ
2
+
3
+ ## ๋ฌธ์ œ ์ƒํ™ฉ
4
+ ์‹ค์„œ๋ฒ„์—์„œ '์ผ๋ฐ˜ ํƒœ๊ทธ ์ƒ์„ฑ'๊ณผ '์ƒ์„ธ ํƒœ๊ทธ ์ƒ์„ฑ'์„ ์‹คํ–‰ํ–ˆ๋Š”๋ฐ ๊ฒฐ๊ณผ ๊ฐ’์ด DB์— ์ €์žฅ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ
5
+
6
+ ## ์›์ธ ๋ถ„์„
7
+ ๊ฐ€์žฅ ๊ฐ€๋Šฅ์„ฑ ๋†’์€ ์›์ธ: **`uploaded_file` ํ…Œ์ด๋ธ”์— `tags` ์ปฌ๋Ÿผ์ด ์—†์Œ**
8
+
9
+ ## ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
10
+
11
+ ### 1๋‹จ๊ณ„: DB ์Šคํ‚ค๋งˆ ํ™•์ธ
12
+
13
+ ์‹ค์„œ๋ฒ„์—์„œ ๋‹ค์Œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ `tags` ์ปฌ๋Ÿผ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค:
14
+
15
+ ```bash
16
+ python check_tags_table_schema.py
17
+ ```
18
+
19
+ ๋˜๋Š” ๊ฐ€์ƒํ™˜๊ฒฝ ์‚ฌ์šฉ ์‹œ:
20
+ ```bash
21
+ .\venv\Scripts\python.exe check_tags_table_schema.py
22
+ ```
23
+
24
+ ### 2๋‹จ๊ณ„: tags ์ปฌ๋Ÿผ ์ถ”๊ฐ€ (ํ•„์š”ํ•œ ๊ฒฝ์šฐ)
25
+
26
+ `tags` ์ปฌ๋Ÿผ์ด ์—†๋‹ค๋ฉด ๋‹ค์Œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค:
27
+
28
+ ```bash
29
+ python migrate_add_tags_column.py
30
+ ```
31
+
32
+ ๋˜๋Š” ๊ฐ€์ƒํ™˜๊ฒฝ ์‚ฌ์šฉ ์‹œ:
33
+ ```bash
34
+ .\venv\Scripts\python.exe migrate_add_tags_column.py
35
+ ```
36
+
37
+ ### 3๋‹จ๊ณ„: ํ™•์ธ
38
+
39
+ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ›„ ๋‹ค์‹œ ํ™•์ธ:
40
+
41
+ ```bash
42
+ python check_tags_table_schema.py
43
+ ```
44
+
45
+ `tags` ์ปฌ๋Ÿผ์ด ์ถ”๊ฐ€๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ํƒœ๊ทธ ์ƒ์„ฑ ๊ธฐ๋Šฅ์„ ๋‹ค์‹œ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.
46
+
47
+ ## ์ถ”๊ฐ€ ํ™•์ธ ์‚ฌํ•ญ
48
+
49
+ ### 1. ์„œ๋ฒ„ ๋กœ๊ทธ ํ™•์ธ
50
+ ํƒœ๊ทธ ์ƒ์„ฑ ์‹คํ–‰ ์‹œ ์„œ๋ฒ„ ๋กœ๊ทธ์—์„œ ๋‹ค์Œ ๋ฉ”์‹œ์ง€๋ฅผ ํ™•์ธํ•˜์„ธ์š”:
51
+
52
+ - `[์ผ๋ฐ˜ ํƒœ๊ทธ ์ƒ์„ฑ] DB ์ €์žฅ ์„ฑ๊ณต` ๋˜๋Š” `[์ƒ์„ธ ํƒœ๊ทธ ์ƒ์„ฑ] DB ์ €์žฅ ์„ฑ๊ณต`
53
+ - `[์ผ๋ฐ˜ ํƒœ๊ทธ ์ƒ์„ฑ] ๊ฒฝ๊ณ : DB ์ €์žฅ ํ›„ ํƒœ๊ทธ๊ฐ€ None์ž…๋‹ˆ๋‹ค!` (์ด ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜์˜ค๋ฉด ๋ฌธ์ œ ์žˆ์Œ)
54
+
55
+ ### 2. DB ์ง์ ‘ ํ™•์ธ
56
+ ์‹ค์„œ๋ฒ„ DB์— ์ง์ ‘ ์ ‘์†ํ•˜์—ฌ ํ™•์ธ:
57
+
58
+ ```sql
59
+ -- ์ปฌ๋Ÿผ ํ™•์ธ
60
+ PRAGMA table_info(uploaded_file); -- SQLite
61
+ -- ๋˜๋Š”
62
+ SELECT column_name, data_type
63
+ FROM information_schema.columns
64
+ WHERE table_name = 'uploaded_file'; -- PostgreSQL
65
+
66
+ -- ํƒœ๊ทธ ๋ฐ์ดํ„ฐ ํ™•์ธ
67
+ SELECT id, original_filename,
68
+ CASE WHEN tags IS NULL THEN 'NULL'
69
+ WHEN tags = '' THEN 'EMPTY'
70
+ ELSE 'HAS DATA' END as tag_status,
71
+ LENGTH(tags) as tag_length
72
+ FROM uploaded_file
73
+ ORDER BY id DESC
74
+ LIMIT 10;
75
+ ```
76
+
77
+ ### 3. ์ฝ”๋“œ ํ™•์ธ
78
+ ํƒœ๊ทธ ์ƒ์„ฑ ํ•จ์ˆ˜๋Š” ๋‹ค์Œ ์œ„์น˜์— ์žˆ์Šต๋‹ˆ๋‹ค:
79
+ - ์ผ๋ฐ˜ ํƒœ๊ทธ: `app/routes.py` - `generate_simple_tags()` ํ•จ์ˆ˜ (๋ผ์ธ 5301)
80
+ - ์ƒ์„ธ ํƒœ๊ทธ: `app/routes.py` - `generate_detailed_tags()` ํ•จ์ˆ˜ (๋ผ์ธ 4952)
81
+
82
+ ์ €์žฅ ๋กœ์ง:
83
+ ```python
84
+ tags_json_str = json.dumps(tags_data_with_type, ensure_ascii=False)
85
+ file.tags = tags_json_str
86
+ db.session.commit()
87
+ db.session.refresh(file)
88
+ ```
89
+
90
+ ## ์˜ˆ์ƒ๋˜๋Š” ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ์ฑ…
91
+
92
+ ### ๋ฌธ์ œ 1: tags ์ปฌ๋Ÿผ์ด ์—†์Œ
93
+ **์ฆ์ƒ**: ํƒœ๊ทธ ์ƒ์„ฑ์€ ์„ฑ๊ณตํ•˜์ง€๋งŒ DB์— ์ €์žฅ๋˜์ง€ ์•Š์Œ
94
+ **ํ•ด๊ฒฐ**: `migrate_add_tags_column.py` ์‹คํ–‰
95
+
96
+ ### ๋ฌธ์ œ 2: DB ์—ฐ๊ฒฐ ๋ฌธ์ œ
97
+ **์ฆ์ƒ**: ํƒœ๊ทธ ์ƒ์„ฑ ์‹œ ์—๋Ÿฌ ๋ฐœ์ƒ
98
+ **ํ•ด๊ฒฐ**: DB ์—ฐ๊ฒฐ ์„ค์ • ํ™•์ธ (`.env` ํŒŒ์ผ์˜ `DATABASE_URL`)
99
+
100
+ ### ๋ฌธ์ œ 3: ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ
101
+ **์ฆ์ƒ**: ์ €์žฅ์€ ๋˜์ง€๋งŒ ๋ฐ”๋กœ ์‚ฌ๋ผ์ง
102
+ **ํ•ด๊ฒฐ**: ์„œ๋ฒ„ ๋กœ๊ทธ์—์„œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ํ™•์ธ, `db.session.rollback()` ํ˜ธ์ถœ ์—ฌ๋ถ€ ํ™•์ธ
103
+
104
+ ### ๋ฌธ์ œ 4: JSON ํŒŒ์‹ฑ ์‹คํŒจ
105
+ **์ฆ์ƒ**: AI ์‘๋‹ต์€ ๋ฐ›์•˜์ง€๋งŒ ์ €์žฅ ์‹คํŒจ
106
+ **ํ•ด๊ฒฐ**: ์„œ๋ฒ„ ๋กœ๊ทธ์—์„œ JSON ํŒŒ์‹ฑ ์—๋Ÿฌ ํ™•์ธ, AI ๋ชจ๋ธ ์‘๋‹ต ํ˜•์‹ ํ™•์ธ
107
+
108
+ ## ์‹ค์„œ๋ฒ„์—์„œ ์‹คํ–‰ ๋ฐฉ๋ฒ•
109
+
110
+ ### SSH ์ ‘์† ํ›„
111
+ ```bash
112
+ # ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์ด๋™
113
+ cd /path/to/project
114
+
115
+ # ๊ฐ€์ƒํ™˜๊ฒฝ ํ™œ์„ฑํ™” (์žˆ๋Š” ๊ฒฝ์šฐ)
116
+ source venv/bin/activate # Linux/Mac
117
+ # ๋˜๋Š”
118
+ .\venv\Scripts\activate # Windows
119
+
120
+ # ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰
121
+ python check_tags_table_schema.py
122
+ python migrate_add_tags_column.py
123
+ ```
124
+
125
+ ### Docker ํ™˜๊ฒฝ์ธ ๊ฒฝ์šฐ
126
+ ```bash
127
+ # ์ปจํ…Œ์ด๋„ˆ ์ ‘์†
128
+ docker exec -it <container_name> bash
129
+
130
+ # ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰
131
+ python check_tags_table_schema.py
132
+ python migrate_add_tags_column.py
133
+ ```
134
+
135
+ ## ์ฐธ๊ณ  ํŒŒ์ผ
136
+ - `check_tags_table_schema.py`: DB ์Šคํ‚ค๋งˆ ํ™•์ธ ์Šคํฌ๋ฆฝํŠธ
137
+ - `migrate_add_tags_column.py`: tags ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜
138
+ - `app/routes.py`: ํƒœ๊ทธ ์ƒ์„ฑ ํ•จ์ˆ˜ (๋ผ์ธ 4952, 5301)
139
+ - `app/database.py`: UploadedFile ๋ชจ๋ธ ์ •์˜ (๋ผ์ธ 39-72)
140
+