프로젝트

일반

사용자정보

개정판 d4ca9bd3

IDd4ca9bd34004491b72500f8be6af4f3b15d18d2d
상위 5ddf3ffe
하위 340d0023

김연진이(가) 5년 이상 전에 추가함

issue #1043 : 시스템분석/설계 : Template.db 파일에 Drawings 테이블 삭제

Change-Id: I333d73e2469012e00a1c6a01a4ec30bf4a83b3d0

차이점 보기:

HYTOS/HYTOS/App.py
24 24
        import locale
25 25
        from AppDocData import AppDocData
26 26

  
27

  
27 28
        super(App, self).__init__(args)
28 29
        app_doc_data = AppDocData.instance()
29 30
        app_style = app_doc_data.loadAppStyle()
HYTOS/HYTOS/AppDocData.py
64 64
class AppDocData(SingletonInstane):
65 65
    
66 66
    def __init__(self):
67
        from DisplayColors import DisplayColors
68

  
67
        
69 68
        self._imgFilePath = None
70 69
        self.imgName = None
71 70
        self.imgWidth = 0
......
167 166

  
168 167
            # Creates or opens a file called mydb with a SQLite3 DB
169 168
            conn = sqlite3.connect(appDatabaseFilePath)
169
            conn.execute('PRAGMA foreign_keys = ON')
170 170
            with conn:
171 171
                # Get a cursor object
172 172
                cursor = conn.cursor()
......
217 217
        res = []
218 218

  
219 219
        conn = sqlite3.connect(self.activeDrawing.path)
220
        conn.execute('PRAGMA foreign_keys = ON')
220 221
        with conn:
221 222
            try:
222 223
                conn.row_factory = sqlite3.Row
......
239 240
    def getInsideDiameter(self, nominaldiameter_uid, schedule_uid):
240 241
        res = []
241 242
        try:
242
            db = sqlite3.connect(self.activeDrawing.path)
243
            conn = sqlite3.connect(self.activeDrawing.path)
244
            conn.execute('PRAGMA foreign_keys = ON')
243 245
            # Get a cursor object
244
            cursor = db.cursor()
246
            cursor = conn.cursor()
