자바스크립트로 웹 개발을 한번이라도 해본 사람이라면 절대 모르고 지나칠 수 없는것이 몇가지 있는데, 그중 하나가 바로 DOM(Document Object Model)이다. JS는 기본적으로 DOM을 사용하여 웹 브라우저의 요소들에 엑세스 하고, 이를 수정 할 수 있다.
 

기본 DOM 계층구조

DOM의 기본적인 계층 구조

 

  • Window : 계층 구조의 최상위층. document를 포함한 환경을 제어 할 수 있다. 브라우저가 페이지를 로드하면 자동으로 윈도우 객체가 만들어지고, 자바스크립트 코드에서 윈도우 개체 속성과 함수에 엑세스 할 수 있음.
  • History : 브라우저의 최근 방문 기록에 대한 세부 내용을 기록함. 히스토리 객체는 브라우저가 앞으로 또는 뒤로 가는 버튼 클릭을 시뮬레이션 할 수 있음.
  • Location : 페이지의 URL에 대한 정보가 들어있음
  • Navigator : 클라이언트 인터넷 브라우저 또는 사용자 에이전트의 개체 표현
  • Screen : 디스플레이 화면의 크기와 같은 사용자 화면에 대한 정보를 도출하는데 사용됨. 모바일 장치에서 실행되는 브라우저의 크기를 결정하는데 중요함
  • Document : 페이지 내의 모든 HTML 요소에 대한 엑세스를 제공함. 브라우저에 로드되는 각각의 HTML요소들은 document 객체가 됨.

 
우리가 자주 사용하는 alert()는 사실 winow.alert()에서 가져온것이지만 더 간단하게 표현하기 위해 접두사를 생략해서 사용한다.
 

그렇다면, DOM을 이용하여 html요소에 접근을 어떻게 할까?

가장 많이 사용되는 함수는 document.getElementById("___"); 이다.
 
아래는 자바스크립트 파일에서 HTML요소를 접근하는 예시이다.
 

<div id = "box">

var divTag = document.getElementById("box")

if(divTag) {
//상황에 맞는 로직 구현
}

 
getElementById를 사용해 괄호 안의 id에 할당된 HTML요소를 가져와 변수 divTag에 할당한 다음, 그 다음에는 그 HTML 요소에 우리가 원하는 로직을 추가 할 수 있다. => 정적인 웹에서 좀 더 반응형 웹으로 변하는 과정이다.
 
이제 그럼 inspect 기능을 활용해 자바 스크립트 코드를 써 보자. 웹 브라우저를 열고, 아무곳이나 커서를 올린다음 우클릭을 하면 inspect라는 탭이 있을것이다. 탭을 누르면 콘솔이 보일것이다. 이 콘솔 창에서 여러가지를 실험해볼것이다.
 
먼저, 모든 프로그래밍의 기본 출력 문장인 Hello world를 출력해보자. 자바에서는 System.out.println()을 통해 출력문을 한줄씩 출력했지만, 자바스크립트에서는 console.log을 통해 출력한다. 해보면,
 

이렇게 나온다. 아래의 undefined는 커맨드(console.log)는 아무 값도 리턴을 하지 않는다는 뜻이다.
 
이번에는 정말 간단한 함수를 하나 작성해보자

function printMyInput(user_input) {
    console.log("The parameter passed is " + user_input)
}

함수는 function 이라는 키워드를 통해 선언하고, 리턴 타입은 지정하지 않아도 되며, 괄호 안에는 파라미터를 받는다.
 
여기서 가장 중요한게 나온다. 화살표 함수!
 
자바스크립트 ES6부터 지원하는 화살표 함수다. 사실 난 화살표 함수를 안 좋아한다. 그냥 function키워드를 사용해 함수를 만드는게 더 가독성도 좋고, 직관적이다. 위의 printMyInput을 화살표 함수로 나타내 보겠다.

let printMyInputES6 = (user_input) => {
    console.log(user_input)
}

 
일단 함수를 let으로 선언해 변수화 하고, 더이상 function이라는 키워드 대신 =>를 쓴다. 나처럼 화살표 함수를 싫어해도 쓰는것이 좋을것이다,,, 왜냐면 이미 javascript 코드를 쓰는 사람들은 화살표 함수가 익숙해져있으므로 그냥 얼른 익숙해지는게 답이다 :(
 
마지막으로, 이벤트 핸들러를 짧게 짚고 넘어가보자. 우리가 웹사이트의 버튼들을 눌렀을때, 어떠한 동작을 하게 하고 싶다면, 자바스크립트의 eventHandler를 사용해야 한다. 예시를 보면 바로 이해가 될 것이다!
 

<button type="button" onclick="showAnswers()"> Show Solution
 <script>
  function showAnswers() {
   //code
   alert("A")
  }
 </script>
</button>

 
위의 코드는 'Show Solution'이라는 button을 만들고, onclick이라는 EventHandler를 통해 버튼이 클릭 되었을때 showAnswers()라는 함수를 실행하도록 한다. 간단하다! 이벤트 핸들러는 이런것이다. 정적인 웹에서 조금 더 동적인 웹으로 바뀌어 가는 중이다.

오늘은 자바스크립트에서 '함수'에 대해 짚고 넘어가겠다.

우선, 프로그래밍을 공부해본 사람이라면 함수에 대해 너무나 잘 알고 있을것이다. 그렇지만 한번만 더 짚고 넘어가보자!

 

함수(메서드)

function add(n, m) {
	return n + m;
}

var x = add(1,2); //3을 반환
x = add(1.23, 3.45) // 4.68을 반환
x = add("hello", "world") //"helloworld"를 반환

 

일단, 자바와 달리 자바스크립트의 함수는 function 키워드로 정의한다. 리턴 타입, 파라미터의 타입등은 명시해주지 않아도 되며, 각각의 타입들은 컴파일시 알아서 타입이 인지된다!(자바랑 관련이 없는데 왜 자바스크립트인지는 아직까지 모르겠다) 

 

위를 보면 세가지의 예시들이 있다. 신기하게도 리턴 타입이 명시되지 않은 함수(메서드)에서 각각은 다른 리턴 타입을 가진다.

 

오브젝트

function Car(make, model, year) {
	this.make = make;
    this.model = model;
    this.year = year;
    this.getName = function() {
    	return this.make + ' ' + this.model + ' ' + this.year;
    }
}

var c = new Car("Hyundai", "Sonata", 2021);
alert(c.getName()); // "Hyundai Sonata 2021"

자바스크립트에서의 오브젝트 생성은 자바와 비슷하다. new 연산자를 통해 새 오브젝트를 만들고, 함수의 인자를 원하는 값으로 넘겨주면 된다. this를 한번 더 설명하고 넘어가겠다. 나 조차도 이 this때문에 자료구조 수업에서 애를 먹었던 기억이 난다,,, 간단 명료하게 말해 여기서의 this는 "자기자신" 이다. 즉, 언제든 우리가 Car에 대한 인스턴스를 만들고, 파라미터들을 넘겨주면, 그 파라미터는 "자기자신" 즉, 그 오브젝트의 것이 된다. 위를 예로 들어 var c는 new 연산자를 통해 오브젝트가 생성되었고, 인자로 "hyndai", "sonata", 2021을 넘겨주었다. 그러면 맨 처음 함수 선언 당시 썼던 this는 "c"를 가리킨다. 아래와 같이 말이다.

c.make
c.model
c.year

 

또한, 위에서의 getName 이라는 함수는 Car이라는 함수 안에 선언된 또다른 함수이다(중첩함수). 함수 내부에서 또 다른 함수를 선언할때, 내부 함수는 외부 함수의 변수에 접근 할 수 있다. 이는 클로저(Closure)라 불리는 개념으로, 함수가 정의된 스코프를 기억하여 나중에 쓸 수 있도록 하는 개념이다.

프로토타입


"자바스크립트는 여타 프로그래밍 언어들과 다르게 '클래스' 라는 개념이 없는 프로그래밍 언어이다. 대신, 프로토타입은 객체 간에 상속을 담당하는데, 이를 '프로토타입 체인'이라고 한다. 자바에서 상속을 배운 사람이라면 프로토타입 체인이 비슷한 개념임을 이해할 수 있을 것이다. 예를 들어, VSCode에서 .js 확장자를 가진 자바스크립트 파일을 만들고, 위의 예시에서 보여준 Car를 입력하고 '.'을 누른다면 자동완성으로 'prototype'이라는 속성을 볼 수 있다. 이것은 자바스크립트가 모든 객체에 자동으로 `Object.prototype`이라는 내장 객체의 프로토타입을 상속해주기 때문이다. 말로는 설명이 너무 복잡하므로 예시를 보면서 이해해보자.

 

function Car(make, model, year) {
    this.make = make;
    this.model = model;
    this.year = year;
    this.getName = function() {
        return this.make + ' ' + this.model + ' ' + this.year;
    };
}

Car.prototype.country = "Korea"

var c = new Car("Hyundai", "Sonata", 2021);

console.log(c.getName() + c.country);

 

여기서 Car 함수는 위의 예시와 동일하다. 그렇지만 아래 다른점이 있다. Car.prototype.country = 'Korea'를 추가해주었다. "prototype"을 일종의 "유전자" 라고 이해하면 이해하기가 쉽다. 위의 함수에서 Car함수는 '부모' 이고, var c = new Car()에서의 c는 '자식' 이다. 그 말은 즉, Car.prototype.country = 'Korea'는 부모함수에 'country'라는 유전자를 추가해주고, 그 유전자의 속성은 'Korea'로 설정해줘. 라는 말이랑 같다. 이제 위의 코드에서 c.country를 하면 console.log문에 Korea가 출력되고, 최종 출력은 아래와 같다.

프로토타입을 추가한 후 출력문

그런데 여기서 이러한 의문이 들었다. 근데 나는 c라는 오브젝트에 Korea라는 속성을 할당한적이 없고, 부모함수 내에서도 Korea라는 속성을 할당한적이 없는데 어떻게 컴퓨터는 이 사실을 알고 Korea라는 속성을 할당했을까?

컴퓨터는 내부적으로 이러한 일련의 과정을 거쳐 속성을 찾아낸다.

[인스턴스에 country라는 속성이 있는지 확인하고 있으면 반환한다(위의 Car의 경우엔 c인스턴스엔 country라는 속성이 정의되어있지 않으므로 c 인스턴스의 부모(== Car 함수)로 올라가서 찾는다 -> 우리는 Car.prototype.country = "Korea"라고 지정해주었으므로 지정한 속성을 찾을때까지 부모를 타고 계속 올라간다) -이걸 프로토타입 체이닝 이라고 부른다!

 

그러면 도대체 왜 '프로토타입'을 사용할까? 그냥 부모함수에 this.country = "Korea" 라고 해주면 되는것 아닌가?

프로토타입을 사용하는 이유는 객체 간의 코드를 공유하고 메모리를 효율적으로 사용하기 위함이다. 특히, 여러 객체가 같은 메서드나 속성을 공유할 경우, 프로토타입을 사용하면 중복된 코드를 피하고 코드의 재사용성을 높일 수 있다.

프로토타입을 사용하지 않고 메서드를 직접 생성자 함수 내에 정의하면, 각 객체가 생성될 때마다 해당 메서드가 새로 생성되기 때문에 메모리 사용이 비효율적일 수 있다. 반면 프로토타입을 사용하면 모든 객체가 동일한 프로토타입을 공유하므로 해당 메서드는 한 번만 생성되고 모든 객체가 이를 공유한다.

아래는 두 가지 방식으로 메서드를 추가한 경우를 비교한 예제이다:

1. **프로토타입 사용:**
    

    function Car(make, model, year) {
        this.make = make;
        this.model = model;
        this.year = year;
    }

    Car.prototype.getName = function() {
        return this.make + ' ' + this.model + ' ' + this.year;
    };


    

2. **객체 내에서 메서드 직접 정의:**
 

    function Car(make, model, year) {
        this.make = make;
        this.model = model;
        this.year = year;

        this.getName = function() {
            return this.make + ' ' + this.model + ' ' + this.year;
        };
    }


  

위의 두 방식을 사용하여 객체를 생성하면, 프로토타입을 사용한 경우에는 모든 객체가 같은 `getName` 메서드를 참조한다. 그러나 객체 내에서 메서드를 직접 정의한 경우에는 각 객체가 독립적으로 `getName` 메서드를 가지며, 이는 메모리의 낭비로 이어질 수 있다.

+ Recent posts