반응형

테스트 자동화에 대해 알아보겠습니다.

 

개발을 진행하다보면 다양한 함수를 작성하고 적용하는 과정에서 다양한 케이스별 테스트를 진행하게 됩니다.

단순한 코드라면 테스트도 크게 어렵지 않고 실수나 잘못된 연산 있더라도 쉽게 수정이 가능하지만, 복잡하고 케이스별로 오류가 발생할수 있는 함수를 작성 중이라면, 아래와 같은 경우를 많이 만나 볼 수 있습니다.

 

1. test( params ); 라는 함수를 작성하였다.

2. test(1)은 정상 동작한다.

3. test(2)는 오류가 발생한다.

4. test(2)에 대한 오류를 수정하고, test(2)가 정상 동작한다.

> 수정을 했으니 이제 정상 동작하는 소스일까? ===> 아니오

수정으로 인해 test(2)는 정상이 되었지만, 정상동작하던 test(1)이 오류가 발생할 수도 있고 다른 케이스별 오류가 발생할 수 있다.

 

개발을 하다보면 생각보다 굉장히 자주 만나는 현상입니다.

그럼 개발자는 무엇을 어떻게 할까요?

정상 동작을 하는지 유형 별로 정리를 하고 작성한 코드를 테스트 > 수정 > 테스트를 반복합니다.

 

이렇게 수동으로 테스트를 진행하다보면 에러가 발생할 여지가 있습니다.

 

 

 

 

 

 

BDD방법론

BDD는 테스트(test), 문서(documentation), 예시(example)를 한데 모아놓은 개념으로 실제 개발사례를 통해 BDD에 대해 알아보겠습니다.

 

 

 

 

거듭제곱 함수를 통해 자동화 테스트 적용해보기

pow라는 이름의 함수를 만들고 BDD에 적용해보겠습니다.
(자바스크립트 자체에 ** 연산자나 Math.pow 메소드를 활용해도 되지만 BDD 학습을 위해 작성합니다.)
 
describe("거듭제곱 출력 함수", function() {
	it("매개변수의 n제곱", function() {
		assert.equal(pow(2, 3), 8);
	});
});
 

describe("title", function(){...})

구현하고자 하는 기능에 대해 설명이 들어갑니다. 여기선 pow함수에 대한 설명을 입력합니다.

 

it("유형별 설명", function(){...})

it의 첫번째 인수엔 특정 유스 케이스에 대한 설명이 들어갑니다. 이곳에는 누가 읽고 이해할 수 있도록 자연어로 작성합니다. 두번째 인수엔 테스트 할 함수가 들어갑니다.

 

assert.equal(value1, value2)

기능을 제대로 작성했다면 해당 구문이 오류없이 실행됩니다.

value1과 value2가 일치하지 않는다면 오류가 발생합니다.

value1에 pow(3, 3)를 value2에는 81을 입력하면 일치할 것이고 value2에 다른값을 입력하면 에러가 발생합니다.

 

 

사용되는 라이브러리

3가지의 라이브러리를 활용합니다.

Mocha - describe, it와 같은 테스팅함수와 테스트 관련 주요함수를 제공합니다.

Chai - 다양한 assertion을 제공하는 라이브러리입니다. (assert.equal)

Sison - 함수의 정보를 캐내는 라이브러리로 내장 함수를 모방합니다.

 

 

 

 

 

자동화 테스트해보기

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css"> <!-- Mocha css -->
</head>
<body>
    <div id="mocha"></div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js"></script> <!-- Mocha -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script> <!-- chai -->
    <script>
        let assert = chai.assert;
        window.onload = function(){
            mocha.setup('bdd'); // mocha setup

            describe("pow함수 검증하기", function(){
                it("주어진 숫자의 n 제곱", function(){
                    assert.equal(pow(3, 3), 27);
                    assert.equal(pow(3, 4), 81);
                    assert.equal(pow(3, 5), 243);
                });

            });

            mocha.run();
        };
        
        //pow 작성
        function pow(x, n) {
            if(n < 2){
                return x;
            }
            return x * pow(x, --n);
        }
    </script>
</body>
</html>

거듭제곱 메소드를 재귀함수를 통해 작성하였습니다.

작성한 pow를 테스트화 시킨 코드입니다. 아래에서 테스트 결과를 보겠습니다.

 

결과

테스트 정상 화면

title과 테스트 결과가 정상적인경우 위 사진처럼 발생합니다.

 

 

 

 

 

오류 발생시키기

describe("pow함수 검증하기", function(){
    it("주어진 숫자의 n 제곱", function(){
        assert.equal(pow(3, 3), 27);
        assert.equal(pow(3, 4), 81);
        assert.equal(pow(3, 5), 243);
        assert.equal(pow(2, 2), 222);
    });
});