245 247

  
246 248
            sql = """select UID
247 249
                          , Milimeter
......
258 260
        # Catch the exception
259 261
        except Exception as ex:
260 262
            # Roll back any change if something goes wrong
261
            db.rollback()
263
            conn.rollback()
262 264
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
263 265
        finally:
264 266
            # Close the db connection
265
            db.close()
267
            conn.close()
266 268

  
267 269
        return res
268 270

  
......
270 272
        res = []
271 273

  
272 274
        conn = sqlite3.connect(self.activeDrawing.path)
275
        conn.execute('PRAGMA foreign_keys = ON')
273 276
        with conn:
274 277
            try:
275 278
                # Get a cursor object
......
293 296

  
294 297
        # Creates or opens a file called mydb with a SQLite3 DB
295 298
        conn = sqlite3.connect(self.activeDrawing.path)
299
        conn.execute('PRAGMA foreign_keys = ON')
296 300
        with conn:
297 301
            try:
298 302
                # Get a cursor object
......
325 329
            # Creates or opens a file called mydb with a SQLite3 DB
326 330
            dbPath = self.getAppDbPath()
327 331
            conn = sqlite3.connect(dbPath)
332
            conn.execute('PRAGMA foreign_keys = ON')
328 333
            # Get a cursor object
329 334
            cursor = conn.cursor()
330 335

  
......
362 367
            # Creates or opens a file called mydb with a SQLite3 DB
363 368
            dbPath = self.getAppDbPath()
364 369
            conn = sqlite3.connect(dbPath)
370
            conn.execute('PRAGMA foreign_keys = ON')
365 371
            # Get a cursor object
366 372
            cursor = conn.cursor()
367 373

  
......
389 395
        try:
390 396
            # Creates or opens a file called mydb with a SQLite3 DB
391 397
            conn = sqlite3.connect(self.activeDrawing.path)
398
            conn.execute('PRAGMA foreign_keys = ON')
392 399
            # Get a cursor object
393 400
            cursor = conn.cursor()
394 401

  
395
            # 0. Delete Points
396
            sql = "delete from Points where Components_UID in (select UID from Components where Drawings_UID = '{}')".format(uid)
397
            cursor.execute(sql)
398

  
399
            # 1. Delete HMB
400
            sql = "delete from HMB where Components_UID in (select UID from Components where Drawings_UID = '{}')".format(uid)
401
            cursor.execute(sql)
402

  
403
            # 2. Delete Components
404
            sql = "delete from Components where Drawings_UID='{}'".format(uid)
402
            sql = "delete from Components"
405 403
            cursor.execute(sql)            
406 404
            
407 405
            conn.commit()
......
417 415
    def deleteDrawingByName(self, drawingName):
418 416
        """ delete given drawing """
419 417
        conn = sqlite3.connect(self.getAppDbPath())
418
        conn.execute('PRAGMA foreign_keys = ON')
420 419
        with conn:
421 420
            try:
422 421
                # Get a cursor object
......
445 444
            # Creates or opens a file called mydb with a SQLite3 DB
446 445
            dbPath = self.getAppDbPath()
447 446
            conn = sqlite3.connect(dbPath)
447
            conn.execute('PRAGMA foreign_keys = ON')
448 448
            # Get a cursor object
449 449
            cursor = conn.cursor()
450 450

  
......
472 472
        ret = None
473 473

  
474 474
        conn = sqlite3.connect(self.activeDrawing.path)
475
        conn.execute('PRAGMA foreign_keys = ON')
475 476
        with conn:
476 477
            cursor = conn.cursor()
477 478

  
......
502 503
        ret = []
503 504

  
504 505
        conn = sqlite3.connect(self.activeDrawing.path)
506
        conn.execute('PRAGMA foreign_keys = ON')
505 507
        with conn:
506 508
            cursor = conn.cursor()
507 509
            
......
535 537
        try:
536 538
            # Creates or opens a file called mydb with a SQLite3 DB
537 539
            dbPath = os.path.join(self.activeDrawing.path)
538
            db = sqlite3.connect(dbPath)
539
            db.row_factory = sqlite3.Row
540
            conn = sqlite3.connect(dbPath)
541
            conn.execute('PRAGMA foreign_keys = ON')
542
            conn.row_factory = sqlite3.Row
540 543
            # Get a cursor object
541
            cursor = db.cursor()
544
            cursor = conn.cursor()
542 545

  
543 546
            sql = """select UID
544 547
                          , Method
......
558 561
        # Catch the exception
559 562
        except Exception as ex:
560 563
            # Roll back any change if something goes wrong
561
            db.rollback()
564
            conn.rollback()
562 565
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
563 566
        finally:
564 567
            # Close the db connection
565
            db.close()
568
            conn.close()
566 569

  
567 570
        return res
568 571

  
......
574 577
    def saveToDatabase(self, item, index):
575 578
        """ save given items to database """
576 579
        conn = sqlite3.connect(self.activeDrawing.path, isolation_level=None)
580
        conn.execute('PRAGMA foreign_keys = ON')
577 581
        with conn:
578 582
            try:
579 583
                # Get a cursor object
580 584
                cursor = conn.cursor()
581 585

  
582 586
                if index == 0:
583
                    # delete Points
584
                    sql = 'delete from Points'
585
                    cursor.execute(sql)
586

  
587
                    # delete HMB
588
                    sql = 'delete from HMB'
589
                    cursor.execute(sql)
590

  
591 587
                    # delete Components 
592 588
                    sql = 'delete from Components'
593 589
                    cursor.execute(sql)
......
626 622
        # Creates or opens a file called mydb with a SQLite3 DB
627 623
        dbPath = self.getAppDbPath()
628 624
        conn = sqlite3.connect(dbPath)
625
        conn.execute('PRAGMA foreign_keys = ON')
629 626
        with conn:
630 627
            try:
631 628
                conn.row_factory = sqlite3.Row
......
636 633
                cursor.execute(sql)
637 634
                rows = cursor.fetchall()
638 635
                for row in rows:
639
                    res.append(Drawing(row[0], row[1], row[2]))
636
                    if os.path.exists(row[1]):
637
                        res.append(Drawing(row[0], row[1], row[2]))
640 638
            # Catch the exception
641 639
            except Exception as ex:
