자습서: Azure App Service에서 엔드투엔드 사용자 인증 및 권한 부여

Azure App Service는 확장성 높은 자체 패치 웹 호스팅 서비스를 제공합니다. 또한, App Service는 사용자 인증 및 권한 부여를 기본적으로 지원합니다. 이 자습서에는 App Service 인증 및 권한 부여를 통해 앱을 보호하는 방법을 보여줍니다. 뷰 프런트 엔드가 있는 Express.js를 예로 사용합니다. App Service 인증 및 권한 부여는 모든 언어 런타임을 지원하며 자습서를 수행하면서 원하는 언어에 적용하는 방법을 알아볼 수 있습니다.

Azure App Service는 Linux 운영 체제를 사용하여 확장성이 뛰어난 자체 패치 웹 호스팅 서비스를 제공합니다. 또한, App Service는 사용자 인증 및 권한 부여를 기본적으로 지원합니다. 이 자습서에는 App Service 인증 및 권한 부여를 통해 앱을 보호하는 방법을 보여줍니다. 뷰가 있는 Express.js를 사용합니다. App Service 인증 및 권한 부여는 모든 언어 런타임을 지원하며 자습서를 수행하면서 원하는 언어에 적용하는 방법을 알아볼 수 있습니다.

이 자습서에서는 다음을 알아봅니다.

  • 기본 제공되는 인증 및 권한 부여를 사용하도록 설정
  • 인증되지 않은 요청에 대해 앱 보호
  • ID 공급자로 Microsoft Entra ID를 사용합니다.
  • 로그인한 사용자 대신 원격 앱에 액세스
  • 토큰 인증으로 서비스 간 호출 보안 유지
  • 서버 코드에서 액세스 토큰 사용
  • 클라이언트(브라우저) 코드에서 액세스 토큰 사용

이 시나리오를 완료한 후 다음 절차를 계속 진행하여 인증된 사용자로 Azure 서비스에 연결하는 방법을 알아봅니다. 일반적인 시나리오에는 특정 기능이 있거나 특정 테이블 또는 파일에 대한 액세스 권한이 있는 사용자로 Azure Storage 또는 데이터베이스에 액세스하는 것이 포함됩니다.

이 절차의 인증은 Azure App Service의 호스팅 플랫폼 계층에서 제공됩니다. 프런트 엔드 및 백 엔드 앱을 배포하고 이 웹앱이 성공적으로 사용되도록 인증을 구성해야 합니다.

Conceptual diagram show the authentication flow from the web user to the frontend app to the backend app.

사용자 프로필 가져오기

프런트 엔드 앱은 백 엔드 API를 안전하게 사용하도록 구성됩니다. 프런트 엔드 애플리케이션은 사용자에게 Microsoft 로그인을 제공한 다음 사용자가 백 엔드에서 가짜 프로필을 가져올 수 있도록 허용합니다. 이 자습서에서는 가짜 프로필을 사용하여 시나리오를 완료하는 단계를 간소화합니다.

소스 코드가 프런트 엔드에서 실행되기 전에 App Service는 App Service x-ms-token-aad-access-token 헤더에서 인증된 accessToken을 삽입합니다. 그런 다음 프런트 엔드 소스 코드는 백 엔드 API에 안전하게 액세스하기 위해 accessToken을 bearerToken으로 백 엔드 서버에 액세스하고 전송합니다. 백 엔드 서버는 백 엔드 소스 코드로 전달되기 전에 bearerToken의 유효성을 검사합니다. 백 엔드 소스 코드가 bearerToken을 받으면 사용할 수 있습니다.

이 시리즈의 다음 문서에서 bearerToken은 Microsoft Graph API에 액세스할 수 있는 범위가 있는 토큰으로 교환됩니다. Microsoft Graph API는 사용자의 프로필 정보를 반환합니다.

필수 조건

Azure를 구독하고 있지 않다면 시작하기 전에 Azure 체험 계정을 만듭니다.

1. 샘플 애플리케이션 복제

  1. Azure Cloud Shell에서 다음 명령을 실행하여 샘플 리포지토리를 복제합니다.

    git clone https://github.com/Azure-Samples/js-e2e-web-app-easy-auth-app-to-app
    

2. 앱 만들기 및 배포

