쇼핑몰을 운영하면서 가장 많이 받는 고객 문의가 무엇일까요? "제 택배 어디쯤 왔나요?"입니다. 배송조회 페이지를 직접 만들어 고객이 스스로 확인할 수 있게 하면 CS 문의를 크게 줄이고 고객 만족도도 높일 수 있습니다.

💡 이 가이드에서 만드는 것

• 백엔드: API 키를 안전하게 관리하는 프록시 서버
• 프론트엔드: 택배사 선택 + 송장번호 입력 폼
• UI: 배송 진행 상황을 보여주는 타임라인
• 에러 처리: 사용자 친화적 에러 메시지

왜 배송조회 페이지가 필요한가?

항목 배송조회 없이 배송조회 추가 후
고객 문의 "배송 어디예요?" 하루 30건+ 고객이 직접 확인, 문의 80% 감소
고객 경험 택배사 사이트로 이탈 내 쇼핑몰 안에서 확인
사이트 체류 외부 사이트 이동 체류 시간 증가, 재구매 유도

1백엔드: API 프록시 만들기

⚠️ 왜 서버를 거쳐야 하나요?
API 키를 프론트엔드 코드에 직접 넣으면 누구나 개발자 도구에서 볼 수 있습니다. 반드시 백엔드 서버를 통해 API를 호출하세요.

Express.js로 구현하기

const express = require('express');
const axios = require('axios');
const app = express();

app.use(express.json());

// 환경변수에서 API 키 관리 (절대 프론트엔드에 노출 금지!)
const API_KEY = process.env.DELIVERY_API_KEY;
const API_SECRET = process.env.DELIVERY_API_SECRET;
const API_URL = 'https://api.deliveryapi.co.kr/v1/tracking/trace';

app.post('/api/tracking', async (req, res) => {
  const { courierCode, trackingNumber } = req.body;

  // 입력값 검증
  if (!courierCode || !trackingNumber) {
    return res.status(400).json({
      isSuccess: false,
      message: '택배사 코드와 송장번호를 입력해주세요.'
    });
  }

  try {
    const response = await axios.post(API_URL, {
      items: [{ courierCode, trackingNumber }]
    }, {
      headers: {
        'Authorization': `Bearer ${API_KEY}:${API_SECRET}`,
        'Content-Type': 'application/json'
      }
    });

    res.json(response.data);
  } catch (error) {
    res.status(500).json({
      isSuccess: false,
      message: '조회 중 오류가 발생했습니다.'
    });
  }
});

app.listen(3000);

Next.js API Route로 구현하기

Next.js를 사용하고 있다면 API Route로 더 간단하게 구현할 수 있습니다:

// pages/api/tracking.ts (Next.js)
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== 'POST') {
    return res.status(405).json({ message: 'Method not allowed' });
  }

  const { courierCode, trackingNumber } = req.body;

  const response = await fetch(
    'https://api.deliveryapi.co.kr/v1/tracking/trace',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.DELIVERY_API_KEY}:${process.env.DELIVERY_API_SECRET}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        items: [{ courierCode, trackingNumber }]
      }),
    }
  );

  const data = await response.json();
  res.status(200).json(data);
}

2프론트엔드: 배송조회 UI 만들기

HTML 구조

택배사 선택, 송장번호 입력, 조회 버튼, 결과 영역으로 구성합니다:

<div class="tracking-container">
  <h2>배송 조회</h2>

  <div class="tracking-form">
    <select id="courierSelect">
      <option value="">택배사 선택</option>
      <option value="cj">CJ대한통운</option>
      <option value="lotte">롯데택배</option>
      <option value="hanjin">한진택배</option>
      <option value="post">우체국택배</option>
      <option value="logen">로젠택배</option>
      <option value="kyungdong">경동택배</option>
      <option value="daesin">대신택배</option>
    </select>

    <input type="text" id="trackingInput"
           placeholder="송장번호를 입력하세요" />

    <button id="trackBtn">조회하기</button>
  </div>

  <!-- 결과 영역 -->
  <div id="trackingResult" class="tracking-result"></div>
</div>

JavaScript 연동

조회 버튼 클릭 시 백엔드 API를 호출하고 결과를 렌더링합니다:

const trackBtn = document.getElementById('trackBtn');

trackBtn.addEventListener('click', async () => {
  const courierCode = document.getElementById('courierSelect').value;
  const trackingNumber = document.getElementById('trackingInput').value;
  const resultDiv = document.getElementById('trackingResult');

  if (!courierCode || !trackingNumber) {
    resultDiv.innerHTML = '<p class="error">택배사와 송장번호를 입력해주세요.</p>';
    return;
  }

  // 로딩 표시
  resultDiv.innerHTML = '<div class="loading">조회 중...</div>';

  try {
    const response = await fetch('/api/tracking', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ courierCode, trackingNumber }),
    });

    const data = await response.json();

    if (!data.isSuccess) {
      resultDiv.innerHTML = '<p class="error">조회 실패: ' + data.message + '</p>';
      return;
    }

    const result = data.data.results[0].data;
    renderTimeline(result, resultDiv);
  } catch (error) {
    resultDiv.innerHTML = '<p class="error">네트워크 오류가 발생했습니다.</p>';
  }
});

3배송 타임라인 UI 표시하기

배송 진행 상황을 시간순으로 보여주는 타임라인 UI는 고객에게 가장 직관적인 형태입니다.

타임라인 렌더링 함수

function renderTimeline(result, container) {
  const statusClass = result.isDelivered ? 'delivered' : 'in-transit';

  let html = `
    <div class="tracking-summary ${statusClass}">
      <h3>${result.courierName} - ${result.deliveryStatusText}</h3>
      <p>송장번호: ${result.trackingNumber}</p>
    </div>
    <div class="timeline">
  `;

  // progresses를 최신순으로 정렬
  const progresses = [...result.progresses].reverse();

  progresses.forEach((progress, index) => {
    const isLatest = index === 0;
    html += `
      <div class="timeline-item ${isLatest ? 'latest' : ''}">
        <div class="timeline-dot"></div>
        <div class="timeline-content">
          <span class="timeline-status">${progress.status}</span>
          <span class="timeline-location">${progress.location}</span>
          <span class="timeline-time">${progress.dateTime}</span>
        </div>
      </div>
    `;
  });

  html += '</div>';
  container.innerHTML = html;
}

타임라인 CSS

.timeline {
  position: relative;
  padding-left: 30px;
}
.timeline::before {
  content: '';
  position: absolute;
  left: 8px;
  top: 0;
  bottom: 0;
  width: 2px;
  background: #e5e7eb;
}
.timeline-item {
  position: relative;
  padding: 15px 0;
}
.timeline-dot {
  position: absolute;
  left: -26px;
  top: 20px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #d1d5db;
  border: 2px solid white;
}
.timeline-item.latest .timeline-dot {
  background: #3b82f6;
  box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2);
}
.tracking-summary.delivered {
  background: #dcfce7;
  border-left: 4px solid #22c55e;
  padding: 16px;
  border-radius: 8px;
  margin-bottom: 20px;
}
.tracking-summary.in-transit {
  background: #dbeafe;
  border-left: 4px solid #3b82f6;
  padding: 16px;
  border-radius: 8px;
  margin-bottom: 20px;
}
✅ 결과
배송 완료 시 초록색, 배송 중일 때 파란색으로 표시되는 직관적인 타임라인이 완성됩니다. 최신 배송 상태는 파란 점으로 강조됩니다.

4에러 처리 & UX 개선

사용자 친화적 에러 메시지

API 에러 코드를 고객이 이해할 수 있는 한글 메시지로 변환합니다:

const ERROR_MESSAGES = {
  'INVALID_TRACKING_NUMBER': '올바른 송장번호를 입력해주세요.',
  'COURIER_NOT_FOUND': '선택하신 택배사를 확인해주세요.',
  'TRACKING_NOT_FOUND': '등록된 배송 정보가 없습니다. 발송 후 1~2일 뒤 다시 조회해주세요.',
  'RATE_LIMIT_EXCEEDED': '잠시 후 다시 시도해주세요.',
};

function getErrorMessage(errorCode) {
  return ERROR_MESSAGES[errorCode] || '일시적인 오류가 발생했습니다. 잠시 후 다시 시도해주세요.';
}

UX 개선 팁

핵심 요약

💡 쇼핑몰 배송조회 페이지 구축 4단계

1. 백엔드 프록시 - API 키를 안전하게 관리하는 서버 구축
2. 프론트엔드 폼 - 택배사 선택 + 송장번호 입력 UI
3. 타임라인 UI - 배송 진행 상황을 직관적으로 표시
4. 에러 처리 - 고객 친화적인 메시지로 변환

다음 단계

지금 바로 배송조회 페이지를 만들어보세요

무료 회원가입 후 API 키를 발급받고 바로 시작할 수 있습니다

무료로 시작하기 →