728x90

동기 I/O(Synchronous I/O)는 입력 또는 출력 작업이 실행될 때 프로그램의 실행이 그 작업이 완료될 때까지 차단(Block)되는 방식을 의미한다. 즉, 동기 I/O 작업을 요청하면 해당 작업이 완전히 끝나야만 다음 코드 라인이 실행된다. 그리고 프로그램의 실행 흐름이 소스 코드의 순서와 일치하기 때문에, 코드를 읽고 이해하기 쉽다.

 

Blocking I/O에서는 I/O 작업(예: 파일 읽기, 네트워크를 통한 데이터 수신 등)을 요청할 때, 해당 작업이 완료될 때까지 해당 요청을 한 프로세스 또는 스레드가 대기 상태에 들어간다. 즉, 작업이 완료될 때까지 아무것도 하지 못하고 기다려야 한다. 이 방식은 프로그래밍 모델이 단순하고 직관적이지만, I/O 작업의 대기 시간 동안에 CPU가 다른 유용한 작업을 수행하지 못하게 하여 자원을 낭비할 수 있다. (동기 I/O는 I/O 작업 중에 CPU가 유휴 상태가 될 수 있으므로 리소스 활용 측면에서 비효율적일 수 있다.)

 

동기 I/O 방식을 썼을 때 일어나는 일이 뭐가 있을까?

  1. 프로그램이 파일 시스템에서 데이터를 읽거나 쓸 때, 해당 작업이 완료될 때까지 다음 작업으로 넘어가지 않는다.
  2. 데이터베이스에 쿼리를 요청하고, 그 결과를 받기 전까지 다른 작업을 진행하지 않는 경우가 생긴다.
  3. 웹 요청을 보내고 응답을 받을 때까지 기다리는 작업 등이 동기 I/O 방식의 예이다.

비동기 I/O와 비교할 때, 동기 I/O는 구현의 단순함과 코드의 직관적인 흐름 때문에 작은 규모의 애플리케이션 또는 I/O 작업의 복잡성이 낮은 경우에 적합하다. 반면, 비동기 I/O는 I/O 작업이 백그라운드에서 수행되어 프로그램의 응답성과 성능을 향상시킬 수 있는 환경에서 유리하다.

 

비동기 I/O(Asynchronous I/O)는 I/O 작업이 시작된 후, 그 작업이 완료될 때까지 프로그램의 실행이 대기하지 않고 바로 다음 작업으로 넘어가는 방식이다. 이는 I/O 작업이 백그라운드에서 처리되며, 작업의 완료와 데이터의 반환을 대기하는 동안 프로그램이 다른 작업을 수행할 수 있게 해준다.

 

비동기 I/O는 비차단(Non-blocking) 방식을 활용하여 I/O 작업이 프로그램의 실행 흐름을 차단하지 않는다. 프로그램은 I/O 작업의 완료를 기다리지 않고 즉시 다음 코드를 실행할 수 있다.

 

Non-blocking I/O에서는 I/O 작업을 요청하더라도 해당 작업이 즉시 완료될 수 없을 때 대기하지 않고 바로 제어권이 호출자에게 반환된다. 이는 I/O 작업이 완료되지 않았다는 신호와 함께, 프로세스 또는 스레드가 다른 작업을 계속 진행할 수 있게 해준다. Non-blocking I/O는 I/O 작업의 완료를 폴링(polling)하거나, 이벤트 기반 모델을 사용하여 알아차리게 된다. 이 방식은 리소스를 더 효율적으로 활용할 수 있지만, 작업 완료를 확인하고 관리하기 위한 추가적인 로직이 필요하다.

 

Non-blocking I/O를 효과적으로 활용하기 위해서는 비동기 프로그래밍 모델, 이벤트 루프, 콜백 함수, 프로미스, async/await 등과 같은 개념과 기술을 이해하고 적용할 필요가 있다. 이러한 방식은 Node.js와 같은 플랫폼에서 널리 사용되며, 고성능 네트워크 서버와 애플리케이션을 구축하는 데 중요한 역할을 한다.

 

