카테고리 없음

바이너리 이미지 DB에 저장해서 쓰기

joo_coding 2025. 6. 18. 21:32

이미지 파일을 DB에 저장하는 방식

  1. 이미지 파일 → 바이너리 데이터(바이트 배열) 변환 → DB의 BLOB 타입 컬럼에 저장
  2. 이미지 조회 시 DB에서 BLOB 데이터를 꺼내서 서버가 바이너리로 클라이언트에 전송

MySQL(BLOB) 기반 저장 방식 예시

CREATE TABLE store_images (
    ST_ID INT PRIMARY KEY,
    IMAGE_DATA LONGBLOB NOT NULL
);
 
  • LONGBLOB 타입은 대용량 바이너리 데이터를 저장할 수 있음

C++ MySQL 바이너리 데이터 저장 예시

 
bool save_image_to_db(MYSQL* conn, int st_id, const std::string& image_path) {
    FILE* file = fopen(image_path.c_str(), "rb");
    if (!file) {
        std::cerr << "이미지 파일 열기 실패: " << image_path << std::endl;
        return false;
    }
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    rewind(file);

    char* buffer = new char[file_size];
    fread(buffer, 1, file_size, file);
    fclose(file);

    // Prepare statement (예시, 실제로는 SQL 인젝션 방지 위해 PrepareStatement 권장)
    std::string query = "REPLACE INTO store_images (ST_ID, IMAGE_DATA) VALUES (?, ?)";

    MYSQL_STMT* stmt = mysql_stmt_init(conn);
    if (!stmt) {
        std::cerr << "mysql_stmt_init 실패" << std::endl;
        delete[] buffer;
        return false;
    }

    if (mysql_stmt_prepare(stmt, query.c_str(), query.length())) {
        std::cerr << "mysql_stmt_prepare 실패: " << mysql_stmt_error(stmt) << std::endl;
        mysql_stmt_close(stmt);
        delete[] buffer;
        return false;
    }

    MYSQL_BIND bind[2];
    memset(bind, 0, sizeof(bind));

    // ST_ID 바인딩
    bind[0].buffer_type = MYSQL_TYPE_LONG;
    bind[0].buffer = (char*)&st_id;
    bind[0].is_null = 0;
    bind[0].length = 0;

    // IMAGE_DATA 바인딩
    bind[1].buffer_type = MYSQL_TYPE_BLOB;
    bind[1].buffer = buffer;
    bind[1].buffer_length = file_size;
    bind[1].is_null = 0;
    unsigned long length = file_size;
    bind[1].length = &length;

    if (mysql_stmt_bind_param(stmt, bind)) {
        std::cerr << "mysql_stmt_bind_param 실패: " << mysql_stmt_error(stmt) << std::endl;
        mysql_stmt_close(stmt);
        delete[] buffer;
        return false;
    }

    if (mysql_stmt_execute(stmt)) {
        std::cerr << "mysql_stmt_execute 실패: " << mysql_stmt_error(stmt) << std::endl;
        mysql_stmt_close(stmt);
        delete[] buffer;
        return false;
    }

    mysql_stmt_close(stmt);
    delete[] buffer;
    return true;
}

이미지 조회 및 클라이언트 전송 예시

bool get_image_from_db(MYSQL* conn, int st_id, std::vector<unsigned char>& image_data) {
    std::string query = "SELECT IMAGE_DATA FROM store_images WHERE ST_ID = " + std::to_string(st_id);

    if (mysql_query(conn, query.c_str())) {
        std::cerr << "쿼리 실패: " << mysql_error(conn) << std::endl;
        return false;
    }

    MYSQL_RES* res = mysql_store_result(conn);
    if (!res) return false;

    MYSQL_ROW row = mysql_fetch_row(res);
    if (!row || !row[0]) {
        mysql_free_result(res);
        return false;
    }

    unsigned long* lengths = mysql_fetch_lengths(res);
    image_data.resize(lengths[0]);
    memcpy(image_data.data(), row[0], lengths[0]);

    mysql_free_result(res);
    return true;
}

// 서버에서 클라이언트에 보내는 부분 예시
bool send_image_data(int client_sock, const std::vector<unsigned char>& image_data) {
    int32_t filesize = static_cast<int32_t>(image_data.size());

    // 4바이트 크기 전송 (리틀엔디안)
    uint8_t size_bytes[4];
    size_bytes[0] = filesize & 0xFF;
    size_bytes[1] = (filesize >> 8) & 0xFF;
    size_bytes[2] = (filesize >> 16) & 0xFF;
    size_bytes[3] = (filesize >> 24) & 0xFF;

    send(client_sock, size_bytes, 4, 0);
    send(client_sock, (const char*)image_data.data(), filesize, 0);

    return true;
}

장단점 및 고려사항

장점단점/고려사항
- 이미지 파일 관리가 DB로 통합되어 일관성 유지 - DB 크기 급증 가능성 (용량 증가 고려)
- 백업, 복원, 접근 권한 관리 일원화 가능 - DB에서 바이너리 읽고 쓰는 비용, 성능 저하 우려
- 이미지 경로 문제 없이 직접 데이터 제공 가능 - 대용량 이미지 트래픽 많으면 서버 부하 주의 필요
 

정리

  • 네, MySQL(BLOB)에 이미지를 저장하고 쓸 수 있습니다.
  • 서버에서 이미지 파일을 로컬에 저장하거나, DB에 저장하는 두 가지 방식 중 선택 가능
  • DB 저장 시 위 예시처럼 LONGBLOB 컬럼 생성 후 C++에서 읽고 쓰기 작업 필요
  • 클라이언트 통신은 바이너리 전송 프로토콜(4바이트 크기+이미지 데이터) 유지하면 그대로 사용 가능