[내일배움캠프] 본 과정 학습내용

[내배캠] 11일차 C++ 학습 내용 정리

홍이니 2026. 7. 10. 17:24

안녕하세요!

어제는 템플릿까지 학습 하였습니다.

오늘은 STL 기초와 객체지향적 설계에 대해 학습 해보도록 하겠습니다.

 

STL(Standard Template Library)

C++ 표준 라이브러리의 하나로 컨테이너, 알고리즘, 반복자, 등의 템플릿 기반 구성요소를 포함하고 있는 라이브러리 입니다.

STL을 이용하면 다양한 자료구조와 알고리즘을 직접 구현하지 않고 사용할 수 있습니다.

STL은 컨테이너, 알고리즘, 반복자, 등의 구성요소를 갖는다고 했는데 모두 템플릿으로 구현되어 있고 메모리 관리를 내부적으로 하고 있기 때문에 STL을 사용하여 컨테이너 등을 사용할 때 메모리 해제를 직접 고려하지 않아도 된다는 편리함이 있습니다.

 

STL : 컨테이너(Container)

컨테이너(Container)는 단어 그대로 데이터를 담는 자료구조를 의미합니다.

위에서 말한 것과 같이 모든 컨테이너(Container)는 템플릿(Template)을 이용하여 구현되었으며, 메모리 관리 또한 STL 내부에서 하고 있기 때문에 사용시 메모리 해제를 사용자가 직접 고려하지 않아도 됩니다.

 

컨테이너 : 벡터(Vector)

삽입되는 원소 개수에 따라 내부 배열의 크기가 자동으로 조정되는 STL 컨테이너 입니다.

배열과 유사하여 배열 첨자 연산자 "[", "]"를 통해 벡터(Vector)안의 값들에 임의 접근이 가능합니다.

또한, 배열과 마찬가지로 벡터(Vector)안의 값을 삽입, 삭제 할 때는 맨 뒤에서 하는 것이 좋습니다. 그 이유는 중간에 삽입, 삭제가 일어나는 경우, 그 뒤의 값들의 위치를 이동시켜 주어야 하는데 삽입, 삭제가 일어나는 공간 뒤에 위치한 값들을 밀어내야 하고 이 때 많은 연산이 필요하기 때문입니다.

기본 형태는 vector<자료형> 벡터명(벡터의 크기, 각 공간의 초기값); 또는 vector<자료형> 벡터명 = {첫 번째 값, 두 번째 값, ..., 마지막 값}; 의 형태를 갖춥니다.

 

벡터(Vector)의 경우, vector<int> vec2(vec1); 또는 vector<int> vec2 = vec1 의 형태로 복사가 가능한데 복사한 vec2는 새로운 메모리 공간을 할당하여 값을 복사합니다.

 

초기화 할 때, 벡터(Vector)의 초기 값을 벡터(Vector)로 넣어 2차원 배열로 선언하는 것도 가능합니다.

vector<int> vec2(3, vector<int> vec1(4, 0)); 의 형태로 선언한다면 vec2는 3행 4열을 가지고 각 공간에 0의 값이 들어있는 2차원 벡터(Vector)가 생성됩니다.

 

벡터(Vector)에서 사용할 수 있는 메서드에 대해서 알아보겠습니다.

 

push_back()

벡터(Vector)의 맨 끝에 원소를 추가하는 메서드 입니다.

// STL (Standard Template Library)

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main(void)
{
    vector<int> examVec;

    examVec.push_back(10);
    examVec.push_back(20);
    examVec.push_back(30);

    cout << "examVec => ";

    for (int i : examVec)
    {
        cout << i << " ";
    }

    cout << endl;

    return 0;
}

 

**for문은 조건식으로 int i에 값에 Vector examVec의 원소를 앞에서부터 하나씩 넣어 examVec의 원소를 전부 순회하면 종료하는 방식입니다.**

 

pop_back()

벡터(Vector)의 맨 끝의 원소를 제거하는 메서드 입니다.

// STL (Standard Template Library)

#include <iostream>
#include <string>
#include <vector>

using namespace std;

template <typename T>
void printVec(vector<T> vec)
{
    cout << "Vector => ";

    for (int i : vec)
    {
        cout << i << " ";
    }

    cout << endl;
}

