WorkBook์—์„œ SAX์œผ๋กœ, 80% ์ด์ƒ ์„ฑ๋Šฅ ๊ฐœ์„ ๊ธฐ

WorkBook์—์„œ SAX์œผ๋กœ, 80% ์ด์ƒ ์„ฑ๋Šฅ ๊ฐœ์„ ๊ธฐ

2025-09-12ยท
TechSpringJavaExcelApache POISAXPerformanceMemory Leak

๋“ค์–ด๊ฐ€๋ฉฐ

์•ˆ๋…•ํ•˜์„ธ์š”,
์–ด๋А ๋‚ , ์‚ฌ๋‚ด QA ์‹œ์Šคํ…œ์— ์ด๋Ÿฐ ์—๋Ÿฌ๊ฐ€ ์˜ฌ๋ผ์˜ต๋‹ˆ๋‹ค.

"100๋งŒ ๊ฑด์˜ ์—‘์…€ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋กœ๋“œํ•˜๋Š” ๋ฐ ๋กœ๋”ฉ๋ฐ”๊ฐ€ ๋๋‚˜์ง€ ์•Š์•„์š”"

๊ฒฐ๋ก ์ ์œผ๋กœ ์›์ธ์€, ์„œ๋ฒ„์—์„œ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ๋„ˆ๋ฌด ๋งŽ์€ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฐ๋‹ค๋Š” ๊ฒ๋‹ˆ๋‹ค.
์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด SAX(Simple API for XML) ๋ฐฉ์‹์˜ ์ŠคํŠธ๋ฆฌ๋ฐ ํŒŒ์„œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋“ฑ,
๊ทธ ๊ณผ์ •์—์„œ ๊ฒช์—ˆ๋˜ ๋‹ค์–‘ํ•œ ๋ฌธ์ œ๋“ค๊ณผ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์„ ํ†ตํ•œ ์ถ”๊ฐ€ ์„ฑ๋Šฅ ๊ฐœ์„  ๋„์ „ ๋ฐ ๊ฒฐ๊ณผ๊นŒ์ง€
82% ๊ฐ€๋Ÿ‰์˜ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•˜๊ธฐ๊นŒ์ง€ ์žˆ์—ˆ๋˜ ๊ณผ์ •์„ ๋‹ค๋ค„๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.


์„  ๊ฒฐ๋ก 

๊ตฌ๋ถ„ XSSFWorkbook (DOM) XSSFReader (SAX) ๋น„๊ณ 
๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ๋งค์šฐ ๋†’์Œ (ํŒŒ์ผ ํฌ๊ธฐ์— ๋น„๋ก€) ๋งค์šฐ ๋‚ฎ์Œ (ํŒŒ์ผ ํฌ๊ธฐ์™€ ๋ฌด๊ด€) 100๋งŒ ๊ฑด ๊ธฐ์ค€ 15GB -> 4GB
์ฒ˜๋ฆฌ ์†๋„(๋Œ€์šฉ๋Ÿ‰) ๋งค์šฐ ๋А๋ฆผ ๋งค์šฐ ๋น ๋ฆ„ 100๋งŒ ๊ฑด ๊ธฐ์ค€ 60์ดˆ+ -> 11์ดˆ
๊ตฌํ˜„ ๋‚œ์ด๋„ ์‰ฌ์›€ (์ง๊ด€์ ) ๋ณต์žกํ•จ (์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜) ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ํ•ต์‹ฌ
ํ•ต์‹ฌ ์‚ฌ์šฉ์ฒ˜ ์†Œ์šฉ๋Ÿ‰ ํŒŒ์ผ, ํŒŒ์ผ ์ˆ˜์ • ํ•„์š” ์‹œ ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์ฝ๊ธฐ ์ „์šฉ ์ด๋ฒˆ ์ด์Šˆ์˜ ์Ÿ์ 

1. ๋ฌธ์ œ ์ƒํ™ฉ ๋ถ„์„

