Frontend

<Frontend> JavaScript 기초 및 용어

이게왜 2024. 3. 24. 16:38

- Backend 공부만 해본 제가 Frontend를 시작해 봅니다. -

열심히 가봅시다!


* 왜 Frontend를 시작했나?

출처 : <https://www.extwebtech.com/blog/front-end-vs-back-end-web-development/>

 

Backend 개발자가 되고 싶어 공부를 시작했습니다.

웹 서비스 프로젝트를 진행할 때에도 "HTML, CSS, JS는 공부 안 해도 괜찮다!" "그때그때 찾아보면서 하면 된다."

라는 안일한 생각을 가지고 진행하였습니다.

 

하지만! 더 나은 개발자가 되기 위해서, 스스로의 경쟁력을 높이기 위해선 프런트엔드 공부가 필수적입니다!

스스로 공부하고 이해한 내용을 바탕으로 블로그를 작성하려 합니다.

JavaScript를 시작으로 공부해 나가겠습니다.

 


< JavaScript > 

* 목 차 

 

  1. 변수란?   
  2. JS 변수 종류         
  3. 선언과 할당         
  4. 리터럴(Literal)       
  5. var Hoisting       
  6. 함수란?           
  7. 함수 표현식, 함수 선언식, 화살표 함수           
  8. JS Iterator         
  9. JS Condition

1. 변수란?

Programming에서 변수란, "메모리에 할당된 공간의 주소값의 이름"입니다.

수식어가 너무 많아서 바로 이해하기 어렵습니다.

결국엔 "이름"입니다.

사용자가 지정한 변수의 "이름"을 사용하여 주소를 검색하고 해당공간에 담긴 데이터를 반환합니다.


 

이해하기 쉽게 정리하자면, 상자를 만들고, 상자에 이름을 붙이고 이름을 사용하여 상자의 내용물에 접근하는 것입니다.

 

상자의 이름을 정하고 상자를 만듭니다. → 상자는 만드는 순간 사라지고 랜덤 한 곳에 위치합니다.

→ 상자의 위치(주소값)는 상자의 이름과 같습니다.

 

메모리상에서 어떻게 동작하는지는 "3. 선언과 할당"에서 자세히 다루겠습니다.

 


2. JS변수 종류

변수의 종류라고 하였지만, JavaScript에서는 "예약어",  "선언 키워드"라고 불립니다.

var a;
let b;
const c;

 

여기서 다루는 것들은 var, let, const와 같은 것들입니다.

JavaScript에서 "var", "let", "const"는 스코프할당가능 여부를 결정합니다.


2-1. var

- 함수 스코프를 가진다. 함수 내에서 선언된 변수는 함수 내에서만 유효하며, 함수 밖에서 접근할 수 없다.

  (var를 사용한 전역변수는 .js파일 자체가 function취급을 받기 때문에 Hoisting에 영향을 받는다.)

- Hoisting이 발생한다. 즉 변수가 선언되기 전에 참조될 수 있다.

- 중복 선언이 가능하다. 같은 변수명으로 여러 번 선언하여도 오류가 발생하지 않는다.

- 값이 재할당될 수 있다.

 

2-2. let

- 블록 스코프를 가진다. {}로 둘러싸인 범위 내에서만 유효하며, 블록 바깥에서는 접근할 수 없다.

- Hoisting이 발생하지만, TDZ(Temporal Dead Zone)라는 개념이 적용되어 초기화되기 전에는 참조할 수 없다.

- 중복 서언이 불가능하다. 같은 변수명으로 여러 번 선언하면 문법 오류가 발생한다.

 

2-3. const

- 블록 스코프를 가진다.

- Hoisting이 발생하지만, TDZ(Temporal Dead Zone)라는 개념이 적용되어 초기화되기 전에는 참조할 수 없다.

- 중복 선언이 불가능하다.

- 값이 한 번 할당되면 재할당할 수 없다. 상수로 선언된 변수에는 값이 고정되어 변경할 수 없다.

 

* Hoistiing은 "5. var Hoisting"에서 더욱 깊이 다루도록 하겠습니다.

* TDZ(Temporal Dead Zone)은 "3. 선언과 할당"에서 더욱 깊이 다루겠습니다.


