JavaScript 시작

JavaScript는 버튼을 눌렀을 때 무언가가 바뀌고, 사용자 입력을 받아서 처리하고, 서버에서 데이터를 가져오는 역할을 한다. HTML과 CSS만으로는 이런 동작을 구현할 수 없다.

오늘은 basic/js/ 폴더에 파일을 하나씩 만들면서 JavaScript 핵심 개념들을 순서대로 익혔다. Python을 먼저 배웠던 덕에 변수나 함수 개념 자체는 낯설지 않았지만, 문법이 다른 곳에서 계속 손이 멈췄다.


변수: let과 const

JavaScript에서 변수 선언은 letconst를 주로 쓴다. 예전에는 var를 썼지만 스코프 문제 때문에 요즘은 잘 쓰지 않는다.

// 변경 가능한 변수
let modifiable = "can be changed";
modifiable = "changed";

// 변경 불가한 상수
const immutable = "cannot be changed";
// immutable = "error"; → 오류 발생

// 블록 스코프
{
  let blockScoped = "confined to {}";
  console.log(blockScoped); // 정상
}
// console.log(blockScoped); → 오류 발생

const는 재할당이 안 될 뿐, 객체나 배열이면 내부 값은 바꿀 수 있다. 이 부분에서 처음에 헷갈렸다.

const user = { name: "Evan" };
user.name = "Tom"; // 가능
user = {}; // 오류

템플릿 리터럴

