본문 바로가기

웹 개발/프론트엔드 - JavaScript

[JavaScript] 한 달 동안 자바스크립트 박살내기 (1) - 프로젝트 시작

「한 달 동안 자바스크립트 박살내기」는, ChatGPT쨩이 선생님이 되어, 매일 수업해주는 느낌으로 진행합니다.

GPT쨩의 말은 이렇게 작성합니다.

 

 

오늘 공부 내용 및 목표

 

시작

오늘은 본격 진도를 나가는 날이라기보다, 내일부터 39일 코스를 제대로 굴리기 위한 준비 + 현재 위치 확인이야.

너는 이미 프론트엔드 실무 경험이 있으니까 변수나 반복문 같은 걸 처음부터 다시 할 필요는 없고, 대신 JavaScript 자체의 실행 방식에서 어디가 낯선지 확인하는 것에 집중하면 돼.

시작은 일부러 좀 상냥하게 해주는거같다.

역시 그 동안 프리미엄으로 매일같이 대화하면서 레포를 쌓아둔 보람이 있다.

 

 

오늘 학습 목표

단계: Day 0 — 준비 / 진단
주제: 학습환경 세팅 + JavaScript 현재 수준 확인
필수 예상시간: 60~90분

브라우저 DevTools와 로컬 HTML/JS 파일에서 JavaScript를 바로 실행할 수 있다. 간단한 JavaScript 코드의 결과를 실행 전에 먼저 예측할 수 있다. var/let/const, ==/===, 함수, 객체, 배열, 비동기 중 어디가 낯선지 구분할 수 있다. 앞으로 계속 사용할 battle 예제 환경을 만들어둘 수 있다.

오늘은 환경 세팅이랑 자가진단 중심으로 진행될 예정인가보다.

 

 

 

사전 진단

 

사전 진단 1

console.log(typeof null);  
console.log(typeof []);  
  
뭐가 출력될까?

각각 null, Array로 출력될 것 같다.

오답. 둘 다 object로 출력된다.

 

 

사전 진단 2

console.log(0 == false);  
console.log(0 === false);  
  
결과와 이유를 같이 적어봐.

각각 false, true일 것 같다.

== 면 아마 0도 true로 판정했던거같은데?

오답. 반대다. true, false. ==의 경우, 비교 전에 타입 변환이 일어나기 때문. ===은 타입까지 같아야 true로 판정한다.

 

 

사전 진단 3

const player = { hp: 100 };  
const copy = player;  
  
copy.hp = 50;  
  
console.log(player.hp);  
  
뭐가 출력될까?

 오... 뭐지?

copy가 얕은 카피라서 player랑은 별개의 것이고, 아마 원래 player.hp인 100이 출력되지 않을까?

오답. 50. copy와 player가 같은 객체를 가리키고 있기 때문.

 

 

사전 진단 4

console.log("A");  
  
setTimeout(() => {  
 console.log("B");  
}, 0);  
  
console.log("C");  
  
출력 순서를 예상해봐.

이건 정말 모르겠는데, 느낌상, A가 먼저 출력되고, B는 뭔가 다음 사이클에 출력될거라 C 다음에 B가 출력될거같다.

정답. setTimeOut()의 callback은 현재 실행 중인 코드가 끝난 뒤 실행될 기회를 받기 때문.

 

 

사전 진단 5


아래 중에서 "들어본 적은 있는데 내가 설명하라면 좀 애매하다" 싶은 것에 체크해봐.

lexical scope
hoisting
TDZ
this
prototype
closure
event loop
microtask
event bubbling
equestAnimationFrame

오 다 모르겠다.

 

 

 

수업 시작

 

오늘 수업 방향성

1. 이번 공부는 Node보다 브라우저 중심으로 갈 거야

목표가 웹 프론트엔드 적응이니까, JavaScript 문법만 떼어놓고 공부하는 것보다 브라우저 안에서 실제로 실행해보는 방식이 훨씬 좋아.

앞으로 자주 쓸 건 이 정도야.

Chrome 또는 Chromium 계열 브라우저 DevTools - Console - Sources - Network - Performance - Elements VS Code

