자바스크립트

 

 

인프런 :  모던 자바스크립트(javascript) 개발을 위한 ES6 강좌

 

1. 모던 자바스크립트 개발을 위한 ES6 강좌 소개

javascript 표준을 정의하는 ECMAScript 중 최근 가장 중요한 변화를 겪었던 ECMAScript 2015(이하 ES6) 를 학습해볼 수 있는 강좌입니다. ES6 에서 새롭게 추가된 syntax 와 기능들을 코드를 통해서 짚어보고 사용해 보면서 javascript 의 이런저런 특징들을 알 수 있게 됩니다. 또한 학습 중간 중간 '배열만들기' 와  '로또번호만들기' 등의 간단한 실습 예제들을 만들어, 생각해보면서 자기의 것으로 만들 수 있도록 도와주는 실습형 강좌입니다.

 

 

자바스크립트 테스트 사이트

1) https://jsbin.com/?html,js,console

 

2 ) https://codepen.io/pen/

 

 

소스 :  https://github.com/braverokmc79/javascript-modern-ES6

 

 

 

 

module

 

32.module(export & import)의 이해

강의  클릭

 

webpack 설치 

 

=> 참조   :  webpack을 활용해 맛깔나게 번들링 해보기

 

 

소스 : https://github.com/braverokmc79/javascript-modern-ES6/commit/557f73ab254cce03c175761326ce91d8d6b58f9b

 

실행 :

$ npm run start

 

index.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hell Code</title>
</head>
<body>
     <div id="root"></div>
     <script src="../dist/bundle.js"></script>
</body>
</html>

 

index.js

import './index.css';
import log from './myLogger';


log("my first test data");

 

myLogger.js

export default function log(data) {
    console.log(data);
}

 

 

 

 

32.module(export & import)기반 서비스코드 구현방법

강의  클릭

CodeSquad.js

export default class CodeSquad {
    constructor(props) {
        this.lectures = ['java', "iOS"];
    }

    getLectures() {
        return this.lectures;
    }

    getTime = () => {
        return Date.now();
    }
    getCurrentHour = () => {
        return (new Date).getHours();
    }
}

 

utility.js

const _ = {
    log(data) {
        if (window.console) console.log(data);
    }
}

export default _;

 

index.js

import CodeSquad from './CodeSquad';
import _ from './utility';

const cs = new CodeSquad();

_.log("my first test data");
_.log(`getCurrentHour :  ${cs.getCurrentHour()}`);
_.log(`Lectures of Codesquad are ${cs.getLectures()}`);



 

 

 

 

 

Proxy

33.Proxy로 interception기능구현

강의  클릭

 

문법:

let proxy = new Proxy(target, handler)
  • target – 감싸게 될 객체로, 함수를 포함한 모든 객체가 가능합니다.
  • handler – 동작을 가로채는 메서드인 '트랩(trap)'이 담긴 객체로, 여기서 프락시를 설정합니다(예시: get 트랩은 target의 프로퍼티를 읽을 때, set 트랩은 target의 프로퍼티를 쓸 때 활성화됨).

proxy에 작업이 가해지고, handler에 작업과 상응하는 트랩이 있으면 트랩이 실행되어 프락시가 이 작업을 처리할 기회를 얻게 됩니다. 트랩이 없으면 target에 작업이 직접 수행됩니다.

먼저, 트랩이 없는 프락시를 사용한 예시를 살펴봅시다.

let target = {};
let proxy = new Proxy(target, {}); // 빈 핸들러

proxy.test = 5; // 프락시에 값을 씁니다. -- (1)
alert(target.test); // 5, target에 새로운 프로퍼티가 생겼네요!

alert(proxy.test); // 5, 프락시를 사용해 값을 읽을 수도 있습니다. -- (2)

for(let key in proxy) alert(key); // test, 반복도 잘 동작합니다. -- (3)

위 예시의 프락시엔 트랩이 없기 때문에 proxy에 가해지는 모든 작업은 target에 전달됩니다.

  1. proxy.test=를 이용해 값을 쓰면 target에 새로운 값이 설정됩니다.
  2. proxy.test를 이용해 값을 읽으면 target에서 값을 읽어옵니다.
  3. proxy를 대상으로 반복 작업을 하면 target에 저장된 값이 반환됩니다.

 

그림에서 볼 수 있듯이 트랩이 없으면 proxy는 target을 둘러싸는 투명한 래퍼가 됩니다.

Proxy는 일반 객체와는 다른 행동 양상을 보이는 '특수 객체(exotic object)'입니다. 프로퍼티가 없죠. handler가 비어있으면 Proxy에 가해지는 작업은 target에 곧바로 전달됩니다.

 

 출처:https://ko.javascript.info/proxy

 

 

 

 

미니 프로젝트

