ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [멋쟁이사자처럼] 프론트엔드 스쿨 7기 - 33일차 기록 및 복습 (DOM target, Class)
    Front-end 개발 2023. 8. 22. 11:50

    목차

    1. DOM target

    2. class


    객체 지향 프로그래밍 (출처:mazer.dev/)

    1.  DOM target


    1-1. target 

    - 브라우저 화면에서 이벤트가 발생하면, 브라우저는 이벤트 대상을 찾아가는 과정에서 이벤트가 차례로 실행되는 이벤트 전파(event propagation) 이 발생한다.

    - 부모부터 자식까지 일련의 요소를 모두 타고가며 진행되는 이러한 이벤트의 특징 덕분에 이벤트 객체에는 target, currentTarget 이라는 속성이 존재한다. 

    - target 속성에는 이벤트가 발생한 진원지의 정보가 담겨 있다.

    - target 속성을 통해 이벤트 리스너가 없는 요소의 이벤트가 발생했을 때도 해당 요소에 접근 할 수 있다.

    - currentTarget 속성에는 이벤트 리스너가 연결된 요소가 참조되어 있다.

    <html>
    <article class="parent">
        <ol>
            <li><button class="btn-first" type="button">버튼1</button></li>
            <li><button type="button">버튼2</button></li>
            <li><button type="button">버튼3</button></li>
        </ol>
    </article>
    
    <script>
        const parent = document.querySelector('.parent');
        parent.addEventListener('click', function (event) {
            console.log(event.target); // 선택한 요소 <button>
            console.log(event.currentTarget); // EventListener에 연결된 요소 <article>
        })
    </script>
    </html>

     

    1-2. 이벤트 위임(Event Delegation = 캡쳐링&버블링, 이벤트 전파) : 면접 질문

    - 이벤트 리스너가 없어도 마치 리스너가 있는 것 처럼 사용 할 수 있는 테크닉을 이벤트 위임이라고 한다.

    - event.target.nodeName 은 대문자로 해야 한다.

    - 캡처링, 버블링 단계는 이벤트가 발생하고 찾기 위해서 일어난다. DOM 트리 최상위부터 훓게 된다.

    - button에는 하위요소가 텍스트기 때문에 텍스트만 바뀌지만 li는 하위요소에 button이 있어서 button까지 텍스트로 바뀐다.

    <body>
        <article class="parent">
            <ol>
                <li><button class="btn-first" type="button">버튼1</button></li>
                <li><button type="button">버튼2</button></li>
                <li><button type="button">버튼3</button></li>
            </ol>
        </article>
    
        <script>
            const parent = document.querySelector('.parent');
            parent.addEventListener('click', function (event) {
                console.log(event.target);
                if (event.target.nodeName === "BUTTON") {
                    event.target.textContent = "버튼4";
                }
                if (event.target.nodeName === "LI") {
                    event.target.textContent = "버튼4";
                }
                // console.log(event.currentTarget);
            })
        </script>
    </body>

     

    1-2. 이벤트의 this

    - 이벤트 리스너 콜백 함수의 this 값은 이벤트가 연결된 노드를 참조한다.

    - 일반 함수일 경우에는 이벤트가 발생하는 노드 객체를 가리킨다.

    <body>
        <article class="parent">
            <ol>
                <li><button class="btn-first" type="button">버튼1</button></li>
                <li><button type="button">버튼2</button></li>
                <li><button type="button">버튼3</button></li>
            </ol>
        </article>
    
        <script>
            const parent = document.querySelector('.parent');
            parent.addEventListener('click', function (event) {
                console.log(this);
                console.log(event.currentTarget);
    
                // 이벤트가 발생한 대상을 조건식으로 특정하기
                if (event.targetnodeName == "LI") {
                    event.target.textContent = "hello";
                }
            });
        </script>
    </body>

    - 이벤트 리스너 콜백 함수가 화살표 함수일 경우 this 값은 상위 스코프를 가리킨다.

    <body>
        <article class="parent">
            <ol>
                <li><button class="btn-first" type="button">버튼1</button></li>
                <li><button type="button">버튼2</button></li>
                <li><button type="button">버튼3</button></li>
            </ol>
        </article>
    
        <script>
            const parent = document.querySelector('.parent');
            parent.addEventListener('click', (event) => {
                console.log(this);
                console.log(event.currentTarget);
    
                // 이벤트가 발생한 대상을 조건식으로 특정하기
                if (event.targetnodeName == "LI") {
                    event.target.textContent = "hello";
                }
            });
        </script>
    </body>

    - 상위 스코프에서 this 값을 상속받는 화살표 함수를 사용하여 메서드를 정의해볼 수 있다.

    - this 는 전역이거나 객체(인스턴스)이거나 상위스코프거나 3종류로 좁혀서 생각할 수 있다.

    myObj = {
        name: 'jaehyun',
        walk() {
            parent.addEventListener('click', () => {
                console.log(this.name, 'is walking');
            });
        }
    };
    myObj.walk();

     

    1-3. select box 이벤트 위임으로 개선하기

    - 추가된 총 기능

     (1) 버튼을 누르면 목록이 나와야한다.

     (2) 목록의 버튼을 누르면 버튼의 텍스트가 첫번째 버튼에 반영되어야 한다.

     (3) li 를 동적으로 생성한다.

     (4) 이벤트를 이벤트 위임을 통해서 구현한다.

    <body>
        <h3>셀렉트 박스 만들기</h3>
        <div class="custom-select">
            <button class="btn-select">최애 프로그래밍 언어</button>
            <ul class="list">
            </ul>
        </div>
        <script>
            // 1. 버튼을 누르면 목록이 나와야한다.
            const btn = document.querySelector('.btn-select');
            btn.addEventListener('click', function () {
                btn.textContent = '최애 프로그래밍 언어';
                btn.classList.toggle('on');
            });
            // 2. 목록의 버튼을 누르면 버튼의 텍스트가 첫번째 버튼에 반영되어야 한다.
            // const btns = document.querySelectorAll('.list button'); // node list (유사배열객체)
            // btns.forEach((itemBtn) => {
            //     itemBtn.addEventListener('click', function () {
            //         console.log(itemBtn.textContent);
            //         btn.textContent = itemBtn.textContent;
            //         btn.classList.remove('on');
            //     })
            // })
    
            // 3. li 를 동적으로 생성해주세요.
            const list = document.querySelector('.list');
            const arrLang = ['Python', 'Java', 'JavaScript', 'C#', 'C/C++'];
    
            // HTML 요소를 생성하는 예전 방식
            // arrLang.forEach((item) => {
            //     const li = document.createElement('li');
            //     const btn = document.createElement('button');
            //     btn.setAttribute('type', 'button');
            //     btn.textContent = item;
            //     // li.appendChild(btn); // appendChild 는 생성된 요소를 반환한다.
            //     // list.append(li); // append는 반환값이 없다.
            //     list.appendChild(li).appendChild(btn); // 연결해서 쓸 수 있다.
            // })
    
            // 배열을 통한 동적인 요소 생성
            arrLang.forEach((item) => {
                const li = document.createElement('li');
                li.innerHTML = `<button type='button'>${item}</button>`
                list.appendChild(li); // append는 반환값이 없다.
            })
    
            // 4. 이벤트를 이벤트 위임을 통해서 구현해주세요.
            list.addEventListener('click', (event) => { // 2번의 반복문 보다 더 빠르다.
                // console.log(event.target.nodeName);
                if (event.target.nodeName === "BUTTON") {
                    btn.textContent = event.target.textContent;
                    btn.classList.remove('on'); // 가장 가까운 상위 요소 제거
                }
            })
        </script>
    </body>

     

    1-4. preventDefault()

    - 브라우저의 기본 이벤트 동작을 취소한다.

    - 브라우저는 HTML 태그를 통해 여러가지 기능들을 제공한다.

    - 하지만 때때로 그러한 기능이 방해가 되는 경우가 있다.

    이렇듯 종종 브라우저의 기본 동작을 중지하고 자바스크립트를 통해 기능을 처리하고자 할 때 사용한다.

    - <a> 태그의 링크 이동을 막고, form 의 제출(submit)을 중단하는 예제

    <body>
        <a href="https://www.naver.com" class="link">네이버 링크입니다만..</a>
    
        <form action="">
            <button type="submit" class="submit">제출</button>
        </form>
    
    
        <script>
            const link = document.querySelector('.link');
            link.addEventListener('click', (event) => {
                console.log('clicked');
                // event.preventDefault();
            })
    
            const myForm = document.querySelector('form');
            myForm.addEventListener('submit', () => {
                console.log('submit!!');
            });
    
        </script>
    </body>

    - form 은 서버 사이트(server side) 렌더링을 기준으로 개발되었고, 제출(submit) 이벤트 발생 후 새로 고침된다.

    - 서버 사이트 렌더링은 form 을 받고 서버에서 클라이언트에 완성된 HTML 화면 전체를 보낸다.

    - react.js, vue.js 프레임워크인 SPA 특징은 새로 고침이 되지 않고, 전달 받은 데이터를 브라우저에서 화면을 만든다.

     

    1-5. preventDefault 실습

    - 이벤트 타입 'contextmenu' 를 이용해 브라우저 상의 마우스 오른쪽 버튼 이벤트를 막고,
    - '해당 페이지에서는 오른쪽 클릭을 제한한다.' alert 띄우기

    <script>
        document.addEventListener('contextmenu', (event) => {
            event.preventDefault();
            alert('해당 페이지에서는 오른쪽 클릭을 제한합니다!!');
        })
    </script>

     

    1-6. stopPropagation 

    - 앞에서 우리는 preventDefault를 통해 브라우저의 기본 이벤트 동작을 취소 해봤다.

    - 하지만 이때 우리가 앞에서 배웠던 이벤트 흐름, 즉  이벤트 전파(event propagation)를 막지는 못한다.

    - 이벤트 전파를 의도적으로 중단하고 싶을 경우에는 stopPropagation 을 사용한다.

    - 캡처링/버블링 이벤트와 복합적으로 생각할 수 있는 예제이다.

    <body>
        <form action="">
            <button type="submit" class="submit">제출</button>
        </form>
    
        <script>
            const btn = document.querySelector('form');
            btn.addEventListener('click', (event) => {
                event.preventDefault(); // btn 의 submit 동작은 막는다.
                console.log('submit');
                // 이벤트의 흐름을 차단합니다.
                event.stopPropagation(); // 이후 이벤트 전파(bubling) 단계를 막는다.
            }) // default === false, 버블링 이벤트
    
            // 캡처링 이벤트로 만들면 버블링 이벤트에서 전파를 막아도 소용없습니다.
            document.body.addEventListener('click', () => {
                console.log('event still alive!!')
            }, true); // capturing event (btn 보다 body 의 event가 먼저 실행된다)
        </script>
    </body>

     

    1-7. stopPropagation 실습

    - 내용
     (1) p 태그를 클릭하면 p 태그의 컨텐츠를 출력하는 alert 창을 띄워주고

     (2) 삭제 버튼을 클릭하면 삭제할 것인지를 물어보는 confirm 창을 띄워주고, 확인을 누르면 P태그를 삭제한다.

    - window.confirm('삭제하시겠습니까?') 는 확인을 누르면 true 를 리턴하고, 취소를 누르면 false 반환

    <body>
        <h1>나의 todo list</h1>
        <p>1. 오늘 저녁에는 부대찌게를 끓여 먹겠다.<button type="button">삭제</button></p>
        <p>2. 후식으로 슈팅스타를 먹겠다.<button type="button">삭제</button></p>
        <p>3. 자기 전에 반드시 내일 아침 메뉴를 생각해두겠다.<button type="button">삭제</button></p>
    
        <script>
            // 1. P 태그를 클릭하면 p 태그의 컨텐츠를 출력하는 alert 창을 띄워주고
            // 2. 삭제 버튼을 클릭하면 삭제할 것인지를 물어보는 confirm 창을 띄워주고, 확인을 누르면 P태그를 삭제합니다.
    
            // p 태그를 선택하여 클릭 이벤트를 붙여줍니다.
            const ps = document.querySelectorAll('p');
            ps.forEach((item) => {
                item.addEventListener('click', (event) => {
                    alert(item.firstChild.textContent);
                    // console.log(item.firstChild.replace('삭제',''));
                    // console.log(item.firstChild.slice(0,-2));
                }); // }, true); // 캡처링(true)으로 바꿔주면 alert 가 먼저 실행된다.
            });
    
            // btn 태그를 선택하여 클릭 이벤트를 붙여줍니다.
            const btns = document.querySelectorAll('button');
            btns.forEach((item) => {
                item.addEventListener('click', (event) => {
                    const result = confirm('삭제하시겠습니까?');
                    event.stopPropagation(); // 버블링 이벤트 전파를 중단한다.
                    if (result) {
                        item.closest('p').remove(); // 상위 가장 가까운 'p'
                        // item.parentElement.remove(); // 상위 요소
                    }
                });
            });
        </script>
    </body>

    2. Class


    2-1. 객체지향 프로그래밍

    - 소프트웨어 개발 방법론은 크게 정보 공학/객체지향/컴포넌트 기반/애자일 방법론으로 나눌 수 있다.

    - 객체지향 프로그래밍(Object Oriented Programming, OOP)은 프로그래밍 방법론 중에 하나로,

    - 프로그램을 작성할 때 객체들을 만들어 서로 소통하도록 하는 방법이라고 할 수 있다.

    - 우리가 앞서서 배운 자바스크립트 객체는 데이터의 묶음이라면 객체 지향의 객체는 우리가 표현하고자 하는 구체적인 사물을 추상적으로 표현한 것이라고 볼 수 있다.

    - 그리고 또한 객체는 행동과 상태를 가진다. 여기서 행동은 메소드, 상태는 프로퍼티 정도로 이해하면 된다.

    - 객체와 객체가 서로 메소드를 통해 상호작용하게 하는것이 바로 객체지향 프로그래밍이라고 할 수 있다.

    <body>
        <script>
            const me = {
                name: '한재현',
                address: '제주도 제주시 인다 1길',
                phoneNum: '010-8000-0000',
                canWalk: function () {
                    console.log('재현이가 걷는다.');
                },
                teaching: function (student) {
                    student.levelUp();
                }
            }
    
            const lion = {
                level: 1,
                levelUp: function () {
                    this.level++;
                }
            }
    
            me.teaching(lion);
            console.log(lion);
        </script>
    </body>

     

    2-2. 생성자 (constructor)

    - 생성자란 객체를 만들 때 new 연산자와 함께 사용하는 함수

    - 생성자의 장점은 생성자를 통해 생성된 객체는 같은 프로퍼티와 메서드를 공유할 수 있다는 것이다.

    - 아래는 새로운 배열을 생성하는 내장 생성자의 예시이다.

    let myArr = new Array(1,2,3);

    - 자신만의 생성자를 만들 수도 있다. 생성자는 함수이기 때문에 기본적으로 함수가 필요하다.

    - 생성자 함수는 암묵적으로 대문자로 시작하는 이름을 가지는 것으로 약속되어 있다.

    function Factory(){}
    let robot1 = new Factory();
    
    console.log(robot1 instanceof Factory); // 생성자를 통해 생성된 instance 인지 확인

    - Factory 생성자 함수는 따로 return 값을 가지지 않지만 new키워드가 앞에 붙게되면 실행되었을 때 자동적으로 객체를 생성하고 반환합니다. 이렇게 반환되어 만들어진 객체를 다른 말로 인스턴스(instance) 라고 합니다.

    - 생성자 함수와 객체의 관계는 instanceof 로 확인할 수 있습니다.

    - 생성자 함수에서는 const 나 let 보다는 this 를 활용한다.

    - 생성자 함수의 this 는 인스턴스를 가리킨다.

    - 생성자 함수를 사용하면 다시 여러줄로 추상화할 필요없이 객체를 계속 찍어낼 수 있다.

     

    2-3. 생성자 함수 실습

    - 실습: 음식 이름의 배열을 전달하면 배열안에서 랜덤하게 메뉴를 뽑아내는 로봇객체의 생성자를 만들어보세요.

    <body>
        <script>
            // 음식 이름의 배열을 전달하면 배열안에서 랜덤하게 메뉴를 뽑아내는 로봇객체의 생성자를 만들어보세요.
            function RobotFactory(foodNames) {
                // Math.random() ===> 배열의 인덱스
                this.menu = foodNames;
                this.sayMenu = function () {
                    // 배열의 길이에 해당하는 범위의 랜덤한 값을 뽑아냅니다.
                    const index = parseInt(Math.random() * this.menu.length); // 0 ~ 2.999
                    console.log(`삐리비리비리 오늘의 메뉴는! ${this.menu[index]}`);
                }
            }
    
            const robot = new RobotFactory(['청국장', '물냉면', '돔배고기']);
            robot.sayMenu();
        </script>
    </body>

     

    2-4. 프로토타입 (prototype)

    - 모든 인스턴스들이 공유하는 공간

    - 손쉽게 객체를 생산할 수 있지만, 객체의 메서드를 등록 할때마다 새로운 메모리 주소에 함수를 생성하고 있다.

    function NewFactory(name){
        this.name = name;
        this.sayYourName = function(){
            console.log(`삐리비리. 제 이름은 ${this.name}입니다. 주인님.`);
        }
    }

    - 객체의 메서드를 등록 할 때마다 함수가 선언되고 할당되고 있다.

    robot instanceof RobotFactory; // true
    robot2 instanceof RobotFactory; // true
    robot.sayMenu === robot2.sayMenu; // false

    - 바로 이 부분이 문제입니다. 우리는 100개의 객체를 생성할때마다 역시 100개의 함수를 새로 만들고 있는 것이다.

    - 이러한 자원의 낭비를 해결하기 위해 등장한 것이 바로 프로토타입이다.

    function NewFactory2(name){
        this.name = name;
    }
    
    NewFactory2.prototype.sayYourName = function(){
        console.log(`삐리비리. 제 이름은 ${this.name}입니다. 주인님.`);
    }
    
    const robot = NewFactory2('john');
    const robot2 = NewFactory2('david');
    
    console.log(robot instanceof NewFactory2); // true
    console.log(robot2 instanceof NewFactory2); // true
    
    console.log(robot.NewFactory2 === robot1.NewFactory2); // true

     

Designed by Tistory.