단위 변환기, AI로 10분 만에 직접 만들어 쓰기
광고 없는 단위 변환기를 AI 프롬프트 3개로 직접 만들었습니다. 길이·무게·온도·넓이·속도를 한 번에 변환하는 React 컴포넌트와 실제 프롬프트를 공개합니다.
위 화면은 이 포스트의 프롬프트를 AI에 입력해서 생성한 샘플입니다
왜 단위 변환기를 직접 만들었나?
구글에서 "1 inch cm"를 검색하면 결과는 금방 나옵니다. 그런데 문제는 그 다음입니다.
- 변환 결과 주위로 광고가 절반을 차지합니다
- 다른 단위로 바꾸려면 또 검색해야 합니다
- 모바일에서는 더 불편합니다
그러다 문득 이런 생각이 들었습니다.
"AI 쓰면 이거 10분 안에 만들 수 있지 않나?"
실제로 해봤더니 8분이 걸렸습니다.
AI에게 어떻게 요청했나?
바이브코딩의 핵심은 "무엇을 만들고 싶은지"를 명확하게 전달하는 것입니다. 코드를 어떻게 짜야 할지 고민하는 시간보다, 내가 원하는 결과물을 잘 설명하는 데 집중합니다.
아래는 실제로 Claude에게 입력한 프롬프트입니다.
완성된 결과물
위의 라이브 데모에서 직접 사용해보세요. 길이, 무게, 온도, 넓이, 속도 — 5가지 카테고리를 지원합니다.
특히 단위 바꾸기 버튼을 누르면 from/to가 바뀌면서 계산 결과가 새 입력값으로 들어갑니다. 예를 들어 1km = 0.621mi 를 확인한 뒤, 바꾸기 버튼을 누르면 0.621mi = 1km 로 역방향도 바로 확인할 수 있습니다.
코드 핵심 포인트
1. 기준 단위 변환 방식
모든 단위를 직접 서로 변환하면 조합이 폭발합니다 (n개 단위 → n² 개 공식). 대신 기준 단위(base unit)를 정해두고 항상 거기서 거쳐가는 방식을 씁니다.
입력값 → [toBase] → 기준 단위 → [fromBase] → 출력값
예시: 1 inch → 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
toBase, fromBase 함수 안에서 단위별로 분기해서 처리합니다.
3. 숫자 표시 포맷
변환 결과가 0.0000254 처럼 너무 작거나 1234567890 처럼 너무 크면 읽기 불편합니다. 임계값을 정해서 자동으로 지수 표기(scientific notation)로 전환합니다.
if (Math.abs(n) >= 1e10 || Math.abs(n) < 0.0001) {
return n.toExponential(4)
}
직접 만들어 쓰면 좋은 이유
단위 변환기 하나를 예시로 들었지만, 이 방식의 진짜 가치는 확장성입니다.
✅ 내가 자주 쓰는 단위만 남길 수 있습니다 ✅ UI를 내 블로그/앱에 맞게 바꿀 수 있습니다 ✅ 광고 없이 깔끔하게 쓸 수 있습니다 ✅ 프롬프트 하나로 기능을 추가할 수 있습니다
다음 편 예고
다음 편은 환율 계산기입니다. 실시간 환율 API를 연결하는 방법도 함께 다룹니다.
'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