ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๋ฐ˜์‘ํ˜•

โœ… MyBatis์˜ ResultHandler๋ฅผ ํ†ตํ•ด POI SXSSF๋กœ ๋Œ€์šฉ๋Ÿ‰ ์—‘์…€ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

๊ฐœ๋ฐœํ™˜๊ฒฝ

  • SpringBoot 2.6.2
  • JAVA 1.8
  • Gradle 7.3.2
  • IntelliJ
์ •์‚ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์—‘์…€ํŒŒ์ผ๋กœ ๋งŒ๋“œ๋Š” ๊ณผ์ •์—์„œ ์ ์€ ๊ฑด์ˆ˜๋ฅผ ๋งŒ๋“ค๋•Œ๋Š” ์ด์Šˆ๊ฐ€ ์—†์—ˆ์ง€๋งŒ ๋ช‡์‹ญ๋งŒ ๊ฑด ๋˜๋Š” ๋ช‡๋ฐฑ๋งŒ๊ฑด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ๋ฒˆ์— ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋ฉด์„œ OOM์„ ๋งŒ๋‚˜๊ฒŒ ๋˜์–ด ์—‘์…€ํŒŒ์ผ๋กœ ์ €์žฅ์„ ํ•  ์ˆ˜๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. MyBatis๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด MyBatis์—์„œ ์ œ๊ณตํ•˜๋Š” ResultHandler๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ช‡๋ฐฑ๋งŒ๊ฑด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ๋ฒˆ์— ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ๋ ˆ์ฝ”๋“œ๋ณ„๋กœ ๋‹ค๋ฃฐ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
https://mybatis.org/mybatis-3/ko/java-api.html#SqlSession (์•„๋ž˜ ResultHandler ์ฐธ๊ณ )
๋‹ค์Œ์€ ๊ฐ€์ง€๊ณ  ์˜จ ๋ฐ์ดํ„ฐ์˜ ์ €์žฅ์€ Apache POI SXSSF ๋ฐฉ์‹์„ ์ด์šฉํ•˜์—ฌ ์—‘์…€ํŒŒ์ผ์— ์ €์žฅํ•˜์˜€์Šต๋‹ˆ๋‹ค. SXSSF๋Š” Streaming๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ๊ณ„์† ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๊ณ  ์ž„์‹œ ํŒŒ์ผ์— ๊ธฐ๋กํ•œ ํ›„ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋น„์›Œ๋‚ด๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์ž„์‹œํŒŒ์ผ์€ ๊ธฐ๋ณธ ๊ฒฝ๋กœ๋Š” ๋ฆฌ๋ˆ…์Šค ์„œ๋ฒ„ ๊ธฐ์ค€์œผ๋กœ /var/tmp/poi??? ๋งฅ๋ถ๊ธฐ์ค€ ํ„ฐ๋ฏธ๋„์—์„œ echo $TMPDIR๋ฅผ ์ณ์„œ ์ž„์‹œ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ๋กœ ๋“ค์–ด๊ฐ€์„œ poi??? ๋””๋ ‰ํ† ๋ฆฌ ๋“ค์–ด๊ฐ€์„œ ํ™•์ธํ•ด๋ณด๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ž„์‹œํŒŒ์ผ ์šฉ๋Ÿ‰์€ ๋ฐ์ดํ„ฐ ์••์ถ•์˜ต์…˜์„ ์•ˆ์ฃผ์—ˆ์„ ๊ฒฝ์šฐ 100๋งŒ๊ฑด๋‹น 2G๊ฐ€ ์ •๋„์˜€๊ณ  ์••์ถ•์˜ต์…˜์„ ์ฃผ์—ˆ์„ ๊ฒฝ์šฐ 256M๊ฐ€ ์ •๋„์˜€์Šต๋‹ˆ๋‹ค. ์••์ถ•ํ•˜๋ฉด ์—‘์…€๋งŒ๋“ค์–ด์ง€๋Š” ์†๋„๊ฐ€ ๋Š๋ฆด๊ฒƒ ๊ฒƒ๊ฐ™์•˜์ง€๋งŒ ๊ฑฐ์˜ ์ฐจ์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„ ๊ธฐ์ค€์œผ๋กœ ์šฉ๋Ÿ‰ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ• ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ž„์‹œํŒŒ์ผ์ด ๋งŒ๋“ค์–ด์ง€๋Š” ๊ฒฝ๋กœ๋„ ์ง€์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
https://poi.apache.org/components/spreadsheet/

POI ์“ฐ๊ธฐ ํ™•์ธ

HSSF ์—‘์…€ ์ด์ „๋ฒ„์ ผ(.xls) 65535๋ผ์ธ๊นŒ์ง€ ๊ฐ€๋Šฅ
XSSF ์—‘์…€ 2007 ์ด์ƒ๋ฒ„์ ผ(.xls)  65535๋ผ์ธ ์ด์ƒ ๊ฐ€๋Šฅ
SXSSF XSSF์˜ Streaming Usermodel API 65535๋ผ์ธ ์ด์ƒ ๊ฐ€๋Šฅ

์—‘์…€ ์‹œํŠธ ์ตœ๋Œ€ ํ–‰์ˆ˜ ํ™•์ธ

xls 65535 ํ–‰
xlsx 1,048,576 ํ–‰

MyBatis์˜ ResultHandler ์…‹ํŒ…

TestDao

@RequiredArgsConstructor
@Repository
public class TestDAOImpl implements TestDAO {

    private final SqlSessionTemplate sqlSessionTemplate;

    @Override
    public boolean createExcelByTmpTableMap(TmpTableSchCmd tmpTableSchCmd, TestExcelHandler testExcelHandler) {
        sqlSessionTemplate.select("com.sample.batch.dao.TestDAO.selectMapOfTmpTable", tmpTableSchCmd, testExcelHandler);
        testExcelHandler.createExcelByTmpFile();
        return testExcelHandler.isSuccess();
    }
}

mybatis sqlsession

์šฐ์„  MyBatis์—์„œ ResultHandler๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ sqlsessionTemplate์˜ selectํ•จ์ˆ˜์— ๋“ค์–ด๊ฐ€ ์‚ดํŽด๋ณด๋ฉด ์œ„์™€ ๊ฐ™์ด ResultHandler๋ผ๋Š” ๊ฒƒ์„ ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ฐ›๋Š” ๊ฒƒ์„ ํ™•์ธ ํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” ResultHandler๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฐ์ฒด๋ฅผ ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. (์นœ์ ˆํžˆ ์ฃผ์„์œผ๋กœ ResultHandler๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ‚ค์™€ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฌธ์— ๋งคํ•‘๋œ ๋‹จ์ผ ํ–‰์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค) ์—ฌ๊ธฐ์„œ ํ•œ๋ฒˆ๋” ResultHandler์˜ ํด๋ž˜์Šค๋„ ๋”ฐ๋ผ ๋“ค์–ด๊ฐ€ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

interface๋กœ ์„ ์–ธ๋˜์–ด์žˆ๊ณ  generic ํƒ€์ž…์œผ๋กœ ๋˜์–ด์žˆ๋Š”๊ฑธ ๋ณด๋‹ˆ MyBatis์— resultType์— ์…‹ํŒ… ๋œ ๊ฐ์ฒด๊ฐ€ ๋ฆฌํ„ด๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ResultHandler๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด MyBatis์—์„œ ResultHandler๋ถ€๋ถ„์˜ ์—ญํ• ์€ ๋์ž…๋‹ˆ๋‹ค

์ฆ‰ selectํ•จ์ˆ˜์— ResultHandler๋ฅผ ๊ตฌํ˜„ํ•œ class๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด handlerResult์— resultType์˜ object๊ฐ€ ๋ฆฌํ„ด๋ฉ๋‹ˆ๋‹ค

@Slf4j
public abstract class ExcelResultHandler implements ResultHandler {

    protected SXSSFWorkbook sxssfWorkbook;
    protected SXSSFSheet sxssfSheet;

    // row = xls:65,535 xlsx:1,048,576
    protected final int MAX_SHEET_ROW = 1000000;

    private FlushControl flushControl;
    protected int sheetRowCnt = 1;

    protected ExcelFileInfo excelFileInfo;


    public ExcelResultHandler(ExcelFileInfo excelFileInfo, FlushControl flushControl) {
        this.excelFileInfo = excelFileInfo;
        this.flushControl = flushControl;
        // 0. ์›Œํฌ๋ถ ์ƒ์„ฑ
        this.sxssfWorkbook = new SXSSFWorkbook(flushControl.getRowAccessWindowSize()); // rowAccessWindowSize๊ฐœ์˜ ๋กœ์šฐ๋งŒ ๋ฉ”๋ชจ๋ฆฌ ๋ณด์œ , ๋‚˜๋จธ์ง€ ๋””์Šคํฌ๋กœ ๋‚ด๋ณด๋‚ธ๋‹ค.
        sxssfWorkbook.setCompressTempFiles(true);                    // ์ž„์‹œํŒŒ์ผ ์••์ถ•์—ฌ๋ถ€
    }
 ... ์ค‘๊ฐ„ ์ƒ๋žต
 
    /**
     * rowCnt๋Š” 1๋ถ€ํ„ฐ ์‹œ์ž‘ํ•จ
     */
    public abstract void createExcelBody(int rowCnt, Object dbData);

    @Override
    public void handleResult(ResultContext resultContext) {
        createExcelBody(resultContext.getResultCount(), resultContext.getResultObject());
    }

ResultHandler๋ฅผ ๋ฐ”๋กœ class์— ๋ฐ›์•„์„œ ๊ตฌํ˜„ํ•ด์ค˜๋„ ๋˜์ง€๋งŒ abstract๋กœ ๋ฐ›์•„์„œ ๊ธฐ๋ณธ์ ์ธ ๊ณตํ†ต ๋ถ€๋ถ„ (POI ์›Œํฌ๋ถ์ƒ์„ฑ, ์‹œํŠธ์ƒ์„ฑ, ํ—ค๋”์ƒ์„ฑ) ๋“ฑ์„ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€๊ณ  @Override๋˜์–ด์žˆ๋Š” handlerResult์— createExcelBody์˜ ํ•จ์ˆ˜๋งŒ ์ถ”์ƒํด๋ž˜์Šค๋กœ ํ•˜์—ฌ ์ƒ์†๋ฐ›๋Š” class๊ฐ€ ๊ตฌํ˜„ํ•˜๊ฒŒ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

@Slf4j
public class TestExcelHandler extends ExcelResultHandler {

    public TestExcelHandler(ExcelFileInfo excelFileInfo, FlushControl flushControl) {
        super(excelFileInfo, flushControl);
    }