3. 선언과 할당(Declaration, Assignment)

+ 초기화 (Initialization)

 

선언, 초기화, 할당은 사용자가 선언문을 사용하여 코드를 입력했을 때, 메모리에 값을 저장하고 사용하기 위한 과정을 의미합니다.

 

"1. 변수"에서 설명했듯이 선언과 할당은 변수를 생성할 때 나타나는 메모리상의 과정입니다.

이러한 과정을 이해하고 있어야 효율적이고 안전한 코드를 작성할 수 있습니다.

 

JS에서는 경우에 따라 선언, 초기화, 할당이 동시에 일어날 수 있고, 순서를 가지고 일어날 수도 있습니다.

변수뿐만 아니라 함수나 객체등 모든 종류의 선언문에 대해 선언, 초기화, 할당이 발생할 수 있습니다.


  • 선언 (Declaration)
    • 실행 콘텍스트의 변수 영역에 공간을 할당.
    • 할당한 공간에 변수이름을 넣는다.
  • 초기화 (Initialization)
    • 데이터 영역에 공간을 할당. (이 상태에서 변수는 Undefined 상태이다.)
  • 할당 (Assignment)
    • 할당받은 데이터 영역의 공간에 값을 넣는다.
    • 이 공간의 주소값을 변수영역의 값으로 넣는다.
let a = 5;
var b = 5;

 

let a = 5;는 선언 초기화 → 할당 순서로 동작하여 메모리에 저장됩니다.

하지만, var b = 5;의 순서는 조금 다릅니다.

데이터영역에 5가 존재하기 때문에 5가 가지고 있는 주소를 재사용하여 b의 값으로 넣습니다.

결국 a와 b는의 값(주소값)은 같습니다.

 

* 주소 재사용은 var에서만 일어납니다.

let과 const는 같은 값이 있는지 확인하지 않고 바로 데이터영역의 공간을 할당합니다.


그럼 여기서 TDZ(Temporal Dead Zone)란 무엇인가?!

var로 선언된 변수는 Declaration과 Initialization이 동시에 일어납니다.

바로 Undefined상태가 되는 거죠.

하지만, let으로 선언된 변수는 Declaration과 Initialization이 순서대로 일어납니다.

따라서 Declaration은 됐지만 Initialization이 안된 상태가 존재한다는 의미입니다.

이러한 구간을 TDZ라고 하고 reference error가 발생합니다.

 

var과 let모두 Hoisting이 발생하지만  let의 경우 error가 발생하여 코드가 동작하지 않기 때문에 Hoisting이 일어나지 않는 느낌을 줍니다.

 

참고로 function키워드로 선언한 함수는 Declaration, Initialization, Assignment가 동시에 일어납니다.


4. 리터럴(Literal)

- JS에서 리터럴은 데이터 그 자체를 의미합니다. -

 

리터럴을 명확히 이해하기 위해선 상수(Constant)를 알아야 합니다.

  • 상수(Constant)
    • 상수는 JS에서 한 번 할당된 후에는 값을 변경할 수 없는 변수를 의미함.
    • const 키워드를 사용하여 선언된 변수는 재할당될 수 없기 때문에 상수로 간주됨.
const PI = 3.14159;
PI = 3; // 이 줄은 오류가 발생합니다.

 

하지만,

const arr = [1, 2, 3];
arr.push(4); // 가능

const obj = { name: 'John', age: 30 };
obj.age = 31; // 가능

 

기본데이터가 아닌 참조데이터의 경우에는 배열이나 객체의 내부 요소나 속성을 변경할 수 있습니다.

 

const a = 5;

 

이 경우 a는 상수이고, 5는 리터럴입니다.


5. var Hoisting

목차에 var Hoisting이라 적었지만, 결국 JS가 실행될 때 발생하는 Hoisting이 무엇인지 어떻게 발생하는지 적어보겠습니다.

 

Hoisting이란 변수 및 함수 선언이 해당 범위(스코프)의 최상단으로 끌어올려지는 동작을 말합니다.

즉 코드 실행 전에 변수 및 함수 선언이 메모리에 저장되는 것을 의미합니다.

단, 할당은 끌어올려지지 않습니다.

 

* 변수 Hoisting

