카테고리 없음

[Spring Framework] SOAP 와 REST(3부작) : 1탄 - SOAP가 뭐야?

공대키메라 2025. 5. 15. 22:01

오늘도 여러 회사의 공고를 기웃거리는 우리의 키메라!

 

오늘 그의 눈에 포착된 것이 있으니... 바로 SOAP!

 

SOAP에 대한 이해를 요구하는 요구사항이 적혀 있기에,

 

키메라는 이에 대해 공부하고 Spring 에서 제공하는 예제를 만들어보고자 한다.

 

 

해당 글은 실제적으로 SOAP를 보고 이해하는 방향으로 작성했다.

 

그러면 떡본 김에 제사 좀 지내보자 숭구리당당 숭당당~

 

 

해당 글을 통해 키메라는 SOAP가 무엇인지,

 

구조와 문법은 어떤지, 

 

마지막으로 어떻게 사용하는지 볼 예정이다.

 


들어가기 전에...

 

WS와 WAS 란?

다들 정말 많이 들어본 면접 단골 문제이다.

 

간단히 필자의 기억을 더듬어 보면... WS는 Web Service, WAS 는 Web Application Service로

 

WS는 단순히 정적 페이지를 제공하는 서버이면,

 

다양한 로직 처리를 요구하는 동적 컨텐츠를 제공하기 위해 만들어진 것이 WAS 이다.

 

URI란?

URI(Uniform Resource Identifier)는 인터넷상의 리소스를 식별하기 위한 문자열이다.

쉽게 말해, 특정 리소스를 고유하게 식별하는 방법을 제공한다.

 

SOAP과 XML에서 URI는 주로 네임스페이스 식별을 위해 사용되며,

실제로 접근 가능한 리소스일 필요는 없고 단시 고유한 식별자로 작동한다.

 

1. SOAP(Simple Object Access Protocol)

인터넷에 공개된 공식 문서에 따르면, SOAP는 분산 환경에서 구조화된 정보를 교환하기 위한 경량 프로토콜이다.

 

다양한 기본 프로토콜을 통해 교환 가능한 메시지 구조를 제공하는 확장 가능한 메시징 프레임워크를 정의하기 위해 XML 기술을 사용한다.

 

여기서 프레임워크는 정말 우리가 아는 spring framework 그런게 아니라 soap가 제공하는 메시지 교환의 구조적 틀을 설명하기 위한 용어다.

 

SOAP는 다른 기술과 프로그래밍 언어를 가진 OS에서 작동하는 어플리케이션들 사이에 communicate하기 위한 한 방법을 제공한다. 주로 기업 간 시스템 간의 메시지를 안전하게 전송하는 데 사용됩니다. 

 

SOAP의 두 개의 주된 디자인 목표는 단순함과 확장성이다. 이 목표 위에 다른 기능은 확장으로 구현한다.

 

 

2. 구조와 문법 규칙(Syntax Rules)

SOAP 메시지는 XML 1.0으로 직렬화할 수 있는 XML 정보집합(infoset)이어야 한다.

 

여기서 XML은 Extensible Markup Language로 W3C에서 개발된, 여러 특수한 목적의 마크업 언어를 만드는 용도로 권장되는 다목적 마크업 언어이다.

 

https://www.ibm.com/docs/en/integration-bus/10.0?topic=soap-structure-message

 

SOAP의 구조는 다음과 같으며 가장 최상위에는 Envelope element가 있다.(mandatory) 

 

Header선택적인 하위 element인데 메시지 경로를 통해 SOAP node에 의해 처리된 어플리케이션과 관련있는 정보를 보내는데 사용된다.

 

Body는 SOAP envelope 하위 element로 필수다.(mandatory)

 

Fault는 SOAP body의 하위 element이며 error를 기록하는데 사용한다.

 

해당 예시 그림에는 fualt 가 적혀있지 않지만 만약 있다면 SOAP body 안에 fault 칸이 생길 것이다.

 

다음 예시를 보자.

 