일부러 마지막에 오류를 내면 어떻게 될까요?

 

오류 결과

정상동작하는 3개에 대해선 문제가 없지만 오류가 발생중인 부분에 대해서 오류 메시지를 출력해줍니다.

오류메시지를 세분화 해보겟습니다.

 

 

 

 

 

오류메시지 분기하기

describe("pow함수 검증하기", function(){
    it("주어진 숫자의 n 제곱 3^3 = 27", function(){
        assert.equal(pow(3, 3), 27);
    });
    it("주어진 숫자의 n 제곱 3^4 = 81", function(){
        assert.equal(pow(3, 4), 81);
    });
    it("주어진 숫자의 n 제곱 3^5 = 243", function(){
        assert.equal(pow(3, 5), 243);
    });
    it("주어진 숫자의 n 제곱 2^2 = 4", function(){
        assert.equal(pow(2, 2), 222);
    });
});

각 유형별로 it로 나누고 각각의 설명을 기재하였습니다.

아래는 오류 메시지 결과입니다.

 

분기처리한 오류 메시지 결과

체크박스 형태로 성공은 체크, 실패한 부분은 x와 함께 메시지를 출력해주고 있네요.

유형별 결과를 한눈에 보기엔 분기처리 한 것이 보기 좋은것 같습니다.

 

 

 

 

 

자료 출처: https://ko.javascript.info/testing-mocha

 

테스트 자동화와 Mocha

 

ko.javascript.info

반응형
반응형

자료 출처 :

- https://medium.com/@gaurav.pandvia/understanding-javascript-function-executions-tasks-event-loop-call-stack-more-part-1-5683dea1f5ec

- https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf

- https://new93helloworld.tistory.com/358

- https://velog.io/@jakeseo_me/2019-03-15-2303-%EC%9E%91%EC%84%B1%EB%90%A8-rmjta5a3xh

- https://meetup.toast.com/posts/89

 

웹 개발자로 년차가 쌓이고 있는 현 시점에서 자바스크립트의 내부 동작을 이해할 필요가 있었고, 정보들을 찾아보면서 원문과 해당 글들을 해석, 분석하신 분들의 글 등을 읽어보고 개인적으로 정리해보기 위해 작성하는 글입니다.

원문이나 기존 글들이 너무 좋아서 해당 글들을 먼저 보시는게 좋을 것 같습니다 :)

 


javascript 콜스택(call stack), 이벤트루프(event Loop)의 개념

원문 글들에서도 보이듯 현대의 자바스크립트 영향력은 기존 웹 페이지의 동적인 변화만을 위한 언어가 아닌 프론트, 백엔드, DB(CouchDB), 임베디드 프로그래밍 등 다양한 영역에서 영향력을 보이고 있습니다.

이렇게 다양한 기능을 하는 자바스크립트는 어떻게 동작이 되고 있는지 궁금해졌고 관련 키워드들을 보면서 콜스택, 이벤트 루프와 같은 개념들이 나왔습니다.🤔

이러한 키워드들의 내용을 학습하고 이해하는 것이 이번 글의 목표입니다.

 

 

자바스크립트 엔진

“V8” 웹 개발을 하면서 한번 정도는 들어봤을 자바스크립트 엔진입니다. 크롬 브라우저, Node.js에서 동작하는데, 메모리힙과 콜스택으로 이루어져 있습니다. 엔진이 코드를 읽고 실행하면서 변수나 함수 객체등을 담아놓는 공간(메모리 힙)과 실행할 코드를 저장하는 공간(콜 스택)으로 나뉩니다.

-메모리 힙(Memory Heap): 변수, 함수, 객체등이 선언되면 자동으로 할당하고 불필요하면 자동으로 해제되는데(가비지 컬렉터) 이곳에 저장이 된다.

-콜 스택(Call Stack): 실행해야 할 코드를 쌓는 공간이다.

 

 

Web API

브라우저에는 엔진뿐만 아니라 Web API가 존재하는데 자바스크립트 개발자들이 사용하는 다양한 API(DOM,  setITimeout, Ajax)들은 자바스크립트 엔진이 아닌 브라우저의 Web API를 통해 제공됩니다.

이러한 API를 통제하기 위해서 Event Loop, Task Queue가 존재합니다.

 

 

콜 스택(Call Stack)

먼저 자료구조 스택(stack)에 대해서 간단히 알아보겠습니다.

쌓는다는 뜻으로 가장 마지막에 들어온게 먼저 나가는 구조로 후입선출 구조입니다. 삽입은 push, 삭제는 pop입니다.

 

자바스크립트는 싱글 스레드 언어입니다.

즉, 한 번에 한 개의 일만 처리 할 수 있다는 뜻이죠.

