섹션 7. 의존관계 자동 주입 수강 완료!!
의존관계 주입 방법
@Autowired를 생략해도 자동으로 주입된다.@Autowired 시에 여러 빈이 매칭되면 @Primary가 우선권을 가진다.
@Primary보다 @Qualifier가 우선권이 높다.
프로세스 동기화
프로세스 내부 데이터 통신: 하나의 프로세스 내에 2개 이상의 스레드가 존재하는 경우의 통신. 프로세스 내부의 스레드는 전역 변수나 파일을 이용하여 데이터를 주고 받는다.
프로세스 간 데이터 통신: 같은 컴퓨터에 있는 여러 프로세스끼리 통신하는 경우. 공용 파일 또는 운영체제가 제공하는 파이프를 사용하여 통신한다.
네트워크를 이용한 데이터 통신: 여러 컴퓨터가 네트워크로 연결되어 있을 때, 프로세스는 소켓을 이용하여 데이터를 주고받는다. 소켓을 이용하는 프로세스 간 통신을 네트워킹이라고 한다.
통신 방향에 따른 분류
양방향 통신(duplex communication): 데이터를 동시에 양쪽 방향으로 전송할 수 있는 구조. 소켓 통신이 양방향 통신에 해당한다.
반양방향 통신(half-duplex communication): 데이터를 양쪽 방향으로 전송할 수 있지만 동시 전송은 불가능하고, 특정 시점에 한쪽 방향으로만 전송할 수 있는 구조다. (예: 무전기)
단방향 통신(simplex communication): 모스 신호처럼 한쪽 방향으로만 데이터를 전송할 수 있는 구조. 전역 변수와 파이프가 단방향 통신에 해당한다.
통신 구현 방식에 따른 분류
전역 변수를 사용하는 통신 방식의 문제점: 언제 데이터를 보낼지 데이터를 받는 쪽에서는 모른다. → 반복적으로 전역 변수의 값을 점검하는 수밖에 없다.
바쁜 대기(busy waiting): 상태 변화를 살펴보기 위해 반복문을 무한 실행하며 기다리는 것. → 자원 낭비가 심하다.
바쁜 대기 문제를 해결하기 위해서는 데이터가 도착했음을 알려주는 동기화(synchronization)를 사용한다. (예: 메신저에서 메시지가 도착했다고 알려주는 알림)
대기가 있는 통신(== 동기화 통신): 데이터를 받는 쪽은 데이터가 도착할 때까지 자동으로 대기 상태에 머물러 있다.
대기가 없는 통신(== 비동기화 통신): 데이터를 받는 쪽은 바쁜 대기를 사용하여 데이터가 도착했는지 여부를 직접 확인한다. (예: 전역 변수와 파일을 이용한 통신)
프로세스 간 통신에서 가장 중요한 것은 프로세스 동기화이다.
전역 변수를 이용한 통신
공동으로 관리하는 메모리를 사용하여 데이터를 주고받는 것이다.
전역 변수를 이용한 통신 방식은 주로 직접적으로 관련이 있는 프로세스 간에 사용한다.
예를 들어, 부모 프로세스가 전역 변수를 선언한 후 자식 프로세스를 만들면 부모 프로세스와 자식 프로세스가 통신을 할 수 있다.
→ 프로세스 P가 전역 변수 R에 데이터를 언제 쓸지 알 수 없다. 따라서 프로세스 C는 전역 변수의 값이 변할 때까지 바쁜 대기를 돌면서 계속 확인해야 한다.
파일을 이용한 통신
fd = open()
write()
read()
close()
파일을 이용한 통신은 부모-자식 관계 프로세스 간 통신에 많이 사용되며, 운영체제가 프로세스 동기화를 제공하지 않는다.
→ 프로세스가 알아서 동기화를 해야 하기 때문에 주로 부모 프로세스가 wait() 함수를 이용하여 자식 프로세스의 작업이 끝날 때까지 기다렸다가 작업을 시작한다.
파이프를 이용한 통신
운영체제가 제공하는 동기화 통신 방식으로, 파일 입출력과 같이 open() 함수로 기술자를 얻고 작업을 한 후, close() 함수로 마무리한다.
파이프를 이용한 통신은 단방향 통신이다. 양방향 통신을 하려면 파이프 2개를 사용해야 한다.
만약 프로세스 B가 파이프 1에 대해 읽기 연산을 수행했는데 프로세스 A가 파이프 1에 아직 쓰기 연산을 하지 않았다면 B는 대기 상태가 된다.
→ A가 1에 데이터를 쓰는 순간 자동으로 대기가 풀려 동기화가 이루어진다.
이름 없는 파이프: 일반적인 파이프. 부모-자식 같이 서로 관련 있는 프로세스 간 통신에 사용.
이름 있는 파이프: 서로 관련 없는 프로세스 간 통신에 사용.
소켓을 이용한 통신
일반적으로 원격 프로시저 호출은 소켓을 이용하여 구현한다.
다른 컴퓨터에 있는 프로세스와 통신을 하려면 그 컴퓨터의 위치를 파악하고, 원격지의 시스템 내 여러 프로세스 중 어떤 프로세스와 통신할지도 결정해야 한다.
이때 통신하고자 하는 프로세스는 자신의 소켓과 상대의 소켓을 연결한다.
프로세스가 소켓을 바인딩한 후 소켓에 쓰기 연산을 하면 데이터가 전송되고, 읽기 연산을 하면 데이터를 받게 된다.
소켓은 하나만 사용해도 양방향 통신이 가능하다. (→ 프로세스 동기화도 지원한다.)
프로세스 간 통신 요약
| 종류 | 운영체제 동기화 지원 | open()/close() 사용 |
|---|---|---|
| 전역 변수 | X (바쁜 대기) | X |
| 파일 | X (wait() 함수 이용) | O |
| 파이프 | O | O |
| 소켓 | O | O |
공유 자원(shared resource): 여러 프로세스가 공동으로 이용하는 변수, 메모리, 파일 등을 말한다.
→ 공동으로 이용되기 때문에 누가 언제 데이터를 읽거나 쓰느냐에 따라 결과가 달라질 수 있다. 따라서 프로세스들의 공유 자원 접근 순서를 정하여 예상치 못한 문제가 발생하지 않도록 해야 한다.
2개 이상의 프로세스가 공유 자원을 병행적으로 읽거나 쓰는 상황을 ‘경쟁 조건(race condition)이 발생했다’고 한다.
→ 경쟁 조건이 발생하면 공유 자원 접근 순서에 따라 실행 결과가 달라질 수 있다.
임계구역(critical section): 공유 자원 접근 순서에 따라 실행 결과가 달라지는 프로그램의 영역.
임계구역에서는 프로세스들이 동시에 작업하면 안 된다.
임계구역 해결 조건
상호 배제(mutual exclusion): 한 프로세스가 임계구역에 들어가면 다른 프로세스는 임계구역에 들어갈 수 없다.
한정 대기(bounded waiting): 어떤 프로세스도 무한 대기(infinite postpone)하지 않아야 한다. 즉, 특정 프로세스가 임계구역에 진입하지 못하면 안 된다. (한 놈이 독점하면 안 됨.)
진행의 융통성(progress flexibility): 한 프로세스가 다른 프로세스의 진행을 방해해서는 안 된다.
임계구역 문제를 해결하는 단순한 방법은 잠금(lock)을 이용하는 것이다.
잠금 해제와 동시에 동기화 신호(사용해도 좋다는 신호)를 보낸다.
세마포어
임계구역에 진입하기 전에 스위치를 사용 중으로 놓고 임계구역으로 들어간다.
이후 도착하는 프로세스는 앞의 프로세스가 작업을 마칠 때까지 기다린다.
프로세스가 작업을 마치면 세마포어는 다음 프로세스에 임계구역을 사용하라는 동기화 신호를 보낸다.
→ 임계구역이 잠겼는지 직접 점검하거나, 바쁜 대기를 하거나, 다른 프로세스에 동기화 메시지를 보낼 필요가 없다.
세마포어에서 잠금이 해제되길 기다리는 프로세스는 세마포어 큐에 저장되어 있다가 wake_up 신호를 받으면 큐에서 나와 임계구역에 진입한다.
→ 바쁜 대기를 하는 프로세스가 없다.
그러나 세마포어의 내부 코드가 실행되는 도중에 다른 코드가 실행되면 상호 배제와 한정 대기 조건을 보장하지 못한다.
세마포어의 가장 큰 문제는 잘못된 사용으로 인해 임계구역이 보호받지 못한다는 것이다.
모니터
공유 자원을 내부적으로 숨기고 공유 자원에 접근하기 위한 인터페이스만 제공함으로써 자원을 보호하고 프로세스 간에 동기화를 시킨다.
시스템 호출과 같은 개념이다.
→ 모니터는 요청받은 작업을 모니터 큐에 저장한 후 순서대로 처리하고, 그 결과만 해당 프로세스에 알려준다.
+) 22. 07. 05. 추가
SELECT NAME, COUNT(NAME) FROM ANIMAL_INS GROUP BY NAME HAVING COUNT(NAME) >= 2 ORDER BY NAME
SELECT HOUR(DATETIME), COUNT(HOUR(DATETIME)) FROM ANIMAL_OUTS WHERE HOUR(DATETIME) >= 9 AND HOUR(DATETIME) < 20 GROUP BY HOUR(DATETIME) ORDER BY HOUR(DATETIME)
➡️ 이건 BETWEEN을 쓰는 게 더 깔끔했을 듯...
SET @hour = -1;
SELECT (@hour := @hour + 1) as HOUR,
(SELECT COUNT(*) FROM ANIMAL_OUTS WHERE HOUR(DATETIME) = @hour) as COUNT
FROM ANIMAL_OUTS
WHERE @hour < 23
➡️ 이거 진짜 개어려워서 구글링 함... 뭐 무슨 조인 쓰는 것도 있던데 그건 진짜 복잡해 보여서;; 이 방법이 더 간단하긴 한데 사실 이해가 잘 안 된다.

