TIL - 21주차 코드

 

1. JS : 230417

1) ES2015

  • ES2015는 ES6를 의미한다.

  • 클래스와 모듈화가 ES6의 가장 큰 특징이다.


2) ORM

  • JPA : 원래는 SQL문을 안 써야 하는데, JPA가 JPQL로 SQL문을 써야 할 수도 있어서 별로 좋지 않은 프레임워크일 수도 있다.
    • Entity에 어노테이션이 너무 많이 붙어버린다.

3) ES6 개념

  • shape.js
let canvas = document.createElement("canvas");

canvas.width=500;
canvas.height=400;

// 1. canvas.style="border:1px solid black";

// 이게 맞다.
canvas.style.border="1px solid black";


// 2. 둘 다 사용하다!
// canvas.style.borderColor="blue";
canvas.style["border-color"]="blue";

document.body.append(canvas);   
// 3. 기본적으로 로드가 된 후 에러 발생 안 하려면, defer 설정을 해줘야 한다.
// defer를 안 쓰려면 window.onload()를 이용해야 한다!

// DOM은 사용자가 이용하기 위해 차별화를 둘 수 있다.

// 4. 상자를 가로로 4개 그리기(반복문 이용 )

let ctx = canvas.getContext("2d");

color = ["yellow", "blue", "green", "red"];

for(let i = 0; i<4; i++){
    ctx.fillStyle= color[i];
    ctx.fillRect(10+60*i, 10, 50, 50);
}

// 5. 박스 반복문 이용하기!!(모듈 넘기기!)
// function Box(x,y,w,h,color) {
//     this.x = x || 0;    // 초기화!
//     this.y = y || 0;
//     this.width = w || 50;
//     this.height = h || 50;
//     this.color = color || "black";
// }

// let boxes =  [
//     new Box(10,10,50,50,"yellow");
//     new Box(10,10,50,50,"green");
//     new Box(10,10,50,50,"blue");
//     new Box(10,10,50,50,"red");
// ]


// 6. 박스를 가로로 4개 그리기

let boxSize = 50;
let gap = 20;
let colors = ["yellow","green","blue","red"];
let boxes = [];

for(let i = 0; i<4; i++)
    boxes.push(new Box(i*(50 + gap), 0, 50, 50, colors[i]));

// 7-1. 박스 그리기!
for(let box of boxes)
    box.draw(ctx);
    

// 이렇게만 하면 prototype의 Box인지 기본 Box 함수 인지 알 수 없다. 
console.log(boxes[0].kor);  

console.log(Object.hasOwn(boxes[0],'x'));
console.log(Object.hasOwn(boxes[0],'draw'));    // false

console.log(typeof Box.prototype);  // object
console.log(typeof Box);  // function

// 8-1. function 이름으로 prototype 얻기
let proto = Box.prototype;

// 8-2. 물려받은 'proto'에서는 'draw' 함수가 있는지?
// 히지만, 우리는 Box라는 클래스를 만들었는데, 역시나 Box 함수와 같다.
// 객체 모델은 과거의 function 모델과 같다.****
console.log(Object.hasOwn(proto,'draw')); // true!

// 9. funtion object로 prototype 얻기
Object.getPrototypeOf(boxes[0]);


// ===============================================

// 10. Object 다루기!!

let box = new Box();
box.x++;

// console.log(`x:${box.getX()}`);
console.log(`x:${box.x}`); // 새로운 방식의 getter!!

let obj = {x:10, y:20};

// let obj1 = Object.create(obj);
// console.log(obj1);

Object.defineProperty(obj, 'z', {
    value:30,
    writable:false  // 값을 사용하게 할 수 있는지 boolean
});

obj.y = 50;
obj.z = 60; // z는 writable가 false라서 값이 바뀌지 않음!

console.log(obj);


// 11. Object 다루기!! : 이터러블

// 11-1. Object.create는 객체를 만든다.
// Object.defineProperty는 속성을 값으로 설정한다.
// Object.entries는 값이 배열로 나온다.

// 11-2. ***** writable를 설정해주면, Object에서는 index로 뽑아서 쓸 수 없다. *****
Object.defineProperty(obj, 'z', {
    value:30,
    writable:false,
    enumerable:false
});


// 11-3. *** Object.preventExtensions는 객체 속성을 잠궈버린다! *** 
Object.preventExtensions(obj);

// 11-4. entries는 Object의 값을 배열로 만들어준다.
for(let p of Object.entries(obj)){
    console.log(`key:${p[0]}, value:${p[1]}`);
}

// 11-5. ***** Object에서는 index로 뽑아서 쓸 수 없다. ***** 
// for(let p of obj)
//     console.log(p);


// 7. 박스 그리기! : ***이게 올바른 방식이다!!**(어렵다!!)
// for(let box of boxes){
//     // let x = box.x;
//     // let y = box.y;
//     // let w = box.w;
//     // let h = box.h;
//     // let color = box.color;

//     // let box = {x,y,w,h,color};
//     let {x,y,w,h,color} = box;  // 8. 디스트럭처링!*******
//     // [디스트럭쳐링 개념]
//     // 1) 배열 :
//     // 2) 중첩 :

//     ctx.fillStyle = color;
//     ctx.fillRect(x,y,w,h,color);
// }



// 9. 디스트럭처링 응용
let kors = [30,20,30,40];

let [kor1, kor2, kor3] = kors;

console.log(kor1);


// 10. 순서 바꾸기 
console.log(`kor1:${kor1}, kor2:${kor2}`);

[kor1,kor2] = [kor2,kor1];

console.log(`kor1:${kor1}, kor2:${kor2}`);

let both = [
    new Box(),
    new Box(10,40,50,50,"red")
];

{
    // 첫번째 Box의 color과 width를 얻기
    // let [b1,b2] = both;
    //console.log(b1);
    
    let [{color,width}, b2] = both;
    console.log(color);
    
}
  • box.js
// 8. class 등장!!

class Box{

    #x; // 은닉화를 지켜주기 위해서 private처럼 사용!

    constructor(x=0, y=0, w=50, h=50, color="black"){

        this.#x = x;    // 이제는 초기화를 위에서 한다.
        this.y = y;
        this.width = w;
        this.height = h;
        this.color = color;
    
    }

    // ** 기존 getter, setter *****
    // getX(){
    //     return this.#x;
    // }

    // setX(x){
    //     this.#x = x;
    // }

    // ** 새로운 getter, setter : 은닉화 기능 업! *****
    get x(){
        return this.#x;
    }

    set x(x){
        this.#x = x;
    }

    draw(){
        let {y,width:w,height:h,color} = this; 
        let x = this.#x;    // 추가!!

        ctx.fillStyle = color;
        ctx.fillRect(x,y,w,h,color);
    }

}



// // 캡슐화는 그 캡슐에 책임을 넘겨주는 것이다.
// // 5. 박스 반복문 이용하기!!

// // 7-1. 생성자 함수!!
// function Box(x,y,w,h,color) {

//     this.x = x || 0;    // 초기화!
//     this.y = y || 0;
//     this.width = w || 50;
//     this.height = h || 50;
//     this.color = color || "black";

