본문 바로가기
javascript

2024-02-29

by rewind 2024. 2. 29.

28일 볼튕기기 이어서 작성

기존에는 클릭할 때마다 이동속도가 증가한다 -> 클릭해도 속도 변화가 없게 변경하기

// script부분↓↓
/*
    clearTimeout(timerId);
    이렇게 하면 되는 것 처럼 보이지만 사용자는 속을수 있다(자원을 만들었다 지웠다 반복) -> 좋은 아이디어가 아니다
    timerId = setTimeout(fMove,30);  // 재귀호출
    */
}
//fMove();
// script 위쪽에 작성 ↓↓
let isStart = false;    // 초기값 아직 시작 안된상태
const fStart = function(){
    if(!isStart){   // 처음 시작시에만 호출하므로 아무리 클릭해도 더이상 빨라지지 않는다
        fMove();
        isStart = true;
    }
}
// body부분 작성 ↓↓
<div id="myBall"></div> 
<button onclick="fStart()">시작</button>

 

stop버튼 만들기

// body부분↓↓
<button onclick="fStop()">끝</button>

// script부분↓↓
const fStop = function(){
    if(timerId){        // 타이머가 있을때만
        clearTimeout(timerId);
        isStart=false;
    }
}

 

프록시 서버 , 리버스 프록시 서버(Reverse Proxy Server), Reverse Proxy Nginx(역방향프록시)

 

데이터베이스를 사용하는 시스템에서 통계를 안하면(안내면) 안한것과 똑같다

 

통계 : 데이터 취합, 조합 -> 정보 , 흐름 , 변화

 

d3.js

chart.js -> getting started 샘플소스 가져오기 -> body부분에 추가

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <!-- chart.js는 아주 잘 만들어져서 , 한번만 확인하면 그 사용법을 잊어버리기가 더 어려움 -->
    <!-- 부모크기 조정시 차트 크기 조정가능 -->
    <div style="width: 600px;">
        <canvas id="myChart"></canvas>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script>
        //  const ctx = document.getElementById('myChart');
        //  -> 아래처럼 변경
        const ctx = document.querySelector('#myChart');

        // chart.js가 잘만들어진 이유는 생성자 리턴값에 옵션내용이 거의 다 들어있어서
        // 직관적으로 값을 조정할 수 있음
        const myChart = new Chart(ctx, {
            type: 'bar',/* bar , line , pie , doughnut , radar (차트종류) */
            data: {
                labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
                datasets:
                [
                    {
                        label: '# of Votes',
                        data: [12, 19, 3, 5, 2, 3],
                        borderWidth: 1
                    },
                    {
                        label: '인호 변화 추이',    // null?
                        data: [100, 190, 30, 50, 20, 0],
                        borderWidth: 1
                    }
                ]
            },
            options: {  // 검색으로 해결
                scales: {
                    y: {
                        beginAtZero: true
                    }
                }
            }
        });
        // null
        /*
        let newDataset = {
            label: '인호 변화 추이',    // null?
            data: [100, 190, 30, 50, 20, 3],
            borderWidth: 1
        };
        myChart.data.datasets.push(newDataset);
        */

//        alert((myChart.data.datasets[0].data[1]));
//        myChart.data.datasets[0].data[1] = 190;
        myChart.update();
    </script>
</body>

</html>

 

cdnjs.com -> 검색

chart.js 가 잘만들어진 이유?

chart.js에서 기억해야할 메소드 1개 -> update() -> 값을 변경했을 경우 다시 그려야하기 때문에(re-rendering)

 

버튼을 넣어보자 , 바디부분에 버튼 추가

<button onclick="fRandom()">값 변화</button>

<!-- script부분에 아래 추가 -->
const fRandom = function(){
    let ranArr = [];
    for(let i = 1; i <= 6 ; i++){
        let ranVal = Math.round(Math.random()*40) + 60; // 60 ~ 100까지
        ranArr.push(ranVal);
    }
    myChart.data.labels = ["본인" , "나" , "자기자신" , "로제","제니","있지","메롱"] // 라벨을 변경하고 싶으면??
    myChart.data.datasets[1].type = "line"; // 이런식으로 각 데이터마다 타입을 다르게 지정 가능, line과 bar만 mix가능하다
    myChart.data.datasets[1].data = ranArr;
    myChart.update();
    setTimeout(fRandom , 1000);
}

 

