Azure Cosmos DB 개요

  • 투명한 다중 마스터 복제 및 잘 정의된 5개의 일관성 모델이 포함된 턴키 방식으로 전 세계 배포
  • 규모와 상관없이 SLA로 지원되며 상위 1%에 속하는 99.999% 고가용성의 한 자릿수 밀리초 대기 시간
  • Apache Spark 및 Jupyter Notebook에 대한 기본 제공 지원과 함께 Cassandra, MongoDB, SQL, Gremlin, Etcd, Table의 유선 프로토콜 호환 API 엔드포인트가 포함된 다중 모델

Cosmos DB에서 제공하는 잘 정의된 5개의 일관성 모델

  1. Strong – 사용자는 항상 최신 커밋 쓰기의 읽기가 보장.
  2. Bounded Staleness – 설정된 지연시간 동안 처리되는 항목에 대해서는 읽기 보장이 되지 않음.
  3. Session – 클라이언트 세션을 범위로 관리(단일 “writer” 세션으로 가정), 단조로운 읽기, 단조로운 쓰기, 읽기 – 쓰기 및 쓰기 – 후행 읽기 보장
  4. Consistent Prefix – 쓰기 순서에 따라 일부 처리된 것을 읽을 수 있음.
  5. Eventual – 읽기에 대한 순서 보장이 없음.

Reauest Unit(RU)

  • 처리량 단위는 RU(Request Unit)로 표현
  • 1RU – 1KB 항목을 읽는데 드는 비용
  • 컨테이너(데이터베이스)당 기본 400RU로 시작하며, 최소 100RU 단위로 스케일 조정 할 수 있음.

Cosmos DB offers a choice of APIs

원하는 데이터 모델 및 API를 선택하여 바로 사용 및 적용 할 수 있다.

출처 : https://docs.microsoft.com/en-us/azure/cosmos-db/

[.NET]Swagger UI에 Customized Data Type 표현하기

API를 개발하면서 정의한 호출/응답 Class Model의 Properties에 별도로 Customized Data Type(Attribute)를 적용하여 사용할때가 있는데, 이런경우, Customized Data Type은 자료형이 아니기 때문에 Swagger에서 표현되지 않아, API 사용자에게 특정 Property의 상세한 스팩을 전달 하기 어렵게 된다.

이런 속성들을 나타내려면 SwaggerConfig의 Schema Filter를 사용하면 된다.

Schema Filter를 사용하면, Swagger Document Json 파일에 사용자가 정의한 Customized DataType을 추가 할 수 있게 되어 Swagger UI에서도 사용자가 정의한 Data Type이 표현 되어 진다.

Customized Data Type(Attribute)을 정의하고 Model에 적용하여 확인해보자.

1. Customized Attribute Class를 정의한다. 
(Tip, Attribute Class 정의 위에 Attribute Usage 속성을 명시적으로 정의 해주면, 사용자가 정의한 Customized Data Type(Attribute)이 의도하지 않은 형태로 사용되는 것을 예방 할 수 있다.)

    /// <summary>
    /// Customized Attribute
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public class CustomizedAttribute : Attribute
    {
        /// <summary>
        /// Customized Attribute
        /// </summary>
        /// <param name="DataType">Attribute Name</param>
        public CustomizedAttribute(string DataType)
        {
            DataTypeName = DataType;
        }

        /// <summary>
        /// Name of DataType
        /// </summary>
        public string DataTypeName { get; set; }
    }

2. 1번에서 정의한 Attribute를 아래와 같이 API에서 응답/호출에 사용할 Model의 Properties에 적용한다.

    /// <summary>
    /// Student Info Model
    /// </summary>
    public class StudentInfo
    {
        /// <summary>
        /// ID
        /// </summary>
        [Required]       
        public string StudentID { get; set; }

        /// <summary>
        /// Name
        /// </summary>
        [Required]
        [CustomizedAttribute("MyType")]
        public string Name { get; set; }

        /// <summary>
        /// Gender
        /// </summary>
        public string Gender { get; set; }

        /// <summary>
        /// Phone Number
        /// </summary>
        [CustomizedAttribute("MyType")]
        public string Phone { get; set; }

        /// <summary>
        /// Address
        /// </summary>
        [CustomizedAttribute("MyType")]
        public string Address { get; set; }
    }

여기까지 하면, 일단은 Model Properties에는 적용되어 시스템에는 반영 되었지만, Swagger UI에서 확인을 할 수 없다.

확인해 보기 위해서 프로젝트를 실행하면 다음과 같이 Customized Data Type을 적용한 Properties들이 일반 자료형인 string으로 표현 되는 것을 볼 수 있다.

이제 String으로 표현된 부분을 Attribute로 정의한 “MyType”으로 나오도록 Schema Filter를 설정해 보자.

