이미지 파일을 DB에 저장하는 방식
- 이미지 파일 → 바이너리 데이터(바이트 배열) 변환 → DB의 BLOB 타입 컬럼에 저장
- 이미지 조회 시 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바이트 크기+이미지 데이터) 유지하면 그대로 사용 가능