기술블로그

JavaScript 무엇인가?(2) 본문

Language & Solution/JavaScript

JavaScript 무엇인가?(2)

hc_Jo 2021. 8. 25. 20:54

웹 페이지에서 JavaScript는 역할

브라우저에서 웹페이지를 불러올 때, 실행 환경(브라우저 탭)안에서 HTML, CSS, Javascript 코드가 실행됩니다. 이는 마치 공장에서 원재료(코드)가 일련의 과정을 거쳐 제품(웹페이지)으로 탄생되는 것과 같습니다.

자바스크립트는 HTML과 CSS가 결합되고 웹페이지 상에서 올려진 후, 브라우저의 자바스크립트 엔진에 의해 실행됩니다. 이는 페이지의 구조와 스타일등을 정해놓고, 자바스크립트가 실행된다는 것과 같은 의미입니다.

 

동적으로 사용자 인터페이스를 업데이트하는 자바스크립트의 사용은 Document Object Model API를 통해 HTML과 CSS를 수정합니다. 만약 JS가 HTML과 CSS 전에 실행되었다면 문제가 발생할 것입니다.

 

브라우저 보안성

각각의 브라우저 탭들은 코드가 실행되는 개별적인 구성입니다. 이는 각 탭의 대부분의 경우는 완전히 독립적이고, 하나의 탭의 코드는 다른 탭이나 웹사이트에 직접적으로 영향을 줄 수 없다는 의미입니다 . 즉, 이는 보안성에 좋은 방법입니다. 만약 이러한 부분이 없다면, 해커들이 다른 웹사이트로 부터 정보를 가로채는 등 위험한 일들을 할 수 있습니다.

 

자바스크립트 실행 순서

브라우저에서 자바스크립트를 만났을 때 일반적으로는 위에서 아래 순서대로 실행됩니다. 이는 순서에 주의해서 코드를 작성해야한다는 의미입니다. 예를 들어, 아래의 첫번째 예재를 통해 자바스크립트 블록을 반환해봅시다:

const para = document.querySelector('p');
//HTML 요소 중 p태그를 선택

para.addEventListener('click', updateName);
//para에 저장된 객체가 클릭되었을 때 updateName 함수를 실행

function updateName() {
  let name = prompt('Enter a new name');
  //'Enter a new name'과 입력란 출력하여 입력받은 값을 name에 저장
  para.textContent = 'Player 1: ' + name;
  //papa(p태그)에 새로운 문자열 저장
}

먼저 p태그의 요소를 para변수에 저장합니다(1번줄). 그리고 event listener를 붙여(3번줄) p태그가 클릭되었을 때 updateName()코드 블록(중괄호로 묶여있는 부분)이 (5-8번줄) 실행되도록 합니다. updateName() 코드 블록(이렇게 계속적으로 사용할 수 있는 코드 블럭을 함수라고 합니다.). 사용자로 하여금 새로운 이름을 입력받기를 요청하고, 사용자가 이름을 입력하면 화면에 출력하게 됩니다.

 

만약 1번줄과 3번줄을 바꿨다면 코드는 실행되지 않을 것입니다. 대신 브라우저의 개발자 콘솔창에 다음과 같은 에러 알림이 뜰 것입니다. — TypeError: para is undefined. 이는 para라는 객체가 아직 존재하지 않는다는 뜻으로, para라는 변수에 event listener는 추가할 수 없습니다

 

해석형 언어와 컴파일러형 언어

프로그래머 입장에서 인터프리트와 컴파일이라는 개념에 대해서는 들어보았을 것입니다. 자바스크립트는 해석형 언어입니다. 따라서 코드가 위에서 아래로 순차적으로 실행되고 그 즉시 결과가 반환됩니다. 브라우저에서 동작하기 전에 다른 방식으로 코드를 변환할 필요가 없습니다.

 

반면에 컴파일러형 언어는 컴퓨터에 의해 동작되기전 다른 형식으로 변환하는 언어입니다. 예를 들면 C/C++과 같은 언어는 어셈블리어로 컴파일되어 동작됩니다.

 

  • 인터프리트 언어 :  컴파일러를 거쳐서 기계어로 변환되지 않고 바로 실행되는 프로그래밍 언어
  • 컴파일 언어 : 구현체들이 컴파일러를 거쳐서 소스 코드로부터 기계어를 생성하여 실행되는 프로그래밍 언어

이 둘의 관점은 각각의 장점을 가지고 있으니 다음장 부터 한번 알아봅시다.

 