<?xml version='1.0' Encoding='UTF-8' ?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> 
 <env:Header>
  <m:reservation xmlns:m="http://travelcompany.example.org/reservation" 
		env:role="http://www.w3.org/2003/05/soap-envelope/role/next">
   <m:reference>uuid:093a2da1-q345-739r-ba5d-pqff98fe8j7d</m:reference>
   <m:dateAndTime>2007-11-29T13:20:00.000-05:00</m:dateAndTime>
  </m:reservation>
  <n:passenger xmlns:n="http://mycompany.example.com/employees" 
		env:role="http://www.w3.org/2003/05/soap-envelope/role/next">
   <n:name>Fred Bloggs</n:name>
  </n:passenger>
 </env:Header>
 <env:Body>
  <p:itinerary xmlns:p="http://travelcompany.example.org/reservation/travel">
   <p:departure>
     <p:departing>New York</p:departing>
     <p:arriving>Los Angeles</p:arriving>
     <p:departureDate>2007-12-14</p:departureDate>
     <p:departureTime>late afternoon</p:departureTime>
     <p:seatPreference>aisle</p:seatPreference>
   </p:departure>
   <p:return>
     <p:departing>Los Angeles</p:departing>
     <p:arriving>New York</p:arriving>
     <p:departureDate>2007-12-20</p:departureDate>
     <p:departureTime>mid-morning</p:departureTime>
     <p:seatPreference></p:seatPreference>
   </p:return>
  </p:itinerary>
 </env:Body>
</env:Envelope>

 

 

 

처음 봣을때 아니 뭐야? Envelop, Header. Body, Fault 라매? 뭐가 뭐야 했는데...

 

더 찾아보니 이게 뭔가 전체적인 구조, 그러니까 콜론 (:) 뒤의 요소 이름은 명확하게 있어야 하는건데, 그 앞에 오는 네임 스페이스는 어떤 접두사든 사용이 가능하다.

 

3. 네임스페이스 설정

태그들 안에는 설정도 넣을 수 있다.

 

여기서 xmlns 는 네임스테이스 부분으로 그 뒤에는 개발자가 원하는 접두사를 사용하면 된다.

 

prefix는 자유롭게 선택 가능하지만, SOAP 표준 태그에는 임의의 속성을 추가할 수 없다는 제약이 있다고 한다.

 

위에 예시에서 다음을 다시 보자.

<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">

 

env:Envelope를 통해 보면 Envelope 태그를 선언할 때 prefix를 env로 설정했다.

 

그리고 Envelope 내부에 선언된 xmlns:env="http://www.w3.org/2003/05/soap-envelope" 는 필수로 선언해야 하는 

 

네임 스페이스이다.

 

prefix는 그런데 원하는 대로 해도 된다고 하지 않았나?

 

고로 위를 다음과 같이도 표현할 수 있다.

<kimera:Envelope xmlns:kimera="http://www.w3.org/2003/05/soap-envelope">

 

Envelope 내의 env 네임 스페이스 외에도 다른것들은 필수가 없는지 정리해보았다.

 

이름 필수 네임스페이스
Envelope  URI값이 필수
Header 필요 없음
Body 필요 없음
Fault 필요 없음

 

 

다른건 사실 다 필요없네... 머쓱...

 

물론 내가 임의로 더 작성도 가능하다.

 

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
  
  <!-- Header 태그에 직접 네임스페이스 선언 -->
  <env:Header xmlns:auth="http://example.org/security/authentication"
             xmlns:tx="http://example.org/transaction">
    
    <!-- 인증 정보 헤더 블록 -->
    <auth:Credentials env:mustUnderstand="true">
      <auth:Username>john.doe</auth:Username>
      <auth:Password>secret123</auth:Password>
    </auth:Credentials>
    
    <!-- 트랜잭션 헤더 블록 -->
    <tx:Transaction>
      <tx:Id>TX-2025-05-14-123456</tx:Id>
      <tx:Timestamp>2025-05-14T15:30:45Z</tx:Timestamp>
    </tx:Transaction>
  </env:Header>
  
  <!-- Body 태그에 직접 네임스페이스 선언 -->
  <env:Body xmlns:order="http://example.org/commerce/orders">
    <order:PlaceOrder>
      <!-- 주문 내용 -->
    </order:PlaceOrder>
  </env:Body>
</env:Envelope>

 

내부적으로 네임스페이스를 선언할 수도 있고, sub element 를 그냥 선언해서 사용할 수도 있다.

 

이는 규약에 맞춰서 사용해야 하니... 편한대로 하면 될 듯 하다.

 

 

 

fault의 경우에는 사용시에 sub elements로 와야 하는 것들이 있다. 이는 버전에 따라 다르다고 한다.

 

4. fault - SOAP 1.1 vs SOAP 1.2

 

SOAP 1.1과 SOAP 1.2는 각자 사용시에 필요한 하위 element 가 존재한다.

 

SOAP1.1

  • <faultCode>: 필수적임.
  • <faultString> : 필수적임.
  • <faultActor>: fault를 만든 SOAP node 의 URI를 포함.
  • <detail> : Body element와 관련있는 어플리케이션 관련 에러 정보를 담는다. 


