오늘은 스프링프레임워크에서 CXF를 이용하여 SOAP기반의 웹서비스를 만드는 과정에 대해 소개합니다. 웹서비스의 전반적인 개요는 포스팅 SOAP 기반 웹서비스 구축하기 을 참고하시면 좋습니다. 이 글은 웹서비스 구축에 초점을 맞추었습니다.

웹서비스는 서비스를 처리하는 부분(Service Provider)과 서비스를 요청하고 처리 결과를 표현하는 부분(Service Consumer/Presenter)이 분리되어 있더라도, Web을 통해 통신하여 실행되는 시스템의 컴포넌트 묶음을 말합니다. 단순히 MVC와 같이 Model과 View, Controller를 분리한 것이 아닌, Back-end Side와 Front-end Side를 컴포넌트 뿐 아니라 개발환경까지 물리적으로 분리 된 상태에서 실행되는 시스템입니다. 몇 달 전 웹서비스 프로젝트에 참여하였을 때, 저희는 서버사이드를 개발하고 인도의 개발사에서 UI를 개발했습니다. 그렇다면 저희가 제공하는 웹서비스를 그들은 어떻게 받아서 처리할 수 있을까요? 아래 그림을 통해 WebService 원리를 살펴봅니다.

그림1. 웹서비스의 구조

그림 1과 같이 Web Service Consumer는 Web Service Provider와 통신을 합니다. 이 때의 데이터는 SOAP으로 바인딩 됩니다. 핵심은 WSDL(Web Services Description Language)입니다. WSDL은 웹서비스를 제공하는 Server와 사용하는 Client을 연결해 주는 문서입니다. 시스템 요구사항대로 구현된 서비스들의 명세를 WSDL에 담아 명시해두면, 서비스를 요청하는 쪽에서 이 문서를 보고 해당 서버의 서비스는 어떤 것들이 있고, 각 서비스들의 파라미터와 리턴객체은 어떤 타입으로 주고 받는지 확인합니다. 즉,  서비스를 어떤형태로 요청하고 응답받아서 처리할 것인지는 WSDL을 통해 결정할 수 있습니다.

그림2. 웹서비스 시스템 구성

웹서비스를 구현하기 위해서는 그림1과 같은 논리적인 구조 뿐 아니라, 어떤 기술을 사용할지에 대한 고민도 필요합니다. 웹서비스 프레임워크와 OXM을 어떻게 결정하느냐에 따라 성능과 구현 효율이 현저하게 달라질 수 있기 때문입니다.
오늘 소개할 예제는 그림2와 같은 웹서비스 시스템 구성을 갖추었습니다. Spring을 사용하였지만 Spring-WS 대신 CXF 프레임워크를 사용하였고, Spring은 POJO Container로 적용하여 빈 설정의 효율을 높였습니다. CXF는 웹서비스를 제공해주는 웹서비스 프레임워크입니다. 대표적인 웹서비스 프레임워크로는 CXF, AXIS, Spring-WS가 있습니다. 세 프레임워크의 자세한 차이는 http://architects.dzone.com/articles/apache-cxf-vs-apache-axis-vs를 참고하시기 바랍니다.

그림3. OXM의 원리

웹서비스는 Consumer가 WSDL에서 검색한 Service에 요청XML을 보내면, Provider가 이 문서를 객체로 변환해서 처리를 한 후 다시 Client에게 응답XML을 보내는 과정을 가집니다. 때문에 서비스요청을 표준문서인 XML로 Binding(Marshaling 혹은 Serialize)하고, 그 문서를 다시 시스템에서 처리 가능한 오브젝트로 Binding(Demarshaling 혹은 Deserialize)하는 과정이 반드시 필요합니다. 따라서 OXM은 웹서비스에서 핵심적인 역할을 담당하고 있습니다.