리소스 그룹, 웹앱 계획, 웹앱을 만들고 단일 단계로 배포합니다.

  1. 프런트 엔드 웹앱 디렉터리로 변경합니다.

    cd js-e2e-web-app-easy-auth-app-to-app/frontend
    
  2. az webapp up을 사용하여 프런트 엔드 웹앱을 만들고 배포합니다. 웹앱 이름은 전역적으로 고유해야 하므로 <front-end-app-name>을 고유한 이름으로 바꿉니다.

    az webapp up --resource-group myAuthResourceGroup --name <front-end-app-name> --plan myPlan --sku FREE --os-type Windows --location "West Europe" --runtime "NODE:16LTS"
    
  3. 백 엔드 웹앱 디렉터리로 변경합니다.

    cd ../backend
    
  4. 동일한 리소스 그룹 및 앱 계획에 백 엔드 웹앱을 배포합니다. 웹앱 이름은 전역적으로 고유해야 하므로 <back-end-app-name>을 고유한 이니셜 또는 숫자 집합으로 바꿉니다.

    az webapp up --resource-group myAuthResourceGroup --name <back-end-app-name> --plan myPlan --os-type Windows --location "West Europe" --runtime "NODE:16LTS"
    
  1. 프런트 엔드 웹앱 디렉터리로 변경합니다.

    cd frontend
    
  2. az webapp up을 사용하여 프런트 엔드 웹앱을 만들고 배포합니다. 웹앱 이름은 전역적으로 고유해야 하므로 <front-end-app-name>을 고유한 이니셜 또는 숫자 집합으로 바꿉니다.

    az webapp up --resource-group myAuthResourceGroup --name <front-end-app-name> --plan myPlan --sku FREE --location "West Europe" --os-type Linux --runtime "NODE:16-lts"
    
  3. 백 엔드 웹앱 디렉터리로 변경합니다.

    cd ../backend
    
  4. 동일한 리소스 그룹 및 앱 계획에 백 엔드 웹앱을 배포합니다. 웹앱 이름은 전역적으로 고유해야 하므로 <back-end-app-name>을 고유한 이니셜 또는 숫자 집합으로 바꿉니다.

    az webapp up --resource-group myAuthResourceGroup --name <back-end-app-name> --plan myPlan --sku FREE --location "West Europe" --runtime "NODE:16-lts"
    

3. 앱 설정 구성

프런트 엔드 애플리케이션은 API 요청에 대한 백 엔드 애플리케이션의 URL을 알아야 합니다. 다음 Azure CLI 명령을 사용하여 앱 설정을 구성합니다. URL은 https://<back-end-app-name>.azurewebsites.net 형식이어야 합니다.

az webapp config appsettings set --resource-group myAuthResourceGroup --name <front-end-app-name> --settings BACKEND_URL="https://<back-end-app-name>.azurewebsites.net"

4. 프런트 엔드가 백 엔드 호출

프런트 엔드 앱을 탐색하고 백 엔드에서 가짜 프로필을 반환합니다. 이 작업은 프런트 엔드가 백 엔드에서 성공적으로 프로필을 요청하고 백 엔드가 프로필을 반환하는지 유효성을 검사합니다.

  1. 브라우저 https://<front-end-app-name>.azurewebsites.net에서 프런트 엔드 웹앱을 엽니다.

    Screenshot of web browser showing frontend application after successfully completing authentication.

  2. Get user's profile 링크를 선택합니다.

  3. 백 엔드 웹앱에서 반환된 가짜 프로필을 봅니다.

    Screenshot of browser with fake profile returned from server.

    falsewithAuthentication 값은 인증이 아직 설정되지 않았다는 것을 나타냅니다.

5. 인증 구성

이 단계에서는 두 웹앱에 대한 인증 및 권한 부여를 사용하도록 설정합니다. 이 자습서에서는 Microsoft Entra ID를 ID 공급자로 사용합니다.

또한 프런트 엔드 앱을 다음과 같이 구성합니다.

  • 프런트 엔드 앱에 백 엔드 앱에 대한 액세스 권한 부여
  • 사용 가능한 토큰을 반환하도록 App Service 구성
  • 코드의 토큰을 사용합니다.

자세한 내용은 App Services 애플리케이션에 대해 Microsoft Entra 인증 구성을 참조하세요.

