이 글은 누구를 위한 것인가
- 장바구니 전환율이 낮아 체크아웃 단계에서 많은 이탈이 생기는 팀
- 결제 플로우를 처음 설계하는 프로덕트 디자이너
- 기존 체크아웃 UX의 어느 단계에서 이탈하는지 파악하고 싶은 팀
들어가며
체크아웃은 이커머스에서 가장 중요한 페이지다. 결제를 클릭한 사람의 70%가 완료하지 않는다. 단계를 하나씩 줄이면 전환율이 그만큼 올라간다.
이 글은 bluefoxdev.kr의 이커머스 UX 최적화 가이드 를 참고하여 작성했습니다.
1. 체크아웃 단계 설계 원칙
[이탈률 높은 체크아웃 패턴]
❌ 나쁜 패턴:
1단계: 회원가입 강요
2단계: 배송 정보 입력 (10개 필드)
3단계: 결제 수단 선택
4단계: 주문 확인
5단계: 완료
→ 총 5단계, 이탈율 80%
✅ 좋은 패턴:
1단계: 이메일 입력 (게스트/로그인 분기)
2단계: 배송 + 결제 (한 페이지에)
3단계: 확인 + 완료
→ 총 3단계, 이탈율 40-50%
[체크아웃 UX 원칙]
1. 진행 상태 표시 (Progress Indicator)
2. 게스트 결제 허용 (회원가입 강요 금지)
3. 자동 완성 최대 활용 (주소, 결제 정보)
4. 오류는 인라인으로 즉시 표시
5. 주요 신뢰 신호 (보안, 무료 반품) 상시 노출
2. 체크아웃 폼 UX
// 주소 자동완성 컴포넌트
function AddressInput({ onAddressSelect }: { onAddressSelect: (addr: Address) => void }) {
const [query, setQuery] = useState("");
const [suggestions, setSuggestions] = useState<Address[]>([]);
const handleSearch = async (value: string) => {
setQuery(value);
if (value.length < 2) return;
// 카카오 우편번호 API 또는 행정안전부 도로명 주소 API
const results = await searchAddress(value);
setSuggestions(results);
};
return (
<div className="relative">
<input
type="text"
value={query}
onChange={(e) => handleSearch(e.target.value)}
placeholder="도로명 주소 입력"
className="w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500"
autoComplete="shipping street-address"
/>
{suggestions.length > 0 && (
<ul className="absolute top-full left-0 right-0 bg-white border rounded-lg shadow-lg z-10 max-h-48 overflow-y-auto">
{suggestions.map((addr) => (
<li
key={addr.id}
onClick={() => {
onAddressSelect(addr);
setSuggestions([]);
}}
className="px-4 py-3 hover:bg-gray-50 cursor-pointer text-sm"
>
<p className="font-medium">{addr.roadAddress}</p>
<p className="text-gray-500 text-xs">{addr.jibunAddress}</p>
</li>
))}
</ul>
)}
</div>
);
}
// 인라인 유효성 검사
function ValidatedInput({
label,
value,
validate,
...props
}: InputProps & { validate: (v: string) => string | null }) {
const [touched, setTouched] = useState(false);
const error = touched ? validate(value) : null;
return (
<div className="space-y-1">
<label className="text-sm font-medium text-gray-700">{label}</label>
<input
value={value}
onBlur={() => setTouched(true)}
className={`w-full px-4 py-3 border rounded-lg ${
error ? "border-red-400 bg-red-50" : "border-gray-300 focus:border-blue-500"
}`}
{...props}
/>
{error && (
<p className="text-red-500 text-xs flex items-center gap-1">
<span>⚠</span> {error}
</p>
)}
</div>
);
}
3. 신뢰 신호 배치
function CheckoutTrustBar() {
return (
<div className="bg-gray-50 rounded-lg p-4 flex flex-wrap gap-4 justify-center">
<TrustItem icon="🔒" text="SSL 보안 결제" />
<TrustItem icon="↩️" text="무료 반품 30일" />
<TrustItem icon="✓" text="구매 안심 보장" />
<TrustItem icon="🏦" text="카드사 무이자" />
</div>
);
}
function OrderSummarySticky({ items, total }: { items: CartItem[]; total: number }) {
return (
<div className="sticky top-4 bg-white border rounded-xl p-6 shadow-sm">
<h3 className="font-semibold mb-4">주문 요약</h3>
{/* 상품 목록 */}
<div className="space-y-3 mb-4">
{items.map((item) => (
<div key={item.id} className="flex justify-between text-sm">
<span className="text-gray-600">{item.name} × {item.quantity}</span>
<span>{(item.price * item.quantity).toLocaleString()}원</span>
</div>
))}
</div>
<div className="border-t pt-4 space-y-2">
<div className="flex justify-between text-sm">
<span className="text-gray-600">배송비</span>
<span className="text-green-600">무료</span>
</div>
<div className="flex justify-between font-bold text-lg">
<span>합계</span>
<span>{total.toLocaleString()}원</span>
</div>
</div>
</div>
);
}
마무리
체크아웃 UX 개선의 최고 ROI는 게스트 결제 허용과 폼 필드 줄이기다. 회원가입을 제거하고, 주소 자동완성을 추가하면 전환율이 15-25% 오른다. 그 다음은 오류 메시지를 제출 후가 아닌 입력 중에 보여주는 인라인 검증이다.