면접에서 제대로 답변 못한 김에 정리하는 객체 지향 프로그래밍 4가지 특징!

 

객체 지향 프로그래밍 (OOP) 란?

Object-Oriented Promgramming의 약자로, 객체 지향 프로그래밍(OOP)은 데이터를 객체라는 단위로 나누고, 객체 간의 상호작용을 통해 프로그램을 설계하는 방식이다.
캡슐화, 상속, 다형성, 추상화 같은 개념을 활용하여 코드의 재사용성, 유지보수성, 확장성을 높인다.
즉, 현실 세계를 모델링하여 프로그래밍에 적용하는 방법론이다.

 

C++ 예제 코드로 각각의 특징에 대해서 알아보자

 

캡슐화 (Encapsulation)

캡슐화는 데이터와 메서드를 하나의 객체로 묶고 데이터 접근을 제어한다. 이를 통해 외부에서 직접 접근하지 못하게 한다.

  • brand 와 speed는 private로 선언되어 외부에서 직접 접근할 수 없음
  • getBrand와 SetSpeed() 같은 메서드를 통해 데이터를 간접적으로 조작
#include <iostream>
#include <string>

class Car 
{
private: // 외부에서 직접 접근 불가
	std::string brand;
	int speed;

public:
	// 생성자
	Car(std::string b, int s) : brand(b), speed(s) {}

	// getter와 setter로 데이터 접근 제어
	std::string getBrand()
	{
		return brand;
	}

	void setBrand(std::string b)
	{
		brand = b;
	}

	int getSpeed()
	{
		return speed;
	}

	void setSpeed(int s)
	{
		if (s >= 0) // 유효성 검사
		{
			speed = s;
		}
	}

	void display()
	{
		std::cout << "Brand: " << brand << "\n";
		std::cout << "Speed: " << speed << "km/h\n";
	}
};

int main()
{
	Car myCar("Tesla", 120);
	myCar.display();

	myCar.setSpeed(150);
	std::cout << "Updated speed: " << myCar.getSpeed() << "km/h\n";

	return 0;
}

 

상속(Inheritance)

상속은 기존의 클래스의 속성과 동작을 새로운 클래스가 물려받는다.

  • Dog 클래스는 Animal 클래스를 상속받아 eat() 메서드를 사용할 수 있음
  • 상속을 통하여 코드 재사용성을 높일 수 있음
#include <iostream>

class Animal
{
public:
	void eat()
	{
		std::cout << "This animal eats food.\n";
	}
};

class Dog : public Animal // Animal class를 public 상속
{
public:
	void bark()
	{
		std::cout << "This dog barks\n";
	}
};

int main()
{
	Dog myDog;
	myDog.eat(); // 상속 받은 메서드
	myDog.bark(); // Dog 클래스 고유의 메서드

	return 0;
}

": public" 은 public 상속으로  아래와 같이 상속된다.

  • 상위 클래스의 public 멤버는 하위 클래스에서 public으로 상속
  • 상위 클래스의 protected 멤버는 하위 클래스에서 protected로 상속
  • 상위 클래스의 private 멤버는 하위 클래스에서 접근 불가능하지만, 상위 클래스의 메서드를 통해 간접적으로 접근 가능

 

 

다형성 (Polymorphism)

다형성은 같은 인터페이스로 다양한 동작을 구현하는 것을 의미한다.

주로 가상 함수와 오버라이딩을 통하여 구현된다.

  • draw() 메서드는 Shape 클래스의 포인터를 통해 호출되지만 실체 객체 타입 (Circle 또는 Rectangle)에 따라 동작이 달라짐
  • 코드의 유연성과 확장성을 높일 수 있음
#include <iostream>

class Shape
{
public:
	virtual void draw() // 가상함수
	{
		std::cout << "Drawing a shape.\n";
	}
};

class Circle : public Shape
{
public:
	void draw() override // 오버라이딩
	{
		std::cout << "Drawing a circle.\n";
	}
};

class Rectangle : public Shape
{
public:
	void draw() override // 오버라이딩
	{
		std::cout << "Drawing a rectangle.\n";
	}
};

