본문 바로가기
개발/NestJS

JavaScript/TypeScript의 for...of와 for await...of 완벽 가이드

by coking 2024. 11. 27.

안녕하세요! 오늘은 for...offor await...of의 차이점에 대해 알아보려고 합니다. 이 두 구문의 차이를 정확히 아는 개발자가 생각보다 적은데요, 실제 프로젝트에서 잘못 사용하면 심각한 버그를 유발할 수 있습니다. 차근차근 알아보도록 할게요!

1. 기본 개념

for...of

for (const item of items) {
  // items는 일반 배열
  console.log(item);
}
  • 일반 배열을 순회할 때 사용
  • Symbol.iterator를 구현한 모든 컬렉션에 사용 가능
  • 배열, 문자열, Map, Set 등에 사용

for await...of

for await (const item of items) {
  // items는 Promise 배열 또는 AsyncIterable
  console.log(item);
}
  • Promise 배열이나 AsyncIterable을 순회할 때 사용
  • 각 요소가 resolve될 때까지 기다림
  • async 함수 내에서만 사용 가능

2. 실제 사용 예시

2.1 일반 배열 순회

const fruits = ['apple', 'banana', 'orange'];

// ✅ 올바른 사용
for (const fruit of fruits) {
  console.log(fruit);
}

// ❌ 불필요한 사용 (일반 배열에는 for await...of가 필요없음)
for await (const fruit of fruits) {
  console.log(fruit);
}

2.2 Promise 배열 순회

const promises = [
  Promise.resolve('data1'),
  Promise.resolve('data2'),
  Promise.resolve('data3')
];

// ✅ 올바른 사용
for await (const result of promises) {
  console.log(result); // 'data1', 'data2', 'data3'
}

2.3 비동기 작업이 포함된 일반 배열 순회

// 데이터베이스 작업 예시
const users = [
  { id: 1, name: 'John' },
  { id: 2, name: 'Jane' },
  { id: 3, name: 'Bob' }
];

// ✅ 올바른 사용
for (const user of users) {
  await db.save(user); // 각 작업마다 개별적으로 await
}

// ❌ 잘못된 사용
for await (const user of users) {
  db.save(user); // users가 Promise 배열이 아니므로 부적절
}

3. 실제 프로젝트 사례

제가 최근에 경험한 실제 프로젝트 사례를 공유해드릴게요.

// 테스트 데이터 설정
const TEST_DATA = {
  users: [
    {
      data: { nickname: 'user1' },
      profile: { email: 'user1@test.com' }
    },
    {
      data: { nickname: 'user2' },
      profile: { email: 'user2@test.com' }
    }
  ]
};

// ✅ 올바른 구현
async function setupTestData() {
  for (const user of TEST_DATA.users) {
    await userRepository.insert(user.data);
    await profileRepository.insert(user.profile);
  }
}

// ❌ 잘못된 구현
async function setupTestData() {
  for await (const user of TEST_DATA.users) {  // TEST_DATA.users는 일반 배열
    userRepository.insert(user.data);          // await 없음
    profileRepository.insert(user.profile);     // await 없음
  }
}

이 경우 잘못된 구현에서는 두 가지 문제가 발생합니다:

  1. TEST_DATA.users는 Promise 배열이 아닌데 for await...of를 사용
  2. 데이터베이스 작업에 await가 없어 순서가 보장되지 않음

4. 결론

언제 for...of를 사용하나요?

  • 일반 배열을 순회할 때
  • 배열 내 각 요소에 대해 비동기 작업이 필요할 경우 개별 await 사용

언제 for await...of를 사용하나요?

  • Promise 배열을 순회할 때
  • AsyncIterable 객체를 순회할 때
  • Stream 데이터를 처리할 때

핵심 포인트

  1. 순회하는 대상이 무엇인지 정확히 파악하기
  2. Promise 배열이 아니라면 for...of 사용
  3. 비동기 작업은 항상 await 잊지 않기

이렇게 사용하시면 실수를 줄일 수 있을 거예요! 혹시 추가 질문이나 궁금한 점이 있다면 댓글로 남겨주세요. 😊

참고자료

댓글