[C#] Soap 통신 서버를 만드는 방법(C#, Java, Php, Python으로 접속)


Development note/C#  2020. 2. 11. 09:00

안녕하세요. 명월입니다.


이 글은 C#에서 Soap 통신 서버를 만드는 방법(C#, Java, Php, Python으로 접속)에 대한 설명입니다.


최근 프로젝트에서 Soap 통신 서버를 만드는 일은 거의 없어진 것 같네요. 최근에는 REST Api가 주를 이루지 않나 싶습니다.

이유는 아무래도 REST Api라 프로토콜의 Method를 사용해서 요청을 하는 부분이니 구현이 쉽고 Soap의 경우는 HTTP 프로토콜에서 XML의 통신 규약(Soap 프로토콜)을 사용하기 때문에 raw 레벨로 개발한다고 하면 조금 복잡할 수도 있습니다.

그리고 기본적으로 XML통신으로 이루어지기 때문에 통신 간의 데이터 전송이 많아서 환경에 따라 느려질 수도 있습니다.


그러나 저의 생각은 조금 다른데, 물론 Soap가 복잡한 건 맞습니다만 그건 IDE 툴(개발 툴)을 이용하지 않을 때나 그런 거고 개발 툴을 이용한다고 하면 오히려 REST Api보다 더 간단하게 작성되지 않을까 싶네요.

그리고 설계를 어떻게 하냐에 따라 다르기는 하지만 Http의 자원을 온전히 사용할 수 있기 때문에 REST Api보다는 보안이 더 편할 수 있습니다.

아.. 역시 생각해 보면 이건 Case by Case입니다. oAuth2를 Soap로 구현한다고 하면 어마어마하게 복잡해 지겠네요.


프로젝트에 사양에 따라서 사용하시길 바랍니다.


먼저 기존 Web Service에서 Soap Server를 만드는 방법은 다음과 같습니다.

예제를 위한 웹 서버를 만들었습니다. 기존 웹 서버에서 Soap를 만들려고 하면 이 부분은 그냥 지나가도 됩니다.

asmx 파일을 추가시킵니다.

이제 Client에서 해당 Web Service의 값이나 함수 식을 사용하기 위한 메소드를 만듭니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

namespace SoapServer
{
  // NameSpace 설정 부분입니다. 없어도 상관은 없지만, 패키지 구분을 위한 것이니 설정하는 게 좋다.
  [WebService(Namespace = "SoapServer")]
  // 이건 기본적으로 설정되는 부분이라 딱히 건드릴 건 없다.
  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  [System.ComponentModel.ToolboxItem(false)]
  // ASP.NET AJAX를 사용하여 스크립트에서 이 웹 서비스를 호출하려면 다음 줄의 주석 처리를 제거합니다. 
  // Soap를 Json 형식으로 사용하기 위한 것이지만, 일단 생략한다.
  // [System.Web.Script.Services.ScriptService]
  public class Example : System.Web.Services.WebService
  {
    // 접속자 address를 넣기 위한 list
    private static List<String> list = new List<string>();
    // 접속 context, request, response를 받기 위한 맴버 함수
    private HttpContext context;
    public Example()
    {
      this.context = HttpContext.Current;
    }
    // 어트리뷰트 WebMethod를 사용하면 외부 Soap에서 사용하기 위한 함수의 표시
    [WebMethod]
    // 접속자 수를 확인하기 위한 함수
    public int Count()
    {
      return list.Count;
    }
    [WebMethod]
    // 접속자 리스트를 받는 함수
    public List<String> GetCurrectors(String name)
    {
      // 접속자 IP 주소를 받는다.
      var address = $@"{this.context.Request.UserHostAddress} ({name})";
      // 등록된게 없으면 list에 추가
      if (!list.Where(x => x.Equals(address)).Any())
      {
        list.Add(address);
      }
      return list;
    }
    [WebMethod]
    // 파라미터를 받고 결과를 내보내는 샘플 함수
    public int Calc(int a, int b)
    {
      return a + b;
    }
  }
}

기동을 하면 Soap를 사용하기 위한 code sample과 사양 설명서가 나옵니다.

서버측 Soap는 만들었습니다. 이걸 통해서 C#, Java, Python, PHP에서 접속해 보겠습니다.

C# client

Console project를 하나 만들고 references의 참조에서 서비스 추가로 Soap연결을 할 수 있습니다.

(스크린 샷 찍은 시점이 조금 달라서 port가 다르게 나옵니다. 사실은 접속 포트를 맞추어야 합니다.)

using System;
using System.Collections.Generic;
// references에서 설정한 namespace를 선언한다.
using Soap_Client.ServiceReference1;

namespace Soap_Client
{
  class Program
  {
    static void Main(string[] args)
    {
      // 이건 Soap 클래스(Example)에서 뒤 SoapClient를 붙이는 것으로 객체 할당을 할 수 있다.
      var client = new ExampleSoapClient();
      // GetCurrectors함수를 실행해서 List<String>으로 값을 받아 온다.
      List<String> list = client.GetCurrectors("C#");
      foreach(var address in list)
      {
        Console.WriteLine(address);
      }
      // Example 클래스의 Count함수를 실행했다.
      Console.WriteLine(client.Count());
      // 파라미터 1과 2를 넣는다.
      var ret = client.Calc(1, 2);
      // 결과는 3이다.
      Console.WriteLine(ret);
      // 콘솔 대기.
      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

C#에서는 Web service에 이렇게 간단하게 접속이 됩니다.

Java client

자바에서는 eclipse를 이용해서 Console 프로젝트 하나 만들겠습니다.

그리고 따로 pom.xml 설정없이 Wizard를 통한 클래스 추가로 환경을 만들 수 있습니다.

C#이외의 언어에서는 url뒤에 ?WSDL를 붙여야 합니다.

별도로 next에서 상세 설정해도 됩니다만 저는 기본 설정으로 가겠습니다. 그대로 Finish를 누르면 Soap를 위한 클래스가 생깁니다.

이제 소스 작성해서 접속해 보곘습니다.

package com;

import java.rmi.RemoteException;

import SoapServer.ExampleSoap;
import SoapServer.ExampleSoapProxy;

public class Program {
  public static void main(String... args) {
    try {
      // 여기는 Soap 클래스(Example)에서 뒤 SoapProxy를 붙이는 것으로 객체 할당을 할 수 있다.
      ExampleSoap client =  new ExampleSoapProxy();
      // getCurrectors함수를 실행해서 List<String>으로 값을 받아 온다.(코딩 규약에 따른 첫글자가 자동으로 소문자로 바뀌었다.)
      String[] list = client.getCurrectors("Java");
      for(String address : list) {
        System.out.println(address);
      }
      // Example 클래스의 count함수를 실행했다. (코딩 규약에 따른 첫글자가 자동으로 소문자로 바뀌었다.)
      System.out.println(client.count());
      // 파라미터 1과 2를 넣는다. 결과는 3이다.
      System.out.println(client.calc(3, 4));
      
    } catch (RemoteException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}

PHP client

PHP에서는 Soap를 사용하기 위해 php.ini를 mod를 추가해야 합니다.

C#이나 java처럼 따로 Provider를 연결할 필요는 없습니다.

<?php
  // Soap의 Example 클래스를 할당한다.
  $client = new SoapClient("http://localhost:44333/Example.asmx?wsdl");
  
  // 파라미터 세팅
  $params = array('name'=>"PHP");
  // GetCurrectors를 실행한다.
  $result = $client->GetCurrectors($params);
  var_dump($result->GetCurrectorsResult);
  
  // 개행
  echo "\r\n\r\n";
  
  // Count함수를 실행한다.
  $result = $client->Count();
  var_dump($result->CountResult);
  
  // 개행
  echo "\r\n\r\n";
  
  // 파라미터 세팅
  $params = array('a'=>5, 'b'=>6);
  // Calc함수를 실행한다.
  $result = $client->Calc($params);
  var_dump($result->CalcResult);

PHP는 C#과 Java에 비해 사용하기가 더 심플하네요..

Python client

Pyhon에서는 사용하기 위해서 zeep 모듈이 필요합니다.

pip install zeep

# request 모듈
import requests;
# soap 모듈
from zeep import Client;
from zeep.transports import Transport;
# 세션 유지
with requests.Session() as session:
  # proxy환경일 경우 trust_env를 False로 해야 한다.
  # session.trust_env = False;
  transport = Transport(session=session)
  # Soap의 Example 클래스를 할당한다.
  client = Client('http://localhost:44333/Example.asmx?wsdl', transport=transport)
  
  # GetCurrectors를 실행한다.
  result = client.service.GetCurrectors("Python");
  print(result);
  # Count함수를 실행한다.
  result = client.service.Count();
  print(result);
  # Calc함수를 실행한다.
  result = client.service.Calc(7,8);
  print(result);

지금까지 접속한 wsdl를 확인해 보면 xml 구조가 다 보입니다. Soap 구조를 조금만 알면 해독이 가능합니다.

이제 보안을 위해서 이 wsdl를 보이지 않도록 설정하는데, C#이나 Java의 경우는 Provider를 클래스 형태로 만들어서 사용하기 때문에 wsdl를 가려도 접속이 됩니다.

하지만 PHP와 Python의 경우는 접속할 때마다 wsdl의 xml문서를 가져와서 읽기 때문에 이 작업을 하면 PHP와 Python에서는 사용할 수 없어지게 되네요..


다시 WebServer로 돌아와서 Web.Config에서 wsdl파일이 보이지 않도록 설정합니다.

이제 다시 접속해 보면 아래처럼 에러가 발생합니다.

여기까지 C#에서 Soap 통신 서버를 만드는 방법(C#, Java, Php, Python으로 접속)에 대한 설명이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.