<aside> 🐱
URL 하나로 크기와 텍스트를 지정하면 고양이 이미지를 즉시 반환하는 플레이스홀더 서비스입니다. https://placecat.1yoouoo.com/400/600처럼 요청하면 400x600 고양이 이미지가 생성될 수 있으며, 54,000장의 이미지 풀과 Edge Runtime으로 초고속 응답을 제공합니다.
</aside>
현재는 서비스 종료.
플레이스홀더 서비스는 이미 picsum.photos 같은 좋은 사이트가 있지만 고양이 특화 버전은 없었습니다. ㅎ
width만 주면 정사각형, width/height 모두 주면 직사각형으로 동작하는 유연한 API가 필요했음Next.js의 OG 이미지 생성을 일반 API로 확장사용했습니다. 이미지를 미리 생성해두지 않고, 요청 시점에 CDN에서 고양이 이미지를 fetch해 Base64로 인코딩한 뒤 JSX로 합성합니다.
export const runtime = 'edge'; // Edge Runtime 선언
export async function renderImageResponse(
imageUrl: string,
width: number,
height: number,
text: string = '',
) {
// CDN에서 fetch 후 base64 변환
const imageBuffer = await (await fetch(imageUrl)).arrayBuffer();
const dataUrl = `data:image/jpeg;base64,${Buffer.from(imageBuffer).toString('base64')}`;
return new ImageResponse(
(
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
<img src={dataUrl} style={{ position: 'absolute', width: '100%', height: '100%', objectFit: 'cover' }} />
{text && <div style={{ position: 'absolute' }}>{text}</div>}
</div>
),
{ width, height },
);
}
URL을 줄이면서도 유연하게 동작하도록 라우팅 상단에서 리다이렉트를 처리했습니다.
// /400 → /400/400/png (정사각형)
// /400/600 → /400/600/png (기본 포맷)
// /400/600/png → 이미지 생성
// app/[width]/route.tsx
export async function GET(request, { params }) {
const { width } = await params;
redirect(`/${width}/${width}/png`); // 높이 생략 시 정사각형
}
텍스트 오버레이 시 이미지 크기에 맞는 폰트 크기를 자동으로 계산합니다. 한글은 영문보다 실질 폭이 넓기 때문에 문자 종류마다 가중치를 다르게 적용했습니다.
const calculateCharWidth = (char: string): number => {
if (/[\\uAC00-\\uD7AF]/.test(char)) return 1.0; // 한글: 넓음
if (/[A-Z!@#$%^&*]/.test(char)) return 0.75; // 영문 대문자/특수문자
return 0.5; // 영문 소문자/숫자
};
// 이미지 종횡비에 따른 추가 조정
const aspectRatio = width / height;
const aspectFactor = aspectRatio > 2 ? 0.85 : aspectRatio < 0.5 ? 1.1 : 1.0;
어떤 텍스트를 넘겨도 이미지를 벗어나지 않고, 어떤 비율의 이미지에도 적절하게 맞춰집니다.
라우트에 도달하기 전, 미들웨어에서 마이너스 크기나 비정상 포맷 요청을 미리차단합니다.