콜 스택이란 함수의 호출 순서를 기록하는 자료구조로 어떤 함수를 실행하면 콜 스택에 추가(push)되며 함수로부턴 반환받을 때 콜 스택의 맨 위를 가져(pop)옵니다.

 

-콜 스택의 동작 예시

function tenSum(x){
	return x + 10;
}

function printSum(x){
	const sum = tenSum(x);
	console.log(sum);
}
printSum(5);

call stack 동작

함수 선언은 메모리 힙에 올라가고 콜 스택은 실행될 함수들이 쌓인다고 했었죠?

Stack1 printSum(5)를 실행하기 위해 push됩니다.

Stack2 printSum(5)안에 tenSum(x)를 실행하고 있습니다 push됩니다.

Stack3 tenSum(x)가 15를 리턴하면서 tenSum(x)는 pop되고, console.log(sum)을 push합니다.

Stack4 console.log(sum)가 리턴(출력)하면서 pop됩니다.

Stack5 printSum(5)가 리턴하면서 pop됩니다.

 

 

싱글 스레드 방식의 문제점

일반적으로 작성한 코드의 경우 문제점이 전혀 없지만 단 하나의 실행함수가 처리해야할게 너무 많아서 다음 함수로 못넘어가고 콜 스택에서 차지하고 있으면 어떻게 될까요?🙄

다음 함수들은 앞의 함수가 끝나길 무기한 기다릴 것이고, 최악의 상황엔 브라우저가 응답이 없다는 메시지와 함께 페이지가 뻗어버리는 현상💢이 발생할 수도 있습니다. (신입시절 실시간(약 3초마다)으로 수천개의 데이터를 불러와서 갱신하는 페이지가 있었는데 반복문 하나를 잘 못 짜서 페이지가 뻗는현상을 경험해봤습니다... 😥)

 

하지만 자바스크립트로 만드는 페이지에서 싱글 스레드라고 느껴본적이 있었나요?

페이지에서 애니메이션이 돌면서 클릭이벤트와 키이벤트가 동시에 이루어지고 한번에 여러곳에 비동기 데이터를 요청을 하면서 프로그레스바같은 애니메이션이 돌고 있습니다.

이런 행위가 가능하게 하는 것은 '이벤트 루프(Event Loop)'덕분입니다.

 

 

이벤트 루프?

자바스크립트 엔진에 존재하는 콜 스택은 요청이 들어올때 해당 요청을 순차적으로 담아서 처리를 한다. 그럼 비동기 요청과 동시성 처리(애니메이션이 돌면서 클릭이벤트 등)는 어떤식으로 처리가 될까?

 

브라우저의 환경은 다음과 같다.

출처:&nbsp;https://meetup.toast.com/posts/89

그림을 살펴보면 우리가 자주 사용하던 API들인 setInterval, XMLHttpRequest와 같은 함수들은 웹 API로 따로 존재하며, 해당 함수들을 자바스크립트 엔진이 제공하지 않는다. 그 외에도 이벤트 루프테스크 큐를 볼 수 있다.

 

자바스크립트는 싱글 스레드 언어라는 말은 엔진이 단일 호출 스택을 사용되는 관점에서 파생된 말이고, 실제 브라우저 환경에서는 여러 개의 스레드가 동작하며 연동을 위해 사용되는 장치가 '이벤트 루프(Event Loop)'이다.

 

 

테스크 큐(Task Queue), 이벤트 루프(Event Loop)

그럼 비동기 Web API 요청들은 어떻게 동작할까?

 

예시 코드를 보겠다.

function run(){
	console.log("run");
	foo();
}

function foo(){
	console.log("foo");
}

function bar(){
	console.log("bar");
}

setTimeout(bar, 10);
run();

동작 결과

 

bar를 먼저 실행하지만 setTimeout 10ms로 인해 run, foo, bar 순으로 console.log가 찍힌다.

실제로 run, foo가 오래걸리는 상황이라 10ms보다 오래걸려도 위 순서는 지켜진다.

 

어떻게 setTimeout함수는 foo가 끝나자마자 bar를 실행 시켰을까?

바로 테스크 큐와 이벤트 루프가 처리 했기 때문이다.

이벤트 루프는 호출 스택이 비워질 때마다 큐에서 콜백함수를 꺼내와서 실행하는 역할을 해준다.

예제를 기준으로 처음 실행될 때, 해당 코드는 "현재 실행중인 테크스"가 된다.

코드를 실행하는 도중 10ms가 지나면 bar를 바로 실행하지 않고 테스크 큐에 추가한다.

이벤트 루프는 "현재 실행중인 테스크"가 종료되면 테스크 큐에 대기중인 첫번째 테스크를 가져와서 실행한다.

 

 

 

 

이벤트 루프는 주기적으로 "현재 실행중인 테스크"가 있는지 체크하고 없다면 "테스크 큐에 테스크가 있는지"를 반복적으로 확인한다.

 