int main()
{
	Shape* myShape; // 기본 클래스 포인터
	Circle myCircle;
	Rectangle myRectangle;

	myCircle.draw();
	myRectangle.draw();

	myShape = &myCircle;
	myShape->draw(); // Circle의 draw() 호출

	myShape = &myRectangle;
	myShape->draw(); // Rectangle의 draw() 호출

	return 0;
}

가상 함수 (Virtual Fucntion)는 기본 클래스(Base Class)에서 선언되며, 하위 클래스(Derived Class)에서 재정의(Override)될 수 있는 함수이다. 재정의하지 않아도 기본 클래스의 구현을 사용할 수 있다.

virtual 키워드를 사용해 선언한다.

 

추상화 (Abstraction)

추상화는 객체의 복잡성을 숨기고 꼭 필요한 부분만 노출하는 것을 의미한다.

C++에는 추상클래스와 순수 가상 함수로 구현할 수 있다.

  • Animal 클래스는 추상 클래스이며, sound()는 순수 가상 함수
  • Dog와 Cat 클래스는 이를 구현하여 자신만의 동작을 정의
#include <iostream>

// 추상 클래스 (순수가상함수 사용)
class Animal
{
public:
	virtual void sound() = 0; // 순수 가상 함수
};

class Dog : public Animal
{
public:
	void sound() override
	{
		std::cout << "Woof! Woof!\n";
	}
};

class Cat : public Animal
{
public:
	void sound() override
	{
		std::cout << "Meow?\n";
	}
}; 

int main()
{
	Animal* myAnimal; // 추상 클래스 포인터
	Dog myDog;
	Cat myCat;

	myAnimal = &myDog;
	myAnimal->sound(); // Dog의 sound() 호출

	myAnimal = &myCat;
	myAnimal->sound(); // Cat의 sound() 호출

	return 0;
}

순수 가상 함수 (Pure Virtual Function)는 C++의 추상 클래스(Abstract Class)에서 사용되는 함수로, 해당 클래스에서 반드시 하위 클래스에서 재정의해야 하는 메서드를 정의할 때 사용한다.

순수 가상 함수는 아래와 같은 형태로 선언된다.

virtual void functionName() = 0;
  • 순수 가상 함수가 포함된 클래스는 추상 클래스가 됨
  • 추상 클래스는 인스턴스화할 수 없음. 즉, 직접 객체를 생성할 수 없음
  • 순수 가상 함수를 정의함으로써, 공통 인터페이스를 제공하고, 해당 인터페이스에서 반드시 구현하도록 강제할 수 있음
  • 순수 가상 함수는 = 0을 사용하여 선언되며, 하위 클래스에서 반드시 재정의해야 함

Memory Mapped IO란?

주기억장치들의 일부 주소를 입출력장치에 할당하는 방법으로,

해당 방식은 별도의 하드웨어 신호핀을 두지않고 Address line에 따라 분기하여 하드웨어에 접근하는 방식이다.

특징으로는 

  • 메모리 일부 공간을 I/O 포트에 할당
  • 메모리와 입출력 번지 사이의 구별이 없음
  • 메모리 명령으로 사용 가능
  • 기억장치 이용 효율이 낮고 (I/O 포트가 주소영역을 공유하기 때문에)  H/W가 간단

상대되는 방식으로는 I/O Mapped I/O가 있다

 

 

STM32F103RB 기준으로 확인하기

임베디드 프로그래밍에 사용되는 ARM  프로세서는 memory mapped I/O 방식을 사용한다.

SSAFY 수업에서 사용했던 STM32F103RB 기준으로 설명해보겠다.

 

STM32F103RB 

datasheet에서 Memory mapping 부분을 확인하면 메모리가 어떻게 사용되는지 확인할 수 있다.

주소가 낮은 곳에 시스템과 기억장치가 맵핑되어 있고,

주소가 높은 곳에 입출력관리 및 peripherals(주변장치)가 메모리에 맵핑되어 있는 것을 확인할 수 있다.

 

많은 임베디드 시스템에 사용되는 memory mapped i/o 방식은 메모리에 맵핑된 해당 장치의 레지스터 직접 접근하여 제어한다. 

제어 방법은 referenece manual을 참고하여 해당 레지스터에 값을 입력하는 방식으로 제어할 수 있다.

 

만약 port A의 GPIO를 제어하고 싶으면 referenece manual의 3.3장 Memory map에서 base address를 찾고