//     // 하지만 지금 이런 코드는 문제가 많다.
//     // 함수가 100번 호출되면 함수가 100개 만들어져서, 문제가 생긴다!
//     // 그래서 우리는 prototype을 이용한다!
//     this.draw = function(ctx){
//         let {x,y,width:w,height:h,color} = this; 
//         ctx.fillStyle = color;
//         ctx.fillRect(x,y,w,h,color);
//     }

//     // this.draw = new function("","");

//     // ** 6-1. 프로토타입은 모든 박스 객체가 공유하는 함수이다.
//     // 즉, 프로토타입은 다른 스타일의 박스를 여러개 만들 수 있게 해준다.

//     // let proto = {
//     //     kor: 10,

//     //     draw:function(){
//     //         let {x,y,width:w,height:h,color} = this; 
            
//     //         ctx.fillStyle = color;
//     //         ctx.fillRect(x,y,w,h,color);
//     //     }
//     // };
//     // Box.prototype = proto;
//     // Box.prototype = proto1;
//     // Box.prototype = proto2;

//     // ** 6-2. 프로토타입은 모든 박스 객체가 공유하는 함수이다.

// 7-2. 구현하는 함수!!
//     Box.prototype = {
//         kor: 10,

//         draw:function(){
//             let {x,y,width:w,height:h,color} = this; 
            
//             ctx.fillStyle = color;
//             ctx.fillRect(x,y,w,h,color);
//         }
//     };
// }




2. 230418

1) JS Classes 개념

class Exam { 
    #kor;
    #eng;
    #math;

    // this에 은닉화를 위한 #이 붙는다!
    // 생성자 초기화 방법 중요!
    constructor(kor=0, eng=0, math=0){
        this.#kor = kor;
        this.#eng = eng;
        this.#math = math;

    }

    // JS의 getter와 setter!
    get kor(){
        return this.#kor;
    }

    set kor(kor){
        this.#kor = kor;
    }

    // JS 클래스 내부의 함수 생성
    total(){
        return this.#kor + this.#eng + this.#math;
    }
    avg(){
        return this.total()/3;
    }    


}

// 상속 받을 새 클래스 생성
class NewlecExam extends Exam{
    #com;
    
    // 상속은 생성자를 super로 받아야 한다.
    constructor(kor=0, eng=0, math=0, com=0){
        
        super(kor,eng,math); // 이렇게 하면 '4'라는 결과가 필요하다!
        this.#com = com;
    }

    // com이 추가되어서 total을 재 정의해야 한다!
    total() {
        return super.total() + this.#com;
    }

    #test(){
        console.log("hehehe");
    }
}
let exam = new NewlecExam(1,1,1,1);

console.log("exam: " + exam);
console.log("exam: " + exam.total());   

// 은닉성?? : 클래스 내부에서만 해당 함수 사용해서 밖에서는 호출 불가능! 
// exam.#test();    // 에러 발생





// "use strict";

// this.x = 30;
// x = 20;

// // JS는 함수에 변수를 인자를 담는 것이 아니라 arguments 컬렉션에 담는다!
// // 화살표 펑션은 매개변수로 값을 받을 수 있다!
// setTimeout(() => {
//     // console.log(arguments.length);
//     console.log(this.x);
// }, 3000);

// setTimeout(function(){
//     console.log(arguments.length);
//     console.log(this.x);
// }, 3000);


// // 람다(애로우 펑션)는 자기만의 this가 없다. 
// // 그래서 outer의 this를 사용한다. 
// class Text{
//     constructor(){

//         this.x=10;
//         setTimeout(()=>{
//             console.log(this.x);

//         },3000);
//         setTimeout(function(){
//             console.log(this.x);

//         },3000);
//     }
// }

// new Text();



2) JS의 Enhanced Object Literals 개념


// ====================== Enhanced Object Literals ======================
let kor = 3;
let eng = 4;
let math = 5;

let obj2 = {
    kor:kor,
    eng:eng,
    math:math,
    total:function(){
        return this.kor + this.eng + this.math;
    }
};  // normal object;



// "Enhanced Object" 
// 1. 변수를 이용해 속성을 정의할 경우, 변수명과 키가 같은 이름일 경우에는 키를 별도로 설정할 필요가 없다.
// 2, 함수를 정의할 때, : function 키워드를 사용할 필요가 없다.
let colname = "haha";
let enObj = {
    kor,
    eng,
    math,
    total(){
        return this.kor + this.eng + this.math;
    },
    "to-string":function(){
        console.log("hello");
    },

    [colname]:function(){
        console.log(colname);
    }

};

// 이상하긴 하다. 변수명이 키가 같은 경우 함수 이름이 같아야한다.
enObj.haha();	// haha



3) JS의 template String 개념

// ----------- Template String -----------
{   
    // String.raw와 ``가 Template String이다.
    let kor = 30;
    let eng = 40;
    let msg = String.raw`\\<span>
                yay~\n
                </span>\\`;

    let template = `kor:${kor}, eng:${eng}, msg:${msg}`;
    console.log(template);
}



4) JS의 Spread Operator 개념



// ----- Spread Operator ----------------

{
    function print(x,y,z){
        console.log(`x:${x}, y:${y}, z:${z}`);
    }
}

let ar = [2,3,5];

// print(ar[0], ar[1], ar[2]); 이것은 아래와 같은 코드이다.
print(...ar);




5) JS의 Array, Set, Map

  • entries, values, keys
    // 일반적인 언어에서 지원하는 콜렉션** : Array, Set, Map

    // 1) Set 계열 : 키가 값과 같다. - 값의 중복을 허용하지 않는다.
    // 2) List 계열 : Array는 값의 삽입의 위치가 키가 된다. 위치(Index) 기반의 콜렉션
    // 3) Map 계열 : 키를 따로 설정할 수 있다. 값에 키를 설정하기 위한 콜렉션으로 임시 객체를 대신해서 사용한다. 

    // *** 콜렉션이 가져야 할 기능 -> 값들을 열거할 수 있느냐?
    // *** JS에서 열거 서비스는 next라는 함수가 있따.

    // let lotto = [2,4,6,29,];
    let lotto = new Set();
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.

    console.log(lotto);

    // ------------------------------------------------------------
    // -------------------- JS에서 열거하는 방법!! --------------------

    lotto.entries().next();
    console.log(lotto.entries().next()); // entries는 키와 값을 한번에 묶어서 배열로 반환
    
    
    lotto.values().next();
    console.log(lotto.values().next()); // Set 계열이라서 keys와 values는 같다. 반환 

    lotto.keys().next();
    console.log(lotto.keys().next());   // Set 계열이라서 keys와 values는 같다. 반환 

}



5) Java의 Collection

  • Collection은 index를 관리할 필요가 없다. 공간을 신경쓰지 않아도 된다. 그래서, ‘가변길이 배열’이라고도 부른다.

a. Object :

  • 모든 클래스는 Object에 모든 데이터를 담을 수 있다. 하지만 값 형식(primitive)은 담을 수 없다.


