1.동기, 비동기/ajax/json/node.js

2022. 8. 22. 17:21javascript

2022.08.22.월


1.동기/비동기

 -동기 방식

/*
    synchronous : 동기적인

    함수가 호출된 순서대로 순차적으로 실행됨 - 동기(sync) 방식
*/

//1.순차 실행
const firstFunc = () => {console.log('첫 번째 함수가 호출 됨.');}

const someLongWork = () => {
    console.log('--------------------');
    console.log('특정 작업 처리중... 시간이 오래 걸림');
    console.log('--------------------');
}

const secondFunc = () => {console.log('두 번째 함수가 호출 됨.');}

//함수들이 작성한 순서대로 순차적으로 ㅗ출됨
firstFunc();
someLongWork();
secondFunc();

/*
    한번의 하나의 작업만 처리하기 때문에 
    특정 작업(ex 특정함수의 호출 someLongWork())이 
    길어질 경우 secondFunc()는 someLongWork()가
    처리될 때까지 작업이 중단됨(Blocking, 블로킹).
*/

console.log(Date.now()); //1970년 1월   1일부터 지금까지 흘러간 초(ms)

function sleep(callbackFn, delay){
    console.log('시간 지연중...');
    const deleyedTime = Date.now() + delay;

    while(Date.now() < deleyedTime);

    callbackFn();
}

function firstWork(){
    console.log('첫번째 작업 수행');
}

function SecondWork(){
    console.log('두번째 작업 수행');
}

sleep(firstWork, 3 * 1000);
SecondWork();

/*
    현재 실행 중인 작업이 종료할 때까지
    다음에 실행될 작업이 대기하는 방식을
    동기(asynchronous) 처리라고 함
    
    장점 : 작업을 순서대로 하나씩 처리, 실행 순서가 보장됨
    단점 : 앞선 작업들이 처리될 때까지 다음 작업들이 블로킹됨

 -비동기 방식

//2.비동기 방식(asynchronous)

/* 
    현재 실행 중인 작업이 아직 종료되지 않은 상태라고 해도,
    다음에 해야 할 작업을 곧바로 실행시키는 방식
*/

function firstWork(){
    console.log('첫번째 작업 수행중');
}

function SecondWork(){
    console.log('두번째 작업 수행중');
}

//비동기 기술을 제공하는 몇가지 함수들(setTimeout(), addEventListner())
setTimeout(firstWork, 3 * 1000);
SecondWork();

/*
    장점 : 비동기 처리 방식은 현재 실행 중인 작업이 종료되지 않은 상태라고 해도
    다음 작업(secondWork)을 곧 바로 실행하기 때문에 블로킹이 발생하지 않는다
    단점 : 작업의 수행 순서가 보장되지 않음

    비동기 처리 방식으로 동작하는 JS 함수
    setTimeout(), setInterval(), HTTP 요청, EventHandler
*/

2.ajax(Asynchronous JavaScript And XML)

//XMLHttpRequest API 객체 생성
const xhr = new XMLHttpRequest();

//XMLHttpRequest 객체가 제공하는 
//메서드를 활용하면 HTTP 요청 전송 가능

//요청을 보낼 준비
//xhr.open(요청 메서드, 요청 URL 주소);

//'GET', 서버의 데이터를 조회하고 싶을 때
xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos');
//readyState 프로퍼티 : 요청 준비 상태 체크
console.log(`OPENED, ${xhr.readyState}`);

//준비 상태(readyState) 프로퍼티의 값이 
//바뀔 때마다 아래의 함수가 호출됨
xhr.onreadystatechange = () => {
    if(xhr.readyState === 2) {
        console.log(`HEADERS_RECEIVED, ${xhr.readyState}`);
    }

    //데이터가 응답, 로딩완료
    if(xhr.readyState === 4 && xhr.status === 200) {
        console.log(`DONE, ${xhr.readyState}`);
        //서버로부터 받은 데이터를 조회할 수 있는 부분
        console.log(xhr.responseText); //서버로 부터 응답받은 데이터
    }
}; 

