인덕대 C++-출처 smile han

14주차

Plki 2024. 12. 2. 16:31
#include <iostream>
using std::cout;
using std::endl;

int Max(int i, int j)
{
   return i>j ?i:j;
}
double Max(double i, double j)
{ 
   return i>j ?i:j;
}
char Max(char i, char j)
{
   return i>j ?i:j;
}
int main()
{
  cout<< "Max값은=" << Max(1,2)<<endl;
  cout<< "Max값은=" << Max(7.5,3.6)<<endl;
  cout<< "Max값은=" << Max('A','B');
  return 0;
}

을 템플렛 사용으로 바꿈 V

#include <iostream>
using std::cout;
using std::endl;
template <class T >T Max(T i, T j)
{
	return i > j ? i : j;
}

int main()
{
	cout << "Max값은=" << Max(1, 2) << endl;
	cout << "Max값은=" << Max(7.5, 3.6) << endl;
	cout << "Max값은=" << Max('A', 'B');
	return 0;
}

눈에 보이는 소스는 줄어들지만 샐행 파일는 변화 없다.

 

포괄적 함수 제작은 제네릭  만드는 것

제네릭 프로그래밍은 데이터 타입에 독립적인 알고리즘과 구조를 작성하여 코드의 재사용성을 높이는 프로그래밍 패러다임입니다. (자료형 나중에 걸정하는 방법)

 

template <typename T >T Max(T i, T j)
{}
template <class T >T Max(T i, T j)
{}

C++에서 템플릿을 만들 때 `typename`과 `class`는 거의 동일한 기능을 수행하지만, 일반적으로 다음과 같은 차이점이 있습니다:
1. **의미적 차이**: `typename`은 타입을 명시할 때 사용되며, 보다 명확하게 "타입"이라는 의미를 전달합니다. 반면, `class`는 클래스 타입을 나타내는 데 사용되며, 일반적으로 클래스에 대한 제너릭 타입을 정의할 때 사용됩니다.
2. **관습**: 템플릿 인자를 정의할 때는 `typename`을 사용하는 것이 더 일반적입니다. 특히, 여러 타입 인자를 사용하는 경우 `typename`을 선호하는 경향이 있습니다.
3. **특정 상황**: 특정 상황에서는 `class`를 사용해야 하는 경우도 있습니다. 예를 들어, 템플릿 특수화(template specialization)에서는 `class`만 사용 가능합니다.

결론적으로, 일반적인 경우에는 `typename`을 사용하는 것이 더 좋고, 코드의 의미를 명확히 할 수 있습니다. 그러나 두 키워드 모두 동일하게 작동하므로, 개인의 취향이나 팀의 코딩 스타일에 따라 선택할 수 있습니다. 

typename(함수), class(클래스)를 사용할때 주로 사용<시험에 매년 나옴>

#include <iostream>
using std::cout;
using std::endl;

template <class T1, class T2, class T3> void fun(T1 x, T2 y, T3 z)
{     // 두 개의 매개변수 자료형이 T1과 T2로 다르다.
	cout << x << "  " << y <<" "<<z<< endl;
}
int main()
{
	fun("Han", 30, 30.5); // T1은 문자열(const char *),T2는 정수형(int)
	fun(25, 50.5, 'a');  // T1은 정수형(int), T2는 double형 
	return 0;
}//재작년 시험 문제

 

 

 

 

 

 

 

 

 

 

 

#include <iostream>
using std::cout;
using std::endl;

class CCC1
{
	int x;
	int y;
public:
	CCC1(int xx, int yy) { x = xx; y = yy; }
	void Print() { cout << x << ',' << y << endl; }
};
class CCC2
{
	double x;
	double y;
public:
	CCC2(double xx, double yy) { x = xx; y = yy; }
	void Print() { cout << x << ',' << y << endl; }
};
class CCC3
{
	char x;
	const char* y;
public:
	CCC3(char xx, const char* yy) { x = xx; y = yy; }
	void Print() { cout << x << ',' << y << endl; }
};

int main()
{
	CCC1 c1(10, 20);
	CCC2 c2(3.5, 5.5);
	CCC3 c3('I', "Love You!");

	c1.Print();
	c2.Print();
	c3.Print();
	return 0;
}

을 템플렛 class 사용하여 바뀜V