백 엔드 앱에 대한 인증 및 권한 부여 사용하도록 설정

  1. Azure Portal 메뉴에서 리소스 그룹을 선택하거나 검색하여 어느 페이지에서든 리소스 그룹을 선택합니다.

  2. 리소스 그룹에서 리소스 그룹을 찾아 선택합니다. 개요에서 백 엔드 앱을 선택합니다.

  3. 백 엔드 앱의 왼쪽 메뉴에서 인증을 선택한 다음 ID 공급자 추가를 선택합니다.

  4. ID 공급자 추가 페이지에서 ID 공급자Microsoft를 선택하여 Microsoft 및 Microsoft Entra ID에 로그인합니다.

  5. 기본 설정을 적용하고 추가를 선택합니다.

    Screenshot of the backend app's left menu showing Authentication/Authorization selected and settings selected in the right menu.

  6. 인증 페이지가 열립니다. Microsoft Entra 애플리케이션의 클라이언트 ID를 메모장에 복사합니다. 이 값은 나중에 필요합니다.

    Screenshot of the Microsoft Entra Settings window showing the Microsoft Entra App, and the Microsoft Entra Applications window showing the Client ID to copy.

여기에서 중지하는 경우 App Service 인증 및 권한 부여로 이미 보호되는 자체 포함된 앱이 있습니다. 나머지 섹션에서는 프런트 엔드에서 백 엔드로 인증된 사용자를 "전달"하여 다중 앱 솔루션의 보안을 유지하는 방법을 보여 줍니다.

프런트 엔드 앱에 대한 인증 및 권한 부여 사용하도록 설정

  1. Azure Portal 메뉴에서 리소스 그룹을 선택하거나 검색하여 어느 페이지에서든 리소스 그룹을 선택합니다.

  2. 리소스 그룹에서 리소스 그룹을 찾아 선택합니다. 개요에서 프런트 엔드 앱의 관리 페이지를 선택합니다.

  3. 프런트 엔드 앱의 왼쪽 메뉴에서 인증을 선택한 다음 ID 공급자 추가를 선택합니다.

  4. ID 공급자 추가 페이지에서 ID 공급자Microsoft를 선택하여 Microsoft 및 Microsoft Entra ID에 로그인합니다.

  5. 기본 설정을 적용하고 추가를 선택합니다.

  6. 인증 페이지가 열립니다. Microsoft Entra 애플리케이션의 클라이언트 ID를 메모장에 복사합니다. 이 값은 나중에 필요합니다.

백 엔드에 프런트 엔드 앱 액세스 권한 부여

두 앱에 대해 인증 및 권한 부여를 사용하도록 설정했으므로 각 앱은 AD 애플리케이션으로 지원됩니다. 인증을 완료하려면 다음 세 가지를 수행해야 합니다.

  • 프런트 엔드 앱에 백 엔드 앱에 대한 액세스 권한 부여
  • 사용 가능한 토큰을 반환하도록 App Service 구성
  • 코드의 토큰을 사용합니다.

오류가 발생하여 앱의 인증/권한 부여 설정을 다시 구성하면 토큰 저장소의 토큰이 새 설정에서 다시 생성되지 않을 수 있습니다. 토큰이 다시 생성되도록 하려면 앱에서 로그아웃하고 다시 로그인해야 합니다. 이 작업을 수행하는 쉬운 방법은 브라우저를 개인 모드로 사용하여 앱의 설정을 변경한 후 개인 모드로 브라우저를 닫았다가 다시 여는 것입니다.

이 단계에서는 사용자를 대신하여 백 엔드 앱에 대한 프런트 엔드 앱 액세스 권한을 부여합니다. (기술적으로는 프런트 엔드의 AD 애플리케이션에 사용자 대신 백 엔드의 AD 애플리케이션에 액세스할 수 있는 권한을 부여합니다.)

  1. 프런트 엔드 앱의 인증 페이지에서 ID 공급자 아래의 프런트 엔드 앱 이름을 선택합니다. 이 앱 등록은 자동으로 생성되었습니다. 왼쪽 메뉴에서 API 권한을 선택합니다.

  2. 권한 추가를 선택한 다음, 내 API><back-end-app-name>을 선택합니다.

  3. 백 엔드 앱에 대한 API 권한 요청 페이지에서 위임된 권한user_impersonation를 선택한 다음, 권한 추가를 선택합니다.

    Screenshot of the Request API permissions page showing Delegated permissions, user_impersonation, and the Add permission button selected.

