AI와 함께하는 PnP PowerShell: SharePoint 데이터 가져오기 및 업무 도구 개발 판단 지점
요약
본 글은 PnP PowerShell을 활용하여 SharePoint 리스트 데이터 및 참조 파일을 가져오는 실무 가이드입니다. 일반 사용자 권한으로 접근하는 환경과, 내부명 확인의 중요성을 강조하며, `Get-PnPListItem` 명령어와 CAML 쿼리를 이용해 데이터를 효율적으로 추출하는 방법을 다룹니다.
핵심 포인트
- SharePoint 데이터 처리는 PnP PowerShell을 사용합니다.
- 일반 사용자 권한으로도 데이터 가져오기가 가능합니다.
- 화면명 대신 내부명을 확인하여 코드를 작성해야 합니다.
- CAML 쿼리를 활용해 조건 및 열 지정이 가능합니다.
서론
지난번에는 업무 요청을 계기로 처음으로 PnP PowerShell을 사용하여 SharePoint의 파일을 검색하고 가져온 경험에 대해 글을 썼습니다.
이 글은 SharePoint의 데이터 가져오기나 오프라인화를 AI에게 코드 작성을 요청하면서 진행하는 실무 담당자들을 위해 정리한 것입니다. Part 1에서는 데이터 구조와 가져오는 명령어, Part 2에서는 구현 중에 사람이 확인하고 판단해야 했던 부분을 소개합니다.
지난번에는 문서 라이브러리 내의 파일이 중심이었습니다. 이번에는 사이트 내 리스트에 저장된 본문과 그 본문에서 참조하는 파일을 다룹니다.
또한, 일련의 작업 과정에서 수십만 파일 규모의 처리를 다루었지만, 이 점에 대해서는 규모가 커졌을 때의 설계상 고려 사항만 기재했습니다.
이 글에서 다룰 내용
| 알고 싶은 것 | 내용 | 장 |
|---|---|---|
| SharePoint 구조를 조사하기 | 리스트, 내부 열 이름, 아이템 ID | 1・2 |
| ... |
Part 1. PnP PowerShell로 SharePoint 데이터 가져오기
1. SharePoint 연결 및 실행 환경
일반 사용자 권한으로 접근하기 위한 전제
이번에도 SharePoint 관리자 권한을 가지지 않은 사용자에 의한 데이터 가져오기를 전제로 합니다.
이 글의 대화형 인증에서는 로그인한 사용자의 접근권과 사용하는 Entra ID 앱의 위임 액세스 범위 내에서 조작합니다. 앱 등록이나 동의 준비는 조직 정책에 의존하므로, 여기서는 준비된 환경으로 연결하는 예시를 보여줍니다. 인증 방법 공식 자료도 참조해 주세요.
PowerShell 및 모듈 버전 확인하기
AI에게 오류 조사를 요청할 때 실행 환경을 전달할 수 있도록 합니다. 모듈명은 PnP.PowerShell입니다.
$PSVersionTable.PSVersion
# 설치된 버전
Get-Module -Name PnP.PowerShell -ListAvailable |
...
로드되기 전의 모듈은 마지막 명령어에서는 표시되지 않습니다.
2026년 10월 17일 기준으로 확인한 설치 절차에서는 PowerShell 7.4.0 이상이 필요합니다. 이번에 사용한 PnP.PowerShell은 3.1.0입니다.
SharePoint에 연결하기
여러 연결 대상이 필요한 경우 다음과 같습니다.
$SiteUrl =
화면에 표시되는 열 이름과 코드에서 지정하는 내부명은 다를 수 있습니다. AI에게 화면상의 이름만 전달하면 생성된 코드가 실제 열을 참조하지 못할 수 있습니다. 따라서 내부명과 타입을 확인한 후 가져올 대상을 지정해야 합니다.
이하에서는 다음과 같은 열이 있는 목록을 예시로 듭니다. `Body`를 포함하여, 열의 존재와 용도는 대상 목록에서 확인해 주세요.
| 내부명의 예 | 용도 |
|---|---|
| ID | 목록 내 아이템 식별 |
| ... |
### 3. 목적별 가져오기 명령어
#### 목록 아이템을 가져오기
Get-PnPListItem -List "DocumentCatalog"
-PageSize 500 `
...
`-PageSize`는 한 번의 페이지 요청으로 가져올 건수를 지정합니다. 이 예시에서는 대상 아이템을 500개씩 페이지로 나누어 가져옵니다. Get-PnPListItem 공식 자료에 구문과 사용 예가 있습니다.
#### CAML로 가져오 조건 및 열 지정하기
CAML은 SharePoint 목록에서 가져올 조건이나 정렬 순서를 XML로 기술하는 쿼리 언어입니다.
다음은 `Status`가 텍스트 타입이고 값이 `Published`인 아이템을 가져오는 예시입니다. 필요한 열도 `<ViewFields>`로 지정하고 있습니다.
$query = @""
<View>
<ViewFields>
...
열의 내부명과 `Value`의 타입은 대상 목록 정의에 맞춥니다. 대량 목록에 적용할 때는 다음 장의 임계값(しきい値)과 인덱스도 확인합니다.
개발 과정에서 `-Query`와 `-Fields`를 동시에 지정하여 매개변수 세트 충돌이 발생했습니다. 공식 구문에서는 이 두 가지를 동시에 지정할 수 없습니다. 위 예시처럼 CAML 내에서 가져올 열을 지정하는 형태로 수정했습니다.
참고로, 쿼리 언어는 다음 절차에 따라 가져오는 것도 가능합니다.
Get-PnPView -List "DocumentCatalog" | Select-Object Title, DefaultView
Title DefaultView
...
#### 아이템의 값 참조하기
$item = Get-PnPListItem -List "DocumentCatalog"
-Id 100 `
...
`FieldValues`에 내부명을 지정하여 값을 참조합니다. 본문의 열에 HTML이 저장되어 있다면, 그 값을 로컬 HTML 생성에 사용할 수 있습니다.
#### 목록 아이템의 첨부 파일 가져오기
$AttachmentPath = "C:\Export\attachments\100"
New-Item -ItemType Directory -Path $AttachmentPath -Force | Out-Null
Get-PnPListItemAttachment `
...
지정한 아이템의 첨부 파일을 저장합니다. 아이템 ID별로 폴더를 나누면 다른 아이템의 동명 파일도 구별할 수 있습니다. 여러 목록을 출력하는 경우, 목록별로도 저장 위치를 나눕니다.
`-Force`는 동명 파일이 있을 때 확인 없이 덮어쓰도록 지정하는 것입니다.
#### URL을 지정하여 파일 가져오기
$ImagePath = "C:\Export\images"
New-Item -ItemType Directory -Path $ImagePath -Force | Out-Null
Get-PnPFile `
...
본문의 이미지가 SiteAssets 등에 저장되어 있는 경우, 해당 파일의 URL을 사용하여 가져옵니다. 라이브러리 내 파일에도 활용할 수 있습니다. Get-PnPFile 공식 자료에 구문이 있습니다.
#### 명령어 요약표
| 목적 | 명령어 |
|---|---|
| 연결 | `Connect-PnPOnline` |
| ... |
### 4. 수십만 파일 규모를 염두에 둔 처리 설계
여기서는 가져올 대상이 늘어날 경우 검토할 항목들을 정리합니다. 본문의 아이템 수와 첨부 파일 수는 별도로 계산합니다. 예를 들어, 5만 개의 아이템에 평균 6개의 파일이 첨부되어 있다면, 첨부 파일만으로 약 30만 개의 파일입니다. 이 숫자는 규모를 설명하기 위한 가정입니다.
#### 5,000개 임계값과 페이지네이션 확인
SharePoint Online의 목록 뷰(list view) 임계값은 5,000건입니다. 이는 쿼리 등의 데이터베이스 작업으로 처리하는 항목 수와 관련이 있습니다. 대상 열에 인덱스를 설정하고, 충분히 대상을 좁힐 수 있는 조건을 검토해야 합니다.
`-PageSize`
는 결과 페이지 가져오기를 제어하는 지정자입니다. 쿼리 평가에서 발생하는 부하에 대해서는 조건이나 정렬 순서를 포함하여 확인합니다.
#### 모든 항목을 배열로 유지하는 부분 재검토
다음 작성 방식에서는 반환된 항목을 배열(array)로 유지합니다.
$items = @(
Get-PnPListItem
-List "DocumentCatalog"
...
호출 측에서 모든 항목을 유지할 필요가 없다면, 파이프라인으로 순차 처리하는 구성으로 만들 수 있습니다.
Get-PnPListItem
-List "DocumentCatalog"
-Fields "ID", "DocumentName", "Body"
...
이 구성에서도 모듈이나 CSOM(Client Side Object Model) 내부에서 유지되는 객체의 영향은 남아 있습니다. 장시간 실행하는 경우에는 실제 데이터로 메모리 사용량을 측정하고, 필요에 따라 가져오는 범위나 연결 컨텍스트를 분리하는 설계를 검토합니다.
#### 스로틀링(Throttling) 대응
SharePoint Online에서는 요청이 집중되면 HTTP 429 또는 503이 반환될 수 있습니다. 응답의 `Retry-After`에 따라 대기하고, 병렬 처리 수나 요청 빈도를 조정합니다. Microsoft 공식 자료에 대응 방법이 제시되어 있습니다.
사용자 정의 재시도(retry)를 추가할 때는 사용하는 명령어 측의 재시도와 함께 대기 시간 및 최대 시도 횟수를 확인해야 합니다. 인증 만료나 접근 권한 부족 등, 대기만으로는 해결되지 않는 오류는 분리하여 처리합니다.
#### 재개 위치와 실패 대상 기록
장시간 처리에서는 처리 대상과 결과를 파일 단위로도 기록합니다.
| 기록 항목 | 용도 |
|---|---|
| 대상・처리 완료된 항목 수 | 본문 가져오기 진행 상황 확인 |
| ... | |
최종 처리 ID를 재개에 사용하는 경우, ID 순서로 가져오는 것과 어느 시점을 완료로 기록할지 결정해야 합니다. 개별 파일의 실패는 별도로 기록하고 재가져오기 대상이 됩니다. 실행 중인 업데이트나 삭제 처리를 어떻게 다룰지도 미리 정해두어야 합니다.
저장 도중인 파일을 완료된 것으로 취급하지 않도록, 임시 이름으로 저장한 후 가져오기가 완료된 후에 공식 이름으로 변경하는 방법도 검토할 수 있습니다.
#### 모든 항목 재생성을 위해 임시 출력 위치 준비
output/
output.staging/
새로운 결과물을 `output.staging`에 생성하고, 가져오기・변환・검증이 끝난 후에 공개 위치에 반영합니다. 생성 도중 실패로 인해 이전에 열람 가능했던 결과물이 손상되는 것을 방지하기 위함입니다.
개별적인 가져오기 실패를 남겨둔 채 공개하는 경우에는 대상과 업무에 미치는 영향을 확인하고, 공개 가능하다고 판단한 이유를 기록해야 합니다.
### 5. HTML 본문 및 첨부 자료 오프라인화
본문의 HTML에는 SharePoint상의 이미지나 첨부 자료에 대한 참조가 포함되어 있습니다. 파일을 저장하고, 본문 내의 참조를 출력 위치의 상대 경로로 수정합니다.
#### 저장 위치와 링크 대응 결정
원래 HTML의 예시입니다.
상세 내역을 items/100.html에 저장하는 경우, 다음과 같이 변환합니다.
<a href="../attachments/100/manual.pdf">
<img src="https://zenn.dev/gsun/images/pdf-icon.png" alt="PDF">
자료 열기
...
가져온 원본 URL과 저장 위치의 대응을 기록해 두면, 링크 변환과 파일 존재 확인에 같은 정보를 사용할 수 있습니다.
참조처별로 가져오는 방법 정리
| 참조처 예시 | 대응 |
|---|---|
Lists/.../Attachments/... | 목록 항목 첨부로 가져오기 |
SiteAssets/... | 파일의 URL을 지정하여 가져오기 |
/_layouts/15/images/... | 로컬에서 사용할 이미지로 대체 |
| 기타 외부 참조 | 필요성을 판단하고, 미대응 시 기록 |
이번 작업에서는 목록 첨부 파일 외의 위치에 저장된 이미지와 SharePoint 표준 아이콘 참조를 발견했습니다. 단순히 첨부 파일을 가져오는 처리만으로는 저장 대상을 모두 포괄할 수 없었기 때문에, 참조하는 곳마다 처리를 추가했습니다.
일본어 파일명, URL 인코딩, HTML 엔티티도 변환 시 확인해야 할 대상입니다. href,
src,
CSS의 url() 등 실제로 포함된 참조 형식을 조사합니다. 정규 표현식으로 일괄 대체만 가능한 범위인지, 아니면 HTML 파싱이 필요한지를 실제 데이터를 통해 판단합니다.
스크립트나 이벤트 속성이 포함된 본문을 표시할 경우, 이를 제거하는 등 출력 HTML에서 실행되게 할 내용도 결정합니다.
브라우저에서 직접 열람하도록 구성하기
offline/
├── index.html
├── css/
...
목록, 상세 내역, 첨부 자료를 로컬 링크로 연결하고 index.html을 직접 여는 구성을 취합니다. 이 구성에서는 열람 시 웹 서버가 필요 없습니다.
JavaScript를 이용한 별 파일 읽기 등을 추가하면, file://으로 열었을 때의 브라우저 제약이 관련됩니다. 최종 확인은 실제로 사용할 브라우저에서 네트워크를 끊은 상태에서도 진행합니다.
Part 2. AI와 업무 도구를 만들 때 판단해야 할 지점
1. 목적・전제・제약・완성 조건을 AI에게 전달하기
이번 도구는 가져온 데이터를 오프라인에서 열람하기 위한 것입니다. AI에게 요청할 때도, 가져올 대상 외에 열람 시 조건과 가져오기 실패 시의 처리 방안을 함께 전달합니다.
| 항목 | 지정하는 내용의 예 |
|---|---|
| 목적 | SharePoint 문서 데이터를 오프라인에서 열람하기 |
| ... |
코드 생성 전에 구현 방침 확인하기
이번에도 코드 생성 전에 구현 방침을 정리했습니다. 요청문으로 만들자면 다음과 같은 형태입니다.
지정한 요구사항에 기반하여, 구현 방침을 정리해 주세요.
사용할 명령어, 가져올 대상, 출력 구성, 링크 변환, 오류 처리를 보여주세요.
구현 전에 확인이 필요한 사항도 언급해 주세요.
이 단계에서 사람이 확인하는 것은 업무와 환경의 조건입니다. 예를 들어, 열람 시 웹 서버를 사용할 수 있는지, 가져오지 못한 자료가 있을 경우 공개할 수 있는지는 업무 측 사정에 따라 결정합니다.
실제 데이터에서 새로운 조건이 발견되면, 요청에 사용했던 명세에도 추가합니다. 링크 변환 대응 범위 등을 남겨두면, 다음 수정 요청 시 전제가 갖춰집니다.
2. 실행 결과와 생성된 산출물을 확인하기
가져오기 처리, HTML 생성, 파일 저장이 각각 작동하더라도, 생성된 HTML에서 자료를 열 수 있는지는 별도로 확인합니다. 이번처럼 본문에서 여러 저장 위치를 참조하는 경우에는, 변환된 링크까지 검증 대상에 포함합니다.
| 검증 항목 | 확인 내용 |
|---|---|
| 실행 | 처리가 끝까지 진행되었는지, 중간 오류는 무엇인지 |
| ... |
검증용 처리도 AI에게 요청하기
AI가 실제 환경에 접근할 수 있는 경우 검증을 요청하고, 별도의 환경에서 작업하는 경우에는 검증용 처리를 만들게 하여 직접 실행합니다. 요청 예시는 다음과 같습니다.
대상 아이템 ID와 출력된 상세 HTML의 ID를 대조해 주세요.
HTML에 남아있는 SharePoint 참조와 로컬에 존재하지 않는 파일 링크를 목록화해 주세요.
가져오기 실패 건수와 미변환 링크 수를 기록해 주세요.
같은 건수라도, 누락과 중복이 상쇄되는 경우가 있습니다. 건수에 더해 ID나 가져온 원본 URL을 대조하고, 본문이나 화면의 표시는 실제 것을 열어 확인합니다.
건수의 예와 이번 수동 수정 분리해서 생각하기
집계 형식의 예입니다. 아래 수치는 설명용입니다.
대상 아이템 수 : 50,000
처리된 아이템 수 : 50,000
가져올 대상 파일 수 : 300,000
...
이 예시에서는 실패한 2개의 파일을 특정하고, 재가져오기 필요 여부나 공개에 미치는 영향을 판단합니다. 미변환 링크 수가 0이라도, 저장 파일의 누락은 확인 대상에 남습니다.
이번 작업에서도 최종 단계에서 경미한 결함이 남아, 생성된 산출물을 수동으로 수정하여 완료했습니다. (이는 실제 사례이며, 위에 제시된 설명용 건수와는 다릅니다)
참고로, 수동 수정된 부분은 재생성할 경우 손실될 가능성이 있습니다. 대상・수정 내용・이유를 기록하고, 다음에도 필요한 수정이라면 생성 처리에 포함시키거나, 재적용하는 절차를 남깁니다.
3. 오류는 코드와 재현 조건을 첨부하여 AI에게 전달하기
이번 개발에서는 다음 파라미터 세트의 오류에 직면했습니다.
설정된 파라미터는, 지정된 이름의 파라미터를 사용해서 해결할 수 없습니다.
원인은 Get-PnPListItem에서 -Query와 -Fields를 동시에 지정했기 때문이었습니다. 해당 줄과 공식 파라미터 세트를 대조하여, CAML 내의 <ViewFields>로 대체하는 수정에 초점을 맞출 수 있었습니다.
AI에게 전달할 정보
- 전체 오류 메시지
- 행 번호와 해당 코드
- PowerShell 및 PnP.PowerShell 버전
- 실행 시 파라미터
- 기대했던 동작
- 재현 조건과 직전에 변경한 내용
채팅에 전달할 정보의 범위를 확인하고, 인증 정보나 사내 기밀 정보는 포함하지 않도록 정리합니다.
원인의 후보가 여러 개일 경우, 다음과 같이 분리 요청할 수 있습니다.
아래 오류에 대해 관련 코드와 실행 환경을 바탕으로 원인을 조사해 주세요.
여러 원인이 생각될 경우에는, 확인할 순서와 분리 방법을 제시해 주세요.
수정 부분과 기존 기능에 미치는 영향 범위도 설명해 주세요.
이번 오류에서는 지정 가능한 파라미터의 조합을 확인함으로써 수정 방침이 결정되었습니다. 오류 문구 외에 실제 명령어 줄을 전달하면, 이러한 구문 문제를 조사하기가 쉬워집니다.
4. 추가 수정 범위와 완료로 판단할 조건 정하기
이번에도 다른 곳에 저장된 이미지나 표준 아이콘 대응을 구현 후에 추가했습니다. 가져오고 변환하는 대상이 늘어나기 때문에, 수정 부분 외에 지금까지 작동했던 출력도 확인합니다.
AI에게 전달할 범위
이번에는 지정한 문제와 관련된 처리를 수정해 주세요.
기존의 데이터 가져오기, HTML 생성, 파일 저장, 로그 출력 동작을 유지해 주세요.
변경 부분, 변경 이유, 영향 범위, 재확인이 필요한 항목을 제시해 주세요.
수정 후에는 설명과 실제 차이점을 대조합니다. 예를 들어 이미지 참조처를 추가했다면, 추가 대상 외에 기존 첨부 링크도 열리는지 확인합니다.
환경 의존 설정 모으기
# 환경 설정
$SiteUrl =
이번에도 예정된 작업에 방해를 받아 시작하게 된 요청이었습니다. 기존 코드와 SharePoint의 구조를 조사하고, AI에게 구현이나 수정을 요청하면서 진행했습니다.
사람이 판단한 부분은 실제 열을 어떻게 참조할지, 본문 내 파일이 어디에 있는지, 개별 실패 후에도 처리를 계속할지, 남은 결함을 수동으로 수정하여 완료할 수 있을지 등의 지점이었습니다.
비슷한 도구를 만들 때는 먼저 대상 목록의 내부 이름(internal name) 및 유형(type), 그리고 본문 내 참조 위치를 조사해 보세요. 이 정보를 AI에게 요청에 포함하면 구현 방침을 구체적으로 확인할 수 있습니다. 생성 후에는 가져온 결과와 로컬에서의 열람 결과를 대조하고, 남은 문제를 업무상 어떻게 처리할지까지 결정하면 작업 완료를 판단할 수 있게 됩니다.
AI와 함께 우아하게 일하세요!
### 논의
AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기