b. Wrapper Class : 오토 Boxing

  • 그래서 primitive 타입인 int형은 Integer에 담아서는 사용할 수 있다.

  • 결론 : 그래서, 우리는 모든 데이터를 Object로 관리한다.


c. Generic :

  • Generic은 무엇이든 될 수 있다는 의미이다.

  • 컴파일러가 자료형을 정해준다. RunTime 환경이 객체를 만들어 준다.




6) Java의 Iterator

a. 열거형 서비스란?

  • 인덱스를 사용하지 않아도 값을 꺼낼 수 있다!

  • 이러한 방식의 문제점** : 멀티스레드에서 문제 발생!

  • *** 과정 : 멀티스레드에서 인덱스가 중간에 빠진 형태로 데이터를 전송해버린다!

  • *** 결론 : 인덱스를 스레드가 공유해서는 안 된다!

  • 따라서, 인덱스는 각자 가져간다. 즉, 이터레이터한테 index를 서로 달라고 하고 스레드를 공유한다!***


b. 이터레이터 개념 실습 코드

import java.util.Iterator;
import java.util.HashSet;
import java.util.Set;

public class App {
    public static void main(String[] args) throws Exception {

        // <Integer>를 우측에 써도되고 안 써도 된다. 
        Set<Integer> set = new HashSet<>();         

        set.add(3);
        set.add(34);
        set.add(35);
        set.add(36);

        // Set 콜렉션의 값을 꺼낼 때, iterator를 통해서 값을 꺼낸다! + next() 이용!
        // System.out.println( set.iterator().next());
        // System.out.println( set.iterator().next());
        // System.out.println( set.iterator().next());
        // System.out.println( set.iterator().next());

        // 열거형 서비스란? 인덱스를 사용하지 않아도 값을 꺼낼 수 있다!
        // 이러한 방식의 문제점** : 멀티스레드에서 문제 발생!
        // *** 과정 : 멀티스레드에서 인덱스가 중간에 빠진 형태로 데이터를 전송해버린다!
        // *** 결론 : 인덱스를 스레드가 공유해서는 안 된다!
        // 따라서, 인덱스는 각자 가져간다. 즉, 이터레이터한테 index를 서로 달라고 하고 스레드를 공유한다!***  

        Iterator<Integer> it = set.iterator();
        // System.out.println(it.next());
        // System.out.println(it.next());
        // System.out.println(it.next());
        // System.out.println(it.next());

        // 이터레이터를 이렇게 꼭 조건문으로 사용해줘야 한다.
        while(it.hasNext())
            System.out.println(it.next());
    }
}


c. 이터레이터를 이용한 for-each문

Iterator<Integer> it = set.iterator();

// 이터레이터를 이렇게 꼭 조건문으로 사용해줘야 한다.
while(it.hasNext())
    System.out.println(it.next());

// for-each문이 직접 이터레이터를 가져온다.
for(Integer n: set)
    System.out.println(n);



7) JS에서 Iterator 이용

    let lotto = new Set();
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.
    lotto.add(Math.floor(Math.random()*45) + 1);   // Math.random()은 기본적으로 0초과 1미만 수이다.

    // console.log(lotto);

    let it = lotto.values();
    let result;
    
    // 이터레이터를 꺼내는 방법 1 : 
    // 객체를 next를 통해 뽑아내서 result에 담고 done으로 boolean으로 반환 
    // 이 방식은 매우 복잡!
    while(!(result = it.next()).done)
        console.log(result.value);

    // 이터레이터를 꺼내는 방법 2 : 
    for(let n of it)
        console.log(n);
        
    // 이터레이터를 꺼내는 방법 3 : 디스트럭쳐링 이용!
    // entries는 키와 값을 모두 가지고있는 이터레이터이다.
    let kvit = lotto.entries(); 

    for(let [k, v] of kvit)
        console.log(`key:${k}, value:${v}`);
}    
    // 이터레이터 3번 활용
{

    let ar = [3,4,2,6,7];
    let kvit = lotto.entries(); 

    for(let [k, v] of kvit)
        console.log(`key:${k}, value:${v}`);
}

a. JS에서 Iterator 응용

  • 배열에 담아 인덱스로 값을 뽑아낼 수도 있다.

// 이터레이터 3번 활용
{

let ar = [3,4,2,6,7];
let kvit = lotto.entries(); 

for(let entry of kvit)
    console.log(`key:${entry[0]}, value:${entry[1]}`);
    
}



8) JS에서 Map 객체 이용!!

{
    let map = new Map();

    map.set("id",1);
    map.set("title","hello");
    map.set("writerId","newlec");

    let kvit = map.entries();

    for(let [k,v] of kvit)
        console.log(`key:${k}, value:${v}`);
    
    // forEach는 value부터 인자를 사용한다!
    map.forEach((v,k,m)=>{
        console.log(`key:${k}, value:${v}, cols:${m}`);
    });



}



8) Java에서 애로우 펑션 이용!!


System.out.println("------------------------------");

// 익명 클래스 모습
set.forEach((v)->{
    System.out.println(v);
});

// 컨슈머 클래스를 구현하기!(이렇게 구현하는 것이 어렵따!!)
// 그래서 아래 코드의 익명 클래스(화살표 펑션)로 구현하자!
set.forEach(new Consumer<Integer>() {
    public void accept(Integer value){
        System.out.println(value);
    }
});

// 익명 클래스를 구현하기는 쉽다.
set.forEach((v)->{
    System.out.println(v);
});





9) JS Symbols 개념

  • 구조가 같아도 이터러블로 사용하지 않는다!

  • 이터레이터 객체를 반환할 수 있는 보장이 있어야 이터레이터를 사용할 수 있다.

  • 결론 : 구조가 같다고 이터레이터라는 보장이 없다!

  • 그래서 등장한 것이 JS의 Symbols 개념이다.

  • 정의 : 접근 제어를 가능하게 해준다.

// --------- 나도 for of 문에서 열거하고 싶다 ~~~~~~~~

{
    // 이 상황에서 이터러블 하지 않는 에러 발생!!
    
    // let exam = {kor:10, eng:20, math:30};   

    // for(let n of exam)
    //     console.log(n);

    // console.log("-------------------------------------");


    // let lotto = [3,6,8,29,33,34];

    // for(let n of lotto)
    //     console.log(n);

    // console.log("-------------------------------------");

    // ================ Symbols 등장 개념 ========================

    // 구조가 같아도 이터러블로 사용하지 않는다! 
    // 이터레이터 객체를 반환할 수 있는 보장이 있어야 이터레이터를 사용할 수 있다.
    // 결론 : 구조가 같다고 이터레이터라는 보장이 없다!
    // 그래서 등장한 것이 Symbol 개념이다. 

}

{
    // ================ Symbols ========================

    // // 얘는 이름은 같은데 다른 역할을 하는 함수이다.
    // 그래서, 이름을 유니크하게 만들어서 다른 객체에서도 못쓰도록 단 하나의 함수로 만들어준다!

    // function iterator(){    
    //     console.log("아히유");
    // }

    // // function iterator(){
    // //     return [2,3,4];
    // // }

    // function print(it){
    //     for(let n of it)
    //         console.log(n);
    // }

    // print(iterator());


}


