우선 이야기를 시작하기 전에 아래 두개의 Soap 요청에 대해서 살펴보기 바란다.
요청 메시지 샘플 1
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<ws:getUser xmlns:ws="http://ws.demo.amf.archnal.com/">
<userNo>1</userNo>
</ws:getUser>
</soapenv:Body>
</soapenv:Envelope>
요청메시지 샘플 2
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<soapenv:Header/>
<soapenv:Body>
<getUser xmlns="http://ws.demo.amf.archnal.com/">
<userNo>1</userNo>
</getUser>
</soapenv:Body>
</soapenv:Envelope>
두 개의 차이점은 getUser 와 userNo의 관계이다.
첫 번째 요청은 userNo의 네임스페이스가 없다. getUser가 ws prefix를 가지고 있기 때문이다.
두 번째 요청은 userNo의 네임스페이스가 'http://ws.demo.amf.archnal.com/'으로 설정되어 있다.
따라서 두 개가 다른 요청이기 때문에 둘 중 하나는 에러가 발생한다.
나는 엘리먼트에 네임스페이스를 지정할 때 prefix를 생략하는 버릇이 있다. localPart와 namespaceURI 만 지정되면 유일한 노드가 되기 때문에 굳이 prefix를 사용하지 않아도 된다는 생각때문이다. 그런 작은 고정관념이 때때로 이런 치명적인 버그를 만들어 낸다.
userNo가 getUser와 같은 네임스페이스를 사용하거나 적어도 다른 네임스페이스라도 사용하고 있다면 에러가 발생하지 않았을 것이다. 하지만 아무런 네임스페이스도 사용하고 있지 않기 때문에 getUser에 prefix를 사용하지 않아서 문제가 발생하게 된 것이다. 이럴 경우에는 반드시 getUser의 prefix를 사용해 주어야 한다. prefix는 굳이 tns가 아니더라도 임의의 문자열로 지정하면 된다.
그럼 웹서비스는 왜 가장 하위 노드에 대해서는 네임스페이스를 만들지 않을까?
사실 웹서비스가 만들지 않은 게 아니라 안 만들도록 스키마를 정의했기 때문이다.
아래의 xs:schema의 속성중에 elementFormDefault="unqualified" 라고 지정된 부분을 qualified로 변경해 주면 된다. CXF에서도 이와 관련하여 설정하는 부분이 있을 것으로 생각되지만, 아직은 설정 방법을 찾아 보지 않았다. 하지만 표준 API의 디폴트 값은 특별한 경우가 아니라면 함께 준수해 주는 편이 좋다. 디폴트 값을 결정한 사람들도 많은 고심 끝에 의미를 부여했기 때문에 그 노력을 믿고 함께 따라 주는 것도 개발자의 미덕이 아닐까 생각한다.
그리고 추가적으로 한 가지 더 살펴보자 네임스페이스와 관련된 클래스가 javax.xml.namespace.QName이다. QName은 namespaceURI, localPart, prefix를 설정할 수 있도록 되어 있다. 하지만 상위 노드의 네임스페이스에 prefix를 지정하지 않을 경우에 네임스페이스를 갖지 않는 하위 노드를 만들어 낼 수 없다.
QName은 namespaceURI와 prefix를 지정하지 않게 되면 상위 노드의 네임스페이스 정보를 그대로 사용하기 때문이다. 특별한 경우가 아니라면 네임스페이스를 지정할 때 조그만 더 상상력을 발휘하여 prefix를 지정하는 습관을 갖도록 하자.
<wsdl:types>
<xs:schema
elementFormDefault="unqualified"
targetNamespace="http://ws.demo.amf.archnal.com/"
version="1.0"
xmlns:tns="http://ws.demo.amf.archnal.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="getUser" type="tns:getUser" />
<xs:element minOccurs="0" name="user" type="tns:sampleUser" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="sampleUser">
<xs:sequence>
<xs:element minOccurs="0" name="email" type="xs:string" />
<xs:element minOccurs="0" name="loginId" type="xs:string" />
<xs:element minOccurs="0" name="loginPassword" type="xs:string" />
<xs:element minOccurs="0" name="name" type="xs:string" />
<xs:element name="userNo" type="xs:long" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="getUser">
<xs:sequence>
<xs:element name="userNo" type="xs:long" />
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>