단위 변환기, AI로 10분 만에 직접 만들어 쓰기
광고 없는 단위 변환기를 AI 프롬프트 3개로 직접 만들었습니다. 길이·무게·온도·넓이·속도를 한 번에 변환하는 React 컴포넌트와 실제 프롬프트를 공개합니다.
위 화면은 이 포스트의 프롬프트를 AI에 입력해서 생성한 샘플입니다
왜 단위 변환기를 직접 만들었나?
일상에서 단위 변환이 필요한 순간은 생각보다 자주 찾아옵니다.
해외 직구를 할 때 사이즈표가 inch로 적혀 있고, 헬스장에서 운동 기록을 파운드로 남기거나, 미국 요리 레시피를 따라 할 때 컵(cup)이나 온스(oz)가 등장합니다. 날씨 앱이 화씨(°F)로 설정돼 있을 때, 혹은 부동산 면적이 평수가 아닌 제곱미터로 표시될 때도 마찬가지입니다.
그때마다 구글에 검색하면 답은 나오지만, 광고가 절반을 차지하고 다른 단위로 바꾸려면 또 검색해야 합니다. 모바일에서는 더 불편합니다.
그래서 직접 만들었습니다. AI 프롬프트 3개, 총 8분이 걸렸습니다.
완성된 단위 변환기 살펴보기
위의 라이브 데모에서 직접 써보세요. 총 5가지 카테고리를 지원합니다.
길이: mm, cm, m, km, inch, ft, yard, mile 무게: mg, g, kg, ton, oz, lb 온도: 섭씨(°C), 화씨(°F), 켈빈(K) 넓이: cm², m², km², 헥타르, ft², 에이커 속도: m/s, km/h, mph, 노트
특히 단위 바꾸기 버튼이 유용합니다. 1km = 0.621마일을 확인한 뒤 버튼을 누르면 0.621마일 = 1km로 역방향도 바로 확인됩니다. 계산 결과가 다음 입력값으로 자동으로 들어가기 때문입니다.
AI에게 어떻게 요청했나?
바이브코딩의 핵심은 코드를 외우는 게 아닙니다. 원하는 것을 정확하게 설명하는 능력입니다.
처음에 막막하게 느껴진다면, 이렇게 생각해보세요.
"내가 친한 개발자 친구에게 부탁한다면 어떻게 설명할까?"
그 설명이 곧 좋은 프롬프트입니다.
1단계 프롬프트 — 기본 구조
Next.js 14 + TypeScript + Tailwind CSS로 단위 변환기 React 컴포넌트를 만들어줘.
요구사항:
- 'use client' 컴포넌트
- 카테고리: 길이(mm/cm/m/km/inch/ft/yd/mile), 무게(mg/g/kg/t/oz/lb), 온도(°C/°F/K)
- 상단 카테고리 탭
- from 입력 + 단위 선택 → to 결과 + 단위 선택
- from/to 스왑 버튼
- 실시간 계산 (onChange)
- CSS 변수로 다크모드 대응
2단계 프롬프트 — 기능 확장
위 컴포넌트에 다음을 추가해줘:
1. 넓이(cm²/m²/km²/ha/ft²/acre) 카테고리
2. 속도(m/s/km/h/mph/knot) 카테고리
3. 각 카테고리별 "자주 쓰는 변환" 빠른 참조 섹션
4. 숫자가 너무 크거나 작으면 지수 표기로 자동 전환
3단계 프롬프트 — UI 다듬기
UI를 개선해줘:
- from/to 영역을 카드 형태로 묶기
- swap 버튼 hover 시 회전 애니메이션
- 결과값을 브랜드 컬러로 강조
- 하단에 "1 inch = 2.54 cm" 형태 수식 요약 한 줄 표시
코드 핵심 포인트 3가지
1. 기준 단위(Base Unit) 패턴
모든 단위를 서로 직접 변환하면 조합 수가 n² 개로 늘어납니다. 길이만 해도 8개 단위 × 8 = 64가지 공식이 필요합니다.
대신 기준 단위 하나를 정해두고 거쳐가는 방식을 씁니다.
입력값 → toBase(기준 단위로 변환) → fromBase(목표 단위로 변환) → 결과
예시: 1인치 → km
- inch → m (toBase): 1 × 0.0254 = 0.0254m
- m → km (fromBase): 0.0254 × 0.001 = 0.0000254km
단위를 10개 추가해도 공식은 10개만 추가하면 됩니다.
2. 온도 예외 처리
온도는 덧셈/뺄셈이 섞여 있어서 단순 곱셈이 아닙니다.
// 섭씨 → 화씨
°F = °C × 9/5 + 32
// 화씨 → 섭씨
°C = (°F - 32) × 5/9
// 켈빈 ↔ 섭씨
K = °C + 273.15
기준 단위를 섭씨(°C)로 잡고 toBase/fromBase에서 단위별로 분기 처리합니다.
3. 숫자 포맷 처리
function fmt(n: number): string {
if (!isFinite(n)) return '—'
// 매우 크거나 매우 작은 경우 지수 표기
if (Math.abs(n) >= 1e10 || (Math.abs(n) < 0.0001 && n !== 0)) {
return n.toExponential(4) // 예: 1.2346e-5
}
// 8자리 유효숫자로 반올림 후 불필요한 0 제거
return parseFloat(n.toPrecision(8)).toString()
}
활용 예시 5가지
1. 해외 직구 미국 신발 사이즈 11 → cm 변환: length 카테고리에서 inch → cm, 11인치 = 27.94cm
2. 헬스장 기록 덤벨 25lb → kg: weight 카테고리, 25lb = 11.34kg
3. 요리 레시피 미국 레시피 350°F → 섭씨: temperature 카테고리, 350°F = 176.67°C
4. 부동산 아파트 84m² → 평: area 카테고리에서 m² → (1평 = 3.306m²이므로 84 ÷ 3.306 = 약 25.4평)
5. 자동차 속도 고속도로 제한속도 100km/h → mph: speed 카테고리, 100km/h = 62.14mph
이렇게 확장할 수 있습니다
지금 버전에서 프롬프트 하나로 추가할 수 있는 기능들:
- 압력 단위: Pa, bar, psi, atm
- 데이터 용량: B, KB, MB, GB, TB
- 에너지: J, cal, kcal, kWh
- 각도: 도(°), 라디안(rad), 그레이드(grad)
- 변환 이력: 최근 변환 기록 5개 표시
- 즐겨찾기: 자주 쓰는 단위 조합 저장
자주 묻는 질문
Q. 이 도구를 내 프로젝트에 그대로 써도 되나요? 네, 오픈소스입니다. 전체 코드는 아래 코드 섹션에서 확인할 수 있습니다.
Q. 모바일에서도 되나요? 됩니다. 반응형으로 제작되어 스마트폰에서도 잘 동작합니다.
Q. 오프라인에서 작동하나요? 네. 외부 API를 쓰지 않고 모든 계산이 브라우저에서 직접 처리됩니다.
다음 편 예고
다음 편은 번역기입니다. 무료 API를 연결해서 7개 언어를 지원하는 번역기를 만들어봅니다.
'use client'
import { useState, useCallback } from 'react'
import { ArrowLeftRight } from 'lucide-react'
// 카테고리 정의
const CATEGORIES = {
length: {
label: '길이', emoji: '📏',
units: [
{ label: '밀리미터', symbol: 'mm' },
{ label: '센티미터', symbol: 'cm' },
{ label: '미터', symbol: 'm' },
{ label: '킬로미터', symbol: 'km' },
{ label: '인치', symbol: 'in' },
{ label: '피트', symbol: 'ft' },
{ label: '야드', symbol: 'yd' },
{ label: '마일', symbol: 'mi' },
],
// 기준 단위: m
toBase: (v, u) => {
const map = { mm: 0.001, cm: 0.01, m: 1, km: 1000,
in: 0.0254, ft: 0.3048, yd: 0.9144, mi: 1609.344 }
return v * map[u]
},
fromBase: (v, u) => {
const map = { mm: 1000, cm: 100, m: 1, km: 0.001,
in: 39.3701, ft: 3.28084, yd: 1.09361, mi: 0.000621371 }
return v * map[u]
},
},
temperature: {
label: '온도', emoji: '🌡️',
units: [
{ label: '섭씨', symbol: '°C' },
{ label: '화씨', symbol: '°F' },
{ label: '켈빈', symbol: 'K' },
],
// 기준 단위: °C
toBase: (v, u) => {
if (u === '°C') return v
if (u === '°F') return (v - 32) * 5 / 9
return v - 273.15
},
fromBase: (v, u) => {
if (u === '°C') return v
if (u === '°F') return v * 9 / 5 + 32
return v + 273.15
},
},
// ... 나머지 카테고리 동일 패턴
}
export function UnitConverter() {
const [catKey, setCatKey] = useState('length')
const [fromUnit, setFromUnit] = useState('m')
const [toUnit, setToUnit] = useState('ft')
const [inputVal, setInputVal] = useState('1')
const cat = CATEGORIES[catKey]
// 변환 함수: 입력 → base → 출력
const convert = (val, from, to, ck) => {
const n = parseFloat(val)
if (isNaN(n)) return ''
const c = CATEGORIES[ck]
const result = c.fromBase(c.toBase(n, from), to)
// 지수 표기 전환
if (Math.abs(result) >= 1e10 || (Math.abs(result) < 0.0001 && result !== 0)) {
return result.toExponential(4)
}
return parseFloat(result.toPrecision(8)).toString()
}
const result = convert(inputVal, fromUnit, toUnit, catKey)
// swap: from ↔ to, 결과값을 새 입력값으로
const handleSwap = () => {
setFromUnit(toUnit)
setToUnit(fromUnit)
setInputVal(result || inputVal)
}
return (
<div>
{/* 카테고리 탭 */}
{Object.entries(CATEGORIES).map(([key, c]) => (
<button key={key} onClick={() => setCatKey(key)}>
{c.emoji} {c.label}
</button>
))}
{/* FROM 입력 */}
<input
type="number"
value={inputVal}
onChange={e => setInputVal(e.target.value)}
/>
<select value={fromUnit} onChange={e => setFromUnit(e.target.value)}>
{cat.units.map(u => (
<option key={u.symbol} value={u.symbol}>{u.symbol}</option>
))}
</select>
{/* Swap 버튼 */}
<button onClick={handleSwap}>
<ArrowLeftRight /> 단위 바꾸기
</button>
{/* TO 결과 */}
<div>{result}</div>
<select value={toUnit} onChange={e => setToUnit(e.target.value)}>
{cat.units.map(u => (
<option key={u.symbol} value={u.symbol}>{u.symbol}</option>
))}
</select>
{/* 수식 요약 */}
{result && <p>{inputVal} {fromUnit} = {result} {toUnit}</p>}
</div>
)
}
// 전체 스타일 포함 버전은 GitHub에서 확인:
// https://github.com/Aun-JY/vibecoding-blog/blob/main/components/tools/UnitConverter.tsx