642 640
                # Roll back any change if something goes wrong
......
659 657
        # Creates or opens a file called mydb with a SQLite3 DB
660 658
        dbPath = self.getAppDbPath()
661 659
        conn = sqlite3.connect(dbPath)
660
        conn.execute('PRAGMA foreign_keys = ON')
662 661
        with conn:
663 662
            try:
664 663
                # Get a cursor object
......
692 691
        from shutil import copyfile       
693 692

  
694 693
        conn = sqlite3.connect(self.getAppDbPath())
694
        conn.execute('PRAGMA foreign_keys = ON')
695 695
        with conn:
696 696
            try:
697 697
                # Get a cursor object
......
722 722
        SymbolCategoryList = []
723 723

  
724 724
        conn = sqlite3.connect(self.activeDrawing.path)
725
        conn.execute('PRAGMA foreign_keys = ON')
725 726
        with conn:
726 727
            cursor = conn.cursor()
727 728
            sql = 'SELECT DISTINCT CATEGORY FROM SymbolType ORDER BY type ASC'
......
743 744
        ComponentList = []
744 745

  
745 746
        conn = sqlite3.connect(self.activeDrawing.path)
747
        conn.execute('PRAGMA foreign_keys = ON')
746 748
        with conn:
747 749
            try:
748 750
                conn.row_factory = sqlite3.Row
......
795 797

  
796 798
        res = None
797 799
        conn = sqlite3.connect(self.activeDrawing.path)
800
        conn.execute('PRAGMA foreign_keys = ON')
798 801
        with conn:
799 802
            conn.row_factory = sqlite3.Row
800 803
            cursor = conn.cursor()
......
819 822
        unitsList = []
820 823

  
821 824
        conn = sqlite3.connect(drawing.path)
825
        conn.execute('PRAGMA foreign_keys = ON')
822 826
        with conn:
823 827
            cursor = conn.cursor()
824 828

  
......
847 851
        ComponentList = []
848 852

  
849 853
        conn = sqlite3.connect(drawing.path)
854
        conn.execute('PRAGMA foreign_keys = ON')
850 855
        with conn:
851 856
            try:
852 857
                conn.row_factory = sqlite3.Row
......
873 878
        symbolTypeList = []
874 879

  
875 880
        conn = sqlite3.connect(self.activeDrawing.path)
881
        conn.execute('PRAGMA foreign_keys = ON')
876 882
        with conn:
877 883
            cursor = conn.cursor()
878 884
            sql = 'SELECT UID, Category, Type FROM SymbolType WHERE Category = "' + category + '"'
......
894 900

  
895 901
        try:            
896 902
            conn = sqlite3.connect(self.activeDrawing.path)
903
            conn.execute('PRAGMA foreign_keys = ON')
897 904
            cursor = conn.cursor()
898 905
            sql = 'Select UID, Key, Value From Units'
899 906
            try:
......
917 924

  
918 925
        if self.activeDrawing:
919 926
            conn = sqlite3.connect(self.activeDrawing.path)
927
            conn.execute('PRAGMA foreign_keys = ON')
920 928
            with conn:
921 929
                cursor = conn.cursor()