콜백 함수는 다른 함수에 인자로 전달되는 함수로, 어떤 이벤트가 발생했거나 특정 작업이 완료된 후에 실행된다. 이를 통해 비동기 작업을 처리할 수 있다. 그러나 콜백 함수를 사용하여 여러 비동기 작업을 연속으로 처리하려고 할 때, 코드는 점점 중첩되고 가독성이 떨어지는 문제가 발생한다. 이러한 상황을 콜백 지옥(Callback Hell) 또는 콜백 피라미드(Pyramid of Doom)라고 한다.

getData(function(a){
    getMoreData(a, function(b){
        getMoreData(b, function(c){ 
            console.log('콜백 지옥');
        });
    });
});

 

프로미스는 비동기 작업의 최종 완료 또는 실패를 나타내는 객체다. 콜백 지옥의 문제를 해결하기 위해 ES6에서 도입되었다. 프로미스는 .then() 메서드를 통해 연속된 비동기 작업을 체이닝할 수 있게 해준다. 또한, .catch() 메서드를 통해 에러를 쉽게 처리할 수 있다.

getData()
  .then(result => getMoreData(result))
  .then(result => getMoreData(result))
  .then(finalResult => console.log(finalResult))
  .catch(error => console.error(error));

 

async/await는 ES2017(ES8)에서 소개된, 비동기 작업을 동기적으로 보이는 코드 스타일로 작성할 수 있게 해주는 문법이다. async 함수 내부에서 await 키워드를 사용하면, 프로미스가 처리될 때까지 함수의 실행을 일시 중단하고, 프로미스의 결과가 반환되면 실행을 재개한다. 이를 통해 비동기 코드를 마치 동기 코드처럼 쉽게 작성하고 이해할 수 있다.

async function asyncCall() {
  try {
    const result = await getData();
    const moreData = await getMoreData(result);
    const finalResult = await getMoreData(moreData);
    console.log(finalResult);
  } catch (error) {
    console.error(error);
  }
}

 

 

비동기 프로그래밍 모델의 장접은 애플리케이션의 응답성을 향상시켜주고, I/O 작업 중에도 CPU는 다른 작업을 처리할 수 있으므로, 자원을 보다 효율적으로 활용할 수 있다. 또한, 여러 I/O 작업을 동시에 처리할 수 있어, 애플리케이션의 처리 능력을 향상시킬 수 있다.

 

하지만 비동기 프로그래밍 모델은 동기 모델에 비해 이해하고 사용하기가 더 복잡할 수 있고 콜백 지옥과 같은 문제를 관리해야 하는 단점이 있다.

728x90
728x90