서버측 코드와 클라이언트측 코드

웹 개발 맥락에서 서버측과 클라이언트측 코드에 대해 들어보았을 것입니다. 클라이언트측 코드란 사용자의 컴퓨터에서 작동되는 코드입니다. 만약 웹페이지를 보고자 한다면, 클라이언트측 코드가 사용자의 컴퓨터로 다운로드되고 브라우저가 이를 표시합니다. 이러한 자바스크립트 모듈을 정확히는 클라이언트측 자바스크립트라고 합니다.

 

반면 서버측 코드는 서버에서 작동되고, 그 결과가 사용자의 브라우저에 넘어가 표시됩니다. PHP, Python, Ruby, ASP.NET등이 서버측 웹 언어의 대표적 예라고 볼 수 있습니다. 물론 자바스크립트도 가능하다. Node.js란 환경을 통해 서버측에서도 자바스크립트가 사용 가능합니다.

Dynamic Websites – Server-side programming에서 서버측 자바스크립트에 대해 더 알 수 있습니다.

 

동적 VS 정적 코드

"동적"이라는 말은 클라이언트측, 서버측 언어 모두를 말합니다. 이는 각기 다른 상황에서 적절한 정보가 보이고, 컨텐츠를 웹페이지나 앱 상에 계속적으로 노출시키는 역할을 합니다. 서버측 코드는 데이터베이스로 부터 데이터를 전달하는 등 동적으로 새로운 컨텐츠들을 만듭니다. 반면에, 클라이언트측 자바스크립트는 새로운 HTML 표를 만들어 서버에서 요청한 데이터를 뿌려 사용자에게 보이는 등 동적으로 브라우저 안에서 작동됩니다. 이 둘 사이는 서로 미묘한 차이가 있지만, 서로 연관되어 있고 서버측 클라이언트측의 관계와 접근에 대해 알 필요가 있습니다.

 

동적으로 바뀌지 않는 페이지를 "정적"페이지라고 합니다. (항상 같은 콘텐츠를 보여줍니다.)

 

웹 페이지에 JavaScript를 어떻게 넣나요?

자바스크립트는 CSS와 같은 방식으로 HTML 페이지에 적용됩니다. CSS는 외부의 스타일시트를 적용하기 위해 link 요소를 사용하거나 내부의 스타일시트를 적용하기 위해 style 요소를 사용하는 반면,자바스크립트는 HTML상에서 오직 script 태크만으로 사용이 가능합니다. 어떻게 작동되는지 한번 살펴봅시다.

HTML 내부의 자바스크립트

  1. 먼저, 예제로 주어진 apply-javascript.html파일을 저장합니다.
  2. 파일을 브라우저와 편집기 상에서 둘다 엽니다. HTML으로 만든 클릭 버튼이 있는 간단한 웹페이지를 볼 수 있습니다.
  3. 그런다음, 편집기로 가서 </body> 태그 직전에 다음의 코드를 추가하도록 합니다:
    <script>
    
      // JavaScript goes here
    
    </script>

그러고 아래의 자바스크립트 코드를 <script></script>사이에 넣음으로서 페이지 상에서 동작이 가능하게끔 할 수 있습니다.( 위 코드에서 "// JavaScript goes here" 부분에 아래의 코드를 추가하면 됩니다.)

 

document.addEventListener("DOMContentLoaded", function() {
  function createParagraph() {
    let para = document.createElement('p');
    para.textContent = 'You clicked the button!';
    document.body.appendChild(para);
  }

  const buttons = document.querySelectorAll('button');

  for(let i = 0; i < buttons.length ; i++) {
    buttons[i].addEventListener('click', createParagraph);
  }
});
  1. 파일을 저장하고 새로고침을 눌러보세요. 이제 버튼을 클릭하면 새로운 문단이 아래쪽에 생기는 것을 볼 수 있습니다.

외부의 자바스크립트