+) 22. 07. 05. 추가 완!
Internet Protocol
3계층에서 논리 주소인 IP 주소를 사용한다.
인터넷에서 다른 컴퓨터와 통신할 때 사용하는 프로토콜.
IP 주소는 크기가 32비트(IPv4)인 주소를 네 부분으로 나눠 8비트의 10진수 형식으로 표현한다.
IPv4로는 전 세계의 모든 컴퓨터를 처리할 수 없기 때문에 IPv6라는 새로운 주소 체계가 등장하였다.
IP의 분류
사용 범위에 따른 구분
공인 IP: 공인 기관에서 인증한 공개형(public) IP 주소. 외부에 공개되어 있어 다른 컴퓨터 등에서 검색, 접근이 가능하다. 전 세계에서 단 하나만 존재한다.
사설 IP(가상 IP): 공유기를 이용해 만들 수 있는 가상의 IP 주소. 외부에 공개되지 않아 외부에서 검색 및 접근이 불가능하다. 가상 IP 주소는 주소 대역이 3개로 고정되어 있다.
192.168.xxx.xxx
172.10.xxx.xxx
10.xxx.xxx.xxx

할당 방식에 따른 구분
고정 IP: 사용자가 직접 IP 주소를 입력해 주소를 설정한다. 해당 컴퓨터의 IP 주소를 고정적으로 사용하며, IP 주소가 변경되면 안 되는 컴퓨터(파일 공유 서버 등) 등에 적합하다.
유동 IP: IP 주소를 할당하는 특정 서버가 보내주는 정보 그대로 컴퓨터에 자동 설정되는 방식(Dynamic Host Configuration Protocol). IP 주소가 수시로 변경된다.

