Microsoft Graph 실무 입문 #2 앱 등록과 인증: Entra ID, delegated vs application 권한

7 분 소요

1편에서 Graph Explorer로 첫 요청을 실행해 봤다면, 이제 내 코드가 같은 일을 하도록 만들 차례입니다. 그 사이에 있는 관문이 인증이고, Graph 학습에서 실질적으로 가장 어려운 부분이 여기입니다. API 자체는 단순한데, “내 앱이 어떤 자격으로, 누구의 데이터에, 어디까지 접근하는가"라는 질문에 답하는 구조가 낯설기 때문입니다. 이번 편은 그 구조를 실무에서 결정하는 순서대로 정리합니다.

미리 지도를 그리면 이렇습니다. ① Entra ID에 앱을 등록해서 신분증을 만들고, ② delegated냐 application이냐를 결정하고(이 결정이 나머지 전부를 좌우합니다), ③ 필요한 권한을 추가하고 동의를 받고, ④ 코드에서 토큰을 받아 호출합니다.

앱 등록부터 하는 이유: 사용자 계정과 앱의 분리 #

Graph를 호출하는 주체는 사람이 아니라 프로그램입니다. Entra ID는 프로그램에게도 신원을 요구하고, 그 신원을 만드는 절차가 앱 등록(app registration)입니다. 등록하면 두 가지 식별자가 생깁니다.

  • 애플리케이션(클라이언트) ID: 앱의 아이디입니다. 비밀이 아닙니다.
  • 테넌트 ID: 조직(테넌트)의 식별자입니다. 우리 회사의 Microsoft 365 공간을 가리킵니다.

등록 절차 자체는 짧습니다. Microsoft Entra 관리 센터(entra.microsoft.com) → App registrations → New registration에서 이름을 정하고, 지원 계정 유형은 사내 자동화라면 “이 조직 디렉터리의 계정만"을 고르면 됩니다. 등록 직후의 Overview 화면에서 위 두 ID를 확인할 수 있습니다. 여기까지는 아무 권한도 없는 빈 신분증입니다.

참고로 앱 등록에는 조직 설정에 따라 관리자 권한이 필요할 수 있습니다. 회사 계정에서 등록 메뉴가 막혀 있다면 IT 부서에 개발용 앱 등록을 요청하는 것이 정석 경로입니다. 그때 아래의 권한 구분을 알고 요청하면 대화가 훨씬 빨라집니다.

가장 중요한 결정: delegated인가 application인가 #

Graph의 모든 권한은 두 유형 중 하나입니다. 같은 “메일 읽기"도 두 가지 버전이 있고, 의미가 완전히 다릅니다.

Delegated (위임)Application (앱 전용)
실행 주체로그인한 사용자를 대신하는사용자 없이 앱 자신
접근 범위그 사용자가 볼 수 있는 것까지만권한이 허용하면 조직 전체
전형적 용도사용자가 조작하는 앱, CLI 도구, 웹 앱서버 배치, 데몬, 야간 자동화
토큰 획득사용자 로그인 과정이 필요시크릿/인증서로 즉시 (client credentials)
동의사용자 본인 동의(+필요시 관리자)항상 관리자 동의 필요

판단 기준은 하나의 질문으로 줄일 수 있습니다. “이 코드가 돌아가는 순간, 화면 앞에 사람이 있는가?” 있다면 delegated, 없다면(스케줄러가 새벽 3시에 돌리는 스크립트라면) application입니다.

이 구분이 중요한 이유는 보안의 무게가 다르기 때문입니다. delegated는 이중 제한입니다. 앱의 권한과 사용자의 권한의 교집합만 접근할 수 있어서, 앱에 Mail.Read가 있어도 로그인한 사용자 자신의 메일만 읽습니다. application은 다릅니다. Mail.Read를 application 권한으로 받으면 조직 전원의 메일함을 읽을 수 있습니다. 그래서 application 권한은 항상 관리자 동의가 필요하고, IT 부서가 신중해지는 이유도 정확히 여기에 있습니다.

시리즈 뒤에서 이 구분이 실제 문제로 돌아옵니다. 예를 들어 Teams 채널에 메시지를 보내는 API는 delegated로만 열려 있어서, “서버가 자동으로 공지를 올리는” 시나리오는 다른 경로를 써야 합니다(6편). API 문서의 Permissions 표에서 두 열을 확인하는 습관이 필요한 이유입니다.

권한 추가와 동의: 스코프의 실무 #

앱 등록 화면의 API permissions에서 Microsoft Graph 권한을 추가합니다. 권한 이름은 리소스.동작 패턴이라 읽기 쉽습니다. User.Read(내 프로필), Mail.Read(메일 읽기), Mail.Send(메일 발송), Files.ReadWrite(파일 읽기·쓰기), Calendars.ReadWrite(일정 읽기·쓰기) 같은 식이고, 뒤에 .All이 붙으면 범위가 “전체"로 넓어집니다(예: delegated의 Files.ReadWrite.All은 사용자가 접근 가능한 모든 파일, application의 같은 권한은 조직의 모든 파일).

동의(consent)는 “이 앱이 이 권한을 쓰는 것을 승인한다"는 절차입니다.

  • delegated 권한 일부는 사용자 본인이 로그인 시 동의할 수 있습니다(조직 정책에 따라 다릅니다).
  • application 권한과 민감한 delegated 권한은 관리자 동의(admin consent)가 필요합니다. API permissions 화면의 “Grant admin consent” 버튼이 그것이고, 권한 목록의 Status 열이 초록색이 됐는지로 확인합니다.