๋จผ์ €, ์ด์Šˆ๋ฅผ ์žฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด QA์—์„œ ์ž…๋ ฅํ–ˆ๋˜ ํŒŒ์ผ์„ ์ œ๊ณต๋ฐ›์•„,
100๋งŒ ๊ฑด์˜ ์—‘์…€ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ง€์ผœ๋ดค์Šต๋‹ˆ๋‹ค.
100๋งŒ๊ฑด ์—…๋กœ๋“œ ์†Œ์š” ์‹œ๊ฐ„

ํ•˜์ง€๋งŒ, ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ๋Š” 1.4๋ถ„๋งŒ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๋Š” ๊ฑธ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

์–ด? ์ด ์ •๋„๋ฉด ๊ดœ์ฐฎ์€ ๊ฒŒ ์•„๋‹Œ๊ฐ€?

๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค ๋•Œ ์ฆˆ์Œ, VisualVM์„ ํ†ตํ•ด ๋ฉ”๋ชจ๋ฆฌ ์ƒํƒœ๋ฅผ ํ™•์ธ ํ•ด ๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.
100๋งŒ๊ฑด ์—…๋กœ๋“œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ, ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋Š” ์—†์ง€๋งŒ 15GB์— ๊ฐ€๊นŒ์šด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
"์•„, QAํŒ€์—์„œ ํ…Œ์ŠคํŠธํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” VM์˜ ์„ฑ๋Šฅ์ด ๋‚ฎ์œผ๋ฉด, ์„œ๋ฒ„๊ฐ€ ์ฃฝ์„ ์ˆ˜๋„ ์žˆ๊ฒ ๊ตฌ๋‚˜..."

๊ฒฐ๊ตญ, ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์žก์•„๋จน๋Š” ๋ถ€๋ถ„์„ ์ฐพ์•„๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.


2. ๋ฌธ์ œ ์›์ธ, XSSFWorkbook

๋ฌธ์ œ๋กœ ์˜์‹ฌ ๋œ ์ฝ”๋“œ๋Š” Apache POI์˜ XSSFWorkbook ๊ฐ์ฒด์— ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
getRow(), getCell() ๋“ฑ ์‚ฌ์šฉ๋ฒ•์ด ๋งค์šฐ ์ง๊ด€์ ์ด๋ผ ๋งŽ์€ ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ฐฉ์‹์ด์ฃ .

try (Workbook workbook = new XSSFWorkbook(file.getInputStream())) {
    Sheet sheet = workbook.getSheetAt(0);
    for (Row row : sheet) {
        // ... ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์„œ List<Map>์— ์ถ”๊ฐ€ํ•˜๋Š” ๋กœ์ง ...
    }
}

์ด ์ฝ”๋“œ์—์„œ, 100๋งŒ ํ–‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง„ ์—‘์…€ ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

XSSFWorkbook์€ DOM(Document Object Model) ํŒŒ์„œ๋กœ, DOM ํŒŒ์„œ๋Š” ํŒŒ์ผ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์ „์—,
ํŒŒ์ผ์˜ ๋ชจ๋“  ๋‚ด์šฉ์„ ๋ฉ”๋ชจ๋ฆฌ์— ์ „๋ถ€ ๋กœ๋“œํ•˜์—ฌ ํŠธ๋ฆฌ ๊ตฌ์กฐ์˜ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

๋ฌธ์ œ๋Š” 100๋งŒ ํ–‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง„ ํŒŒ์ผ์ด 30MB์˜€๊ณ , ์ด๋Ÿฌํ•œ ํฐ ํŒŒ์ผ์„ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด Heap ์‚ฌ์šฉ๋Ÿ‰์€ ์—„์ฒญ๋‚˜๊ฒŒ ๋งŽ์•„์ง‘๋‹ˆ๋‹ค.
๊ฒฐ๊ตญ OutOfMemoryError๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜, ์‹ฌ๊ฐํ•œ GC ์ง€์—ฐ์„ ๋ฐœ์ƒ์‹œํ‚ค๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค.


3. ์ฒซ๋ฒˆ์งธ ํ•ด๊ฒฐ ์‹œ๋„ (Simple API for XML ํ™œ์šฉ)

SAX(Simple API for XML)๋ž€?

SAX๋Š” DOM๊ณผ ๋‹ฌ๋ฆฌ,
ํŒŒ์ผ์„ ํ•œ ๋ฒˆ์— ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œํ•˜์ง€ ์•Š๊ณ  ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ์ˆœ์ฐจ ํŒŒ์‹ฑํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
์ผ์ข…์˜ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์ฒ˜๋Ÿผ, ํ•œ์ค„ ํ•œ์ค„ ์ฝ์–ด๋‚˜๊ฐ‘๋‹ˆ๋‹ค.

์ฆ‰, <row>, <c> ๋“ฑ์˜ XML ํƒœ๊ทธ๋ฅผ ๋งŒ๋‚  ๋•Œ๋งˆ๋‹ค ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ
ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์—‘์…€์€ ๋‚ด๋ถ€์ ์œผ๋กœ XML๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Œ (.xlsx = ZIP + XML)
  • XSSFReader๋ฅผ ์ด์šฉํ•˜๋ฉด SAX ๋ฐฉ์‹์œผ๋กœ ์—‘์…€ ๋ฐ์ดํ„ฐ๋ฅผ ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

์ด๋Ÿฌํ•œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๋งŒ ํŠน์ • ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฉด,
ํŒŒ์ผ ํฌ๊ธฐ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ฑฐ์˜ ์ผ์ •ํ•œ ๋ฉ”๋ชจ๋ฆฌ๋งŒ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๊ธฐ์— ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์ฒ˜๋ฆฌ์— ์ด์ ์ด ์žˆ์„๊ฑฐ๋ผ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

1๋‹จ๊ณ„: ๋ฐ์ดํ„ฐ ํƒ€์ž… ์ฒ˜๋ฆฌ

SAX๋Š” ๋ชจ๋“  ์…€์„ ๋‹จ์ˆœ ๋ฌธ์ž์—ด๋กœ ์ฝ๊ธฐ ๋•Œ๋ฌธ์—,
t, s ์†์„ฑ๊ฐ’์„ ์ง์ ‘ ํŒ๋ณ„ํ•ด ํƒ€์ž…์„ ๊ตฌ๋ถ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์—‘์…€์—์„œ ์‚ฌ์šฉ์ž ์ง€์ • ํ˜•์‹ ๊ฐ™์€ ๊ฒƒ์ด๋ผ ์ดํ•ดํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

  • ๋ฌธ์ž์—ด(t="s") โ†’ SharedStringsTable๋กœ๋ถ€ํ„ฐ ์‹ค์ œ ๊ฐ’ ํ™•์ธ
  • ๋‚ ์งœํ˜• โ†’ StylesTable + DateUtil.isADateFormat() ์กฐํ•ฉ์œผ๋กœ ํ™•์ธ
// XML ํŒŒ์ผ ์ฝ๊ธฐ
XSSFReader r = new XSSFReader(OPCPackage...);
SharedStringsTable sst = r.getSharedStringsTable();
...
// XML ์ฝ๊ธฐ ์‹œ์ž‘ ๋ถ€๋ถ„
@Override
public void startElement(... Attributes attributes ...) throws SAXException {
    ...
    isStringCell = "s".equals(attributes.getValue("t"));
    ...
}
... // ์…€ ๋ฐ์ดํ„ฐ ์ €์žฅ๋ถ€๋Š” ์ƒ๋žต

// XML ์ฝ๊ธฐ ์ข…๋ฃŒ ๋ถ€๋ถ„
@Override
public void endElement(...) throws SAXException {
    ...
    if (isStringCell) { cellValue = sst.getItemAt(...).getString(); }
    ...
}

์œ„ ์ฝ”๋“œ์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ StringCell ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ํ•จ๊ป˜ ์ฒจ๊ฐ€ํ•˜์—ฌ
์ˆซ์ž, ๋‚ ์งœ, ํ…์ŠคํŠธ๋ฅผ ๊ตฌ๋ถ„ํ•˜์—ฌ ์ œ๋Œ€๋กœ ์ฝ์–ด์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ์„ค์ • ํ•ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.
๋ฌผ๋ก , String์ด ์•„๋‹Œ ๊ฒฝ์šฐ๋„ ์ฝ”๋“œ์ƒ์—๋Š” ์ „๋ถ€ ๋…น์—ฌ ์žˆ์Šต๋‹ˆ๋‹ค.