console.log(x); // undefined
var x = 5;
console.log(x); // 5

 

위 코드가 Hoisting 된다면

1. var x;
2. console.log(x); // undefined
3. x = 5;
4. console.log(x); // 5

 

이러한 순서로 실행됩니다. 

"3. 선언과 할당"에서 설명했듯 var로 선언한 변수는 Declaration과 Initializationdl 동시에 이루어집니다.

따라서 1번 줄에서 x는 Undefined로 초기화되었기 때문에 2번 줄에서 undefined를 반환합니다.


* 함수 Hoisting

hello(); // "안녕하세요!"

function hello() {
  console.log("안녕하세요!");
}

위 코드가 Hoisting 된다면

1. function hello() {
2.   console.log("안녕하세요!");
3. }
4. hello(); // "안녕하세요!"

이러한 순서로 실행될 것입니다.

 

코드를 작성할 때 함수를 선언 위치에 상관없이 호출할 수 있게 해 주므로, 코드의 구조가 단순해지고 가독성이 향상될 수 있습니다.

하지만 Hoisting은 실수를 유발할 수 있는 요소이기 때문에 코드를 작성할 때 주의가 필요합니다.

 

** let과 const, 밑에서 설명할 함수 표현식, 선언식, 화살표 함수를 적절히 사용하여 Hoisting을 조절해야 합니다.


6. 함수란?

programming에서 함수란 특정한 작업을 수행하고 값을 반환하는 재사용 가능한 코드 블록입니다.

함수의 사용은 코드의 모듈화와 재사용성을 높입니다.

 

JS에서 함수는 크게 2가지로 나뉩니다. 순수함수와 일급함수입니다.

순수함수와 일급함수가 무엇인지 특징과 장점들 파악해 보겠습니다.

 

  • 순수함수 (Pure Function)
    • 특징 
      • 동일한 입력에 대해 항상 동일한 출력을 반환한다.
      • Side Effect가 없다.
    • 장점
      • 예측 가능하고 테스트가 용이하다.
      • 병렬 처리 및 동시성을 쉽게 다룰 수 있다.
function add(a, b) {
  return a + b;
}

 

function add는 항상 동일한 입력(a, b)에 대해 항상 동일한 출력을 반환하고, 외부 상태를 변경하지 않습니다.


  • 일급함수 (First - Class Function)
    • 특징 
      • 함수를 변수나 데이터 구조에 할당할 수 있다.
      • 함수를 다른 함수의 매개변수로 전달할 수 있다.
      • 함수를 다른 함수의 반환값으로 사용할 수 있다.
    • 장점
      • 함수를 값처럼 취급할 수 있기 때문에 코드를 추상화하고 모듈화 하기 쉽다.
      • 고차 함수(higher-order-function)를 활용하여 코드를 간결하게 작성할 수 있다.
// 함수를 변수에 할당할 수 있음
const sayHello = function() {
  console.log("안녕하세요!");
};

// 함수를 다른 함수의 매개변수로 전달할 수 있음
function greet(func) {
  func();
}

greet(sayHello); // 출력: "안녕하세요!"

// 함수를 다른 함수의 반환값으로 사용할 수 있음
function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
console.log(add5(2)); // 출력: 7

7. 함수 표현식, 함수 선언식, 화살표 함수

  • 함수 표현식
console.log(add(1,2));
var add = function(a, b) {
  return a + b;
};

 

Hoisting 후

var add;

console.log(add(1,2)); // "add is not a function" 반환 후 런타임 종료

add = function(a, b) {
  return a + b;
};

 

- 변수에 함수를 할당하는 방식으로 정의됨.

- var로 선언 시, 변수 선언과 동일하게 메모리의 데이터영역에 공간을 할당되지만 값이 없는 상황이 발생한다.

- 변수 → Undefined 반환

- 함수 → (변수 이름) is not a function 반환

- Hoisting으로 인한 혼란을 줄일 수 있으며, 유연하게 사용가능.

 

** let, const로 선언 시, Hoisting이 발생하지만 TDZ (Initialization이 되지 않음.)에 의해 Reference Error가 발생한다.

hello(); // ReferenceError: Cannot access 'hello' before initialization
let hello = function() {
  console.log("안녕하세요!");
};

  • 함수 선언식