만약에 외부 파일로 자바스크립트를 위치시키고 싶다면 어떻게 할까요? 이에 대해서 알아봅니다.

  1. 먼저, HTML 파일이 있는 디렉토리에 script.js라는 새로운 파일을 만듭니다. 파일의 확장자가 .js이면 그 파일이 자바스크립트로 이루어져 있음을 뜻합니다.
  2. 아래의 태그를 HTML 코드에 복사 후 저장합니다.
  3. <script src="script.js"></script>​
  4. script.js 의 내용을 다음과 같이 바꿉니다.
    function createParagraph() {
      let para = document.createElement('p');
      para.textContent = 'You clicked the button!';
      document.body.appendChild(para);
    }
    
    const buttons = document.querySelectorAll('button');
    
    for(let i = 0; i < buttons.length ; i++) {
      buttons[i].addEventListener('click', createParagraph);
    }
  5. 저장하고 브라우저를 새로고침하면 앞과 똑같은 결과가 나올것입니다. 똑같이 작동하기 때문에 이제 자바스크립트는 외부에서 만들 수 있음을 알 수 있습니다. 이는 코드를 만들고 구성하는 입장에서 좋으며, 여러 HTML파일로 부터 재사용이 가능합니다. 더군다나 HTML은 스크립트의 본문이 외부로 분리되어 간결해집니다.

인라인 JavaScript 처리기

실제 HTML 속에 포함된 자바스크립트코드를 함께 쓸 수 있습니다. 

function createParagraph() {
  let para = document.createElement('p');
  para.textContent = 'You clicked the button!';
  document.body.appendChild(para);
}
//HTML 내의 <scirpt>태그 내부에 작성
<button onclick="createParagraph()">Click me!</button>

이 예제는 <button>태그에 onclick속성에 대한 값을 함수이름으로 넣어 버튼이 클릭될 때마다 함수가 실행되도록 작성하였습니다.

 

하지만, 이 방법은 효율적이지 않습니다. 이는 자바스크립트와 함께 HTML 소스를 복잡하게 할 수 있습니다. 또한 함수를 만들기 위한 모든 버튼 마다 onclick="createParagraph()" 속성을 포함해야합니다.

 

JavaScript 코드만으로도 모든 버튼에 함수를 연결할 수 있습니다. 위의 내용을 의도한대로 수정한다면 다음과 같습니다:

const buttons = document.querySelectorAll('button');
//모든 <button>태그를 List 형태로 buttons 변수에 저장한다.

for (let i = 0; i < buttons.length ; i++) {
  buttons[i].addEventListener('click', createParagraph);
}
//복수이기 때문에 for를 사용해 루프를 돌린다.

이 코드는 onclick 속성 코드 보다 조금 길어보이지만, 페이지가 많든, 버튼의 수가 많든 적든 상관없이 모든 버튼들이 같은 기능을 할 수 있도록 합니다. 그리고 자바스크립트 코드를 변경할 필요가 없습니다.

 

스크립트의 로딩 방법

 작성된 스크립트를 브라우저가 적절한 때에 로딩하는것에 대해 몇가지 이슈가 있습니다. 중요한 것은 모든 HTML 요소는 순서대로 페이지에 로드된다는 것입니다. 만약 당신이 자바스크립트를 이용해 HTML 요소를 조작할 경우(정확하게는 DOM), 자바스크립트 코드가 조작 대상인 HTML 요소보다 먼저 실행된다면 조작할 요소가 존재하지 않는 상태이기 때문에 제대로 동작하지 않을 것입니다.

 

위의 코드 예제에서, 내부와 외부의 자바스크립트는 HTML Document의 body가 해석되기 전인 head 부분에 로드되고 실행되었습니다. 이는 에러를 일으킬 수 있습니다. 그래서 여기에 사용되는 몇가지 해결방법들이 있습니다.

 

내부 자바스크립트 예제에서는 다음과 같이 구성하면 됩니다:

document.addEventListener("DOMContentLoaded", function() {
  ...
});

이 이벤트리스너는 "DOMContentLoad" 이벤트가 발생되었을 때 function()을 실행한다는 의미입니다. "DOMContentLoad" 이벤트는 브라우저가 완전히 로드되고 해석될때 발생됩니다. function(){} 내부의 자바스크립트 구문은 이벤트가 발생되기 전까지는 실행되지 않습니다. 따라서 모든 body태그의 요소가 로드된 이후 자바스크립트 코드가 실행되도록 만들어 에러를 피할 수 있습니다.

 

외부 자바스크립트 예제에서는 좀더 최신의 자바스크립트 문법인 async 속성을 사용하게 됩니다. 일반적으로 HTML요소를 로딩하는 중 <scirpt>태그를 만나면 JavaScript의 내용이 모두 다운될 때까지 HTML로딩은 멈추게 되는데, async요소는 비동기방식으로 <script>태그에 도달했을 때 브라우저에게 HTML 요소를 멈추지 않고 다운받도록 유지시킵니다.

<script src="script.js" async></script>

이 경우 scriptHTML은 모두 동시에 로드되고 작동할 것입니다.

 