int main(void)
{
    vector<int> examVec;

    // push_back()
    examVec.push_back(10);
    examVec.push_back(20);
    examVec.push_back(30);

    printVec(examVec);
    cout << endl;

    // pop_back()
    examVec.pop_back();
    examVec.pop_back();

    printVec(examVec);

    return 0;
}

벡터(Vector)를 출력하는 함수 printVec() 함수를 하나 만들었습니다.

그리고 위에서 했던 push_back()을 통해 벡터(Vector)에 값을 추가해주고 pop_back()을 통해 값을 2개 삭제하였습니다.

 

size()

현재 벡터(Vector)의 원소 개수를 확인할 때 사용하는 메서드 입니다.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

template <typename T>
void printVec(vector<T> vec) {
    cout << "Vector => ";

    for (T i : vec) {
        cout << i << " ";
    }

    cout << endl;
}

int main(void) {
    vector<int> examVec;

    // push_back()
    examVec.push_back(10);
    examVec.push_back(20);
    examVec.push_back(30);

    printVec(examVec);

    // vector.size()
    cout << "examVec.size() => " << examVec.size() << endl;
    cout << endl;

    // pop_back()
    examVec.pop_back();
    examVec.pop_back();

    printVec(examVec);

    // vector.size()
    cout << "examVec.size() => " << examVec.size() << endl;

    return 0;
}

printVec() 함수의 for문을 조금 수정하였습니다. "for (int i : vec)"에서 "for (T i : vec)"으로 수정하여 벡터의 원소 자료형이 어떤 값이 오더라도 printVec() 함수를 사용할 수 있도록 수정하였습니다.

 

erase()

특정 위치 또는 특정 구간의 원소를 제거하는 메서드 입니다.

위에서 말한 것과 같이 벡터(Vector)의 맨 끝 값을 변경하는 push와 pop과 달리 erase는 중간 값을 수정할 수 있기 때문에 되도록 사용을 지양하는 것이 좋습니다.

연산량이 늘어날 수 있습니다. 또한, 시간 복잡도가 커질 수 있다고도 표현할 수 있습니다.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

template <typename T>
void printVec(vector<T> vec) {
    cout << "Vector => ";

    for (T i : vec) {
        cout << i << " ";
    }

    cout << endl;
}

int main(void) {
    vector<int> examVec;

    // push_back()
    examVec.push_back(10);
    examVec.push_back(20);
    examVec.push_back(30);
    examVec.push_back(40);

    printVec(examVec);

    // vector.size()
    cout << "examVec.size() => " << examVec.size() << endl;
    cout << endl;

    // vector.erase()
    examVec.erase(examVec.begin() + 1, examVec.begin() + 3);

    printVec(examVec);

    // vector.size()
    cout << "examVec.size() => " << examVec.size() << endl;
    cout << endl;

    return 0;
}

 

 

컨테이너 : 맵(Map)

키(Key)와 값(Value)을 가지는 컨테이너(Container)로 배열에서 인덱스(Index)를 통해 해당 위치의 값을 찾는 것 처럼 맵(Map)은 키(Key)를 통해 값(Value)을 검색하는 기능을 제공합니다.

맵(Map)은 대표적인 연관 컨테이너 입니다.

 

기본 선언 형태는 map<키의 자료형, 값의 자료형> 맵명; 의 형태를 갖춥니다.

맵(Map)의 원소를 추가할 때는 맵명[키] = 값; 으로 원소를 추가할 수 있습니다.

 

주요 특징으로는 (1) 키(Key)와  값(Value)을 한 쌍으로 저장하고 관리합니다. (2) 키(Key)를 기준으로 내부 데이터가 자동으로 정렬됩니다. (3) 키(Key)는 중복을 허용하지 않습니다.

 

// 컨테이너 : 맵(Map)

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main(void) {
    map<int, string> studentMap;

    studentMap[10] = "Alice";
    studentMap[11] = "Bob";

    for (const auto& i : studentMap) {
        cout << "Student Number: " << i.first << ", Name: " << i.second << endl;
    }

    return 0;
}
// 컨테이너 : 맵(Map)

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main(void) {
    map<int, string> studentMap;

    studentMap[10] = "Alice";
    studentMap[11] = "Bob";

    studentMap[1] = "Cheol Su";
    studentMap[2] = "Young hee";

    for (const auto& i : studentMap) {
        cout << "Student Number: " << i.first << ", Name: " << i.second << endl;
    }

    return 0;
}