{
    // ================ Symbols ========================
    // 함수명이 심볼로 구현되어 있어야 한다!

    let s = Symbol();

    let exam = {
        kor:10,
        eng:20,
        [s](){
            return this.kor+this.eng;
        }
    };

    let aa = {
        a:20,
        b:30,
        total(){
            console.log("속ㅇㅈ?");
        }
    };

    // 함수명이 심볼로 구현되어 있어야 한다!
    // 그래서, 그것으로 심볼 [s]
    console.log(exam[s]());
}





3. JS : 230419

1) Symbol을 이용한 인터페이스 정의

{
    // ------- 1. [Symbol을 이용한 인터페이스] 정의 ------------
    // 심볼은 약속을 기반으로 동작한다. 
    let examInterface = {
        total: Symbol(),
        avg: Symbol()
    };

    class Exam{
        constructor(){
            this.kor = 20;
            this.eng = 30;
            this.math = 40;
        }

        [examSymbol.total](){
            return this.kor+this.eng+this.math;
        }

        avg(){
            return this.total()/3;
        }
    }

    let exam = new Exam();
    exam.total();    // (x)
    let result = exam[examSymbol.total]();
    console.log(result);
}



2) Iterator 인터페이스 구현하기!!

    let lotto = {
        // values(){
            [Symbol.iterator](){

            let nums = [2,13,24,21];
            let index = 0;

            // return 안의 함수인 next가 이터레이터를 구현시키는 부분이다.
            return {
                next(){
                    return {
                        // done은 마지막 값을 알려주려고 boolean 처리
                        done: index==4?true:false,    
                        value:nums[index++]
                    }
                }
            }
        }
    }



3) Iterator 인터페이스 사용하기!!

        // let it = lotto.iterator();
        // console.log(it.next().value);
        // console.log(it.next().value);
        // console.log(it.next().value);
        // console.log(it.next().value);
        for(let n of lotto)
            console.log(n);
    }
    console.log("================================");
    let ar = [12,13,14,15,16];

    {
        let ar = lotto.iterator();
        // console.log(it.next().value);
        // console.log(it.next().value);
        // console.log(it.next().value);
        // console.log(it.next().value);
        for(let n of ar)
            console.log(n);
    }




4) Generators 개념

{   

    // 4. [Generators]
    
    // Iterator를 구현을 쉽게 해주는 생성기(Generators) =======

    let exam = {
        kor:10;
        eng:39;
        math:20,

        // '*'이 제너레이터이고 yield를 통해 값을 열거해준다!!
        *[Symbol.iterator](){
            yield this.kor;
            yield this.eng;
            yield this.math;
        }

    };

    for(let n of exam)
        console.log(n):

    let lotto = {
    
        // 이 함수를 이터레이터로 만들고 싶으면, *을 붙여서 Generator을 사용 
        
        *[Symbol.iterator](){
            let nums = [2,13,24,21];

            let index = 0;

            // yield(양보라는 뜻)는 값을 멈추게해므로 값을 리턴해준다!
            // yield nums[index++];
            // yield nums[index++];
            // yield nums[index++];

            for(let i=0; i<nums.length; i++)
                yield nums[i];
        }
    };

    for(let n of lotto)
        console.log(n);

    console.log("================================");    

}



5) Unicode, 정규식 응용


{
    // 5. Unicode : 문자 코드오 RegExp 검색
    // ** 백엔드에서 크롤링해보기! 프론트의 크롤링은 'CORS'로 막혀버림.
 
    console.log("안녕".length);  // '2'
    console.log("".length);   // '1' 

    // let phone = "010-1234-2321";    // 패턴? 상수(*)?
    // let phone = "010-????-????";    // 패턴(*)을 만족하는 문자!!

    // *** /D/에서 '//'는 new RegExp()와 같은 정규식을 의미한다.

    let phone = "aassd010-1234-22222232";
    const exp = /^01[016789]-\d{3,4}-\d{4}$/    // ^, &가 시작과 끝

    console.log(exp.test(phone));

}




5-1) 정규식 응용 1

  • 크롤링이나 DB에서 데이터 검색시 사용!

let st = `hello good hoho
            haha bye
            nazzo noohoo`;

// ** a 또는 b또는 c또는 o까지 1개만 나오면 조건에 만족해서 그걸 반환 
// /[a-o]/g는 전역(글로벌)에서 다 찾는다.
// /[a-o]+/g는 이 문자 범위(a-o)의 문자가 나올때까지 찾기(단, 빈공백은 포함하지 않는다.)

let exp = /[a-o]+/g;    
let result = st.match(exp);
console.log(result);


5-2) 정규식 응용 2


// (a-o)의 문자가 나올때까지 그리고 1부터 5까지 범위 찾기
let st = `hello good hoho 3273
            haha bye 7821
            nazzo noohoo`;
let exp = /[a-o]+|[1-5]+/g;    
let result = st.match(exp);
console.log(result);


5-3) 정규식 응용 : 꺽음쇠를 넣어서 다음처럼 검색 1

  • <[a-o]+>가 꺽음쇠(<>)안에서 알파벳 범위로 정해지는 것이 꺽음쇠가 닫혀질 때(‘>’)까지 찾는다.

// 5-3. 정규식 응용 : 꺽음쇠를 넣어서 다음처럼 검색
// '<hello>', '32', '3' 순서로!

let st = `<hello> good hoho 3273
    haha <bye> 7821
    nazzo noohoo`;
let exp = /<[a-o]+>|[1-5]+/g;
let result = st.match(exp);
console.log(result);


5-4) 정규식 응용 : 꺽음쇠를 넣어서 다음처럼 검색 2(내가 한 것)

  • 이렇게 검색하면, 이런 결과 : ['<hello>', 'good', '</hello>', 'hoho', 'haha', '<bye>', 'nazzo', 'noohoo']
// '<hello>', '32', '3' 순서로!
// <[a-o]+>가 꺽음쇠(<>)안에서 알파벳 범위로 정해지는 것이 꺽음쇠가 닫혀질 때('>')까지 찾는다.

let st = `<hello> good </hello> hoho 3273
    haha <bye> 7821
    nazzo noohoo`;
let exp = /<[a-o]+>|[a-z]+|<\/[a-o]+>|<[a-z]+>/g;
let result = st.match(exp);
console.log(result);


5-4-정답) 정규식 응용 : 꺽음쇠를 넣어서 다음처럼 검색 2(정답)

  • ’.’은 문자인지 숫자인지 상관없이 검색한다.

  • 그래서, ‘.’은 꺽음쇄 사이에 문자인지 숫자인지 상관없이 검색한다.

  • 검색 결과 : [‘ good ’, ‘ 7821 ’]

// '<hello>', '32', '3' 순서로!
// <[a-o]+>가 꺽음쇠(<>)안에서 알파벳 범위로 정해지는 것이 꺽음쇠가 닫혀질 때('>')까지 찾는다.

let st = `<hello> good </hello> hoho 3273
    haha <bye> 7821 </bye>
    nazzo noohoo`;
// let exp = /<[a-o]+>|[a-z]+|<\/[a-o]+>|<[a-z]+>/g;
// let exp = /<\w+>+<\/\w+>/g;

