상세 컨텐츠

본문 제목

Worker (Thread)사용 공아 달려랑!

자바스크립트

by e7e 2023. 7. 27. 22:55

본문

뜨레드(thread)!, 비동기(asynchronouse)!

초보개발자나, 이제 막 프로그램 개발 입문 비전공자에게는 웨엔지 외계어 같당!

역시 항상 그렇진 않지만 알고 보면 별거 없는 정도가 아니라 고저 일상생활 용어당!

우리 삶은 뜨레드와 비동기가 아닌 걸 찾기가 사실 더 미치게 어렵당.

 

당신은 혼자서 밭일 하면서, 동시에 물고기 낚시를 할 수 있는가?(로보트 사용 안됨!)

요때 당신 이름이 뜨레드당, 곧 뜨레드가 혼자라서 특정시간에 한가지 일밖에 못한당.

야채에 생선을 얹어 구어먹고픈 당신은  clone신공을 매초마다 연습하여 마침내 드뎌

자신을 복사할  수 있는 능력을 갖게 된당.  복사된 당신도 이름이 뜨레드당.

 

뜨레드 한명이 밭일을 하는 동안, 뜨레드 한명은 낚시를 한당.

뜨레드가 1개 이상이니, 영어로 괘니 멋지겡 표현하면 멀티 뜨레드당.

뜨레드가 1개 이상이 동시에 동작하면 그거이 비동기당! 그렇다 우리네 삶이당.

바다에서 생선이 잡히는 동안에도 당신은 생선을 구어먹는당.

 

비동기가 어려운 건 개념이 아니고, 여러가지 일이 동시에 진행 될 때,

일의 결과가 관계가 없으면 상관없지만,  관계가 있어 순서가 필요해지면, 머리를 

꽤 써야 하는뎅, 뜨레드 숫자가 많아지고, 관계도 많아지면 그냥 머리가 쪼메 피곤탕!

 

억지롱 예를 1개 들어보장. 당신이 생선구이를 해 먹으려 한당.

다른 재료는 다 있는뎅, 생선이 없당. 그래서 현이에겡 생선을 사오라고 한당.(비동기시작)

당신은 불을 켜고, 물을 데우공, 야채를 다듬고 등을 한당. 그 시간에

현이는 마트에서 생선을 사고 있당.

당신은 현이가 생선을 가져오기 전까지는 생선에 대한 처리는 할 수 없당.(생선이 손에 없당)

컴퓨터 세계에서는 현이가 돌아 온 걸 누군가 당신에게 알려줘야 한당(이벤토)

희에게는 마늘을 사오라고 했다면 상황은 쪼메 더 복잡해진당.  레시피가 정확한 순서에

따른다면 당신은 시간차를 잘 감안해서 레시피 순서가 잘 지켜지도록 해야 한당.

(이 부분은 생각보단 경험이 깊은 이해를 맹근당!, 뜨레드나 AJAX 마니 마니 해보장)

 

자바에 뜨레드(thread)가 있다면 자바스크립트에는  워커(worker)가 있당.

워커는 화면제어에는 사용하지 못하는 제약사항을 일단 머리에 담아버리장!

 

보통 기존 자바스크립트를 실행하는 뜨레드를 main 뜨레드라 부르고, 요 main 뜨레드와

생성된  worker(일꾼)들이 서로 통신을 하며  도와서 일을 하게 될 거시당.

 

통신 방법은 너무도 너무도 쉽당. 보낼 때는 postMessage라는 메소드를 사용하고,

받을 때는  onmessag 이벤트를 사용한당.  그냥 이게 기본이당. 

또 생각업시 마리 기럭지가 기러지고 말았당. 누네 결과를 보여 줘야 맘이 따라간당.

 

아래 소스를 카피,붙여넣기롱 myWorker.js를 맹글공 , self.onmessag 에 주목하장!

self는 생성된 worker가 자신을 부르는 말이당(나?)

myWorker.js

self.onmessage = function(msg){
    console.log("from main",msg.data);
    let gakdo=0;
    function loop(){
        gakdo=(gakdo+10) % 360;
        self.postMessage(gakdo);
        let ranTime = 150 - Math.round(Math.random()*100);
        //timer를 변수 선언하지 않고 쓴 이유를 생각해 보삼!
        timer=setTimeout(loop,ranTime);    
    }
    if(msg.data == "E7ES"){
        loop();
    }else {
        clearTimeout(timer);
    }
}