34.step0. 미니 프로젝트 소개

강의  클릭

 

 

 

 

 

 

35.step1. nodeJS 기반 환경구성과 webpack

강의  클릭

 

 

소스 : https://github.com/braverokmc79/javascript-modern-ES6/tree/main/minproject

 

해당 프로젝트에서  : $npm install 

 

 

설치 안되면 npm i 대신에  yarn add 

 

 

웹팩 공식문서 :  webpack logo

https://webpack.js.org/guides/getting-started/

 

 

 

 

 

출처: https://junjangsee.tistory.com/entry/webpack을-활용해-맛깔나게-번들링-해보기 [개발 여행:티스토리]

 

webpack (웹팩)

현재 웹 시장에서는 bundler(번들러)를 아주 유용하게 사용하고 있습니다. 대표적으로 webpack, parcel, rollup 등 많은 번들러들이 있는데요, 그 많은 번들러들 중 웹팩은 가장 많이 사용되고 있으며 최고의 자리를 지키고 있는 번들러입니다.

 

최고의 자리를 지키는 이유에는 몇가지가 있는데 대표적인 이유는 바로 생태계가 넓으며, 광범위한 커스터마이징이 가능하다는 점입니다.

가장 많이 사용되고있는 만큼 생태계는 그만큼 넓을 수 밖에 없고 plugin을 통해 특정 기능을 내가 원하는대로 커스터마이징 할 수 있다는 것이 가장 장점입니다.

 

 

bundle (번들)

bundle의 뜻이 무엇일까요? 

바로 "묶음"입니다. 여기에 -er이 붙어주면 묶어주는 역할을 하는 bundler가 됩니다. 여기에 진행형이 붙는다면 bundling이 되겠죠?

즉, 묶어주는 것을 말하는데요 이 역할을 webpack이라는 도구가 해주게 됩니다.

그렇다면 도대체 뭘 묶어주는걸까요?

 

프로젝트 구조

웹팩이 5 버전이 추가됨에 따라 이전 버전인 4 버전으로 예제를 만들어보았습니다.

 

 

 

 

프로젝트 생성

먼저 예제를 구현할 프로젝트를 생성합니다.

npm init -y

 

package.json

 

{
  "name": "webpack-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

 

 

최초 프로젝트를 생성하면 위와 같이 생성됩니다.

그렇다면 웹팩을 프로젝트에 설치해야겠죠?

 

 

webpack 설치

웹팩을 활용하기 위해선 webpackwebpack-cli 두 가지가 필요합니다.

결과물에서 동작하는게 아닌 영향을 주는 빌드에 활용되는 친구들이니 dev로 추가하겠습니다. 

 

npm i --save-dev webpack@4.44.2 webpack-cli

 

 

4.44.2 버전을 설치한 이유는 현재 웹팩이 5 버전으로 변경되었기 때문입니다. 아직 충분한 자료가 없기에 4.44.2 버전으로 예시를 만들어보겠습니다.

 

설치가 완료되면 패키지에는 위 두 가지가 추가가 되어있을겁니다. 추가가 되었다면 루트 디렉토리에 webpack.config.js를 생성합니다.

 

앞으로 알아볼 프로퍼티들은 전부 webpack.config.js에 추가되는 부분이니 이점 참고해주시면 됩니다. 

자 이제 설치한 웹팩을 본격적으로 사용해보겠습니다.

 

 

Entry

entry는 웹팩이 파일을 읽기 시작하는 시작점입니다. 이 파일을 시작으로 하여 번들링을 진행합니다.

module.exports = {
  entry: './src/index.js',
};

 

 

src/index.js를 시작점으로 하여 번들링을 하겠다는 것을 의미합니다.

이제 시작을 했으니 끝이 있어야겠죠?

 

 

Output

빌드가 되서 최종적으로 번들링 파일이 나오는 지점을 설정합니다. 여기서는 pathfilename을 통해 경로와 파일명을 정할 수 있습니다.

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
};

 

 

 

=> 추가 설정 보기

출처: https://junjangsee.tistory.com/entry/webpack을-활용해-맛깔나게-번들링-해보기 [개발 여행:티스토리]

 

 

 

 

36.step2. babel preset 설정

강의  클릭

npm install --save-dev babel-loader @babel/core @babel/preset-env

 

 

 

 

37.step3. webpack-dev-server와 html 구성

강의  클릭

npm i --save html-webpack-plugin

 

 

index.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Project</title>
</head>
<body>
   
    <section class="controller">     
        <button class="start">코드스쿼드 블로그 입니다.</button>
    </section>

    <section class="blogList">
        <ul></ul>
    </section>

    <section class="like-list">
         <h4>내찜목록</h4>
         <ul></ul>
    </section>


</body>
</html>

 

index.css

.controller{
  margin: 1rem;
  color: red;
}


