스크립트를 통한 UTxO 지출 이해

https://cexplorer.io/article/understanding-utxo-spending-through-a-script

UTxO 지출은 스크립트 정의 논리를 사용하여 조건부로 수행할 수 있습니다. 이러한 경우 UTxO 지출자는 UTxO 잠금 해제를 가능하게 하는 데이터를 스크립트에 제공해야 합니다. 이 기사에서는 UTxO를 잠그는 방법과 유효성 검사기 스크립트를 실행하여 잠금을 해제하는 방법을 살펴보겠습니다.

UTxO는 트랜잭션을 통해 소비됩니다.
기사를 이해하려면 UTxO 회계 모델, UTxO의 구조, Shelley 주소 및 주요 자격 증명을 통해 UTxO를 소비하는 일반적인 방법을 이해해야 합니다. 우리는 이전 기사에서 이에 대해 설명했습니다.

아래 이미지에서는 관련 UTxO를 사용하는 두 가지 옵션을 포함하여 지불 자격 증명이 포함된 Shelley 주소를 볼 수 있습니다. 하나의 주소는 여러 UTxO와 연결될 수 있습니다.
image

결제 자격 증명은 주소의 자금을 소유한 사람과 UTxO를 사용할 수 있는 사람을 식별하는 Shelley 주소의 일부입니다. 결제 자격 증명은 키 자격 증명(공개/확인 키 기반) 또는 스크립트 자격 증명(스크립트 해시)일 수 있습니다.

UTxO를 소비하는 계기는 거래입니다. 다른 매개변수 중에서 트랜잭션에는 증인이 포함되어야 합니다. 증인은 자금 소유자가 거래를 승인했음을 증명하는 데이터입니다. 감시가 유효한 경우 트랜잭션은 입력 UTxO를 사용할 수 있습니다. 감시는 서명(키 자격 증명의 경우)이거나 스크립트 실행(스크립트 자격 증명의 경우)일 수 있습니다.

키 자격 증명의 경우 증인은 소유자의 개인 키로 만든 서명입니다. 서명은 UTxO를 보유하는 주소에 속하는 키 자격 증명(공개 키)과 일치해야 합니다.

사용자가 다른 사람에게 자금을 보내고 싶을 때 지갑은 그들을 위해 정기적인 거래를 구성합니다. 지갑은 적절한 UTxO를 선택하고 보낸 사람에게 거래에 서명하도록 요청합니다. 백그라운드에서는 소비되는 각 UTxO에 대한 트랜잭션에 증인이 삽입됩니다.

거래가 유효하고 Cardano 네트워크에 의해 완료될 경우(블록체인에 영구적으로 저장됨), 거래는 새 UTxO가 생성될 입력 UTxO를 사용합니다(정확히 발신자가 정의한 대로). 지출은 UTxO(자금)를 한 주소에서 다른 주소로 이동하는 것으로 간단히 상상할 수 있습니다. 이동 중에 자금을 재분배할 수 있지만 총 가치는 유지되어야 합니다(또는 그 이하).

블록체인의 마지막에는 거래가 포함된 새 블록이 일정한 간격으로 추가됩니다. Cardano 블록체인은 UTxO 전송에 대한 회계 기록으로 볼 수 있습니다.

이전 기사에서 자세히 설명했습니다. 이 기사에서는 증인이 스크립트 실행인 경우에 중점을 둡니다.

UTxO 잠금
스크립트(검증기 스크립트)를 통해 UTxO를 사용하는 것은 두 개의 트랜잭션을 포함하는 2단계 프로세스입니다. 첫 번째 거래는 자금을 잠그고 두 번째 거래는 스크립트를 실행하여 자금의 잠금을 해제합니다. 우리는 첫 번째 거래를 잠금 거래라고 부르고 두 번째 거래를 지출 거래라고 부릅니다.

첫 번째 트랜잭션은 스크립트 주소에 잠기는 UTxO를 생성하는 트랜잭션입니다. UTxO가 스크립트 주소에서 잠겨 있으면 스크립트가 실행되지 않습니다. (잠금 트랜잭션의 출력 UTxO에 정의된) 대상 주소, 즉 스크립트 주소만 중요합니다.

