JavaScript로 더 명확한 Excel 차트: 다중 레벨 레이블과 이중 축

2026-09-23 08:01:40 Allen Yang
AI Summarize:
ChatGPT
ChatGPT ✓
Claude ✓
Grok ✓
Perplexity ✓
Quick
Quick
Concise overview
Highlights
Key takeaways
Detailed
Structured explanation
Brief
One sentence summary
Summarize |

Creating multi-level category labels and adding a secondary axis to an Excel chart in the browser with Spire.XLS for JavaScript

지역과 월을 같은 축에 놓은 세로 막대형 차트에는 두 가지 문제가 있으며, 이 둘은 같은 문제가 아닙니다. 첫 번째는 범주 레이블이 단일 행 — "북부", "1월", "북부", "2월" — 으로 합쳐져서, 읽는 사람이 어느 월이 어느 지역에 속하는지 머릿속으로 다시 묶어야 한다는 점입니다. 두 번째는 매출 계열이 수백만 단위에 달하는데 성장률 계열을 함께 추가하면, 하나의 값 축이 두 가지 규모를 동시에 처리할 수 없기 때문에 성장률이 기준선에 붙은 평평한 선이 된다는 점입니다.

다중 수준 범주 레이블은 첫 번째 문제를 해결합니다. 보조 축은 두 번째 문제를 해결합니다. 이 둘은 같은 차트에서 유용하게 쓰일 뿐 서로 독립적인 기능이며, Spire.XLS for JavaScript는 차트 축 API를 통해 두 가지를 모두 처리합니다. 즉, 백엔드 없이 가상 파일 시스템(VFS)을 통해 파일을 이동하면서 브라우저의 WebAssembly에서 직접 실행됩니다.

프로젝트 설정은 React 프로젝트에 Spire.XLS for JavaScript 통합하기를 참조하세요. 아래 예제는 패키지가 설치되어 있고 WebAssembly 모듈이 초기화되었다고 가정합니다.


하나의 축으로 충분하지 않을 때

두 문제는 같은 종류의 워크시트 — 범주에 계층이 있고 값의 범위가 넓은 워크시트 — 에서 나타나지만, 원인은 서로 다릅니다:

문제 원인 차트 모양 해결 방법
레이블이 한 행에 몰림 범주가 계층적임(지역 → 월, 연도 → 분기)이지만 축은 이를 평면적으로 처리함 외부 및 내부 범주가 시각적 그룹화 없이 번갈아 나타나는 단일 레이블 행 다중 수준 범주 레이블
한 계열이 선으로 평평해짐 두 계열이 규모 차이가 큼(매출은 백만 단위, 성장률은 퍼센트)이지만 하나의 값 축을 공유함 더 작은 계열이 거의 0에 가깝게 압축되어 변동이 보이지 않음 보조 축

둘 다 스타일 문제가 아닙니다. 둘 모두 축이 알아야 할 무언가를 모르기 때문에 발생합니다. 즉, 범주에 계층이 있다는 것, 또는 값의 눈금이 서로 호환되지 않는다는 것입니다. 아래 두 섹션에서 이를 차례로 다루며, 두 번째 섹션은 첫 번째를 기반으로 하여 최종 차트가 두 가지 해결책을 모두 갖추도록 합니다.


사전 요구 사항

Spire.XLS for JavaScript가 설치되고 WebAssembly 모듈이 초기화된 React 프로젝트가 필요하며, 모듈은 window.wasmModule.spirexls에서 접근할 수 있어야 합니다. 샘플은 차트를 만들기 전에 글꼴과 미리 준비된 데이터 파일을 VFS에 로드하며, 둘 다 프로젝트의 public 폴더에서 가져옵니다.


다중 수준 레이블 뒤의 데이터

다중 수준 레이블은 속성 하나만으로 만들어지지 않습니다. 데이터에서 읽어옵니다. 범주 축은 CategoryLabels가 가리키는 범위에 있는 열 수만큼 레이블 수준을 그립니다. 따라서 워크시트는 계층이 열을 가로질러 배치되도록 구성해야 합니다:

