마이크로서비스 아키텍처의 서비스 디스커버리

NGINX | 2015년 10월 12일

편집자 – 총 7부로 구성된 이 연재 기사가 이제 완결되었습니다:

  1. 마이크로서비스 소개
  2. 마이크로서비스 구축: API 게이트웨이 사용
  3. 마이크로서비스 구축: 마이크로서비스 아키텍처의 프로세스 간 통신
  4. 마이크로서비스 아키텍처의 서비스 디스커버리(이 글)
  5. 마이크로서비스를 위한 이벤트 기반 데이터 관리
  6. 마이크로서비스 배포 전략 선택
  7. 모놀리스를 마이크로서비스로 리팩터링하기

전체 기사 모음과 NGINX Plus를 사용한 마이크로서비스 구현 정보를 전자책으로 다운로드할 수도 있습니다. 전자책 제목은 마이크로서비스: 설계부터 배포까지입니다. 또한 새로운 마이크로서비스 솔루션 페이지도 확인해 보세요.

이 글은 마이크로서비스를 활용한 애플리케이션 구축 시리즈의 네 번째 글입니다. 첫 번째 글에서는 마이크로서비스 아키텍처 패턴을 소개하고 마이크로서비스 사용의 장단점을 살펴보았습니다. 이 시리즈의 두 번째<.htmla> 글과 세 번째 글에서는 마이크로서비스 아키텍처 내 통신의 다양한 측면을 설명합니다. 이 글에서는 이와 밀접하게 관련된 서비스 디스커버리 문제를 살펴봅니다.

서비스 디스커버리를 사용하는 이유

REST API 또는 Thrift API를 제공하는 서비스를 호출하는 코드를 작성한다고 가정해 보겠습니다. 요청을 보내려면 코드에서 서비스 인스턴스의 네트워크 위치(IP 주소와 포트)를 알아야 합니다. 물리적 하드웨어에서 실행되는 기존 애플리케이션에서는 서비스 인스턴스의 네트워크 위치가 비교적 고정되어 있습니다. 예를 들어, 코드는 가끔 업데이트되는 구성 파일에서 네트워크 위치를 읽을 수 있습니다.

하지만 최신 클라우드 기반 마이크로서비스 애플리케이션에서는 다음 다이어그램에서 볼 수 있듯이 이 문제를 해결하기가 훨씬 더 어렵습니다.

최신 클라우드 기반 마이크로서비스 애플리케이션에서는 인스턴스 집합과 해당 IP 주소가 끊임없이 변경되므로 서비스 검색이 어렵습니다.

서비스 인스턴스에는 네트워크 위치가 동적으로 할당됩니다. 또한 자동 확장, 장애 및 업그레이드로 인해 서비스 인스턴스 집합도 동적으로 변경됩니다. 따라서 클라이언트 코드에서는 보다 정교한 서비스 디스커버리 메커니즘을 사용해야 합니다.

서비스 디스커버리에는 두 가지 주요 패턴이 있습니다. 바로 클라이언트 측 디스커버리와 서버 측 디스커버리입니다. 먼저 클라이언트 측 디스커버리부터 살펴보겠습니다.

클라이언트 측 서비스 디스커버리 패턴

When using 클라이언트 측 검색을 사용하는 경우 클라이언트는 사용 가능한 서비스 인스턴스의 네트워크 위치를 확인하고, 해당 인스턴스 간에 요청을 분산할 책임이 있습니다. 클라이언트는 사용 가능한 서비스 인스턴스의 데이터베이스인 서비스 레지스트리를 조회합니다. 그런 다음 부하 분산 알고리즘을 사용하여 사용 가능한 서비스 인스턴스 중 하나를 선택하고 요청을 보냅니다.

다음 다이어그램은 이 패턴의 구조를 보여 줍니다.

클라이언트 측 서비스 디스커버리에서는 클라이언트가 사용 가능한 서비스 인스턴스의 네트워크 위치를 파악하고 인스턴스 간에 요청 부하를 분산합니다.

서비스 인스턴스가 시작되면 해당 인스턴스의 네트워크 위치가 서비스 레지스트리에 등록됩니다. 인스턴스가 종료되면 서비스 레지스트리에서 제거됩니다. 서비스 인스턴스의 등록 정보는 일반적으로 하트비트 메커니즘을 통해 주기적으로 갱신됩니다.

Netflix OSS는 클라이언트 측 디스커버리 패턴의 훌륭한 사례입니다. Netflix Eureka는 서비스 레지스트리입니다. 서비스 인스턴스 등록을 관리하고 사용 가능한 인스턴스를 조회할 수 있는 REST API를 제공합니다. Netflix Ribbon은 Eureka와 연동하여 사용 가능한 서비스 인스턴스 간에 요청을 부하 분산하는 IPC 클라이언트입니다. 이 글의 뒷부분에서 Eureka를 더 자세히 살펴보겠습니다.

