Cloudinary 사용법: URL 하나로 이미지 최적화하기

Cloudinary 사용법: URL 하나로 이미지 최적화하기

웹 서비스에서 이미지가 차지하는 비중이 정말 크죠. HTTP Archive에 따르면 평균 웹 페이지 용량의 절반 가까이가 이미지라고 합니다. 그래서 이미지를 얼마나 잘 관리하느냐가 사이트 성능에 직접적인 영향을 미치는데요.

이미지를 직접 서버에서 처리하려면 Sharplibvips 같은 라이브러리를 설치하고, 리사이징·포맷 변환·캐싱 로직을 일일이 구현해야 합니다. 소규모 프로젝트에서는 이 정도면 충분하지만 이미지가 수만 장 이상이거나 글로벌 사용자를 대상으로 한다면 CDN 배포와 on-the-fly 변환까지 직접 구축하기가 만만치 않죠.

Cloudinary는 이런 고민을 URL 하나로 해결해 주는 서비스입니다. 클라우드에서 이미지를 관리하고 최적화해 주거든요. 이미지를 업로드하면 전 세계 CDN으로 자동 배포되고, URL에 파라미터만 추가하면 리사이징, 크롭, 포맷 변환, 품질 조절까지 서버 코드 한 줄 없이 처리할 수 있어요. 이 글에서는 Cloudinary의 핵심 기능을 하나씩 살펴보겠습니다.

계정 만들기

Cloudinary는 무료 플랜을 제공하고 있어요. 가입도 간단합니다. 신용카드 없이 가입할 수 있고 매월 25 크레딧이 주어지는데요. 크레딧 하나당 변환 1,000건, 저장 공간 1GB, 전송 대역폭 1GB에 해당합니다. 개인 프로젝트나 블로그에 쓰기에는 넉넉한 양이에요.

cloudinary.com에서 가입하면 대시보드에서 세 가지 정보를 확인할 수 있습니다.

  • cloud_name — 계정을 식별하는 고유 이름
  • api_key — API 인증용 키
  • api_secret — API 인증용 비밀 키

이 정보는 이후 SDK를 설정하거나 API를 호출할 때 필요하니 메모해 두세요.

URL 기반 이미지 변환

Cloudinary에서 가장 편한 기능은 URL만으로 이미지를 변환할 수 있다는 점이에요. 서버에 별도의 이미지 처리 로직을 구현할 필요 없이 URL 경로에 변환 파라미터를 넣으면 Cloudinary가 알아서 처리해 줍니다.

기본적인 이미지 URL 구조는 이렇습니다.

https://res.cloudinary.com/{cloud_name}/image/upload/{변환}/{public_id}.{포맷}

예를 들어 sample.jpg라는 이미지를 업로드했다면 원본 URL은 이렇게 됩니다.

https://res.cloudinary.com/demo/image/upload/sample.jpg

여기에 변환 파라미터를 추가하면 이미지가 실시간으로 변환됩니다. 너비를 400px로 줄이고 싶다면 w_400을 넣으면 돼요.

https://res.cloudinary.com/demo/image/upload/w_400/sample.jpg

400x300 크기로 얼굴 중심 크롭을 하고 싶다면 이렇게 쓸 수 있습니다.

https://res.cloudinary.com/demo/image/upload/c_fill,g_face,w_400,h_300/sample.jpg

같은 변환 그룹 안에서는 쉼표(,)로 파라미터를 연결하고 변환 그룹끼리는 슬래시(/)로 구분합니다. 파라미터 이름이 직관적이라 금방 익숙해질 거예요.

자주 쓰는 변환 파라미터

URL 변환에서 가장 많이 쓰는 파라미터를 몇 가지 정리해 볼게요.

리사이징

너비와 높이를 지정하는 기본적인 리사이징입니다.

# 너비 800px로 리사이징 (높이는 비율 유지)
w_800

# 너비 400, 높이 300으로 맞추기
w_400,h_300

# 원본의 50%로 축소
w_0.5