xhr.onprogress = () => {console.log(`LOADING, ${xhr.readyState}`);};
xhr.onload = () => {console.log(`DONE, ${xhr.readyState}`);};

//실제로 요청 전송
xhr.send();

/*
    1.readyState : HTTP 요청의 현재 상태를 가지고 있는 함수값 프로퍼티
    0-(UNSENT) - 초기화 전, open() 호출하기 전
    1-(OPENED) - 열림, open()을 호출하고, send()는 호출하지 않은 상태
    2-(HEADERS_RECEIVED) - 요청 전송, send() 호출했지만 서버로부터 응답은 받지 못한 상태
    3-(LOADING) - 데이터 수신중, 응답 데이터의 일부를 받고 있는 상태
    4-(DONE) - 완료, 응답 데이터를 모두 받은 상태

    2.status : HTTP요청에 대한 응답의 성공 여부를 나타내느 값
    ex) 200(응답 성공), 404(요청 실패, 잘못된 경로 요청으로 인한)

    3.statusText : HTTP요청에 대한 응답 메시지를 나타내는 문자열
    ex) status가 200일 경우, 'OK'

    4.response Type : 응답된 데이터의 타입
    ex)document, json, text, blob 등

    5.response : HTPP 요청에 대한 응답 몸체 부분(body)

    이벤트 핸들러와 관련된 프로퍼티
    onreadstatechange : readystate 프로퍼티의 값이 변경된 경우

    메서드
    1.open() : HTTP 요청 초기화(준비 단계)
    2.send() : HTTP 요청 실제 전송
*/

3.json

/*
    json : JavaScript Object Notation

    annotation : @, at기호

    클라이언트와 서버 간의 HTTP 통신을 위한 텍스트 데이터 포맷(format)
    .json이라는 확장자의 파일
    JS에 종속되지 않는 언어 독립형 데이터 포맷
    대부분의 프로그래밍 언어에서 사용하는 대표적인 포맷팅 개념
*/

//json의 작성 방식 Javascript 객체 작성 방식과 유사함

//JS object
const book = {
    title : '노인과 바다',
    author : '헤밍웨이',
    isSold : false,
    genre : ['소설', '경험담'],
}

// JSON 작성 방식 : key 값 및 문자열은 ""(쌍따옴표만 가능)로 작성해야함, 홑따옴표 불가
// {
//     "title": "노인과 바다",
//     "author": "헤밍웨이",
//     "isSold": false,
//     "genre": ["novel", "essay"],
// }

//Client 측에서 서버로 데이터(일반적으로 객체)를
//전송하기 위해서 객체를 문자열화(직렬화, Serialization)해야 함

console.log(book);
console.log(book.title); //JS object 정상적으로 접근 가능

const jsonData = JSON.stringify(book);
console.log(typeof jsonData); //type:string
console.log(jsonData.title); //undefined
console.log(jsonData[10]); //문자열이기 때문에 인덱스로 접근

/*
    JSON.stringfy() 객체 뿐만 아니라 배열(Array)도
    JSON 포맷의 문자열로 직렬화할 수 있음
    ->일반적으로 서버로 보내는 데이터는 한개(객체) 일 수도 있지만,
    여러개(배열) 일 수도 있기 때문에
*/

const books = [
    {id:1, title:'하농', author:'하농', isSold:false},
    {id:2, title:'체르니', author:'체르니', isSold:false},
    {id:3, title:'부르크밀러', author:'부르크밀러', isSold:true},
];

//배열을 JSON 포맷의 문자열로 변환
const jsonData2 = JSON.stringify(books);
console.log(typeof jsonData2, jsonData2);

//jsonData2가 서버로부터 응답받은 json 데이터라고 가정.

//JSON 포맷의 문자열을 JS object로 변환(parsing)
const parsedData = JSON.parse(jsonData2);
console.log(parsedData);

4.node.js

 -NPM(Node Package Manager)