1. Schema Filter로 사용할 Filter Class “ApplySchemaVendorExtensions”를 다음과 같이 추가(정의)한다.

    /// <summary>
    /// Vendor Extensions
    /// </summary>
    public class ApplySchemaVendorExtensions : ISchemaFilter
    {
        /// <summary>
        /// Get Customed Attribute Schema
        /// </summary>
        /// <param name="schema">Document Schema</param>
        /// <param name="schemaRegistry">Document Schema Repository</param>
        /// <param name="type">Model Definition</param>
        public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
        {            
            var propertyMappings = type.GetProperties()
                                   .Join(schema.properties ?? new Dictionary<string, Schema>(),
                                   x => x.Name.ToLower(), y => y.Key.ToLower(),
                                   (x, y) => new KeyValuePair<PropertyInfo, KeyValuePair<string, Schema>>(x, y)).ToList();

            foreach (var propertyMapping in propertyMappings)
            {
                var propertyInfo = propertyMapping.Key;
                var propertyNameToSchemaKvp = propertyMapping.Value;

                foreach (var attribute in propertyInfo.GetCustomAttributes())
                {
                    SetSchemaDetails(propertyNameToSchemaKvp, attribute);
                }
            }
        }

        /// <summary>
        /// Set Customized Schema Details
        /// </summary>
        /// <param name="propertyNameToSchemaKvp">Property Schema</param>
        /// <param name="propertyAttribute">Customized Attribute</param>
        private static void SetSchemaDetails(KeyValuePair<string, Schema> propertyNameToSchemaKvp, object propertyAttribute)
        {
            var schema = propertyNameToSchemaKvp.Value;

            if (propertyAttribute is CustomizedAttribute)
            {
                string dataType = ((CustomizedAttribute)propertyAttribute).DataTypeName;

                schema.type = dataType;

                schema.example = "string";
            }        
        }
    }

위 코드에서 작성한 Schema Filter에서는, Swagger에서 화면을 구성하기 위한 Swagger Document Json 파일을 생성할때, 해당 Schema Filter에서 사용자가 정의한 Customized Data Type을 Swagger Document Json파일 내용에 반영 하도록 한다. 

2. 1번에서 정의한 Schema Filter인 “ApplySchemaVendorExtensions”을 다음과 같이 SwaggerConfig에 추가 설정하여, 필터가 동작 할 수 있도록 한다.

GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
 c.SchemaFilter<ApplySchemaVendorExtensions>();
})
/*.......... 생략 ............*/

여기까지 Customized Data Type에 대한 정의 및 표현(구현)을 위한 Schema Filter 설정까지 모두 완료 되었다. 

이제, 프로젝트를 다시 실행 시켜 보면 자료형으로 표현 되었던 Properties들이 모두 Customized Data Type인 MyType이 표현 된 것을 확인 할 수 있다. 

Schema Filter 적용 전

Schema Filter 적용 후

– 유의사항 –
Swagger UI는 API 사용자에게 제공되는 스팩 문서인기 때문에, API 사용자가 어떻게 이해 할 것인가를 우선 고려 해야 한다. 위 방법을 통하면, 해당 Model Property가 특정하게 이용(처리)되는 것을 명시적으로 표현 할 수 있지만, 만약 정의한 속성 때문에 API 사용자가 모델에 사용할 Data Type이 혼동이 온다면, 필터를 적용하지 않고 일반 자료형이 표현되게 하고 Implementation Note에 별도로 작성하여 안내하는 것이 더 좋을 수 있다.   

[.NET]XML주석을 활용하여 Swagger UI에 세부 메타 데이터 정보 추가 하기

Swagger를 사용하여 개발하다 보면 API에 대한 좀더 자세한 설명을 제공 하고 싶을 때가 있는데, 스팩화면을 별도로 개발하거나 문서를 작성하지 않고 C#코드에 추가한 XML주석을 활용하여 Swagger UI에 바로 세부 메타 데이터 정보 추가 할 수 있다.

먼저, C# 코드의 각 Class, Method, Properties 등에 XML 주석을 추가 작성해 보자.
(TIP, 정의 코드 위에서 ‘/(Slash)’를 연속 3번 누르면 자동으로 XML 테그를 생성해준다.)

이제 작성된 XML 코드들이 문서(파일)로 저장 될 수 있도록 프로젝트 환경설정에 설정 하도록 한다.
프로젝트 속성에서 빌드 메뉴를 살펴 보면 출력 관련 설정하는 영역을 확인 할 수 있다.

XML 문서 파일 체크 박스를 그림과 같이 체크한다, 설정(체크) 하면 우측에 경로가 기본값으로 셋팅 되며, 해당 경로 값을 복사해 둔다.
(원하는 경로가 있다면 변경해도 되지만, 기본값 쓰는 것을 권장한다.)

다음으로, 저장된 XML 문서 파일을 Swagger에서 열어서 화면에 표시 할 수 있도록 설정 해야 한다. 
SwaggerConfig.cs을 열어 다음과 같이 코드를 추가 한다.  

GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
 c.IncludeXmlComments(string.Format(@"{0}\bin\SwaggerFramework.XML"
, System.AppDomain.CurrentDomain.BaseDirectory));
})

/*.......... 생략 ............*/

(복사한 경로값으로 “\bin\SwaggerFramework.XML”부분을 변경하여 설정 한다.)   

이제, 설정이 완료 되었으니 프로젝트를 실행 시켜 어떻게 나오는지 확인해 보자.

Swagger UI를 확인해 보면 위 XML코드를 작성한 내용이 태그 이름에 따라 화면에 나타는 것을 확인 할 수 있다.

이와 같이, Swagger UI에서 C#코드의 사용자 정의 XML(주석)을 사용하여 API들에 대한 설명들을 메타 데이터 표현할 수 있도록 함으로써, 추가적인 별다른 노력 없이 좀 더 자세한 API 스팩 문서를 작성 및 제공 할 수 있다.

[.NET]Swagger에서 Namespace를 이용하여 API 버전 구분(그룹화)하기

API를 개발 하다보면 자연스럽게 생기는 대표적인 고민이 바로 버전 관리 이다.

예를 들어

  1. 버전을 개별 기능(URL) 별로 관리 할 것인가? 아니면 개별 기능을 구룹화하여 구룹별로 할 것인가?
  2. 버전에 대한 URL 설계는 어떻게 할 것인가?
  3. 버전 관리를 한다면 구버전 지원은 어떻게 할 것인가? 

등등…

이러한 고민들을 Swagger의 MultipleApiVersions을 이용하여 멀티 버전을 설정하면 해결 할 수 있다.

API APP 프로젝트를 만들면 다음과 같은 경로에 SwaggerConfig.cs라는 파일을 볼 수 있다. (위차: ~/App_Start/SwaggerConfig.cs)

클릭해서 열어보면 대략 Line:35 정도에 c.SingleApiVersion 이라는 것이 기본으로 설정되어 있는 것을 볼 수 있는데 이것은 API APP프로젝트를 처음 만들면 API 샘플(~/Controller/ValueController.cs) 1개가 등록 되어 있기때문에 멀티 버전 관리를 할 수 있는게 없기 때문이다.

멀티 버전을 관리 설정을 하기 전에 우선 간단히 아래 그림과 같이 2개의 API를 만들어 보도록 하겠다.
(프로젝트 샘플 소스를 가지고 실제 기능은 없고 단순히 호출 할 수 있는 Method만 만들었다. Student – ClassRoom: 학생 API / Teacher – Report :  선생님 API)

위와 같이 구성하면, 각각의 컨트롤러 파일의 Namespace는 
Student : SwaggerFramework.Controllers.Student
Teacher : SwaggerFramework.Controllers.Teacher

로 설정 된 것을 확인 할 수 있다.

이제 위와 같이 설정된 Namespace를 가지고 API 버전을 구분 할 수 있도록 MultipleApiVersions를 설정해 보자.

앞서 언급한 SwaggerConfig.cs라는 파일을 열어 Line:35 정도에 있는 c.SingleApiVersion으로 이동하여, 해당 줄을 삭제(보통 주석 해두는 것이 좋다.)하고 아래와 같이 코드를 추가 하도록 한다.

//c.SingleApiVersion("v1", "SwaggerFramework");
c.MultipleApiVersions((apiDesc, targetApiVersion) =>
{
   var controllerNamespace = apiDesc.ActionDescriptor.ControllerDescriptor.ControllerType.FullName;
	
   if(CultureInfo.InvariantCulture.CompareInfo.IndexOf(controllerNamespace, string.Format(".{0}.", targetApiVersion), CompareOptions.IgnoreCase) >= 0)
   { 
	return true; 
   }
   return false;
},(vc) =>
{
   vc.Version("Student", "Student API v1");
   vc.Version("Teacher", "Teacher API v1");
});

위와 같이 설정하면 각 Namespace(Student, Teacher)이름에 따라서 버전이 구분을 할 수 있다. 

실제로 구분이 되는지 프로젝트를 실행 해보면 다음 그림과 같이 API가 Namespace 별로 구분되어 확인 할 수 있다.

Swagger Explore 창에 Namespace를 이용하여 검색해보면, SwaggerConfig.cs에 설정해 둔 각 API설명과 해당 Namespace를 가지고 있는 API 기능을 구별하여 확인 할 수 있다.
(Explore 검색 예시 – http://{ Domain Path }/swagger/docs/[ Studnet | Teacher ])

이와 같이 Swagger의 환경 설정에서 Namespace를 이용하면 간단히 API를 버전별로 구분(그룹화)하여 제공 할 수 있기 때문에 버전관리를 쉽게 할 수 있다. 

[.NET]Swagger를 이용하여 Azure API APP 문서 만들기

Swagger를 사용하면 개발된 Restful API에대한 스팩을 별도의 문서를 만들지 않고도 자동으로 문서화 하여 웹형태로 쉽게 제공 할 수 있다.

먼저, Swagger에서 제공하는 API 스팩 문서 화면을 살펴 보자.

Rest API의 URL(Route 주소)목록을 제일 먼저 볼 수 있는데, 직관적으로 해당 API APP에 어떤기능들 있는지 한번에 파악 할 수 있게해준다. 특정 URL을 클릭하면 그 API안에 구현된 HTTP Method 리스트를 볼 수 있다.

Restful API의 동작은 동일한 Route 주소에 HTTP Method를 통해서 CRUD(생성 – Post, 조회 – Get, 변경 – Put, 삭제 – Delete)기능을 구분하며 이러한 구분을 Swagger에서는 4가지 색으로 구분하여 표현해준다.

다음으로, Get Method를 클릭하여 Swagger에서 해당 API에 대한 문서를 어떻게 제공하는지 실제 코드랑 비교하 살펴보자.

Swagger Source Code

// GET api/values/5
[SwaggerOperation("GetById")]
[SwaggerResponse(HttpStatusCode.OK)]        
[SwaggerResponse(HttpStatusCode.NotFound)]
public string Get(int id)
{
    return "value";
}

Swagger Document

첫 번째, 최상단의 GET /api/Values/{id}는 Rest API를 호출하기 위한 라우트 주소로 메서드 역할을 정의하는 public string Get(int id) 의 정보이다.

두 번째, 최상단 바로 및의 Parameters영역은 API의 메서드의 변수에 대한 설명을 확인 할 수 있으며, Value 필드에 임의 값을 넣고 테스트 해볼 수 도 있다. 
(값을 넣고 아래 Try it Out! 이라는 버튼을 누르면 개발한 API가 동작한다.)

마지막, Response Messages영역은 API가 HTTP통신을 할때 반환하는 HttpResponseMessage의 HTTP State Code에 대한 정의이다. Swashbuckle의 속성 설정 방법을 통해서 정의 할 수 있다.
(예: [SwaggerResponse(HttpStatusCode.OK)], [SwaggerResponse(HttpStatusCode.NotFound)])

Restful API를 개발하는 사람이라면 시각화가 되어 있지 않은 API의 각종 정보들을 다른 사람에게 전달하기 정말 까다로운 일이라는 것을 알고 있을 것이다. 이런 고충을 Swagger Framework을 통해서 해소 할 수가 있다. 

Azure의 APP API서비스는 Swagger Framework을 기본으로 제공해 주고 있어서 Azure에서 API를 개발 할 때, 몇가지 정의 방법으로 인터페이스 설명 및 기본 설정을 하면, Swagger.UI에서 보기 좋게 문서화 해서 Web으로 잘 보여 준다. 그래서 개발자가 문서 작업때문에 시간들일 필요도, 머리 아플일도 전혀 없다. 그리고 표준 Rest API 정의에 대한 표현을 Swagger에서 정말 잘 다루고 있기 때문에 어느정도 Rest API에 대한 인식이 있다면 API를 제공받은 사람 입장에서도 서비스에 대한 정보를 별다른 노력 없이 쉽게 인식할 수 있다. 

[.NET]HttpResponseMessage 클래스 메서드 오류 CS1061 해결 방법

프로젝트에서 API를 호출하고 나서 결과를 읽어 Model(Object)에 담을때 다음과 같이 코드를 한다.

그런데 간혹 잘 코딩 했다고 생각을 했어도 다음과 같은 오류 메시지가 나올 때가 있다. 

오류 CS1061 ‘HttpContent’에는 ‘ReadAsAsync’에 대한 정의가 포함되어 있지 않고, ‘HttpContent’ 형식의 첫 번째 인수를 허용하는 확장 메서드 ‘ReadAsAsync’이(가) 없습니다. 
using 지시문 또는 어셈블리 참조가 있는지 확인하세요. 

이런경우 참조 패키지가 잘 준비되지 않은 경우이다. 
먼저 NuGet Package에 들어가서 System.Net.Http 버전을 최신버전으로 업데이트 하고,bMicrosoft.AspNet.WebApi.Client 패키지가 설치 되어 있는지 확인 해본다. 아마 안되어 있거나 구 번전일 것이다.

install-package Microsoft.AspNet.WebApi.Client

설치 및 업데이트가 완료 되었다면 문제 없이 작동 할 것이다.