두 번째 트랜잭션은 스크립트 주소가 보유한 UTxO를 사용하려고 시도합니다. 그러면 유효성 검사기 스크립트가 실행됩니다. 스크립트의 반환 값(스크립트 실행 결과)에 따라 UTxO 잠금 해제 여부가 결정됩니다.

아래 이미지에서는 스크립트를 통해 UTxO를 잠그고 잠금 해제하는 과정을 볼 수 있습니다. 잠금 트랜잭션에는 대상 주소가 스크립트 주소를 표시하는 하나의 출력 UTxO가 있습니다. 그러면 UTxO가 스크립트 주소로 잠깁니다. 지출 거래는 스크립트 주소에서 UTxO를 소비하며, 이는 Cardano 노드(빨간색 화살표)에서 스크립트 실행을 트리거합니다. 그림은 기본 개념을 설명합니다. 이에 대해서는 아래에서 더 자세히 설명하겠습니다.


스크립트 주소라는 용어를 설명하겠습니다. Shelley 주소는 Cardano의 Shelley 시대에서 지원되는 모든 주소를 가리키는 일반적인 용어입니다. Shelley 주소는 자격 증명 및 네트워크 태그에 따라 다양한 유형과 형식을 가질 수 있습니다. Shelley 주소는 결제 자격 증명으로 스크립트 해시를 가질 수 있으며 이를 통해 스크립트 주소가 됩니다. 스크립트 주소라는 용어를 들으면 UTxO 지출은 스크립트를 통해서만 가능하다는 것을 알 수 있습니다.

스크립트 주소는 유효성 검사기 스크립트의 해시에서 파생됩니다. 개발자는 스크립트를 작성하고 해시를 만듭니다. 해시는 Shelley 주소의 결제 자격 증명으로 사용됩니다. UTxO가 스크립트 주소와 연결되면 스크립트를 통해서만 잠금을 해제할 수 있습니다. 스크립트는 여러 입력(나중에 자세히 설명)을 전달하며 실행이 True 반환 값으로 끝나면 자금이 사용될 수 있습니다.

중요한 한 가지를 참고하세요. 스크립트 주소가 생성되었으나 해당 스크립트(스크립트 내용)가 블록체인에 저장되지 않습니다. 스크립트 주소를 생성하는 데에는 스크립트의 해시만 사용되었습니다(실행하기에 충분하지 않습니다). 아래에서는 지출 거래의 유효성을 검사하기 위해 스크립트 내용이 Cardano 노드에 전달되는 방법을 알아봅니다.

아래 이미지에서는 결제 자격 증명에 스크립트 해시가 설정된 주소를 볼 수 있습니다. 스크립트의 소스 코드가 완료되고 테스트되면 개발자는 이를 스크립트 해시 생성을 위한 기초로 사용했습니다. 스크립트 해시는 스크립트 콘텐츠에서 파생된 고유 식별자입니다. 스크립트 해시는 해시를 생성하는 데 사용된 정확한 스크립트(스크립트 내용)가 유효성 검사(예: 스크립트 실행) 중에 사용되도록 보장하는 짧은 디지털 지문의 한 형태입니다. 이렇게 하면 다른 스크립트를 사용하여 UTxO 지출 조건을 변경할 수 없습니다.


스크립트 해시는 노드가 올바른 스크립트를 검증하는지 확인할 때 Cardano 노드에 의해 검증됩니다. 원본 스크립트에서 단일 문자를 변경하면 해당 해시가 완전히 달라집니다. 스크립트가 거래를 통해 전달되는 경우 스크립트 내용은 결제 자격 증명에 저장된 해시와 동일한 해시를 생성해야 합니다.

결제 자격 증명에 저장된 해시와 거래를 검증하기 위해 실행되는 스크립트 소스 코드의 해시가 항상 일치해야 합니다. 해시가 다르면 유효성 검사가 실패합니다.

거래가 자금을 전송(연결)하기 전에 스크립트 주소를 생성해야 합니다. 그렇지 않으면 잠금 트랜잭션은 자금을 보낼 위치와 스크립트를 사용하여 자금을 잠그는 방법을 알 수 없습니다.

