<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>moonseokchoi-kr.log</title>
        <link>https://velog.io/</link>
        <description>다양한 경험을 하는걸 좋아하는 개발자입니다.</description>
        <lastBuildDate>Wed, 13 Jul 2022 11:43:28 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>moonseokchoi-kr.log</title>
            <url>https://images.velog.io/images/moonseokchoi-kr/profile/b3c018b0-65be-4610-97e4-95e637481ce7/KakaoTalk_20220221_183854550.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. moonseokchoi-kr.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/moonseokchoi-kr" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Rust] 열거자 (enumuerations)]]></title>
            <link>https://velog.io/@moonseokchoi-kr/Rust-%EC%97%B4%EA%B1%B0%EC%9E%90-enumuerations</link>
            <guid>https://velog.io/@moonseokchoi-kr/Rust-%EC%97%B4%EA%B1%B0%EC%9E%90-enumuerations</guid>
            <pubDate>Wed, 13 Jul 2022 11:43:28 GMT</pubDate>
            <description><![CDATA[<h2 id="열거자를-공부해야하나요">열거자를 공부해야하나요?</h2>
<p>C,C++,JAVA등 다른 언어를 공부했던 사람이라도 Rust의 열거자는 조금 차이가 있기 때문에 공부를 하고 가는게 좋다. 우리가 흔히 생각하는 열거보단 구조체에 훨씬 가깝기 때문이다.</p>
<p>이러한 형태는 F#,하스켈등 주로 함수형언어에서 볼 수 있는 형태이다.</p>
<h2 id="열거자의-값">열거자의 값</h2>
<pre><code class="language-rust">enum IpAddrKind{
    V4,
    V6,
}</code></pre>
<p>정의 자체는 우리가 기존에 사용했던 열거형과 크게 다르지 않다. 실사용도 다음과 같으므로 거의 차이가 없다고 할 수 있다.</p>
<pre><code class="language-rust">
// 이렇게 선언한다.    
    let four = IpAddrKind::V4;
    let six  = IpAddrKind::V6;
    //매개변수로도 사용가능!
    fn route(ip_type:IpAddrKind){}</code></pre>
<p>열거자의 값을 이용하면 구조체에 데이터를 다음과 같이 적용할 수 있다.</p>
<pre><code class="language-rust">enum IpAddrKind{
    V4,
    V6,
}

struct IpAddr{
    kind: IpAddrKind,
    address: String,
}
//여기까지는 기존의 언어들과 큰차이가 없다.
let home = IpAddr{kind: IpAddrKind::V4, address: String:from(&quot;127.0.0.1&quot;)};
let loopback = IpAddr{kind: IpAddrKind::V6, address: String:from(&quot;::1&quot;)};
</code></pre>
<p>rust의 열거자가 가진 기능은 지금부터가 본격적인 시작이다. 먼저 rust는 문자열로도 열거값을 지정할 수 있다.</p>
<pre><code class="language-rust">enum IpAddr{
    V4(String),
    V6(String),
}
//이전과 같은 코드 훨씬 간단해졌다.
let home = IpAddr::V4(String::from(&quot;127.0.0.1&quot;));
let loopback = IpAddr::V6(String::from(&quot;::1&quot;));
</code></pre>
<p>거기에다가 정수로 모든 값이 고정되었던 기존의 열거자들과 달리 열거값마다 타입을 다르게도 가능하다.</p>
<pre><code class="language-rust">enum IpAddr{
//ipv4는 한자리가 0~255까지니까
    V4(u8,u8,u8,u8),
    V6(String),
}

let home = IpAddr::V4(127.0.0.1);
let loopback = IpAddr::V6(String::from(&quot;::1&quot;));
</code></pre>
<p>사실 IPAddress에 관한 열거형이나 구조체는 표준 라이브러리에 모두 선언되어있기 때문에 이번에는 조금 다양한 데이터 타입들로 새로운 열거형을 선언해보자.</p>
<pre><code class="language-rust">    enum Message{
        Quit, //연관 데이터 값x
        Move{x:i32, y:i32},//익명의 구조체를 포함
        Write(String),//하나의 스트링
        ChangeColor(i32,i32,i32),//3개의 i32값
    }

    //위의 열거형을 구조체로 표현해보자

    struct QuitMessage;
    struct MoveMessage{
        x: i32,
        y: i32,
    };
    struct WriteMessage(String);//튜플 구조체
    struct ChangeColor(i32,i32,i32);//튜플 구조체</code></pre>
<p>슬슬 열거형이 어떠한곳에 사용되는지 감이 올거라고 생각한다. 만약 위의 코드에서 선언한 데이터를 모두 받아야하는 함수를 작성할 경우, 구조체를 이용하면 파라미터가 4개가 필요하지만 열거형은 1개의 파라미터로 4가지를 다 처리할 수 있는 장점이 있다.</p>
<p>거기다, 열거형도 함수와 method선언이 가능하기때문에 구조체와 기능이 거의 비슷하다.</p>
<pre><code class="language-rust">    impl Message{
        fn call(&amp;self){
            //여기에 메서드 본문을 작성한다.
        }

    }

    let m= Message::Wirte(String::from(&quot;hello&quot;));

    m.call();</code></pre>
<h2 id="option-열거자">Option 열거자</h2>
<p>표준 라이브러리에 존재하는 Option 열거자는 매우 범용적으로 많이 쓰인다. 왜냐하면 설계 자체가 데이터가 존재할 수도 있고, 존재 하지 않을 수도 있는 상황에 쓸 수 있도록 만들어졌기 때문이다.</p>
<p>이런 타입을 언어가 제공할 수 있다는건 코드가 모든 경우의 수를 처리하고 있는지를 컴파일러가 확인 할 수 있다는 것을 의미한다.</p>
<p>(이러한 특징때문에 Rust가 사랑을 받는게 아닐까? 컴파일 타임에 모든걸 검사할 수 있다니...)</p>
<p>Rust Null(아무런 값도 가지지않는)값이 존재하지 않는 프로그래밍 언어이다. 이 Option 열거자를 사용하면 rust에서도 Null의 개념을 사용할 수 있다. </p>
<p>정확히 말하면 Option 열거자를 통해 어떤값의 존재여부를 확인함으로 처리를 분기시킬 수 있다.</p>
<pre><code class="language-rust">enum Option&lt;T&gt; {
    Some(T),
    None,
}</code></pre>
<p>Option 열거자는 프렐류드(prelude)에 포함되어 있다. 여기서 말하는 프렐류드가 무엇이냐면 </p>
<p><strong>prelude</strong></p>
<blockquote>
<p>A prelude is a collection of names that are automatically brought into scope of every module in a crate.</p>
</blockquote>
<p>모든 모듈이 만들어 질때 자동적으로 가져오게 되는 집합이라고 말한다. 별도의 선언이 없어도 우리가 예약어를 쓰는것처럼 쓸 수 있는 개념으로 생각하면 된다.</p>
<p>즉 우리는 Option 열거자를 사용하기 위해 라이브러리를 import 할 필요가 없으며, <strong>Some</strong>이나 <strong>None</strong>을 사용하기 위해 <strong>Option:</strong>: 을 명시할 필요도 없다.</p>
<p>(여기서 generic에 대한 개념이 나오지만 이부분은 따로 설명하지 않겠다.)</p>
<p>Option 열거자를 이용해 변수 선언을 해보도록 하겠다.</p>
<pre><code class="language-rust">let some_number = some(5);
let some_number = some(&quot;a string&quot;);

let absent_number:Option&lt;i32&gt; = None;
</code></pre>
<p>여기서 중요한건 None의 선언이다. Some과 다르게 데이터 타입을 명시해준것을 확인 할 수 있다. 왜냐하면 None값만으로 Some에 어떤 값이 들어올지 알 수 없기때문이다(None은 열거자에서 별도의 타입을 갖지 않는다.)</p>
<p>그렇다면 Option을 사용하는것은 null을 쓰는것에 비해 어떠한 장점이 있을까?</p>
<pre><code class="language-cpp">    int a= 5;
    int* b = nullptr;

    int* c = a+b;</code></pre>
<p>&lt;cpp로 작성한  null코드&gt;</p>
<pre><code class="language-rust">let a  = 5;
let b : Option&lt;i8&gt; = 5;

let sum = a+b;
</code></pre>
<p>&lt;Option을 사용한 rust코드&gt;</p>
<p>이 두언어를 각각 compile해보면 cpp코드는 컴파일이 되지만, rust코드는 컴파일 단계에서 오류를 찾아내 사용자에게 알려준다.</p>
<p> rust에서 오류가 나는 이유는 a와 b의 타입이 다르기 때문이다.</p>
<p> 즉, Option열거자를 우리가 아는 T타입으로 사용하려면 별도의 변환 과정이 필요하게 되고, 이 과정은 사용자가 자신을 가지고 프로그래밍 할 수 있도록 만들어준다.</p>
<p>이러한 T타입으로 변환하기위한 다양한 메서드를 Option열거자는 제공하고 있고, 이 메서드들을 이용하면 상황에 맞게 T타입으로 변경할 수 있게된다.</p>
<p>하지만 Option열거자도 한가지 단점이 있는데, 열거자에 나열된 개별 값들을 처리할 코드를 작성해 한다. 어떤 코드는 Some(T) 값을 가진 경우에만 실행해야 하며, 이때 이 코드는 열거자 안에 저장된 T의 값에 접근 할 수 있다. None값을 가진 경우에 실행될 코드가 있다면 이코드는 T값에 접근 할 수 없다. </p>
<p>이 부분을 쉽게 작성할 수 있는 연산자를 Rust에서 제공해주는데 바로 match 키워드이다.</p>
<h3 id="match-흐름-제어-연산자">match 흐름 제어 연산자</h3>
<p>다른언어를 배워본 사람이라면 흐름제어 연산자가 무엇인지 알고 있을거라고 생각한다. (if, switch,for, while등...)</p>
<p>match는 타 언어에서 사용하는 switch-case와 매우 비슷하기 때문에 기존의 switch가 뭔지 알고 있는 분이라면 쉽게 이해할 수 있다. </p>
<pre><code class="language-rust">enum Coin{
    Penny,
    Nickle,
    Dime,
    Quarter
}

fn value_in_cents(coin : Coin) -&gt; u32{
//값에 따라 분류한다.
    match coin{
        Coin::Penny =&gt; 1,
        Coin::Nickle =&gt; 5,
        Coin::Dime =&gt; 10,
        Coin::Quarter =&gt; 25,
    }
}
</code></pre>
<p>간단한 예제를 살펴 본것처럼, 열거의 값에 따라 분류하는걸 확인 할 수 있다. 참고로 단순히 함수에 대한 값을 리턴하는것 만아닌 다음과 같은 방법으로 함수를 호출해 실행할 수도 있다.</p>
<pre><code class="language-rust">fn value_in_cents(coin : Coin) -&gt; u32{
//값에 따라 분류한다.
    match coin{
        Coin::Penny =&gt;{
            println!(&quot;Lucky Penny!);
            1
        },
        Coin::Nickle =&gt; 5,
        Coin::Dime =&gt; 10,
        Coin::Quarter =&gt; 25,
    }
}
</code></pre>
<p>그렇다면 열거자를 사용하는 예제를 봐보자</p>
<pre><code class="language-rust">fn plush_one(x: Option&lt;i32&gt;)-&gt;Option&lt;i32&gt;{
    match x{
        None =&gt; None,
        Some(i) =&gt; Some(i+1),
    }

    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);

}</code></pre>
<p>&lt;굳이 설명을 달지 않겠다 이 코드의 문제가 없는지 확인해 보자&gt;</p>
<p>다만 Match를 사용 할때도 지켜야하는 조건이 있는데, 바로 모든 경우에 대해 처리가 되어야한다는것이다. 열거값이 6개면 6개 전부에 대한 처리가 구현되어있어야한다. 사용자가 이 모든걸 체크하면서 개발할 필요는없다. 우리가 무언가를 빼먹었어도 컴파일러가 친절하게 알려주기때문에 우리는 그걸 보고 수정하면된다.</p>
<p>하지만, 모든 걸 처리해야하는 특성때문에 괜히 불필요한 처리를 해야할 상황이 올수도 있다. 이런 상황을 피하기 위해 제공되는 기능이 있는데 바로 자리지정자 이다. match를 사용할때 처리를 하지않을 값에 대해 자리 지정자를 사용하면 된다. 단 자리 지정자는 모든 값에 일치함을 의미함으로 무언가 처리를 하게된다면 유의해야한다.</p>
<pre><code class="language-rust">
let some_u8_value = 0u8;

match some_u8_value {
    1 =&gt; println!(&quot;one&quot;),
    3 =&gt; println!(&quot;three&quot;),
    5 =&gt; println!(&quot;five&quot;),
    7 =&gt; println!(&quot;seven&quot;),
    _ =&gt; (),
}
//if let
if let Some(3) = some_u8_value{println!(&quot;three&quot;), _=&gt;(),}</code></pre>
<p>&lt;이러면 짝수 값들은 처리를 하지 않는다. ()는 단순한 유닛값이기 때문이다&gt;</p>
<p>match를 쓰다보면 너무 코드가 장황해진다고 느낄수도 있다. 이런 느낌이 든다면 if let을 통해 코드를 더 간결하게 표현할 수있다.
단순히 if let으로 끝나는게 아닌 if let ~ else로 확장도 가능하니 필요할때 사용하면 매우 유용하게 작성이 가능하다.</p>
<h2 id="다음시간-부터는">다음시간 부터는</h2>
<p>이제는 API를 지원하기위해 Rust에서 제공해주는 기능인 모듈에 대해 공부해볼 예정이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Rust] 구조체]]></title>
            <link>https://velog.io/@moonseokchoi-kr/Rust-%EA%B5%AC%EC%A1%B0%EC%B2%B4</link>
            <guid>https://velog.io/@moonseokchoi-kr/Rust-%EA%B5%AC%EC%A1%B0%EC%B2%B4</guid>
            <pubDate>Wed, 06 Jul 2022 13:05:04 GMT</pubDate>
            <description><![CDATA[<p>C, C++를 주로 다루었던 개발자라면 매우 익숙한 개념이라고 할 수 있다. Rust의 구조체는 앞에서 말한 기존의 구조체와 어떤 차이가 있는지 한번 알아보자.</p>
<p>먼저 C, C++의 구조체의 정의이다.</p>
<pre><code class="language-cpp">struct User
{
    std::string userName;
    std::string email;
    unsigned int signInCount;
    bool active;
}</code></pre>
<p>만약 이 구조체를 Rust로 선언하면 어떨까?</p>
<pre><code class="language-rust">struct User {
    username: String,
    email: String,
    sign_in_count : u64,
    active : bool
}</code></pre>
<p>정의를 하는건 기존의 구조체와 큰차이가 없는걸 확인 할 수 있다. 생성역시 기존의 언어들에서 사용하는 구조체와 큰 차이가 없다는걸 알 수 있는데</p>
<pre><code class="language-rust">let user1 = User{
  email: String:from(&quot;someone@example.com&quot;), 
  username: String:from(&quot;someoneusername123&quot;), 
  active: true, 
  sign_in_account : 1,
};</code></pre>
<p>이 역시도 기존의 구조체와 비슷하다. 큰 차이가 없으니, 필드값을 바꾸거나 생성자를 어떻게 만드는지는 빠르게 치나가 보겠다.</p>
<pre><code class="language-rust">let user1 = User{
  email: String:from(&quot;someone@example.com&quot;), 
  username: String:from(&quot;someoneusername123&quot;), 
  active: true, 
  sign_in_account : 1,
};

user1.email = String::from(&quot;anotheremail@example.com);</code></pre>
<p>&lt;객체의 필드값 변경&gt;</p>
<pre><code class="language-rust">fn build_user(email:String, username: String) -&gt; user {
    User {
        email: email,
        username: username,
        active: true,
        sign_in_count: 1,
    }
}
</code></pre>
<p>&lt;user의 객체를 반환하는 함수&gt;</p>
<p>단축 문법을 사용하면 더 간단하게 표현도 가능하다</p>
<pre><code class="language-rust">fn build_user(email:String, username: String) -&gt; user {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}
</code></pre>
<p>&lt;전부 다 적은것과 같은 결과가 나온다&gt;</p>
<p>Rust에는 기본적으론 Copy의 개념이 없다보니, 기존 구조체 인스턴스를 갱신해서 적용할 수 있는 문법이 존재한다.</p>
<pre><code class="language-rust">let user1 = User{
  email: String:from(&quot;someone@example.com&quot;), 
  username: String:from(&quot;someoneusername123&quot;), 
  active: true, 
  sign_in_account : 1,
};

let user2 = User{
  email: String:from(&quot;other@example.com&quot;), 
  username: String:from(&quot;otherusername123&quot;), 
  active: user1.active, 
  sign_in_account : user1.sign_in_account,
};

//이런방식도 가능하다.

let user2 = User {
     email: String:from(&quot;other@example.com&quot;), 
       username: String:from(&quot;otherusername123&quot;), 
     ..user1
};
//이렇게 하면 선언을 통해 값을 집어넣은 필드를 제외하곤 user1이 가진값이 채워진다.</code></pre>
<p>Rust에서는 필드 이름이 없는 튜플 구조체도 만들 수가 있다. 이름을 정할 필요가 없는 간단한 데이터의 집합을 만들때 사용하면 매우 유용하다.</p>
<pre><code class="language-rust">struct Color(i32,i32,i32);
struct Point(i32,i32,i32);

let black = Color(0,0,0);
let origin = Point(0,0,0);</code></pre>
<p>여기서 주의할 점은 Color와 Point의 필드의 갯수가 같고 모든 타입이 같다고 해서 Color 객체를 인자로 필요로 하는 함수에 Point를 넘겨줄 수 없다는 것이다. 이 점을 제외하면 튜플 구조체는 튜플과 똑같이 작동한다.</p>
<h2 id="어-c-c은-구조체에-함수선언도-가능했는데">어? C, C++은 구조체에 함수선언도 가능했는데...</h2>
<p>Rust도 가능하다. 처음에는 선언 방식때문에 헷갈릴 수도 있으나, 개인적으론 이 방법이 더 편하다고 생각한다.</p>
<p>먼저 구조체에 선언할 수 있는건 메소드(Method)와 함수 2가지이다.</p>
<p>여기서 메소드(Method)가 무엇이냐면, 함수의 매개변수로 자신의 객체를 받는 특별한 함수를 메소드(Method)라 부른다. 이때 사용하는 예약어가 있는데 바로 self이다. 밑에 적은 예시를 한번 보자 </p>
<pre><code class="language-rust">struct Rectangle{
    width : u32,
    height : u32,
}
//구조체에 대한 함수는 여기서 정의
impl Rectangle {
//메서드는 이렇게 구현한다.
    fn get_area(&amp;self) -&gt; u32 {
        self.width * self.height
    }
//이것도 메서드...
    fn can_hold(&amp;self, other: &amp;Rectangle) -&gt; bool {
        return self.width&gt;other.width &amp;&amp; self.height&gt; other.height
    }
}</code></pre>
<p>self를 사용할 경우 매개변수 타입을 따로 명시하지 않아도 괜찮다. 자기자신을 가리키기 때문에 타입을 알려줄 필요가 없기 때문이다.</p>
<p>이렇게 self매개변수를 사용하는 함수를 Method라고 한다.</p>
<h3 id="그럼-self-함수를-사용하지-않아도-선언이-가능한가요">그럼 self 함수를 사용하지 않아도 선언이 가능한가요?</h3>
<p>물론 가능하다. 우리는 self를 사용하지 않고 정의되는 함수를 연관 함수라고 부른다.</p>
<pre><code class="language-rust">impl Rectangle{
//보통 생성자를 선언할때 많이 쓰인다.
    fn square(size:u32) -&gt; Rectangle{
        Rectangle{width: size, height: size}
    }
}

//연관함수 호출은 이렇게 한다. String::from도 연관함수이기 때문에 이것과 같다.
let rect = Rectangle::square(10);
</code></pre>
<p>::(콜론)을 사용하는 문법은 연관함수를 호출하는것 뿐만아니라, namespace를 호출할때도 사용되기 때문에 기억하고 있으면 편하다.</p>
<p>자 여기서 문제이다. 연관함수와 메서드를 각각 다른 impl블록에 선언했다. 이때 동작에 차이가 있을까?</p>
<p>차이는 없다. 분리해서 구현해도 한번에 다 적는것과 완전히 똑같이 동작한다. 지금 당장은 이걸 알 필요는 없지만, 나중에 제너릭이랑 트레이트를 구현할때 유용하게 사용된다는것을 기억해두자</p>
<h3 id="다음은">다음은?</h3>
<p>이제 구조체를 배웠으니, 열거형, 클래스 순으로 객체지향 프로그래밍과 관련된 부분들을 쭉 공부할 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Rust] 참조, 대여]]></title>
            <link>https://velog.io/@moonseokchoi-kr/Rust-%EC%B0%B8%EC%A1%B0-%EB%8C%80%EC%97%AC</link>
            <guid>https://velog.io/@moonseokchoi-kr/Rust-%EC%B0%B8%EC%A1%B0-%EB%8C%80%EC%97%AC</guid>
            <pubDate>Mon, 30 May 2022 11:27:02 GMT</pubDate>
            <description><![CDATA[<h2 id="지난-글에-이어서">지난 글에 이어서...</h2>
<p>최근에 적은 <a href="https://velog.io/@moonseokchoi-kr/Rust-%EC%86%8C%EC%9C%A0%EA%B6%8C%EA%B3%BC-%ED%8F%AC%EC%9D%B8%ED%84%B0">소유권</a> 2탄이라고 할 수 있다 이전에는 소유권이 무엇인지 알아보았다면 이 소유권을 잘 쓰기위한 몇가지 기능들에 대해 알아볼 예정이다.</p>
<h2 id="참조와-대여">참조와 대여</h2>
<p>소유권을 이용할 경우 소유권이 흐름에 따라 이동을 하면서 여러가지 불편한 점이 많았다. 특히 함수 내부로 소유권이 이동할 경우 이를 다시 받기 위해 무조건 리턴값을 써야만 하는 문제가 있었다.</p>
<p>이를 해결하기 위한 기능이 바로 <strong>참조와 대여</strong>다</p>
<p>먼저 아래의 코드를 보자</p>
<pre><code class="language-rust"> fn main(){
 let s1 = String::from(&quot;hello&quot;);
 let len = calculate_length(&amp;s1);
 println!(&quot;&#39;{}&#39;의 길이는 {}입니다.&quot;, s1, len);
 }
 fn calculate_length(s:&amp;String)-&gt;usize{
     s.len();
 }</code></pre>
<p>이 코드에서 이전과 달리 일반적인 String이 아닌 &amp;String을 매개변수로 받고 있다. </p>
<p>&amp;(ampersands, 엠퍼센드)를 우리는 참조라고 부르며 만약 C,C++경험이 있는 독자라면 너무나도 친숙한 표현이라고 생각한다.</p>
<p>이 참조를 사용하는 이유는 소유권을 가져오지 않고도 값을 참조할 수 있기 때문이다.</p>
<p>함수의 호출 부분을 다시 한번 보면 다음과 같다.</p>
<pre><code class="language-rust"> let s1 = String::from(&quot;hello&quot;);
 let len = calculate_length(&amp;s1);

  fn calculate_length(s:&amp;String)-&gt;usize{// String에 대한 참조다.
     s.len(); 
 }// 이시점에 s가 범위를 벗어난다.
 //참조이기때문에 소유권을 가지지 않는다. 따라서 아무일도 일어나지 않는다.</code></pre>
<p>이전에 우리가 소유권에서 보았든 매개변수가 유효한 범위는 달라지지 않는다. 다만 차이점이라면 일반적인 매개변수를 사용했을때는 소유권이 있기때문에 s를 리턴하지 않으면 drop이 발생하며 소유권이 소멸하지만, 참조를 이용하면 매개변수에 소유권을 전해주지 않기때문에 아무일도 일어나지 않는다.</p>
<p>또한 소유권을 전달받지 않았기 때문에 다시 소유권을 돌려주기위해 리턴값을 적을 필요가 없다.</p>
<p>이처럼 함수 매개변수로 참조를 전달 하는것을 &#39;대여(borrowing)&#39;라고 한다.</p>
<p>만약에 이 대여받은 매개변수를 수정하려고 하면 무슨일이 일어날까?</p>
<pre><code class="language-rust"> fn main(){
    let s =  String::from(&quot;hello&quot;);

    change(&amp;s)
 }

 fn change(some_string:&amp;String)-&gt;usize{
     some_string.push_str(&quot;,world&quot;);
 }</code></pre>
<p>이 코드를 실행해보면 에러가 난다. 왜냐하면 기본적으로 변수가 불변이듯 참조도 불변이기 때문이다.</p>
<h2 id="가변참조">가변참조</h2>
<p>위에서 본 예제가 정상적으로 실행되게 하기 위해서는 조금만 수정을 하면된다.</p>
<pre><code class="language-rust"> fn main(){
    let mut s =  String::from(&quot;hello&quot;);

    change(&amp;s)
 }

 fn change(some_string:&amp;mut String)-&gt;usize{
     some_string.push_str(&quot;,world&quot;);
 }</code></pre>
<p>우리가 수정가능한 변수를 만들었을때 mut키워드를 사용했듯 참조에도 mut을 붙여서 선언해주면 가변참조를 사용할 수 있다.
이렇게 가변참조가 만능일것 같지만, 한가지 제약이 있는데 특정 범위내에서 특정 데이터에 대한 가변참조는 오직 한개만 존재 할 수 있다는 제약이다. 말이 조금 어려운데 예제를 보면 한번에 이해가 된다.</p>
<pre><code class="language-rust"> fn main(){
    let mut s =  String::from(&quot;hello&quot;);

    let r1 = &amp;mut s;
    let r2 = &amp;mut s;
 }
</code></pre>
<p>이렇게 예제를 작성하고 실행하면 에러를 만나게 된다. </p>
<p>이 부분이 다른 언어들과 가장 다른점이라고 할 수 있는데, 다른 언어에서는 참조나 가변참조에 대한 제약이 크게 없지만, rust에서는 우리가 공부했듯 제약이 많기 때문에 이부분을 가장 어려워한다.</p>
<p>이렇게 사람들이 어려워 함에도 Rust가 이러한 제약을 고수하는 이유는 &#39;데이터 경합&#39;을 컴파일 시점에 방지해 안전한 코딩이 가능해진다.</p>
<p>(데이터 경합이 무엇인지는 각자 찾아보기를 바란다. 여기에 이 내용까지 다 적으면 너무 글이 길어져서...)</p>
<p>아마 여기까지 공부를 했다면, 이런 의문이 들 수도 있다.</p>
<p><strong>&quot;그럼 불변참조와 가변참조는 동시에 사용할 수 있는거야?&quot;</strong></p>
<pre><code class="language-rust"> fn main(){
    let mut s =  String::from(&quot;hello&quot;);

    let r1 = &amp;s;
    let r2 = &amp;s;
    let r3 = &amp;mut s;
 }
</code></pre>
<p>이런 간단한 예제를 실행해보면 r3에서 에러가 발생하는걸 알 수 있다. </p>
<p>기본적으로 불변참조가 범위내에 존재한다면 그 범위내에서는 그 값이 변경되면 안된다. 그렇기 때문에 같은 범위내에서는 불변과 가변을 동시에 사용할 수 없다. 반대로 불변참조는 여러개를 사용할 수 있는데, 불변 참조는 여러개 사용한다고 데이터가 변경될 가능성이 없기때문에 여러개 선언이 가능하다.</p>
<h2 id="죽은참조">죽은참조</h2>
<p>포인터를 사용하는 언어를 주로 이용하다보면 죽은 포인터로 인해 에러가 발생하기 쉽상이다.</p>
<p>이러한 경우를 dangling이라고 부르는데 C나 C++에서 프로그래밍을 하다보면 너무나도 쉽게 만날 수 있다.</p>
<p>문제는 C나 C++언어에서는 프로그램이 실행되고 해당지점에 도달할때까지 이러한 오류가 있는지 모른다는 점에 원인을 찾기가 힘들다.</p>
<p>하지만 rust에서는 이러한 오류를 컴파일 시점에서 찾아내어 사용자에게 알려준다 어떻게 이게 가능한걸까?</p>
<pre><code class="language-rust"> fn main(){
    let reference_to_nothing = dangle();
 }

 fn dangle()-&gt;&amp;String{
     let s = String::from(&quot;hello&quot;);

    &amp;s
 }</code></pre>
<p>이 예제를 컴파일하면 오류가 발생한다.</p>
<p>오류가 발생하는 이유는 소멸할 s에 대한 참조를 return값으로 넘겨주었기 때문이다.</p>
<p>우리는 앞서 배웠듯이 한 변수에 소유권이 유지되는건 범위 내에서만이다. dangle()함수에 선언된 s는 함수 내에서만 소유권이 유지가 된다. 지금처럼 s의 참조를 넘기게 되면 곧 소멸할 변수의 참조를 리턴하게 되므로 죽은 참조를 갖게 된다는걸 알 수 있다.</p>
<p>만약 이렇게 사용하고 싶다면 다음과 같이 참조가 아닌 소유권의 이동을 이용하면 원하는 대로 동작하게 할 수 있다.</p>
<pre><code class="language-rust"> fn main(){
    let reference = no_dangle();
 }

 fn no_dangle()-&gt;String{
     let s = String::from(&quot;hello&quot;);

    s
 }</code></pre>
<p>이제 우리가 배운 내용인 참조에 대한 간단한 규칙을 정리해보자</p>
<ul>
<li><p>어느 한 심저에 코드는 하나의 가변 참조 또는 여러 개의 불변 참조를 생성할 수 는 있지만, 둘 모두를 생성할 수 없다.</p>
</li>
<li><p>참조는 항상 유효해야 한다.</p>
</li>
</ul>
<p>다음에는 slice에 대해 작성을 할것 같은데 잘 모르겠다. 만약 작성하게 된다면 이 글에 이어서 간단하게 작성해보겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Rust] 소유권]]></title>
            <link>https://velog.io/@moonseokchoi-kr/Rust-%EC%86%8C%EC%9C%A0%EA%B6%8C%EA%B3%BC-%ED%8F%AC%EC%9D%B8%ED%84%B0</link>
            <guid>https://velog.io/@moonseokchoi-kr/Rust-%EC%86%8C%EC%9C%A0%EA%B6%8C%EA%B3%BC-%ED%8F%AC%EC%9D%B8%ED%84%B0</guid>
            <pubDate>Tue, 17 May 2022 09:54:48 GMT</pubDate>
            <description><![CDATA[<h2 id="본-글을-시작하기-전에">본 글을 시작하기 전에</h2>
<p>먼저 이글을 적는 필자는 원래 C++이나 dart, java같이 메모리를 직접관리하거나 garbage collector와 같이 메모리를 관리해주는 프로그래밍 언어만 사용해보았다.</p>
<p>그래서 rust의 주요 기능을 관통하는 이 소유권이라는 개념에 관심이 많았고, 이번에 공부를 시작하면서 먼저 배워둔 개념때문에 쉬운부분도 있었지만 오히려 그 부분이 더 이해를 힘들게 만드는것도 있었다. </p>
<p>필자가 잘못 이해하거나 설명하는 부분도 있을지도 모르니 언제든 댓글로 피드백을 주면 좋겠다</p>
<h2 id="왜-기존과-다른-방법을-사용할까">왜 기존과 다른 방법을 사용할까?</h2>
<p>우리는 먼저 왜 Rust가 소유권이라는 독자적인 시스템을 채용했는지 그것 부터 이해를 해야한다. 만약 현재 대부분의 언어에서 사용하는 garbage collector나, 직접적으로 메모리를 관리하는 방법이 크게 문제가 없다면 Rust라는 언어는 등장하지 않았을지도 모른다.</p>
<p>먼저 기존에 사용하던 메모리관리방법에는 다음과 같은 문제가 있다.</p>
<ul>
<li><p>포인터를 통한 메모리 관리방법(이런방법을 사용하는 언어를 Unmanaged Laguage 라고 부른다.)</p>
<ul>
<li><p>대표적으로 C/C++이 있다.</p>
</li>
<li><p>장점</p>
<ul>
<li>managed Language에 비해 상대적으로 빠르며, 메모리의 할당과 해제를 사용자의 의도에 따라 세밀하게 조정할수 있으므로, 프로그래밍 자유도가 높고 최적화가 용이하다.</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>프로그래머가 실수할 경우 Memory Leak이 발생할 수 있다.</li>
</ul>
</li>
</ul>
</li>
<li><p>Garbage collector를 사용하는 메모리관리 방법(Managed Laguage라고 부른다)</p>
<ul>
<li>대표적으로 JAVA, C#, GO가 있다.</li>
<li>장점<ul>
<li>런타임 환경에서 다양한 도움, 특히 메모리 관리를 자동으로 해주기 때문에 메모리 누수의 문제에서 보다 자유롭다.<br/>코드가 런타임환경에 의존하므로 하드웨어나 OS에 종속되지 않는다.</li>
</ul>
</li>
<li>단점<ul>
<li>중간 매개체를 두는 만큼 성능적인 부분에서 손실이 날 수 밖에 없다.</li>
<li>GC에 의해 의도치 않은 오작동이 발생할 위험성이 존재한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>위의 설명대로 각각의 방법들은 장점과 단점을 가지고 있다. 안정성을 높인 대신 속도와 자유도를 포기했고, 자유도를 높인대신 안정성을 포기했다. rust는 이러한 두 관리방법의 장점을 모두 가지기 위해 &#39;소유권&#39; 이라는 제 3의 방법을 선택했다고 볼 수 있다.</p>
<h3 id="소유권ownership에-대해">소유권(ownership)에 대해</h3>
<p>먼저 소유권이 어떤 시스템인지 공식 가이드 설명을 참고해보자</p>
<blockquote>
<p>모든 프로그램은 실행하는 동안 컴퓨터의 메모리를 관리해야 한다. 어떤 언어는 실행하는 동안 사용하지 않는 메모리를 계속 추적해서 관리하는 garbage collection을 사용하기도 하고, 어떤 언어는 직접 메모리를 할당하고 풀어줘야 한다. Rust는 또 다른 방식을 사용한다: 컴파일 시점에서 컴파일러가 체크하는 ownership 시스템의 규칙을 통해 메모리를 관리한다. 따라서 ownership은 프로그램의 런타임 성능을 저하시키지 않는다.
출처 : <a href="https://rinthel.github.io/rust-lang-book-ko/ch04-01-what-is-ownership.html">https://rinthel.github.io/rust-lang-book-ko/ch04-01-what-is-ownership.html</a></p>
</blockquote>
<p>공식가이드에서도 설명하듯이 위에서 설명한 2가지 방법이 아닌 제 3의방법인 owenrship(소유권)을 사용한다고 말하고 있다.</p>
<p>자 그럼 이제 위의 설명대로 어떤 규칙을 통해 메모리를 관리하길래, 안전하면서 속도에 영향을 주지않는 방법을 사용하는지 알아보자.</p>
<h3 id="소유권의-규칙">소유권의 규칙</h3>
<p>본격적인 설명의 들어가기 앞서 이 3가지의 설명을 잘 기억하면서 다음 글을 읽으면 이해가 훨씬 편하다.</p>
<ul>
<li>rust가 다루는 각각의 값은 소유자(owner)라고 부르는 변수를 가지고 있다.</li>
<li>특정 시점에 값의 소유자는 단 하나뿐이다.</li>
<li>소유자가 범위를 벗어나면 그 값은 제거된다.</li>
</ul>
<p>소유자의 설명에 앞서 programming laguage가 rust로 처음 입문한 사람이라면 &#39;범위&#39;가 어떤의미 인지 모를 수 있기 때문에 이 부분을 먼저 정의하겠다.</p>
<p>자 여기 간단한 rust 코드가 있다.</p>
<pre><code class="language-rust">fn main(){

    let s= &quot;hello&quot;
}</code></pre>
<p>범위에 대한 설명을 하기전에 한번 생각해보자, 위의 코드에 선언된 &quot;hello&quot;를 저장한 변수 s를 접근할 수 있는 범위는 어디까지일까?</p>
<p>아마 다른 언어를 배운 사람들은 이미 짐작했겠지만 자세히 정답을 적어보면 이렇게 적을 수 있다.</p>
<pre><code class="language-rust">fn main(){
                    //변수 s가 선언전이기 때문에 사용할 수 없음
    let s= &quot;hello&quot;    //변수 s가 선언됨
                    //변수 s를 이용해 각종 동작을 실행 할 수 있다.
}//여기서 범위를 벗어나므로 더 이상 변수s에 접근 할 수 없다.</code></pre>
<p>간단히 말하면, &#39;변수 s가 선언됨 지점으로 부터 괄호가 끝나기 전까지&#39; 라고 말할 수 있다. 여기서 우리가 말하는 범위는 함수의 중괄호({})를 말하며 중괄호 안에 있으면 범위 안에있다고 말하고 중괄호 밖에 선언되면 범위 밖에있다 말한다.</p>
<p>그러므로 이 예제를 통해 알 수 있는 핵심은 다음 2가지이다.</p>
<ul>
<li>변수 s가 범위 안으로 들어오면 유효하다.</li>
<li>변수는 범위를 벗어나기 전까지 유효하다.</li>
</ul>
<p>이 설명을 바탕으로 String 타입에 대해 알아보도록 하자.</p>
<h3 id="string-타입">String 타입</h3>
<p>먼저 int와 String 두가지 타입의 차이를 정확히 알기 위해서는 스택과 힙에 대한 이해가 필요하지만 이 부분을 설명하기에는 너무 글이 길어지도 하니 설명이 잘된 글의 링크를 달아두겠다.
<a href="https://rinthel.github.io/rust-lang-book-ko/ch04-01-what-is-ownership.html">스택과 힙(rust 공식가이드)</a></p>
<p>앞으로 설명은 위의 글을 읽었다는 가정하에 진행될 예정이므로 꼭! 읽기바란다.</p>
<p>글을 읽고 온사람이라면, 기본적인 데이터들은 스택에서 관리가 된다는것을 이해했을것이다. 하지만 우리가 이제부터 알아볼 String은 스택이 아닌 힙에서 관리된다.</p>
<p>이전 예제에서 우리는 s라는 변수에 하드코딩한 &quot;hello&quot;라는 문자열 리터럴을 할당했다. 하지만 문자열 리터럴은 텍스트를 다루어야 하는 모든 경우에 적합한 방법은 아니다. 그 이유 중 하나는 문자열은 immutable하기 때문이다. 또 다른이유는 코드를 작성하는 시점에 필요한 모든 문자열 값을 알 수 없기 때문이다.</p>
<p>이런 경우에 우리는 String타입을 사용한다. 아 타입은 힙에 할당되므로 컴파일 시점에 알 수 없는 크기의 문자열을 저장할 수 있다. 다음과 같이 from 함수를 이용하면 문자열 리터럴을 이용해  String 인스턴스를 생성할 수 있다.</p>
<pre><code class="language-rust">    let s = String::from(&quot;hello&quot;);</code></pre>
<p>여기서 콜론(::)을 두개 사용하는건 명확한 타입을 명시해주는거라고 생각하면 된다.</p>
<p>이부분은 C++와 매우 비슷하기 때문에 C++경험이 있는 사람이라면 쉽게 적응할 수 있다.</p>
<p>자 이렇게 생성한 문자열은 이제 변경을 할 수 있다.</p>
<pre><code class="language-rust">let mut s = String::from(&quot;hello&quot;);//변경을 위한 변수는 무조건 mut키워드를 명시해줘야한다.
s.push_str(&quot;, world!&quot;)
println!(&quot;{}&quot;,s);</code></pre>
<p>이 예제를 보고 문자열 리터럴을 mut으로 선언하면 바꿀 수 있지 않을까 생각이 들 수도 있다.</p>
<pre><code>fn main() {
    let mut s = &quot;Hello&quot;;
    s.to_owned().push_str(&quot;, World!&quot;);
    println!(&quot;{}&quot;,s); 
}</code></pre><p>이런 코드를 작성하고 실제로 코드를 동작해보면 에러는 나오지 않지만, 우리가 원하는 결과를 얻을 수 없다는걸 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/moonseokchoi-kr/post/2c0332f5-f416-434c-834d-5f0d02ef8cb0/image.png" alt="어떻게하면 정상적으로 작동할 수 있을까요?"></p>
<p>이 코드가 정상적으로 작동하려면 결국은 String으로 받아야하기 때문에 변경할 예정이 있는 문자열 변수라면 그냥 String으로 선언하는게 깔끔하다는걸 알 수 있다.</p>
<p>그렇다면 왜 String은 변경이 가능하고, 문자열 리터럴은 변경이 불가능 한걸까?</p>
<h2 id="메모리와-할당">메모리와 할당</h2>
<p>이 글은 메모리의 구조를 설명하는 글이 아니기 때문에 자세히 설명할 수 는 없다. 프로그래밍 언어들은 일반적으로 하드 코딩된 문자열의 경우 최적화를 위해 미리 바이너리로 바꾸어버린다. 이게 가능한 이유는 이 문자열의 크기를 이미 알고 있고 이게 변하지 않을거라는(불변) 확신이 있기 때문이다.</p>
<p>그렇다면 String의 경우는 어떨까? String으로 선언된 문자열의 경우 그 크기가 변하지 않는다는 보장이 없다. 그렇기 때문에 별도의 메모리를 할당해야만 한다(우리는 이 메모리를 힙이라고 부르며, 이러한 할당을 동적할당이라고 한다)</p>
<p>이렇게 할당이라는 행위를 위해서는 두가지 조건이 필요하다.</p>
<ul>
<li>해당 메모리는 반드시 런타임에 운영체제에 요청해야한다.</li>
<li>String타입의 사용이 완료된 후에는 이 메모리를 운영체제에 다시 돌려줄 방법이 필요하다.</li>
</ul>
<p>감이 빠른 사람이라면 이 두가지 조건이 무엇을 말하는지 눈치챈 사람도 있을거라고 생각한다. 사실 이 두가지에 대해 rust가 어떻게 동작을 하는지 명확히 설명할 수 있다면 소유권을 거의 다 이해한것이나 마찬가지이다. </p>
<p>우리는 앞에서 다른 언어의 메모리 관리방법을 간단히 설명했고, 그때 2가지의 종류로 구분했다. &#39;프로그래머가 직접적으로 메모리를 관리해야하는 언어&#39;, &#39;GC가 관리를 해주는 언어&#39;</p>
<p>그리고 rust 제3의 방법을 사용한다고 했었다. 이 3가지의 방법을 위의 조건을 만족하기 위해 어떻게 동작하는지 확인해보자</p>
<ul>
<li>해당 메모리는 반드시 런타임에 운영체제에 요청해야한다.<ul>
<li>프로그래머가 직접적으로 메모리를 관리해야하는 언어, GC가 관리를 해주는 언어, rust<ul>
<li>사용자가 메모리 할당을 요청한다.(allocate, new, from...)</li>
</ul>
</li>
</ul>
</li>
<li>String타입의 사용이 완료된 후에는 이 메모리를 운영체제에 다시 돌려줄 방법이 필요하다.<ul>
<li>프로그래머가 직접적으로 메모리를 관리해야하는 언어<ul>
<li>사용자가 직접해준다.(delete, free...)</li>
</ul>
</li>
<li>GC가 관리해주는 언어<ul>
<li>사용자가 신경쓰지 않아도 GC가 알아서 쓰레기주소들을 정리해준다.</li>
</ul>
</li>
<li>rust<ul>
<li>범위가 벗어나는 순간 해제한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>우리는 앞에서 &#39;범위&#39;가 무엇을 의미하는지 이미 보았기 때문에 더 설명하지 않겠다.</p>
<p>어떤 타입의 변수가 범위를 벗어나면 drop함수를 호출해서 메모리를 해제한다(이 drop함수는 C++의 RAII패턴과 매우 유사하다. RAII패턴으로 구현된 것이 스마트 포인터이다)</p>
<p>이제 본격적으로 소유권이 실제 상황에서 어떻게 동작하는 지 알아보자</p>
<h3 id="1-이동move">1. 이동(Move)</h3>
<p>이전에 다른언어를 배워본 사람이라면 첫 단계부터 험난할 수도 있다. </p>
<pre><code class="language-rust">let x = 5;
let y= x;</code></pre>
<p>이런식으로 일반적인 정수형 데이터에 대해서는 우리가 아는 대로 복사가 진행된다. 그렇다면 String에 대해서도 이렇게 동작을 할까?</p>
<pre><code class="language-rust">let s = String::from(&quot;hello&quot;);
let s1 = s;</code></pre>
<p>이렇게 선언한뒤 s를 이용해 출력하고 하면 다음과 같은 오류를 볼 수 있을것이다.</p>
<p><img src="https://velog.velcdn.com/images/moonseokchoi-kr/post/88f3c8f2-a2ce-475b-a404-db419d5e0eaa/image.png" alt=""></p>
<p>이미 이동을 한뒤 값을 대여하고 있어 발생하는 오류라고 적혀있다. 어째서  rust에서는 복사를 허용하지 않았을까?</p>
<p>바로 이중해제에러 때문이다. </p>
<p>rust에서 에러해제 시점은 범위를 벗어났을때라는것을 기억할 것이다. 기본적으로 복사를 할경우 포인터의 길이와 용량만 복사를 한다. 데이터 자체를 복사할 경우 오버헤드가 그만큼 커져 성능이 떨어지기 때문이다.</p>
<p>이제 예제를 다시 살펴보자, s가 s1에게 주소를 복사해주었다면 어땠을까? s를 해제하고 s1을 해제하려는 순간, 메모리 오류가 발생한다. 이제 해제된 쓰레기 값에 대해 다시 해제 동작을 수행하므로 당연한 일이다.</p>
<p>그렇기 때문에 rust에서는 기본적으로 복사(copy)가 아닌 이동(move)를 원칙으로 하고 있다. </p>
<p>하지만 프로그램을 만들다보면 복사본이 필요할 수도 있다. 이런 경우를 대비해서 rust에서는 한가지 함수를 지원하는데, 이 함수가 바로 clone이다. clone을 통하면 데이터를 복사해 정상적으로 동작하게 할 수 있다.</p>
<h3 id="2-복사copy">2. 복사(Copy)</h3>
<p>앞에서 간단히 살펴보긴 했지만, rust가 복사를 지원하는 경우는 정해져있다.</p>
<ul>
<li>컴파일 시점에 실제 데이터의 크기를 알 수 있는 경우</li>
</ul>
<p>즉 문자열 리터럴, 정수형 데이터, boolean 같은 값들을 의미한다. 그렇다면 Copy는 어떻게 동작을 하는걸까?</p>
<p>문자열 리터럴, 정수형 데이터 등에는 Copy라는 trait가 특별한 특성을 제공해준다(이 부분은 나중에 따로 설명하겠다. 그냥 지금은 그렇구나 하고 넘어가자)
이 Copy는 아무곳에서 만들어서 쓸 수가 없는데 타입의 일부에 Drop 트레이트(trait)가 적용되어있으면 Copy trait를 사용할 수 없다. </p>
<p>예를 들어 새로운 타입을 정의하는데 그 안에 String같은 값을 사용해야한다면 그 타입은 Copy trait를 이용할 수 없게 되는것이다.</p>
<p>복사가 허용되는 타입은 다음과 같다.</p>
<ul>
<li>u32와 같은 정수형 타입</li>
<li>true와 false값만을 가지는 boolean</li>
<li>문자타입, char</li>
<li>f64같은 부동 소수점 타입</li>
<li>Copy 트레이트가 적용된 타입을 포함하는 튜플
EX.(i32,i32)</li>
</ul>
<h3 id="소유권과-함수">소유권과 함수</h3>
<p>함수에서 값을 받아오고, 파라미터로 전달하는 과정에서도 역시나 소유권이 적용되기 때문에 타입에 따라 복사나 이동이 발생한다. </p>
<p>일단 다음 예를 한번 봐보자</p>
<pre><code class="language-rust">fn main() {
    let s = String::from(&quot;hello&quot;);  // s가 스코프 안으로 들어왔습니다.

    takes_ownership(s);             // s의 값이 함수 안으로 이동했습니다...
                                    // ... 그리고 이제 더이상 유효하지 않습니다.
    let x = 5;                      // x가 스코프 안으로 들어왔습니다.

    makes_copy(x);                  // x가 함수 안으로 이동했습니다만,
                                    // i32는 Copy가 되므로, x를 이후에 계속
                                    // 사용해도 됩니다.

} // 여기서 x는 스코프 밖으로 나가고, s도 그 후 나갑니다. 하지만 s는 이미 이동되었으므로,
  // 별다른 일이 발생하지 않습니다.

fn takes_ownership(some_string: String) { // some_string이 스코프 안으로 들어왔습니다.
    println!(&quot;{}&quot;, some_string);
} // 여기서 some_string이 스코프 밖으로 벗어났고 `drop`이 호출됩니다. 메모리는
  // 해제되었습니다.

fn makes_copy(some_integer: i32) { // some_integer이 스코프 안으로 들어왔습니다.
    println!(&quot;{}&quot;, some_integer);
} // 여기서 some_integer가 스코프 밖으로 벗어났습니다. 별다른 일은 발생하지 않습니다.</code></pre>
<p>이 코드를 보고 왜 이렇게 동작을 하는지 설명을 할 수 있다면 이동과 복사에 대한 개념은 마스터 했다고 볼 수 있다.
(사실 주석을 참고하면 아주 쉽게 이해할 수 있다. 복잡해 보인다고 포기하지 말자)</p>
<h3 id="리턴값과-범위">리턴값과 범위</h3>
<pre><code class="language-rust">코fn main() {
    let s1 = gives_ownership();         // gives_ownership은 반환값을 s1에게
                                        // 이동시킵니다.

    let s2 = String::from(&quot;hello&quot;);     // s2가 스코프 안에 들어왔습니다.

    let s3 = takes_and_gives_back(s2);  // s2는 takes_and_gives_back 안으로
                                        // 이동되었고, 이 함수가 반환값을 s3으로도
                                        // 이동시켰습니다.

} // 여기서 s3는 스코프 밖으로 벗어났으며 drop이 호출됩니다. s2는 스코프 밖으로
  // 벗어났지만 이동되었으므로 아무 일도 일어나지 않습니다. s1은 스코프 밖으로
  // 벗어나서 drop이 호출됩니다.

fn gives_ownership() -&gt; String {             // gives_ownership 함수가 반환 값을
                                             // 호출한 쪽으로 이동시킵니다.

    let some_string = String::from(&quot;hello&quot;); // some_string이 스코프 안에 들어왔습니다.

    some_string                              // some_string이 반환되고, 호출한 쪽의
                                             // 함수로 이동됩니다.
}

// takes_and_gives_back 함수는 String을 하나 받아서 다른 하나를 반환합니다.
fn takes_and_gives_back(a_string: String) -&gt; String { // a_string이 스코프
                                                      // 안으로 들어왔습니다.

    a_string  // a_string은 반환되고, 호출한 쪽의 함수로 이동됩니다.
}</code></pre>
<p>만일 우리가 s를 takes_ownership 함수를 호출한 이후에 사용하려 한다면, 러스트는 컴파일 오류를 내준다. 이러한 정적검사는 실수를 방지하기 위해 마련된 기능이다.(아마 C나 C++ 개발을 해본사람이라면 이래서 rust 컴파일러를 사랑 할 수 밖에 없는것 같다.)</p>
<p>하지만.. 변수의 소유권의 규칙은 매번 같은 패턴을 따르기 때문에, 제대로 코딩을 하려면 모든 함수가 소유권을 확보하고 그 값을 리턴하는 방식으로 짜야하지만, 이는 매우 거추장 스러우며 짜증나는 작업이다. 예를 들어 함수에 전달한 값을 다시 사용하기 위해 매번 리헌 해줘야한다고 생각해봐라, 생각만 해도 짜증이 나지 않는가</p>
<p>물론 아래 예 처럼 튜플을 이용하면 그런 문제를 해결 할 수 있다. 하지만 이 방법을 보편적으로 이용하기에는 매우 불편하다.</p>
<pre><code class="language-rust">fn main() {
    let s1 = String::from(&quot;hello&quot;);

    let (s2, len) = calculate_length(s1);

    println!(&quot;The length of &#39;{}&#39; is {}.&quot;, s2, len);
}

fn calculate_length(s: String) -&gt; (String, usize) {
    let length = s.len(); // len()함수는 문자열의 길이를 반환합니다.

    (s, length)
}</code></pre>
<p>이러한 문제를 해결하는 기능은 참조(Reference)이다. 참조부터는 다음글에서 이어서 적도로 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Graphics Rendering Pipeline]]></title>
            <link>https://velog.io/@moonseokchoi-kr/Graphics-Rendering-Pipeline</link>
            <guid>https://velog.io/@moonseokchoi-kr/Graphics-Rendering-Pipeline</guid>
            <pubDate>Mon, 28 Feb 2022 01:35:27 GMT</pubDate>
            <description><![CDATA[<h3 id="real-time-rendering-책을-통해-공부한-내용을-정리했습니다-부족하거나-잘못된-점은-댓글로-부탁드리겠습니다">Real Time Rendering 책을 통해 공부한 내용을 정리했습니다. 부족하거나 잘못된 점은 댓글로 부탁드리겠습니다.</h3>
<h2 id="graphics-rendering-pipeline">Graphics Rendering Pipeline?</h2>
<ul>
<li>간단한게 파이프라인이라고도 알려져있다.</li>
<li>주요 기능으로는 점을 생성하고 주어진 가상의 카메라로 3차원의 물체나 광원 등을 촬영해 2차원 이미지로 렌더링한다.</li>
<li>이미지안에서 위치와 모양은 그들의 지오메트리, 환경적 특성, 카메라가 위치한 장소에의해 결정된다.</li>
<li>오브젝트는 마테리얼 속성이나, 광원, 텍스처, 셰이더등의 영향을 받는다.</li>
<li>파이프라인의 각단계는 병렬로 실행된다.</li>
</ul>
<h2 id="21-architecture">2.1 Architecture</h2>
<hr>
<ul>
<li>파이프라인은 여러개의 단계로 이루어져 있으며 앞단계의 결과에 의존하게된다.</li>
<li>파이프라인의 형태를 사용하는 이유는 비 파이프라인인 시스템을 n개의 파이프라인 단계를 가진 형태로 나누게 되면 속도의 향상을 기대할 수 있다.</li>
<li>실시간 렌더링의 파이프라인은 4단계 메인 스테이지를 갖는다<ul>
<li>[Application]</li>
<li>[Geometry Processing]</li>
<li>[Rasterization]</li>
<li>[Pixel Processing]</li>
</ul>
</li>
</ul>
<p><img src="https://images.velog.io/images/moonseokchoi-kr/post/79e4853a-a5b6-4397-82db-deffc58d797c/image.png" alt="Pipeline Stage"></p>
<ul>
<li>4단계 메인 스테이지 안에는 여러개의 서브스테이지로 구성되어있다. 자세한내용은 각 스테이지 설명을 참고하자.</li>
</ul>
<h2 id="22-application">2.2 Application</h2>
<hr>
<ul>
<li>Application 단계에서 발생하는 모든일은 개발자가 직접 제어할 수 있다.</li>
<li>개발자는 구현과 성능향상을 위한 수정등을 단계 전체에 대해 결정할 수 있다.</li>
<li>이 단계에서의 변경사항은 추후 단계의 성능에 영향을 미칠 수도 있다.<ul>
<li>예를들어 어플리케이션 단계에서 그릴 삼각형 수를 감소시킨다면 이전에 비해 후속단계들은 훨씬 빠른 결과를 나타낼수 있다.</li>
</ul>
</li>
<li>기본적으로 Application Stage는 CPU에서 동작하지만 GPU로 작업을 처리할 수도 있다. 이러한 모드를 Compute Shader라 부른다.</li>
<li>이러한 Compute Shader를 사용하는 이유는 GPU의 높은 병렬처리능력을 이용하기 위함이다.</li>
<li>어플리케이션 단계에서 가장 중요한 일 중 하나는 다음단계인 Geometry Processing을 하기 위한 rendering primitive(points, lines, and triangles) 정보를 전달해주는것이다.</li>
<li>어플리케이션 단계의 소프트웨어 기반 구현의 결과는 하위단계로 나누어져 있지 않지만 성능을 위해  여러개의 프로세서 코어에서 어플리케이션단계를 병렬적으로 호출할 수 있도록 할 수 있다.</li>
<li>어플리케이션 단계는 다른 파이프라인 단계에서 구현하기 힘든것들을 구현한다. 충돌을  감지한다거나, 부분적으로 컬링을 진행한다거나, 가속 알고리즘을 적용한다거나 이러한 부분은 어플리케이션 단계에서 진행한다.</li>
</ul>
<h2 id="23-geometry-processing">2.3 Geometry Processing</h2>
<hr>
<ul>
<li>Geometry Processing은 대부분의 삼각형 당 그리고 점 당 작업을 책임진다.</li>
<li>다음과 같은 작은 스테이지로 분류된다.<ul>
<li>[Vertex Shading]</li>
<li>[Projection]</li>
<li>[Clipping]</li>
<li>[Screen Mapping]</li>
</ul>
</li>
</ul>
<h3 id="231-vertex-shading">2.3.1 Vertex Shading</h3>
<hr>
<ul>
<li>가장 중요한 2가지 역할은 점의 위치를 계산하고, 프로그래머가 설정한 꼭짓점 출력데이터를 전달한다.(노말벡터나 텍스처 좌표)</li>
<li>전통적으로 물체에 대한 그림자를 계산할때 이 단계에서 진행했는데 이러한 프로그래밍가능한 계산 유닛을 Vertex Shader라고 부른다.</li>
<li>점의 위치를 계산하는 방법은 다음과 같다.<ul>
<li>각 모델들에게는 모델 좌표계라는 한 공간에 모델하나만 존재하는 좌표계가 존재한다.</li>
<li>이러한 모델들을 한 공간에 넣기 위해서는 변환을 거쳐야하는데 이러한 변환을 model transform이라고 부른다.</li>
<li>이 결과 점들은 월드 공간안에 있는 월드 좌표계로 위치가 바뀌게 된다.</li>
<li>그 다음은 공간을 찍고 있는 카메라가 존재할텐데 이 카메라에 를 원점으로 한 변환이 다시 일어나게 된다. 이걸하는 이유는 카메라를 중심으로 보이는 오브젝트와 안보이는 오브젝트나 잘려서 보이는 오브젝트를 표현하기 위해서이다.</li>
<li>이 과정을 진행할때 사용하는 API에 따라서 -z convention(OpenGL)이나 +z convention(DirectX)으로 진행된다.</li>
<li>이러한 과정을 view transform 이라고 부른다. 이 view tranform이 일어날때 투영(Projection), 절단(Clipping)과정이 포함되어있다.</li>
</ul>
</li>
</ul>
<pre><code>   ![View Transform](https://images.velog.io/images/moonseokchoi-kr/post/87b75801-a6e6-43ab-a108-2a63aab357ac/image.png)</code></pre><h3 id="232-optional--vertex-processing">2.3.2 Optional  Vertex Processing</h3>
<hr>
<ul>
<li>Tessellation Stage<ul>
<li>hull shader, tessellator, domain shader로 구성</li>
<li>모델이 위치하고 있는 거리나 공간에 따라 삼각형의 세부적인 모델링 수준을 설정할 수 있는 스테이지(거리가 멀면 적은 삼각형으로 흐릿하게 그리고 가까우면 가까울수록 많은삼각형으로 자세히 그림)</li>
</ul>
</li>
<li>Geometry Shader<ul>
<li>다양한 렌더링 프리미티브들을 정렬하고 새로운 점을 추가할 수 있는 단계(주로 폭죽같이 파티클을 만들때 사용)</li>
</ul>
</li>
</ul>
<h3 id="233-clipping">2.3.3 Clipping</h3>
<hr>
<ul>
<li>투영 행렬을 통해 렌더링 프리미티브들을 상자 안으로 위치를 바꾸는 과정</li>
<li>이 과정을 통해 렌더링 프리미티브들은 뷰 좌표계에서 투영 좌표계로 좌표계가 변경된다.</li>
<li>이 때 좌표계는 (-1,-1,-1) 에서 (1,1,1)로 제한된다.</li>
</ul>
<h3 id=""></h3>
<h2 id="24-rasterization">2.4 Rasterization</h2>
<hr>
<ul>
<li>변환과 투영을 통해 윗 단계에서 제공받은 데이터에서 모든 픽셀을 찾아내는 걸 목적으로 두는 단계이다.</li>
<li>기본적으로 렌더링 프리미티브이 가지고 있는 점들에 대해 진행한다.</li>
<li>다음과 같은 하위단계를 가진다.<ul>
<li>Triangle Setup : 데이터로 부터 삼각화를 진행하는 단계</li>
<li>Triangle Traversal : 삼각화가 진행된 데이터로 부터 점들을 계산하는 단계 이 단계를 통해 색을 칠하기 위한 사전 작업을 마친다.</li>
</ul>
</li>
</ul>
<h2 id="25-pixel-processing">2.5 Pixel Processing</h2>
<hr>
<ul>
<li>삼각화를 통해 계산된 점들에 대해 색을 칠하는 단계이다.</li>
<li>다음과 같은 하위 단계가 있다.<ul>
<li>Pixel Shading : Shader를 통해 점에 입힐 색을 구하는 단계 프로그래머가 모두 프로그래밍이 가능한 단계이다. DirectX는 Pixel Shader라 부르고 OpenGL 은 fragment shader라 부른다.</li>
<li>Merging : 입력된 색에 대해 블렌딩을 진행하는 단계이다. 대표적으로 알파 블렌딩이 있으며 블렌딩까지 적용하여 최종적인 결과물을 만들어내는 단계이다  .</li>
</ul>
</li>
</ul>
<hr>
<p>지금은 간단한 전체적인 개요에 대해서만 소개를 했다. 다음 포스트 부터 하나하나 자세히 각각의 단계에 대해 소개해볼 예정이다. 물론 모든 스테이지에 대해서할 생각은 아니고 좀 어렵다고 생각되어지는 스테이지에 대해 진행할 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Direct X Voxel Engine에 대한 생각]]></title>
            <link>https://velog.io/@moonseokchoi-kr/Direct-X-Voxel-Engine%EC%97%90-%EB%8C%80%ED%95%9C-%EC%83%9D%EA%B0%81</link>
            <guid>https://velog.io/@moonseokchoi-kr/Direct-X-Voxel-Engine%EC%97%90-%EB%8C%80%ED%95%9C-%EC%83%9D%EA%B0%81</guid>
            <pubDate>Wed, 19 Jan 2022 11:59:14 GMT</pubDate>
            <description><![CDATA[<p>원래는 무작정 Voxel engine을 만들고 싶어서 시작을 했다.</p>
<p>하지만 생각해보니 Direct 2D Engine을 이미 만들어 둔것이 있었다.</p>
<p><a href="https://github.com/moonseokchoi-kr/Direct2DEngine">https://github.com/moonseokchoi-kr/Direct2DEngine</a></p>
<p>Voxel Engine을 만들고 싶다면 이것을 발전시키는 방향이 더 맞다는 생각이 들었다.</p>
<p>오늘 본 면접에서 면접관님이 <strong>&quot;Window Denpendency가 매우높게 코드를 짰던데 이유가 있어요?&quot;</strong>
라는 질문에 머리를 강하게 맞은듯 했다.</p>
<p>그동안 cross-platform에 대해 중요하게 생각한다고 느꼈는데, 정작 코드에서는 그러한 부분을 배제한체 개발을 하고 있었다.</p>
<p>아무리 학원에서 DirectX11으로 수업을 듣고선 이것만 하면 취업을 할 수 있다는 생각이었는지 <strong>내 개인적인 커리에 대해서는 고민을 하지 않았던것이었다.</strong></p>
<p>내가 원래 목표로 했던것이 무엇있었는지, 내가 만들고 싶던게 어떤것이었는지에 대해 고민을 하며 돌아보았다.</p>
<p>게임도 좋았지만, 원래 난 렌더링에 더 관심이 많고 렌더링 관련한 기술을 만들어보고 연구를 해보고 싶었다. 그래서 Blender 같은 프로그램을 만들기 꿈꿔왔던것 같다.</p>
<p>따라서, 기존의 Voxel Engine의 방향을 틀어 cross-platform에서 동작하는 engine으로 만들어볼 예정이다.</p>
<p>현재 계획 중인 기능은 다음과 같다</p>
<blockquote>
<ul>
<li>물리기반 렌더링</li>
</ul>
</blockquote>
<ul>
<li>애니메이션</li>
<li>프리팹 기능</li>
<li>Scripting</li>
<li>voxel 지형 생성기능</li>
</ul>
<p>이렇게 될경우 directX를 사용할 수 없기 때문에 vulkan이나 opengl중에 하나로 작업을 진행하게 될것같다.</p>
<p>그래서 다음 글 부터는 그래픽 라이브러리 개념부터 다시 공부하는 것으로 찾아올것 같다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DirectX 11로 Voxel Engine 만들기] 2일차]]></title>
            <link>https://velog.io/@moonseokchoi-kr/DirectX-11%EB%A1%9C-Voxel-Engine-%EB%A7%8C%EB%93%A4%EA%B8%B0-2%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@moonseokchoi-kr/DirectX-11%EB%A1%9C-Voxel-Engine-%EB%A7%8C%EB%93%A4%EA%B8%B0-2%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 07 Jan 2022 15:20:37 GMT</pubDate>
            <description><![CDATA[<h2 id="진행사항">진행사항</h2>
<h3 id="pathmanager-제작">PathManager 제작</h3>
<p>PathManager를 만든이유는 셰이더같은 리소스들을 편하게 관리하기 위해서이다.
보통 리소스들은 리소스용 폴더에 모아놓고 사용을 하기때문에 PathManager로 해당 path를 쉽게 가져올 수 있도록 제작해두면 매우 편하다.</p>
<p>또한 파일 경로에서 상대경로만 알고 싶을때도 있고, 경로에 대한 여러가지 작업을 해야할 수도 있기때문에 이러한 매니저 클래스를 만든다.</p>
<pre><code>//header
class PathManager :
    public Singleton&lt;PathManager&gt;
{
    SINGLE(PathManager);
public:
    void Init();
    const wstring&amp; GetContentPath() { return content_path_; }

    wstring GetRelativePath(const wstring&amp; _filepath);
private:
    wstring        content_path_;
};

//cpp
#include &quot;pch.h&quot;
#include &quot;PathManager.h&quot;
#include &lt;boost/filesystem.hpp&gt;
using boost::filesystem::current_path;
using boost::filesystem::path;


PathManager::PathManager()
{

}

PathManager::~PathManager()
{

}


void PathManager::Init()
{
    path fullPath = current_path();
    fullPath += L&quot;\\content\\&quot;;
    content_path_ = fullPath.wstring();
}

wstring PathManager::GetRelativePath(const wstring&amp; _filepath)
{
    wstring relativePath = _filepath.substr(content_path_.size(), _filepath.size() - content_path_.size());
    return relativePath;
}</code></pre><p>Boost에 있는 FileSystem library를 이용해 경로를 가져왔다. 그리고 리소스용 폴더를 추가적으로 지정해서 셰이더를 쉽게 가져올수있는 content path를 만들었다.</p>
<p>참고로 PathManager는 초기화 순서가 거의 1순위라고 생각해야한다.(그래야 리소스들과 각종 셰이더를 불러올수있다.)</p>
<h3 id="셰이더-코드-작성">셰이더 코드 작성</h3>
<p>본격적으로 렌더링을 하기전에 먼저 셰이더 코드를 간단히 작성했다.</p>
<pre><code>//std_vertex_shader.hlsl
#ifndef _STD_SHADER
#define _STD_SHADER

struct VertexInput
{
    float3 position : POSITION;

    float4 color : COLOR;
};

struct VertexOutput
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};

VertexOutput vs_main(VertexInput input)
{
    VertexOutput output = (VertexOutput) 0;
    output.position = float4(input.position, 1.f);
    output.color = input.color;


    return output;
}
#endif</code></pre><p>아직 카메라가 없기 때문에 정점 정보를 그대로 출력을 해주도록 작성했다.
나중에 변환을 적용해야한다. </p>
<pre><code>//std_pixel_shader.hlsl
struct VertexOutput
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};


float4 ps_main(VertexOutput input) : SV_TARGET
{
    float4 outputColor = (float4) 0;
    outputColor = float4(1.f, 0.f, 0.f, 1.f);
    return outputColor;
}</code></pre><p>그래픽파이프라인을 거쳐서 나온 영역에 붉은 색을 칠하도록 코드를 작성했다.</p>
<h3 id="그래픽-파이프-라인">그래픽 파이프 라인</h3>
<p>셰이더를 작성하고 그래프 파이프라인을 설명하는 이유는 개인적으로 공부를 할때 쭉 설명을 듣는것보단 연결지어 생각하는게 머리에 더 잘 남기 때문이다.
<img src="https://images.velog.io/images/moonseokchoi-kr/post/051d5ecc-3242-4573-b24b-db963cbf9f84/image.png" alt="directX Graphics pipeline"></p>
<p>위 그림들의 파이프라인 스테이지를 정리한 내용이다. 위 이미지에서 사각형들은 고정 스테이지로 프로그래밍이 불가능한 부분을 말하고 타원형 부분들은 프로그래밍이 가능하다.</p>
<ul>
<li>Input Assembler<ul>
<li>입력에 대한 준비를 하는 단계로, 정점 버퍼, 픽셀버퍼등의 메모리를 준비하고 연결하는 단계이다. 그리고 렌더링에 필요한 데이터를 전달한다.</li>
</ul>
</li>
<li>Vertex Shader Stage<ul>
<li>어셈블러에서 정점을 처리 하 고 변환, 스키닝, 모핑 및 정점 별 조명 등의 정점 작업을 수행 한다.</li>
<li>정점 셰이더는 항상 단일 입력 정점에 대해 작업을 수행 하고 단일 출력 정점을 생성 한다.</li>
</ul>
</li>
<li>Hull Shader Stage<ul>
<li>각 입력 패치 (쿼드, 삼각형 또는 선)에 해당 하는 기 하 도형 패치 (및 패치 상수)를 생성 하는 프로그래밍 가능한 셰이더 단계</li>
</ul>
</li>
<li>Tesellation Stage<ul>
<li>기하 도형 패치를 나타내는 도메인의 샘플링 패턴을 만들고 이러한 예제를 연결 하는 작은 개체 (삼각형, 요소 또는 선) 집합을 생성 하는 고정 함수 파이프라인 단계</li>
</ul>
</li>
<li>Domain Shader<ul>
<li>각 도메인 샘플에 해당 하는 꼭 짓 점 위치를 계산 하는 프로그래밍 가능한 셰이더 단계입니다.</li>
</ul>
</li>
<li>Geometry Shader<ul>
<li>꼭짓점을 입력으로 사용하여 애플리케이션 지정 셰이더 코드를 실행하고 출력 시 꼭짓점을 생성하는 기능을 실행</li>
<li>주로 파티클 기능을 만드는데 사용</li>
</ul>
</li>
<li>Rasterizer<ul>
<li>기본 형식은 픽셀로 변환 되 고 각 기본 형식에는 정점 별 값을 보간 합니다.</li>
<li>래스터화에는 뷰에 클리핑 꼭지점이 포함 됩니다. 즉, 원근감을 제공 하기 위해 z로 나누기를 수행 하고, 기본 형식을 2D 뷰포트에 매핑하고, 픽셀 셰이더를 호출 하는 방법을 결정 합니다</li>
</ul>
</li>
<li>Pixel shader<ul>
<li>화면의 색상을 지정하는 셰이더 선택된 타겟의 해당하는 픽셀의 색상을 지정한다.</li>
<li>여기서 지정된 색상을 통해 BlendState에서 보간을 진행하고 최종색상이 지정된다.</li>
</ul>
</li>
<li>Output Merge State Stage<ul>
<li>Depth Stencil State<ul>
<li>깊이 텍스쳐에 따라 물체의 깊이를 평가하는 단계</li>
<li>Direct에서는 z축의 값에 따라 깊이를 평가해 깊이가 얕은부분을 그리고 깊은 부분은 그리지 않는다.</li>
<li>그렇기 때문에 물체가 곂쳐지게 되면 깊이가 앝은 물체가 보이게 된다.</li>
<li>이러한 깊이버퍼덕분에 렌더링 순서에 영향을 받지 않는다.</li>
</ul>
</li>
<li>Blend State<ul>
<li>색상을 조합하는 단계 보간을 통해 메쉬의 색상을 결정한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>우리는 프로그래밍 가능한 부분중 VertexShader와 PixelShader에 대해서만 작성을 했다.
기본적인 삼각형을 그리는데는 이 두가지정도면 충분하기 때문이다.</p>
<p>Input Assembly를 제외한 고정스테이지의 경우 기본설정값이 있기 때문에 별도의 설정을 하지 않아도 렌더링을 진행하는데 큰 문제는 없다.</p>
<p>그렇기 때문에 우리는 그릴 삼각형좌표의 위치, 색상에 대한 정보만 입력을 하도록 코드를 만들어 주면 된다.</p>
<h3 id="붉은-삼각형-그리기">붉은 삼각형 그리기</h3>
<p>이제 삼각형을 그리기 위한 준비가 끝났다. 기본적으로 렌더링 진행과정은 다음과 같다</p>
<blockquote>
<p>정점버퍼 만들기, 인덱스(색인)버퍼만들기 -&gt; 셰이더 컴파일 -&gt; 레이아웃지정 -&gt; InputAssambly에 정점버퍼와 인덱스버퍼, 레이아웃 세팅-&gt; 셰이더 세팅-&gt; 드로우 콜</p>
</blockquote>
<p>이러한 과정을 거치게 된다. 다음 코드들은 위의 과정을 하나씩 진행한 코드들이다.</p>
<ul>
<li><p>버퍼만들기</p>
<pre><code>  // 삼각형 하나 만들기
  array&lt;Vertex, 4&gt; arrVTX = {};

  // 투영 좌표계 기준
  arrVTX[0].position = Vec3(-0.5f, 0.5f, 0.5f);
  arrVTX[0].color = Vec4(1.f, 1.f, 1.f, 1.f);

  arrVTX[1].position = Vec3(0.5f, 0.5f, 0.5f);
  arrVTX[1].color = Vec4(1.f, 1.f, 1.f, 1.f);

  arrVTX[2].position = Vec3(0.5f, -0.5f, 0.5f);
  arrVTX[2].color = Vec4(1.f, 1.f, 1.f, 1.f);

  arrVTX[3].position = Vec3(-0.5f, -0.5f, 0.5f);
  arrVTX[3].color = Vec4(1.f, 1.f, 1.f, 1.f);

  //버퍼 정보 작성
  D3D11_BUFFER_DESC desc = {};

  desc.ByteWidth = sizeof(Vertex) * (UINT)arrVTX.size();
  desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
  desc.Usage = D3D11_USAGE_DEFAULT;
  desc.CPUAccessFlags = 0;

  D3D11_SUBRESOURCE_DATA sub = {};
  sub.pSysMem = arrVTX.data();

  if (FAILED(DEVICE-&gt;CreateBuffer(&amp;desc, &amp;sub, g_vertex_buffer_.GetAddressOf())))
  {
      MessageBox(nullptr, L&quot;Vertex Buffer 생성실패&quot;, L&quot;Engine 초기화 실패&quot;, MB_OK);
      return E_FAIL;
  }

  //index buffer
  array&lt;UINT, 6&gt; indexArray = { 0,1,2,2,3,0 };
  desc = {};
  desc.ByteWidth = sizeof(UINT) * (UINT)indexArray.size();
  desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
  desc.Usage = D3D11_USAGE_DEFAULT;
  desc.CPUAccessFlags = 0;

  sub = {};
  sub.pSysMem = indexArray.data();

  if (FAILED(DEVICE-&gt;CreateBuffer(&amp;desc, &amp;sub, g_index_buffer_.GetAddressOf())))
  {
      MessageBox(nullptr, L&quot;Index Buffer 생성실패&quot;, L&quot;Engine 초기화 실패&quot;, MB_OK);
      return E_FAIL;
  }</code></pre><p>삼각형이라고 해놓고 왜 점을 4개나 저장을 했는지 의문이 들수도 있다. 일단 삼각형을 만들고 바로 사각형도 테스트 해보기 위해 4개를 저장했다.</p>
</li>
</ul>
<p>인덱스버퍼를 사용하는 이유는 효율성때문인데. 만약 인덱스 버퍼없이 사각형을 그리게 된다면 6개의 점을 버퍼에 저장해야한다. 지금 Vertex구조체의 크기는 12+16+8= 36 바이트이다. 6개를 저장하면 총 216바이트가 소모가된다.</p>
<p>반면 인덱스버퍼는 int를 저장하기때문에 4바이트만 소모를한다. 점4개에 인덱스버퍼 6개(168바이트)를 지정하는것이 훨씬 메모리적인 측면에서 효율적이라는것을 알 수 있다. 그렇기때문에 인덱스버퍼를 이용해 드로우를 한다.</p>
<ul>
<li><p>셰이더 컴파일</p>
<pre><code>  //shader
  wstring path = PathManager::Get().GetContentPath();
  path += L&quot;shader\\std_vertex_shader.hlsl&quot;;
  if (FAILED(D3DCompileFromFile(path.c_str()
      , nullptr
      , D3D_COMPILE_STANDARD_FILE_INCLUDE
      , &quot;vs_main&quot;, &quot;vs_5_0&quot;
      , D3DCOMPILE_DEBUG, 0
      , g_vs_blob_.GetAddressOf()
      , g_error_blob_.GetAddressOf())))
  {
      if(nullptr != g_error_blob_)
          MessageBox(nullptr, (wchar_t*)g_error_blob_-&gt;GetBufferPointer(), L&quot;Shader 컴파일 실패&quot;, MB_OK);
  }
  path = PathManager::Get().GetContentPath();
  path += L&quot;shader\\std_pixel_shader.hlsl&quot;;
  if (FAILED(D3DCompileFromFile(path.c_str()
      , nullptr
      , D3D_COMPILE_STANDARD_FILE_INCLUDE
      , &quot;ps_main&quot;, &quot;ps_5_0&quot;
      , D3DCOMPILE_DEBUG, 0
      , g_ps_blob_.GetAddressOf()
      , g_error_blob_.GetAddressOf())))
  {
      if (nullptr != g_error_blob_)
          MessageBox(nullptr, (wchar_t*)g_error_blob_-&gt;GetBufferPointer(), L&quot;Shader 컴파일 실패&quot;, MB_OK);
  }

  DEVICE-&gt;CreateVertexShader(g_vs_blob_-&gt;GetBufferPointer(), g_vs_blob_-&gt;GetBufferSize(), nullptr, g_vs_.GetAddressOf());
  DEVICE-&gt;CreatePixelShader(g_ps_blob_-&gt;GetBufferPointer(), g_ps_blob_-&gt;GetBufferSize(), nullptr, g_ps_.GetAddressOf());</code></pre><p>셰이더를 바로 생성하는게 아닌 파일에서 셰이더를 컴파일해서 그 정보를 Blob이라고하는 포인터 덩어리에 저장을한다.
그다음 셰이더를 생성하는 과정을 진행한다. 여기서 중요한 것은 진입함수이름을 꼭 셰이더에 작성한 함수이름으로 맞추어 줘야한다는점이다.</p>
</li>
<li><p>레이아웃 생성</p>
<pre><code>D3D11_INPUT_ELEMENT_DESC g_layout[3] =
{
  D3D11_INPUT_ELEMENT_DESC {&quot;POSITION&quot;,0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA, 0},
  D3D11_INPUT_ELEMENT_DESC {&quot;COLOR&quot;,0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA, 0},
  D3D11_INPUT_ELEMENT_DESC {&quot;TEXCOORD&quot;,0,DXGI_FORMAT_R32G32_FLOAT,0,28,D3D11_INPUT_PER_VERTEX_DATA, 0},
};
if (FAILED(DEVICE-&gt;CreateInputLayout(g_layout, 3, g_vs_blob_-&gt;GetBufferPointer(), g_vs_blob_-&gt;GetBufferSize(), g_input_layout_.GetAddressOf())))
{
  MessageBox(nullptr, L&quot;layout 생성실패&quot;, L&quot;Engine 초기화 실패&quot;, MB_OK);
  return E_FAIL;
}</code></pre></li>
</ul>
<p>레이아웃은 셰이더에 있는 Sementic의 정보를 셰이더에 알려주는 역할을 한다. 이것이 작성되지 않으면 셰이더에서 정확한 정보를 읽어들일 수 없기때문에 자신의 셰이더에 맞게 꼭 작성해주어야한다.</p>
<ul>
<li>삼각형 렌더링<pre><code>DirectDevice::Get().ClearTarget();
</code></pre></li>
</ul>
<p>//Render Code
UINT stride = sizeof(Vertex);
UINT offset = 0;</p>
<p>CONTEXT-&gt;IASetVertexBuffers(0, 1, g_vertex_buffer_.GetAddressOf(), &amp;stride, &amp;offset);
CONTEXT-&gt;IASetIndexBuffer(g_index_buffer_.Get(), DXGI_FORMAT_R32_UINT, 0);</p>
<p>CONTEXT-&gt;IASetInputLayout(g_input_layout_.Get());
//점들을 어떻게 연결해서 그릴지
CONTEXT-&gt;IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);</p>
<p>CONTEXT-&gt;VSSetShader(g_vs_.Get(), nullptr, 0);
CONTEXT-&gt;PSSetShader(g_ps_.Get(), nullptr, 0);</p>
<p>CONTEXT-&gt;DrawIndexed(6, 0, 0);</p>
<p>DirectDevice::Get().Present();</p>
<pre><code>여기서 설명을 하지않은건 Topology(위상구조)에 대해서인데 간단히 말하면 들어온 점들을 어떻게 연결해 그릴지에 정하는것이다. 크게 점, 삼각형, 라인이 있으며 그려야하는 모양에 맞게 지정하면된다.