예전 방식은 scirpt요소를 body태그의 맨 끝에 넣는 방법이었습니다(</body> 바로 위에). 이 방식을 사용해도 body태그가 모두 로드된 이후 scirpt가 실행되게 만들 수 있습니다. 문제는 이 방법과 DOMContentLoaded를 이용한 방법 모두 HTML DOM이 로드되기 전까지 script의 로딩과 파싱이 완전히 차단된다는 것입다. 이는 많은 자바스크립트 코드를 다루는 규모가 큰 사이트의 경우 사이트를 느리게 만드는 중요한 성능 문제를 야기할 수 있습니다. 이것이 async 속성을 사용해야 하는 이유입니다!

async & defer

더 깊게 들어가보면 이러한 코드문제를 해결하기 위한 방법은 실제로 두가지가 있습니다. — async 와defer 입니다. 두 가지의 차이를 봅시다.

 

async 스크립트는 페이지 렌더링의 중단 없이 스크립트를 다운로드 하고, 또한 스크립트의 다운로드가 끝나자 마자 이를 실행시킵니다. async는 외부 스크립트끼리의 구체적인 실행 순서는 보장하지 않고, 단지 나머지 페이지가 나타나는 동안 스크립트가 비동기방식으로 다운로드 되어 중단되지 않는다는 것만 보장합니다. async는 각각의 스크립트가 독립적으로, 서로에게 의존하지 않는 관계일 때 적절합니다.

 

아래의 예제를 보시죠:

<script async src="js/vendor/jquery.js"></script>

<script async src="js/script2.js"></script>

<script async src="js/script3.js"></script>

3개의 스크립트를 로딩하지만 이들의 순서는 보장할 수 없습니다. 이는 script2.jsscript3.js에 있는 함수가 jquery.js의 함수를 사용한다면 에러를 발생될 수 있다는 것을 의미합니다.

Defer는 이와 다르게 순서대로 다운로드 한 후 모든 스크립트와 내용이 다운로드 되었을 때 실행됩니다:

<script defer src="js/vendor/jquery.js"></script>

<script defer src="js/script2.js"></script>

<script defer src="js/script3.js"></script>

따라서 위의 예제의 경우에는 jquery.js -> script2.js -> script3.js 의 순서가 보장됩니다.

 

요약:

  • 만약 scirpt들이 각각의 스크립트에 의존하지 않고 독립적으로 파싱되도 상관없다면, async 를 사용합니다.
  • 먄약 sciprt들이 의존하고 하나의 스크립트가 파싱될때까지 기다려야 한다면, defer 를 사용하고 각각의 <script> 태그들을 실행되길 원하는 순서대로 작성합니다.

주석

HTML과 CSS와 같이, 자바스크립트에서도 주석의 사용이 가능합니다. 주석은 브라우저 실행때는 무시되어 넘어가고 다른 개발자로 하여금 어떻게 구성되고 작동되는지 설명해주는 역할을 합니다. 주석문은 매우 유용하고 코딩시 자주 사용됩니다(특히 큰 프로젝트에서). 주석문에는 두가지 종류가 있습니다:

  • 두개의 슬래시(//)를 통해 한 줄의 주석이 가능합니다.
    // I am a comment
  • /* 와 */를 사용하여 그 사이에 여러 줄의 주석문의 구성이 가능합니다.
    /*
      I am also
      a comment
    */

예를 들자면, 앞의 예제에 주석문을 다음과 같이 달 수 있습니다.

// Function: creates a new paragraph and append it to the bottom of the HTML body.

function createParagraph() {
  let para = document.createElement('p');
  para.textContent = 'You clicked the button!';
  document.body.appendChild(para);
}

/*
  1. Get references to all the buttons on the page and sort them in an array.
  2. Loop through all the buttons and add a click event listener to each one.

  When any button is pressed, the createParagraph() function will be run.
*/

const buttons = document.querySelectorAll('button');

for (let i = 0; i < buttons.length ; i++) {
  buttons[i].addEventListener('click', createParagraph);
}

정리

자바스크립트를 왜 사용하고 어떻게 사용하는지에 대한 방법들에 대한 기초적인 부분을 익히고 여러 예제 코드를 봄으로써, 웹사이트와 다른 곳에서의 코드상 자바스크립트가 어떻게 구성되어있는지 알게되었습니다.

'Language & Solution > JavaScript' 카테고리의 다른 글

JavaScript 무엇인가?(1)  (0) 2021.08.25