탭 작업

Google Docs용 Apps Script를 사용하면 문서의 모든 탭에서 콘텐츠에 액세스할 수 있습니다.

탭이란 무엇인가요?

Google Docs에는 이라는 조직 레이어가 있습니다. Docs를 사용하면 현재 Sheets에 탭이 있는 것처럼 사용자가 단일 문서 내에 하나 이상의 탭을 만들 수 있습니다. 각 탭에는 자체 제목과 ID (URL에 추가됨)가 있습니다. 탭에는 다른 탭 아래에 중첩된 하위 탭이 있을 수도 있습니다.

탭 액세스

탭 속성과 콘텐츠는 Tab 목록을 반환하는 Document.getTabs()를 사용하여 액세스할 수 있습니다. 후반 섹션에서는 Tab 클래스에 관해 간략히 설명합니다. 탭 클래스 문서에서도 자세한 정보를 확인할 수 있습니다.

탭 속성

탭 속성은 Tab.getId()Tab.getTitle()와 같은 메서드를 사용하여 가져올 수 있습니다.

탭 콘텐츠

각 탭 내의 문서 콘텐츠는 Tab.asDocumentTab()를 사용하여 가져올 수 있습니다. 문서 클래스 구조 변경사항 섹션에서는 이를 사용하는 방법을 설명합니다.

탭 계층 구조

하위 탭은 Tab.getChildTabs()를 통해 Google Apps Script에 노출됩니다. 모든 탭에서 콘텐츠에 액세스하려면 하위 탭의 '트리'를 탐색해야 합니다. 예를 들어 다음과 같이 탭 계층 구조가 포함된 문서를 살펴보겠습니다.

일부 탭에 하위 탭이 있는 3개의 최상위 탭이 포함된 탭 목록 UI

탭 3.1.2에 액세스하려면 다음 단계를 따르세요.

// Print the ID of Tab 3.1.2.
const doc = DocumentApp.getActiveDocument();
const tab = doc.getTabs()[2].getChildTabs()[0].getChildTabs()[1];
console.log(tab.getId());

문서의 모든 탭을 반복하는 샘플 코드를 제공하는 후반 섹션의 샘플 코드 블록을 참고하세요.

탭을 검색하는 다른 방법

탭을 가져오는 다른 두 가지 방법은 다음과 같습니다.

문서 클래스 구조 변경사항

이전에는 문서에 탭 개념이 없었으므로 문서 클래스는 문서의 텍스트 콘텐츠에 직접 액세스하고 수정하는 메서드를 노출했습니다. 다음 메서드는 이 카테고리에 속합니다.

탭의 구조적 계층 구조가 추가됨에 따라 이러한 메서드는 더 이상 문서의 모든 탭의 텍스트 콘텐츠를 의미론적으로 나타내지 않습니다. 이제 텍스트 콘텐츠가 다른 레이어로 표시됩니다. 앞서 언급한 모든 텍스트 메서드는 DocumentTab를 통해 액세스할 수 있습니다.

Document 클래스의 이러한 기존 메서드는 활성 탭 (특정 문서에 결합된 스크립트의 경우) 또는 첫 번째 탭 (활성 탭을 사용할 수 없는 경우)의 콘텐츠에 액세스하거나 콘텐츠를 수정합니다.

특정 탭 내 텍스트 콘텐츠에 액세스

Document의 텍스트 메서드를 사용하는 대신 DocumentTab 클래스에서 사용할 수 있는 메서드 (Tab.asDocumentTab() 메서드를 통해 사용할 수 있음)를 사용하는 것이 좋습니다. 예를 들면 다음과 같습니다.

// Print the text from the body of the active tab.
const doc = DocumentApp.getActiveDocument();
const documentTab = doc.getActiveTab().asDocumentTab();
const body = documentTab.getBody();
console.log(body.getText());

사용자 선택 변경사항

텍스트 선택 방법

Document 클래스는 사용자가 활성 문서 내에서 선택하는 텍스트 위치를 관리하는 getter 및 setter를 제공합니다. 이러한 메서드는 스크립트를 실행하는 사용자의 활성 탭 컨텍스트 내에서 작동합니다.

  • Document.getCursor(): 활성 탭에서 사용자의 커서 위치를 반환합니다.
  • Document.getSelection(): 활성 탭에서 사용자의 선택 범위를 반환합니다.
  • Document.setCursor(position): 활성 문서에서 사용자의 커서 위치를 설정합니다. 위치가 비활성 탭에 있는 경우 사용자의 활성 탭도 해당 위치와 연결된 탭으로 전환됩니다.
  • Document.setSelection(range): 활성 문서에서 사용자의 선택 범위를 설정합니다. 범위가 비활성 탭에 있는 경우 사용자의 활성 탭도 해당 범위와 연결된 탭으로 전환됩니다.