즉, 모든 비동기 API들은 작업이 완료되면 콜백 함수를 테스크 큐에 추가해두고 자신의 순서를 기다린다.

"현재 실행중인 테스크"가  없으면 테스크큐의 첫번째 큐("콜백함수")를 가져와서 실행한다.

반응형
  1. 익명 2022.05.13 22:32

    비밀댓글입니다

    • Favicon of https://myhappyman.tistory.com Park.S.W 2022.05.16 09:01 신고

      앗.. 너무 급하게 조사해서 정리한거같네요 ㅋㅋㅋㅋ 다시 한번 확인하고 수정해서 공개하겠습니다~

반응형

데이터 시각화 작업을 하다보면 다양한 오픈소스와 차트 라이브러리를 사용하게 되는데, 그 중 오픈소스이며 MIT 라이선스를 따르는 chart.js로 세계지도 만들기 예제를 알아보겠습니다.🚀

공식 홈페이지를 가도 샘플페이지등을 확인해봐도 지도형 차트는 보이지 않는데, github 지도 관련하여 배포중인 사이트가 있습니다.

 

 

https://github.com/sgratzl/chartjs-chart-geo

 

GitHub - sgratzl/chartjs-chart-geo: Chart.js Choropleth and Bubble Maps

Chart.js Choropleth and Bubble Maps. Contribute to sgratzl/chartjs-chart-geo development by creating an account on GitHub.

github.com

위 페이지를 들어가보면 다양한 모양의 지도차트를 볼 수 있습니다. 제공중인 샘플소스를 CodePen페이지를 통해 제공중이니 적용해보셔도 좋습니다.

 

 

Chart.js 세계지도 데이터 시각화하기

필요한 리소스

- chart.js 지도 geo처리 > https://unpkg.com/chartjs-chart-geo@3.5.2/build/index.umd.min.js

- 지도를 그리기 위한 위 경도  json정보 > https://unpkg.com/world-atlas@2.0.2/countries-50m.json

 
world-atlas는 세계 지리 위 경도 지도정보를 제공하고 있습니다.
해당 url로 fetch함수를 통해 json데이터 받고, chart.js geo에 맞도록 가공하여 세팅합니다.
 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chart.js 세계지도</title>
</head>
<body>
    <div>
        <canvas id="myChart"></canvas>
    </div>

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script type="text/javascript" src="https://unpkg.com/chartjs-chart-geo@3.5.2/build/index.umd.min.js"></script>
<script>
const url = "https://unpkg.com/world-atlas@2.0.2/countries-50m.json"; //세계지도를 그리기 위한 json
fetch(url).then(result => result.json()).then((datapoint) => {
    //나라 뽑아내기
    const countries = ChartGeo.topojson
                        .feature(datapoint, datapoint.objects.countries).features;

    //차트를 그리기 위한 데이터셋
    const data = {
        labels: countries.map(country => country.properties.name),
        datasets: [{
            label: 'Countries',
            data: countries.map(country => ({feature: country, value: Math.random()*100})) //노출데이터는 랜덤값으로 표기
        }]
    };
    
    //차트 옵션
    const config = {
        type: 'choropleth', //차트 모양 > 지구본 타입 설정
        data,
        options: {
            showOutline: true, //지구본 원형라인 잡아주기
            showGraticule: true, //지구본 위, 경도선 그려주기
            scales: {
                xy: {
                    projection: 'equalEarth' //지구본 모양으로 변경
                }
            },
            plugins: {
                legend: {
                    display: false //chart.js차트 범례 숨기기(별도 표기가 됨)
                }
            }
        }

    };

    const myChart = new Chart(
        document.getElementById('myChart'),
        config
    );  
});
</script>
</body>
</html>

상세 옵션정보는 주석 또는 chart.js geo github페이지를 확인해주세요.

 

결과물

chart.js를 활용한 세계지도 차트

값은 랜덤으로 설정하여 페이지 로드시 매번 바뀌는걸 볼 수 있습니다.

반응형
반응형

웹 개발을 하다보면 서버에서도 유효성 검증을 필수로 하지만 프론트에서 1차적으로 필터링을 해주면 아무래도 서버에 부하도 적어지고 데이터 타입등 유효하지 않은 데이터를 걸러낼수 있어서, 자바스크립트를 통해 필수적으로 ajax나 submit 처리 전에 데이터 검사를 하게 됩니다.

 

 

문자열 검사하기

문자열을 검사하고 싶으면 생각보다 굉장히 간단하게 검사할 수 있습니다.

기존에는 undefined, null, 공백값등을 체크하는 조건문을 길게 쓰기 싫어서 함수를 만들어서 적용하는 방식으로 사용하거나 조건문에 길게 나열하곤 했었는데, 그 모습은 아래와 같습니다.

 