사용 가능한 액세스 토큰을 반환하도록 App Service 구성

이제 프런트 엔드 앱에는 로그인한 사용자로 백 엔드 앱에 액세스하는 데 필요한 권한이 있습니다. 이 단계에서는 백 엔드 액세스에 사용 가능한 액세스 토큰을 제공하도록 App Service 인증 및 권한 부여를 구성합니다. 이 단계에서는 백 엔드 앱에 대한 인증 및 권한 부여 사용에서 복사한 백 엔드의 클라이언트 ID가 필요합니다.

Cloud Shell의 프런트 엔드 앱에서 다음 명령을 실행하여 scope 매개 변수를 identityProviders.azureActiveDirectory.login.loginParameters 인증 설정에 추가합니다. <front-end-app-name><back-end-client-id>를 바꿉니다.

az extension add --name authV2
authSettings=$(az webapp auth show -g myAuthResourceGroup -n <front-end-app-name>)
authSettings=$(echo "$authSettings" | jq '.properties' | jq '.identityProviders.azureActiveDirectory.login += {"loginParameters":["scope=openid offline_access api://<back-end-client-id>/user_impersonation"]}')
az webapp auth set --resource-group myAuthResourceGroup --name <front-end-app-name> --body "$authSettings"

이 명령은 추가 사용자 지정 범위가 있는 loginParameters 속성을 효과적으로 추가합니다. 요청 범위에 대한 설명은 다음과 같습니다.

  • openid는 App Service에서 기본적으로 이미 요청했습니다. 자세한 내용은 OpenID Connect 범위를 참조하세요.
  • 사용자 편의를 위해 여기에 offline_access를 포함합니다(토큰을 새로 고치는 경우).
  • api://<back-end-client-id>/user_impersonation은 백 엔드 앱 등록에서 노출된 API입니다. 토큰 대상 그룹으로 백 엔드 앱을 포함하는 JWT 토큰을 제공하는 범위입니다.

  • Azure Portal에서 api://<back-end-client-id>/user_impersonation 범위를 보려면 백 엔드 앱에 대한 인증 페이지로 이동하여 ID 공급자 아래의 링크를 클릭한 다음, 왼쪽 메뉴에서 API 공개를 클릭합니다.
  • 대신 웹 인터페이스를 사용하여 필요한 범위를 구성하려면 인증 토큰 새로 고침의 Microsoft 단계를 참조하세요.
  • 일부 범위에는 관리자 또는 사용자 동의가 필요합니다. 이 요구 사항으로 인해 사용자가 브라우저에서 프런트 엔드 앱에 로그인할 때 동의 요청 페이지가 표시됩니다. 이 동의 페이지를 방지하려면 클라이언트 애플리케이션 추가를 클릭하고 프런트 엔드 앱 등록의 클라이언트 ID를 제공하여 API 표시에서 권한 있는 클라이언트 애플리케이션으로 프런트 엔드 앱 등록을 추가합니다.

이제 앱이 구성되었습니다. 이제 프런트 엔드는 적절한 액세스 토큰을 사용하여 백 엔드에 액세스할 준비가 되었습니다.

다른 공급자에 대한 액세스 토큰을 구성하는 방법에 대한 자세한 내용은 ID 공급자 토큰 새로 고침을 참조하세요.

6. 프런트 엔드가 인증된 백 엔드 호출

프런트 엔드 앱은 올바른 user_impersonation 범위의 사용자 인증을 백 엔드로 전달해야 합니다. 다음 단계에서는 이 기능에 대해 샘플에 제공된 코드를 검토합니다.

프런트 엔드 앱의 소스 코드 보기:

  1. 프런트 엔드 App Service 삽입 x-ms-token-aad-access-token 헤더를 사용하여 프로그래밍 방식으로 사용자의 accessToken을 가져옵니다.

    // ./src/server.js
    const accessToken = req.headers['x-ms-token-aad-access-token'];
    
  2. Authentication 헤더의 accessToken을 bearerToken 값으로 사용합니다.

    // ./src/remoteProfile.js
    // Get profile from backend
    const response = await fetch(remoteUrl, {
        cache: "no-store", // no caching -- for demo purposes only
        method: 'GET',
        headers: {
            'Authorization': `Bearer ${accessToken}`
        }
    });
    if (response.ok) {
        const { profile } = await response.json();
        console.log(`profile: ${profile}`);
    } else {
        // error handling
    }
    

    이 자습서는 시나리오를 간소화하기 위해 가짜 프로필을 반환합니다. 이 시리즈의 다음 자습서에서는 백 엔드 bearerToken을 Microsoft Graph와 같은 다운스트림 Azure 서비스 범위의 새 토큰으로 교환하는 방법을 보여 줍니다.