mixed도 가능하다(같이 사용가능한 차트를 동시에 보여준다?) -> line과 bar만 mixed 가능

 

가로축과 세로축을 바꾸려면? option에 indexAxis : 'y' 추가 -> 수평차트가 된다

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <button onclick="fRandom()">값 변화</button>
    <!-- chart.js는 아주 잘 만들어져서 , 한번만 확인하면 그 사용법을 잊어버리기가 더 어려움 -->
    <!-- 부모크기 조정시 차트 크기 조정가능 -->
    <div style="width: 600px;">
        <canvas id="myChart"></canvas>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script>
        const fRandom = function(){
            let ranArr = [];
            let ranArr1 = [];
            for(let i = 1; i <= 7 ; i++){
                let ranVal = Math.round(Math.random()*40) + 60; // 60 ~ 100까지
                let ranVal1 = Math.round(Math.random()*40) + 60; // 60 ~ 100까지
                ranArr.push(ranVal);
                ranArr1.push(ranVal1);
            }
            myChart.data.labels = ["본인" , "나" , "자기자신" , "로제","제니","있지","메롱"] // 라벨을 변경하고 싶으면??
            myChart.data.datasets[1].type = "line"; // 이런식으로 각 데이터마다 타입을 다르게 지정 가능, line과 bar만 mix가능하다
            myChart.data.datasets[1].data = ranArr;
            myChart.data.datasets[0].data = ranArr1;
            myChart.update();
            setTimeout(fRandom , 1000);
        }
        //  const ctx = document.getElementById('myChart');
        //  -> 아래처럼 변경
        const ctx = document.querySelector('#myChart');
        // chart.js가 잘만들어진 이유는 생성자 리턴값에 옵션내용이 거의 다 들어있어서
        // 직관적으로 값을 조정할 수 있음
        const myChart = new Chart(ctx, {
            type: 'bar',/* bar , line , pie , doughnut , radar (차트종류) */
            data: {
                labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
                datasets:
                [
                    {
                        label: '# of Votes',
                        data: [12, 19, 3, 5, 2, 3],
                        borderWidth: 1
                    },
                    {
                        label: '인호 변화 추이',    // null? , 이런식으로 추가도 가능
                        data: [100, 190, 30, 50, 20, 0],
                        borderWidth: 1
                    }
                ]
            },
            options: {  // 검색으로 해결
                indexAxis : 'y' ,
                scales: {
                    y: {
                        beginAtZero: true
                    }
                }
            }
        });
        // null
        /*
        let newDataset = {
            label: '인호 변화 추이',    // null?
            data: [100, 190, 30, 50, 20, 3],
            borderWidth: 1
        };
        myChart.data.datasets.push(newDataset);
        */
//        alert((myChart.data.datasets[0].data[1]));
//        myChart.data.datasets[0].data[1] = 190;
        myChart.update();
    </script>
</body>
</html>

 

차트를 보여주게 된다면 차트의 의미까지 어느정도는 설명하고 넘어가면 좋다 (차트가 아니더라도 통계가 추가 된다면)

 

aggridjs.html 생성 -> 아래 샘플코드 추가(샘 블로그)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Ag-Grid 시작 예제</title>
    <script src="https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js"></script>
</head>