위에서 말한 것과 같이 맵(Map)의 값들은 키(Key)를 기준으로 오름차순 정렬되기 때문에 for문을 통해 출력해보면 순서대로 잘 출력되는 것을 확인할 수 있습니다.

 

insert()

맵(Map)은 맵명[키] = 값; 으로도 값을 추가할 수 있지만 맵명.insert(make_pair(키, 값)); 혹은 맵명.insert({키, 값}); 으로도 맵(Map)에 pair<키(Key), 값(Value)>를 추가해줄 수 있습니다.

// 컨테이너 : 맵(Map)
// 맵 : insert()

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main(void) {
    map<int, string> studentMap;

    studentMap.insert(make_pair(10, "Alice"));
    studentMap.insert(make_pair(11, "Bob"));

    studentMap.insert({ 1, "Cheol Su" });
    studentMap.insert({ 2, "Young hee" });

    for (const auto& i : studentMap) {
        cout << "Student Number: " << i.first << ", Name: " << i.second << endl;
    }

    return 0;
}

 

find()

맵(Map)안에 특정 키가 있는지 확인하는 메서드 입니다.

찾고자하는 키가 맵(Map) 안에 있을 경우, 해당 키의 위치(정확히는 이터레이터)를 반환하고 찾고자하는 키가 없을 경우, map.end()를 반환합니다.

// 컨테이너 : 맵(Map)
// 맵 : find()

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main(void) {
    map<int, string> studentMap;

    studentMap.insert(make_pair(10, "Alice"));
    studentMap.insert(make_pair(11, "Bob"));

    studentMap.insert({ 1, "Cheol Su" });
    studentMap.insert({ 2, "Young hee" });

    int key = 10;
    auto findKey = studentMap.find(key);

    if (findKey != studentMap.end()) {
        cout << "Found Key: " << findKey->first << ", Value: " << findKey->second << endl;
    }
    else {
        cout << "Key " << key << " not found.." << endl;
    }

    return 0;
}

 

erase()

벡터와 같이 맵의 특정 위치 또는 특정 구간의 원소를 제거하는 메서드 입니다.

다만, 벡터처럼 사용을 지양하고 있지 않습니다.

// 컨테이너 : 맵(Map)
// 맵 : erase()

#include <iostream>
#include <string>
#include <map>

using namespace std;

template <typename T1, typename T2>
void printMap(map<T1, T2> inputMap) {
    for (const auto& i : inputMap) {
        cout << "Student Number: " << i.first << ", Name: " << i.second << endl;
    }
}

int main(void) {
    map<int, string> studentMap;

    studentMap.insert(make_pair(10, "Alice"));
    studentMap.insert(make_pair(11, "Bob"));

    studentMap.insert({ 1, "Cheol Su" });
    studentMap.insert({ 2, "Young hee" });

    printMap(studentMap);
    cout << endl;

    studentMap.erase(1);
    cout << "studentMap.erase(1);\n" << endl;

    printMap(studentMap);

    return 0;
}

맵(Map)의 원소를 출력하는 printMap() 함수를 만들었습니다.

그리고 studentMap에서 첫 번째 원소 pair<1, "Cheol Su">를 삭제하였습니다.

 

size()

현재 맵(Map)의 원소(pair<key, value>)의 갯수를 확인할 때 사용하는 메서드 입니다.

// 컨테이너 : 맵(Map)
// 맵 : size()

#include <iostream>
#include <string>
#include <map>

using namespace std;

template <typename T1, typename T2>
void printMap(map<T1, T2> inputMap) {
    for (const auto& i : inputMap) {
        cout << "Student Number: " << i.first << ", Name: " << i.second << endl;
    }
}