1단계. 비슷한 코드가 있다면 단 1나 빼고 다 지운다

2단계. 앞에 template 작성

3단계. 함수 변형

class 자리에 typename을 사용해도 되나 class을 사용시에는 class를 주로 쓴다.<시험에 냄 배점높음>

#include <iostream>
using std::cout;
using std::endl;

template <class T1, class T2>class CCC
{
	T1 x;
	T2 y;
public:
	CCC(T1 xx, T2 yy) { x = xx; y = yy; }
	void Print() { cout << x << ',' << y << endl; }
};

int main()
{
	CCC <int, int> c1(10, 20);
	CCC <double, double> c2(3.5, 5.5);
	CCC <char, const char*> c3('I', "Love You!");

	c1.Print();
	c2.Print();
	c3.Print();
	return 0;
}
이 C++ 코드는 제네릭 클래스를 정의하고 사용하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다.

### 코드 설명

1. **헤더 파일 포함**:
   ```cpp
   #include <iostream>
   ```
   이 줄은 C++의 입출력 스트림 라이브러리를 포함하여, `cout`과 `endl`을 사용할 수 있게 합니다.

2. **네임스페이스 사용**:
   ```cpp
   using std::cout;
   using std::endl;
   ```
   `std` 네임스페이스에서 `cout`과 `endl`을 직접 사용할 수 있도록 합니다.

3. **제네릭 클래스 정의**:
   ```cpp
   template <class T1, class T2> class CCC
   ```
   `CCC`라는 이름의 클래스 템플릿을 정의합니다. `T1`과 `T2`는 클래스에서 사용할 두 개의 타입 매개변수입니다.

4. **멤버 변수**:
   ```cpp
   T1 x;
   T2 y;
   ```
   클래스 `CCC`는 두 개의 멤버 변수 `x`와 `y`를 가지고 있으며, 각각 `T1`과 `T2` 타입입니다.

5. **생성자**:
   ```cpp
   CCC(T1 xx, T2 yy) { x = xx; y = yy; }
   ```
   생성자는 두 개의 매개변수 `xx`와 `yy`를 받아서 멤버 변수 `x`와 `y`에 각각 할당합니다.

6. **Print 메서드**:
   ```cpp
   void Print() { cout << x << ',' << y << endl; }
   ```
   `Print` 메서드는 멤버 변수 `x`와 `y`의 값을 출력합니다. `cout`을 사용하여 값들을 출력하고, `endl`로 줄바꿈을 합니다.

7. **main 함수**:
   ```cpp
   int main()
   {
       CCC <int, int> c1(10, 20);
       CCC <double, double> c2(3.5, 5.5);
       CCC <char, const char*> c3('I', "Love You!");
   
       c1.Print();
       c2.Print();
       c3.Print();
       return 0;
   }
   ```
   - `CCC<int, int> c1(10, 20);`: `int` 타입의 두 값을 저장하는 `CCC` 객체 `c1`을 생성합니다.
   - `CCC<double, double> c2(3.5, 5.5);`: `double` 타입의 두 값을 저장하는 `CCC` 객체 `c2`를 생성합니다.
   - `CCC<char, const char*> c3('I', "Love You!");`: `char`와 `const char*` 타입을 저장하는 `CCC` 객체 `c3`를 생성합니다.

   각 객체에 대해 `Print` 메서드를 호출하여 값을 출력합니다.

### 출력 결과
위 코드를 실행하면 다음과 같은 출력이 생성됩니다:
```
10,20
3.5,5.5
I,Love You!
```

이 코드는 제네릭 프로그래밍을 통해 다양한 데이터 타입을 처리할 수 있는 클래스를 정의하고, 이를 통해 객체를 생성하고 메서드를 호출하여 데이터를 출력하는 예제를 보여줍니다.

 

 

 

표준 어디에서나 똑같이 움직인다.

아래는 자료구조에서 사용하는 자료구조와 C++ STL(Standard Template Library)에서 제공하는 자료구조를 비교한 표입니다. `벡터`, `스택`, `큐`를 포함하여 각 특성을 정리했습니다.