.controller button.start{
  width: 250px !important;
  height: 50px;
  font-size: 1.1rem;
  cursor: pointer;
  font-weight: bold;
  box-shadow: 2px 2px 2px rgba(0,0,0, 0.2);
}

.blogList{
  overflow: hidden;
}

.blogList li , .like-list  li{
  display: block;
  list-style: none;
  margin-top: 1rem;
  font-size: 1.0rem;
  width: 120px;
  height: 250px;
  border: 1px solid gray;
  float: left;
  margin: 1em;
  padding: 0.4em;
  word-wrap: break-word;
  position: relative;
  text-align: center;
}

.blogList a , .like-list a{
  text-decoration: none;
  color:#222;
  font-size: 0.9rem;
}


.blogList  a:hover, .like-list a:hover{
  color:gray;
}

.blogList .like , .blogList .unlike , .like-list .unlike{
    cursor: pointer;
    position: absolute;
    bottom: 0;
    color: #fff;
    width: 100%;
    left: 0;
    background: #3c3b3bee;
    border: none;
}

.blogList .like:hover{
    background: #000;
}

.blogList .unlike{
  cursor: pointer;
  position: absolute;
}

.unlike{
   background: #d61024ee !important;
   color: #fff;
}
.unlike:hover{
   background: #880c18ee;
    color: #fff;
}

.like-list h4{
  font-size: 1.2rem;
  font-weight: bold;
}

.blogList img , .like-list img{
  width: 100px;
}

 

main.js

class Blog {
    constructor() {
        console.log("Blog is start !");
    }
}

export default Blog;

 

index.js

import './css/index.css';
import blog from './main';

const myblog = new blog();

 

 

 

 

 

 

38.step4-1. XHR통신

 

강의  클릭

main.js

class Blog {
    constructor() {
        console.log("Blog is start !");
        const dataURL = "https://jsonplaceholder.typicode.com/photos?_start=0&_end=10";
        this.setInitData(dataURL);
    }

    setInitData(dataURL) {
        this.getData(dataURL);
    }

    async getData(dataURL) {
        const list = await fetch(dataURL).then(res => res.json()).then(data => data);
        console.log(" list type :", typeof list);
        list.map((v) => {
            console.log(v.title);
            return v;
        })

    }


}

export default Blog;


 

 

 

 

39.step4-2. bloglist 추가

강의  클릭

main.js

class Blog {
    constructor() {
        console.log("Blog is start !");
        const dataURL = "https://jsonplaceholder.typicode.com/photos?_start=0&_end=10";
        this.setInitData(dataURL);
    }

    setInitData(dataURL) {
        this.getData(dataURL);
    }

    async getData(dataURL) {
        const list = await fetch(dataURL).then(res => res.json()).then(data => data);
        this.insertPosts(list);

    }

    insertPosts(list) {
        const ul = document.querySelector(".blogList > ul");
        list.map((item) => (
            ul.innerHTML += `<li><a href=${item.url}>
            <img src="${item.thumbnailUrl}">
            ${item.title}</a >
            </li > `
        ));

    }

}

export default Blog;


 

 

 

40.step5. Set자료에 데이터 추가(찜하기기능)

강의  클릭

 

main.js

class Blog {
    constructor() {
        this.setInitVariables();
        this.registerEvents();
        this.likedMap = new Map();
    }

    setInitVariables() {
        this.blogList = document.querySelector(".blogList > ul");
    }


    registerEvents() {
        const dataURL = "https://jsonplaceholder.typicode.com/photos?_start=0&_end=10";
        const startBtn = document.querySelector(".start");

        startBtn.addEventListener("click", () => {
            const li = document.querySelectorAll(".blogList li");
            if (li.length === 0) this.setInitData(dataURL);
            else this.blogList.innerHTML = "";

        })

        this.blogList.addEventListener("click", ({ target }) => {
            const targetClassName = target.className;
            console.log("targetClassName : ", targetClassName);

            //like 버튼 클릭시 속성값 데이터를 가져온 후에 Set 저장한다.
            if (targetClassName === "like") {
                const setData = {
                    albumId: target.getAttribute("data-id"),
                    id: target.getAttribute("data-id"),
                    title: target.getAttribute("data-title"),
                    url: target.getAttribute("data-url"),
                    thumbnailUrl: target.getAttribute("data-url")
                }
                this.likedMap.set("key-" + setData.id, setData);

                this.likedMap.forEach((value, key) => {
                    console.log("key : ", key, value);
                })

            }

        });
    }

    setInitData(dataURL) {
        this.getData(dataURL);
    }

    async getData(dataURL) {
        const list = await fetch(dataURL).then(res => res.json()).then(data => data);
        this.insertPosts(list);

    }

