- ๋ชจ๋ ํจํด : ํด๋ก์ ๋ฅผ ์ด์ฉํด์ ๊ฐ์ ์๋.
- ์บก์ํ๋ฅผ ์ํ๋๋ฐ?????? ์ ๋ํ ์๋ฌธ์ด ๊ณ์ ์์๋๋ฐ, ์์๋ฅผ ๋๋๋ผ๊ณ ๋ณ์๋ฅผ ํ๋๋ง ๋ฃ๊ณ get set ํ๋ ํจ์๋ฅผ ๋ค ๊ตฌํํด์ ๊ทธ๋ฅ ๊ฐ์ฒด๋ ์ฐจ์ด๊ฐ ์์ด๋ณด์๋ ๊ฒ ๊ฐ๋ค. ํด๋น ๊ฐ์ฒด์ ํ์ํ ๋ณ์๊ฐ ์ฌ๋ฌ๊ฐ์ด๊ณ , ์ด๋ค ๊ฒ์ ์๋ํ๊ณ ์ด๋ค ๊ฒ์ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅํ๊ณ ์ด๋ค ๋ณ์๋ ์ฝ๊ธฐ ์ฐ๊ธฐ๊ฐ ๋ค ๊ฐ๋ฅํด์ผ ํ๋ค๋ฉด..? ์ด๋ ๊ฒ ์ฐ๋ ์ ๋ฐ์!
// 1. ๋ชจ๋ ํจํด // age๋ ์ง์ ์ ๊ทผ ๋ถ๊ฐ๋ฅ. ์จ๊ฒจ์ง ๊ฐ์. // ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ ๊ฒ๋ ์๋. function person() { let age = 123; return { getAge: function () { return age }, setAge: function (data) { age = data } } }
- ์ฌ์ฉ์ ์ ์ ํ์ ํจํด : ์์ฑ์ ํจ์ ์ฌ์ฉ. ๊ฐ์ด ์๋๋์ง ์์.
// ์ธ์คํด์ค์์ ์ ๊ทผ ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ age๋ ์จ๊ฒจ์ง ๊ฐ์ด ์๋. function PersonType() { this.age = 135; } PersonType.prototype.getage = function () { return this.age } const instancePerson = new PersonType();
- ๊ทธ๋ฅ ๊ฐ์ฒด ์์ฑ
// 3. ๊ทธ๋ฅ Object // ๊ฐ์ฒด๋ฅผ ๋ฑ ํ๋๋ง ๋ง๋ค์ด์ ์ฐ๋ ๊ฒ์ ์ฑ๊ธํค ํจํด์ด๋ผ๊ณ ํ๋ค. let person2 = { age: 35 };
- ๋ชจ๋ + ์ฌ์ฉ์ ์ ์ ํ์ ํจํด
// ์ธ์คํด์ค๋ฅผ ํจ์จ์ ์ผ๋ก ์์ฑํ๋ฉด์ ๊ฐ์ ์๋. // IIFE ํจํด : ํจ์๋ฅผ ์คํํด์ผ ์์ฑ์ ํจ์๊ฐ return ๋๊ธฐ ๋๋ฌธ์ ์ฆ์ ์คํ ํจ์๋ฅผ ์ด์ฉํ๋ค!!!! const Person3 = (function PersonType2() { // closure ๊ณต๊ฐ. // ์ฌ๋ฌ๊ฐ์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด์ age ๊ฐ์ ๊ณต์ ํ๊ฒ ํ๋ ค๋ฉด ์์๊ฐ์ด ์๋๋ผ ๊ฐ์ฒด๋ก ๋ง๋ค์ด์ผ ํ๋ค. let age = { data: 142 }; // ํจ์ ๋ด๋ถ ๊ณต๊ฐ์์ ์์ฑ์ ํจ์๋ฅผ ๋ง๋ค์ด์ return function innerPersonType() { } innerPersonType.prototype.getAge = function() { // age์ ์ ๊ทผํ ์ ์๋ closure ํจ์. return age; } return innerPersonType; })(); // const Person3 = PersonType2(); const person3 = new Person3(); person3.getAge()
๋ฐ์ดํฐ ๊ด๋ฆฌ ๊ฐ์ฒด
textManager.js// ๋ชจ๋ + ์ฌ์ฉ์ ์ ์ ํ์ ํจํด์ ์ด์ฉ! const TextManager = (function () { let value = { data: "Hello Lions!"}; function innerTextManager() { } innerTextManager.prototype.getValue = function () { return value.data; } innerTextManager.prototype.setValue = function(newValue) { value = newValue; } return innerTextManager; })();
textManager.spec.js
describe('ํ ์คํธ ๊ด๋ฆฌ์์ ๋๋ค.', () => { // beforeEach : it ํจ์ ํธ์ถ ์ง์ ์ ์คํ. // ๊ฐ it์์ ๊ฒน์น๋ ๋ถ๋ถ์ ์ด๋ ๊ฒ ๋ก ๋นผ์ ์ฌ์ฉํ๋ค. let textManger; beforeEach(() => { textManager = new TextManager(); }) it('ํ ์คํธ ๊ฐ์ ์ ๋ฌํฉ๋๋ค', () => { const initValue = textManager.getValue(); expect(textManager.getValue()).toBe(initValue); }) it('ํ ์คํธ ๊ฐ์ ์์ ํฉ๋๋ค.', () => { const newText = { data: 'Hello Zebras' }; textManager.setValue(newText); expect(textManager.getValue()).toBe(newText.data); }) })
๋ทฐ์ด ๊ฐ์ฒด
viewManager.jsfunction ViewManager(textManager, options) { if(!textManager || !options.btnEl || !options.viewerEl || !options.inpTxt) { // null์ด ํ๋๋ผ๋ ์์ ๋ // ์ฌ์ฉ์ ์ ์ ์ค๋ฅ๋ฅผ ๋ง๋ค๊ณ ํ๋ก๊ทธ๋จ ์ข ๋ฃ. throw Error('์ ๋ฌ ์ธ์ ์ค์ ๋น ๊ฐ์ด ์กด์ฌํฉ๋๋ค.'); } this.textManager = textManager; this.viewerEl = options.viewerEl; this.inpTxt = options.inpTxt; options.btnEl.addEventListener('click', () => { this.changeValue(); }) } // ๋ฒํผ์ ๋๋ฅด๋ฉด ๊ฐ์ ์ธํ . ViewManager.prototype.changeValue = function() { this.textManager.setValue({ data: this.inpTxt.value }); this.updateView(); } ViewManager.prototype.updateView = function () { this.viewerEl.textContent = this.textManager.getValue(); }
viewManager.spec.js
describe('click event handling๊ณผ view๋ฅผ ๋ด๋นํ๋ ํจ์์ ๋๋ค.', () => { // dependency injection (์์กด ์ฃผ์ ) : ๋ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์์ ์๊ฒ ์ฃผ์ ์์ผ์ผ๋ง ์ฌ์ฉํ ์ ์์. // view๋ textManager๋ฅผ ์ ๋ฌ๋ฐ์์ผ ์ฌ์ฉํ ์ ์๋ค. let textManager, viewerEl, btnEl, inpTxt, viewManager; beforeEach(() => { textManager = new TextManager(); viewerEl = document.createElement('strong'); btnEl = document.createElement('button'); inpTxt = document.createElement('input'); viewManager = new ViewManager(textManager, {viewerEl, btnEl, inpTxt}); }); it('viewManager์ ์ธ์๊ฐ ์ ์ ๋ฌ๋๋์ง ํ์ธํฉ๋๋ค.', () => { const textManager = null; const btnEl = null; const viewerEl = null; const inpTxt = null; // // ์ธ์๊ฐ ์ ๋ฌ๋๋์ง ํ์ธํ๋ ํจ์. const actual = () => new ViewManager(textManager, { btnEl, viewerEl, inpTxt }); // ๊ฐ์ ์ ๋ฌํด์ฃผ๋ ํจ์ actual์ด๋๊น, ๊ฐ์ด ์ ๋๋ก ์ ๋ฌ๋๋์ง๋ฅผ ํ์ธํ๋ฉด ๋จ. // expect(ํจ์) ๋ก ์คํํ ์ ์๋ ํจ์๋ฅผ ์ ๋ฌํด์ผ ํ๋ค. expect(actual).toThrowError(); }) it('click event๊ฐ ๋ฐ์ํ์ ๊ฒฝ์ฐ changeValue ํจ์๋ฅผ ์คํํฉ๋๋ค.', () => { // spyOn : ํน์ ๋ชจ๋์ ํจ์ ๊ฐ์ spyOn(viewManager, 'changeValue'); btnEl.click(); // toHaveBeenCalled : ํจ์๊ฐ ํธ์ถ์ด ๋ ์ ์ด ์๋์ง ํ๋ณ - spyOn์ด ์์ด์ผ ํจ. expect(viewManager.changeValue).toHaveBeenCalled(); }) it('updateView ํจ์๋ฅผ ์คํํฉ๋๋ค.', () => { // updateView๋ changeValue๊ฐ ์ผ์ด๋๋ฉด ๋ฐ๋ก ์คํ. spyOn(viewManager, 'updateView'); viewManager.changeValue(); expect(viewManager.updateView).toHaveBeenCalled(); }) })
index.html
// ๋ถ๋ฌ์์ ์ฐ๊ฒฐ๋ง ํด์ฃผ๋ฉด ๋๋ค. const viewerEl = document.querySelector('.viewer'); const inpTxt = document.querySelector('.inp-txt'); const btnEl = document.querySelector('.btn-push'); // ๋ฐ์ดํฐ ๊ด๋ฆฌ / ๋ทฐ ๊ฐ์ฒด const textManager = new TextManager(); const viewManager = new ViewManager(textManager, { viewerEl, inpTxt, btnEl }); // ์ด๊ธฐ๊ฐ์ ํ๋ฉด์ ๋ณด์ฌ์ค. viewManager.updateView();
์ฝ๊ฐ.. ์ง๊ธ์ ๋น์ฐํ ๊ฐ์์ผ ๋๋๊ฑฐ ์๋๊ฐ..? ํ ์คํธ๊น์ง ํ ํ์๊ฐ ์๋? ์ถ์๋ฐ ๊ตฌ์กฐ๊ฐ ๋ ๋ณต์กํด์ง๋ฉด ํ ์คํธ ์ฝ๋๊ฐ ์ง๊ฐ๋ฅผ ๋ฐํํ ๊ฒ ๊ฐ๋ค.
๋ชจ๋ ์ฝ๋์ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑ๋ฐ๋๊ฒ ์๋๋ผ, ViewManager ์ฒ๋ผ ๋ญ๊ฐ๋ฅผ ๋ฐ์์์ ์คํํ๋ utility ์ฝ๋์๋ง ์์ฑํ๋ค.
ํ ์คํธ ์ฝ๋๋ ์ฌ๋์ด ์์ฑํ๋๊ฑฐ๋๊น ๋ฐฉ์ฌํ์ง ๋ง์!
ํ๋ก์ ํธ ํ์ ์ํ HTML/CSS ์ํ์ ๋ดค๋ค.
ํ๋ฆฐ ๋ฌธ์ ์ค ๋์ถฉ ์ฝ์๊ฑฐ๋ ์๋ ๋ด์ฉ์ธ๋ฐ ๋นจ๋ฆฌ ํ๊ณ ์ถ์ด์ ํธ๋ก๋ก ํ๋ค๊ฐ ํ๋ฆฐ ๊ฒ์ ์ ์ธํ๊ณ
์ง์ง๋ก ๋ชฐ๋ผ์ ํ๋ฆฐ ๊ฒ
๊ธฐ์ต ํ๋ ค๊ณ ํ๋ ์๋์ ํ๋ฆฐ ๊ฒ
js ๊ฐ์๋ฅผ ์๊ฐํ๋ค. ๋ก๋ ๋ฒํธ ์ถ์ฒจ๊ธฐ, textarea ๊ธ์ ์ ์ธ๋ ๊ฐ๋จํ ์ฝ๋๋ฅผ ์งฐ๋ค. ์๋ ์ ๋ค์์ผ๋ฉด ๋ง๋ฅ ๊ฐ๋จํ๋ค๊ณ ์๊ฐ ์ํ์ ๊ฒ ๊ฐ์๋ฐ, ๊ฐ๋จํ๊ฒ ๋๊ปด์ก๋ค.
์์ค์ฝ๋ - ๋ก๋ ๋ฒํธ ์ถ์ฒจ๊ธฐ, textarea ๊ธ์ ์ ์ธ๊ธฐ