비동기 프로그래밍

2025. 1. 21. 17:52·Computer Science

Selenium 사용할 때 발생했던 문제

JavaScript로 대학교 공지사항 스크래퍼를 개발할 때 Selenium 공식문서를 참고해 코드를 작성하고 있었다. Selenium에서 설명하는 여러 메소드를 참고하던 중 'await' 라는 예약어가 공식문서에 있었지만 필요없다고 생각해 예약어를 지우고 코드를 작성했다.

 

그러자 코드에선 여러 알 수 없는 오류를 내뿜기 시작했다. 나중에 알고보니 async함수를 사용하고도 await가 필요한 코드 문장에 예약어를 넣지 않아 함수끼리 값이 섞여 프로그램이 다운되는게 원인이었다.

 

그래서 나는 await가 무슨 역할을 했는지 궁금해져 검색해보니 비동기 프로그래밍을 위해 사용되는 명령어라는 걸 알았다. 비동기 프로그래밍이 뭘까?

 

 

 

 

동기 프로그래밍

코드를 작성하면 코드 안의 함수들은 작성한 순서대로 실행된다는 것을 쉽게 알 수 있다. 여러개의 함수가 있다면 먼저 나온 함수부터 실행되어 순차적으로 나머지 함수들도 실행되는 것을 알 수 있다.

 

a()
b()
c()

 

동기 프로그래밍이라면 a() -> b() -> c() 함수 순으로 실행될 것이다.

 

동기 프로그래밍 방식에서는 작업이 순차적으로 실행되며, 한 작업이 완료될 때까지 다음 작업이 대기한다. 이는 한 함수에 다른 함수가 들어있어도 마찬가지다.

 

def fetch_data():
    print("데이터 가져오는 중...") # f1
    # 데이터 가져오기
    print("데이터 가져오기 완료!") # f2
    return "데이터"

def process_data():
    print("데이터 처리 시작") # p1
    data = fetch_data()  # 데이터가 필요하므로 대기
    print(f"받은 데이터: {data}") # p2

process_data()

 

 위의 코드에서 실행순서를 보자. 우선 process_data()의 p1이 실행된다. 그리고 fetch_data() 함수가 실행되어 f1, f2가 실행되고 마지막으로 p2가 실행될 것이다. 한 함수가 실행중일 때 다른 함수는 대기하는 것이 동기 프로그래밍이다. 

 

 

 

 

비동기 프로그래밍

비동기 프로그래밍은 한 함수가 실행중일 때에도 다른 함수가 그냥 실행하는 되는 것이다. 위의 코드에서 나온 대기 없이 여러 작업이 동시에 진행될 수 있다.

 

import asyncio

async def fetch_data():
    print("데이터 가져오는 중...") # f1
    await asyncio.sleep(3)  # 3초 대기
    print("데이터 가져오기 완료!") # f2
    return "데이터"

async def process_data():
    print("데이터 처리 시작") # p1
    data = await fetch_data()  # 데이터가 필요하므로 대기
    print(f"받은 데이터: {data}") # p2

asyncio.run(process_data())

 

비동기 프로그래밍에서는 여러 작업이 대기 없이 그냥 실행될 수 있다. 위 코드를 보면 async(비동기) 예약어 덕분에 process_data() 가 실행되면 p1이 실행됨에 동시에 fetch_data()가 실행된다. 다만 await라는 대기 예약어 때문에 fetch_data()가 값을 반환할 때까지 대기하는 것이다.

 

동기 프로그래밍 코드와 결과는 같지만, 함수들이 대기하지 않고 다함께 실행된다는 점에서 큰 차이가 있다. 여러 함수들이 동시에 실행된다면 당연히 함수들이 순차적으로 대기하며 실행할 때보다 더 프로그램의 실행시간이 더 빠르다. 다만 동시에 실행되다 보면 특정 관계에 있는 서로 다른 함수들이 필요한 값 없이 우후죽순 실행될 수 있어 오류의 위험성이 커진다.

 

 

