반응형

office 문서의  관리 / 편집을 프로그램으로 하기 위해서는 vb나 c#을 이용해야하죠. 특히 다음과 같은 마소의 본연 라이브러리로 하는데요, 기술의 발달로 .Net Framework 가 아닌 .NET core를 넘어 .NET Stardard에서 

 

Microsoft.Office.Interop.Word

Microsoft.Office.Interop.Excel

Microsoft.Office.Interop.Powerpoint

Microsoft.Office.Interop.Outlook 

의 com api를 사용하면 다음과 같은 에러메시지를 맞게 되었죠.

 

일단 이유에 대해서 설명드리겠습니다. 윈도우 전용이었던 .Net Framework 에서 cross platform을 추구하는 .NET core, .NET Standard로 넘어오면서 기본 세팅하에서는 com api를 인식할 수 없게 되었어요. com api는 윈도우에만 존재하기 때문이죠.

 

구글링했을때는 ".net core에서는 사용할 수 없다." 라고 기술되어 포기했었는데, 직접참조하면 된다고해서 도전을 다시 해봤죠. 역시나 안됐어요.

 

그렇다면 어떻게 세팅하면 사용할 수 있을까?

방법은 의외로 간단했어요.

 

platform 세팅을 any 에서 window로 바꾸는 것이죠.

 

1. 솔루션 바로 하단의 어플리케이션을 우클릭 

2. 애플리케이션 -> 일반 

 

3. 대상 OS를 찾아 기본값인 (없음)을 클릭

 

4.Windows를 클릭

 

5. 솔루션 내 종속성 우클릭 후 COM 참조 추가

(Nuget으로 microsoft.office.interop 라이브러리를 추가하면 에러가 나던데, 성공하신분 연락 또는 댓글로 방법 남겨주시기 바랍니다!!)

 

6. 필요한 라이브러리 추가 ( 스크린샷 예제에서는 Microsoft.Office.Interop.Word 를 사용하기 위해 word com object를 추가)

 

word com 객체를 추가하였고 이제 using Microsoft.Office.Interop.Word를 사용하면 라이브러리 사용이 가능하답니다.

 

 

주의할점!!!

1. 플랫폼을 바꾸면 어떻게 되느냐 - 당연히 지정한 플랫폼에서만 사용가능합니다. window10/11 등의 window os가 설치된 PC 또는 window server에서만 사용이 가능한 것이죠.

 

2. 위 예제에서 Microsoft Word 16.0 Object Library가 안보이시는 분들 특히, 이 com 객체를 추가하여 라이브러리를 사용하기 위해서는 개발된 어플리케이션이 설치될 pc에 office가 설치되어 있어야 합니다. 

 

3. 이 포스팅을 업로드하는 시점까지 확정된 microsoft의 com api 지원기간은 2029년까지로 알려져있습니다.

반응형
반응형

github에는 작성해놓았는데 블로그에는 이것조차 없네요. 머쓱

 

20241217 - 불필요부분을 뺀다고 뺐는데도 작성길이가 길어 현재 본문에서는

라이브러리 사용시 알아야할 내용과 Get방식만 설명드립니다.

 

 

참고로 .NET Standard 6.0 부터 사용해보았기 때문에

이전 버전은 다르던데 어떻게하는거냐라고 물으신다면 저도 모를꺼예요.

특히 System.Net.Http.Json는 Newtonsoft.Json이 .NET Standard에 와서 System.Text.Json으로 내장되면서 생긴 것으로 알고 있어서, 이전 버전에 없을 확률이 높습니다.

 

 

 

HTTP기초를 먼저 작성하면 좋았을텐데,,, 이 것 부터 쓰게 되네요.

우선 다른데서 공부하고 오셨다는 전제하에 작성해보겠습니다.

 

 

System.Net.Http / System.Net.Http.Json 라이브러리를 사용하기위해 근간에 되는 HttpClient의 인스턴스를 선언합니다.

예제에서는 단순하게 선언하겠습니다.

 

참고로 실제 서버나 프로그램에서 사용하시게되면 httpClientFactory에 넣어서 CreateClient로 가져오시면 매번 선언하지 않고 자원을 아끼면서 적절하게 사용하실 수 있을텐데요. 

 

여기서는 호출방법 자체에 포인트를 맞추고 있어 따로 다루지 않습니다.

(위 내용도 궁금하신 분들은 IHttpClientFactory 인터페이스를 참조하세요.)

 

HttpClient httpClient = new(){
    // 기본 URL 설정
    BaseAddress = new Uri("https://api.example.com/"),

    // 타임아웃 설정 (예: 30초)
    Timeout = TimeSpan.FromSeconds(30)
};

 

 HTTP 메소드에 따라 의 용법도 조금씩 다릅니다.

 

HTTP 헤더 입력값
httpClient.DefaultRequestHeaders.Add("Key", "Value");

위와 같은 형태로 헤더를 입력합니다. 영어를 보시면 Default Request Headers에서 감이 오시는 분이 있을까요?

이렇게 헤더를 추가하게 되면, 같은 인스턴스의 httpClient를 호출할때 항상 헤더값에 입력되게 됩니다.

 

따라서 하나의 httpClient 인스턴스를 돌려쓰시면서, 헤더값을 다르게 해야한다면,

아래의 첫문단과 같이 Clear를 해줍니다.

 

항상 같게 해줘야한다면 아래의 if문과 같이 추가해주는 것이 안정적인 방법일 것입니다.

httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Clear();

if (!httpClient.DefaultRequestHeaders.Contains("Key"))
    httpClient.DefaultRequestHeaders.Add("Key", "Value");

 

GET 방식

본문 상단에서 httpClient를 선언할때 baseAddress를 적어주었기 때문에 호출할때는 뒷부분(endpoint)만 작성하면 됩니다.

 

.NET Standard에 오기전 구 버전에서는 HttpRequestMessage로 작성하여 이것저것 넣어줘야 했다면, 비동기 방식의 System.Net.Http 메소드는 사용이 아주 간편합니다.

string endpoint = "/class/students"
HttpResponseMessage response = await httpClient.GetAsync(endpoint);

 

요청이 성공했는지 보는 방법은 단순합니다.

response.StatusCode에 HTTP status code가 반환됩니다.

 

 

System.Net.Http에서 제공하는 enum type의 HttpStatusCode 중 일부를 정리해보았습니다.
HttpStatusCode.{아래 알파벳}으로 사용하시면 되겠습니다.

다만 목록의 코드별 내용을 자세히 설명하기에는 너무 길어 예제에서 사용하는 것들 정도만 언급드리겠습니다.


ex) if (response.StatusCode ==HttpStatusCode.OK)
또는 if (response.StatusCode == 200)

200번대
OK = 200, Created = 201,  Accepted = 202, NoContent = 204, ResetContent = 205
   
300번대
Ambiguous = 300, Moved = 301, Found = 302, Redirect = 302, RedirectMethod = 303,  Unused = 306,        TemporaryRedirect = 307, PermanentRedirect = 308

400번대
BadRequest = 400, Unauthorized = 401, Forbidden = 403, NotFound = 404, MethodNotAllowed = 405,        NotAcceptable = 406, RequestTimeout = 408, RequestEntityTooLarge = 413, RequestUriTooLong = 414,        UnsupportedMediaType = 415,      

