웹페이지에 이미지를 넣기 위해서는 웹서버 혹은 별도의 이미지 서버에 파일 형식으로 업로드를 한 뒤, img src HTML 태그를 통해 웹페이지에 포함시키는 것이 일반적인 사용 방법이나, 이미지를 DB에 저장하고 저장된 이미지를 DB로부터 select하여 웹페이지를 통해 노출할 수 있는 방법이 있어 공유해드리고자 합니다.
테스트 환경
테스트 환경은 아래와 같습니다.
DB - AWS RDS(Mariadb 10)
웹서버 - apache 2.4(php 7.4)
데이터베이스 생성 및 테이블 생성
바이너리 데이터인 이미지 파일을 DB에 저장하기 위해 blob 데이터 타입으로 설정하여 테이블을 생성합니다. BLOB 이란 Binary large object 로 바이너리 데이터를 DB에 저장하기 위한 데이터 타입으로 이미지, 멀티미디어 등의 데이터를 저장할 수 있는 데이터 타입입니다. blob 데이터타입의 종류는 아래와 같이 TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB 으로 나뉘며, 각 타입에 따라 한 필드에 입력할 수 있는 최대 데이터 용량이 제한되어 있습니다. (예를 들어 BLOB 데이터 타입으로 생성한 필드에 50KB의 이미지는 입력이 가능하지만 100KB의 이미지는 입력이 불가능합니다)
아래와 같이 데이터베이스 및 테이블을 생성합니다.
create database image_test;
use image_test
CREATE TABLE imgs(
img_num int NOT NULL AUTO_INCREMENT PRIMARY KEY,
img_contents longblob
);
소스 이미지 다운로드
wget https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png -O google.png
이미지 파일을 DB에 인서트
이미지 파일을 DB에 인서트 하기 위한 php 소스를 작성합니다. 파일명은 insert.php 입니다.
<?php
$Picture="google.png";
$servername="RDS도메인";
$username="DB사용자";
$password="DB비밀번호";
$dbname="image_test";
$db = mysqli_connect($servername, $username, $password, $dbname);
$sql = "INSERT INTO imgs(img_num,img_contents) VALUES(null,'" . addslashes(file_get_contents($Picture)) . "')";
$qry = mysqli_query($db, $sql);
?>
php 파일을 로컬에서 실행하여 이미지를 DB에 인서트합니다.
php insert.php
이미지를 노출하기 위한 웹페이지 소스 작성
DB에 인서트한 이미지를 웹을 통해 확인할 수 있도록 웹페이지 소스를 작성합니다. 파일명은 select.php 입니다.
<?php
$servername="RDS도메인";
$username="DB사용자";
$password="DB비밀번호";
$dbname="image_test";
$db = mysqli_connect($servername, $username, $password, $dbname);
// 가장 마지막에 업로드한 이미지 1개만 표시하도록 작성했습니다
$sql = "SELECT * FROM imgs order by img_num desc limit 1";
$sth = $db->query($sql);
$result=mysqli_fetch_array($sth);
echo 'img_num '.$result['img_num'];
echo '';
// img src 태그에 파일 경로 대신 base64 포맷으로 인코딩된 dataURI를 불러오도록 작성했습니다
echo '<img src="data:image/png;base64,'.base64_encode( $result['img_contents'] ).'"/>';
?>
접속 테스트
브라우저에서 http://서버아이피/select.php 주소를 입력하여 접속테스트 진행합니다.
테스트 이미지의 dataURI는 아래와 같이 매우 길게 표시됩니다.
data:image/png;base64,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
일반적인 방식과 비교(통신 횟수)
일반적으로 웹서버 상에 있는 파일을 img src 태그로 첨부하여 페이지를 구성할 때의 통신 방식과 비교하여 아래와 같은 과정으로 페이지를 불러옵니다.
웹서버의 이미지 파일을 img src 태그로 포함시켰을 경우
클라이언트 -> (http://서버아이피/select.php 요청) -> 웹서버 -> (select.php 응답) -> 클라이언트 -> (소스에 포함된 http://서버아이피/google.png 요청) -> 웹서버 -> (google.png 응답) -> 클라이언트 -> (로드된 데이터를 브라우저에 표시)
웹서버와의 통신이 2번(select.php, google.png) 발생하며, 웹서버에서 DB와의 통신은 발생하지 않습니다.(웹서버에서 모두 처리)
이미지를 DB에 저장하고 dataURI 형식으로 포함시켰을 경우
클라이언트 -> (http://서버아이피/select.php 요청) -> 웹서버 -> (select.php 파일에 포함된 select 쿼리를 수행) -> DB -> (select 쿼리에 대한 응답) -> 웹서버 -> (select.php 응답(소스 안에 dataURI가 포함되어 있음)) -> 클라이언트 -> (dataURI를 파싱하여 이미지로 변환하고 로드된 데이터를 브라우저에 표시)
웹서버와의 통신이 1번 발생하며, 웹서버에서 DB로 통신이 1회 발생합니다. 또한 클라이언트에서는 dataURL를 파싱하는데에 추가적인 리소스가 소모됩니다.
일반적인 방식과 비교(트래픽)
dataURI는 이미지의 크기에 비례하여 길어지기 때문에 이미지의 크기가 크면 클수록 dataURI의 크기도 커집니다. 웹서버에서 발생하는 트래픽도 아래와 같이 직접 이미지 파일에 대해 응답하는 것과 별 차이가 없습니다. 아래는 3.6MB의 이미지를 이용하여 테스트 진행한 로그입니다.
웹서버의 이미지 파일을 img src 태그로 포함시켰을 경우
이미지를 DB에 저장하고 dataURI 형식으로 포함시켰을 경우
결론
이미지를 blob 데이터타입으로 DB에 저장하고 웹페이지 호출 시 DB로 부터 dataURI를 받아와서 사용하는 방법은 이미지를 웹서버 혹은 별도의 이미지 서버에 저장하지 않아도 된다는 점, 클라이언트와 웹서버 간 통신(요청, 응답) 횟수를 줄일 수 있는 점 등의 장점이 있으나, DB에 이미지를 저장하기 위해 DB에 추가적인 데이터 용량 사용이 발생하고 dataURI가 포함된 웹페이지를 호출할 때마다 DB에 쿼리가 발생하는 점(이미지가 많아질 경우 DB 성능 저하), 이미지를 DB에 저장한다고 하여도 웹서버의 트래픽이 줄어들지는 않는 점(비슷하거나, dataURI가 더 큼) 등의 단점이 있어, 상황에 따라 적절히 사용하는 것이 좋을 것으로 보입니다.
'MySQL' 카테고리의 다른 글
MySQL MHA(Master High Availability) 설정 (2) | 2023.03.23 |
---|