let text;
if(text === undefined || text === null || text === ''){
	alert("문자열이 비었습니다.");
}

하지만 실상 빈값에 대한 유효성 검증은 변수에 !(not 연산자) 하나만 붙여주면 됩니다.

 

 

 

NOT연산자 하나를 통한 유효성 검증

let text = '';
if(!text){
	alert("문자열이 비었습니다.");
}

!undefined //true
!null      //true
!''        //true
!0         //true
!NaN       //true

 

반응형
반응형

어떤 블로거분 글을 보다가 너무 이쁘게 꾸며져 있어서 이건 뭘까하고 개발자 정신이 발동되어 f12부터 눌렀는데 자꾸 티스토리 페이지로 강제 이동이 되었다...

 

오잉 뭐지?! 하고 또 시도해봤는데.. 2차 팅...

이번엔 컨트롤 쉬프트 c로 강제로 열어봤는데 또 튕긴다...

관리자모드를 열어둔상태로 가봤는데도 튕기고 옵션에 찾아가서 클릭으로 열어도 튕긴다

 

... 우와 신기하다!!

 

어떻게 한건지 찾아보니 이미 감시하는 이벤트를 만들어두신분들이 있었다.

 

출처: https://dev.to/composite/a-simple-way-to-detect-devtools-2ag0

 

A simple way to detect devtools.

After I made this implementation, I found related issue on stack overflow....

dev.to

!function() {
  function detectDevTool(allow) {
    if(isNaN(+allow)) allow = 100;
    var start = +new Date(); // Validation of built-in Object tamper prevention.
    debugger;
    var end = +new Date(); // Validates too.
    if(isNaN(start) || isNaN(end) || end - start > allow) {
      // input your code here when devtools detected.
    }
  }
  if(window.attachEvent) {
    if (document.readyState === "complete" || document.readyState === "interactive") {
        detectDevTool();
      window.attachEvent('onresize', detectDevTool);
      window.attachEvent('onmousemove', detectDevTool);
      window.attachEvent('onfocus', detectDevTool);
      window.attachEvent('onblur', detectDevTool);
    } else {
        setTimeout(argument.callee, 0);
    }
  } else {
    window.addEventListener('load', detectDevTool);
    window.addEventListener('resize', detectDevTool);
    window.addEventListener('mousemove', detectDevTool);
    window.addEventListener('focus', detectDevTool);
    window.addEventListener('blur', detectDevTool);
  }
}();

if문쪽에 처리하고 싶은 로직을 넣으면 된다.

alert, location.href  등등

 

 

2022/05/12 추가----------------

당시 브라우저 설정이나 문제가 있었던거 같습니다. 결국 해당 코드도 디버거 효과로 중간에 강제로 읽게하고 꺼지는 기능인지라... 사실 관리자도구로 찍어볼만한건 다 찍어보고 끌수 있습니다.(즉 별 의미 없다는 소리입니다...ㅜㅠ)

반응형
반응형

자바스크립트 공부를 하다보면 심심치 않게 만나는 호이스팅이라는 개념...😲

들어보기도 하고 어느정도는 알고 있었지만 혼란스러운 부분이 있어서 정리가 필요했습니다!

 

먼저 간단한 자바스크립트 문법부터 보겠습니다.

 

 

함수의 호이스팅 동작

console.log("결과는? >" + test());
function test(){
    return "hi";
}

 

해당 코드의 결과는?! 아래와 같습니다!

함수 test를 호출함으로써 "hi"라는 문자열이 정상적으로 반환되어서 콘솔에 hi가 잘 찍히는걸 볼 수 있습니다.

 

실질적으로 정의된 test함수는 출력하는 콘솔로그보다 아래에 위치하지만 신기하게도 동작하는걸 볼 수 있는데 이러한 동작을 호이스팅이라고 합니다.

변수와 함수간에 동작이 조금 다른데, 이번엔 변수 동작부분을 확인해보겠습니다.

 

 

변수의 호이스팅 동작

var a = "테스트입니다~";
function test(){
    console.log("a1 >",a);
    var a = "테스트 함수에서 a변수를 재정의합니다!";
    console.log("a2 >",a);
}
test();

이번에 작성한 코드의 결과는 어떨까요? 저도 이부분에 처음에 당황을 했습니다.

 

결과는 아래와 같습니다.

예상한 결과와 같나요?

제가 처음 예상했던 답은 "테스트입니다~"가 먼저 출력되고, 재정의되었다는 문자열이 출력 될것이라고 생각했지만, 호이스팅으로 인해 전혀 다른 결과가 나왔습니다.

 

아래는 호이스팅 방식을 기반으로 해석한 코드입니다.