스크립트 주소가 생성되면 아래 이미지에서 볼 수 있듯이 해당 주소로 자금을 보낼 수 있습니다. 트랜잭션에는 하나의 입력 UTxO와 두 개의 출력이 있습니다. 거래의 발신자는 Alice입니다. Alice는 입력 UTxO에 대한 단일 증인을 제공하여 트랜잭션을 승인합니다. Alice는 DEX에 속한 스크립트 주소로 1000 ADA를 보내고 싶어합니다. 그녀의 지갑에는 2500 ADA가 있는 UTxO만 있습니다. 첫 번째 대상 주소는 그녀의 것이며 1500 ADA를 그녀의 주소로 다시 반환합니다. 두 번째 대상 주소는 DEX에 속한 스크립트 주소입니다.

아래 이미지에서는 네트워크가 거래를 검증(및 완료)한 후의 스크립트 주소를 볼 수 있습니다. UTxO는 스크립트 주소와 연결됩니다.

Datum은 UTxO 지출과 관련된 일부 정보 또는 상태를 저장하는 데 사용할 수 있는 선택적 매개변수입니다. 데이텀은 UTxO와 함께 스크립트 주소에 저장되며 실행 중에 유효성 검사기 스크립트에 의해 액세스될 수 있습니다.

데이텀은 스크립트 주소에서 UTxO를 잠그는 트랜잭션에 삽입되므로 스크립트 주소에서 UTxO를 잠금 해제하는 트랜잭션에도 삽입됩니다.

아래 이미지에서는 Datum과 함께 스크립트 주소에 UTxO를 잠그는 트랜잭션을 볼 수 있습니다. 이 잠금 트랜잭션은 UTxO와 연결될 데이텀을 포함한다는 점에서만 이전 잠금 트랜잭션과 다릅니다.

아래 이미지에서는 네트워크가 거래를 검증(및 완료)한 후의 스크립트 주소를 볼 수 있습니다. UTxO는 스크립트 주소와 연관되어 있으며 Datum을 포함합니다.

스크립트 주소의 자금은 UTxO 지출 조건을 정의하는 스크립트 자체에 의해 제어됩니다. 스크립트는 증인, 데이텀, 구속자 및 스크립트 컨텍스트와 같은 일부 입력을 취하고 지출이 유효한지 여부를 나타내는 True 또는 False를 반환합니다. 스크립트 입력에 대해서는 나중에 자세히 설명하겠습니다.

이제 스크립트 주소에 UTxO가 잠겨 있습니다.

스크립트에 액세스
UTxO가 잠금 해제되는 방법을 설명하기 전에 노드가 스크립트 주소에서 UTxO를 사용하려는 트랜잭션을 검증하는 데 사용되는 스크립트를 찾는 방법을 살펴봐야 합니다. 스크립트의 내용은 Cardano 노드가 실행할 수 있도록(예: 지출 거래의 유효성 확인) 사용할 수 있어야 합니다.

스크립트 내용은 지출 거래 데이터에 스크립트 감시로 삽입될 수 있습니다. 이는 UTxO를 사용하려는 트랜잭션이 트랜잭션 본문의 일부로 스크립트 내용을 포함해야 함을 의미합니다. 이로 인해 거래 규모가 커지고 수수료도 증가합니다. 일반적으로 개발자가 스크립트를 가능한 한 작게 만들도록 하기 때문에 스크립트의 복잡성을 제한합니다(블록 크기가 제한되어 있고 사용자는 높은 수수료를 지불하고 싶지 않습니다).

사용자가 이러한 트랜잭션을 여러 번 보내면 동일한 스크립트의 내용이 블록체인에 여러 번 저장되므로 불필요한 공간 낭비가 됩니다.

아래 이미지에서 Witness 섹션(빨간색)의 스크립트 내용이 포함된 지출 거래를 볼 수 있습니다. 입력 UTxO가 유효성 검사에 사용해야 하는 스크립트가 스크립트 해시를 통해 어떻게 정의되는지 확인하세요. Cardano 노드는 거래 본문에서 스크립트 콘텐츠를 가져와 해시를 생성합니다. 해시는 결제 자격 증명(빨간색 화살표)에 포함된 스크립트 해시와 일치해야 합니다.

또 다른 가능성은 스크립트가 참조 스크립트로 UTxO와 연결될 수 있다는 것입니다. 이는 UTxO를 생성하는 잠금 트랜잭션이 트랜잭션 출력의 일부로 스크립트를 포함해야 함을 의미합니다. 스크립트의 내용은 블록체인에 참조 스크립트로 저장되며 해시를 통해 액세스할 수 있습니다.