500번대
InternalServerError = 500, NotImplemented = 501, BadGateway = 502, ServiceUnavailable = 503,        GatewayTimeout = 504,

 

Get 방식의 http method는 정보의 조회 등인 경우가 많아 대부분 200번 즉 response.StatusCode 가 HttpStatusCode.OK일 거예요. 다만, 구체적으로 반환되는 HTTP Status Code는 API를 제작한 개발사/개발자에 문의 또는 설명문서를 참조하여 정하시는 것이 좋겠습니다.

 

다만, 200번대가 보통 성공을 나타낸다고 약속되어져 있습니다. (제가 약속한거 아닙니다.)

따라서 다음과 같은 방법으로 200번대면 성공이라고 가정, 아니면 실패를 예외를 던지는 속성과 함수도 존재합니다.

// response.IsSuccessStatusCode: response가 200번 대면 true를 반환하는 bool값

if(response.IsSuccessStatusCode) {
// 성공
}



// if문도 필요 없이 200번대면 다음 레코드를 읽고, 200번대가  아니면 예외를 던지는 함수
response.EnsureSuccessStatusCode();

 

응답이 성공했으면 원하는 응답을 읽어와야하고, 그 방법에는 여러가지가 있을텐데요. 두가지를 설명드리겠습니다.

 

1. 응답을 모두 string으로 받아오는 방법

c#의 함수나 속성명은 상당히 직관적입니다.

[응답][내용][문자열]으로 불러오겠습니다.

(response)(content)(string)

var responseContent = await response.Content.ReadAsStringAsync();

 

 

그런데 대부분은 string 자체를 사용하진 않을 것입니다. 약속된 API라면 반환값이 json형태의 문자열이기 때문이죠.

따라서 json형태의 문자열을 다음과 같이 클래스로 바꿔줍니다.( System.Text.Json 필요)

var result =  JsonSerializer.Deserialize<ResultClass>(responseContent, new JsonSerializerOptions());

 

JsonSerializerOptions 옵션 등과 같은 System.Text.Json의 고유 기능은 제가 자세히 설명해놓은 글