// '.'은 꺽음쇄 사이에 문자인지 숫자인지 상관없이 찾음.
let exp = /<\w+>.+<\/\w+>/g;    

let result = st.match(exp);
console.log(result);


5-5. 정규식 응용 : 최종 모습

  • ’.’을 이용하여 검색

let html = `<div>
                <h2>hello</h2>
                <ul>
                    <li>okay</li>
                    <li>
                        <b>bye</b>
                    </li>
                </ul>
            </div>`

let exp = /<.>/;
let result = html.match(exp);
console.log(result);




6) 프로미스 개념 정리

  • promise 콜백의 중첩을 줄이는 도구

//---- promise 콜백의 중첩을 줄이는 도구 -------------------------
{
    // 이 함수는 우리가 만든 함수가 아니라고 가정하자.
    // function download(){
    //     // 다운로드 완료
    //     // 오랜 시간...이..흐..른뒤
    //     return [2,4,3];
    // }
    let url = "http://..";
    function download(url,callback){
        // 다운로드 완료
        // 오랜 시간...이..흐..른뒤
        callback([2,4,3]);
    }

    
    

    // -------------------------------------
    // 여기는 download 라이브러리를 사용하는 내 코드
    // let result = download(); // 오랜 시간을 잡아먹는 코드
    // console.log(result);    // 먹통 언제 오지??....ㅜㅜ

    // 내가 그랬지..오래 걸린다고..그니까.. 이따 완료되면 알려줄거니까
    // 네 코드를 부를 수 있게 함수를 알려줘~~
    function downloadHandler(result){
        console.log(result);    // 우앗..데이터가 왔다.
    }
    // 미리 함수를 만들어서 나중에 이 함수를 호출해서 다운로드 완료를 알려달라고 함수를 위임 함.
    /* let result = */ download(url,downloadHandler); 

    // 그런데 일반적으로는 함수를 미리 만들지 않고 넘겨주면서 정의한다.
    download(url,function(result){
        console.log(result);    // 우앗..데이터가 왔다.
    }); 

    // 위의 방식이 다음처럼 바뀌는 것을 바란다.
    // 1. 서비스 함수가 인자로 직접 콜백을 받지않고, 콜백을 받는 도구를 따로 두도록 하자.
    // 2. 콜백을 직접 받지 않음으로써 중첩을 줄이자.
    // 이런 것들을 위한 도구가 Promise 객체인가지유~
    console.log("--------promise ---------------")
    function downloadPromise(url){
        // 다운로드 완료
        // 오랜 시간...이..흐..른뒤
        return new Promise(function(resolve){            
            resolve([2,4,3]);            
        })
    }

    let promise = downloadPromise(url);

    promise
    .then(function(result){
        console.log("pro1-----------------");
        console.log(result);
        return result[0];    // 우앗..데이터가 왔다.
    })
    .then(function(n){
        console.log("pro2-----------------");
        console.log(n);
    });

    console.log("/---------- promise -------------")

}





4. JS DOM : 230420

  • dom.html
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
    <script type="module" src="dom.js" defer="defer"></script>
</head>
<body>
    <section>
        <h1>이미지목록</h1>
        <style>
            .s2 ul{
                padding : 0;
                list-style-type: none;
                display: flex;
            }

            .s2 span{
                display: inline-flex;
                width:100px;
                height:100px;
                background-color: blue;
            }

        </style>
    </section>
    텍스트
    <section id="s1" class="tool section1">
        <h1 class="aaa">문서</h1>
        <div>
            <button class="btn-up"></button>
            <button class="btn-down">아래</button>
        </div>
        <ul>
            <li class="current">a-current</li>
            <li>b</li>
            <li>c</li>
            <li>d</li>
            <li>e</li>
        </ul>
    </section>
    hello
</body>
</html>



  • dom.js


1) 노드 선택

  • node 개념

  • node 생성

  • Selectors API

window.addEventListener("load",function(){


// 1. 노드 선택
{


    // Selectors API : 
    // 장점은? 슈도 클래스를 이용해서 자유도가 높다.
    let s1 = document.querySelector("#s1");
    let lis = document.querySelectorAll("li");


    // node란? 문서를 구성하는 모든 객체
    // node의 뜻은? (나무) 마디 : 가지가 뻣어나가는 분기점


    
    // 직접 문서를 찾아가는 과정
    // let s1 = document.body.getElementsByClassName("section");
    // s1.getElmentsByClassName("aaa");
    // console.log(s1[0]);

    // document 객체를 이용해서 선택한 컴포넌트의 하위 자식을 
    // 선택할 때, 유용하다.
    // let s1 = document.getElementById("s1");
    // console.log(s1[0]);
    // let s1 = document.getElementsByTagName("section");
    // console.log(s1[0]);

    // 노드 생성
    // let node = document.createTextNode("hello");
    // document.body.append(node);

    

}

});


2) 노드 선택

  • 노드 지우는 방법

  • 노드 추가하는 방법

  • 노드 변경하는 방법

// 2. 노드 선택
{

    let s1 = document.querySelector("#s1");
    let first = document.querySelector("li:first-child");
    console.log(first); // 'NodeList [li]' 출력

    // ** 노드 지우는 방법 1 :
    // first.parentNode.removeChild(first); // Node 인터페이스의 기능 
    
    // ** 노드 지우는 방법 2 :엘리먼트 인터페이스의 기능 
    // first.remove(); 

    // ==============================

    // ** 노드 추가하는 방법 : 
    let li = this.document.createElement("li");
    let txt = this.document.createTextNode("c");
    let ul = s1.querySelector("ul");

    // ul.appendChild(li);
    // ul.appendChild(txt);
    // ul.append(li); // 엘리먼트 인터페이스의 기능
    ul.append(txt);
}

{
    // ** 노드 변경하는 방법 :  
    let ul = s1.querySelector("ul");
    let li = ul.querySelector("li:first-child");
    let newOne = ul.querySelector("li:last-child");

    // ** li->last 위치로 옮기는? 코드?
    // let oldOne = ul.replaceChild(newOne,li);
    // ul.appendChild(oldOne);
}


3) 노드 조작을 위한 예제 1

  • 노드 순회
    • current 항목을 위로 올리는 것
    • current 항목을 아래로 내리는 것
    // 3. 노드 조작을 위한 예제 1
{   // *** 노드 순회
    let s1 = document.querySelector("#s1");
    let btnUp = s1.querySelector(".btn-up");
    let btnDown = s1.querySelector(".btn-down");
    let current = s1.querySelector(".current");

    btnUp.onclick = function(){
        // current 항목을 위로 올리는 것

        // current.replaceChild();

        // ul.replaceChild(newOne,li);
        // ul.appendChild(oldOne);
        // let a = current.nextElementSibling;
        // a.insertAdjacentElement("afterend", current);
        let b = current.previousElementSibling;
        b.insertAdjacentElement("beforebegin", current);
        
    }

    btnDown.onclick = function(){

        // current 항목을 밑으로 내리는 것  
        // nextElementSibling(엘리먼트용) vs nextSibling(노드용)
        // ex)
        // current.nextElementSibling();
        // current.parentElement();

        // let ul = s1.lastElementChild;

        // console.log(s1.lastElementChild);
        // console.log(ul.firstElementChild);

        // ul.firstElementChild.remove(current);
        // ul.firstElementChild.append(current);

        // ** 가장 편한 방법 **
        // current.nextElementSibling.after(current);

        // ** 기본 방법 **
        // current.parentElement.replaceChild(current, current.nextElementSibling);
        
        current.insertAdjacentElement("beforebegin", current.nextElementSibling);
        
    }
}