SOAP1.2

  • <Code> : 필수적임. 하위에 <Value>가 오며 <Subcode>는 optional하다.
  • <Reason> : 필수적임. 사람이 읽을 수 있는 형태의 오류 설명 제공. 각 <Text> element는 xml:lang으로 설정 가능.
  • <Node> : 오류를 발생시킨 SOAP 노드의 URI 정보 제공. 최종 수신자가 아닌 SOAP 노드가 오류를 발생시킬 경우 반드시 포함
  • <Role> :  역할을 정의하는 URI 를 포함.
  • <Detail> : SOAP 오류 코드와 관련된 추가 상세 정보 포함. 이 요소의 존재 여부는 SOAP 메시지의 어느 부분이 처리되었는지와 무관

 

사실 1.1보다는 1.2가 좀 더 명확하고 많이 쓰일듯 하다.

 

아무래도 오래된건데... 좀 자세히 알아야지 않늬..? ㅠㅠ

 

고로 1.2를 기준으로 다음 예시를 보자.

 

<env:Fault xmlns:env="http://www.w3.org/2003/05/soap-envelope">
  <env:Code>
    <env:Value>env:Sender</env:Value>
    <env:Subcode>
      <env:Value>err:ValidationError</env:Value>
    </env:Subcode>
  </env:Code>
  <env:Reason>
    <env:Text xml:lang="en">Input validation failed</env:Text>
    <env:Text xml:lang="ko">입력 유효성 검사 실패</env:Text>
  </env:Reason>
  <env:Node>http://example.org/paymentService</env:Node>
  <env:Role>http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver</env:Role>
  <env:Detail>
    <err:ValidationDetails xmlns:err="http://example.org/errors">
      <err:Field>creditCardNumber</err:Field>
      <err:Message>Invalid card number format</err:Message>
    </err:ValidationDetails>
  </env:Detail>
</env:Fault>

 

자 좀 읽히는가요? 후훗...

 

그럼 이제 완벽히 Envelope부터 포함한 예시를 보도록 하자.

 

5. 복잡한 예시

예시1

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
  <env:Header>
    <!-- 응답 추적을 위한 헤더 정보 -->
    <t:Transaction xmlns:t="http://example.org/transaction" 
                  env:mustUnderstand="true">
      <t:Id>TX-2025-05-14-789012</t:Id>
      <t:Timestamp>2025-05-14T16:42:23Z</t:Timestamp>
    </t:Transaction>
  </env:Header>
  
  <env:Body>
    <env:Fault>
      <!-- 1. Code 요소: 소프트웨어가 처리할 오류 코드 -->
      <env:Code>
        <env:Value>env:Sender</env:Value>
        <env:Subcode>
          <env:Value xmlns:err="http://example.org/errors">err:ValidationError</env:Value>
        </env:Subcode>
      </env:Code>
      
      <!-- 2. Reason 요소: 사람이 읽을 수 있는 다국어 오류 메시지 -->
      <env:Reason>
        <env:Text xml:lang="en">Payment validation failed</env:Text>
        <env:Text xml:lang="ko">결제 유효성 검사 실패</env:Text>
      </env:Reason>
      
      <!-- 3. Node 요소: 오류를 발생시킨 서비스 노드 식별 -->
      <env:Node>http://example.org/services/payment</env:Node>
      
      <!-- 4. Role 요소: 노드가 수행 중이던 역할 -->
      <env:Role>http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver</env:Role>
      
      <!-- 5. Detail 요소: 응용 프로그램 특정 오류 정보 -->
      <env:Detail>
        <pay:PaymentError xmlns:pay="http://example.org/payment/errors">
          <pay:ErrorCode>PAY-4023</pay:ErrorCode>
          <pay:Field>creditCard.number</pay:Field>
          <pay:Severity>Error</pay:Severity>
          <pay:Description>The provided credit card number is invalid or expired</pay:Description>
          
          <!-- 추가 세부 정보 -->
          <pay:ValidationDetails>
            <pay:ExpectedFormat>XXXX-XXXX-XXXX-XXXX</pay:ExpectedFormat>
            <pay:SupportedCardTypes>
              <pay:CardType>Visa</pay:CardType>
              <pay:CardType>MasterCard</pay:CardType>
              <pay:CardType>Amex</pay:CardType>
            </pay:SupportedCardTypes>
          </pay:ValidationDetails>
          
          <!-- 문제 해결 가이드 -->
          <pay:ResolutionSteps>
            <pay:Step>Check that the card number format is correct</pay:Step>
            <pay:Step>Verify that the card is not expired</pay:Step>
            <pay:Step>Ensure the card type is supported by our system</pay:Step>
          </pay:ResolutionSteps>
        </pay:PaymentError>
      </env:Detail>
    </env:Fault>
  </env:Body>
