자바 스크립트(java script)/자바 스크립트 기초

자바 스크립트 기초(9) - 반복문(loop, 루프)

lamarcK 2025. 3. 20. 04:09

반복문(Loop)이란?

 

루프라는 단어는 타임루프물(시간 반복물) 같은데서 여러번 들어봤을 것이다. 루프는 말 그대로 반복을 뜻한다.

 

우리가 일상 생활을 원할하게 하려면 특정 행동을 반복해야하는 경우가 있다. 잠을 자거나 밥을 먹는 등의 필수불가결한 행위같은 것들 말이다.

 

프로그래밍도 마찬가지로 프로그래밍 구동(일상 생활)을 원활하게 하기 위해서 반복되는 코드(수면, 식사)들이 존재한다. 그런데 그때마다 거의 동일한 코드를 매번 새로 적어 넣는 것은 코드의 가독성도 떨어지고 효율도 떨어질 것이다.

때문에 지속적으로 반복이 필요한 코드를 매번 다시 적는 대신에 하나의 코드 블록을 정해진 횟수만큼 반복하게 하는 방식을 사용하는 것이 바로 반복문이다.

반복문을 사용하는 이유

예를 들어 1 + 1이라는 행동을 반복해서 1부터 10까지 더해야 한다고 치자. 만약에 그냥 정직하게 1씩 더하는 코드를 사용하면 코드의 길이가 무척 길어질 것이다.

let result = 0;
result = result + 1; // 1
result = result + 1; // 2
result = result + 1; // 3
result = result + 1; // 4
result = result + 1; // 5
result = result + 1; // 6
result = result + 1; // 7
result = result + 1; // 8
result = result + 1; // 9
result = result + 1; // 10
console.log(result); // 10

만약에 이것이 10이 아니라 100이나 1000이되면? 코드 자체가 최소 100줄, 1000줄이 되어버리는 것이다.

 

하지만 반복문을 사용해서 동일한 과정을 반복하게 한다면 1000까지 더하는 코드도 몇줄로 간략화 할 수 있다.

let result = 0;
for (let i = 0; i < 1000; i++) {
  result = result + 1;
}
console.log(result); // 1000

 

이처럼 반복문을 사용하면

  • 간결성 
  • 가독성 
  • 유지보수성
  • 재사용성
  • 오류 발생 가능성 감소

등의 이점을 얻을 수 있게된다.

반복문의 종류

반복문은 크게 for, while, do...while, foreach 루프(for...in, for...of 루프)로 나눌 수 있다.

1. for 루프

반복문의 기본 구조는 대부분 비슷하지만 가장 기본이 되는 것이 바로 for문이다.

가장 기본적인 반문으로, 초기화, 조건, 증감식을 사용하여 반복 횟수를 제어한다.

구조

대략 다음과 같은 구조를 가지고 있다.

  • 시작 : for로 for문이라는 의미이다.
  • 변수 초기화 : for문은 동일한 코드를 몇 번(n번) 반복할지 정하게 되는데 시작 전에 변수를 초기화하게 된다.
    • 간단하게 말하면 변수의 시작 값을 정하는 과정이다.
    • i = 0이라면 0회부터 시작해서 0회차, 1회차, 2회차 식으로 반복한다는 얘기다.
  • 조건식 : 루프가 실행되는 조건이다.
    • 보통은 몇 번째(n번째)까지 반복할지 정하기 때문에 i번까지 반복한다 식으로 설명하게 되는데 실제로는 해당 조건을 만족하는 동안까지만 반복을 하게 되는 것이다.
    • 현재는 i < 5라고 되어있어서 i가 5미만 일때까지 반복하게 된다.
  • 증감식 : i++ 한번 반복할때마다 i에 +1씩 더하겠다는 소리다.
    • 즉 i = 0은 1번 반복마다 1씩 증가해서 최초 i=0에서 i = 1, i = 2, i = 3, i = 4까지 반복하게 된다는 소리다.
  • 반복되는 코드 블록 : for 문을 통해서 n번 반복하게 되는 코드의 블록이다. 해당 블록에 있는 코드를 반복하게 된다.

실제 코드는 다음과 같다. 

for (let i = 0; i < 5; i++) {
  console.log(i); // 0, 1, 2, 3, 4
}

 

실제로 실행을 시키면 다음과 같은 결과값이 나온다.