<body>
    <h1>지금은 뉴진스 시대</h1>
    <!-- 테마는  -->
    <div id="myGrid" style="height: 250px; width:600px" class="ag-theme-quartz-dark"></div>
    <script>
        // 샘플 ROW 데이타 정의, DB Select의 1줄 Row들의 모임 list가 될거시당
        const rowData = [
            { name: "민지", birth: "2004-05-07", nationality: "대한민국" },
            { name: "하니", birth: "2004-10-06", nationality: "호주|베트남" },
            { name: "다니엘", birth: "2005-04-11", nationality: "호주|한국" },
            { name: "해린", birth: "2006-05-15", nationality: "대한민국" },
            { name: "혜인", birth: "2008-04-21", nationality: "대한민국" }
        ];

        // 통합 설정 객체, 아주 많은 속성들이 제공됨(일단 몇개만)
        const gridOptions = {
            rowData: rowData,
            columnDefs: [                            // 컬럼 정의
                { field: "name", headerName: "이름" },
                { field: "birth", headerName: "생일" },
                { field: "nationality", headerName: "국적" }
            ],
            autoSizeStrategy: {                    // 자동사이즈정책
                type: 'fitGridWidth',              // 그리드넓이기준으로
                defaultMinWidth: 150               // 컬럼 최소사이즈
            },
            rowHeight: 45                          // row 높이지정
        };

        const gridDiv = document.querySelector('#myGrid');
        //  new agGrid.Grid(gridDiv, gridOptions);  // deprecated
        const gridApi = agGrid.createGrid(gridDiv, gridOptions);
    </script>
</body>

</html>

 

객체지향데이터 스타일? , JSON?

테이블 -> rowData

정렬기능 : default

컬럼에 fiter : true 작성시 아래처럼 컬럼별로 해당 하는 검색기능 사용 가능(날짜면 날짜)

 

컬럼이 많아지면 직접 추가하기 불편하기 때문에 default Colum을 제공한다 -> 여기에 컬럼 추가하면 된다

defaultColDef: {
    flex: 1,       // 자동으로 같은 사이즈롱
    filter: true,
    resizable: true,
    minWidth: 100,
    headerClass: "centered"
},

 

dummyData.json 생성

[
    {"name": "태한경", "alias": "숨겨진 고민", "strong": "다 이해됨", "weak": "근거가 없음"},
    {"name": "공석규", "alias": "보면 다 이해", "strong": "담배피며 여유찾기", "weak": "찾을 여유가 없음"},
    {"name": "노해건", "alias": "수학의 달인", "strong": "수학잘함", "weak": "산수못함"},
    {"name": "뢰슬우", "alias": "허리 사선", "strong": "눈에 안띔", "weak": "존재가 안보임"},
    {"name": "태래은", "alias": "일단 직진", "strong": "의외로 귀여움", "weak": "외모는 전혀 안 귀여움"},
    {"name": "옥화정", "alias": "경로당 느낌?", "strong": "나이로 대우받기", "weak": "신체나이는?"},
    {"name": "도중희", "alias": "외모는 진지", "strong": "눈빛은 남자", "weak": "말투는 여성여성"},
    {"name": "구산아", "alias": "알람 그게 뭐얌", "strong": "계속 잠", "weak": "깨워도 잠"},
    {"name": "좌하주", "alias": "부끄부끄 초딩", "strong": "챙겨주고 싶음", "weak": "맛집을 모름"},
    {"name": "나승회", "alias": "나름욜씨미 ", "strong": "친한척하기", "weak": "안 친하고 픔"},
    {"name": "은진온", "alias": "멋짱이", "strong": "파란차색깔", "weak": "본인은 안 푸름"},
    {"name": "창승해", "alias": "깨달은자", "strong": "남의 실수 잘 찾기", "weak": "본인 실수는 못 찾음"},
    {"name": "곡루이스", "alias": "배탈", "strong": "넘 열심", "weak": "취미 없음"},
    {"name": "상현덕", "alias": "오마이걸", "strong": "유아 좋아함", "weak": "본인 나이는 어쩔"},
    {"name": "교근찬", "alias": "술쎄보임", "strong": "좋은마우스", "weak": "힘없음"},
    {"name": "연건한", "alias": "그저 웃지요", "strong": "답변은 웃음으로", "weak": "입이 할일이 없음"},
    {"name": "홍길동", "alias": "전공자", "strong": "서류상 전공자", "weak": "서류빼면 비전공자"},
    {"name": "김방구", "alias": "엄마", "strong": "노력 산더미", "weak": "결과는 티끌"},
    {"name": "김채현", "alias": "비밀의 숲", "strong": "잘 감춤", "weak": "보여주면서 감춤"}
]

 