열 A(외부) 열 B(내부) 열 C(값)
북부 1월 120,000
북부 2월 135,000
남부 1월 98,000
남부 2월 110,000

열 A의 외부 레이블은 해당 레이블이 포괄하는 행에 걸쳐 병합됩니다. "북부"는 1월과 2월의 두 행에 걸쳐 있습니다. 이 병합 덕분에 차트가 렌더링될 때 해당 수준이 그룹당 단일 레이블로 시각적으로 합쳐집니다. 병합하지 않으면 축은 여전히 두 수준을 표시하지만, 외부 수준이 그룹화되지 않고 각 행마다 레이블을 반복합니다.

이것은 차트 API의 문제가 아니라 데이터 배치의 문제입니다. 차트 코드는 CategoryLabels가 두 열을 모두 가리키도록 하기만 하면 됩니다. 외부 셀이 병합되는지 여부는 차트 개체가 아니라 통합 문서에서 결정됩니다.


다중 수준 범주 레이블이 있는 차트 만들기

데이터가 배치되면 차트 코드는 두 가지를 수행합니다. CategoryLabels가 외부 열과 내부 열을 모두 포괄하는 범위를 가리키게 하고, MultiLevelLable을 켜서 축이 해당 열을 누적된 행으로 확장하도록 합니다. 단계는 다음과 같습니다:

  1. 글꼴과 테스트 데이터 파일을 VFS에 로드합니다.
  2. 통합 문서를 로드하고 워크시트를 가져옵니다.
  3. 세로 막대형 차트를 추가하고 이름이 지정된 매출 계열을 추가합니다.
  4. 범주 레이블이 지역과 월 열을 모두 가리키도록 합니다.
  5. 범주 축에 대해 다중 수준 레이블을 켜고 통합 문서를 저장합니다.
function App() {
  const createMultiLevelChart = async () => {
    // Get the Spire.XLS WASM module
    const xlsModule = window.wasmModule?.spirexls;

    // Check if the module is ready
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // Load the font and the test data file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'MultiLevelChartData.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // Load the workbook and get the first worksheet
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });
    const sheet = workbook.Worksheets.get(0);

    // Add a column chart
    const chart = sheet.Charts.Add({ chartType: xlsModule.ExcelChartType.ColumnClustered });
    chart.ChartTitle = "Sales";
    chart.Legend.Delete();

    // Add the sales series and give it a name
    const serie = chart.Series.Add({ name: "Sales", serieType: xlsModule.ExcelChartType.ColumnClustered });
    serie.Values = sheet.Range.get("C2:C7");

    // Point the category labels at both the region and the month column
    serie.CategoryLabels = sheet.Range.get("A2:B7");

    // Turn on multi-level category labels so each level gets its own row
    chart.PrimaryCategoryAxis.MultiLevelLable = true;

    // Place the chart on the worksheet
    chart.LeftColumn = 5;
    chart.TopRow = 1;
    chart.RightColumn = 14;

    // Save the workbook
    const outputFileName = "MultiLevelLabels.xlsx";
    workbook.SaveToFile({ fileName: outputFileName });

    // Dispose of the workbook object to free resources
    workbook.Dispose();

    // Read the result file from the VFS and trigger the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Multi-Level Labels</h1>
      <button onClick={createMultiLevelChart}>Start</button>
    </div>
  );
}

export default App;

각 수준이 자체 행에 표시되는 다중 수준 범주 레이블이 있는 차트

Create a chart with multi-level category labels

축이 두 수준을 표시하도록 하는 것은 A2:B7 범위입니다. B2:B7과 같은 단일 열 범위를 바인딩하면 MultiLevelLable이 true로 설정되어 있어도 여전히 하나의 수준만 생성됩니다. 이 속성은 여러 수준을 행으로 확장할지 여부를 제어할 뿐, 확장할 데이터 수준이 존재하는지 여부를 제어하지 않습니다.


성장 계열이 사라지는 이유