React와 Express는 Node.js 환경에서 실행되므로, Node.js가 설치되어 있지 않다면 먼저 설치한다. Node.js 공식 웹사이트(https://nodejs.org)에서 안내에 따라 설치할 수 있다.

 

Node.js — Run JavaScript Everywhere

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

터미널을 열고, 원하는 위치에 프로젝트를 위한 새 디렉토리를 생성한다.

mkdir my-web-app
cd my-web-app

 

Create React App을 사용해 React 프론트엔드 애플리케이션을 생성한다.

npx create-react-app client

 

이 명령은 client라는 이름의 디렉토리에 React 애플리케이션을 생성한다.

 

React 애플리케이션 외부에서 Express 애플리케이션을 설정한다.

 

루트 디렉토리(my-web-app)로 돌아가고 Express 애플리케이션을 위한 server 디렉토리를 생성하고 초기화한다.

mkdir server
cd server
npm init -y

 

Express를 설치한다.

npm install express

 

server 디렉토리 내에 index.js 파일을 생성하고, 기본 서버 설정을 추가한다.

const express = require('express');
const app = express();
const PORT = process.env.PORT || 5000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

 

개발 중에 React 애플리케이션과 Express 서버를 동시에 실행하기 위해, 루트 디렉토리(my-web-app)에 concurrentlynodemon을 사용하는 설정을 추가한다.

 

my-web-app로 돌아가 package.json 파일을 생성한다.

npm init -y

 

concurrentlynodemon을 설치한다.

npm install concurrently nodemon --save-dev

 

my-web-app/package.json 파일에서 scripts 섹션을 다음과 같이 수정한다.

"scripts": {
  "client": "cd client && npm start",
  "server": "cd server && nodemon index.js",
  "dev": "concurrently \"npm run server\" \"npm run client\""
}

 

이 설정을 통해, npm run dev 명령으로 React 애플리케이션과 Express 서버를 동시에 실행할 수 있다.

 

이러면 DeprecationWarning 이 뜨는데,

 

DeprecationWarning은 React 스크립트나 사용 중인 라이브러리에서 더 이상 사용하지 않는 기능을 사용했을 때 발생한다. 이 경고들은 주로 개발 환경 설정에 관련되어 있으며, 애플리케이션의 실행에는 직접적인 영향을 주지 않는다. 만약 webpack 관련 버전 문제로 DeprecationWarning이 났고 이를 해결하기 위해서는 webpack 설정을 확인하고 업데이트하는 것이 좋다. React와 관련된 설정은 node_modules 안의 관련 라이브러리 설정에서 찾을 수 있으며, 일반적으로 사용자가 직접 수정할 필요는 없다.

 

webpack 설정 확인하고 업데이트 하는 법:

 

프로젝트 폴더에서 터미널을 열고, 현재 설치된 Webpack 버전을 확인한다.

npm list webpack

 

npm 레지스트리에서 Webpack의 최신 버전을 확인한다.

npm view webpack version

 

원하는 버전(대개 최신 버전)으로 Webpack을 업데이트한다.

npm install webpack@latest --save-dev

 

@latest 대신 특정 버전을 지정하고 싶다면, @ 뒤에 해당 버전 번호를 명시한다 (예: webpack@4.44.1). --save-dev는 이 패키지가 개발 의존성임을 나타낸다.

 

Webpack과 함께 사용되는 로더(loader) 및 플러그인(plugin)도 최신 버전으로 업데이트하는 것이 좋다. 이는 호환성 문제를 방지하기 위함이다.

npm install webpack-cli@latest webpack-dev-server@latest --save-dev

 

여기서 webpack-cliwebpack-dev-server는 Webpack을 사용하는 데 흔히 필요한 패키지이며, 프로젝트에 따라 추가적인 로더나 플러그인이 필요할 수 있다.

 

업데이트 후 프로젝트가 여전히 정상적으로 작동하는지 확인해야한다. 때때로 메이저 버전 업데이트는 기존 설정이나 코드와 호환되지 않을 수 있으므로, 특히 중요한 변경 사항을 적용한 후에는 프로젝트를 꼼꼼히 테스트해야 한다.

 

업데이트 후 의존성 문제나 버전 충돌이 발생할 수 있다. 이런 경우, 해당 패키지의 문서를 참고하여 새 버전에 맞게 설정을 조정하거나 필요한 변경을 적용한다.

 

최신 버전으로 업데이트 한 후에도 여전히 동일한 경고 메시지가 나타나면, react - scripts 패키지 내부에서 사용되는 webpack 설정 때문에 발생할 수 있다. react - scripts 는 Create React App을 통해 생성된 프로젝트에서 Webpack과 같은 도구들을 추상화해 제공한다. 때문에 react - scripts 내부의 webpack 설정은 직접적으로 수정할 수 없다.

 

Create React App 팀이 react-scripts의 새 버전을 출시하여 이러한 경고를 해결할 수 있다. 따라서 react-scripts의 최신 버전을 확인하고 업데이트하는 것이 좋다.

npm install react-scripts@latest --save

 

ESLint 경고는 개발자가 직접 해결할 수 있다. App.tsx 파일에서 사용하지 않는 logo 변수를 제거하면 된다.

 

npm audit보안 취약점에 대한 경고다. npm audit 또는 npm audit fix를 실행하여 취약한 의존성을 해결할 수 있다. 일부 의존성은 메이저 업데이트를 필요로 할 수 있으며, npm audit fix --force를 사용하여 강제로 수정할 수 있다. 하지만, 이는 기존 코드에 영향을 줄 수 있으므로 주의해서 사용해야 한다.

 

npm audit를 정기적으로 실행하여 보안 취약점을 확인하고 해결하는 것이 좋다.

728x90
728x90

셀레니움(Selenium)을 활용하여 동적인 웹 사이트의 데이터를 크롤링하는 방법을 간단하게 설명하는 글이다. 

 

프로젝트에 필요한 라이브러리와 의존성을 관리하기 위해, 먼저 가상 환경을 생성하고 활성화한다. PowerShell에서 다음 명령어를 실행한다.

 

python -m virtualenv venv
.\venv\Scripts\activate

 

가상 환경이 활성화된 후, 셀레니움과 크롬 드라이버 자동 설치를 위한 chromedriver_autoinstaller를 설치한다.

 

pip install selenium chromedriver_autoinstaller

 

셀레니움을 사용하여 네이버에서 "python flask"에 대한 검색 결과 중 특정 블로그 글의 랭킹을 찾는 과정은 다음과 같다.

from selenium import webdriver
from selenium.webdriver.common.by import By
import chromedriver_autoinstaller
import time

chromedriver_autoinstaller.install()
driver = webdriver.Chrome()

 

chromedriver_autoinstaller를 사용하여 크롬 드라이버를 자동으로 설치하고, 셀레니움 웹 드라이버를 초기화한다.

query = "python flask"
search_link = f"https://search.naver.com/search.naver?where=view&sm=tab_jum&query={query}"
driver.get(search_link)
time.sleep(2)

 

"python flask" 검색 결과 페이지로 이동한다.

target_blog_link = "https://blog.naver.com/surper_/223140462387"
link_selector = f'a[href^="{target_blog_link}"]'

 

페이지 내에서 특정 블로그 링크를 찾기 위해 CSS 선택자를 사용한다. 이 링크는 동적 요소이므로 페이지를 스크롤하여 랭크 정보가 로드될 때까지 기다린다.

element = driver.find_element(By.CSS_SELECTOR, link_selector)
while True:
    new_element = element.find_element(By.XPATH, "./..")
    rank = new_element.get_attribute("data-cr-rank")
    if rank:
        print("랭크 찾음 : ", rank)
        break
    element = new_element

 

블로그의 랭킹 정보를 포함하는 상위 요소를 찾기 위해 XPATH를 사용하여 부모 요소로 이동한다. data-cr-rank 속성에서 랭킹 정보를 추출한다.

for _ in range(7):
    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(3)

 

만약 특정 요소를 찾지 못하면, 페이지를 더 스크롤하여 추가적인 데이터를 로드해야 할 수도 있다. 이를 위해 driver.execute_script 메소드를 사용하여 페이지 하단으로 스크롤한다.

728x90

'웹 개발 > 웹 크롤링' 카테고리의 다른 글

셀레니움 예제1  (0) 2024.03.22
728x90

셀레니움(Selenium)은 동적 웹 페이지와의 상호작용에 최적화된 라이브러리다. 웹 페이지의 동적 요소들, 예를 들어 JavaScript로 불러오는 데이터까지 크롤링하는 데 유용하다.

 

1. Python으로 프로젝트를 진행할 때, 프로젝트별로 독립된 환경을 만들기 위해 가상 환경을 사용한다.

 

pip install virtualenv

 

2. 테스트할 폴더(test_folder)로 이동한 후, 해당 폴더 내에 가상 환경(folder)을 생성한다.

 

cd test_folder
python -m virtualenv folder
.\folder\Scripts\activate

 

3. 가상 환경이 활성화된 상태에서 셀레니움과 크롬 드라이버 자동 설치 라이브러리를 설치한다.

 

pip install selenium chromedriver_autoinstaller

 

셀레니움을 이용해 크롬 드라이버를 자동으로 설치하고, 브라우저를 제어하는 기본적인 코드는 다음과 같다.

from selenium import webdriver
from selenium.webdriver.common.by import By
import chromedriver_autoinstaller
import time

chromedriver_autoinstaller.install()
driver = webdriver.Chrome()
driver.get("https://www.naver.com")
time.sleep(3)  # 로딩 대기

 

셀레니움을 사용해 동적인 웹 페이지의 데이터를 크롤링하고, 페이지 내 이동, 뒤로 가기, 앞으로 가기 등의 브라우저 동작을 자동화할 수 있다.

 

# 페이지 이동
driver.get("https://google.com")
time.sleep(1)

# 뒤로 가기
driver.back()
time.sleep(2)

# 앞으로 가기
driver.forward()
time.sleep(2)

# 새로고침
driver.refresh()
time.sleep(2)

print("동작 완료")

 

셀레니움은 웹 페이지의 특정 요소가 로딩될 때까지 대기하고, 해당 요소와 상호작용하는 기능도 제공한다.

 

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

try:
    selector = "#shortcutArea > ul"
    WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.CSS_SELECTOR, selector)))
except:
    print("예외 발생")
print("엘리먼트 로딩 끝")
print("다음 코드 실행")
input()

 

 

728x90

'웹 개발 > 웹 크롤링' 카테고리의 다른 글

셀레니움 예제2  (0) 2024.03.22
728x90

이 웹 게임은 플래피 버드처럼 space bar를 누를 때마다 위로 오르고 가만히 있으면 새가 점점 아래로 내려가는 게임이다.

좌측 상단에 있는 퀴즈를 보고 정답 표지판을 들고 있는 원숭이에게 새를 닿게하면 점수가 오르고 최대 점수인 8점을 득점하는 것이 게임의 목표다.

 

 

JWT는 JSON Web Tokens의 약자로, HEADER:ALGORITHM & TOKEN TYPE, PAYLOAD:DATA, VERIFY SIGNATURE로 구분된다. 나는 이 게임을 만들면서 HS256이라는 대칭키 해싱 알고리즘을 사용하여 보안을 강화시켰다.

 

 

세션과 토큰은 사용자 인증 방식에서 자주 사용되는 두 가지 개념이다. 둘 다 사용자의 로그인 상태를 관리하는 데 사용되지만, 관리 방식에 차이가 있다. 세션과 토큰의 차이점은 무엇이 있을까?

 

세션은 서버 기반 인증 시스템에서 사용된다. 사용자가 로그인하면 서버는 사용자에 대한 세션을 생성하고 세션 ID를 발급한다. 이 세션 ID는 사용자의 브라우저에 쿠키로 저장되며, 사용자가 서버에 요청할 때마다 이 쿠키를 통해 서버는 사용자를 식별한다. 세션 정보는 서버의 메모리나 데이터베이스에 저장된다.

 

