2026년 1월 21일 수요일

dhx lib

 // myGrid는 생성된 dhtmlXGrid 객체입니다.

myGrid.attachEvent("onEditCell", function(stage, rId, cInd, nValue, oValue){

    

    // stage 2: 셀 편집이 완료되어 값이 데이터베이스/그리드에 반영된 직후

    if(stage === 2 && nValue !== oValue){

        

        // 1. 변화를 감지할 특정 컬럼 인덱스 확인 (예: 1번 컬럼이 수량일 때)

        if(cInd === 1){

            var quantity = nValue; // 변경된 새로운 값

            var price = myGrid.cells(rId, 2).getValue(); // 2번 컬럼(단가) 값 가져오기

            

            // 2. 계산 로직 수행

            var total = quantity * price;

            

            // 3. 동적으로 변경할 컬럼에 값 세팅 (예: 3번 컬럼이 총합일 때)

            myGrid.cells(rId, 3).setValue(total);

            

            // 4. (선택사항) 변경된 셀의 색상 등을 바꿔 시각적 피드백 제공

            myGrid.cells(rId, 3).setBgColor("#e6f7ff");

        }

    }

    return true; // 반드시 true를 반환해야 편집 내용이 반영됩니다.

});


2026년 1월 14일 수요일

zstd redis 처리

 // 저장 시

byte[] data = originalContent.getBytes(StandardCharsets.UTF_8);

int originalLength = data.length;

byte[] compressed = Zstd.compress(data);


// [4바이트(길이) + 압축데이터] 형태로 병합

ByteBuffer buffer = ByteBuffer.allocate(4 + compressed.length);

buffer.putInt(originalLength);

buffer.put(compressed);

jedis.set(key.getBytes(), buffer.array());


// 조회 시

byte[] raw = jedis.get(key.getBytes());

ByteBuffer wrapped = ByteBuffer.wrap(raw);

int length = wrapped.getInt(); // 앞의 4바이트 읽기

byte[] compressedPart = new byte[raw.length - 4];

wrapped.get(compressedPart);   // 나머지 데이터 읽기


byte[] decompressed = Zstd.decompress(compressedPart, length);


2026년 1월 13일 화요일

z standard 압축해제

 import com.github.luben.zstd.Zstd;

import java.io.*;

import java.nio.charset.StandardCharsets;


public class ZstdFileProcessor {


    /**

     * 파일을 읽어 라인별로 Zstd 압축하여 저장 (기존 파일 초기화)

     */

    public void compressFile(String inputPath, String outputPath) {

        try (BufferedReader reader = new BufferedReader(new FileReader(inputPath));

             DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outputPath, false)))) {


            String line;

            while ((line = reader.readLine()) != null) {

                byte[] data = line.getBytes(StandardCharsets.UTF_8);

                

                // Zstd.compress()는 기본 압축 레벨 3을 사용하며, 속도와 압축률의 균형이 가장 좋습니다.

                byte[] compressed = Zstd.compress(data);


                // [원본길이][압축데이터길이][압축데이터] 순으로 기록

                dos.writeInt(data.length);

                dos.writeInt(compressed.length);

                dos.write(compressed);

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

    }


    /**

     * Zstd로 압축된 파일을 읽어 라인별로 해제

     */

    public void decompressFile(String inputPath) {

        try (DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(inputPath)))) {

            while (dis.available() > 0) {

                int originalLen = dis.readInt();

                int compressedLen = dis.readInt();


                byte[] compressed = new byte[compressedLen];

                dis.readFully(compressed);


                // 원본 길이를 알고 있으므로 정확한 크기의 버퍼로 바로 해제 (가장 빠른 방식)

                byte[] decompressed = Zstd.decompress(compressed, originalLen);


                String line = new String(decompressed, StandardCharsets.UTF_8);

                // 비즈니스 로직 수행

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}


lz4 압축 해제

 import net.jpountz.lz4.*;

import java.io.*;

import java.nio.charset.StandardCharsets;


public class Lz4FileProcessor {

    private static final LZ4Factory factory = LZ4Factory.fastestInstance();

    private static final LZ4Compressor compressor = factory.fastCompressor();

    private static final LZ4SafeDecompressor decompressor = factory.safeDecompressor();


    /**

     * 파일을 읽어 라인별로 LZ4 압축하여 저장 (기존 파일 초기화)

     */

    public void compressFile(String inputPath, String outputPath) {

        try (BufferedReader reader = new BufferedReader(new FileReader(inputPath));

             DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outputPath, false)))) {


            String line;

            while ((line = reader.readLine()) != null) {

                byte[] data = line.getBytes(StandardCharsets.UTF_8);

                int maxLen = compressor.maxCompressedLength(data.length);

                byte[] compressed = new byte[maxLen];

                

                // 실제 압축된 길이 반환

                int compressedLen = compressor.compress(data, 0, data.length, compressed, 0, maxLen);


                // [원본길이][압축데이터길이][압축데이터] 순으로 기록

                dos.writeInt(data.length);

                dos.writeInt(compressedLen);

                dos.write(compressed, 0, compressedLen);

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

    }


    /**

     * LZ4로 압축된 파일을 읽어 라인별로 해제

     */

    public void decompressFile(String inputPath) {

        try (DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(inputPath)))) {

            while (dis.available() > 0) {

                int originalLen = dis.readInt();

                int compressedLen = dis.readInt();


                byte[] compressed = new byte[compressedLen];

                dis.readFully(compressed);


                byte[] decompressed = new byte[originalLen];

                // 압축 해제 수행

                decompressor.decompress(compressed, 0, compressedLen, decompressed, 0, originalLen);


                String line = new String(decompressed, StandardCharsets.UTF_8);

                // 비즈니스 로직 수행 (예: 로그 분석 등)

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}


zip으로 압출/해제하기

 import java.io.*;

import java.util.zip.*;

import java.nio.charset.StandardCharsets;


public class DeflaterProcessor {


    /**

     * 파일을 읽어 라인별로 Deflater 압축하여 저장

     */

    public void compressFile(String inputPath, String outputPath) {

        // 'false'로 설정하여 기존 파일이 있으면 내용을 지우고 새로 생성

        try (BufferedReader reader = new BufferedReader(new FileReader(inputPath));

             DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outputPath, false)))) {


            String line;

            Deflater deflater = new Deflater();

            byte[] buffer = new byte[1024];


            while ((line = reader.readLine()) != null) {

                byte[] data = line.getBytes(StandardCharsets.UTF_8);

                deflater.setInput(data);

                deflater.finish();


                // 압축 수행 및 결과물을 임시로 담을 바이트 배열 생성

                ByteArrayOutputStream baos = new ByteArrayOutputStream();

                while (!deflater.finished()) {

                    int count = deflater.deflate(buffer);

                    baos.write(buffer, 0, count);

                }

                deflater.reset();


                byte[] compressedData = baos.toByteArray();


                // [원본길이][압축데이터길이][압축데이터] 순으로 저장

                dos.writeInt(data.length);

                dos.writeInt(compressedData.length);

                dos.write(compressedData);

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

    }


    /**

     * 압축된 파일을 읽어 라인별로 Inflater 해제

     */

    public void decompressFile(String inputPath) {

        try (DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(inputPath)))) {

            Inflater inflater = new Inflater();

            

            while (dis.available() > 0) {

                int originalLen = dis.readInt();

                int compressedLen = dis.readInt();


                byte[] compressedData = new byte[compressedLen];

                dis.readFully(compressedData);


                byte[] decompressedData = new byte[originalLen];

                inflater.setInput(compressedData);

                inflater.inflate(decompressedData);

                inflater.reset();


                String line = new String(decompressedData, StandardCharsets.UTF_8);

                // System.out.println(line); // 복구된 라인 출력

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}


resulthandler 사용방식

 import org.apache.ibatis.session.ResultContext;

import org.apache.ibatis.session.ResultHandler;

import org.apache.ibatis.session.SqlSession;

import java.io.*;

import java.nio.charset.StandardCharsets;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;


public class LargeDataExportService {


    private SqlSessionFactory sqlSessionFactory;


    public void exportDataWithBatch(String filePath) {

        // 1. 파일 초기화 (기존 파일이 있으면 덮어쓰기 위해 false, 이후 true로 append)

        try (SqlSession session = sqlSessionFactory.openSession()) {

            

            // MyBatis ResultHandler 구현

            session.select("com.example.mapper.ProductMapper.selectLargeData", new ResultHandler<Map<String, Object>>() {

                private final List<Map<String, Object>> batchList = new ArrayList<>(1000);

                private boolean isFirstWrite = true;


                @Override

                public void handleResult(ResultContext<? extends Map<String, Object>> resultContext) {

                    batchList.add(resultContext.getResultObject());


                    // 1,000건이 쌓이면 파일에 씀

                    if (batchList.size() >= 1000) {

                        flushBatch(batchList, filePath, !isFirstWrite);

                        isFirstWrite = false;

                        batchList.clear();

                    }

                }

                

                // 마지막에 남은 데이터 처리 (예: 1000건이 안 되는 나머지)

                public void flushRemaining() {

                    if (!batchList.isEmpty()) {

                        flushBatch(batchList, filePath, !isFirstWrite);

                    }

                }

            });

            

        } catch (Exception e) {

            e.printStackTrace();

        }

    }


    private void flushBatch(List<Map<String, Object>> data, String filePath, boolean append) {

        // 2. BufferedWriter의 append 모드 활용

        try (FileOutputStream fos = new FileOutputStream(filePath, append);

             OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);

             BufferedWriter writer = new BufferedWriter(osw)) {


            for (Map<String, Object> row : data) {

                // 가공 로직 (압축 등) 수행 후 쓰기

                String processedLine = processRow(row); 

                writer.write(processedLine);

                writer.newLine();

            }

            writer.flush();

        } catch (IOException e) {

            throw new RuntimeException("파일 쓰기 중 오류 발생", e);

        }

    }


    private String processRow(Map<String, Object> row) {

        // 여기서 아까 논의한 Zstd/LZ4 압축 로직을 넣으시면 됩니다.

        return row.get("ID").toString() + "," + row.get("NAME").toString();

    }

}