await을 생략하면 비동기 작업이 즉시 실행되지만, 결과를 기다리지 않기 때문에 값이 섞이거나 코드의 실행 순서가 꼬이는 문제가 발생할 수 있다. 작업의 결과값이 반드시 필요한 경우, await을 사용해 의도적으로 대기해야 한다.

 

 

 

 

Selenium 에서 비동기 프로그래밍 사용 예시

다음은 내가 만든 코드에서의 비동기 프로그래밍 예시이다.

import { By, Builder, Browser, until } from "selenium-webdriver";
import chrome from "selenium-webdriver";

let pageNum = 0;
try {
  // URL 연결
  let driver = await new Builder().forBrowser(Browser.CHROME).build();
  await driver.get(
    "학교 URL"
  );

  // Page 분류
  for (let page = 1; page < 6; page++) {
    console.log(`page : ${page}`);

    // 목차 변경
    if (page == 3) pageNum = page + 2; // page >= 3일 경우 DOM 변경되는 거 적용
    let button_xpath = `//*[@id="cms-content"]/div/div/div[2]/div[3]/div/ul/li[${
      page >= 3 ? pageNum : page
    }]/a`;
    const nextButton = await driver.findElement(By.xpath(button_xpath));

    // button이 나올때까지 명시적 대기
    await driver.wait(until.elementIsVisible(nextButton), 10000);
    await driver.wait(until.elementIsEnabled(nextButton), 10000);
    await nextButton.click();

    // 스크래핑
    for (let x = 1; x <= 10; x++) {
      let list_xpath = `//*[@id="cms-content"]/div/div/div[2]/div[2]/table/tbody/tr[${x}]/td[2]/div/a`;
      let date_xpath = `//*[@id="cms-content"]/div/div/div[2]/div[2]/table/tbody/tr[8]/td[4]`;
      const list = await driver.findElement(By.xpath(list_xpath)); // 리스트
      const date = await driver.findElement(By.xpath(date_xpath)); // 날짜
      console.log(await list.getText(), await date.getText());
      console.log(await list.getAttribute("href")); // URL
    }
    pageNum++;
  }

  // 드라이버 종료
  driver.quit();
} catch (e) {
  console.log(e);
}

 

for (let x = 1; x <= 10; x++) {
      let list_xpath = `//*[@id="cms-content"]/div/div/div[2]/div[2]/table/tbody/tr[${x}]/td[2]/div/a`;
      let date_xpath = `//*[@id="cms-content"]/div/div/div[2]/div[2]/table/tbody/tr[8]/td[4]`;
      const list = await driver.findElement(By.xpath(list_xpath)); // 리스트
      const date = await driver.findElement(By.xpath(date_xpath)); // 날짜
      console.log(await list.getText(), await date.getText());
      console.log(await list.getAttribute("href")); // URL
    }
    pageNum++;
  }

 

위 코드에서 list와 date 변수는 값이 꼭 필요하다. 그래서 비동기 프로그래밍 속 await 예약어를 두어 값을 받을 때까지 대기하도록 조정했다. 이렇게 하고 나니 값을 건너뛰고 출력하는 일이 없어져 오류가 생기지 않았다.

'Computer Science' 카테고리의 다른 글

가상 머신(Virtual Machine)  (0) 2025.03.28
ANSI C grammar Lex Specification  (0) 2024.05.08
'Computer Science' 카테고리의 다른 글
  • 가상 머신(Virtual Machine)
  • ANSI C grammar Lex Specification
BestTomaTo
BestTomaTo
  • BestTomaTo
    기록보관소
    BestTomaTo
  • 전체
    오늘
    어제
    • 분류 전체보기 (36) N
      • Algorithm (8)
      • Computer Science (3)
      • Backend (3)
      • DevOps (4)
        • Kubernetes (3)
        • Docker (0)
      • Data Engineering (8)
      • Cloud (2)
      • AI (1)
      • Security (3) N
        • SK Shieldus Rookies (3) N
      • Reference (2)
      • Project (1)
      • Experience (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    sql 개발자
    AWS
    langchain memory
    langsmith
    3단계 모델링
    홈 서버
    airlfow
    해커톤 후기
    SQLD
    동기 프로그래밍
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
BestTomaTo
비동기 프로그래밍
상단으로

티스토리툴바