프로젝트

[텀프로젝트] 파일 압축&암호화 유틸리티 개발 - 2 (압축 기능 구현)

poiri3r 2025. 12. 11. 21:22

안녕하세요 오늘 포스팅에서는 먼저 압축 기능을 구현해보도록 하겠습니다.

압축 기능을 구현하기 전에 있어서 코드 구현에 있어서 편의성을 제공하기 위한 기능을 몇 가지 생각해보았습니다.

 

처음에는 프로그램을 실행하면 압축 or 압축해제 기능을 고르는 메뉴를 만드려고 했는데 두가지 대안이 떠올랐습니다.

1. 파일 선택 시 파일명에 .Encrypted 이 있을경우 자동으로 복호화 진행, 그 외의 경우엔 압축+암호화 진행

2. 파일 헤더의 맨 앞에 Signiture 값 추가, 파일을 읽을때 첫 바이트를 읽은 뒤 Signiture 값 검사

 

구현은 1번이 더 쉬울 것 같긴한데, 조금 더 완성된 프로그램에는 2번이 가까울 것 같다는 생각을 했습니다.

 

다음은 압축 기능 구현입니다. 압축/압축 해제를 지원하는 라이브러리를 찾아보았는데, 아래와 같은 라이브러리가 있었습니다.

라이브러리 장점 단점
zlib 제일 많이 쓰이는 표준, OpenSSL도 내부적으로 zlib을 사용하여, 의존성 관리가 편함, 안정성이 보장됨 최신 알고리즘에 비해 속도가 떨어짐
Zstandard Facebook에서 만든 최신 알고리즘으로 zlib보다 빠르고 압축률이 더 좋음. 유연성이 높아 압축 레벨 조절 범위가 넓음 빌드 및 링킹이 복잡함, 작은 데이터에는 오버헤드가 발생
LZ4 압축률보다는 속도에 중점을 둔 라이브러리, CPU를 거의 쓰지 않고 빠르게 압축함 파일 용량이 크게 줄지 않음

 

셋 중에서 zlib으로 결정을 했는데, 이유는 다음과 같습니다.

  1. OpenSSL을 사용하므로 호환성이 좋음
  2. 개인적으로 보관하는 파일이 pdf나 한글파일 등의 크기가 작은 파일이기 때문에 Zstandard는 오버헤드 발생 가능성이 있음
  3. 개인 과제용 텀프로젝트이기 때문에, 성능보단 안정성에 초점을 맞춤

압축을 하는 함수는 perform_compress로 이름을 정해뒀습니다.

압축의 큰 과정은 다음과 같습니다.

1. 안전한 버퍼 크기 계산

2. 계산한 크기만큼의 메모리를 Heap영역에 할당

3. 압축 : zlib는 compress()라는 함수를 제공 (인자로는 결과 버퍼, &결과 버퍼 길이, 원본 버퍼, 원본 길이가 들어감)

 

압축 기능은 다음과 같이 작성했습니다.

//압축 기능 구현 (zlib)
unsigned char* perform_compress(const unsigned char* src, long src_len, long* out_len) {
    //1. 압축 후 최대 예상 크기 계산 (buff에 할당)
    uLongf dest_len = compressBound((uLong)src_len);
    //2. 결과를 담을 메모리 할당
    unsigned char* dest = (unsigned char*)malloc(dest_len);
    if (dest == NULL) {
        printf("메모리 할당 실패(압축)");
        return NULL;
    }
    //3. 실제 압축 수행
    int res = compress(dest, &dest_len, src, (uLong)src_len);

    //4. 결과 확인
    if (res != Z_OK) { //Z_OK는 압축 성공을 의미
        printf("zlib 압축 실패. Error code : %d\n", res);
        free(dest);
        return NULL;
    }
    //5. 성공 시, 실제 줄어든 출력 변수에 저장하고 버퍼 반환
    *out_len = (long)dest_len;
    return dest;
}

 

일단 압축과 압축한 파일을 메모리상의 버퍼에 저장하는 기능까지 구현했습니다.

기존의 process_file에는 다음과 같이 구현했습니다.

//압축 수행 파트
long compressed_len = 0;
unsigned char* compressed_data = perform_compress(file_data, file_len, &compressed_len);

