update: 2024.3.9

항목에 대한 정보

  1. Model: 모델의 이름 또는 식별자입니다.
  2. Params: 모델의 매개변수 수 (일반적으로 모델의 크기를 나타냅니다).
  3. HumanEval: 모델의 인간 평가 점수입니다. 이는 모델이 사람의 품질에 얼마나 근접한지를 나타냅니다.
  4. MBPP: Mean Binary Perplexity (MBPP)는 모델이 이진 데이터의 평균 퍼플렉서티를 나타냅니다. 이것은 모델이 이진 데이터를 얼마나 잘 예측하는지를 측정합니다.
  5. HF: Hugging Face 모델 허브(Hugging Face Model Hub)의 해당 모델에 대한 링크입니다.
  6. Source: 해당 모델에 대한 추가 정보를 찾을 수 있는 링크입니다.
Model Params HumanEval MBPP HF Source
GPT-4 + Reflexion ? 91.0 77.1 paper
GPT-4 (latest) ? 84.1 80.0 github
DeepSeek-Coder-Instruct 33B 79.3 70.0 ckpt github
DeepSeek-Coder-Instruct 7B 78.6 65.4 ckpt github
GPT-3.5-Turbo (latest) ? 76.2 70.8 github
Code-Llama 34B 62.2 61.2 paper
Pangu-Coder2 15B 61.6 paper
WizardCoder-15B 15B 57.3 51.8 ckpt paper
Code-Davinci-002 ? 47.0 paper
StarCoder-15B (Prompted) 15B 40.8 49.5 ckpt paper
PaLM 2-S ? 37.6 50.0 paper
PaLM-Coder-540B 540B 36.0 47.0 paper
InstructCodeT5+ 16B 35.0 paper
StarCoder-15B 15B 33.6 52.7 ckpt paper
Code-Cushman-001 ? 33.5 45.9 paper
CodeT5+ 16B 30.9 paper
LLaMA2-70B 70B 29.9 ckpt paper
CodeGen-16B-Mono 16B 29.3 35.3 paper
PaLM-540B 540B 26.2 36.8 paper
LLaMA-65B 65B 23.7 37.7 paper
CodeGeeX 13B 22.9 24.4 paper
LLaMA-33B 33B 21.7 30.2 paper
CodeGen-16B-Multi 16B 18.3 20.9 paper
AlphaCode 1.1B 17.1 paper
반응형

'개발' 카테고리의 다른 글

Mac이 AI PC로서 최고인 이유  (0) 2024.02.03
2024년 프로그래밍 랭킹  (1) 2024.01.02
gitlab 설치(apache --proxy--> gitlab)  (0) 2023.12.27
elixir vs rust 비교  (1) 2023.12.27
vscode dev containers  (1) 2023.12.22

개인적으로 새로운 언어를 공부하는 것은 너무나 재미있습니다. 새로운 대륙을 향해 돛단배를 몰고가는 것과 같습니다. 어디로 갈지 모르지만, 미지의 세계를 목표로 출발하는 마음은 셀렘이라는 감정일 것입이다.

이 글에서는 두 가지 언어의 기능, 장단점, 코드 예제를 통해 이 두 가지 언어를 비교해 보겠습니다. 이 글을 읽고 나면 어떤 프로그래밍 언어가 여러분의 필요에 맞는지 더 잘 알 수 있기를 바랍니다.

엘릭서

Elixir는 Erlang VM에서 실행되는 함수형 프로그래밍 언어입니다. Ruby와 동일한 스크립팅 스타일을 사용하지만, Ruby는 객체 지향 언어 패러다임을 따르고 Elixir는 함수형 패러다임을 따른다는 차이점이 있습니다.

특징

  • Elixir는 처음부터 동시성을 가장 잘 처리하는 데 중점을 두었습니다.
  • Elixir는 함수형 프로그래밍 패러다임을 따르기 때문에 반복적인 코드 없이 쉽게 작성할 수 있는 언어를 만들기 위해 노력합니다.
  • Elixir는 내결함성(결함 감내 시스템: Fault tolerant system)을 갖도록 설계되어 문제가 발생했을 때 재시작할 수 있습니다.
  • Elixir는 확장성이 뛰어납니다. 예를 들어, GPU에서 실행할 수 있습니다.
  • 대화형 또는 컴파일된 형태로 Repl에서 Elixir를 실행할 수 있습니다.

Elixir는 확장 가능하고 내결함성 있는 애플리케이션을 구축하는 데 적합한 강력하고 다재다능한 프로그래밍 언어입니다. 함수형 프로그래밍 패러다임, 동시성 기능, Erlang 에코시스템과의 호환성 덕분에 다양한 도메인 및 산업에서 널리 사용되고 있습니다.

엘릭서를 사용하는 곳

Elixir는 Pinterest, Bleacher Report, Discord를 비롯한 다양한 기업과 조직에서 사용하고 있습니다. 대량의 트래픽을 처리할 수 있고 내결함성이 뛰어나 웹 애플리케이션과 분산 시스템을 구축하는 데 널리 사용됩니다.

문서에 따르면 주목할 만한 Elixir 사용자는 다음과 같습니다.

  1. 펩시코: PepsiCo는 소비재 판매를 위한 전자상거래 도구로 Elixir를 사용한다고 보고했습니다. 이들은 Elixir가 강력하고, 간단하며, 배우기 쉽고, 효율적이라는 점을 발견했습니다. Elixir를 사용한 펩시코 지점은 2019년에 20억 달러의 수익을 올렸습니다.
  2. Spotify: Spotify는 백엔드 개발에 Elixir를 사용합니다. 방대한 데이터베이스와 수많은 활성 사용자로 인해 초당 수천 건의 요청을 처리하고 처리할 수 있는 기술이 필요했습니다. Elixir는 필요한 수준의 동시성을 제공하여 세계적인 음악 및 엔터테인먼트 플랫폼으로 성장하는 데 도움을 주었습니다.
  3. Discord: 게이머를 위한 채팅 플랫폼인 Discord 역시 Elixir를 사용하고 있습니다. 대용량 데이터와 엄청난 트래픽을 처리할 수 있는 기술이 필요했습니다. Elixir를 통해 수백만 명의 사용자도 실시간으로 처리할 수 있었습니다. Elixir는 기술 스택의 주요 언어가 되었습니다.
  4. WhatsApp: 전 세계에서 가장 인기 있는 메시징 앱 중 하나인 WhatsApp은 Elixir를 사용합니다. 이 애플리케이션은 처음에 Erlang으로 완전히 작성된 후 Elixir 기반 요소를 도입하여 개선되었습니다. Elixir는 WhatsApp이 방대한 트래픽과 대량의 요청을 처리하는 데 도움이 됩니다.
  5. Heroku: Elixir와 Phoenix 프레임워크를 사용하여 대시보드를 구동하는 Heroku

Elixir 장단점

Elixir의 장점

  • 내결함성(결함 감내 시스템: Fault tolerant system): 동시성을 사용하면 시스템은 종종 많은 문제에 직면합니다. 이러한 문제 중 하나는 하드웨어 오류, 소프트웨어 버그, 네트워크 문제 또는 리소스 제약으로 인해 시스템의 일부가 다운될 수 있다는 것입니다. Elixir는 시스템의 안정성과 장애에 대한 복원력을 보장하는 데 탁월합니다.
  • 확장성(Scalability): Elixir는 동시성 시스템 개발에 항상 우선순위를 두어온 Erlang VM(BEAM)에서 실행되므로 확장성이 뛰어납니다. 이 언어는 서로 통신하는 경량의 격리된 스레드에서 실행됩니다. 또한 Elixir를 사용하여 여러 머신에서 프로세스를 실행하고 통신할 수 있으므로 수평적 확장성을 달성할 수 있습니다.
  1. 웹 API
  2. 강력하고 동적으로(런타임에 확인) 입력됩니다.
  3. 소프트 실시간 서비스는 챗봇이나 Google 문서와 같은 소켓 연결로 구축할 수 있습니다. 하드 실시간 서비스에는 적합하지 않습니다(두 서비스의 차이점은 여기에 있습니다).
  4. 높은 확장성과 내결함성 지원을 제공합니다.
  5. 동시성은 액터 모델을 기반으로 합니다. 이는 Agha의 클래식 액터 모델과 대체로 유사합니다. 이러한 유형의 액터 모델을 프로세스 기반 액터라고 합니다. 이는 시작부터 완료까지 실행되는 계산으로 정의됩니다. 대신 Agha의 클래식 액터 모델은 액터를 거의 행동의 상태 머신과 그 사이를 전환하는 로직으로 정의합니다. 프로세스 기반 액터 모델은 에릭슨에서 Erlang을 개발하면서 처음에 개발되었습니다.
  6. 또한 패턴 매칭을 지원하므로 더 복잡한 데이터 유형을 분해하는 데 도움이 될 수 있습니다.
  7. 안정적인 라이브러리와 지원 커뮤니티가 있지만 Nodejs나 Python과 같지는 않지만 충분히 괜찮은 수준입니다(인기 있는 커뮤니티는 거의 없음).
  8. 개발자 경험이 좋습니다.

Elixir의 단점

  • 소규모 커뮤니티: Elixir 프로젝트에서 문제가 발생하면 커뮤니티가 상대적으로 작기 때문에 해결책을 찾거나 질문에 대한 답을 찾기가 어려울 수 있습니다.
  • 구직 시장에서 낮은 수요: 향후에는 달라질 수 있지만, 개발자 커뮤니티의 많은 부분에서 숙련된 Elixir 개발자에 대한 수요가 적기 때문에 구직 시장에서 이들을 찾기가 어려울 수 있습니다.

    Exlixir의 코드

Elixir의 코드는 스레드보다 가볍고 저렴한 프로세스를 통해 실행되므로 수천 개 또는 수백만 개의 프로세스를 생성할 수 있습니다. Elixir에서 프로세스는 격리되어 메모리를 공유하지 않고 서로에게 메시지를 전송하여 통신합니다.
아래 코드 블록에 따라 함수를 인수로 받고 프로세스 식별자(PID)를 반환하는 spawn/1 함수를 사용하여 새 프로세스를 생성하려면 다음과 같이 하세요:

process_pid = spawn(fn ->
    IO.puts("Starting process")
end)

IO.puts("PID: #{inspect(process_pid)}") # PID: #PID<0.98.0>