4) 이벤트 객체

  • Event 객체의 속성


  • dom.html
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
    <script type="module" src="dom.js" defer="defer"></script>
</head>
<body>
    <section class="s2">
        <h1>이미지목록</h1>
        <style>
            .s2 ul{
                padding: 0;
                list-style-type: none;                
            }            
            .s2 span{
                display: inline-flex;                
                width:100px;
                height: 100px;
                align-items: center;
                justify-content: center;
                color: white;
                font-weight: bold;
                background-color: blue;
            }            
        </style>
        <ul>
            <li><span>1</span></li>
            <li><span>2</span></li>
            <li><span>3</span></li>
            <li><span>4</span></li>
        </ul>
    </section>
    <hr>
    텍스트
</body>


  • dom.js
window.addEventListener("load",function(){
{
    // 4. 이벤트 객체 

    // Event 객체의 속성
    // target / currentTarget
    // ┌─────────┐
    // │   A     │
    // │ ┌─────┐ │
    // │ │ B   │ │
    // │ │     │ │
    // 커서가 B를 클릭한거야
    // 그럼 현재 이벤트가 발생한 녀석은 B이다. 맞죠?

    // 첫 번쨰 , 예시 
    // A.onclick = function(e){
    //     console.log(e.target);  // -> ? -> B
    //     console.log(e.currentTarget);   // -> ? -> A
    // }

    // 두 번쨰 , 예시 
    // B.onclick = function(e){
    //     console.log(e.target);  // -> ? -> B
    //     console.log(e.currentTarget);   // -> ? -> B
    // }

}
});




5. JS 이벤트 객체 정리, CSS 애니메이션 : 230421

1) CSS 애니메이션 정리

  • dom.html

<!DOCTYPE html>
<html lang="en">
<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>Document</title>
    <script type="module" src="dom.js" defer="defer"></script>
</head>
<body>
    <section class="s2">
        <h1>이미지목록</h1>
        <style>
            @keyframes zoomIn {
                from {
                    transform: scale(1,1);
                }
                /* 중간에 중간단계로서 50%의 동작도 구현할 수 있다. */
                30%{
                    transform: rotate3d(1,1,1,90deg);
                }
                60%{
                    transform: rotate3d(2,-1,-1,-0.5turn);
                }
                to {
                    transform: scale(1.2,1.2);
                }
            }
            
            @keyframes selectedani {
                from {
                    transform: scale(1,1);
                }
                /* 중간에 중간단계로서 50%의 동작도 구현할 수 있다. */
                30%{
                    transform: rotate3d(1,1,1,90deg);
                }
                60%{
                    transform: rotate3d(2,-1,-1,-0.5turn);
                }
                to {
                    transform: scale(1.2,1.2);
                }
            }
            .s2 ul{
                padding: 0;
                list-style-type: none;
                display: flex;                
            }            

            .s2 span{
                display: inline-flex;                
                width:100px;
                height: 100px;
                align-items: center;
                justify-content: center;
                color: white;
                font-weight: bold;
                background-color: blue;

                /* 시간 초 딜레이 넣기 */
                /* transition: 0.5s; */
            }

            /* 여기서 li.current가 되는구나 JS를 굳이 안써도 된다. */
            .s2 li.current .box,
            .s2 li.current span
            {
                box-sizing: border-box;
                background-color: red;
                border:3px solid yellow;
                /* 박스를 사이즈를 키울 때, 전체 사이즈를 안 밀리고 싶으면 tranform을 이용한다! */
                /* 다른 것에 영향을 주지 않고 사이즈를 늘릴 수 있다. 그래서 tranform을 사용함. */
                /* transform: scale(1.2,1.2); */

                /* 애니메이션 이용하기! */
                animation-name: zoomIn;
                animation-duration: 3s;     /* 반복 시간 */
                animation-iteration-count: infinite;    /* 반복 횟수 */
                animation-direction: alternate;
            }      

            .s2 li.selected .box,
            .s2 li.selected span
            {
                /* 애니메이션 이용하기! */
                animation-name: selectedani;
                animation-duration: 2s;     /* 반복 시간 */
                animation-direction: alternate;
            }        
        </style>
        <ul>
            <!-- 기존보다 구조가 달라지면 문제가 생김. -->
            <li><span>1</span></li>
            <li>
                <div>
                    <span>2</span>
                </div>
            </li>
            <li><span>3</span></li>
            <li><span>4</span></li>
        </ul>
    </section>
    <hr>
    텍스트
    <section id="s1" class="tool section1">
        <h1 class="aaa">문서</h1>
        <div>
            <button class="btn-up"></button>
            <button class="btn-down">아래</button>
        </div>
        <ul>
            <li class="current">a-current</li>
            <li>b</li>
            <li>c</li>
            <li>d</li>
            <li>e</li>
        </ul>
    </section>
    hello
</body>
</html>



2) 이벤트 객체 속성 정리

{
    // 4-1. 이벤트 객체 

    // Event 객체의 속성
    // target / currentTarget
    // ┌─────────┐
    // │   A     │
    // │ ┌─────┐ │
    // │ │ B   │ │
    // │ │     │ │
    // 커서가 B를 클릭한거야
    // 그럼 현재 이벤트가 발생한 녀석은 B이다. 맞죠?

    // 첫 번쨰 , 예시 
    // A.onclick = function(e){
    //       // -> ? -> B
    //        // -> ? -> A
    // }

    // 두 번쨰 , 예시 
    // B.onclick = function(e){
    //       // -> ? -> B
    //        // -> ? -> B
    // }

    // let s1 = document.querySelector(".s2");
    // let ul = s1.querySelector("ul");
    // let current = null;
    // let lis = ul.querySelectorAll("li");

    // for(let li of lis)
    //     li.onclick = function(e){
    //         // current = e.target.parentElement;
    //         // current.style.backgroundColor = "red";
    //         // 
            
    //         // while로 반복문
    //         // 타겟의 부모가 li인 모든 것을 찾아낸다.(div, span)
           
    //         // 이렇게 하면, li를 출력한다. 
    //         current = e.target.parentElement;
    //         while(current.tagName !== "LI"){
    //             current = current.parentElement;
    //         }

    //         let span = current.firstElementChild;
    //         while(span.tagName !== "SPAN"){
    //             span = current.firstElementChild;
    //         }
    //         // for문으로 반복문(복잡)
    //         // let span = null;
    //         // for(let span = current.firstElementChild; 
    //         //     span.tagName !== "SPAN"; 
    //         //     span = span.firstElementChild)
    //         span.style.backgroundColor = "red";
            
    //     }
    // // current.style.backgroundColor = "red";


}