7. 백 엔드가 프로필을 프런트 엔드로 반환

프런트 엔드의 요청이 권한 부여되지 않은 경우 백 엔드 앱 서비스는 요청이 애플리케이션 코드에 도달하기 전에 401 HTTP 오류 코드와 함께 요청을 거부합니다. 백 엔드 코드에 도달하면(권한 부여된 토큰이 포함되어 있기 때문에) 액세스 토큰을 가져오기 위해 bearerToken을 추출합니다.

백 엔드 앱의 소스 코드 보기:

// ./src/server.js
const bearerToken = req.headers['Authorization'] || req.headers['authorization'];

if (bearerToken) {
    const accessToken = bearerToken.split(' ')[1];
    console.log(`backend server.js accessToken: ${!!accessToken ? 'found' : 'not found'}`);

    // TODO: get profile from Graph API
    // provided in next article in this series
    // return await getProfileFromMicrosoftGraph(accessToken)

    // return fake profile for this tutorial
    return {
        "displayName": "John Doe",
        "withAuthentication": !!accessToken ? true : false
    }
}

8. 앱으로 이동

  1. 브라우저에서 프런트 엔드 웹 사이트를 사용합니다. URL은 https://<front-end-app-name>.azurewebsites.net/ 형식입니다.

  2. 브라우저는 웹앱에 대한 인증을 요청합니다. 인증을 완료합니다.

    Screenshot of browser authentication pop-up requesting permissions.

  3. 인증이 완료되면 프런트 엔드 애플리케이션은 앱의 홈페이지를 반환합니다.

    Screenshot of web browser showing frontend application after successfully completing authentication.

  4. Get user's profile를 선택합니다. 이는 전달자 토큰의 인증을 백 엔드로 전달합니다.

  5. 백 엔드는 가짜 하드 코딩된 프로필 이름(John Doe)으로 응답합니다.

    Screenshot of web browser showing frontend application after successfully getting fake profile from backend app.

    truewithAuthentication 값은 인증이 아직 설정되었음을 나타냅니다.

9. 리소스 정리

이전 단계에서는 리소스 그룹에서 Azure 리소스를 만들었습니다.

  1. Cloud Shell에서 다음 명령을 실행하여 리소스 그룹을 삭제합니다. 이 명령을 실행하는 데 1분 정도 걸릴 수 있습니다.

    az group delete --name myAuthResourceGroup
    
  2. 이전에 백 엔드 및 프런트 엔드 앱에 대한 Enable authentication and authorization 섹션에서 찾아서 기록한 인증 앱의 클라이언트 ID를 사용합니다.

  3. 프런트 엔드 및 백 엔드 앱 모두에 대한 앱 등록을 삭제합니다.

    # delete app - do this for both frontend and backend client ids
    az ad app delete <client-id>
    

자주 묻는 질문

내 로컬 개발 컴퓨터에서 이 인증을 어떻게 테스트하나요?

이 절차의 인증은 Azure App Service의 호스팅 플랫폼 계층에서 제공됩니다. 동등한 에뮬레이터가 없습니다. 인증을 사용하려면 프런트 엔드 및 백 엔드 앱과 각각에 대한 구성 인증을 배포해야 합니다.

앱이 가짜 프로필을 표시하지 않습니다. 어떻게 디버깅하나요?