따라서 스크립트는 블록체인에 한 번만 저장될 수 있으며 스크립트 내용을 포함하지 않고도 후속 트랜잭션에서 재사용할 수 있습니다. 대신 후속 트랜잭션은 해시로 스크립트를 참조할 수 있으며, 이는 스크립트 주소 내에서 식별되는 방식이기도 합니다. 이를 통해 거래 규모와 수수료가 줄어들고 스크립트의 유연성과 확장성이 향상됩니다.

아래 이미지에서는 스크립트 주소의 블록체인에 스크립트 내용을 저장하는 잠금 트랜잭션을 볼 수 있습니다.


먼저 위와 같이 스크립트 주소를 생성합니다. 스크립트 주소를 생성한 후에는 스크립트의 내용이 포함되지 않습니다. 스크립트 주소가 존재하면 스크립트의 내용이 트랜잭션을 통해 해당 주소에 저장될 수 있습니다.

아래 이미지에서는 네트워크가 거래를 검증(및 완료)한 후의 스크립트 주소를 볼 수 있습니다. 스크립트의 내용은 블록체인에 저장되며 다른 트랜잭션에서 참조할 수 있습니다.

스크립트와 연결된 스크립트 주소는 항상 하나만 있습니다. 그런 다음 사용자는 잠금 트랜잭션을 통해 다른 UTxO를 동일한 스크립트 주소로 보내고(따라서 잠그고) 지출 트랜잭션을 통해 해당 주소에서 해당 UTxO를 사용하려고 시도할 수 있습니다(잠금 해제에는 항상 스크립트 실행이 필요함).

스크립트가 포함된 UTxO는 스크립트를 충족하여 소비되지 않는 한 항상 스크립트 주소에 있습니다.

따라서 UTxO를 소비하는 트랜잭션은 트랜잭션 데이터에 스크립트를 포함하지 않고도 해시로 스크립트를 참조할 수 있습니다.

아래 그림에서는 스크립트의 내용을 저장하는 UTxO가 포함된 스크립트 주소(빨간색)와 다른 사용자가 해당 주소로 전송하여 해당 주소에서 사용하게 될 3개의 UTxO(파란색)를 볼 수 있습니다. 스크립트 실행. 스크립트가 사용되는 한 주소는 재사용됩니다.


이제 스크립트 주소에서 UTxO를 잠그는 방법과 Cardano 노드가 스크립트의 내용을 가져오는 방법을 알았습니다. UTxO 잠금 해제에 대해 자세히 살펴보겠습니다.

UTxO 잠금 해제
스크립트 주소에 잠겨 있는 UTxO는 스크립트 실행을 트리거하는 지출 거래를 통해 소비될 수 있습니다. UTxO가 스크립트 주소와 연결되어 있는 경우 스크립트는 항상 실행되어야 하며 이를 피할 수 있는 방법은 없습니다.

지출 거래의 출력 UTxO에 정의된 대상 주소와 새 UTxO를 통해 자금이 어떻게 배포되는지는 중요하지 않습니다.

Cardano 노드가 스크립트 실행이 필요한 지출 거래를 검증하는 방법을 살펴보겠습니다.

노드는 지출 거래가 1단계 유효한지 확인합니다. 즉, 올바르게 구성되었으며 처리 수수료를 지불할 수 있음을 의미합니다.

거래는 유효성 검사기 스크립트의 실행 비용을 충당할 수 있을 만큼 충분한 수수료를 지불해야 하며, 이는 스크립트의 크기와 복잡성에 따라 달라집니다. 수수료는 스크립트 코드 바이트당 필요한 ADA 양을 결정하는 coinPerUTxOWord 프로토콜 매개변수를 사용하여 계산됩니다.

트랜잭션이 1단계 검증을 통과하면 스크립트가 실행될 수 있으며, 그렇지 않으면 트랜잭션이 거부됩니다.

2단계 검증 중에 스크립트가 실행됩니다. 거래는 각 입력 UTxO를 사용(잠금 해제)할 수 있는 경우에만 유효합니다. 스크립 주소와 연결된 각 입력 UTxO에 대해 스크립트의 반환 값은 True여야 합니다.