console.log(add(1,2));

function add(a, b){
  return a + b;
};

 

Hoisting 후

function add(a, b){
  return a + b;
};

console.log(add(1,2)); // 3

 

- 함수 선언식 자체가 Hoisting 된다.

- Hoisting으로 인해 어디서든 함수를 호출할 수 있다.

- 일부 상황에서 예상치 못한 동작을 일으킬 수 있다.

- 코드 실행 전 함수의 선언이 메모리에 등록되므로 함수를 선언하기 전에 호출해도 정상적으로 작동함.


  • 화살표 함수
const multiply = (a, b) => {
  return a * b;
};

8. JS Iterator

  • for(초기식, 조건식, 증감식)

 

for(let i=0; i<5; i++) {
   console.log(i);
}

 

- 출력

0
1
2
3
4

 

1. i = 0이고 5보다 작기 때문에 console.log(i)가 실행된다. 이후 i++(증감식)로 인해 i=1이 된다.

2. 1에서 i=1로 끝났기 때문에 i=1이다. 마찬가지로 5보다 작기 때문에 console.log(i)가 실행된다. 이후 i++(증감식)로 인해 i=2가 된다.

3. 2에서 i=2로 끝났기 때문에 i=2이다. 마찬가지로 5보다 작기 때문에 console.log(i)가 실행된다. 이후 i++(증감식)로 인해 i=3가 된다.

4. 3에서 i=3로 끝났기 때문에 i=3이다. 마찬가지로 5보다 작기 때문에 console.log(i)가 실행된다. 이후 i++(증감식)로 인해 i=4가 된다.

5. 4에서 i=4로 끝났기 때문에 i=4이다. 마찬가지로 5보다 작기 때문에 console.log(i)가 실행된다. 이후 i++(증감식)로 인해 i=5가 된다.

6. 5에서 i=5로 끝났기 때문에 i=5이다. i가 5보다 작지 않기 때문에 for문이 종료된다. 이 시점에서 i = 5이다.


  • while(조건문)
let i = 0;
while (i < 5) {
  console.log(i);
  i++;
}

 

- 출력

0
1
2
3
4

 

위의 코드에서 i가 5보다 작은 동안에 계속해서 console.log(i)가 실행되고, i가 1씩 증가합니다.


  • while(true)
let i = 0;
while (true) {
  console.log(i);
  i++;
  if (i >= 5) {
    break; // 무한루프를 종료하기 위한 조건
  }
}

 

- 출력

0
1
2
3
4

 

while(true) 루프는 조건식이 항상 참이기 때문에 무한루프가 발생합니다. 따라서 if문과 break문을 사용하여 i가 5 이상이 되면 루프가 종료됩니다.


  • do-while(조건문)
let i = 0;
do {
  console.log(i);
  i++;
} while (i < 5);

 

- 출력

0
1
2
3
4

do-while 루프의 가장 큰 특징은 루프 내의 코드를 실행한 후 조건을 확인하는 것입니다.

 

또한,

let i = 5;
do {
  console.log(i);
  i++;
} while (i < 5);

 

-출력

5

 

이러한 경우 console.log(i), i++가 실행되고 i는 6이 되어 조건문이 거짓이기 때문에 루프가 종료됩니다.

즉, 5는 출력됩니다.


  • for-in
const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
  console.log(key, obj[key]);
}

 

- 출력

a 1
b 2
c 3

 

for-in 루프는 객체의 열거 가능한 속성을 반복합니다.

위의 코드에서는 obj 객체의 속성을 반복하면서 각 속성의 키와 값에 접근하여 출력합니다.


  • for-of
const arr = [1, 2, 3];
for (let num of arr) {
  console.log(num);
}

 

- 출력

1
2
3

 

for-of 루프는 Iterable 객체를 반복합니다.

위의 코드에서는 arr배열의 요소를 반복하면서 각 요소를 출력합니다.


  • for-await-of

for-await-of 루프는 반복문 내에서 일어나는 모든 비동기 구문을 기다려줍니다.

 

- for each 루프를 사용한 경우 -

const names = ['a', 'b', 'c', 'd'];