실무 원칙은 최소 권한입니다. 문서의 Permissions 표에는 “least privileged"가 명시돼 있으니 항상 그것부터 시작합니다. Mail.Read로 충분한 자동화에 Mail.ReadWrite를 받아 두는 것은 사고 시의 폭발 반경을 스스로 키우는 일이고, IT 부서의 승인도 느려집니다. 참고로 application 권한에는 범위를 특정 자원으로 좁히는 장치(Exchange의 ApplicationAccessPolicy, SharePoint의 Sites.Selected 등)도 있어서, “조직 전체는 과하다"는 우려에는 이런 옵션을 함께 제시하면 승인 논의가 쉬워집니다.

자격 증명: 시크릿과 인증서 #

application 권한(그리고 일부 delegated 흐름)에서는 앱이 자신을 증명할 자격 증명이 필요합니다. 두 가지가 있습니다.

  • 클라이언트 시크릿: 문자열 비밀번호입니다. 만들기 쉽고, 시작할 때는 이것으로 충분합니다. 유효 기간(최대 2년, 짧게 잡는 것을 권장)이 있어서 만료가 곧 장애가 됩니다. 만료일을 캘린더에 적어 두는 것이 아니라, 만료 전 알림을 자동화하는 것까지가 운영입니다.
  • 인증서: 공개 키를 앱에 등록하고 개인 키로 서명하는 방식입니다. 시크릿보다 유출에 강해서 프로덕션 권장 사항이고, 조직에 따라 인증서만 허용하는 정책도 흔합니다.

어느 쪽이든 자격 증명은 코드와 저장소에 넣지 않습니다. 환경 변수나 비밀 관리자(Azure Key Vault 등)에 보관하는 것이 맞고, Git에 비밀키를 커밋했을 때의 절차를 겪지 않는 것이 최선입니다. Azure 안에서 돌아가는 워크로드라면 시크릿 자체가 없는 매니지드 아이덴티티가 가장 좋은 답이라는 것도 기억해 둘 만합니다.

토큰 흐름: 코드에서는 어떻게 보이는가 #

개념 정리를 코드 관점으로 옮기면 이렇게 됩니다. 상세 구현은 3편에서 하고, 여기서는 흐름만 봅니다.

두 권한 유형의 토큰 흐름
[delegated]
앱 → 사용자를 로그인 페이지로 안내 → 사용자 인증·동의
  → 앱이 "그 사용자를 대신하는" 토큰 획득 → Graph 호출
  (CLI 도구라면 device code flow: 코드를 보여 주고 사용자가 브라우저에서 입력)

[application]
앱 → 테넌트 ID + 클라이언트 ID + 시크릿/인증서로 토큰 요청
  → "앱 자신의" 토큰 즉시 획득 → Graph 호출
  (client credentials flow. 사람의 개입 없음)

토큰은 수명이 짧고(약 1시간) SDK가 갱신을 알아서 처리하므로, 실무에서 토큰 문자열을 직접 다룰 일은 드뭅니다. 문제가 생겼을 때 토큰 안의 권한(scp 또는 roles 클레임)을 확인하는 디버깅 요령만 3편에서 짚습니다.

인증이 막혔을 때: 에러 읽는 법 #

인증 단계의 에러는 패턴이 정해져 있습니다. 미리 알아 두면 몇 시간을 아낍니다.

  • 401 Unauthorized: 토큰 자체가 없거나 잘못됐습니다. 테넌트 ID·클라이언트 ID·시크릿 값을 다시 확인합니다.
  • 403 Forbidden: 토큰은 유효한데 권한이 없습니다. 대부분 셋 중 하나입니다. 권한을 추가만 하고 관리자 동의를 안 받았거나(Status 열 확인), delegated와 application을 헷갈렸거나, 그 API가 요구하는 권한이 아예 다릅니다. 문서의 Permissions 표와 대조하는 것이 가장 빠릅니다.
  • AADSTS로 시작하는 에러 코드: Entra ID의 에러입니다. 코드로 검색하면 공식 문서가 나오고, 메시지 안에 원인이 꽤 구체적으로 적혀 있는 편입니다.

정리 #

  • 인증의 순서는 앱 등록(신분증) → 권한 유형 결정 → 권한 추가와 동의 → 코드에서 토큰입니다. 클라이언트 ID·테넌트 ID는 식별자이고 비밀이 아니며, 시크릿·인증서가 비밀입니다.
  • 핵심 결정은 delegated vs application입니다. 화면 앞에 사람이 있으면 delegated(사용자 권한과의 교집합), 무인 자동화면 application(조직 전체 접근, 관리자 동의 필수)입니다.
  • 권한은 문서의 least privileged에서 시작합니다. application 권한의 범위를 좁히는 장치(ApplicationAccessPolicy, Sites.Selected)를 알아 두면 승인 논의가 빨라집니다.
  • 시크릿은 만료가 곧 장애입니다. 만료 알림을 자동화하고, 프로덕션은 인증서 또는 매니지드 아이덴티티를 권장합니다.
  • 401은 자격 증명, 403은 권한(동의 누락, 유형 혼동)입니다. AADSTS 코드는 검색이 가장 빠릅니다.

다음 편에서 이 인증 위에 Python SDK를 얹어 실제 코드로 첫 호출을 합니다. delegated(device code)와 application(client credentials) 양쪽을 모두 구현합니다.

X