922 930
                sql = """SELECT dn.DISPLAY_NAME
......
948 956
        category = None
949 957

  
950 958
        conn = sqlite3.connect(self.activeDrawing.path)
959
        conn.execute('PRAGMA foreign_keys = ON')
951 960
        with conn:
952 961
            try:
953 962
                cursor = conn.cursor()
HYTOS/HYTOS/ConfigurationDialog.py
10 10
from App import App
11 11
from AppDocData import AppDocData
12 12
from AppDocData import Config
13
from AppDocData import Color
14 13
import Configuration_UI
15 14

  
16 15
class QConfigurationDialog(QDialog):
HYTOS/HYTOS/Fitting_2KDialog.py
10 10
from App import App
11 11
from AppDocData import AppDocData
12 12
from AppDocData import Config
13
from AppDocData import Color
14 13
import Fitting_2K_UI
15 14

  
16 15
class QFitting_2KDialog(QDialog):
HYTOS/HYTOS/Fitting_CraneKDialog.py
10 10
from App import App
11 11
from AppDocData import AppDocData
12 12
from AppDocData import Config
13
from AppDocData import Color
14 13
import Fitting_CraneK_UI
15 14

  
16 15
class QFitting_CraneKDialog(QDialog):
HYTOS/HYTOS/Fitting_EquivalentLengthDialog.py
9 9
from App import App
10 10
from AppDocData import AppDocData
11 11
from AppDocData import Config
12
from AppDocData import Color
13 12
import Fitting_EquivalentLength_UI
14 13

  
15 14
class QFitting_EquivalentLengthDialog(QDialog):
HYTOS/HYTOS/HMBTable.py
437 437
            self._hmbs = []
438 438

  
439 439
            conn = sqlite3.connect(drawing.path)
440
            conn.execute('PRAGMA foreign_keys = ON')
440 441
            with conn:
441 442
                try:
442 443
                    cursor = conn.cursor()
......
501 502

  
502 503
        app_doc_data = AppDocData.instance()
503 504
        conn = sqlite3.connect(app_doc_data.activeDrawing.path)
505
        conn.execute('PRAGMA foreign_keys = ON')
504 506
        with conn:
505 507
            try:
506 508
                # Get a cursor object
HYTOS/HYTOS/MainWindow.py
1
# coding: utf-8
1
# -*- coding: utf-8 -*-
2 2
""" This is MainWindow module """
3 3

  
4 4
import sys
......
41 41
import SymbolEditorDialog
42 42
from UserInputAttribute import UserInputAttribute
43 43
from TextItemFactory import TextItemFactory
44
from DisplayColors import DisplayColors
45
from DisplayColors import DisplayOptions
46 44
import uuid
47 45

  
48 46
class MainWindow(QMainWindow, MainWindow_UI.Ui_MainWindow, SingletonInstane):
......
66 64
        try:
67 65
            super(self.__class__, self).__init__()
68 66
            self.setupUi(self)
67

  
69 68
            self._label_mouse = QLabel(self.statusbar)
70 69
            self._label_mouse.setText(self.tr('mouse pos : ({},{})'.format(0,0)))
71 70
            self.statusbar.addWidget(self._label_mouse)
......
144 143
            action.triggered.connect(partial(self.load_language, file))
145 144
        # up to here
146 145

  
147
    def setMainWindowTitle(self, drawingName = None):   
146
    def setMainWindowTitle(self, drawingName = None):
147
        
148
        #         
148 149
        _translate = QCoreApplication.translate
149 150
             
150 151
        version = QCoreApplication.applicationVersion()
151 152
        if drawingName is None:
152 153
            self.setWindowTitle(_translate(App.NAME  + "({})".format(version), App.NAME + "({})".format(version)))
153 154
        else:
155
            #drawingName = drawingName.encode('utf-8')            
154 156
            self.setWindowTitle(_translate(App.NAME  + "({}) - {}".format(version, drawingName), App.NAME + "({}) - {}".format(version, drawingName)))
155 157

  
156 158

  
......
174 176
                newDrawingAction.triggered.connect(lambda: self.newDrawingActionClickEvent(item))
175 177
                menu.addAction(newDrawingAction)
176 178
            elif level == 1:            
177
                saveAsDrawingAction = menu.addAction(self.tr("Save As..."))
178
                saveAsDrawingAction.triggered.connect(lambda: self.saveAsDrawingActionClickEvent(item))
179
                menu.addAction(saveAsDrawingAction)
179
                #saveAsDrawingAction = menu.addAction(self.tr("Save As..."))
180
                #saveAsDrawingAction.triggered.connect(lambda: self.saveAsDrawingActionClickEvent(item))
181
                #menu.addAction(saveAsDrawingAction)
180 182

  
181 183
                deleteDrawingAction = menu.addAction(self.tr("Delete"))
182 184
                deleteDrawingAction.triggered.connect(lambda: self.deleteDrawingActionClickEvent(item))
......
211 213
                app_doc_data = AppDocData.instance()
212 214
                app_doc_data.deleteDrawingByName(drawingPath)
213 215

  
214
                if drawingName == app_doc_data.activeDrawing.name and drawingPath == app_doc_data.activeDrawing.path:
215
                    app_doc_data.activeDrawing = None
216
                if app_doc_data.activeDrawing:
217
                    if drawingName == app_doc_data.activeDrawing.name and drawingPath == app_doc_data.activeDrawing.path:
218
                        app_doc_data.activeDrawing = None
216 219

  
217
                    if self.graphicsView.hasImage():
218
                        self.graphicsView.clearImage()
219
                        self.graphicsView.scene.clear()
220
                        if self.graphicsView.hasImage():
221
                            self.graphicsView.clearImage()
222
                            self.graphicsView.scene.clear()
220 223

  
221
                    self.initTableWidgetHMB()
224
                        self.initTableWidgetHMB()
222 225

  
223 226
                self.load_drawing_list()                            
224 227
        except Exception as ex:
......
407 410
            self.open_drawing(path)
408 411

  
409 412

  
410
    def dbUpdate(self):
411
        '''
412
            @brief      db update when save or recognition
413
            @author     euisung
414
            @date       2018.11.12
415
            @history    2018.11.02      euisung     remove scene dependency
416
        '''
417
        from AppDocData import AppDocData
418

  
419
        try:
420
            appDocData = AppDocData.instance()
421

  
422
            titleBlockProps = appDocData.getTitleBlockProperties()
423
            #items = self.graphicsView.scene.items()
424
            items = appDocData.activeDrawing.allItems
425
            titleBlockItems = []
426
            for item in items:
427
                #if type(item) is QEngineeringLineNoTextItem:
428
                #    item.saveLineData()
429
                if type(item) is QEngineeringTextItem:
430
                    for titleBlockProp in titleBlockProps:
431
                        if item.area == titleBlockProp[0]:
432
                            titleBlockItems.append(item)
433

  
434
            dbItems = [item for item in items if type(item) is QEngineeringInstrumentItem or type(item) is QEngineeringEquipmentItem or type(item) is QEngineeringReducerItem or\
435
            type(item) is QEngineeringNoteItem or type(item) is SymbolSvgItem or type(item) is QEngineeringLineNoTextItem] + titleBlockItems
436
            appDocData.saveToDatabase(dbItems)
437
        except Exception as ex:
438
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
439
            self.addMessage.emit(MessageType.Error, message)
440

  
441 413
    '''
442 414
        @brief      action save click event
443 415
        @author     kyouho
HYTOS/HYTOS/OptionsDialog.py
14 14
from App import App
15 15
from AppDocData import AppDocData
16 16
from AppDocData import Config
17
from AppDocData import Color
18 17
import Options_UI
19 18

  
20 19
class QOptionsDialog(QDialog):
HYTOS/HYTOS/RoughnessDialog.py
10 10
from App import App
11 11
from AppDocData import AppDocData
12 12
from AppDocData import Config
13
from AppDocData import Color
14 13
import Roughness_UI
15 14

  
16 15
class QRoughnessDialog(QDialog):
HYTOS/HYTOS/Scripts/CreateTables.sql
1 1
CREATE TABLE IF NOT EXISTS Components (
2 2
    UID          TEXT NOT NULL,
3
    Drawings_UID TEXT NOT NULL,
4 3
    Symbols_UID  TEXT NOT NULL,
5 4
    Name         TEXT,
6 5
    X            REAL,
......
9 8
    Scale        REAL DEFAULT (1),
10 9
    CONSTRAINT PK_Components PRIMARY KEY (
11 10
        UID
12
    ),
13
    CONSTRAINT FK_Drawings_UID FOREIGN KEY (
14
        Drawings_UID
15
    )
16
    REFERENCES Drawings (UID),
11
    ),    
17 12
    CONSTRAINT FK_Symbols_UID FOREIGN KEY (
18 13
        Symbols_UID
19 14
    )
......
50 45
    )
51 46
);
52 47

  
53
CREATE TABLE IF NOT EXISTS Drawings (
54
    UID      TEXT NOT NULL,
55
    NAME     TEXT NOT NULL,
56
    DATETIME TEXT NOT NULL,
57
    CONSTRAINT PK_Drawings PRIMARY KEY (
58
        UID
59
    ),
60
    CONSTRAINT UQ_Drawings UNIQUE (
61
        NAME
62
    )
63
);
64 48

  
65 49
CREATE TABLE IF NOT EXISTS DrawingsUnits (
66 50
    UID          TEXT NOT NULL,
67
    Drawings_UID TEXT NOT NULL,
68 51
    Units        TEXT NOT NULL,
69 52
    Units_UID    TEXT NOT NULL,
70 53
    CONSTRAINT PK_DrawingsUnits PRIMARY KEY (
71 54
        UID
72
    ),
73
    CONSTRAINT FK_Drawings_UID FOREIGN KEY (
74
        Drawings_UID
75
    )
76
    REFERENCES Drawings (UID),
55
    ),    
77 56
    CONSTRAINT FK_Units_UID FOREIGN KEY (
78 57
        Units_UID
79 58
    )
......
127 106
    CONSTRAINT FK_Components_UID FOREIGN KEY (
128 107
        Components_UID
129 108
    )
130
    REFERENCES Components (UID) 
109
    REFERENCES Components (UID) ON DELETE CASCADE
131 110
);
132 111

  
133 112
CREATE TABLE IF NOT EXISTS Fittings_CraneK (
......
217 196
    CONSTRAINT FK_Components_UID FOREIGN KEY (
218 197
        Components_UID
219 198
    )
220
    REFERENCES Components (UID) 
199
    REFERENCES Components (UID) ON DELETE CASCADE
221 200
);
222 201

  
223 202
CREATE TABLE IF NOT EXISTS Fittings_EquivalentLength (
......
242 221
    CONSTRAINT FK_Components_UID FOREIGN KEY (
243 222
        Components_UID
244 223
    )
245
    REFERENCES Components (UID) 
224
    REFERENCES Components (UID) ON DELETE CASCADE
246 225
);
247 226

  
248 227
CREATE TABLE IF NOT EXISTS HMB (
......
280 259
    CONSTRAINT FK_Components_UID FOREIGN KEY (
281 260
        Components_UID
282 261
    )
283
    REFERENCES Components (UID),
262
    REFERENCES Components (UID) ON DELETE CASCADE,
284 263
    CONSTRAINT UQ_HMB UNIQUE (
285 264
        Components_UID,
286 265
        Stream_No
......
358 337
    CONSTRAINT FK_Components_UID FOREIGN KEY (
359 338
        Components_UID
360 339
    )
361
    REFERENCES Components (UID) 
340
    REFERENCES Components (UID) ON DELETE CASCADE
362 341
);
363 342

  
364 343
CREATE TABLE IF NOT EXISTS Roughness (
HYTOS/HYTOS/Shapes/EngineeringStreamlineItem.py
410 410
        dbUid = symbolInfo.uid
411 411
        uid = self.uid
412 412

  
413
        cols = ['UID', 'Drawings_UID', 'Symbols_UID']
414
        values = ['?','?','?']
415
        param = [str(uid), str(appDocData.activeDrawing.UID), str(dbUid)]
413
        cols = ['UID', 'Symbols_UID']
414
        values = ['?','?']
415
        param = [str(uid), str(dbUid)]
416 416
        sql = 'insert or replace into Components({}) values({})'.format(','.join(cols), ','.join(values))
417 417
        res.append((sql, tuple(param)))
418 418

  
HYTOS/HYTOS/Shapes/SymbolSvgItem.py
203 203
        
204 204
        rect = self.sceneBoundingRect()
205 205

  
206
        cols = ['UID', 'Drawings_UID', 'Symbols_UID', 'X', 'Y', 'Rotation', 'Scale']
207
        values = ['?','?','?', '?', '?', '?', '?']
208
        param = [str(self.uid), str(appDocData.activeDrawing.UID), str(self.dbUid), rect.left(), rect.top(), str(self.angle), self.transform().m11()]
206
        cols = ['UID', 'Symbols_UID', 'X', 'Y', 'Rotation', 'Scale']
207
        values = ['?','?', '?', '?', '?', '?']
208
        param = [str(self.uid), str(self.dbUid), rect.left(), rect.top(), str(self.angle), self.transform().m11()]
209 209
        sql = 'insert or replace into Components({}) values({})'.format(','.join(cols), ','.join(values))
210 210
        res.append((sql, tuple(param)))
211 211
        

내보내기 Unified diff

클립보드 이미지 추가 (최대 크기: 500 MB)