var a = "테스트입니다~";
function test(){
    var a;
    console.log("a1 >",a);  //undefined
    a = "테스트 함수에서 a변수를 재정의합니다!";
    console.log("a2 >",a);  //테스트 함수에서 a변수를 재정의합니다!
}
test();

해석한 코드와 기존 코드의 차이가 보이나요?

호이스팅으로 인해 test함수 최상단에 a라는 변수가 선언되었고, 콘솔로그에서 출력이 되면서 undefined라는 결과물이 나왔고, 그 이후 대입연산자를 통해 재정의라는 문자열이 대입된 것을 볼 수 있습니다.

 

이처럼 블록 구문(함수) 최상단에 변수 또는 함수명인 식별자를 최상단으로 끌어올리는 효과를 보여주는 것을 호이스팅이라고 부릅니다.

 

 

복잡한 호이스팅 동작과 전역 스코프

이번엔 복잡해진 호이스팅을 확인해보겠습니다.

var a = 1;
function outer () {
    function inner () {
        console.log(a);
        var a = 3;
        console.log(a);
        var a;
        console.log(a);
        var a = 5;
        console.log(a);
    }
    inner();
    console.log(a);
}
outer();
console.log(a);

복잡한 결과물

 

복잡하지만 차근차근 호이스팅을 분석해보겠습니다.

var a;
a = 1;
function outer () {
    function inner () {
        var a;
        var a;
        var a;

        console.log(a); //undefined
        a = 3;
        console.log(a); //3
        console.log(a); //3
        a = 5;
        console.log(a); //5
    }
    inner();
    //내부에 a변수가 없으므로 외부 스코프영역에서 a가 존재하는지 찾음
    console.log(a); //1
}
outer();
console.log(a); //1

어떤가요? 분석구문과 실제 결과와 동일한가요?

내부 스코프에 해당 변수의 선언된게 없으면 외부 스코프에서 해당 변수가 선언되었는지 확인을 합니다.

아우터부분의 a에서 그래서 1이 찍히는걸 볼 수 있습니다.

 

 

정리

다만, 변수와 함수는 또 다른 차이점이 있습니다.

- 변수는 변수명만 최상단에 정의 된 후 선언 위치에서 값이 대입되는 모습을 보여줍니다.

- 함수는 정의부 자체와 함께 최상단에 선언되는 것을 볼 수 있습니다.

 

이러한 차이점때문에 함수는 처음에 호출하여도 정의부가 잘 동작 후 출력되는것을 볼 수 있엇고, 변수는 최초 선언만 되었기 때문에, 결과 호출시 undefined와 같은 결과를 만날 수 있습니다.

 

 

다만, 함수 동작의 호이스팅에서 함수 정의 방법에 따라 달라는 결과를 볼 수 있습니다.

 

 

다양한 함수 표현방법의 호이스팅 결과

console.log(test1());
console.log(test2());
console.log(test3());

function test1(){
    return "test1";
}

var test2 = function(){
    return "test2";
}

var test3 = () => {
    return "test3";
}

function 키워드를 통해 함수를 정의하지만 변수에 함수를 대입하여 함수의 동작을 처리할 수도 있고, 화살표 함수를 통해 표현할 수도 있습니다.

다만 호이스팅 처리방식의 변수와 함수 표현식의 차이로 인해 test2, test3는 정상적으로 동작하지 않습니다.

 

아래는 동작 결과입니다.

초점을 동작하는 행위에 두지 않는다면 당연한 결과일수도 있습니다. (변수로 시작했는지, 함수로 시작했는지)

상단에 정의된 내용까지 호이스팅하는 동작은 function키워드로 정의한 부분만 동작하는 것을 볼 수 있습니다.

반응형
반응형

카카오 주소🏠 찾기 API

 

https://postcode.map.daum.net/guide

 

Daum 우편번호 서비스

우편번호 검색과 도로명 주소 입력 기능을 너무 간단하게 적용할 수 있는 방법. Daum 우편번호 서비스를 이용해보세요. 어느 사이트에서나 무료로 제약없이 사용 가능하답니다.

postcode.map.daum.net

 

해당 공식 홈페이지에 접속해보면 어렵지 않게 연동을 해 볼 수 있습니다.

요즘 많은 사이트들의 주소 입력창을 보면 아래 캡처와 같은 형태를 자주 접할 수 있었는데, 많은 사이트에서 활용 중인것 같습니다.🙆‍♂️

카카오 주소찾기 양식

 

카카오는 카카오 발송, 지도 API 등 다양한 서비스를 제공하는데 사용을 위해 키 발급, 일일 사용량 제한 또는 용도에 따른 유료처리 등 이슈가 존재합니다.

하지만, 주소 API는 조금 다르게 제공이 됩니다. 👀

아래는 제공되는 형태와 제한에 대한 내용입니다.

https://postcode.map.daum.net/guide