문자열 안에 변수를 넣을 때 쓴다. 백틱(`)으로 감싸고 ${} 안에 변수나 표현식을 넣는다.

const name = "이름";
const age = 25;

// 예전 방식
console.log("이름: " + name + ", 나이: " + age);

// 템플릿 리터럴
console.log(`이름:${name}, 나이:${age}`);

여러 줄 문자열도 그대로 쓸 수 있어서 훨씬 편하다.


함수: 일반 함수와 화살표 함수

JavaScript에서 함수를 만드는 방법은 여러 가지다.

// 일반 함수
function greet(name = "고객님", age = 999) {
  return `안녕하세요, ${name}님! (${age}세)`;
}

// 화살표 함수 - 한 줄이면 return 생략 가능
const circleArea = (r) => 3.14 * r * r;
const square = (n) => n * n;

// 인자가 하나면 괄호도 생략 가능
const double = n => n * 2;

화살표 함수는 처음에 문법이 낯설었다. => 이후에 중괄호 없이 바로 표현식을 쓰면 그게 리턴값이 된다. 중괄호를 쓰면 반드시 return을 명시해야 한다.

const add1 = (a, b) => a + b;       // return 생략
const add2 = (a, b) => { return a + b; }; // return 명시

자료형 변환

Python과 달리 JavaScript는 자동으로 자료형을 변환하는 경우가 많아서 오히려 헷갈린다. 명시적으로 변환하는 습관이 중요하다.

// 문자열로 변환
String(123)     // "123"
String(true)    // "true"

// 숫자로 변환
Number("42")    // 42
Number("abc")   // NaN
Number(true)    // 1
Number(false)   // 0

// 불리언으로 변환
Boolean(0)      // false
Boolean("")     // false
Boolean(null)   // false
Boolean("abc")  // true
Boolean(1)      // true

Number("abc")가 오류가 아니라 NaN을 반환한다는 점이 Python과 달랐다. NaN은 “Not a Number”인데, 타입은 number다. 이 부분이 처음에는 직관에 반했다.


단축 평가 (Short-circuit Evaluation)

&&|| 연산자는 JavaScript에서 단순 논리 연산 이상의 역할을 한다.

// && : 앞이 falsy면 앞을 반환, 앞이 truthy면 뒤를 반환
const user = null;
const name = user && user.name; // null (user가 falsy라 단락)

// || : 앞이 falsy면 뒤를 반환, 앞이 truthy면 앞을 반환
const input = "";
const display = input || "기본값"; // "기본값"

// 실용적인 활용
const isLoggedIn = true;
isLoggedIn && console.log("로그인됨"); // 실행됨

const value = null;
const safeValue = value || "fallback";

React에서 JSX 안에서 조건부 렌더링할 때 &&를 자주 쓰게 된다. 오늘 여기서 익혀두니까 나중에 자연스럽게 연결됐다.


배열 메서드: map과 filter

07_map.js에서 가장 실용적으로 느껴진 부분이다. 데이터 배열을 원하는 형태로 가공하는 데 쓴다.

const users = [
  { id: 1, name: "철수", age: 25, isActive: true },
  { id: 2, name: "영희", age: 30, isActive: false },
  { id: 3, name: "민수", age: 22, isActive: true },
];

// map: 각 요소를 변환해서 새 배열 반환
const names = users.map((user) => user.name);
// ["철수", "영희", "민수"]

// HTML 태그 형태로 변환
const userTags = users.map((user) => `<p>${user.name}(${user.age})</p>`);

// filter: 조건에 맞는 요소만 추려서 새 배열 반환
const activeUsers = users.filter((u) => u.isActive === true);
// 철수, 민수만 남음

// 조합: 활성 유저의 이름만 추출
const activeNames = users
  .filter((u) => u.isActive)
  .map((u) => u.name);

mapfilter는 원본 배열을 바꾸지 않는다. 새 배열을 반환한다. 이 점이 처음에는 왜 중요한지 몰랐는데, React에서 상태를 불변으로 다뤄야 할 때 핵심이 되는 개념이라는 걸 나중에 알게 됐다.


스프레드 연산자 (…)

배열이나 객체를 펼쳐서 복사하거나 합칠 때 쓴다.

// 배열 복사 및 합치기
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]

// 배열에 요소 추가
const newArr = [...arr1, 99]; // [1, 2, 3, 99]

// 객체 복사 및 수정
const original = { name: "Evan", age: 30 };
const updated = { ...original, age: 31 }; // age만 덮어씀
// { name: "Evan", age: 31 }

React에서 배열 상태에 새 항목을 추가할 때 setItems([...items, newItem])처럼 쓰는 패턴이 여기서 온다. 오늘 이걸 먼저 익혀두지 않았으면 나중에 코드를 보고 무슨 뜻인지 몰랐을 것이다.


비동기 처리: async/await

10_비동기.js에서 다룬 내용이다. 서버에서 데이터를 받아오는 건 시간이 걸린다. 그 시간 동안 브라우저를 멈추지 않으려면 비동기 처리가 필요하다.

async function getUserPosts() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts");
    const posts = await response.json();
    console.log(posts);
  } catch (error) {
    console.log("에러 발생:", error.message);
  }
}

getUserPosts();

async 함수 안에서 await를 쓰면 그 줄이 완료될 때까지 기다린다. fetch()는 브라우저 내장 함수로 HTTP 요청을 보낸다. 응답은 response.json()으로 파싱해야 실제 데이터를 쓸 수 있다.

Promise.all은 여러 요청을 동시에 보내고 전부 완료되면 결과를 받는다.

async function fetchAll() {
  const [posts, users] = await Promise.all([
    fetch("https://jsonplaceholder.typicode.com/posts").then(r => r.json()),
    fetch("https://jsonplaceholder.typicode.com/users").then(r => r.json()),
  ]);
  console.log(posts, users);
}

헷갈렸던 점

== vs ===: JavaScript는 ==로 비교하면 자료형을 자동으로 맞춰서 비교한다. 1 == "1"true가 된다. ===는 값과 자료형 모두 같아야 true다. 항상 ===를 쓴다.

undefined vs null: undefined는 값이 할당되지 않은 것, null은 의도적으로 없음을 나타낸 것이다. 둘 다 falsy지만 엄연히 다르다.

NaN !== NaN: NaN은 자기 자신과 같지 않다. isNaN() 함수로 확인해야 한다.


복습용 질문

1. const로 선언한 객체의 속성을 바꿀 수 있는가?

가능하다. const는 재할당만 막는다. 객체 내부 속성은 바꿀 수 있다.

const user = { name: "Evan" };
user.name = "Tom"; // OK
user = {};         // TypeError
2. map과 filter의 차이는?

map은 배열의 모든 요소를 변환해서 같은 길이의 새 배열을 반환한다. filter는 조건에 맞는 요소만 걸러내서 길이가 같거나 짧은 새 배열을 반환한다. 둘 다 원본 배열을 바꾸지 않는다.

3. async/await에서 try/catch를 쓰는 이유는?

await로 기다리는 작업이 실패할 수 있다. 네트워크 오류, 서버 오류 등. 이때 에러를 잡지 않으면 프로그램이 예기치 않게 멈춘다. try/catch로 에러를 잡아서 적절히 처리한다.


한 줄 정리

JavaScript 하루 치를 다 쓰고 나니, 내일부터 할 React가 왜 이런 문법들을 이렇게 많이 쓰는지 이해가 됐다. 내일은 React의 JSX와 프로젝트 세팅이다.

Community

Comments

0 comments

Comments appear immediately. Use report if something needs review.

No comments yet.