엘리스 16일차 월요일 실시간강의 비동기

치즈말랑이·2022년 4월 25일
0

엘리스

목록 보기
17/47
post-thumbnail

이고잉님 강의

비동기

오늘은 비동기를 배웠다.

npx json-server --watch db.json 터미널에 이거 치면 그 폴더에 db.json파일이 생성되고 서버가 켜진다.
json 파일에 있는 데이터를 전송할 수 있게 되는 것이다.
참고: https://redux-advanced.vlpt.us/3/01.html

http://localhost:3000/에 들어가면 다음과 같은 화면이 뜬다.

db.json 파일 내용을 다음과 같이 수정했다.

{
  "topics": [
    {
      "id": 1,
      "title": "html",
      "body": "html is ..."
    },
    {
      "id": 2,
      "title": "css",
      "body": "css is ..."
    },
    {
      "title": "깐쭈럴",
      "body": "control",
      "id": 3
    }
  ]
}

http://localhost:3000/topics 링크로 접속하면 저 내용이 그대로 보인다.
http://localhost:3000/topics/1 로 접속하면 id가 1인 내용이 보인다.

<html>
    <body>
        <header>
            <h1><a href="/" onclick="
                event.preventDefault();
                welcome(event);                    
            ">WEB</a></h1>
        </header>
        <nav>
            <ol>
                <!-- 
                    <li><a href="/read/1">html</a></li>
                    <li><a href="/read/2">css</a></li> 
                -->
            </ol>
        </nav>
        <article>
            <!-- 
                <h2>Welcome</h2>
                Hello, web 
            -->
        </article>
        <ol id="control">
            <!-- 
                <li><a href="/create">Create</a></li>
                <li><a href="/update">Update</a></li>
                <li><a href="/delete">Delete</a></li> 
            -->
        </ol>
        <script>
            // const topics = [
            //     {id:1, title:'html', body:'html is ...'},
            //     {id:2, title:'css', body:'css is ...'},
            //     {id:3, title:'js', body:'js is ...'}
            // ];
            let nextId = 4;
            let selectedId = null;
            function navHandler(e){                
                // 1. 링크가 작동하지 않아야 한다. 
                e.preventDefault();
                // 2. 아이디 값을 알아낸다. 
                selectedId = Number(e.target.id);
                read();
                
            }
            function nav(){
                fetch('http://localhost:3000/topics')
                    .then(res => res.json())
                    .then(topics => {
                        const tag = topics.map(e=>`
                        <li>
                            <a href="/read/${e.id}" id="${e.id}">
                            ${e.title}
                            </a>
                        </li>`).join('');
                    document.querySelector('nav>ol').innerHTML = tag;
                    })
                    document.querySelector('nav>ol').innerHTML = 'loading ...'
                
            }
            function welcome(e){
                document.querySelector('article').innerHTML = `<h2>Welcome</h2>Hello, WEB`;
                selectedId = null;
                control();
            }
            function read(){
                fetch('http://localhost:3000/topics/' + selectedId)
                    .then(res => res.json())
                    .then(topic => {
                        const content = `<h2>${topic.title}</h2>${topic.body}`;
                        document.querySelector('article').innerHTML = content;
                        control();
                    })
                // const topic = topics.filter(e => e.id === selectedId)[0]; 
                // const content = `<h2>${topic.title}</h2>${topic.body}`;
                // document.querySelector('article').innerHTML = content;
                // control();
            }
            function createHandler(e){
                e.preventDefault();
                const t = e.target.title.value;
                const b = e.target.body.value;
                fetch('http://localhost:3000/topics/', {
                    method:'POST',
                    headers: {
                      'Content-Type':'application/json'  
                    },
                    body:JSON.stringify({title: t, body: b})
                    
                })
                    .then(res => res.json())
                    .then(data => {
                        console.log(data);
                        nav();
                        selectedId = data.id;
                        read();
                    })
            }
            function create(){
                const content = `
                    <form>
                        <p><input type="text" name="title" placeholder="제목"></p>
                        <p><textarea name="body" placeholder="본문"></textarea></p>
                        <p><input type="submit" value="create"></p>
                    </form>
                `;
                document.querySelector('article').innerHTML = content; 
            }
            function updateHandler(e){
                e.preventDefault();
                const t = e.target.title.value;
                const b = e.target.body.value;
                fetch('http://localhost:3000/topics/'+selectedId, {
                    method:'PUT',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body:JSON.stringify({title:t, body:b})
                })
                    .then(response=>response.json())
                    .then(data=>{
                        nav();
                        selectedId = data.id;
                        read();
                    })
            }
            function update(){
                fetch('http://localhost:3000/topics/' + selectedId)
                    .then(res => res.json())
                    .then(topic => {
                        
                        const content = `
                    <form>
                        <p><input type="text" name="title" placeholder="제목" value="${topic.title}"></p>
                        <p><textarea name="body" placeholder="본문">${topic.body}</textarea></p>
                        <p><input type="submit" value="update"></p>
                    </form>
                    `;

                    document.querySelector('article').innerHTML = content; 

                    control();
                    })

            }
            
            function del(){
                fetch('http://localhost:3000/topics/' + selectedId, {
                    method:'DELETE'
                })
                    .then(response => response.json())
                    .then(data => {
                        nav();
                        selectedId = null;
                        welcome();
                    })
                ;
                // window.location.reload();

            }
            function control(){
                let contextUI = ''
                if(selectedId !== null){
                    contextUI = `
                        <li><a href="/update">Update</a></li>
                        <li><a href="/delete">Delete</a></li> 
                    `;
                }
                document.querySelector('#control').innerHTML = `
                    <li><a href="/create">Create</a></li>
                    ${contextUI}
                `;
            }
            nav();
            welcome();
            control();
        </script>
    </body>