DevTools는 그냥 편의 도구가 아니라, 이번 공부에서 거의 교과서 + 현미경 역할을 한다고 보면 돼.

뭔 말인지 모르겠는데 아무튼 그렇다고 한다.

 

 

학습 환경 구축

2. 공부용 폴더 하나 만들자.
// index.html
<!doctype html>  
<html lang="ko">  
<head>  
 <meta charset="UTF-8" />  
 <title>GBF FE Study</title>  
</head>  
<body>  
 <h1>Battle Lab</h1>  
  
 <script src="./main.js"></script>  
</body>  
</html>
// main.js
console.log("Battle Lab ready");
브라우저에서 index.html을 열고 DevTools → Console에서

Battle Lab ready

가 보이면 준비 끝.

 

준비 완!

 

 

공부 방침

3. 앞으로 제일 중요하게 쓸 공부 루프

이번 코스에서는 설명을 읽고 "아 그렇구나" 하고 끝내지 않을 거야.

계속 이 순서로 갈 거야.

실행 전에 결과를 예상한다. 실제로 실행한다. 틀렸으면 "내가 어떤 규칙으로 생각해서 틀렸지?"를 본다. 코드 하나를 바꾼다. 다시 결과를 예상한다.

커리큘럼을 짤 때부터 GPT쨩은 「코드를 보고 먼저 동작을 예상해보기」를 항상 강조했다. 왜일까?

 

왜 이게 브라우저 게임 / 레거시 코드에서 중요할까?

오래된 대규모 프론트엔드는 코드를 처음부터 끝까지 읽어서 이해하는 경우보다,

이 값은 어디서 들어왔지? 이 이벤트 다음에는 뭐가 호출되지? 누가 이 객체를 바꿨지?

이런 식으로 실행 흐름을 추적하는 일이 훨씬 많아.

나중에 this, callback, 이벤트, 비동기, Backbone 같은 코드가 나와도 결국 이 능력으로 읽게 될 거야.

알고보니, 내가 들어갈 프로젝트까지 고려해준거였다. 이 친구 좀 치네.

이미 오랜 기간 동안 개발되어온 프로젝트에 들어가다보니, 기존 코드를 보고 움직임을 따라가는 것이 아마 첫 몇 달 동안, 혹은 그 뒤로도 계속 해야 할 일임은 분명하다.

그래서 코드를 보고 동작을 예상하는 학습법을 강조하는 것 같다.

 

 

 

코드 보고 동작 예측하기

 

동작 예측해보기

실행 전 예측

여기서는 진짜로 먼저 실행하지 말고 예상부터 해봐.

 

 

예측 1

let hp = 100;  
  
{  
 let hp = 50;  
 console.log(hp);  
}  
  
console.log(hp);

첫 번째 출력은 50, 두 번째 출력은 100이다.

중괄호로 닫혀 있는 부분에서 hp가 다시 선언되어있는데, 이건 중괄호 밖의 것과 별개의 지역변수이기 때문.

정답.

 

 

예측 2

const player = { hp: 100 };
const copy = { ...player };

copy.hp = 1;

console.log(player.hp);

출력되는 값은 100.

copy가 player와 별개의 새 객체이기 때문.

정답.

 

 

예측 3

function attack(callback) {  
 console.log("attack");  
 callback();  
}  
  
attack(function () {  
 console.log("damage");  
});

attack이 출력된 뒤에 damage가 출력된다.

attack 메소드의 인수로 damage를 출력하는 함수가 전달되고 있고, 그 함수는 attack이 출력된 뒤에 실행되기 때문.

정답.

 

 

 

직접 코딩 - Battle 상태 출력기

 

작은 전투 로직 만들어보기

 

이제 main.js에 아주 작은 전투 로직을 직접 만들어보자.

시작 코드는 이거야.
const player = {  
 name: "Djeeta",  
 hp: 100,  
 mp: 0,  
};  
  
const enemy = {  
 name: "Goblin",  
 hp: 30,  
};  
  
function attack() {  
 // TODO  
}  
  
attack();  
attack();  
attack();  
attack();
해야 할 건 단순해.