    @Override
    public void createExcelBody(int rowCnt, Object dbData) {
        //์›Œํฌ๋ถ์ƒ์„ฑ -> ์‹œํŠธ์ƒ์„ฑ -> ์‰˜ ํƒ€์ดํ‹€์ƒ์„ฑ
        if(rowCnt % MAX_SHEET_ROW == 1) {
            createSXSSFSheet((rowCnt/MAX_SHEET_ROW)+1);
            //HashMap ์š”๋ถ€๋ถ„์€ resultType์œผ๋กœ castingํ•˜์„ธ์š” ์ €๋Š” map
            createCellTitle((HashMap<String, Object>)dbData);
            //์ƒˆ๋กœ์šด ์‹œํŠธ์˜ ROW๋Š” 1๋ถ€ํ„ฐ ์‹œ์ž‘
            sheetRowCnt = 1;
        }
        //ROW ์ƒ์„ฑ
        createDataCell((HashMap<String, Object>)dbData);

        //...์ค‘๊ฐ„์ค‘๊ฐ„์— flush
        flushMem(rowCnt);
    }

}

POI ์ž„์‹œํŒŒ์ผ ์šฉ๋Ÿ‰ ์••์ถ• ๋ฐ ๊ฒฝ๋กœ ์ˆ˜์ •

// 0. ์›Œํฌ๋ถ ์ƒ์„ฑ
this.sxssfWorkbook = new SXSSFWorkbook(flushControl.getRowAccessWindowSize()); // rowAccessWindowSize๊ฐœ์˜ ๋กœ์šฐ๋งŒ ๋ฉ”๋ชจ๋ฆฌ ๋ณด์œ , ๋‚˜๋จธ์ง€ ๋””์Šคํฌ๋กœ ๋‚ด๋ณด๋‚ธ๋‹ค.
sxssfWorkbook.setCompressTempFiles(true);                    // ์ž„์‹œํŒŒ์ผ ์••์ถ•์—ฌ๋ถ€

POI ์ž„์‹œํŒŒ์ผ์˜ ์••์ถ•์€ setCompressTempFiles(true)๋กœ ์„ค์ •ํ•˜๋ฉด gz์œผ๋กœ ์••์ถ•์ด ๋ฉ๋‹ˆ๋‹ค

๋ญ ์ด๊ฒƒ๋„  ํ•จ์ˆ˜ ๋”ฐ๋ผ๊ฐ€๋‹ค ๋ณด๋ฉด ๊ทธ๋ ‡๊ฒŒ ์ •์˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค ^^;;

์ž„์‹œํŒŒ์ผ๊ฒฝ๋กœ๋Š” new SXSSFWorkbook์˜ ํด๋ž˜์Šค๋ฅผ ๋”ฐ๋ผ์„œ ์ถ”์ ํ•˜๋‹ค๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด TempFIle.createTempFile ์ด๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

TempFIle.createTempFile์„ ๋˜ ์ซ“์•„๊ฐ€๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์œผ๋ฉฐ

strategy๋ฅผ ์ซ“์•„๊ฐ€๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๊ธฐ๋ณธ์œผ๋กœ DefaultTempFileCreationStrategy๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋„ค์š”

DefaultTempFileCreationStrategy๋ฅผ ๋ณด๋ฉด ์ƒ์„ฑ์ž์— ํŒŒ์ผ์˜ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ๋ณด์ž…๋‹ˆ๋‹ค

TempFile์˜ ๊ทธ ์•„๋ž˜๋ฅผ ๋ณด๋ฉด strategy๋ฅผ setTempFileCreationStrategy(TempFileCreationStrategy strategy)๊ฐ€ ์žˆ๋„ค์š”

์ด์ œ ํผ์ฆ์„ ๋งž์ถฐ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

TempFile์˜ .setTempFileCreationStrategy ํ•จ์ˆ˜์— TempFileCreationStrategy๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค DefaultTempFileCreationStrategy๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ๋˜๊ฒ ๋„ค์š” ๋”ฐ๋ผ์„œ DefaultTempFileCreationStrategy๋ฅผ ์ƒ์„ฑํ• ๋•Œ File Dir๋ฅผ ๋‚ด๊ฐ€ ์ƒ์„ฑํ•ด ์ง€์ •ํ•ด์ฃผ๊ณ  DefaultTempFileCreationStrategy๋ฅผ ์ƒ์„ฑํ•ด์„œ setํ•ด์ฃผ๋ฉด ํ•ด๊ฒฐ ๋˜๊ฒ ๋„ค์š”

/**
* org.apache.poi tmpํŒŒ์ผ ๊ฒฝ๋กœ ์ˆ˜์ •
*/
private void setTmpPath() {
	TempFile.setTempFileCreationStrategy(new DefaultTempFileCreationStrategy(new File(excelProperties.getTmpDirPath())));
}

์ด์ œ setTmpPathํ•จ์ˆ˜๋ฅผ POI ์›Œํฌ๋ถ์„ ์ƒ์„ฑํ•˜๊ธฐ์ „์— ๋ฏธ๋ฆฌ ์‹คํ–‰์„ ํ•˜๋ฉด ์ž„์‹œํŒŒ์ผ์€ ๋‚ด๊ฐ€ ์ง€์ •ํ•œ ๊ฒฝ๋กœ์— ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค

POI SXSSF ์ž๋™ or ์ˆ˜๋™ flush

์„œ๋‘์—์„œ ๋งํ–ˆ๋“ฏ์ด SXSSF๋Š” Streaming๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ๊ณ„์† ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๊ณ  ์„ค์ •๋œ ๊ฐ’์„ ๋„˜์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ์— ๋“ค๊ณ ์žˆ๋˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ž„์‹œ ํŒŒ์ผ์— ๊ธฐ๋กํ•œ ํ›„ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋น„์›Œ๋‚ด๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ฐ˜๋ณตํ•˜์—ฌ ๋งˆ์ง€๋ง‰๊นŒ์ง€ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ์ž„์‹œํŒŒ์ผ์„ ๋งˆ์ง€๋ง‰์—๋Š” ์„ค์ •ํ•œ ์—‘์…€ํŒŒ์ผ๋กœ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋น„์›Œ๋‚ผ๋•Œ ์ž๋™์œผ๋กœ ์…‹ํŒ…์„ ํ• ์ˆ˜ ์žˆ๊ณ  ์•„๋‹ˆ๋ฉด ์ˆ˜๋™์œผ๋กœ ์„ค์ •ํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