</html>

CRUD를 ajax를 통해 구현한 것이다.

fetch('url주소') -> 개발자 콘솔창 network탭에서 이 주소로 접속된걸 확인할 수 있다.
1. 첫번째 then에서는 data type을 결정한다.
fetch('url주소').then(res => res.text())
2. 두번째 then에서는 data를 전달받는다.
fetch('url').then(res => res.text()).then(data => {console.log(data)});
3. JSON.parse('text data'); 하면 json데이터가 자바스크립트 객체 데이터로 변환된다.

C - CREATE - POST
R - READ - GET
U - UPDATE - PUT
D - DELETE - DELETE

POST할때 ID값은 autoIncrement 된다.

가장 먼저 실행되는 nav() 함수를 보자

function nav(){
                fetch('http://localhost:3000/topics')
                    .then(res => res.json())
                    .then(topics => {
                        const tag = topics.map(e=>`
                        <li>
                            <a href="/read/${e.id}" id="${e.id}">
                            ${e.title}
                            </a>
                        </li>`).join('');
                    document.querySelector('nav>ol').innerHTML = tag;
                    })
                    document.querySelector('nav>ol').innerHTML = 'loading ...'
                
            }

http://localhost:3000/topics 이 주소에서 json파일의 데이터를 가져와 첫번째 then에서 json()으로 데이터지정을 해준다.
두번째 then에서 [{}, {}, {}] 이렇게 되어 있는 topics를 map함수로 {} 하나씩 e로 꺼내 목차를 만들어준다.
fetch에서 정보를 얻어오기까지 걸리는 시간동안 loading ...을 출력한다.
그렇게 생성된 목차 하나하나는 {}의 title로 출력되고 그걸 누르면 navHandler(event) 함수가 실행된다

function navHandler(e){                
                // 1. 링크가 작동하지 않아야 한다. 
                e.preventDefault();
                // 2. 아이디 값을 알아낸다. 
                selectedId = Number(e.target.id);
                read();
                
            }

각 목차는 a태그이기때문에 누르면 href 링크로 이동하므로 그 기능을 막기 위해 e.preventDefault();를 해준다.
selectedId는 목차버튼의 id이므로 e.target.id로 지정한다.
그 후 read() 함수가 실행된다.

read() 그 목록의 내용 읽어오기

function read(){
                fetch('http://localhost:3000/topics/' + selectedId)
                    .then(res => res.json())
                    .then(topic => {
                        const content = `<h2>${topic.title}</h2>${topic.body}`;
                        document.querySelector('article').innerHTML = content;
                        control();
                    })
                // const topic = topics.filter(e => e.id === selectedId)[0]; 
                // const content = `<h2>${topic.title}</h2>${topic.body}`;
                // document.querySelector('article').innerHTML = content;
                // control();
            }

e.target.id로 얻은 selectedId와 같은 ID값을 갖는 json파일의 객체를 fetch로 불러와서 json()으로 데이터형식을 지정한다.
그리고 그 {} 객체의 정보를 html태그들에 넣어 출력한다.
그 후 control() 함수를 실행한다.