int main(void) {
    map<int, string> studentMap;

    studentMap.insert(make_pair(10, "Alice"));
    studentMap.insert(make_pair(11, "Bob"));

    studentMap.insert({ 1, "Cheol Su" });
    studentMap.insert({ 2, "Young hee" });

    cout << "studentMap.size(); => " << studentMap.size() << endl;

    printMap(studentMap);
    cout << endl;

    studentMap.erase(1);
    cout << "studentMap.erase(1);\n" << endl;

    cout << "studentMap.size(); => " << studentMap.size() << endl;

    printMap(studentMap);

    return 0;
}

 

clear()

맵(Map)의 모든 원소를 제거하는 메서드 입니다.

// 컨테이너 : 맵(Map)
// 맵 : clear()

#include <iostream>
#include <string>
#include <map>

using namespace std;

template <typename T1, typename T2>
void printMap(map<T1, T2> inputMap) {
    for (const auto& i : inputMap) {
        cout << "Student Number: " << i.first << ", Name: " << i.second << endl;
    }
}

int main(void) {
    map<int, string> studentMap;

    studentMap.insert(make_pair(10, "Alice"));
    studentMap.insert(make_pair(11, "Bob"));

    studentMap.insert({ 1, "Cheol Su" });
    studentMap.insert({ 2, "Young hee" });

    cout << "studentMap.size(); => " << studentMap.size() << endl;

    printMap(studentMap);
    cout << endl;

    studentMap.clear();
    cout << "studentMap.clear();\n" << endl;

    cout << "studentMap.size(); => " << studentMap.size() << endl;

    return 0;
}

**벡터(Vector)에도 clear() 메소드가 존재합니다.**

 

STL : 알고리즘(Algorithm)

STL(Standard Template Library)에는 다양한 컨테이너와 독립적으로 동작하는 범용 알고리즘을 제공합니다.

알고리즘(Algorithm)은 컨테이너의 특정 요소를 찾거나 컨테이너의 요소를 정렬하는 등, 컨테이너의 요소들을 관리하는 기능(함수)들을 제공합니다.

 

sort()

컨테이너의 내부 데이터를 정렬하는 함수 입니다.

 

기본 형태는 sort(배열명, 배열의 크기); 의 형태를 갖춥니다.

컨테이너의 경우, 기본 형태는 sort(컨테이너명.begin(), 컨테이너명.end()); 의 형태를 갖춥니다.

 

sort() 함수는 사용자가 정렬 함수를 입력하면 해당 방식으로 정렬하고 사용자 정렬 함수를 입력하지 않으면 오름차순으로 정렬합니다.

sort() 함수의 정렬 방식은 배열(혹은 컨테이너)의 요소를 순회하면서 각 인덱스(Index)에서 앞의 값과 뒤의 값을 비교하여 정렬 방식에 부합하면 true(참)을 반환하고 부합하지 않으면 false(거짓)을 반환합니다.

false(거짓)을 반환할 경우, 앞의 값과 뒤의 값의 순서를 바꾸어 정렬하고 다음 인덱스(Index)를 계속해서 순회하면서 정렬하게 됩니다.

 

ex)

// STL : 알고리즘(Algorithm)
// 알고리즘 : sort()

#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

int main(void) {
    int examArr[] = { 10, 20, 80, 60, 50, 30, 70, 40 };
    int arrSize = sizeof(examArr) / sizeof(examArr[0]);

    cout << "Print Array => { ";

    for (int i = 0; i < arrSize; i++) {
        cout << examArr[i] << " ";
    }

    cout << "}" << endl;

    // sort()
    sort(examArr, examArr + arrSize);

    cout << "\nSorted Array\n" << endl;

    cout << "Print Array => { ";

    for (int i = 0; i < arrSize; i++) {
        cout << examArr[i] << " ";
    }

    cout << "}" << endl;

    return 0;
}

 

이번에는 sort() 함수에 정렬 함수를 입력하여 배열을 정렬해 보도록 하겠습니다.

#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

// 내림차순 정렬 함수
bool compare(int a, int b) {
    return a > b;
}