크롭 모드

이미지를 잘라내는 방식을 c_ 파라미터로 지정합니다.

# 지정된 크기에 꽉 채우기 (비율 유지, 넘치는 부분 잘라냄)
c_fill,w_400,h_300

# 지정된 크기 안에 맞추기 (비율 유지, 여백 가능)
c_fit,w_400,h_300

# 지정된 크기에 맞추되 여백을 배경색으로 채우기
c_pad,w_400,h_300,b_white

# 얼굴 중심으로 자동 크롭
c_fill,g_face,w_200,h_200

# AI가 판단한 관심 영역 기준 크롭
c_fill,g_auto,w_400,h_300

g_face는 얼굴 인식 기반으로 크롭 위치를 잡아주고, g_auto는 AI가 이미지에서 가장 중요한 영역을 찾아서 크롭합니다. 프로필 이미지나 썸네일을 만들 때 특히 유용하죠.

포맷 변환

# WebP로 변환
f_webp

# AVIF로 변환
f_avif

# PNG로 변환
f_png

품질 조절

# 품질 80%
q_80

# 자동 품질 (기본: good)
q_auto

# 자동 품질 수준 지정
q_auto:best
q_auto:good
q_auto:eco
q_auto:low

효과

# 흑백으로 변환
e_grayscale

# 블러 처리 (강도 100~2000)
e_blur:500

# 둥근 모서리 (프로필 이미지용)
r_max

# 회전 (각도)
a_90

자동 최적화: f_auto와 q_auto

Cloudinary에서 가장 실용적인 기능을 하나만 꼽으라면 f_autoq_auto를 들겠어요. 이 두 파라미터만 URL에 추가하면 별다른 설정 없이 이미지 용량을 크게 줄일 수 있거든요.

f_auto는 브라우저가 보내는 Accept 헤더를 분석해서 지원하는 가장 효율적인 포맷으로 자동 변환합니다. Chrome이면 AVIF나 WebP로, Safari면 WebP로, 아주 오래된 브라우저면 원본 JPEG 그대로 보내주는 식이에요. HTML의 <picture> 요소로 포맷 분기를 직접 구현하는 것과 같은 효과를 URL 파라미터 하나로 얻을 수 있는 셈이죠.

q_auto는 이미지 내용을 분석해서 시각적 품질을 유지하면서 최대한 용량을 줄여줍니다. 실제로 얼마나 차이가 나는지 볼까요?

설정용량감소율
원본569 KB-
q_auto:best65.9 KB88%
q_auto:good (기본)56.9 KB90%
q_auto:eco45.0 KB92%
q_auto:low35.0 KB94%

기본값인 q_auto:good만 써도 원본 대비 90%나 줄어듭니다. 거의 모든 상황에서 이 두 파라미터를 기본으로 넣어두는 것을 추천합니다.

https://res.cloudinary.com/demo/image/upload/f_auto,q_auto/sample.jpg

한 가지 알아둘 점은, f_auto는 브라우저의 요청 시점에 동작하기 때문에 업로드 시 eager 변환에서는 효과가 없다는 것입니다. 업로드 시 미리 변환을 걸어두려면 f_webp처럼 포맷을 명시해야 합니다.

Node.js SDK 설치와 설정

브라우저에서 직접 URL을 조립해도 되지만, 서버에서 이미지를 업로드하거나 URL을 프로그래밍 방식으로 생성하려면 SDK를 쓰는 게 편합니다.

bun add cloudinary
# 또는
npm install cloudinary

SDK를 설정하는 방법은 두 가지예요. 코드에서 직접 설정하거나 환경 변수를 사용할 수 있습니다.

config.js
import { v2 as cloudinary } from "cloudinary";

// 방법 1: 코드에서 직접 설정
cloudinary.config({
  cloud_name: "my_cloud",
  api_key: "123456789012345",
  api_secret: "abcdefghijklmnopqrstuvwxyz",
  secure: true,
});