	//์ž๋™ ์ƒ์„ฑํ• ๋•Œ ์ง€์ •ํ•œ ๊ฐ’(100)์— ๋„๋‹ฌํ•˜๋ฉด flush
	this.sxssfWorkbook = new SXSSFWorkbook(100);


	//์ˆ˜๋™์ผ๋•Œ๋Š” -1์ด๋ฉด ๋ฌด์ œํ•œ ์•ก์„ธ์Šค์ด๊ณ  ์ค‘๊ฐ„์ค‘๊ฐ„์— ์ˆ˜๋™์œผ๋กœ flushํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
	this.sxssfWorkbook = new SXSSFWorkbook(-1);

    /**
     *  rowAccessWindowSize -1์ด๋ฉด ์ˆ˜๋™ flush์ž‘๋™
     */
    protected void flushMem(int rowCnt) {
        if(flushControl.getRowAccessWindowSize() == -1 && rowCnt % flushControl.getManualFlushSize() == 1) {
            try {
            	//100 ์Œ“์ด๋ฉด flush
                ((SXSSFSheet)sxssfSheet).flushRows(100);
                log.info("[Excel] flushMem {}", rowCnt);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

์œ„์˜ ๋‚ด์šฉ์€ https://poi.apache.org/components/spreadsheet/how-to.html#sxssf ์—ฌ๊ธฐ์— ์ž˜ ๋‚˜์™€์žˆ์Šต๋‹ˆ๋‹ค

 

ํ…Œ์ŠคํŠธ๋กœ ๋งŒ๋“ค์–ด๋ณธ ์†Œ์Šค

๊นƒํ—™ : https://github.com/moyanada/bigsize_excel_sample

๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
ยซ   2024/12   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
๊ธ€ ๋ณด๊ด€ํ•จ
๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (80)
๐Ÿ“บ Develop (0)
๐ŸŒ‹ Error Fixed (5)
๐Ÿ— Tool (5)
๐Ÿ’ป MacBook M1 (15)
๐Ÿ“ฆ ETC (1)

์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค.