https://javascript30.com/

 

JavaScript 30

Build 30 things with vanilla JS in 30 days with 30 tutorials

javascript30.com

 

라이브러리나 프레임워크 사용 없이 모던자바스크립트만으로 하루에 하나씩 30일 동안 30가지 미니 프로젝트를 만들어보는 챌린지입니다

이메일만 등록하면 무료로 참여할 수 있고 동영상 튜토리얼 30개와 스타터파일이 제공돼요

동영상은 영어지만 (영어)자막이 있어서 이해하기가 아주 어렵지는 않아요

 

개발을 잘하려면 프로젝트를 경험을 많이 쌓는 게 중요하다고 하는데

프로젝트 아이디어를 직접 떠올리고 무에서 유를 창조해내는(?) 게 부담스러운 분들은

이렇게 주제와 디자인이 정해진(+튜토리얼까지 제공되는) 미니 프로젝트를 따라 만들어보는 것으로 먼저 시작해보는 것도 좋을 것 같습니다. 

 

사이트에 들어가면 다음과 같은 화면이 뜹니다

 

이메일을 입력하고 Join을 누른다음 간단한 이메일 인증절차를 거치면 바로 시작할 수 있어요

 

이메일을 등록하고 메인화면 오른쪽 위에 있는 'my account' 버튼을 눌러서 들어가면 

이렇게 My Course 화면이 뜹니다. 

여기에서 Stream Course를 누르면 바로 동영상 튜토리얼을 볼 수 있어요

참고로 동영상 튜토리얼은 유튜브에도 올라와있어요. Javascript30 이라고 검색하면 바로 나오더라구요

튜토리얼을 시작하기 전에 먼저 Starter Files로 들어가서 깃허브링크에서 파일을 다운로드 해야합니다

(슬랙 채널은 유료 사용자들만 이용할 수 있다는 것 같네요)

 

깃허브 링크를 타고 들어가면

이렇게 챌린지별로 폴더가 있습니다. 

 

첫번째 챌린지 폴더 안을 보면 html과 css파일, 그 외에 필요한 자료들이 있습니다. 

파일 이름만 봐도 아시겠지만 index-FINISHED.html 파일은 완성본이고

우리는 index-START.html 파일에 직접 코드를 작성해서 완성본처럼 동작하게 해야합니다. 

 

완성본을 먼저 확인해보고 어떻게 구현해야할지 생각해본 뒤, 튜토리얼을 참고하면서 만들어나가면 될 것 같네요 

 

저도 오늘부터 시작하고 진행하면서 틈틈이 후기를 올릴 예정입니다 !! 

 

https://geonlee.tistory.com/48

 

[JavaScript] 🎹 JavaScript로 악기 만들기

🎹 Day 1 - JavaScript로 악기 만들기 JavaScript 30의 첫 번째 프로젝트는 '드럼 킷 만들기'이다. 특정 키보드 버튼을 누르면 해당하는 드럼 소리가 나와서 연주를 할 수 있는 프로젝트이다. 😃 HTML 코드

geonlee.tistory.com

https://takeuu.tistory.com/37?category=733951

 

1. JavaScript Drum Kit

JavaScript Drum Kit 위에 첨부한 사진처럼 해당하는 키보드를 누르면 화면에 효과와 함께 소리가 나는 드럼 킷을 만드는 것 기본 코드

takeuu.tistory.com

다른 블로거 분들이 구현코드까지 올려주신 상세한 후기가 있길래 링크 올립니다. 언어의 장벽이 있거나 막히는 부분이 있을 때 참고하시면 좋을 것 같아요

객체(Object)

자바스크립트에서 데이터를 저장하고 처리하는 기본 단위. (자바스크립트는 객체 기반 언어)

하나의 변수에 여러 데이터를 담기 위해 사용하는 데이터타입.

자바스크립트 프로그램에서 인식할 수 있는 모든 대상을 가리킴. (ex- 웹 브라우저나 웹 문서와 관련된 것들)

 

객체의 종류 in Javascript

  • 내장 객체(Built-in Object) : Number, Boolean, Array, Math, Date객체 등

  • 문서 객체 모델(DOM) : 객체를 사용해 웹 문서를 관리하는 방식. 웹 문서뿐만 아니라 웹 문서에 포함된 모든 요소들(이미지, 링크, 텍스트필드 등)도 각각 별도의 객체로 미리 만들어 놓고 관리. ex) Document객체, Image객체 등

  • 브라우저 객체 모델(BOM) : 웹 브라우저 정보(주소 표시줄, 창 크기 등)을 객체로 다루는 것.

    ex) Navigator객체(사용 중인 브라우저 종류, 버전 정보), History객체(브라우저에서 방문한 기록), Location객체(주소 표시줄 정보), Screen객체(화면 크기 정보) 등

  • 사용자 정의 객체

 

객체의 속성(Property)과 메서드(Method)

속성 : 값을 담고 있는 정보. '객체.속성이름'으로 속성에 담긴 값을 불러올 수 있다.

메서드 : 객체가 어떻게 동작할지를 선언해 놓은 함수. '객체.메서드이름()'으로 메서드를 실행할 수 있다. *Window 객체의 메서드는 'window.'을 생략하고 메서드 이름만으로 실행가능

 

객체의 프로토타입과 인스턴스

프로토타입(Prototype): 기본 틀이 되는 객체

인스턴스(Instance) : 프로토타입을 사용해 만들어낸 객체

ex) Image 객체 : 모든 웹 이미지가 공통으로 가지는 속성과 기능을 모아놓은 것.

→ 웹 이미지를 만들기 위한 기본 틀, 프로토타입

→ Image 객체를 사용해 만든 객체에 원하는 웹 이미지를 담은 것, 인스턴스

 

객체의 인스턴스 만들기

var now = new Date();2019
now.toLocaleString();

Date객체의 인스턴스를 만들어서 변수 now에 담는다. → 변수 now를 통해 Date 객체에 정의된 속성과 함수를 사용할 수 있다.

developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_objects

ㄴ자바스크립트 내장 객체 정보

*Math객체는 다른 객체와 달리 인스턴스를 생성하지 않고 사용할 수 있음.

(Math객체의 모든 속성과 메서드는 정적.)

 

사용자 정의 객체 만들기

1) 객체 리터럴 표기법

리터럴 : 프로그래밍에서 자료를 표기하는 방식. 변수를 선언하면서 동시에 값을 지정해 주는 표기 방식을 말함.

객체 리터럴 표기법 : 객체를 선언하면서 동시에 값을 지정하는 것. 프로토타입(객체 틀)을 만들지 않고 개별적으로 객체를 선언하고 사용하는 방법.(일회용 객체 만들기)

공통으로 들어가는 속성과 메서드가 별로 없어서 각 객체를 따로 정의하는 것이 편할 때 사용.

var book = {
	title: "자바스크립트",
	author: "고쌤",
	price: 15000,
	info: function() {
		alert(this.title + "책의 가격은 " + this.price +"원 입니다.");
	}
};

book.title;
book.info();

book.field = "IT"; //객체 생성 후 새로운 속성 추가

 

2) 생성자 함수

생성자 함수(Constructor) : 객체를 만들어 내는 함수.

function Book(author, pages, price, title) {
	this.author = author;
	this.pages = pages;
	this.price = price;
	this.title = title;
} 
//생성자 함수 선언해 객체 만들기

var jsBook = new Book("문선경", 500, 15000, "자바스크립트");
//생성자 함수를 실행해 객체의 인스턴스 만들어 jsBook 변수에 담기

함수

: 처리해야할 과제에 따라 기능별로 여러 명령을 묶어 놓은 것(특정 기능을 수행하는 소스 코드를 따로 묶어놓은 덩어리)

재사용성, 가독성, 선언(정의)과 실행의 분리

ex) 자바스크립트의 내장함수 alert() : 함수 내부와 작동과정을 몰라도 함수의 기능을 사용할 수 있음.

 

함수의 선언과 실행 순서

웹 브라우저에서 자바스크립트 소스를 해석할 때, 함수가 선언된 부분을 가장 먼저 해석한다. 함수 선언 위치는 프로그램 흐름에 영향을 주지 않기 때문에 원하는 위치에 선언하면 된다. (주로 스크립트 소스의 앞부분이나 뒷부분에 함수 선언 소스를 모아 놓음.)

 

[ES6] 함수의 매개변수 기본 값 지정하기

function multiple(a, b=5, c=10) { return a*b + c; }

 

자바스크립트의 스코프(scope)

: 변수가 적용되는 범위. (=변수가 유효한 범위)

지역 변수(Local Variable) : 한 함수 내에서만 사용할 수 있는 변수

전역 변수(Global Variable) : 스크립트 소스 전체에서 사용할 수 있는 변수

[ES6] 블록 변수(Block Variable)

: 변수를 선언한 블록 {} 안에서만 유효하고

블록을 벗어나면 사용할 수 없는 변수

(let 예약어를 통해 선언된 변수는 블록 변수가 됨)

 