원하는 설정 값에 따라서 base address + offset 주소에 설정 값을 넣어주면 제어할 수 있다!

'Computer Science > 임베디드' 카테고리의 다른 글

임베디드 개발 영역  (1) 2024.12.13
유선 통신 기초  (0) 2024.08.19

임베디드 개발 영역

임베디드 개발 영역은 크게 3개로 나뉜다

  • Application Level 임베디드 sw 개발자
  • Middleware Level 임베디드 sw 개발자
  • Low level 임베디드 sw 개발자

임베디드에서 사용되는 운영체제

  • 리눅스
  • RTOS
  • 자체적 운영체제 (firmware)

Applicaiton level 개발자

application이란?

운영체제 안에서 동작하는 프로그램

 

application 개발이란?

  • 각각 운영체제에 맞는 앱을 개발
    • 리눅스 운영체제
      • 리눅스 App 개발
    • RTOS 운영체제
      • RTOS 운영체제 App 개발
  • GUI 개발
  • 검증 SW 개발
  • 주로 C / C++ / C#을 주로 사용

 

Middleware level 개발자

Middleware란?

application과 운영체제의 중간 다리 역할을 한다

  • 운영체제의 신호를 App이 가져할 수 있는 API
  • App level에서 운영체제에 신호를 전달하는 API

middleware 개발이란?

  • application level 개발자를 위한 library / API 개발
  • 해당 문서 작성
  • 성능 최적화
  • 시스템 통합 및 테스트
  • 주로 사용되는 언어
    • Android
      • JAVA / C / C++
    • RTOS
      • C

Low level 개발자

Low level 개발이란?

  • Firmware 개발
    • H/W를 제어하는, 작은 운영체제 직접 개발
    • 주언어: C, Assembly
  • Device Driver 개발
    • 커널 내부에서 동작되는 프로그램으로 hw를 제어하는 프로그램 개발
    • 주언어: C

 

'Computer Science > 임베디드' 카테고리의 다른 글

Memory Mapped IO  (0) 2024.12.13
유선 통신 기초  (0) 2024.08.19

통신의 기본 요소

  1. 시간 동기화
  2. 데이터를 주고 받는 선
  3. 통신 대상 지정

 

시간 동기화

SPI 통신 - 해당 그림은 clock이 low에서 high로 갈 때 신호 구분 (라이징) / 반대의 경우인 폴링일 수도 있음

통신은 low와 high로 구분되어 오게 되는데 해당 신호 구분을 위하여 신호를 쪼갤 수 있는 기준이 필요

  1. 동기: master와 slave 간의 동기화 신호인 clock을 사용
  2. 비동기: master와 slave 간 같은 시간 베이스를 사용 (ex: baud rate), clock을 사용할 때에 비해 전송 속도에 한계가 있음

데이터를 주고 받는 선

데이터는 주는 선, 받는 선을 구분할 수도 있고, 한 선을 공통으로 같이 사용할 수 있음.

한 선을 공통으로 같이 사용하는 경우에는 송신과 수신이 동시에 이루어질 수 없음

  1. 전이중 방식: 송신선과 수신선이 구분, 송수신이 동시에 이루어짐
  2. 반이중 방식: 공통된 송수신선 사용,  송수신이 동시에 이루어질 수 없음

통신 대상 지정

master에서 보낸 신호가 어떤 slave로 갈지 정해줘야 함

  1. 별도의 선 존재 (ex: cs선)
  2. 모든 slave에 신호를 전달하는 대신 신호 앞에 주소를 명시하여 어떤 slave에게 보내는 신호인지 알 수 있도록 함

 

예시

UART (Universal Asynchronous Receiver/Transmitter)

UART

  • CLK선 없음 = 해당 통신은 비동기 통신이며, 신호 구분을 위하여 공통된 time base를 사용한다 (baud rate)
  •  RX, TX선이 각각 존재 = 전이중 방식으로 송수신이 동시에 가능. master에서 slave로 송신만 필요한 경우에는 TX 선만 있어도 됨
  • 기본적으로  UART는 1:1 통신을 염두에 두고 설계됨
  • GND선은 신호의 low 기준을 잡아줘서 안정적인 통신을 가능하게 함

SPI (Serial Peripheral Interface)