노드는 트랜잭션이 사용하려고 하는 UTxO와 연관된 스크립트를 검색합니다. 스크립트는 해시로 참조되거나 거래 데이터에 삽입될 수 있습니다. 또한 노드는 검색되거나 삽입된 스크립트의 해시가 출력의 지불 자격 증명에 저장된 해시와 일치하는지 확인합니다. 일치하는 항목이 없으면 거래가 유효하지 않은 것으로 거부됩니다.

노드는 입력을 스크립트에 전달하고 실행합니다. 유효성 검사기 스크립트는 다음 입력을 얻을 수 있습니다.

데이텀(Datum): 스크립트가 잠그는 UTxO에 첨부된 데이터 조각입니다. Datum은 UTxO 소비와 관련된 일부 상태 또는 조건을 저장하는 데 사용될 수 있습니다.
Redeemer: 지출 입력에 첨부된 데이터 조각입니다. 이는 일반적으로 지출자로부터 스크립트에 대한 입력을 제공하는 데 사용됩니다. 이는 자금 잠금을 해제하는 데 필요한 일부 정보나 논리를 제공하는 데 사용될 수 있습니다. Redeemer는 스크립트 유형이나 내용에 관계없이 스크립트 기반 주소가 있는 모든 입력 UTxO에 필수입니다.
거래 컨텍스트: 지출 거래에 대한 정보를 나타내는 데이터 조각입니다. 이는 출력이 소비되는 방식에 대한 어설션을 만드는 데 사용됩니다. 예를 들어 스크립트 논리에 서명이 필요할 수 있습니다. 서명은 스크립트에서 요구하는 일부 자금이나 토큰의 소유권을 증명하는 데 사용될 수 있습니다.
스크립트는 지출 조건이 충족되는지 여부를 나타내는 True 또는 False를 반환합니다. True이면 트랜잭션이 승인되고 UTxO가 소비됩니다. False인 경우 트랜잭션이 거부되고 UTxO는 사용되지 않은 상태로 유지됩니다.

아래 그림은 지출 거래를 통한 UTxO 잠금 해제를 보여줍니다. 트랜잭션의 입력은 스크립트 주소(파란색 화살표)와 연결된 UTxO입니다.

트랜잭션은 스크립트를 참조합니다(이 예에서 스크립트 내용은 트랜잭션 본문의 일부가 아닙니다). Cardano 노드는 블록체인(UTxO)에 저장된 스크립트 콘텐츠를 가져오고 스크립트 콘텐츠의 해시를 결제 자격 증명(빨간색 화살표)의 스크립트 해시와 비교합니다.

다음으로 스크립트에 대한 입력을 가져와서 전달해야 합니다(녹색 화살표).

데이텀이 이미 블록체인에 저장되어 있는 경우 이를 지출 거래에 다시 삽입할 필요가 없습니다. 노드는 UTxO에 저장된 Datum 해시를 조회하여 Datum 값을 찾을 수 있습니다. 그러나 Datum이 블록체인에 저장되지 않은 경우 지출 거래는 UTxO의 Datum 해시와 일치하는 Datum 값을 제공해야 합니다. 이는 지출 거래를 확인할 때 스크립트가 Datum 값에 액세스할 수 있도록 하기 위한 것입니다.

또한 Redeemer 및 트랜잭션 컨텍스트가 스크립트의 입력으로 전달됩니다. 트랜잭션 컨텍스트에는 트랜잭션에 필요한 모든 정보가 포함되어 있으므로 입력 UTxO 및 출력 UTxO 목록, 수수료, 증인(서명), 데이텀, 유효성 검사 스크립트(또는 이에 대한 참조) 등이 포함됩니다.

입력이 스크립트에 전달되고 스크립트 로직이 실행됩니다. 입력은 반환 값에 영향을 미칩니다. 이 예에서는 반환 값이 True이므로 UTxO를 사용할 수 있습니다(검은색 화살표). 자금은 새로운 목적지 주소로 이동됩니다.


DEX 스크립트의 논리 정보
이번 장에서는 Cardano의 스마트 계약과 관련된 좀 더 자세한 내용을 설명하겠습니다.

이 예에서 Alice는 1000 ADA를 DEX의 스크립트 주소로 보냈습니다. 이는 스크립트 주소로 잠긴 UTxO를 생성했음을 의미합니다. 스크립트 주소는 DEX 플랫폼에서 토큰을 교환하기 위한 논리를 포함하는 DEX 유효성 검사기 스크립트의 해시에서 파생됩니다. 스크립트에는 주문 취소 논리도 포함될 수 있습니다.