| **특징**              | **자료구조**                   | **STL**                             |
|-----------------     --|-------------------------------|-------------------------------------|
| **정의**              | 데이터를 저장하고 관리하는 구조 | STL에서 제공하는 템플릿 기반 자료구조 |
| **구조**               | 동적 배열, 연결 리스트 등       | `vector`, `stack`, `queue`          |
| **메모리 관리**   | 프로그래머가 직접 관리           | 자동으로 메모리 관리                |
| **접근 방식**       | 배열: 인덱스 접근, 연결 리스트: 포인터 접근 | `vector`: 인덱스 접근<br>`stack`, `queue`: 제한된 접근 
| **삽입/삭제 속도**| 배열: O(n), 연결 리스트: O(1) | `vector`: O(n) (중간 삽입 시)<br>`stack`: O(1)<br>`queue`: O(1) |
| **검색 속도**         | 배열: O(1), 연결 리스트: O(n) | `vector`: O(n)<br>`stack`: O(n)<br>`queue`: O(n) |
| **사용 편의성**     | 직접 구현 시 복잡함                 | 간단하고 직관적인 인터페이스 제공    |
| **기능**                 | 기본적인 데이터 조작 기능 제공 | `vector`: 동적 크기 조정, 정렬 등<br>`stack`: 푸시, 팝<br>`queue`: 인큐, 디큐 |
| **예외 처리**          | 직접 처리해야 함               | STL에서 예외 처리 기능 제공         |
| **성능**                  | 직접 구현 시 성능 최적화 가능   | 일반적으로 최적화된 성능 제공        |
| **범용성**             |   특정 문제에 맞춤형으로 설계    | 범용적으로 사용 가능                 |

이 표는 `벡터`, `스택`, `큐` 각각의 특징을 자료구조와 STL에서 제공하는 구조로 비교하여 각자의 장점과 단점을 이해하는 데 도움이 됩니다. STL은 사용하기 편리하고, 다양한 기능과 최적화된 성능을 제공하는 반면, 직접 구현한 자료구조는 특정 요구사항에 맞춤형으로 최적화할 수 있는 장점이 있습니다. 

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector <int> x;  //int x[10]와 차이
    // 여러 개 int형을 가지고 노는 배열 공간을 만들고 싶어요 
    x.push_back(1);
    x.push_back(2);
    x.push_back(5);
    for (int i = 0; i < x.size(); i++)//.size 배열의 크기
        cout << x[i] << endl;
    return 0;
}
//작년 시험문제
//1
//2
//5


#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector <char> x;  //int x[10]와 차이
    // 여러 개 int형을 가지고 노는 배열 공간을 만들고 싶어요 
    x.push_back('a');
    x.push_back('t');
    x.push_back('l');
    for (int i = 0; i < x.size(); i++)//.size 배열의 크기
        cout << x[i] << endl;
    return 0;
}

 

프로그래밍 언어에서 예외 처리(Exception handling)는 프로그램 실행 중 발생할 수 있는 오류나 
예외적인 상황을 관리하는 메커니즘입니다. 예외 처리를 통해 프로그램이 예기치 않은 상황에서도 
정상적으로 동작하거나, 적절한 오류 메시지를 출력하고 종료할 수 있도록 도와줍니다.

### 주요 개념

1. **예외(Exception)**: 프로그램 실행 중 발생하는 오류나 비정상적인 상황을 의미합니다. 예를 들어, 
파일을 열 수 없거나, 배열의 범위를 초과하여 접근하는 경우 등이 있습니다.

2. **예외 발생(Throwing an Exception)**: 예외가 발생했을 때, 해당 예외를 "던진다"는 의미로, 
프로그램이 오류 상태에 도달했음을 알리는 것입니다.

3. **예외 처리(Handling an Exception)**: 발생한 예외를 처리하는 과정입니다. 이를 통해 프로그램이
중단되지 않고, 적절한 조치를 취할 수 있습니다.

4. **try-catch 블록**: 대부분의 프로그래밍 언어에서 예외 처리를 구현하는 기본 구조입니다.
   - **try**: 예외가 발생할 수 있는 코드를 포함합니다.
   - **catch**: 발생한 예외를 처리하는 코드를 포함합니다.

### 예시 (C++)

```cpp
#include <iostream>
#include <stdexcept>

int main() {
    try {
        throw std::runtime_error("예외가 발생했습니다!");
    } catch (const std::runtime_error& e) {
        std::cout << "Caught: " << e.what() << std::endl;
    }
    return 0;
}
```