2๋‹จ๊ณ„: ๋นˆ ์…€(Empty Cell) ์ฒ˜๋ฆฌ

SAX ํŒŒ์‹ฑ์—์„œ ํฐ ๋ฌธ์ œ๊ฐ€, ๋น„์–ด์žˆ๋Š” ์…€์— ๋Œ€ํ•ด์„œ๋Š” XML ํƒœ๊ทธ ์ž์ฒด๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋”๊ตฐ์š”.
์˜ˆ๋ฅผ ๋“ค์–ด, ์‹ค ์—‘์…€ ํŒŒ์ผ ๋‚ด์—์„œ C3 ์…€์ด ๊ณต๋ฐฑ(๋น„์–ด์žˆ๋Š”) ์ƒํƒœ์ผ ๋•Œ, ์‹ค์ œ XML ํŒŒ์ผ์—์„œ๋Š” <C1>A</C2> <C4>B</C4> ์ด๋ ‡๊ฒŒ ์ฝ์–ด์ง‘๋‹ˆ๋‹ค.
๋น„์–ด์žˆ๋Š” ์…€์„ ๊ฑด๋„ˆ๋›ฐ๊ธฐ ๋•Œ๋ฌธ์—, ์—ด ์ˆœ์„œ๊ฐ€ ๋ฐ€๋ฆฌ๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ, HashMap<์ปฌ๋Ÿผ ์ธ๋ฑ์Šค, ๋ฐ์ดํ„ฐ> ํ˜•ํƒœ๋กœ ์ž„์‹œ ์ €์žฅ ํ›„
ํ–‰์ด ์ข…๋ฃŒ๋  ๋•Œ List๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด์„œ ๋นˆ ์ธ๋ฑ์Šค์— ""(๋นˆ ๋ฌธ์ž์—ด)์„ ์ฑ„์›Œ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค.

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    if ("row".equals(qName)) {
        currentRow = new TreeMap<>(); // ๋ฐ์ดํ„ฐ ์ดˆ๊ธฐํ™”
    } else if ("c".equals(qName)) {
        // ์…€ ์ฃผ์†Œ(r) ์ถ”์ถœ โ†’ ์˜ˆ: "C3"
        String cellRef = attributes.getValue("r");
        currentColIndex = getColumnIndex(cellRef);
        isStringCell = "s".equals(attributes.getValue("t")); // ๋ฌธ์ž์—ด ์—ฌ๋ถ€ ํ™•์ธ
        cellValue.setLength(0);
    }
}

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
    cellValue.append(ch, start, length);
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
    // ...
    } else if ("row".equals(qName)) {
        // ํ–‰ ์ข…๋ฃŒ ์‹œ โ†’ TreeMap์„ List๋กœ ๋ณ€ํ™˜ํ•˜๋ฉฐ ๋นˆ ์นธ ์ฑ„์›€
        if (!currentRow.isEmpty()) {
            int maxCol = currentRow.lastKey();
            List<String> rowData = new ArrayList<>(Collections.nCopies(maxCol + 1, ""));
            for (Map.Entry<Integer, String> entry : currentRow.entrySet()) {
                rowData.set(entry.getKey(), entry.getValue());
            }
            rows.add(rowData); // ์ตœ์ข… ๋ฐ์ดํ„ฐ ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€
        }
    }
}

// ์…€ ์ฃผ์†Œ๋ฅผ ์—ด ์ธ๋ฑ์Šค๋กœ ๋ณ€ํ™˜ ("A"->0, "B"->1, "AA"->26 ...)
private int getColumnIndex(String cellRef) {
    int index = 0;
    for (char c : cellRef.replaceAll("\\d", "").toCharArray()) {
        index = index * 26 + (c - 'A' + 1);
    }
    return index - 1;
}