덕분에 연동방법만 익히면 굉장히 쉽게 사용할 수 있습니다. (뭐 거의 준비물이 외부망이 되는 환경이면 끝이네요! 😎)

 

사용법은 아래와 같습니다.

<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script>
    new daum.Postcode({
        oncomplete: function(data) {
            // 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분입니다.
            // 예제를 참고하여 다양한 활용법을 확인해 보세요.
        }
    }).open();
</script>

 

카카오 주소찾기 적용 예제🐱‍🏍

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kakao API</title>
</head>
<body>
    <table>
        <tr>
            <th>이름</th>
            <td><input type="text" name="user_name"></td>
        </tr>
        <tr>
            <th>주소</th>
            <td><input type="text" id="address_kakao" name="address" readonly /></td>
        </tr>
        <tr>
            <th>상세 주소</th>
            <td><input type="text" name="address_detail" /></td>
        </tr>
    </table>
</body>
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script>
window.onload = function(){
    document.getElementById("address_kakao").addEventListener("click", function(){ //주소입력칸을 클릭하면
        //카카오 지도 발생
        new daum.Postcode({
            oncomplete: function(data) { //선택시 입력값 세팅
                document.getElementById("address_kakao").value = data.address; // 주소 넣기
                document.querySelector("input[name=address_detail]").focus(); //상세입력 포커싱
            }
        }).open();
    });
}
</script>
</html>

테스트 예시.gif

 

반응형
  1. Favicon of https://myhappyman.tistory.com/234 도와주십쇼 2021.07.09 14:55

    선생님께서 올리신 게시글 https://myhappyman.tistory.com/234 를 보고 저도 똑같이 수정을 하고 있습니다.
    워드프레스의 tinymce advanced 프로그램을 쓰고 있는데, 당췌 저 코드를 어디다 넣어야 하는지 못찾겠습니다.
    도움을 좀 주신다면 감사하겠습니다... https://open.kakao.com/o/sPljjFI 1:1 대화방입니다. 설명이 어려우시면 여기서라도...
    혹 번거로우시다면 포스팅만으로도 감사합니다.

    • Favicon of https://myhappyman.tistory.com Park.S.W 2021.07.09 15:26 신고

      안녕하세요 tinyMce 에디터 적용 글에 대한 문의를 남겨주셨는데 wordpress는 제가 제대로 사용해본적이 없어서 자세하게는 모르지만 관리자페이지-플러그인 쪽에서 tincymce를 적용할 수 있는것으로 보입니다. 해당글은 html페이지에서 적용법이라 문의하신 내용과 성격이 조금 다른것같습니다 :)

  2. 무지한 2021.08.27 07:58

    저 주소 검색창을 구글 설문지에서도 구현할 수 있을까요?
    사실 이런 분야에 대해 1도 모르는 문외한입니다 ㅠㅜ

    • Favicon of https://myhappyman.tistory.com Park.S.W 2021.08.27 08:39 신고

      구글 설문지는 제가 사용해본 플랫폼은 아니라서 자세한건 모르지만 질문형식과 답변형식을 작성하면 제공하는형태일것같은데 html과 스크립트 코딩이 커스텀으로 지원이 되는지 모르겠네요!

  3. 익명 2021.11.19 14:02

    비밀댓글입니다

    • 익명 2021.11.22 08:51

      비밀댓글입니다

반응형

새로운 프로젝트를 들어가면서 새로운 텍스트 에디터를 적용해보기 위해 이것저것 찾던 도중 IE에서 잘 동작이되며(이놈의 IE...) 무료 라이센스인 것들을 고르고 고르다보니 기능이 없거나 이쁘지가 않거나 여러 이슈들이 있었는데, 만족할만한 에디터를 찾아서 공유해보고자 합니다.

 

텍스트 에디터 종류들은 WYSIWYG HTML Editor 형태로 검색해보시면 더 많은 에디터를 찾아 볼 수 있습니다.

 

TinyMce License로서 특별히 추가하는 plugin을 사용하지 않는다는 전제하에 무료입니다.

https://www.tiny.cloud/blog/tinymce-free-wysiwyg-html-editor/

 

The TinyMCE WYSIWYG HTML editor is free forever

Looking for a free WYSIWYG HTML editor? The TinyMCE core editor is free to use for commercial and noncommercial purposes. Fully functional with 44 plugins and ready to customize as needed.

www.tiny.cloud

 

Tiny Editor

https://www.tiny.cloud/docs/quick-start/

 

TinyMCE | Quick start

Get an instance of TinyMCE 5 up and running using the Tiny Cloud.

www.tiny.cloud

 

해당 글을 보고 먼저 테스트용 계정을 생성해서 api key를 받고 간단하게 http서버를 열어서 소스코드 테스트를 진행해보았습니다.

 

