R&D
개발현황
자바스크립트로 QR 코드를 포함한 word 양식 생성하기 : 유예솔 연구원
- 관리자
- 2025.07.15
개요
EQ-0는 AI 기반 점검 기록 관리 서비스로 점검 기록 시 점검 일정 목록에서 일정을 선택하여 기록을 수행하던 방식에서 점검 대상 주변에 QR 코드를 부착하여 점검 앱으로 대상을 촬영 시 즉시 기록이 이루어지도록 시스템을 구축하였습니다.
이를 위해, 널리 사용되는 Formtec 라벨지 3102 양식을 활용하여 점검 대상 정보를 담은 QR코드를 생성하고 인쇄할 수있는 기능을 추가하였습니다. 관리자 웹 서비스에서 QR 코드를 발급하고, 해당 정보를
포함한 Formtec 3102 양식 기반의 .docx
워드 문서로 출력할 수 있습니다.
QR 코드 생성 및 워드 문서 출력 기능은 브라우저 환경의 자바스크립트로 구현되었습니다.
본 글에서는 자바스크립트를 활용하여 QR 코드를 포함한 Formtec 3102 양식의 워드 문서를 생성하고 다운로드하는 방법을 소개합니다.
요구사항
본 기능 구현을 위한 핵심 요구사항은 다음과 같습니다.
- QR 코드 생성: 각 점검 항목의 고유 정보를 기반으로 QR 코드를 생성하고 word 문서에 삽입될 수 있도록 이미지 URL로 변환해야합니다.
- 문서 생성: Formtec 3102 규격에 부합하는
.docx
파일 생성해야합니다. - 브라우저 다운로드 지원: 생성된
.docx
파일을 브라우저 환경에서 다운로드 하도록 구현해야 합니다.
워드 문서 생성, 그리고 특정 규격의 문서 형식을 커스터마이징하는 과정에서 관련 정보가 부족했지만, docx 라이브러리를 활용하고 이 과정에서 AI(ChatGPT)의 도움을 받아 필요한 정보를 점진적으로 구체화하여 효율적으로 구현할 수 있었습니다.
초기에는 'Formtec 2102 양식을 docx 파일로 생성해달라"는 프롬프트 또는 이미지를 전달하는 방식의 포괄적인 요청으로 시도해보았으나 행, 열 수와 사이즈가 전혀 다른 결과가 나왔습니다. 이에 포커스를 좁혀 다음과 같이 점진적으로 요청을 구체화하고 중간 결과물을 확인함으로써 최종적으로 원하는 결과물을 도출할 수 있었습니다.
요청 및 검증 절차
- 자바스크립트로 docx 파일 생성하는 코드 작성하기
- 가로는 4칸, 세로 10칸 표 생성하기
- 행 크기는 2.68cm, 열 크기는 4.95cm로 설정하기
- 여백은 위쪽 1.42cm, 아래쪽 0cm, 왼쪽 0.72cm, 오른쪽 0.72cm으로 설정하기
- 표 테두리 제거, 눈금선 보기 적용하기
- 들여쓰기 왼쪽, 오른쪽 각각 0.13cm 씩 적용하기
- 단위 환산은 DXA_PER_CM를 상수로 정의하여 사용하기
- 각 기능별로 함수화, 모듈화 적용하기
아래는 이러한 과정을 통해 최종적으로 생성된 코드와 문서 예시입니다.
사용 라이브러리
라이브러리 | 역할 | 주요 특징 |
---|---|---|
docx |
Word .docx 문서 생성 |
문단, 테이블, 이미지 삽입 등을 코드로 제어 가능. 브라우저 및 Node.js에서 모두 사용 가능 |
qrcode |
텍스트 → QR 코드 변환 | data:image/png;base64,... 형태의 이미지 데이터 URL 생성 |
file-saver |
브라우저에서 파일 저장 | Blob 객체를 사용해 .docx 파일을 다운로드 |
기능 구현
QR 코드 생성
점검 항목의 고유 데이터를 기반으로 QR 코드를 생성하고, 각 QR 코드를 toDataURL()
매서드를 통해 Base64 이미지 데이터 URL로 변환합니다. 이렇게 생성된 QR 코드는 워드 문서의 셀에
삽입됩니다
import QRCode from "qrcode";
/**
* 주어진 항목들에 대한 QR 코드를 생성합니다.
* @param {Array} items - QR 코드로 변환할 항목들의 배열
* @returns {Promise} - QR 코드 이미지 데이터 URL의 배열
*/
export const generateQRCodes = async (items) => {
const qrCodes = await Promise.all(
items.map((item) => {
const qrContent = item;
return QRCode.toDataURL(qrContent);
})
);
return qrCodes;
};
워드 문서 생성
모듈 가져오기
먼저, 워드 문서를 생성하기 위해 필요한 docx 라이브러리의 모듈을 가져옵니다.
import {
Document,
Paragraph,
Table,
TableRow,
TableCell,
WidthType,
TextRun,
HeightRule,
ImageRun,
AlignmentType,
VerticalAlign,
} from "docx";
표 생성
테이블 셀을 생성하는 함수입니다. 이 함수는 셀의 내용, 너비, 들여쓰기, 테두리 스타일을 받아서 새로운 셀 TableCell
객체를 생성합니다.
export const createTableCell = ({
content,
width,
indent,
borders = {top: "none", bottom: "none", left: "none", right: "none"},
verticalAlign,
}) =>
new TableCell({
width: {
size: width,
type: WidthType.DXA,
},
borders,
verticalAlign: verticalAlign || VerticalAlign.CENTER,
children: [
new Paragraph({
indent: {
left: indent,
right: indent,
},
children: [
typeof content === "string" ? new TextRun(content) : content,
],
}),
],
});
여기서 WidthType.DXA
는 셀 너비 단위를 지정합니다. 워드에서 1 포인트 (pt)
는 약 1/72 인치
에 해당하며 1DXA
는 1/20 포인트 단위입니다. 따라서 1 DXA
는
1/1440 인치
에 해당합니다.
단위 | 의미 | 설명 |
---|---|---|
DXA |
1/20 pt | 워드 내부의 가장 세밀한 단위 중 하나 |
PT |
1 pt = 1/72 inch | 일반적인 글꼴 크기나 여백 단위 |
INCH |
인치 | 1 inch = 72 pt = 1440 DXA |
다음으로는 테이블 행을 생성합니다.
export const createTableRow = ({
cellContents,
cellWidth,
indent,
rowHeight,
borders,
}) => {
const cells = cellContents.map((content) =>
createTableCell({
content,
width: cellWidth,
indent,
borders,
})
);
return new TableRow({
height: {
value: rowHeight,
rule: HeightRule.EXACT,
},
children: cells,
});
};
생성된 셀들을 TableRow로 묶어 반환합니다.
다음으로는 QR 코드가 포함된 테이블을 생성합니다.
전체 동작 흐름
- QR 정보 배열이
createTable
함수로 전달됩니다. - 각 셀 위치별로 QR 정보 존재 여부를 확인합니다.
- QR 있는 경우, QR 이미지를 포함한 셀을 생성합니다.
- QR 없는 경우, 일반 텍스트 셀을 생성합니다.
- 모든 셀은 행으로 그룹핑되어 하나의 테이블을 완성합니다.
- 완성된 테이블은
Document
로 래핑하여 최종.docx
파일을 생성됩니다.
export const createTable = ({
rowCount = 10,
colCount = 4,
getCellContent = () => ``,
constants,
qrInfoList = [],
borders = {
top: "none",
bottom: "none",
left: "none",
right: "none",
insideHorizontal: "none",
insideVertical: "none",
},
}) => {
const rows = [];
for (let row = 0; row < rowCount; row++) {
const cells = [];
for (let col = 0; col < colCount; col++) {
const idx = row * colCount + col;
const qrInfo = qrInfoList[idx];
let children = [];
if (qrInfo?.qrCode) {
const base64Data = qrInfo.qrCode.replace(
/^data:image\/\w+;base64,/,
""
);
children.push(
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new ImageRun({
data: Buffer.from(base64Data, "base64"),
transformation: {width: 95, height: 95},
}),
],
})
);
} else {
children.push(
new Paragraph({
children: [
new TextRun({
text: getCellContent(row, col, qrInfo),
}),
],
})
);
}
cells.push(
new TableCell({
children,
width: {
size: constants?.COL_WIDTH || 2000,
type: WidthType.DXA,
},
verticalAlign: VerticalAlign.CENTER,
})
);
}
rows.push(
new TableRow({
children: cells,
height: {
value: constants?.ROW_HEIGHT,
},
})
);
}
return new Table({
rows,
borders,
width: {
size: 100,
type: WidthType.PERCENTAGE,
},
});
};
테이블을 포함한 워드 문서를 생성하는 함수입니다.
export const createDocument = (table, constants) =>
new Document({
sections: [
{
properties: constants
? {
page: {
margin: {
top: constants.PAGE_MARGIN_TOP,
bottom: constants.PAGE_MARGIN_BOTTOM,
left: constants.PAGE_MARGIN_LEFT,
right: constants.PAGE_MARGIN_RIGHT,
gutter: constants.GUTTER,
gutterAtTop: false,
},
},
}
: {},
children: [table],
},
],
});
Formtec 3102 양식에 대한 상수 정의
Formtec 3102 양식의 규격에 맞춰 행 높이, 열 너비, 여백 등의 상수를 정의합니다. DXA
단위를 활용하여 cm
단위를 워드 내부 단위인 DXA로 정확하게 변환합니다.
export const FORMTEC3102 = {
rowHeightCm: 2.68,
colWidthCm: 4.95,
indentCm: 0.3,
pageMarginTopCm: 1.42,
pageMarginLeftCm: 0.72,
pageMarginRightCm: 0.72,
pageMarginBottomCm: 0,
gutterCm: 0,
};
export const getConstants = ({
rowHeightCm = FORMTEC3102.rowHeightCm,
colWidthCm = FORMTEC3102.colWidthCm,
indentCm = FORMTEC3102.indentCm,
pageMarginTopCm = FORMTEC3102.pageMarginTopCm,
pageMarginLeftCm = FORMTEC3102.pageMarginLeftCm,
pageMarginRightCm = FORMTEC3102.pageMarginRightCm,
pageMarginBottomCm = FORMTEC3102.pageMarginBottomCm,
gutterCm = FORMTEC3102.gutterCm,
} = {}) => {
const DXA_PER_CM = 567;
return {
ROW_HEIGHT: Math.round(rowHeightCm * DXA_PER_CM),
COL_WIDTH: Math.round(colWidthCm * DXA_PER_CM),
INDENT_DXA: Math.round(indentCm * DXA_PER_CM),
PAGE_MARGIN_TOP: Math.round(pageMarginTopCm * DXA_PER_CM),
PAGE_MARGIN_LEFT: Math.round(pageMarginLeftCm * DXA_PER_CM),
PAGE_MARGIN_RIGHT: Math.round(pageMarginRightCm * DXA_PER_CM),
PAGE_MARGIN_BOTTOM: Math.round(pageMarginBottomCm * DXA_PER_CM),
GUTTER: Math.round(gutterCm * DXA_PER_CM),
};
};
마지막으로 워드 문서를 생성하고 다운로드하는 최종 함수입니다.
import {saveAs} from "file-saver";
import {Packer} from "docx";
import {createTable, createDocument} from "./docxUtils";
import {getConstants} from "./formtec";
import {inspectQRFileName} from "@/constants/common";
export const generateDocxTable = ({
rowCount = 10,
colCount = 4,
getCellContent,
constantsOptions,
fileName = inspectQRFileName,
qrInfoList = [],
} = {}) => {
const constants = constantsOptions
? getConstants(constantsOptions)
: undefined;
const table = createTable({
rowCount,
colCount,
getCellContent: getCellContent || (() => ``),
constants,
qrInfoList,
});
const doc = createDocument(table, constants);
Packer.toBlob(doc).then((blob) => {
saveAs(blob, fileName);
});
};
최종 문서 예시
결론
이 글에서는 자바스크립트를 사용하여 QR 코드를 포함한 워드 문서를 자동 생성하는 방법을 소개했습니다. docx
라이브러리를 활용하여 Formtec 3102 양식에 부합하는 문서를 생성하고, qrcode
라이브러리로 QR 코드를 생성하여 문서에 삽입하는 과정을 살펴보았습니다. 또한, file-saver
라이브러리를 사용하여 브라우저 환경에서도 손쉽게 .docx
파일을 생성하고 다운로드할 수 있습니다.
특히, ChatGPT를 활용하여 복잡한 워드 문서 규격에 맞는 표 생성을 점진적으로 구체화하고 구현한 과정은 AI를 활용하여 개발 효율성을 높이는 좋은 경험이었습니다.
본 글이 자바스크립트를 이용한 QR 및 워드 문서 생성에 대한 이해를 돕고, 실제 프로젝트에서 활용할 수 있는 유용한 참고자료가 되기를 바랍니다.
감사합니다