์…€์˜ ์ฃผ์†Œ(r ์†์„ฑ)์—์„œ ์ถ”์ถœํ•œ ์ปฌ๋Ÿผ ์ธ๋ฑ์Šค๋ฅผ Key๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ํ–‰์ด ๋๋‚˜๋Š” ์‹œ์ ์—, ์ด TreeMap๋ฅผ List๋กœ ๋ณ€ํ™˜ํ•˜๋ฉฐ ๋น„์–ด์žˆ๋Š” ์ธ๋ฑ์Šค์—๋Š” ๋นˆ ๋ฌธ์ž์—ด("")์„ ์ฑ„์›Œ ๋„ฃ์Šต๋‹ˆ๋‹ค.
์ด๋ ‡๊ฒŒ ๋˜๋ฉด, ์ตœ์ข… List์˜ ๋ฌด๊ฒฐ์„ฑ๋„ ์œ ์ง€๊ฐ€ ๋˜๊ณ , ์›ํ•˜๋˜ ๋นˆ ์…€๋„ ์–ป์–ด๋‚ผ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

3๋‹จ๊ณ„: ์„ฑ๊ณต์ ์ธ ๊ฒฐ๊ณผ

์ด๋Ÿฐ ์ž‘์—…์€ ํฐ ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ๋ณด์—ฌ์คฌ์Šต๋‹ˆ๋‹ค.
์ฒ˜์Œ ์ƒ๊ฐํ–ˆ๋˜ ๊ฐ€์„ค์ธ ์ธํ„ฐํ”„๋ฆฌํ„ฐ ํ˜•์‹์œผ๋กœ ์ฝ์œผ๋‹ˆ ๋ฉ”๋ชจ๋ฆฌ ๋ฝ๋„ ์ ๊ฒŒ ๋“ค์–ด๊ฐˆ ๊ฒƒ์ด ๋“ค์–ด๋งž์•˜์—ˆ์ฃ .
๊ทธ ๋ฟ ์•„๋‹ˆ๋ผ, ์ฝ๋Š” ์‹œ๊ฐ„ ์ž์ฒด๋„ 11์ดˆ๋ผ๋Š” ์—„์ฒญ๋‚œ ์„ฑ๋Šฅ์„ ๋ณด์—ฌ์ฃผ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ฐœ์„  ํ›„ 100๋งŒ๊ฑด ์—…๋กœ๋“œ ์†Œ์š” ์‹œ๊ฐ„

๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ๋˜ํ•œ Heap ๊ธฐ์ค€ 4GB๋กœ, ์—„์ฒญ๋‚œ ๊ฐ์†Œ๋ฅผ ๋ณด์—ฌ์คฌ์Šต๋‹ˆ๋‹ค. ๊ฐœ์„  ํ›„ 100๋งŒ๊ฑด ์—…๋กœ๋“œ ๋ฉ”๋ชจ๋ฆฌ


4. ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๋„์ „

์‹ฑ๊ธ€์Šค๋ ˆ๋“œ SAX ํŒŒ์„œ๋งŒ์œผ๋กœ๋„ 11์ดˆ๋ผ๋Š” ๋†€๋ผ์šด ๊ฒฐ๊ณผ๋ฅผ ์–ป์—ˆ์ง€๋งŒ,
โ€˜10์ดˆ ๋ฏธ๋งŒโ€™์„ ๋ชฉํ‘œ๋กœ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ(์ƒ์‚ฐ์ž-์†Œ๋น„์ž ํŒจํ„ด)๋ฅผ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค.

SAX๋กœ ์ฝ๋Š” ์Šค๋ ˆ๋“œ(์ƒ์‚ฐ์ž)์™€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•˜๋Š” ์Šค๋ ˆ๋“œ(์†Œ๋น„์ž)๋ฅผ ๋ถ„๋ฆฌํ•˜๋ฉด ๋” ๋น ๋ฅด์ง€ ์•Š์„๊นŒ?

BlockingQueue<List<String>> queue = new LinkedBlockingQueue<>(1000);
ExecutorService consumers = Executors.newFixedThreadPool(4);

