Spring Boot 도로명 주소 API 연동

송진우·2025년 12월 29일
post-thumbnail

도로명 주소 API란?

사용자가 배송지나 주소를 입력할 때 정확한 도로명 주소를 검색하고 선택할 수 있도록 도와주는 API입니다.

주요 사용처

  • 주문 시 배송지 입력
  • 회원가입/정보수정 시 주소 입력
  • 지점/매장 위치 검색

주소 API 통합 흐름

방식 1: 프론트엔드 중심 (Daum 우편번호)


방식 2: 백엔드 중심 (카카오 주소 검색 API)


프로젝트 구조

OnAndHome (백엔드)
├── order/
│   ├── entity/Order.java
│   ├── dto/CreateOrderRequest.java
│   └── OrderService.java

OnAndHomeFront (프론트엔드)
├── public/
│   └── index.html        # Daum 스크립트 로드
└── src/
    └── pages/Checkout.jsx

백엔드 구현

Order 엔티티 설계

@Entity
@Getter
@Setter
@Table(name = "orders")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;

    // 배송 정보
    private String recipientName;      // 받는 사람
    private String recipientPhone;     // 연락처
    private String shippingAddress;    // 배송지 주소 
    private String shippingRequest;    // 배송 요청사항

    @Column(nullable = false)
    private int totalPrice;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private OrderStatus status;
}

DTO 정의

@Getter
@Setter
public class CreateOrderRequest {

    private Long userId;
    private List<OrderItemRequest> orderItems;
    private String paymentMethod;

    // 배송 정보
    private String recipientName;
    private String recipientPhone;
    private String shippingAddress;
    private String shippingRequest;
}

Service 로직

@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderRepository orderRepo;
    private final UserRepository userRepo;

    @Transactional
    public OrderDTO createOrder(CreateOrderRequest request) {
        // 사용자 조회
        User user = userRepo.findById(request.getUserId())
            .orElseThrow(() -> new IllegalArgumentException(
                "사용자를 찾을 수 없습니다."));

        // 주문 생성
        Order order = Order.create(user, orderItems, paymentMethod);

        // 배송 정보 저장
        order.setRecipientName(request.getRecipientName());
        order.setRecipientPhone(request.getRecipientPhone());
        order.setShippingAddress(request.getShippingAddress());
        order.setShippingRequest(request.getShippingRequest());

        // 주소 유효성 검증
        validateAddress(request.getShippingAddress());

        // 저장
        return OrderDTO.fromEntity(orderRepo.save(order));
    }

    /**
     * 주소 유효성 검증
     */
    private void validateAddress(String address) {
        // 1. 빈 값 체크
        if (address == null || address.trim().isEmpty()) {
            throw new IllegalArgumentException(
                "배송지를 입력해주세요.");
        }

        // 2. 길이 체크
        if (address.length() < 10) {
            throw new IllegalArgumentException(
                "배송지가 너무 짧습니다.");
        }

        if (address.length() > 200) {
            throw new IllegalArgumentException(
                "배송지가 너무 깁니다.");
        }

        // 3. 특수문자 체크
        if (address.matches(".*[<>\"'].*")) {
            throw new IllegalArgumentException(
                "배송지에 사용할 수 없는 문자가 포함되어 있습니다.");
        }
    }
}

프론트엔드 연동

Daum 스크립트 로드

OnAndHomeFront/public/index.html

<head>
  <!-- Daum 우편번호 서비스 -->
  <script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
</head>

주소 검색 함수

const handleAddressSearch = () => {
  new window.daum.Postcode({
    oncomplete: function (data) {
      // 도로명 주소 우선, 없으면 지번 주소
      const address = data.roadAddress || data.jibunAddress;
      setShippingAddress(address);
    },
  }).open();
};

실제 화면


Postman 테스트

1. 정상 케이스 - 주문 생성

POST URL http://localhost:8080/api/orders
Headers
KeyValueContent-Typeapplication/jsonAuthorizationBearer {your_token}
{
  "userId": 1,
  "orderItems": [
    {
      "productId": 10,
      "quantity": 2
    }
  ],
  "paymentMethod": "CARD",
  "recipientName": "김현수",
  "recipientPhone": "010-1234-5678",
  "shippingAddress": "서울특별시 강남구 테헤란로 123 (선릉타워)",
  "shippingRequest": "문 앞에 놓아주세요"
}

기대 결과

{
  "success": true,
  "message": "주문이 완료되었습니다.",
  "data": {
    "orderId": 42,
    "shippingAddress": "서울특별시 강남구 테헤란로 123 (선릉타워)"
  }
}


{
  "userId": 1,
  "orderItems": [
    {
      "productId": 10,
      "quantity": 2
    }
  ],
  "paymentMethod": "CARD",
  "recipientName": "김현수",
  "recipientPhone": "010-1234-5678",
  "shippingAddress": "서울특별시 강남구 테헤란로 123 (선릉타워)",
  "shippingRequest": "문 앞에 놓아주세요"
}

0개의 댓글