int main(void) {
    int examArr[] = { 10, 20, 80, 60, 50, 30, 70, 40 };
    int arrSize = sizeof(examArr) / sizeof(examArr[0]);

    cout << "Print Array => { ";

    for (int i = 0; i < arrSize; i++) {
        cout << examArr[i] << " ";
    }

    cout << "}" << endl;

    // sort()
    sort(examArr, examArr + arrSize, compare);

    cout << "\nSorted Array\n" << endl;

    cout << "Print Array => { ";

    for (int i = 0; i < arrSize; i++) {
        cout << examArr[i] << " ";
    }

    cout << "}" << endl;

    return 0;
}

 

벡터 컨테이너를 sort() 함수를 통해 정렬 해보도록 하겠습니다.

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>

using namespace std;

template <typename T>
void printVec(vector<T> vec) {
    cout << "Vector => ";

    for (T i : vec) {
        cout << i << " ";
    }

    cout << endl;
}

// 내림차순 정렬 함수
bool compare(int a, int b) {
    return a > b;
}

int main(void) {
    vector<int> examVec;

    // push_back()
    examVec.push_back(10);
    examVec.push_back(20);
    examVec.push_back(80);
    examVec.push_back(60);
    examVec.push_back(50);
    examVec.push_back(30);
    examVec.push_back(70);
    examVec.push_back(40);

    printVec(examVec);

    // 오름차순 정렬
    sort(examVec.begin(), examVec.end());

    printVec(examVec);

    // 내림차순 정렬
    sort(examVec.begin(), examVec.end(), compare);

    printVec(examVec);

    return 0;
}

 

추가적으로 클래스(Class)를 sort() 함수로 정렬할 수 있도록 구현 해보도록 하겠습니다.

 

일단 클래스는 사용자가 작성한 것이기 때문에 sort() 함수는 어떻게 정렬해야 할지 알 수 없습니다.

따라서 클래스를 sort() 함수로 정렬할 때는 무조건 사용자 정의 정렬 함수가 입력되어야 합니다.

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>

using namespace std;

// Person 클래스 정의
class Person {
private:
    string name;
    int age;

public:
    Person(string defaultName = "None", int defaultAge = 0) : name(defaultName), age(defaultAge) {}

    string getName(void) const { return name; }
    int getAge(void) const { return age; }
};

// 다중 기준 정렬 함수 (나이 오름차순 => 이름 오름차순)
bool compareByAgeAndName(const Person& a, const Person& b) {
    if (a.getAge() == b.getAge()) {
        return a.getName() < b.getName();
    }

    return a.getAge() < b.getAge();
}

// 벡터 출력 함수
void peopleList(vector<Person> people) {
    cout << "People List" << endl;

    for (const auto& i : people) {
        cout << "Name: " << i.getName() << ", Age: " << i.getAge() << endl;
    }

    cout << endl;
}

int main(void) {
    vector<Person> people = {
        Person("Alice", 14),
        Person("Bob", 37),
        Person("Cheol Su", 14),
        Person("Young Hee", 29)
    };

    peopleList(people);

    sort(people.begin(), people.end(), compareByAgeAndName);
    cout << "Sorted Vector\n" << endl;

    peopleList(people);
}

 

 

find()

컨테이너 내부의 특정 원소를 찾아 해당 원소의 반복자를 반환하는 함수 입니다.

기본 형태는 find(first, last, 찾는 값); 의 형태를 갖춥니다.

 

find() 함수에서 first는 시작 지점을 의미하고 list는 종료 지점입니다.

주의해야 할 점은 first는 포함하고 list는 범위에 포함되지 않는 범위 안에서 값을 찾습니다. (first <= 찾는 값 < list)

 

find() 함수에서 찾고 있는 값을 찾은 경우에는 해당 값의 반복자를 반환하고 찾지 못했을 경우에는 list의 반복자를 반환합니다.

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

using namespace std;

int main(void) {
    vector<int> examVec = { 10, 20, 30, 40, 50, 60, 70, 80, 90 };

    // examVec의 시작과 끝 범위 안에서 40을 찾음
    auto findValue = find(examVec.begin(), examVec.end(), 40);

    if (findValue != examVec.end()) {
        cout << "Value Found!, Value Location: " << (findValue - examVec.begin()) << endl;
    }
    else {
        cout << "Value Not Found.." << endl;
    }

    return 0;
}

 

배열에서 find() 함수를 사용 해보겠습니다.

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