클라이언트 측 디스커버리 패턴에는 다양한 장단점이 있습니다. 이 패턴은 비교적 간단하며 서비스 레지스트리를 제외하면 다른 구성 요소가 없습니다. 또한 클라이언트가 사용 가능한 서비스 인스턴스를 알고 있으므로 일관된 해싱을 사용하는 등 애플리케이션에 특화된 지능적인 부하 분산 결정을 내릴 수 있습니다. 이 패턴의 한 가지 큰 단점은 클라이언트가 서비스 레지스트리에 결합된다는 점입니다. 서비스 클라이언트에서 사용하는 각 프로그래밍 언어와 프레임워크별로 클라이언트 측 서비스 디스커버리 로직을 구현해야 합니다.

지금까지 클라이언트 측 디스커버리를 살펴보았으니, 이제 서버 측 디스커버리를 살펴보겠습니다.

서버 측 디스커버리 패턴

서비스 디스커버리의 또 다른 접근 방식은 서버 측 디스커버리 패턴입니다. 다음 다이어그램은 이 패턴의 구조를 보여 줍니다.

클라이언트는 로드 밸런서를 통해 서비스에 요청을 보냅니다. 로드 밸런서는 서비스 레지스트리를 조회하고 각 요청을 사용 가능한 서비스 인스턴스로 라우팅합니다. 클라이언트 측 디스커버리와 마찬가지로 서비스 인스턴스는 서비스 레지스트리에 등록되거나 등록 해제됩니다.

The AWS Elastic Load Balancer(ELB)는 서버 측 검색 라우터의 한 예입니다. ELB는 일반적으로 인터넷에서 유입되는 외부 트래픽의 부하를 분산하는 데 사용됩니다. 하지만 가상 프라이빗 클라우드(VPC) 내부의 트래픽 부하를 분산하는 데도 ELB를 사용할 수 있습니다. 클라이언트는 ELB의 DNS 이름을 사용하여 ELB를 통해 요청(HTTP 또는 TCP)을 보냅니다. ELB는 등록된 Elastic Compute Cloud(EC2) 인스턴스 또는 EC2 Container Service(ECS) 컨테이너 간에 트래픽 부하를 분산합니다. 별도의 서비스 레지스트리는 없습니다. 대신 EC2 인스턴스와 ECS 컨테이너가 ELB 자체에 등록됩니다.

HTTP 서버와 NGINX Plus 및 NGINX 같은 로드 밸런서도 서버 측 디스커버리 로드 밸런서로 사용할 수 있습니다. 예를 들어, 이 블로그 게시물에서는 Consul Template을 사용하여 NGINX 리버스 프록시 구성을 동적으로 변경하는 방법을 설명합니다. Consul Template은 Consul 서비스 레지스트리에 저장된 구성 데이터를 바탕으로 임의의 구성 파일을 주기적으로 다시 생성하는 도구입니다. 파일이 변경될 때마다 임의의 셸 명령을 실행합니다. 이 블로그 게시물의 예에서 Consul Template은 리버스 프록시를 구성하는 nginx.conf 파일을 생성한 다음, NGINX에 구성을 다시 로드하도록 지시하는 명령을 실행합니다. 더 정교하게 구현하면 HTTP API 또는 DNS를 사용하여 NGINX Plus를 동적으로 재구성할 수 있습니다.

일부 배포 환경(예: Kubernetes 및 Marathon)에서는 클러스터의 각 호스트에서 프록시를 실행합니다. 이 프록시는 서버 측 검색 로드 밸런서 역할을 합니다. 서비스에 요청하기 위해 클라이언트는 호스트의 IP 주소와 서비스에 할당된 포트를 사용하여 프록시를 통해 요청을 라우팅합니다. 그러면 프록시는 클러스터 내에서 실행 중인 사용 가능한 서비스 인스턴스로 요청을 투명하게 전달합니다.

서버 측 디스커버리 패턴에는 여러 장단점이 있습니다. 이 패턴의 큰 장점 중 하나는 디스커버리의 세부 사항이 클라이언트로부터 추상화된다는 점입니다. 클라이언트는 로드 밸런서에 요청하기만 하면 됩니다. 따라서 서비스 클라이언트에서 사용하는 각 프로그래밍 언어와 프레임워크마다 디스커버리 로직을 구현할 필요가 없습니다. 또한 앞서 언급했듯이 일부 배포 환경에서는 이 기능을 기본으로 제공합니다. 하지만 이 패턴에도 몇 가지 단점이 있습니다. 배포 환경에서 로드 밸런서를 제공하지 않는다면, 직접 설정하고 관리해야 하는 고가용성 시스템 구성 요소가 하나 더 늘어납니다.

