이 가이드에서는 Google Docs API를 사용하여 하나 이상의 외부 데이터 소스의 정보를 기존 템플릿 문서에 병합하는 방법을 설명합니다.
템플릿 은 고정 텍스트와 동적 콘텐츠의 자리표시자가 포함된 문서 유형입니다. 예를 들어 계약 템플릿에는 수신자의 이름과 주소의 자리표시자가 포함된 고정 텍스트가 포함될 수 있습니다. 그러면 앱은 사용자별 데이터를 템플릿에 병합하여 완성된 문서를 만듭니다.
이 접근 방식이 유용한 이유는 다음과 같습니다.
디자이너는 Google Docs를 사용하여 문서 디자인을 미세 조정할 수 있습니다. 앱에서 렌더링된 레이아웃을 설정하기 위해 매개변수를 조정하는 것보다 간단합니다.
콘텐츠와 프레젠테이션을 분리하는 것은 많은 이점이 있는 잘 알려진 디자인 원칙입니다.
문서 병합 작동 방식
다음은 Docs API를 사용하여 문서를 병합하는 방법의 예입니다.
자리표시자 콘텐츠를 사용하여 디자인 및 형식에 도움이 되는 문서를 만듭니다. 바꾸려는 텍스트 서식은 유지됩니다.
삽입할 각 요소에 대해 자리표시자 콘텐츠를 태그로 바꿉니다. 일반적으로 발생하지 않을 문자열을 사용해야 합니다. 예를 들어
{{account-holder-name}}이 좋은 태그일 수 있습니다.코드에서 Google Drive API를 사용하여 문서 사본을 만듭니다.
코드에서 문서 이름과 함께 Docs API의
batchUpdate메서드를 사용하고ReplaceAllTextRequest를 포함합니다.
문서 ID는 문서를 참조하며 URL에서 파생될 수 있습니다.
https://docs.google.com/document/d/DOCUMENT_ID/edit
템플릿 관리
앱이 정의하고 소유하는 템플릿 문서의 경우 앱을 나타내는 전용 계정을 사용하여 템플릿을 만듭니다. 서비스 계정은 좋은 선택이며 공유를 제한하는 Google Workspace 정책과 관련된 복잡한 문제를 방지합니다.
템플릿에서 문서 인스턴스를 만들 때는 항상 최종 사용자 인증 정보를 사용하세요. 이렇게 하면 사용자가 결과 문서를 완전히 제어할 수 있으며 Google Drive의 사용자별 한도와 관련된 확장 문제를 방지할 수 있습니다.
서비스 계정을 사용하여 템플릿을 만들려면 앱 인증 정보로 다음 단계를 실행하세요.
- Docs API에서
documents.create를 사용하여 문서를 만듭니다. - Drive API에서
permissions.create를 사용하여 문서 수신자가 문서를 읽을 수 있도록 권한을 업데이트합니다. - Drive API에서
permissions.create를 사용하여 템플릿 작성자가 문서를 작성할 수 있도록 권한을 업데이트합니다. - 필요에 따라 템플릿을 수정합니다.
문서 인스턴스를 만들려면 사용자 인증 정보로 다음 단계를 실행하세요.
- Drive API에서
files.copy를 사용하여 템플릿 사본을 만듭니다. - Docs API에서
documents.batchUpdate를 사용하여 값을 바꿉니다.
예: 템플릿에 데이터 병합
다음 코드 샘플에서는 템플릿의 모든 탭에서 두 개의 필드를 실제 값으로 바꿔 완성된 문서를 생성하는 방법을 보여줍니다.
이 병합을 실행하려면 다음 코드를 사용하세요.
자바
String customerName = "Alice"; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); String date = formatter.format(LocalDate.now()); // Make a copy of the template document using the Drive API. String copyTitle = "Merged Document"; File copyMetadata = new File().setName(copyTitle); File documentCopyFile = driveService.files().copy(DOCUMENT_ID, copyMetadata).execute(); String documentCopyId = documentCopyFile.getId(); Listrequests = new ArrayList<>(); // One option for replacing all text is to specify all tab IDs. requests.add(new Request() .setReplaceAllText(new ReplaceAllTextRequest() .setContainsText(new SubstringMatchCriteria() .setText("{{customer-name}}") .setMatchCase(true)) .setReplaceText(customerName) .setTabsCriteria(new TabsCriteria() .addTabIds(TAB_ID_1) .addTabIds(TAB_ID_2) .addTabIds(TAB_ID_3)))); // Another option is to omit TabsCriteria if you are replacing across all tabs. requests.add(new Request() .setReplaceAllText(new ReplaceAllTextRequest() .setContainsText(new SubstringMatchCriteria() .setText("{{date}}") .setMatchCase(true)) .setReplaceText(date))); BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest(); service.documents().batchUpdate(documentCopyId, body.setRequests(requests)).execute();
Node.js
let customerName = 'Alice'; let date = yyyymmdd() let requests = [ // One option for replacing all text is to specify all tab IDs. { replaceAllText: { containsText: { text: '{{customer-name}}', matchCase: true, }, replaceText: customerName, tabsCriteria: { tabIds: [TAB_ID_1, TAB_ID_2, TAB_ID_3], }, }, }, // Another option is to omit TabsCriteria if you are replacing across all tabs. { replaceAllText: { containsText: { text: '{{date}}', matchCase: true, }, replaceText: date, }, }, ]; // Make a copy of the template document using the Drive API. let copyTitle = 'Merged Document'; driveService.files.copy({ fileId: '1yBx6HSnu_gbV2sk1nChJOFo_g3AizBhr-PpkyKAwcTg', resource: { name: copyTitle, }, }, (err, driveResponse) => { if (err) return console.log('The Drive API returned an error: ' + err); let documentCopyId = driveResponse.data.id; google.options({auth: auth}); google .discoverAPI( 'https://docs.googleapis.com/$discovery/rest?version=v1&key={YOUR_API_KEY}') .then(function(docs) { docs.documents.batchUpdate( { documentId: documentCopyId, resource: { requests, }, }, (err, {data}) => { if (err) return console.log('The API returned an error: ' + err); console.log(data); }); }); });
Python
customer_name = 'Alice' date = datetime.datetime.now().strftime("%y/%m/%d") # Make a copy of the template document using the Drive API. copy_title = 'Merged Document' body = { 'name': copy_title } drive_response = drive_service.files().copy( fileId=DOCUMENT_ID, body=body).execute() document_copy_id = drive_response.get('id') requests = [ # One option for replacing all text is to specify all tab IDs. { 'replaceAllText': { 'containsText': { 'text': '{{customer-name}}', 'matchCase': 'true' }, 'replaceText': customer_name, 'tabsCriteria': { 'tabIds': [TAB_ID_1, TAB_ID_2, TAB_ID_3], }, }}, # Another option is to omit TabsCriteria if you are replacing across all tabs. { 'replaceAllText': { 'containsText': { 'text': '{{date}}', 'matchCase': 'true' }, 'replaceText': str(date), } } ] result = service.documents().batchUpdate( documentId=document_copy_id, body={'requests': requests}).execute()
동적 목록 및 표 처리
표준 문서 병합은 ReplaceAllTextRequest를 사용하여 개별 일회성 플레이스홀더 (예: {{customer-name}} 또는 {{date}})를 바꿉니다. 하지만 데이터에 동적 항목 목록 (예: 인보이스의 행, 주문한 제품 목록 또는 동적 테이블)이 포함되어 있는 경우 템플릿 디자인 중에 항목 수를 알 수 없으므로 표준 텍스트 바꾸기를 사용할 수 없습니다.
동적 목록 콘텐츠를 처리하려면 다음 전략 중 하나를 사용하세요.
옵션 1: 템플릿 표에 행 추가
템플릿 문서에 이미 서식이 지정된 표 (예: 헤더 행과 단일 자리표시자 행)가 포함되어 있는 경우 목록의 각 항목에 대해 행을 동적으로 클론하고 채울 수 있습니다.
- 템플릿 구조 읽기:
documents.get메서드를 사용하여 표를 찾고 템플릿 행의 색인을 식별합니다. - 새 행 삽입: 데이터 목록의 각 항목 (기존 템플릿 행을 재사용할 수 있는 첫 번째
항목 제외)에 대해
InsertTableRowRequest을 호출하여 템플릿 행 아래에 새 행을 삽입합니다. - 셀 데이터 채우기: 자리표시자를 바꿔 템플릿 행의 셀을 채웁니다. 새로 만든 행의 경우
InsertTextRequest를 사용하여 각 셀의 좌표 위치에 해당 텍스트를 삽입합니다.
표 행을 삽입하는 방법의 예는 표 작업을 참고하세요.
옵션 2: 생성된 표로 태그 바꾸기
프로그래매틱 방식으로 표를 처음부터 빌드하려면 다음 단계를 따르세요.
- 자리표시자 태그 배치: 템플릿 문서에서 단일 태그 (예:
{{invoice-table}})를 사용하여 목록이 표시될 위치를 표시합니다. - 자리표시자 찾기: 검색 작업을 사용하여 태그의 시작 색인 을 찾습니다.
- 자리표시자 삭제:
DeleteContentRangeRequest를 사용하여{{invoice-table}}텍스트를 삭제합니다. - 표 삽입: 해당 시작 색인에서
InsertTableRequest를 보내 데이터 소스를 기반으로 행 및 열 수를 지정합니다. - 값 쓰기: 각 표 셀을 순차적으로 채웁니다.
프로그래매틱 방식으로 표를 삽입하는 방법의 예는 표 작업을 참고하세요.