### 장점

- **안정성**: 프로그램이 예외적인 상황에서도 종료되지 않고, 적절하게 대응할 수 있게 해줍니다.
- **코드 가독성**: 예외 처리를 통해 오류 처리를 명확하게 구분할 수 있어 코드의 가독성이 높아집니다.
- **유지보수성**: 예외 처리 구조를 통해 오류 발생 시의 처리 방법을 쉽게 수정할 수 있습니다.

예외 처리는 프로그램의 안정성과 신뢰성을 높이는 데 중요한 역할을 합니다.

 

 

 

#include <iostream>
using std::cout;
using std::cin;
using std::endl;

void Div(double ja, double mo)
{
	try { //1
		if(mo==0)throw mo;//2
		cout << "결과:" << ja / mo << endl;
	}
	catch (double) {//3 //던지는 값에 자료형을 ()안에 넣음 //매년 시험냄
		cout << "오류 분모가 0이기 때문에 나눌수 없음." << endl;
	}//catch는 여러개 사용가능 자료형이 맞는 catch가 사용됨
}
int main()
{
	double x, y;
	cout << "분자를 입력하세요=";
	cin >> x;
	cout << "분모를 입력하세요=";
	cin >> y;
	Div(x, y);
	return 0;
}



뤼튼 주석
#include <iostream>
using std::cout;
using std::cin;
using std::endl;

// 나눗셈을 수행하는 함수
void Div(double ja, double mo)
{
    try { // 1. 예외 처리를 시작하는 블록
        if(mo == 0) throw mo; // 2. 분모가 0인 경우 예외를 던짐
        cout << "결과: " << ja / mo << endl; // 나눗셈 결과 출력
    }
    catch (double) { // 3. double 타입 예외를 처리하는 catch 블록
        cout << "오류: 분모가 0이기 때문에 나눌 수 없음." << endl; // 예외 발생 시 오류 메시지 출력
    }
}

int main()
{
    double x, y; // 분자와 분모를 저장할 변수 선언
    cout << "분자를 입력하세요 = "; // 사용자에게 분자 입력 요청
    cin >> x; // 사용자로부터 분자 입력 받기
    cout << "분모를 입력하세요 = "; // 사용자에게 분모 입력 요청
    cin >> y; // 사용자로부터 분모 입력 받기
    Div(x, y); // 입력받은 값으로 Div 함수 호출
    return 0; // 프로그램 종료
}



| **프로그래밍 언어** | **예외 처리 키워드**    | **설명**                                   |
|---------------------|--------------------------|--------------------------------------------|
| C++                 | `try`, `catch`, `throw` | `try` 블록 내에서 예외 발생 시 `catch`로 처리하며, `throw`로 예외를 던진다. |
| Java                | `try`, `catch`, `throw`, `throws`, `finally` | `try` 블록 내에서 예외 발생 시 `catch`로 처리, `throws`는 메서드에서 예외를 선언, `finally`는 예외 발생 여부와 관계없이 실행됨. |
| Python              | `try`, `except`, `raise`, `finally` | `try` 블록 내에서 발생한 예외를 `except`로 처리하며, `raise`로 예외를 던진다. `finally`는 항상 실행됨. |
| C#                  | `try`, `catch`, `throw`, `finally`, `using` | `try` 블록 내에서 발생한 예외를 `catch`로 처리하며, `using`은 IDisposable 객체의 자동 해제를 위해 사용됨. |
| JavaScript          | `try`, `catch`, `throw`, `finally` | `try` 블록 내에서 예외 발생 시 `catch`로 처리하며, `finally`는 항상 실행됨. |
| Ruby                | `begin`, `rescue`, `raise`, `ensure` | `begin` 블록 내에서 발생한 예외를 `rescue`로 처리하며, `raise`로 예외를 던진다. `ensure`는 항상 실행됨. |
| PHP                 | `try`, `catch`, `throw`, `finally` | `try` 블록 내에서 예외 발생 시 `catch`로 처리하며, `finally`는 항상 실행됨. |
| Swift               | `do`, `try`, `catch`, `throw` | `do` 블록 내에서 발생한 예외를 `catch`로 처리하며, `throw`로 예외를 던진다. |
이 표는 다양한 프로그래밍 언어에서 예외 처리를 위한 키워드를 정리하여 각 언어의 예외 처리 메커니즘을 이해하는 데 도움이 됩니다. 

 

