알고보면 쉬운 웹서비스
웹 서비스를 적용한 애플리케이션을 구축하는 것은 쉽지 않습니다. 그러나 웹서비스의 개념과 구성을 큰 그림으로 이해한다면, 웹서비스를 구축하는 과정이 다르게 느껴질 수 있습니다. 오늘은 그동안 웹서비스를 공부하면서 개운치 않았던 부분들을 긁어드리려 합니다. 알고보면 쉬운 웹서비스에 대해 함께 살펴봅니다.
왜 웹 서비스를 선택할까요?
먼저 왜 웹서비스를 사용하는지에 대한 궁금증부터 해소해볼까요. 우리가 웹서비스를 사용하는 목적 중 핵심은 바로 **플랫폼 중립(neutrality)**입니다. 하나의 서비스를 제공하면, 다양한 플랫폼의 클라이언트가 서비스를 받아서 사용할 수 있도록 위함입니다. 외부 애플리케이션의 기능을 연결하기 위해서는 여러 기술적 제약이 따르기 마련입니다. 하지만 현재, 많은 애플리케이션은 PC 뿐 아니라 모바일기기와 태블릿 PC 등 다양한 기기가 사용 가능합니다. 하나의 기능을 각 플랫폼에 맞게 붙이는 것은 아주 비효율적이기 때문에, 플랫폼에 상관없이 기능을 제공하고 사용할 수 있는 기술이 필요했습니다. 이러한 목적을 가지고 애플리케이션을 서비스로 제공하자며 등장한 아키텍쳐가 **SOA(Service Oriented Archetecture)**이며, 이를 실현시키는 방법이 바로 웹서비스인 것입니다.
하지만 웹서비스를 이해함에 있어서, 플랫폼 중립만큼 중요한 것이 독립성입니다. 플랫폼 중립을 이루겠다는 것은 서비스의 기술 셋을 알지 못해도 클라이언트는 서비스를 사용할 수 있도록 해주겠다는 의미가 담겨있습니다. 클라이언트가 플랫폼에 관한 결합도를 제거하여 재사용성을 크게 높일 수는 있었지만, 효율적인 상호작용을 위해서는 플랫폼 이외의 발생하는 결합도를 고려해야 합니다. 즉 기술셋 뿐 아니라 서비스를 사용하기 위해서 알고 있어야 하는 것들을 최소화시키는 것이 좋습니다. 그래야만 클라이언트와 서버간의 느슨한 결합을 이룰 수 있고 서버와 클라이언트가 각각 독립적인 진화를 지속할 수 있습니다.
예를 들어, 웹서비스 소유자가 서비스의 데이터 타입을 하나 변경했다고 가정합니다. 클라이언트는 변경된 데이터타입을 적용시키기 위해서 클라이언트 쪽의 로직을 수정해야만 합니다. 클라이언트는 단순히 UI를 표현하는 애플리케이션일 수도 있고, 또 다른 기능을 연결하는 애플리케이션일 수도 있습니다. 클라이언트 쪽에서도 충분히 기능구현에 대한 복잡함이 존재하고 있습니다. 그런데 서비스의 진화에 영향을 받게 되면 그림1과 같이 클라이언트는 서비스보다도 무수히 많은 변화를 적용해야만 합니다. 즉, 좋은 웹서비스란 기존 클라이언트의 중단없이 서비스를 진화시킬 수 있어야 합니다. 이렇게 웹서비스는 독립성을 강조하고 있습니다. 때문에 제대로 웹서비스를 설계한다면 서버와 클라이언트가 독립적으로 진화할 수 있기 때문에, 유지보수 및 재사용성이 큰 이점이 됩니다.
웹서비스는 어떻게 연결될까요?
이번엔 비즈니스 로직의 흐름에서 웹서비스가 어떻게 자리잡고 있는지 알아봅니다. 웹서비스의 서비스와 클라이언트는 어떠한 레이어로 제한되어 있지 않고 기능을 제공하고 받는 역할에 따라 구분합니다. 반드시 서비스 프로바이더는 비즈니스로직의 구현체가 있고 클라이언트는 UI구현만 있는 것은 아닙니다. 하나의 기능을 처리하기 위해서 여러 웹서비스 호출이 일어날 수 있고, 클라이언트는 또다른 웹서비스를 제공하는 프로바이더가 될 수 있습니다. 그래서 웹서비스는 어딘가 위치하고 있는 비즈니스 로직의 흐름의 한 영역이라고 말합니다.
위 그림2와 같이 웹서비스는 비즈니스로직과 비즈니스로직을 연결합니다. 먼저 서비스 소유자는 제공하려는 비즈니스 로직을 구현합니다. 이렇게 바로 제공할 수도 있지만, 웹서비스로 제공하기 위해서는 인터페이스에 제공하려는 서비스를 나열합니다. 그럼 서비스를 클라이언트들이 사용할 수 있도록 특정 위치에 제공해야 합니다. 웹서비스의 특징 중 하나는 서비스가 네트워크상의 공간에 위치하고 있어야 한다는 것입니다. 그래서 웹서비스는 서비스를 설명하는 언어인 WSDL을 사용합니다. WSDL을 통해 서비스를 구성하는 오퍼레이션과 요청/응답 메시지 구조, 데이터 타입 등을 인지할 수 있습니다. 때문에 WSDL이 어디에 있는지만 안다면 바로 적용해서 서비스를 사용할 수 있기 때문에, 적용이 아주 편리합니다.
그럼 WSDL은 어디에 위치하고 있을까요? 흔히 WSDL이 위치하는 곳을 UDDI(Universal Description Discovery & Integragion)라고 설명하는 경우가 많습니다. 하지만 WSDL은 UDDI에 있을 수도 있고 혹은 없을 수도 있습니다. UDDI는 웹서비스 공개저장소라고 생각하시면 이해가 쉽습니다. WSDL을 찾을 수 있는 마켓이라고 보시면 됩니다. 클라이언트가 UDDI에서 WSDL을 Find해서 호출한다는 문구를 많이 보셨을 겁니다. UDDI는 여러 WSDL을 가지고 있는 마켓이기 때문에 이 곳에서 원하는 WSDL을 이름별, 카테고리별로 검색하고 사용하는 공간입니다. 원하는 서비스를 찾아서 적용한다는 의미가 바로 이것입니다. 하지만, UDDI가 아니더라도 네트워크상으로 접근이 가능한 곳에 위치시킨다면 웹서비스는 성립됩니다. 따라서 반드시 WSDL이 UDDI에 존재하는 것은 아닙니다.
실무에서는 주로 서비스 개발사와 클라이언트 개발사 사이의 협약을 통해 WSDL의 위치를 공개합니다. WSDL은 단순히 서비스에 관련한 정보를 가지고 있기 때문에, 프로젝트 초기에 웹서비스를 제공하는 쪽과 받는 쪽의 협약이 필요합니다. 이러한 과정을 서비스 협약이라고 합니다. 서비스 협약을 통해서 웹서비스의 위치, 성능, 품질 등에 관하여 논의합니다. 하지만 서비스 협약에서는 서비스를 호출할 때 어떻게 보내야하고 어떤 형태로 응답받는지에 대한 세부내용은 논의하지 않습니다. 이 것은 WSDL을 통해 유추할 수 있기 때문입니다. 그렇다면 실제 프로젝트에서 WSDL만 가지고 어떻게 웹서비스를 연결할 수 있는지 알아봅니다.
우리는 어떻게 웹서비스를 호출할 수 있을까요?
앞에서 웹서비스는 WSDL로부터 서비스에 요청하고 응답받는 구조를 유추할 수 있다고 했습니다. 그렇다면 실무에서 클라이언트를 개발할 때 WSDL을 어떻게 적용하는지 알아봅니다.
클라이언트가 서비스 프로바이더의 서비스를 연결하기 위해서는 어떤 형태로 웹서비스를 보내야하는지 어떤 형태의 응답이 오는지 등을 알고 있어야 합니다. 하지만 개발자가 직접 이것을 인지하고 개발하는것은 어려움이 있을 뿐아니라, 독립적인 웹서비스의 구현을 방해하게 됩니다. 예를 들어, 클라이언트의 핵심로직에 온통 웹서비스를 부르는 통신관련 로직이 섞여 있다면, 서비스의 변경이 곧 클라이언트의 변경으로 직접 연결됩니다. 좋은 클라이언트는 서비스에 대한 의존도가 낮기 때문에 클라이언트의 핵심로직이 서비스의 변경으로 인해 영향을 받는 일을 최소화해야 합니다.
이러한 독립적인 클라이언트 구현을 위해 가장 보편적으로 사용되는 패턴이 프록시(Proxy) 패턴입니다. 프록시는 웹서비스 통신을 위한 클래스 혹은 라이브러리의 집합입니다. 프록시패턴을 적용하여 클라이언트의 비즈니스로직과 통신관련로직을 분리합니다. 프록시는 서비스의 오퍼레이션의 in/out 구성 및 데이터타입, 요청/응답 메시지의 구조 등으로 구성됩니다. 따라서 클라이언트 로직에서 서비스를 호출할 때는 이 프록시만 호출하고, 그 후의 통신을 모두 이 프록시에게 위임합니다.
프록시를 개발자가 직접 개발한다면 이러한 독립이 의미가 없겠지요. 하지만 프록시는 wsdl을 통해 생성할 수 있습니다. 실제로 java에서는 jdk 1.6부터 제공하는 wsimport이나, Maven plugin을 통해 WSDL 위치만 입력하면 손쉽게 프록시 파일 묶음을 얻을 수 있습니다. 때문에 우리는 프록시를 직접 구현할 필요가 없고 핵심 비즈니스 로직에 집중할 수 있습니다. 뿐만 아니라 서비스에 변화가 생기더라도 프록시만 변경하면 되기 때문에 비즈니스 로직에는 웹서비스 기술이 침투하지 않습니다. WSDL은 웹 서비스의 오퍼레이션과 메시지를 표현하고, 이 문서를 이용하여 프록시를 생성하며, 프록시를 통해 원하는 서비스를 호출할 수 있는 것입니다.
막연하기만 했던 WSDL과 서비스의 관계가 프록시를 통해 명확히 이해할 수 있습니다. 그림 3과 같이 웹서비스 소유자는 구현한 서비스 메소드와 메시지를 서비스 설명자인 WSDL로 제공하고, 클라이언트는 WSDL로 부터 구현된 클래스들을 사용하여 웹서비스와 실질적인 연결을 이룹니다.
하지만 Proxy 패턴은 통신모듈이 캡슐화되어 있기 때문에 개발자가 통신오류에 관한 대비를 놓치기 쉽다는 단점이 있습니다. 따라서 웹서비스 통신에 관한 예외처리를 정확히 해주어야 합니다. 또한 그림 3과 같이 서비스 프록시는 서비스와의 결합을 풀어주는 대신 서비스 설명자와 강하게 결합하고 있습니다. 그렇기 때문에 서비스 설명자가 변하면 서비스 프록시를 다시 생성해서 연결해야 된다는 번거로움이 있습니다.
웹서비스 요청/응답메시지는 어떤 형태일까요?
웹서비스는 표준 통신규약으로 SOAP(Simple Object Access Protocol)을 사용합니다. 객체를 접근을 쉽게 하기 위해서 사용하는 것이 SOAP인데 공부해보신 분들은 아시겠지만 의미는 Simple로 시작하는데 절대 간단하지 않습니다. 그러나 SOAP메시지만 보는 것을 넘어서, 웹서비스의 전체적인 구성 속에서 SOAP 의 역할을 이해하면 이 Simple의 의미를 알 수 있습니다.
SOAP의 이해를 돕기 위해서 위 그림4를 살펴봅니다. 그림과 같이 SOAP은 내가 보내고자 하는 메시지를 포장하는 포장지라고 보시면 됩니다. 그래서 SOAP을 표준포장구조(standard packaging structure)라고 부르기도 합니다. 클라이언트와 서버가 상호작용하는데 있어서 전송하는 매커니즘이 다 다르다면, 웹서비스가 플랫폼 중립을 이루기는 불가능할 것입니다. 보내고자 하는 메시지를 SOAP으로 감싸서 주고받으면, .Net에서 EJB를 호출하는 것이 가능해지는 것입니다. 따라서 웹서비스는 SOAP이라는 동일한 포장구조를 사용하여, 서로 다른 플랫폼의 통신을 편리하게 만듭니다.
XML은 정보를 구조적으로 조립할 수 있는 좋은 언어이기 때문에 XML 속의 정보를 꺼내어 사용하기가 편리합니다. 그래서 SOAP으로 감싸기 전에 객체를 XML로 변환하는 과정이 필요합니다. 클라이언트에서 요청을 보낼 땐 요청을 위한 정보를 XML로 변환시키고 SOAP으로 포장하여 보내면, 서버에서는 SOAP을 벗긴 후 내부 XML을 필요한 객체로 변환합니다. 이렇게 객체와 XML을 변환해주는 것을 **OXM(Object-XML Mapping)**이라고 부르고, 대표적인 OXM으로는 JIBX, Castor, JAXB 등이 있습니다.
웹서비스에서 OXM을 처음 접해보신 분들은 OXM이 SOAP메시지로 변환해준다고 오해하시는 경우가 종종있습니다. 그러나 OXM은 콘텐츠객체와 XML 사이의 변환을 담당하기 때문에 웹서비스가 아닌 다른 기술환경에서도 많이 사용됩니다. 즉 OXM은 웹서비스 안에서만 사용되는 기술이 아닙니다. 또한, 웹서비스에서는 그림과 같이 OXM을 통해 변환된 XML을 SOAP형태로 포장하여 보내는 과정을 거칩니다. SOAP Envelop 내부에는 응답/요청메시지가 존재합니다. 요청/응답 메시지의 엘리먼트 구조와 사용하는 타입의 형태는 WSDL과 매핑되기 때문에, WSDL로부터 생성된 Proxy를 보고 클라이언트 개발자는 해당서비스의 요청 파라미터가 어떤 엘리먼트로 감싸질지 유추할 수 있습니다.
그리고 이러한 OXM을 사용하고 SOAP 바인딩을 실현시켜주는 것은 웹서비스 프레임워크의 역할입니다. 웹서비스 프레임워크는 JAVA기반의 JAX-WS(Java API for XML - Web Service)와 .NET기반의 WCF(Windows Communication Foundation)가 있습니다. JAX-WS의 구현체로는 대표적으로 CXF, AXIS2가 있습니다. 이 프레임워크가 웹서비스의 WSDL의 생성과 SOAP 메시지의 바인딩 OXM 등을 적용하여 웹서비스의 전반적인 과정을 처리해줍니다. 때문에 개발자는 본인이 제공하려는 서비스와 클라이언트의 개발에 집중할 수 있고, CXF를 적용하고 몇 가지 옵션 등을 설정하여 원하는 웹서비스를 구축할 수 있습니다.
그럼 마지막으로 웹서비스 구성을 정리해봅니다.
정리
웹서비스에 대한 이야기를 그림 5를 통해 정리할 수 있습니다.
첫 째, 웹서비스는 클라이언트와 서버의 분리를 통해 플랫폼 중립과 독립적인 진화를 이룹니다.
이 것을 이루기 위해 서비스 프로바이더와 클라이언트는 각각의 노력을 합니다. 먼저 서비스 프로바이더는 서비스의 구현과 클라이언트간의 기능 결합도를 줄이기 위해 서비스의 Spec을 분리하여 서비스 API를 제공합니다. 그럼 클라이언트는 API에만 의존하게 되고, 구현체와는 낮은 결합도를 가지게 됩니다. 그리고 클라이언트에서는 프록시에게 통신관련 처리를 위임하여, 서비스와의 URI결합도와 데이터구조 결합도를 줄일수 있습니다. 즉, 서비스의 위치가 변하거나 데이터구조가 변하여도 클라이언트의 핵심 비즈니스로직에는 영향이 미치지 않으며, 프록시만 재생성하여 적용할 수 있습니다.
둘 째, 웹서비스는 위치투명성을 가집니다.
웹서비스는 네트워크 공간에서 접근 가능해야 하기 때문에 서비스만의 위치를 가지고 있습니다. 이 것은 웹서비스의 특징 중 하나인 위치투명성을 의미합니다. 누구나에게 공개해야된다는 의미가 아닌, 정확한 위치가 있다는 의미로 이해할 수 있습니다.
셋 째, 웹서비스의 전반적인 과정은 웹서비스 프레임워크가 도와줍니다.
웹서비스의 구현을 보다 쉽게 하기 위해서 훌륭한 웹서비스 프레임워크가 많이 등장했습니다. 즉, 웹서비스 프레임워크를 적절히 사용하면 내가 웹서비스를 구현하고 있는지 느끼지 못할 정도로 편리합니다. WSDL의 생성과 적용된 OXM으로 메시지를 변환해서 SOAP에 담아 통신하는 과정까지 담당해주고 있습니다.
웹서비스는 SOA를 기반으로 하기 때문에 처음에 기술보다는 개념을 이해하시는 것이 좋습니다. 저도 처음에 웹서비스를 사용할 때는 WSDL의 존재와 SOAP 메시지니의 구조와 같이 상세한 부분부터 접근하다보니 웹서비스의 전체적인 흐름에 대해서는 이해하지 못했습니다. 책을 읽고 그림으로 그리면서 이해하다 보니 보다 쉽게 이해할 수 있어서 그 내용을 공유해봅니다. 웹서비스를 공부하시는 분들께 조금이나마 도움이 되었기를 바랍니다. 끝까지 읽어주셔서 감사드리며 글을 마칩니다.
참고 서적
- "서비스 디자인 패턴" , 로버트 다이뇨 저, 윤창석, 조성배 역, 에이콘, 2013
- "자바 웹서비스" , 데이비드 채플 저, 이창신 역 , 한빛미디어, 2002.06.29
참고 URL
- 위키 백과 - SOA, 웹서비스, SOAP, 플랫폼, OXM, UDDI, JAX-WS, WCF
- "About Object-XML Mapping", http://www.eclipse.org/eclipselink/documentation/2.4/concepts/blocks002.htm
- "SOAP Binding", http://www.w3.org/2000/xp/Group/1/10/11/soap12-part2.html
- "SOAP기반 웹 서비스 구축하기", http://www.nextree.co.kr/p2010/
- "Spring으로 SOAP기반 CXF 웹서비스 구축하기", http://www.nextree.co.kr/p11410/
- "WSDL", http://www.w3.org/TR/wsdl
- "SOAP 서비스", http://api.epeople.go.kr/guide/contents/wstype_soap.html
- "웹서비스란?", http://api.epeople.go.kr/guide/
- "CXF", http://cxf.apache.org/
- "wsimport", http://isurues.wordpress.com/2009/10/01/how-to-generate-your-jaxws-service-from-a-wsdl-using-wsimport/
- "Accessing a Web Service from JAVA", http://wwu-pi.github.io/tutorials/lectures/eai/050_tutorial_ws_java.html