공인 IP가 필요할 때
만약 포털 사이트의 IP가 유동 사설 IP라면, 이전에 접속하던 IP 주소로 접속하려 할 때 오류가 발생할 것이다.
따라서 서버 역할을 하는 컴퓨터는 반드시 고유한 주소가 필요하다.
서버가 유동 IP일 경우엔 DDNS(Dynamic DNS)를 사용하면 문제가 없다.
→ DNS 서버가 바뀐 IP를 저장하고 있기 때문에 클라이언트는 도메인 네임으로 접근이 가능하다.
사설 IP의 특징
하나만 존재하지 않기 때문에 외부에서 내 컴퓨터를 찾을 수 없다. (공중전화기를 생각해 보자. 공중전화는 사용자가 다른 전화기로 전화를 걸 순 있지만 상대방이 공중전화기로 전화를 거는 것은 불가능하다.)
*포트 포워딩을 사용하면 사설 IP로도 서버를 운영할 수 있다.
*특정 포트로 들어오는 패킷을 내부 컴퓨터로 넘겨주는 것.
Transmission Control Protocol
TCP 프로토콜은 신뢰할 수 없는 공용망에서도 정보유실 없는 통신을 보장하기 위해 세션을 안전하게 연결하고 데이터를 분할하고, 분할된 패킷이 잘 전송되었는지 확인하는 기능이 있다.
TCP에서는 분할된 패킷을 잘 분할하고 수신 측이 잘 조합하도록 패킷에 순서를 주고 응답 번호를 부여한다.
두 번호가 상호작용해 순서가 바뀌거나 중간에 패킷이 손실된 것을 파악할 수 있다.
송신자 | 수신자
1번 전송 → 1번 수신
ACK 2 수신 ← ACK 2 전송
2번 전송 → 2번 수신
ACK 3 수신 ← ACK 3 전송
송신 측이 1번 패킷을 보내고 수신 측이 이 패킷을 잘 받았다면 1번을 잘 받았으니 다음에는 2번을 달라는 표시로 ACK 번호 2를 준다.
윈도 사이즈와 슬라이딩 윈도
패킷이 잘 전송됐는지 확인하기 위해 별도 패킷을 받는 것 자체가 통신 시간을 늘린다. 송신자와 수신자가 먼 거리에 떨어져 있으면 응답을 기다리는 시간이 더 길어질 것이다.
그래서 데이터를 보낼 때 패킷을 하나만 보내는 것이 아니라 많은 패킷을 한꺼번에 보내고 응답을 하나만 받는다.
가능하면 최대한 많은 패킷을 한꺼번에 보내는 것이 효율적이지만 네트워크 상태가 안 좋으면 패킷 유실 가능성이 커지므로 적절한 송신량을 결정해야 한다.
TCP는 데이터에 유실이 발생하면 윈도 사이즈를 절반으로 떨어뜨리고 정상적인 통신이 되는 경우 서서히 하나씩 늘린다.
3-Way Handshake
TCP는 통신 전, 데이터를 안전하게 보내고 받을 수 있는지 미리 확인하는 작업을 거친다.
3번의 패킷을 주고받으면서 통신을 서로 준비하므로 ‘3방향 핸드셰이크‘라고 부른다.
서버는 클라이언트의 접속을 받아들일 수 있는 LISTEN 상태로 대기한다.
클라이언트에서 통신을 시도할 때 SYN 패킷을 보내는데, 이 상태를 SYN-SENT라고 한다.
SYN을 받은 서버는 SYN-RECEIVE 상태로 변경되고 SYN, ACK로 응답한다.
이 응답을 받은 클라이언트는 ESTABLISHED 상태로 변경하고 그에 대한 응답을 다시 서버로 보낸다.
서버도 클라이언트의 응답을 받고 ESTABLISHED 상태로 변경된다.
ESTABLISHED 상태는 서버와 클라이언트 간의 연결이 성공적으로 완료되었음을 나타낸다.
User Datagram Protocol
TCP와 달리 UDP는 4계층 프로토콜이 가져야 할 특징이 거의 없다.
UDP 헤더는 TCP와 비교하면 내용이 거의 없다. 4계층의 특징인 신뢰 통신을 위한 내용(시퀀스 번호, ACK 번호, 플래그, 윈도 사이즈)이 없다.
UDP는 데이터 전송을 보장하지 않는 프로토콜이므로 제한된 용도로만 사용된다.
음성 데이터나 실시간 스트리밍과 같이 시간에 민감한 프로토콜이나 애플리케이션을 사용하는 경우나, 사내 방송 혹은 증권 시세 데이터 전송에 사용되는 멀티캐스트처럼 단방향으로 다수의 단말과 통신해 응답을 받기 어려운 환경에서 주로 사용된다.
음성, 동영상은 디지털 환경에서는 연속된 데이터가 아닌 매우 짧은 시간 단위로 잘게 분할한 데이터가 전송되는 형태다.
실세계 데이터를 디지털화할 때는 시간을 잘게 쪼개 데이터를 샘플링하는데, 이런 데이터들은 다른 일반 데이터처럼 취급하면 시간 지연에 따른 어려움이 있다.
중간에 데이터 몇 개가 유실되는 것보다 재전송하기 위해 잠시 동영상이나 음성이 멈추는 것을 더 이상하게 느낄 수 있다.
이렇게 데이터를 전송하는 데 신뢰성보다 일부 데이터가 유실되더라도 시간에 맞추어 계속 전송하는 것이 중요한 화상회의 시스템과 같은 서비스인 경우, UDP를 사용한다.
30프레임짜리 동영상에서 1프레임이 잘린 29프레임만으로도 사람들의 눈에는 이질감이 별로 없지만 실시간 동영상을 전송하는 경우, 잘린 1개 프레임을 재전송하기 위해 일시적으로 화면이 멈추면 음성과 영상이 뒤늦게 전달되어 사용자는 네트워크 품질이 떨어진다고 생각한다.
UDP는 TCP와 달리 통신 시작 전 3-Way Handshake처럼 사전에 연결을 확립하는 절차가 없다.
대신 UDP에서 첫 데이터는 리소스 확보를 위해 인터럽트(Interrupt)를 거는 용도로 사용하고 유실된다.
그래서 UDP 프로토콜을 사용하는 애플리케이션이 대부분 이런 상황을 인지하고 동작하거나, 연결 확립은 TCP 프로토콜을 사용하고 애플리케이션끼리 모든 준비를 마친 후 실제 데이터만 UDP를 이용하는 경우가 대부분이다.
| TCP | UDP |
|---|---|
| 연결 지향(Connected Oriented) | 비연결형(Connectionless) |
| 오류 제어 수행함 | 오류 제어 수행 안 함 |
| 흐름 제어 수행함 | 흐름 제어 수행 안 함 |
| 유니캐스트 | 유니캐스트, 멀티캐스트, 브로드캐스트 |
| 전이중(Full Duplex) | 반이중(Half Duplex) |
| 데이터 전송 | 실시간 트래픽 전송 |
HyperText Transfer Protocol
분산 하이퍼미디어 환경에서 빠르고 간편하게 데이터를 전송하는 프로토콜.
웹 서버와 사용자의 인터넷 브라우저 사이에 문서를 전송하기 위해 사용되는 통신 규약을 말한다.
인터넷 주소를 지정할 때 http://www… 와 같이 하는 것은 www로 시작되는 인터넷 주소에서 하이퍼텍스트 문서의 교환을 http 통신규약으로 처리하라는 뜻이다.
HTTP는 Request / Response (요청 / 응답) 동작에 기반하여 서비스를 제공한다.
HTTP의 명령에 해당하는 HTTP Method를 이용해 클라이언트가 서버에 데이터를 전송하고, 반대로 서버에서 클라이언트로 데이터를 회신할 수 있다.
주요 명령
GET
클라이언트가 서버에 URL이 가리키는 웹 문서의 내용을 전송하도록 요구한다.
GET 방식은 데이터가 주소 입력란에 표시되기 때문에 보안에 매우 취약하다.
POST
클라이언트가 서버에 정보를 전송할 수 있도록 한다.
보통 게시판처럼 사용자로부터 입력된 정보를 서버에 전달하는 용도로 사용한다.
GET 방식과는 달리 보내려는 데이터가 URL을 통해서 노출되지 않기 때문에 최소한의 보안성은 갖추고 있다.
일반적으로 게시판의 목록이나 글 보기 화면은 접근 자유도를 부여하기 위해 GET 방식을 사용하고 글 저장, 수정, 삭제나 많은 양의 데이터를 전송할 때는 POST 방식을 사용한다.
HTTP는 0.9 버전부터 사용되었다.
0.9 버전은 서버로부터의 단순 읽기 기능만 지원한다. (GET 메서드만 지원)

