반응형
Jasper Report에서는 한글이랑 영어랑 다 잘 나오는데 한글은 아예 나오지 않는 경우가 있었다.
해결방법은 두가지가 있는거같다. 첫번쨰는 개발자들 모두가 Jasper Report내에서 폰트설정을 하는것,
두번쨰는 JAVA에서 설정을 하면은Jasper Report내에서 폰트설정을 하지 않아도 됌.
그래서 두번쨰 방법을 해보기로했다.
1. resources 폴더에 fonts 폴더를 만들고 fonts.xml파일을 만든다.
2. 이후 NotoSansKR-Regular.ttf,NotoSansKR-Bold.ttf 파일을 넣어준다. NotoSansKR-Regular.ttf,NotoSansKR-Bold.ttf 이 두파일은 구글 폰트이다 .
3. resources에 jasperreports_extenstion.properties 파일을 만들어준다 .
4. build.gradle 확인
5. config 생성

첫번쨰로는 1번에 대한 fonts.xml 설정이다.
<?xml version="1.0" encoding="UTF-8"?>
<fontFamilies>
<fontFamily name="NotoSansKR">
<normal>fonts/NotoSansKR-Regular.ttf</normal>
<bold>fonts/NotoSansKR-Bold.ttf</bold>
<pdfEncoding>Identity-H</pdfEncoding>
<pdfEmbedded>true</pdfEmbedded>
</fontFamily>
</fontFamilies>
두번쨰로는 구글 폰트 다운이다.
https://fonts.google.com/noto/specimen/Noto+Sans+KR?utm_source=chatgpt.com
Noto Sans Korean - Google Fonts
Noto is a global font collection for writing in all modern and ancient languages. Noto Sans KR is an unmodulated (“sans serif”) design for the Korean language u
fonts.google.com
.


여기까지 다운로드가 완료됐으면은 압축을 풀고 해당 두 파일을 fonts폴더에다가 넣어주자.


세번쨰로는 jasperreports_extension.properties 파일을 resource 밑에 만들어준 후 아래와 같이 설정을 해주자.,

net.sf.jasperreports.extension.registry.factory.simple.font.families=net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory
net.sf.jasperreports.extension.simple.font.families.default=fonts/fonts.xml
net.sf.jasperreports.default.font.name=NotoSansKR
네번쨰로는 build.gradle에 implementation 'net.sf.jasperreports:jasperreports-fonts:6.21.5' 가 있는지 확인을 꼭해줘야한다.

마지막 다섯번쨰로 config-> JasperProsinitializer 파일을 만들어주고 다음과같이 폰경로 및 기본 폰트를 잡아준다.,
이후 clean -> build를 꼭해줘야한다 gradle에서 !! 잊지말자

import jakarta.annotation.PostConstruct;
import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import org.springframework.stereotype.Component;
@Component
public class JasperPropsInitializer {
@PostConstruct
public void init() {
var ctx = DefaultJasperReportsContext.getInstance();
// 팩토리(클래스 지정)
JRPropertiesUtil.getInstance(ctx).setProperty(
"net.sf.jasperreports.extension.registry.factory.simple.font.families",
"net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory"
);
// fonts.xml 경로 지정
JRPropertiesUtil.getInstance(ctx).setProperty(
"net.sf.jasperreports.extension.simple.font.families.default",
"fonts/fonts.xml"
);
// 기본 폰트
JRPropertiesUtil.getInstance(ctx).setProperty(
"net.sf.jasperreports.default.font.name",
"NotoSansKR"
);
}
}
이후 현재 사용중이며 API를 콜하는 Controller -> Service 소스이다 우선은 contorller이다 .
해당 소스들은 API를 만든것이며 , Jasper Report를 웹에서 불러오기위한 소스이다.
또한 웹에서 Jasper Report를 호출을 할때 PDF, EXCEL , WORD를 다운받기 위한 소스들이다 .

package com.test.gw.controller.reports;
import com.test.gw.model.rnd.RndArticleReq;
import com.test.gw.service.reports.ReportsService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/in/reports")
public class ReportsController {
private final ReportsService reportsService;
@PostMapping(value = "/rnd/article/export")
public ResponseEntity<byte[]> exportRndArticleReport(@RequestBody RndArticleReq req) {
return reportsService.exportReport("rndArticleReportGenerator", req.getDocType(), req);
}
}
이어서 서비스이다. 총3개의 파일이있다.

ReportsService 파일이다.
package com.test.gw.service.reports;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class ReportsService {
private final Map<String, ReportGenerator> generatorMap;
/**
* ReportGenerator 구현체를 수집하여 Map에 저장
*/
public ReportsService(List<ReportGenerator> generators) {
this.generatorMap = new HashMap<>();
for (ReportGenerator generator : generators) {
String beanName = generator.getClass().getAnnotation(Service.class).value();
if (beanName == null || beanName.isBlank()) {
throw new IllegalArgumentException("ReportGenerator 구현체에는 @Service(\"beanName\")로 식별자 지정이 필요합니다.");
}
generatorMap.put(beanName, generator);
}
}
/**
* 공통 리포트 export 처리
* @param reportType: generator의 bean name
* @param docType: "pdf" 또는 "excel"
* @param dto: 리포트 생성에 필요한 요청 객체
*/
public ResponseEntity<byte[]> exportReport(String reportType, String docType, Object dto) {
ReportGenerator generator = generatorMap.get(reportType);
if (generator == null) throw new IllegalArgumentException("등록되지 않은 reportType: " + reportType);
byte[] fileBytes = generator.generate(docType, dto);
String extension;
MediaType mediaType;
if ("excel".equalsIgnoreCase(docType)) {
extension = "xls"; // 기존 유지
mediaType = MediaType.APPLICATION_OCTET_STREAM;
} else if ("word".equalsIgnoreCase(docType)) {
extension = "docx";
mediaType = MediaType.parseMediaType(
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
);
} else {
extension = "pdf";
mediaType = MediaType.APPLICATION_PDF;
}
String fileName = reportType + "_report." + extension;
return ResponseEntity.ok()
.contentType(mediaType)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.body(fileBytes);
}
/**
* Resource 형태로 반환할 경우 (프론트에서 Blob 처리 없이 iframe/open 등으로 사용 시)
*/
public ResponseEntity<Resource> exportReportAsResource(String reportType, String docType, Object dto) {
ReportGenerator generator = generatorMap.get(reportType);
if (generator == null) throw new IllegalArgumentException("등록되지 않은 reportType: " + reportType);
byte[] fileBytes = generator.generate(docType, dto);
String extension;
MediaType mediaType;
if ("excel".equalsIgnoreCase(docType)) {
extension = "xls";
mediaType = MediaType.APPLICATION_OCTET_STREAM;
} else if ("word".equalsIgnoreCase(docType)) {
extension = "docx";
mediaType = MediaType.parseMediaType(
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
);
} else {
extension = "pdf";
mediaType = MediaType.APPLICATION_PDF;
}
String fileName = reportType + "_report." + extension;
return ResponseEntity.ok()
.contentType(mediaType)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.body(new ByteArrayResource(fileBytes));
}
}
ReportGenerator 파일이다.
package com.test.gw.service.reports;
public interface ReportGenerator {
byte[] generate(String docType, Object dto);
}
RndArticleReportGenerator 파일이다.
package com.test.gw.service.reports;
import com.panko.gw.model.rnd.RndArticleReq;
import lombok.RequiredArgsConstructor;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.export.JRXlsExporter;
import net.sf.jasperreports.engine.export.ooxml.JRDocxExporter;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.export.SimpleDocxReportConfiguration;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
import net.sf.jasperreports.export.SimpleXlsReportConfiguration;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
@Service("rndArticleReportGenerator")
@RequiredArgsConstructor
public class RndArticleReportGenerator implements ReportGenerator {
private final DataSource dataSource;
/*@Override
public byte[] generate(String docType, Object dto) {
try (
InputStream in = getClass().getResourceAsStream("/com/test/gw/reports/rnd/fabricRecapHangTag.jrxml");
Connection conn = dataSource.getConnection()
) {
if (in == null)
throw new IllegalStateException("fabricRecapHangTag.jrxml 리소스를 못 찾음");
JasperReport report = JasperCompileManager.compileReport(in);
Map<String, Object> params = new HashMap<>();
RndArticleReq req = (RndArticleReq) dto;
params.put("cdCompany", req.getCdCompany());
params.put("seqArticle", req.getSeqArticle());
JasperPrint jp = JasperFillManager.fillReport(report, params, conn);
return JasperExportManager.exportReportToPdf(jp);
} catch (Exception e) {
throw new RuntimeException("RndArticle 리포트 생성 중 오류 발생", e);
}
}*/
@Override
public byte[] generate(String docType, Object dto) {
try (
InputStream in = getClass().getResourceAsStream("/com/test/gw/reports/rnd/fabricRecapHangTag.jasper");
Connection conn = dataSource.getConnection()
) {
if (in == null)
throw new IllegalStateException("fabricRecapHangTag.jasper 리소스를 못 찾음");
JasperReport report = (JasperReport) JRLoader.loadObject(in);
Map<String, Object> params = new HashMap<>();
RndArticleReq req = (RndArticleReq) dto;
params.put("cdCompany", req.getCdCompany());
params.put("seqArticle", req.getSeqArticle());
JasperPrint jp = JasperFillManager.fillReport(report, params, conn);
if ("excel".equalsIgnoreCase(docType)) {
return exportToExcel(jp);
} else if ("word".equalsIgnoreCase(docType)) {
return exportToDocx(jp);
} else {
return JasperExportManager.exportReportToPdf(jp);
}
} catch (Exception e) {
throw new RuntimeException("RndArticle 리포트 생성 중 오류 발생", e);
}
}
private byte[] exportToDocx(JasperPrint print) throws JRException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
JRDocxExporter exporter = new JRDocxExporter();
exporter.setExporterInput(new SimpleExporterInput(print));
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(out));
SimpleDocxReportConfiguration config = new SimpleDocxReportConfiguration();
// 필요 시 옵션들 (선택)
// config.setFlexibleRowHeight(true);
// config.setFramesAsNestedTables(true);
exporter.setConfiguration(config);
exporter.exportReport();
return out.toByteArray();
}
/**
* JasperPrint → Excel (.xls) 변환
*/
private byte[] exportToExcel(JasperPrint print) throws JRException {
ByteArrayOutputStream xlsOutput = new ByteArrayOutputStream();
JRXlsExporter exporter = new JRXlsExporter();
exporter.setExporterInput(new SimpleExporterInput(print));
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(xlsOutput));
SimpleXlsReportConfiguration config = new SimpleXlsReportConfiguration();
config.setOnePagePerSheet(false);
config.setDetectCellType(true);
config.setWhitePageBackground(false);
config.setRemoveEmptySpaceBetweenRows(true);
exporter.setConfiguration(config);
exporter.exportReport();
return xlsOutput.toByteArray();
}
}
출력결과물이다 .

반응형
'Report' 카테고리의 다른 글
| [Jaspersoft Studio] JAVA 연동 QR CODE 출력 안됌 해결 - (7) (0) | 2025.10.21 |
|---|---|
| [Jaspersoft Studio] Jasper Report - React 연동 (6) (1) | 2025.08.25 |
| [Jaspersoft Studio] Jasper Report InteliJ 연동 Gradle (5) (2) | 2025.08.11 |
| [Jaspersoft Studio] jxrml 파일 생성 및 DB SELECT(4) (3) | 2025.08.11 |
| [Jaspersoft Studio] Data Adapters DBMS 연결 ClassNotFoundException 해결 (3) (4) | 2025.08.06 |