[TIL] day14. 모듈과 비동기

kcm dev blog·2021년 8월 19일
0

TIL

목록 보기
8/19
post-thumbnail

Intro

과거 Javascript에 대해서는 거의 문외한이던 시절 처음 React를 배워 교내 프로젝트를 하던 때 뭔지도 모르고 만들었던 Javascript 코더에서 벗어났다는 느낌을 받은 날이었다. 컴포넌트 단위로 코드를 분리하여 관리하는 방식을 배우고, 비동기 방식에 대해 배웠다.

html에서 script:src로 script를 관리하는 방법, 모듈 단위로 관리하는 방법 2가지를 모두 배워봄으로써 모듈 단위 관리의 중요성을 직접 경험해볼 수 있었고, 비동기 파트에서는 async/await만 잘 쓰면 되겠지 싶었던 생각을 뜯어고칠 수 있었다.

Vanila JS에 대해 제대로 배우기 시작한지 고작 이틀 밖에 안되었지만, 과거에는 JS에서 왜 컴포넌트 단위로 관리를 하는지, 왜 비동기를 써야하는지 등 이유를 모르고 있었기에 제대로 쓸줄도 모르고, 써야하는 이유도 몰랐다. 오늘 그 이유를 배웠기에 나는 과거에서 탈출했다고 할 수 있다.

또한 JS는 다른 언어들에 비해 비논리적인 괴랄한 언어라는 인식에서 벗어난 날이었다. 어떤 언어든 장점과 단점은 트레이드 오프 관계이다. 어떤 언어든 장점만 존재하지 않는다. 하나의 장점을 살리면 하나의 단점을 만들어 낸다. 다만 JS는 초기에 단점이 좀더 두드러져 보였고, 버전을 거듭할수록 효율적인 방향으로 진화하고 있다. 그 자리에 정체되어 있지 않고, 보다 나은 방향으로 변화하고 발전해 나가는 언어라는 점에서 최근들어 JS라는 언어가 점점 매력적으로 보인다.

Module

import/export

  • import: export 키워드로 보내지는 변수, 함수 등을 불러올 수 있는 키워드

    • export default: module-name 내에 export default로 내보내진 것을 가저온다(대개 컴포넌트 단위로 파일을 만들어서 관리하면 export default 규칙을 잘 따르게 된고, 이러한 규칙을 따르면 export default를 보고 컴포넌트라는 것을 판별할 수도 있다.)
          export default myFunc;//내보내는 파일에서 선언
          
          import defaultExport from 'module-name';//내려받는 파일에서 선언
  • export: 여러개의 변수와 함수를 가져올 수 있다.

        import * as allItems from 'module-name';
  • 모듈을 쓰는 경우 html에서 script 선언할 때 맨처음에 불러오는 entry 모듈의 경우, type으로 module을 명시해야 한다

    <script src="./src/index.js" type="module"></script>
  • module-name 내에서 export 된 것 중에 특정 값만 가져오는 경우
    import {loadItem} from 'module-name';
  • module-name 내에서 export 된 것중에 특정 값만 이름을 바꿔서 가져오는 경우
    import {loadItem as loadSomthing} from 'module-name';
  • import/export를 활용함으로써 기존의 script:src를 사용하거나 의존되는 순서대로 스크립트 로직을 테트리스 하듯이 쌓을 필요가 없어진다.
  • import/export를 활용하면 사용/미사용 스크립트를 쉽게 분류할 수 있다.

module(import/export)를 사용하면 좋은 점

  • js별로 사용되는 모듈을 명시적으로 import 해오기 때문에, 사용되거나 사용되지 않는 스크립트를 추적할 수 있다.
  • script tag로 로딩하는 경우 불러오는 순서가 중요하지만, import로 불러오면 순서는 무관하다
  • script src로 불러오는 것과 달리 전역오염이 일어나지 않는다.

import를 사용하려면

  • 웹 서버가 필요(File System에선 동작하지 않는다)
  • 뭔가 잘 안되면 대개 from이후 모듈 이름 맨 뒤에 '.js'를 안붙여서 발생하는 경우가 많다.

비동기 다루기

비동기 처리란 무엇인가?

  • 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고, 다음 코드를 먼저 실행하는 자바스크립트의 특성을 이야기 한다.
function onButtonClick(){
    alert('clicked');
}

document.querySelector('.save-button').addEventListener('click',onButtonClick);