SPI

  • CLK선 존재(SCLK) = 동기 통신, UART에 비해서 빠른 데이터 전송 속도
  • MOSI (Master Out Slave In), MISO (Master In Slave Out)으로 송수신 선이 각각 존재하는 전이중 방식, 데이터의 송수신이 동시에 가능
  • SS (Slave Select or CS Chip Select)선이 존재하여 특정 slave와 활성화

I2C (Inter-Integrated Circuit)

I2C 통신선 및 데이터 구조

  • CLK선 존재(SCL) = 해당 통신은 동기 통신
  • 한 개의 데이터 선 = 반이중 방식으로 송수신이 동시에 불가능
  • SS선 없음, 데이터 구조 앞에 address 존재 = 모든 slave에 신호를 전달하고 데이터 앞의 address로 수신 slave를 지정 

CAN (Controller Area Network)

CAN 통신선 및 데이터 구조

  • CLK 선 없음 = 비동기 통신
  • CAN_H, CAN_L이라는 2개의 데이터 선으로 통신. 신호선 간의 전압 차이를 통해 데이터가 전송하는 차동 신호 방식. 해당 방식은 노이즈에 강하고, 데이터 신호의 무결성을 보장.
  • 따라서 데이터 선은 2개를 사용하지만 이는 송수신 선이 따로 구성되어 있는 것이 아니라 신뢰성이 높은 공통된 송수신선을 사용하는 방식인 반이중 통신
  • SS 선 없음, 데이터 구조 앞에 identifier 존재 = 모든 node에 신호를 전송하고 각 node는 identifier를 보고 메세지의 우선 순위와 필터링을 적용하여 필요한 메세지만을 수신하게 됨

'Computer Science > 임베디드' 카테고리의 다른 글

Memory Mapped IO  (0) 2024.12.13
임베디드 개발 영역  (1) 2024.12.13

HTTP 프로토콜

서버 쪽에 저장된 웹 표준 데이터(HTML, JavaScript, CSS)를 브라우저(클라이언트)로 받아오는 것이 HTTP 프로토콜

  • Hyper Transfer Protocol 
  • www에서 쓰이는 핵심 프로토콜로 문서의 전송을 위해 쓰이며, 오늘 날 거의 모든 웹 애플리케이션에서 사용 (음성, 화상 등 여러 종류의 데이터를 MIME로 정의하여 전송 가능)
  • Request / Response 동작에 기반하여 서비스 제공

 

 HTTP 1.0

  • 연결 수립, 동작, 연결 해체 단순함이 특징 (하나의 URL은 하나의 TCP 연결)
  • HTML 문서를 전송 받은 뒤, 연결을 끊고 다시 연결하여 데이터를 전송
  • 단순 동작 (연결 수립, 동작, 연결 해체)이 반복되어 통신 부하

HTTP 1.0은 데이터를 하나 받을 때마다 다시 연결

HTTP 1.1

한 번 연결할 경우, 필요한 데이터를 다 받은 다음 연결 종료


HTTP 요청 프로토콜

요청하는 방식을 정의하고 클라이언트 정보를 담고 있는 요청 프로토콜 구조

Request Line

  • 요청 타입
    • GET: 데이터를 서버에 보낼 때, URI(주소창)에 포함하여 보냄
    • POST: 데이터를 서버에 보낼 때, body에 포함하여 보냄

  • URI(Uniform Resource Identifier): 주소 전체; 인터넷 상에서 특정 자원(파일)을 나타내는 유일한 주소
    • scheme: 프로토콜의 형식로 이해하자
    • IP주소 [:포트]
      • 보통 도메인 주소 사용 (컴퓨터가 도메인 주소를 IP 주소로 바꿔줌) 
      • 포트 번호는 http 80번 / https 443번으로 자동으로 입력
    • /폴더이름/파일이름: 서버 쪽의 파일(프로그램)의 경로로 이해하자
    • ?query: 앞의 파일(프로그램)에 입력해주는 값


HTTP 응답 프로토콜

 

사용자가 볼 웹 페이지를 담고 있는 응답 프로토콜 구조

  • 200: client  요청 성공
  • 403: client가 권한이 없는 페이지 요청
  • 404: client가 서버에 없는 페이지를 요청
  • 500: server가 멈춘 경우
  • 504: 최대 session 수가 초과