if (compressed_data != NULL) {
    printf("\n 압축 성공!\n");
    printf(" 원본 크기 : %ld bytes \n", file_len);
    printf(" 압축 크기 : %ld bytes \n", compressed_len);

    //TODO : 여기서 암호화 함수 호출
    free(compressed_data);
}
free(file_data); // 사용 후 메모리 해제

 

해당 코드를 수행하고 아무 파일이나 골라줬습니다.

오류는 나지 않지만, 압축된 버퍼를 저장하지 않기 때문에 파일이 따로 생성되진 않습니다.

이제 파일을 저장하는 기능을 구현해보겠습니다.

버퍼에 담겨있는 파일을 저장하는 기능을 먼저 구현하고, 그 후에 버퍼에 담긴 압축 파일을 암호화/복호화하는 로직을 만들면 텀프로젝트가 매끄럽게 진행될 것 같습니다.

 

저장 기능은 rb말고 wb모드로 (write binary) 열어야하기 때문에 함수를 새로 만들어서 구현하겠습니다.

함수 이름은 save_file로 만들었습니다.

void save_file(const char* path, const unsigned char* data, long size, const char* original_path) {
    // 1. 쓰기 모드로 파일을 열어야됨
    FILE* f = fopen(path, "wb");

    if (f == NULL) {
        printf(" 파일을 저장할 수 없습니다 : %s\n", path);
        return;
    }
    // 2. 데이터 쓰기(fwrite)
    fwrite(data, 1, size, f);

    //3. 파일 닫기
    fclose(f);

    printf("파일 저장 완료: %s\n", path);

    //원본 파일 삭제 로직
    //original_path가 NULL이 아니고 저장된 파일이 존재할 때 실행
    if (original_path != NULL && fs::exists(path)) {

        // 원본 삭제 시도
        try {
            if (fs::remove(original_path)) {
                printf("[Success] 원본 파일이 삭제되었습니다: %s\n", original_path);
            }
            else {
                printf("[Warning] 원본 파일을 삭제하지 못했습니다 (이미 없거나 권한 부족).\n");
            }
        }
        catch (const fs::filesystem_error& err) {
            printf("[Error] 파일 삭제 중 에러 발생: %s\n", err.what());
        }
    }
}

 

쓰다보니까 좀 길어졌는데, 함수 저장 기능을 구현하고 나니 좀 길어진 느낌이 있는데, 파일을 저장하고 원본 경로를 삭제해야하고, 삭제과정에서는 예외처리를 좀 신경써서, 압축이 실패했을때에는 원본 파일이 유지되게 파일을 만들었습니다.

프로그램을 실행해보니 다음과 같이 정상적으로 실행됩니다.

이제 추가로 구현해야 하는 부분은, 암호화와 복호화, 압축해제입니다.

꽤 많이 만들었다고 생각했는데 아직 할게 많이 남았네요.

생각보다 기능들에 대한 세부 기능들이 많이 필요해서 코드가 점점 길어지는 것 같습니다.

마지막으로 확장자가 poir인지 검사하는 로직만 추가하겠습니다. if else로만 덮으면 됩니다.

std::string ext = p.extension().string();

printf("\n[Log] Processing file: %s\n", path);

// 2. 확장자에 따른 분기 처리
if (ext == ".poir") {
    printf(" >> 복호화 & 압축 해제 실행\n");

    // TODO: 나중에 여기에 복호화 및 압축해제 기능 구현
}
else {
    printf(" >> 압축 & 암호화 실행.\n");

 

이러면 일단 1차적인 틀은 맞춘 것 같은데, 스켈레톤 코드 작성을 안해서 그런가 점점 코드가 길어지고 보기가 힘들어지네요..

그리고 소스코드랑 메인 코드에 대한 분리가 필요할 것 같은데 이것도 고민을 해봐야 할 것 같습니다.

추가로 암호화,복호화,압축해제를 구현해야 하는데, 암호화에서 패스워드를 어떻게 입력할지에 대한 고민도 좀 필요한 것 같습니다.

막연히 key값에 넣으면 될것이라고 생각을 했는데, password에 대한 검증은 어떻게 할건지, 틀린 값을 넣었을 경우  어떻게 하는지 이런 경우의 수를 조금 더 생각해봐야 될 것 같습니다.

 

일단 이상으로 2번째 개발일지 작성을 마치겠습니다. 읽어주셔서 감사합니다.