Package : JS파일들(모듈)이 모여있는 묶음

Manager : 그런 묶음 폴더들을 관리해주는 사람

→누군가가 JS기반으로 여러 편의 기능들을 라이브러리의 형태로 개발해서

    패키로 묶고(패키징), 다른 사람들도 사용할 수 있도록 공개

→패키징된 라이브러리들을 검색, 설치해서 사용할 수 있는 사이트(npmjs.com)

→일련의 자바스크립트 코드들이 패키지의 형태로 구성되어 있음

 

04.practice folder : Node.js 기반의 프로젝트 root 폴더

package.json

 

npm으로 시작하는 명령어를 쓰기 위해서는

package.json 파일이 위치한 경로에서만 명령어 사용이 가능

//Node.js, Express FW를 활용하여 간단한 Backend 서버 구성

//express 패키지 import
const express = require('express');

const dotenv = require('dotenv');
dotenv.config();

const app = express();

const clientId = process.env.CLIENT_ID;
const clientSecret = process.env.CLIENT_SECRET;

const request = require('request');

//express static 미들웨어 활용
//express한테 static 파일들의 경로가 어디에 있는지 명시
app.use(express.static("public"));

//express의 json 미들웨어 활용
app.use(express.json());

//일반적으로 /를 root 경로라고함
//root url : 127.0.0.1/
//IP주소 : 127.0.0.1, Port : 3000
//127.0.0.1의 Domain name : localhost
//http://localhost:3000
//app.get() -> 첫번째 인수로 지정한 경로로 클라이언트로부터 요청이 들어왔을 때
//두번째 인수로 작성된 콜백함수가 호출되면서 동작함
//그 콜백함수는 2개의 인수를 받음, 1)reqeust(req), 2)response(res)
app.get('/', (req, res) => {
    // res.send('응답 완료!');
    //root url, 즉 메인 페이지로 접속했을 때
    //우리가 만든 Node 서버는 papago의 메인 화면인
    //public/index.html을 응답해줘야 함
    res.sendFile("index.html");
});

//localhost:3000/detectLangs 경로로 요청했을 때
app.post('/detectLangs', (req, res) => {
    console.log('/detectLangs로 요청됨');
    //request.getParameter();
    console.log(req.body);

    //text 프로퍼티에 있는 값을 query 변수에 담고 싶고,
    //targetLanguage는 동일한 이름의 변수로 담고 싶음
    const { text:query, targetLanguage } = req.body;
    console.log(query, targetLanguage);

    //실제 papago 서버에 요청 전송, 택배를 보낼 주소
    const url ='https://openapi.naver.com/v1/papago/detectLangs';

    const options = { //택배를 보낼 물건
        url,
        form: { query },
        headers: { 
            'X-Naver-Client-Id': clientId, 
            'X-Naver-Client-Secret': clientSecret
        },
    };

    //실제 언어 감지 서비스 요청 부분
    //options 변수에 요청 전송시 필요한 데이터 및 보낼 주소 동봉
    //() => {} : 요청에 따른 응답 정보를 확인하는 부분
    request.post(options, (error, response, body) => {
        if(!error && response.statusCode === 200) { //응답이 성공적으로 완료되었을 경우
           //body를 parsing처리
           const parseData = JSON.parse(body);

           //papago 번역 url('/translate')로 redirect(요청 재지정)
           res.redirect(`translate?lang=${parseData['langCode']}&targetLanguage=${targetLanguage}&query=${query}`);
        } else { //응답이 실패했을 경우
            console.log(`error = ${response.statusCode}`);
        }
    });
});

