화면은 스스로 판단하지 못한다
HTML과 CSS만으로 만든 페이지는 정직합니다. 적어 둔 대로 보이고, 정해 둔 색과 크기로 표시됩니다. 하지만 사용자가 버튼을 눌렀는지, 입력칸이 비어 있는지, 로그인한 사람인지 아닌지는 스스로 판단하지 못합니다.
여기서 JavaScript가 등장합니다. JavaScript는 페이지에 이런 질문을 던질 수 있습니다.
사용자가 로그인했을까?
입력한 비밀번호가 너무 짧지는 않을까?
장바구니에 상품이 하나라도 있을까?
상품 목록을 몇 개나 화면에 그려야 할까?
이 질문에 따라 다른 코드를 실행하는 것이 조건입니다. 그리고 같은 일을 여러 번 자동으로 처리하는 것이 반복입니다.
조건과 반복은 프로그램의 흐름을 바꾸는 장치입니다. 그래서 흔히 흐름 제어라고 부릅니다. 지금까지 배운 값, 변수, 문자열, 숫자, 불리언이 재료였다면, 조건과 반복은 그 재료를 가지고 요리를 시작하는 방법입니다.
const isLoggedIn = true;
if (isLoggedIn) {
console.log("환영합니다!");
} else {
console.log("로그인이 필요합니다.");
}이 짧은 코드에는 웹앱의 핵심이 들어 있습니다. 상태를 확인하고, 그 상태에 맞는 반응을 보여 줍니다. 사용자는 같은 페이지를 보고 있어도, JavaScript는 상황에 따라 전혀 다른 경험을 만들어 낼 수 있습니다.