서비스 레지스트리

The 서비스 레지스트리는 서비스 디스커버리의 핵심 요소입니다. 서비스 인스턴스의 네트워크 위치를 저장하는 데이터베이스입니다. 서비스 레지스트리는 고가용성을 갖추고 최신 상태로 유지되어야 합니다. 클라이언트는 서비스 레지스트리에서 가져온 네트워크 위치를 캐시할 수 있습니다. 하지만 이 정보는 결국 오래되어 클라이언트가 서비스 인스턴스를 찾을 수 없게 됩니다. 따라서 서비스 레지스트리는 일관성을 유지하기 위해 복제 프로토콜을 사용하는 서버 클러스터로 구성됩니다.

앞서 언급했듯이 Netflix Eureka는 서비스 레지스트리의 좋은 예입니다. 서비스 인스턴스를 등록하고 조회할 수 있는 REST API를 제공합니다. 서비스 인스턴스는 POST 요청을 사용하여 네트워크 위치를 등록합니다. 30초마다 PUT 요청을 사용하여 등록 정보를 갱신해야 합니다. 등록 정보는 HTTP DELETE 요청을 사용하거나 인스턴스 등록이 시간 초과되면 삭제됩니다. 예상할 수 있듯이 클라이언트는 HTTP GET 요청을 사용하여 등록된 서비스 인스턴스를 조회할 수 있습니다.

Netflix는 고가용성을 실현합니다 각 Amazon EC2 가용 영역에서 하나 이상의 Eureka 서버를 실행하는 방식으로 말입니다. 각 Eureka 서버는 탄력적 IP 주소가 할당된 EC2 인스턴스에서 실행됩니다. DNS TEXT 레코드는 Eureka 클러스터 구성을 저장하는 데 사용됩니다. 이 구성은 가용 영역을 Eureka 서버의 네트워크 위치 목록에 매핑한 것입니다. Eureka 서버가 시작되면 DNS를 조회하여 Eureka 클러스터 구성을 가져오고, 피어를 찾은 다음, 사용되지 않은 탄력적 IP 주소를 자체적으로 할당합니다.

Eureka 클라이언트(서비스 및 서비스 클라이언트)는 DNS를 조회하여 Eureka 서버의 네트워크 위치를 찾습니다. 클라이언트는 동일한 가용 영역에 있는 Eureka 서버를 우선적으로 사용합니다. 그러나 사용 가능한 서버가 없으면 다른 가용 영역의 Eureka 서버를 사용합니다.

서비스 레지스트리의 다른 예는 다음과 같습니다:

  • etcd – 공유 구성 및 서비스 검색에 사용되는 고가용성 분산형 일관성 키-값 저장소입니다. etcd를 사용하는 대표적인 프로젝트로는 Kubernetes와 Cloud Foundry가 있습니다.
  • consul – 서비스를 검색하고 구성하기 위한 도구입니다. 클라이언트가 서비스를 등록하고 검색할 수 있는 API를 제공합니다. Consul은 상태 검사를 수행하여 서비스 가용성을 확인할 수 있습니다.
  • Apache Zookeeper – 분산 애플리케이션을 위해 널리 사용되는 고성능 조정 서비스입니다. Apache Zookeeper는 원래 Hadoop의 하위 프로젝트였지만, 현재는 최상위 프로젝트입니다.

또한 앞서 언급했듯이 Kubernetes, Marathon, AWS와 같은 일부 시스템에는 명시적인 서비스 레지스트리가 없습니다. 대신 서비스 레지스트리가 인프라에 기본적으로 내장되어 있습니다.

지금까지 서비스 레지스트리의 개념을 살펴보았으니, 이제 서비스 인스턴스가 서비스 레지스트리에 등록되는 방식을 살펴보겠습니다.

서비스 등록 옵션

앞서 언급했듯이 서비스 인스턴스는 서비스 레지스트리에 등록되고 등록 해제되어야 합니다. 등록 및 등록 해제를 처리하는 방법에는 몇 가지가 있습니다. 한 가지 방법은 서비스 인스턴스가 직접 등록하는 자체 등록 패턴입니다. 다른 방법은 다른 시스템 구성 요소가 서비스 인스턴스의 등록을 관리하는 서드 파티 등록 패턴입니다. 먼저 자체 등록 패턴을 살펴보겠습니다.

자체 등록 패턴

When using the 자체 등록 패턴을 사용하는 경우, 서비스 인스턴스는 서비스 레지스트리에 자신을 등록하고 등록을 해제할 책임이 있습니다. 또한 필요한 경우 서비스 인스턴스는 등록이 만료되지 않도록 하트비트 요청을 보냅니다. 다음 다이어그램은 이 패턴의 구조를 보여 줍니다.

서비스 검색에 자체 등록 패턴을 사용하면 서비스 인스턴스가 서비스 레지스트리에 스스로 등록하고 등록을 해제합니다.

이 접근 방식의 좋은 예로는 Netflix OSS Eureka 클라이언트가 있습니다. Eureka 클라이언트는 서비스 인스턴스의 등록 및 등록 해제와 관련된 모든 작업을 처리합니다. 서비스 디스커버리를 비롯한 다양한 패턴을 구현하는 Spring Cloud 프로젝트를 사용하면 서비스 인스턴스를 Eureka에 손쉽게 자동 등록할 수 있습니다. Java 구성 클래스에 @EnableEurekaClient 애너테이션을 추가하기만 하면 됩니다.

자체 등록 패턴에는 여러 장단점이 있습니다. 장점 중 하나는 비교적 간단하며 다른 시스템 구성 요소가 필요하지 않다는 것입니다. 하지만 서비스 인스턴스가 서비스 레지스트리에 종속된다는 큰 단점이 있습니다. 서비스에서 사용하는 각 프로그래밍 언어와 프레임워크별로 등록 코드를 구현해야 합니다.

서비스를 서비스 레지스트리와 분리하는 대안적 접근 방식은 서드파티 등록 패턴입니다.

서드 파티 등록 패턴

When using the 서드 파티 등록 패턴을 사용하는 경우 서비스 인스턴스는 서비스 레지스트리에 직접 등록할 책임이 없습니다. 대신 서비스 등록자라고 하는 다른 시스템 구성 요소가 등록을 처리합니다. 서비스 등록자는 배포 환경을 폴링하거나 이벤트를 구독하여 실행 중인 인스턴스 집합의 변경 사항을 추적합니다. 새로 사용 가능한 서비스 인스턴스를 감지하면 해당 인스턴스를 서비스 레지스트리에 등록합니다. 또한 종료된 서비스 인스턴스의 등록을 해제합니다. 다음 다이어그램은 이 패턴의 구조를 보여 줍니다.

서비스 디스커버리에 서드 파티 등록 패턴을 사용하면 별도의 서비스 등록자가 서비스 레지스트리에 서비스 인스턴스를 등록하고 등록을 해제합니다.

서비스 등록자의 한 예로 오픈 소스 Registrator 프로젝트가 있습니다. 이 프로젝트는 Docker 컨테이너로 배포된 서비스 인스턴스를 자동으로 등록하고 등록 해제합니다. Registrator는 etcd와 Consul을 비롯한 여러 서비스 레지스트리를 지원합니다.

서비스 등록자의 또 다른 예로는 NetflixOSS Prana가 있습니다. 주로 비 JVM 언어로 작성된 서비스를 위해 설계된 이 애플리케이션은 서비스 인스턴스와 나란히 실행되는 사이드카 애플리케이션입니다. Prana는 Netflix Eureka에 서비스 인스턴스를 등록하고 등록을 해제합니다.

서비스 등록자는 배포 환경에 내장된 구성 요소입니다. Auto Scaling 그룹에서 생성한 EC2 인스턴스는 ELB에 자동으로 등록할 수 있습니다. Kubernetes 서비스는 자동으로 등록되어 검색할 수 있게 됩니다.

서드 파티 등록 패턴에는 다양한 장단점이 있습니다. 주요 장점은 서비스가 서비스 레지스트리와 분리된다는 것입니다. 개발자가 사용하는 각 프로그래밍 언어와 프레임워크마다 서비스 등록 로직을 구현할 필요가 없습니다. 대신 전용 서비스에서 서비스 인스턴스 등록을 중앙 집중식으로 처리합니다.

이 패턴의 한 가지 단점은 배포 환경에 기본적으로 포함되어 있지 않은 경우, 별도로 설정하고 관리해야 하는 또 하나의 고가용성 시스템 구성 요소가 된다는 점입니다.

요약

마이크로서비스 애플리케이션에서는 실행 중인 서비스 인스턴스 집합이 동적으로 변경됩니다. 각 인스턴스에는 네트워크 위치가 동적으로 할당됩니다. 따라서 클라이언트가 서비스에 요청을 보내려면 서비스 디스커버리 메커니즘을 사용해야 합니다.

서비스 디스커버리의 핵심 요소는 서비스 레지스트리입니다. 서비스 레지스트리는 사용 가능한 서비스 인스턴스의 데이터베이스입니다. 서비스 레지스트리는 관리 API와 쿼리 API를 제공합니다. 서비스 인스턴스는 관리 API를 통해 서비스 레지스트리에 등록되거나 등록 해제됩니다. 시스템 구성 요소는 쿼리 API를 사용하여 사용 가능한 서비스 인스턴스를 검색합니다.

서비스 디스커버리에는 클라이언트 측 디스커버리와 서비스 측 디스커버리라는 두 가지 주요 패턴이 있습니다. 클라이언트 측 서비스 디스커버리를 사용하는 시스템에서는 클라이언트가 서비스 레지스트리를 조회하고 사용 가능한 인스턴스를 선택한 후 요청을 보냅니다. 서버 측 디스커버리를 사용하는 시스템에서는 클라이언트가 라우터를 통해 요청을 보냅니다. 라우터는 서비스 레지스트리를 조회한 후 사용 가능한 인스턴스로 요청을 전달합니다.

서비스 인스턴스를 서비스 레지스트리에 등록하고 등록 해제하는 방식에는 크게 두 가지가 있습니다. 한 가지는 서비스 인스턴스가 서비스 레지스트리에 직접 등록하는 자체 등록 패턴입니다. 다른 한 가지는 다른 시스템 구성 요소가 서비스를 대신하여 등록 및 등록 해제를 처리하는 타사 등록 패턴입니다.

일부 배포 환경에서는 Netflix Eureka, etcd, 또는 Apache Zookeeper와 같은 서비스 레지스트리를 사용하여 자체 서비스 검색 인프라를 구축해야 합니다. 다른 배포 환경에는 서비스 검색 기능이 기본으로 제공됩니다. 예를 들어, Kubernetes와 Marathon은 서비스 인스턴스의 등록 및 등록 해제를 처리합니다. 또한 각 클러스터 호스트에서 서버 측 검색 라우터 역할을 하는 프록시를 실행합니다.

NGINX와 같은 HTTP 리버스 프록시 및 로드 밸런서를 서버 측 검색 로드 밸런서로 사용할 수도 있습니다. 서비스 레지스트리는 라우팅 정보를 NGINX로 푸시하고 무중단 구성 업데이트를 호출할 수 있습니다. 예를 들어 Consul Template을 사용할 수 있습니다. NGINX Plus는 추가적인 동적 재구성 메커니즘을 지원합니다. 즉, DNS를 사용하여 레지스트리에서 서비스 인스턴스 정보를 가져올 수 있으며 원격 재구성을 위한 API도 제공합니다.

향후 블로그 게시물에서는 마이크로서비스의 다른 측면도 계속해서 자세히 살펴보겠습니다. 아래 양식을 통해 NGINX 메일링 리스트에 가입하시면 이 시리즈의 새 글이 게시될 때 알림을 받으실 수 있습니다.

편집자 – 총 7부로 구성된 이 연재 기사가 이제 완결되었습니다:

  1. 마이크로서비스 소개
  2. 마이크로서비스 구축: API 게이트웨이 사용
  3. 마이크로서비스 구축: 마이크로서비스 아키텍처의 프로세스 간 통신
  4. 마이크로서비스 아키텍처의 서비스 디스커버리(이 글)
  5. 마이크로서비스를 위한 이벤트 기반 데이터 관리
  6. 마이크로서비스 배포 전략 선택
  7. 모놀리스를 마이크로서비스로 리팩터링하기

전체 아티클 모음과 NGINX Plus를 사용한 마이크로서비스 구현 정보를 전자책으로도 다운로드할 수 있습니다. 전자책 제목은 마이크로서비스: 설계부터 배포까지입니다.

객원 블로거 Chris Richardson은 Amazon EC2용 초기 Java PaaS(서비스형 플랫폼)였던 최초의 CloudFoundry.com을 설립했습니다. 현재 그는 여러 조직을 대상으로 애플리케이션 개발 및 배포 방식을 개선할 수 있도록 컨설팅하고 있습니다. 또한 https://microservices.io에서 마이크로서비스에 관한 글을 정기적으로 게시합니다.


공유

관련 블로그 게시물

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
NGINX | 2023. 11. 30.

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric

이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
NGINX | 2023. 03. 23.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법

앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
NGINX | 2021. 09. 27.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션

개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX | 2021. 07. 06.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법

NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX | 2021. 06. 30.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리

NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
NGINX | 2015. 03. 25.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유

Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.