</env:Envelope>

 

예시2

<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"
              xmlns:m="http://www.example.org/timeouts"
              xmlns:xml="http://www.w3.org/XML/1998/namespace">
 <env:Body>
  <env:Fault>
   <env:Code>
     <env:Value>env:Sender</env:Value>
     <env:Subcode>
      <env:Value>m:MessageTimeout</env:Value>
     </env:Subcode>
   </env:Code>
   <env:Reason>
     <env:Text xml:lang="en">Sender Timeout</env:Text>
   </env:Reason>
   <env:Detail>
     <m:MaxTime>P5M</m:MaxTime>
   </env:Detail>    
  </env:Fault>
 </env:Body>
</env:Envelope>

 

 

namespace 를 직접 입력해줘도 되지만 그럴 경우 짜증이 많이 날거같다. 

 

Java 에서 코드로 치면 field에 선언 안하고 쓸데없이 annotation으로 선언해서 찾게 만드는거... 그런거는 우우... 별로라고 생각한다.

 

주의점은 namespace에는 uri로만 선언을 하고, 

 

uri의 prefix를 바탕으로 sub-element들이 만들어지고 있는 점을 유의하자.

 

6. 더더더! 복잡한 속성들 같이 사용하기

사실 이것만으로 끝난게 아니다.

 

속성들이나 요소들에 대해서 좀 더 집고 넘어가야한다.

 

가장 많이 쓰이는것이라고 하는데, 다음이 있다.

 

중요 속성

  • encodingStyle : 메시지 또는 그 일부의 직렬화 규칙을 지정
  • role : 노드 역할 정의 
  • mustUnderstand: 필수 이해 여부(true, false)
  • relay : 중계 노드가 처리하지 않은 헤더 전달 여부
  • qname : 이해하지 못한 헤더 블록의 정규화된 이름을 지정.

 

또  SOAP1.2에서는 fualt code사용시 다음과 같이 표준 오류 코드를 정의하고 있다.

 

표준 오류 코드

  • env:Sender : 메시지가 잘못 구성되었거나 필요한 정보가 누락되었음
  • env:Receiver : 메시지를 처리하는 동안 서버 측에서 문제가 발생
  • env:DataEncodingUnknown : 메시지에 사용된 인코딩이 지원되지 않음
  • env:VersionMismatch : SOAP 버전이 맞지 않음

 

이를 사용한 예시는 다음과 같다.

 

시나리오는 궁금하면 펼쳐보면 된다.

 

더보기

SOAP 메시지 처리 시나리오 (확장 버전)

시나리오 배경

글로벌 금융 거래 시스템에서 국제 송금 요청을 처리하는 과정입니다.

 

이 시나리오는 모든 주요 SOAP 속성과 오류 코드를 포함합니다.

참여 시스템 및 역할

  1. 클라이언트 뱅킹 애플리케이션
    • 송금 요청 SOAP 메시지 생성
    • 역할: 메시지 발신자
  2. 경계 보안 게이트웨이
  3. 메시지 라우터
  4. 규정 준수 로깅 서비스
  5. 사기 탐지 엔진
  6. 계좌 관리 시스템

 