if문: 조건이 맞을 때만 실행하기
가장 기본적인 조건문은 if입니다. if는 "만약 이 조건이 참이라면 이 코드를 실행해"라는 뜻입니다.
const age = 20;
if (age >= 19) {
console.log("성인입니다.");
}괄호 안의 age >= 19는 조건입니다. 이 조건의 결과는 true 또는 false가 됩니다. age가 20이므로 20 >= 19는 true입니다. 따라서 중괄호 안의 코드가 실행됩니다.
조건이 맞지 않으면 중괄호 안의 코드는 조용히 건너뜁니다.
const age = 16;
if (age >= 19) {
console.log("성인입니다.");
}
console.log("확인이 끝났습니다.");이 경우 "성인입니다."는 출력되지 않습니다. 하지만 그 아래의 "확인이 끝났습니다."는 조건문 밖에 있으므로 실행됩니다.
조건문을 읽을 때는 이렇게 생각하면 쉽습니다.
if (조건) {
조건이 true일 때 실행할 코드
}웹페이지에서는 이런 식으로 사용할 수 있습니다.
const username = "윤서";
if (username) {
console.log(`${username}님, 반갑습니다.`);
}username에 값이 들어 있으면 인사말을 보여 줍니다. 나중에 DOM을 배우면 이 메시지를 콘솔이 아니라 실제 화면에 표시할 수 있습니다.
else: 그렇지 않다면 다른 코드 실행하기
if만 있으면 조건이 맞을 때만 처리할 수 있습니다. 하지만 실제 웹페이지에서는 조건이 맞지 않을 때도 할 일이 많습니다.
로그인한 사용자는 환영 메시지를 보여 주고, 로그인하지 않은 사용자는 로그인 버튼을 보여 줘야 합니다. 입력값이 올바르면 다음 단계로 보내고, 올바르지 않으면 오류 메시지를 보여 줘야 합니다.
이럴 때 else를 사용합니다.
const isLoggedIn = false;
if (isLoggedIn) {
console.log("환영합니다!");
} else {
console.log("로그인 후 이용해 주세요.");
}if의 조건이 true이면 첫 번째 중괄호가 실행됩니다. 조건이 false이면 else의 중괄호가 실행됩니다.
const password = "123";
if (password.length >= 8) {
console.log("사용할 수 있는 비밀번호입니다.");
} else {
console.log("비밀번호는 8자 이상이어야 합니다.");
}여기서 password.length는 문자열의 길이를 나타냅니다. "123"의 길이는 3이므로 3 >= 8은 false입니다. 따라서 else 쪽 코드가 실행됩니다.
이처럼 if와 else는 둘 중 하나를 선택합니다. 조건이 맞으면 A, 아니면 B입니다.
else if: 여러 갈림길 만들기
세상에는 둘 중 하나로만 나눌 수 없는 상황이 많습니다. 점수에 따라 등급을 나누거나, 주문 상태에 따라 다른 메시지를 보여 주거나, 사용자 권한에 따라 메뉴를 다르게 보여 주는 경우가 그렇습니다.
이때는 else if를 사용합니다.
const score = 82;
if (score >= 90) {
console.log("A 등급입니다.");
} else if (score >= 80) {
console.log("B 등급입니다.");
} else if (score >= 70) {
console.log("C 등급입니다.");
} else {
console.log("조금 더 연습해 볼까요?");
}이 코드는 위에서 아래로 조건을 확인합니다. score가 82이므로 첫 번째 조건인 score >= 90은 false입니다. 다음 조건인 score >= 80은 true입니다. 그래서 "B 등급입니다."가 출력되고, 나머지 조건은 더 이상 확인하지 않습니다.
여기서 중요한 점은 순서입니다.
const score = 92;
if (score >= 70) {
console.log("C 등급 이상입니다.");
} else if (score >= 90) {
console.log("A 등급입니다.");
}이 코드는 의도대로 동작하지 않습니다. 92 >= 70이 이미 true이기 때문에 첫 번째 코드가 실행되고 끝납니다. 그 아래의 score >= 90은 확인할 기회조차 없습니다.
여러 조건을 쓸 때는 더 구체적인 조건을 먼저 두는 것이 좋습니다.
const score = 92;
if (score >= 90) {
console.log("A 등급입니다.");
} else if (score >= 70) {
console.log("C 등급 이상입니다.");
}조건문은 위에서 아래로 흐르는 길입니다. 길목을 잘못 세우면 원하는 방향으로 가지 못합니다.
입력값 검사하기
조건문이 가장 자주 쓰이는 곳 중 하나는 입력값 검사입니다. 사용자가 이름을 입력하지 않았거나, 이메일 형식이 이상하거나, 비밀번호가 너무 짧다면 다음 단계로 넘어가면 안 됩니다.
먼저 간단한 회원가입 상황을 생각해 보겠습니다.
const email = "";
if (email === "") {
console.log("이메일을 입력해 주세요.");
} else {
console.log("입력한 이메일:", email);
}email === ""는 email이 빈 문자열인지 확인합니다. 빈 문자열은 아무 글자도 없는 문자열입니다.
이번에는 비밀번호 길이를 함께 검사해 보겠습니다.
const email = "[email protected]";
const password = "1234";
if (email === "") {
console.log("이메일을 입력해 주세요.");
} else if (password.length < 8) {
console.log("비밀번호는 8자 이상이어야 합니다.");
} else {
console.log("가입을 진행합니다.");
}이 코드는 먼저 이메일이 비었는지 확인합니다. 이메일이 비어 있지 않으면 비밀번호 길이를 확인합니다. 모든 조건을 통과하면 마지막 else에서 가입을 진행합니다.
실제 웹앱에서도 흐름은 크게 다르지 않습니다. 다만 console.log 대신 화면에 오류 메시지를 보여 주거나, 버튼을 비활성화하거나, 서버로 데이터를 보내는 코드가 들어갈 뿐입니다.
비교 연산자: 조건을 만드는 도구
조건문에서 가장 많이 사용하는 재료는 비교 연산자입니다. 비교 연산자는 두 값을 비교해서 true 또는 false를 만듭니다.
console.log(10 > 5); // true
console.log(10 < 5); // false
console.log(10 >= 10); // true
console.log(10 <= 9); // false값이 같은지 비교할 때는 ===를 사용합니다.
const role = "admin";
console.log(role === "admin"); // true
console.log(role === "user"); // false같지 않은지 확인할 때는 !==를 사용합니다.
const status = "pending";
if (status !== "done") {
console.log("아직 완료되지 않았습니다.");
}JavaScript에는 ==와 !=도 있습니다. 하지만 처음 JavaScript를 배울 때는 ===와 !==를 사용하는 습관을 들이는 것이 좋습니다. ==는 비교 전에 값을 자동으로 바꾸려고 해서 예상하기 어려운 결과를 만들 수 있습니다.
console.log(1 == "1"); // true
console.log(1 === "1"); // false위 코드에서 1은 숫자이고 "1"은 문자열입니다. ==는 둘을 같다고 판단하지만, ===는 값뿐 아니라 타입까지 비교하므로 다르다고 판단합니다.
처음에는 조금 엄격한 편이 안전합니다. 웹페이지는 작은 오해 하나로도 엉뚱한 메시지를 보여 줄 수 있기 때문입니다.
논리 연산자: 조건을 조합하기
현실의 조건은 하나로 끝나지 않는 경우가 많습니다.
"로그인했고, 관리자라면 관리자 페이지를 보여 준다."
"이메일이 비었거나, 비밀번호가 비었으면 경고한다."
"품절이 아니라면 구매 버튼을 활성화한다."
이런 문장을 코드로 만들 때 논리 연산자를 사용합니다.
그리고: &&
&&는 양쪽 조건이 모두 true일 때만 true가 됩니다.
const isLoggedIn = true;
const role = "admin";
if (isLoggedIn && role === "admin") {
console.log("관리자 페이지에 접근할 수 있습니다.");
} else {
console.log("접근 권한이 없습니다.");
}isLoggedIn이 true이고, 동시에 role === "admin"도 true여야 합니다. 둘 중 하나라도 맞지 않으면 전체 조건은 false가 됩니다.
또는: ||
||는 양쪽 조건 중 하나만 true여도 true가 됩니다.
const email = "";
const password = "12345678";
if (email === "" || password === "") {
console.log("이메일과 비밀번호를 모두 입력해 주세요.");
} else {
console.log("로그인을 시도합니다.");
}이메일이 비었거나 비밀번호가 비었으면 경고합니다. 둘 다 입력되어 있을 때만 else로 넘어갑니다.
아니다: !
!는 값을 반대로 뒤집습니다. true는 false로, false는 true로 바꿉니다.
const isSoldOut = false;
if (!isSoldOut) {
console.log("구매할 수 있습니다.");
}isSoldOut은 false입니다. 여기에 !를 붙이면 true가 됩니다. 그래서 품절이 아니라면 구매할 수 있다는 메시지가 출력됩니다.
논리 연산자가 여러 개 섞이면 괄호를 사용해 의도를 분명히 하는 것이 좋습니다.
const isLoggedIn = true;
const role = "user";
const hasCoupon = true;
if (isLoggedIn && (role === "admin" || hasCoupon)) {
console.log("할인 혜택을 적용합니다.");
}괄호 안의 조건을 먼저 읽으면 이해가 쉬워집니다. "관리자이거나 쿠폰이 있고, 로그인한 상태라면" 할인 혜택을 적용합니다.
truthy와 falsy: 조건문 속 숨은 판단
조건문에는 꼭 age >= 19처럼 비교식만 들어가는 것은 아닙니다. 값 자체를 넣을 수도 있습니다.
const nickname = "별빛";
if (nickname) {
console.log("닉네임이 있습니다.");
}nickname은 문자열입니다. 그런데 조건문 안에서 true처럼 동작합니다. JavaScript는 조건문에서 값을 만나면 그 값을 불리언처럼 평가합니다.
이때 true처럼 취급되는 값을 truthy라고 하고, false처럼 취급되는 값을 falsy라고 합니다.
대표적인 falsy 값은 다음과 같습니다.
false0""nullundefinedNaN
이 값들을 조건문에 넣으면 false처럼 동작합니다.
if ("") {
console.log("실행될까요?");
} else {
console.log("빈 문자열은 falsy입니다.");
}
if (0) {
console.log("0은 true일까요?");
} else {
console.log("0은 falsy입니다.");
}반대로 대부분의 값은 truthy입니다.
if ("hello") {
console.log("글자가 있는 문자열은 truthy입니다.");
}
if (42) {
console.log("0이 아닌 숫자는 truthy입니다.");
}
if ([]) {
console.log("빈 배열도 truthy입니다.");
}마지막 줄이 조금 의외일 수 있습니다. 빈 배열 []은 비어 있지만 truthy입니다. 빈 객체 {}도 truthy입니다.
truthy와 falsy를 알면 짧고 자연스러운 코드를 쓸 수 있습니다.
const username = "";
if (!username) {
console.log("이름을 입력해 주세요.");
}username이 빈 문자열이면 falsy입니다. 여기에 !를 붙이면 true가 되므로 경고 메시지가 출력됩니다.
하지만 너무 줄이는 것이 항상 좋은 것은 아닙니다. 처음에는 의도가 분명한 코드가 더 좋습니다.
const username = "";
if (username === "") {
console.log("이름을 입력해 주세요.");
}두 코드는 비슷하게 동작하지만, 두 번째 코드는 "빈 문자열인지 확인한다"는 의도가 더 직접적으로 드러납니다. 코드는 컴퓨터뿐 아니라 사람도 읽습니다.
선택 상태에 따라 다르게 반응하기
웹페이지에는 선택 상태가 자주 등장합니다. 탭 메뉴에서 어떤 탭이 선택되었는지, 상품 필터에서 어떤 카테고리를 골랐는지, 다크 모드가 켜져 있는지 꺼져 있는지에 따라 화면이 달라집니다.
간단한 예를 보겠습니다.
const selectedCategory = "book";
if (selectedCategory === "all") {
console.log("전체 상품을 보여 줍니다.");
} else if (selectedCategory === "book") {
console.log("도서 상품만 보여 줍니다.");
} else if (selectedCategory === "digital") {
console.log("디지털 상품만 보여 줍니다.");
} else {
console.log("알 수 없는 카테고리입니다.");
}선택값이 고정되어 있고, 그 값에 따라 다른 메시지를 보여 주는 구조입니다. 이런 코드는 뒤에서 배울 이벤트와 만나면 훨씬 재미있어집니다.
예를 들어 사용자가 "도서" 버튼을 누르면 selectedCategory가 "book"이 되고, 그에 따라 도서 상품만 보이게 만들 수 있습니다. 지금은 콘솔에 출력하지만, 원리는 실제 UI에서도 같습니다.
switch문: 정해진 선택지를 깔끔하게 처리하기
if, else if, else만으로도 대부분의 조건을 처리할 수 있습니다. 하지만 하나의 값이 여러 고정 선택지 중 무엇인지 확인하는 경우에는 switch문이 더 읽기 쉬울 때가 있습니다.
const menu = "latte";
switch (menu) {
case "americano":
console.log("아메리카노를 준비합니다.");
break;
case "latte":
console.log("라테를 준비합니다.");
break;
case "tea":
console.log("차를 준비합니다.");
break;
default:
console.log("메뉴를 다시 선택해 주세요.");
}switch는 괄호 안의 값 menu를 확인한 뒤, 일치하는 case로 이동합니다. 여기서는 menu가 "latte"이므로 case "latte" 아래의 코드가 실행됩니다.
break는 여기서 멈추라는 뜻입니다. break가 없으면 다음 case의 코드까지 이어서 실행될 수 있습니다.
const menu = "latte";
switch (menu) {
case "latte":
console.log("라테를 준비합니다.");
case "tea":
console.log("차를 준비합니다.");
}이 코드는 "라테를 준비합니다."를 출력한 뒤, 멈추지 않고 "차를 준비합니다."까지 출력합니다. 그래서 일반적으로 각 case 끝에는 break를 넣습니다.
default는 어떤 case에도 해당하지 않을 때 실행됩니다. if문의 마지막 else와 비슷합니다.
switch문은 이런 상황에 잘 어울립니다.
const payment = "card";
switch (payment) {
case "card":
console.log("카드 결제를 진행합니다.");
break;
case "bank":
console.log("무통장 입금 정보를 보여 줍니다.");
break;
case "phone":
console.log("휴대폰 결제를 진행합니다.");
break;
default:
console.log("지원하지 않는 결제 방식입니다.");
}반면 범위를 비교할 때는 if문이 더 자연스럽습니다.
const temperature = 31;
if (temperature >= 30) {
console.log("무척 더운 날입니다.");
} else if (temperature >= 20) {
console.log("산책하기 좋은 날입니다.");
} else {
console.log("겉옷을 챙기세요.");
}정리하면, 여러 범위를 판단할 때는 if, 하나의 값이 여러 선택지 중 무엇인지 볼 때는 switch가 잘 어울립니다.
반복문: 같은 일을 여러 번 시키기
조건문이 "어느 길로 갈까?"를 정한다면, 반복문은 "이 일을 몇 번 할까?"를 정합니다.
예를 들어 콘솔에 상품 이름을 세 번 출력한다고 해 봅시다.
console.log("상품을 표시합니다.");
console.log("상품을 표시합니다.");
console.log("상품을 표시합니다.");세 번 정도는 직접 쓸 수 있습니다. 하지만 상품이 30개라면 어떨까요? 300개라면요? 직접 쓰는 순간 실수할 가능성이 커지고, 나중에 수정하기도 어려워집니다.
반복문은 같은 구조의 작업을 자동으로 처리합니다.
for (let i = 0; i < 3; i++) {
console.log("상품을 표시합니다.");
}이 코드는 같은 메시지를 세 번 출력합니다. 처음 보면 낯설지만, 천천히 나누어 보면 어렵지 않습니다.
for (초기값; 조건; 변화식) {
반복할 코드
}실제 코드를 다시 보겠습니다.
for (let i = 0; i < 3; i++) {
console.log(i);
}let i = 0은 반복을 시작할 숫자입니다. 보통 반복문의 숫자 변수 이름으로 i를 많이 사용합니다. i < 3은 반복을 계속할 조건입니다. 이 조건이 true인 동안 중괄호 안의 코드가 실행됩니다. i++는 반복이 한 번 끝날 때마다 i를 1씩 증가시킵니다.
흐름을 따라가면 이렇습니다.
// 1번째 반복: i는 0, i < 3은 true
// 2번째 반복: i는 1, i < 3은 true
// 3번째 반복: i는 2, i < 3은 true
// 다음 확인: i는 3, i < 3은 false → 반복 종료그래서 콘솔에는 0, 1, 2가 출력됩니다. 3은 출력되지 않습니다. 조건이 i < 3이기 때문입니다.
0부터 세는 습관
JavaScript에서는 많은 경우 숫자를 0부터 셉니다. 배열의 첫 번째 위치도 0입니다. 처음에는 어색하지만, 반복문과 배열을 함께 다루다 보면 자연스럽게 익숙해집니다.
const fruits = ["사과", "바나나", "귤"];
console.log(fruits[0]); // 사과
console.log(fruits[1]); // 바나나
console.log(fruits[2]); // 귤배열에는 length라는 속성이 있습니다. 배열 안에 값이 몇 개 들어 있는지 알려 줍니다.
const fruits = ["사과", "바나나", "귤"];
console.log(fruits.length); // 3이제 반복문으로 배열의 모든 값을 출력해 보겠습니다.
const fruits = ["사과", "바나나", "귤"];
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}i는 0, 1, 2로 변합니다. fruits[i]는 차례대로 fruits[0], fruits[1], fruits[2]가 됩니다. 그래서 배열 안의 모든 과일 이름을 출력할 수 있습니다.
여기서 조건을 i <= fruits.length로 쓰면 문제가 생깁니다.
const fruits = ["사과", "바나나", "귤"];
for (let i = 0; i <= fruits.length; i++) {
console.log(fruits[i]);
}fruits.length는 3입니다. 하지만 마지막 인덱스는 2입니다. i가 3일 때 fruits[3]을 찾으면 존재하지 않는 값을 찾게 되고, 결과는 undefined가 됩니다.
배열을 반복할 때는 보통 i < 배열.length를 사용합니다.
while문: 조건이 참인 동안 반복하기
for문은 반복 횟수가 어느 정도 분명할 때 자주 사용합니다. 반면 while문은 조건이 참인 동안 계속 반복합니다.
let count = 0;
while (count < 3) {
console.log(count);
count++;
}이 코드는 for문과 비슷하게 0, 1, 2를 출력합니다. count < 3이 true인 동안 중괄호 안의 코드가 반복됩니다.
while문을 사용할 때는 조건이 언젠가 false가 되도록 만들어야 합니다.
let count = 0;
while (count < 3) {
console.log(count);
}이 코드는 위험합니다. count가 계속 0이기 때문에 count < 3은 영원히 true입니다. 이런 상황을 무한 반복 또는 무한 루프라고 부릅니다. 브라우저가 멈추거나 페이지가 응답하지 않을 수 있습니다.
그래서 while문 안에서는 조건을 바꿀 수 있는 코드가 필요합니다.
let count = 0;
while (count < 3) {
console.log(count);
count++;
}count++가 있기 때문에 언젠가 count는 3이 되고, count < 3은 false가 됩니다. 그러면 반복이 끝납니다.
while문은 이런 상황에 잘 어울립니다.
let battery = 100;
while (battery > 0) {
console.log(`배터리 잔량: ${battery}%`);
battery -= 20;
}
console.log("배터리가 모두 소모되었습니다.");반복 횟수를 정확히 세기보다, 어떤 상태가 유지되는 동안 반복하고 싶을 때 while문을 떠올리면 됩니다.
break와 continue: 반복 흐름 조절하기
반복문은 기본적으로 조건이 끝날 때까지 계속됩니다. 하지만 중간에 멈추거나, 이번 반복만 건너뛰고 싶을 때가 있습니다. 이때 break와 continue를 사용합니다.
break는 반복문을 즉시 끝냅니다.
const numbers = [1, 3, 5, 8, 9];
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 === 0) {
console.log("첫 번째 짝수를 찾았습니다:", numbers[i]);
break;
}
}numbers[i] % 2 === 0은 숫자를 2로 나누었을 때 나머지가 0인지 확인합니다. 즉 짝수인지 확인하는 조건입니다. 이 코드에서는 8을 찾는 순간 break로 반복을 끝냅니다. 이미 원하는 값을 찾았으므로 더 볼 필요가 없습니다.
continue는 이번 반복만 건너뛰고 다음 반복으로 넘어갑니다.
const numbers = [1, 2, 3, 4, 5];
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 !== 0) {
continue;
}
console.log("짝수:", numbers[i]);
}숫자가 홀수이면 continue가 실행됩니다. 그러면 아래의 console.log는 실행하지 않고 다음 숫자로 넘어갑니다. 결과적으로 짝수만 출력됩니다.
break와 continue는 강력하지만 너무 많이 쓰면 흐름을 따라가기 어려워질 수 있습니다. 필요한 순간에만 조심스럽게 사용하는 것이 좋습니다.
배열을 반복하는 더 쉬운 방법
배열은 JavaScript에서 아주 자주 사용합니다. 상품 목록, 댓글 목록, 할 일 목록, 검색 결과 모두 배열로 표현할 수 있습니다.
const todos = ["장보기", "운동하기", "JavaScript 공부하기"];앞에서 배운 for문으로 배열을 반복할 수 있습니다.
for (let i = 0; i < todos.length; i++) {
console.log(todos[i]);
}이 방식은 인덱스가 필요할 때 유용합니다. 하지만 단순히 배열의 값을 하나씩 꺼내고 싶을 때는 for...of문이 더 읽기 쉽습니다.
const todos = ["장보기", "운동하기", "JavaScript 공부하기"];
for (const todo of todos) {
console.log(todo);
}이 코드는 "todos 안의 todo를 하나씩 꺼내서 출력한다"라고 읽으면 됩니다. 인덱스 번호를 직접 다루지 않아도 되므로 실수할 가능성이 줄어듭니다.
물론 인덱스가 필요할 때는 기존 for문이 더 편합니다.
const todos = ["장보기", "운동하기", "JavaScript 공부하기"];
for (let i = 0; i < todos.length; i++) {
console.log(`${i + 1}. ${todos[i]}`);
}결과는 다음과 같습니다.
1. 장보기
2. 운동하기
3. JavaScript 공부하기배열을 반복하는 방법은 앞으로 더 배웁니다. 다음 장에서는 map, filter, find, reduce 같은 배열 메서드를 자세히 다룹니다. 지금은 "배열의 여러 값을 반복문으로 하나씩 처리할 수 있다"는 감각을 잡으면 충분합니다.
반복문으로 UI 목록 만들기
이제 JavaScript가 웹페이지를 살아 움직이게 만드는 장면을 조금 더 가까이에서 보겠습니다.
상품 목록이 있다고 해 봅시다.
const products = ["무선 마우스", "기계식 키보드", "노트북 거치대"];이 상품들을 HTML에 직접 적으면 이렇게 됩니다.
<ul>
<li>무선 마우스</li>
<li>기계식 키보드</li>
<li>노트북 거치대</li>
</ul>상품이 세 개라면 괜찮습니다. 하지만 상품이 100개라면 HTML을 직접 쓰는 방식은 곧 한계에 부딪힙니다. 데이터가 바뀔 때마다 HTML도 손으로 고쳐야 합니다.
JavaScript에서는 배열과 반복문을 사용해 HTML 문자열을 만들 수 있습니다.
const products = ["무선 마우스", "기계식 키보드", "노트북 거치대"];
let productHTML = "";
for (let i = 0; i < products.length; i++) {
productHTML += `<li>${products[i]}</li>`;
}
console.log(productHTML);productHTML은 처음에 빈 문자열입니다. 반복문이 한 번 실행될 때마다 <li> 문자열이 뒤에 붙습니다. 최종 결과는 이런 모양이 됩니다.
<li>무선 마우스</li><li>기계식 키보드</li><li>노트북 거치대</li>이 문자열을 실제 화면에 넣으면 목록이 만들어집니다.
<ul id="product-list"></ul>const products = ["무선 마우스", "기계식 키보드", "노트북 거치대"];
const productList = document.querySelector("#product-list");
let productHTML = "";
for (let i = 0; i < products.length; i++) {
productHTML += `<li>${products[i]}</li>`;
}
productList.innerHTML = productHTML;여기서 document.querySelector("#product-list")는 HTML에서 id가 product-list인 요소를 찾습니다. innerHTML은 그 요소 안의 HTML 내용을 바꿉니다. DOM은 뒤에서 더 자세히 배우지만, 지금은 "JavaScript가 HTML 요소를 찾아 내용을 넣을 수 있다" 정도로 이해하면 됩니다.
이 코드의 멋진 점은 상품이 늘어나도 반복문은 그대로라는 것입니다.
const products = [
"무선 마우스",
"기계식 키보드",
"노트북 거치대",
"USB 허브",
"모니터 받침대"
];배열에 상품을 추가하면 화면에 표시되는 목록도 함께 늘어납니다. 같은 일을 반복문이 대신 처리해 주기 때문입니다.
조건과 반복을 함께 사용하기
조건과 반복은 따로 쓰이기도 하지만, 실제로는 함께 쓰이는 일이 훨씬 많습니다. 상품 목록에서 품절 상품은 "품절"이라고 표시하고, 재고가 있는 상품은 "구매 가능"이라고 표시해 보겠습니다.
아직 객체를 자세히 배우기 전이지만, 아래 코드는 어렵지 않게 읽을 수 있습니다. {} 안에 상품 이름과 품절 여부를 함께 담아 둔 것입니다.
const products = [
{ name: "무선 마우스", isSoldOut: false },
{ name: "기계식 키보드", isSoldOut: true },
{ name: "노트북 거치대", isSoldOut: false }
];
for (let i = 0; i < products.length; i++) {
const product = products[i];
if (product.isSoldOut) {
console.log(`${product.name}: 품절`);
} else {
console.log(`${product.name}: 구매 가능`);
}
}반복문은 상품을 하나씩 꺼냅니다. 조건문은 각 상품의 isSoldOut 값을 확인합니다. 이렇게 하면 데이터마다 다른 결과를 만들 수 있습니다.
화면에 표시하는 코드로 바꾸면 조금 더 웹앱다운 모습이 됩니다.
<ul id="product-list"></ul>const products = [
{ name: "무선 마우스", isSoldOut: false },
{ name: "기계식 키보드", isSoldOut: true },
{ name: "노트북 거치대", isSoldOut: false }
];
const productList = document.querySelector("#product-list");
let productHTML = "";
for (let i = 0; i < products.length; i++) {
const product = products[i];
if (product.isSoldOut) {
productHTML += `<li>${product.name} - 품절</li>`;
} else {
productHTML += `<li>${product.name} - 구매 가능</li>`;
}
}
productList.innerHTML = productHTML;이제 JavaScript는 단순히 목록을 반복해서 그리는 데서 멈추지 않습니다. 각 데이터의 상태를 보고 다른 문장을 만들어 냅니다. 이것이 동적인 화면의 출발점입니다.
빈 목록 처리하기
웹페이지에서 자주 놓치는 상황이 있습니다. 바로 "데이터가 없을 때"입니다.
검색 결과가 없을 수 있습니다. 장바구니가 비어 있을 수 있습니다. 아직 작성한 할 일이 없을 수도 있습니다. 이런 상황을 처리하지 않으면 화면이 텅 비어 보여서 사용자는 무슨 일이 일어났는지 알 수 없습니다.
조건문으로 빈 배열을 확인해 보겠습니다.
const todos = [];
if (todos.length === 0) {
console.log("아직 등록된 할 일이 없습니다.");
} else {
for (let i = 0; i < todos.length; i++) {
console.log(todos[i]);
}
}배열의 길이가 0이면 비어 있다는 뜻입니다. 이때는 목록을 반복하지 않고 안내 메시지를 보여 줍니다.
화면에 표시하는 흐름도 비슷합니다.
<div id="todo-area"></div>const todos = [];
const todoArea = document.querySelector("#todo-area");
if (todos.length === 0) {
todoArea.innerHTML = "<p>아직 등록된 할 일이 없습니다.</p>";
} else {
let todoHTML = "<ul>";
for (let i = 0; i < todos.length; i++) {
todoHTML += `<li>${todos[i]}</li>`;
}
todoHTML += "</ul>";
todoArea.innerHTML = todoHTML;
}이 코드는 할 일이 없을 때와 있을 때를 다르게 처리합니다. 작은 차이처럼 보이지만, 사용자가 느끼는 완성도는 크게 달라집니다.
좋은 웹페이지는 데이터가 있을 때만 예쁜 것이 아닙니다. 데이터가 없을 때, 로딩 중일 때, 문제가 생겼을 때도 친절해야 합니다. 조건문은 그 친절함을 만드는 첫 번째 도구입니다.
실전 흐름: 로그인 메시지와 메뉴 만들기
조건과 반복을 함께 사용해 간단한 화면 흐름을 만들어 보겠습니다. 로그인 여부에 따라 메시지를 다르게 보여 주고, 로그인한 사용자에게만 메뉴 목록을 표시합니다.
먼저 HTML을 준비합니다.
<div id="message"></div>
<ul id="menu-list"></ul>이제 JavaScript를 작성합니다.
const isLoggedIn = true;
const username = "민지";
const menus = ["내 정보", "주문 내역", "로그아웃"];
const message = document.querySelector("#message");
const menuList = document.querySelector("#menu-list");
if (isLoggedIn) {
message.textContent = `${username}님, 다시 오셨군요!`;
let menuHTML = "";
for (let i = 0; i < menus.length; i++) {
menuHTML += `<li>${menus[i]}</li>`;
}
menuList.innerHTML = menuHTML;
} else {
message.textContent = "로그인 후 이용해 주세요.";
menuList.innerHTML = "";
}이 코드에는 지금까지 배운 내용이 모두 들어 있습니다. isLoggedIn이라는 불리언 값으로 조건을 판단합니다. 로그인했다면 환영 메시지를 보여 주고, menus 배열을 반복해서 목록을 만듭니다. 로그인하지 않았다면 다른 메시지를 보여 주고 메뉴는 비워 둡니다.
이런 구조는 실제 웹앱 곳곳에서 사용됩니다. 관리자만 볼 수 있는 버튼, 장바구니 상품 목록, 검색 결과, 알림 목록, 댓글 목록 모두 조건과 반복을 기반으로 만들어집니다.
조건문을 읽기 좋게 쓰는 습관
조건문은 조금만 길어져도 복잡해 보일 수 있습니다. 그래서 처음부터 읽기 좋은 습관을 들이는 것이 중요합니다.
먼저 조건의 이름을 변수로 분리하면 코드가 쉬워집니다.
const age = 22;
const hasTicket = true;
if (age >= 19 && hasTicket) {
console.log("입장할 수 있습니다.");
}이 코드도 나쁘지 않지만, 조건이 더 복잡해지면 읽기 어려워질 수 있습니다. 의미 있는 변수 이름을 사용하면 의도가 더 잘 보입니다.
const age = 22;
const hasTicket = true;
const isAdult = age >= 19;
const canEnter = isAdult && hasTicket;
if (canEnter) {
console.log("입장할 수 있습니다.");
}canEnter라는 이름 덕분에 조건문이 문장처럼 읽힙니다. "입장할 수 있다면, 입장할 수 있다고 출력한다."
또한 중첩을 너무 깊게 만들지 않는 것이 좋습니다.
const username = "민지";
const password = "12345678";
if (username !== "") {
if (password.length >= 8) {
console.log("로그인을 시도합니다.");
} else {
console.log("비밀번호가 너무 짧습니다.");
}
} else {
console.log("이름을 입력해 주세요.");
}이 코드는 동작하지만 안쪽으로 계속 들어가서 읽기 어렵습니다. else if를 사용하면 조금 더 평평하게 만들 수 있습니다.
const username = "민지";
const password = "12345678";
if (username === "") {
console.log("이름을 입력해 주세요.");
} else if (password.length < 8) {
console.log("비밀번호가 너무 짧습니다.");
} else {
console.log("로그인을 시도합니다.");
}코드가 왼쪽에 가까울수록 읽기 편합니다. 조건문이 깊어질 때마다 "이걸 더 단순하게 표현할 수 없을까?"라고 한 번씩 생각해 보세요.
반복문을 안전하게 쓰는 습관
반복문에서 가장 흔한 실수는 시작과 끝을 잘못 정하는 것입니다. 특히 배열을 다룰 때는 인덱스 범위를 조심해야 합니다.
const items = ["A", "B", "C"];
for (let i = 0; i < items.length; i++) {
console.log(items[i]);
}이 형태를 기본으로 기억해 두면 좋습니다. i는 0에서 시작하고, items.length보다 작을 때까지만 반복합니다.
반복문 안에서 반복 조건에 영향을 주는 값을 함부로 바꾸는 것도 조심해야 합니다.
const items = ["A", "B", "C"];
for (let i = 0; i < items.length; i++) {
items.push("새 항목");
console.log(items[i]);
}이 코드는 위험합니다. 반복할 때마다 배열에 새 항목을 추가하므로 items.length가 계속 늘어납니다. 의도하지 않은 긴 반복이 생길 수 있습니다.
반복문을 쓸 때는 세 가지를 항상 확인하세요.
반복이 어디에서 시작하는가
반복이 언제 끝나는가
반복할 때마다 값이 어떻게 변하는가
이 세 가지가 분명하면 반복문은 두렵지 않습니다.
작은 프로젝트: 점수 목록에서 합격자 세기
조건과 반복을 연습하기 위해 점수 목록에서 합격자가 몇 명인지 세어 보겠습니다. 합격 기준은 60점 이상입니다.
const scores = [80, 45, 67, 90, 52];
let passCount = 0;
for (let i = 0; i < scores.length; i++) {
if (scores[i] >= 60) {
passCount++;
}
}
console.log(`합격자는 ${passCount}명입니다.`);반복문은 점수를 하나씩 확인합니다. 조건문은 각 점수가 60점 이상인지 판단합니다. 조건이 맞으면 passCount를 1 증가시킵니다.
이번에는 합격자와 불합격자를 나누어 출력해 보겠습니다.
const scores = [80, 45, 67, 90, 52];
for (let i = 0; i < scores.length; i++) {
const score = scores[i];
if (score >= 60) {
console.log(`${score}점: 합격`);
} else {
console.log(`${score}점: 불합격`);
}
}숫자 목록을 다루는 코드지만, 구조는 상품 목록이나 댓글 목록을 다루는 코드와 같습니다. 데이터를 하나씩 꺼내고, 상태에 따라 다르게 처리합니다.
바로 이 흐름이 JavaScript 웹앱의 기본 뼈대입니다.
작은 프로젝트: 장바구니 금액 계산하기
이번에는 장바구니 상품 가격의 합계를 구해 보겠습니다.
const prices = [12000, 8000, 15000];
let total = 0;
for (let i = 0; i < prices.length; i++) {
total += prices[i];
}
console.log(`총 금액은 ${total}원입니다.`);total += prices[i]는 다음 코드와 같습니다.
total = total + prices[i];반복문이 돌 때마다 상품 가격이 total에 더해집니다. 처음 total은 0이고, 최종적으로 모든 가격의 합계가 됩니다.
조건을 추가해 할인 여부도 판단해 보겠습니다.
const prices = [12000, 8000, 15000];
let total = 0;
for (let i = 0; i < prices.length; i++) {
total += prices[i];
}
if (total >= 30000) {
console.log(`총 ${total}원입니다. 무료 배송 대상입니다.`);
} else {
console.log(`총 ${total}원입니다. 배송비가 추가됩니다.`);
}반복문으로 합계를 만들고, 조건문으로 결과를 판단했습니다. 이렇게 역할을 나누면 코드가 이해하기 쉬워집니다.
조건과 반복이 만드는 동적인 페이지
이제 처음의 질문으로 돌아가 봅시다. JavaScript는 어떻게 웹페이지를 살아 움직이게 만들까요?
사용자가 입력한 값이 비어 있으면 경고를 보여 줍니다. 로그인 상태라면 환영 메시지를 보여 주고, 아니라면 로그인 버튼을 보여 줍니다. 상품 배열을 반복해서 목록을 만들고, 품절 상품에는 다른 표시를 붙입니다. 검색 결과가 없으면 "결과가 없습니다"라는 메시지를 보여 줍니다.
이 모든 장면의 중심에 조건과 반복이 있습니다.
조건은 상황을 판단합니다. 반복은 여러 데이터를 처리합니다. 둘이 만나면 화면은 더 이상 고정된 종이가 아닙니다. 사용자의 행동과 데이터의 상태에 따라 바뀌는 살아 있는 공간이 됩니다.
다음 장에서는 이 흐름을 더 강력하게 만들어 줄 함수를 배웁니다. 조건문과 반복문이 길어지기 시작하면, 우리는 같은 코드를 더 작고 의미 있는 단위로 나누고 싶어집니다. 함수는 그때 등장합니다. 여러 줄의 코드를 이름 붙여 보관하고, 필요할 때 다시 실행하게 해 주는 도구입니다.
조건과 반복으로 페이지가 움직이기 시작했다면, 함수는 그 움직임에 질서를 만들어 줄 것입니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.