위 myWorker.js와 같은 폴더(디렉토리)에 아래 소스롱 rollingUp.html을 맹글공,

서버 위에서 실행시켜보장!

rollingUp.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>E7E 메에롱</title>
<style>
    * {
        box-sizing: border-box;
    }
    #wrapper {
        margin: 10px auto;
        width:800px;
        border:1px solid black;
        text-align: center;
    }
    .lane {
        position: relative;
        width:100%;
        height:202px;
        border:1px solid pink;
        font-size: 4em;
        line-height: 60%;
    }
    .ball {
        position: absolute;
        width: 200px;
        height: 200px;
        border-radius: 50%;
        background-color: pink;
        background-image: url("https://cdn-icons-png.flaticon.com/512/468/468681.png");
        background-size: 100% 100%;
    }
    #header {
        font-size: 1.2em;
        font-weight: bolder;
    }
    .merong1 {
        transform: scale(1.5);
    }
    .merong2 {
        transform: scale(1.2);
        font-weight: bolder;
    }
</style>
</head>
<body>
<div id="wrapper">
    <h1 style="background-color: orange;margin-top: 0;"> &copy; E7E 메롱</h1>
    <div id="header" style="padding-bottom: 10px;">
        경주 레인 수&nbsp;&nbsp;&nbsp; 
        <select id="sel" class="merong1" onchange="fChg(this)">
            <option value="2">2갱</option>
            <option value="3">3갱</option>
            <option value="4" selected>4갱</option>
            <option value="5">5갱</option>
            <option value="6">6갱</option>
            <option value="7">7갱</option>
            <option value="8">8갱</option>
        </select>
        &nbsp;&nbsp;&nbsp;
        <input type="button" class="merong2" id="start" value="시이작" onclick="fStart(this)">
        &nbsp;&nbsp;&nbsp;
        반복?<input type="checkbox" class="merong2" value="endless" onchange="fChk(this)">        
    </div>
    <div id="lnbody">
        <div id="lane" class="lane">
            <div id="ball" class="ball" style="left:0px"></div>
        </div>
        <div id="lane" class="lane">
            <div id="ball" class="ball" style="left:0px"></div>
        </div>
        <div id="lane" class="lane">
            <div id="ball" class="ball" style="left:0px"></div>
        </div>
        <div id="lane" class="lane">
            <div id="ball" class="ball" style="left:0px"></div>
        </div>
    </div>
</div>
<script>
let ranking = 1;   // 등수용
let chgWidth=200;
const maxLane = 8;  // 레인 최대 갯수
let myBalls, myLanes; // ball들과 lane들을 동적으로 담을 변수
let wCnt = 4;        // 처음에 그냥 ball 4개 넣어 놓았음
// 내맘대로 등수 색깔, 금,은,동, 맘대롱
const medalColors = ["gold","silver","#624637","pink","blue","cadetblue","blueviolet","gray"];
// 글자색깔
const charColors = ["blueviolet","blue","white","cadetblue","pink","yellow","gray","aqua"];
// 이미지
const imgURL = "url('https://cdn.pixabay.com/photo/2014/02/17/20/41/gold-268640_1280.jpg')";

// 레인과 볼 HTMLs
let laneHtml = `
    <div id="lane" class="lane">
        <div id="ball" class="ball" style="left:0px"></div>
    </div>
`;
// 레인들을 담을 div
const myLnbody = document.querySelector("#lnbody");
const mySel = document.querySelector("#sel");
const myStart = document.querySelector("#start");

let notInit = false;
let isEndless = false;
let timer=null;

// select 체이징
function fChg(pThis){
    if(pThis){
        if(timer){ clearTimeout(timer);}
        isEndless=false;
        myStart.disabled=false;
    }
    
    wCnt = mySel.value;              // select 선택 값
    myLnbody.innerHTML = "";         // 비우고
    for(let i=1; i<=wCnt; i++){      // 새로 맹글어 넣깅
        myLnbody.innerHTML += laneHtml;
    }
    fLaneBallSize();
    notInit = false;
    myStart.focus();
}