Alice가 수동으로 트랜잭션을 생성할 필요가 없다는 것이 실제로 어떻게 가능합니까? 그녀는 거래소의 사용자 인터페이스를 사용하여 요청(스왑 주문)을 입력한 다음 거래를 확인하라는 메시지를 받았습니다. DEX(애플리케이션)가 Alice를 위한 트랜잭션을 생성했습니다.

아래 이미지를 보면 Cardano 생태계에서 스마트 계약이 애플리케이션의 오프체인 부분과 온체인 부분으로 구성될 수 있음을 알 수 있습니다. 오프체인 로직은 잠금 및 지출 트랜잭션 구성을 담당합니다. DEX의 경우 오프체인 로직은 서버(클라우드)에서 처리됩니다. 이 기사에서는 Cardano Virtual Machine을 통해 블록체인에서 발생하는 온체인 로직, 즉 스크립트 검증만 다루었습니다.
image

Alice는 트랜잭션을 구성하는 DEX의 오프체인 부분과만 상호 작용합니다. 잠금 거래(스왑 주문)에는 Alice의 서명이 필요합니다.

Alice는 DEX에서 주문을 취소하기로 결정하면 자금을 돌려받을 수 있습니다. 이를 위해 그녀는 스크립트 주소로 잠긴 UTxO를 소비하고 이를 자신의 주소로 다시 보내는 트랜잭션을 생성해야 합니다. 취소(지출) 트랜잭션은 오프체인 로직으로 구성되므로 Alice는 DEX의 인터페이스만 사용합니다. 그녀는 UTxO의 소유권을 증명하는 증인과 주문 취소 의사를 나타내는 구속자를 제공해야 합니다. 그런 다음 유효성 검사기 스크립트는 Alice가 실제로 UTxO의 소유자인지 확인합니다.

다양한 DEX(또는 기타 애플리케이션)의 유효성 검사기 스크립트는 기능과 사용자에게 제공하는 옵션이 크게 다릅니다.

자금을 잠금 해제하려면 DEX, Alice 또는 UTxO를 사용하려는 다른 사람이 서명, Redeemer 또는 Datum과 같은 유효성 검사 스크립트에 올바른 입력을 제공해야 합니다. 그런 다음 유효성 검사기 스크립트는 입력이 제안 가격 일치, 수수료 지불 및 유효 간격 준수와 같은 교환 규칙을 충족하는지 확인합니다.

기본적으로 누구나 자금을 사용할 수 있지만 DEX는 주문을 일치시키고 스왑을 수행할 것으로 예상됩니다. 즉, 스크립트 조건을 가장 잘 충족할 수 있습니다. 하지만 스크립트의 조건 중 하나가 Alice의 개인 키나 DEX로 서명하는 것일 수 있으므로 자금을 사용할 수 있는 그룹을 정의하는 것이 가능합니다.

DEX는 스크립트 주소의 자금을 훔칠 수 없습니다. DEX는 입력을 제공하고 사용자의 명령을 실행함으로써만 스크립트를 만족시킬 수 있습니다. DEX가 주문을 이행할 때 Alice의 서명이 필요하지 않습니다. DEX는 Alice와의 추가 상호작용 없이 스왑을 실행할 수 있습니다. 즉, DEX는 스크립트의 조건을 만족하는 지출 거래를 구성할 수 있습니다. Alice는 한 쌍의 토큰 교환 요청의 결과로 자신의 주소에서 토큰을 받습니다.

결론
유효성 검사기 스크립트는 일반적으로 매우 간단하며 제한된 양의 정보로 작동합니다. 그들은 기본적으로 스크립트 주소에서 자금을 쓸 수 있는지 여부만 결정합니다. 이를 위해서는 간단한 조건이면 충분할 수 있습니다. 대부분의 애플리케이션 로직은 오프체인 부분에 있어야 합니다. 유효성 검사 스크립트를 실행하면 분산 네트워크의 리소스가 소비되므로 서버나 사용자의 로컬 컴퓨터 리소스를 사용하는 것보다 비용이 더 많이 들 수 있습니다.

완벽하게
완벽하게
완벽하게