    insertPosts(list) {

        list.map((item) => (
            this.blogList.innerHTML += `
            <li>
             <a href=${item.url}><img src="${item.thumbnailUrl}"></a >
                ${item.title}
                <button class="like" data-id="${item.id}" data-title="${item.title}"
                    data-url="${item.url}" data-thumbnailUrl="${item.thumbnailUrl}">찜하기</button>
            </li > `
        ));

    }

}

export default Blog;


 

 

 

 

 

41.step6. 찜목록뷰 업데이트

강의  클릭

 

main.js

class Blog {
    constructor() {
        this.setInitVariables();
        this.registerEvents();
        this.likedMap = new Map();
    }

    setInitVariables() {
        //fetch 코드스쿼드에서  가져오는   dataLlist
        this.dataLlist = null;
        this.blogList = document.querySelector(".blogList > ul");
        this.likeList = document.querySelector(".like-list > ul");

    }


    registerEvents() {
        const dataURL = "https://jsonplaceholder.typicode.com/photos?_start=0&_end=10";
        const startBtn = document.querySelector(".start");

        startBtn.addEventListener("click", () => {
            const li = document.querySelectorAll(".blogList li");
            if (li.length === 0) this.setInitData(dataURL);
            else this.blogList.innerHTML = "";

        })

        //코드스쿼드 블로그에서 찜버튼 클릭시 이벤트 처리
        this.blogList.addEventListener("click", ({ target }) => {
            this.likeAndUnLikeButtonEvent(target);
        });

        //찜목록 에서 버튼 클릭시 이벤트 처리
        this.likeList.addEventListener("click", ({ target }) => {
            this.likeAndUnLikeButtonEvent(target);
        });
    }

    likeAndUnLikeButtonEvent(target) {
        const targetClassName = target.className;
        // console.log("targetClassName : ", targetClassName);

        //like 버튼 클릭시 속성값 데이터를 가져온 후에 Set 저장한다.
        if (targetClassName === "like") {
            const setData = {
                albumId: target.getAttribute("data-id"),
                id: target.getAttribute("data-id"),
                title: target.getAttribute("data-title"),
                url: target.getAttribute("data-url"),
                thumbnailUrl: target.getAttribute("data-url")
            }
            //찜 목록에 추가
            this.likedMap.set("key-" + setData.id, setData);

            //찜 된 목록(div)의 클래스를 like 에서 unlike 로 변경하기.
            target.className = "unlike";
            target.innerText = "찜취소";

            //내찜목록 뷰에 추가
            this.updateLikedList();

        }

        if (targetClassName === "unlike") {
            //찜 취소를 클릭한 경우에, 찜하기로 다시 변경하고, 찜목을 제거하고, 찜 목로뷰로 랜더링한다.
            target.className = "like";
            target.innerText = "찜하기";
            const id = target.getAttribute("data-id");
            // console.log("찜 취소 키 값 : ", key);
            this.likedMap.delete("key-" + id);

            this.dataLlist.map((item) => {
                if (item.id == id) {
                    console.log(" 찜취소 아이디 : ", id);
                    const unLikeTarget = document.querySelector(`#blogList-${id} .unlike`);
                    unLikeTarget.className = "like";
                    unLikeTarget.innerText = "찜하기";
                }
            });

            //내찜목록 뷰에 제거
            this.updateLikedList();
        }
    }


    //내찜목록 
    updateLikedList() {
        let likedSum = "";

        Array.from(this.likedMap).map((value) => {
            console.log(value);
            const item = value[1];
            likedSum += `
            <li id="likeList-${item.id}">
             <a href=${item.url}><img src="${item.thumbnailUrl}"></a >
                ${item.title}
                <button class="unlike" data-id="${item.id}" data-title="${item.title}"
                    data-url="${item.url}" data-thumbnailUrl="${item.thumbnailUrl}">찜취소</button>
            </li > `
        });

        this.likeList.innerHTML = likedSum;
    }


    setInitData(dataURL) {
        this.getData(dataURL);
    }

    async getData(dataURL) {
        this.dataLlist = await fetch(dataURL).then(res => res.json()).then(data => data);
        this.insertPosts();

    }

    insertPosts() {

        this.dataLlist.map((item) => (
            this.blogList.innerHTML += `
            <li id="blogList-${item.id}">
             <a href=${item.url}><img src="${item.thumbnailUrl}"></a >
                ${item.title}
                <button class="like" data-id="${item.id}" data-title="${item.title}"
                    data-url="${item.url}" data-thumbnailUrl="${item.thumbnailUrl}">찜하기</button>
            </li > `
        ));

    }

}

export default Blog;


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

모든 사람들이 원망하고 번민하고 탐욕하는 속에서 나 혼자만이라도 벗어나 마음 편히 살자. -법구경

댓글 ( 4)

댓글 남기기

작성