//papgo  번역 요청 부분
app.get('/translate', (req, res) => {
    const url = 'https://openapi.naver.com/v1/papago/n2mt';
    const source = req.query.lang;
    const target = req.query.targetLanguage;
    const text = req.query.query;
    //req.query = {source, target, text};
    console.log(source, target, text);
    const options = {
        url,
        form: { source, target, text },
        headers: { 
            'X-Naver-Client-Id': clientId, 
            'X-Naver-Client-Secret': clientSecret
        },
    }

    //실제 번역 요청 전송부분
    request.post(options, (error, response, body) => {
        if(!error && response.statusCode === 200) { //응답이 성공적으로 완료되었을 경우
            res.send(body); //front에 해당하는 app.js에 papago로부터 받은 응답 데이터를 전송
         } else { //응답이 실패했을 경우 
             console.log(`error = ${response.statusCode}`);
         }
    });

});

//서버가 실행되었을 때 몇번 포트에서 실행시킬 것인지
app.listen(3000, () => console.log('http://127.0.0.1:3000/ app listening on port 3000'));

//Node.js 기반의 js파일 실행시에는 node로 시작하는 명렁어
//파일명까지 작성하면 됨
//ex) node server.js -> server.js 실행
//server.js는 express framework로 구성한 백엔드 서버 실행 코드 있음
//변수 네이밍 컨벤션, 도메인과 관련된 용어를 미리 정의
//source : 번역할 텍스트와 관련된 명칭
//target : 번역된 결과와 관련된 명칭

//[왼쪽 텍스트 영역, 오른쪽 텍스트 영역]
const [sourceTextArea, targetTextArea] = document.getElementsByClassName('Card__body__content');
const [sourceSelect, targetSelect] = document.getElementsByClassName('form-select');

//번역하고자 하는 언어의 타입(ko? en? ja?)
let targetLanguage = 'en'; //기본값으로 en

//어떤 언어로 번역할지 선택하는 target select박스의
//선택지의 값이 바뀔 때마다 이벤트 발생하고
//지정한 언어의 타입 값을 targetlanguage 변수에 할당, 출력
//change 이벤트 사용, select 박스가 객체가 가지고 있는 프로퍼티
targetSelect.addEventListener('change', () => {
    const targetValue = targetSelect.value;
    console.log(targetValue);
    targetLanguage = targetValue;
});

let debouncer;

sourceTextArea.addEventListener('input', (event) => {
    if(debouncer){
        clearTimeout(debouncer);
    }
    //setTimeout(callback, 지연할 시간(ms))
    //콜백함수 : 지연된 시간 후에 동작할 코드
    debouncer = setTimeout(() => {
        const text = event.target.value; //sourceTextArea의 입력한 값
        console.log(text);

        //ajax 활용하여 비동기 HTTP 요청 전송
        const xhr = new XMLHttpRequest();

        const url = '/detectLangs'; //node 서버의 특정 url 주소

        xhr.onreadystatechange = () => {
            if(xhr.readyState === 4 && xhr.status === 200) {
                //최종적으로  papago가 번역해준 번역된 텍스트 결과를 받는 부분
                //서버의 응답 결과 확인하는 부분
                const parsedData = JSON.parse(xhr.responseText);
                const result = parsedData.message.result;

                targetTextArea.value = result.translatedText;

                const options = sourceSelect.options;

                for (let i = 0; i < options.length; i++) {
                    if(options[i].value === result.srcLangType) {
                        sourceSelect.selectedIndex = i;
                    }
                    
                }
            }
        }
        //요청 준비
        xhr.open('POST', url);

        //요청 보낼 때 동봉할 객체(object)
        const requestData = {
            text, //text: text 프로퍼티와 변수명이 동일한 경우 하나만 작성 가능
            targetLanguage,
        };

        //클라이언트가 서버에게 보내는 요청
        //데이터의 형식이 json 형식임을 명시
        xhr.setRequestHeader('Content-type', 'application/json'); //header : 제품의 설명서

        //보내기 전에 해야 할 일, JS obejct JSON으로 변환(직렬화)
        const jsonData = JSON.stringify(requestData);

        //실제 요청 전송
        xhr.send(jsonData);

    }, 3000);
})

'javascript' 카테고리의 다른 글

2.ES6/Promise  (0) 2022.09.13
0.javascript 문법  (0) 2022.08.18