SOAP 이란?
"Simple Object Access Protocol"로 HTTP, HTTPS, SMTP 등을 사용해서 XML 기반의 메세지를 컴퓨터 네트워크 상에서 교환하는 통신 프로토콜을 말한다.
JSON과 같이 XML도 플랫폼과 프로그래밍 언어에 종속적이지 않기 때문에 이기종간의 통신이 가능하다는 장점이 있다.
SOAP는 WSDL(Web Service Description Language)를 통해서 이 서비스가 현재 어떤 내용들을 제공해주는지 알려준다.
REST의 예로 들면 api/v1/github/docs 라는 api가 해당 api에 대한 명세에 대한 목록들을 담고 있다 가정하면, SOAP에서는 WSDL이 그러한 역할을 해준다고 볼 수 있다.
동작원리
이 그림은 SOAP 기반 Web Service의 동작 원리를 3단계로 나눈 구조다. 쉽게 설명하면, "서비스를 등록하고" > "찾고" > "사용하는 흐름" 이다.
UDDI 란?
WSDL을 담아두는 Public Registry다. 전역 저장소기 때문에 공개적으로 접근하고 WSDL을 검색할 수 있다.
Web Service Broker : 여기선 UDDI, 서비스 등록 및 검색, 저장, 관리하는 주체
Web Service Provider : 웹 서비스를 구현하여 운영하고 제공하는 주체
Web Service Consumer : 웹 서비스를 요청하는 주체
1. 서비스 제공자는 UDDI에 사용 가능한 WSDL 등록
- 서버 개발자는 배송 조회 같은 기능을 웹서비스로 개발하고, getDeliveryStatus(String trackingNo) 같은 메서드를 제공함.
- 그런 다음 이 웹서비스를 설명하는 WSDL 문서를 자동으로 생성함. (예: https://api.cjlogistics.com/DeliveryService?wsdl)
2. 서비스 사용자는 원하는 서비스를 위해 UDDI를 검색
- 다른 시스템(예:스마트스토어)이 이 서비스를 검색해서 찾아볼 수 있도록 함. (※ 최근에는 UDDI 사용이 줄고, 그냥 WSDL URL을 직접 제공하는 방식이 많음.)
3. 원하는 서비스에 대한 WSDL 다운로드
4. 찾은 WSDL로부터 Stub 코드 생성 및 바인딩(BIND)
- 자바 개발자는 JDK에서 제공하는 wsimport 명령어로 Stub을 생성함
wsimport -keep -p com.cj.stub https://api.cjlogistics.com/DeliveryService?wsdl
5. 클라이언트가 Stub을 통해 실제 Web Service에 SOAP 요청
public class DeliveryCheck {
public static void main(String[] args) {
// 서비스 객체 생성
DeliveryService service = new DeliveryService();
// 서비스 포트(실제 인터페이스) 획득
DeliveryServicePortType port = service.getDeliveryServicePort();
// 요청 객체 생성
GetDeliveryStatusRequest req = new GetDeliveryStatusRequest();
req.setTrackingNumber("1234567890");
// SOAP 요청 실행
GetDeliveryStatusResponse res = port.getDeliveryStatus(req);
// 결과 출력
System.out.println("배송 상태: " + res.getStatus());
}
}
- 여기서 port.getDeliveryStatus(req) 이 한 줄이 실제로 SOAP 메시지를 HTTP로 전송하고, XML 응답을 객체로 파싱해서 반환하는 역할을 함.
6. Web Service는 요청을 받아서 처리 후 SOAP 응답 전송
- 서버에서는 클라이언트가 보낸 SOAP 요청(XML)을 파싱해서, 1234567890 송장번호에 대한 배송 정보를 조회함.
- 그 결과를 다시 SOAP 형식(XML)으로 포장해서 클라이언트에게 응답함.
<soapenv:Envelope>
<soapenv:Body>
<getDeliveryStatusResponse>
<status>배송완료</status>
</getDeliveryStatusResponse>
</soapenv:Body>
</soapenv:Envelope>
WSDL 이란?
웹서비스의 구체적인 내용이 기술되어 있는 문서이고 서비스 제공장소, 서비스 메세지 포맷, 프로토콜들이 기술되어 있다.
- Types : 교환될 메세지 설명, 사용될 데이터 형식 정의
- Interface : operation 정의 (input / output)
- Binding : Interface에 정의된 작업에 대해 메세지 형식과 프로토콜 정의
- Service : WebService URL endPoint 정의
Types
<wsdl:types>
...
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://example.org/calculator"
targetNamespace="http://example.org/calculator"
elementFormDefault="qualified"
attributeFormDefault="qualified">
<xs:element name="AddValuesRequest" type="tns:AddValuesType" />
<xs:element name="AddValuesResponse" type="tns:AddValuesResponseType" />
<xs:complexType name="AddValuesType">
<xs:sequence>
<xs:element name="FirstValue" type="xs:int" minOccurs="1" maxOccurs="1" />
<xs:element name="SecondValue" type="xs:int" minOccurs="1" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="AddValuesResponseType">
<xs:sequence minOccurs="1" maxOccurs="1">
<xs:element name="Result" type="xs:int" />
</xs:sequence>
</xs:complexType>
...
</xs:schema>
</wsdl:types>
type 파트에서는 교환될 메세지를 설명하고, 사용될 데이터 형식을 정의하게 된다.
schema를 통해서 필요한 부분을 import하고 wsdl에서 사용할 데이터 형식을 정의한다.
예시 코드에서는 AddValuesRequest와 AddValuesResponse을 선언하고 데이터의 형식을 <xs:complexType>안에서 정의한다.
[위 코드 예시]
1. AddValuesRequest는 데이터 타입으로 AddValuesType을 사용
2. AddValuesType은 FirstValue와 SecondValue을 자식 요소로 순서대로 가짐
3. FirstValue와 SecondValue는 int타입이며, 최소1개 최대1개만 정의될 수 있음 (값이 반드시 있어야 함)
Interface
Operation, 즉 함수를 정의하게 된다.
<wsdl:interface name="CalculatorInterface">
<wsdl:fault name="fault" element="calc:CalculationFault" />
<wsdl:operation name="AddValues" pattern="http://www.w3.org/ns/wsdl/in-out" style="http://www.w3.org/ns/wsdl/style/iri" wsdl:safe="true">
<wsdl:documentation>Adds up passed values and returns the result</wsdl:documentation>
<wsdl:input messageLabel="in" element="calc:AddValuesRequest" />
<wsdl:output messageLabel="out" element="calc:AddValuesResponse" />
<wsdl:outfault messageLabel="fault" ref="tns:fault" />
</wsdl:operation>
</wsdl:interface>
[위 코드 예시]
1. Interface 이름은 CalculatorInterface
2. 실패 했을 때 type에서 정의 했던 CalculationFalut 타입의 메세지 출력
3. Operation의 이름은 AddValues
4. 함수의 input의 형식은 type에서 정의 했던 AddValueRequest
5. output의 형식은 AddValuesResponse
input과 output의 순서도 중요한데 총 4가지 경우가 있다.
1. One-Way : input만 있는 경우 (클라이언트가 메세지만 보냄)
2. Request-Response : 클라이언트 요청 -> 서버 응답 (input-output)
Binding
<wsdl:binding name="CalculatorBinding" interface="tns:CalculatorInterface" type="http://www.w3.org/ns/wsdl/soap" soap:protocol="http://www.w3.org/2003/05/soap/bindings/HTTP/">
<wsdl:operation ref="tns:AddValues" soap:mep="http://www.w3.org/2003/05/soap/mep/soap-response" />
<wsdl:fault ref="tns:fault" soap:code="soap:Sender" />
</wsdl:binding>
Binding 파트에서는 Interface에 정의된 작업에 대해 메세지 형식과 프로토콜을 정의하게 된다.
[위 코드 예시]
1. CalculatorBinding Interface에 대해 프로토콜로 soap을 사용
Service
<wsdl:service name="CalculatorService" interface="tns:CalculatorInterface">
<wsdl:endpoint name="CalculatorEndpoint" binding="tns:CalculatorBinding" address="http://localhost:8080/services/calculator" />
</wsdl:service>
Service 파트에서는 WebService URL endPoint를 정의하게 된다.
[위 코드 예시]
1. CalculatorService에 대한 요청은 CalculatorEndPoint에서 처리
RESTful API VS SOAP 차이는 뭘까?
SOAP는 위의 규약과 WSDL등의 규칙이 존재하기때문에 데이터 요청을 주고받을 때도 SOAP Standards 를 지켜서 보내야한다.
SOAP 요청 메시지 구조
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:m="http://your-service.com/messages">
<soapenv:Header>
<!-- 인증 정보 같은 메타데이터가 들어갈 수 있음 -->
<m:AuthToken>abc123xyz</m:AuthToken>
</soapenv:Header>
<soapenv:Body>
<m:GetUserInfoRequest>
<m:userId>101</m:userId>
</m:GetUserInfoRequest>
</soapenv:Body>
</soapenv:Envelope>
Envelope | SOAP 메시지 전체를 감싸는 최상위 루트 |
Header | (선택) 인증, 로깅 등 메타정보를 담는 영역 |
Body | 실제 요청 데이터(비즈니스 로직 관련 요청/응답)를 담는 핵심 영역 |
예시) 항공편 검색 SOAP 요청
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:air="http://airline.example.com/schema">
<soapenv:Header/>
<soapenv:Body>
<air:SearchFlightsRequest>
<air:from>ICN</air:from>
<air:to>NRT</air:to>
<air:departureDate>2025-07-15</air:departureDate>
</air:SearchFlightsRequest>
</soapenv:Body>
</soapenv:Envelope>
이게 REST API 였다면, GET /flights/search?from=ICN&to=NRT&date=2025-07-15 이렇게 요청 해야된다.
REST는 HTTP method와 URL 구조에 의미를 부여해서 간결하게 표현할 수 있는 반면, SOAP은 모든 요청이 POST + XML 메시지로 통일되며, WSDL이 정의한 구조를 반드시 따라야 한다.
Spring Boot 예제: WSDL 기반 SOAP 날씨 호출 서비스 만들기
1. XML 스키마 파일 작성
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://weather.soap.com"
targetNamespace="http://weather.soap.com"
elementFormDefault="qualified">
<!-- GetWeatherByLocationRequest 추가 -->
<xs:element name="GetWeatherByLocationRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="nx" type="xs:string"/>
<xs:element name="ny" type="xs:string"/>
<xs:element name="baseDate" type="xs:string"/>
<xs:element name="baseTime" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="GetWeatherByLocationResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="resultCode" type="xs:string"/>
<xs:element name="resultMsg" type="xs:string"/>
<xs:element name="weatherItems" type="tns:WeatherItems"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- 날씨 항목 타입 -->
<xs:complexType name="WeatherItems">
<xs:sequence>
<xs:element name="item" type="tns:WeatherItem" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="WeatherItem">
<xs:sequence>
<xs:element name="baseDate" type="xs:string"/>
<xs:element name="baseTime" type="xs:string"/>
<xs:element name="category" type="xs:string"/>
<xs:element name="fcstDate" type="xs:string"/>
<xs:element name="fcstTime" type="xs:string"/>
<xs:element name="fcstValue" type="xs:string"/>
<xs:element name="nx" type="xs:string"/>
<xs:element name="ny" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
위 WSDL은 날씨 정보를 조회할 수 있는 SOAP 서비스를 정의하고 있다. 전체 구조를 간단히 요약하면 아래와 같다.
- GetWeather, GetWeatherByLocation 두 가지 주요 오퍼레이션을 제공하며, 각 요청(Request)과 응답(Response)에 대해 XML 스키마 타입을 정의하고 있다.
- WeatherItems, WeatherItem 같은 복합 타입도 포함되어 있어, 지역별로 여러 개의 예보 데이터를 한 번에 응답받을 수 있도록 설계돼 있다.
- 마지막으로 <soap:address> 항목을 통해 실제 서비스가 호출될 엔드포인트 URL(http://localhost:8080/ws/weather)이 명시되어 있다.
이처럼 WSDL은 단순한 명세 이상의 역할을 한다.
요청과 응답 메시지 형식은 물론, 호출 가능한 메서드 이름, 네임스페이스, 엔드포인트까지 SOAP 서비스를 사용하는 클라이언트가 알아야 할 거의 모든 정보를 포함하고 있다.
이제 이 WSDL 파일을 기반으로 Java에서 Stub 클래스를 자동 생성하고, 실제로 이 SOAP 서비스를 호출해보자.
2. WSDL 기반 Java 클래스 생성
SOAP 서비스를 호출하려면 WSDL에 정의된 요청/응답 구조에 맞는 자바 클래스가 필요하다. 이를 일일이 수동으로 작성하는 대신, xjc 태스크를 이용해 자동으로 생성할 수 있다.
generateJaxbClasses 태스크는 WSDL 파일을 읽어서 요청/응답에 필요한 JAXB 클래스들을 지정된 패키지에 생성해준다.
즉, WSDL 명세에 맞는 Java 객체를 만들어 SOAP 요청/응답 처리를 가능하게 해주는 필수 작업이다.
[build.gradle]
// WSDL에서 Java 클래스 생성을 위한 태스크
task generateJaxbClasses {
doLast {
def wsdlDir = file("src/main/resources") // 변환할 WSDL 파일이 위치한 디렉토리
def generatedDir = file("src/main/java") // 생성된 Java 파일이 저장될 경로
// JAXB 클래스 생성
ant.taskdef(name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask', classpath: configurations.compileClasspath.asPath)
ant.xjc(destdir: generatedDir, package: 'com.soap.soap.generated') {
schema(dir: wsdlDir, includes: 'weather.wsdl')
} // 실제 WSDL → Java 변환을 수행하는 핵심 작업
}
}
이후 터미널에서 아래 명령어를 입력하면 소스가 생성된다.
./gradle generateJaxbClasses
XSD에서 정의한 타입에 해당하는 Java 파일이 생성됐는지 확인하고 난 뒤 SOAP 엔드포인트를 구현해보자.
2. EndPoint 구현
클라이언트가 SOAP으로 GetWeatherByLocationRequest 요청을 보내면, 메소드가 호출되어 비즈니스 로직을 수행하고 응답(GetWeatherByLocationResponse)을 만들어 반환한다.
@Endpoint
public class WeatherEndPoint {
@PayloadRoot(namespace = "http://weather.soap.com", localPart = "GetWeatherByLocationRequest")
@ResponsePayload
public GetWeatherByLocationResponse getWeatherByLocation(@RequestPayload GetWeatherByLocationRequest request) {
// 비즈니스 로직 구현
GetWeatherByLocationResponse response = new GetWeatherByLocationResponse();
// response.setResultCode(...);
// response.setResultMsg(...);
// response.setWeatherItems(...);
return response;
}
}
3. SOAP 클라이언트 코드 작성
import com.soap.soap.generated.GetWeatherByLocationRequest;
import com.soap.soap.generated.GetWeatherByLocationResponse;
import org.springframework.ws.client.core.WebServiceTemplate;
public class SoapClientExample {
public static void main(String[] args) {
// 1. WebServiceTemplate 인스턴스 생성
WebServiceTemplate template = new WebServiceTemplate();
// 2. 요청 객체 생성 및 값 세팅
GetWeatherByLocationRequest request = new GetWeatherByLocationRequest();
request.setNx("60");
request.setNy("127");
request.setBaseDate("20240601");
request.setBaseTime("1200");
// 3. SOAP 요청 전송 및 응답 수신
// (엔드포인트 URL, 요청 객체, 응답 타입)
GetWeatherByLocationResponse response = (GetWeatherByLocationResponse)
template.marshalSendAndReceive("http://localhost:8080/ws", request);
// 4. 응답 결과 출력
System.out.println("resultCode: " + response.getResultCode());
System.out.println("resultMsg: " + response.getResultMsg());
// ... 필요에 따라 weatherItems 등 추가 출력
}
}
WebServiceTemplate은 스프링에서 제공하는 SOAP 호출 도구다.
marshalSendAndReceive()는 내부적으로 요청 객체를 XML로 변환하고, 응답을 다시 Java 객체로 역직렬화 해준다.
참고문헌
https://brewagebear.github.io/soap-and-wsdl/
Spring 공식 예제로 알아보는 SOAP와 WSDL
GDS(Global Distribution Systems) SOAP와 WSDL SOAP(Simple Object Access Protocol)란? WSDL(Web Services Description Language) 란? Spring Boot SOAP 예시 XML 스키마 파일 작성 XML 파일을 자바 클래스 파일로 만들기 Endpoint 클래스 및
brewagebear.github.io
'Study > Spring' 카테고리의 다른 글
Spring MVC 구조의 처리 과정 정리 (0) | 2025.08.26 |
---|---|
[Spring] RestTemplate 정리 (1) | 2024.09.03 |
redirect와 forward에 대해서(ViewResolver를 곁들인..) (0) | 2024.08.04 |
ModelAndView 정리 (0) | 2024.08.03 |