#include <iostream>
using namespace std;

int main() {
    cout << "디폴트\n"; // 기본 출력

    cout.width(10); // 다음 출력의 너비를 10으로 설정
    cout << -50 << endl; // 너비 설정 후 -50 출력

    cout << "[ * fill ]\n"; // 출력 구분을 위한 메시지

    cout.fill('A'); // 빈 공간을 'A'로 채움
    cout.width(10); // 다음 출력의 너비를 10으로 다시 설정
    cout << -50 << endl; // 'A'로 채워진 너비 10에 -50 출력

    cout.width(10); // 다음 출력의 너비를 10으로 설정
    cout << 100.25 << endl; // 너비 10에 100.25 출력

    cout.width(10); // 다음 출력의 너비를 10으로 설정
    cout << "HanSH" << endl; // 너비 10에 문자열 "HanSH" 출력

    cout.fill(' '); // 빈 공간을 공백으로 채움 (기본값 복원)
    cout.precision(6); // 전체 자리수를 6자리로 설정 (소수점 제외)
    cout << 12.34567 << endl; // 6자리로 설정된 12.34567 출력

    cout << fixed; // 소수점 이하의 자리수만 다루게 설정
    cout.precision(3); // 소수점 이하 3자리로 설정
    cout << 12.34567 << endl; // 소수점 이하 3자리로 설정된 12.34567 출력

    return 0; // 프로그램 종료
}





#include <iostream>
#include <iomanip> // 입출력 조작을 위한 헤더 파일
using namespace std;

int main() {
    cout << "abcdefg\n"; // 문자열 출력

    cout << 12345 << endl; // 정수 12345 출력
    cout << 123.45 << endl; // 실수 123.45 출력

    cout << "10칸\n"; // 출력 구분을 위한 메시지

    cout << setfill('*'); // 빈 공간을 '*'로 채우도록 설정
    cout << setw(10) << "abcdefg" << endl; // 너비 10에 'abcdefg' 출력, 부족한 부분은 '*'로 채워짐

    cout << setw(10) << 12345 << endl; // 너비 10에 12345 출력, 부족한 부분은 '*'로 채워짐

    cout << setw(10) << 123.45 << endl; // 너비 10에 123.45 출력, 부족한 부분은 '*'로 채워짐

    return 0; // 프로그램 종료
}

 

 

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	ofstream hout("www.txt");
	hout << "ananana";
	cout << "Smile";

	return 0;
}

이 C++ 코드는 파일 입출력을 사용하여 텍스트 파일에 데이터를 작성하는 간단한 프로그램입니다. 각 부분을 설명하겠습니다.
### 코드 설명
1. **헤더 파일 포함**:
   ```cpp
   #include <iostream>
   #include <fstream>
   ```
   - `iostream`: 표준 입력 및 출력을 위한 라이브러리입니다.
   - `fstream`: 파일 입출력을 위한 라이브러리입니다. 파일을 읽거나 쓰는 데 사용됩니다.

2. **네임스페이스 사용**:
   ```cpp
   using namespace std;
   ```
   - `std` 네임스페이스의 모든 요소를 사용할 수 있게 해줍니다. 이를 통해 `cout`, `ofstream` 등을 직접 사용할 수 있습니다.

3. **main 함수**:
   ```cpp
   int main()
   {
       ofstream hout("www.txt");
   ```
   - `ofstream`: 파일에 데이터를 쓰기 위한 스트림 객체입니다.
   - `hout("www.txt")`: `"www.txt"`라는 이름의 파일을 생성하거나 열고, 그 파일에 데이터를 쓸 준비를 합니다. 파일이 이미 존재하면 기존 파일을 덮어씁니다.

4. **파일에 데이터 쓰기**:
   ```cpp
   hout << "ananana";
   ```
   - `hout` 스트림을 사용하여 `"ananana"`라는 문자열을 `www.txt` 파일에 씁니다.

5. **콘솔 출력**:
   ```cpp
   cout << "Smile";
   ```
   - 콘솔에 `"Smile"`이라는 문자열을 출력합니다.

