diff --git a/docs/12_ko.html b/docs/12_ko.html index 396188bf2549d58cc1829c9ca2608417d0f8a40f..1b07bc9ecb7a8128dc7eeb4bf8a280c7cad333e2 100644 --- a/docs/12_ko.html +++ b/docs/12_ko.html @@ -40,8 +40,8 @@ <p>다행히 Rust는 <strong>as</strong> 키워드를 사용하여 숫자형을 쉽게 변환할 수 있습니다.</p> <p>또는 <code>parse</code>를 자주 사용합니다.</p> <pre><code class="rust">let my_string = "42"; -let my_integer = my_string.parse::<i32>().unwrap(); -// double colon op, ::<i32> syntax tells the compiler to parse the string as an i32 type</code></pre> +let my_integer = my_string.parse::<i32>().unwrap(); +// double colon op, ::<i32> syntax tells the compiler to parse the string as an i32 type</code></pre> <div class="bottomnav"> <span class="back"><a href="11_ko.html" rel="prev">❮ 이전</a></span> <span class="next"><a href="13_ko.html" rel="next">다음 ❯</a></span> diff --git a/docs/14_ko.html b/docs/14_ko.html index 0798fd9b4fe8f56ea01353a431efa572566c56dc..f9763e12409fe7015e1b80e81c3adac58386ec8d 100644 --- a/docs/14_ko.html +++ b/docs/14_ko.html @@ -87,7 +87,7 @@ fn main() { } number += 1; - if number > 20 { + if number > 20 { break 'search; } } diff --git a/docs/19_ko.html b/docs/19_ko.html index c65e14ba4469f4e353fb7341db68e707429ebff7..a8829810ede26d695ab8c0a0bfb7fcea44d617b9 100644 --- a/docs/19_ko.html +++ b/docs/19_ko.html @@ -43,20 +43,20 @@ let number = 42; match number { - 0 => println!("숫자는 영입니다"), - 1 => println!("숫자는 일입니다"), - 42 => println!("인생, 우주, 그리고 모든 것에 대한 답"), - _ => println!("다른 숫자입니다"), + 0 => println!("숫자는 영입니다"), + 1 => println!("숫자는 일입니다"), + 42 => println!("인생, 우주, 그리고 모든 것에 대한 답"), + _ => println!("다른 숫자입니다"), } }</code></pre> <p>여기서는 number 변수의 값을 여러 패턴과 비교합니다.</p> <p><code>_</code> 패턴은 이전 패턴에서 명시적으로 다루지 않은 모든 값을 매치하는 <code>catch-all</code> 패턴입니다.</p> <pre><code class="rust">fn classify_age(age: u8) { match age { - 0..=12 => println!("어린이"), - 13..=19 => println!("청소년"), - 20..=64 => println!("성인"), - _ => println!("노인"), + 0..=12 => println!("어린이"), + 13..=19 => println!("청소년"), + 20..=64 => println!("성인"), + _ => println!("노인"), } } fn main() { diff --git a/docs/20_ko.html b/docs/20_ko.html index 1b619577b8246451668f387697e3eb90338b9cc5..3c744c762d45c526f7680266b30397c92bfedbfb 100644 --- a/docs/20_ko.html +++ b/docs/20_ko.html @@ -64,9 +64,9 @@ <p>열거형을 다루려면, 종종 match 표현식을 사용하며, 이를 통해 열거형 변종에 따라 다른 작업을 수행할 수 있습니다:</p> <pre><code class="rust">fn print_status_message(status: Status) { match status { - Status::Active => println!("사용자가 활성 상태입니다."), - Status::Inactive => println!("사용자가 비활성 상태입니다."), - Status::Pending => println!("사용자가 보류 중입니다."), + Status::Active => println!("사용자가 활성 상태입니다."), + Status::Inactive => println!("사용자가 비활성 상태입니다."), + Status::Pending => println!("사용자가 보류 중입니다."), } } fn main() { @@ -102,8 +102,8 @@ fn main() { // 튜플 열거형 변종의 값에 접근 match circle { - Shape::Circle(radius) => println!("원의 반지름: {}", radius), - _ => (), + Shape::Circle(radius) => println!("원의 반지름: {}", radius), + _ => (), } }</code></pre> <hr /> diff --git a/docs/22_ko.html b/docs/22_ko.html index 52701e39f79e218347c185490a81570a80203c6a..f9d8a052d3dc9ebd1668cf1318a79e11c0bbebd5 100644 --- a/docs/22_ko.html +++ b/docs/22_ko.html @@ -47,10 +47,10 @@ Rust에서는 결과를 나타내기 위해 Result 열거형을 사용합니다. <p>예를 들어, 정수를 문자열로 변환하는 간단한 함수를 작성해 봅시다.</p> <p>이 함수는 문자열을 입력으로 받아 정수로 변환하려고 시도하고, 변환에 성공하면 Ok 값을 반환합니다.</p> <p>만약 변환에 실패하면, Err 값을 반환합니다.</p> -<pre><code class="rust">fn parse_integer(input: &str) -> Result<i32, String> { - match input.parse::<i32>() { - Ok(value) => Ok(value), - Err(_) => Err(format!("'{}' is not a valid integer.", input)), +<pre><code class="rust">fn parse_integer(input: &str) -> Result<i32, String> { + match input.parse::<i32>() { + Ok(value) => Ok(value), + Err(_) => Err(format!("'{}' is not a valid integer.", input)), } }</code></pre> <p><strong>match 문을 사용한 오류 처리</strong></p> @@ -61,8 +61,8 @@ Rust에서는 결과를 나타내기 위해 Result 열거형을 사용합니다. let parsed = parse_integer(input); match parsed { - Ok(value) => println!("The integer value is: {}", value), - Err(error) => println!("Error: {}", error), + Ok(value) => println!("The integer value is: {}", value), + Err(error) => println!("Error: {}", error), } }</code></pre> <p>이 코드는 parse_integer 함수를 호출하여 결과를 가져옵니다.</p> diff --git a/docs/24_ko.html b/docs/24_ko.html index d7faa173653cad4b323d1a568ccac0f53d0a44e8..98a9df415832762da9b7f12eb6f83eda25d9d4f9 100644 --- a/docs/24_ko.html +++ b/docs/24_ko.html @@ -52,11 +52,11 @@ } // 넓이를 계산하는 함수 -fn area(shape: &Shape) -> f64 { +fn area(shape: &Shape) -> f64 { match shape { - Shape::Circle(r) => 3.14 * r * r, - Shape::Rectangle(w, h) => w * h, - Shape::Triangle(a, b, c) => { + Shape::Circle(r) => 3.14 * r * r, + Shape::Rectangle(w, h) => w * h, + Shape::Triangle(a, b, c) => { // 삼각형의 넓이 공식 let s = (a + b + c) / 2.0; let area = s * (s - a) * (s - b) * (s - c); @@ -71,15 +71,15 @@ fn area(shape: &Shape) -> f64 { } // 둘레를 계산하는 함수 -fn perimeter(shape: &Shape) -> f64 { +fn perimeter(shape: &Shape) -> f64 { match shape { - Shape::Circle(r) => 2.0 * 3.14 * r, - Shape::Rectangle(w, h) => 2.0 * (w + h), - Shape::Triangle(a, b, c) => { + Shape::Circle(r) => 2.0 * 3.14 * r, + Shape::Rectangle(w, h) => 2.0 * (w + h), + Shape::Triangle(a, b, c) => { // 삼각형의 둘레 공식 let p = a + b + c; // 둘레가 음수면 에러 발생 - if p < 0.0 { + if p < 0.0 { panic!("Invalid triangle"); } else { p @@ -89,11 +89,11 @@ fn perimeter(shape: &Shape) -> f64 { } // 정사각형인지 판별하는 함수 -fn is_square(shape: &Shape) -> bool { +fn is_square(shape: &Shape) -> bool { match shape { - Shape::Circle(_) => false, - Shape::Rectangle(w, h) => w == h, - Shape::Triangle(_, _, _) => false, + Shape::Circle(_) => false, + Shape::Rectangle(w, h) => w == h, + Shape::Triangle(_, _, _) => false, } } diff --git a/docs/27_ko.html b/docs/27_ko.html index 341c603675c4d72dde27144a59e339b1d67a2391..008073577a406b3a31042971e96b42d422e39bef 100644 --- a/docs/27_ko.html +++ b/docs/27_ko.html @@ -70,7 +70,7 @@ fn main() { <h2 id="clone"><code>clone</code> 메서드를 사용한 소유권 이전 방지</h2> <pre><code class="rust">let s1 = String::from("hello"); let s2 = s1.clone();</code></pre> <h2 id="">함수로 소유권 이전 후 반환</h2> -<pre><code class="rust">fn takes_and_gives_back(s: String) -> String { +<pre><code class="rust">fn takes_and_gives_back(s: String) -> String { s } diff --git a/docs/29_ko.html b/docs/29_ko.html index 7a85462c8935d5fa0fbd14b1d9db2886963e34bd..c4615efb8dd6486115f8aa2276edf84db7cbe2cb 100644 --- a/docs/29_ko.html +++ b/docs/29_ko.html @@ -49,8 +49,8 @@ <p>예제 1: 함수 시그니처에서 수명 표시</p> <pre><code class="rust">// 여기에서 사용된 'a는 수명을 나타내는 표시입니다. // 이를 통해 입력과 출력의 참조들이 동일한 수명을 가지도록 합니다. -fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str { - if s1.len() > s2.len() { +fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str { + if s1.len() > s2.len() { s1 } else { s2 @@ -59,7 +59,7 @@ fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str { <p>예제 2: 구조체에서 수명 표시</p> <pre><code class="rust">// Person 구조체는 이름을 문자열 슬라이스로 저장합니다. // 여기에서 사용된 'a는 구조체의 이름 필드가 참조하는 문자열 슬라이스의 수명을 나타냅니다. -struct Person<'a> { +struct Person<'a> { name: &'a str, }</code></pre> <p>수명과 빌림 검사기:</p> @@ -68,12 +68,12 @@ struct Person<'a> { <p>그러나 복잡한 상황에서는 개발자가 수명을 명시해야 할 수도 있습니다.</p> <p>수명을 이해하고 올바르게 사용함으로써 Rust의 빌림 검사기가 메모리 관리를 안전하게 수행할 수 있도록 지원할 수 있습니다.</p> <p>이는 Rust 프로그램의 성능과 안정성에 크게 기여합니다.</p> -<pre><code class="rust">struct Wrapper<'a, T: 'a> { +<pre><code class="rust">struct Wrapper<'a, T: 'a> { value: &'a T, } -impl<'a, T> Wrapper<'a, T> { - fn new(value: &'a T) -> Self { +impl<'a, T> Wrapper<'a, T> { + fn new(value: &'a T) -> Self { Wrapper { value } } } diff --git a/docs/34_ko.html b/docs/34_ko.html index bd18db65619bc1cc7675ecdcf532b5b79756c949..7ac08056ab46be14f62b6bc54e834a0f438ae331 100644 --- a/docs/34_ko.html +++ b/docs/34_ko.html @@ -41,7 +41,7 @@ <h2 id="a">a. 벡터 생성 및 초기화</h2> <p>벡터를 생성하려면 다음과 같은 방법을 사용할 수 있습니다.</p> <pre><code class="rust">// 빈 벡터 생성 -let mut vec1: Vec<i32> = Vec::new(); +let mut vec1: Vec<i32> = Vec::new(); // 초기 값이 있는 벡터 생성 let vec2 = vec![1, 2, 3, 4, 5];</code></pre> diff --git a/docs/35_ko.html b/docs/35_ko.html index 2770095579ab6c607eb3aedd2606f67ac27d67d2..5910688fc63a283c537225b9f6c453fe50723464 100644 --- a/docs/35_ko.html +++ b/docs/35_ko.html @@ -61,7 +61,7 @@ println!("변경된 벡터: {:?}", vec);</code></pre> <p>이를 통해 벡터의 요소를 다른 데이터 구조로 쉽게 옮길 수 있습니다.</p> <pre><code class="rust">let vec = vec!["a".to_string(), "b".to_string(), "c".to_string()]; -let mut uppercased_vec: Vec<String> = Vec::new(); +let mut uppercased_vec: Vec<String> = Vec::new(); for element in vec.into_iter() { uppercased_vec.push(element.to_uppercase()); diff --git a/docs/36_ko.html b/docs/36_ko.html index e33040c9c4e2e7425facda0a5b69094f7906f21d..1344baebc933ef997a60ae3154cdcbb6f3188c98 100644 --- a/docs/36_ko.html +++ b/docs/36_ko.html @@ -44,13 +44,13 @@ <pre><code class="rust">use std::collections::HashMap; // 빈 해시맵 생성 -let mut scores: HashMap<String, u32> = HashMap::new(); +let mut scores: HashMap<String, u32> = HashMap::new(); // 초기 값이 있는 해시맵 생성 let scores = vec![("Alice", 50), ("Bob", 60)] .into_iter() .map(|(k, v)| (k.to_string(), v)) - .collect::<HashMap<String, u32>>();</code></pre> + .collect::<HashMap<String, u32>>();</code></pre> <h2 id="b">b. 키-값 쌍 삽입 및 업데이트</h2> <p>해시맵에 키-값 쌍을 삽입하거나 업데이트하려면 <code>insert</code> 메서드를 사용합니다.</p> <pre><code class="rust">// 키-값 쌍 삽입 diff --git a/docs/37_ko.html b/docs/37_ko.html index 9bc2b3a335acc596e3bca9357080b8ead7a2ecce..e21df657b35660793bd2e9c55f9e2914f567434d 100644 --- a/docs/37_ko.html +++ b/docs/37_ko.html @@ -62,7 +62,7 @@ while let Some(number) = numbers.next() { <h3 id="collect">collect</h3> <p><code>collect</code> 메서드는 이터레이터의 요소를 다른 컬렉션 타입으로 변환합니다.</p> <pre><code class="rust">let numbers = vec![1, 2, 3, 4, 5]; -let doubled_numbers: Vec<_> = numbers.iter().map(|x| x * 2).collect(); +let doubled_numbers: Vec<_> = numbers.iter().map(|x| x * 2).collect(); println!("Doubled numbers: {:?}", doubled_numbers);</code></pre> <p>이 외에도 다양한 이터레이터 메서드가 있습니다.</p> diff --git a/docs/41_ko.html b/docs/41_ko.html index 89533905613007fa440993311741b3ec41f03a75..5df52f895b6170fcf17ce1404ccffa13c07b7354 100644 --- a/docs/41_ko.html +++ b/docs/41_ko.html @@ -45,7 +45,7 @@ <p>선언형 매크로는 매크로 규칙을 사용하여 코드를 생성하는 매크로입니다.</p> <p><code>macro_rules!</code> 키워드를 사용하여 선언형 매크로를 정의할 수 있습니다.</p> <pre><code class="rust">macro_rules! vec { - ( $( $x:expr ),* ) => { + ( $( $x:expr ),* ) => { { let mut temp_vec = Vec::new(); $( @@ -70,7 +70,7 @@ use quote::quote; use syn::{parse_macro_input, DeriveInput}; #[proc_macro_derive(HelloMacro)] -pub fn hello_macro_derive(input: TokenStream) -> TokenStream { +pub fn hello_macro_derive(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); let name = &ast.ident; let gen = quote! { @@ -112,7 +112,7 @@ fn main() { <li><code>$(...)+</code>: 1회 이상 반복</li> </ul> <pre><code class="rust">macro_rules! create_function { - ($func_name:ident) => ( + ($func_name:ident) => ( fn $func_name() { println!("함수 {}가 호출되었습니다.", stringify!($func_name)); } diff --git a/docs/42_ko.html b/docs/42_ko.html index 84562b2d3ed44b97ba012b76a1d6bbc62e269b5f..ab59b544a5ca439a86a0063e88987412ac20da9c 100644 --- a/docs/42_ko.html +++ b/docs/42_ko.html @@ -75,7 +75,7 @@ fn main() { <p>외부 함수를 사용하려면 <code>extern</code> 키워드와 <code>unsafe</code>를 사용해야 합니다.</p> <pre><code class="rust">// C 언어의 함수를 호출하는 예시 extern "C" { - fn abs(input: i32) -> i32; + fn abs(input: i32) -> i32; } fn main() { diff --git a/docs/43_ko.html b/docs/43_ko.html index a29cdc2174b9cd876161ac2e8bd5ef45b9524d84..d5fddd9442e263fec5ef86a9615fcbf2a88b7e14 100644 --- a/docs/43_ko.html +++ b/docs/43_ko.html @@ -47,7 +47,7 @@ </ol> <div> <button type="button" class="collapsible">정답 보기</button> <div class="content"> <p> <pre><code class="rust">// 매크로를 사용하여 두 벡터의 덧셈을 수행하는 함수를 작성합니다. macro_rules! add_vectors { - ($vec1:expr, $vec2:expr) => { + ($vec1:expr, $vec2:expr) => { add_vectors_unsafe(&$vec1, &$vec2) }; } @@ -60,7 +60,7 @@ fn main() { println!("벡터 덧셈 결과: {:?}", result); } -fn add_vectors_unsafe(vec1: &[i32], vec2: &[i32]) -> Vec<i32> { +fn add_vectors_unsafe(vec1: &[i32], vec2: &[i32]) -> Vec<i32> { // 벡터의 길이가 같은지 확인합니다. assert_eq!(vec1.len(), vec2.len()); diff --git a/docs/49_ko.html b/docs/49_ko.html index 4b2c551b0e13245fc71c0cf16376f24220aabd39..7114304071da2f758ce38fde8336ad1f2b962ea5 100644 --- a/docs/49_ko.html +++ b/docs/49_ko.html @@ -42,7 +42,7 @@ <p><img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGZOnJ%2Fbtra8y6n31j%2FZyIuDLgVyQPqzknNrIXqSk%2Fimg.png" alt="BasicCard" /></p> <h1 id="">준비물:</h1> <ol> -<li>public에서 접속 가능한 주소 (포트포워딩이나 AWS EC2를 이용)</li> +<li>public에서 접속 가능한 머신 (포트포워딩이나 AWS EC2, <a href="https://fly.io" target="_blank" rel="noopener">fly.io</a> 등 이용)</li> <li>카카오 i 챗봇 만들기 <a href="https://i.kakao.com/" target="_blank" rel="noopener">@링크</a></li> <li>카카오톡 채널 만들기 <a href="https://center-pf.kakao.com/profiles" target="_blank" rel="noopener">@링크</a></li> </ol> diff --git a/docs/50_ko.html b/docs/50_ko.html index 4c518346b0062b4fa47fc6d0df2594fd7353ece5..963341c103ea86915ff4f405d9bf0c603479ca17 100644 --- a/docs/50_ko.html +++ b/docs/50_ko.html @@ -35,14 +35,64 @@ <span class="toc"><a href="TOC_ko.html">목차</a></span> </div> <div class="page"> - <h1>Chapter 6 - Conclusion</h1> - <p>CSW</p> + <h1>Hello World API</h1> + <p>이번 튜토리얼에서는 Rust와 Actix-web을 이용하여 'Hello World' 메시지를 출력하는 기본적인 웹 서버를 만들어 보겠습니다.</p> +<h2 id="">시작하기</h2> +<p>첫 단계로, 새로운 binary 기반의 Cargo 프로젝트를 생성합니다:</p> +<pre><code class="bash">cargo new hello-world +cd hello-world</code></pre> +<p>그 후, 프로젝트에 actix-web을 의존성으로 추가해야 합니다.</p> +<p>이를 위해 <code>Cargo.toml</code> 파일을 열고 다음과 같이 입력합니다:</p> +<pre><code class="toml">[dependencies] +actix-web = "4"</code></pre> +<h2 id="-1">핸들러 작성하기</h2> +<p>웹 서버에서 요청을 처리하기 위해 핸들러 함수를 작성합니다.</p> +<p>이 함수들은 비동기 함수로, 필요한 매개변수를 받아 HttpResponse를 반환합니다.</p> +<p>이 HttpResponse는 웹 서버가 클라이언트에게 보낼 응답입니다.</p> +<p><code>src/main.rs</code> 파일을 다음과 같이 수정합니다:</p> +<pre><code class="rust">use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder}; + +#[get("/")] +async fn hello() -> impl Responder { + HttpResponse::Ok().body("Hello world!") +} + +#[post("/echo")] +async fn echo(req_body: String) -> impl Responder { + HttpResponse::Ok().body(req_body) +} + +async fn manual_hello() -> impl Responder { + HttpResponse::Ok().body("Hey there!") +}</code></pre> +<p>각 핸들러는 HTTP 메소드와 경로에 따라 요청을 처리합니다.</p> +<p>수동으로 경로를 설정하고 싶다면, 그에 맞는 함수를 작성하면 됩니다.</p> +<h2 id="app">App 생성 및 요청 핸들러 등록</h2> +<p>다음 단계로, App 인스턴스를 생성하고 요청 핸들러를 등록합니다.</p> +<p>경로 정보가 있는 핸들러는 <code>App::service</code>를 사용하고, 수동으로 경로를 설정한 핸들러는 <code>App::route</code>를 사용합니다.</p> +<pre><code class="rust">#[actix_web::main] +async fn main() -> std::io::Result<()> { + HttpServer::new(|| { + App::new() + .service(hello) + .service(echo) + .route("/hey", web::get().to(manual_hello)) + }) + .bind(("127.0.0.1", 8080))? + .run() + .await +}</code></pre> +<h2 id="-2">서버 실행</h2> +<p>이제 <code>cargo run</code> 명령어를 통해 프로그램을 컴파일하고 실행할 수 있습니다.</p> +<p>웹 브라우저에서 http://127.0.0.1:8080/ 주소로 접속하면 'Hello World' 메시지를 확인할 수 있습니다.</p> +<p>이 간단한 예제를 통해 Rust와 Actix-web을 이용하여 웹 서버를 어떻게 만드는지 배웠습니다.</p> +<p>이러한 기본 원리를 이용하면 다양한 웹 서비스를 만들어볼 수 있습니다.</p> <div class="bottomnav"> <span class="back"><a href="49_ko.html" rel="prev">❮ 이전</a></span> - <span class="next"><a href="chapter_7_ko.html" rel="next">다음 ❯</a></span> + <span class="next"><a href="51_ko.html" rel="next">다음 ❯</a></span> </div> </div> - <div class="code"><center><img src="/ferris_lofi.png" alt="Rust Tutorial" width="80%" height="100%"></center></div> + <div class="code"><center><img src="/8080.png" alt="Rust Tutorial" width="80%" height="100%"></center></div> </div> <script> var pres = document.querySelectorAll("pre>code"); diff --git a/docs/51_ko.html b/docs/51_ko.html new file mode 100644 index 0000000000000000000000000000000000000000..beb6dfdd7793970a870810939bf24c7eed56878e --- /dev/null +++ b/docs/51_ko.html @@ -0,0 +1,112 @@ +<!DOCTYPE html> + <html lang="ko"> + <head> + <title>Rust 튜토리얼 - 자기주도프로젝트</title> + + <meta charset="UTF-8"> + <meta content="text/html;charset=utf-8" http-equiv="Content-Type"> + <meta content="utf-8" http-equiv="encoding"> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2"> + <meta name="keywords" content="Rust, Programming, Learning"> + <meta name="description" content="Rust tutorial website based on tour_of_rust by 최석원"> + <meta name="theme-color" content="#ff6801"/> + <meta http-equiv="Cache-Control" content="max-age=3600"> + + <link rel="stylesheet" href="tour.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/night-owl.min.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.1/css/all.min.css"> + + <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> + <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> + <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> + <link rel="/manifest" href="./site.webmanifest"> + + <script src="//unpkg.com/@highlightjs/cdn-assets@11.7.0/highlight.min.js"></script> + + <script src="./tour.js" defer></script> + <!-- <script>hljs.highlightAll();</script> --> + <script src="./highlight.badge.min.js"></script> + </head> + <body> + <div class="tour"> + <div class="header"> + <span class="title"><a href="index.html">Rust 튜토리얼</a></span> + <span class="nav"> + <span class="toc"><a href="TOC_ko.html">목차</a></span> + </div> + <div class="page"> + <h1>main() && impl Responder</h1> + <h2 id="actixrsmain">actix-rs main()</h2> +<p>아래는 웹 서버를 시작하는 역할을 합니다. </p> +<pre><code class="rust">#[actix_web::main] +async fn main() -> std::io::Result<()> { + HttpServer::new(|| { + App::new() + .service(hello) + .service(echo) + .route("/hey", web::get().to(manual_hello)) + }) + .bind(("127.0.0.1", 8080))? // localhost:8080 or 127.0.0.1:8080 + .run() + .await +}</code></pre> +<p>여기서 <code>#[actix_web::main]</code>은 Actix 웹 프레임워크에서 제공하는 매크로로, 이를 이용해 비동기 메인 함수를 생성할 수 있습니다. </p> +<p>그리고 <code>HttpServer::new</code> 함수를 호출하여 새로운 HTTP 서버 인스턴스를 만듭니다.</p> +<p>이 함수의 인자로는 클로저가 들어가며, 이 클로저 내부에서 App 인스턴스를 만들고,</p> +<p>우리가 앞서 정의한 hello, echo, 그리고 manual_hello 함수를 각각 서비스로 등록합니다.</p> +<p>또한, <code>/hey</code> 라는 경로는 수동으로 설정된 경로입니다.</p> +<p><code>web::get().to(manual_hello)</code>를 통해 get 요청이 들어왔을 때 <code>manual_hello</code> 함수가 호출되도록 설정했습니다.</p> +<p>그 후 <code>.bind(("127.0.0.1", 8080))?</code>를 통해 서버를 로컬 호스트의 8080 포트에 바인딩하게 됩니다.</p> +<p>만약 바인딩에 실패하면 에러를 반환하며 프로그램은 종료됩니다.</p> +<p>마지막으로 <code>.run().await</code>를 통해 서버를 실행시키며, 이 서버는 비동기적으로 작동하게 됩니다.</p> +<h2 id="implresponder">impl Responder</h2> +<p>Rust의 'Trait'에 대해 알아보고, 특히 actix-web에서 제공하는 'Responder' Trait에 대해 살펴보겠습니다.</p> +<p>Responder는 웹 응답을 만드는데 굉장히 중요한 역할을 하는 Trait입니다.</p> +<h2 id="trait">Trait 이란?</h2> +<p>Rust에서 Trait는 특정 기능이나 행동을 정의한 것으로,</p> +<p>Trait를 구현하면 해당 구조체나 열거형은 Trait에서 정의된 메소드를 사용할 수 있게 됩니다.</p> +<p>Rust는 상속 대신 Trait를 사용하여 코드의 재사용성과 모듈성을 증가시킵니다.</p> +<h2 id="respondertrait">Responder Trait</h2> +<p>Responder는 actix-web에서 제공하는 Trait 중 하나로, HTTP 응답을 생성하는 메소드를 제공합니다.</p> +<p>이 Trait를 이용하면 웹 서버가 클라이언트에게 보내는 HTTP 응답을 쉽게 만들 수 있습니다. </p> +<p>Responder Trait은 두 가지 메소드를 정의합니다: </p> +<ol> +<li><code>respond_to</code>: HttpRequest 객체를 받아 HttpResponse를 생성하는 메소드로, 이는 핸들러에서 클라이언트의 요청을 받아 적절한 응답을 생성하는 데 사용됩니다.</li> +<li><code>customize</code>: 응답을 커스터마이징 할 수 있는 메소드로, 이 메소드는 Responder가 구현되어 있는 경우 사용할 수 있습니다.</li> +</ol> +<h2 id="responder">Responder 사용하기</h2> +<p>핸들러 함수에서는 <code>impl Responder</code>를 리턴 타입으로 사용합니다.</p> +<p>이렇게 하면 어떤 값이든, 그 값이 Responder Trait를 구현하고 있다면 리턴할 수 있게 됩니다.</p> +<p>예를 들어, 'String', 'HttpResponse' 등은 모두 Responder를 구현하고 있습니다.</p> +<p>이를 통해 해당 값을 리턴하는 핸들러를 쉽게 만들 수 있습니다.</p> +<p>Rust의 강력한 타입 시스템 덕분에 컴파일 타임에 각 핸들러가 어떤 타입의 값을 리턴하는지를 확인할 수 있게 되어,</p> +<p>런타임 에러를 사전에 방지하는 데 매우 유용합니다.</p> +<p>결국, Responder는 웹 응답을 만드는 과정을 추상화하고, 다양한 타입의 값을 HTTP 응답으로 쉽게 변환할 수 있게 해주는 역할을 합니다.</p> +<p>이를 통해 강력하면서도 유연한 웹 응답을 만들 수 있게 됩니다.</p> +<p>Responder를 활용하면 웹 서버 개발이 훨씬 편리해집니다.</p> + <div class="bottomnav"> + <span class="back"><a href="50_ko.html" rel="prev">❮ 이전</a></span> + <span class="next"><a href="52_ko.html" rel="next">다음 ❯</a></span> + </div> + </div> + <div class="code"><center><img src="/8080.png" alt="Rust Tutorial" width="80%" height="100%"></center></div> + </div> + <script> + var pres = document.querySelectorAll("pre>code"); + for (var i = 0; i < pres.length; i++) { + hljs.highlightElement(pres[i]); + } + var options = { + loadDelay: 0, + copyIconClass: "far fa-clipboard", + checkIconClass: "fa fa-check text-success", + blogURL: "http://rust-study.ajousw.kr/" + }; + window.highlightJsBadge(options); + </script> + + <footer> + <p><a target="_blank" rel="noopener" href="https://www.youtube.com/c/SoftwareToolTime">아주대학교 Software Tool Time</a> - Rust 튜토리얼 (Basic)</p> + </footer> + </body> +</html> \ No newline at end of file diff --git a/docs/52_ko.html b/docs/52_ko.html new file mode 100644 index 0000000000000000000000000000000000000000..e7b96a86950ccc87867078d559a7a96413fdd9f7 --- /dev/null +++ b/docs/52_ko.html @@ -0,0 +1,89 @@ +<!DOCTYPE html> + <html lang="ko"> + <head> + <title>Rust 튜토리얼 - 자기주도프로젝트</title> + + <meta charset="UTF-8"> + <meta content="text/html;charset=utf-8" http-equiv="Content-Type"> + <meta content="utf-8" http-equiv="encoding"> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2"> + <meta name="keywords" content="Rust, Programming, Learning"> + <meta name="description" content="Rust tutorial website based on tour_of_rust by 최석원"> + <meta name="theme-color" content="#ff6801"/> + <meta http-equiv="Cache-Control" content="max-age=3600"> + + <link rel="stylesheet" href="tour.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/night-owl.min.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.1/css/all.min.css"> + + <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> + <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> + <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> + <link rel="/manifest" href="./site.webmanifest"> + + <script src="//unpkg.com/@highlightjs/cdn-assets@11.7.0/highlight.min.js"></script> + + <script src="./tour.js" defer></script> + <!-- <script>hljs.highlightAll();</script> --> + <script src="./highlight.badge.min.js"></script> + </head> + <body> + <div class="tour"> + <div class="header"> + <span class="title"><a href="index.html">Rust 튜토리얼</a></span> + <span class="nav"> + <span class="toc"><a href="TOC_ko.html">목차</a></span> + </div> + <div class="page"> + <h1>kakao-rs</h1> + <p>카카오톡 API 템플릿을 쉽게 만들어 주는 <code>kakao-rs</code> 라이브러리에 대해 알아보겠습니다. </p> +<h2 id="kakaors">kakao-rs 라이브러리란?</h2> +<p>kakao-rs는 Rust 언어로 작성된 카카오 챗봇 서버를 만들 때 사용할 수 있는 라이브러리입니다.</p> +<p>이 라이브러리는 SimpleText, SimpleImage, ListCard, Carousel, BasicCard, CommerceCard, ItemCard 등의 JSON 데이터를 쉽게 생성할 수 있도록 돕는 도구들을 제공합니다.</p> +<h2 id="">사용 방법</h2> +<p>kakao-rs 라이브러리를 사용하려면, 먼저 프로젝트의 <code>Cargo.toml</code> 파일에 kakao-rs를 의존성으로 추가해야 합니다. </p> +<pre><code class="toml">[dependencies] +kakao-rs = "0.3"</code></pre> +<p>이 라이브러리를 이용하면 다양한 종류의 버튼(예: 공유 버튼, 링크 버튼, 일반 메시지 버튼, 전화 버튼 등)을 쉽게 만들 수 있습니다.</p> +<h2 id="json">카카오 JSON 데이터 연동</h2> +<p>kakao-rs는 카카오 JSON 데이터와의 연동이 매우 간단합니다.</p> +<p>유저의 발화문을 얻기 위해서는 아래와 같이 작성하면 됩니다.</p> +<pre><code class="rust">#[post("/end")] +pub async fn test(kakao: web::Json<Value>) -> impl Responder { // actix + println!("{}", kakao["userRequest"]["utterance"].as_str().unwrap()); // 발화문 + unimplemented!() +}</code></pre> +<p>이 라이브러리를 이용하면 다양한 형태의 카카오 챗봇 메시지를 쉽게 생성할 수 있습니다.</p> +<p>예를 들어, ListCard를 생성하는 코드는 아래와 같습니다.</p> +<pre><code class="rust">let mut list_card = ListCard::new("리스트 카드 제목!"); // 제목 +// ... +result.add_output(list_card.build()); // moved list_card's ownership</code></pre> +<p>kakao-rs 라이브러리를 통해 SimpleText, SimpleImage, BasicCard, CommerceCard, Carousel 등의</p> +<p>다양한 형태의 카카오 챗봇 메시지를 쉽게 생성할 수 있습니다.</p> +<p>카카오 챗봇 서버를 Rust로 구현하려는 개발자들에게 kakao-rs 라이브러리는 매우 유용한 도구가 될 것입니다.</p> + <div class="bottomnav"> + <span class="back"><a href="51_ko.html" rel="prev">❮ 이전</a></span> + <span class="next"><a href="53_ko.html" rel="next">다음 ❯</a></span> + </div> + </div> + <div class="code"><center><img src="/ferris_lofi.png" alt="Rust Tutorial" width="80%" height="100%"></center></div> + </div> + <script> + var pres = document.querySelectorAll("pre>code"); + for (var i = 0; i < pres.length; i++) { + hljs.highlightElement(pres[i]); + } + var options = { + loadDelay: 0, + copyIconClass: "far fa-clipboard", + checkIconClass: "fa fa-check text-success", + blogURL: "http://rust-study.ajousw.kr/" + }; + window.highlightJsBadge(options); + </script> + + <footer> + <p><a target="_blank" rel="noopener" href="https://www.youtube.com/c/SoftwareToolTime">아주대학교 Software Tool Time</a> - Rust 튜토리얼 (Basic)</p> + </footer> + </body> +</html> \ No newline at end of file diff --git a/docs/53_ko.html b/docs/53_ko.html new file mode 100644 index 0000000000000000000000000000000000000000..dac6ab8354356a41e78ca841c13a7cbac165a56c --- /dev/null +++ b/docs/53_ko.html @@ -0,0 +1,172 @@ +<!DOCTYPE html> + <html lang="ko"> + <head> + <title>Rust 튜토리얼 - 자기주도프로젝트</title> + + <meta charset="UTF-8"> + <meta content="text/html;charset=utf-8" http-equiv="Content-Type"> + <meta content="utf-8" http-equiv="encoding"> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2"> + <meta name="keywords" content="Rust, Programming, Learning"> + <meta name="description" content="Rust tutorial website based on tour_of_rust by 최석원"> + <meta name="theme-color" content="#ff6801"/> + <meta http-equiv="Cache-Control" content="max-age=3600"> + + <link rel="stylesheet" href="tour.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/night-owl.min.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.1/css/all.min.css"> + + <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> + <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> + <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> + <link rel="/manifest" href="./site.webmanifest"> + + <script src="//unpkg.com/@highlightjs/cdn-assets@11.7.0/highlight.min.js"></script> + + <script src="./tour.js" defer></script> + <!-- <script>hljs.highlightAll();</script> --> + <script src="./highlight.badge.min.js"></script> + </head> + <body> + <div class="tour"> + <div class="header"> + <span class="title"><a href="index.html">Rust 튜토리얼</a></span> + <span class="nav"> + <span class="toc"><a href="TOC_ko.html">목차</a></span> + </div> + <div class="page"> + <h1>실행</h1> + <p><code>kakao-rs</code>를 <code>actix-rs</code>에 적용시켜보겠습니다.</p> +<p>단순하게 SimpleText를 반환할 것이며 더 많은 카카오톡 반응 디자인은 다음을 참고하세요: <a href="https://i.kakao.com/docs/skill-response-format" target="_blank" rel="noopener">@링크</a></p> +<div align="center"> +<p> + <img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn5Ud0%2FbtraVUpTmyM%2FLSdvhi5uKkzx9tcN2SFbh1%2Fimg.png"> +</p> +</div> +<ol> +<li>의존성 추가</li> +</ol> +<pre><code class="bash">cargo add kakao-rs</code></pre> +<p>카카오톡 챗봇 POST json 구조: 챗봇 관리자 > 스킬 > 편집 </p> +<pre><code class="json">"intent": { + "id": "hequ", + "name": "블록 이름" + }, + "userRequest": { + "timezone": "Asia/Seoul", + "params": { + "ignoreMe": "true" + }, + "block": { + "id": "op", + "name": "블록 이름" + }, + "utterance": "발화 내용", + "lang": null, + "user": { + "id": "138", + "type": "accountId", + "properties": {} + } + }, + "bot": { + "id": "5fe45a6", + "name": "봇 이름" + }, + "action": { + "name": "yl", + "clientExtra": null, + "params": {}, + "id": "xx89p2dlfm", + "detailParams": {} + } +}</code></pre> +<ol start="2"> +<li>메인 함수</li> +</ol> +<pre><code class="bash">cargo add serde_json</code></pre> +<pre><code class="rust">use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder}; +use kakao_rs::prelude::*; +use serde_json::Value; + +#[get("/")] +async fn hello() -> impl Responder { + HttpResponse::Ok().body("Hello world!") +} + +#[post("/kakao")] +async fn kakao(kakao: web::Json<Value>) -> impl Responder { + let mut result = Template::new(); + + result.add_output(SimpleText::new("안녕하세요~").build()); + + let body = serde_json::to_string(&result).unwrap(); + + HttpResponse::Ok() + .content_type("application/json") + .body(body) +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + HttpServer::new(|| { + App::new() + .service(hello) + .service(kakao) + }) + .bind(("127.0.0.1", 8080))? + .run() + .await +}</code></pre> +<p>해당 코드는 Rust의 Actix 웹 프레임워크에서 사용하는 함수 정의의 한 예입니다.</p> +<p>이 함수는 HTTP POST 요청을 처리하고 있으며, <code>/kakao</code> 라는 경로에 대한 요청을 처리하는 역할을 합니다.</p> +<p>함수의 정의에 대해 세부적으로 살펴보겠습니다.</p> +<h2 id="">함수 시그니처</h2> +<pre><code class="rust">#[post("/kakao")] +async fn kakao(kakao: web::Json<Value>) -> impl Responder { + let mut result = Template::new(); + + result.add_output(SimpleText::new("안녕하세요~").build()); + + let body = serde_json::to_string(&result).unwrap(); + + HttpResponse::Ok() + .content_type("application/json") + .body(body) +}</code></pre> +<p>위 함수는 다음과 같은 요소들로 구성되어 있습니다.</p> +<ul> +<li><code>async fn</code>: 함수가 비동기 함수임을 나타내는 키워드입니다. Rust에서는 이 키워드를 사용하여 비동기 함수를 정의하고, 이 함수 내에서는 <code>.await</code> 연산자를 사용하여 비동기 작업을 기다릴 수 있습니다.</li> +<li><code>kakao</code>: 함수의 이름입니다. 이 이름은 이 함수가 무슨 작업을 하는지를 설명하는 데 사용됩니다.</li> +<li><code>(kakao: web::Json<Value>)</code>: 함수의 인자 목록입니다. 이 함수는 <code>web::Json<Value></code> 타입의 인자 하나를 받습니다. 이 인자는 HTTP 요청의 본문을 JSON 형식으로 파싱한 결과입니다. <code>Value</code>는 <code>serde_json</code> 라이브러리에서 제공하는 타입으로, 임의의 JSON 데이터를 나타냅니다.</li> +<li><code>-> impl Responder</code>: 함수의 리턴 타입입니다. 이 함수는 <code>impl Responder</code>라는 리턴 타입을 가집니다.<code>Responder</code>는 Actix 웹 프레임워크에서 정의한 트레잇(trait)으로, HTTP 응답을 생성하는 메소드를 제공합니다.</li> +</ul> +<p>Template 부분은 kakao-rs 라이브러리를 참고하시면 모든 카카오톡 챗봇 응답 타입들을 일단 Template에 담아야 합니다.</p> +<p>1가지 이상의 응답을 보낼 수 있기 때문입니다. (ex. SimpleText, BasicCard 같이)</p> +<p>그후 <code>serde_json</code>의 <code>to_string</code>을 이용해서 serialize 가능한 구조체들을 쉽게 json string으로 변환해서 return 할 수 있습니다.</p> + <div class="bottomnav"> + <span class="back"><a href="52_ko.html" rel="prev">❮ 이전</a></span> + <span class="next"><a href="54_ko.html" rel="next">다음 ❯</a></span> + </div> + </div> + <div class="code"><center><img src="/ferris_lofi.png" alt="Rust Tutorial" width="80%" height="100%"></center></div> + </div> + <script> + var pres = document.querySelectorAll("pre>code"); + for (var i = 0; i < pres.length; i++) { + hljs.highlightElement(pres[i]); + } + var options = { + loadDelay: 0, + copyIconClass: "far fa-clipboard", + checkIconClass: "fa fa-check text-success", + blogURL: "http://rust-study.ajousw.kr/" + }; + window.highlightJsBadge(options); + </script> + + <footer> + <p><a target="_blank" rel="noopener" href="https://www.youtube.com/c/SoftwareToolTime">아주대학교 Software Tool Time</a> - Rust 튜토리얼 (Basic)</p> + </footer> + </body> +</html> \ No newline at end of file diff --git a/docs/54_ko.html b/docs/54_ko.html new file mode 100644 index 0000000000000000000000000000000000000000..f42282da2f919663842f6090401326418c2a3a0f --- /dev/null +++ b/docs/54_ko.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> + <html lang="ko"> + <head> + <title>Rust 튜토리얼 - 자기주도프로젝트</title> + + <meta charset="UTF-8"> + <meta content="text/html;charset=utf-8" http-equiv="Content-Type"> + <meta content="utf-8" http-equiv="encoding"> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2"> + <meta name="keywords" content="Rust, Programming, Learning"> + <meta name="description" content="Rust tutorial website based on tour_of_rust by 최석원"> + <meta name="theme-color" content="#ff6801"/> + <meta http-equiv="Cache-Control" content="max-age=3600"> + + <link rel="stylesheet" href="tour.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/night-owl.min.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.1/css/all.min.css"> + + <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> + <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> + <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> + <link rel="/manifest" href="./site.webmanifest"> + + <script src="//unpkg.com/@highlightjs/cdn-assets@11.7.0/highlight.min.js"></script> + + <script src="./tour.js" defer></script> + <!-- <script>hljs.highlightAll();</script> --> + <script src="./highlight.badge.min.js"></script> + </head> + <body> + <div class="tour"> + <div class="header"> + <span class="title"><a href="index.html">Rust 튜토리얼</a></span> + <span class="nav"> + <span class="toc"><a href="TOC_ko.html">목차</a></span> + </div> + <div class="page"> + <h1>Chapter 6 - Conclusion</h1> + <p>지금까지 <code>actix-rs</code>를 이용해서 카카오톡 챗봇 서버를 만들어보았습니다.</p> +<p>데이터베이스 연동이나 24시간 실행하는 등 방법들을 배워보고 추가해보시길 바랍니다. (ex. <a href="https://choiseokwon.tistory.com/332" target="_blank" rel="noopener">actix-rs + mongodb</a>)</p> + <div class="bottomnav"> + <span class="back"><a href="53_ko.html" rel="prev">❮ 이전</a></span> + <span class="next"><a href="chapter_7_ko.html" rel="next">다음 ❯</a></span> + </div> + </div> + <div class="code"><center><img src="/weather.png" alt="Rust Tutorial" width="80%" height="100%"></center></div> + </div> + <script> + var pres = document.querySelectorAll("pre>code"); + for (var i = 0; i < pres.length; i++) { + hljs.highlightElement(pres[i]); + } + var options = { + loadDelay: 0, + copyIconClass: "far fa-clipboard", + checkIconClass: "fa fa-check text-success", + blogURL: "http://rust-study.ajousw.kr/" + }; + window.highlightJsBadge(options); + </script> + + <footer> + <p><a target="_blank" rel="noopener" href="https://www.youtube.com/c/SoftwareToolTime">아주대학교 Software Tool Time</a> - Rust 튜토리얼 (Basic)</p> + </footer> + </body> +</html> \ No newline at end of file diff --git a/docs/8080.png b/docs/8080.png new file mode 100644 index 0000000000000000000000000000000000000000..c1f6a72e19706eebfaaf45187a5a47071d3a0759 Binary files /dev/null and b/docs/8080.png differ diff --git a/docs/TOC_ko.html b/docs/TOC_ko.html index 6ffd90147f67159404eb39d1ae6e5eb38bea625c..519b5907b702fd6fcb7df61461c863f2dfb3e47d 100644 --- a/docs/TOC_ko.html +++ b/docs/TOC_ko.html @@ -87,7 +87,11 @@ <li><a href="47_ko.html">Chapter 5 - Conclusion</a></li> </ul><h3><a href="chapter_6_ko.html">Chapter 6 - 웹 서버</a></h3><ul> <li><a href="49_ko.html">프로젝트 준비</a></li> -<li><a href="50_ko.html">Chapter 6 - Conclusion</a></li> +<li><a href="50_ko.html">Hello World API</a></li> +<li><a href="51_ko.html">main() && impl Responder</a></li> +<li><a href="52_ko.html">kakao-rs</a></li> +<li><a href="53_ko.html">실행</a></li> +<li><a href="54_ko.html">Chapter 6 - Conclusion</a></li> </ul><h3><a href="chapter_7_ko.html">마치며</a></h3><ul> </ul> </div> diff --git a/docs/chapter_7_ko.html b/docs/chapter_7_ko.html index 2c8989b0c6d6641392846adf64f7c626bcafa8c1..126cff67aff741fa7b2da328ff88bd80767d1313 100644 --- a/docs/chapter_7_ko.html +++ b/docs/chapter_7_ko.html @@ -36,9 +36,22 @@ </div> <div class="page"> <h1>마치며</h1> - <p>…</p> + <p><strong>Rust 언어 🎉</strong></p> +<p>여러분이 이 Rust 언어 영상 강좌를 끝까지 수강하신 것을 축하드립니다!</p> +<p>이 강좌에서는 Rust 언어의 기본부터 고급 문법, 소유권과 빌림, 콜렉션과 이터레이터, Cargo를 다루는 방법, 그리고 actix-rs를 이용한 카카오톡 챗봇 서버 제작에 이르기까지 다양한 주제를 다루었습니다.</p> +<p>우리는 챕터1에서 Rust의 소개와 설치 방법을 배웠고, 챕터2에서는 Rust의 기본 문법을 익혔습니다.</p> +<p>챕터3에서는 Rust의 핵심 개념인 소유권과 빌림에 대해 배웠고, 챕터4에서는 다양한 콜렉션과 이터레이터를 사용하는 방법을 다뤘습니다. </p> +<p>챕터5에서는 Rust의 고급 문법인 unsafe와 macro 등을 배웠고, 챕터6에서는 Rust의 패키지 관리자인 Cargo를 이용하는 방법을 익혔습니다.</p> +<p>마지막으로, 챕터7에서는 actix-rs 라이브러리를 이용하여 카카오톡 챗봇 서버를 제작하는 방법에 대해 배웠습니다.</p> +<p>이러한 지식들을 통해 여러분은 이제 Rust로 강력하고 효율적인 애플리케이션을 작성할 수 있는 기본적인 역량을 갖추게 되었습니다.</p> +<p>Rust 학습 여정이 이 강좌에서 끝나지 않길 바랍니다. 이제 Rust를 사용하여 여러분만의 프로젝트를 시작해보시기를 권장드립니다.</p> +<p>향후 프로젝트에서 만나는 도전과 문제를 해결하며, Rust에 대한 더 깊은 이해와 능력을 쌓아가시기 바랍니다.</p> +<p>이 강좌가 여러분의 Rust 학습에 도움이 되었기를 바라며, 여러분의 코딩 여정이 계속되기를 기원합니다. 감사합니다.</p> +<ul> +<li>최석원 (ikr@kakao.com)</li> +</ul> <div class="bottomnav"> - <span class="back"><a href="50_ko.html" rel="prev">❮ 이전</a></span> + <span class="back"><a href="54_ko.html" rel="prev">❮ 이전</a></span> </div> </div> diff --git a/docs/weather.png b/docs/weather.png new file mode 100644 index 0000000000000000000000000000000000000000..6be06f1e015021e3d58e03e5e0a535717ee610ff Binary files /dev/null and b/docs/weather.png differ diff --git a/frontend/generate.js b/frontend/generate.js index 28bdf01cf75470c4a67d663249587f87165c2494..1232bc52cdd0af4096a76c973aff5eb030fd47a8 100644 --- a/frontend/generate.js +++ b/frontend/generate.js @@ -9,7 +9,16 @@ const rustExtension = { type: "lang", regex: /%rust%([^]+?)%end%/gi, replace: (s, match) => - `<pre><code class="rust">${match.trim().replace("<", "<")}</code></pre>`, + `<pre><code class="rust">${match + .trim() + .split("{{") + .join("<<") + .split("}}") + .join(">>") + .split("<") + .join("<") + .split(">") + .join(">")}</code></pre>`, }; const langExtension = { diff --git a/frontend/lessons/ko/chapter_3.yaml b/frontend/lessons/ko/chapter_3.yaml index fed70720cc60652ae16158458871a08a712f8d5a..ad9e5ec06985f4db28ecd0271cea78d2df70b916 100644 --- a/frontend/lessons/ko/chapter_3.yaml +++ b/frontend/lessons/ko/chapter_3.yaml @@ -313,7 +313,7 @@ let scores = vec![("Alice", 50), ("Bob", 60)] .into_iter() .map(|(k, v)| (k.to_string(), v)) - .collect::<HashMap<String, u32>>(); + .collect::<HashMap<String, u32}}(); %end% diff --git a/frontend/lessons/ko/chapter_6.yaml b/frontend/lessons/ko/chapter_6.yaml index e1f218991666be6fd06c29a9b190dede6225285f..331bf44d77a51fb5d4b1fb0d735fce200c75cf69 100644 --- a/frontend/lessons/ko/chapter_6.yaml +++ b/frontend/lessons/ko/chapter_6.yaml @@ -32,11 +32,373 @@ # 준비물: - 1. public에서 접속 가능한 주소 (포트포워딩이나 AWS EC2를 이용) + 1. public에서 접속 가능한 머신 (포트포워딩이나 AWS EC2, [fly.io](https://fly.io) 등 이용) 2. 카카오 i 챗봇 만들기 [@링크](https://i.kakao.com/) 3. 카카오톡 채널 만들기 [@링크](https://center-pf.kakao.com/profiles) -- title: Chapter 6 - Conclusion +- title: Hello World API + source: >- + /8080.png + content_markdown: | + 이번 튜토리얼에서는 Rust와 Actix-web을 이용하여 'Hello World' 메시지를 출력하는 기본적인 웹 서버를 만들어 보겠습니다. + + ## 시작하기 + + 첫 단계로, 새로운 binary 기반의 Cargo 프로젝트를 생성합니다: + + ```bash + cargo new hello-world + cd hello-world + ``` + + 그 후, 프로젝트에 actix-web을 의존성으로 추가해야 합니다. + + 이를 위해 `Cargo.toml` 파일을 열고 다음과 같이 입력합니다: + + ```toml + [dependencies] + actix-web = "4" + ``` + + ## 핸들러 작성하기 + + 웹 서버에서 요청을 처리하기 위해 핸들러 함수를 작성합니다. + + 이 함수들은 비동기 함수로, 필요한 매개변수를 받아 HttpResponse를 반환합니다. + + 이 HttpResponse는 웹 서버가 클라이언트에게 보낼 응답입니다. + + `src/main.rs` 파일을 다음과 같이 수정합니다: + + ```rust + use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder}; + + #[get("/")] + async fn hello() -> impl Responder { + HttpResponse::Ok().body("Hello world!") + } + + #[post("/echo")] + async fn echo(req_body: String) -> impl Responder { + HttpResponse::Ok().body(req_body) + } + + async fn manual_hello() -> impl Responder { + HttpResponse::Ok().body("Hey there!") + } + ``` + + 각 핸들러는 HTTP 메소드와 경로에 따라 요청을 처리합니다. + + 수동으로 경로를 설정하고 싶다면, 그에 맞는 함수를 작성하면 됩니다. + + ## App 생성 및 요청 핸들러 등록 + + 다음 단계로, App 인스턴스를 생성하고 요청 핸들러를 등록합니다. + + 경로 정보가 있는 핸들러는 `App::service`를 사용하고, 수동으로 경로를 설정한 핸들러는 `App::route`를 사용합니다. + + ```rust + #[actix_web::main] + async fn main() -> std::io::Result<()> { + HttpServer::new(|| { + App::new() + .service(hello) + .service(echo) + .route("/hey", web::get().to(manual_hello)) + }) + .bind(("127.0.0.1", 8080))? + .run() + .await + } + ``` + + ## 서버 실행 + + 이제 `cargo run` 명령어를 통해 프로그램을 컴파일하고 실행할 수 있습니다. + + 웹 브라우저에서 http://127.0.0.1:8080/ 주소로 접속하면 'Hello World' 메시지를 확인할 수 있습니다. + + 이 간단한 예제를 통해 Rust와 Actix-web을 이용하여 웹 서버를 어떻게 만드는지 배웠습니다. + + 이러한 기본 원리를 이용하면 다양한 웹 서비스를 만들어볼 수 있습니다. + +- title: main() && impl Responder + source: >- + /8080.png + content_markdown: | + ## actix-rs main() + + 아래는 웹 서버를 시작하는 역할을 합니다. + + ```rust + #[actix_web::main] + async fn main() -> std::io::Result<()> { + HttpServer::new(|| { + App::new() + .service(hello) + .service(echo) + .route("/hey", web::get().to(manual_hello)) + }) + .bind(("127.0.0.1", 8080))? // localhost:8080 or 127.0.0.1:8080 + .run() + .await + } + ``` + + 여기서 `#[actix_web::main]`은 Actix 웹 프레임워크에서 제공하는 매크로로, 이를 이용해 비동기 메인 함수를 생성할 수 있습니다. + + 그리고 `HttpServer::new` 함수를 호출하여 새로운 HTTP 서버 인스턴스를 만듭니다. + + 이 함수의 인자로는 클로저가 들어가며, 이 클로저 내부에서 App 인스턴스를 만들고, + + 우리가 앞서 정의한 hello, echo, 그리고 manual_hello 함수를 각각 서비스로 등록합니다. + + 또한, `/hey` 라는 경로는 수동으로 설정된 경로입니다. + + `web::get().to(manual_hello)`를 통해 get 요청이 들어왔을 때 `manual_hello` 함수가 호출되도록 설정했습니다. + + 그 후 `.bind(("127.0.0.1", 8080))?`를 통해 서버를 로컬 호스트의 8080 포트에 바인딩하게 됩니다. + + 만약 바인딩에 실패하면 에러를 반환하며 프로그램은 종료됩니다. + + 마지막으로 `.run().await`를 통해 서버를 실행시키며, 이 서버는 비동기적으로 작동하게 됩니다. + + ## impl Responder + + Rust의 'Trait'에 대해 알아보고, 특히 actix-web에서 제공하는 'Responder' Trait에 대해 살펴보겠습니다. + + Responder는 웹 응답을 만드는데 굉장히 중요한 역할을 하는 Trait입니다. + + ## Trait 이란? + + Rust에서 Trait는 특정 기능이나 행동을 정의한 것으로, + + Trait를 구현하면 해당 구조체나 열거형은 Trait에서 정의된 메소드를 사용할 수 있게 됩니다. + + Rust는 상속 대신 Trait를 사용하여 코드의 재사용성과 모듈성을 증가시킵니다. + + ## Responder Trait + + Responder는 actix-web에서 제공하는 Trait 중 하나로, HTTP 응답을 생성하는 메소드를 제공합니다. + + 이 Trait를 이용하면 웹 서버가 클라이언트에게 보내는 HTTP 응답을 쉽게 만들 수 있습니다. + + Responder Trait은 두 가지 메소드를 정의합니다: + + 1. `respond_to`: HttpRequest 객체를 받아 HttpResponse를 생성하는 메소드로, 이는 핸들러에서 클라이언트의 요청을 받아 적절한 응답을 생성하는 데 사용됩니다. + 2. `customize`: 응답을 커스터마이징 할 수 있는 메소드로, 이 메소드는 Responder가 구현되어 있는 경우 사용할 수 있습니다. + + ## Responder 사용하기 + + 핸들러 함수에서는 `impl Responder`를 리턴 타입으로 사용합니다. + + 이렇게 하면 어떤 값이든, 그 값이 Responder Trait를 구현하고 있다면 리턴할 수 있게 됩니다. + + 예를 들어, 'String', 'HttpResponse' 등은 모두 Responder를 구현하고 있습니다. + + 이를 통해 해당 값을 리턴하는 핸들러를 쉽게 만들 수 있습니다. + + Rust의 강력한 타입 시스템 덕분에 컴파일 타임에 각 핸들러가 어떤 타입의 값을 리턴하는지를 확인할 수 있게 되어, + + 런타임 에러를 사전에 방지하는 데 매우 유용합니다. + + 결국, Responder는 웹 응답을 만드는 과정을 추상화하고, 다양한 타입의 값을 HTTP 응답으로 쉽게 변환할 수 있게 해주는 역할을 합니다. + + 이를 통해 강력하면서도 유연한 웹 응답을 만들 수 있게 됩니다. + + Responder를 활용하면 웹 서버 개발이 훨씬 편리해집니다. +- title: kakao-rs + source: >- + /ferris_lofi.png + content_markdown: | + 카카오톡 API 템플릿을 쉽게 만들어 주는 `kakao-rs` 라이브러리에 대해 알아보겠습니다. + + ## kakao-rs 라이브러리란? + + kakao-rs는 Rust 언어로 작성된 카카오 챗봇 서버를 만들 때 사용할 수 있는 라이브러리입니다. + + 이 라이브러리는 SimpleText, SimpleImage, ListCard, Carousel, BasicCard, CommerceCard, ItemCard 등의 JSON 데이터를 쉽게 생성할 수 있도록 돕는 도구들을 제공합니다. + + ## 사용 방법 + + kakao-rs 라이브러리를 사용하려면, 먼저 프로젝트의 `Cargo.toml` 파일에 kakao-rs를 의존성으로 추가해야 합니다. + + ```toml + [dependencies] + kakao-rs = "0.3" + ``` + + 이 라이브러리를 이용하면 다양한 종류의 버튼(예: 공유 버튼, 링크 버튼, 일반 메시지 버튼, 전화 버튼 등)을 쉽게 만들 수 있습니다. + + ## 카카오 JSON 데이터 연동 + + kakao-rs는 카카오 JSON 데이터와의 연동이 매우 간단합니다. + + 유저의 발화문을 얻기 위해서는 아래와 같이 작성하면 됩니다. + + ```rust + #[post("/end")] + pub async fn test(kakao: web::Json<Value>) -> impl Responder { // actix + println!("{}", kakao["userRequest"]["utterance"].as_str().unwrap()); // 발화문 + unimplemented!() + } + ``` + + 이 라이브러리를 이용하면 다양한 형태의 카카오 챗봇 메시지를 쉽게 생성할 수 있습니다. + + 예를 들어, ListCard를 생성하는 코드는 아래와 같습니다. + + ```rust + let mut list_card = ListCard::new("리스트 카드 제목!"); // 제목 + // ... + result.add_output(list_card.build()); // moved list_card's ownership + ``` + + kakao-rs 라이브러리를 통해 SimpleText, SimpleImage, BasicCard, CommerceCard, Carousel 등의 + + 다양한 형태의 카카오 챗봇 메시지를 쉽게 생성할 수 있습니다. + + 카카오 챗봇 서버를 Rust로 구현하려는 개발자들에게 kakao-rs 라이브러리는 매우 유용한 도구가 될 것입니다. +- title: 실행 source: >- /ferris_lofi.png content_markdown: | - CSW + `kakao-rs`를 `actix-rs`에 적용시켜보겠습니다. + + 단순하게 SimpleText를 반환할 것이며 더 많은 카카오톡 반응 디자인은 다음을 참고하세요: [@링크](https://i.kakao.com/docs/skill-response-format) + + <div align="center"> + <p> + <img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn5Ud0%2FbtraVUpTmyM%2FLSdvhi5uKkzx9tcN2SFbh1%2Fimg.png"> + </p> + </div> + + + 1. 의존성 추가 + + ```bash + cargo add kakao-rs + ``` + + 카카오톡 챗봇 POST json 구조: 챗봇 관리자 > 스킬 > 편집 + + ```json + "intent": { + "id": "hequ", + "name": "블록 이름" + }, + "userRequest": { + "timezone": "Asia/Seoul", + "params": { + "ignoreMe": "true" + }, + "block": { + "id": "op", + "name": "블록 이름" + }, + "utterance": "발화 내용", + "lang": null, + "user": { + "id": "138", + "type": "accountId", + "properties": {} + } + }, + "bot": { + "id": "5fe45a6", + "name": "봇 이름" + }, + "action": { + "name": "yl", + "clientExtra": null, + "params": {}, + "id": "xx89p2dlfm", + "detailParams": {} + } + } + ``` + + 2. 메인 함수 + + ```bash + cargo add serde_json + ``` + + ```rust + use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder}; + use kakao_rs::prelude::*; + use serde_json::Value; + + #[get("/")] + async fn hello() -> impl Responder { + HttpResponse::Ok().body("Hello world!") + } + + #[post("/kakao")] + async fn kakao(kakao: web::Json<Value>) -> impl Responder { + let mut result = Template::new(); + + result.add_output(SimpleText::new("안녕하세요~").build()); + + let body = serde_json::to_string(&result).unwrap(); + + HttpResponse::Ok() + .content_type("application/json") + .body(body) + } + + #[actix_web::main] + async fn main() -> std::io::Result<()> { + HttpServer::new(|| { + App::new() + .service(hello) + .service(kakao) + }) + .bind(("127.0.0.1", 8080))? + .run() + .await + } + ``` + + 해당 코드는 Rust의 Actix 웹 프레임워크에서 사용하는 함수 정의의 한 예입니다. + + 이 함수는 HTTP POST 요청을 처리하고 있으며, `/kakao` 라는 경로에 대한 요청을 처리하는 역할을 합니다. + + 함수의 정의에 대해 세부적으로 살펴보겠습니다. + + ## 함수 시그니처 + + ```rust + #[post("/kakao")] + async fn kakao(kakao: web::Json<Value>) -> impl Responder { + let mut result = Template::new(); + + result.add_output(SimpleText::new("안녕하세요~").build()); + + let body = serde_json::to_string(&result).unwrap(); + + HttpResponse::Ok() + .content_type("application/json") + .body(body) + } + ``` + + 위 함수는 다음과 같은 요소들로 구성되어 있습니다. + + - `async fn`: 함수가 비동기 함수임을 나타내는 키워드입니다. Rust에서는 이 키워드를 사용하여 비동기 함수를 정의하고, 이 함수 내에서는 `.await` 연산자를 사용하여 비동기 작업을 기다릴 수 있습니다. + - `kakao`: 함수의 이름입니다. 이 이름은 이 함수가 무슨 작업을 하는지를 설명하는 데 사용됩니다. + - `(kakao: web::Json<Value>)`: 함수의 인자 목록입니다. 이 함수는 `web::Json<Value>` 타입의 인자 하나를 받습니다. 이 인자는 HTTP 요청의 본문을 JSON 형식으로 파싱한 결과입니다. `Value`는 `serde_json` 라이브러리에서 제공하는 타입으로, 임의의 JSON 데이터를 나타냅니다. + - `-> impl Responder`: 함수의 리턴 타입입니다. 이 함수는 `impl Responder`라는 리턴 타입을 가집니다.`Responder`는 Actix 웹 프레임워크에서 정의한 트레잇(trait)으로, HTTP 응답을 생성하는 메소드를 제공합니다. + + Template 부분은 kakao-rs 라이브러리를 참고하시면 모든 카카오톡 챗봇 응답 타입들을 일단 Template에 담아야 합니다. + + 1가지 이상의 응답을 보낼 수 있기 때문입니다. (ex. SimpleText, BasicCard 같이) + + 그후 `serde_json`의 `to_string`을 이용해서 serialize 가능한 구조체들을 쉽게 json string으로 변환해서 return 할 수 있습니다. +- title: Chapter 6 - Conclusion + source: >- + /weather.png + content_markdown: | + 지금까지 `actix-rs`를 이용해서 카카오톡 챗봇 서버를 만들어보았습니다. + + 데이터베이스 연동이나 24시간 실행하는 등 방법들을 배워보고 추가해보시길 바랍니다. (ex. [actix-rs + mongodb](https://choiseokwon.tistory.com/332)) diff --git a/frontend/lessons/ko/chapter_7.yaml b/frontend/lessons/ko/chapter_7.yaml index 81dcb4041865b1eaeb2bb4675b653d32901efed8..b5b977b549ca9fdd7a3ec757b37b1a00aa11b184 100644 --- a/frontend/lessons/ko/chapter_7.yaml +++ b/frontend/lessons/ko/chapter_7.yaml @@ -2,4 +2,26 @@ source: >- /ferris_lofi.png content_markdown: | - ... + **Rust 언어 🎉** + + 여러분이 이 Rust 언어 영상 강좌를 끝까지 수강하신 것을 축하드립니다! + + 이 강좌에서는 Rust 언어의 기본부터 고급 문법, 소유권과 빌림, 콜렉션과 이터레이터, Cargo를 다루는 방법, 그리고 actix-rs를 이용한 카카오톡 챗봇 서버 제작에 이르기까지 다양한 주제를 다루었습니다. + + 우리는 챕터1에서 Rust의 소개와 설치 방법을 배웠고, 챕터2에서는 Rust의 기본 문법을 익혔습니다. + + 챕터3에서는 Rust의 핵심 개념인 소유권과 빌림에 대해 배웠고, 챕터4에서는 다양한 콜렉션과 이터레이터를 사용하는 방법을 다뤘습니다. + + 챕터5에서는 Rust의 고급 문법인 unsafe와 macro 등을 배웠고, 챕터6에서는 Rust의 패키지 관리자인 Cargo를 이용하는 방법을 익혔습니다. + + 마지막으로, 챕터7에서는 actix-rs 라이브러리를 이용하여 카카오톡 챗봇 서버를 제작하는 방법에 대해 배웠습니다. + + 이러한 지식들을 통해 여러분은 이제 Rust로 강력하고 효율적인 애플리케이션을 작성할 수 있는 기본적인 역량을 갖추게 되었습니다. + + Rust 학습 여정이 이 강좌에서 끝나지 않길 바랍니다. 이제 Rust를 사용하여 여러분만의 프로젝트를 시작해보시기를 권장드립니다. + + 향후 프로젝트에서 만나는 도전과 문제를 해결하며, Rust에 대한 더 깊은 이해와 능력을 쌓아가시기 바랍니다. + + 이 강좌가 여러분의 Rust 학습에 도움이 되었기를 바라며, 여러분의 코딩 여정이 계속되기를 기원합니다. 감사합니다. + + - 최석원 (ikr@kakao.com) diff --git a/package-lock.json b/package-lock.json index c6f33e214919df8bfda7cb1cb45722cf70590efc..50e6e872b85eb8fd1b50b64e163717747d613a06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,9 +37,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", - "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -69,9 +69,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz", - "integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz", + "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -805,15 +805,15 @@ } }, "node_modules/eslint": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz", - "integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz", + "integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", "@eslint/eslintrc": "^2.0.2", - "@eslint/js": "8.38.0", + "@eslint/js": "8.39.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -823,7 +823,7 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", + "eslint-scope": "^7.2.0", "eslint-visitor-keys": "^3.4.0", "espree": "^9.5.1", "esquery": "^1.4.2", @@ -901,9 +901,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -977,9 +977,9 @@ } }, "node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -987,6 +987,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { @@ -1860,9 +1863,9 @@ "dev": true }, "node_modules/lru-cache": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.0.1.tgz", - "integrity": "sha512-C8QsKIN1UIXeOs3iWmiZ1lQY+EnKDojWd37fXy1aSbJvH4iSma1uy2OWuoB3m4SYRli5+CUjDv3Dij5DVoetmg==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", + "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -2170,9 +2173,9 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.6.4.tgz", - "integrity": "sha512-Qp/9IHkdNiXJ3/Kon++At2nVpnhRiPq/aSvQN+H3U1WZbvNRK0RIQK/o4HMqPoXjpuGJUEWpHSs6Mnjxqh3TQg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.7.0.tgz", + "integrity": "sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==", "dev": true, "dependencies": { "lru-cache": "^9.0.0", @@ -2216,9 +2219,9 @@ } }, "node_modules/prettier": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", - "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -2278,14 +2281,14 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" diff --git a/package.json b/package.json index a31d85534b646a9d71766b09f82eb2e6ea6f4f4a..bde6e3b41de586874642bdc73e70a89a9068d03b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "rust", "private": true, - "version": "1.0.0", + "version": "1.0.5", "scripts": { "lint:lessons": "prettier --write ./frontend/lessons/*/*.yaml", "lint": "npm run lint:lessons && npm run lint:webassembly && eslint --fix generate.js docs/", @@ -9,8 +9,9 @@ "build": "node ./frontend/generate.js ./frontend/lessons docs", "serve": "npm run build && python fast.py", "clean": "rimraf --glob ./docs/*.html", - "watch": "nodemon -w ./frontend/lessons/**/* -e yaml --exec npm run serve", - "tauri": "tauri" + "watch": "nodemon -w ./frontend/lessons/**/* -w ./frontend/generate.js -w ./docs/tour.css -w ./docs/tour.js -e yaml --exec npm run serve", + "tauri": "tauri", + "ajou": "xcopy .\\docs\\* ..\\rust-study.ajousw.kr\\docs\\ /EXCLUDE:exclude.txt /E /Y >nul 2>&1 && xcopy .\\frontend\\* ..\\rust-study.ajousw.kr\\frontend\\ /EXCLUDE:exclude.txt /E /Y >nul 2>&1" }, "dependencies": { "js-yaml": "^4.1.0",