⬆️ HTTP 0.9 버전의 연결
클라이언트가 웹 브라우저를 이용해 서버에 연결을 요청하면 서버는 서비스를 준비한다.
서버가 준비 상태가 되면(Connect) 클라이언트는 읽고자 하는 문서를 서버에 요청(Request)한다.
서버는 클라이언트가 요청한 문서를 전송(Response)하고 연결을 끊는다.(Close)
이 기본 연결 기능은 HTTP의 버전에 관계없이 동일하다.
하지만 HTTP 0.9는 하나의 웹 페이지 안에서도 텍스트와 그림마다 Connect 과정을 반복해야 했기 때문에 비효율적이고 오래 사용되지 못했다.
1.0 버전부터는 헤더가 생겼다.

요청문의 GET은 요청 메소드고, /index.php는 URL, HTTP/1.1은 HTTP 버전이다.
Response에는 상태 코드도 붙었다.

하지만 커넥션 하나 당 요청 하나랑 응답 하나만 처리할 수 있기 때문에 성능이 저하되고 통신 부하 문제도 발생하였다.
HTTP Response의 주요 상태 코드
그래서 1.1 버전에서는 *Persistent Connection이라는 개념을 도입하였다.
*지정한 timeout 동안 커넥션을 닫지 않는 방식
→ 한 커넥션을 열어두면 여러 요청이 커넥션을 사용할 수 있다.
하지만 HTTP는 순차적으로 요청을 처리하기 때문에 1번 요청에 대한 응답이 들어와야지만 다음 요청을 처리할 수 있다. 이 대기 시간을 해결하기 위해 파이프라이닝 기법을 도입한다.
Pipelining이란, 하나의 커넥션에서 응답을 기다리지 않고 순차적인 여러 요청을 연속적으로 보내 그 순서에 맞춰 응답을 받는 방식으로 지연 시간을 줄이는 방법이다.
하지만 파이프라이닝에도 문제는 있다.
바로 Head Of Line Blocking이다.
첫 번째 요청이 왔는데 서버에서 처리하는 시간이 너무 오래 걸리면 두 번째 요청, 세 번째 요청도 기다려야 한다는 점이다. 뒤의 요청이 더 짧게 걸리는데도 앞에 요청이 처리가 안 되고 있으면 뒤에 놈은 꿈도 못 꾼다는 거다.
또 다른 문제로는 Header 구조의 중복이 있다.
연속된 요청 같은 경우에는 헤더의 값이 같은 경우가 많은데도 중복을 거르지 않고 그대로 전송해 버린다.
그래서 나온 HTTP 2의 가장 큰 특징은 메시지 전송 방식을 바꿨다는 것이다.
기존에는 일반 텍스트 형식의 메시지를 보냈지만 2 버전에서는 바이너리 프레임이라는 계층이 생겼다.
메시지를 프레임이란 단위로 분할하고 바이너리로 인코딩한다.
그래서 일반 텍스트로 전송할 때보다 전송 속도가 빨라지고 오류 발생 가능성이 낮아졌다.
이를 바탕으로 Request와 Response의 Multiplexing이 가능해졌다.
프레임 단위로 쪼개져서 순서가 없어졌기 때문이다. (프레임은 인터리빙이라는 스킬을 갖고 있어서, 먼저 끝난 프레임이 끼어들어 조립할 수 있기 때문이다.)
→ Head Of Line Blocking 문제를 해결할 수 있게 됐다.
HTTP 2의 또 다른 특징으로는 Stream Prioritization이 있다.
리소스 간의 우선 순위를 설정할 수 있는 것이다.
먼저 끝내야 할 놈한테 우선순위 가중치를 줘서 먼저 응답하게끔 만든다.
또한 Server Push라는, 클라이언트가 요청하지 않은 리소스를 서버에서 알아서 Push해 보내줄 수 있는 기능도 생겼다.
클라이언트가 /page.html을 요청했는데 /script.js랑 /style.css도 덤으로 같이 보내는 것이다.
클라이언트가 나중에 요청할 것이라 생각하고 미리 줘 버리는 것이다.
마지막 특징! Header Compression
Static Dynamic Table을 통해 중복을 검출한다.
중복되지 않은 데이터만 인코딩하여, 헤더의 크기를 줄여 페이지의 로드 시간을 감소시켰다.
참고 자료
고재성 · 이상훈, IT 엔지니어를 위한 네트워크 입문
양대일, 정보 보안 개론 : 한 권으로 배우는 보안 이론의 모든 것
박기현, 데이터 통신과 컴퓨터 네트워크
이문규, “용어로 보는 IT”, https://terms.naver.com/list.naver?categoryId=59096
따라하면서 배우는 IT, “HTTP 프로토콜이란?”, https://youtu.be/TwsQX1AnWJU
우아한Tech, “쿨라임의 HTTP/1.1, HTTP/2, 그리고 QUIC”, https://youtu.be/xcrjamphIp4
동빈나, “아이피 주소(IP Address)의 개념을 자세히 이해해보자”, https://youtu.be/pRWD7oTeuFw