using namespace std;

int main(void) {
    int examArr[] = {10, 20, 30, 40, 50, 60, 70, 80, 90};
    int arraySize = sizeof(examArr) / sizeof(examArr[0]);

    // examArr의 시작과 끝 범위 안에서 40을 찾음
    auto findValue = find(examArr, examArr + arraySize, 40);

    if (findValue != examArr + arraySize) {
        cout << "Value Found!, Value Location: " << (findValue - examArr) << endl;
    }
    else {
        cout << "Value Not Found.." << endl;
    }

    return 0;
}

결과 값은 벡터의 원소 위치를 찾는 코드와 동일하게 출력됩니다.

 

마지막으로 문자열에서 find() 함수를 이용하여 원하는 문자를 찾아 보겠습니다.

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

using namespace std;

int main(void) {
    string examString = "Hello World";

    // examString의 시작과 끝 범위 안에서 'o'을 찾음
    auto findValue = find(examString.begin(), examString.end(), 'o');

    if (findValue != examString.end()) {
        cout << "Value Found!, Value Location: " << (findValue - examString.begin()) << endl;
    }
    else {
        cout << "Value Not Found.." << endl;
    }

    return 0;
}

이 코드 역시 첫 번째 'o' 위치에 인덱스 4가 출력되는 것을 확인할 수 있습니다.

 

STL : 반복자(Iterator)

STL에서 컨테이너가 어떻게 구현되어 있는지 확실하게 몰라도 알고리즘을 활용하는데 큰 어려움이 없었습니다.

이 이유는 알고리즘이 반복자를 기반으로 동작하도록 구현되어 있어, 배열이나 각 컨테이너에게서 입력 받는 값을 일반화 하였기 때문입니다.

 

즉, 반복자는 컨테이너의 요소에 대해 일관된 접근 방법을 제공하여 알고리즘이 컨테이너의 내부 구현과 무관하게 동작할 수 있도록 코드가 작성 되었습니다.

 

순방향 반복자

앞에서부터 순차적으로 순회하는 반복자 입니다.

순방향 반복자에는 begin() 함수와 end() 함수가 있습니다.

 

begin() 함수는 컨테이너의 첫 번째 원소를 가리키는 반복자 입니다.

end() 함수는 컨테이너의 마지막 원소가 아닌 마지막 원소의 다음을 가리키는 반복자입니다.

 

end() 함수가 컨테이너의 마지막 원소 다음을 가리키는 이유는 일관된 반복 구조를 유지하고 탐색 실패를 쉽게 표현하기 위해서 이렇게 구현되었습니다.

즉, 마지막 원소를 반환하면 탐색에 실패해서 반환했는지 원하는 값이 마지막에 있어서 반환했는지 알 수 없지만 마지막 다음을 반환하게 되면 명확하고 간단하게 탐색에 실패했다는 것을 알 수 있습니다.

 

역방향 반복자

순방향 반복자와 반대로 뒤에서부터 역순으로 순회하는 반복자 입니다.

역방향 반복자에는 rbegin() 함수와 rend() 함수가 있습니다.

 

rbegin() 함수는 컨테이너의 마지막 원소를 가리키는 반복자 입니다.

rend() 함수는 컨테이너의 첫 번째 원소 이전을 가리키는 반복자 입니다.

 

rend() 함수가 컨테이너의 첫 번째 원소 이전을 가리키는 이유는 end() 함수가 마지막 원소 다음을 가리키는 이유와 동일합니다.

 

**단, 순방향 반복자를 사용했을 경우, 탐색의 결과가 인덱스 번호하고 동일하였지만 역방향 반복자를 사용하여 탐색한 경우에는 뒤에서부터 0번, 1번, ..., 첫번 째 원소로 탐색하므로 인덱스 번호와 다른 값이 출력되는 것을 확인할 수 있습니다.**

 

오늘은 텍스트의 양이 많아서 여기서 한 번 끊고 계속 학습을 진행하겠습니다.

긴 글 읽어주셔서 감사드리며 혹시 잘못된 부분이나 의문이 있으시면 댓글 남겨주세요.

 

오늘도 고생많으셨고 내일도 힘내서 열심히 공부하도록 하겠습니다!