# Drawing Module API ๋ฌธ์„œ ## ๐Ÿ“‹ ๊ฐœ์š” Drawing Module์€ ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋…๋ฆฝ์ ์ธ Canvas ๊ธฐ๋ฐ˜ ๊ทธ๋ฆฌ๊ธฐ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ๋ชจ๋‹ฌ ํ˜•ํƒœ์˜ UI๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, ๋‹ค์–‘ํ•œ ๊ทธ๋ฆฌ๊ธฐ ๊ธฐ๋Šฅ๊ณผ ์ด๋ฏธ์ง€ ํŽธ์ง‘ ๊ธฐ๋Šฅ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ## ๐Ÿš€ ๋น ๋ฅธ ์‹œ์ž‘ ### 1. ํŒŒ์ผ ํฌํ•จ ```html ``` ### 2. ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ• ```javascript // ๊ธฐ๋ณธ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ const drawer = new DrawingModule({ container: 'body', onSave: (data) => { console.log('์ €์žฅ๋œ ์ด๋ฏธ์ง€:', data.imageData); console.log('์ €์žฅ ์‹œ๊ฐ„:', data.timestamp); } }); // ๊ทธ๋ฆฌ๊ธฐ ๋„๊ตฌ ํ‘œ์‹œ drawer.show(); ``` ## ๐Ÿ“Š API ์ฐธ์กฐ ### ์ƒ์„ฑ์ž (Constructor) ```javascript new DrawingModule(options) ``` #### ์˜ต์…˜ ๋งค๊ฐœ๋ณ€์ˆ˜ | ์˜ต์…˜ | ํƒ€์ž… | ๊ธฐ๋ณธ๊ฐ’ | ์„ค๋ช… | |------|------|--------|------| | `container` | string | 'body' | ๋ชจ๋‹ฌ์„ ์ƒ์„ฑํ•  ์ปจํ…Œ์ด๋„ˆ ์„ ํƒ์ž | | `width` | number | 800 | ๋ชจ๋‹ฌ์˜ ์ „์ฒด ๋„ˆ๋น„ (px) | | `height` | number | 600 | ๋ชจ๋‹ฌ์˜ ์ „์ฒด ๋†’์ด (px) | | `canvasWidth` | number | 320 | ์บ”๋ฒ„์Šค์˜ ์‹ค์ œ ๋„ˆ๋น„ (px) | | `canvasHeight` | number | 240 | ์บ”๋ฒ„์Šค์˜ ์‹ค์ œ ๋†’์ด (px) | | `title` | string | '๊ทธ๋ฆฌ๊ธฐ ๋„๊ตฌ' | ๋ชจ๋‹ฌ ํ—ค๋” ์ œ๋ชฉ | | `initialImage` | string | null | ์ดˆ๊ธฐ์— ๋กœ๋“œํ•  ์ด๋ฏธ์ง€ URL ๋˜๋Š” ๋ฐ์ดํ„ฐ URL | | `onSave` | function | null | ์ €์žฅ ์‹œ ํ˜ธ์ถœ๋˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜ | | `onCancel` | function | null | ์ทจ์†Œ ์‹œ ํ˜ธ์ถœ๋˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜ | #### ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜ **onSave ์ฝœ๋ฐฑ:** ```javascript onSave: (data) => { // data.imageData - base64 ์ธ์ฝ”๋”ฉ๋œ ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ // data.timestamp - ์ €์žฅ ์‹œ๊ฐ (Date ๊ฐ์ฒด) // data.width - ์บ”๋ฒ„์Šค ๋„ˆ๋น„ // data.height - ์บ”๋ฒ„์Šค ๋†’์ด } ``` **onCancel ์ฝœ๋ฐฑ:** ```javascript onCancel: () => { // ์‚ฌ์šฉ์ž๊ฐ€ ์ทจ์†Œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ํ˜ธ์ถœ } ``` ### ๋ฉ”์„œ๋“œ (Methods) #### show() ๊ทธ๋ฆฌ๊ธฐ ๋„๊ตฌ ๋ชจ๋‹ฌ์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ```javascript drawer.show(); ``` #### hide() ๊ทธ๋ฆฌ๊ธฐ ๋„๊ตฌ ๋ชจ๋‹ฌ์„ ์ˆจ๊น๋‹ˆ๋‹ค. ```javascript drawer.hide(); ``` #### loadImage(imageUrl) ์บ”๋ฒ„์Šค์— ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ```javascript drawer.loadImage('path/to/image.png'); // ๋˜๋Š” ๋ฐ์ดํ„ฐ URL drawer.loadImage('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='); ``` #### clear() ์บ”๋ฒ„์Šค์˜ ๋ชจ๋“  ๋‚ด์šฉ์„ ์ง€์›๋‹ˆ๋‹ค. ```javascript drawer.clear(); ``` #### getImageData() ํ˜„์žฌ ์บ”๋ฒ„์Šค์˜ ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ```javascript const imageData = drawer.getImageData(); console.log(imageData); // base64 ๋ฌธ์ž์—ด ``` ## ๐ŸŽจ ์ฃผ์š” ๊ธฐ๋Šฅ ### ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋“œ - **์ ์—ฐ๊ฒฐ (Polyline)**: ํด๋ฆญํ•œ ์ ๋“ค์„ ์—ฐ๊ฒฐํ•˜์—ฌ ์„ ์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค - **์ง์„  (Line)**: ์‹œ์ž‘์ ๊ณผ ๋์ ์„ ํด๋ฆญํ•˜์—ฌ ์ง์„ ์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค - **์ž์œ ์„  (Free)**: ๋งˆ์šฐ์Šค๋ฅผ ๋“œ๋ž˜๊ทธํ•˜์—ฌ ์ž์œ ๋กญ๊ฒŒ ์„ ์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค ### ์ถ”๊ฐ€ ๊ธฐ๋Šฅ - **์ง๊ฐ ๋ชจ๋“œ**: ์ˆ˜ํ‰/์ˆ˜์ง์„ ๋งŒ ๊ทธ๋ฆฌ๊ธฐ (์ฒดํฌ๋ฐ•์Šค๋กœ ํ™œ์„ฑํ™”) - **์ƒ‰์ƒ ์„ ํƒ**: 16์ง„์ˆ˜ ์ปฌ๋Ÿฌ ํ”ผ์ปค๋กœ ์„  ์ƒ‰์ƒ ๋ณ€๊ฒฝ - **์„  ๊ตต๊ธฐ ์กฐ์ ˆ**: 1-10px ๋ฒ”์œ„์˜ ์Šฌ๋ผ์ด๋”๋กœ ์„  ๊ตต๊ธฐ ์กฐ์ • - **์ง€์šฐ๊ฐœ**: ์›ํ˜• ์ง€์šฐ๊ฐœ๋กœ ํŠน์ • ์˜์—ญ ์‚ญ์ œ (ํฌ๊ธฐ ์กฐ์ ˆ ๊ฐ€๋Šฅ) - **ํ…์ŠคํŠธ ์ถ”๊ฐ€**: ํด๋ฆญํ•œ ์œ„์น˜์— ํ…์ŠคํŠธ ์ž…๋ ฅ - **์‹คํ–‰์ทจ์†Œ (Undo)**: ๋งˆ์ง€๋ง‰ ์ž‘์—… ๋˜๋Œ๋ฆฌ๊ธฐ - **์ดˆ๊ธฐํ™”**: ์บ”๋ฒ„์Šค์˜ ๋ชจ๋“  ๋‚ด์šฉ ์‚ญ์ œ - **ํ„ฐ์น˜ ์ง€์›**: ๋ชจ๋ฐ”์ผ/ํƒœ๋ธ”๋ฆฟ ๋””๋ฐ”์ด์Šค ํ„ฐ์น˜ ์ด๋ฒคํŠธ ์ง€์› ## ๐Ÿ“ฑ ์‚ฌ์šฉ ์˜ˆ์ œ ### 1. ๊ธฐ๋ณธ ๊ทธ๋ฆฌ๊ธฐ ๋„๊ตฌ ```javascript const basicDrawer = new DrawingModule({ container: 'body', onSave: (data) => { // ์ด๋ฏธ์ง€๋ฅผ ๋กœ์ปฌ ์ €์žฅ์†Œ์— ์ €์žฅ localStorage.setItem('drawing', data.imageData); alert('๊ทธ๋ฆฌ๊ธฐ๊ฐ€ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!'); } }); basicDrawer.show(); ``` ### 2. ์ปค์Šคํ…€ ํฌ๊ธฐ ์บ”๋ฒ„์Šค ```javascript const largeDrawer = new DrawingModule({ container: '#myContainer', canvasWidth: 640, canvasHeight: 480, title: 'ํฐ ์บ”๋ฒ„์Šค ๊ทธ๋ฆฌ๊ธฐ', onSave: (data) => { console.log(`์บ”๋ฒ„์Šค ํฌ๊ธฐ: ${data.width}x${data.height}`); downloadImage(data.imageData, 'large-drawing.png'); } }); largeDrawer.show(); ``` ### 3. ์ด๋ฏธ์ง€ ํŽธ์ง‘ ```javascript const imageEditor = new DrawingModule({ container: 'body', initialImage: '/path/to/existing-image.jpg', title: '์ด๋ฏธ์ง€ ํŽธ์ง‘', onSave: (data) => { // ํŽธ์ง‘๋œ ์ด๋ฏธ์ง€๋ฅผ ์„œ๋ฒ„์— ์—…๋กœ๋“œ uploadImageToServer(data.imageData); }, onCancel: () => { console.log('์ด๋ฏธ์ง€ ํŽธ์ง‘์ด ์ทจ์†Œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'); } }); imageEditor.show(); ``` ### 4. ์„œ๋ฒ„๋กœ ์ด๋ฏธ์ง€ ์ €์žฅ ```javascript const drawer = new DrawingModule({ container: 'body', onSave: (data) => { // AJAX๋กœ ์„œ๋ฒ„์— ์ด๋ฏธ์ง€ ์ „์†ก $.ajax({ url: '/api/save-drawing', method: 'POST', data: { image: data.imageData, timestamp: data.timestamp.toISOString(), width: data.width, height: data.height }, success: function(response) { alert('์„œ๋ฒ„์— ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!'); console.log('์ €์žฅ๋œ ์ด๋ฏธ์ง€ ID:', response.imageId); }, error: function(error) { alert('์ €์žฅ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค: ' + error.responseText); } }); } }); drawer.show(); ``` ### 5. ๋‹ค์ค‘ ์ธ์Šคํ„ด์Šค ```javascript // ๋™์‹œ์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๊ทธ๋ฆฌ๊ธฐ ๋„๊ตฌ ์‚ฌ์šฉ const drawer1 = new DrawingModule({ container: 'body', title: '๊ทธ๋ฆฌ๊ธฐ 1', canvasWidth: 200, canvasHeight: 200, onSave: (data) => console.log('Drawing 1 saved') }); const drawer2 = new DrawingModule({ container: 'body', title: '๊ทธ๋ฆฌ๊ธฐ 2', canvasWidth: 300, canvasHeight: 200, onSave: (data) => console.log('Drawing 2 saved') }); drawer1.show(); setTimeout(() => drawer2.show(), 100); // ์•ฝ๊ฐ„์˜ ์ง€์—ฐ ``` ## ๐Ÿ› ๏ธ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜ ### ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ ํ•จ์ˆ˜ ```javascript function downloadImage(dataUrl, filename) { const link = document.createElement('a'); link.download = filename; link.href = dataUrl; link.click(); } // ์‚ฌ์šฉ๋ฒ• const drawer = new DrawingModule({ onSave: (data) => { downloadImage(data.imageData, 'my-drawing.png'); } }); ``` ### Base64๋ฅผ Blob์œผ๋กœ ๋ณ€ํ™˜ ```javascript function dataURLtoBlob(dataURL) { const arr = dataURL.split(','); const mime = arr[0].match(/:(.*?);/)[1]; const bstr = atob(arr[1]); let n = bstr.length; const u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], {type: mime}); } // ์‚ฌ์šฉ๋ฒ• const drawer = new DrawingModule({ onSave: (data) => { const blob = dataURLtoBlob(data.imageData); const formData = new FormData(); formData.append('image', blob, 'drawing.png'); // FormData๋ฅผ ์„œ๋ฒ„์— ์ „์†ก fetch('/upload', { method: 'POST', body: formData }); } }); ``` ## ๐ŸŽฏ ํŒŒ์ผ ๊ตฌ์กฐ ``` project/ โ”œโ”€โ”€ test_drawing.php # ํ…Œ์ŠคํŠธ ํŽ˜์ด์ง€ (PHP) โ”œโ”€โ”€ css/ โ”‚ โ””โ”€โ”€ drawingModule.css # ์Šคํƒ€์ผ์‹œํŠธ โ”œโ”€โ”€ js/ โ”‚ โ””โ”€โ”€ drawingModule.js # ๋ฉ”์ธ JavaScript ๋ชจ๋“ˆ โ””โ”€โ”€ drawModule.md # ์ด ๋ฌธ์„œ ``` ## ๐Ÿ”ง ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ### CSS ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ์ฃผ์š” CSS ํด๋ž˜์Šค๋“ค์„ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜์—ฌ ์Šคํƒ€์ผ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: ```css /* ๋ชจ๋‹ฌ ๋ฐฐ๊ฒฝ์ƒ‰ ๋ณ€๊ฒฝ */ .dm-modal { background-color: rgba(0, 0, 0, 0.8); } /* ๋ฒ„ํŠผ ์ƒ‰์ƒ ๋ณ€๊ฒฝ */ .dm-btn-primary { background-color: #custom-color; } /* ์บ”๋ฒ„์Šค ํ…Œ๋‘๋ฆฌ ์Šคํƒ€์ผ ๋ณ€๊ฒฝ */ #dmCanvas { border: 3px solid #custom-border-color; } ``` ### JavaScript ์ด๋ฒคํŠธ ํ›… ๋ชจ๋“ˆ์˜ ๋™์ž‘์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•˜๋ ค๋ฉด ์ฝœ๋ฐฑ์„ ์‚ฌ์šฉํ•˜์„ธ์š”: ```javascript const drawer = new DrawingModule({ onSave: (data) => { // ์ €์žฅ ์ „ ๊ฒ€์ฆ if (data.width < 100 || data.height < 100) { alert('๊ทธ๋ฆผ์ด ๋„ˆ๋ฌด ์ž‘์Šต๋‹ˆ๋‹ค!'); return; } // ์ปค์Šคํ…€ ์ €์žฅ ๋กœ์ง customSaveFunction(data); }, onCancel: () => { // ์ทจ์†Œ ์‹œ ์ •๋ฆฌ ์ž‘์—… cleanupResources(); } }); ``` ## ๐Ÿšจ ์ฃผ์˜์‚ฌํ•ญ 1. **jQuery ์˜์กด์„ฑ**: ์ด ๋ชจ๋“ˆ์€ jQuery์— ์˜์กดํ•˜๋ฏ€๋กœ jQuery๋ฅผ ๋จผ์ € ๋กœ๋“œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 2. **CSS ํŒŒ์ผ ํ•„์ˆ˜**: `drawingModule.css` ํŒŒ์ผ์ด ์—†์œผ๋ฉด UI๊ฐ€ ์ œ๋Œ€๋กœ ํ‘œ์‹œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. 3. **๋ธŒ๋ผ์šฐ์ € ํ˜ธํ™˜์„ฑ**: HTML5 Canvas๋ฅผ ์ง€์›ํ•˜๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ๋งŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. 4. **์ด๋ฏธ์ง€ ํฌ๊ธฐ ์ œํ•œ**: ๋งค์šฐ ํฐ ์บ”๋ฒ„์Šค๋Š” ๋ธŒ๋ผ์šฐ์ € ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 5. **๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ**: ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์ ์ ˆํžˆ ์ •๋ฆฌํ•˜์„ธ์š”. ## ๐Ÿ› ๋ฌธ์ œ ํ•ด๊ฒฐ ### ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ๋“ค **Q: ๋ชจ๋‹ฌ์ด ํ‘œ์‹œ๋˜์ง€ ์•Š์•„์š”.** A: CSS ํŒŒ์ผ์ด ์ œ๋Œ€๋กœ ๋กœ๋“œ๋˜์—ˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  `show()` ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ–ˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. **Q: ๊ทธ๋ฆฌ๊ธฐ๊ฐ€ ์ž‘๋™ํ•˜์ง€ ์•Š์•„์š”.** A: jQuery๊ฐ€ ๋กœ๋“œ๋˜์—ˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  JavaScript ์ฝ˜์†”์— ์˜ค๋ฅ˜๊ฐ€ ์—†๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. **Q: ์ด๋ฏธ์ง€ ์ €์žฅ์ด ์•ˆ ๋ผ์š”.** A: `onSave` ์ฝœ๋ฐฑ์ด ์ œ๋Œ€๋กœ ์„ค์ •๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. **Q: ๋ชจ๋ฐ”์ผ์—์„œ ํ„ฐ์น˜๊ฐ€ ์ž‘๋™ํ•˜์ง€ ์•Š์•„์š”.** A: ์ตœ์‹  ๋ฒ„์ „์˜ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. ํ„ฐ์น˜ ์ด๋ฒคํŠธ๋Š” ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ## ๐Ÿ“„ ๋ผ์ด์„ ์Šค ์ด ๋ชจ๋“ˆ์€ MIT ๋ผ์ด์„ ์Šค ํ•˜์— ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. ## ๐Ÿ”„ ์—…๋ฐ์ดํŠธ ๋‚ด์—ญ - **v1.0.0**: ์ดˆ๊ธฐ ๋ฆด๋ฆฌ์Šค - ๊ธฐ๋ณธ ๊ทธ๋ฆฌ๊ธฐ ๊ธฐ๋Šฅ - ๋ชจ๋‹ฌ UI - ์ด๋ฏธ์ง€ ๋กœ๋“œ/์ €์žฅ - ํ„ฐ์น˜ ์ง€์›