(링크:  https://pichen.tistory.com/52 ) 을 참조하시기 바랍니다.

 

[.NET 라이브러리] System.Text.Json

안녕하세요. 예전에 NewtonSoft.Json 라이브러리를 설명드렸었는데요. 이번엔 System안에 속한 Json 컨버팅 라이브러리를 설명드리도록 하겠습니다. NewtonSoft.Json( [c# dotnet nuget] Newtonsoft.Json (tistory.com) )

pichen.tistory.com

저의 경우 NewtonSoft.Json은 .Net Framework에서는 많이 사용했지만. Net Standard에 와서는 내장된 System.Text.Json을 적극 사용하고 있습니다. 특히 아래 2번 방법과 JsonSerializerOptions 을 공유하고 있기 때문에 사용하시는 것을 권장드립니다.

 

 

 

2. 응답내용을 불러올때 클래스로 만드는 방법입니다. (using System.Net.Http.Json 필요)

ResultClass result = await response.Content.ReadFromJsonAsync<ResultClass>();

저는 보통 클라이언트 호출부를 함수로 만들어

return await response.Content.ReadFromJsonAsync<ResultClass>();

형태로 사용하거나 var로 선언하긴하지만, 여러분에게 명시적으로 보여드리기 위해 클래스로 선언부를 작성하였습니다.

개발 초기에는 응답내용을 전부 로그를 찍기 위해 1번으로 시작할때도 있지만, 주로 2번을 사용하고 있습니다.

ReadFromJsonAsync의 정의를 보면, System.Text.Json의 JsonSerializerOptions 옵션을 공유합니다.

null처리, 기본값 처리 등을 손쉽게 할 수 있고, 그에 따라 api의 상세한 요구조건을 컨트롤 할 수 있으니 반드시 알아두시면 좋겠습니다.

 

ResultClass가 무엇인가요? 라고 물으실 수 있을 것 같아요. 아무렇게나 작성한 임의의 클래스입니다.

{"number": 1} 이라는 형태의 json이 왔고 약속된 형태라면,

ResultClass은 아래와 같이 정의가 되었을 것입니다.

class ResultClass{

	public int Number {get; set;}

}

// 또는

class ResultClass{

	[JsonPropertyName("number")]
	public int Sequence {get; set;}
    
}

 

serialize했을때 json이 되는 클래스를 만들었다라고 보시면 되겠습니다.

이 포스트는 json, json과 클래스의 관계에 대한 설명글이 아니기 때문에 이 정도만 작성하도록하고, 여태까지 설명 내용을 한 번에 보실 수 있도록 담아보겠습니다.

 

// json문자열을 역직렬화할 Model 클래스
class ResultClass{

	[JsonPropertyName("number")]
	public int Sequence {get; set;}
    
}


// HttpClient 선언
HttpClient httpClient = new(){
    BaseAddress = new Uri("https://api.example.com/"),
    Timeout = TimeSpan.FromSeconds(30)	// 굳이 적지 않아도 되나 기본값이 30초
};

// 필요시 헤더 추가
if (!httpClient.DefaultRequestHeaders.Contains("Key"))
    httpClient.DefaultRequestHeaders.Add("Key", "Value");

// Get 방식 호출
HttpResponseMessage response = await httpClient.GetAsync("/class/students");

// 성공 여부 검증
response.EnsureSuccessStatusCode();

// 성공시 json을 역직렬화하여 변수에 담는다.
var result = await response.Content.ReadFromJsonAsync<ResultClass>();
반응형
반응형

프로그램을 개발할때 많이 쓰는 것 중에는 사용자에게 어떤 알림메시지를 보내는 것입니다.

사용자에게 메시지를 보내는 방법은 크게

SMS(문자) 발송, 이메일 발송, 우리나라 기준 한정으로 카카오 메시지 발송이 있을 것입니다.

 

이번 포스트에서는 그중에서도 이메일 발송에 대해 알아보도록 하겠습니다.

 

대개 email을 발송하기 위해서는 

이메일을 발송하는 이메일 서버(SMTP)와 이메일을 발송할 이메일 계정이 필요합니다.

 

C#에서는

.Net 라이브러리 중 System.Net.Mail 라이브러리 내

 

SmtpClient 클래스로 SMTP와 연결하고,

MailMessage 클래스로 이메일을 작성할 수 있습니다.

다만 이 포스트에서는 메일서버 구축이 아닌 보내기에 초점을 두었기 때문에 SMTP서버의 구축 관련한 내용은 다루지 않겠습니다.

 

참고로 무료로 풀려있는 SMTP 서버들이라 하더라도 일정 갯수가 넘어가게 발송하게되면 상업적용도로 간주되어 결제를 요구하기도 하니, 상업적용도로 사용하실 분들은 SMTP서버를 제공하는 각 기업에 절차에 맞게 결제 등을 하여 사용하시면 됩니다. c# 개발자 또는 엔지니어라면 아마도 Microsoft, 한국에서는 역시 naver, 그리고 kakao도 daum을 인수했기 때문에 smtp서버가 있을 것이라 판단됩니다. 

 

SqlClient도 그렇고 호출을 담당하는 클래스들은 ~Client로 명명하는 것이 규칙으로 보입니다.

먼저, SmtpClient를 선언합니다.

    using SmtpClient SmtpServer = new(smtpAddress)
    {
        Port = smtpPort,
        EnableSsl = true,
        UseDefaultCredentials = false,
        DeliveryMethod = SmtpDeliveryMethod.Network,
        Credentials = new NetworkCredential(mailSender, mailSenderPassword)
    };

smtpAddress 자리에는 SMTP Server의 주소를 입력하면되고

smtpPort에는 SMTP Server의 열려있는 포트를 이용하면 됩니다. 보통 암호화 보안연결을 사용하면 465나 587, 사용하지 않으면 25가 기본값으로 알려져있습니다.(https가 443, http가 80 인 것과 동일하게 생각하시면 되겠습니다.)

SMTP서버를 직접 구축하신 경우에는 네트워크에 지정한 포트번호를 이용하시면 되겠습니다.

 

SMTP에 연결할 계정은 NetworkCredential() 클래스에 (계정, 비밀번호) 형태의 생성자가 있으므로, SmtpClient 클래스의 인스턴스 필드 중 Credentials로 입력해주면 됩니다.

 

SmtpClient는 IDisposabled이 구현되어있어서 선언부 앞에 using을 붙여주면 사용하지 않을때 메모리를 해체할 수 있습니다.

 

Visual Studio 기준으로 ctrl버튼을 누른채로 작성한 SmtpClient를 클릭하면, 해당 클래스를 모두 보실 수 있는데요.

이외에도 여러 필드가 있지만 포스트의 예제에서는 저 정도만 사용하도록 하겠습니다.

 

SMTP를 연결했으면 이제는 이메일을 작성해야겠죠.

    using MailMessage email = new()
    {
        From = new MailAddress(mailSender),
        Subject = emailSubject,
        BodyEncoding = Encoding.UTF8,
        IsBodyHtml = true,
        Body = "본문 내용",
    };

 

만일 받는이가 1명이라면 MailMessge(string from, string to)라는 생성자가 존재하기 때문에 아래와 같이 선언하는 방법도 있습니다.

    using MailMessage email = new(mailSender, mailReceiver)
    {
        Subject = emailSubject,
        BodyEncoding = Encoding.UTF8,
        IsBodyHtml = true,
        Body = "본문 내용",
    };

 

 

왜 선언부에 To, Cc, Bcc를 안적으셨냐고 물으실 수 있는데요, 여러명한테 보낼 수 있다보니 세 변수는 컬렉션으로 구성되어있어요. (class MailAddressCollection : Collection ) 한명한테만 보낸다면 모르겠지만, 여러명한테도 보낼 상황이 있는 작업물이라면 선언하고 추가하는 방식이 좋습니다.

email.To.Add(받을사람1);
email.To.Add(받을사람2);
email.Cc.Add(참조1);
email.Bcc.Add(숨은참조1);

 

실제로 사용하실때는 받을 사람 자체가 컬렉션에 있어서 루프문을 통해서 Add하는 방식으로 가게 되곤 합니다.

 

BodyEncoding을 UTF.8 로 해줘야 영어가 아닌 문자가 깨지는 것을 방지할 수 있고,

IsBodyHtml = true 를 사용하게 되면, <div></div> <p></p> 등 html을 이메일 본문 내에 사용하면 랜더링 해줍니다.

false를 사용하면 글자 그대로 나오게 됩니다.

 

이메일을 다 작성했다면 메일 보내기를 해야겠죠?

 

SmtpServer.Send(email);

비동기 방식의 SendMailAsync(MailMessage email) 도 존재하고, cancellationToken을 사용하신다면, SendAsync(MailMessage email, CancellationToken  cancellationToken) 도 있습니다.

 

저같은 경우에는 email Body에 html을 넣어서 테이블로 어떤 데이터를 정리해서 보내주는 형식으로 개발을 많이 해서 Body에 엄청난 노가다를 하곤 했었는데요. 여러분의 상황에 맞게 잘 활용하시길 바라겠습니다.

반응형
반응형

안녕하세요. 

예전에 NewtonSoft.Json 라이브러리를 설명드렸었는데요.

이번엔 System안에 속한 Json 컨버팅 라이브러리를 설명드리도록 하겠습니다.

 

NewtonSoft.Json( [c# dotnet nuget] Newtonsoft.Json (tistory.com) ) 글에 설명을 자세히 적어놨기 때문에

기본적인 시리얼라이즈, 디시시리얼라이즈 원리 자체는 동일하기때문에 여기서는 설명은 최소화 합니다.

 

[c# dotnet nuget] Newtonsoft.Json

이번에는 Newtonsoft.Json에 대해서 소개하도록 하겠습니다. System.Text.Json으로도 json데이터를 처리할 수 있지만 너무나 편리하고 기존에 만들어진 소스에서 많이 사용하고 있어서 사용하게 되었고

pichen.tistory.com

 

 - 인스턴스를 json형태의 문자열로 바꾸는 메소드(직렬화)

// 그냥
string jsonString = JsonSerializer.Serialize(어떤 인스턴스);

// 제네릭을 사용
string jsonString = JsonSerializer.Serialize<어떤 클래스>(어떤 클래스의 인스턴스);

 

 

- System.Text.Json에도 컨버팅 옵션이 존재합니다!

// 명확하게 써놓은것
var options = new JsonSerializerOptions { WriteIndented = true };
string jsonString = JsonSerializer.Serialize(인스턴스, options);

// 한줄로
string jsonString = JsonSerializer.Serialize(
	인스턴스, 
    new JsonSerializerOptions { WriteIndented = true }
);

    JsonSerializerOptions에 쓰일 수 있는 변수를 설명드리겠습니다. Newtonsoft.Json을 보고 오신분이라면 WriteIndented가 뭔지 감이 오실텐데요.

 

    WriteIndented =true :  를 설정하면 엔터등 공백을 포함하여 json문자열을 만듭니다.

 

    PropertyNameCaseInsensitive=true :  대소문자를 구분하여 비교를 사용합니다.

    Newtonsoft.Json에서는 대소문자를 구분하지 않아 false와 동일하며 System.Text.Json에서는 구분이 가능하다는 장점이 있는겁니다.

    

  IgnoreReadOnlyFields=true : 프로퍼티가 readonly인 경우 이를 무시하고 serialize합니다.

 

  DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull : null값을 무시합니다.

 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault : 기본값을 무시합니다.

 

 

   - json형태의 문자열을 Oject또는 특정 클래스로 바꾸는 메소드(역직렬화)

var oject = JsonSerializer.Deserialize(json);

클래스 인스턴스 = JsonSerializer.Deserialize<클래스>(json);

 

 

 - 인스턴스를 db 등이 아닌 파일에 기록할때 사용하는 메소드

 string fileName = "아무파일명.json";
using FileStream createStream = File.Create(fileName);
await JsonSerializer.SerializeAsync(createStream, 파일화하고싶은 인스턴스);
await createStream.DisposeAsync();

 

json파일로 만든 것은 ReadAllText로 읽어와서 Desirialize하여 사용하시면 됩니다.

 

 

제가 사용하는 기능 위주로 작성했기 때문에 어떤 특정한 내용을 찾고자 오셨다면 내용이 부족하다 느낄 수 있습니다.

 

Newtonsoft.Json에서 System.Text.Json으로 마이그레이션 - .NET | Microsoft Learn

 

Newtonsoft.Json에서 System.Text.Json으로 마이그레이션 - .NET

로 마이그레이션하는 Newtonsoft.JsonSystem.Text.Json 방법과 차이점에 대해 알아봅니다 System.Text.Json.

learn.microsoft.com

 

microsoft 공식 링크를 통해 자세한 부분을 찾아보시기 바립니다.

반응형
반응형

Sicos1977/MSGReader: C# Outlook MSG file reader without the need for Outlook (github.com)

 

Msgreader는 이메일 파일인 .msg와 .eml확장자 파일을 읽을 수 있도록하는 라이브러리입니다.

원본 github주소는 위 그림을 클릭하면 이동하실 수 있습니다.

 

이메일 파일에 담긴 이메일 정보(보낸이, 받는이, 보낸시각, 받은시각, cc, bcc, 메일제목, 메일내용, 첨부파일 등)의 정보를 읽을 수 있도록합니다.

 

상세한 예외처리는 제외하고 용법을 보겠습니다.

 

먼저, 메일정보를 쉽게 주고받을 수 있도록 프로퍼티를 충분히 가진 클래스를 선언하겠습니다.

 

class MailFile {
    public string FileDirectory { get; set; }          // 파일 경로
    public string FileName { get; set; }               // 파일명
    public string FilePath { get; set; }               // 파일경로 + 파일명
    public string Originator { get; set; }             // 보낸사람
    public string Addressee { get; set; }              // 받는사람
    public string CC { get; set; }                     // CC
    public string BCC { get; set; }                    // BCC
    public DateTime SentDate { get; set; }             //  발신 시간
    public DateTime ReceivedDate { get; set; }         // 수신 시간
    
    public MailFile(string fileDirectory, string fileName)
    {
        FileDirectory = fileDirectory;
        FileName = fileName;
        FilePath = Path.Combine(fileDirectory, fileName);  
        // 그냥 string으로 더하지 않고 Path.Combine으로 쓴이유를 모른다면 독자는 꼭 찾아봐야한다!
    }
}

 

 

예제이기 때문에 msg인지 eml파일인지 여부를 비교할 대상은 그냥 선언하겠습니다.

(여기서는 확장자로만 구분합니다.)

// 메일 파일을 읽기전에 있어야할 것들을 기입한 부분입니다.
// msg인지 eml인지 비교할 확장자,
// MailFile 클래스의 선언
// 생성된 첨부파일을 다운받을 경로 등

string msgExtention = ".MSG";
string emlExtention = ".EML";

string mailFileDirectory = @"파일경로, \가 보통들어가서 @를 붙임";
string mailFileName = @"파일명";

var mailFile = new MailFile(mailFileDirectory, mailFileName);
string downloadDirectory = @"첨부파일이있다면 다운받을 경로이다.";

 

다음은 메일파일의 정보가 있는지 여부를 받아서 받아오는 것이다.

FileInfo mailFileInfo = new FileInfo(mailFile.FilePath);

if (fileInfo.Exists && fileInfo.Length > 0)	
// fileInfo.Exists는 파일이 존재하는지 여부를 리턴
// fileInfo.Length는 파일의 크기를 byte단위로 리턴
{
    if (mailFileInfo.Extension.ToUpper().Equals(msgExtention))
    // 파일 경로 비교 ToUpper()를 해도되고 StringComparison.OrdinalIgnoreCase 를 사용해도된다.
    {
        using var msgFile = new MsgReader.Outlook.Storage.Message(mailFilePath);
        // 메일 파일의 경로에 따라 여기서는 확장자값이 .MSG이면 msg인스턴스(Storage.Message)를 생성한다.
        
        // 아래는 읽으면 감이 올 것이다. 각 정보를 받아오는 것이고 
        // 여기에 매칭되도록 초반에 MailFile을 선언했다.
        // 실제 사용시에는 MailFile에 첨부파일 존재여부도 넣어주면 좋다. 
        mailFile.Originator = msg.Sender?.DisplayName;                                    // 보낸이
        mailFile.Addressee = msg.GetEmailRecipients(RecipientType.To, false, false);      // 받는이
        mailFile.CC = msg.GetEmailRecipients(RecipientType.Cc, false, false);             // CC
        mailFile.BCC = msg.GetEmailRecipients(RecipientType.Bcc, false, false);           // BCC
        mailFile.SentDate = msg.SentOn ?? DateTime.MinValue;                              // 보낸시각
        mailFile.ReceivedDate = msg.ReceivedOn ?? DateTime.MinValue;                      // 받는시각
    	
        // 첨부파일은 아래와 같은방법으로 가져올 수 있다.  
        // #### 첨부파일류들은 data[]로 파일데이터를 들고있기때문에 아주 무겁다. ####
        // ## 그러나 IDisposable을 구현하고 있어서 dispose()나 using을 사용할 수 있다.
        // ## 해당부분은 여기에서는 포함하고 있지 않아서 참고하여 개발시 이 부분을 반드시 확인하기바란다.
        // ## 아니면 메모리가 탈탈 털리는 현상을 볼 지도...
        // ## 또한 파일데이터를 들고있기때문에 너무 용량이 큰 친구들은 이것으로 첨부파일 뽑으려면
        // ## 메모리가 아주커야할 것이다... 파일 용량에 꼭 제한을 두자.
        // ## 도입부에 FileInfo 선언했는데 거기서 처리할 수 있다.
        var attachmentsObject = msgFile.Attachments;

        attachmentsObject.ForEach(attachment =>
        {
            try
            {
                if (!Directory.Exists(downloadDirectory)){    // 첨부파일을 다운받을 경로가 없으면 만든다.
                    Directory.CreateDirectory(downloadDirectory);
                }
                
                if (attachment != null && attachment is Storage.Attachment)  // 일반적인 첨부파일이면
                {
                    var attachmentCast = attachment as Storage.Attachment ?? throw new Exception($"형변환했는데 null이면 던질 예외");
                    string attachmentPath = Path.Combine(downloadDirectory, attachmentCast.FileName);             // 내려받을 첨부파일 path
                    File.WriteAllBytes(attachmentPath, attachmentCast.Data);                                      // 다운로드           
                }
                else if (attachment != null && attachment is Storage.Message)  // 앞에 using var msgFile = new MsgReader.Outlook.Storage.Message(mailFilePath); 부분을 보면 눈치 챘을 수도 있지만 이는 첨부파일이 MSG파일인 경우이다.
                {
                  var attachmentCast = attachment as Storage.Message ?? throw new Exception($"형변환했는데 null이면 던질 예외");
                  string attachmentPath = Path.Combine(downloadDirectory, attachmentCast.FileName);
                  attachmentCast.Save(attachmentPath);                       // Storage.Message는 Save함수를 사용하여 파일을 내려받아야 한다.
                }
                else
                {
                  // 둘다 아닌경우에 취할 조치를 하면 된다. 예외처리를 해도 좋다.
                }
            }
            catch (Exception ex)
            {
              Log.Error(ex.ToString());
            }
      });
    }
    else if (mailFileInfo.Extension.ToUpper().Equals(emlExtention))
    {
        var emlFile = MsgReader.Mime.Message.Load(mailFileInfo);    // eml 파일을 읽는 방법이다.

        if (emlFile.Headers != null)    // eml 파일의 메일 파일정보 불러오기는 복잡하다... msgReader 공식 문서를 참조하면 이렇게 해야한다.
        {
            if (emlFile.Headers.From != null && !string.IsNullOrEmpty(eml.Headers.From.DisplayName))
            { mailFile.Originator = emlFile.Headers.From.DisplayName.ToString().Replace("\"", string.Empty); }
            else if (eml.Headers.From != null && !string.IsNullOrEmpty(eml.Headers.From.Address))
            { mailFile.Originator = emlFile.Headers.From.Address.ToString().Replace("\"", string.Empty); }
            else { mailFile.Originator = ""; }
        
            emlFile.Headers.To.ForEach(to => mailInfo.Addressee += "," + to.ToString());
            if (mailFile.Addressee == null) { mailFile.Addressee = ""; }
            else { mailFile.Addressee = mailFile.Addressee.Substring(1).Replace("\"", string.Empty); }
        
            emlFile.Headers.Cc.ForEach(cc => mailFile.CC += "," + cc.ToString());
            if (mailFile.CC == null) { mailFile.CC = ""; }
            else { mailFile.CC = mailFile.CC.Substring(1).Replace("\"", string.Empty); }
        
            emlFile.Headers.Bcc.ForEach(bcc => mailFile.BCC += "," + bcc.ToString());
            if (mailFile.BCC == null) { mailFile.BCC = ""; }
            else { mailFile.BCC = mailInfo.BCC.Substring(1).Replace("\"", string.Empty); }
        
            if (emlFile.Headers.DateSent != null) { mailFile.SentDate = emlFile.Headers.DateSent; }
            else { mailFile.SentDate = DateTime.MinValue; }
        
            if (emlFile.Headers.Received != null && emlFile.Headers.Received.Count > 0)
            {
                mailFile.ReceivedDate = emlFile.Headers.Received.Last().Date;
            }
            else
            {
                mailFile.ReceivedDate = DateTime.MinValue;
            }
        }    // 여기까지가 메일정보 읽기
        
        ObservableCollection<MessagePart> attachments = emlFile.Attachments;    
        var attachmentsCount = attachments.Count;       
    
        foreach (var attachment in attachments) // eml파일은 정보읽기는 복잡하지만 첨부파일 다운로드는 간단하다.
        { 
            try
            {
                if (!Directory.Exists(downloadDirectory)) 
                    Directory.CreateDirectory(downloadDirectory);
    
                if (attachment.IsAttachment)
                    attachment.Save(new FileInfo(Path.Combine(downloadDirectory, attachment.FileName)));             
                
            }
            catch (Exception ex)
            {
                Log.Error(ex.ToString());
            }
        }
    }
}

 

생각보다 코드가 길기 때문에 설명도 코드블럭내에 같이 적어놨습니다. 특히 ####, ## 붙은 부분을 반드시 읽어서 참고시에 메모리 등 관리문제를 최대한 방지 하시기 바랍니다.

 

반응형
반응형

이번에는 Newtonsoft.Json에 대해서 소개하도록 하겠습니다.

 

 

System.Text.Json으로도 json데이터를 처리할 수 있지만

너무나 편리하고 기존에 만들어진 소스에서 많이 사용하고 있어서 사용하게 되었고 설명드리고자 합니다.

 

 

요즘에는 xml을 거의 사용하지 않고 json으로 데이터를 주고받으면서

 

많이 사용하고 있죠

 

using Newtonsoft.Json;

으로 설치한 nuget 패키지를 사용할 파일에서 import하시면 사용가능합니다.

 

Newtonsoft.Json의 주요기능

 

문자열로 변환 가능한 클래스를 json 데이터로 변환하는 것

json 형태의 문자열을 클래스로 변환하는 것입니다.

 

 

 

먼저 예시로 사용할 클래스를 만들어보도록 하겠습니다.

( 참고로 github에 올려두었습니다. 원본 소스가 궁금하신분들은 여기서 내려받거나 복사하시면 되겠습니다. - msab2170/tistory50_Newtonsoft.Json_Example: 티스토리 블로그 50번 글 소스파일 (github.com)

 

GitHub - msab2170/tistory50_Newtonsoft.Json_Example: 티스토리 블로그 50번 글 소스파일

티스토리 블로그 50번 글 소스파일. Contribute to msab2170/tistory50_Newtonsoft.Json_Example development by creating an account on GitHub.

github.com

 

class Example
{
    public List<Fruit> Fruits;
    public string Str;
    public int Number = 0;
    public int NumberDefault0 = 0;
    public bool IsOk;
    public string? NullableStr;
    public int? NullableInteger;
}

class Fruit
{
    public string Name;
    public int price;
}

 

 

그리고 아래는 실행할 소스 입니다.

 

using Newtonsoft.Json;

Example example = new()
{
    Str = "문자열",
    Number = 10,
    NumberDefault0 = 0,
    IsOk = true,
    NullableStr = null,
    NullableInteger = null
};

var exampleJson0 = JsonConvert.SerializeObject(example);
var exampleJson1 = JsonConvert.SerializeObject(example, Formatting.None);
var exampleJson2 = JsonConvert.SerializeObject(example, Formatting.Indented);
var exampleJson3 = JsonConvert.SerializeObject(example, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
var exampleJson4 = JsonConvert.SerializeObject(example, Formatting.None, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore });
var exampleJson5 = JsonConvert.SerializeObject(example, Formatting.Indented, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore });
var exampleJson6 = JsonConvert.SerializeObject(example, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });

Console.WriteLine($"exampleJson0: {exampleJson0}");
Console.WriteLine($"exampleJson1: {exampleJson1}");
Console.WriteLine($"exampleJson2: {exampleJson2}");
Console.WriteLine($"exampleJson3: {exampleJson3}");
Console.WriteLine($"exampleJson4: {exampleJson4}");
Console.WriteLine($"exampleJson5: {exampleJson5}");
Console.WriteLine($"exampleJson5: {exampleJson6}");

 

아래는 실행 결과값입니다.

exampleJson0: {"Fruits":null,"Str":"문자열","Number":10,"NumberDefault0":0,"IsOk":true,"NullableStr":null,"NullableInteger":null}
exampleJson1: {"Fruits":null,"Str":"문자열","Number":10,"NumberDefault0":0,"IsOk":true,"NullableStr":null,"NullableInteger":null}
exampleJson2: {
  "Fruits": null,
  "Str": "문자열",
  "Number": 10,
  "NumberDefault0": 0,
  "IsOk": true,
  "NullableStr": null,
  "NullableInteger": null
}
exampleJson3: {"Str":"문자열","Number":10,"NumberDefault0":0,"IsOk":true}
exampleJson4: {"Str":"문자열","Number":10,"IsOk":true}
exampleJson5: {
  "Str": "문자열",
  "Number": 10,
  "IsOk": true
}
exampleJson5: {
  "Str": "문자열",
  "Number": 10,
  "NumberDefault0": 0,
  "IsOk": true
}

JsonConvert.SerializeObject(example); 했을때 default로 들어가는 설정들이 보이시나요?

 

위에 적은 예시에서 exampleJson 뒤에 붙은 숫자대로 설명하겠습니다.

 

0. 기본적으로 JsonConver.SerializeObject() 내에 별다른 설정 값이 없다면

 

Example 클래스를 토대로 json형태로 변환하는데

null값이 있으면 "변수명":null로 입력이 된다는 것을 알 수 있고,

클래스의 모양을 무시하고 한 줄에 출력된다는 것을 알 수 있습니다.

 

 

1. 을 보면 JsonConvert.SerializeObject(example, Formatting.None); 가 0의 예시와 동일하다는 것을 알 수 있죠,

이를 토대로 우리는

 

클래스 모양을 무시하고 한 줄로 치는 값은 Formatting형 값인 Formatting.None이라는 것을 알 수 있습니다.

 

 

2.에서는 Formatting형 값을 Formatting.Indented로 주자 클래스 모양처럼 보기 쉽게 문자열이 변형됨을 알 수 있습니다.

가독성을 높인 대신에 쓸데없이 라인을 많이 잡아먹는다는 단점이 있습니다.

 

제 경험상 주고받을때는 굳이 공간을 더 쓸 필요없기 때문에 한줄로 주로 사용하는 경우가 많았고

로그로 남길때는 라인수냐 가독성이냐 일부 논의를 할 수 있지만 역시 한 줄로 사용하는 경우가 많았습니다.

 

 

3번과 4번은 설계에 따라서 많이 사용하기도 합니다.

JsonConvert.SerializeObject(example, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });

 

JsonConvert.SerializeObject 함수의 세번째 변수자리에는 JsonSerializerSettings 클래스 변수를 사용하는데 이는 변환시 필터작업?을 포함합니다.

 

JsonSerializerSettings  내에는 대표적으로 3번과 4번에 쓰인 NullValueHandlingDefaultValueHandling 가 있습니다.

사용 형식은 위 예제대로

new JsonSerializerSettings {

    NullValueHandling = NullValueHandling.Ignore

}

의 방식이고 쉼표로 구분해 둘 또는 다른 설정과 같이 쓸 수도 있으며,

 

 

NullValueHandling.Ignore은 변환하고자하는 인스턴스 내 변수 값이 null 이면 이를 없는 셈치고 json으로 변환합니다.

3번과 6번을 보면 null 값이 담겨있는 Fruits와 NullableStr, NullableInteger 가 사라졌다는 것을 알 수 있습니다.

반대 값은 NullValueHandling.Include 이며 이는 디폴트값이라 따로 지정할 필요는 없습니다.

 

 

DefaultValueHandling도 마찬가지 기능입니다. Ignore와 Include가 있으며 비교대상이 null에서 지정한 기본값으로 변했습니다. int는 보통 기본값이 0이고 그외는 null 이기 때문에 지정해놓은 기본값이 없다면 null인 경우에 무시됩니다.

 

4번과 5번을 보면 null값과 기본값으로 지정한 값 모두 사라졌다는 것을 알 수 있습니다.

 

 

JsonSerializerSettings 에는 그 외에도 StringEscapeHandling, TypeNameHandling, Converters 등이 있는데 제가 사용해본적은 없기 때문에 이번 설명에서는 제외하였습니다.

 

 

JsonConvert.SerializeObject() 에 대한 설명이었습니다.

-----------------------------------------------------------------------------------------------------------------

인스턴스를 json형태로 변환했는데 json형태의 문자열을 클래스의 인스턴스로 변환하는 것도 있어야겠죠?

 

우선 이해를 돕기 위한 코드를 작성하겠습니다.

 

class Example
{
    public List<Fruit> Fruits;
    public string Str;
    public int Number = 0;
    public int NumberDefault0 = 0;
    public bool IsOk;
    public string? NullableStr;
    public int? NullableInteger;

    public override string? ToString() => JsonConvert.SerializeObject(this);
    public string? ToStringJson() => JsonConvert.SerializeObject(this, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore});
    public string? ToString2() =>
        "Fruits=" + (Fruits == null ? "null" : Fruits.ToString()) +
        "\n&Number=" + Number +
        "\n&NumberDefault0=" + NumberDefault0 +
        "\n&IsOk=" + IsOk +
        "\n&NullableStr=" + (NullableStr == null ? "null" : NullableStr.ToString()) +
        "\n&NullableInteger=" + (NullableInteger == null ? "null" : NullableInteger.ToString());
}

class Fruit
{
    public string Name;
    public int price;
}

Example 클래스를 출력 가능하게 바꾸는 클래스를 기본 ToString()을 override 한것 외에도 두개를 더 만들었고

 

 

다음은 실행 코드 입니다.

Example example = new()
{
    Str = "문자열",
    Number = 10,
    NumberDefault0 = 0,
    IsOk = true,
    NullableStr = null,
    NullableInteger = null
};

// 1.------------------------------------------------
var exampleJson0 = JsonConvert.SerializeObject(example);
Console.WriteLine($"example: {example}");
Console.WriteLine($"example.ToStringJson(): {example.ToStringJson()}");
Console.WriteLine($"example.ToString2(): {example.ToString2()}");

// 2.------------------------------------------------
var ex0 = JsonConvert.DeserializeObject(exampleJson0);
Console.WriteLine($"{ex0.GetType().Name}: {ex0}");
//Console.WriteLine($"ex0.ToStringJson(): {ex0.ToStringJson()}");
//Console.WriteLine($"ex0.ToString2(): {ex0.ToString2()}");

// 3.------------------------------------------------
var ex1 = JsonConvert.DeserializeObject<Example>(exampleJson0);
Console.WriteLine($"{ex1.GetType().Name}: {ex1}");
Console.WriteLine($"ex1.ToStringJson(): {ex1.ToStringJson()}");
Console.WriteLine($"ex1.ToString2(): {ex1.ToString2()}");

실행 코드에서

 

우선 설명드릴 내용은 json 형태의 string문자열을 객체로 변환하는 함수는 

 

JsonConvert.DeserializeObject(json문자열)

JsonConvert.DeserializeObject<T>(json문자열); 

 

 

1. 단락은 example 인스턴스를 json으로 변환하여 exampleJson0 인스턴스에 string 문자열로 담았던 내용이구요

2. 는 JsonConvert.DeserializeObject() 를 이용해서 다시 객체로 만든 것입니다.

2.에 하단부는 왜 주석처리를 해놨냐, JsonConvert.DeserializeObject()로 리턴 받은 ex0의 객체는 Example클래스의 인스턴스가 아니기 때문입니다.

 

더 정확히 말하면 타입을 지정하지 않아 JObject형태가 되버린 ex0은

Object클래스의 메소드인 .ToString()은 쓸 수 있으나

Example클래스의 메소드인 ToStringJson()와 ToString2()를 사용할 수 없기 때문입니다.

 

이를 보정하기 위한 3.은 json문자열을 원하는 클래스타입을 제네릭으로 작성하면 해당 클래스타입으로 받아옵니다.

따라서 Example? 형태이며, Example클래스의 메소드를 모두 사용할 수 있게 됩니다.

 

받아오는 형태를 모르는 것이 아니라면 꼭 제네릭으로 사용하시는 것을 권장드립니다.

 

 

이것으로 Newtonsoft.Json 에대한 설명을 마칩니다. 

부족한 부분이나 문의는 댓글로 남겨 주시면 반영하도록 노력하겠습니다. 

반응형
반응형

안녕하세요. 이 카테고리의 지난 글에서 Serilog를 설명드렸는데요.

(이전 글 링크 : https://pichen.tistory.com/48)

 

[c# - dotnet nuget] serilog

블로그에는 글을 너무 안쓴 모양이다... 아주 초반부터 사용하던 이런 것조차 없어서 작성한다. Serilog는 로그를 아주 손쉽게 콘솔 또는 파일로 작성가능하도록 하는 너겟? 누겟? 패키지이다. 참

pichen.tistory.com

 

해당 내용은 Console app, Winform, Wpf 등에서 사용할 수 있는 방법이었구요.

 

web을 할때는 로그를 어떻게 남기냐? -> asp.net core 버전인 Serilog.AspNetCore가 있습니다.

 

 

여기서는 asp.net core mvc 혹은 wep api 프로젝트에서의 사용법을 설명드리겠습니다.

 

 

먼저 visual studio 내 도구 탭에 nuget 패키지 관리자를 통해서 다음과 같이 검색하면

 

Serilog.AspNetCore가 있습니다. 사용하시는 버전에 맞게 설치하시기 바랍니다.

 

다만 이번 예제는 .net7.0버전을 사용했다는 점을 알려드립니다.

 

program.cs에서 

using Serilog;를 적어주시고

var builder = WebApplication.CreateBuilder(args);의 아래에 다음과 같이 입력하시면 됩니다.

Log.Logger = new LoggerConfiguration()
    .ReadFrom.Configuration(builder.Configuration)
    .CreateLogger();

builder.Host.UseSerilog();

var configuration = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .Build();

이전 글의 세팅과는 사뭇 다르죠,

 

이전 글의 방법대로도 asp.net core에서 사용할 수도 있으나

 

 

asp.net core에서는 appsettings.json파일에서 설정정보를 받아오기 때문에 그걸 가능하게 하기 위한 방법입니다.

이전 글의 세팅도 사용가능하나 추천하지 않기 때문에 따로 적지는 않겠습니다.

 

만일 개발자버전에서는 다르게 지정하고 싶다면 IsDevelopment() 블록 내에 다른 .json 파일을 적어주시면 됩니다.

여기서는, 개발버전의 디폴트 .json파일인 appsettings.Development.json을 이용하도록 적었습니다.

 

if (app.Environment.IsDevelopment())
{
	// 여기 부분
	  var configuration = new ConfigurationBuilder()
              .AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true)
              .Build();

}

이렇게 입력한 후 로그를 남길 컨트롤러로 돌아갑니다.

 

이후 부터는 사실 이미 설치되어있는 ILogger를 이용하면 되는데요. 그래도 혹시 처음 접하시는분들을 위해 방법을 남깁니다.

[Route("[controller]")]
public class HomeController : ControllerBase
{
    private readonly IConfiguration _configuration;
    private readonly ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger, IConfiguration configuration)
    {
        _logger = logger;
        _configuration = configuration;
    }
}

이와 같이 private readonly + 생성자의 변수를 입력하면 ILogger에 대한 의존성 주입이 완료되며,

ILogger로 작성한 로그가 Serilog 라이브러리에서 파일이나 콘솔에 남길 수 있게 됩니다.

 

각각 메소드에

_logger.LogVerbose();

_logger.LogDebug();

_logger.LogInformation();

_logger.LogWarning();

_logger.LogFatal();

_logger.LogError();

 

등과 같이 사용하실 수 있습니다. 괄호 내에는 string 변수를 입력하면 되며, 서식 문자열 등은 앞에 Serilog 게시글을 확인하시기 바랍니다.

 

마지막으로 appsettings.json에 어떻게 입력하면 되는지 설명드리겠습니다.

 

"Serilog": {
  "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
  "MinimumLevel": {
    "Default": "Information",
    "Override": {
      "Microsoft": "Information",
      "System": "Error"
    }
  },
  "WriteTo": [
    {
      "Name": "Console"
    },
    {
      "Name": "File",
      "Args": {
        "path": "로그파일을 저장할 경로이며, .txt 바로앞에년월일이 찍힙니다. log-.txt",
        "rollingInterval": "Day",
        "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
      }
    }
  ]
}

앞 글의 Serilog를 입력할때 넣었던 설정정보가 모두 들어있죠.

Using에는 Serilog.AspNetCore를 설치하면 이미 담겨있는 라이브러리들입니다.

 

using

   "Serilog.Sinks.Console" - console창에도 찍으려면 기입

   "Serilog.Sinks.File" - file을 남기려면 기입

MinimumLevel - 최저 로그레벨, 최저 로그레벨 자체에 대한 설명은 이전 Serilog 설명글에 남겨두었기 때문에 생략합니다. (이전 글을 보고 싶으시다면 글의 최상단으로 이동하시면 링크가 있습니다.)

 

위에 using은 라이브러리 참조를 나타냈다면, 실제로 쓰기역할을 하는 부분이

WriteTo 입니다. 

"Name": "Console" 기입시 콘솔에 찍히게 되고

"Name": "File"  기입시 로그파일이 남게 됩니다.

 

위에 using 부분이랑 console끼리, File끼리 짝이라고 생각하시면 됩니다.

 

WriteTo 내 Args 에는 파일에 대한 설정정보를 기입할 수 있으며

"path"에는 로그파일이 작성될 경로와 파일명을 입력하는데, 확장자 바로 앞에는 날짜가 자동으로 붙습니다.

가령 "log-.txt"로 입력하게되면 프로젝트 경로 내 log-20231027.txt 파일로 로그파일이 생성됩니다.

그러나 서버에 사이트를 올릴때에는 절대경로로 적어두는 것이 좋다고 생각하고, 이 형식을 감안해서 파일이름도 별도로 정해두어 알아보기 쉽게하는 것이 좋다고 생각합니다.

"rollingInterval" 은 파일이 새로 생성되는 주기이고 "Day"로 적으면 하루가 지나면 새로운 로그파일이 생성됩니다.

"outputTemplate"은 로그가 찍히는 line의 형식을 나타냅니다. 기본 세팅을 변경하고싶다면 여기서 변경하면 됩니다.

 

 

이상으로 Serilog.AspNetCore에 대한 기본적인 설명을 마칩니다.

사용하면서 궁금한 점이나 추가했으면 좋겠는 점은 댓글로 남겨주시면 업데이트하도록 하겠습니다.

 

감사합니다.

 

반응형
반응형

블로그에는 글을 너무 안쓴 모양이다... 아주 초반부터 사용하던 이런 것조차 없어서 작성한다.

필자의 github

 
Serilog는 로그를 아주 손쉽게 콘솔 또는 파일로 작성가능하도록 하는 너겟? 누겟? 패키지이다.
 
참고로 콘솔에도 로그를 찍고 싶다면 : Serilog.Sinks.Console을,
로그파일로도 작성하고 싶다면 : Serilog.SInks.File을 같이 설치 해주어야 한다.
 
설치과정만 세분화 되어있을 뿐 각 용량이나 설치시간은 매우 짧고 using Serilog만 치면 나머지 둘은 알아서 따라오기 때문에 아주 간편하다.
 
필자같은 경우는
로그찍을때 파일 경로 등 때문에
LogHandler 라는 이름으로 클래스를 하나 작성하는데 그 방법대로 설명하려고 한다.
 

class LogHandler
{
    public string today;
    public string[] todayYMD;
    public string logFilePath;

    public LogHandler()
    {
        today = DateTime.Now.ToString("yyyy-MM-dd");
        todayYMD = today.Split('-');
        logFilePath = $"프로젝트명-logs/{todayYMD[0]}/{todayYMD[0]}-{todayYMD[1]}/프로젝트명-log-.txt";
    }
}

사실 이 방법은 바보 같을 수도 있다. 결국에는 logFilePath 하나만을 사용하기 때문이다.
하지만 한번 만들어두니 알아보기도 쉬워서 매번 복사해다 쓰고 있다.
 
------------------------------------------------------
 
 
다음은 main() 또는 program.cs에 작성해야하는 부분이다.
using Serilog; 하나면 Serilog.Sinks~류 들을 별도로 설치했다고해서 using도 또 써줘야하는건 아니다.
 
아래 소스는 위에서 만든 클래스를 선언하여 logFIlePath를 담을뿐
실질적으로Log.Logger 부분만 봐도 무방하다.

using Serilog;

LogHandler logHandler = new();   

Log.Logger = new LoggerConfiguration()
    //.WriteTo.Console()                          // Serilog.Sinks.Console를 설치한 경우 사용가능
    .WriteTo.File(                                // Serilog.Sinks.File를 설치한 경우 사용가능
        logHandler.logFilePath,                   // 로그를 지정할 파일 경로 
        rollingInterval: RollingInterval.Day,
        retainedFileCountLimit: null,
        fileSizeLimitBytes: 50 * 1024 * 1024,
        rollOnFileSizeLimit: true,
        outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level}] {Message}{NewLine}{Exception}")   
    .MinimumLevel.Information()    // 로그 최저레벨을 설정한다.                
    // .MinimumLevel.Is(Serilog.Events.LogEventLevel.Information) // 바로 위 .MinimumLevel.Information()를 제거하고 이 방법을 사용하면 설정 등으로 가져와서 최저레벨을 지정할 수도 있다.
    .CreateLogger();

WriteTo.Console() 소스에 주석으로 적혀있기 때문에 패스하고
 
WriteTo.File()에는 여러가지 변수가 올 수 있는데
 
1. rollingInterval은 새로 파일을 만드는 기간 또는 주기를 말하는 것이다.
위 소스에는 RollingInterval.Day이기 때문에 일이 바뀔때마다 새로운 날짜의 파일이 생성된다.
 
2. retainedFileCountLimit은 로그파일 갯수 상한을 정하는 것이다.
 
필자의 경우는 서버 관리자가 직접 지울만큼 쌓이기 전까지는 보관하는 것을 선호하기 때문에 null을 입력했다.
그러나 하드용량이 매우적은 컴퓨터나 서버에서 사용한다면 제한을 두는 것이 좋다.
 
3. fileSizeLimitBytes는 로그파일의 최대크기를 정해놓는 것으로 byte단위로 입력하면 된다. 필자의 짧은 경험상으로는 50mb가 넘어가면 키는데 좀 딜레이가 생기는 것 같아서 저렇게 작성했다.
 
4. rollOnFileSizeLimit: 은 3번에서 지정한 로그파일의 용량 한계가 넘어가면 어떻게 할지를 결정하는 것이다. true면 다음 파일을 생성하고 false면 로그파일의 용량이 한계에 다다르면 더이상 파일을 작성하지 않는다. 따라서 근방의 로그가 작성되지 않길 바랄리가 없기 떄문에 true를 해야만 한다.
 
5. outputTemplate: 은 찍히는 로그의 모양을 나타낸 것이다. 미세한 시간차이를 확인하고 싶은게 아니라면 fff를 빼도 좋다.
시간 그 부분 제외하고는 딱히 수정해본적이 없기 때문에 필요하다면 Template작성방식을 검색해보아야 할 것이다.
 
그리고 
 
.MinimumLevel. 은 찍히는 최저로그레벨을 정하는 것이다.
필자가 기억하기로 Verbose, Debug, Information, Waring, Error, Fatal  정도가 있는데
우측으로 갈수록 높은 레벨이다. 따라서 .MinimumLevel.Information()으로 작성하면 Information, Waring, Error, Fatal 는 찍히고 Verbose, Debug는 무시된다.
( .MinimumLevel.Information() - Information()자리에 Verbose()부터 Fatal()까지 모두 올 수 있다)
 
.MimimumLevel은 Is()라는 함수도 갖고있는데 변수로 Serilog.Events.LogEventLevel 안에있는 프로퍼티들을 사용할 수 있다. 위의 .MinimumLevel.과 동일한 기능이다. 그러나 Is()가 매력적인 것은 개발시 미리 지정이 아니라 실행시 어떤 로직에 따라서 로그 최저레벨을 정할 수 있다는 점이다.
 
예를 들면 .MinimumLevel.Is(WhatIWant()) 로 두고
public WhatIWant()의 리턴값으로 Serilog.Events.LogEventLevel.Warning 등을 사용하여 결정할 수있다.
 
 
------------------------------------------------------
 
마지막으로 예제 및 serilog의 서식문자열 사용법을 설명하고 포스팅을 마치도록 하겠다.

// 서식문자열의 사용방법
// Console.WriteLine()에서는 {순서,글자너비 + 좌우 정렬여부} 
// -> Console.WriteLine("{0,-10} | {1,-20}", "asdfsadfs", "test1string");
// 방식으로 사용하지만 Serilog의 서식문자열 형식은 별도로 정해져있음
// 순서가 아닌 아무런? 단어를 사용하고 쉼표(,) 앞뒤에 공백이 없어야만 함 
// (필자는 버릇처럼 띄어쓰기를 넣다가 꽤많은 출력실패를 경험하였음)

Log.Information("| {Status,-10} | {Address,-30} | {RoundtripTime,-15} | {BufferLength,-20} |",
	"Status", "Address", "RoundTrip time", "Buffer size");
    
// 다양한 로그레벨로
string a = "아무 문자열";
Log.Verbose($"아무아무아무아무 {a}");
Log.Debug("껄껄껄");
Log.Information(@"C:\directory\ddong");
Log.Waring("위험!! 위험!!");
Log.Error("에러가 나부러쓰");
Log.Fatal("치명적~");

 
마지막으로,  

throw new Exception("여기서 적는말은 에러다");

throw new Exception으로 string 문자열을 던지면 익셉션이 던져진 것과 별개로
Log.Error에 적은 것처럼 로그가 남는다.
 
 
 
 
asp.net core 프로젝트에서도 appsettings.json으로 설정정보를 담은 serilog를 사용할 수 있다.
이는 차후에 적도록하겠다. 

23년에 이 글과 비슷하게 작성했는데 링크를 깜빡하여 25년 초에 붙입니다.
link:
https://pichen.tistory.com/m/49

[c# dotnet nuget] Serilog.AspNetCore

안녕하세요. 이 카테고리의 지난 글에서 Serilog를 설명드렸는데요. (이전 글 링크 : https://pichen.tistory.com/48) [c# - dotnet nuget] serilog 블로그에는 글을 너무 안쓴 모양이다... 아주 초반부터 사용하던

pichen.tistory.com

반응형

+ Recent posts