함수 표현 식

익명 함수 : 이름 없는 함수. '식(Expression)'으로 이루어진 함수로, 변수에 할당할 수 있고 다른 함수의 매개변수로 사용할 수 있다.

var add = function(a, b) { return a + b; } var sum = add(10, 20);

익명 함수를 할당한 변수(add)를 함수 이름처럼 사용해 익명 함수를 호출할 수 있다.

 

즉시 실행 함수 : 함수를 정의함과 동시에 실행하는 함수. '식(Expression)'으로 이루어진 함수.

(function() {…})(); (function() {…}());

변수에 할당할 수 있고, 함수에서 반환하는 값을 변수에 할당할 수도 있다.

 

[ES6] 함수의 화살표(⇒) 표기법

let hi = ()=> "안녕하세요?"; hi(); let greet = name => `${name}님, 안녕하세요?`; greet("선경"); let add = (a, b) => a + b; add(10, 20);

 

이벤트

이벤트 : 웹 브라우저나 사용자가 행하는 어떤 동작.

ex) 웹 문서에서 버튼을 누르거나 이미지 위에 마우스 포인터를 올려놓는 것

(※ 사용자가 웹 문서 영역을 벗어나 행하는 동작은 이벤트가 아님)

 

이벤트의 종류 : 마우스 이벤트, 키보드 이벤트, 문서 로딩 이벤트, 폼* 이벤트

developer.mozila.org/en-US/docs/Web/Events

불러오는 중입니다...

 

이벤트 처리기(이벤트 핸들러) : 이벤트와 이벤트 처리 함수를 연결해주는 것. 이벤트 이름 앞에 'on'을 붙여 사용.

 

*폼(Form) : 사용자가 데이터를 입력할 수 있는 모든 요소

 


※ 출처 : Do It 자바스크립트 기본편, 5강 함수와 이벤트

제어문 : 조건에 따라 소스의 실행 순서를 바꾸거나 특정 부분을 반복하는 등 실행 흐름을 조절한다.

 

If문, If…else문

1) if문

if문과 else문의 명령이 1줄인 경우 중괄호를 생략하기도 함.

if(age ≥ 18) alert("성인입니다");

else alert("미성년입니다");

 

2) 조건 연산자

조건이 하나이고 true/false일 때 실행할 명령도 하나뿐이라면 if…else문 대신 조건 연산자를 사용할 수 있음.

(score ≥ 60) ? alert("통과") : alert("실패");

 

3) truthy값과 falsy값

(true로 인정할 수 있는 값, false로 인정할 수 있는 값)

 

falsy값

더보기

0

" "(빈 문자열)

NaN(Not a Number, 숫자가 아님. 보통 변수를 선언만 하고 값을 할당하지 않은 상태에서 연산을 했을 때 볼 수 있는 값)

undefined

null

=> falsy값을 제외한 나머지 값은 truty값이 됨.

 

*if…else문을 만들 때 true가 될 가능성이 높은 조건을 if문의 조건으로 설정하면 프로그램 실행시간을 줄일 수 있다.

 

switch문

조건이 많을 때 흐름 조절하기 위해 사용.

조건이 3개 이상일 때는 if…else문을 중첩하여 사용하기보다는 switch문을 사용 → 한꺼번에 여러 개의 조건을 확인할 수 있다.

 

break;

명령을 실행한 후에는 switch문을 빠져나오도록 함

(break;를 작성하지 않으면 다음에 오는 조건들의 명령이 모두 실행된 후 switch문이 종료됨)

 

default: alert("일치하는 조건이 없습니다");

case문에서 지정한 조건 값과 전부 일치하지 않을 때 실행할 명령. default:에서는 break문을 사용하지 않음

 

for문

값이 일정하게 커지면서 명령을 반복 실행할 때, 많은 양의 연산을 처리해야할 때 사용.

for(var i=0; i<5; i++) {
	//반복 실행할 명령
}

i - 카운터 변수. for문에서만 사용할 카운터 변수. for문을 실행할 때 반복 횟수의 기준이 된다.

 

◆ [ES6] :  for…of문

인덱스를 사용하지 않고 값을 기준으로 반복.

let seasons = ["봄", "여름", "가을", "겨울"];

for (let value of seasons) {
	console.log(value);
}

 

while문, do…while문

1) for문 vs while문, do...while문

for문 : 횟수가 정해져있는 반복 명령을 작성할 때(초깃값이 있으면서 일정한 간격으로 반복)

while문, do…while문 : 특정 조건을 만족하는 동안 명령을 반복할 때

(초깃값이나 반복 간격이 없고 조건만 주어지기 때문에 그 조건을 만족하는 동안 반복)

 

2) while문

var i = 0;

while (i < 10) {

document.write('반복 조건이 true면 반 복<br>');

}

조건부터 확인하고 실행 여부 결정

(조건이 false일 경우 단 한번도 실행되지 않을 수 있음)

 

3) do…while문

명령을 먼저 실행한 후 조건 확인.

(조건이 false이더라도 명령이 최소한 한 번은 실행됨.)

 

break문, continue문

반복을 멈추거나(break) 건너뛰기(continue). 반복문의 흐름을 조절.

1) break문

특정 지점에서 반복문을 중단하고 반복문에서 빠져나옴.

2) continue문

다음에 오는 명령들을 건너뛰고 반복문의 시작 지점으로 되돌아감.

 


※ 출처 : Do It 자바스크립트 기본편, 4장 흐름을 제어한다! 제어문

자바스크립트가 데이터를 어떻게 저장하고 관리하는지, 처리하는지 알아보자.

 

변수

변수(Variable) : 변하는 값을 저장할 때 사용

상수(Constant) : 변하지 않는 값

변수 선언 규칙

  1. 변수의 의미를 잘 나타낼 수 있는 이름으로 짓는다(무의미한 이름x)

  2. 낙타 표기법(Cammel Case)

  3. 첫 글자는 문자나 밑줄(_), 달러($)로 시작해야 한다. (그 후에는 문자, 밑줄, 달러, 숫자 사용가능)

◆ [ES6] : 변수를 선언할 때 var 예약어 외에 let(블록 안에서만 사용), const(상수) 예약어도 사용 가능.

 

자료형(데이터 타입)

자바스크립트는 변수를 선언할 때 데이터타입을 지정하지 않고 값을 할당할 때 그 값에 따라 데이터 타입이 결정됨.

느슨한 자료형 체크(Weak Data Type Check)

C언어, 자바 등 강한 자료형 체크(Strong Data Type Chek) 언어는 변수를 선언할 때 데이터 타입을 미리 지정해야함.

이렇게 데이터 타입을 프로그래머가 제한하면 프로그램의 오류를 미리 방지할 수 있다.

따라서 자바스크립트와 같은 Weak Data Type Check 언어를 사용할 때는 오류가 발생하지 않도록 변수에 의도한 값이 정확하게 들어갔는지 꼭 확인해야 한다.

*타입스크립트(Typescript) : 자바스크립트에 Strong Type을 추가한 언어로, 자바스크립트가 웹 문서에 동적인 효과를 추가하기 위해 등장한 '가벼운 언어'에서 큰 규모의 웹 사이트나 애플리케이션도 만들 수 있는 언어가 되면서 Strong Type Check의 필요성이 커져서 등장한 언어이다.

 

1) 데이터 타입의 종류

기본형 : 하나의 변수에 하나의 값 저장

number : 35

string : 'a', "abc", "35"

boolean : true, false 두 가지 값만 가짐

undefined : 데이터 타입을 지정하지 않았을 때

null : 값이 유효하지 않을 때

 

복합형 : 하나의 변수에 여러 개의 값 저장

array(배열), object(객체)

 

2) 데이터 타입 확인 - typeof 연산자

console.log(typeof 1990);
> "number"

 

데이터타입

1) 정수(number)

표현 방식에 따라 10진수, 8진수, 16진수로 나누기도 함.

8진수 : 0~7로 표현. 10진수와 구별하기 위해 숫자0을 맨 앞에 붙임. ex) 012(10), 013(11), 02000(512)

16진수 : 0~9와 A~F(a~f)로 표현. 10진수와 구별하기 위해 0x(0X)를 맨 앞에 붙임.

 

2) 실수(number)

실수를 계산할 때는 정밀도에 주의해야 함. ex) 0.1 + 0.2 = 0.30000…4

2진수로 변환해 계산하는 과정에서 0.1과 0.2가 자릿수가 많은 소수로 변환되고, 그 상태에서 연산을 하기 때문

 

3) 문자(string)

: 작은 따옴표(')나 큰 따옴표(")로 묶은 데이터.

큰 따옴표(작은 따옴표)의 중복 X

"<span class="num">" X

"<span class='num'>" O

 

4) 논리형(Boolean)

true / false 두 가지 값만 가지는 데이터 타입.

 

5) undefined와 null

자바스크립트는 변수를 선언할 때 데이터타입을 지정하지 않고 값을 할당할 때 그 값에 따라 데이터 타입이 결정된다.

undefined : '처음부터 변수에 값이 할당되지 않았다'(값이 할당된 적이 없다)는 뜻. (변수가 undefined라는 데이터타입을 가진다는 의미가 아님.)

null : '처음에 할당된 값이 더는 유효하지 않다'는 뜻. 보통 변수를 초기화할 때 많이 사용.

 

6) 배열(array)

배열의 선언

var addValue = [];

var seasons = ["봄", "여름", "가을", "겨울"];

배열의 인덱스

seasons[0]

배열의 길이

seasons.length

 

7) 객체(object)

var book = {
	title : "Do It! 자바스크립트", 
    writer : "고경희", 
    price : 18,000
}

 

연산자

1) 기초 산술 연산자

+, -, *, / 사칙 연산자 

% 나머지 연산자

++, — 증감 연산자

※증감 연산자는 사칙 연산자, 나머지 연산자(피연산자의 개수 최소 두 개 이상)와 달리 피연산자의 개수가 하나이며,

다른 연산자와 함께 사용할 수 있는 연산자이다.

 

2) 할당 연산자(Assignment Operator, =)

y += x;
y = y + x;
//두 식은 같은 의미이다.

 

3) 더하기 연산자(숫자를 더할 때) / 연결 연산자(문자열을 연결할 때)

문자열과 숫자의 연산 :

자바스크립트에서는 서로 다른 데이터 타입의 피연산자를 연산하면

자동으로 데이터타입을 변환하여 피연산자들의 데이터 타입을 통일하고,

결괏값의 데이터 타입을 지정해 준다.

◆ [ES6] : '템플릿 문자열' 도입.

`원래 가격은 ${originPrice}원 입니다.`;

` : 백 쿼트, 백 틱, 그레이브 등으로 부름.

 

4) 비교 연산자

== 연산자와 === 연산자의 차이

== 연산자 : 자동으로 데이터타입 변환해 비교. 데이터 타입이 달라도 값이 같으면 (ex- 10과 '10') true

=== 연산자 : 데이터타입의 변환 허용X. 값과 데이터 타입이 모두 일치해야 true

*문자와 문자의 크기를 비교할 경우, 각 문자의 아스키 값으로 비교한 결과값을 얻게 된다.

 

5) 논리 연산자

true, false를 피연산자로 하는 연산자.

&& (AND 연산자), || (OR 연산자), ! (NOT 연산자)

 

6) 연산자의 연산 순서

단항 연산자(!, ++, --) → 산술 연산자 → 비교 연산자 → 논리 연산자 → 할당 연산자

*단항 연산자 중 ++, -- 의 경우 피연산자 앞에 오면 다른 연산자보다 먼저 연산하고, 피연산자 뒤에 오면 다른 연산을 끝낸 후 연산한다. 

a++ 전체 수식의 처리가 끝난 후 적용

++a 전체 수식을 처리하기 전에 적용


※ 출처 : Do It 자바스크립트 기본편, 3장 변수와 자료형 그리고 연산자

 

HTML 문서 안에 자바스크립트 소스 작성하기

: <script> 태그 안에 작성. <script> 태그는 한 문서 안에서 여러 개를 사용해도 되며 HTML 문서 어디에든 사용할 수 있고, 삽입된 위치에서 소스가 실행된다.(주로 </body>태그 앞에 삽입)

 

외부 스크립트 파일 연결하기

: 자바스크립트 소스를 따로 작성하여 HTM문서에 연결하는 것.

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

</body>

 

자바스크립트 프로그램의 실행

웹 브라우저에는 HTML 분석기(HTML parser), CSS 분석기(CSS parser), 자바스크립트 해석기(Javascript interpreter)가 포함되어 있다.

HTML 분석기는 주로 HTML 태그의 순서와 포함 관계를 확인하고 →

CSS 분석기는 HTML 분석기가 분석을 끝낸 다음 HTML 문서의 <style>태그 안의 정보를 분석 →

마지막으로 자바스크립트 인터프리터가 <script> 태그 안의 자바스크립트 소스를 분석한다.

 

자바스크립트의 입력과 출력

크롬 브라우저의 콘솔 도구 : 자바스크립트 소스를 간편하게 연습해볼수 있는 도구(열기 : Ctrl + Shift + J)

 

사용자 입력값 받기

prompt() 등

EX) prompt("이름을 입력하세요(안내)", "홍길동(힌트)");

출력하기

alert(), document.write(), console.log() 등

 

자바스크립트 소스 작성 규칙

  • 대소문자 구별
  • 가독성을 위해 들여쓰기
  • 문장 끝에 세미콜론(;) 붙이기(줄바꿈을 해도 되지만 오류를 줄이기 위해 가급적이면 ;을 붙이는 것이 좋음)
  • 주석(comment) 사용가능
// 한 줄 주석

/*

여러 줄 주석

*/
  • 식별자(Identifier)* 작성 규칙
    • 1) 첫 글자는 문자나 밑줄(_), 달러($)로 시작해야 한다
    • 2) 예약어(Keyword)는 식별자로 사용할 수 없다

*식별자 : 변수, 함수, 속성 등을 구별하기 위해 개발자가 이름을 붙인 특정 단어

*예약어 : 자바스크립트에 등록되어있는 이름들. ex) var, arguments, with, void, super, default 등

 


※ 출처 : Do It 자바스크립트 기본편, 2장 자바스크립트와 친해지기

웹 프로그래밍이란?

웹 프로그래밍 : '웹 브라우저'와 관련된 프로그램을 작성하는 것

서버 : 사용자의 요청을 처리하거나 데이터를 관리하는 컴퓨터 → 백엔드 개발

클라이언트 : 웹 브라우저를 통해 서버에 필요한 정보를 요청하고, 서버가 전달한 정보를 보여주는 컴퓨터(PC, 노트북, 모바일 기기 등) → 프런트엔드 개발

 

자바스크립트로 할 수 있는 일

  • 웹 사이트에 동적인 기능을 추가할 수 있다
  • 웹 브라우저에서 실행되는 프로그램(웹애플리케이션)을 만들 수 있다
  • 자바스크립트 프레임워크 Node.js를 이용해 서버 프로그램을 개발할 수 있다

 

자바스크립트의 특징

자바스크립트는 웹 브라우저에서 사용하기 위해 만든 언어.

  • 모든 웹 브라우저에서 동작한다
  • 별도의 프로그램 설치 없이, 웹 브라우저만 있으면 실행할 수 있고 실행 결과를 즉시 확인할 수 있다
  • 웹 개발(프론트엔드+백엔드) 외에도 다양한 용도의 프로그램을 만들 수 있다
  • 다양한 공개API, 라이브러리와 프레임워크를 사용할 수 있다

*라이브러리 : 자바스크립트로 미리 구현해 놓은 기능들을 묶어 놓은 것 ex)제이쿼리(jQuery)

*프레임워크 : 프레임워크에서 기본으로 제공하는 소스를 수정하거나 추가하는 방법으로 웹 프로그램을 만들 수 있게 미리 준비한 일종의 틀 ex)앵귤러(Angular), 뷰(View)

 


※ 출처 : Do it! 웹 프로그래밍을 위한 자바스크립트 기본 편

※ 책 <속 깊은 Javascript> 2장. 자바스크립트의 스코프와 클로저 단원에서 '클로저'부분을 보고 작성한 글입니다. 글에 사용된 예제는 책에 있는 예제를 살짝 변형해서 만들었습니다. 공부하려고 쓴 글이라 부정확한 부분이 있을 수 있습니다.

 


클로저에 대해 설명하기 전에, 반드시 알고 넘어가야할 '스코프 체인'에 대해 먼저 알아보자.

스코프 체인

자바스크립트는 중괄호( {} )를 기준으로 스코프가 생성되는 다른 언어들과 달리,

'함수 단위'로 스코프가 생성된다. 스코프는 함수가 호출될 때마다 새로 생성된다.

이 코드는 다음과 같은 스코프 체인을 생성한다.

 

 

 

스코프 체인에서 변수를 탐색할 때는 다음과 같은 과정을 거친다.

 

inner함수에서 어떤 변수를 사용하려고 하면, inner 함수의 스코프에서 먼저 그 변수를 찾아보고 ->

없으면 상위 스코프 체인인 outer 함수의 스코프에서 그 변수를 찾아보고 ->

그래도 없으면 글로벌 스코프에서 그 변수를 찾아서 사용한다.

 

스코프 체인은 이렇게 하위 스코프가 상위 스코프에 포함되는 관계를 이루고 있기 때문에,

하위 스코프에서는 체인으로 연결된 상위 스코프의 변수들에 접근할 수 있다.

 


클로저란?

 

클로저는 기본적으로 function 안에 function이 있을 때(=스코프 안에 스코프가 있을 때) 생성된다.

클로저가 발생하는 가장 대표적인 두 가지 케이스는,

1. 내부함수가 반환되어 호출될 때

2. 이벤트 핸들러의 콜백함수로 활용될 때

이다.

 


클로저 이해하기

 

먼저 내부함수가 반환되어 호출되는 경우의 간단한 예시를 통해 클로저가 무엇인지 알아보자.

var func = outer(); 에서 outer()함수를 호출했다.

그러면 outer함수가 실행되고 inner함수가 리턴되어 func변수에 담긴다.

그리고 다음 줄에서 func()을 호출했다. -> inner 함수가 실행된다.

 

이 때, inner 함수는 outer함수의 변수인 'a'를 참조하고 있는데,

outer()함수는 이미 실행이 끝난 후이므로 (= 즉, outer함수와 outer함수의 변수(=outer함수의 스코프)가 사라진 상태이기 때문에)

원래라면 inner함수가 정상적으로 실행되지 않아야 한다.(참조하는 변수가 존재하지 않으므로)

 

그러나 결과를 보면 inner 함수는 정상적으로 실행됨을 알 수 있다.

inner함수가 참조하는 변수 a를 포함하는 외부함수 outer()와, 외부함수 outer()의 스코프가 사라지지 않고 유지되고 있기 때문이다.

 

 

이처럼 외부함수가 내부함수를 리턴하고, 리턴한 내부함수를 글로벌 변수에 담아 실행했을 경우, 내부함수가 참조하고 있는 외부함수와 외부함수의 스코프가 사라지지 않고 유지된다.

이때 (외부함수 + 외부함수의 스코프)를 묶은 덩어리를 '클로저'라고 한다.

클로저를 통해 각 함수는 자기만의 고유한 값을 보유하고 스코프 체인을 유지하면서 그 체인 안에 있는 모든 변수들의 값을 유지한다.

 

다시 한 번 설명하면,

outer()함수의 실행은 앞에서 이미 끝났지만, 실행이 끝난 후에도 outer()함수의 변수인 'a'가 담겨있는 스코프가 사라지지 않고 '클로저'를 생성했다.

따라서 inner()함수에서 outer()함수의 스코프에 있는 변수 a를 참조할 수 있는 것이다.

 

 

또한 위 예시에서 outer()함수의 변수인 a 는 outer()함수의 로컬 변수이므로,

원래는 외부에서 접근할 수 없다. 함수 내부에서만 접근할 수 있다.

그런데 inner 함수는 outer 함수의 내부에 있으므로 outer함수의 변수인 a에 접근할 수 있는데,

outer함수의 로컬 변수인 a에 접근할 수 있는 inner 함수를 글로벌 변수인 'func'에 할당함으로써

outer함수 외부에서도 변수 a에 접근할 수 있게 됐다.

var는 전역적인 특성을 지니고 있지만, 클로저의 특성을 이용해서 private 변수처럼 사용할 수 있다.

let가 const가 등장하기 전에는 특히 클로저가 변수의 제어에 많이 활용되어왔다고 한다. (*var은 함수 단위의 스코프 생성, let은 중괄호 단위로 스코프 생성)

 

 

두 번째 예제를 보자.

 

앞의 예제에 코드 한 줄이 추가되었다.

 

스코프는 함수가 호출될 때마다 새롭게 생성되므로,

func2 변수를 선언하면서 outer()함수를 호출 할 때, 새로운 outer 함수 스코프가 추가로 생성된다.

그래서 다음과 같은 스코프 체인이 만들어진다.

func에 담긴 inner함수와

func2에 담긴 inner함수가 각각 다른 outer함수의 스코프를 참조하고 있음을 알 수 있다.

 

그런데 변수들이 이렇게 따로따로 저장되고 활용되는 대신에,

모든 func, func2, func3 ... 이 동일한 a변수를 공유하도록 할 수는 없을까?

 

여러 함수 간에 같은 변수를 공유할 수 있도록 하려면, 변수 a를 static 변수처럼 만들면 된다.

static변수를 만들기 가장 쉬운 방법은 '글로벌 변수'를 사용하는 것이다.

그러나 많은 개발자들이 글로벌 변수 사용을 지양하므로, 글로벌 변수를 쓰는 대신

클로저를 이용해 변수 a를 static 변수처럼 공유할 수 있게 만들어보자.

 

 

IIFE로 outer함수 스코프 상위에 스코프 하나를 더 추가했다.

 

IIFE 스코프에는 staticA 변수를, outer 함수의 스코프에는 localA 변수를 선언했다.

이렇게 하면 outer함수를 여러번 호출해서 outer함수의 스코프를 여러 개 생성해도, 모든 outer함수의 스코프가 같은 IIFE 상위 스코프를 공유하게 된다.

따라서 staticA변수를 static 변수처럼 사용할 수 있다.

반면에 localA 변수는 outer함수의 스코프에 있으므로, local 변수와 같은 기능을 하게 된다.

 

프로토타입 기반 객체지향 개념을 지원하는 자바스크립트에서는 이렇게 클로저를 통해서

클래스 기반 객체지향 언어처럼 캡슐화, 모듈화 작업을 수행할 수 있다.

 


클로저 활용하기

 

다음으로 이벤트 핸들러의 콜백함수로 활용되는 예시를 보고

클로저를 어떻게 활용해서 문제를 해결할 수 있는지 알아보자.

 

문제가 있는 소스이다.

Div 0, 1, 2를 눌렀을 때 각각 You clicked div #0, 1, 2가 알림창으로 떠야 하는데

어떤 div를 눌러도 "You clicked div #3"이라는 알림창이 뜬다.

 

이런 문제가 발생하는 이유는, 자바스크립트의 스코프가 함수 단위로 생성되기 때문이다.

 

for문의 중괄호 {} 에서 따로 스코프가 생성되지 않아

for문 안의 변수 i가 for문 밖에서 선언된 전역변수 i를 참조하게 된다.

코드의 흐름을 따라가 보면

5번째 줄에서 변수 i를 선언하면서 값을 3으로 초기화 했다. ( i = 3 )

6번째 줄 for문의 선언부에서 i에 0이 대입된다. ( i = 0 )

divScope0에 이벤트 핸들러(addEventListener)를 연결해준 다음,

다시 for문의 선언부로 들어와 i의 값이 1 증가한다. ( i = 1 )

 

이렇게 for문이 진행되면서 각각의 divScope에 이벤트 핸들러를 연결해주고, 다시 선언부로 돌아오는 과정을 거치면서

i값은 1씩 증가해서 최종적으로 다시 3이 된다. ( i = 3 )

 

그 후에 사용자가 div를 클릭하는 이벤트가 발생했을 때 콜백함수가 호출되는데, 이때 i의 값은 3으로 저장되어있으므로 i = 3으로 출력이 된다.

 

스코프 체인을 보면, 각 div에 연결된 세 개의 콜백함수가 모두 글로벌 스코프에 있는 변수 i 를 참조하기 때문에 (세 개의 콜백함수가 똑같은 변수 i를 공유하는 형태) 똑같이 div# 3이 출력되는 것이다.

 

 

이 소스의 문제를 클로저를 통해 해결해보자.

 

콜백 함수 상위에 스코프 하나를 추가하는 방법으로 간단하게 해결할 수 있다.

코드의 실행 과정은 이렇다.

즉시 호출 함수 function (index) { /**/ }(i) 안에서 새로운 함수를 선언해서 반환한다.

→ 반환된 함수는 index 변수를 상위 스코프 체인에 추가한 뒤,

→ addEventListener() 함수의 두번째 인자로 들어간다(=콜백함수가 된다)

 

IIFE를 이용해 상위 스코프를 하나 추가함으로써 다음과 같은 스코프 체인이 만들어진다.

 

각 div의 콜백함수들이 글로벌 스코프의 i 변수가 아닌,

자신의 상위 스코프 체인에 추가된 IIFE의 스코프에 있는 index 변수를 참조하기 때문에

어떤 div를 클릭하는지에 따라 각각 다른 값이 출력된다.

 

클로저를 활용해 소스코드의 문제를 해결해 의도한 결과를 얻을 수 있게 됐다.

 

 


클로저의 단점

 

클로저의 단점과, 클로저를 활용할 때 유의해야할 점에 대해서도 알아보자.

 

  • 메모리를 소모한다.

루프를 돌면서 클로저를 계속 생성하는 설계는 피해야 한다.

클로저를 생성할 때는 하나의 커다란 클로저를 생성하기보다는 각 변수나 함수들의 생명주기를 분석한 다음 효율적으로 나누면 좋다.

 

  • 스코프 생성과 이후 변수 조회에 따른 퍼포먼스 손해가 있다.

하위 스코프에서 상위 스코프의 변수에 접근하고자 한다면,

스코프 체인을 따라 상위 스코프로 올라가면서 탐색을 해나가야하기 때문에,

퍼포먼스에 영향을 미칠 수 있다.

따라서 클로저를 활용할 때는, 과하게 사용하면 퍼포먼스에 영향을 미칠 수 있다는 점을 염두에 둬야한다.

 

  • 익숙하지 않으면 이해하기 어렵다

협업할 때는 명확한 주석과 문서화를 할 필요가 있다

+ Recent posts