토큰은 사용자가 로그인하면 서버는 특정 정보를 포함한 토큰을 생성하고 이를 사용자에게 발급한다. 사용자는 이 토큰을 저장해두었다가, 서버에 요청할 때마다 헤더에 토큰을 포함시켜 서버에 전송한다. 서버는 토큰의 유효성을 검증하여, 사용자를 식별한다. 토큰은 클라이언트 측에서 관리되며, 서버는 상태를 유지할 필요가 없어서 무상태 통신이 가능하다.

 

 

jinja2는 flask의 render_template 라는 기능을 사용하여 데이터를 넘겨 html로 구현하는 것이다. 웹 페이지를 동적으로 생성하는 데 유용하다.

 

function showRanking(ranking_list) {
    $("#modalBackground").addClass("opacity-100", "z-1000", "transition-all", "duration-500");
    $("#modalBackground").removeClass("-z-10");
    $("#ranking-list").empty();

    for (var i = 0; i < ranking_list.length; i++) {
        var rank = i + 1;
        var id = ranking_list[i]['id'];
        var score = ranking_list[i]['score'];
        $("#ranking-list").append("<li class='mx-20 px-10 pl-40 text-customListFontSize1 text-nowrap font-jgF1'>" + rank + "위 " + id + "      " + score + "점" + "</li>");
    }
}

function closeRanking() {
    $("#modalBackground").removeClass("opacity-100", "z-1000", "transition-all", "duration-500");
    $("#modalBackground").addClass("-z-10");
}

window.onload = function () {
    $("#ranking").click(function () {
        showRanking(ranking_list);
    });
    $("#close").click(closeRanking);
}

 

Jinja2의 서버 사이드 렌더링 방식

1. 사용자가 웹 페이지를 요청하면 서버는 해당 요청을 받아 처리한다.

2. 필요한 데이터는 데이터베이스나 다른 서비스로부터 로드된다.

3. 로드된 데이터를 Jinja2 템플릿에 삽입하여 HTML을 동적을 생성한다.

4. 생성된 HTML 페이지는 사용자의 브라우저로 전송되어 표시된다

 

 <script type=text/javascript>
    var ranking_list = JSON.parse({{ ranker | tojson | safe}});
  </script>

 

jinja2는 javascript 코드를 직접 실행하거나 조작하는 기능은 지원이 안 되기 때문에 위 코드처럼 구현하여야한다.

 

해당 게임 github URL

 

GitHub - JeongJongMun/JungleGap: 크래프톤 4기 미니프로젝트 - 정글차이

크래프톤 4기 미니프로젝트 - 정글차이. Contribute to JeongJongMun/JungleGap development by creating an account on GitHub.

github.com

728x90

+ Recent posts