OXM은 Jibx, Castor, JAXB 등 다양한 종류가 있는데, 이 중에서도 JAXB를 선택한 이유는 웹서비스 플랫폼인 CXF에 가장 적용하기 편리한 OXM이기 때문입니다. 뿐만 아니라 JAXB는 Java Architecture for XML Binding의 약자로서 JAVA구조에 가장 적합하기 때문에 JAVA로 구현하는 웹서비스에서 적용하기 편리한 기술입니다.


웹서비스 시스템을 구성할 기술을 선택하였으니, 이제 웹서비스 구축 방법에 대해 알아봅니다.

1. 웹 프로젝트를 생성

: 저는 Spring Web Maven Project를 생성하였습니다. Eclipse에 STS plugin을 설치하면 쉽게 생성할 수 있습니다.

그림4. Spring Web Maven Project 생성

2. CXF Dependency 추가

:  pom.xml 에 CXF관련 dependency를 추가합니다. dependencies 탭에서 Add버튼을 눌러 검색하여 추가하거나, 아래 소스코드를 복사하여 붙인 후 Maven Update를 하면 자동으로 관련 라이브러리들이 다운로드 됩니다.

<!-- CXF -->
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-frontend-jaxws</artifactId>
	<version>2.7.11</version>
</dependency>
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-transports-http</artifactId>
	<version>2.7.11</version>
</dependency>

3. Sevlet 설정

: web.xml 서버로 들어오는 요청을 CXF-Servlet으로 연결하는 설정을 추가해야 합니다. 기본으로 Dispatcher-Servlet이 추가되어 있다면 이를 삭제하거나, CXF-Servlet을 url pattern으로 구분하여 추가해서 해당 url일 경우에만 CXF로 연결되도록 설정할 수 있습니다.

<servlet>
  <servlet-name>cxf</servlet-name>
  <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>cxf</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>

4. WebService Annotation을 활용하여 서비스 구현

: 웹서비스 구현체에 @WebService Annotation을 붙이고 그 안에 서비스 명, 네임스페이스, Port 명 등 여러 정보들을 원하는대로 설정할 수 있으며, 설정하지 않아도 기본값으로 자동으로 생성되므로 편리합니다. 또한 @WebParam을 서비스 파라미터 타입 앞에 쓰면 XML에 원하는 이름으로 표현할 수 있습니다. 만약 @WebParam을 설정하지 않으면 arg0, arg1... 과 같은 형태로 생성되기 때문에 요청XML을 작성하는데 어려움이 생깁니다. 따라서 정확한 명시를 위해 @WebParam 이름은 반드시 설정하도록 합니다. 아래와 같이 적절한 Annotation을 사용하여 서비스 인터페이스와 구현체 클래스를 생성합니다. 웹서비스 어노테이션에 경고가 없어지지 않는다면, dependency에 javaee-web-api를 설정하여 내려받으면 더욱 편하게 작업하실 수 있습니다.

@WebService(endpointInterface = "ws.HelloWs", serviceName = "HelloWs", portName="HelloPort", targetNamespace="nextree.co.kr")
public class HelloWsLogic implements HelloWs {

	public String helloWebService(@WebParam(name="user") User user) {
		return user.getName() + "(" + user.getEmail() +")님 안녕하세요";
	}
}

5. WebService Endpoint Bean 등록

: applicationContext.xml 또는 본인이 설정한 Spring Bean Configuration File에 WebService의 Endpoint로 사용할 서비스구현체를 빈으로 등록합니다. 해당 구현체의 위치를 등록하고, 해당 서비스에 접근할 수 있는 url을 address속성에 지정합니다.  JAXWS SOAP 바인딩 설정도 함께 잡아줍니다. 그리고 아래 소스코드처럼 CXF 파일을 import하여 CXF관련 설정을 추가합니다.
여기서 주의할 점은 지정된 설정파일에 추가해야된다는 점입니다. web.xml에 Application Context File에 대한 어떤 설정도 없다면 기본적으로 applicationContext.xml 파일을 탐색하여 참조하기 때문에, 추가한 파일이 web.xml에 제대로 설정되어 있는지 확인하기시 바랍니다.