뭐 간단한 세팅은 진짜 간단합니다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>

    <script>
      tinymce.init({
        selector: '#mytextarea'
      });
    </script>

  </head>

  <body>
  <h1>TinyMCE Quick Start Guide</h1>
    <form method="post">
      <textarea id="mytextarea">Hello, World!</textarea>
    </form>
  </body>
</html>

간단하게 에디터의 모습으로 보입니다.

 

하지만 좀 더 블로그에서 사용하는 에디터나 이쁘게 커스텀을 하고 싶어서 여러 옵션을 통해 좀 더 변경을 해보았습니다.

 

 

개인적으로 변경한 Tiny Editor

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.tiny.cloud/1/각자 발급받은 api key/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>
</head>
<body>
    <h1>TinyMCE Quick Start Guide</h1>
    <form method="post">
        <textarea id="editor">Hello, World!</textarea>
        <button type="button" id="save">게시글 저장</button>
    </form>
</body>

<script>
$(function(){
    var plugins = [
        "advlist", "autolink", "lists", "link", "image", "charmap", "print", "preview", "anchor",
        "searchreplace", "visualblocks", "code", "fullscreen", "insertdatetime", "media", "table",
        "paste", "code", "help", "wordcount", "save"
    ];
    var edit_toolbar = 'formatselect fontselect fontsizeselect |'
               + ' forecolor backcolor |'
               + ' bold italic underline strikethrough |'
               + ' alignjustify alignleft aligncenter alignright |'
               + ' bullist numlist |'
               + ' table tabledelete |'
               + ' link image';

    tinymce.init({
    	language: "ko_KR", //한글판으로 변경
        selector: '#editor',
        height: 500,
        menubar: false,
        plugins: plugins,
        toolbar: edit_toolbar,
        
        /*** image upload ***/
        image_title: true,
        /* enable automatic uploads of images represented by blob or data URIs*/
        automatic_uploads: true,
        /*
            URL of our upload handler (for more details check: https://www.tiny.cloud/docs/configure/file-image-upload/#images_upload_url)
            images_upload_url: 'postAcceptor.php',
            here we add custom filepicker only to Image dialog
        */
        file_picker_types: 'image',
        /* and here's our custom image picker*/
        file_picker_callback: function (cb, value, meta) {
            var input = document.createElement('input');
            input.setAttribute('type', 'file');
            input.setAttribute('accept', 'image/*');

            /*
            Note: In modern browsers input[type="file"] is functional without
            even adding it to the DOM, but that might not be the case in some older
            or quirky browsers like IE, so you might want to add it to the DOM
            just in case, and visually hide it. And do not forget do remove it
            once you do not need it anymore.
            */
            input.onchange = function () {
                var file = this.files[0];

                var reader = new FileReader();
                reader.onload = function () {
                    /*
                    Note: Now we need to register the blob in TinyMCEs image blob
                    registry. In the next release this part hopefully won't be
                    necessary, as we are looking to handle it internally.
                    */
                    var id = 'blobid' + (new Date()).getTime();
                    var blobCache =  tinymce.activeEditor.editorUpload.blobCache;
                    var base64 = reader.result.split(',')[1];
                    var blobInfo = blobCache.create(id, file, base64);
                    blobCache.add(blobInfo);

                    /* call the callback and populate the Title field with the file name */
                    cb(blobInfo.blobUri(), { title: file.name });
                };
                reader.readAsDataURL(file);
            };
            input.click();
        },
        /*** image upload ***/
        
        content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }'
    });


    $("#save").on("click", function(){
        var content = tinymce.activeEditor.getContent();
        console.log(content);
    });
    
});
</script>
</html>

* tiny 동작 script를 적용시 저는 개인적으로 발급받은 url주소를 입력하였습니다. 자신만의 api key를 발급받은 url로 연결하시면 될 것 같습니다.

 

변경 후에는 좀 더 깔끔하게 메뉴바를 지우고 툴바들로 위치시켰고, 필요한 테이블표, 이미지, 링크등을 추가하였습니다.

텍스트 제어 관련된 색상, 폰트, 컬러 등을 순차적으로 배치해보았습니다. 또한, language 옵션에서 한글번역이 가능하여 변경하였습니다.

 

form태그 내부에 게시글 저장이라는 button태그를 추가하였는데, 클릭하면 tiny자체에서 제공하는 getContent메소드를 통해 게시글 내부의 HTML 코드를 확인 할 수 있습니다.

 

해당 내용을 서버로 전송하여 저장하는 형태로 처리하면 될 것 같습니다.

 

 

 

여러가지 옵션 변경은 아래글을 참고하여주세요.

https://www.tiny.cloud/docs/

 

TinyMCE | Documentation

Official documentation for the most advanced and widely deployed rich text editor platform.

www.tiny.cloud

 

반응형