aggridTest.html 생성후 아래 붙여넣기

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Ag-Grid Basic Example</title>
    <style>
        #wrapper {
            width: 650px;
            height: 500px;
            margin: 50px auto;
            text-align: center;
        }

        #myGrid {
            height: 100%;
        }

        /* 헤더 텍스트 가운데 정렬 위함 */
        .centered {
            .ag-header-cell-label {
                justify-content: center !important;
            }
        }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js"></script>
</head>

<body>
    <div id="wrapper">
        <h1>AG-Grid 꽤 잘 만들었어용</h1>
        <div id="myGrid" class="ag-theme-quartz-dark">
        </div>
        <button onclick="fExp()">내보내깅</button>
    </div>
    <script>
        // 가져오려는 데이터의 컬럼명과 맞춰줘야 한다
        const columnDefs = [
            { field: "name", headerName: "이름", sortable: true },
            { field: "alias", headerName: "별명", sortable: true, cellStyle: { textAlign: "left" } },
            { field: "strong", headerName: "장점", cellStyle: { textAlign: "left" } },
            { field: "weak", headerName: "단점", cellStyle: { textAlign: "left" } },
            { field: "birth", headerName: "생일", cellRenderer: MyComp }
        ];

        // 일단 빈 데이터 설정(초기값), 설정 안하면 주구창창 로딩중 메세지
        const rowData = [];

        // 설정 옵션: 중요, 위에 정의한 것들이 여기서 통합됨에 주목
        const gridOptions = {
            columnDefs: columnDefs,
            rowData: rowData,
            defaultColDef: {
                flex: 1,       // 자동으로 같은 사이즈롱
                filter: true,
                resizable: true,
                minWidth: 100,
                headerClass: "centered"

            },
            // 페이지 설정
            pagination: true,
            //paginationAutoPageSize:true,  // 요게 열려있으면 아래껀 무시당함!
            paginationPageSizeSelector: [5, 10, 20],  // 원하는 페이지 수 나열
            paginationPageSize: 10,    // 위에 꺼 중 하나를 선택
            onCellClicked: params => {
                console.log('cell was clicked', params);
                //alert("클릭하신 값은: " + params.value); //요게 있음 뭔가? 방해됨
            }
        };

        function getData() {
            var xhr = new XMLHttpRequest();
            xhr.open("get", "dummyData.json", true);
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    // 데이타 구조 억지 땜빵!
                    let rslt = JSON.parse(xhr.responseText);
                    for (let i = 0; i < rslt.length; i++) {
                        rslt[i].birth = ranDate();
                    }

                    //gridOptions.api.setRowData(rslt);  // deprecated

                    gridApi.setGridOption('rowData', rslt);  //아래 라인과 동일한 의미
                    //gridApi.updateGridOptions({ rowData: rslt });
                }
            }
            xhr.send();
        }

        // csv 내보내기, 별로 안 중요, 그냥 이대로 설정하고 값만 고치면 됨
        function fExp() {
            var v_params = {
                suppressQuotes: "true",                 // none, true
                columnSeparator: "   ",                 // default값이 ,
                customHeader: "이름 별명 장점 단점",    // 헤더명 추가 출력
                customFooter: "이거슨 푸터"             // 데이타 아래에 footer추가
            };
            gridApi.exportDataAsCsv(v_params);
        }

        // 그리드 셋업

        let gridApi;
        document.addEventListener('DOMContentLoaded', () => {
            const gridDiv = document.querySelector('#myGrid');
            // new agGrid.Grid(gridDiv, gridOptions);  // deprecated
            gridApi = agGrid.createGrid(gridDiv, gridOptions);
            getData();  // 데이터 불러오깅
        });


        function MyComp() {
            return this;
        }

        // AG-GRID 문서에 나오는 커스텀 Cell Renderer
        MyComp.prototype.init = function (params) {
            this.eGui = document.createElement('div');
            this.eGui.innerHTML = `
            <input type="date" value="" class="myCal" style="border:0px;width:0px"> 
            <span class="mySpan">${params.value}</span> 
        `;

            this.myCal = this.eGui.querySelector('.myCal')
            this.mySpan = this.eGui.querySelector('.mySpan')
            this.mySpan.onclick = () => {
                this.myCal.showPicker();
            }
            this.myCal.onchange = () => {
                console.log("check", this.myCal.value);
                this.mySpan.innerHTML = this.myCal.value;
            }
        }
        MyComp.prototype.getGui = function () {
            console.log("check1");
            return this.eGui;
        }

        MyComp.prototype.refresh = function () {
            console.log("check2");
            return true;

        }

        MyComp.prototype.destroy = function () {
            console.log("check3");
        }

        function ranDate() {
            let now = new Date();
            now.setDate(now.getDate() - Math.floor(Math.random() * 90));

            let rYear = now.getFullYear();

            let rMonth = now.getMonth() + 1;
            if (rMonth < 10) rMonth = "0" + rMonth;

            let rDate = now.getDate();
            if (rDate < 10) rDate = "0" + rDate;

            return `${rYear}-${rMonth}-${rDate}`;
        }
    </script>