주석처리한것은 json-server를 도입하기 전 코드이다.

control() 버튼 생성

function control(){
                let contextUI = ''
                if(selectedId !== null){
                    contextUI = `
                        <li><a href="/update">Update</a></li>
                        <li><a href="/delete">Delete</a></li> 
                    `;
                }
                document.querySelector('#control').innerHTML = `
                    <li><a href="/create">Create</a></li>
                    ${contextUI}
                `;
            }

하나의 목록을 클릭했따면 selectedId가 존재할 것이고, 그렇다면 update 버튼과 delete버튼이 생성 및 create버튼까지 생성된다.
목록을 선택하지 않은 상태라면 update와 delete할 데이터가 없으므로 버튼을 출력하지 않는다.
데이터를 생성하기 위해서는 create 버튼을 누르면 되고 create() 함수가 실행된다.

create() 글 생성

function create(){
                const content = `
                    <form>
                        <p><input type="text" name="title" placeholder="제목"></p>
                        <p><textarea name="body" placeholder="본문"></textarea></p>
                        <p><input type="submit" value="create"></p>
                    </form>
                `;
                document.querySelector('article').innerHTML = content; 
            }

form태그 안에 input type="text"로 제목 넣는 칸과 textarea로 본문 넣는 칸이 있고, create버튼으로 submit을 했을때 createHandler(event) 함수가 실행된다.

createHandler(event) 글 생성 내부 제어

function createHandler(e){
                e.preventDefault();
                const t = e.target.title.value;
                const b = e.target.body.value;
                fetch('http://localhost:3000/topics/', {
                    method:'POST',
                    headers: {
                      'Content-Type':'application/json'  
                    },
                    body:JSON.stringify({title: t, body: b})
                    
                })
                    .then(res => res.json())
                    .then(data => {
                        console.log(data);
                        nav();
                        selectedId = data.id;
                        read();
                    })
            }

form 태그는 submit 후에 페이지를 reload하므로 e.preventDefault()로 그 기능을 막는다.
name값인 title과 body로 제목, 본문 요소에 접근하여 값을 저장해둔다.
http://localhost:3000/topics/ 에 fetch하는데, POST, header에 application/json방식으로 설정하고 body에 title과 body값을 js객체에서 json데이터로 변환한다.
그 데이터들을 json()함수를 통해 json이라고 지정하고 nav()함수 실행, data.id를 selectedId로 지정한다.
목록을 생성한 직후에는 생성된목록을 보게 해야하므로 nav()와 read()함수가 실행되게 한다.

update() 글 수정

function update(){
                fetch('http://localhost:3000/topics/' + selectedId)
                    .then(res => res.json())
                    .then(topic => {
                        
                        const content = `
                    <form>
                        <p><input type="text" name="title" placeholder="제목" value="${topic.title}"></p>
                        <p><textarea name="body" placeholder="본문">${topic.body}</textarea></p>
                        <p><input type="submit" value="update"></p>
                    </form>
                    `;

                    document.querySelector('article').innerHTML = content; 

                    control();
                    })

            }

글을 수정하려면 이미 존재하는 데이터를 불러와서 create폼에 담아야 한다.
그래서 fetch로 객체정보를 가져와 create 폼과 같은 양식으로 만든 후 제목, 본문 부분만 넣어준다. 제목은 value 속성에 넣어주고, 본문에는 직접 값으로 넣는다. 그러고 그걸 출력하고 버튼이 보이게 하기 위해 control()함수를 불러온다.
폼에서 submit을 하면 updateHandler(event)함수를 불러온다.

updateHandler(event)

function updateHandler(e){
                e.preventDefault();
                const t = e.target.title.value;
                const b = e.target.body.value;
                fetch('http://localhost:3000/topics/'+selectedId, {
                    method:'PUT',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body:JSON.stringify({title:t, body:b})
                })
                    .then(response=>response.json())
                    .then(data=>{
                        nav();
                        selectedId = data.id;
                        read();
                    })
            }

이건 create와 거의 같고,fetch 주소와 method만 'PUT'으로 다르다.

delete()

function del(){
                fetch('http://localhost:3000/topics/' + selectedId, {
                    method:'DELETE'
                })
                    .then(response => response.json())
                    .then(data => {
                        nav();
                        selectedId = null;
                        welcome();
                    })
                ;
                // window.location.reload();

            }