function fChk(pThis){
    if(pThis.checked){
        isEndless=true;
    }else {
        isEndless=false;
    }
}

// 시이작 버튼 클릭
function fStart(pThis){
     myStart.disabled=true;
     mySel.disabled=true;   
    if(notInit) fChg();   //억지 땜빵!

    myLanes = document.querySelectorAll("#lane"); // 동적으로 가져오깅
    myBalls = document.querySelectorAll("#ball"); // 동적으로 가져오깅

    let workerList = [];     // worker 담을 배열
    for(let i=1; i<= wCnt; i++){
        workerList.push(new Worker("./myWorker.js"))
    }
    for(let i=0; i<wCnt; i++){
        workerList[i].addEventListener("message",function(msg){
            console.log(`from worker${i}`,msg.data);
            myBalls[i].style.transform = `rotate(${msg.data}deg)`;
            myBalls[i].style.left = parseInt(myBalls[i].style.left) + (chgWidth/2*Math.PI/36) + "px";
            if((parseInt(myBalls[i].style.left) + chgWidth) >= 800 ){
                this.terminate();
                let medalMsg = `<h1>${ranking}등</h1>`;
                if(ranking == 1){
                    medalMsg = `<h1>🤍우승</h1>`;
                    myLanes[i].style.fontWeight = 'bolder';
                    myBalls[i].style.backgroundImage = imgURL;
                }
                myLanes[i].innerHTML += medalMsg;
                myLanes[i].style.backgroundColor = medalColors[ranking-1];
                myLanes[i].style.color = charColors[ranking-1];
                ranking++;
                if(ranking > wCnt) {
                    ranking=1;
                    notInit = true;
                    if(isEndless){
                       timer = setTimeout(()=>{
                            myStart.disabled=false;
                            myStart.click();
                        },2500);
                    }else {
                        myStart.disabled=false;
                        mySel.disabled=false; 
                    }
                }
            }
        })   
    }

    // worker 구동 시작
    for(let i=0; i<wCnt; i++){
        workerList[i].postMessage("E7ES");
    }
}

//사이즈 조정
function fLaneBallSize(){
    let myLaneStyle = fStyleSearch(".lane");
    let myBallStyle = fStyleSearch(".ball");
    
    myLaneStyle.height = "201px";
    myLaneStyle.fontSize = "4em";
    myBallStyle.width = "200px";
    myBallStyle.height = "200px";
    chgWidth = 200;

    if(wCnt > 4){
        myLaneStyle.height = "101px";
        myLaneStyle.fontSize = "2em";
        myBallStyle.width = "100px";
        myBallStyle.height = "100px";
        chgWidth =100;
    }
}


// style 태그내 스타일 정의 검색
const myStyleSheets = document.styleSheets;
function fStyleSearch(pSelector){
    for(let i=0; i< myStyleSheets.length; i++){
        let myCssRules = myStyleSheets[i].cssRules;
        for(j=0; j < myCssRules.length; j++){
            // cssText, selectorText, style
            if(myCssRules[j].selectorText == pSelector){
                return myCssRules[j].style;
            }
        }
    }
    return null; 
}
</script>
</body>
</html>

참고로 나는 아래 캡쳐와 같은 브라우져 모양을 얻었당.(F12 화면을 노려보장)

뽀인토는 new Worker("./myWorker.js") 와 아페서 언급한

postMessage와 onmessage가 되겠당

뽀인토만 넣으면 넘 쉬운 것 같아서, 일부러 JS로 style태그에 접근해서 그 안의 style을 

바꾸는 것과, 초기화를 위한 상태변수를 난잡하게 사용하여, 이해를 애매모호하게 만들었당.

리팩토링 하면서 가지고 놀기 딱 좋은 소스당.(개인적 내 의견이......) 가지고 놀아보장!

 

 

아무것도 당신과 비교 될 수 없당.

당신에게 진정 득이 되는 일이 뭘깡?  시공간이 가리키는 그 바람을 따르장!

 

 

https://www.youtube.com/watch?v=0-EF60neguk 

 

관련글 더보기