탭 선택 방법 및 사용 사례

탭이 도입됨에 따라 스크립트를 실행하는 사용자의 활성 탭을 가져오고 설정하는 것이 유용할 수 있습니다. 다음 방법을 사용하여 이 작업을 수행할 수 있습니다.

사용자의 전체적인 '선택'은 활성 탭과 현재 커서 위치 또는 선택 범위의 조합으로 구성됩니다. 활성 선택을 사용하는 두 가지 패턴은 사용자의 활성 탭을 특정 탭으로 명시적으로 수정하거나 사용자의 활성 탭을 사용하는 것입니다.

Document.setActiveTab(tabId)를 사용하여 사용자의 활성 탭을 명시적으로 변경할 수 있습니다. 또는 비활성 탭의 Position 또는 Range를 사용하여 Document.setCursor(position) 또는 Document.setSelection(range)를 호출하면 해당 탭이 새로 활성화됩니다.

스크립트의 의도된 동작이 사용자의 활성 탭을 변경하지 않고 사용하는 것이라면 Document.setActiveTab(tabId)는 필요하지 않습니다. Document.getCursor()Document.getSelection() 메서드는 사용자가 스크립트를 실행하는 탭을 기반으로 이미 활성 탭에서 작동합니다.

문서는 여러 탭 선택이나 여러 탭의 여러 위치 또는 범위를 지원하지 않습니다. 따라서 Document.setActiveTab(tabId)를 사용하면 이전 커서 위치 또는 선택 범위가 삭제됩니다.

특정 탭의 위치 및 범위 메서드

특정 탭은 PositionRange의 텍스트 선택 개념에 의미를 부여합니다. 즉, 커서 위치 또는 선택 범위는 스크립트가 위치 또는 범위가 포함된 특정 탭을 알고 있는 경우에만 의미가 있습니다.

이는 메서드가 호출된 특정 DocumentTab를 타겟팅하는 위치 또는 범위를 구성하는 DocumentTab.newPosition(element, offset)DocumentTab.newRange() 메서드를 사용하여 실행됩니다. 반면 Document.newPosition(element, offset)Document.newRange()는 활성 탭 (또는 스크립트가 바인딩되지 않은 경우 첫 번째 탭)을 타겟팅하는 위치 또는 범위를 생성합니다.

선택 항목을 사용하는 샘플 코드를 제공하는 후반 섹션의 샘플 코드 블록을 참고하세요.

탭의 일반적인 사용 패턴

다음 코드 샘플은 탭과 상호작용하는 다양한 방법을 설명합니다.

문서의 모든 탭에서 탭 콘텐츠 읽기

탭 기능이 도입되기 전에 이를 실행했던 기존 코드는 탭 트리를 탐색하고 Document 대신 TabDocumentTab에서 getter 메서드를 호출하여 탭을 지원하도록 이전할 수 있습니다. 다음의 일부 코드 샘플은 문서의 모든 탭에서 모든 텍스트 콘텐츠를 출력하는 방법을 보여줍니다. 이 탭 탐색 코드는 탭의 실제 구조를 고려하지 않는 다른 많은 사용 사례에 맞게 조정할 수 있습니다.

/** Logs all text contents from all tabs in the active document. */
function logAllText() {
  // Generate a list of all the tabs in the document, including any
  // nested child tabs. DocumentApp.openById('abc123456') can also
  // be used instead of DocumentApp.getActiveDocument().
  const doc = DocumentApp.getActiveDocument();
  const allTabs = getAllTabs(doc);

  // Log the content from each tab in the document.
  for (const tab of allTabs) {
    // Get the DocumentTab from the generic Tab object.
    const documentTab = tab.asDocumentTab();
    // Get the body from the given DocumentTab.
    const body = documentTab.getBody();
    // Get the body text and log it to the console.
    console.log(body.getText());
  }
}

/**
 * Returns a flat list of all tabs in the document, in the order
 * they would appear in the UI (i.e. top-down ordering). Includes
 * all child tabs.
 */
function getAllTabs(doc) {
  const allTabs = [];
  // Iterate over all tabs and recursively add any child tabs to
  // generate a flat list of Tabs.
  for (const tab of doc.getTabs()) {
    addCurrentAndChildTabs(tab, allTabs);
  }
  return allTabs;
}