한 프로세스에서 다른 프로세스로 메시지를 보내려면 수신자의 PID와 메시지를 지정하여 send/2 함수를 사용할 수 있습니다. 메시지를 받으려면 지정된 패턴과 일치하는 메시지가 올 때까지 기다리는 receive/1 함수를 사용할 수 있습니다. 메시지는 패턴과 순차적으로 매칭되므로 메시지 내용에 따라 수신할 메시지를 선택할 수 있습니다.
엘릭서에서 메시지를 보내는 방법은 다음과 같습니다:

send(process_pid, {:print_hello})

Elixir 프로세스는 동시에 실행되므로 겹치는 방식으로 실행될 수 있지만 반드시 여러 CPU 코어에서 병렬로 실행되지는 않습니다. 동시성 모델은 프로세스가 메시지를 전달하여 통신하는 액터 모델을 기반으로 합니다. 이 모델은 공유 상태를 없애고 메시지를 통해 통신하는 격리된 프로세스에 집중함으로써 동시 프로그래밍을 단순화합니다.
Elixir는 작업을 동시에 실행하기 위해 서로 다른 프로세스에서 작업을 예약하는 Task 모듈을 제공합니다. Task 모듈을 사용하여 여러 작업을 병렬로 실행하는 방법은 다음과 같습니다:

tasks = [
Task.async(fn -> IO.puts("Doing work 1") end),
Task.async(fn -> IO.puts("Doing work 2") end)
]

for task <- tasks do
 Task.await(task)
end

주어진 URL에 대한 모든 링크를 찾는 웹 크롤러를 만들어 보겠습니다.

우리가 구축하는 크롤러는 주어진 URL의 모든 링크를 가져오는 것을 목표로 주어진 URL에 대한 모든 링크를 크롤링합니다. 피해야 할 사항은 다음과 같습니다:

  • 중복 크롤링 링크 없음
  • 크롤러가 동시에 실행되며 다음과 같이 구성할 수 있습니다.
  • 크롤러의 실행 시간과 메모리 사용량 비교

이 튜토리얼에는 모든 코드가 포함되어 있지는 않지만 가장 중요한 부분을 보여드리겠습니다. 먼저 Elixir는 함수형 언어이므로 웹사이트를 재귀적으로 크롤링할 것입니다:

def run(start_url, scraper_fun, max_concurrency) do
    Stream.resource(
      fn -> {[start_url], []} end,
      fn
        {[], _found_urls} ->
          {:halt, []}
        {urls, found_urls} ->
          {new_urls, data} = crawl(urls, scraper_fun, max_concurrency)
          new_urls =
            new_urls
            |> List.flatten()
            |> Enum.uniq()
            |> Enum.reject(&diff_host?(URI.parse(start_url), &1))
            |> Enum.map(&to_string/1)
            |> Enum.reject(&Enum.member?(found_urls, &1))
          {data, {new_urls, new_urls ++ found_urls}}
      end,
      fn _ -> IO.puts("Finished crawling for '#{start_url}'.") end
    )
  end

  defp crawl(urls, scraper_fun, max_concurrency) when is_list(urls) do
    urls
    |> Task.async_stream(&crawl(&1, scraper_fun, max_concurrency),
      ordered: false,
      timeout: 15_000 * max_concurrency,
      max_concurrency: max_concurrency
    )
    |> Enum.into([], fn {_key, value} -> value end)
    # |> Enum.map(&crawl(&1, scraper_fun)) # To run without concurrency
    |> Enum.reduce({[], []}, fn {scraped_urls, scraped_data}, {acc_urls, acc_data} ->
      {scraped_urls ++ acc_urls, scraped_data ++ acc_data}
    end)
  end

따라서 크롤러는 중복 링크를 필터링하여 크롤링하고, 크롤러를 현재 도메인으로만 제한하며, 동시성으로 실행합니다. 다음은 Elixir에서 크롤러의 메모리 사용량입니다:

Rust

Rust는 정적이고 강력한 타이핑 및 컴파일 언어입니다. 그 결과 커뮤니티가 매우 빠르게 성장했습니다.

  • 구조는 완전히 객체 지향이 아닌 객체 지향 위에 존재합니다. Java의 인터페이스와 같은 특성이 존재합니다. 상속은 없지만 a/is 모델을 사용하여 상속을 지원합니다.
  • 컴파일 시 가비지 컬렉션으로 인해 메모리 안전성을 제공하고 성능이 향상됩니다.
  • Rust에는 가비지 컬렉션이 없지만 차용 검사기가 있습니다.
  • 멀티 스레드가 스레드 안전하지 않은 경우 컴파일 시 유형 오류가 발생합니다.

    Rust 의 장점

  1. 강력한 타입 시스템 언어
  2. 동시성이 가능하며 행위자 모델을 따릅니다.
  3. 컴파일러는 훌륭하고 사전에 상황을 파악합니다.
  4. 배포를 위해 출력에 정적 바이너리를 생성합니다.
  5. CPU 집약적인 애플리케이션과 같이 효율성성능이 필요한 시스템을 위한 시스템입니다.
  6. Async io는 Tokio 모듈과 함께 안정화되고 있습니다.
  7. 사용하지 않는 기능에는 비용을 지불하지 않습니다.
  8. 추상화 비용이 들지 않습니다.

Rust의 단점

  1. 학습 곡선이 더 높고, 코드 작성이 복잡합니다.
  2. 성장이 필요한 라이브러리와 커뮤니티
  3. 느린 컴파일러
  4. 100% 자체 지원 컴파일러는 아니며 LLVM 컴파일러에서 구현됩니다.

    Elixir와 Rust 비교

Rust는 안정적이고 효율적인 소프트웨어를 구축하는 프로그래밍 언어입니다. Rust의 초점 중 하나는 프로그램이 메모리 안전성을 보장하는 것입니다. Rust는 Elixir나 Go처럼 스레드나 코루틴을 지원하지는 않지만, 런타임을 구현하기 위한 하위 수준의 빌딩 블록을 제공합니다.
오늘은 동시 런타임을 빌드하지는 않겠지만, Rust에서 가장 많이 사용되는 비동기 런타임 중 하나를 사용하겠습니다: Tokio를 사용하겠습니다. 비동기와 동시성은 다르지만, Tokio 라이브러리의 일부를 사용하여 동시 런타임처럼 작동하도록 만들 수 있습니다.

다음은 Tokio 상자를 사용하여 동시 프로그램을 만드는 방법입니다:

  • 프로세스: Tokio에서 프로세스를 실행하려면 tokio::spawn을 사용할 수 있습니다. Tokio는 현재 스레드에서 실행할지 다른 스레드에서 실행할지 처리합니다. 또한 하나의 스레드에서만 실행하거나 여러 스레드에서 실행하도록 구성할 수도 있습니다.
  • 메시지: 프로세스 간 통신을 위해, Gol에서 채널처럼 작동하는 Tokio 메시지 채널 모듈 tokio::sync::mpsc를 사용할 수 있습니다.
  • 공유 상태: 공유 상태는 위험한 작업이지만 다행히도 Rust는 우리가 올바르게 수행하고 있는지 확인하는 데 도움을 줄 것입니다. Tokio를 사용하면 tokio::sync::Mutex를 사용하여 공유 상태에 안전하게 액세스할 수 있습니다.

다음은 Tokio를 사용한 간단한 비동기 작업의 예입니다:

use tokio::time::{sleep, Duration};

#[tokio::main] // 새 Tokio 런타임을 시작합니다.
async fn main() {
    let task = tokio::spawn(async {
    // 일부 작업을 시뮬레이션합니다.
    sleep(Duration::from_millis(50)).await;
    println!("작업이 완료되었습니다");
});

// 작업이 완료될 때까지 기다림
task.await.unwrap();
}

이 예제에서 tokio::spawn은 Tokio 런타임에서 새 작업을 시작하고, tokio::time::sleep은 일정 시간 후에 완료되는 퓨처를 생성합니다. await 키워드는 이러한 퓨처가 완료될 때까지 기다리는 데 사용되지만, 스레드를 차단하지 않고 다른 작업을 실행할 수 있도록 허용합니다.
동시성 프로그래밍에서는 여러 작업 간에 상태를 공유해야 하는 경우가 많습니다. 이를 위해 일반적으로 메시지 전달을 사용하는 것이 더 안전하며 코드를 더 쉽게 추론할 수 있기 때문에 선호되는 방법입니다. 하지만 상태 공유가 필요한 경우도 있습니다.
작업 간에 상태를 공유하려면, std::sync의 일부인 Arc를 제외한 tokio::sync 모듈의 일부인 Arc, RwLock, Mutex 및 Semaphore와 같은 유형을 사용할 수 있습니다. 이러한 유형을 사용하면 여러 작업에서 변경 가능한 상태를 안전하게 공유할 수 있습니다:

  1. Arc(원자 참조 카운트): Arc를 사용하면 작업이나 스레드에서 공유할 모든 타입이나 구조체를 래핑할 수 있습니다. 예를 들어 Arc는 User를 힙에 저장합니다. 그런 다음 해당 인스턴스를 복제하여 힙에서 User를 참조하는 새 Arc 인스턴스를 만들 수 있습니다.
  2. Mutex(상호 제외): 한 번에 최대 하나의 스레드만 일부 데이터에 액세스하거나 변경할 수 있도록 합니다.
  3. RwLock(읽기-쓰기 잠금): 쓰기자가 잠그지 않는 한, 여러 작업 또는 스레드가 데이터를 읽을 수 있도록 합니다.
  4. 세마포어: 세마포어는 동시성에서와 같이 워커를 구현하는 데 사용할 수 있습니다. 예를 들어, 데이터가 1,000개인데 1,000개의 워커를 스폰하고 싶지 않은 경우 세마포어를 사용하면 스폰할 워커 수를 제한할 수 있습니다. 세마포어는 워커가 많다고 해서 프로젝트가 더 빨리 작동하는 것은 아니기 때문에 유용합니다.

ArcMutex의 예제

use std::sync::Arc;
use tokio::sync::Mutex;
use tokio::spawn;

#[tokio::main]
async fn main() {
    let data = Arc::new(Mutex::new(0));

    let data1 = Arc::clone(&data);
    let task1 = spawn(async move {
        let mut lock = data1.lock().await;
        *lock += 1;
    });

    let data2 = Arc::clone(&data);
    let task2 = spawn(async move {
        let mut lock = data2.lock().await;
        *lock += 1;
    });

    task1.await.unwrap();
    task2.await.unwrap();

    assert_eq!(*data.lock().await, 2);
}