fetch로 데이터를 불러온 후 method를 delete로 하면 데이터가 삭제되어 두번째 .then에서 넘어오는 객체는 {}로 빈 객체가 된다. 그래서 목록을 다시 생성하기 위해 nav(), welcome()함수를 실행한다.
reload함수는 내가 임의로 해본건데, 이고잉님은 페이지 리로드 시키지 않는게 주 목적이라 이렇게 안한다고 했다.

welcome()

function welcome(e){
                document.querySelector('article').innerHTML = `<h2>Welcome</h2>Hello, WEB`;
                selectedId = null;
                control();
            }

welcome함수는 초기화면으로 돌리는 함수이다.

코치님 강의

https://copilot.github.com/

setTimeout

setTimeout(() => {
	alert('hello world')
}, 1000)

setTimeout(() => {
	alert('hello world')
}. 1000)

3 -> 2 -> 1 -> '끝' 출력

for문 이용

function countDown() {
	console.log(3);
  
  for(let i = 3; i>0; i--) {
  	setTimeout(() => {
    	i === 1 ? console.log('끝') : console.log(i-1)
    }, (4-i) * 1000);
  }
}

하드코딩

console.log(3);
setTimeout(() => {console.log(2);}, 1000);
setTimeout(() => {console.log(1);}, 2000);
setTimeout(() => {console.log('끝');}, 3000);

클릭 후 n초 후 경고창

car.addEventListener('click', function(e) {
	e.preventDefault();
  setTimeout(() =>  {alert('오')}, n * 1000);
})

참고: https://developer.mozilla.org/ko/docs/Web/API/setTimeout

디바운싱 구현

input.addEventListener('input', function(e) {
	if(timer) {
    	clearTimeout(timer);
    }
  timer = setTimeout(() => {
  	console.log(e.target.value);
    alert(`${input.value}`);
  }, 1000)
})

디바운싱: 연이어 호출되는 함수들 중 마지막 함수(또는 제일 처음)만 호출하도록 하는 것

input에다가 input 행위를 하면 내부콜백함수가 실행된다.
input 행위는 타이핑하는것이다
input 행위를 하면 처음에는 timer가 존재하지 않기때문에 if문은 패스한다.
setTimeout이 실행되는데, 지연시간이 1초라서 바로 실행이 되지는 않는다. 그런데 이때 input 행위, 즉 타이핑을 계속 하면 input행위가 또 발생한 것이므로 내부콜백함수가 또 실행되는데 이때는 timer가 이미 존재하는 상황이므로 if문이 발동하여 clearTimeout을 해서 현재시점에 존재하는 setTimeout을 삭제한다. 그러고 나서 setTimeout을 다시 그시점부터 실행한다. 그러니까 타이핑을 하는 도중에는 alert가 안나오고 타이핑을 멈추고 1초가 지나면 더이상 새로운 input 이벤트가 없고 콜백함수가 발동하지않아 clearTimeout이 실행되지 않기때문에 alert가 나온다.

참고: https://www.zerocho.com/category/JavaScript/post/59a8e9cb15ac0000182794fa

fetch

console.log ('Starting');
let image;

fetch('coffee.jpg').then((response) => {
  console.log('It worked :)')
  return response.blob();
}).then((myBlob) => {
  let objectURL = URL.createObjectURL(myBlob);
  image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
}).catch((error) => {
  console.log('There has been a problem with your fetch operation: ' + error.message);
});

console.log ('All done!');

참고: https://developer.mozilla.org/ko/docs/Learn/JavaScript/Asynchronous/Introducing

promise

fetch함수를 만들어본다.

function promisifyRequest(request) {
  return new Promise(function(resolve, reject) {
    request.onsuccess = function() {
      resolve(request.result);
    };

    request.onerror = function() {
      reject(request.error);
    };
  });
}

성공하면 resolve를 실행하고 실패하면 reject를 실행한다.
그럼 그거 자체가 내부콜백함수니까 그거에대한 결과값이 return된다.

promisifyRequest(request)
	.then(text => console.log('성공')
    .catch(text => console.log('실패')

이런식으로 사용 가능하다

비동기 참고: https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/
참고: https://developer.mozilla.org/ko/docs/Learn/JavaScript/Asynchronous/Promises

find() 메서드

filter랑 비슷한데 find()는 첫번째 요소가져오고 filter는 다가져온다

myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}, etc.]

myArray.find(x => x.id === '45'); // {'id':'45','foo':'bar'}

myArray.find(x => x.id === '45').foo; // bar

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/find
https://hianna.tistory.com/406

profile
공부일기

0개의 댓글