그다음에는 DrawIndexed를보면 이전에 우리가 6개의 인덱스를 지정했기 때문에 크기를 6으로 잡아두었는데 만약 삼각형을 그리고 싶다면 3으로 바꿔주면된다.

### 오늘의 작업결과
+ 삼각형
![](https://images.velog.io/images/moonseokchoi-kr/post/4a4d70db-9872-44ea-9841-341b20285281/image.png)
+ 사각형
![](https://images.velog.io/images/moonseokchoi-kr/post/2865a506-1f23-442b-8682-db9cc9d54758/image.png)


### 추후 진행사항

+ 상수버퍼
+ TimeManager
+ KeyMananger

아마 위 3가지에 대해 작업을 할 예정이다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[DirectX 11로 Voxel Engine 만들기] 도전 시작]]></title>
            <link>https://velog.io/@moonseokchoi-kr/DirectX-11%EB%A1%9C-Voxel-Engine-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EB%8F%84%EC%A0%84-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@moonseokchoi-kr/DirectX-11%EB%A1%9C-Voxel-Engine-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EB%8F%84%EC%A0%84-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Thu, 06 Jan 2022 14:19:47 GMT</pubDate>
            <description><![CDATA[<h2 id="도전을-시작하면서">도전을 시작하면서</h2>
<p>8개월간 게임학원에서 열심히 공부를 하다가 어느 순간부터 자신의 코드가
아닌 남의 코드가 있어야만 개발을 하는 모습을 발견하게 되었다. 강사님이 제공해주는 코드에 너무 익숙해진 나머지
누군가 해결해서 알려주겠지라는 생각을 하게 된것이다.</p>
<p>이 상태라면 학원을 졸업해서 취업에 성공한다고 해도 내가 능력적인 부분에서 많이 부족할거라는 생각이 들었다.
비록 남들보다 돌아가는 미련한 방법이라고 해도, 학원을 잠시쉬고 다시 처음부터 DirectX 11를 공부하고 계속 만들었던 
2D가 아닌 3D에 도전해보려고한다.</p>
<p>최종적인 목표는 예전부터 관심이 많았던 Voxel 기반의 Game Engine을 만들어보는것이다.</p>
<p>쉽지 않을 목표이지만 계획을 시작할때 나 자신에게 말한것처럼 매일 다짐하려고 한다. </p>
<p><strong>&quot;넌 잘하지 않아, 그러니까 맘편히 먹고 천천히 한걸음씩 나아가, 한번에 모든걸 이해할 수 없으니까&quot;</strong></p>
<p>항상 내 발목을 붙잡았던 자만심을 버리고 이번에는 꼭 도전에 성공할 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DirectX 11로 Voxel Engine 만들기] 1일차]]></title>
            <link>https://velog.io/@moonseokchoi-kr/DirectX-11%EB%A1%9C-Voxel-Engine-%EB%A7%8C%EB%93%A4%EA%B8%B0-1%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@moonseokchoi-kr/DirectX-11%EB%A1%9C-Voxel-Engine-%EB%A7%8C%EB%93%A4%EA%B8%B0-1%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 06 Jan 2022 14:19:26 GMT</pubDate>
            <description><![CDATA[<p>1일차에는 다음과 같은 내용을 진행했다.</p>
<ul>
<li>프로젝트 생성과 디렉토리정리및 프로젝트설정</li>
<li>DirectX 11 Device Class제작</li>
<li>Engine Core Class 기본 골격 제작</li>
<li>Sigleton Template Class 제작</li>
<li>화면 클리어 작업 진행</li>
</ul>
<h3 id="프로젝트-관련-작업">프로젝트 관련 작업</h3>
<p> 일단 작업을 시작해야하니 프로젝트를 생성했다.
 Client 프로젝트는 어플리케이션으로, Engine은 정적 라이브러리로 만들었다.
 그후 각각 파일들을 출력할 디렉토리를 정리했다. 
 자세히 적을까 하다가, 사실 이건 사람마다 개인취향이라고 생각해 굳이 정리하는 의미가 없다는 생각이 들었다. </p>
<h3 id="sigleton-template-class-제작">Sigleton Template Class 제작</h3>
<p> main.cpp에서 게임루프를 만들어주고 나서 한 일은 Sigleton 클래스를 만드는 일이었다.</p>
<p> 매니저급 클래스에서는 대부분 사용하기때문에 굉장히 자주 이용하는 디자인패턴중 하나이다. 그래서 상속을 이용해 만들수 있도록
 sigleton패턴을 가진 부모클래스를 구현하기로 했다.</p>
<pre><code class="language-c++">template&lt;typename T&gt;
class Singleton
{
public:
    static T&amp; Get()
    {
        static T instance;
        return instance;
    }
private:
    Singleton()
    {}
    ~Singleton() {}
};</code></pre>
<blockquote>
<p>여기서 템플릿으로 구현한 이유는 singleton의 패턴의 특징때문이다. private나 내가 작성한 방식으로 instance를 캡슐화 해버리기 때문에 일반적인 클래스로 구현하면 객체를 가져올 수가 없게된다.또 다형성이있다고해도 상속하고 실제 객체를 가져오려면 매번 다운캐스팅을 해줘야한다는 문제점이있다.</p>
</blockquote>
<h3 id="directx11-device-class-제작">DirectX11 Device Class 제작</h3>
<p>위에서 만든 singleton 클래스를 상속받아 제작을 했다.
풀코드를 올리기에는 너무 코드량이 많아서 헤더파일만 적어 구조에 대해 설명하겠다.</p>
<pre><code>#include &quot;singleton.h&quot;
class DirectDevice :
    public Singleton&lt;DirectDevice&gt;
{
    SINGLE(DirectDevice);

public:
    HRESULT InitDevice(HWND _mainHwnd, Vec2 _resolution);
    void ClearTarget();
    void Present();
private:
    HRESULT CreateSwapChain();
    HRESULT CreateView();
    HRESULT CreateRasterizerState();
    HRESULT CreateDepthStencilState();
    HRESULT CreateSampler();

private:
    ComPtr&lt;IDXGIAdapter&gt; adapter_;
    ComPtr&lt;ID3D11Device&gt; device_;
    ComPtr&lt;ID3D11DeviceContext&gt; context_;

    ComPtr&lt;IDXGISwapChain&gt; swap_chain_;
    ComPtr&lt;ID3D11Texture2D&gt; depth_stencil_buffer_;

    ComPtr&lt;ID3D11Texture2D&gt; render_target_texture_;
    ComPtr&lt;ID3D11Texture2D&gt; depth_stencil_texture_;

    ComPtr&lt;ID3D11RenderTargetView&gt; render_target_view_;
    ComPtr&lt;ID3D11DepthStencilView&gt; depth_stencil_view_;

    D3D11_VIEWPORT view_port_;

    Vec2 resolution_;
    HWND hWnd_;
    bool window_;

};</code></pre><p>나중에 좀더 최적화가 필요하다고 생각하긴 하지만, 일단 화면을 띄울수 있는 수준의 초기화를 목적으로 두고 구현했다.</p>
<p>아직 블렌드나 깊이평가 상태, 샘플러같은것도 구현이 안되어있어서 추후 구현할 예정이다.</p>
<p>여기서 주목해야할것은 ComPtr이다.</p>
<p>ComPtr은 Microsoft에서 제공해주는 스마트포인터로 std::shared_ptr와 매우 유사한 성질을 가지고 있다. 해당 변수를 참조하게되면, 참조 카운트가 증가하게 되고, 참조를 마치면 다시 감소하게된다. 이 참조 카운트가 0이 되어야지만 소멸자가 작동을 한다.</p>
<p>Direct X를 설정할때 사용되는 클래스들은 참조과정중 지워지거나 삭제되는 일이 발생하면 안되기 때문에 Smart Pointer로 관리하는게 여러므로 좋다.</p>
<h3 id="core-class">Core Class</h3>
<p>Engine의 전체적은 루프를 담당하는 코어클래스 이다. 마찬가지로 싱클턴으로 제작했다.</p>
<pre><code>#include &quot;singleton.h&quot;
class EngineCore :
    public Singleton&lt;EngineCore&gt;
{
    SINGLE(EngineCore);

public:
    HRESULT Init(HWND  _hWnd, UINT _resolutionX, UINT _resolutionY);
    void Progress();
public:
    void ChangeWindowSize(UINT _x, UINT _y);
private:
    HWND main_hWnd_;
    Vec2 window_resolution_;
};</code></pre><p>현재는 아주 간단한 구조를 가지고 있지만, 나중에 많은 매니저들의 초기화와 업데이트가 진행될 중요한 중추 역할을 한다.</p>
<p>여기까지 보면 작업이 오래 걸릴리가 없는데 생각보다 오래걸린 이유는 오랜만에 설정을 하다보니 DirectX 의 설정을 잘못진행해 조금 꼬였기 때문이었다. 그걸 해결하고 난다음에  테스트 하면서 한가지 오류를 겪었는데, 바로 RenderTarget과 DepthStecil의 해상도가 달라서 clear가 되지 않는 문제였다.</p>
<p>이를 해결하기 위해, 윈도우가 초기화되고나서 다시 화면크기를 내가 정한 해상도에 맞도록 설정해주는 작업을 진행했다. 그 작업은 ChangeWindowSize()를 확인해보면 된다.</p>
<h3 id="main-loop">main loop</h3>
<pre><code>int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
...
    // 애플리케이션 초기화를 수행합니다:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    //Engine Initialize

    if (FAILED(EngineCore::Get().Init(g_hwnd, 1600, 900)))
    {
        MessageBox(nullptr, L&quot;Core 초기화 실패&quot;, L&quot;Engine 초기화 오류&quot;, MB_OK);
        return FALSE;
    }


HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_VOXELENGINE));

    MSG msg;

    // 기본 메시지 루프입니다:
    while (true)
    {
    //게임 루프
        if (PeekMessage(&amp;msg, nullptr, 0, 0, PM_REMOVE))
        {
            if (WM_QUIT == msg.message)
            {
                break;
            }
            if (!TranslateAccelerator(msg.hwnd, hAccelTable, &amp;msg))
            {
                TranslateMessage(&amp;msg);
                DispatchMessage(&amp;msg);
            }
        }
        else
        {
            EngineCore::Get().Progress();
        }

    }
    return (int)msg.wParam;


}</code></pre><p>메인 게임 루프 같은경우 기존 프로젝트를 생성했을때와 조금 다르게 만들어야한다.
게임 루프의 경우 Windows 작업이 진행되지 않는 시간동안 처리가 되기 때문에 PeekMessage값이 false일때 동작하도록 위와 같이 만든다.</p>
<p>이렇게 만드는 이유는 프로그램의 대부분이 windows 작업과 상관없시 사용되는 시간들이기 때문에 해당시간 동안 게임루프를 돌려 게임을 진행하고 이벤트가 발생하면 멈추고 끝나면 다시 게임이 진행된다.</p>
<h3 id="작업-진행-결과">작업 진행 결과</h3>
<p><img src="https://images.velog.io/images/moonseokchoi-kr/post/b8713070-b164-40fc-bfe4-b2ed86aade97/image.png" alt=""></p>
<h3 id="소감">소감</h3>
<p>오랜만에 다시 DirectX를 설정하려고 하니 머리가 하얗게 백지가 되어가는 경험을 느겼지만 하나하나 차근차근 하다보니 어느새 화면이 멋지게 나타났고, 그동안 그냥 지나쳤던 설정값하나하나의 의미를 다시한번 머릿속에 새길 수 있었던 좋은 경험이었다.</p>
]]></description>
        </item>
    </channel>
</rss>