환경 변수를 쓰면 코드에 인증 정보를 넣지 않아도 됩니다.

export CLOUDINARY_URL=cloudinary://123456789012345:abcdefghijklmnopqrstuvwxyz@my_cloud

CLOUDINARY_URL 환경 변수가 설정되어 있으면 SDK가 자동으로 읽어오기 때문에 cloudinary.config()를 호출하지 않아도 됩니다.

이미지 업로드

SDK로 이미지를 업로드하는 코드를 살펴볼게요.

upload.js
import { v2 as cloudinary } from "cloudinary";

// 로컬 파일 업로드
const result = await cloudinary.uploader.upload("photo.jpg", {
  folder: "blog",
  public_id: "my-post-cover",
});

console.log(result.secure_url);
// https://res.cloudinary.com/my_cloud/image/upload/v1234567890/blog/my-post-cover.jpg

folder 옵션으로 경로를 지정할 수 있고 public_id로 파일 이름을 정할 수 있어요. 업로드 결과에는 secure_url, width, height, format, bytes 같은 정보가 포함됩니다.

원격 URL에 있는 이미지도 바로 업로드할 수 있습니다.

const result = await cloudinary.uploader.upload(
  "https://example.com/photo.jpg",
  { folder: "external" },
);

업로드 시 변환을 미리 적용해 둘 수도 있는데요. eager 옵션을 사용하면 됩니다.

const result = await cloudinary.uploader.upload("photo.jpg", {
  eager: [
    { width: 400, height: 300, crop: "fill" },
    { width: 200, height: 200, crop: "thumb", gravity: "face" },
  ],
});

이렇게 하면 원본 이미지와 함께 변환된 버전들이 미리 생성되어 CDN에 캐싱됩니다. 자주 사용하는 크기가 정해져 있다면 첫 요청 시 변환 지연 없이 바로 서빙할 수 있어서 좋습니다.

SDK로 URL 생성하기

이미 업로드된 이미지의 변환 URL을 SDK로 만들 수도 있습니다. URL을 직접 문자열로 조립하는 것보다 실수가 적고 타입 안전하죠.

url.js
import { v2 as cloudinary } from "cloudinary";

// 기본 URL 생성
const url = cloudinary.url("blog/my-post-cover.jpg", {
  secure: true,
});
// https://res.cloudinary.com/my_cloud/image/upload/blog/my-post-cover.jpg

// 변환이 적용된 URL 생성
const optimizedUrl = cloudinary.url("blog/my-post-cover.jpg", {
  width: 800,
  crop: "fill",
  fetch_format: "auto",
  quality: "auto",
  secure: true,
});
// https://res.cloudinary.com/my_cloud/image/upload/c_fill,f_auto,q_auto,w_800/blog/my-post-cover.jpg

fetch_format: "auto"가 URL의 f_auto에 대응하고, quality: "auto"q_auto에 대응합니다.

반응형 이미지 서빙

화면 크기에 따라 다른 크기의 이미지를 서빙하고 싶을 때도 Cloudinary가 유용해요. HTML의 srcset 속성과 함께 쓰면 브라우저가 알아서 적절한 크기를 골라갑니다.

<img
  src="https://res.cloudinary.com/demo/image/upload/w_800,f_auto,q_auto/sample.jpg"
  srcset="
    https://res.cloudinary.com/demo/image/upload/w_400,f_auto,q_auto/sample.jpg   400w,
    https://res.cloudinary.com/demo/image/upload/w_800,f_auto,q_auto/sample.jpg   800w,
    https://res.cloudinary.com/demo/image/upload/w_1200,f_auto,q_auto/sample.jpg 1200w
  "
  sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
  alt="반응형 이미지"
  loading="lazy"
/>

URL의 w_ 값만 바꾸면 되니까 같은 이미지를 여러 크기로 미리 만들어 둘 필요가 없습니다. 요청이 들어올 때 Cloudinary가 변환해서 CDN에 캐싱하기 때문에 두 번째 요청부터는 바로 서빙됩니다.

