← 홈으로 돌아가기
INSIGHTS

JavaScript로 이미지 크기와 비율을 변경하는 방법, Canvas로 직접 구현하기

자바스크립트로 이미지 크기 변경과 비율 유지, 자르기(크롭) 기능을 프론트엔드에서 직접 구현하는 방법을 알아봅니다. Canvas drawImage() 사용법부터 마우스 드래그 좌표 계산, toBlob() 이미지 파일 변환까지 이미지 처리 핵심 코드를 알기 쉽게 정리했습니다.

Pixoas 본문 이미지

웹사이트나 웹툴을 만들다 보면 이미지를 원하는 크기로 바꾸거나, 특정 비율에 맞춰 잘라내야 하는 경우가 자주 생깁니다.

겉으로 보기에는 단순히 이미지의 크기만 바꾸면 될 것 같지만, 실제로 기능을 만들어보면 생각보다 신경 써야 할 부분이 많습니다. 원본 이미지의 비율을 유지하면서 크기를 바꿀지, 정해진 비율에 맞춰 이미지를 잘라낼지, 변경된 이미지를 어떤 위치에 배치할지까지 함께 계산해야 하기 때문입니다.

그렇다면 이런 이미지 크기 변경과 비율 조절은 웹 브라우저에서 어떻게 구현할 수 있을까요?

저 역시 이미지 처리 웹툴 🔗 ImagePrep를 만들면서 이 기능을 직접 구현했고, 그 과정에서 JavaScriptCanvas를 이용하면 이미지의 크기와 비율을 원하는 형태로 변경하고, 필요한 부분만 잘라낼 수 있다는 것을 알게 되었습니다.

이번 글에서는 Canvas가 이미지를 처리하는 기본 원리부터 이미지 크기 변경, 비율 유지, 위치 조정, 크롭까지 실제 구현에 필요한 내용을 하나씩 살펴보겠습니다.


🎨 Canvas는 이미지를 어떻게 처리할까?

브라우저에서 이미지를 처리하려면 먼저 Canvas를 이해할 필요가 있습니다.

Canvas는 웹페이지에 그림을 그릴 수 있는 영역입니다. JavaScript를 이용하면 이 공간에 이미지나 도형 등을 그릴 수 있고, 그려진 결과를 다시 이미지 데이터로 변환할 수도 있습니다.

CanvasHTML에서 다음과 같이 간단히 만들 수 있습니다.

<canvas id="canvas"></canvas>

그리고 JavaScript에서 이 Canvas를 가져와 그림을 그릴 준비를 합니다.

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

여기서 Canvas이미지를 그릴 도화지이고, ctx그 위에 그림을 그리는 도구라고 생각하면 됩니다.

이제 이 도화지에 실제 이미지를 그려보겠습니다.


🖼️ 이미지를 Canvas에 그려보기

먼저 JavaScript에서 이미지 객체를 하나 만들고 이미지 파일을 지정합니다.

const image = new Image();
image.src = 'photo.jpg';

다만 이미지를 지정했다고 바로 Canvas에 그릴 수 있는 것은 아닙니다. 이미지가 브라우저에 모두 불러와진 후 그려야 하기 때문에 onload를 사용합니다.

image.onload = () => {
  ctx.drawImage(image, 0, 0);
};

앞에서 만든 ctx를 이용해 drawImage()를 호출하면 이미지가 Canvas에 그려집니다.

여기서 0, 0은 이미지가 그려지기 시작하는 위치입니다. Canvas의 좌표는 왼쪽 위의 (0, 0)에서 시작하고, 오른쪽으로 갈수록 X 값이 커지고 아래로 갈수록 Y 값이 커집니다.

(0, 0) ───────────→ X
  │
  │
  │
  ↓
  Y

지금까지는 원본 이미지의 크기 그대로 Canvas에 그렸습니다. 그렇다면 이미지의 크기를 원하는 값으로 바꾸려면 어떻게 해야 할까요?


📐 drawImage()로 이미지 크기 변경하기

drawImage()는 이미지를 그릴 위치뿐만 아니라 그릴 크기도 지정할 수 있습니다.

앞에서 사용한 코드에 이미지의 너비와 높이를 추가하면 됩니다.

ctx.drawImage(image, 0, 0, 800, 600);

여기서 800, 600은 각각 이미지의 너비와 높이입니다.

만약 원본 이미지가 1200×900px이라면 800x600px와 비율이 동일하기 때문에 이미지가 왜곡되지 않습니다.

반대로 원본의 비율과 다른 값을 지정하면 이미지가 늘어나거나 눌릴 수 있습니다.

따라서 이미지 크기를 변경할 때는 가로 또는 세로 중 하나를 기준으로 정한 뒤, 원본 이미지의 비율에 맞춰 나머지 크기를 계산하는 방식을 사용해야 합니다.

그런데 이미지 처리 기능을 만들다 보면 단순히 비율을 유지하는 것만으로는 부족한 경우가 있습니다. 예를 들어 정해진 800×800 영역에 사진을 꽉 채워야 한다면, 이미지의 일부를 잘라내는 크롭이 필요합니다.


✂️ 원하는 비율에 맞춰 이미지 크롭하기

drawImage()에는 이미지 전체를 그리는 것뿐만 아니라, 원본 이미지에서 필요한 부분만 선택해서 그리는 기능도 있습니다.

ctx.drawImage(
  image,
  sx, sy, sWidth, sHeight,
  dx, dy, dWidth, dHeight
);

앞의 네 값은 원본 이미지에서 가져올 영역을 정하고, 뒤의 네 값은 그 영역을 Canvas의 어디에 어떤 크기로 그릴지를 정합니다.

구분의미
sx, sy원본 이미지에서 가져올 시작 위치
sWidth, sHeight원본에서 가져올 영역의 크기
dx, dyCanvas에 그릴 위치
dWidth, dHeightCanvas에 그릴 최종 크기

예를 들어 원본 이미지의 (200, 0) 위치에서 800×800 크기의 영역을 잘라 Canvas에 그리려면 다음과 같이 사용할 수 있습니다.

ctx.drawImage(
  image,
  200, 0, 800, 800,
  0, 0, 800, 800
);

이처럼 원본 이미지에서 필요한 영역을 선택할 수 있기 때문에 정사각형이나 16:9처럼 특정 비율에 맞춰 이미지를 잘라내는 기능도 구현할 수 있습니다.

다만 실제 웹툴에서는 사용자가 직접 이미지의 위치를 움직이면서 원하는 부분을 선택할 수 있어야 합니다. 이 경우에는 사용자의 드래그 위치를 받아 크롭 영역과 이미지의 위치를 계속 계산해서 Canvas에 다시 그리는 방식으로 구현할 수 있습니다.


🧭 크롭 영역 안에서 이미지 위치 조정하기

이미지 위치는 드래그를 하는 것 만으로도 원하는 크롭 영역을 쉽게 지정할 수 있습니다.

이 기능을 만들려면 마우스가 움직인 거리를 계산해서 이미지의 좌표(x, y)를 바꾸고, 화면을 새로 그리는 과정이 필요합니다.

JavaScriptmousemove 이벤트를 사용하면 사용자가 마우스를 얼마나 움직였는지 감지할 수 있습니다.

canvas.addEventListener('mousemove', (event) => {
  if (!isDragging) return;

  x += event.movementX;
  y += event.movementY;

  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.drawImage(image, x, y, imageWidth, imageHeight);
});

이 코드가 작동하는 흐름은 다음과 같습니다.

  • 드래그 중: 마우스를 움직인 픽셀만큼 x, y 값이 계속 변합니다.
  • 화면 갱신: 좌표가 바뀔 때마다 도화지를 지우고(clearRect), 새 위치에 이미지를 다시 그립니다(drawImage). 이 과정이 아주 빠르게 반복되며 이미지가 마우스를 따라오는 것처럼 보입니다.
  • 위치 고정: 마우스 버튼을 놓으면 isDragging이 꺼지면서 코드가 멈춥니다. 마우스를 놓는 순간의 x, y 값이 최종 위치로 고정됩니다.

