본문 바로가기
Development/멋쟁이사자처럼 게임개발 부트캠프

[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 8일차 - Environment & Stopwatch & 클래스 & 값 형식과 참조 형식 & 문자열 다루기 & 예외 처리 & List & Null

by jjeondeuk1008 2025. 3. 11.

[ 목차 ]

     


     

    오늘은 상당히~~~~ 많을 것이다..

     

    제목부터 알 수 있듯이 저것을 다 기록할 것이다..

     

    그래도 하나마다 내용이 길지 않고,

    이런 것이 있구나! 하고 알아가는 시간을 가지기 위함이다.

     

    양이 많아서 그런지 시작부터 아득하지만

    복습 of 복습하기 위해

    힘차게 시작합니다!

     

     


     

    1. Environment 클래스

     

    Environment 메서드를 사용해 프로그램을 강제 종료할 수 있다.

    이 이후에 아무리 코드를 입력해도 저 메서드에서 종료된다.

    static void Main(string[] args)
    {
        Console.WriteLine("Exiting the program...");
        Environment.Exit(0);
    }

     

     

     


     

     

    환경변수 (Environment Variables)

     

    환경 변수는 

    프로세스가 어떠한 작업을 할 때 필요로 하는 정보를 접근과 처리할 수 있게 가능하다.

     

    위의 Environment 클래스를 사용한다.

     

    환경 변수를 활용해 API를 사용할 때에도 쓰임이 있다.

    API를 환경 변수에 저장해 보안을 강화하고, 유출되는 것을 방지할 수 있다.

     

    그 외에도 다양한 방법으로 쓰임이 있을 수 있으니 알아두길 바란다!

     

    (1) 환경 변수를 읽기

    string? path = Environment.GetEnvironmentVariable("PATH");
    Console.WriteLine($"PATH: {path}");

     

     

    (2) 환경 변수를 설정하기

    Environment.SetEnvironmentVariable("MY_VARIABLE", "MyValue");

     

     

    (3) 환경 변수 삭제하기

    Environment.SetEnvironmentVariable("MY_VARIABLE", null);

     

     

     


     

    2. Stopwatch

     

    Stopwatch 클래스 프로그램이 실행되는 시간을 구하는 것이다.

     

    Stopwatch 객체를 생성하는 코드이다.

    Stopwatch stopwatch = new Stopwatch();

     

     

    반복문을 통해 계산을 지속적으로 호출하게 한다.

    Thread.Sleep(1)

    코드를 통해 계산이 너무 빠르기 때문에 0.001초씩 딜레이를 준다.

    이건 선택사항이다.

     

    stopwatch.Stop()

    코드를 통해 시간을 멈추는 것이다.

    //프로그램 실행 시간 구하기
    Stopwatch stopwatch = Stopwatch.StartNew();
    //실행코드
    for(int i = 0; i<100; i ++)
    {
        Thread.Sleep(1); //계산이 너무 빠르기에 0.001초씩 멈춘다.
    }
    
    stopwatch.Stop();
    
    Console.WriteLine($"Execution Time: {stopwatch.ElapsedMilliseconds} ms");
    

     

     

     


     

    3. 정규식 Regex

     

     

    패턴을 검색하거나 입력이 맞는지 검증하는 역할을 한다.

     

    주로 회원가입 및 로그인에 활용되는 경우가 많다.

    전화번호와 비슷한 패턴을 검색 및 검사를 잘할 수 있게 한다.

    ex) 전화번호, 이메일 등

    string input = "Hello, my phone number is 123-456-7890";
    
    //전화번호만 추출하기
    string pattern = @"^\\d{3}-\\d{3}-\\d{4}$"; //전화번호 패턴
    bool isMatch = Regex.IsMatch(input, pattern);
    
    Console.WriteLine($"전화번호가 존재하는가?: {isMatch}");
    

     

     

     


     

    4. 클래스 시그니처 기본구성

     

     

    클래스 시그니처는 클래스의 선언부를 의미한다.

    즉, 클래스의 구조와 구성요소를 정의하는 부분이다.

     

    클래스가 어떠한 데이터와 기능을 담고 있는지 나타낸다.

     

    [접근 지정자]                 [수정자]               class 클래스 이름 : 부모클래스, 인터페이스

    public(공개)                   abstract               클래스 이름       : BaseClass, IComparable
    private(비공개)               sealed
    protected(상속된)            static
                                           partial

     

     

    클래스 앞에 붙이는 접근 지정자 3가지

    전에 언급했던 public (공개)과 private (비공개),

    그리고 protected (상속된)이다.

    protected (상속된) : 해당 클래스와 파생 클래스에서 접근할 수 있다. 외부에선 접근 불가능

     

    수정자는 클래스의 기능과 특성을 추가하는 속성이다.

    • abstract : 클래스를 상속하는 자식 클래스에서 반드시 메서드를 구현해야 한다.
    • sealed : 파생 클래스로부터 상속할 수 없는 클래스를 정의한다. 해당 클래스를 더 이상 확장할 수 없다는 것이다.
    • static : 인스턴스(instance)를 만들지 않고 클래스 자체에서 사용하는 것이다.
    //6. 클래스 시그니처 기본구성
    //C#에서 클래스 시그니처는 클래스의 선언부를 의미한다.
    
    //기본클래스
    public class Player //부모
    {
        public string Name { get; set; }
        public int Score { get; set; }
    }
    //상속하는 클래스
    public class Warrior : Player //자식
    {
        public int Strength { get; set; }
    }
    //인터페이스 구현하는 클래스
    public class Enemy :  IAttackable, IMovable
    {
        public void Attack() { }
        public void Move() { }
    }
    
    //추상 클래스 (abstract)
    public abstract class Animal
    {
        public abstract void MakeSound();
    }

     

     

     


     

    5. 값 형식과 참조 형식

     

    값 형식(Value Type)참조 형식(Reference Type)

    데이터가 메모리에 저장되는 방식과 데이터 접근 방식에 따라 구분이 된다.

     

    스택에 저장되고, 참조 형식은 힙에 저장된다.

    //1. 값 형식과 참조 형식
    //스택에 저장되고, 참조형식은 힙에 저장된다.
    
    int valueType = 10;
    object referenceType = valueType;
    
    valueType = 20;
    
    Console.WriteLine($"ValueType: {valueType}"); //20
    Console.WriteLine($"Reference: {referenceType}"); //10
    

     

     

     


     

    박싱 언박싱

     

     

    박싱은 값 형식을 참조형식으로 변환하는 것이고

    언박싱은 참조형식을 다시 값 형식으로 변환하는 것이다.

     

    박싱을 하려면 object 변수를 써야 한다.

    object모든 데이터 타입의 슈퍼타입이다.

    각종 데이터 타입을 하나의 변수에 저장할 수 있도록 해주는 것이다.

     

    즉, 언박싱은 다시 object 변수로 변환한다는 것이다.

    //2. 박싱 언박싱
    //값 형식을 참조형식으로 변환(박싱) , 다시 값 형식으로 변환(언박싱)
    
    int value = 42;
    object boxed = value; //박싱
    int unboxed = (int)boxed; //언방식
    
    Console.WriteLine($"Boxed: {boxed}, Unboxed : {unboxed}");
    

     

     

     


     

    is 연산자 형식 비교

     

    객체가 특정 형식인지 확인할 수 있다.

    is 연산자를 통해 어떤 변수인지 알 수 있게 해 준다.

    //3. is 연산자 형식 비교하기
    //객체가 특정 형식인지 확인할 수 있다.
    object obj = "Hello";
    
    Console.WriteLine(obj is string); //true - 문자열이냐?의 형식 질문에 대한 답변
    Console.WriteLine(obj is int); //false - 정수형이냐? 형식 질문에 대한 답변
    

     

     

     


     

    as 연산자 형식 변환

     

    as 연산자로 안전하게 형 변환이 가능하다.

    //4. as 연산자로 형식 변환하기
    //안전하게 형 변환 가능
    
    object obj = "Helllo";
    string str = obj as string;
    
    Console.WriteLine(str is string);
    

     

     


     

    패턴 매칭

     

    패턴 매칭은 변수의 타입을 확인하고, 

    if 문is 연사자를 이용해서 패턴 매칭을 하는 것이다.

    //5. 패턴 매칭: if 문과 is연산자 사용
    var obj = 42;
    
    if(obj is int number)
    {
        Console.WriteLine($"Number: {number}");
    }
    else
    {
        Console.WriteLine("Not a number");
    }
    

     

     

     


     

     

    6. 문자열 다루기

     

    아래는 문자열을 다루는 다양한 방법을 보여주는 예시이다.

     

    문자열의 길이를 알 수 있는

    Length

     

    대문자로 변환할 수 있는

    ToUpper()

     

    부분 문자열을 출력할 수 있는

    Substring()

     

    길이와 대문자는 전에 했던 내용과 겹치기 때문에 복습 개념이고,

    부분 문자열은 예시로

    Substring(1)로 가정하면 인덱스 1 문자열부터 출력한다.

    고로 두 번째 문자부터 시작한다.

    이유는?

    0부터 시작하기 때문이다.

    //6. 문자열 다루기
    string greeting = "Hello";
    string name = "Alice";
    
    string message = greeting + "," + name + "!";
    Console.WriteLine(message);
    
    Console.WriteLine($"Length of name: {name.Length}"); //문자열 길이
    Console.WriteLine($"To Upper {name.ToUpper()}"); //대문자 변환
    Console.WriteLine($"Substring: {name.Substring(1)}"); //q부분 문자열
    

     

     

     


     

    문자열 처리 관련 주요 API 살펴보기

     

    string의 다양한 메서드에 관해 보여준다.

     

    • Contanis : 특정 문자열이 포함되어 있는지 확인한다. 반환값은 참 거짓이다.
    • StartsWith : 특정 문자열로 시작하는지 확인한다. 반환값은 참 거짓이다.
    • IndexOf : 특정 문자열의 위치를 찾는다. 반환값은 인덱스의 숫자로 반환하며, 문자열이 없으면 '-1' 을 반환한다.
    • Replace : 문자열의 내용을 다른 내용으로 교체한다. 기존 내용을 앞에,  변경하고 싶은 내용을 뒤에 적어준다.
    //6 - 1. 문자열 처리 관련 주요 API 살펴보기
    //string 다양한 메서드
    string text = "C# is awesome!";
    Console.WriteLine($"Cotains 'awesome' : {text.Contains("awesome")}");
    Console.WriteLine($"Starts with 'C#' : {text.StartsWith("C#")}");
    Console.WriteLine($"Index of 'is' : {text.IndexOf("is")}");
    Console.WriteLine($"Replace 'awesome' with 'great' : {text.Replace("awesome", "great")} ");
    

     

     

     


    StringBuilder 클래스

     

    StringBuilder 클래스문자열 연결 작업에 성능이 뛰어나다.

    Append 메서드를 통해 기존 문자열 뒤에 새로운 문자열을 추가한다.

    //6 - 2. StringBuilder 클래스
    //효율적인 문자열 연결 작업
    StringBuilder sb = new StringBuilder("Hello");
    sb.Append(",");
    sb.Append("World!");
    Console.WriteLine(sb.ToString());
    

     

     


     

    String과 StringBuiler 차이

     

    반복적으로 문자열을 수정할 때에는 StringBuilder가 효율적이다.

    //6 - 3. String과 StringBuiler 클래스 성능차이 비교
    //반복적으로 문자열을 수정할때 StringBuilder가 효율적이다.
    int iterations = 10000;
    Stopwatch sw = Stopwatch.StartNew();
    
    string text = "";
    
    for(int i =0; i<iterations; i++)
    {
        text += "a";
    }
    
    sw.Stop();
    Console.WriteLine($"String: {sw.ElapsedMilliseconds}ms");
    
    sw.Restart();
    StringBuilder sb = new StringBuilder();
    
    for(int i =0; i<iterations; i++)
    {
        sb.Append("a");
    }
    
    sw.Stop();
    Console.WriteLine($"StringBuilder: {sw.ElapsedMilliseconds}ms");

     

     

     

     


     

     

    7. 예외처리하기

     

     

    프로그램 실행 중에 발생할 수 있는 오류를 관리하는 메커니즘이다.

    아래의 코드는 예외 처리를 해 프로그램이 중단되지 않고 실행을 계속할 수 있다.

    //7. 예외처리하기
    //예외는 프로그램 실행 중 발생하는 오류.
    //예외를 처리하면 프로그램이 중단되지 않고 실행을 계속할 수 있다.
    //try catch
    //게임을 실행하면서 오류를 잡고 싶을 때 사용
    
    while (true)
    {
        try
        {
            int[] numbers = { 1, 2, 3 };
            Console.WriteLine(numbers[5]); //오류 발생
        }
        catch (IndexOutOfRangeException ex) //오류 잡기
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }

     

     


     

    finally

     

    finally는 예외 발생 여부와 상관없이 항상 실행된다.

    //7 - 1. finally 블록은 에외 발생여부와 상관없이 항상 실행된다.
    try
    {
        int number = int.Parse("NotNumber"); //오류 발생
    }
    catch(FormatException ex)
    {
        Console.WriteLine($"Format Error: {ex.Message}");
    }
    finally
    {
        Console.WriteLine("항상 실행됩니다.");
    }
    

     

     

     


     

    Exception

     

    Exception 클래스는 발생할 수 있는 모든 예외를 처리한다.

    //7 - 2. Exception 클래스로 예외 처리
    try
    {
        int nuumber = int.Parse("abc");
    }
    catch(Exception ex)
    {
        Console.WriteLine($"General Error: {ex.Message}");
    }
    

     

     

     


     

    throw 구문

     

    throw 구문수동적으로 예외를 발생시키는 데에 사용되는 키워드이다.

    //7 - 3. throw 구문으로 직접 예외 발생시키기
    try
    {
        int age = -5;
        if (age < 0)
        {
            throw new ArgumentException("Age cannot be negative");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Exception : {ex.Message}");
    }
    

     

     

     


     

    8. List

     

    list가변의 크기를 저장할 때 사용한다.

    배열과 달리 크기가 고정되어 있지 않아서 요소를 추가하거나 삭제할 때 자동으로 크기 조정이 된다.

     

    예시로 List<string> 이렇게 지정해 두면 문자열만 저장할 수 있다.

    Add 메서드를 이용하면 새로운 문자열을 리스트에 추가적으로 저장할 수 있다.

     

    반대로 Remove를 하면 리스트에서 삭제가 된다.

    //1. List : 가변의 크기를 저장할 때 사용
    List<string> names = new List<string> { "Alice", "Bob", "Charlie" };
    
    names.Add("Dave");
    names.Remove("Bob");
    
    foreach (var name in names)
    {
        Console.WriteLine(name);
    }

     

     

     


     

    Stack 클래스

     

    데이터를 저장하고 LIFO(Last In, First Out) 구조이다.

     

    LIFO 구조란?

    마지막에 추가된 데이터가 가장 먼저 제거되는 방식이다.

     

    Push 메서드를 사용해 스택을 추가하는 것이다.

     

    아래의 예시 코드에서는

    가장 마지막에 추가된 요소가 3이고 LIFO 원칙에 의해

    출력은 3 2 1 이다.

    Stack stack = new Stack(); //위의 using 제너릭 삭제하면 타입을 안 써도 사용 가능
    
    stack.Push(1);
    stack.Push(2);
    stack.Push(3);
    
    while (stack.Count > 0)
    {
        Console.WriteLine(stack.Pop());
    }
    

     

     

     


     

    Queue

     

    Queue 클래스 FIFO(First In First Out) 구조로 데이터를 저장하고 처리한다.

    Enqueue 메서드를 사용해 요소를 추가하고

    Count 사용하여 순서대로 출력한다.

    //1 - 3. Queue
    Queue queue = new Queue();
    queue.Enqueue(1);
    queue.Enqueue(2);
    queue.Enqueue(3);
    
    while (queue.Count > 0)
    {
        Console.WriteLine(queue.Dequeue());
    }

     

     

    queue를 사용하여 실습을 진행하였다.

    //1 - 4. 실습
    Queue queue = new Queue();
    
    queue.Enqueue("q");
    queue.Enqueue("w");
    queue.Enqueue("e");
    queue.Enqueue("r");
    queue.Enqueue("점멸");
    queue.Enqueue("평타");
    
    while (queue.Count > 0)
    {
        Console.WriteLine(queue.Dequeue());
    }

     


     

    ArrayList

     

    ArrayList 는 크기가 동적으로 조정되는 배열이다.

    다양한 형식의 데이터를 저장할 수 있다.

     

    Add를 사용해 요소를 추가하고,

    foreach를 사용해 모든 요소를 출력한다.

    Remove 메서드를 통해 특정 요소를 삭제한다.

     //1 - 5. ArrayList
     ArrayList arrayList = new ArrayList();
    
     //요소추가
     arrayList.Add(1); //정수
     arrayList.Add("Hello"); //문자열
     arrayList.Add(3.14); //실수
    
     //요소 접근
     Console.WriteLine("ArrayList 요소:");
     foreach (var item in arrayList)
     {
         Console.WriteLine(item);
     }
    
     //요소 제거
     arrayList.Remove(1); //값이 1인 요소 제거
    
     Console.WriteLine("\\n ArrayList 요소 제거 후: ");
     foreach (var item in arrayList)
     {
         Console.WriteLine(item);
     }

     

     

     


     

    Hashtable 클래스

     

    키 - 값을 쌍으로 저장하는 컬렉션이다.

    즉, 키를 이용해 값을 빠르게 검색한다는 것이다.

     

    예를 들어

    hashtable["문자열"] = 1;

    이렇게 문자열과 정수열로 서로 다른 데이터 타입을 가질 수 있다.

     

    여기서 문자열이 키이고, 1이 값이다.

     

    그래서 (변수명).Key / (변수명).Value

    이렇게 호출하게 되면

    Key 에 문자열이 호출하게 되고,

    Value 에 1이 호출된다.

    //1 - 7. Hashtable 클래스
    //키-값 쌍을 저장하는 컬렉션이다.
    //키를 이용해 값을 빠르게 검색
    
    Hashtable hashtable = new Hashtable();
    
    hashtable["Alice"] = 25;
    hashtable["Bob"] = 30;
    hashtable["포션"] = 20;
    
    Console.WriteLine("Hashtable 요소: ");
    
    foreach(DictionaryEntry entry in hashtable)
    {
        Console.WriteLine($"Key: {entry.Key}, Value: {entry.Value}");
    }
    
    //특정 키의 값을 가져오기
    Console.WriteLine($"\\n Alice의 나이: {hashtable["Alice"]}");
    
    //요소 제거
    hashtable.Remove("Bob");
    
    Console.WriteLine("Hashtable 요소: ");
    
    foreach (DictionaryEntry entry in hashtable)
    {
        Console.WriteLine($"Key: {entry.Key}, Value: {entry.Value}");
    }

     

     

     

     


     

    IEnumerator

     

    사전적 의미로는 열거 행위를 수행하는 객체이다.

    IEnumeraotr는 컬렉션을 반복하는데 필요한 메서드와 속성을 정의한다.

     

    IEnumerator를 사용하는 이유

    ✔ 컬렉션을 직접 제어하며 순회할 수 있음

    ✔ foreach 없이도 컬렉션 순회 가능

    ✔ LINQ나 커스텀 컬렉션을 만들 때 유용함

     

    • bool MoveNext() : 컬렉션의 다음 요소로 이동하는 메서드이다. 다음 요소가 있으면 true, 이동할 요소가 없으면 false
    • object 변수명 { get; } : 현재 요소를 반환한다.
    • void Reset() : 반복기를 처음 위치로 리셋하는 메서드이다.
    public interface IEnumerator
    {
        bool MoveNext(); // 다음 요소로 이동 (이동할 요소가 있으면 true 반환)
        object Current { get; } // 현재 요소 반환
        void Reset(); // 처음 위치로 리셋
    }

     

     

    ArrayList를 생성하고,

    IEnumberator를 통해서 GetEnumerator() 메서드가 ArrayList 요소를 순회하게 해 준다.

     
                ArrayList list = new ArrayList { "Apple", "Banana", "Cherry" };
    
                IEnumerator enumerator = list.GetEnumerator(); //열거자 가져오기
    
                while(enumerator.MoveNext()) //다음 요소가 있는지 확인
                {
                    Console.WriteLine(enumerator.Current);//현재 요소 출력
                }

     

     

     

    커스텀 컬렉션 예제

    IEnumerable<T> 를 구현하여 사용자 정의(커스텀) 컬렉션을 만들 수 있다.

     

    yield return을 사용하여 메서드의 실행을 일시 중지하고, 다음 호출에서부터 계속 실행할 수 있게 한다.

    foreach 루프를 통해 SimpleCollection 모든 요소를 쉽게 출력할 수 있다.

      class SimpleCollection: IEnumerable<int>
     {
         private int[] data = { 1, 2, 3, 4, 5 };
    
         public IEnumerator<int> GetEnumerator()
         {
             foreach(var item in data)
             {
                 yield return item;
             }
         }
         IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
     }
    
     static void Main(string[] args)
     {
    
         var collection = new SimpleCollection();
    
         foreach(var i in collection)
         {
             Console.WriteLine(i);
         } 
    }

     

     

     

     


     

    Dictionary <T, T> 제너릭 클래스

     

    키-값 쌍을 효율적으로 관리하는 컬렉션이다.

    예시로 된 코드에 ["금도끼"]는 키가 되고, 10이 쌍이 된다.

    //Dictionary <T,T> 제너릭 클래스
    Dictionary<string, int> ages = new Dictionary<string, int>();
    
    ages["금도끼"] = 10;
    ages["은도끼"] = 5;
    ages["돌도끼"] = 1;
    
    foreach(var pair in ages)
    {
        Console.WriteLine($"{pair.Key} : {pair.Value}");
    }

     

     

     


     

    9. Null

     

    Null값

    참조형식 null을 가질 수 있으며, 값 형식은 기본적으로 null을 가질 수 없다.

    //null값
    //참조형식 null을 가질 수 있으며, 값 형식은 기본적으로 null읕 가질 수 없다.
    
    string str = null;
    
    if (str == null) //null 초기화
    {
        Console.WriteLine("str is null");
    }
    
    int? nullableInt = null;
    Console.WriteLine(nullableInt.HasValue ? nullableInt.Value.ToString() : "No value");
    
    nullableInt = 10;
    
    Console.WriteLine(nullableInt.HasValue ? nullableInt.Value.ToString() : "No value");

     

     

     

     

     

    null 값을 다루는 연산자

     

    null 조건부 연산자 사용

    str?.Length; 코드를 풀어쓰면 아래 코드와 동일

    if (str != null)
    {
    str.Length;
    }

     

     

    ?? 연산자 (null 병합 연산자)

    ?? 연산자는 왼쪽 연산자가 null일 경우 오른쪽 연산자의 값을 반환합니다.

     

    만약 null이 아니면 str의 값을 출력한다.

    ? 연산자는 객체가 null인지 확인하고, null이 아닌 경우에만 해당 속성이나 메서드에 접근하게 한다.

    //null 값을 다루는 연산자 소개하기 ?? , ?. 연산자
    //?? 연산자를 이용해 null인 경우 대체값을 제공하고, ?.은 null안전 접근을 합니다.
    string str = null;
    
    Console.WriteLine(str ?? "DefaultValue"); //str이 null이면 "DefaultValue" 출력
    
    str = "Hello";
    Console.WriteLine(str?.Length); //str이 null이 아니므로 길이 출력

     

     

    //직관적인 코드 위의 코드와 동일시함. 
    if(str == null)
        {
            Console.WriteLine( "DefaultValue");
        }
    

     

     

     


     

    10. LINQ

     

    LINQ(Language Integrated Query)는 확장 메서드 형태를 제공한다.

    이것을 사용해 컬렉션을 쿼리 할 수 있다.

     

    정수형 배열을 선언 및 초기화하고,

    Where()는 LINQ의 확장 메서드 중 하나이다.

    주어진 조건에 맞는 요소를 필터링해 새로운 컬렉션을 생성하는 것이다.

     

    람다식으로 n이 짝수인지 검사하고 짝수면 true, 홀수면 false

    그렇게 현재 짝수를 출력하는 간단한 예제가 된다.

    //LINQ : 확장 메서드 형태로 제공
    //LINQ(Language Integrated Query)를 사용해 컬렉션을 쿼리할 수 있다.
    //짝수를 출력하는 코드를 좀 더 간편히 할 수 있음
    int[] numbers = { 1, 2, 3, 4, 5 };
    var evenNumbers = numbers.Where(n => n % 2 == 0);
    
    foreach (var num in evenNumbers)
    {
        Console.WriteLine(num);
    }

     

     

     

     


     

     

    오늘은 상당한 양이었어서..

    블로그 쓰기에도 벅찼다 ㅜ.ㅜ

     

    알아야 할 내용도 많으니 헷갈리고..

    그래도 나도 모르게 내가 성장하고 있다는 것이 느껴진다.

     

    처음엔 글 하나 적는 것도 어려웠지만,

    나름! 쉬운 건...! 할 수 있다...!

    (아마도)

     

    나중에는 이 프로그래밍을 기반으로

    멋진 게임을 만들 수 있겠지

     

    기대된닷

    내일도 힘내보자고!!!! 😊

     

     

     


    목차