요청 SOAP 메시지 예시

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope 
    xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
    xmlns:sec="http://example.org/banking/security"
    xmlns:tx="http://example.org/banking/transaction"
    xmlns:log="http://example.org/banking/logging"
    xmlns:fraud="http://example.org/banking/fraud"
    xmlns:enc="http://www.w3.org/2003/05/soap-encoding"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xml="http://www.w3.org/XML/1998/namespace"
    soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
    
  <soap:Header>
    <!-- 보안 인증 - 모든 중간 노드에서 처리 필수 -->
    <sec:Credentials soap:mustUnderstand="true" soap:role="http://www.w3.org/2003/05/soap-envelope/role/next">
      <sec:BankID>GLOBBANK12345</sec:BankID>
      <sec:Token>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...</sec:Token>
      <sec:Timestamp>2025-05-15T13:45:30Z</sec:Timestamp>
    </sec:Credentials>
    
    <!-- 로깅 정보 - 로깅 서비스만 처리, 처리 못해도 진행 가능 -->
    <log:AuditTrail soap:role="http://example.org/banking/logging" soap:mustUnderstand="false" soap:relay="true">
      <log:RequestID>FT-20250515-78901234</log:RequestID>
      <log:Channel>OnlineBanking</log:Channel>
      <log:UserIP>198.51.100.234</log:UserIP>
    </log:AuditTrail>
    
    <!-- 사기 탐지 정보 - 사기 탐지 엔진만 처리 -->
    <fraud:RiskAssessment soap:role="http://example.org/banking/fraud" soap:relay="true">
      <fraud:UserDeviceID>DEV-HASH-2da7f846e</fraud:UserDeviceID>
      <fraud:GeoLocation>37.7749,-122.4194</fraud:GeoLocation>
      <fraud:UserBehaviorScore>87</fraud:UserBehaviorScore>
    </fraud:RiskAssessment>
    
    <!-- 트랜잭션 정보 - 최종 수신자만 처리 -->
    <tx:TransactionControl soap:role="http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver" soap:mustUnderstand="true">
      <tx:ID>TX-INTL-20250515-123456</tx:ID>
      <tx:Priority>High</tx:Priority>
      <tx:ExpiryTime>2025-05-15T14:45:30Z</tx:ExpiryTime>
    </tx:TransactionControl>
  </soap:Header>
  
  <soap:Body>
    <tx:InternationalTransfer>
      <tx:SourceAccount>
        <tx:AccountNumber>CH93-0000-0000-0000-0000-0</tx:AccountNumber>
        <tx:Currency>CHF</tx:Currency>
        <tx:HolderName>Swiss Corporate AG</tx:HolderName>
      </tx:SourceAccount>
      
      <tx:DestinationAccount>
        <tx:IBAN>GB29NWBK60161331926819</tx:IBAN>
        <tx:SWIFT>NWBKGB2L</tx:SWIFT>
        <tx:HolderName>British Trading Ltd</tx:HolderName>
      </tx:DestinationAccount>
      
      <tx:Amount>250000.00</tx:Amount>
      <tx:SourceCurrency>CHF</tx:SourceCurrency>
      <tx:TargetCurrency>GBP</tx:TargetCurrency>
      <tx:ExchangeRate>1.234</tx:ExchangeRate>
      <tx:TransferPurpose xml:lang="en">International procurement payment</tx:TransferPurpose>
    </tx:InternationalTransfer>
  </soap:Body>
</soap:Envelope>

 

 

요청 SOAP 메시지 예시를 잘 보면 상단에는 네임스페이스를 명시했다.

 

각각 header, body 에서 sub element들을 파악하는데, 여기서 어떤것과 이 message 들이 연결되어 있는지 확인이 가능하다.

 

 

예를 들어, sec:Credentials 을 보자.

 

이 헤더를 수신한 노드는 반드시 이해하고 처리해야 한다. 이해하지 못하면 오류를 반환해야 하며,

 

메시지 경로의 모든 중간 노드가 이 헤더를 처리해야 한다.

 

 

다른 예시로 log:AuditTrail 을 보자.

 

이 헤더는 로깅 역할을 수행하는 노드만 처리해야 한다.

 

로깅 역할을 하는 노드가 이 헤더를 이해하지 못해도 오류를 반환하지 않고 계속 진행할 수 있다.

 

 


 

 

사실 공식문서를 보려고 햇는데 exmaple을 만들다가 만 처참한 수준에 이해를 못하겠어서 다른 내용을 더 눈여겨 보았다.

 

SOAP Version1.2가 W3C recommendation 으로 2007년 4월 27일라고 적혀 있으니 이 때 글을 적은 것으로 짐작한다.

(이거보고 어떻게 만들어서 햇냐... 옛날엔 대단햇네 참나~)

 

심지어 엄청 딱딱하게 작성되어 있고 영어니 하나도 이해 안돼!

 

그래서 클로드AI의 도움을 많이 받았다.

 

다음에는 SOAP의 많이 비교되며, 현재 웹을 주름잡고 있는 REST에 대해서 알아보고자 한다.

 

그리고 그 다음! Spring 으로 SOAP를 구현하는 방법에 대해 알아볼 것이다.

 

출처:

https://www.ibm.com/docs/en/integration-bus/10.0?topic=soap-structure-message

https://www.w3.org/TR/soap12-part1/

https://www.w3schools.com/xml/xml_soap.asp

https://brewagebear.github.io/soap-and-wsdl/

https://ko.wikipedia.org/wiki/XML