프런트 엔드 및 백 엔드 앱 모두에는 이 애플리케이션이 가짜 프로필을 반환하지 않을 때 인증을 디버깅하는 데 도움이 되는 /debug 경로가 있습니다. 프런트 엔드 디버그 경로는 유효성을 검사할 중요한 부분을 제공합니다.

  • 환경 변수:
    • BACKEND_URLhttps://<back-end-app-name>.azurewebsites.net으로 올바르게 구성되었습니다. 후행 슬래시나 경로를 포함하지 마세요.
  • HTTP 헤더:
    • x-ms-token-* 헤더가 삽입됩니다.
  • 로그인한 사용자의 Microsoft Graph 프로필 이름이 표시됩니다.
  • 토큰에 대한 프런트 엔드 앱의 범위user_impersonation입니다. 범위에 이 항목이 포함되어 있지 않으면 타이밍 문제일 수 있습니다. Azure 리소스에서 프런트 엔드 앱의 login 매개 변수를 확인합니다. 인증 복제를 위해 몇 분 정도 기다리세요.

애플리케이션 소스 코드가 각 웹앱에 올바르게 배포되었나요?

  1. 웹앱용 Azure Portal에서 개발 도구 -> 고급 도구를 선택한 다음 이동 ->을 선택합니다. 새 브라우저 탭 또는 창이 열립니다.

  2. 새 브라우저 탭에서 디렉터리 찾아보기 -> 사이트 wwwroot를 선택합니다.

  3. 다음이 디렉터리에 있는지 확인합니다.

    • package.json
    • node_modules.tar.gz
    • /src/index.js
  4. package.json의 name 속성이 웹 이름(frontend 또는 backend)과 동일한지 확인합니다.

  5. 소스 코드를 변경했고 다시 배포해야 하는 경우 해당 앱의 package.json 파일이 있는 디렉터리에서 az webapp up을 사용합니다.

애플리케이션이 올바르게 시작되었나요?

홈페이지가 요청되면 두 웹앱 모두 무언가를 반환해야 합니다. 웹앱에서 /debug에 연결할 수 없다면 앱이 올바르게 시작되지 않은 것입니다. 해당 웹앱의 오류 로그를 검토합니다.

  1. 웹앱용 Azure Portal에서 개발 도구 -> 고급 도구를 선택한 다음 이동 ->을 선택합니다. 새 브라우저 탭 또는 창이 열립니다.
  2. 새 브라우저 탭에서 디렉터리 찾아보기 -> 배포 로그를 선택합니다.
  3. 각 로그를 검토하여 보고된 문제를 찾습니다.

프런트 엔드 앱이 백 엔드 앱과 통신할 수 있나요?

프런트 엔드 앱은 서버 소스 코드에서 백 엔드 앱을 호출하기 때문에 브라우저 네트워크 트래픽에서 볼 수 있는 것이 아닙니다. 다음 목록을 사용하여 백 엔드 프로필 요청 성공 여부를 확인합니다.

  • 백 엔드 웹앱은 도달한 경우 프런트 엔드 앱에 오류를 반환합니다. 도달하지 못한 경우 프런트 엔드 앱은 상태 코드와 메시지를 보고합니다.
    • 401: 사용자가 인증을 올바르게 통과하지 못했습니다. 이는 범위가 올바르게 설정되지 않았음을 나타낼 수 있습니다.
    • 404: 서버에 대한 URL이 서버에 있는 경로와 일치하지 않습니다.
  • 백 엔드 앱의 스트리밍 로그를 사용하여 사용자 프로필에 대한 프런트 엔드 요청을 만드는 것을 확인합니다. 오류가 발생한 위치를 확인하는 데 도움이 되는 console.log의 소스 코드에 디버그 정보가 있습니다.

프런트 엔드 토큰이 만료되면 어떻게 되나요?

액세스 토큰은 일정 시간 후에 만료됩니다. 사용자에게 앱 재인증을 요구하지 않고 액세스 토큰을 새로 고치는 방법은 ID 공급자 토큰 새로 고침을 참조하세요.

다음 단계

학습한 내용은 다음과 같습니다.

  • 기본 제공되는 인증 및 권한 부여를 사용하도록 설정
  • 인증되지 않은 요청에 대해 앱 보호
  • ID 공급자로 Microsoft Entra ID를 사용합니다.
  • 로그인한 사용자 대신 원격 앱에 액세스
  • 토큰 인증으로 서비스 간 호출 보안 유지
  • 서버 코드에서 액세스 토큰 사용
  • 클라이언트(브라우저) 코드에서 액세스 토큰 사용

다음 자습서로 이동하여 이 사용자의 ID를 사용하여 Azure 서비스에 액세스하는 방법을 알아봅니다.