3) 이벤트 객체 2

  • selected.classList.add("selected"); 이용하기!
// 4-2. 이벤트 객체
{
    let s1 = document.querySelector(".s2");
    let ul = s1.querySelector("ul");
    let current = null; // ul.querySelectorAll("li.current");
    let lis = ul.querySelectorAll("li");

    for(let li of lis)
        li.onclick = function(e){

            // ** 클릭 이벤트가 두 번째 선택인지 파악하는 방법***

            // 1) 다음처럼 for문을 사용하는 코드는 바람직하지 않다.
            // {
            //     // 하수 코드
            //     let lis = ul.querySelectorAll("li");
            //     let hasCurrent = false;
            //     for(let li of lis)
            //         if(li.classList.contains("current")){
            //             hasCurrent = true;
            //             break;
            //         } 
            // }


            // 2) 해결법 : li를 모두 순회하지말고 이렇게 부모로 올라가서 
            // li.current를 가지고 있는지 파악!
            if(ul.querySelector("li.current"))
            //     

            let selected = ul.querySelector("li.current");
            if(current){
                
                // ** 실습 1 : 내가 만든 것 **
                // 엘리먼트 or 노드 자리 바꾸기!! : current, selected <-> e.target
                
                // 부모 : replace 
                // 형제 : replaceWith
                
                current = selected.replaceWith(e.target);
                
                current = selected.replaceWith(e.currentTarget, selected);
                
                if(e.currentTarget.prepend !== null)
                    current.append(e.currentTarget);

                e.currentTarget.after(selected);

                current = selected.replaceWith(selected, e.currentTarget);

                // ** 실습 2 : 내가 만든 것 **

                let selected = e.target.parentElement;
                while(selected.tagName !== 'LI')
                    selected = e.parentElement;

                let selectedBefore = selected.previousElementSibling;
                current.replaceWith(selected);

                if(selectedBefore)
                    selected.after(current);
                else   
                    ul.prepend(current);

                current.classList.remove("current");
                current=null;

                // *** 실습 3 ; 박스 위치 바꾸기,  selected.classList.add("selected"); 이용!!

                let selected = e.target.parentElement;
                while(selected.tagName !== 'LI')
                    selected = e.parentElement;

                selected.classList.add("selected");

                let selectedBefore = selected.previousElementSibling;
                current.replaceWith(selected);

                if(selectedBefore)
                    selected.after(current);
                else   
                    ul.prepend(current);

                current.classList.remove("current");
                current=null;

                return;
            }

            // * 현재 선택된 노드 찾기!
            current = e.target.parentElement;
            while(current.tagName !== "LI"){
                current = current.parentElement;
            }
            
            // * 선택된 노드임을 표시하는 스타일 변경!
            // **** 이 부분은 style 태그에서 실행 ****
            // let span = current.firstElementChild;
            // while(span.tagName !== "SPAN"){
            //     span = current.firstElementChild;
            // }
            // * 이렇게 스타일을 적용하는 것은 바람직하지 않다.
            span.style.backgroundColor = "red";
            current.classList.add(".s2 li.current .box");
            current.classList.add(".s2 li.current span");
            
            // *** 더 맞는 방식이다!??! ***
            // 위 코드에서 current가 li가 되어서 거기에 클래스 current 추가
            // 그래서, li.current가 되고
            // html에서 li.current의 구조인 .s2 li.current span를 갖게됌.
            current.classList.add("current");

            
        }
}    





4) 전체 코드 :

  • dom.html

<!DOCTYPE html>
<html lang="en">
<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>Document</title>
    <script type="module" src="dom.js" defer></script>
</head>
<body>
    <section class="s2">
        <h1>이미지목록</h1>
        <style>

            @keyframes current {
                from {
                    transform: scale(1,1);
                }

                /* 30%{
                    transform: rotate(360deg);
                }

                60%{
                    transform: rotate(-360deg);
                } */

                to {
                    transform: scale(1.2,1.2);
                }
            }

            @keyframes selectedani {
                from {
                    transform: rotate(0deg);
                }

                to {
                    transform: rotate(360deg);
                }
            }

            .s2 ul{
                padding: 0;
                list-style-type: none;
                display: flex;
            }

            .s2 span{
                display: inline-flex;                
                width:100px;
                height: 100px;
                align-items: center;
                justify-content: center;
                color: white;
                font-weight: bold;
                background-color: blue;

                /* transition: .5s; */                
            }            

            .s2 li.current .box,
            .s2 li.current span
            {
                box-sizing: border-box;
                background-color: red;
                border:3px solid yellow;
                /* width: 120px;
                height: 120px; */
                /* transform: scale(1.2,1.2); */
                animation-name: current;
                animation-duration: .5s;
                animation-iteration-count: infinite;
                animation-direction: alternate;
            }

            .s2 li.selected .box,
            .s2 li.selected span
            {                
                animation-name: selectedani;
                animation-duration: 2s;
                animation-direction: alternate;
            }
        </style>
        <ul>
            <li><span>1</span></li>
            <li>
                <div>
                    <span>2</span>
                </div>
            </li>
            <li><span>3</span></li>
            <li><span>4</span></li>
        </ul>
    </section>
    <hr>
    <section id="s1" class="tool section1">
        <h1 class="aaa">문서</h1>
        <div>
            <button class="btn-up"></button>
            <button class="btn-down">아래</button>
        </div>
        <ul>
            <li class="current">a-current</li>
            <li>b</li>
            <li>c</li>
            <li>d</li>
            <li>e</li>
        </ul>
    </section>
    hello
</body>
</html>


  • dom.js