1번 반복할때마다 console.log로 i 값을 출력하기 때문에 최초 0에서 4까지 1씩 더해진 것을 알 수 있다.

저 반복문을 풀어서 실제로 작동한 코드를 작성하면 아래와 같다. 저렇게 긴 코드를 3줄에 압축할 수 있었던 것이다.

let i = 0;
console.log(i); // 0
i++;

console.log(i); // 1
i++;

console.log(i); // 2
i++;

console.log(i); // 3
i++;

console.log(i); // 4

 

조건식은 반복 횟수가 아니다.

반복문에선 조건식을 대부분 반복 횟수로 잡기 때문에 조건식 자체가 반복 횟수가 들어가야 한다고 생각할 수 있는데 조건식 어떠한 조건이든지 넣을 수 있다.

더보기

아래 예시는 if문이 있기 때문에 if 문을 배우지 않았을 경우 참고용으로만 사용해야한다.

1. 문자열 길이 조건

const str = "Hello, world!";
for (let i = 0; i < str.length; i++) {
  console.log(str[i]);
}

 

  • .length는 문자열의 길이를 반영하는 속성이다. Hello, world!가 몇 글자인지를 세는 것이다. 총 13글자다.
  • str[0]은 문자열 "Hello, world!"의 첫 번째 문자 "H"를 나타낸다.
  • str[1]은 두 번째 문자 "e"를 나타낸다.
  • str[4]는 다섯 번째 문자 "o"를 나타낸다.
  • 이렇게 1글자씩 출력하는 반복문이다.

 

2. 배열 요소 조건

const arr = ["apple", "banana", "cherry", "date"];
for (let i = 0; i < arr.length; i++) {
  if (arr[i].startsWith("b")) {
    console.log(arr[i]);
  }
}
  • arr이라는 변수에 ["apple", "banana", "cherry", "date"]라는 변수를 할당(대입)한 것이다.
  • arr.length는 배열의 길이를 반환하는데 총 4개의 요소가 있기 때문에 4이다.
  • arr[i]는 arr에 할당된 변수의 i번째 요소를 반환한다.
  • .startsWith()는 문자열 메서드인데 문자열이 특정 문자열로 시작하는지 여부를 확인하고, 결과를 불리언 값(true 또는 false)으로 반환한다.
  • b로 시작하는 요소는 banana 뿐이기 때문에 i가 1일 때 true값이 나온다.
  • 때문에 값은 banana를 반환하고 반복문이 끝나게 된다.

 3. 객체 속성 조건