전년 대비 성장률에 대한 두 번째 계열 — 10%대 초반의 백분율 값 — 을 추가하고 매출과 같은 값 축에 표시해 보겠습니다. 매출 막대는 120,000에 도달하지만 성장률은 12에 도달합니다. 0에서 140,000까지 눈금이 매겨진 축에서는 숫자 12가 0과 구별되지 않습니다. 계열은 존재하고 데이터도 정확하지만, 차트에는 기준선에 붙은 평평한 선이 표시됩니다.

이것은 데이터나 차트의 버그가 아닙니다. 가장 큰 계열을 포괄하는 범위를 매핑하는 값 축이 제 역할을 하는 대신, 가장 작은 계열을 희생한 결과입니다. 두 계열을 모두 선명하게 보려면 각각 고유한 눈금을 부여해야 하며, 보조 축이 바로 그 역할을 합니다.


계열을 보조 축으로 이동

성장 계열은 막대가 아니라 선으로 추가합니다. 선은 막대 너비를 차지하지 않으므로, 같은 범주를 공유하는 막대 계열과 대비되어 선명하게 보입니다. 이를 기본 축에서 분리하는 방법은 속성 하나입니다: UsePrimaryAxis = false. 단계는 다음과 같습니다:

  1. 글꼴과 테스트 데이터 파일을 VFS에 로드합니다.
  2. 통합 문서를 로드하고 워크시트를 가져옵니다.
  3. 세로 막대형 차트를 추가하고 이름이 지정된 매출 계열을 추가합니다.
  4. 성장 계열을 선으로 추가합니다.
  5. 성장 계열을 보조 축으로 이동하고 통합 문서를 저장합니다.
function App() {
  const addSecondaryAxis = async () => {
    // Get the Spire.XLS WASM module
    const xlsModule = window.wasmModule?.spirexls;

    // Check if the module is ready
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // Load the font and the test data file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'MultiLevelChartData.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // Load the workbook and get the first worksheet
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });
    const sheet = workbook.Worksheets.get(0);

    // Add a column chart
    const chart = sheet.Charts.Add({ chartType: xlsModule.ExcelChartType.ColumnClustered });
    chart.ChartTitle = "Sales and YoY Growth";

    // Add the sales series, which stays on the primary axis
    const salesSerie = chart.Series.Add({ name: "Sales", serieType: xlsModule.ExcelChartType.ColumnClustered });
    salesSerie.Values = sheet.Range.get("C2:C7");

    // Point the category labels at both the region and the month column
    salesSerie.CategoryLabels = sheet.Range.get("A2:B7");

    // Add the growth series as a line
    const growthSerie = chart.Series.Add({ name: "YoY Growth", serieType: xlsModule.ExcelChartType.Line });
    growthSerie.Values = sheet.Range.get("D2:D7");

    // Move the growth series to the secondary axis so it plots on its own percentage scale
    growthSerie.UsePrimaryAxis = false;

    // Turn on multi-level category labels
    chart.PrimaryCategoryAxis.MultiLevelLable = true;

    // Place the chart on the worksheet
    chart.LeftColumn = 5;
    chart.TopRow = 1;
    chart.RightColumn = 14;

    // Save the workbook
    const outputFileName = "SecondaryAxis.xlsx";
    workbook.SaveToFile({ fileName: outputFileName });

    // Dispose of the workbook object to free resources
    workbook.Dispose();

    // Read the result file from the VFS and trigger the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Secondary Axis</h1>
      <button onClick={addSecondaryAxis}>Start</button>
    </div>
  );
}

export default App;

성장률 선 계열을 위한 보조 축이 있는 세로 막대형 차트

Add a secondary axis to the chart

UsePrimaryAxis = false는 설정된 계열에만 영향을 미치며, 다른 모든 계열은 기본 축에 남습니다. 차트에는 두 번째 값 축과 범주 축 쌍이 생겨 두 개의 별도 눈금 범위를 갖게 됩니다. Series.Add는 계열 이름을 동시에 받으므로, 범례에는 자동 생성된 "Series 1" 대신 전달한 이름이 표시됩니다.


보조 축 눈금 설정

계열이 보조 축으로 이동하면 해당 축은 자체적으로 눈금을 계산하며, 기본 축과 독립적으로 계산합니다. 두 범위는 서로를 알지 못하므로 보조 축이 데이터와 잘 맞지 않는 범위를 선택할 수 있습니다.