attack()이 호출되면 enemy.hp를 10 줄인다.
공격 전 HP와 공격 후 HP를 콘솔에 출력한다.
HP가 0 아래로 내려가면 안 된다.


완료 조건

attack() 4번 호출 후에도 enemy.hp가 음수가 되지 않는다.
매 공격마다 이전 HP와 이후 HP가 보인다.
기존 enemy 객체를 사용한다.
문제를 피하려고 별도의 전역 HP 변수를 하나 더 만들지 않는다.

 

다음과 같이 작성해주었다.

const player = {
  name: "지존마검사",
  hp: 100,
  mp: 0,
};

const enemy = {
  name: "고블린",
  hp: 30,
};

function attack() {
  const beforeHp = enemy.hp;
  const afterHp = beforeHp - 10;

  if (afterHp >= 0) {
    enemy.hp -= 10;
    console.log("공격 전 : ", beforeHp, "공격 후 : ", afterHp);
  }
}

attack();
attack();
attack();
attack();

동작상 문제는 없는 것 같다.

 

 

추가 과제


attack(damage) 형태로 바꿔서 데미지를 매개변수로 받아봐.

attack(5);
attack(20);

처럼 사용할 수 있으면 돼.

 

attack() 함수를 다음과 같이 바꿔주었다. 

그리고 동작 확인은 콘솔창에서 attack(15); 이런 식으로 호출해서 확인하였다.

function attack(damage) {
  const beforeHp = enemy.hp;
  const afterHp = beforeHp - damage;

  if (afterHp >= 0) {
    enemy.hp -= damage;
    console.log("공격 전 : ", beforeHp, "공격 후 : ", afterHp);
  }
}

잘 작동하는 것 같다.

 

 

코드 읽기 / 디버깅 드릴

아래 코드는 보기엔 공격이 잘 되는 것 같은데 실제로는 바깥의 적 HP가 안 줄어.
const enemy = { hp: 20 };  
  
function attack() {  
 const enemy = { hp: 20 };  
 enemy.hp -= 10;  
}  
  
attack();  
console.log(enemy.hp);
먼저 직접 답해봐.

마지막 console.log()는 뭘 출력할까?
왜 바깥쪽 enemy.hp는 바뀌지 않았을까?
가장 단순하게 고치려면 어디를 바꾸면 될까?

아직 scope를 본격적으로 배우기 전이라 용어까지 정확히 몰라도 괜찮아. "함수 안에서 같은 이름의 다른 걸 만들었다" 정도까지 보이면 충분해.

마지막 console.log는 그대로 20을 출력한다. attack()에서는 함수 내부 지역 변수인 enemy의 hp만 깎고 있기 때문.

가장 단순하게 고치려면, attack() 내의 지역 변수 선언 부분만 삭제하면 된다.

정답.

 

 

 

종료 테스트

 

1번 문제

===가 ==보다 동작을 예측하기 쉬운 이유를 한 문장으로 적어봐.

===는 값이랑 타입까지 같을 때 true를 반환하기 때문이다.

그리고, ==는 자동으로 형변환도 해버리기 때문.

정답.

 

 

2번 문제

객체를 const로 선언해도 내부 property는 바꿀 수 있을까? 이유도 같이 적어봐.

const로 선언된 것은 그 객체의 주소값일 뿐이라, 내부 프로퍼티는 바꿀 수 있다.

정답.

 

 

3번 문제

실행 전에 결과를 예상해봐.
const a = { value: 1 };  
const b = a;  
  
b.value = 2;  
  
console.log(a.value);

a는 b와 같은 객체를 참조하고 있기 때문에, 2가 출력된다.

정답.

 

 

4번 문제

callback이 뭔지 코드 없이 네 말로 설명해봐.

함수를 호출하기 위한 인수?

오답. '다른 함수에 전달해두었다가 그 함수가 필요한 시점에 호출하는 함수'

 

5번 문제

DevTools에서 JavaScript 실행을 한 줄씩 따라가고 call stack을 보고 싶다면 어떤 패널을 주로 쓰게 될까?

Sources 패널.

 

 

 

감상

 

음.. 좀 수업 내용이 부실한 감이 있어서 프롬프트를 수정했다.

내일은 아마 더 내용이 충실해질 예정.