</body>

</html>

 

Load(window.load 이벤트) , DomContentLoaded 차이?(면접질문체크)

window.load(onload) -> 리소스까지 모두 불러오면 발생

DomContentLoaded -> 태그해석만 끝나면 발생(load보다 약간 더 빠르다)

 

실행 후 확인

 

cellRenderer

 

csv -> comma-separated values -> 잘 사용하지 않다가 빅데이터 사용때문에 다시 사용함

(용량이 작다 -> JSON보다도 작다)

 

주소에서 community -> enterprise(유료버전)로 변경시 사용가능(상업적 용도만 아니면)

 

선언적 프로그래밍? 미리 짜여진걸 얼마나 잘 가져다 쓰는지

 

요새는 쿠키를 잘 사용하지 않고 로컬스토리지 , 세션스토리지를 주로 사용한다(왜지 보안문제??)

 

PWA : Progressive Web Application

 

로컬스토리지 사용법 알아보기 localStorage.html 생성

 

<!DOCTYPE html>
<meta charset="UTF-8">
<script>
// localStorage vs sessionStorage
// 영구저장        일시저장(브라우저 종료시 지워짐)
// 사용법은 완전히 동일하다(100% 일치)
// cookie보다 사용법이 쉽고 용량도 커서 점점 더 많이 사용됨(유용하다)
// PMA(Progressive Web Application)의 일부라 할 수 있다
// 객체지향 DB모양(JSON)으로 많이 만들어 사용한다
// 크롬기준으로 사이트당 5MB 정도
// 주로 사용처? 사용자 테마 , 사용자 설정값 등등

/* 기본 제공 명령어!
//localStorage.setItem("name", "현주");  // 쓰기
localStorage.setItem("짝궁", "현장");  // 쓰기
//localStorage.setItem("name", "로또");  // 수정(덮어쓰기 , key값이 같다)
//localStorage.merong="메롱";
// 객체의 속성을 내맘대로 줄 수있다. 위처럼 쓰면 key : merong , value : "메롱"

// alert(localStorage.key(0)); // ??
// alert(localStorage.key(1)); // ??

localStorage.removeItem("name"); // key값에 해당하는 value값을 지운다
//alert(localStorage.getItem("name"));
//alert(localStorage.getItem("짝궁"));
//alert(localStorage.length); // ??
//localStorage.clear(); // 싹다 지운다
*/

</script>

 

제한사항? Object를 직접 저장이 불가능하다 , string만 저장 가능 -> 직렬화/역직렬화 해야함

// 제한사항 : Object를 직접 저장 할 수 없음 , 오직 string만 저장가능
// localStorage.setItem("check" , {name:"이렇게 쓰면", alias:"어떻게되나 확인"});
// alert(localStorage.getItem("check"));
// alert({name:"이렇게 쓰면", alias:"어떻게되나 확인"});

// 객체를 저장할 떄는 꼭 문자열로 변환(serialize)해서 저장해야한다
// JSON.stringify
localStorage.setItem("check" , JSON.stringify({name:"이렇게 쓰면", alias:"어떻게되나 확인"}));
localStorage.setItem("check2" ,["인","호","여","친","null"]);   // 문제점?

//alert(typeof(localStorage.getItem("check2")));  // 꺼내보면 string타입이다, 아래와같이 작성
localStorage.setItem("check2" ,JSON.stringify(["인","호","여","친","null"]));   // 이렇게 작성