// ์†Œ๋น„์ž, ์Šค๋ ˆ๋“œ 4๊ฐœ ํ…Œ์ŠคํŠธ
for (int i = 0; i < 4; i++) {
    consumers.submit(() -> {
        try {
            while (true) {
                List<String> row = queue.take();
                if (row.isEmpty()) break;  
                // ๋กœ์ง...
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    });
}

// ์ƒ์‚ฐ์ž
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
    if ("row".equals(qName)) {
        queue.put(new ArrayList<>(currentRow.values()));
    }
}

// ๋ธ”๋กœํ‚น ๋ถ€๋ถ„
for (int i = 0; i < 4; i++) {
    queue.put(Collections.emptyList());
}
consumers.shutdown();

ํ•˜์ง€๋งŒ ๊ฒฐ๊ณผ๋Š” ๋ฐ˜๋Œ€์˜€์Šต๋‹ˆ๋‹ค.
11์ดˆ โ†’ 30.02์ดˆ๋กœ ์˜คํžˆ๋ ค ๋А๋ ค์กŒ์Šต๋‹ˆ๋‹ค.

๋ณ‘๋ ฌ์ฒ˜๋ฆฌ ์ถ”๊ฐ€ ํ›„ 100๋งŒ๊ฑด ์—…๋กœ๋“œ ์†Œ์š” ์‹œ๊ฐ„

์›์ธ์€ ๋ช…ํ™•ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ํ–‰ ๋‹จ์œ„ ๋ณ€ํ™˜ ์ž‘์—…์€ ๋งค์šฐ ๊ฐ€๋ฒผ์šด ์—ฐ์‚ฐ์ด์—ˆ๊ณ 
  • ์Šค๋ ˆ๋“œ ๋™๊ธฐํ™”(BlockingQueue) ๋ฐ ์ „ํ™˜ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋” ์ปธ๋˜ ๊ฒƒ

ํ–‰ ํ•˜๋‚˜๋ฅผ HashMap์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—… ์ž์ฒด๊ฐ€ ๋งค์šฐ ๊ฐ€๋ฒผ์› ๋˜ ํƒ“์—, ์Šค๋ ˆ๋“œ ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋™๊ธฐํ™”ํ•˜๊ณ (BlockingQueue, ConcurrentSkipListMap),
์Šค๋ ˆ๋“œ๋ฅผ ์ „ํ™˜ํ•˜๋Š” ๋น„์šฉ์ด ์‹ค์ œ ์ž‘์—… ์‹œ๊ฐ„๋ณด๋‹ค ๋” ๋งŽ์ด ์†Œ์š”๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด ๊ฒฝํ—˜์œผ๋กœ, ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์€ ๋งŒ๋Šฅ์ด ์•„๋‹ˆ๋ฉฐ, ์ž‘์—… ์„ฑ๊ฒฉ์— ๋”ฐ๋ผ ๋ช…ํ™•ํ•œ ์‚ฌ์šฉ์ฒ˜๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋‹ค์‹œ๊ธˆ ๊นจ๋‹ซ๊ฒŒ ๋˜์—ˆ๋„ค์š”.


๋งˆ๋ฌด๋ฆฌ

์ด๋ฒˆ ์ด์Šˆ๋ฅผ ํ†ตํ•ด ์—‘์…€ ํ•ธ๋“ค๋ง์— ๋Œ€ํ•ด ์กฐ๊ธˆ ๋” ๋ฐฐ์›Œ๋ณผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

  • ๋Œ€์šฉ๋Ÿ‰ ์—‘์…€ ์ฒ˜๋ฆฌ์—์„œ๋Š” SAX๋ฅผ ์šฐ์„  ๊ฒ€ํ† ํ•ด๋ณผ ๊ฒƒ, ๋ฉ”๋ชจ๋ฆฌ์ƒ ์ด์ต์ด ์žˆ๊ธฐ์—.
  • ์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ๊ฐ์ด ์•„๋‹Œ ์‹ค ์ธก์ •์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ,
  • ๋ณต์žกํ•œ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์€ ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ๋ฌธ์ œ๋ฅผ ๋‹จ์ˆœํ™” ํ•œ ๋‹ค์Œ ๊ณ ๋ คํ•  ๊ฒƒ.

์„ฑ๋Šฅ ๊ฐœ์„ ์€, ์–ธ์ œ๋‚˜ ์ดํ•ด๊ฐ€ ๋จผ์ €๋‹ค.

๊ธด ๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํ‹€๋ฆฐ ๋ถ€๋ถ„์ด๋‚˜ ๋” ๋ณด์™„ํ• ๋งŒํ•œ ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด,
๋Œ“๊ธ€ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค! ๐Ÿ˜Š

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.