압축 해제 thread local 사용

 import net.jpountz.lz4.*;

import com.github.luben.zstd.Zstd;

import java.nio.charset.StandardCharsets;


public class CompressedRedisClient {

    // 1. LZ4 공용 팩토리 (Thread-safe)

    private static final LZ4Factory lz4Factory = LZ4Factory.fastestInstance();

    private static final LZ4Compressor lz4Compressor = lz4Factory.fastCompressor();

    private static final LZ4SafeDecompressor lz4Decompressor = lz4Factory.safeDecompressor();


    // 2. ThreadLocal을 이용한 재사용 버퍼 (64KB 기준, 필요시 자동 확장 가능)

    private static final ThreadLocal<byte[]> BUFFER_CACHE = ThreadLocal.withInitial(() -> new byte[64 * 1024]);


    private static byte[] getBuffer(int size) {

        byte[] buf = BUFFER_CACHE.get();

        if (buf.length < size) {

            buf = new byte[size];

            BUFFER_CACHE.set(buf);

        }

        return buf;

    }


    /**

     * LZ4 압축 (Thread-local 버퍼 활용)

     */

    public byte[] compressLZ4(String input) {

        byte[] data = input.getBytes(StandardCharsets.UTF_8);

        int maxLen = lz4Compressor.maxCompressedLength(data.length);

        

        // 버퍼 재사용

        byte[] outputBuffer = getBuffer(maxLen);

        int compressedLen = lz4Compressor.compress(data, 0, data.length, outputBuffer, 0, maxLen);


        // 실제 압축된 크기만큼만 복사해서 반환 (레디스 저장용)

        byte[] result = new byte[compressedLen];

        System.arraycopy(outputBuffer, 0, result, 0, compressedLen);

        return result;

    }


    /**

     * Zstd 압축 해제 (Thread-local 버퍼 활용)

     */

    public String decompressZstd(byte[] compressed, int originalLen) {

        // 원본 크기에 맞는 버퍼 확보

        byte[] decompressedBuffer = getBuffer(originalLen);

        

        // Zstd 해제 (버퍼에 직접 씀)

        Zstd.decompress(decompressedBuffer, compressed);


        return new String(decompressedBuffer, 0, originalLen, StandardCharsets.UTF_8);

    }

}