이 예제에서는 일부 데이터를 저장하기 위해 Arc<Mutex<i32>>가 생성됩니다. 그런 다음 각각 데이터를 증가시키는 두 개의 작업이 생성됩니다. Mutex는 한 번에 하나의 작업만 데이터에 액세스할 수 있도록 보장하기 때문에 데이터 경합이 발생하지 않으며 최종 결과는 예상대로 나타납니다.
한 가지 주목할 점은 Tokio에서 제공하는 잠금(예: Mutex 및 RwLock)은 비동기식이며, 경합이 발생할 때 전체 스레드를 차단하지 않는다는 점입니다. 대신 현재 작업만 차단하여 다른 작업이 동일한 스레드에서 계속 실행될 수 있도록 합니다.
이제 Rust와 Tokio를 사용해 간단한 웹 크롤러를 만들어 보겠습니다. 먼저 크롤러를 실행할 특성을 정의해 보겠습니다:

use crate::error::Error;
use async_trait::async_trait;

pub mod web;

#[async_trait]
pub trait Spider: Send + Sync {
    type Item;

    fn name(&self) -> String;
    fn start_url(&self) -> String;
    async fn scrape(&self, url: String) -> Result<Vec<Self::Item>, Error>;
}

Spider를 구현합니다.

impl WebSpider {
    pub fn new(start_url: String, worker: usize) -> Self {
        let http_timeout = Duration::from_secs(4);

        let http_client = Client::builder()
            .timeout(http_timeout)
            .user_agent(
                "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0",
            )
            .pool_idle_timeout(http_timeout)
            .pool_max_idle_per_host(worker)
            .build()
            .expect("WebSpider: Building HTTP client");

        WebSpider {
            http_client,
            start_url,
        }
    }
}

#[async_trait]
impl super::Spider for WebSpider {
    type Item = String;

    fn name(&self) -> String {
        String::from("WebSpider")
    }

    fn start_url(&self) -> String {
        self.start_url.to_string()
    }

    async fn scrape(&self, url: String) -> Result<Vec<String>, Error> {
        println!("Scraping url: {}", &url);
        let raw_url = Url::parse(&url)?;
        let host = raw_url.scheme().to_owned() + "://" + raw_url.host_str().unwrap();

        let start = Instant::now();
        let body_content = self.http_client.get(&url).send().await?.text().await?;

        let seconds = start.elapsed().as_secs_f64();
        if seconds > 3.0 {
            println!(
                "Parsing res body: {} in \x1B[32m{:.2}s\x1B[0m",
                &url, seconds
            );
        }
        let parser = Html::parse_document(body_content.as_str());
        let selector = Selector::parse("a[href]").unwrap();

        let links: Vec<String> = parser
            .select(&selector)
            .filter_map(|element| element.value().attr("href"))
            .filter_map(|href| {
                let parsed_link = raw_url.join(href);
                match parsed_link {
                    Ok(link) => {
                        let mut absolute_link = link.clone();
                        absolute_link.set_fragment(None);
                        absolute_link.set_query(None);
                        if absolute_link.to_string().starts_with(&host) {
                            Some(absolute_link.to_string())
                        } else {
                            None
                        }
                    }
                    Err(_) => None,
                }
            })
            .collect();

        Ok(links)
    }
}

실행

$ ./crawler_rust --work 10 --url https://react.dev

Elixir, Rust 성능의 벤치마크

지금까지 웹 크롤러를 Rust, Elixir에 구현했습니다. 이제 각각의 성능을 확인하기 위해 벤치마크를 실행해 보겠습니다. 작업을 완료하는 데 걸리는 시간과 테스트 중에 사용되는 메모리 양이라는 두 가지를 측정하고자 합니다. 크롤러는 https://react.dev 도메인의 모든 링크를 가져옵니다. 127개의 링크를 가져올 것으로 예상됩니다.

시간 측정 벤치마크

10개의 동시성 작업자를 사용하여 각 웹 크롤러가 링크를 가져오는 데 걸린 시간은 다음과 같습니다:

  • Elixir: 7.6초
  • Rust: 7.2초

HTTP 요청을 수행하는 데 시간이 걸립니다. 이는 각 연결에서 SSL/TLS 핸드셰이크가 생성되기 때문입니다. 기본 Go HTTP 라이브러리는 이 두 가지를 동시에 실행하기 때문에 이 시나리오에서 Go의 성능이 가장 우수합니다.
Elixir와 Rust를 모두 최적화하여 TLS/TLS 핸드셰이크를 한 번만 생성하고 워커 전체에서 사용하도록 할 수 있습니다:

TLS/SSL 핸드셰이크를 한 번만 만들고 작업자 전체에서 사용해야 합니다:

Web Crawler Performance With Elixir, Rust, And Go

다음은 50명의 워커의 결과입니다. 더 많은 동시성 워커를 사용한다고 해서 크롤러의 속도가 향상되는 것은 아니며, 네트워크 지연에 의해 제한되기 때문입니다

Web Crawler Result With 50 Workers

다음은 동시성(Concurency)를 조외한 밴치마크 입니다.

Web Crawler Without Concurrency

메모리 사용 밴치마크

메모리 사용량과 관련해서는 흥미로운 점이 있습니다:

  1. Elixir: 295MB
  2. Rust: 50MB

메모리 사용량에서는 Rust가 더 나은 성능을 보이는 반면, Elixir의 성능은 비슷하게 떨어지는 것을 알 수 있습니다:

Memory Usage Comparison Across Elixir, Rust, And Go

결론

이 튜토리얼에서는 Elixir, Rust로 동시 프로그램을 실행하는 방법을 배웠습니다. 각 언어마다 동시성을 처리하는 방법에 대한 고유한 아이디어가 있지만, Go의 기본 라이브러리는 동시성을 훌륭하게 지원하지만 메모리를 많이 사용한다는 것을 알 수 있었습니다. 이는 힙에 메모리를 할당하고 가비지 컬렉션을 사용하여 메모리를 정리하기 때문입니다. 이로 인해 GC가 정리를 수행할 때 메모리 사용량이 많아지고 일부 결함이 발생할 수 있습니다.

Elixir에서는 프로세스를 스폰하고 프로세스 간 공유 상태를 관리하는 것이 얼마나 쉬운지 확인했습니다. 하지만 동시에 Elixir에서 동시 프로그램의 성능을 향상시키는 것은 쉽지 않습니다. Elixir는 가비지 컬렉션을 사용해 메모리를 관리하기 때문에 Go과 비슷한 성능을 발휘합니다.

Rust로 동시 프로그램을 작성하는 것은 쉽지 않지만, Rust의 소유권 및 타입 시스템 덕분에 메모리 안전성을 확보할 수 있다는 이점이 있습니다. Rust는 가비지 컬렉션을 사용하지 않기 때문에 가비지 컬렉션을 사용하는 골랑이나 엘릭서보다 메모리 사용 측면에서 더 효율적입니다.

이 글의 전체 소스코드는 여기에서 확인할 수 있습니다(https://github.com/ahmadrosid/elixir-go-rust). 궁금한 점이나 공유하고 싶은 추가 정보가 있으면 언제든지 알려주세요.

반응형

'개발' 카테고리의 다른 글

2024년 프로그래밍 랭킹  (1) 2024.01.02
gitlab 설치(apache --proxy--> gitlab)  (0) 2023.12.27
vscode dev containers  (1) 2023.12.22
svn to git 마이그레이션 (맥)  (0) 2023.12.13
intelliJ gitlab plugin(merge request) 연결 오류  (0) 2023.08.23

여기에서 나온 정보를 학습한 내용입니다.

Visual Studio Code Dev Containers 확장을 사용하여 컨테이너 기반 개발 환경을 가져오고 만들고 구성합니다.

이 모듈을 마치면 다음을 수행할 수 있습니다.

  • Visual Studio Code Dev Containers 확장을 설치합니다.
  • Docker에서 프로젝트를 로드 및 연결합니다.
  • 로컬 컴퓨터에서 컨테이너의 포트에 액세스합니다.
  • 컨테이너에서 작업하는 동안 설정을 사용자 지정합니다.
  • 컨테이너 환경에 소프트웨어를 추가합니다.

Visual Studio Code 개발 컨테이너 확장을 사용하여 Docker 컨테이너 내에서 개발할 수 있습니다.

다양한 언어와 런타임 환경을 중심으로 소프트웨어 컨설팅을 수행하는 에이전시에서 근무한다고 가정합니다. 모든 개발자가 Visual Studio Code를 사용하고 있습니다. 에이전시에서는 수십 개의 프로젝트가 진행 중이며, 각각 고유한 구성 및 런타임 요구 사항이 있습니다. 에이전시 개발자는 먼저 머신을 설정하거나 구성하지 않고도 모든 프로젝트에서 작업할 수 있어야 합니다.

이 모듈에서는 기존 프로젝트에 구성 파일을 추가합니다. 이러한 파일은 프로젝트가 “작동하는” 환경을 빌드하는 방법을 Visual Studio Code에 알려줍니다. Dev Container 구성을 사용하여 런타임 환경을 구성합니다. 또한 Docker와 Visual Studio Code가 있는 모든 사용자가 사용할 수 있는 개발 환경의 구성을 자동화합니다.

필수 구성 요소

  • 소프트웨어 개발에 대한 기본 지식(예: 코드 실행 또는 새 언어 설치의 의미)
  • Docker 및 기본 Docker 지식:
  • Git 및 Git 리포지토리에 대한 기본 지식

연습 - 프로제트 준비

샘플 리포지토리 복제

  1. Docker Desktop이 머신에 설치되어 실행 중인지 확인합니다.
  2. 샘플 리포지토리의 URL을 클립보드에 복사합니다.
    https://github.com/MicrosoftDocs/mslearn-python-products
  3. Visual Studio Code의 새 인스턴스를 엽니다.
  4. 사이드바에서 리포지토리 복제 단추를 선택하거나 F1 키를 눌러 명령 팔레트를 열고 Git: Clone을 검색합니다.
  5. 클립보드에서 URL을 붙여넣습니다.
  6. 디스크에서 프로젝트를 복제할 수 있는 위치를 선택합니다.
  7. Visual Studio Code의 알림에서 열기를 선택합니다.
  8. 작성자를 신뢰할 수 있는지 묻는 팝업이 표시되면 예, 작성자를 신뢰합니다를 선택합니다.

dev container

이 확장의 작동 방식

Dev Containers extension 을 사용하면 특정 기술 스택 또는 종속성이 이미 설정된 개발 컨테이너를 가져와 프로젝트를 열고, 로컬 머신에 아무것도 다운로드하지 않고도 코드가 제대로 작동하는지 확인할 수 있습니다. Dev Containers 실행 중인 컨테이너에 Visual Studio Code를 연결하는 방식으로 작동합니다. 작업 영역 파일은 로컬 파일 시스템에서 탑재되거나 컨테이너에 복사 또는 복제됩니다.

Visual Studio Code Extension은 컨테이너 안에 설치되고 내부에서 실행됩니다. 여기서 확장은 도구, 플랫폼, 파일 시스템에 대한 모든 권한을 갖습니다. 개발자에게 이 환경은 Visual Studio Code에서 정상적으로 프로젝트를 연 것과 같습니다.

다른 컨테이너에 연결하는 것만으로 전체 개발 환경을 원활하게 전환할 수 있습니다. 이 확장은 ‘.devcontainer’ 라는 폴더에 포함된 몇 가지 구성 파일을 기반으로 모든 설정을 처리합니다.

기존 프로젝트에 개발 컨테이너 추가

    1. F1 키를 눌러 명령 팔레트를 엽니다.
    2. Dev Container: new Dev Container를 누릅니다
    3. python 3를 선택합니다
    4. default 버전을 선택합니다.

[!info] 저는 microsoft 기본가이인 3.11 버전으로 하면 container가 생성이 안되는 일이 있었습니다. 그래서, default 버전으로 변경했습니다.

    1.  
    2. container가 실행되는 것을 확인할 수 있습니다.
    3. 명령줄에서 close remote connection을 한 후에 다시 프로젝트 파일을 열면 현재 로컬 폴더를 포함하는 Folder contians a Dev Container configuration file. Reopen folder to develop in a contain라는 알림창을 확인할 수 있습니다.

만약 이게 안 나온다면, 명령 줄에서 dev container: contain

  1.  

python 프로젝트 실행

종속성 설치

pip3 install --user -r requirements.txt
pip3 install flask

실행

$ python3 app.py

...
[notice] A new release of pip is available: 23.2.1 -> 23.3.2
[notice] To update, run: pip install --upgrade pip
vscode ➜ /workspaces/mslearn-python-products (main) $ python3 app.py 
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 717-269-910

[!info] flask 오류로 실행이 안된다면, pip3 install --upgrade flask 를 실행해서 업그레이드 후 진행 합니다.

실행이 되는 것을 확인했다.

project 편집기 설정 사용자 지정

에이전시의 프로젝트 중 하나에 대해 개발 컨테이너를 설정했습니다. 이제 이 프로젝트는 Docker 및 Dev Containers 확장이 있는 사용자를 위해 “저절로 작동”합니다. 여전히 종속성을 설치해야 합니다. 잘 모르는 몇 가지 Visual Studio Code 확장이 필요할 수도 있습니다.

다행히 devcontainer.json 파일을 사용하여 모든 프로젝트 설정을 완전하게 사용자 지정하고 자동화할 수 있습니다.

devcontainer.json 자세히 보기

Products Dashboard 프로젝트의 .devcontainer/devcontainer.json 파일에 있는 기본 옵션을 살펴보겠습니다. 한 번에 모두 확인하려면 다소 길기 때문에 섹션별로 살펴보겠습니다.

빌드 구성

image 속성은 컨테이너 이미지로 알려져 있는 항목을 기준으로 컨테이너를 만드는 방법을 정의합니다.

JSON

"image": "mcr.microsoft.com/devcontainers/python:0-3.11"
},

이 이미지는 devcontainers/images 리포지토리에서 호스트되며 여기서 자세히 확인할 수 있습니다.

Dockerfile 또는 Docker Compose 파일로 알려진 파일을 사용하여 설정을 구성할 수도 있습니다. 이러한 파일은 .devcontainer 폴더에 저장되며 추가 소프트웨어 설치와 같은 특정 설치 요구 사항을 추가로 구성할 수 있습니다. dev container 설명서에서 자세히 알아볼 수 있습니다.

기능

Dev Containers Extension은 자체 포함되고 공유 가능한 설치 코드 단위 및 개발 컨테이너 구성입니다. 이름은 그 중 하나를 참조하면 사용자 또는 협력자가 사용할 수 있도록 개발 컨테이너에 도구, 런타임 또는 라이브러리 "기능"을 빠르고 쉽게 추가할 수 있다는 아이디어에서 비롯되었습니다.

VS Code 명령 Dev Containers: 개발 컨테이너 구성 파일 추가를 사용하는 경우 Git 또는 Azure CLI 설치와 같은 기존 개발 컨테이너 구성을 사용자 지정하는 스크립트 목록이 표시됩니다.

프로젝트 설정

파일의 뒤쪽 섹션에서는 프로젝트 구성을 직접 처리합니다.

customizations는 VS Code 및 GitHub Codespaces와 같은 개발 컨테이너를 지원하는 제품에 대한 제품별 속성을 설정합니다.

예를 들어 컴퓨터별 설정을 컨테이너에 복사하도록 vscode.settings를 설정할 수 있습니다. 고유한 Visual Studio Code 설정에 해당 설정이 있을 수도 있습니다. settings에 프로젝트를 추가하여 이러한 프로젝트를 여는 누구든지 이러한 특정 VS Code 설정을 가져오도록 할 수 있습니다.

이 Python 컨테이너에서는 기본 이미지 mcr.microsoft.com/devcontainers/python:0-3.11에서 이러한 설정을 볼 수 있습니다. 이러한 설정은 사용자에게 향상된 Python 편집 환경을 제공합니다.

  • extensions 배열을 사용하면 컨테이너에 연결할 때 Visual Studio Code에 설치할 Visual Studio Code 확장을 지정할 수 있습니다. Dev Containers를 사용하는 경우 일반적인 Visual Studio Code 설정과 이미 설치한 모든 확장은 표시되지 않습니다. 여기에서 확장은 ID로 지정됩니다.

postCreateCommand

postCreateCommand 속성을 사용하면 컨테이너가 생성된 후 원하는 명령을 실행할 수 있습니다. 첫 번째 연습에서는 종속성을 설치하기 위해 pip3 명령을 실행해야 했습니다. 하지만 그 방법을 어떻게 알 수 있나요? 방법을 모를 수도 있습니다. 자동으로 수행되어 다른 사용자가 걱정하지 않아도 되도록 여기에서 구성할 수 있습니다.

다음 연습에서는 프로젝트의 여러 부분을 자동화하여 다른 개발자가 즉시 성공할 수 있도록 devcontainer.json 파일을 수정합니다.

vscode extension 자동 설치

remote 로 열려 있는 vscode에서 extension을 설치하고, 설치한 extension을 마우스 우클릭을 누른 다음에, Add to devcontainer.json을 누른다.

~/.devcontain/devcontainer.json 에 다음 값이 추가된 것을 확인할 수 있다.

{
    "name": "Python 3",
    // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
    "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye",
    "customizations": {
        "vscode": {
            "extensions": [
                "wholroyd.jinja"
            ]
        }
    }
}

python 종속성 자동 설치

flaks 버전이 낮기 때문에, devcontainer.json파일을 다음과 같이 수정한다.

# Flask Framework
Flask==3.0.0

devcontainer.json에 아래 내용을 추가 한다.

...
    "postCreateCommand": "pip3 install --user -r requirements.txt"

rebuild 을 다시 선택한다.

이제 컨테이너가 사용자 지정되고 에이전시에 대해 자동화됩니다. Dev Containers를 사용하여 이 프로젝트를 여는 개발자는 즉시 프로젝트를 실행하고 코드 작성을 시작할 수 있습니다.

# 기존 컨테이너에 소프트웨어 추가

사용자 지정 컨테이너는 에이전시에 적합합니다. Dev Containers의 기능을 사용하여 미리 구성된 개발 컨테이너를 추가할 수 있었으며, 지금까지의 연습을 통해 devcontainer.json 파일을 활용해 환경을 사용자 지정할 수 있었습니다. 그러나 해당 이미지나 미리 구성된 개발 컨테이너에서 사용할 수 있는 것 외에 다른 소프트웨어를 추가하려면 어떻게 해야 할까요?

위 페이지에서 추가할 수 있는 기능들에 대해서 확인 할수 있습니다

devcontainer.json파일에 아래와 같이 추가 합니다.

"features": {
    "ghcr.io/devcontainers/features/node:1": {
        "version": "18"
    }
}

rebuild 를 실행하면 node가 추가 설치 된 것을 확인할 수 있습니다

$ node -v

rust function 추가하기

"features": {
    "ghcr.io/devcontainers/features/rust:1": {
        "version": "1.70.0"
    }
}
반응형

#초보자 #생산력

본 글은 아래 3가지 내용을 담고 있습니다.

  • 기술 조언에 대한 중요한 맥락과 동기
  • 기술 조언
  • 추천 읽을거리 시작하기에 좋은 고품질 책과 블로그에 대한 링크

주니어를 위한 일반적인 조언

1. 코드가 포인트가 아니다

개발자로서 우리는 코드 작성을 좋아합니다. 우리 대부분은 명확하고 멋진 작업을 원하고, 다른 세계에 관심을 기울이지 않고, 풀 수 있는 재미있는 기술 퍼즐입니다.

올바른 문제를 해결하고 있는지 확인하기 위해 합리적인 노력을 해야 합니다. Peter Drucker의 말을 인용하자면: 전혀 하지 말아야 할 일을 효율적으로 하는 것만큼 쓸모없는 것은 없습니다. 일반적으로개발 초기에 실제 사용자에게 지속적으로 피드백을 수집합니다.

무엇이 중요한 문제인가?
올바른 일을 하는 것과 일을 제대로 하는 것 사이에 놓인 효과와 효율성의 혼란에서 모든 문제는 비롯된다. 확실한 것은 하지 않아도 될 일을 효율적으로 하는 것 만큼 쓸모없는 일은 없다는 것이다.

  • 피터 드러커

소프트워어 개발은 비용이 많이 들고, 실제 프로젝트의 대부분의 유지보수 비용으로 사용됩니다. 이를 시용자/비즈니스 결과라는 목표와 결합하게 되면, 좋은 코드는 대부분 없는 경우가 많습니다
소프트웨어 개발은 유지 관리 비용이 들어가는 실제 프로젝트의 노력의 대부분과 함께 비용이 많이 듭니다. 이것을 사용자/비즈니스 결과라는 목표와 결합하면 최상의 코드는 코드가 없는 경우가 많습니다. Bill Gates의 말을 인용하자면 "코드로 프로그래밍 진행 상황을 측정하는 것은 항공기 제작 진행 상황을 무게로 측정하는 것과 같습니다."

2. 소프트웨어 설계 문제

개발 경력의 첫 5년 동안 저는 소프트웨어 설계가 소프트웨어 설계자나 특별한 역할을 맡은 다른 사람들을 위한 것이라고 생각했습니다. 나는 "일을 끝내는 것"에 집중했고, 소프트웨어 디자인과 테스트 작성과 같은 관행이 방해가 된다고 생각했습니다. 내 코드는 작동했고, 많은 일을 하고 있었습니다. 많은 일을 하고 있다고 생각했습니다.

그런 다음 Robert C. Martin 의 Clean Code 를 읽었습니다 . 이 책은 소프트웨어 디자인에 관심을 갖도록 동기를 부여하고, 예제와 많은 기술적 발견을 포함하고 있습니다. 가장 개념적인 테이크어웨이는 "빨리 가는 유일한 길은 잘 가는 것"입니다. 즉, 엉망으로 만들면 속도가 느려집니다.

잘 설계된 깨끗한 코드를 작성하는 방법을 배우는 것은 물론 시간과 노력이 필요합니다. 그리고 시작하면 속도가 느려지고 실수를 하게 됩니다. 단순한 것은 쉽지 않습니다.

3. 모범 사례 사용

테스트 작성은 유익한 경향이 있습니다. 예외가 있지만 대부분의 경우 자동화된 테스트를 작성하는 것이 좋습니다. 테스트 작성은 모범 사례의 한 예입니다.

테스트 작성이 처음이라면 모범 사례를 따르고 모든 것에 대한 테스트를 작성하십시오. 시작할 때 맹목적으로 모범 사례를 따르는 것이 자신의 미숙한 판단을 따르는 것보다 낫습니다 . 시간이 지남에 따라 테스트를 효과적으로 작성하는 방법을 배우고, 테스트를 작성하는 것이 가치가 없는 상황과 엉망인 상황을 구분할 수 있게 될 것입니다. 또한 디버깅 세션의 감소와 테스트에서 활성화된 걱정 없는 리팩토링을 경험함으로써 테스트가 보다 본능적인 수준으로 가져오는 가치를 이해하기 시작할 것입니다. 판단력을 개발한 후에는 모범 사례를 초월할 수 있습니다 .

이 조언은 당신이 2학년인 모든 영역의 모범 사례에 적용됩니다. 자동화된 테스트는 하나의 예일 뿐입니다.

한 가지 큰 문제는 합리적인 모범 사례와 무의미하거나 심지어는 역효과를 낳는 것과 구별하기가 쉽지 않다는 것입니다. 이것은 대부분의 기존 코드가 엉망이고 "숙련된" 및 "선배" 개발자를 포함하여 대부분의 개발자가 소프트웨어 설계 기본 사항을 모르기 때문에 더 복잡해집니다. 이것은 좋은 멘토를 갖는 것을 매우 가치있게 만듭니다. 그것을 제외하고 내 자신의 경험에 기반한 조언 중 하나는 귀하의 언어 또는 프레임워크 커뮤니티에 특정한 모범 사례를 주의하라는 것입니다. 수십 년 동안 주변에 있었던 상록 조언을 찾으십시오.

주니어를 위한 기술 조언

1. 테스트 코드 작성

자동화된 테스트를 작성합니다. TDD( Test Driven Development ) 를 통해 코드보다 먼저 테스트를 작성할 수 있습니다. 이렇게 하면 반복 가능한 방식으로 코드가 올바른지 쉽게 확인할 수 있으므로 수동으로 재시도하거나 세션을 디버깅하지 않아도 됩니다.

디버그를 실행합니다.

아마도 중요한 것은 테스트를 통해 코드를 리팩토링할 수 있는 안전망을 얻을 수 있다는 것입니다. 그리고 코드를 깨끗하게 유지하려면, 지속적인 리팩토링이 필요합니다. 신뢰할 수 있는 테스트 모음이 없으면 코드가 썩을 가능성이 훨씬 더 높아집니다.

코드 재사용을 위해 상속을 사용하거나 정적 함수를 사용할 때와 같이 코드 디자인이 좋지 않으면 테스트 작성이 어렵습니다. 반면에 전역 종속성이 없는 SOLID 클래스가 있다면, 멋진 테스트를 작성하는 것이 그렇게 어렵지 않습니다.

잘못 작성된 테스트는 속도를 늦추기 때문에 테스트 디자인이 중요합니다. 테스트 중인 코드의 구현 세부 사항이나 시스템 구조에 테스트를 바인딩하지 마십시오. Mocks의 남용을 피하고 더 나은 Test Doubles를 작성 하십시오 .

2. 코드 재사용을 위해 상속을 사용하지 마십시오

이것은 "모범 사례 사용" 섹션을 염두에 두는 모범 사례 중 하나입니다. 내 조언: 시작할 때 코드 재사용을 위해 상속을 전혀 사용하지 마십시오. 그것은 거의 올바른 호출이 아니며 많은 피해를 줄 수 있습니다. 상속보다 구성을 선호 합니다.

3. 객체 지향 코드 작성

STUPID 가 아닌 SOLID 코드를 작성 하십시오. 이러한 원칙과 반패턴을 이해하는 데는 많은 가치가 있습니다.

실제로 개체를 만듭니다. 정적 메서드만 있는 클래스는 OO가 아닙니다. 정적 코드를 완전히 사용하지 마십시오.

참조: SOLID에 대한 나의 방어 .

4. 기능 코드 작성

( 함수형 프로그래밍 은 구조 프로그래밍 과 혼동되어서는 안 됩니다 .)

이 요점은 기능적 언어로 완전히 전환하는 것에 관한 것이 아닙니다. OO 언어에서 기능적 스타일을 사용하면 이점을 얻을 수 있습니다. 특히 변경 가능한 상태를 최소화 하고 함수에서 한 가지 작업을 수행합니다.

5. 정보에 입각한 복제 사용

많은 양의 코드 덩어리를 여러 위치에 복사하여 붙여넣는 것은 현명하지 않습니다. 자존심이 강한 개발자라면 누구나 곧 이것을 배우고 DRY( Don't Repeat Yourself ) 형식을 따르기 시작합니다. 불행히도, 의도적으로 DRY를 추구하면 과도한 엔지니어링과 우발적인 복잡성으로 이어지는 경우가 많습니다. DRY의 대응물인 WET(Write Everything Twice)가 여기에 해당합니다. WET의 기본 개념은 중복이 세 번째 발생하는 경우에만 중복을 제거하는 것입니다.

6. 유형, 이름 및 설명

자체 문서화 코드를 작성하고 주석을 피하십시오.

설명을 작성할 때마다 찡그린 표정을 짓고, 표현력의 실패를 느껴야 합니다. -- 로버트 C. 마틴

코멘트는 거짓말을 할 수 있기 때문에 위험합니다. 코드는 주석이 업데이트되지 않고 변경될 수 있습니다. 주석 바로 아래에 새 코드를 추가할 수 있습니다. 댓글이 처음부터 잘못되었거나 정확하지 않을 수 있습니다. 이런 일이 발생하면 댓글은 쓸모없게 될 뿐만 아니라 오해의 소지가 있습니다.

자체 문서화 코드를 작성하려면:

  • 기능에서 한 가지 작업 수행
    • 함수에서 한 가지 작업을 수행하여 명확한 이름을 지정할 수 있습니다.
    • 주석을 추가하여 함수의 다른 섹션이 수행하는 작업을 설명할 필요가 있을까요? 대신, 각 섹션을 고유한 이름의 함수로 추출하십시오.
    • "추출할 때까지 추출": 의미 있는 기능을 추출할 수 있다면 아마도 그렇게 해야 할 것입니다. 작은 기능을 두려워하지 마십시오.
    • 명령 쿼리 분리
    • 클래스 에 대한 단일 책임 원칙 (SOLID의 S)과 유사
  • 상태 최소화
  • 유형을 사용하십시오 . 코드를 실행하는 테스트 스위트와 결합하여 진실을 말하는 유형에 의존할 수 있습니다.
  • 혼합 유형을 피하십시오 . 정수, 부울 또는 문자열이 될 수 있는 매개변수 또는 반환 값을 피하십시오. 이것은 한 가지만 수행하는 집중 함수를 작성하는 경우 자연스럽게 발생합니다.
  • 테스트 작성 . 잘 작성되고 포괄적인 테스트 스위트는 프로덕션 코드가 어떻게 사용되며 어떻게 작동하는지 보여줍니다.

Robert C. Martin의 Clean Code 에는 이름 지정 및 주석에 대한 몇 가지 좋은 경험 법칙이 있습니다.

주니어 추천도서

서적

반응형

Tour of RUST #1

유용한 학습 사이트

Cargo project 만들기

바이너리 파일을 생성하는 프로젝트

$ cargo new [프로젝트명]
# Cargo.toml, main.rs 생성

라이브러리 파일을 생성하는 프로젝트

$ cargo new [프로젝트명] -lib
# Cargo.toml, lib.rs 가 생성

유용한 도구

rustfmt

  • Rust 팀에서 개발, 관ㄹㅣ하고 있는 공식 포맷터(Formatter)
  • 공식 스타일 가이드라인을 참고해서 자동으로 코드 스타일을 수정
cargo fmt

clippy

  • Rust 팀에서 개발, 간리하고 있는 코드 린터 (Linter)
  • 현재 코드의 문제점을 파악하고, 자동으로 수정할 수 있다.
cargo clippy

변수

let 키워드 사용

변수의 자료형을 대부분 유추할 수 있다.

변수 숨김(Variable Shadowing)을 지원

변수의 이름은 언제나 snake_case형태로 짓는다.

fn main() {
    let x = 13;
    println!("{}", x);

    let x: f64 = 3.14159;
    println!("{}", x);

    let x;
    x = 0;
    println!("{}", x);
}

Rust에서 변수는 기본적으로 변경 불가(Immutable) 타입이다.

변경 가능(Mutable)한 값을 원한다면 mut키쿼드로 표시해줘야 한다.

fn main() {
    let mux x = 42;
    println!("{}", x);
    x = 13;
    println!("{}", x);
}

2. 기본 데이터 구조

기본 자료형

부울값: bool

부호가 없는 정수형: 양의 정수를 나타내는 u8, u16,u32, u64, u128

부호가 있는 정수형 - 양/음의 정수를 나타내는 i8, i16, i32, i64, i128

포인터 사이즈 정수: 메모리에 있는 값들의 인덱스와 크리를 나타내는 usize, isize

부동소수점: f32, f64

튜플(tuple): stack에 있는 값들의 고정된 순서를 전달하기 위한 (value, value, …)

배열(array): 컴파일 타임에 정해진 길이를 갖는 유사한 원소들의 모음(Collection)인 [value, value, …]

슬라이스(slice): 런타임에 길이가 정해지는 유사한 원소들의 collection

str(문자열 slice): 런타임에 길이가 정해지는 텍스트

자료형 변환을 할 때는 as 키워드를 사용한다. ( Rust에서는 숫자형 자료형을 쓸 때 명시적으로 사용해야 한다.)

fn main{
    let a = 13u8;
    let b = 7u32;
    let c = a as u32 + b;
    println!("{}", c);

    let t = true;
    println!("{}", t as u8);
}

상수

상수는 변수와 달리 반드시 명시적으로 자료형을 지정해야 한다.

상수의 이름은 언제나 SCREAMING_SNAKE_CASE형태로 짓는다.

const PI: f32 = 3.14159;

fn main() {
    println!(
        PI
    );
}

배열

고정된 길이로 된 모든 같은 자료형의 자료를 갖는 Collection

[T; N]으로 표현한다.

  • T는 원소의 자료형
  • n은 컴파일 타임에 주어지는 고정된 길이

각각의 원소는 [x] 연산자로 가져올 수 있다

fn main() {
    let nums: [i32; 3] = [1,2,3];
    println!("{:?}", nums);
    println!("{}", nums[1]);
}

함수

함수의 0개 또는 그 이상의 인자를 가진다.

함수의 이름은 언제나 snake_case형태로 짓는다.

fn add(x: i32, y: i32) -> i32 {
    return x + y;
}

fn main() {
    println!("{}", add(42,13));
}

여러개의 리턴 값

함수에서 튜플(Tuple)을 리턴하면 여러개의 값을 리턴할 수 있다.

fn swap(x: i32, y: i32) -> (i32, i32) {
    return (y, x);
}

fn main() {
    let result = swap(123, 321);
    println!("{} {}", result.0, result.1);

    let (a,b,) = swap(result.0, result.1);
    println!("{} {}", a, b);
}

아무것도 리턴하지 않기

함수에 리턴형을 지정하지 않는 경우 빈 튜플을 리턴하는데, ()로 표현한다.

fn make_nothing() -> () {
    return ();
}

fn make_nothing2() {
    // Do nothing
}

fn main() {
    let a = make_nothing();
    let b = make_nothing2();

    pritnln!("The value of a: {:?}", a);
    println!("The value of b: {:?}", b);
}

if/else if/else

조건문에 괄호가 없다

fn main() {
    let x = 42;
    if x < 42 {
        println!("Less then 42");
    } else if x == 42 {
        println!("Eqeual 42");
    } else {
        println!("Greater than 42");
    }
}

loop

무한 반복문이 필요할 때 사용

fn main() {
    let mux x = 0;
    loop {
            x += 1;
            if x == 49 {
                break;
            }
    }
    println!("{}", x);
}

while

반복문에 조건을 간단히 넣을 수 있다

조건의 평가 결과가 false인 경우, 종료한다.

fn main() {
    let mux x = 0;
    while x != 42 {
            x += 1;
    }
}

for

..연산자는 시작 숫자에서 끝 숫자 전까지의 숫자들을 생성하는 반복자를 만든다.

..=연산자는 시작 숫자에서 끝 숫자까지들을 생성하는 반복자를 만든다.

fn main() {
    for x in 0..5 {
        println!("{}", x);
    }

    for x in 0..=5 {
        println!("{}", x);
    }
}

match

switch를 대체하는 구문

모든 케이스를 빠짐없이 처리해야 한다.

fn main() {
    let x = 41;

    match x {
        0 => {
            println!("Foudn 0");
        }
        1 | 2 => {
            println!("Fount 1 or 2!");
        }
        3..=9 => {
            println!("Fount between 3 and 9!");
        }
        matched_num @ 10..=100 => {
            println!("Fount {} between 10 and 100!", matched_num);
        }
        _ => {
            println("Found something else!");
        }
    }
}

구조체

필드(Filed)들의 Collection

메모리 상에 필드들을 어떻게 배치할 지에 대한 컴파일러의 청사진

struct SeaCreature {
    animal_type: String,
    name: String,
    arms: i32,
    legs: i32,
    weapon: String,
}

3. 기초적인 흐름 제어

메소드 호출하기

스태틱 메소드(Static Methods)

  • 자료형 그 자체에 속하는 메소드
  • ::연산자를 이용해 호출

인스턴스 메소드(Instance Methods)

  • 자료형의 인스턴스에 속하는 메소드
  • .연산자를 이용해 호출
fn main() {
    let s = String::from("Hello world!");
    println!("The length of {} is {}.", s, s.len());
}

메모리에 데이터 생성하기

코드에서 구조체를 인스턴스화(Instaniate)하면 프로그램은 연관된 필드 데이터들을 메모리 상에 나란히 생성한다.

구조체의 필드값들은 .연산자를 통해 접근한다.

struct SeaCreature {
    animal_type: String,
    name: String,
    arms: i32,
    legs: i32,
    weapon: String,
}

fn main() {
    let ferris = SeaCreature {
        animal_type: String::from("crab"),
        name: String::from("Ferris"),
        arms: 2,
        legs: 4,
        weapon: String::from("claw"),
    }

열거형

enum키워드를 통해 몇 가지 태그된 원소의 값을 갖는 새로운 자료형을 생성할 수 있다

match와 함께 사용하면 품질 좋은 코드를 만들 수 있다.

enum Species {
    Crab,
    Octopus,
    Fish,
    Clam,
}

struct SeaCreature {
    spcies: Species,
    name; String,
}

fn main() {
    let ferris = SeaCreate {
        species: Species::Crab,
        name: String::from("Ferris"),
    }
    match ferris.species {
        Species::Crab => println("{} is Crab", ferries,name),
        Species::Octopus => println("{} is Octopus", ferries,name),
        Species::Fish => println("{} is Fish", ferries,name),
        Species::Clam => println("{} is Clam", ferries,name),
    }
}

4. Generic 자료형

Generic 자료형

structenum을 부분적으로 정의해, 컴파일러가 컴파일 타임에 코드 사용을 기반으로 완전히 정의된 버전을 만들 수 있게 해준다.

struct BagOfHolding<T> {
    item: T,
}

fn main() {
    let i32_bag = BagOfHolding::<i32> { tiem: 42 };
  let bool_bag = BagOfHolding::<bool> { item: true };
  let float_bag = BagOfHolding { item: 3.14 };
  let bag_in_bag = BagOfHolding {
    item: BagOfHolding { item: "boom!" },
  };

  println!(
    "{} {} {} {}",
    i32_bag.item, bool_bag.item, float_bag.item, bag_in_bag.item.item
  );
}

Option

null을 쓰지 않고도 Nullable한 값을 표현할 수 있는 내장된 Generic 열거체

enum Option<T> {
    None,
    Some(T),
}
struct BagOfHolding<T> {
    item: Option<T>,
}

fn main() {
    let i32_bag = BagOfHolding::<i32> { item: None };
    if i32_bag.item.is_none() {
        println!("Nothing!")
    } else {
        println!("Found Something!")
    }

    let i32_bag = BagOfHolding::<i32> { item: Some(42) };
    if i32_bag.item.is_some() {
        println!("Found Something!")
    } else {
        println!("Nothing!")
    }

    match i32_bag.item {
        Some(v) => println!("Found {}!", v),
        None => println!("Nothing!"),
    }
}

Result

실패할 가능성이 있는 값을 리턴할 수 있도록 해주는 내장된 Genric 열거체

enum Result<T, E> {
    Ok(T),
    Err(E),
}
fn do_something_that_might_fail(i: i32) -> Result<f32, String> {
    if i == 42 {
        Ok(3.14)
    } else {
        Err(String::from("Not match!"))
    }
}

fn main() {
    let result = do_something_that_might_fail(12);
    match result {
        Ok(value) => println!("Success: {}", value),
        Err(error) => println!("Error: {}", error),
    }
}

우아한 오류 처리

Result와 함께 쓸 수 있는 강력한 연산자 ?

do_somrthing_that_might_fail()?

match do_something_that_might_fail() {
    Ok(v) => v,
    Err(e) => return Err(e),
}
fn do_something_that_might_fail(i: i32) -> Result<f32, String> {
    if i == 42 {
        Ok(13.0)
    } else {
        Err(String::from("Not match!"))
    }
}

fn main() -> Result<(), String>{
    let v = do_something_that_might_fail(42)?;
    println!("Found {}", v);
    Ok(())
}

추한 옵션/결과 처리

간단한 코드를 작성 할 때에도 Option/Result를 쓰는 것은 귀찮은 일일 수 있다

unwap이라는 함수를 사용해 빠르고 더러운 방식으로 값을 가져올 수 있다.

  • Option/Result 내부의 값을 꺼내오고
  • enumNone/Err인 경우에는 panic!
fn do_something_that_might_fail(i: i32) -> Result<f32, String> {
    if i == 42 {
        Ok(13.0)
    } else {
        Err(String::from("Not match!"))
    }
}

fn main() -> Result<(), String> {
    let v = do_something_that_might_fail(42).unwrap();
    println!("Found {}", v);

    let v = do_something_that_might_fail(1).unwrap();
    println!("Found {}", v);
    Ok(())
}

벡터

  • Vec구조체로 표현하는 가변 크기의 리스트
  • Vec!머크로를 통해 손쉽게 생성할 수 있다.
  • iter()메소드를 통해 반복자를 생성할 수 있다.
fn main() {
    let mut float_vec = Vec::new();
    float_vec.push(1.3);
    float_vec.push(2.4);
    float_vec.push(3.5);

    let string_vec = vec![String::from("Hello"), String::from("World")];

    for word in string_vec.iter() {
        println!("{}", word);
    }
}

5. 소유권과 데이터 대여

소유권 및 범위 기반 리소스 관리

자료형을 인스턴스화해 변수명에 할당(Binding)하면, Rust 컴파일러가 전체 생명주기(Lifetime) 동안 검증할 메모리 리소스를 생성한다.

할당된 변수는 리소스의 소유자(Owner)라고 한다.

Rust는 범위(scope)가 끌나는 곳에서 리소스를 소멸하고 할당 해제한다.

이 소멸과 할당 해제를 의미하는 용어로 drop을 사용한다. ( C++ 에서는 Resource Acquisition Is Initialization(RAII)라고 부른다)

구조체가 Drop될 때 구조체 자신이 제일 먼저 Drop되고, 이후 자식들이 각각 Drop된다.

소유권 이전

소유자가 함수의 인자로 전달되면, 소유권은 그 함수의 매개 변수로 이동(Move)된다.

이동된 이후에는 원래 함수에 있던 변수는 더 이상 사용할 수 없다.

struct Foo {
    x: i32,
}

fn do_somerhing(f: foo) {
    println!("{}", f.x);
}

fn main() {
    let foo = Foo { x: 42 };
    do_something(foo);
}

소유권 리턴하기

소유권은 함수에서도 리턴될 수 있다.

struct Foo {
    x: i32,
}

fn do_somerhing() -> {
    Foo {x: 42}
}

fn main() {
    let foo =    do_something(foo);
}

참조로 소유권 대여하기

&연산자를 통해 참조로 리소스에 대한 접근 권한을 대여할 수 있다

참조도 다른 리소스와 마찬가지로 Drop된다.

struct Foo {
    x: i32,
}

fn main() {
    let foo = Foo { x: 42};
    let f = &foo;
    println!("{}", f.x);
}

참조로 변경 가능한 소유권 대여하기

&mut연산자를 통해 리소스에 대해 변경 가능한 접근 권한도 대여할 수 있다.

리소스의 소유자는 변경 가능하게 대여된 상태에서 이동되거나 변경될 수 없다.

struct Foo {
    x: i32,
}

fn do_something(f: Foo) {
    println!("{}", f.x);
}

fn main() {
    let mut foo = Foo { x:42 };
    let f= &mut foo;

    // do_something(foo); // error: use of moved value: `foo`
    // foo.x = 13;
    f.x = 13;
    println!("{}", foo.x);

    foo.x = 7;
    do_something(foo);
}

역참조

&mut참조를 이용해 *연산자로 소유자의 값을 설정할 수 있다.

*연산자로 소유자의 값의 복사본도 가져올 수 있다.(복사 가능한 경우만)

fn main() {
    let mut foo =42;
    let f = &mut foo;
    let bar = *f;
    *f = 13;
    println!("{}", bar);
    println!("{}", foo);
}

대여한 데이터 전달하기

Rust의 참조 규칙

  • 단 하나의 변경 가능한 참조 또는 여러개의 변경 불가능한 참조만 허용하며, 둘다는 안된다.
  • 참조는 그 소유자보다 더 오래 살 수 없다.

보통 함수로 참조를 넘겨줄 때에는 문제가 되지 않는다.

struct Foo {
    x: i32,
}

fn do_something(a: &Foo) -> &i32 {
    return &a.x;
}
fn main() {
    let mut foo = Foo { x: 42 };
    let x = &mut foo.x;
    *x = 13;
    let y = do_something(&foo);
    println!("{}", y);
}

명시적인 생명주기

Rust 컴파일러는 모든 변수의 생명 주기를 이해하며, 참조가 절대로 그 소유자보다 더 오래 존재하지 못하도록 검증을 시도한다.

함수에서 어떤 매개 변수와 리턴 값이 서로 같은 생명 주기를 공유하는지 식별할 수 있도록 심볼로 표시해 명시적으로 셩명 주기를 지정할 수 있다.

생명 주기 지정자는 언제나로 시작한다. (ex: ‘a, ‘b, ‘c)

struct Foo {
    x: i32,
}

fn do_something<'a>(foo: &'a Foo) -> &'a i32 {
    return &foo.x;
}

fn main() {
    let mut foo = Foo { x: 42 };
    let x = &mut foo.x;
    *x = 43;
    let y = do_something(&foo);
    println!("{}", y);
}

여러 개의 생명 주기

생명 주지 지정자는 컴파ㅊ일러가 스스로 함수 매개 변수들의 생명 주기를 판별하지 못하는 경우, 이를 명시적으로 지정할 수 있게 도와준다.

struct Foo {
    x: i32,
}

fn do_something<'a, 'b>(foo_a: &'a Foo, foo_b: &'b Foo) -> &'b i32 {
    println!("{}", foo_a.x);
    println!("{}", foo_b.x);
    return &foo_b.x;
}

fn main() {
    let foo_a = Foo { x: 42 };
    let foo_b = Foo { x: 12 };
    let x = do_something(&foo_a, &foo_b);
    println!("{}", x);
}

정적인 생명주기

static변수는 컴파일 타임에 생성되어 프로그램의 시작부터 끝까지 존재하는 메모리 리소스다. 이들은 명시적으로 자료형을 지정해 주어야 한다.

static생명 주기는 프로그램이 끝날 때까지 무한정 유지되는 메모리 리소스다. 따라서 static이라는 특별한 생명주기 지정자를 갖는다.

static한 리소스는 절대 drop 되지 않는다.

만약 static 생명 주기를 갖는 리소스가 참조를 포함하는 경우, 그들도 모두 static이어야 한다. (그 이하의 것들은 충분히 오래 살아남지 못한다)

static PI: f64 = 3.1415;

fn main() {
    static mut SECRET: &'static str = "swordfish";

    let msg: &'static str = "Hello World";
    let p: &'static f64 = &PI;
    println!("{} {} ", msg, p);
    unsafe {
        SECRET = "abracadbra";
        println!("{}", SECRET);
    }
}

데이터 자료형의 생명주기

함수와 마찬가지로 데이터 자료형의 구성원들도 생명 주기 지정자로 지정할 수 있다.

Rust는 참조가 품고 있는 데이터 구조가 참조가 가리키는 소유자보다 절대 오래 살아남지 못하도록 검증한다.

아무것도 아닌 것을 가리키는 참조를 들고 다니는 구조체는 있을 수 없다.

struct Foo<'a> {
    i:&'a i32
}

fn main() {
    let x = 42;
    let foo = Foo {
        i: &x
    };
    println!("{}", foo.i);
}
반응형

'개발 > rust' 카테고리의 다른 글

RUST 학습 2주차  (0) 2022.10.24
RUST 소개  (0) 2022.10.09
rust 유용한 명령어  (0) 2022.10.07
angular vs react vs vue

javascript framework 개발환경이 2017년에는 많은 변화가 있었습니다. 급부상하고 있는 Vue.js (ver.2 로 진화중) 와 엄청난 업데이트를 진행하고 있는 Angular (현재 5.x), Facebook이 이끌어가고 있는 React.js.

angular를 사용하고 있었는데, 갑작스런 angular5 와 typescript 와 혼용되면서 많은 변화에 적응을 못하고 있는 상황입니다. 가벼운 것들을 만드려고 하는데 시작부터 과대한 몸집을 가지고 있어서 균형을 맞춰주기 힘든 실정입니다.
하이브리드앱 개발을 위해서는 위에 3개의 Javascript을 잘 알아야합니다.

먼저 단일 페이지 응용 프로그램 (SPA)이 필요한지 또는 여러 페이지 접근 방식을 택해야하는지 확실하게 결정해야합니다.

  • 프레임워크 라이브러리 생태계의 크기가 얼마나 되는가.
  • 해당 언어의 커뮤니티가 얼마나 활성화되어 있는가.
  • 프레임워크 개발자를 쉽게 접할 수 있는가
  • 프레임워크의 기본개념에 대해서는 알고 있는가.
  • 프로그램의 크기는 얼마나 될 것인가.
  • 프레임워크의 학습곡선은 어떻게 되는가
  • 어떤 종류의 성능을 기대할 수 있는가.
  • 선택한 프레임워크로 어떻게 개발을 시작하는가

역사

Angular

TypeScript를 기반의 Javascript 프레임워크. 구글이 개발유지 중.
슈퍼 MVW(Model-View-Whatever) 프레임워크라고 불리운다.

React

"사용자 인터페이스를 만들기 위한 Javascript

2013년 등장해서 Facebook 이 개발, 유지관리합니다.

Facebook은 React Fiber의 출시를 위해 노력하고 있습니다. 후드에서 React가 변경됩니다. 결과적으로 렌더링 속도가 훨씬 빠르지 만 변경 후에는 이전 버전과의 호환성이 유지됩니다. 페이스 북은 2017 년 4 월 개발자 회의에서 변경 사항에 대해 이야기하고 새로운 아키텍처에 대한 비공식적 인 기사가 공개되었습니다. React Fiber는 2017 년 9 월에 React v16과 함께 발표되었습니다.

Vue

Vue는 2016 년에 가장 빠르게 성장하는 JS 프레임 워크 중 하나입니다.
Vue는 자체를 "대화 형 인터페이스 구축을 위한 직관적이고 신속하며 구성 가능한 MVVM"이라고 표현합니다.

Google 직원 인 Evan You가 2014 년 2 월에 처음 출시했습니다. Vue가 큰 회사의 후원없이 1 인 쇼처럼 많은 견인력을 얻고 있다는 점을 감안할 때 상당히 성공적이었습니다.

Vue로 제작된 페이지

트렌드

여러 가지 이름과 버전으로 인해 Google 검색 트렌드에서 Angular, React 및 Vue를 비교하기가 어렵습니다.
아래은 구글트렌드로 검색을 한 최근 5년간의 분류로 검색 이슈에 대한 기록입니다.

Imgur

Stackoverflow 2017 설문 조사에 따르면 React는 조사 대상 개발자의 67 %와 AngularJS에 의해 52 %의 사랑을 받았습니다.

AngularJS (48 %) 대 React (33 %)는 "개발을 계속할 관심이 없습니다."
Vue는 두 경우 모두 상위 10 위 안에 들지 않습니다.

그런 다음 "프런트 엔드 프레임 워크"를 비교하는 statejs.com 설문 조사가 있습니다. 가장 흥미로운 사실은 React와 Angular가 100 % 인지도를 지니고 있으며, Vue는 설문 조사 대상 사람들의 23 %에게 알려지지 않았습니다.
만족도와 관련하여 React는 "다시 사용할 것"에 대해 92 %, Vue 89 % 및 Angular 2 만 65 %를 기록했습니다.

Stackoverflow 2017 설문 조사에서 재미난 설문조사 자료를 얻을 수 있습니다.

개발인력

Javascript를 더 배우고 싶지않는 개발자라면 Angular 또는 Vue를 선택하는 것이 좋습니다.
React는 더 많은 Javascript를 사용해야 합니다.

Angular 프레임 워크에 대한 좋은 점은 다른 회사의 새로운 Angular 2 개발자가 필요한 모든 규칙을 신속하게 익히게된다는 것입니다. 반응 프로젝트는 아키텍처 의사 결정 측면에서 각기 다르며, 개발자는 특정 프로젝트 설정에 익숙해 져야합니다.
Angular는 객체 지향 배경을 가진 개발자이거나 Javascript가 마음에 들지 않는 개발자에게 유용합니다.

라이브러리 생태계

Angular는 라이브러리가 아닌 프레임 워크이므로 응용 프로그램을 구조화하는 방법에 대해 강력한 의견을 제시하고 상자에서 더 많은 기능을 사용할 수 있기 때문입니다. Angular는 쾌적한 시작을 제공 할 수있는 "완벽한 해결책"입니다.
라이브러리, 라우팅 솔루션 등을 분석 할 필요가 없이 바로 시작할 수 있습니다.

반대로 React와 Vue는 유연합니다. 라이브러리는 모든 종류의 패키지와 쌍을 이룰 수 있습니다 (npm에 대한 React는 꽤 많이 있지만, Vue는 여전히 어리기 때문에 패키지가 적습니다).
React를 사용하면 Inferno와 같은 API 호환 대안을 위해 라이브러리 자체를 교환 할 수도 있습니다. 그러나 큰 유연성에는 큰 책임이 따릅니다. React에는 규칙이없고 제한된 지침이 없습니다. 모든 프로젝트는 아키텍처와 관련된 결정을 요구하며, 상황을 어렵게 만들 수 있습니다.

two-way data binding (양방향 데이터 바인딩)

React와 Angular의 가장 큰 차이점은 단방향 대 양방향 바인딩입니다. Angular의 양방향 바인딩은 UI 요소 (예 : 사용자 입력)가 업데이트 될 때 모델 상태를 변경합니다.
반응은 한 가지 방법으로 진행됩니다. 먼저 모델을 업데이트 한 다음 UI 요소를 렌더링합니다.
Angular의 메서드는 코드에서보다 깨끗하며 개발자가 쉽게 구현할 수 있습니다. 데이터가 한 방향으로 만 흐르기 때문에 (즉, 디버깅이 쉬워 짐) React의 방법은 더 나은 데이터 개요를 제공합니다.
두 개념 모두 장단점이 있습니다.
개념을 이해하고 이것이 프레임 워크 결정에 영향을 미치는지 확인해야합니다.
여기에 몇 가지 대화식 코드 예제를 찾을 수 있습니다.
마지막으로 Vue는 단방향 바인딩과 양방향 바인딩 (기본값은 단방향)을 모두 지원합니다.

코드작성에서

Angular는 하나의 객체가 다른 객체 (클라이언트)에 의존성주입 을 제공하는 패턴 인 종속성 주입을 포함합니다. 이로 인해 더 많은 유연성과 코드가 생성됩니다.

model-view-controller 패턴 (MVC)은 프로젝트를 모델, 뷰 및 컨트롤러의 세 가지 구성 요소로 나눕니다.
Angular MVC 프레임 워크는 MVC를 기본적으로 제공합니다.
React에만 V가 있습니다 - 당신은 스스로 M과 C를 풀어야 합니다.

유연성(Flexibility), 간소화(Microservice)

소스 코드에 Javascript 라이브러리를 추가하기 만하면 React 또는 Vue로 작업 할 수 있습니다.
Angular는 TypeScript를 사용하기 때문에 Angular에서 불가능합니다.

React와 Vue를 사용하면 실제로 필요한 것만 선택하여 응용 프로그램의 크기를 조정할 수 있습니다. 이전 응용 프로그램의 일부를 사용하여 SPA에서 마이크로 서비스로 전환하는 데 더 많은 유연성을 제공합니다. SPA에 가장 적합한 Angular는 마이크로 서비스에 사용하기에는 너무 덩치가 큽니다.

다음 사이트에서 JS 프레임워크의 벤치결과 및 테스트를 진행할 수 있습니다.

Testing (테스트)

Angular2에는 Jasmine, Facebook은 Jest를 사용하여 React 코드를 테스트합니다.

Vue는 테스트 가이드가 없지만 Evan은 2017 년 미리보기에서 팀이이 작업을 계획하고 있다고합니다. 카르마를 사용하는 것을 권장하고 있습니다.
Vue는 Jest와 함께 작동하며 테스트 유틸리티로 avoriaz도 있습니다.

Universal, native apps (유니버셜, 네이티브앱)

하이브리드앱을 제작하는데 사용되고 있습니다.
어플리케이션은 웹, 데스크탑 및 네이티브 애플 리케이션의 세계로 어플리케이션을 소개하고 있습니다.

React와 Angular 모두 네이티브 개발을 지원합니다.
Angular는 네이티브 응용 프로그램 용 NativeScript (Telerik 지원) 및 하이브리드 응용 프로그램 용 Ionic Framework를 보유하고 있습니다.
React를 사용하면 반응 네이티브 렌더러를 통해 크로스 플랫폼 iOS 및 Android 앱을 제작하거나 네이티브 앱에 반응 할 수 있습니다.

Javascript 프레임 워크는 클라이언트에서 페이지를 렌더링합니다. 이것은 인식 된 성능, 전반적인 사용자 경험 및 SEO에 좋지 않습니다. 서버 측 사전 렌더링은 장점입니다. 세 가지 프레임 워크에는 모두 도움이되는 라이브러리가 있습니다. React에는 next.js가 있고 Vue에는 nuxt.js가 있고 Angular에는 Angular Universal이 있습니다.

학습곡선

확실히 Angular의 가파른 학습 곡선이 있습니다.
그것은 포괄적 인 문서를 가지고 있지만 때로는 생각보다 어렵기 때문에 좌절감을 느낄 수도 있습니다.
Javascript에 대한 깊은 이해가있는 경우에도 프레임 워크의 내부에서 진행중인 작업을 알아야합니다.
설치를 시작하면 엄청난 코드와 패키지를 한꺼번에 설치합니다. 시간이 지남에 따라 배울 필요가 있는 생태계가 있기 때문에 부정적인 요소로 작용할 수 있습니다.
다른 한편으로는, 많은 설정이 되어있기 때문에 바로 결과물을 만들 수 있는 장점이 있습니다.

React를 사용하면 타사 라이브러리와 관련하여 많은 설정을 할 수 있습니다. React에서만 선택할 수있는 상태 관리를위한 16 개의 다른 flux 패키지들이 있습니다.

Vue는 배우기가 꽤 쉽습니다.
주니어 개발자에게는 Vue가 훨씬 쉬워 보이기 때문에 Vue로 많이들 전환합니다.
여기에서는 Angular에서 Vue로 이동에 관한 글에 대해 읽을 수 있습니다. 다른 사용자에 따르면, 회사의 React 앱은 너무 복잡하여 새로운 개발자가 코드를 따라갈 수 없다고 합니다.
Vue를 사용하면 중학교 및 고위 개발자 간의 격차가 줄어들고 버그, 문제 및 개발 시간을 최소화하면서보다 쉽게 ​​공동 작업을 수행 할 수 있습니다.

어떤 사람들은 React에서 작업이 Vue에서 더 잘 작동한다고 주장합니다. 주니어 Javascript 개발자이거나 지난 10 년 동안 주로 jQuery로 작업 한 적이 있다면, Vue 사용을 고려해야 합니다.
패러다임 이동은 React로 이동할 때 더욱 두드러집니다.
Vue는 일반 Javascript와 유사 해 보이지만 구성 요소, event-driven-model(이벤트 중심 모델) 및 one-way data flow(단방향 데이터 흐름)과 같은 새로운 아이디어를 소개합니다.

결론

Typescript를 좋아한다면 : Angular
OOP(Object Orientated Programming)를 한다면 : Angular
가이드라인, 구조, 도움의 손길이 필요하다면 : Angular
거대한 생태계를 좋아한다면 : React
유연성을 원한다면 : React
원하는 패키지만 사용하고 싶다면 : React
Javascript를 사랑하고, 자바스크립트 접근법을 가지고 있다면 : React
깔끔한 코드를 좋아한다면 : Vue
가벼운 프레임워크를 원한다면 : Vue
쉬운 학습곡선을 원한다면 : Vue
혼자 일하거나 작은 팀으로 일한다면 : Vue
앱의 덩치가 크다면 : Angular
하이브리드 네이트브앱을 만들고 싶다면 : React, Angular
깔끔한 HTML 파일이 필요하고 디자이너와 일하고 있다면 : Angular, Vue
제한된 생태계를 싫어한다면 : React

다른 사이트들을 참고해서 작성했지만, 솔직히 나도 알고 싶은 결론을 도출하기까지 너무 많은 시간이 걸렸다.
React와 Vuew은 아직 사용해 보지 않아서 잘은 모르지만 혼자 간단한 사이트를 제작하는데 Vue로 작성을 고려해 보는게 좋을 꺼 같다. 그런데, Javascript 가 조금 싫은데…

반응형

안드로이드 HTML 파싱하기

JSoup을 사용해서 하면 간단하게 사용할 수 있다.

자바에서도 위에 사용하는 소스를 통해서 이요할 수 있지만, 안드로이드를 통해서 사용할 것이기 때문에 Gradle 을 통해 설치를 해 보겠다.

gradle 의 build.gradle (Module: app) 에 아래와 같은 문구를 추가합니다.

// jsoup HTML parser library @ https://jsoup.org/
compile 'org.jsoup:jsoup:1.10.3'

그리고 Gradle Sync Now를 눌러주는 것을 잊으면 안됩니다. 그래야 적용이 됩니다.

주의할점
메인화면에서 네트워크 연결을 사용하면 에러가 납니다. 메인에서 네트워크 연결을 바로 시도 하면 에러가 납니다. 이 때문에 하루를 날려 버렸습니다.

여러 예제를 보면 다들 AsyncTask를 통해서 이용하고 있습니다. 사용법도 간단하고, 예제도 있지 때문에 저도 같은 방법을 사용할 것입니다.

AsyncTask에 대한 설명이 필요하시면 IT 마이닝 블로그로 이동하기

class MainActivity 아래에

다음과 같은 코드를 추가 합니다.


private class MainPageTask extends AsyncTask<Void,Void,Void> {
    private Elements element;

    @Override
    protected void onPostExecute(Void result) {
        //doInBackground 작업이 끝나고 난뒤의 작업
        mainHello.setText(element.text());
    }

    @Override
    protected Void doInBackground(Void... params) {
        //백그라운드 작업이 진행되는 곳.
        try {
            Document doc = Jsoup.connect("http://example.com").get();
            element = doc.select("#algoList > tbody");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

사이트 example.com 를 파싱하려고 합니다.

doInBackground에서 작업을 하고 onPostExcute에서 결과가 끝나고 난 뒤의 작업을 수행합니다.

doInBackground에서 작업을 처리하려고 하면 오류가 납니다. 다른 메소드에 입력 하시는 것을 잊으면 안됩니다.

반응형

+ Recent posts