Fetch 모드로 외부 이미지 최적화

이미 다른 서버에 올려둔 이미지도 Cloudinary를 통해 최적화할 수 있습니다. image/upload 대신 image/fetch를 쓰면 돼요.

https://res.cloudinary.com/demo/image/fetch/f_auto,q_auto,w_800/https://example.com/photo.jpg

이렇게 하면 Cloudinary가 원본 이미지를 가져와서 변환한 뒤 CDN으로 서빙합니다. 기존 서비스의 이미지를 건드리지 않고도 최적화 효과를 누릴 수 있어서, 레거시 시스템에 Cloudinary를 점진적으로 도입할 때 유용한 방법이에요.

프론트엔드 프레임워크 연동

React나 Next.js 같은 프론트엔드 프레임워크에서는 Cloudinary URL을 그대로 <img> 태그의 src에 넣으면 됩니다. 별도의 프레임워크용 라이브러리 없이도 충분히 쓸 수 있어요.

ImageCard.jsx
function ImageCard({ publicId, alt }) {
  const cloudName = "my_cloud";
  const baseUrl = `https://res.cloudinary.com/${cloudName}/image/upload`;

  return (
    <img
      src={`${baseUrl}/c_fill,w_400,h_300,f_auto,q_auto/${publicId}`}
      alt={alt}
      loading="lazy"
      width={400}
      height={300}
    />
  );
}

만약 Astro를 사용하고 있다면 Astro의 Image 컴포넌트와 Cloudinary를 함께 쓸 수도 있습니다. astro.config.mjs에서 Cloudinary 도메인을 허용해 주면 외부 이미지로 취급되어 Astro의 최적화 파이프라인도 함께 적용됩니다.

astro.config.mjs
export default defineConfig({
  image: {
    domains: ["res.cloudinary.com"],
  },
});

다만 이 경우 Cloudinary의 변환과 Astro의 변환이 이중으로 적용되니, 어느 쪽에서 최적화를 담당할지 정해두는 게 좋습니다. Cloudinary URL에서 이미 리사이징과 포맷 변환을 했다면 Astro 쪽에서는 추가 변환 없이 그대로 쓰는 것이 효율적이에요.

이미지 삭제와 관리

업로드한 이미지를 삭제하거나 이름을 바꾸는 것도 SDK로 할 수 있습니다.

manage.js
import { v2 as cloudinary } from "cloudinary";

// 단일 이미지 삭제
await cloudinary.uploader.destroy("blog/my-post-cover");

// 이름 변경
await cloudinary.uploader.rename("blog/old-name", "blog/new-name");

// 폴더 안의 리소스 목록 조회
const resources = await cloudinary.api.resources({
  type: "upload",
  prefix: "blog/",
  max_results: 30,
});

대시보드에서도 이미지를 확인하고 관리할 수 있지만, 자동화가 필요하면 SDK의 Admin API를 쓰는 게 편합니다.

마치며

Cloudinary는 이미지 업로드, 변환, 최적화, CDN 배포를 하나의 URL로 처리할 수 있는 서비스예요. 특히 f_autoq_auto만 URL에 붙여도 이미지 용량을 90% 가까이 줄일 수 있어서 웹 성능을 개선하는 가장 간단한 방법 중 하나라고 할 수 있어요.

물론 모든 프로젝트에 Cloudinary가 필요한 것은 아닙니다. 이미지가 많지 않은 정적 블로그라면 Sharp로 빌드 시 처리하거나, Astro의 이미지 최적화처럼 프레임워크 내장 기능을 쓰는 것으로 충분할 수 있습니다. 하지만 사용자가 올리는 이미지를 실시간으로 변환해야 하거나, 글로벌 CDN이 필요하거나, 이미지 처리 인프라를 직접 관리하고 싶지 않다면 Cloudinary가 좋은 선택이 될 거예요.

참고자료

This work is licensed under CC BY 4.0 CC BY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord