๐ Documentation ๐ npm ๐ GitHub
6์ ํ ๋ฌ๊ฐ ๋ฉ๋์คํธ๋ฆผ์์ ๊ธฐ์
ํ์
์ผ๋ก ์ฌ์งํธ์งํด ๊ฐ๋ฐ์ ์งํํ๋ค. ์ฌ์ง์ ๋ํ ๊ธฐ๋ฅ๋ค์ ๋ด์ ๋ชจ๋์ core js
๋ก, ๊ธฐ๋ฅ๋ค์ ์ฌ์ฉํ UI๋ vue.js
๋ก ๊ฐ๋ฐํ์ผ๋ฉฐ ์คํ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก npm ํจํค์งํ์ฌ ๋ฐฐํฌ๊น์ง ์๋ฃํ๋ค. ๊ธฐ์ต๋ ฅ์ด ์ ์ข์ ๋ ์ค์ค๋ก๋ฅผ ์ํด ๊ฐ๋ฐ ํ ๋ฌ ๋์์ ์ด์ผ๊ธฐ๋ฅผ ์ ๋ฆฌํ๊ณ ์ ํ๋ค. ์ด๊ฑด ๋ด๊ฐ ํฌ๋จธ์นํ ์ปค๋ผ ๊ธ์ด ์งฑ์งฑ ๊ธธ์ด์ง ๊ฒ์ ๋ํ ๋ฐ๋ฐฅ์ด๋ค.
๋ชฉ์ฐจ
- ํ๋ก์ ํธ ๊ฐ์
- ๐จ Canvas API
- ๐ฉ๐ปโ๐ป ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฆฌ์์น
- ๐ง ์คํฐ์ปค ๊ธฐ๋ฅ ๊ตฌํ์ ๋ํ ๊ณ ๋ฏผ
- ๐ณ UI ๊ตฌ์กฐ
- ๐ ๊ธฐ๋ฅ ํจ์ ๋ง๋ค๊ธฐ
- ๐ฅ Blocker
- โก npm ๋ฐฐํฌ์ ์นํฉ ์ค์
- ๐ฉ๐ปโ๐ ๋ฐฐ์ด์
- ๋ง๋ฌด๋ฆฌํ๋ฉฐ
๐ ํ๋ก์ ํธ ๊ฐ์
- ๊ตฌํ ๊ธฐ๋ฅ : ์์ ํฌ๋กญ, ๋น์จ ํฌ๋กญ, ํ์ , ๋ฐ์ , ํ๋/์ถ์, ์คํฐ์ปค ์ถ๊ฐ, ๊ฒฐ๊ณผ๋ฌผ png ์ด๋ฏธ์ง ๊ฐ์ฒด๋ก ๋ฐํ
- ์ฌ์ฉ ์คํ :
core js
ES6,vue.js
2.x - ์ฌ์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ :
cropper.js
1.5.11,fabric.js
4.5
ํ์ฌ ํ๋ก๋ํธ์ ๊ธ์ฐ๊ธฐ ๊ธฐ๋ฅ์์ ์ฌ์ฉํ ์ฌ์งํธ์งํด์ด ํ์ํด ๋จ๋ ๋ชจ๋๋ก ๊ฐ๋ฐ์ ์งํํ๋ค. ์ด์์ด๋ฉด ํ์ฌ์ ๋ธ๋๋ฉ์๋ ๋์์ด ๋๊ณ ์ ํ์ฌ์ ์บ๋ฆญํฐ๋ก ์คํฐ์ปค๋ฅผ ์ถ๊ฐํ ์ ์๊ฒ๋ ํ๋ค.
๋ฑ ์ฌ์ง ํธ์ง์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ํ ๊ธฐ๋ฅ๋ค๋ง ์ปดํฉํธํ๊ฒ ๋ด๊ณ , ์คํฐ์ปค๋ฅผ ์ถ๊ฐํ ์ ์๋ ๊ธฐ๋ฅ๊น์ง ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐพ์๋ณด์์ผ๋ ๋ง๋
ํ ๊ฒ์ด ์์๋ค. ์คํฐ์ปค ๊ธฐ๋ฅ์ด ์์ผ๋ฉด ๋ค๋ฅธ ๋ถํ์ํ ๊ธฐ๋ฅ๋ค๋ ๋ง์ด ์๊ณ , ๋ฑ ์ปดํฉํธํ๊ฒ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ธฐ๋ฅ๋ง ์๋ค ์ถ์ผ๋ฉด ์คํฐ์ปค ๊ธฐ๋ฅ์ด ์์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์ ๋ง๋ ๊ฒ์ ์คํ ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋ฐฐํฌํ๊ธฐ๋ก ํ๊ณ ์ด๋ฅผ ํตํด ํ์ ์๋ฌด ์๊ฐ์์ด npm install
ํ๋ ๊ฒ์์๋ ๋ฒ์ด๋๋ณด๊ธฐ๋ก ํ๋ค.
๐จ Canvas API
HTML 5์ Canvas API๋ ๊ทธ๋ฆผ, ์ด๋ฏธ์ง ๋ฟ ์๋๋ผ ๋์์๊ณผ ๊ฐ์ ๋ฏธ๋์ด๋ฅผ ๋ค๋ฃฐ ์ ์๊ฒ ๋์์ฃผ๋ API์ด๋ค. ํ๋ คํ ๊ทธ๋ํฝ์ด๋ ๊ฒ์์ ๋ง๋๋ ๋ฑ Canvas API๋ฅผ ๊ฐ์ง๊ณ ํ ์ ์๋ ๊ฒ์ ๋ฌด๊ถ๋ฌด์งํ์ง๋ง ์ด๋ณด(=๋)๋ฅผ ์ํ ํ๋ก์ ํธ๋ก๋ ๊ทธ๋ฆผํ ๋ง๋ค๊ธฐ ๋ฑ์ด ์๋ค ^0^.
์ฐ๋ฆฌ๋ ์ ์ ๊ฐ ์
๋ก๋ํ๋ ์ฌ์ง, ์ฆ ์ด๋ฏธ์ง๋ฅผ ๋ค๋ฃจ์ด์ผ ํ๊ธฐ ๋๋ฌธ์ Canvas API๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํ์ผ๋ ์น ๊ฐ๋ฐ์ด๋์ ๋ ๋ค๋ฅธ ๋ณ์ฒ์ง ์ธ๊ณ๋ผ๊ณ ํด์ Canvas API๋ฅผ ์ฝ๊ฒ ๋ค๋ฃจ๊ฒ ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํ๋ค. (๋๊ตฐ๊ฐ์ ๋ง์ ์ํ๋ฉด ์ด๊ฑธ ์ฐ๋ผ๊ณ ๋ง๋ค์ด ๋ ๊ฒ์ธ์ง ์์ฌ์ด ๋๋ ์ ๋๋ผ๊ณ โฆ๐)
๊ฐ๋ฐ์ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ํ ๋ถ๋ถ์ ํ์ ํ ๊ฒ์ด๊ณ , ์ด ํํธ์์ ๋ ๊ฒ์ Canvas API๋ฅผ ์ฌ์ฉํ ๋ถ๋ถ๋ง ๊ฐ๋จํ ๋์ง์ด ๊ธฐ๋กํด๋ณธ๋ค.
How To Use
์ฐ์ ์บ๋ฒ์ค ์์๋ฅผ ์์ฑํ๋ค. div
๋ img
์ฒ๋ผ <canvas id="canvas"></canvas>
๋ก ๋ง๋ค์ด๋ผ ์ ์๋๋ฐ ๊ธฐ๋ณธ ์ฌ์ด์ฆ๋ 300x150 ํฝ์
์ด๋ค. ์ผ๋ฐ์ ์ธ html ํ๊ทธ๋ค์ฒ๋ผ css๋ก ์กฐ์ ํ ์๋ ์์ง๋ง canvas ํ๊ทธ์ width, height๋ก ์ค์ ํ๊ธธ ๊ถ์ฅํ๋ค. css๋ก width, height๊ฐ์ ๋ณ๊ฒฝํด๋ ๋ ์ด์์์ ํฌ๊ธฐ๋ ๊ทธ๋๋ก๋ผ ๋๋ฌธ์ ๊ทธ๊ฒ์ ๋ง์ถฐ์ ๐์๊ณก๋๋ ํ์์ด ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ด์ ์ด ์บ๋ฒ์ค๋ฅผ ์กฐ์ํ ์ฐจ๋ก~!
์ฐ๋ฆฌ๊ฐ ๊ทธ๋ฆผ์ ๊ทธ๋ฆด ๋ ํฐ ์ค์ผ์น๋ถ ์์ ์์ฐํ๋ก ์ ์ ๊ธ๋ฏ์ด ์บ๋ฒ์ค๋ ๋ง์ฐฌ๊ฐ์ง์ด๋ค. ์บ๋ฒ์ค์์ ํฐ ์ค์ผ์น๋ถ์ Context๋ผ๊ณ ํ๊ณ , getContext()
๋ผ๋ ์บ๋ฒ์ค์ ๋ฉ์๋๋ฅผ ์ด์ฉํด ์ ๊ทผํ๋ค. ๋ค์ด๋ฐ๋ ๊ด์ต์ ์ผ๋ก context
๋ผ๊ณ ํ๊ฑฐ๋ ctx
๋ผ๊ณ ํ๋ค.
๊ทธ๋ฆผ์ ๊ทธ๋ฆด ์์ญ์ ์ง์ ํ์ผ๋ ์ด์ ๊ทธ ์์ ๊ทธ๋ฆด ์ฐจ๋ก์ด๋ค. ๊ฐ์ฅ ๊ธฐ๋ณธ์ด ๋๋ ์ ์ด๋ ์ ์ ๊ทธ๋ฆด ์๋ ์์ง๋ง ์ฐ๋ฆฌ๋ ์ด๋ฏธ์ง๋ฅผ ๋ค๋ฃฐ ๊ฒ์ด๋ฏ๋ก, drawImage()
๋ผ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ค. ์ฒซ ๋ฒ์งธ ์ธ์๋ก๋ ๊ทธ๋ ค๋ผ ์ด๋ฏธ์ง ๊ฐ์ฒด, ๋ ๋ฒ์งธ์ ์ธ ๋ฒ์งธ ์ธ์๋ก๋ ์ขํ๋ฅผ ์ง์ ํ ์ ์๋ค.
๊ทธ๋ผ ์ด๋ฐ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ค.
//add sticker image on canvas
const canvas = document.querySelector('#canvas');
const context = canvas.getContext('2d');
context.drawImage(stickerImage, 0, 0);
๊ทธ๋ฆฌ๊ณ ํด๋น ์บ๋ฒ์ค๋ฅผ ์ด๋ฏธ์ง๋ก ๋ด๋ณด๋ผ ์ ์๋ค. toDataURL
๋ฉ์๋๋ฅผ ์ด์ฉํ๋ฉด data URL๋ฅผ ๋ฐํํด์ค๋ค. ์ฒซ๋ฒ์งธ ์ธ์๋ก ์ด๋ฏธ์ง ํฌ๋งท๋ ์ง์ ํ ์ ์๋๋ฐ ๊ธฐ๋ณธ๊ฐ์ png์ด๋ค. ์ด๋ฅผ ํ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ด๋ฏธ์ง ๊ฐ์ฒด๋ฅผ ๋ฐํํ ์ ์๋ค. (new Image()์ html image element๋ ๋ค๋ฅด๋ค!)
const result = new Image();
result.src = canvas.toDataURL('image/png');
return result;
์ด๋ฐ ์์ ์ ์ ๋ถ ์ค์ผ์น๋ถ ์์ ์ฌ๋ฆฌ๋ ์ด๋ฏธ์ง, ์ , ์ ๋ฑ์ ์์ง์ฌ ํ๋ ๊ฒ์ด๋ผ๊ธฐ๋ณด๋ค ์ค์ผ์น๋ถ์ ์์ง์ฌ ์ ์ ๊ทธ๋ ค๋ด๊ณ ์ด๋ฏธ์ง๋ฅผ ๊ทธ๋ ค๋ด๋ ์์ด๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ง๊ด์ ์ด์ง ์๊ณ ์ด๋ ต๊ฒ ๋ค๊ฐ์จ๋ค. ์ด๋ฐ ๋ถ๋ถ์ ์ข ๋ ์ฝ๊ฒ ์ด์ฉํ๊ฒ ํด์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐพ์๋ณด์๋ค.
๐ฉ๐ปโ๐ป ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฆฌ์์น
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฆฌ์์น ํ๋ ๊ณผ์ ์์ ์ ์ผ ๊ณ ๋ฏผ์ด ๋์๋ ๋ถ๋ถ์ ์ ๋ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์์ ๋ฌด์์ ๊ธฐ์ค์ผ๋ก ์ ํํด์ผํ๋ ์ง์๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฒ์ ์จ๋ด์ ๊ฐ๋ ์ ์๋ค. ๊ทธ๋์ ๊ทธ๋ฅ ์๋ฌด๊ฑฐ๋ ๊ฒ์ํด์ ๋ฅ์น๋ ๋๋ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ํ ๊ธ๋ค์ ์ฝ์๋ค. A๋ผ๋ ์ฌ๋์ ์ด๋ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ํธํ๋๋ฐ ๊ทธ ์ด์ ๋ ๋ฌด์์ธ์ง ๊ฐ์๊ฑฐ? ๊ทธ๋ฐ ๊ฒ์ ๋ํด์ ํ์ ๋ ์ด์ผ๊ธฐ ๋๋ ๊ฒ์ ๋ฐํ์ผ๋ก ์๋์ ๊ฐ์ ์ฒดํฌ๋ฆฌ์คํธ๋ค์ ๋ง๋ค ์ ์์๋ค.
โ
๋ฐฐ๋ณด๋ค ๋ฐฐ๊ผฝ์ด ๋ ํฌ์ง ์์๊ฐ? ํ์ ๊ธฐ๋ฅ๋ง ์ปดํฉํธํ๊ฒ ๋ค์ด์๋๊ฐ?
โ
์ค์ง์ ์ธ ์
๋ฐ์ดํธ๊ฐ ๊พธ์คํ ์ด๋ค์ง๊ณ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ๊ฐ?
โ
๋ฌธ์ํ๊ฐ ์์ด ๋๋ ํ๊ตญ์ด๋ก ์์ธํ๊ฒ ๋์ด์๋๊ฐ?
โ
Core ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๊ฐ๋ฐ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ๊ฐ?
โ
์ํ๊ณ๊ฐ ํ์ฑํ ๋์ด ์๋๊ฐ? ๊นํ ์ด์, ์คํ์ค๋ฒํ๋ก์ฐ ๋ฑ
โ
๋ง์ด ์ฌ์ฉ๋๋๊ฐ? star, npm download ์ ๋ฑ
โ
์ฌ์ฉ์ด ์ด๋ ค์ธ ์ ๋์ ํฌ๋ฆฌํฐ์ปฌํ ์ด์๊ฐ ์๋๊ฐ?
โ
๋ชจ๋ฐ์ผ์ ์ง์ํ๋๊ฐ?
์์ ๊ฐ์ ๊ธฐ์ค๋ค๋ก ๋ฆฌ์์น๋ฅผ ํด๋ณด๋ ์์ธ๋ก ํ๋ณด๊ฐ ๋ง์ง ์์๋ค. ๋ฌผ๋ก crop์ด๋ rotate๊ฐ์ด ๊ธฐ๋ฅ ํ ๊ฐ์ง๋ง ๊ฐ์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊น์ง ํฌํจํ๋ค๋ฉด ํ๋ณด๊ฐ ๋ ๋ง์์ก๊ฒ ์ง๋ง, ์์ธกํ ์ ์๋ ์ฌ์ด๋ ์ดํํธ๋ฅผ ์ต์ํ ํ๊ธฐ ์ํด ์ต์ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ง์ ์ฌ์ฉํ๊ธฐ๋ก ํ๊ธฐ ๋๋ฌธ์ ํ๋ฝ์์ผฐ๋ค.
๊ทธ๋ ๊ฒ ํ์ ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๐cropper.js
์๋ค. 2014๋
์ jquery๋ก ์ฒ์ ๊ฐ๋ฐ๋ ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ ์์๊ฐ ์ง์์ ์ผ๋ก ํ๋ก์
ํ์ฌ ํ์ฌ๋ ์ต์ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฌธ๋ฒ์ผ๋ก ์
๋ฐ์ดํธ ๋์ด์๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐพ๋ค ๋ณด๋ฉด ๋ ๊ฑฐ์ ์ฝ๋๋ฅผ ๋ง์ด ๋ณด๊ฒ ๋๋๋ฐ ๊ทธ๋ ์ง ์์ ์ ์ด ์ธ์์ ์ด์๋ค.
๐ง ์คํฐ์ปค ๊ธฐ๋ฅ ๊ตฌํ์ ๋ํ ๊ณ ๋ฏผ
๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด ์ฌ์ง ํธ์ง์ฉ ์บ๋ฒ์ค์ ์คํฐ์ปค ์ถ๊ฐ์ฉ ์บ๋ฒ์ค๋ฅผ ๋ฐ๋ก ๋์ด ๋ ์บ๋ฒ์ค๋ฅผ ์ค์์นญํ๊ธฐ๋ก ํ๋ค.
์ฌ์ง ํธ์ง์ cropper๋ฅผ ์ด์ฉํด์ ํ ๊ฒ์ด์ง๋ง, ์คํฐ์ปค๋ฅผ ์ถ๊ฐํ๋ ๊ฒ๋ ๊ฒฐ๊ตญ์ ์คํฐ์ปค๋ผ๋ ์ฌ์ง์ ์ถ๊ฐํ๋ ๊ฒ์ด ์๋๋๋ ์ด์ผ๊ธฐ๊ฐ ํ์ ๋ ๋์๋ค. ๋๋ฌธ์ ์คํฐ์ปค๋ ์ง์ ๊ตฌํํ๊ธฐ๋ก ์ด์ผ๊ธฐ๊ฐ ๋๊ณ ์ด๋ฅผ ์ํด ์ฌ๋ฌ ๋ฐฉ๋ฒ์ ๋ฆฌ์์นํด๊ฐ๋ฉฐ 4๊ฐ์ ๋ฐ๋ชจ๋ฅผ ๋ง๋ค์์๋ค. ์์ฃผ์์ฃผ ๋ ๊ฒ์ ๋ฐ๋ชจ๋ฅผ ๋ง๋ค์ด ๋ณด๋ ๊ฒ์ ์์ง์ ๋ฏ์ค๊ธฐ๋ง ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ์ ์ํ ์ ์๋ ๋ฐฉ๋ฒ์ด์๋ค. ํ์ ๋ ์ค๋ช ์ ํ๊ธฐ์๋ ์ง์ ๋ณด๋ฉด์ ์ด์ผ๊ธฐ๋ฅผ ํ ์ ์์ผ๋ ๋ ์ข์๋ค. ๐ ๋ฐ๋ชจ ์ฝ๋์๋๋ฐ์ค ๐ ์ฐธ๊ณ ํ ๋ฐฉ๋ฒ ์ค ํ๋
์คํฐ์ปค ๊ธฐ๋ฅ์ ์ง์ ๊ตฌํํ๋ฉด์ ๋ง์ฃผํ ์์ํ์ง ๋ชปํ๋ ๋ฌธ์ ์ ์ cropper.js
๊ฐ ํ ์บ๋ฒ์ค ์์ ์ฌ๋ฌ ๊ฐ์ ๊ฐ์ฒด๋ฅผ ์ถ๊ฐํ์ง ๋ชปํ๋ค๋ ๊ฒ์ด์๋ค. ๋์ ๋น์ทํ ๋์ฆ๋ฅผ ๊ฐ์ง ์ด๋ค ๊ฐ๋ฐ์๊ฐ ๋ฌธ์๋ฅผ ํ์๋๋ฐ ์ ์์๊ฐ ๋ฏธ์, ๊ทธ๊ฑด ์ง์ ์ ํด
๋ผ๊ณ ๋๋ตํ ๊ฒ์ ๋ณด์๋ค. cropper์ ์ฌ์ง์ ์กฐ์ํ๋ ๊ธฐ๋ฅ์ ์ด์ฉํด์ ์คํฐ์ปค๋ฅผ ์กฐ์ํ๊ณ ์ถ์ ๋์ฆ์ ํ ์บ๋ฒ์ค ์์์ ์ฌ๋ฌ ๊ฐ์ ์คํฐ์ปค๋ฅผ ์กฐ์ํ๊ณ ์ถ์ ๋์ฆ๋ ๊ณต์กดํ ์ ์๋ ๋์ฆ์๋ ๊ฒ์ด๋ค.
์ ๋ง ๋ณ ๊ฒ ์๋์ง๋ง, ๋ด๊ฐ ๋ง๋ค์๋ ๋ฐ๋ชจ๋ ๊ทธ ๋ฌธ์ ์ ํด๊ฒฐ์ ๋ํ ๊ฒ์ด์๋ค. ์คํฐ์ปค ๋น ์บ๋ฒ์ค๋ฅผ ๋ง๋ค๊ณ z-index๋ฅผ ์ค์ ๋ ์ด์ด์ฒ๋ผ ์๊ณ , ์ต์ข ์ ์ฅ ์์๋ ์บ๋ฒ์ค๋ค์ ํฉ์ณ์ ํ๋์ ์ด๋ฏธ์ง๋ฅผ ๋ฐํํ ์ ์์๋ค. ๊ทธ์น๋ง ๊ทธ๋ ๊ฒ ๋๋ฉด ์ฐ๋ฆฌ๊ฐ ์ผ๋ฐ์ ์ผ๋ก ์๊ฐํ๋ ์คํฐ์ปค ๊ธฐ๋ฅ(=ํ ๋ฒ์ ์ฌ๋ฌ ์คํฐ์ปค๋ฅผ ์ถ๊ฐ ๊ฐ๋ฅ)๊ณผ ๋ฉ์ด์ง ๋ฟ๋๋ฌ ์ฌ์ฉ์ ์ ์ฅ์์ ์ฐ๋ค๊ฐ ๋นก์ณ์ ๋ ์ด์ ์ ์ธ ๊ฒ ๊ฐ์๋ค. ์ ๋ฐฉ๋ฒ์ ๊ฒฐ๊ตญ ํ ๋ฒ์ ํ๋์ ์คํฐ์ปค๋ง ์กฐ์์ด ๊ฐ๋ฅํ๊ณ , ๋ ๋ฒ์งธ ์คํฐ์ปค๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ์ฒซ ๋ฒ์งธ ์คํฐ์ปค๋ฅผ ์ฌ์ฉํ ๊ฒ์ ์ ์ฅํ๋ ๊ณผ์ ์ ๊ผญ ๊ฑฐ์ณ์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ฌ๋ฉด cropper๋ฅผ ์ฐ์ง ์๊ณ ์ฉ Canvas๋ฅผ ์จ์ ๊ตฌํํ๋ค๋ฉด?
์คํฐ์ปค๋ฅผ ์บ๋ฒ์ค ์์ ์ถ๊ฐํ๊ณ ๋๋๊ทธ์ค ๋๋์ผ๋ก ์ํ๋ ์์น๋ก ์ด๋์ํค๋ ๊ฒ๊น์ง๋ ๊ตฌํ์ ํ์ง๋ง, ์คํฐ์ปค๊ฐ ์ฌ๋ฌ ๊ฐ๊ฐ ๋๊ฑฐ๋ ํน์ ์คํฐ์ปค๋ฅผ ๋๋ฆฌ๊ฑฐ๋ ๋ฆฌ์ฌ์ด์งํ๊ฑฐ๋ ๋ฐ์ ํ๊ฑฐ๋ ํ๋ ๊ฒ์ ๋ ๋ค๋ฅธ ๋ฌธ์ ์๋ค. ๊ทธ๊ฒ๋ค์ ๋ค ์ง์ ๊ตฌํ์ ํ๋ฉด ๋ฌผ๋ก ๋๋ฌด ์ข๊ฒ ์ง๋งโฆ๐ ๊ทธ๋ฌ๋ฉด ๊ฒฐ๊ตญ ์ฌ์ง ํธ์ง๋ ์ง์ ๊ตฌํํ ๊ฑธ๋ก ํ๋ฉด ๋๊ธฐ์ ์ ์ด์ cropper.js
๋ฅผ ์ฐ๊ธฐ๋ก ๊ฒฐ์ ํ๋ ๊ฒ์์ ๋๋ฌด ๋ง์ด ๋ฒ์ด๋๋ ๊ฒ ๊ฐ์๋ค. ์ฐ๋ฆฌ ํ๋ก์ ํธ์ ๋ชฉํ๋ โ๋ฌด์กฐ๊ฑด ์์ฑโ ์ด์๋๋ฐ ๋๋ฒ๊น
๊ณผ ๋ฐฐํฌ, ๋ฌธ์ํ๊น์ง ๊ณ ๋ คํ๋ค๋ฉด ๋จ์ ์๊ฐ์ด 2์ฃผ๊ฐ๋๋ฐ์ ๋์ง ์๋ ๋ค๋ ๊ฒ๋ ๋ฌธ์ ์๋ค.
๊ทธ๋ ๊ฒ ๋ฆฌ์์น๋ฅผ ๊ณ์ ํ๋ ์ค, fabric.js
์ ๋ํด ์๊ฒ๋์ด ์ด๊ฒ์ ์ฐ๊ธฐ๋ก ํ๋ค. core javascript๋ก ๋๋๊ทธ ์ด๋ฒคํธ์ ์ฉ Canvas API๋ฅผ ์ด์ฉํด ๊ตฌํํด๋ณด๋ ค ํ๋ ์ ๋ฐ๋ชจ๋, ๋์ ์๋ฆฌ๋ฅผ ์๊ฒ ํด์ฃผ๋ ์์คํ ์๋์์ง๋ง, fabric.js๋ ํ์คํ โจ์ ์ธ๊ณโจ์๋ค.
๋๊ฐ์ด canvas๋ฅผ ์ธ์คํด์ค๋ก ์์ฑํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๊ณ cropper์ ์ถฉ๋ํ ์ผ์ด ์์๋ค. ๋ค๋ง ์๋ก์ด ์บ๋ฒ์ค๊ฐ ์์ฑ๋๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋ ๊ฐ์ง ์์ด๋์ด ์ค์ ๊ณ ๋ฏผํ๋ค.
- ์คํฐ์ปค ํธ์ง ๋ชจ๋ ์ง์ ์ ํ๋ ํธ์ง์ ์ ์ฅํ์ฌ ์คํฐ์ปค ์บ๋ฒ์ค์ ๋ฐฐ๊ฒฝ์ผ๋ก ๊น๊ฒ ํจ. ๋ค์ ์ฌ์ง ํธ์ง์ผ๋ก ๋์๊ฐ ๊ฒฝ์ฐ์๋ ์คํฐ์ปค ์บ๋ฒ์ค์ ๋ด์ฉ์ ์ ์ฅํ์ฌ ์ฌ์ง ํธ์ง ์บ๋ฒ์ค๋ก ๋ก๋ํจ.
- ์คํฐ์ปค ์บ๋ฒ์ค์ ์ฌ์ง ์บ๋ฒ์ค๋ฅผ ๋ฐ๋ก ๋๊ณ ํด๋ฆญํ ๋ชจ๋์ ๋ฐ๋ผ ์บ๋ฒ์ค๋ฅผ ์ค์์นญํจ. ์คํฐ์ปค ์บ๋ฒ์ค๊ฐ ์ฌ์ง ์บ๋ฒ์ค ์๋ก ์์นํด์ ๋ง์น ์ฌ์ง ์บ๋ฒ์ค๊ฐ ๋ฐฐ๊ฒฝ์ธ ๊ฒ์ฒ๋ผ ๋ณด๊ณ ์คํฐ์ปค๋ฅผ ๋๊ฒ ํจ. ์ ์ฅ์์๋ ๋ ์บ๋ฒ์ค๋ฅผ ํฉ์ณ ํ๋์ ์ด๋ฏธ์ง๋ก ๋ง๋ค์ด ์ ์ฅํจ.
์ฒ์์๋ ์ฒซ ๋ฒ์งธ ๋ฐฉ๋ฒ์ผ๋ก ํ๋ ค๊ณ ํ์ผ๋ ๋ชจ๋ ๋ณ๊ฒฝ์๋ง๋ค ์บ๋ฒ์ค๋ฅผ ๋ฐ๋ก ์ ์ฅํ์ฌ ๋ฐฐ๊ฒฝ์ผ๋ก ๊น๋ ๊ฒ๊ณผ ์ ์ฒด์ ์ธ ๋ก์ง์ ๋ณต์ก๋๊ฐ ๋ ๋ฒ์งธ ๋ฐฉ๋ฒ๋ณด๋ค ๋๋ค๊ณ ํ๋จ๋์ด ๋ ๋ฒ์งธ ๋ฐฉ๋ฒ์ผ๋ก ๋ณ๊ฒฝ๋์๋ค.
๐จ fabric.js
fabric.js๋ ์ง๊ด์ ์ด์ง ๋ชปํ๋ Canvas API์ ๋์ ๋ฐฉ์์ ์ง๊ด์ ์ด๊ฒ ํด์ค๋ค. Canvas API๋ฅผ ๊ทธ๋ฅ ์ฐ๋ ๊ฒ๊ณผ ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ ํฐ ์ค์ผ์น๋ถ ์์ ์ฌ๋ฆฌ๋ ์ , ์ , ์ด๋ฏธ์ง ๋ฑ์ ๊ฐ์ฒดํ์ด๋ค. ๊ธฐ์กด์๋ ํฐ ์ค์ผ์น๋ถ์ ์กฐ์ํ์ฌ ์ , ์ , ์ด๋ฏธ์ง ๋ฑ์ ๊ทธ๋ ค๋ด์๋ค๋ฉด Fabric.js๋ฅผ ์ฌ์ฉํ๋ฉด ํฐ ์ค์ผ์น๋ถ์ ๊ทธ๋๋ก ์๊ณ ์ , ์ , ์ด๋ฏธ์ง๋ฅผ ์ํ๋ ๋ชจ์ต์ผ๋ก ์ธํ ํด ์ฌ๋ฆด ์ ์๋ค.
์ด๋ ๊ฒ ํ๋๊น ๊ทธ๋ผ cropper.js๋ ์ฌ์ฉํ์ง ๋ง๊ณ ์ ๋ถ fabric์ผ๋ก ํด๋ณผ๊น?
ํ๋ ์๊ฐ์ด ๋ค์์ง๋ง ์ด๋ฏธ ์๊ฐ์ด ์ข ํ๋ฅธ ๋ค๋ผ์, ๊ทธ๊ฑด ํ ๋ฌ์ด๋ผ๋ ํ๋ก์ ํธ ๊ธฐ๊ฐ ์ธ์ ์ผ๋ก ํด๋ณด๊ธฐ๋ก ํ๋ค. ์ด๋๊น์ง๋ ์ฐ๋ฆฌ์ ๋ชฉํ๋ ์์ฑ์ด์๊ธฐ์!
cropper.js
๋ณด๋ค๋ rawํด์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์ปค์คํฐ๋ง์ด์ง์ด ๊ฐ๋ฅํ ๋ฒ์๊ฐ ๋ ๋์๋ค. ๋ค๋ง ์ฐ๋ฆฌ์๊ฒ ๋ถํ์ํ ๊ธฐ๋ฅ๋ ๋ง์์ ์ปค์คํ
๋น๋๋ฅผ ํด๋ณด๋ ค ํ์ง๋ง, fabric์ ์ปค์คํ
๋น๋๋ npm install์ ํ ํ ๋ชจ๋ ๋ด๋ถ์ ํ์ผ๋ง ๋ฐ๊พธ๋ ์์ด์๊ธฐ์ ์ถํ ์
๋ฐ์ดํธ๊ฐ ์๋ค๋ฉด ์๋์ผ๋ก ์งํํด์ผ ํ๋ ๋ฌธ์ ๊ฐ ์์๋ค. ๊ทธ๋ ๊ฒ ํ์ง ์๊ณ npm์ ํตํด ํ๋ ๋ฐฉ๋ฒ์ ์์ง ์ฐพ์ง ๋ชปํด ๋ณด๋ฅํด๋ ์ํฉ์ด๋ค.
์ด์จ๋ ๋ค์ ๋์์์, fabric.js๋ ๋ด๊ฐ ํ๋ ๊ณ ๋ฏผ๋ค์ ์๋ฒฝํ๊ฒ ํด๊ฒฐํด์ฃผ๋ ๋ง๋ฒ๊ฐ์ ์กด์ฌ์๋ค.
- ํ๋์ ์บ๋ฒ์ค ์์ ์ฌ๋ฌ๊ฐ์ ์คํฐ์ปค ๊ฐ์ฒด๋ฅผ ์ฌ๋ฆด ์ ์๋ค
- ์ฌ๋ฆฌ๋ ๊ฒ ๋ง์ผ๋ก๋ ๋ฐ์ ,๋ฆฌ์ฌ์ด์ง,์ด๋,ํ์ ๋ฑ์ ์กฐ์์ ํ ์ ์๋ค
์ด๋ฅผ ์ํ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
/**
* Add sticker image on canvas
* @param {string} src - The src of sticker image
* @param {Object} options - The options of sticker image
*/
addSticker(src, options) {
fabric.Image.fromURL(
src,
sticker => {
sticker.scaleToWidth(this.stickerCanvas.width * 0.2);
sticker.scaleToHeight(this.stickerCanvas.width * 0.2);
this.stickerCanvas.add(sticker).renderAll();
},
{
borderColor: '#39f',
cornerColor: '#39f',
cornerSize: 5,
transparentCorners: false,
...options
}
);
}
๋ฐ๋ชจ์์์ฒ๋ผ ์คํฐ์ปค๋ฅผ ์ถ๊ฐํ ๋๋ง๋ค ๋๋คํ ์์น์ ์์ฑ๋๊ฒ๋ ํด๋ณด๋ ค๊ณ ํ๋๋ฐโฆ ์๊ฐํด๋ณด๋ ๊ทธ๊ฒ๋ณด๋ค๋ 0,0 ์ด๋ผ๋ ์์ธกํ ์ ์๋ ์์น์ ์ถ๊ฐ๋๋ ๊ฒ์ด ๋ ์ข์ ์ฌ์ฉ์ ๊ฒฝํ์ผ ๊ฒ ๊ฐ์์ ๋ฐ๋ก ์ต์
์ ์ฃผ์ง ์๊ณ ๊ธฐ๋ณธ ์์น์ธ 0,0์ ์์ฑ๋๊ฒ ํ๋ค. ๋ง์น ์ฌ์ฉ์๋ฅผ ์๊ฐํ ๊ฒ์ฒ๋ผ ์ผ๋๋ฐ ์ฌ์ค ๊ฑ ๋๋ผ๋ฉด ๊ทธ๋ด ๊ฒ ๊ฐ์๋ค.
version 0.1.7์์ ์คํฐ์ปค๊ฐ ์ ๊ฐ์ด๋ฐ์ ์์ฑ๋๊ฒ ๋ฐ๊พธ์๋ค. ํ๋๋ ์ด๋ฏธ์ง๊ฐ ui ๋ฐ์ผ๋ก ๊น๋ ค๋ฒ๋ฆฌ๋ฉด 0,0์ ๋์ธ ์คํฐ์ปค์ ์ ์ ๊ฐ ์ ๊ทผํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์คํฐ์ปค์ ์ฌ์ด์ฆ๊ฐ ๋๋ฌด ํฌ์ง๋ ์์ง๋ ์๊ฒ ์บ๋ฒ์ค ์์์ ๋ณด์ผ ์ ์๋๋ก fabric์ scaleToWidth(), scaleToHeight()
๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์๋์ ์ธ ํฌ๊ธฐ๋ฅผ ์ง์ ํ๋ค. ๋ง์ง๋ง์ผ๋ก cropper์ ์คํ์ผ๊ณผ ๋๊ฐ์ด ํ๊ธฐ ์ํด์ ์ฝ๋ ์ฌ์ด์ฆ์ ํ
๋๋ฆฌ ์์์ ์ต์
์ผ๋ก ์ง์ ํ๋ค. ์๋๋ ํฐ์์ผ๋ก ํต์ผํ๋ ค ํ๋๋ฐ, cropper์ ๊ฒฝ์ฐ ๊ทธ๋ ๊ฒ ํ๋ฉด ์ปค์คํ
๋น๋๋ฅผ ํด์ผํด์ ๋ณด๋ฅํ๋ค. ์ด ์ธ์๋ ๋ชจ๋์ ์ฌ์ฉํ๋ ์ฌ์ฉ์๊ฐ ์ถ๊ฐ๋ก ์ง์ ํ๊ณ ์ถ์ ์ต์
์ด ์๋ค๋ฉด ๋ฐ์์ฌ ์ ์๊ฒ๋ ํ๋ค.
์ด๋ฐ ์์ผ๋ก ์ฐ๋ฆฌ์๊ฒ ํ์ํ ๊ธฐ๋ฅ๋ค์ ๋ชจ์์ class๋ก ๋ชจ๋์ ๋ง๋ค์๋ค. cropper๊ณผ fabric์ด ์บ๋ฒ์ค ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด๋ด์ ๋์ํ๋ ๋ฐฉ์์ด๋ผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ๊ฐ ์ธ์คํด์ค๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์ด๋ค.
์๋๋ ๋ด ํ ์ผ์ ์ฌ๊ธฐ์ ๋์ธ ์ค ์์๋ค. ๊ทธ๋์ '์ญ? ๋ ๋๋ฌด ํ ์ผ์ด ์๋๋ฐ? ์ผ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ๋ค ํ๋๋ฐ?'
์ถ์ด์ ์๊ดด๊ฐ์ ๋น ์ง๋ ค๋ ์ฐธ์โฆ ๋ชจ๋์ด ์ ๋๋ก ๋์ํ๋์ง ํ
์คํธํ๊ธฐ ์ํด์๋ UI๊ฐ ํ์ํ๊ณ , Vue๋ฅผ ์ฌ์ฉํด UI๋ฅผ ๋ง๋ค๊ธฐ๋ก ํ๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ตญ ๋๋ Vue๋ฅผ ์ธ ์ค ์์์ผ ํ๋ค. ์๋๋ ๋ชจ๋๋ง ๋ง๋ค๋ฉด ๋๋์ค ์์๋๋ฐ ์๋์์. Vue UI๋ ํจ๊ป ํ๋ก์ ํธ๋ฅผ ์งํํ ์คํ๋์ด ๋งก์์ฃผ์
จ๊ณ , ์คํ๋์ด UI ์์
์ ์งํํ์๋ ๋์ UI์ ์ฐ๊ฒฐํ vue์์์ ๊ธฐ๋ฅ ํจ์๋ฅผ ๋ง๋๋ ๊ฒ์ ๋ด๊ฐ ์งํํ๋ค.
๐ณ UI ๊ตฌ์กฐ
์คํ๋์ด Vue๋ก UI๋ฅผ ๋ง๋์๋ ๊ณผ์ ์์ Vue Slot
๊ตฌ์กฐ์ ๋ํด ํจ๊ป ๊ณ ๋ฏผํ๋๋ฐ ์ข์ ๊ฒฝํ์ด์์ด์ ๊ธฐ๋กํ๊ณ ์ ํ๋ค.
์ฐ๋ฆฌ๊ฐ ๊ฐ๋ฐํ๋ ค๋ ์ฌ์ง ํธ์งํด์ ๊ฑฐ๋ํ ๊ท๋ชจ๊ฐ ์๋๊ธฐ๋ ํ๊ณ , ๋จ๋ ๊ธฐ๋ฅ ๋ชจ๋์ด๊ธฐ์ UI๊ฐ ๋ณต์กํ์ง๋ ์์๋ค. ์ฝ๋์ ์๋ ๋ง์ง ์์ ํ๋์ ํ์ผ ์์์ ์ฝ๋ ์์ฑ์ ๋ง์น ์ ์์๋ค.
๊ทธ๋ ์ง๋ง ๋ช ์์ ์ผ๋ก ์ด๊ฒ UI์ ๋ถ๋ถ์ธ์ง, ๋ชจ๋์ ์ด์ฉํ๋ ๋ถ๋ถ์ธ์ง ๊ตฌ๋ถํ๊ธฐ ์ํด์ ๋ ๊ฐ์ ์ปดํฌ๋ํธ๋ก ๋๋์๋ค. ์ปดํฌ๋ํธ๋ฅผ ๋๋๋ ๊ฒ์ ์ฌ์ฌ์ฉ์ฑ์ด ์์ ๋๋ผ๊ณ ๋ง ์๊ณ ์์๋๋ฐ, ํ์ ๋ ๋จ์ํ ๊ทธ ๋ฟ๋ง์ด ์๋๋ผ ๋ช ์์ ์ผ๋ก ๊ธฐ๋ฅ์ ๊ตฌ๋ถํ๊ธฐ ์ํด ๋๋๊ธฐ๋ ํ๋ค๋ ๊ฒ์ ์๊ฒ๋์๋ค.
๋ฆฌ์กํธ๋ฅผ ๊ณต๋ถํ๋ค ์์ ๊ทธ๋ฐ์ง vue์ ๊ธฐ๋ฅ๋ค์ ์ธ ๋๋ง๋ค ๋ฆฌ์กํธ๋ฅผ ๋ ์ฌ๋ฆฌ๊ฒ ๋๋ ๊ฒ์ ์ด์ฉ ์ ์๋ ๊ฒ ๊ฐ๋ค. slot์ ๋ฆฌ์กํธ์ props children๊ณผ ์ ์ฌํ๋ค. ImojiEditor.vue
๋ ๋ค์๊ณผ ๊ฐ์ด ImojiEditorCanvas.vue
์ปดํฌ๋ํธ๋ฅผ ํ์๋ก ๊ฐ๊ณ , named slot์ผ๋ก ๊ฐ ํํธ์ UI๋ฅผ ๊ทธ๋ ค๋ธ ํ ImojiEditorCanvas.vue
์ ๊ธฐ๋ฅ๋ค์ ๋๊ฒจ๋ฐ์ ์ฐ๊ฒฐํ๋ค.
// ImojiEditor.vue ๊ฐ์ํ ์ฝ๋
<template>
<imoji-editor-canvas>
<template
#controllerBar="{reset, stickerCanvas, onInputImage, crop, layout, photoCanvas}"
>
</template>
<template #toolBar="{photoCanvas, layout, zoom, rotate, clearCrop}">
</template>
<template #stickerToolBar="{stickerCanvas , layout}"> </template>
<template #toolNavigation="{openImageEditor, openStickerEditor}">
</template>
</imoji-editor-canvas>
</template>
์ด๋ ๊ฒ ํ๋ฉด ๋ชจ๋์ ์ด์ฉํด์ ์ฌ์ง ํธ์ง ๊ธฐ๋ฅ๊ณผ ๊ด๋ จ๋ ํจ์๋ฅผ ๋ง๋๋ ๋ถ๋ถ์ ImojiEditorCanvas.vue
์์๋ง ๊ด๋ฆฌํ ์ ์์๋ค. ์ด๊ฑธ๋ก ๋๋ ImojiEditorCanvas.vue
์์๋ง ์ฃผ๋ก ์์
ํ์ฌ UI๋ฅผ ์์
ํ์๋ ์คํ๋๊ณผ ์๋ก ์ถฉ๋ํ์ง ์์ ์ ์์๋ค.
๐ ๊ธฐ๋ฅ ํจ์ ๋ง๋ค๊ธฐ
ImojiEditorCanvas.vue
์์๋ ๋ชจ๋์ ๋ถ๋ฌ์์ ๊ธฐ๋ฅ ํจ์๋ฅผ ๋ง๋ค์๋ค. ๊ทธ๋ฆฌ๊ณค ๊ฐ๊ฐ์ UI์ slot props๋ก ํ์ํ ํจ์ ๋๋ ๋ฐ์ดํฐ๋ฅผ ๋๊ฒจ์ฃผ์๋๋ฐ, ํด๋น ๋ถ๋ถ์ ๊ฐ๋ตํจ์ ์ํด ์๋์ ์ฝ๋์์๋ ์ง์ ๋ค.
์ด ์ปดํฌ๋ํธ์ ํฌ์ธํธ๋ ์บ๋ฒ์ค๋ฅผ ๋ค๋ฃจ๋ ๋ถ๋ถ์ด์๋ค.
// ImojiEditorCanvas.vue ๊ฐ์ํ ์ฝ๋
<template>
<section
:style="{
width: `${width}px`,
height: `${height}px`,
position: 'relative'
}"
>
<slot name="controllerBar"></slot>
// Point !
<div class="imoji-editor-wrapper">
<div class="imoji-editor-container">
<div id="sticker-wrapper" :class="[hide ? 'hide' : '']">
<canvas id="sticker-canvas"></canvas>
</div>
<div
id="uploaded-photo-wrapper"
:style="{
width: `${width}px`,
height: `${height}px`
}"
>
<img id="user-photo" ref="uploadedPhoto" :src="uploadedImageSrc" />
</div>
</div>
</div>
<div class="all-tool-bar-wrapper">
<slot name="toolBar"></slot>
<slot name="stickerToolBar"></slot>
<slot name="ratioCropToolBar"></slot>
<slot name="toolNavigation"></slot>
</div>
</section>
</template>
์ฌ์ง ํธ์ง๋ง์ ๋ด๋นํ๋ uploaded-photo-wrapper
๊ฐ ์๊ณ , ์คํฐ์ปค ํธ์ง์ ๋ด๋นํ๋ sticker-wrapper
๊ฐ ์๋ค. ๊ฐ๊ฐ์ ์บ๋ฒ์ค๋ ๋ชจ๋์ ์ํด ์์ฑ๋ ๊ฒ์ด๊ณ ๊ฑด๋๋ฆด ์ ์๊ธฐ ๋๋ฌธ์ ๊ฐ๊ฐ์ ๊ฐ์ธ๋ div๋ฅผ ๋ง๋ค์ด์ ์์น ์กฐ์ ๋ฑ ์ํ๋ ์์
์ ํ๋ค.
๊ธฐ๋ณธ์ ์ฌ์ง ํธ์ง ์บ๋ฒ์ค์ด๊ณ ์คํฐ์ปค ๋ชจ๋๋ก ์ง์
์์๋ ์คํฐ์ปค ์บ๋ฒ์ค๊ฐ ๊ทธ ์์ ์์ฌ ์๋์ ๋์ธ ์ฌ์ง ํธ์ง ์บ๋ฒ์ค๋ฅผ ๋ณด๋ฉด์ ์ํ๋ ์์น์ ์คํฐ์ปค๋ฅผ ์์ฑํ ์ ์๊ฒ ํ๋ค. ์์ ๋์ฌ์๊ธฐ ๋๋ฌธ์ ์คํฐ์ปค ๋ชจ๋์์๋ ์ฌ์ง ํธ์ง ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ค. ๋ค์ ์ฌ์ง ํธ์ง ๋ชจ๋๋ก ์ง์
ํ๋ฉด ์คํฐ์ปค ์บ๋ฒ์ค๊ฐ ์จ๊ฒจ์ง๋ฉฐ ์ฌ์ง ํธ์ง์ ๋ค์ ํ ์ ์๋ค. ์ด ๋ถ๋ถ์ Vue์ reactive data๋ฅผ ํ์ฉํ์ฌ display:none
์์ฑ์ class๋ฅผ ์ถ๊ฐํ๋ค ์ ๊ฑฐํ๋ค ํ ๊ฒ์ธ๋ฐ, ๋ฆฌ์กํธ ์ด์ฉ์๋ผ๋ฉด setState๋ฅผ ํ์ฉํ ๊ฒ์ด๋ผ๊ณ ๋ณด๋ฉด ์ฝ๋ค.
UI์ ๋ชจ๋์ ์ฐ๊ฒฐํ๋ ๊ณผ์ ์ ๋๋ฒ๊น ์ ์ฐ์์ด๊ธฐ๋ ํ๋ค. ์๊ฐ๋ณด๋ค ๋ค์ํ ๊ฒฝ์ฐ์ ์๊ฐ ์์๊ธฐ ๋๋ฌธ์ด๋ค.
2์ฃผ์ฐจ๊น์ง ํ์ ๊ธฐ๋ฅ๋ค์ ๋ด์ 1์ฐจ ํ๋กํ ํ์ ์ ์์ฑํ ํ์๋ ์๋ฌ์์ด ์ ๋์๊ฐ๋๋ก ๋ค์ํ ์ฌ์ฉ ํ๋ฆ์ ํ์ธํ๊ณ ์๋๋ก์ด๋/ios/๊ตฌ๊ธํฌ๋กฌ/์จ์ผ/์ผ์ฑ์ธํฐ๋ท ๋ฑ ๋ค์ํ ํ๊ฒฝ์์ ํ ์คํธํด๋ณด๋ฉฐ ์ด์๋ฅผ ์ฒดํฌํ๊ณ fixํ๋๋ฐ ์ง์คํ๋ค. ์๋๋ ์ด์๋ณด๋์ธ๋ฐ ์ด์ ํ๋๋ฅผ ํด๊ฒฐํ๋ฉด ์ง์ ๊ฐ ๋ ์ฏค ์๋ก์ด ์ด์๊ฐ ์๊ฒจ๋๋ ํ๋ฃจํ๋ฃจ๋ฅผ ๋ณด๋๋คโฆ๐
ํนํ ์ฌ์ฉ์ ์ ์ฅ์์ ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ด๋ค ํ๋ฆ์ ๊ฐ์ง๊ณ ์์ด์ผ ์ข์์ง, ์คํ ์์ค์ธ๋งํผ ๊ฐ๋ฐ์๋ค์ด ์ฝ๋๋ง ๋ณด๊ณ ๋ ์์์ฐจ๋ฆด ์ ์๊ฒ๋ ๋ค์ด๋ฐ๊ณผ ๊ตฌ์กฐ์ ์ธ ๋ถ๋ถ ๋๋ ์์ํ ์์๊ฐ์ ๊ฒ์๋ ์ ๊ฒฝ์ผ๋ค. ์ด ๊ธฐ๊ฐ๋์ ๋ด๊ฐ ๊ฒช์๋ ๋ธ๋ก์ปค๋ ์๋์ ๊ฐ๋ค. ์์งํ ์ง๊ธ์์ ์๊ฐํด๋ณด๋ฉด ์ ๊ทธ๋ ๊ฒ ๋๋๋์๊น ์ถ๊ธฐ๋ ํ์ง๋งโฆ ํ์ฌ์ ๋ด๊ฐ ๊ทธ๋์ ๋๋ณด๋ค ์ฑ์ฅํด์ ๊ทธ๋ฐ๊ฑฐ๋ผ๊ณ ๋ฏฟ๊ณ ์ถ๋ค ๐คฆโโ๏ธ
๐ฅ Blocker
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ์ ๋ํ ์ด๋ ค์
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐ๋ฉด ๋ฌด์กฐ๊ฑด ์ฝ๊ณ ํธํ๊ณ ์ข๊ธฐ๋ง ํ ์ค ์์๋ค. ์ ๋ ๊ทธ๋ ์ง ์๊ณ ์คํ๋ ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐ๋ฉด ํ ์์ ๊ฐํ๊ฒ ๋ ์๋ ์๋ค๋ ์ ์ ์ด๋ฒ์ ์ข ๋ง์ด ๋๊ผ๋ค.
์๋ฅผ ๋ค์ด์ cropper.js
์ ํน์ ์บ๋ฒ์ค๋ฅผ position relative๋ก ๋๊ณ ์คํฐ์ปค ์บ๋ฒ์ค๊ฐ cropper์ ์บ๋ฒ์ค๋ฅผ ๋ฐ๋ผ๊ฐ๋๋ก ๋ง๋ค๊ณ ์ถ์๋ฐ, cropper์ ์บ๋ฒ์ค๋ ๋ฌด์กฐ๊ฑด position absolute์ฌ์ผ๋ง ํ๋ ์ํฉ์ด ์์๋ค. ๋, cropper์ ์์ ์คํ์ผ์ ํฐ์์ผ๋ก ๋ฐ๊พธ๊ณ ์ถ์๋ฐ, ๊ทธ๋ด๋ผ๋ฉด ์ปค์คํ
๋น๋๋ฅผ ํด์ผํ๋ ์ํฉ๋ ์์๋ค.
์ด๋ฐ์์ผ๋ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ธ์คํด์ค๋ฅผ ์ ๊ณตํ๊ณ ๊ทธ์ ๋ฐ๋ฅธ ๋ฉ์๋๋ค์ ์ ๊ณตํ๋ค๋ฉด, ๊ทธ๋ฆฌ๊ณ ๊ทธ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ฌ๋ฌ ๊ฐ ์ฌ์ฉํ๋ค๋ฉด ํ ์์ ํ ์์ ๊ฐํ๊ฒ ๋์ด ๋ต๋ตํด์ง๋ ์ํฉ์ ๋ง์ฃผํ ์๋ ์๊ฒ ๋ค ์ถ์๋ค. ์ด๋ฐ ๋ถ๋ถ์ ๊ฒฝ๊ณํ๋ฉฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ผ ํ๊ณ , ์ ํํด์ผ ํ์ง ์์์๊น? ๋ค์์ ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ธ ์ผ์ด ์๋ค๋ฉด ์ผ๋ง๋ ์์ ๋ก์ด์ง๋ฅผ ๊ผญ ํ์ธํด์ผ๊ฒ ๋ค ์ถ์๋ค.
์ด๋ฏธ์ง ์ต์ข ์ ์ฅ๊ณผ ๋น๋๊ธฐ
์ด๋ฒ ํ๋ก์ ํธ์์ ์ ์ผ ๊ฐ๊ณผํ๋ ๋ถ๋ถ์ ๋น๋๊ธฐ์๋ค. ๋ง์
์ ๊ทผ์
์ธ๋ถ์์ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฒ ์์ฒด๊ฐ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌ๋๊ธฐ ๋๋ฌธ์, ๋ด๊ฐ ์ํ๋ ์์๋ก ์ผ๋ จ์ ์ฝ๋๋ค์ด ์คํ๋๊ฒ ๋ณด์ฅํ๋ ๊ฒ์ด ์ค์ํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ๊ฐ ๊ฐ๋ฐํ๋ ์ด ํ๋ก์ ํธ๊ฐ ์ด๋ฏธ์ง๋ฅผ ๋ค๋ฃจ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์, ๋ด๊ฐ ๋ง์ฃผํ ๋ฌธ์ ๋ค์ ํ 80%๋ ์์ธ์ด ๋น๋๊ธฐ์๋ ๊ฒ ๊ฐ๋ค.
๊ทธ ์ค์์ ๊ธฐ์ต๋๋ ๊ฒ์ ๊ผฝ์๋ฉด ์ด๋ฏธ์ง์ ์ต์ข ์ ์ฅ๊ณผ ๊ด๋ จ๋ ๋ถ๋ถ์ด์๋ค. ๋๋ ์ ์ ๊ฐ ์ ํํ ๋ชจ๋์ ๋ฐ๋ผ ๋ ์บ๋ฒ์ค๋ฅผ ์ค์์นญํ๊ณ ์์๊ณ , ์ต์ข ์ ์ฅ์์๋ ๋ ์บ๋ฒ์ค๋ฅผ ํ๋๋ก ํฉ์ณ์ ์ด๋ฏธ์ง ๊ฐ์ฒด๋ก ๋ด๋ณด๋ด์ผ ํ๋ค. ํฉ์น๋ ๋ฐฉ๋ฒ์ ๋ ๊ฐ์ง๊ฐ ์์๋ค. (1) ํธ์ง๋ ์ฌ์ง์ ์ด๋ฏธ์ง๋ก ๋ด๋ณด๋ด ์คํฐ์ปค ์บ๋ฒ์ค์ ๋ฐฐ๊ฒฝ์ผ๋ก ๊น๋๋, (2) ์คํฐ์ปค ์บ๋ฒ์ค๋ฅผ ์ด๋ฏธ์ง๋ก ๋ด๋ณด๋ด ์ฌ์ง ํธ์ง ์บ๋ฒ์ค ์ ์ผ ์์์ ๋๋๋.
๊ฒฐ๋ก ๋ถํฐ ๋งํ์๋ฉด ํ์์ ๋ฐฉ๋ฒ์ ์ ์ฉํ๋ค. ์ฌ์ง ํธ์ง ์บ๋ฒ์ค์์๋ ๋ณด์ฌ์ง๋ ๊ฒ๋ง ๋ ์ด์์์ ํฌ๊ธฐ์ ๋ฐ๋ผ ์ค์ด๋ณด์ด๋ ๊ฒ์ผ ๋ฟ ์์ํ์ ์ค์ ์ฌ์ง์ ํฌ๊ธฐ์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ฉด ์คํฐ์ปค ์บ๋ฒ์ค์ ํฌ๊ธฐ๋ ๋ ์ด์์์ ํฌ๊ธฐ์ ์๋ฒฝํ ์ผ์นํ๊ธฐ ๋๋ฌธ์ ํธ์ง๋ ์ฌ์ง์ ์คํฐ์ปค ์บ๋ฒ์ค์ ๋ฐฐ๊ฒฝ์ผ๋ก ์ค์ ํ๋ ๊ฒฝ์ฐ, ์ต์ข ์ ์ฅ์ ์ฌ์ง์ด ์๋ณธ ํฌ๊ธฐ๋ก ์ ์ฅ๋๋ ๊ฒ์ด ์๋๋ผ ์ ์ ๊ฐ ์ด๋ ์ฌ์ด์ฆ์ ์๋ํฐ๋ฅผ ์ฐ๊ณ ์๋์ ๋ฐ๋ผ ํฌ๊ธฐ๊ฐ ๊ฒฐ์ ๋๋ ๋ฌธ์ ๊ฐ ์์๋ค. ์๋ฅผ ๋ค์ด ๋ชจ๋ฐ์ผ์์ ํธ์งํ๊ณ ์๋ค๋ฉด ๋ฑ ๋ชจ๋ฐ์ผ ํ๋ฉด๋งํ ์ฌ์ด์ฆ์ ์ฌ์ง์ด ๋ฐํ๋๊ณ , ๋ฐ์คํฌํ์์ ํธ์งํ๊ณ ์๋ค๋ฉด ๋ฐ์คํฌํ UI์ ๋ชจ๋ฌ์ฐฝ ์ฌ์ด์ฆ์ ์ฌ์ง์ด ๋ฐํ๋๊ฒ ์๊ธด ๊ฒ์ด๋ค. ์ด๊ฑด ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ๋ ์๋๊ณ , ์ฌ์ฉ์๋ ์ ๋๋ก ์ํ์ง ์์ ๊ฒ์ด๋ค.
ํ์์ ๋ฐฉ๋ฒ์ ๊ทธ๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์๋ค. ์๋์ ๊ฐ์ด ๋ชจ๋์์ fabric์ ๋ฉ์๋๋ฅผ ์จ์ ๋ฆฌ์ฌ์ด์ง ํจ์๋ฅผ ๋ง๋ค์๋ค. ๋ชฉํํ๋ ํฌ๊ธฐ (์๋ณธ ์ฌ์ง์ ํฌ๊ธฐ)์ ๋ง๊ฒ ์บ๋ฒ์ค์ ํฌ๊ธฐ์ ์บ๋ฒ์ค ์์ ์คํฐ์ปค ๊ฐ์ฒด๋ค์ ํฌ๊ธฐ๋ฅผ ๋๋ ธ๋ค.
resizeStickerToNatural(naturalWidth) {
if (this.stickerCanvas.width != naturalWidth) {
const scaleMultiplier = naturalWidth / this.stickerCanvas.width;
const objects = this.stickerCanvas.getObjects();
for (let i in objects) {
objects[i].scaleX = objects[i].scaleX * scaleMultiplier;
objects[i].scaleY = objects[i].scaleY * scaleMultiplier;
objects[i].left = objects[i].left * scaleMultiplier;
objects[i].top = objects[i].top * scaleMultiplier;
objects[i].setCoords();
}
this.stickerCanvas.discardActiveObject();
this.stickerCanvas.setWidth(
this.stickerCanvas.getWidth() * scaleMultiplier
);
this.stickerCanvas.setHeight(
this.stickerCanvas.getHeight() * scaleMultiplier
);
this.stickerCanvas.renderAll();
this.stickerCanvas.calcOffset();
}
}
์ด๋ ๊ฒ ํ์ ๋ ์คํฐ์ปค๊ฐ svgํ์ผ์ด๋ผ ํ์ง์ ์ ํ๋ ๋ฐ์ํ์ง ์์๋ค. ๊ทธ๋์ ๋ฌธ์์ ์ฐ๋ฆฌ๋ ์คํฐ์ปค์ ํ์์ svg๋ฅผ ๊ถ์ฅํ๋ค๊ณ ๊ฐ์กฐํด๋จ๋ค. ์ด์ ๋ค๋ฅธ ๋ฌธ์ ์ ์ ์คํฐ์ปค ์บ๋ฒ์ค๋ฅผ ์ด๋ฏธ์ง๋ก ๋ณํํ ๊ฒ(=์ดํ ์คํฐ์ปค ์ด๋ฏธ์ง)์ ๋ถ๋ฌ์ค๋ ๊ณผ์ ์ด ๋น๋๊ธฐ๋ผ๋ ๊ฒ์ด์๋ค. ์ด ๋ถ๋ถ์ ๋ฌด์ํ๋ฉด ์คํฐ์ปค ์๋ ๊ฒฐ๊ณผ๋ฌผ์ด ๋ฐํ๋์๋ค ๐คฆโโ๏ธ
์คํฐ์ปค ์ด๋ฏธ์ง์ ๋ก๋ฉ์ด ๋๋ ํ, ์ฌ์ง ํธ์ง ์บ๋ฒ์ค์ ๊ทธ๋ ค๋ด์ด์ผ ํ๋๋ฐ ๊ทธ ์์๋ฅผ ๋ณด์ฅํ๊ธฐ ์ํด promise๋ฅผ ๋ง๋ค์ด ์ฒ๋ฆฌํ๋ ํด๊ฒฐ ๋๋ค.
// ImojiEditor.js
exportResultPhoto(stickerImage) {
// ์คํฐ์ปค ํธ์ง ์์ด ์ฌ์ง ํธ์ง๋ง ํ๋ ๊ฒฝ์ฐ์ ๊ทธ๋๋ก ์ด๋ฏธ์ง ๊ฐ์ฒด๋ก ๋ง๋ค์ด ๋ฐํํ๋ค.
const canvas = this.cropper.getCroppedCanvas();
const editedPhoto = new Image();
editedPhoto.src = canvas.toDataURL('image/png');
//return Image Object
if (!stickerImage) return editedPhoto;
//add sticker image on photo canvas
const context = canvas.getContext('2d');
let loadResultPhoto = new Promise(resolve => {
stickerImage.onload = () => {
context.drawImage(stickerImage, 0, 0);
resolve(canvas);
};
});
//return promise
return loadResultPhoto.then(res => {
const withStickerImage = new Image();
withStickerImage.src = res.toDataURL('image/png');
return withStickerImage;
});
}
๋ฐํ๋ promise๋ vue ์ปดํฌ๋ํธ์์ ์๋์ ๊ฐ์ด ์ฒ๋ฆฌํ๊ณ ์๋ค.
// ImojiEditorCanvas.vue
async exportResultPhoto() {
// case 1. only Edit
if (!this.stickerCanvas && this.photoCanvas) {
return this.photoCanvas.exportResultPhoto();
}
// case 2. only Sticker
if (!this.photoCanvas && this.stickerCanvas) {
const width = this.photoCanvas.getNaturalSize()[0];
return await this.photoCanvas.exportResultPhoto(
this.stickerCanvas.saveStickerImage(width)
);
}
// case 3. Edit & Sticker
if (this.photoCanvas && this.stickerCanvas) {
const width = this.photoCanvas.getNaturalSize()[0];
return await this.photoCanvas.exportResultPhoto(
this.stickerCanvas.saveStickerImage(width)
);
}
},
// ImojiEditor.vue
async done() {
const resultImage = await this.$refs.Imoji.exportResultPhoto();
this.$emit('done', resultImage);
}
$refs
๋ฅผ ์ฌ์ฉํ ์ด์ ๋ vue์ ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์์ ์ปดํฌ๋ํธ์ ๋ฉ์๋๋ฅผ ์ด์ฉํ๊ธฐ ์ํจ์ด๋ค. ๊ฒ์ํด๋ดค์ ๋ ์ด๋ฒคํธ ๋ฒ์ค๋ฅผ ์ด์ฉํ๊ธฐ๋ ํ๋ค๋๋ฐ, ๋ด ์งง์ ๊ฒ์ ์ค๋ ฅ์ผ๋ก๋ ์ด๋ฒคํธ ๋ฒ์ค๋ก ๋ฐ์ดํฐ๋ ์ฃผ๊ณ ๋ฐ์๋ ์์ ์ปดํฌ๋ํธ ๋ฉ์๋์ ์คํ ์ ์ด๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ์์ ๋ ์ฐพ์๋ณด์ง ๋ชปํ๋ค. ๊ทธ๋ ๋ค๊ณ ์ปค์คํ
์ด๋ฒคํธ๋ฅผ ๋ง๋ค์ด์ emit์ ๋ ๋ฒ ๋๊ธฐ๊ณ ๊ทธ๋ฌ๋๋, ๊ทธ๋ฅ $refs
๋ฅผ ์ผ๋ค.
์ด์จ๋ ! ์ด์ ์ ์ ์ฅ๋๋ค ๐
์๋ก ๋ค๋ฅธ ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ๊ณตํ๋ ์บ๋ฒ์ค ์ฐ๋ํ๊ธฐ
์ฌ์ง ํธ์ง์ ๋ด๋นํ๋ ์บ๋ฒ์ค ํ๋, ์คํฐ์ปค ํธ์ง์ ๋ด๋นํ๋ ์บ๋ฒ์ค ํ๋, ์ด๋ ๊ฒ ๋ ์บ๋ฒ์ค๋ฅผ ๋๊ณ ์ฐ๋ค ๋ณด๋๊น ๋ ์บ๋ฒ์ค์ ์ฌ์ด์ฆ๋ฅผ ์ฐ๋๋๊ฒ ํ๋ ๊ฒ์ด ์ค์ํ๋ค. ์ ํํ๋ ์คํฐ์ปค ์บ๋ฒ์ค๊ฐ ์ฌ์ง ํธ์ง ์บ๋ฒ์ค๋ฅผ ๋ฐ๋ผ๊ฐ๊ฒ ํด์ผ ํ๋ค. ์๋ํ๋ฉด ์คํฐ์ปค๋ ์ฌ์ง ์์์๋ง ์์ง์ด๊ฒ ํ๋ ๊ฒ์ด ์์ฐ์ค๋ฌ์ฐ๋๊น!
์ด ๊ณผ์ ์์ ์ฌ์ง ํธ์ง ์บ๋ฒ์ค ์ฌ์ด์ฆ์ ๋ณํ๋ฅผ ๊ฐ์งํ์ฌ ์คํฐ์ปค ์บ๋ฒ์ค์ ์ฌ์ด์ฆ๊ฐ ์๋์ผ๋ก ์ฌ์กฐ์ ๋๋๋ก vue์ watch๋ฅผ ์ฌ์ฉํ ๊น๋ ์ถ์์ผ๋, ๋ค์๊ณผ ๊ฐ์ ์ด์ ๋ก ์ฌ์ฉํ์ง ์์๋ค.
- watch๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ ๋๋ฒ๊น ์ ํ๋ฆ์ ์ถ์ ํ๊ธฐ ์ด๋ ค์. watch๋ฅผ ์ฌ์ฉํ์ง ์์ ์ ์๋ ๋ฐฉ๋ฒ์ด ์๋์ง ์ต๋ํ ์ฐพ์๋ณด๊ณ , ์ ๋ฐฉ๋ฒ์ด ์์ ๊ฒฝ์ฐ ์ตํ์ ์๋จ์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํจ
- ํ์ง๋ง ์ปดํฌ๋ํธ ์ธ๋ถ์ ๋์์ด๋ ๋น๋๊ธฐ๋ฅผ ์ฒ๋ฆฌํด์ผ ํ๋ ๊ฒฝ์ฐ์๋ watch๋ฅผ ์ฌ์ฉํ๋๋ก ๊ถ์ฅํจ
- ๊ทธ๋ฆฌ๊ณ ์๋ชป ์ฐ๋ฉด ๋ฆฌ์กํธ useEffect์ฒ๋ผ ๋ฌดํ๋ฃจํ๊ฐ ๋ฐ์ํ๋ค ๐ ์๋๋ ์์๊ฐ์ ์๋ฌ๊ฐ ๋ช๋ฐฑ๊ฐ ์์ฌ์ ๊น๋ํด์ ์ฐ์ด๋ ์ค์ท.
watch ๋์ ์ฌ์ง ํธ์ง ์บ๋ฒ์ค์ ์ฌ์ด์ฆ๋ฅผ ์ป๋ ํจ์์, ์คํฐ์ปค ์บ๋ฒ์ค์ ์ฌ์ด์ฆ๋ฅผ ๋ฆฌ์ฌ์ด์ง ํ๋ ํจ์๋ฅผ ๋ง๋ค์๋ค. ํต์ฌ์ cropํ ๋ ๋ฟ๋ง ์๋๋ผ ์ฌ์ง ์ฌ์ ๋ก๋, rotate ์์๋ ๋ณ๊ฒฝ๋ ์ฌ์ด์ฆ๋ฅผ ์ป์ด์ผ ํ๋ ๊ฒ์ด์๋ค. ๊ทธ๋ฆฌ๊ณ ์ญ์ ๋น๋๊ธฐ๊ฐ ๋ง์ฝ์ด์๋ค ใ ใ
์ด๋ค ์กฐ์์ด ์ด๋ฃจ์ด์ง ์ด๋ฏธ์ง๊ฐ ๋ก๋๋ ํ์, ๊ทธ์ ๋ง๊ฒ ์ฌ์กฐ์ ๋ ์บ๋ฒ์ค์ ์ฌ์ด์ฆ๋ฅผ ๊ฐ์ ธ์์ผ ํ๊ณ , ๊ทธ ์ฌ์ด์ฆ๋ฅผ ๊ฐ์ ธ์จ ํ์ data์ ์ ์ฅํด์ผ ํ๋ค. ๊ทธ๋ฐ๋ฐ ์ด๊ฒ ์์๊ฐ ๋ณด์ฅ์ด ๋์ง ์์ผ๋ ์๊พธ undefined๊ฐ ๋จ๊ณ , ์คํฐ์ปค ์บ๋ฒ์ค์ ์ฌ์ด์ฆ๊ฐ ๋ณํ์ง ์๊ณ ๊ทธ๋ฌ๋ ๊ฒ์ด๋ค. ์๋ ๋ง์ฐฌ๊ฐ์ง๋ก promise, await, cropper.js
์์ ์ ๊ณตํ๋ โreadyโ์ด๋ฒคํธ๋ฅผ ์ด์ฉํด์ ํด๊ฒฐํ๋ค.
// ImojiEditor.js
getPhotoCanvasSize() {
return new Promise((resolve, reject) => {
try {
this.userImage.addEventListener(
'ready',
() => {
const { width, height } = this.cropper.getCanvasData();
resolve([width, height]);
},
{ once: true }
);
} catch (error) {
reject(error);
}
});
}
// ImojiEditorCanvas.vue
async setPhotoCanvasSize() {
const [width, height] = await this.photoCanvas.getPhotoCanvasSize();
this.$set(this.photoCanvasSize, 0, width);
this.$set(this.photoCanvasSize, 1, height);
this.resizeStickerCanvas();
},
๋ค์ํ ์ผ์ด์ค์ ๋์ํ๊ธฐ
๋ง๋ค์ด๋ ๊ธฐ๋ฅ๋ค์ ์๋ฌ์์ด ์ ๊ณตํ๊ธฐ ์ํด ๋ค์ํ ์ผ์ด์ค๋ฅผ ํ ์คํธํด๋ณด์๋๋ฐ, ์๊ฐ๋ณด๋ค ๊ฒฝ์ฐ์ ์๊ฐ ๋ง์์ ์๊ฒ ๋์๋ค. ๋์ ๋๐ง ์ฉ๋์ผ๋ก๋ ๋์ ํ ์ฐ์ง ์๊ณ ๋ ๊ธฐ์ตํ๊ธฐ ์ด๋ ค์ ํ๋ฆ๋๋ฅผ ๋์ถฉ์ด๋ผ๋ ์จ์ ์์ธ์ฒ๋ฆฌ ํ ๋ถ๋ถ์ ์์ธ์ฒ๋ฆฌ ํ๊ณ if๋ฌธ์ ์จ์ ์กฐ๊ฑด ๋ถ๊ธฐ๋ฅผ ํ๊ธฐ๋ ํ๋ค.
์ด ๊ณผ์ ์์ ์ฐ๋ฆฌ์ ์๋์ ๋ง์ง ์๊ฒ ํ๋ํ๋ ์ฝ๋๋ค์ ๋ฐ๋ก์ก๋๋ผ ์๊ฐ์ ๋ง์ด ์ผ๋ค. ๊ฐ๋ฐ์์ ๊ท์ฐฎ์์ ์ฌ์ฉ์์ ๋ถํธํจ์ผ๋ก ๋ ๋๊ธฐ๋ฉด ์๋๋ค๋ ์ด๋์ ์ฃผ์๋ค์ ๋ฉ์ง ๋ง์ ์ค์ฒํ๋๋ผ ๊ทธ๋ฌ๋ค. ํญ์ ์ฃผ์ด์ง ์ผ์ (์๊ฐ)์์ ์์ฑ์ ์ฐ์ ์ ํ ๊ฒ์ธ์ง ์์ฑ๋๋ฅผ ์ฐ์ ์ ํ ๊ฒ์ธ์ง ๋๋ ๋ง์ ๋น ์ง๋ ๊ฒ ๊ฐ๋ค. ๋ ์ธ์ ์ฏค ๋ ๊ฐ์ง๋ฅผ ์๋ฒฝํ๊ฒ ๋ง์กฑ์ํฌ ์ ์๋ ์ฌ๋์ด ๋ ์ ์์๊น? ๐คง
โก npm ๋ฐฐํฌ
npm ๋ฐฐํฌ๋ npm ๊ณ์ ์ ๋ง๋ ๋ค ์ฐ๋ฆฌ ํ๋ก์ ํธ๋ฅผ build, ํจํค์งํด์ ๋ฑ๋กํ๋ฉด ๋๋ค. vue์ ๊ฒฝ์ฐ์๋ ๋ค์๊ณผ ๊ฐ์ด ํด์ ์ฌ์ฉ์๊ฐ ํ๋ฌ๊ทธ์ธ์ฒ๋ผ ์ฐ๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ปดํฌ๋ํธ๋ก ์ฌ์ฉํ ์ ์๊ฒ ํ๋ค.
import ImojiEditor from '@/components/ImojiEditor.vue';
export default {
install(Vue) {
Vue.component('imoji-editor', ImojiEditor);
},
};
//usage
import ImojiEditor from 'imoji-editor';
Vue.use(ImojiEditor);
๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ๋ฐ์ดํธ๋๋ฉด (master๋ธ๋์น ์ ๋ฐ์ดํธ) npm ๋ฐฐํฌ๋ ์๋์ผ๋ก ์งํ๋ ์ ์๋๋ก ๊นํ๋ธ ์ก์ ์ ์ด์ฉํ๋ค.
name: Npm Publish
on:
push:
branches:
- master
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: master
- uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install Dependencies
run: npm install
- name: Webpack build
run: npm run build
- uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}
์ด๋ฐ ํจํค์ง ๊ณผ์ ์ ๋ณดํ๋์ด ๋์์ฃผ์ จ๋๋ฐ, ๋ง์ฃผํ๋ ๋ฌธ์ ์ ์ ๊ธฐ๋ณธ ์คํฐ์ปค๋ก ๋ฃ์ด๋ ๋ฉ๋์คํธ๋ฆผ ์บ๋ฆญํฐ svg๊ฐ ํจํค์ง์ ํฌํจ๋์ง ์๋๋ค๋ ์ ์ด์๋ค. ํจํค์ง-๋ฐฐํฌ ๊ณผ์ ์์ ์นํฉ๊ณผ์ ์ธ์์ด ๊ฐ์ฅ ํฐ ์ง๋ถ์ ์ฐจ์งํ๋ ๋๋์ด์๋ค.
์ด ๋ถ๋ถ์ ์ด๋ป๊ฒ ํด๊ฒฐํ๋์ง ๐์ด์ ํญ์ ์์ธํ๊ฒ ๊ณต์ ํด์ฃผ์ จ๋ค. ๋๋ ๊ทธ๋์ ๋ง์ฃผํ๋ ์ด์๋ค์ ํธ๋ ๋ก์ ์ด์ ๋ณด๋์ ๋ฑ๋ก๋ง ํด๋๊ณ ์์ธํ ๋ด์ฉ์ ๋งค์ผ ๊ฐ์ก๋ ์คํฌ๋ผ ๋ฏธํ ์ด๋ ์ฃผ๊ฐ ํ์, ๋๋ ์ ์๋ฆฌ์ ์คํ๋๊ณผ ๋ง๋ก๋ง ์ฃผ๊ณ ๋ฐ์ผ๋ฉฐ ํด๊ฒฐํ์๋๋ฐโฆ ์ ๋ ๊ฒ ์ด์ํญ์ ๋ฌธ์ํ๋ฅผ ํด๋๋ ๊ฒ์ ๋ ํ ๋ฒ ๋ฐฐ์๊ฐ๋ค ๐๐ป๐
โ README ์์ฑ
vue๋ก ui ๋ฉํ์ ํ๊ธฐ ๋๋ฌธ์ vue์์๋ ๊ทธ๋ฅ ์ค์นํด์ ํ๋ฌ๊ทธ์ธ์ฒ๋ผ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋๊น ์ด๋ค ์ต์ ๋ค์ ์ปดํฌ๋ํธ์ prop์ผ๋ก ๋๊ฒจ์ค ์ ์๋์ง๋ฅผ ๋ฌธ์์ ๋จ๊ฒผ๋ค. ์ด๋ค ๊ธฐ๋ฅ๋ค์ ์ง์ํ๋์ง๋ ๋ฌธ์๋ก ๋จ๊ฒผ๋ค.
ํท๊ฐ๋ ธ๋ ๋ถ๋ถ์ ๋ชจ๋์ ๋ํ ๊ฒ๋ ๋ฌธ์ํ๋ฅผ ํ๋ ๊ฒ์ธ์ง ์๋์ง์๋ค. ๋ชจ๋ ์์ฒด๋ ๋ฆฌ์กํธ๋ ์ต๊ทค๋ฌ ๊ฐ๋ฐ์๋ ์ฌ์ฉํ ์ ์๊ฒ๋ core javascript๋ก ๊ฐ๋ฐํ๋๋ฐ ์ด ๋ถ๋ถ๋ ๋ฆฌ๋๋ฏธ์ ๋จ๊ธฐ๋ ๊ฑด๊ฐ? ํ๊ณ ๋ง ์ฐ๋ค๊ฐ, ํ์ ๋ ์ฌ์ญค๋ณด๋๊น ๋ฌผ๋ก ๊ทธ๋ ๊ฒ ๊ฐ๋ฐ์๋ฅผ ์ํ ๋ฌธ์๋ฅผ ๋จ๊ธฐ๊ธฐ๋ ํ์ง๋ง ๋๋ถ๋ถ์ ์ฝ๋์ ์ฃผ์์ ๋ณด๊ณ ๋ ์ ์ ์๊ฒ ํ๋ ๊ฒ์ด ๋ฒ ์คํธ์ด๊ณ ๋น์ฅ ๊ธํ ์ฐ์ ์์๋ ์๋๋ผ ํด์ ๋ณด๋ฅํ๊ธฐ๋ก ํ๋ค. ๋ชจ๋์์ ์ฃผ์์ ๋ฌ์๋๊ธด ํ์ง๋ง ์ฌ์ฉ ์์ ๊น์ง ๋ฐ๋ก ์๊ฐ๋๋ฉด ์ ๋ฆฌํ๊ณ ์ถ๋ค.
๊นํ๋ธ์ README์ Vuepress ๋ ๊ฐ์ง๋ก ์์ฑํ๋๋ฐ, ๊นํ๋ธ์ README๋ npm์ readme๋ก ํจ๊ป ์ฌ๋ผ๊ฐ๊ณ vuepress๋ ๋ฐ๋ก ํด๋ ๊ตฌ์ฑ์ ํด์(docs) ์์ฑํ๋ค. ๊นํ๋ธ ํ์ด์ง์ ์ฐ๊ฒฐ๋๋ค. ๊ธฐ์กด ๊นํ๋ธ ๋ฆฌ๋๋ฏธ์์ ์ฐจ์ด์ ์ ์ปดํฌ๋ํธ๋ฅผ ์ง์ ๋ถ๋ฌ์์ playground๋ฅผ ๊ตฌํํ ์ ์๋ค๋ ์ ์ธ๋ฐ, ์์ง ํ์ง๋ ๋ชปํ๊ณ ๋์ ํฌ๋๋ฆฌ์คํธ์ ๋ด๊ฒจ์๋ค. ์๋ง๋ ๊ณงโฆ๐ playground ๋์ ์ฐ์ ์ gif๋ก ์ด๋ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ์ง๋ฅผ ๋ณด์ฌ์ค ์ ์๊ฒ ํด๋์๋ค.
๐ค๐ป ์ง์์ ์ธ ํ๋ก์
์ฐ๋ฆฌ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฆฌ์์นํ๋ฉด์ ์ธ์ ๋ ๊ธฐ์ค ์ค ํ๋๊ฐ ์ง์์ ์ผ๋ก ์ ๋ฐ์ดํธ ๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ์ง, ์๋์ง์๋ค. ์ฌ์ค ํ ๋ฌ๊ฐ์ ํ๋ก์ ํธ ๋์ ์ด๊ธฐ๋ฒ์ ์ ๋ฐฐํฌํ๊ธด ํ์ง๋ง, ์ข ๋ ์์ ํ๊ณ ๋ณด์ํด์ผํ๋ ๋ถ๋ถ์ด ๋ง๋ค. ๋น์ฅ ํด๊ฒฐํด์ผ ํ๋ ์๋ฌ๋ ์ด์ํญ์ ๋ฌ์๋์๊ณ โฆ ํฐ ์ด๋ฏธ์ง์ผ์๋ก, ํ์์ด png๊ฐ ์๋์๋ก ์์ ์ด ์ง์ฐ๋๋ ํ์์ด ์๋๋ฐ ๊ทธ ๋ถ๋ถ๋ ์์ฌ๋๋ ๋ถ๋ถ์ด ์์ด์ ํด๊ฒฐํ๊ณ ๋ก๋ฉ์ค ui๋ ๋ณด์ฌ์ฃผ์ด์ผ ํ๋ค.
์ง์์ ์ธ ํ๋ก์ ์ ์ํด ๊ฐ์ฅ ์ค์ํ ์ฝ๋์ ๊ฐ๋ ์ฑ๋ ๋ ๊ณ ๋ฏผํ๊ณ ์์ ํด์ผํ ๋ถ๋ถ์ด ๋ง๋ค. ๊ฐ๋ ์ฑ์ ์ํด ๊ตฌ์กฐ๋ถํดํ ๋น์ ์๋ํ์์ผ๋ ์ ์ฉ์ ์คํจํ๋ค.
vue์์์ ํต์ฌ์ this์๋๋ฐ, ์ด this๊ฐ ๋๋ฌด ๋ง์ด ๋ณด์ด๋ ๊ฒ์ด ์ง์ ๋ถํด ๋ณด์ฌ์ ๊ตฌ์กฐ๋ถํด๋ฅผ ์ฐ๊ณ ์ถ์๋ค. ๋๋ง ์ด๋ฐ ์๊ฐ์ ํ๋ ๊ฑด๊ฐ? this๋ฅผ ๊ตฌ์กฐ๋ถํดํ๋ ๊ฑด ๋ทฐ์ค๋ฝ์ง ์์ ์ผ์ธ๊ฐ? ์ถ์ด์ ๊ฒ์ํด๋ณด๋ ๋คํํ ๋๋ ๋๊ฐ์ ์๊ฐ์ ํ๋ ์ฌ๋๋ค์ด ์์๋ค.
๐๐ป ๋งํฌ
๊ทธ๋ฐ๋ฐ ๋ง์ ์จ๋ณด๋๊น, reactive data๊ฐ์ ์ ๋ฐ์ดํธ ํ ๋์๋ ๊ตฌ์กฐ๋ถํด๋ฅผ ์ธ ์๊ฐ ์์๋ค.
์๋๋ฉด constant๊ฐ ๋์ด๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์ด๋คโฆ ๋ทฐ๋ ๋ฆฌ์กํธ์ฒ๋ผ state๊ฐ์ ์
๋ฐ์ดํธํ ๋ ์ฐ๋ setState๊ฐ์ ๊ฒ ์๊ณ ๊ทธ๋ฅ this.something = 'update'
์ด๋ฐ์์ผ๋ก ์ฐ๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ ์ผ์ด์๋ค.
๊ทธ๋์ ๋๋ ์ ํ์ ํด์ผ ํ๋ค. ์ ๋ฐ์ดํธ๊ฐ ๋ฐ์ํ๋ ๋ถ๋ถ๋ง ๊ตฌ์กฐ๋ถํด๋ฅผ ์ฐ์ง ์๊ณ ๋ค๋ฅธ ๊ฒ๋ค์ ๊ตฌ์กฐ๋ถํด๋ฅผ ์ธ์ง, ์๋๋ฉด ์์ ์ ์ฒด์ ์ผ๋ก ๊ตฌ์กฐ๋ถํด๋ฅผ ์ฐ์ง ์์์ง. ์๋ฌด๋ฆฌ ์๊ฐํด๋ด๋ ์ผ๋ถ๋ถ๋ง ๊ตฌ์กฐ๋ถํด๋ฅผ ์ฐ๋ ๊ฑด ์ด์ํ ๊ฒ ๊ฐ์๋ค. ์ ์ธ๊ฑฐ๋ฉด ์ ์ฒด ๋ค ์ ์ฐ๋๊ฒ ํต์ผ๊ฐ ์๊ณ ์ฝ๋๋ฅผ ์ฝ๋ ์ฌ๋์ด ์์ธก๊ฐ๋ฅํด์ ์ข์ ๊ฒ ๊ฐ์๋ค. ๊ทธ๋์ ์ ์ฐ๊ธฐ๋ก ๊ฒฐ์ ํจ. ์ด๊ฒ ๋๋ฌธ์ vue ์ฝ๋๋ค์์ this๋ฅผ ๊ตฌ์กฐ๋ถํดํ๋ ๊ฒฝ์ฐ๋ฅผ ์ฐพ๊ธฐ ์ด๋ ค์ ๋ ๊ฑฐ์๋๋ณด๋ค ์ถ์๋ค. ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์์ง ๋ชป ์ฐพ์ ์ํฉ์ธ๋ฐ ๋ ์ฐพ์๋ด์ผ์ง.
๋ถ์กฑํจ์ด ๋ง์ ์์ฑ์ด๋ผ๊ณ ์๊ฐํ๋ค. ์ฌ๊ธฐ์ ๋์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋๋ผ ์ง์์ ์ผ๋ก ์์ ๋ณด์ํ์ฌ ๋ ์์ ์ ์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ํด ํ๋ก์ ํ๊ฒ ๋ค ๐ฆพ๐ช
๐ฉ๐ปโ๐ ๋ฐฐ์ด์
์์์ ์ธ๊ธํ์ง ์์ ๋ถ๋ถ๋ค ๋ช ๊ฐ์ง๋ง ๊ผฝ์๋ณด๊ฒ ๋ค.
์ค๊ณ์ ์ธ ๊ด์ ์ ๊ฐ์ง๋ ๊ฐ๋ฐ์๊ฐ ๋๊ธฐ
ํ์ฅ๋ ๋ง์ ์ค์ ๊ฐ์ฅ ์ธ์๊น์๋ ๋ถ๋ถ์ด๋ค.
๊ธฐ๋ฅ ๊ตฌํ์ ๋๊ตฌ๋ ํ ์ ์๋ค. ์ค์ํ ๊ฒ์ ์ค๊ณ์ ์ธ ๊ด์ ์ ๊ฐ์ง ์ ์๋, ์๋๋์ด๋ค.
๊ธฐ๋ฅ ๊ตฌํ์๋ง ๊ธ๊ธํ ์ฌ๋์ด ์๋๋ผ ๋์ ์์ผ๋ฅผ ๊ฐ์ง๊ณ ๋ฏธ๋๊น์ง ์๊ฐํ์ฌ ์ค๊ณํ๋ ๊ฐ๋ฐ์๊ฐ ๋์ด์ผ ํ๋ค๋ ๊ฒ์ ๋๊ผ๋ค. ๋จ์ํ ์ธ์ฃผ ๊ฐ๋ฐ์ ํ๊ณ ๋~! ํ๋ ๊ฒ์ด ์๋๋ผ, ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฒฝ์ฐ์๋ ์ง์์ ์ธ ์ ๋ฐ์ดํธ๊ฐ ์ด๋ฃจ์ด์ ธ์ผ ํ๊ณ ์์ฒด์๋น์ค๋ฅผ ๊ฐ๋ฐํ๋ ๊ฒฝ์ฐ์๋ ์ ์ง-๋ณด์-์ ๋ฐ์ดํธ๊ฐ ์ง์์ ์ผ๋ก ์ด๋ฃจ์ด์ ธ์ผ ํ๋ค. ์ด๋ ๊ฒ ๋ฒ์ ์ ์ ํ ๋๋ฅผ ๋๋นํด์ ์ด๊ธฐ๋จ๊ณ์์๋ถํฐ ๋ฏธ๋๋ฅผ ์ ํํ ์๋ ์๋ ๋ค์ด๋ฐ์ ์ง์ํ๊ณ , deprecated๋ ์ฐ๋ ค๊ฐ ์๋ ์ฝ๋๋ ๊ตฌ์กฐ์ธ์ง ํญ์ ์๊ฐํ๋ ๊ฒ์ด ์ค์ํจ์ ๋ฐฐ์ ๋ค. ๊ฐ๋จํ ๊ธฐ๋ฅ์ด๋ผ๋ ์ด๋ฐ ๊ฒ์ ํญ์ ์๊ฐํ๋ ๊ฐ๋ฐ์๊ฐ ๋์ด์ผ ํ๋ค.
๋ฏธ๋์ ์ผ์ด๋ ์ผ์ ์๋ฌด๋ ์์ธกํ ์ ์๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๋น์ฅ์ ๋จ๊ณ์์ ์ด๊ฒ deprecated๋ ์ฝ๋์ธ์ง ์๋์ง๋ ์ ํํ ์์ํ ์ ์์ง๋ง, ๊ทธ๋์ ์ด๋ ต์ง๋ง, ๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ธฐ์ฌํ ๋๊ตฐ๊ฐ์ ๋ฏธ๋์ ์ ์ง-๋ณด์-ํ์ฅํ ๋๋ฅผ ์ํด ์ง๊ธ ๋ฏธ๋ฆฌ๋ฏธ๋ฆฌ ์๊ฐ์ ๋ง์ด ํ๊ณ ์ฝ๋๋ฅผ ์ง์ผํจ์ ๋ค์ ํ ๋ฒ ๊นจ๋ซ๋๋ค.
์ฌ์ฉ์ flow๋ฅผ ์๊ฐํด์ผ ํจ์ ์ฌ๋ฌ ๋ฒ ๊ฐ์กฐํด๋ ์ง๋์น์ง ์๋ค
ํนํ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ๊ณต๋ถํ๋ค๋ฉด ์ง๊ฒน๋๋ก ๋ค์์ ๋ง์ด๋ผ๊ณ ์๊ฐํ๋ค. ์๋ฌด๋ฆฌ ๋ฉ์ง๊ณ ์ข์ ๊ธฐ์ ์ด๋ผ๋ ์ฌ์ฉ์๊ฐ ์ ์ฐ๋ฉด ์ ์ฐ๋๊ฑฐ๋ค. ๋ถํธํด์ ์์ด ์ ๊ฐ๋ฉด ๊ทธ ํ๋ก์ ํธ๋ ๊ทธ๋ฅ ์ฝ๋ฉ ์ง์ ์๋๋ฐ์ ๋์ง ์๋๋ค. ์ฌ์ค ์ฒ์ ๋ค์์ ๋ ๋ฌด์จ ๋ง์ธ์ง๋ ์๊ฒ ๋๋ฐ, ๋จธ๋ฆฌ๋ก๋ ์ดํด๋ ๋๋๋ฐ ์จ ๋ชธ์ผ๋ก ์ฒด๊ฐ์ ์ ์๋์๋ค. ๊ทธ๋ฐ๋ฐ ์ด๋ฒ์ imoji editor ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ํญ์ ๋ชจ๋ ํ์์์ 1์์๋ก ์๊ฐํ ๊ฒ์ด ์ด๊ฑฐ์๋ค๋ณด๋๊น ์ด์ ๋ ์๋ฟ๋๋ค. ์ญ์ ๋ญ๋ ์ง ์ง์ ๊ฒช์ด๋ด์ผโฆ
์์งํ UX ๋์์ด๋๋ PM์ด ํ๋ฏ์ด ์ฌ์ฉํ๋ฆ์ ๋ํ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ๊ณ ๊ทธ๋ก๋ถํฐ ๊ฒฐ๊ณผ๋ฅผ ๋์ถํด๋ด์ ๊ทผ๊ฑฐ๋ก ์ฐ๋ ๊ทธ ์ ๋๋ฅผ ํ ์ค ์๋ ๊ฒ์ ์๋์ง๋ง, โ๋๋ผ๋ฉด ์ด๋ ์ง ์์๊น?โ ๋ถํฐ ์์ํด์ โ์ด๋ฐ ์ฌ๋๋ ์์ ์ ์์ง ์์๊น?โ ํ๊ณ ๋๊ณ ๋ค์ํ๊ฒ ์๊ฐํด๋ณด๋ ์ฐ์ต์ ํ ์ ์์๋ ๊ฒ ๊ฐ๋ค.
๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๋์๋ ์๊ฐ์ ๊ฐํ๋ ๊ฒ์ ๊ฒฝ๊ณํ์
์ด์๋ณด๋์ ์ด์๋ค์ ํด๊ฒฐํ๋ ๊ณผ์ ์์ ํ์ ๋์์ ๋ฐ๊ธฐ๋ ํ๋ค. ์ด๋ค ๋ฌธ์ ๊ฐ ์๋๋ฐ ์๋ํ ๊ฒ์ ์ด๊ฑฐ์์ง๋ง ์ด๋์ ์๋๋คโฆ๋ก ์์ํ๋ฉด ๋ชจ๋ ํจ๊ป ๊ณ ๋ฏผํด์ฃผ์ จ๋ค. ๊ทธ ๊ณผ์ ์์๋ ๋ ํ๋ ๋ฐฐ์ ๋ ๊ฒ์ ํน์ ์ฌ๊ณ ๋ ์๊ฐ์ ๊ฐํ๋ ๊ฒ์ ๊ฒฝ๊ณํด์ผ ํ๋ค๋ ๊ฒ์ด๋ค. โ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๋ฐฉ๋ฒ์ด a๊ฐ์๋ฐโฆโ ์ถ์ ์๊ฐ์ด ๋ค๋ฉด ์๊พธ ๊ทธ a๋ก ๋ต์ ์ ํด๋๊ณ ์๊ฐํ๊ณ ๋ฆฌ์์นํ๊ฒ ๋๋ค๋ ๊ฒ์ด๋ค. ๊ทธ๋ผ ์ฌ์ค b๋ผ๋ ์์ฃผ ๊ฐ๋จํ ํด๊ฒฐ์ฑ ์ด ์์์๋ ๊ทธ๊ฑธ ๋ณด์ง ๋ชปํ๊ฒ ๋๋ค. ์ด๋ฐ ๋ถ๋ถ๋ค์ ๊ฒฝ๊ณํ๋ฉฐ ์ข ๋จ์ด์ ธ์ ๊ตฌ์กฐ์ ์ธ ๊ด์ ์์๋ ๋ณด๋ ๋ฑ, ์ง์ฝ์ ์ธ ์ฌ๊ณ ๋ฅผ ํผํด์ผ ํ๋ค.
๋ง๋ฌด๋ฆฌํ๋ฉฐ
์ด ๊ธด ๊ธ์ ๋๊น์ง ์ฝ์ผ์ ์ธ๋ด์ฌ ๋ง์ ๋น์ ์๊ฒ ๊ณ ๋ง๋ค๋ ๋ง์ ์ ํฉ๋๋ค. ์ด์ผ ํ๋