๐ [ko] A Case Study on Handling OutOfMemory Issues in Excel Processing(XSSF Workbook)
โํด์น์ ๋..? ๐คจโ
Monitoring revealed that our serverโs Old Generation memory was filling up, leading to Out Of Memory (OOM). The main issue was processing large data using Apache POIโs XSSFWorkbook, which loads all data into memory.
์ ์ ์์ฌ๊ฐ๋ Old generationโฆ
Grafana ๋ก ์๋ฒ๋ฅผ ๋ชจ๋ํฐ๋ง ํ๋ ์ค, ์ด์ ์๋ฒ์ Old Generation ์์ญ์ด ์ ์ฐจ ์์ด๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
์ฌ๊ธฐ์ Old Generation ์ด๋ JVM ์ heap memory ์์ญ ์ค ํ๋๋ก,
GC(Garbage Collection) ์ ์ํด ํ์๋์ง ๋ชปํ๊ณ ์ค๋ ์ด์๋จ์ ๊ฐ์ฒด๋ค์ด ๋ชจ์ด๋ ์์ญ์ ๋๋ค.
Old Generation ์์ญ์์ ๋ฐ์ํ๋ GC ๋ major GC ํน์ full GC ๋ผ๊ณ ํ๋๋ฐ,
์ด ๊ณผ์ ์์ Stop - The - World(STW) ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ฌ Application ์ ๋ชจ๋ thread ๊ฐ ์ผ์์ ์ง๋ฉ๋๋ค.
major GC ๋ Young Generation ์์ญ์์ ๋ฐ์ํ๋ minor GC ์ ๋นํด ๋ ๊ธด STW ์๊ฐ์ ๊ฐ์ง๋๋ค.
๋์ฉ๋์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ณผ์ ์์ Old Generaiton ์์ญ์์์ ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ์ผ๋ก ์ธํด
Out Of Memory (OOM) ๊ฐ ๋ฐ์ํ์ฌ ์๋ฒ๊ฐ ๋ค์ด๋ ์ ์์ต๋๋ค.
์ต๊ทผ ์ฐ๋ฆฌ ์๋น์ค์์๋ ์ด์ ๊ฐ์ ๋ฌธ์ ๊ฐ ์์๋๋ฐ์,
์ฃผ ์์ธ์ ๋๊ท๋ชจ ๋ฐ์ดํฐ๋ฅผ Excel ํ์ผ๋ก ๋ณํํ๋ ๊ณผ์ ์์ ๋ฐ์ํ์ต๋๋ค.
Apache POI Library
heap dump
๋น์์ JVM heap dump ๋ฅผ ๋ ์ Old Gen ์ ์ด๋ ํ ๊ฐ์ฒด๊ฐ ์์ด๊ณ ์๋์ง ํ์ธํ๊ณ ์ ํ์์ต๋๋ค.
๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๊ณ ์๋ ๊ฐ์ฒด๋ Apache POI Library ๊ด๋ จ ํด๋์ค๋ค์ด์์ต๋๋ค.
์ฐ๋ฆฌ ์๋น์ค์ Excel ํ์ผ ์์ฑ API ์์ Apache POI ์ XSSFWorkbook ์ ์ฌ์ฉํ๊ณ ์์์ต๋๋ค.
Stack overflow: POI OutOfMemory Exception with XLSX
Apache POI Library ์ XSSFWorkbook ์ Excel ๊ด๋ จ ๋ฐ์ดํฐ๋ค์ ๋ฉ๋ชจ๋ฆฌ์ ๋ชจ๋ tree map ์ผ๋ก ๋ก๋ํ์ฌ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์,
๋ฐ์ดํฐ์ ์์ด ๋ง์์ง๋ฉด ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ๋์ด๋์ OOM ์ด ๋ฐ์ํ๊ฒ ๋ ๊ฒ์ด์ฃ .
SXSSFWorkbook
Apache ์์๋ XSSFWorkbook ์ธ์๋ SXSSFWorkbook ์ ์ ๊ณตํ๊ณ ์์ต๋๋ค.
SXSSFWorkbook ์ Streaming ๋ฐฉ์์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ํ์ํ ๋ฐ์ดํฐ์ ์ผ๋ถ๋ง ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋ํ๊ณ ๋๋จธ์ง๋ ์์ํ์ผ๋ก์จ ๋์คํฌ์ ์ ์ฅํ์ฌ ์ฌ์ฉํฉ๋๋ค.
์ฆ, ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฌ์ง ์์๋ ๋๊ธฐ ๋๋ฌธ์ XSSFWorkbook ์ ๋นํด ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ํฌ๊ฒ ์ค์ผ ์ ์์ผ๋ฏ๋ก,
๊ธฐ์กด์ ๋ฐ์ํ๋ OOM ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ ๊ฒ์ผ๋ก ์์ํ์ต๋๋ค.
XSSFWorkbook ์์๋ Excel read / write ๋ฅผ ๋ชจ๋ ์ง์ํ๋๋ฐ ๋ฐํด
SXSSFWorkbook ์์๋ write ๋ง ์ง์ํ๋ค๋ ๊ฒ๋ ์ฃผ์ํ ์ฐจ์ด์ ์ธ๋ฐ์,
์ฐ๋ฆฌ ์๋น์ค์ ์๊ตฌ์ฌํญ์ Excel Write ์๊ธฐ ๋๋ฌธ์
SXSSFWorkbook ์ผ๋ก ๊ต์ฒดํ์ฌ ์๋ฒ ๋ฉ๋ชจ๋ฆฌ์ ๋ถ๋ด์ ์ค์ด๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค
์๋ง์ ๋ ๊ฑฐ์ ์ฝ๋
ํ๋ก์ ํธ ์ ๋ฐ์ ๊ฑธ์ณ ํฉ์ด์ ธ ์๋ ์์ ๊ด๋ จ ์ฝ๋๋ค์ ์ค๋ณต๋์ ๋ณต์ก์ฑ์ด ๋งค์ฐ ๋์์ต๋๋ค.
์ด๋ฅผ ์ ๋ถ ๋ค ๊ฑท์ด๋ด๊ณ ๋ฆฌํฉํ ๋งํ์ฌ ์์ ํ์ผ ๊ด๋ จ ๋ก์ง์ ๋ช ํํ๊ณ ์ฌ์ฌ์ฉ์ด ์ฌ์ด ํํ๋ก ๊ฐ์ ํ๊ณ ์ ํ์ต๋๋ค.
ํ๊ท ์ ์ผ๋ก 200~300 ๋ผ์ธ์ ๋ฌํ๋ ์ก์ ํ์ผ ์์ฑ ์๋น์ค ์ฝ๋๋ค์ด ๊ฐ ๋๋ฉ์ธ(ํ์, ๊ฒฐ์ , ์ฟ ํฐ, โฆ) ๋ณ๋ก ์กด์ฌํ์ต๋๋ค.
๊ฐ ๋ฉ์๋๋ง๋ค 100 ~ 150 ๋ผ์ธ์ Excel ๊ด๋ จ ์ด๊ธฐํ ๋ก์ง์ด ์ค๋ณต๋์ด ์์์ต๋๋ค.
์ด๋ ๊ด๋ฆฌํ๊ธฐ ์ด๋ ต๊ณ ,
๋ค์ํ ๋ฌธ์ ๋ฅผ ์ ๋ฐํ ์ ์์ต๋๋ค.
์์คํ ์ ์์ ์ ์ผ๋ก ์ด์ํ๊ธฐ ์ํด์๋ ๊ด๋ฆฌ ํฌ์ธํธ๋ฅผ ์ต์ํํ๋ ๊ฒ์ด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค.
์ด๋ฒ ์ฌ๋ก์์๋ ์์ ๊ด๋ จ ๊ธฐ๋ฅ์ ์บก์ํ ๋ฐ ์ถ์ํํ๋ ๊ฒ์ด ํต์ฌ์ด์์ต๋๋ค.
๋ด๋ถ ๊ตฌํ์ ์จ๊ธฐ๊ณ ์ธ๋ถ์์๋ ๋จ์ํ ๋ฉ์๋ ํธ์ถ๋ง์ผ๋ก ์์ ํ์ผ ๋ ๋๋ง์ด ๊ฐ๋ฅํ๋ ๊ฒ์ด ์ด์์ ์ธ ํํ๋ผ๊ณ ํ๋จํ์ต๋๋ค.
ํ์ง๋ง ์์ ํ์ผ์ ์๊ตฌ์ฌํญ๋ง๋ค ํค๋์ ๋ฐ๋๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ์ ์ ํ๊ฒ ๊ณ ๋ คํด์ค ํ์๊ฐ ์์์ต๋๋ค.
Template Method Pattern
์์ ํ์ผ ์์ฑ๊ณผ ๋ ๋๋ง์ ์๊ณ ๋ฆฌ์ฆ ์์๋ฅผ ์ ์ํ๊ณ ,
์์ ๋ฐ์ดํฐ ์๊ตฌ์ฌํญ ๋ณ๋ก ์ธ๋ถ ๊ตฌํ์ ๋ํ ์ปค์คํฐ๋ง์ด์ง์ด ํ์ํ์ต๋๋ค.
์ด๋ฌํ ์ํฉ์ ์ฝ๊ฒ ํด๊ฒฐํ ์ ์๋ ๋์์ธ ํจํด์ด ๋ฐ๋ก Template method pattern ์ด์์ต๋๋ค.
Template method pattern
์์ ํ์ผ์ ์์ฑํ๊ณ ๋ ๋๋งํ๋ ์๊ณ ๋ฆฌ์ฆ์ ์์๋ฅผ ์ ์ํ๊ณ ,
์ปค์คํฐ๋ง์ด์ง์ด ํ์ํ ๊ฐ๊ฐ์ step ์ ๋ํด์๋ง subclass ์์ ๋ฐ๋ก ๊ตฌํํ๋๋ก ํ๋ ๊ฒ์ ๋๋ค.
public abstract class ExcelViewTemplate<T> {
private final SXSSFWorkbook workbook;
private SXSSFSheet sheet;
public ExcelViewTemplate() {
// ์ด๊ธฐํ ๋ก์ง ์์ฑ
}
public final void renderData(List<T> dataList) {
// ๋ ๋๋ง ๋ก์ง์ ์์ฑ
}
protected abstract void renderHeader(Workbook workbook, Sheet sheet, CellStyle cellStyle);
protected abstract void renderBody(Row row, T data, CellStyle cellStyle);
}
ํ์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํด์ผ ํ๋ค๊ณ ๊ฐ์กํ์ฃ .
public class UserExcelView extends ExcelViewTemplate<UserExcelDto> {
@Override
protected void renderHeader(Workbook workbook, Sheet sheet, CellStyle cellStyle) {
// UserExcelDto ์ ๋ง๊ฒ ๊ตฌํ
}
@Override
protected void renderBody(Row row, UserExcelDto data, CellStyle cellStyle) {
// UserExcelDto ์ ๋ง๊ฒ ๊ตฌํ
}
}
UserExcelDto
๋ ์์
๋ฐ์ดํฐ ์ฉ๋๋ก ์ฌ์ฉํ๋ dto ํด๋์ค์
๋๋ค. (์์๋ก ๋ณด์ฌ์ฃผ๊ธฐ ์ํ ์ฝ๋์
๋๋ค.)
๊ฐ๊ฐ์ ํค๋๊ฐ ์ด๋ค title ์ ๊ฐ์ง๋์ง, ๋ช ๋ฒ์งธ ์ด์ ํด๋นํ๋ ์ง ๋ช ์ํ๊ธฐ ์ํด์๋ ๋ณ๋์ ExcelColumnInfo ํด๋์ค๋ฅผ ๊ตฌํํ์ฌ ์ฌ์ฉํ ์ ์์ต๋๋ค.
public interface ExcelColumnInfo {
String getText(); // ์ด๋ฆ
int getColumn(); // ์ด ์์
}
@Getter
@RequiredArgsConstructor
public enum UserExcelColumnInfo implements ExcelColumnInfo {
YOUR_HEADER1("ํค๋1", 0),
YOUR_HEADER2("ํค๋2", 1),
;
private final String text;
private final int column;
public static List<UserExcelColumnInfo> getAllColumnInfos() {
return Arrays.asList(values());
}
}
ํ์์ ๋ฐ๋ผ CellStyle ์ ์ง์ ํ๋ ํ๋๋ฅผ ๋ณ๋๋ก ๋ฃ์ด, ํค๋๋ณ๋ก ๋ค๋ฅธ ํฐํธ, ํ ๋๋ฆฌ, ๋ฐฐ๊ฒฝ์ ์ง์ ํ ์๋ ์์ต๋๋ค.
์ด์ ๊ฐ์ด ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ ํตํด ์ถ์ํ ํ ExcelView ํด๋์ค๋ฅผ ์ด์ฉํ๋ฉด ์๋น์ค ๋ก์ง์ด ๋งค์ฐ ๊ฐ๋จํด์ง๋๋ค.
๊ธฐ์กด์๋ ์์ ํ์ผ์ ๋ ๋๋งํ๋ ๋ฉ์๋๋ง๋ค 200~300 ๋ผ์ธ์ด์๋ค๋ฉด
ํ์ฌ๋ ๊ฐ๊ฐ 30 ๋ผ์ธ ์ดํ๋ก ํฌ๊ฒ ์ค์ผ ์ ์์์ต๋๋ค.
๊ด๋ฆฌ ํฌ์ธํธ๋ ์์ง๋์ด, ์ ์ง๋ณด์์๋ ๋์ฑ ์ ๋ฆฌํด์ก์ฃ .
์์ ๊ฐ์ ๊ตฌ์กฐ๋ก ๋ฆฌํฉํ ๋งํ ํ,
๊ฐ๋ฐ์ฉ์ผ๋ก ๋ฐ๋ก ๋์๋ ์๋ฒ์์ ๋ฐ์ดํฐ 5๋ง ๊ฐ์ ๋ํ์ฌ ์์ ํ์ผ ์์ฒญ์ ๋ฐ๋ณต์ ์ผ๋ก ์์ฒญํ๋ฉฐ ์๋ฒ ํ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋๊ณผ ์ฒ๋ฆฌ ์๋๋ฅผ ๋ชจ๋ํฐ๋ง ํด๋ณธ ๊ฒฐ๊ณผ,
๊ธฐ์กด์ ๋ ๊ฑฐ์ API ์ ๋ฌ๋ฆฌ ๊ต์ฅํ ์์ ์ ์ด์์ต๋๋ค.
์ฒ๋ฆฌ ์๋ ๋ํ ๊ธฐ์กด์๋ 40 ~ 1๋ถ ์ด์์ API ์๋ต ์๋์์ง๋ง,
๊ฐ์ ๋ ํ์ฌ๋ ์ต๋ 10์ด ์ด๋ด๋ก ์๋ตํ๊ณ ์์ต๋๋ค. (๋ฐ์ดํฐ 5~6๋ง๊ฐ ๊ธฐ์ค)
๋ณธ ์์ ์ ์งํ ๊ณผ์ ์์, ํด๊ฒฐํด์ผ ํ๋ ๋ฌธ์ ์ํฉ๊ณผ ์ ๊ฐ ํ๊ณ ์ ํ๋ ๋ฐฉํฅ๊ณผ ์ ํํ ์ผ์นํ ์ฌ๋ก ์ฐ์ํ ๊ธฐ์ ๋ธ๋ก๊ทธ; ์์ ๋ค์ด๋ก๋ ๋ชจ๋ ๊ฐ๋ฐ๊ธฐ ๋ฅผ ๋ฐ๊ฒฌํ์๊ณ ,
์์ฃผ ๋ง์ ๋์์ด ๋์์ต๋๋ค.
ํด๋น ์๋ฃ์์๋ Reflection ๊ณผ Custom Annotation ์ ์ด์ฉํ์ฌ, ์์ ๊ด๋ จํด์ ํ์ธต ๋ ๊ฐํธํ๊ฒ ์์ ํ ์ ์๋๋ก ๊ณ ๋ํ ํ๋ ๊ณผ์ ์ ์๊ฐํ๊ณ ์๋๋ฐ์..
(๋ง์ง๋ง์๋ ๋๋ฌด ๊ฐํธํด์ ๊ฐ๋๋ฐ์)
ํด๋น ์์ ์ ์์ํ ์ ์๋ ์ฃผ์ด์ง ์๊ฐ์ด ์ ์๊ณ
๋น์ฅ ๋ค๋ฅธ ๊ธํ ์ด์๋ฅผ ์ฒ๋ฆฌํด์ผ ํ๊ธฐ์
์์ ๋ชจ๋์ ์ถ์ํํ๋ ๋จ๊ณ๊น์ง๋ง ๊ณ ๋ํํ๊ธฐ๋ก ํ์์ต๋๋ค.
์ง๊ธ์.. ๋น์ฅ ๊ธํ ๋ถ์ ๊ป์ผ๋
๋ ๊ธํ ๋ถ์ ๋๊ณ ๋ค์ ๋์์์ผ๊ฒ ๋ค์.. ๐
์ค๋์ ํ๋ก์ ํธ ์ ์ฒด์ ์ผ๋ก ์ฝํ์๋ ๋ ๊ฑฐ์ ์ฝ๋๋ฅผ ์ ๊ฑฐํ๊ณ
์์ ๋ชจ๋์ ์๋ก ๊ฐ๋ฐํ ํ๊ธฐ์ ๋ํด์ ์ด์ผ๊ธฐํด๋ณด์์ต๋๋ค.
์์ง ํ์ฌ์ ํฉ๋ฅํ ์ง, ์ธํด์ผ๋ก๋ 4๊ฐ์ ๋์๊ณ ์ ๊ท์ง์ผ๋ก๋ 1~2์ฃผ ๋ฐ์ ๋์ง ์์๋๋ฐ์,
๊ทธ๋ผ์๋ ์์นซ ์๋ชปํ๋ฉด ํ๋ก์ ํธ ์ ๋ฐ์ ์ผ๋ก ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์๋งํผ
์ํฅ๋ฒ์๊ฐ ์ปธ๋ ์์ ์,
์ง์ ๊ณํํ์ฌ ํผ์ ๋ด๋นํ์ฌ ๋ฐฐํฌ๊น์ง ๋ง๋ฌด๋ฆฌ์ง์๋๋ฐ์..
์ด๊ฒ์ด ์คํํธ์ ์ ๋งค๋ ฅ์ธ๊ฑธ๊น์..
๋ฌดํํ ์์ ์ ์ฑ ์์ด ๊ณต์กดํ๋ ๊ณณ ๊ฐ์ต๋๋ค.
์ด๋ฒ ์์ ์ด ์ฑ๊ณต์ ์ผ๋ก ๋ง๋ฌด๋ฆฌ๋์ด ์๋ฒ ์์ ํ์ ๊ธฐ์ฌํ ์ ์ด ์ ๋ง ๋คํ์ค๋ฝ๋ค์.
์๋ก์ด ์์ ๋ชจ๋๋ก์ ๋ฆฌํฉํ ๋ง ๊ณผ์ ์ ์ ๋ง ์ฌ๋ฐ์๊ณ , ๊ทธ๋ ๊ฒ ์ด๋ ต์ง ์์์ต๋๋ค.
๋ค๋ง ๊ฐ์ฅ ํ๊ฒจ์ ๋ ๋ถ๋ถ์ ์ค์ํ ๋น์ง๋์ค ๋ก์ง์๋ ์ฝํ์๋ ๊ณ ๋์ ๋ ๊ฑฐ์ ์ฝ๋๋ฅผ ์ดํดํ๊ณ ์ ๊ฑฐํ๋ ๊ฒ์ด์์ต๋๋ค..
์ด๋ด๋ ๋์์ด ๋๋ ๊ฒ์ ๋ํ ์ผํ๊ฒ ์ ์์ฑ๋ ํ ์คํธ ์ฝ๋ ์ผ ํ ๋ฐ์,
์ฐ๋ฆฌ ์คํํธ์ ์ ์์ง ์ด๊ธฐ ๋จ๊ณ๋ผ
๋ฐฑ์๋ ๊ฐ๋ฐ ์ธ๋ ฅ์ด ๋๋ฌด ์ ๊ณ , ์๊ตฌ์ฌํญ์ ์ ๋ง ๋ง์ ํ์
์ฌํ๊ฒ๋ ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง๊ฐ ์์ง์ ๋งค์ฐ ๋ฎ์๋ฐ์..
์ด๋ฌํ ์ํฉ์์์ ๊ณผ๊ฐํ ๋ฆฌํฉํ ๋ง์ ํฐ ๋ถ๋ด์ผ๋ก ๋ค๊ฐ์์ต๋๋ค.
๋น์ง๋์ค ๋ก์ง์ ์ ๋ง ๋ค ์๊ณ ์์ง ์์ผ๋ฉด,
์์ํ์ง ๋ชปํ ๋ถ๋ถ์์ ์ฅ์ ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ด์์ฃ ..
์ด ๋ถ๋ด๊ฐ์ ์จ์ ํ ๋๋ผ๋ฉฐ,
๋งค์ฐ ์กฐ์ฌ์ค๋ฝ๊ฒ ๋ ๊ฑฐ์๋ฅผ ๊ฑท์ด๋ด๊ณ ๊ฒฐ๊ตญ ๋ชจ๋ ์ ๊ฑฐํ๋ ๋ฐ์ ๋คํํ ์ฑ๊ณตํ์ต๋๋ค.
์ญ์.. ์ ๋ง๋ค์ด์ง ํ ์คํธ ์ฝ๋๊ฐ ์ผ๋ง๋ ์ค์ํ ์ง ๋ค์ ํ๋ฒ ๊นจ๋ซ๊ฒ ๋๋ ๊ณ๊ธฐ์์ต๋๋ค.
์ด๋ฒ ํ์ฌ์ ์๋ ๋์,
ํ ์คํธ ํ๊ฒฝ์ ์ฒ์๋ถํฐ ์ ๊ตฌ์ถํ๊ณ
ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ต์ 7~80% ๊น์ง๋ ์ฌ๋ฆฌ๊ณ ์ถ์ ๊ฐ์ธ์ ์ธ ๋ชฉํ๊ฐ ์์ต๋๋ค.
์ ๋ง ๋ง์ ์๊ตฌ์ฌํญ๊ณผ ํ๋ก์ ํธ๊ฐ ์์์ง๋ ํ๊ฒฝ์์,
ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฐ์ ์์ ์๊ฐ์ด ๋ฌผ๋ฆฌ์ ์ผ๋ก ์กฐ๊ธ ๋ถ์กฑํ๊ฒ ๋๊ปด์ง๋ ๊ฒ์ ์ฌ์ค์ด์ง๋ง
์ฅ๊ธฐ์ ์ผ๋ก๋ ๊ฒฐ๊ตญ ๊ฐ๋ฐ ์์ฐ์ฑ์ ๊ฐ์ํ ํด์ฃผ๋ ๊ฒ์ด ํ ์คํธ ์ฝ๋๋ผ๊ณ ์๊ฐํ๋ฏ๋ก
๊ผญ ํด๋ผ ์ ์๋๋ก ํด์ผ๊ฒ ์ต๋๋ค.
ํ ์ค์์ ์ฌ๋ฆฐ ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง 100% ์ด์๊ธฐ ์์์ ๋ค์ ํ๋ฒ ๋ณด๋ฉด์ ๋ง์์ ๋ค ์ก์์ผ๊ฒ ๋ค์
Reference
์ฐ์ํ ๊ธฐ์ ๋ธ๋ก๊ทธ; ์์ ๋ค์ด๋ก๋ ๋ชจ๋ ๊ฐ๋ฐ๊ธฐ
Leave a comment