만약 이미지를 다시 클릭해서 드래그하면, 고정되어 있던 위치부터 값이 다시 갱신되며 이미지를 계속 이동시킬 수 있습니다.


📦 Canvas의 결과를 이미지 파일로 만들기

지금까지의 과정으로 이미지를 원하는 크기와 비율로 Canvas에 그릴 수 있게 되었습니다.

하지만 Canvas에 그려진 결과가 곧바로 다운로드 가능한 JPG나 PNG 파일이 되는 것은 아닙니다. Canvas는 브라우저 화면에 이미지를 렌더링하는 영역일 뿐이므로, 이를 실제 파일 데이터로 변환하는 과정이 추가로 필요합니다.

이때 toBlob() 메서드를 사용할 수 있습니다.

canvas.toBlob(
  (blob) => {
  },
  'image/jpeg',
  0.8
);

여기서 'image/jpeg'는 결과 파일의 포맷을, 0.8은 이미지 품질(0.0~1.0)을 의미합니다. 필요에 따라 포맷을 PNG나 WebP로 변경하거나 품질 값을 자유롭게 조절할 수 있습니다.

이렇게 변환된 Blob 데이터를 활용해야만 브라우저에서 최종 이미지 파일을 만들어 다운로드하거나 서버로 전송할 수 있습니다.


🛠️ 전체 코드 흐름 모아보기

지금까지 살펴본 전체적인 이미지 처리 과정을 요약하면 다음과 같은 4단계로 이루어집니다.

  1. 이미지 불러오기: new Image()onload를 사용해 브라우저에 원본 이미지를 로드합니다.
  2. 도화지 크기 설정: 최종 결과물의 크기에 맞춰 canvas.widthheight를 설정합니다.
  3. 이미지 다시 그리기: 비율과 위치를 계산한 뒤 drawImage()를 통해 Canvas에 렌더링합니다.
  4. 파일 데이터 변환: 완성된 CanvastoBlob()을 통해 실제 포맷과 품질이 적용된 파일 데이터로 추출합니다.

그리고 위 내용에 대한 코드를 하나로 연결하면 다음과 같습니다.
(예를 들어 1200x900 px 규격의 원본 이미지를 (200, 50) 위치에서 800x800 px 규격의 JPEG 파일로 만드는 상황을 가정해 보겠습니다.)

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const image = new Image();

image.src = 'photo.jpg';

image.onload = () => {
  canvas.width = 800;
  canvas.height = 800;

  ctx.drawImage(
    image,
    200, 50, 800, 800,
    0, 0, 800, 800
  );

  canvas.toBlob(
    (blob) => {
    },
    'image/jpeg',
    0.8
  );
};

이처럼 브라우저에서 이미지의 크기와 비율을 변경하는 과정은 생각보다 단순하지 않습니다.

Canvas에 이미지를 그린 뒤 원본 비율에 맞춰 크기를 계산하고, 필요한 경우 원하는 영역을 크롭하고, 이미지의 위치까지 조정한 후 최종 결과를 이미지 데이터로 변환해야 합니다.

저 역시 🔗 ImagePrep를 만들면서 이 과정을 직접 구현했고, 단순히 이미지의 가로·세로 값을 변경하는 것보다 원하는 결과를 만들기 위해 이미지의 크기와 위치, 크롭 영역을 어떻게 계산할 것인지가 중요하다는 것을 알게 되었습니다.

Canvas의 기본 원리를 이해하면 이미지 리사이즈와 크롭뿐만 아니라, 브라우저에서 다양한 이미지 처리 기능을 직접 구현할 수 있는 기반을 만들 수 있습니다.