names.forEach(async (name) => {
  const result = await fetch(`https://someurl.com/names/${name}`);
  console.log(result.json());
});

console.log('모든 api 통신 완료'); // 이 부분이 forEach의 반복문 작업이 모두 끝나기 전에 실행된다.

 

- 출력

$ 모든 api 통신 완료
$ <json result 'a'...>
$ <json result 'b'...>
$ <json result 'c'...>
$ <json result 'd'...>

 

for each 루프는 모든 비동기 작업이 끝나는 것을 기다리지 않습니다.

하지만 for-await-of 루프를 사용해서 비동기 작업을 기다리게 만들 수 있습니다.

 

- for-await-of 루프를 사용한 경우 -

const names = ['a', 'b', 'c', 'd'];

for await (let name of names) {
  const result = await fetch(`https://someurl.com/names/${name}`);
  console.log(result.json());
}

console.log('모든 api 통신 완료'); // forEach의 반복문이 끝나기를 기다린 후 로깅을 한다.

 

- 출력

$ <json result 'a'...>
$ <json result 'b'...>
$ <json result 'c'...>
$ <json result 'd'...>
$ 모든 api 통신 완료

  • for each Method

루프문이 아닌 method를 사용하는 경우입니다.

const arr = [1, 2, 3];
arr.forEach(function(num) {
  console.log(num);
});

 

- 출력

1
2
3

 

for each method는 배열의 각 요소에 대해 주어진 함수를 실행합니다.

위의 코드에서는 arr 배열의 각 요소를 반복하면서 각 요소를 출력합니다.


  • map Method

마찬가지로 루프문이 아닌 method를 사용하는 경우입니다.

const arr = [1, 2, 3];
const newArr = arr.map(function(num) {
  return num * 2;
});
console.log(newArr); // [2, 4, 6]

 

- 출력

[2, 4, 6]

 

map method는 배열의 각 요소에 대해 주어진 함수를 호출하나 결과로 새로운 배열을 만듭니다.

위의 코드에서는 arr 배열의 각 요소를 반복하면서 각 요소에 2를 곱한 새로운 배열 newArr을 만들고 출력합니다.


9. JS Condition

  • if

주어진 조건식이 참이면 특정 블록을 실행합니다.

if (condition) {
  // 조건이 참일 때 실행되는 코드
}

 

  • else

if문의 조건이 거짓일 때 실행되는 코드 블록을 정의합니다.

if (condition) {
  // 조건이 참일 때 실행되는 코드
} else {
  // 조건이 거짓일 때 실행되는 코드
}

 

  • else if

이전 조건이 거짓일 때 추가적인 조건을 검사하는 데 사용됩니다.

if (condition1) {
  // condition1이 참일 때 실행되는 코드
} else if (condition2) {
  // condition1이 거짓이고 condition2가 참일 때 실행되는 코드
} else {
  // 모든 조건이 거짓일 때 실행되는 코드
}

 

  • switch case

특정 변수 또는 식의 여러 가능한 값을 기반으로 다른 작업을 수행하도록 지시합니다.

switch (expression) {
  case value1:
    // expression이 value1과 일치할 때 실행되는 코드
    break;
  case value2:
    // expression이 value2와 일치할 때 실행되는 코드
    break;
  default:
    // 위의 case에 해당되지 않을 때 실행되는 코드
}

 

  • 삼항 연산자 (Ternary Operation)

조건에 따라 다른 값을 반환하는 연산자입니다.

let result = (condition) ? value1 : value2;
// condition이 참이면 value1을, 거짓이면 value2를 반환

 

  • Nullish 병합 연산자

null 또는 undefined일 때만 우측 피연산자를 반환하는 연산자입니다.

let result = a ?? b;
// a가 null 또는 undefined이면 b를 반환, 그렇지 않으면 a를 반환

 


이렇게 JavaScript의 기초와 용어에 대해 알아보았습니다.

메모리상에서 동작방식을 완벽하게 이해하진 못하였습니다. (대략 70~80% 정도 이해한 것 같습니다.)

JS에 기초와 용어가 이게 전부가 아니기 때문에 계속해서 공부해 나가며 모두 명확히 이해하고 코드에 의도를 가지고 적용할 수 있도록 노력하겠습니다.