HTTP 헤더 포맷

요청 헤더

클라이언트 정보를 담고 있는 요청 헤더

  • Content-Length: 메세지 바디 길이를 나타냄
  • Content-Type: 메세지 바디에 들어있는 컨텐츠 종류 (HTML 문서는 text/html )
  • Cookie: 서버로부터 받은 쿠키를 다시 서버에 보내주는 역할
  • Host: 요청된 URL에 나타난 호스트명을 상세하게 표시 (HTTP 1.1에서 필수)
  • User-Agent: client program에 대한 식별 가능 정보 제공

 응답 헤더

서버 정보를 담고 있는 응답 헤더

  • Server: 사용하고 있는 웹 서버의 소프트웨어에 대한 정보를 포함
  • Set-Cookie: 쿠키를 생성하고 브라우저에 보낼 때 사용, 해당 쿠키 값을 브라우저가 서버에게 다시 보낼 때 사용

 

 

 


 

실습

Burp Suite로 http 응답 intercept 해보기

Burp Suite는 요청과 응답을 중간에 가로채서 수정할 수 있게 해준다!
Burp Suite community edtion 설치

Burp Suite 설치 사이트

 

Professional / Community 2023.9.3

This release upgrades Burp's built-in browser and fixes a bug when scanning GraphQL APIs. Browser upgrade We have upgraded Burp's built-in browser to 116.0.5845.110 for Mac and Linux and 116.0.5845.11

portswigger.net

프로젝트 생성 신경 쓰지 않고 다음으로 진행 후
Proxy 탭의 Proxy settings 이동

Proxy settings에서 Intercept requests와 Intercept responses 체크

chrome extension: Proxy SwitchyOmega 설치

chrome store: Proxy SwitchyOmega

 

Proxy SwitchyOmega

Manage and switch between multiple proxies quickly & easily.

chrome.google.com

아래와 Option에서 설정해준다.
✅참고: 127.0.0.x 는 자기자신을 뜻하는 주소

Proxy SwitchOmega Proxy 설정 / Burp Suite intercept on 설정

⛔주의: Proxy SwitchOmega Proxy와 Burp Suite intercept 설정의 주소와 포트가 일치해야 함 

http 응답을 intercept하고 싶은 사이트에 접속해본다

Proxy SwitchOmega Proxy에서 Forward 버튼을 눌러서 조작하고 싶은 response가 보일 때까지 Forward를 누른다

response 페이지에서 우클릭과 관련된 rightClickOpenYn 변수를 true로 변경한다

받은 페이지에서 우클릭이 되는 것을 확인할 수 있다


참고자료

https://www.youtube.com/watch?v=TwsQX1AnWJU&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=28 

위 강의를 보고 정리한 글입니다.

 

NAT

  • NAT(Network Address Translation)은 IP 패킷의 TCP/UDP 포트 숫자와 소스 및 목적지의 IP 주소 등을 재기록하면서 라우터를 통해 네트워크 트래픽을 주고 받는 기술
  • 패킷에 변화가 생기기 때문에  IP나  TCP/UDP의 체크섬(checksum)도 다시 계산되어 재기록되어야 함
  • NAT를 이용하는 이유는 대개 사설 네트워크에 속한 여러 개의 호스트가 하나의 공인 IP 주소를 사용하여 인터넷에 접속하기 위함
  • 하지만 꼭 사설 IP를 공인 IP로 변환하는데에만 사용하는 기술은 아님


포트 포워딩

  • 포트 포워딩 또는 포트 매핑(port mapping)은 패킷이 라우터나 방화벽과 같은 네트워크 장비를 가로지르는 동안 특정 IP 주소와 포트 번호의 통신 요청을 특정 다른  IP와 포트 번호로 넘겨주는 네트워크 주소 변환(NAT)의 응용
  • 게이트웨이(외부망)의 반대쪽에 위치한 사설 네트워크에 상주하는 호스트에 대한 서비스를 생성하기 위해 흔히 사용

참고자료

https://www.youtube.com/watch?v=Qle5cfCcuEY&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=26

위 강의를 보고 정리한 글입니다.

 

 

TCP 프로토콜