window.addEventListener("load", function(){

    // 4. 이벤트 객체
    {
        let s1 = document.querySelector(".s2");
        let ul = s1.querySelector("ul");
        let current = null;//ul.querySelector("li.current");
        let lis = ul.querySelectorAll("li");

        for(let li of lis)
            li.onclick = function(e){

                // 이전에 current 로 선택된 li가 있는지를 알아보는 코드

                // 다음처ㅓㄻ for문을 사용하는 코드는 바람직하지 않다.
                // {
                //     // 하수 코드
                //     let lis = ul.querySelectorAll("li");
                //     let hasCurrent = false;
                //     for(let li of lis)
                //         if(li.classList.contains("currenet"))}
                //             hasCurrent = true;
                //             break;
                //         }
                // }

                // 위의 방법 보다는 다음처럼 그냥 있는지 확인할 수 있다.
                //let selected = ul.querySelector("li.current");
                //console.log(selected + "...");
                if(current){
                    console.log("두 번째 선택입니다.");

                    //current<->selected
                    // 1. 현재 2번째 선택된 엘리먼트 얻기
                    let selected = e.target.parentElement;
                    while(selected.tagName !== 'LI')
                        selected = e.parentElement;

                    selected.classList.add("selected");
                        
                    // let selectedBefore = selected.previousElementSibling;
                    // current.replaceWith(selected);
                    // if(selectedBefore)
                    //     selectedBefore.after(current);
                    // else
                    //     ul.prepend(current);

                    // current.classList.remove("current");
                    // current=null;
                    
                    
                    return;
                }


                //* 현재 선택된 노드를 찾고
                current = e.target.parentElement;
                while(current.tagName !== "LI")
                    current = current.parentElement;
                //* 선택된 노드임을 표시하는 스타일 변경                
                // let span = current.firstElementChild;
                // while(span.tagName !== "SPAN")
                //     span = span.firstElementChild;                 
                // 이렇게 스타일을 적용하는 것은 바람직하지 않다.
                // 다음과 같이 스타일을 미리 만들어 놓았다면 
                // 위와 같이 노드 찾기는 하지 않아도 된다.
                // .s2 li.current .box,
                // .s2 li.current span
                // {
                //     background-color: red;
                // }
                
                // span.style.backgroundColor = "red";
                // 위의 스타일 대신 클래스를 추가해보자.
                
                current.classList.toggle("current");
                
                console.log(current);
            }
    }

    // {
    //     let s1 = document.querySelector(".s2");
    //     let ul = s1.querySelector("ul");
    //     let current = ul.querySelector("li.current");
    //     let lis = ul.querySelectorAll("li");

    //     for(let li of lis)
    //         li.onclick = function(e){
    //             //current = e.target;
    //             // 현재 current가 span이다. 
    //             // 그럼 다음처럼 고쳐보자.
    //             // 그럼 자 ㄹ되나?

    //             //* 현재 선택된 노드를 찾고
    //             current = e.target.parentElement;
    //             while(current.tagName !== "LI")
    //                 current = current.parentElement;

    //             // 안되죠? 이유는 li의 배경색은 변경해도 바뀌지 않는다.
    //             //current.style.backgroundColor = "red";
    //             // 그럼 다음처럼 firstChild를 이용해 자식(span)의 스타일을 바꾸면 디ㅗ네요
    //             //current.firstElementChild.style.backgroundColor = "red";

    //             // 그런데 이 상황에서 조금만 더 생각해볼 수 있는여지가 있다.
    //             // UI 구조를 이용하는 방식에는 조금 더 생각해볼 것이 있다.
    //             // 1. 구조의 중첩이 있을 것 같다는 예정이 되었다면
    //             // .firstElementChild 보다는 자손이 될 수도 있기 때문에 
    //             // 검색 코드로 변경하는 것이 올바른 방법이 될 수 있다.
    //             // 즉. span은 확실한데 그게 자식일지, 자식의 자식일지, 자식의 자식의 자식일지... 모르는 상황
    //             //* 선택된 노드임을 표시하는 스타일 변경
    //             let span = current.firstElementChild;
    //             while(span.tagName !== "SPAN")
    //                 span = span.firstElementChild; 
                
    //             // 이렇게 스타일을 적용하는 것은 바람직하지 않다.
    //             span.style.backgroundColor = "red";

    //             // 2. 구조의 중첩에서 태그명이 고정이 아닌경우는?
    //             // li도 span도 다른 태그로 변경이 될 수 있다면? -> 클래스명을 이용하시오.
                


    //             // 현재 선택된 박스가 빨간색으로 변한다.
    //             // 하지만 선택에 문제가 있다. 이걸 대충하면 나중에 문제가 커질 수 있다.
    //             // current 값을 출력해보시오.
    //             console.log(current);


    //         }

        


    //     // 이벤트 종류
    //     // Event 객체의 속성
    //     // target / currentTarget
    //     // ┌─────────┐
    //     // │   A     │
    //     // │ ┌─────┐ │
    //     // │ │ B   │ │
    //     // │ │     │ │
    //     // 커서가 B를 클릭한거야
    //     // 그럼 현재 이벤트가 발생한 녀석은 B이다. 맞죠?
    //     // A.onclick = funciton(e){
    //     //    console.log(e.target);//->?->B
    //     //    console.log(e.currentTarget); //->?->A
    //     // }
    //     // B.onclick = funciton(e){
    //     //    console.log(e.target);//->?->B
    //     //    console.log(e.currentTarget); //->?->B
    //     // }
        

    // }

    // 3. 노드 조작을 위한 예제 1
    {
        let s1 = document.querySelector("#s1");
        
        let btnUp = s1.querySelector(".btn-up");
        let btnDown = s1.querySelector(".btn-down");
        let current = s1.querySelector(".current");;
        btnUp.onclick = function(){
            // current 항목을 위로 올리기
        }

        btnDown.onclick = function(){
            // current 항목을 밑으로 내리기
            // 그 다음놈???? 어떻게 얻지?
            // 노드 순회 **************
            // current.nextSibling?? 모든 노드를 대상으로함.
            // current.nextElementSibling; // Element 인터페이스 기능
            // current.parentElement;
            // current.previousElementSibling;
            // current.firstElementChild;
            // current.lastElementChild;

            // current
            // nextElementSibling
            // 다음을 내리는 코드 1
            // after() / append() / prepend() / append()
            //current.nextElementSibling.after(current);

            // 다음으로 내리는 코드 2
            // insertAdjacentElement() / replaceChildren()
            // 부모.replaceChild() / 자식.replaceWith(자식);
        }
    }

    // 2. 노드 조작
    {
        let s1 = document.querySelector("#s1");
        let first = s1.querySelector("li:first-child");
        console.log(first);
        // 삭제
        //first.parentNode.removeChild(first); // Node 인터페이스에 있는 기능
        //first.remove(); // Element 인터페이스에 있는 기능

        // 추가
        {
            let li = this.document.createElement("li");
            let txt = this.document.createTextNode("c");
            let ul = s1.querySelector("ul");
            // ul.appendChild(li); 
            // li.appendChild(txt);// Node 인터페이스에 정의되어 있는 기능
            //ul.append(li);  // Element 인터페이스
            li.append(txt);
        }
        // 변경
        {
            // let li = s1.querySelector("ul>li:first-child");
            // let last = s1.querySelector("ul>li:last-child");
            let ul = s1.querySelector("ul");
            let li = ul.querySelector("li:first-child");
            let newOne = ul.querySelector("li:last-child");

            // li->last 위치로 옮기는? 코드?
            //let oldOne = ul.replaceChild(newOne,li);
            //ul.appendChild(oldOne);
        }
    }

    // 1. 노드 선택
    {
        // Selecters API
        // let s1 = document.querySelector("#s1");
        // let lis = s1.querySelectorAll("li");

        //------------------------------------------

        // document 객체를 이용한 선택:id 또는 tagName
        // document 객체를 이용할 때는 섹션이나 콤포넌트 단위를
        // 선택할 때 유용하다.
        //let s1= document.getElementById("s1");
        //let s1 = document.getElementsByTagName("section");
        //let s1 = document.body.getElementsByClassName("section1");
        //console.log(s1[0]);

        // document 객체를 이용해서 선택한 콤포넌트의 하위 자식을
        // 검색할 때는 Element Node 객체의 기능을 이용한다.
        //s1.getElementsByTagName("");
        // s1.getElementsByClassName("aaa");
        // console.log(s1[0]);

        // 노드 생성
        // let node = document.createTextNode("hello");
        // document.body.append(node);
    }

});