PrimaryValueAxis.MinValue, MaxValue, MajorUnit은 기본 축만 제어합니다. 보조 축 눈금을 설정하려면 SecondaryValueAxis를 사용하세요:

// Give the secondary axis a 0-20 scale with a major unit of 5
chart.SecondaryValueAxis.MinValue = 0;
chart.SecondaryValueAxis.MaxValue = 20;
chart.SecondaryValueAxis.MajorUnit = 5;

눈금은 계열을 보조 축으로 이동한 후에 설정하세요. 보조 축을 사용하는 계열이 없으면 할당은 받아들여지지만 파일에 기록되지 않습니다. 즉, 계열이 그 위에 그려지기 전까지 출력에 해당 축은 존재하지 않습니다.


일반적인 문제

범주 축에 한 수준의 레이블만 표시됩니다. CategoryLabels가 단일 열 범위를 가리키고 있습니다. 수준 수는 범위가 포괄하는 열 수에 따라 결정되며, MultiLevelLable 속성에 따라 결정되지 않습니다. A2:B7과 같은 여러 열 범위를 가리키고, 데이터에서 외부 레이블 셀이 병합되었는지 확인하세요.

보조 축 눈금이 잘못된 것처럼 보입니다. 기본 및 보조 값 축은 각각 독립적으로 눈금을 계산합니다. PrimaryValueAxis에 MinValue 또는 MaxValue를 설정해도 보조 축에는 영향을 주지 않습니다. chart.SecondaryValueAxis를 사용하여 해당 눈금을 직접 설정하고, 계열을 그 축으로 이동한 후에 설정하세요.

보조 축을 추가한 후에도 성장 계열이 여전히 평평하게 나타납니다. UsePrimaryAxis = false가 매출 계열이 아니라 성장 계열에 설정되어 있는지 확인하세요. 이 속성은 계열별로 적용되므로, 잘못된 계열에 설정하면 잘못된 계열이 보조 축으로 이동합니다.

범례에 계열 이름 대신 "Series 1"이 표시됩니다. 이름이 Series.Add에 전달되지 않았습니다. chart.Series.Add({ name: "Sales", ... })를 사용하여 범례가 자동 생성 레이블이 아니라 의도한 이름을 가져오도록 하세요.


자주 묻는 질문

범주 레이블을 두 수준 이상으로 가질 수 있나요?

예. 수준 수는 CategoryLabels 범위가 포괄하는 열 수에 따라 결정됩니다. 3열 범위는 세 수준을 생성합니다. 예를 들어 연도, 분기, 월입니다. 각 수준이 올바르게 그룹화되려면 데이터에서 외부 레이블 셀이 병합되어야 합니다.

보조 축이 세로 막대형 및 선형 외의 차트 종류에서도 작동하나요?

예. 보조 축은 특정 차트 종류에 묶여 있지 않습니다. 일반적인 패턴은 세로 막대형과 선의 조합입니다. 선은 막대 너비를 차지하지 않아 막대와 대비되어 선명하게 보입니다. 하지만 UsePrimaryAxis = false를 설정하면 모든 계열을 보조 축으로 이동할 수 있습니다.

이 차트를 만들려면 Excel이 설치되어 있어야 하나요?

아니요. 스프레드시트 엔진은 패키지에 포함되어 제공되며 브라우저에서 WebAssembly로 실행됩니다. 통합 문서는 전적으로 클라이언트 측에서 작성, 차트 생성, 저장됩니다.

보조 범주 축을 별도로 제어할 수 있나요?

계열이 보조 축으로 이동하면 차트에는 보조 값 축 외에도 보조 범주 축이 생깁니다. 두 범주 축은 기본적으로 동일한 범주 레이블을 공유하므로 다중 수준 레이블이 둘 다에 적용됩니다.

출력 파일이 Excel과 호환되나요?

예. 통합 문서는 .xlsx로 저장되며, 다중 수준 레이블과 보조 축을 포함한 차트는 Excel이 기본적으로 읽는 표준 차트 XML로 작성됩니다.


참고 항목