6. **프로그램 종료**:
   ```cpp
   return 0;
   ```
   - 프로그램을 정상적으로 종료합니다.

### 요약
이 프로그램은 `www.txt`라는 텍스트 파일을 생성하고, 그 파일에 `"ananana"`라는 문자열을 기록한 후, 콘솔에 `"Smile"`이라는 메시지를 출력합니다. 프로그램이 실행된 후, `www.txt` 파일에는 `"ananana"`라는 내용이 저장되어 있게 됩니다. 

파일 만들때 ofstream

파일 불어올때 ifstream

파일을만든 다음ㅇ[ .close을 해야 파일이 닫아지고 저데로 저장된다.

#include <iostream>
#include <fstream> // 파일 입출력을 위한 헤더 파일 포함
using namespace std;

int main()
{
    int x; // 정수를 저장할 변수 x 선언

    ofstream hout("a1.txt"); // "a1.txt"라는 이름의 파일을 생성하거나 연다.
    hout << "100"; // 파일에 문자열 "100"을 기록
    hout.close(); // 파일 쓰기를 마치고 파일을 닫음

    //cout << "Smile"; // 주석 처리된 코드: 콘솔에 "Smile"을 출력하는 코드 (현재는 실행되지 않음)

    ifstream hin("a1.txt"); // "a1.txt" 파일을 열어 읽기 위한 입력 스트림 hin 생성
    hin >> x; // 파일에서 데이터를 읽어 변수 x에 저장
    cout << x; // 변수 x의 값을 콘솔에 출력 (이 경우 100이 출력됨)

    return 0; // 프로그램 종료
}
주석 요약
헤더 파일 포함: iostream은 표준 입출력을 위한 라이브러리이고, 
fstream은 파일 입출력을 위한 라이브러리입니다.
변수 선언: 정수를 저장할 변수 x를 선언합니다.
파일 쓰기:
ofstream을 사용하여 파일에 데이터를 쓸 준비를 합니다.
"a1.txt" 파일에 "100"이라는 문자열을 기록합니다.
파일을 닫아 리소스를 해제합니다.
파일 읽기:
ifstream을 사용하여 파일을 읽기 위해 열고, 파일에서 값을 읽어 x에 저장합니다.
읽은 값 x를 콘솔에 출력합니다.
프로그램 종료: 프로그램이 정상적으로 종료됩니다.
이 주석은 코드의 각 부분이 어떤 역할을 하는지 설명하여, 코드를 이해하는 데 도움이 됩니다. 
각 단계에서 어떤 작업이 수행되는지를 명확히 알 수 있습니다.

 

 

#include <iostream>
using std::cout;
using std::endl;
int main(void)
{
    int x = 10;
    int& rx = x;//rx는 x의 참조자
    cout << x << " " << rx << endl;
    rx = rx + 10;
    cout << x << " " << rx << endl; //참조자(rx)에 변화를 주면 그 타켓(x)도 변함
    x = x + 10;
    cout << x << " " << rx << endl; //타켓(x)에 변화를 주면 그 참조자(rx)도 변함
    return 0;
}
//10 10
//20 20
//30 30

 

 

 

 

 

 

이번 학기에 뭘 학습했나?

g1학년 2학기 C++ 과목을 통해서 객체지향언어의 기본적인 문법을 익혔습니다.  C++11이후 바뀐 모던 C++의 특징에 대해서도 학습하였습니다.
g클래스를 통해 캡슐화하는 방법, 다형성으로 함수 오버로딩, 상속과 overriding을 통해 코드 재사용 등의 특징을 학습했습니다.
g또한, template, STL, exception handing, 파일 입출력 등의 문법도 익혀서 C++로 실무 프로젝트를 진행하는데 무리가 없도록 기본 문법을 확실히 이해하고 있습니다.
g포트폴리오?
n구글링/AI : C++ stl file 성적처리
g직접 만드는 것도 좋지만 좋은 소스를 많이 보고 만들기
g블로그 관리(저작권, 복붙, 비공개, 꾸준, 습관)

'인덕대 C++-출처 smile han' 카테고리의 다른 글

14예습  (0) 2024.12.01
13주  (0) 2024.11.25
13주차 예습  (0) 2024.11.24
12주차  (1) 2024.11.18
12주차 예습  (0) 2024.11.17