const obj = { a: 1, b: 2, c: 3, d: 4 };
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
  if (obj[keys[i]] % 2 === 0) {
    console.log(keys[i], obj[keys[i]]);
  }
}
  • const obj = { a: 1, b: 2, c: 3, d: 4 }는 오브젝트(객체)에 4개의 키: 쌍을 할당한다.
  • const keys = Object.keys(obj)는 keys라는 배열에 4개의 키를 할당한다.
  • 여기서 Object.keys()는 메서드로 obj라는 변수에서 키 값만을 뽑아서 문자 배열로 할당한다.
  • const keys = ['a', 'b', 'c', 'd'] 이다. 문자이기 때문에 무조건 따옴표(')가 필요하다
  • keys.length는 배열의 요소 수를 반영하기 때문에 4이다.
  • obj[keys[i]]는 obj라는 객체에서 keys[i]에 해당하는 키(key)의 값(value)을 반환한다.
  • keys[i]는 각각 'a', 'b', 'c', 'd'이다.
  • 결과적으로 obj['a'], obj['b'], obj['c'], obj['d']가 되고 obj 변수의 키가 'a', 'b', 'c', 'd'인 값을 반환한다.
  • const obj = { a: 1, b: 2, c: 3, d: 4 }이기 때문에 각각 1, 2, 3, 4가 반환된다.
  • %는 나눈 이후 남는 나머지를 반환하는 나머지 연산자(remainder operator)이다.
  • % 2는 2로 나눈다는 것이며 나눈 이후 나머지를 반환한다.
  • ===0는 2로나눈 나머지가 0과 동일한지 확인하는 비교 연산자이다.
  • 2로 나눈 나머지가 0이 되려면 2나 4여야 하는데 b와 d가 각각 2와 4이다.
  • obj[keys[i]가 2나 4가 나오려면 i의 값이 1과 3이어야 한다.
  • 때문에 console.log(keys[i], obj[keys[i]]) 값을 반환하는 것은 i가 1과 3일때이며
  • keys[1], obj[keys[1]]은 각각 b, 2이고 keys[3], obj[keys[3]]은 각각 d, 4이다.

2. while 루프

주어진 조건이 True(참)인 동안 코드 블록을 반복 실행한다.

더보기

 i가 < 5일때 동안만 반복되는 while문

let i = 0;
while (i < 5) {
  console.log(i); // 0, 1, 2, 3, 4
  i++;
}

 

요청한 값을 넣어야만 종료되는 while문. 유효한 값을 넣지 못하면 무한 반복된다.

let input;
let isValid = false;

while (!isValid) {
  input = prompt("5에서 10 사이의 숫자를 입력하세요:");
  if (input >= 5 && input <= 10) {
    isValid = true;
  } else {
    alert("유효하지 않은 입력입니다. 다시 시도하세요.");
  }
}

console.log("입력된 값:", input);

무한 루프 가능성 존재

while루프는 조건식이 참이면 무한하게 코드를 반복하기 때문에 무한 루프에 빠질 수도 있다.

더보기

예를 들면 4자리 숫자로 이루어진 비밀번호를 맞춰야 하는 자물쇠가 있다고 가정해보자. 아무런 힌트가 없다면 비밀번호를 맞추기까지 얼마나 걸릴까?

만약에 아무런 힌트가 없다면 비밀번호를 맞추기 전까지 '비밀번호를 입력하고 확인하는 행위'를 계속 반복해야 할것이다. 실제로는 무한 반복까지는 아니겠지만 의도한 것 이상으로 많이 반복해야 할 수도 있다.

따라서, 일정 횟수 비밀번호를 틀리면 힌트를 제공하여 비밀번호를 맞출 수 있도록 도와주는 것이 좋다. 

 

이러한 접근 방식은 while 루프에서 특정 횟수 이상 반복했을 경우 힌트를 제공하거나, 반복을 멈추거나, 실행 시간을 제한하는 것과 같은 맥락이라고 할 수 있다.

 

아무런 힌트도 주지 않아서 개발자의 의도 이상으로 많이 반복 되는 것을 while문의 '무한 루프'라고 친다면,

도중에 힌트를 줘서 의도한 시도횟수 안에서 비밀번호를 맞출 수 있게 도와주는 것은 while문의 무한 반복을 방지하는 방법과 본질적으로 같다고 할 수 있다.

 

실제로 힌트를 주지 않아서 무한 루프하는 코드는 다음과 같다.

(답이 존재하기에 완전 무한루프는 아니지만 아무런 힌트가 없어서 운으로 때려 맞추지 않는 이상은 못 푼다.)

const correctPassword = "1234"; // 실제 비밀번호
let attempts = 0;
let isUnlocked = false;

while (!isUnlocked) {
  const inputPassword = prompt("비밀번호를 입력하세요:");
  attempts++;

  if (inputPassword === correctPassword) {
    isUnlocked = true;
    alert("자물쇠가 열렸습니다!");
  } else {
    alert(`비밀번호가 틀렸습니다. (시도 횟수: ${attempts})`);
  }
}

// 이 부분은 isUnlocked가 true가 되면 실행됨
// alert("자물쇠가 열렸습니다!");

 

특정 횟수를 실패하면 반복이 멈추도록 한 코드는 다음과 같다.

(문제 형식의 반복문이라면 힌트를 줘도 되지만 그냥 특정 조건을 만족하면 멈추도록 하는 것이 더 좋다.)

const correctPassword = "1234";
let inputPassword = "";
let attempts = 0;
const maxAttempts = 3;

while (inputPassword !== correctPassword && attempts < maxAttempts) {
  inputPassword = prompt("4자리 비밀번호를 입력하세요:");
  attempts++;

  if (inputPassword === correctPassword) {
    alert("자물쇠가 열렸습니다!");
  } else {
    alert(`비밀번호가 틀렸습니다. (시도 횟수: ${attempts}/${maxAttempts})`);
  }
}

if (inputPassword !== correctPassword) {
  alert("시도 횟수를 초과했습니다. 자물쇠가 잠겼습니다.");
}

무한 루프를 방지하는 방법들

더보기

1. 반복 횟수 제한

루프 내에서 특정 조건이 충족되면 루프를 종료하도록 조건을 추가한다.

let count = 0;
while (true) {
  console.log(count);
  count++;
  if (count >= 10) {
    break; // count가 10 이상이 되면 루프 종료
  }
}

 

2. 조건식 변경

루프 내에서 조건식에 사용되는 변수의 값을 변경하여 루프가 종료될 수 있도록 한다.

 
let input = "";
while (input !== "exit") {
  input = prompt("명령어를 입력하세요 (exit 입력 시 종료):");
  console.log("입력:", input);
}
  • 사용자로부터 입력을 받아 input 변수에 저장하고, input이 "exit"와 같으면 루프를 종료합니다.

3. break 문 사용

break 문을 사용하여 루프를 강제로 종료할 수 있다.

 
let i = 0;
while (true) {
  console.log(i);
  i++;
  if (i > 10) {
    break; // i가 10보다 커지면 루프 종료
  }
}

4. 플래그 변수 사용

플래그 변수를 사용하여 루프의 종료 여부를 제어할 수 있다.

 
let isRunning = true;
let count = 0;
while (isRunning) {
  console.log(count);
  count++;
  if (count >= 10) {
    isRunning = false; // count가 10 이상이 되면 플래그 변수를 false로 설정
  }
}

 

5. 시간 제한

루프의 실행 시간을 제한하여 무한 루프를 방지할 수 있다.

 
const startTime = Date.now();
while (Date.now() - startTime < 5000) {
  // 5초 동안 실행되는 코드
}

 

3. do...while 루프

while 루프와 유사하지만, 조건식을 코드 블록 뒤에서 후행 평가하기 때문에 조건식이 거짓이어도 코드 블록을 최소한 한 번은 실행한다.

더보기

1. 사용자 입력 검증

 
let input;
do {
  input = prompt("0부터 10 사이의 숫자를 입력하세요:");
} while (input < 0 || input > 10);
console.log("입력한 숫자:", input);
  • prompt() 함수는 사용자로부터 입력을 받는 함수다.
  • input < 0 || input > 10 조건식은 입력 값이 0보다 작거나 10보다 큰 경우 참(true)을 반환한다.
  • 때문에 해당 루프에서는 조건을 만족 못하면(입력한 값이 true가 아니라 false)이면 무한 반복하게된다.

2. 메뉴 선택

let choice;
do {
  choice = prompt("1. 새 게임\n2. 게임 불러오기\n3. 종료\n메뉴를 선택하세요:");
} while (choice !== "3");
console.log("게임을 종료합니다.");
  • 사용자가 "3"을 입력할 때까지 메뉴를 반복해서 보여준다.
  • choice !== "3" 조건식은 선택 값이 "3"이 아닌 경우 참(true)을 반환한다.

3. 최소 한 번 실행되는 루프

let i = 0;
do {
  console.log(i);
  i++;
} while (i < 5);
  • i가 5보다 작을 때까지 루프를 반복한다.
  • i가 0일 때 코드 블록이 실행되고, i가 1씩 증가하면서 루프가 반복된다.

무한 루프 가능성 존재

do...while 루프도 while 계통 반복문이다 보니 조건식이 참이 아닐경우 무한하게 반복하게 된다.

while 루프는 조건이 참일 때만 반복문이 시작되기 때문에 조건이 거짓이면 시작 자체를 안하지만

do...while는 무조건 1번은 실행되기 때문에 그냥 while문보다 무한 루프에 빠질 가능성이 더 크다.

때문에  do...while 반복문은 더욱 강력한 조건을 걸거나 반복에서 빠져나 올 수 있는 수단이 반드시 있어야 한다.

 

  • 조건식에 변수를 사용하여 특정 횟수만큼 반복하도록 제어한다.
  • 특정 조건 만족 시 break 문을 사용하여 루프를 종료한다.
  • 플래그 변수를 사용하여 루프의 종료 여부를 제어한다.
  • 시간 제한을 사용하여 루프의 실행 시간을 제한한다.

4. for...in 루프

for (variable in object) {코드} 식의 구조이기 때문에 for...in 루프라고 부른다.

객체의 각 속성을 순회하면서 지정된 코드 블록을 실행하며, 속성의 키를 변수에 할당한다.

  1. 코드 실행과
  2. 속성의 키를 변수에 할당하는 것은 동시에 진행되는 별개의 작업이다.
더보기
const person = {
  name: "John",
  age: 30,
  city: "New York",
};

for (let key in person) {
  console.log(key); // 할당된 키 출력
  console.log(person[key]); // 할당된 키를 사용하여 값 출력
}
  1. person이라는 객체(object)가 선언됐다.
  2. for (let key in person) 문이 실행된다.
  3. key 변수에 "name" 키가 할당된다.
  4. console.log(key)와 console.log(person[key])가 실행되어 "name"과 "John"이 출력된다.
  5. key 변수에 "age" 키가 할당된다.(재할당)
  6. console.log(key)와 console.log(person[key])가 실행되어 "age"와 30이 출력된다.
  7. key 변수에 "city" 키가 할당된다.(재할당)
  8. console.log(key)와 console.log(person[key])가 실행되어 "city"와 "New York"이 출력된다.

여기서 let key로 선언된 key라는 변수는 문자열 변수이다. 반복되면서 계속 값이 재할당된다.

5. for...of 루프

for (variable of iterable) {코드} 식의 구조이기 때문에 for...of 루프라고 부른다.

반복 가능한 객체(iterable objects, 이터러블 객)를 순회하면서 지정된 코드 블록을 실행하며, 객체의 요소를 변수에 할당한다.

  1. 코드 실행과
  2. 요소를 변수에 할당하는 것은 동시에 진행되는 별개의 작업이다.
const numbers = [1, 2, 3, 4, 5];
for (let number of numbers) {
  console.log(number); // 1, 2, 3, 4, 5
}

 

반복 가능한 객체(iterable objects, 이터러블 오브젝트)란?

여러 개의 요소(element)를 순차적으로 접근할 수 있는 객체를 의미한다.

쉽게 말해, 반복문(예: for...of 루프)을 사용하여 각 요소에 하나씩 접근할 수 있는 객체이다.

 

대표적인 반복 가능한 객체

  • 배열 (Array): 여러 개의 값을 순서대로 저장하는 자료형이다.
  • 문자열 (String): 여러 개의 문자를 순서대로 저장하는 자료형이다.
  • Map: 키-값 쌍을 저장하는 자료형으로, 삽입 순서를 기억한다.
  • Set: 중복되지 않는 값들을 저장하는 자료형으로, 삽입 순서를 기억한다.
  • NodeList: 문서 객체 모델(DOM)에서 선택된 노드들의 컬렉션이다.
  • arguments 객체: 함수 내부에서 전달된 인자들을 저장하는 유사 배열 객체이다.

for...of 루프 사용 예시

더보기

1. 배열 순회

 
const numbers = [1, 2, 3, 4, 5];

for (const number of numbers) {
  console.log(number);
}
// 출력 결과:
// 1
// 2
// 3
// 4
// 5

2. 문자열 순회

const text = "Hello";

for (const char of text) {
  console.log(char);
}
// 출력 결과:
// H
// e
// l
// l
// o

3. Map 순회

const map = new Map([
  ["name", "John"],
  ["age", 30],
]);

for (const [key, value] of map) {
  console.log(key, value);
}
// 출력 결과:
// name John
// age 30

4. Set 순회

const set = new Set([1, 2, 3, 4, 5]);

for (const value of set) {
  console.log(value);
}
// 출력 결과:
// 1
// 2
// 3
// 4
// 5

5. NodeList 순회

const listItems = document.querySelectorAll("li");

for (const item of listItems) {
  console.log(item.textContent);
}
// 출력 결과:
// 각 <li> 요소의 텍스트 콘텐츠

6. arguments 객체 순회 (함수 내부)

function printArguments() {
  for (const arg of arguments) {
    console.log(arg);
  }
}

printArguments(1, "hello", true);
// 출력 결과:
// 1
// hello
// true

7. 사용자 정의 반복 가능한 객체 순회

const myIterable = {
  data: [1, 2, 3],
  [Symbol.iterator]() {
    let index = 0;
    return {
      next: () => {
        if (index < this.data.length) {
          return { value: this.data[index++], done: false };
        } else {
          return { done: true };
        }
      },
    };
  },
};

for (const value of myIterable) {
  console.log(value);
}
// 출력 결과:
// 1
// 2
// 3