<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

<jaxws:endpoint id="helloWs" implementor="ws.logic.HelloWsLogic" address="/helloWs">
	<jaxws:binding>
		<soap:soapBinding style="document" use="literal" version="1.1" />
	</jaxws:binding>
</jaxws:endpoint>

6. WSDL 확인

: 서버를 띄우고, CXF를 통해 자동으로 생성된 wsdl을 확인합니다. Endpoint Bean의 address를 url뒤에 붙이고 "?wsdl"을 덧붙여 익스플로러 URL창에 입력합니다.  그럼 해당 주소에 WSDL이 생성된 것을 확인할 수 있습니다.

그림5. 생성된 wsdl 확인

7. SoapUI로 XML 확인

: WebService Publisher는 UI가 없기 때문에 명확한 테스트를 하기 어렵습니다. 물론 개발자가 단위테스트는 진행할 수 있지만, 팀원들이나 현업과 테스트를 공유하기 위해서 Soap UI 웹서비스 테스트 프로그램을 사용하면 WebService Publisher가 제공하는 XML과 입력에 따른 결과도 확인 할 수 있어서 편리합니다. SoapUI는 무료버전과 유료버전을 나누어 배포하기 때문에 무료로도 간편하게 설치할 수 있습니다.(Soap UI 다운로드 URL: http://sourceforge.net/projects/soapui/files/)

그림6. SoapUI 프로젝트 생성

SoapUI는 wsdl 주소만 입력하여 편리하게 프로젝트를 생성할 수 있습니다. 새로운 프로젝트가 생성되면 메소드별로 테스트 요청파일이 자동 생성됩니다.

그림7. Soap UI 테스트 화면 구성

요청할 서비스를 선택하고 XML에 "?"로 명시된 부분에 요청할 값을 입력합니다. 그 후 좌측 상단에 Submit버튼을 클릭하면 응답 XML이 출력됩니다. 또한 응답시간까지 확인할 수 있기 때문에 성능검증에도 효율적인 툴입니다.

여기까지 WebService Publisher 구현하는 방법에 대해 알아봤습니다. 프레임워크와 라이브러리를 활용하여 쉽게 구현할 수 있습니다.
Spring Framework는 DI/IOC 기능을 통해 빈 설정 파일에 웹서비스 Endpoint만 정의해주면 구현체에 웹서비스 설정을 주입해주기 때문에, WebService 구현이 간결해지도록 합니다. 또한 CXF와 같은 WebService Framework는 편리한 라이브러리를 제공하고 wsdl를 자동 생성합니다. JAXB는 자바의 객체를 XML로 매핑하여 Client에게 전송해주고, Client가 보낸 요청 XML을 다시 자바 객체로 매핑해줍니다. 이처럼 웹서비스는 직접 구현해보면 실행원리를 쉽게 이해할 수 있습니다.

프로젝트에 참여하면 시스템의 설정보다는 비즈니스 로직을 구현하는 시간이 많습니다. 따라서 자신이 참여한 프로젝트라도 시스템의 구성을 정확히 모르는 경우도 발생합니다. 저도 웹서비스 프로젝트에 참여했었지만, 이렇게 다시 되돌아보니 배운 점들이 많습니다. 원리와 구성을 알면 사용법은 자연스럽게 익힐 수 있다는 것을 깨달았습니다.

이 글이 웹서비스를 공부하시는 분들께 작은 도움이 되기를 바랍니다. 끝까지 읽어주셔서 감사합니다.

참고 문서

Spring 5 for Beginner
나무소리에서 제작한 Spring 5 기초 강의입니다. Spring 프레임워크의 핵심 내용과 주요 프로젝트(Spring Framework, Spring MVC, Spring Boot, Spring Data JPA)에 대해 학습합니다.