/**
 * Adds the provided tab to the list of all tabs, and recurses
 * through and adds all child tabs.
 */
function addCurrentAndChildTabs(tab, allTabs) {
  allTabs.push(tab);
  for (const childTab of tab.getChildTabs()) {
    addCurrentAndChildTabs(childTab, allTabs);
  }
}

문서의 첫 번째 탭에서 탭 콘텐츠 읽기

이는 모든 탭을 읽는 것과 유사합니다.

/** 
 * Logs all text contents from the first tab in the active 
 * document. 
 */
function logAllText() {
  // Generate a list of all the tabs in the document, including any
  // nested child tabs.
  const doc = DocumentApp.getActiveDocument();
  const allTabs = getAllTabs(doc);

  // Log the content from the first tab in the document.
  const firstTab = allTabs[0];
  // Get the DocumentTab from the generic Tab object.
  const documentTab = firstTab.asDocumentTab();
  // Get the body from the DocumentTab.
  const body = documentTab.getBody();
  // Get the body text and log it to the console.
  console.log(body.getText());
}

첫 번째 탭의 탭 콘텐츠 업데이트

다음의 일부 코드 샘플은 업데이트할 때 특정 탭을 타겟팅하는 방법을 보여줍니다.

/** Inserts text into the first tab of the active document. */
function insertTextInFirstTab() {
  // Get the first tab's body.
  const doc = DocumentApp.getActiveDocument();
  const firstTab = doc.getTabs()[0];
  const firstDocumentTab = firstTab.asDocumentTab();
  const firstTabBody = firstDocumentTab.getBody();

  // Append a paragraph and a page break to the first tab's body
  // section.
  firstTabBody.appendParagraph("A paragraph.");
  firstTabBody.appendPageBreak();
}

활성 탭 또는 선택한 탭의 탭 콘텐츠 업데이트

다음의 일부 코드 샘플은 업데이트할 때 활성 탭을 타겟팅하는 방법을 보여줍니다.

/**
 * Inserts text into the active/selected tab of the active
 * document.
 */
function insertTextInActiveTab() {
  // Get the active/selected tab's body.
  const doc = DocumentApp.getActiveDocument();
  const activeTab = doc.getActiveTab();
  const activeDocumentTab = activeTab.asDocumentTab();
  const activeTabBody = activeDocumentTab.getBody();

  // Append a paragraph and a page break to the active tab's body
  // section.
  activeTabBody.appendParagraph("A paragraph.");
  activeTabBody.appendPageBreak();
}

활성 탭에서 커서 위치 또는 선택 범위 설정

다음의 일부 코드 샘플은 사용자의 활성 탭 내에서 커서 위치 또는 선택 범위를 업데이트하는 방법을 보여줍니다. 이는 바인딩된 스크립트에만 관련이 있습니다.

/**
 * Changes the user's selection to select all tables within the tab
 * with the provided ID.
 */
function selectAllTables(tabId) {
  const doc = DocumentApp.getActiveDocument();
  const tab = doc.getTab(tabId);
  const documentTab = tab.asDocumentTab();

  // Build a range that encompasses all tables within the specified
  // tab.
  const rangeBuilder = documentTab.newRange();
  const tables = documentTab.getBody().getTables();
  for (let i = 0; i < tables.length; i++) {
    rangeBuilder.addElement(tables[i]);
  }
  // Set the document's selection to the tables within the specified
  // tab. Note that this actually switches the user's active tab as
  // well.
  doc.setSelection(rangeBuilder.build());
}

활성 또는 선택한 탭 설정

다음의 일부 코드 샘플은 사용자의 활성 탭을 변경하는 방법을 보여줍니다. 이는 바인딩된 스크립트에만 관련이 있습니다.

/**
 * Changes the user's selected tab to the tab immediately following
 * the currently selected one. Handles child tabs.
 *
 * 

Only changes the selection if there is a tab following the * currently selected one. */ function selectNextTab() { const doc = DocumentApp.getActiveDocument(); const allTabs = getAllTabs(doc); const activeTab = doc.getActiveTab(); // Find the index of the currently active tab. let activeTabIndex = -1; for (let i = 0; i < allTabs.length; i++) { if (allTabs[i].getId() === activeTab.getId()) { activeTabIndex = i; } } // Update the user's selected tab if there is a valid next tab. const nextTabIndex = activeTabIndex + 1; if (nextTabIndex < allTabs.length) { doc.setActiveTab(allTabs[nextTabIndex].getId()); } }