첫 번째 인자로 넘겨진 함수는 바로 실행되지 않고, setTimeout or setInterval의 시간만큼 지난 후에 실행된다.

function work(){
    console.log('works')
}
setTimeout(work,1000);
setInterval(work,5000);
console.log('work process');
// work process
// works
// works.
// ...

setTimeout의 시간을 0으로 넣거나 지정하지 않아도 바로 실행되진 않는다
-> setTimeout과 같은 함수들은 무조건 work()가 모두 실행되고 난 이후에 실행된다.

function work(){
    console.log("start work");
    setTimeout(function(){
        console.log('working');
    },0)
    console.log("finish work");
    //start work
    //finish work
    //working 
}

비동기 처리의 원리

JS 엔진을 보면 call stack, web api, task queue가 존재하는데, call stacksetTimeout()이 호출되면 web API에 있는 함수이므로 해당 함수를 호출하여 call stack에서 비운뒤 Web API에서 함수를 처리한뒤 콜백함수를 task queue에 등록한다. 그리고 난 다음에 event loop에서 call stack이 비는지 관찰하고 있다가, call stack이 비면 그때 task queue에 있는 함수들을 순차적으로 요청사항에 맞게 실행하게 된다.

XHR(XMLHttpRequest) (~= AJAX): 데이터를 비동기로 요청하고, 요청 후의 동작을 비동기로 처리한다.

function request(url,successCallback,failCallback){
    const xhr=new XMLHttpRequest();
    xhr.addEventListener("load",(e)=>{
        if(xhr.readyState===4){
            if(xhr.status===200){
                successCallback(JSON.parse(xhr.responseText));
            }else{
                failCallback(xhr.statusText);
            }
        }
    })
    xhr.addEventListener('error',(e)=>failCallback(xhr.statusText));

    xhr.open('GET',url);
    xhr.send();
}

(sync방식을 쓰면 가독성이 워낙 좋아지니 복잡한 async대신 sync를 쓰고 싶다는 유혹이 생길 수 있다. 그러나.. sync 방식을 사용하게 되면 요청 후 응답이 오기전까지 브라우저가 굳어져버린다!!! .. 콘솔에서도 sync XMLHttpRequest는 warning을 준다)

Promise

  • Promise는 비동기 작업을 제어하기 위해 나온 개념으로, callback hell에서 어느정도 벗어날 수 있게 해준다
  • Promise로 정의된 작업끼리는 연결할 수 있으며, 이를 통해 코드의 depth가 크게 증가하지 않는 효과가 있다

Promise 내장함수

  • Promise.all(iterable): 여러 promise를 동시에 처리할 때 유용하다.
const promise1=delay(1000);
const promise2=delay(2000);
const promise3=delay(3000);

Promise.all([promise1,promise2,promise3].then(()=>{
    console.log("works done");//6초후에 실행됨
}))
  • Promise.race(iterable): 여러 promise중 하나라도 resolve 또는 reject 되면 종료된다.

  • Promise.any(iterable): 여러 promise중 하나라도 resolve되면 종료된다.

  • Promise.allSettled(iterable): 여러 promise들이 성공/실패 상관없이 모두 이행된 경우를 처리한다.

  • Promise.resolve: 주어진 값으로 이행하는 Promise.then 객체를 만든다. 그리고 주어진 값이 Promise인 경우 해당 Promise가 반환된다.

const cached={
    'roto':'guitar'

}
const findMember=(memberName)=>{
    if(cached[memberName]){
        return Promise.resolve(cached[memberName]);
        //Promise객체를 리턴함
        //Promise 없이 resolve()만 하면 String이 리턴되어, 함수 호출시 then에서 값을 받을 수 없어 에러가 발생한다. 따라서 Promise.resolve()를 하여 Promise객체를 리턴해준다.
    }
    //(request()는 걍 만든 Promise함수임)
    // then()에서 리턴을 하면 무조건 Promise객체를 만들기 떄문에 findMember().then()에 대응할 수 있다.
    return request(`/members/${memberName}`).then((member)=>{
        cached[member.memberName]=memberName;
        return memberName;
    })
}
findMember('roto').then((memberName)=>console.log(memberName));

async, await

선언하기

// 방법1
async function asyncFunc(){
    const res=await request(...);
}
// 방법2
const asyncFunc=async (){
    const res=await request(...);
}

resolve, reject, finally를 통한 예외처리는 try..catch..finally를 쓰면 된다.

profile
오늘 배운건 오늘 소화하자!

0개의 댓글