TCP가 하는 일

  • 전송 제어 프로토콜(Transmission Control Protocol, TCP)은 인터넷에 연결된 컴퓨터에서 실행되는 프로그램 간에 통신을 안정적으로, 순서대로, 에러 없이 교환할 수 있게 함
  • TCP의 안정성을 필요로 하지 않는 애플리케이션의 경우 일반적으로 TCP 대신 비접속형 사용자 데이터그램 프로토콜(UDP)을 사용
  • TCP는 UDP보다 안전하지만 느림

TCP의 구조

안전한 연결을 지향하는 TCP 프로토콜

  • window: 데이터를 얼마나 더 보내야할 지 알려줌


TCP 플래그

TCP 플래그의 종류

어떤 플래그를 셋팅하고 보내느냐에 따라서 연결 유형이 달라짐

TCP의 주된 기능이 플래그에 따라 나뉘어 짐

  • U(Urgent Flag): 보내는 데이터에 급한 데이터가 있음
    • 밑의 Urgent Pointer 와 셋트
  • A(Acknowledgement Flag): 승인을 해주는 플래그
  • P(Push Flag): TCP 버퍼에 관계 없이 계속해서 데이터를 밀어넣음
  • R(Reset Flag): 연결을 새로고침
  • S(Synchronization Flag): 상대방과 연결을 시작할 때 무조건 사용, 보내진 다음부터 연결 동기화 시작 (가장 중요!)
  • F(Finish Flag): 종료


TCP를 이용한 통신과정

연결 수립 과정

TCP를 이용한 데이터 통신을 할 때, 프로세스와 프로세를 연결하기 위해 가장 먼저 수행되는 과정

  1. 클라이언트가 서버에게 요청 패킷을 보냄
  2. 서버가 클라이언트 요청을 받아들이는 패킷을 보냄
  3. 클라이언트는 이를 최종적으로 수락하는 패킷을 보냄

3개 과정을 3 Way Handshake 라고 함

데이터 송수신 과정

TCP를 이용한 데이터 통신을 할 때, 단순히 TCP 패킷만을 캡슐화해서 통신하는 것이 아닌 페이로드를 포함한 패킷을 주고 받을 때의 일정한 규칙

  • 보낸 쪽에서 또 보낼 때에는 SEQ 번호와 ACK 번호가 그대로
  • 받는 쪽에서 SEQ 번호 =  받은 ACK 번호
  • 받는 쪽에서 ACK 번호 = 받은 SEQ번호 + 데이터의 크기  

위 handshake에서 이어짐


TCP 상태전이도

  • 실선: 클라이언트의 상태 변화
  • 점선: 서버의 상태 변화
  • Listen
    • 포트 번호를 열어놓고 있는 상태
    • 서버가 프로그램을 사용하고 있는 상태
    • 서버가 요청을 듣고 있는 상태
  • Established
    • 연결이 수립된 상태

TCP 상태 변화

참고자료

https://www.youtube.com/watch?v=cOK_f9_k_O0&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=21

위 강의를 보고 정리한 글입니다.

 

UDP 프로토콜

UDP가 하는 일

  • 사용자 데이터그램 프로토콜 (User Datagram Protocol, UDP)은 유니버셜 데이터그램 프로토콜 (Universal Datagram Protocol)이라고 일컫기도 함
  • 전송 방식은 너무 단순해서 서비스의 신뢰성 낮음, 데이터 도착 순서가 바뀌거나, 중복되거나, 통보 없이 누락됨
  • 일반적으로 오류의 검사와 수정이 필요 없는 프로그램에서 수행할 것으로 가정

UDP 구조


UDP 프로토콜을 사용하는 프로그램

DNS 서버

도메인을 물으면 IP 을 알려줌

tftp 서버

UDP로 파일 공유

RIP 프로토콜

라이팅 정보를 공유


실습

tftpd를 사용하여 데이터 공유

tftpd64 다운로드

해당 프로그램 실행

 

호스트: 공유할 경로 설정 및 IP 주소를 선택

클라이언트: Tftp Client 탭으로 이동해서 Host 및 Port 설정

Tftp는 well-known이라서 포트가 69 고정

Get 버튼을 누르면 해당 파일을 호스트로부터 받게 됨

 


참고자료

https://www.youtube.com/watch?v=3MkI3FBFzX8&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=19

위 강의를 보고 정리한 글입니다.

 

+ Recent posts