2026년 1월 7일 수요일

Ews 일정 접속 방법 및 O365 일정 접속 방법

using Microsoft.Exchange.WebServices.Data; using Newtonsoft.Json.Linq; using RestApi.Controllers; using RestApi.Models; using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Http; using System.Net.NetworkInformation; using System.Text; using System.Threading.Tasks; using System.Web.Http; using System.Windows.Forms; namespace RestApi.Algorithms { public class EwsHelper : BaseController { public ExchangeService mEws; #region Ews 권한 획득 public ExchangeService GetAccessToken(string userEmail) { if (mEws == null) { try { string ewsUri = ConfigurationManager.AppSettings["EWSUri"]; string DomainName = ConfigurationManager.AppSettings["DomainName"]; //string AdminID = ConfigurationManager.AppSettings["AdminID"]; //string AdminPwd = ConfigurationManager.AppSettings["AdminPwd"]; mEws = new ExchangeService(ExchangeVersion.Exchange2010_SP2); //mEws.Credentials = new WebCredentials(emailAddr, userPw, DomainNm); mEws.UseDefaultCredentials = true; mEws.PreAuthenticate = true; mEws.Url = new Uri(ewsUri); mEws.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, userEmail); mEws.HttpHeaders.Add("X-AnchorMailbox", userEmail); } catch (Exception ex) { ComHelper.WriteErrorLog(ex, userEmail, "E_R021"); } } return mEws; } #endregion #region 일정 등록 public MdResultT1 CreateEvent(string userEmail, string subject, string cttsType, string ctts, DateTime startDate, DateTime endDate, string timeZone , string location, string recName, int reminderMinutes, string sensitivity, string importance, JArray attendees, bool isDetail, HttpRequestMessage request, string bodyData, string tmpLog) { GetAccessToken(userEmail); MdResultT1 rstAt = new MdResultT1(false, ""); string scheduleID = ""; if (mEws == null) { rstAt.message = "Ews 권한획득에 실패하였습니다."; ComHelper.WriteErrorLog(new Exception(), request, bodyData, "E_R022"); return rstAt; } StringBuilder addLog = new StringBuilder(2000); try { Appointment appointment = new Appointment(mEws) { Subject = subject, Body = new MessageBody((BodyType)Enum.Parse(typeof(BodyType), cttsType, true), ctts), Start = startDate, End = endDate, Location = location, ReminderMinutesBeforeStart = reminderMinutes, Sensitivity = (Sensitivity)Enum.Parse(typeof(Sensitivity), sensitivity, true), Importance = (Importance)Enum.Parse(typeof(Importance), importance, true) }; if (attendees != null && attendees.Count > 0) { try { foreach (JObject item in attendees) { string atdNm = item["attedName"].ToString(); string atdEmail = item["attedEmail"].ToString(); if (item["attedType"].ToString() == "required") { appointment.RequiredAttendees.Add(new Attendee(atdNm, atdEmail)); } else { appointment.OptionalAttendees.Add(new Attendee(atdNm, atdEmail)); } addLog.Append($"{Environment.NewLine} name:{atdNm}, email:{atdEmail}, type:{item["attedType"].ToString()}"); } } catch (Exception ex1) { ComHelper.WriteErrorLog(ex1, request, bodyData, "E_R023"); rstAt.message = "참석자 정보 필수값이 없습니다. 확인하여 주세요.[EWS]."; return rstAt; } } appointment.Save(SendInvitationsMode.SendToAllAndSaveCopy); string evtID = appointment.Id.UniqueId; // 회의실 이름을 Resources 컬렉션에 추가 ExtendedProperty로 ResourceName 저장 ExtendedPropertyDefinition resourceNameProp = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "ResourceName", MapiPropertyType.String); PropertySet propSet = new PropertySet(BasePropertySet.FirstClassProperties); propSet.Add(resourceNameProp); Appointment savedAppt = Appointment.Bind(mEws, evtID, propSet); if (savedAppt.ExtendedProperties != null && savedAppt.ExtendedProperties.Any(p => p.PropertyDefinition.Equals(resourceNameProp))) { savedAppt.RemoveExtendedProperty(resourceNameProp); savedAppt.SetExtendedProperty(resourceNameProp, recName); savedAppt.Update(ConflictResolutionMode.AlwaysOverwrite); } else if (string.IsNullOrWhiteSpace(recName) == false) { savedAppt.SetExtendedProperty(resourceNameProp, recName); savedAppt.Update(ConflictResolutionMode.AlwaysOverwrite); } rstAt.success = true; rstAt.key = evtID;// 이벤트 ID 가져오기 if (isDetail) { MdResultT1 tmpAt = GetEventDetail(userEmail, rstAt.key, timeZone, request, bodyData); rstAt.data = tmpAt.data; } } catch (Exception ex2) { rstAt.message = "Ews 일정등록에 실패하였습니다."; rstAt.exmsg = ex2.Message; ComHelper.WriteErrorLog(ex2, request, bodyData, "E_R024" + addLog); } finally { string strLog = $"userEmail:{userEmail}{Environment.NewLine}subject:{subject}{Environment.NewLine}cttsType:{cttsType}{Environment.NewLine}location:{location}{Environment.NewLine}{tmpLog}{Environment.NewLine}scheduleID : {scheduleID}"; ComHelper.WriteTextLog(request, bodyData, strLog, ""); } return rstAt; } #endregion #region 일정 수정 public MdResultT1 UpdateEvent(string userEmail, string evtID, string subject, string cttsType, string ctts, DateTime startDate, DateTime endDate, string timeZone, string location, string recName, int reminderMinutes, string sensitivity, string importance, JArray attendees, bool isDetail, HttpRequestMessage request, string bodyData, string tmpLog) { string strLog = $"evtID:{evtID}{Environment.NewLine}userEmail:{userEmail}{Environment.NewLine}subject:{subject}{Environment.NewLine}cttsType:{cttsType}{Environment.NewLine}location:{location}{Environment.NewLine}" + tmpLog; ComHelper.WriteTextLog(request, bodyData, strLog, ""); GetAccessToken(userEmail); MdResultT1 rstAt = new MdResultT1(false, ""); if (mEws == null) { rstAt.message = "Ews 권한획득에 실패하였습니다."; return rstAt; } try { PropertySet props = new PropertySet( BasePropertySet.FirstClassProperties, AppointmentSchema.RequiredAttendees, AppointmentSchema.OptionalAttendees ); // 3. Item 바인딩 Appointment appointment = Appointment.Bind(mEws, new ItemId(evtID), props); // 4. 기본 속성 업데이트 appointment.Subject = subject; appointment.Body = new MessageBody( (BodyType)Enum.Parse(typeof(BodyType), cttsType, true), ctts ); appointment.Start = startDate; appointment.End = endDate; appointment.Location = location; appointment.ReminderMinutesBeforeStart = reminderMinutes; appointment.Sensitivity = (Sensitivity)Enum.Parse(typeof(Sensitivity), sensitivity, true); appointment.Importance = (Importance)Enum.Parse(typeof(Importance), importance, true); appointment.RequiredAttendees.Clear(); appointment.OptionalAttendees.Clear(); // 6. 새 참석자 추가 if (attendees != null && attendees.Count > 0) { try { foreach (JObject item in attendees) { string atdNm = item["attedName"].ToString(); string atdEmail = item["attedEmail"].ToString(); if (item["attedType"].ToString() == "required") appointment.RequiredAttendees.Add(new Attendee(atdNm, atdEmail)); else appointment.OptionalAttendees.Add(new Attendee(atdNm, atdEmail)); } } catch (Exception ex1) { ComHelper.WriteErrorLog(ex1, request, bodyData, "E_R025"); rstAt.message = "참석자 정보 필수값이 없습니다. 확인하여 주세요.[EWS]"; return rstAt; } } // 7. 업데이트 수행 appointment.Update( ConflictResolutionMode.AutoResolve, SendInvitationsOrCancellationsMode.SendToAllAndSaveCopy ); // 회의실 이름을 Resources 컬렉션에 추가 ExtendedProperty로 ResourceName 저장 ExtendedPropertyDefinition resourceNameProp = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "ResourceName", MapiPropertyType.String); PropertySet propSet = new PropertySet(BasePropertySet.FirstClassProperties); propSet.Add(resourceNameProp); Appointment savedAppt = Appointment.Bind(mEws, evtID, propSet); if (savedAppt.ExtendedProperties != null && savedAppt.ExtendedProperties.Any(p => p.PropertyDefinition.Equals(resourceNameProp))) { savedAppt.RemoveExtendedProperty(resourceNameProp); savedAppt.SetExtendedProperty(resourceNameProp, recName); savedAppt.Update(ConflictResolutionMode.AlwaysOverwrite); } else if(string.IsNullOrWhiteSpace(recName) == false) { savedAppt.SetExtendedProperty(resourceNameProp, recName); savedAppt.Update(ConflictResolutionMode.AlwaysOverwrite); } // 8. 결과 처리 rstAt.success = true; rstAt.key = appointment.Id.UniqueId; if (isDetail) { MdResultT1 tmpAt = GetEventDetail(userEmail, rstAt.key, timeZone, request, bodyData); rstAt.data = tmpAt.data; } } catch (Exception ex2) { ComHelper.WriteErrorLog(ex2, request, bodyData, "E_R026"); rstAt.message = "Ews 일정수정에 실패하였습니다."; rstAt.exmsg = ex2.Message; } return rstAt; } #endregion #region 일정 삭제 public MdResultBase DeleteEvent(string userEmail, string evtID, HttpRequestMessage request, string bodyData, string tmpLog) { ComHelper.WriteTextLog(request, bodyData, $"userEmail:{userEmail}, {tmpLog}", evtID); GetAccessToken(userEmail); MdResultBase rBase = new MdResultBase(false, "", ""); if (mEws == null) { ComHelper.WriteErrorLog(new Exception(), request, bodyData, $"E_R027 → evtID : {evtID}"); return rBase.SetRst(false, "Ews 권한획득에 실패하였습니다.", ""); } try { Appointment appointment = Appointment.Bind(mEws, new ItemId(evtID)); appointment.Delete(DeleteMode.HardDelete, SendCancellationsMode.SendToAllAndSaveCopy); rBase.SetRst(true, "일정 삭제가 완료 되었습니다.", "SSS"); } catch (Exception ex) { rBase.SetRst(false, "Ews 일정삭제에 실패하였습니다.", "E00", ex.Message); ComHelper.WriteErrorLog(ex, request, bodyData, $"E_R028 → evtID : {evtID}"); } return rBase; } #endregion #region 일정 리스트 public MdResultT1> GetEventList(string userEmail, DateTime startDate, DateTime endDate, string timeZone, HttpRequestMessage request, string bodyData) { GetAccessToken(userEmail); MdResultT1> rstAt = new MdResultT1>(false, ""); List rstList = new List(); if (mEws == null) { rstAt.message = "Ews 권한획득에 실패하였습니다."; ComHelper.WriteErrorLog(new Exception(), request, bodyData, "E_R029"); return rstAt; } try { CalendarView calendarView = new CalendarView(startDate, endDate, 1000) { //PropertySet = new PropertySet(BasePropertySet.FirstClassProperties) PropertySet = new PropertySet( BasePropertySet.FirstClassProperties, AppointmentSchema.Subject, AppointmentSchema.Start, AppointmentSchema.End, AppointmentSchema.Location, AppointmentSchema.Organizer , AppointmentSchema.Importance, AppointmentSchema.Sensitivity, AppointmentSchema.AppointmentState) }; ExtendedPropertyDefinition resourceNameProp = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "ResourceName", MapiPropertyType.String); calendarView.PropertySet.Add(resourceNameProp); FindItemsResults appointments = mEws.FindAppointments(WellKnownFolderName.Calendar, calendarView); JArray eventList = new JArray(); foreach (Appointment appt in appointments) { // 취소된 일정은 건너뛰기 if (appt.AppointmentState == 7) continue; /* 개별 바인딩해서 참석자 등 상세 속성 로드 */ PropertySet bindProps = new PropertySet(BasePropertySet.FirstClassProperties, AppointmentSchema.Body, AppointmentSchema.RequiredAttendees, AppointmentSchema.OptionalAttendees); bindProps.Add(resourceNameProp); Appointment fullAppt = Appointment.Bind(mEws, appt.Id, bindProps); string wName = ComHelper.NullToEmpty(appt.Organizer?.Name); string uName = wName; string wEmail = ComHelper.NullToEmpty(appt.Organizer?.Address); if (wEmail.Contains("/O=")) // X500 주소인 경우 { // ExchangeResolveName 사용 NameResolutionCollection nameRes = mEws.ResolveName(wEmail, ResolveNameSearchLocation.DirectoryOnly, true); if (nameRes.Count > 0) { wEmail = nameRes[0].Mailbox.Address; } } string uEmail = wEmail; MdCldData tmpList = new MdCldData(); tmpList.schKey = ComHelper.NullToEmpty(appt.Id.UniqueId); tmpList.writeName = wName; tmpList.writeEmail = wEmail; tmpList.subject = ComHelper.NullToEmpty(appt.Subject); tmpList.startDate = DateTime.Parse(appt.Start.ToString("yyyy-MM-ddTHH:mm:ss")); tmpList.startZone = ComHelper.NullToEmpty(appt.Start.Kind); tmpList.endDate = DateTime.Parse(appt.End.ToString("yyyy-MM-ddTHH:mm:ss")); tmpList.endZone = ComHelper.NullToEmpty(appt.End.Kind); tmpList.location = ComHelper.NullToEmpty(appt.Location); // 장소 tmpList.Importance = ComHelper.NullToEmpty(appt.Importance); tmpList.contentType = ComHelper.NullToEmpty(fullAppt.Body.BodyType); tmpList.content = ComHelper.NullToEmpty(fullAppt.Body.Text); tmpList.sensitivity = ComHelper.NullToEmpty(appt.Sensitivity); tmpList.repeatedYn = "N"; // 참석자 정보 추가 JArray attendees = new JArray(); foreach (Attendee atd in fullAppt.RequiredAttendees) { string tmpEmail = atd.Address; string tmpName = atd.Name; MdCldAttendee mdAttendees = new MdCldAttendee { attedEmail = tmpEmail, attedName = tmpName, attedType = "required" }; tmpList.attendees.Add(mdAttendees); if (userEmail == tmpEmail) { // 일정 불러온사람 기준이면(참석자가 불러온경우 일정호출자 기준이면 uEmail = tmpEmail; uName = tmpName; } } foreach (Attendee atd in fullAppt.OptionalAttendees) { string tmpEmail = atd.Address; string tmpName = atd.Name; MdCldAttendee mdAttendees = new MdCldAttendee { attedEmail = tmpEmail, attedName = tmpName, attedType = "required" }; tmpList.attendees.Add(mdAttendees); if (userEmail == tmpEmail) { // 일정 불러온사람 기준이면(참석자가 불러온경우 일정호출자 기준이면 uEmail = tmpEmail; uName = tmpName; } } tmpList.userEmail = uEmail; tmpList.userName = uName; tmpList.recName = string.Empty; //tmpList.recName = GetResourceName(mEws, fullAppt, resourceNameProp, wEmail, uEmail, false); // 리스트에서 부어주기는 너무 비효율적임 rstList.Add(tmpList); } rstAt.success = true; } catch (Exception ex) { rstAt.message = "Ews 일정조회에 실패하였습니다."; rstAt.exmsg = ex.Message; ComHelper.WriteErrorLog(ex, request, bodyData, "E_R030"); } rstAt.data = rstList; return rstAt; } #endregion #region 일정 상세정보 public MdResultT1 GetEventDetail(string userEmail, string evtID, string timeZone, HttpRequestMessage request, string bodyData) { GetAccessToken(userEmail); MdCldData rstData = new MdCldData(); MdResultT1 rstAt = new MdResultT1(false, ""); if (mEws == null) { ComHelper.WriteErrorLog(new Exception(), request, bodyData, "E_R031"); return rstAt.SetRst(false, "Ews 권한획득에 실패하였습니다.", "", "", rstData); } try { ExtendedPropertyDefinition resourceNameProp = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "ResourceName", MapiPropertyType.String); PropertySet propertySet = new PropertySet(BasePropertySet.FirstClassProperties) { RequestedBodyType = BodyType.Text }; Appointment appt = Appointment.Bind(mEws, evtID, propertySet); string wName = ComHelper.NullToEmpty(appt.Organizer?.Name); string uName = wName; string wEmail = ComHelper.NullToEmpty(appt.Organizer?.Address); if (wEmail.Contains("/O=")) // X500 주소인 경우 { // ExchangeResolveName 사용 NameResolutionCollection nameRes = mEws.ResolveName(wEmail, ResolveNameSearchLocation.DirectoryOnly, true); if (nameRes.Count > 0) { wEmail = nameRes[0].Mailbox.Address; } } string uEmail = wEmail; rstData.schKey = ComHelper.NullToEmpty(appt.Id.UniqueId); rstData.writeName = wName; rstData.writeEmail = wEmail; rstData.subject = ComHelper.NullToEmpty(appt.Subject); rstData.startDate = DateTime.Parse(appt.Start.ToString("yyyy-MM-ddTHH:mm:ss")); rstData.startZone = ComHelper.NullToEmpty(appt.Start.Kind); rstData.endDate = DateTime.Parse(appt.End.ToString("yyyy-MM-ddTHH:mm:ss")); rstData.endZone = ComHelper.NullToEmpty(appt.End.Kind); rstData.location = ComHelper.NullToEmpty(appt.Location); // 장소 rstData.Importance = ComHelper.NullToEmpty(appt.Importance); rstData.contentType = ComHelper.NullToEmpty(appt.Body.BodyType); rstData.content = ComHelper.NullToEmpty(appt.Body.Text); rstData.sensitivity = ComHelper.NullToEmpty(appt.Sensitivity); rstData.repeatedYn = "N"; // 참석자 정보 추가 JArray attendees = new JArray(); foreach (Attendee atd in appt.RequiredAttendees) { string tmpEmail = atd.Address; string tmpName = atd.Name; MdCldAttendee mdAttendees = new MdCldAttendee { attedEmail = tmpEmail, attedName = tmpName, attedType = "required" }; rstData.attendees.Add(mdAttendees); if (userEmail == tmpEmail) { // 일정 불러온사람 기준이면(참석자가 불러온경우 일정호출자 기준이면 uEmail = tmpEmail; uName = tmpName; } } foreach (Attendee atd in appt.OptionalAttendees) { string tmpEmail = atd.Address; string tmpName = atd.Name; MdCldAttendee mdAttendees = new MdCldAttendee { attedEmail = tmpEmail, attedName = tmpName, attedType = "required" }; rstData.attendees.Add(mdAttendees); if (userEmail == tmpEmail) { // 일정 불러온사람 기준이면(참석자가 불러온경우 일정호출자 기준이면 uEmail = tmpEmail; uName = tmpName; } } rstData.userEmail = uEmail; rstData.userName = uName; rstData.recName = string.Empty; rstData.recName = GetResourceName(mEws, appt, resourceNameProp, wEmail, uEmail, true); rstAt.SetRst(true, "", "", "", rstData); } catch (Exception ex) { rstAt.SetRst(false, "Ews 일정조회를 실패하였습니다.", "", ex.Message, rstData); ComHelper.WriteErrorLog(ex, request, bodyData, "E_R032"); } return rstAt; } #endregion #region 자원명 속성 찾기 /// /// 메시지를 못 찾으면 Organizer SentItems에서 읽기 /// /// /// CalendarItem ID 기반으로 ResourceName 안전하게 조회 /// Organizer/Attendee 모두 처리 가능 /// public string GetResourceName(ExchangeService service, Appointment fullAppt, ExtendedPropertyDefinition resourceNameProp, string wEmail, string uEmail, bool isAttend) { string resourceName = string.Empty; try { if (wEmail == uEmail) { resourceName = GetResourceNmFromSentItems(service, fullAppt, resourceNameProp); } else if (isAttend) { // 참석자도 해당 값을 보게 할건지 여부 // 일정을 불러오는 사용자와 작성자 다른경우에는 작성자의 일정을 찾아야 하는데 키값이 다르기 때문에... 찾는 방법은 원 작성자로 접속을 해서 제목과 시작시간을 조회해서 찾아야함 (동일한게 있으면 오류가능성 있음) ExchangeService tmpEws = new ExchangeService(ExchangeVersion.Exchange2010_SP2); try { bool isO365 = O365UserCheck(wEmail.Split('@')[0]).GetAwaiter().GetResult(); if (isO365 == false) { // 메일함에서 오류시 M365 사용자로 판단 string ewsUri = ConfigurationManager.AppSettings["EWSUri"]; string DomainName = ConfigurationManager.AppSettings["DomainName"]; tmpEws.UseDefaultCredentials = true; tmpEws.PreAuthenticate = true; tmpEws.Url = new Uri(ewsUri); tmpEws.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, wEmail); tmpEws.HttpHeaders.Add("X-AnchorMailbox", wEmail); // Organizer 캘린더에서 Subject + StartTime으로 조회 (주의: 동일 일정 존재 시 첫 번째) SearchFilter filter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter[] { new SearchFilter.IsEqualTo(AppointmentSchema.Subject, fullAppt.Subject), new SearchFilter.IsEqualTo(AppointmentSchema.Start, fullAppt.Start), new SearchFilter.IsEqualTo(AppointmentSchema.End, fullAppt.End) }); ItemView view = new ItemView(5); view.PropertySet = new PropertySet(BasePropertySet.IdOnly); FindItemsResults originalItems = tmpEws.FindItems(WellKnownFolderName.Calendar, filter, view); if (originalItems.TotalCount > 0) { PropertySet propSet = new PropertySet(BasePropertySet.FirstClassProperties); propSet.Add(resourceNameProp); Appointment originalAppt = Appointment.Bind(tmpEws, originalItems.Items[0].Id, propSet); resourceName = GetResourceNmFromSentItems(tmpEws, originalAppt, resourceNameProp); } } } catch (Exception) { } } } catch (Exception ex) { ComHelper.WriteErrorLog(ex, $"writetUserEmail={wEmail}, askUserEmail={uEmail}", "GetResourceName"); } return resourceName ?? string.Empty; } private string GetResourceNmFromSentItems(ExchangeService _service, Appointment appt, ExtendedPropertyDefinition resourceNameProp) { string resourceName = string.Empty; try { if (string.IsNullOrEmpty(appt.Organizer?.Address)) return resourceName; SearchFilter filter = new SearchFilter.IsEqualTo(EmailMessageSchema.Subject, appt.Subject); ItemView view = new ItemView(10); // 최대 10개 조회 view.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties); view.PropertySet.Add(resourceNameProp); FindItemsResults results = _service.FindItems(WellKnownFolderName.SentItems, filter, view); foreach (EmailMessage msg in results.OfType()) { if (msg.Sender?.Address?.Equals(appt.Organizer.Address, StringComparison.OrdinalIgnoreCase) == true) { if (msg.TryGetProperty(resourceNameProp, out resourceName)) { break; } } } } catch (Exception ex) { // 로그 남기기 ComHelper.WriteErrorLog(ex, "", "GetResourceNameFromSentItems"); } return resourceName; } #endregion #region 메일 발송 public MdResultBase SendMail(string userEmail, string senderEmail, List<(string Name, string Email)> toRct, List<(string Name, string Email)> ccRct, string subject, string body, string bodyType) { GetAccessToken(userEmail); MdResultBase rBase = new MdResultBase(false, "", ""); List rstList = new List(); if (mEws == null) { ComHelper.WriteErrorLog(new Exception(), senderEmail, "E_R033"); return rBase.SetRst(false, "Ews 권한획득에 실패하였습니다.", ""); } try { EmailMessage email = new EmailMessage(mEws); email.Subject = subject; email.Body = new MessageBody(BodyType.HTML, body); // To 수신자 추가 (이름 + 이메일) foreach (var recipient in toRct) { email.ToRecipients.Add(new EmailAddress(recipient.Name, recipient.Email)); } foreach (var recipient in ccRct) { email.CcRecipients.Add(new EmailAddress(recipient.Name, recipient.Email)); } email.Send(); // 메일발송 (보낸편지함에 저장 안함) //email.SendAndSaveCopy(); //메일발송 후 보낸편지함에 저장 rBase.SetRst(true, "메일발송을 완료하였습니다."); } catch (Exception ex) { ComHelper.WriteErrorLog(ex, senderEmail, "E_R034"); rBase.SetRst(false, "메일발송에 실패하였습니다.", "", ex.Message); } return rBase; } public MdResultBase SendMail(string userEmail, string senderEmail, List toRct, List ccRct, string subject, string body, string bodyType) { GetAccessToken(userEmail); MdResultBase rBase = new MdResultBase(false, "", ""); if (mEws == null) { ComHelper.WriteErrorLog(new Exception(), senderEmail, "E_R035"); return rBase.SetRst(false, "Ews 권한획득에 실패하였습니다.", ""); } try { EmailMessage email = new EmailMessage(mEws); email.Subject = subject; email.Body = new MessageBody(BodyType.HTML, body); // To 수신자 추가 (이름 + 이메일) foreach (MdRscAttendee recipient in toRct) { email.ToRecipients.Add(new EmailAddress(recipient.userNm, recipient.mail)); } foreach (MdRscAttendee recipient in ccRct) { email.CcRecipients.Add(new EmailAddress(recipient.userNm, recipient.mail)); } email.Send(); // 메일발송 (보낸편지함에 저장 안함) //email.SendAndSaveCopy(); //메일발송 후 보낸편지함에 저장 rBase.SetRst(true, "메일발송을 완료하였습니다."); } catch (Exception ex) { ComHelper.WriteErrorLog(ex, senderEmail, "E_R036"); rBase.SetRst(false, "메일발송에 실패하였습니다.", "", ex.Message); } return rBase; } #endregion } } ------------------------------------------------------------------------------------------------ using Microsoft.Exchange.WebServices.Data; using Microsoft.Identity.Client; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using RestApi.Models; using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using System.Web.Http.Results; namespace RestApi.Services { public class GraphHelper { private string _GraphTenantID; private string _GraphClientID; private string _GraphClientSecret; private string _GraphAuthority; private string[] _GraphScope; private string _Endpoint; private string _AccessToken; #region 생성자 기초데이터터 획득 public GraphHelper() { _GraphTenantID = ConfigurationManager.AppSettings["GraphTenantID"]; _GraphClientID = ConfigurationManager.AppSettings["GraphClientID"]; _GraphClientSecret = ConfigurationManager.AppSettings["GraphClientSecret"]; _GraphAuthority = $"https://login.microsoftonline.com/{_GraphTenantID}"; _GraphScope = new[] { ConfigurationManager.AppSettings["GraphScope"] }; //_GraphGrantType = ConfigurationManager.AppSettings["GraphGrantType"]; } #endregion #region Graph 권한 획득 public async Task GetAccessTokenAsync(string userEmail) { if (string.IsNullOrWhiteSpace(_AccessToken)) { try { _Endpoint = $"https://graph.microsoft.com/v1.0/users/{userEmail}/events"; var app = ConfidentialClientApplicationBuilder.Create(_GraphClientID).WithClientSecret(_GraphClientSecret).WithAuthority(new Uri(_GraphAuthority)).Build(); var result = await app.AcquireTokenForClient(_GraphScope).ExecuteAsync(); _AccessToken = result.AccessToken; } catch (Exception ex) { ComHelper.WriteErrorLog(ex, userEmail, "E_R041"); } } return _AccessToken; } #endregion #region 일정 등록 public async Task> CreateEventAsync(string userEmail, string subject, string cttsType, string ctts, DateTime startDate, DateTime endDate, string timeZone , string location, string recName, int reminderMinutes, string sensitivity, string importance, JArray attendees, bool isDetail, HttpRequestMessage request, string bodyData, string tmpLog) { await GetAccessTokenAsync(userEmail); MdResultT1 rstAt = new MdResultT1(false, ""); string scheduleID = ""; if (string.IsNullOrWhiteSpace(_AccessToken)) { rstAt.message = "M365 권한획득에 실패하였습니다."; ComHelper.WriteErrorLog(rstAt.message, request, bodyData, "E_R042"); return rstAt; } string tmpPoint = _Endpoint; try { JObject newEvent = new JObject(); newEvent["subject"] = subject; newEvent["body"] = new JObject { ["contentType"] = cttsType, ["content"] = ctts }; newEvent["start"] = new JObject { ["dateTime"] = startDate.ToString("yyyy-MM-ddTHH:mm:ss"), ["timeZone"] = timeZone }; newEvent["end"] = new JObject { ["dateTime"] = endDate.ToString("yyyy-MM-ddTHH:mm:ss"), ["timeZone"] = timeZone }; newEvent["importance"] = importance; // 중요도 설정 newEvent["sensitivity"] = sensitivity; // 민감도 설정 newEvent["reminderMinutesBeforeStart"] = reminderMinutes; // 몇분전 전에 알림 newEvent["isAllDay"] = false; // 하루 종일 일정이 아님 //newEvent["categories"] = new JArray { "Red category" }; // Outlook에서 설정한 카테고리 이름 //newEvent["showAs"] = "busy"; // 바쁨 상태로 표시 newEvent["location"] = new JObject { ["displayName"] = location }; /* 속성 저장 newEvent["singleValueExtendedProperties"] = new JArray { new JObject { ["id"] = "String {00020329-0000-0000-C000-000000000046} Name ResourceName", ["value"] = recName } }; */ if (attendees != null && attendees.Count > 0) { try { JArray formattedAttendees = new JArray(); foreach (JObject itm in attendees) { string name = itm["attedName"].ToString(); string email = itm["attedEmail"].ToString(); string type = itm["attedType"].ToString(); JObject formattedAttendee = new JObject { ["emailAddress"] = new JObject { ["address"] = email, ["name"] = name }, ["type"] = type.ToLower() }; formattedAttendees.Add(formattedAttendee); } newEvent["attendees"] = formattedAttendees; } catch (Exception ex1) { ComHelper.WriteErrorLog(ex1, request, bodyData, "E_R043"); rstAt.message = "참석자 정보 필수값이 없습니다. 확인하여 주세요.[M365]"; return rstAt; } } using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _AccessToken); var sendJson = JsonConvert.SerializeObject(newEvent, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); var content = new StringContent(newEvent.ToString(), Encoding.UTF8, "application/json"); var response = await client.PostAsync(tmpPoint, content); string result = await response.Content.ReadAsStringAsync(); rstAt.success = response.IsSuccessStatusCode; if (response.IsSuccessStatusCode) { // JSON 파싱 var jsonResult = JObject.Parse(result); if (string.IsNullOrWhiteSpace(jsonResult["id"].ToString()) == false) { scheduleID = jsonResult["id"].ToString();// 이벤트 ID 가져오기 rstAt.key = scheduleID; if (isDetail) { MdResultT1 tmpAt = await GetEventDetailAsync(userEmail, rstAt.key, timeZone, request, bodyData); rstAt.data = tmpAt.data; } } } else { rstAt.message = "M365 일정등록에 실패하였습니다."; rstAt.exmsg = result; ComHelper.WriteErrorLog(result, request, bodyData, "E_R044"); } } } catch (Exception ex2) { rstAt.message = "M365 일정등록에 실패하였습니다."; rstAt.exmsg = ex2.Message; ComHelper.WriteErrorLog(ex2, request, bodyData, "E_R045"); } finally { string strLog = $"userEmail:{userEmail}{Environment.NewLine}subject:{subject}{Environment.NewLine}cttsType:{cttsType}{Environment.NewLine}location:{location}{Environment.NewLine}{tmpLog}{Environment.NewLine}scheduleID : {scheduleID}"; ComHelper.WriteTextLog(request, bodyData, strLog, ""); } return rstAt; } #endregion #region 일정 수정 public async Task> UpdateEventAsync(string userEmail, string evtID, string subject, string cttsType, string ctts, DateTime startDate, DateTime endDate, string timeZone , string location, string recName, int reminderMinutes, string sensitivity, string importance, JArray attendees, bool isDetail, HttpRequestMessage request, string bodyData, string tmpLog) { string strLog = $"evtID:{evtID}{Environment.NewLine}userEmail:{userEmail}{Environment.NewLine}subject:{subject}{Environment.NewLine}cttsType:{cttsType}{Environment.NewLine}location:{location}{Environment.NewLine}" + tmpLog; ComHelper.WriteTextLog(request, bodyData, strLog, ""); await GetAccessTokenAsync(userEmail); string tmpPoint = $"{_Endpoint}/{evtID}"; MdResultT1 rstAt = new MdResultT1(false, ""); if (string.IsNullOrWhiteSpace(_AccessToken)) { rstAt.message = "M365 권한획득에 실패하였습니다."; ComHelper.WriteErrorLog(rstAt.message, request, bodyData, "E_R046"); return rstAt; } try { JObject newEvent = new JObject(); newEvent["subject"] = subject; newEvent["body"] = new JObject { ["contentType"] = cttsType, ["content"] = ctts }; newEvent["start"] = new JObject { ["dateTime"] = startDate.ToString("yyyy-MM-ddTHH:mm:ss"), ["timeZone"] = timeZone }; newEvent["end"] = new JObject { ["dateTime"] = endDate.ToString("yyyy-MM-ddTHH:mm:ss"), ["timeZone"] = timeZone }; newEvent["importance"] = importance; // 중요도 설정 newEvent["sensitivity"] = sensitivity; // 민감도 설정 newEvent["reminderMinutesBeforeStart"] = reminderMinutes; // 몇분전 전에 알림 newEvent["isAllDay"] = false; // 하루 종일 일정이 아님 //newEvent["categories"] = new JArray { "Red category" }; // Outlook에서 설정한 카테고리 이름 //newEvent["showAs"] = "busy"; // 바쁨 상태로 표시 newEvent["location"] = new JObject { ["displayName"] = location }; /* 속성 저장 newEvent["singleValueExtendedProperties"] = new JArray { new JObject { ["id"] = "String {00020329-0000-0000-C000-000000000046} Name ResourceName", ["value"] = recName } }; */ if (attendees != null && attendees.Count == 0) { newEvent["attendees"] = new JArray(); } else { try { JArray formattedAttendees = new JArray(); foreach (JObject itm in attendees) { string name = itm["attedName"].ToString(); string email = itm["attedEmail"].ToString(); string type = itm["attedType"].ToString(); JObject formattedAttendee = new JObject { ["emailAddress"] = new JObject { ["address"] = email, ["name"] = name }, ["type"] = type.ToLower() }; formattedAttendees.Add(formattedAttendee); } newEvent["attendees"] = formattedAttendees; } catch (Exception ex1) { ComHelper.WriteErrorLog(ex1, request, bodyData, "E_R047"); rstAt.message = "참석자 정보 필수값이 없습니다. 확인하여 주세요.[M365]"; return rstAt; } } using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _AccessToken); var sendJson = JsonConvert.SerializeObject(newEvent, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); var content = new StringContent(sendJson, Encoding.UTF8, "application/json"); var rquest = new HttpRequestMessage(new HttpMethod("PATCH"), tmpPoint) { Content = content }; var response = await client.SendAsync(rquest); string result = await response.Content.ReadAsStringAsync(); rstAt.success = response.IsSuccessStatusCode; if (response.IsSuccessStatusCode) { // JSON 파싱 var jsonResult = JObject.Parse(result); rstAt.key = jsonResult["id"].ToString();// 이벤트 ID 가져오기 if (isDetail) { MdResultT1 tmpAt = await GetEventDetailAsync(userEmail, rstAt.key, timeZone, request, bodyData); rstAt.data = tmpAt.data; } } else { rstAt.message = "M365 일정수정에 실패하였습니다."; rstAt.exmsg = result; ComHelper.WriteErrorLog(result, request, bodyData, "E_R048"); } } } catch (Exception ex2) { rstAt.message = "M365 일정수정에 실패하였습니다."; rstAt.exmsg = ex2.Message; ComHelper.WriteErrorLog(ex2, request, bodyData, "E_R049"); } return rstAt; } #endregion #region 일정 삭제 public async Task DeleteEventAsync(string userEmail, string evtID, HttpRequestMessage request, string bodyData, string tmpLog) { ComHelper.WriteTextLog(request, bodyData, $"userEmail:{userEmail}, {tmpLog}", evtID); MdResultBase rBase = new MdResultBase(false, ""); await GetAccessTokenAsync(userEmail); if (string.IsNullOrWhiteSpace(_AccessToken)) { rBase.SetRst(false, "M365 권한획득에 실패하였습니다.", "G02"); ComHelper.WriteErrorLog("M365 권한획득에 실패하였습니다", request, bodyData, "E_R050"); return rBase; } string tmpPoint = $"{_Endpoint}/{Uri.EscapeDataString(evtID)}"; try { using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _AccessToken); var response = await client.DeleteAsync(tmpPoint); if (response.IsSuccessStatusCode) { rBase.SetRst(true, "일정 삭제가 완료 되었습니다.", "SSS"); } else { string result = await response.Content.ReadAsStringAsync(); rBase.SetRst(false, "M365 일정삭제에 실패하였습니다.", "G03", result); ComHelper.WriteErrorLog(result, request, bodyData, "E_R051"); } } } catch (Exception ex) { rBase.SetRst(false, "M365 일정삭제에 실패하였습니다.", "G00", ex.Message); ComHelper.WriteErrorLog(ex, request, bodyData, "E_R052"); } return rBase; } #endregion #region 일정 리스트 public async Task>> GetEventListAsync(string userEmail, DateTime startDate, DateTime endDate, string timeZone, HttpRequestMessage request, string bodyData) { await GetAccessTokenAsync(userEmail); TimeZoneInfo kst = TimeZoneInfo.FindSystemTimeZoneById("Korea Standard Time"); DateTime utcSTime = TimeZoneInfo.ConvertTimeToUtc(startDate, kst); DateTime utcETime = TimeZoneInfo.ConvertTimeToUtc(endDate, kst); MdResultT1> rstAt = new MdResultT1>(false, "", "", "", new List()); if (string.IsNullOrWhiteSpace(_AccessToken)) { rstAt.message = "M365 권한획득에 실패하였습니다."; ComHelper.WriteErrorLog(rstAt.message, request, bodyData, "E_R053"); return rstAt; } // calendarView 사용: 기간 지정 string tmpPoint = $"https://graph.microsoft.com/v1.0/users/{userEmail}/calendarView?startDateTime={utcSTime.ToString("o")}&endDateTime={utcETime.ToString("o")}&$top=1000"; try { using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _AccessToken); client.DefaultRequestHeaders.Add("Prefer", $"outlook.timezone=\"{timeZone}\""); var response = await client.GetAsync(tmpPoint); string result = await response.Content.ReadAsStringAsync(); rstAt.success = response.IsSuccessStatusCode; if (response.IsSuccessStatusCode) { JObject json = JObject.Parse(result); JArray events = (JArray)json["value"]; foreach (var item in events) { //취소된 일정인 경우 생략 if (bool.Parse(item["isCancelled"].ToString()) == false) { string appName = ComHelper.NullToEmpty(item["organizer"]?["emailAddress"]?["name"]); string appEmail = ComHelper.NullToEmpty(item["organizer"]?["emailAddress"]?["address"]); MdCldData tmpList = new MdCldData(); tmpList.schKey = ComHelper.NullToEmpty(item["id"]); tmpList.userName = appName; tmpList.writeName = appName; tmpList.userEmail = appEmail; tmpList.writeEmail = appEmail; tmpList.subject = ComHelper.NullToEmpty(item["subject"]); tmpList.startDate = DateTime.Parse(item["start"]?["dateTime"]?.ToString()); tmpList.startZone = ComHelper.NullToEmpty(item["start"]?["timeZone"]?.ToString()); tmpList.endDate = DateTime.Parse(item["end"]?["dateTime"]?.ToString()); tmpList.endZone = ComHelper.NullToEmpty(item["end"]?["timeZone"]?.ToString()); tmpList.location = ComHelper.NullToEmpty(item["location"]?["displayName"]?.ToString()); // 장소 tmpList.Importance = ComHelper.NullToEmpty(item["importance"]); tmpList.contentType = ComHelper.NullToEmpty(item["body"]?["contentType"]?.ToString()); tmpList.content = ComHelper.NullToEmpty(item["body"]?["content"]?.ToString()); tmpList.sensitivity = ComHelper.NullToEmpty(item["sensitivity"]?.ToString()); tmpList.repeatedYn = "N"; string resourceName = string.Empty; /* 속성 불러오기 var extProps = item["singleValueExtendedProperties"] as JArray; if (extProps != null && extProps.Count > 0) { resourceName = ComHelper.NullToEmpty(extProps[0]?["value"]?.ToString()); } */ tmpList.recName = resourceName; var attendeesArray = item["attendees"] as JArray; if (attendeesArray != null) { foreach (var attendee in attendeesArray) { string tmpEmail = ComHelper.NullToEmpty(attendee["emailAddress"]?["address"]?.ToString()); string tmpName = ComHelper.NullToEmpty(attendee["emailAddress"]?["name"]?.ToString()); MdCldAttendee mdAttendees = new MdCldAttendee { attedEmail = tmpEmail, attedName = tmpName, attedType = ComHelper.NullToEmpty(attendee["type"]) }; tmpList.attendees.Add(mdAttendees); if (userEmail == tmpEmail) { // 일정 불러온사람 기준이면(참석자가 불러온경우 일정호출자 기준이면 tmpList.userEmail = tmpEmail; tmpList.userName = tmpName; } } } rstAt.data.Add(tmpList); } } } else { rstAt.message = "M365 데이터를 가져오는데 실패하였습니다."; ComHelper.WriteErrorLog(rstAt.message, request, bodyData, "E_R054"); } } } catch (Exception ex) { rstAt.message = "M365 일정조회에 실패하였습니다."; rstAt.exmsg = ex.Message; ComHelper.WriteErrorLog(ex, request, bodyData, "E_R055"); } return rstAt; } #endregion #region 일정 상세정보 public async Task> GetEventDetailAsync(string userEmail, string evtID, string timeZone, HttpRequestMessage request, string bodyData) { await GetAccessTokenAsync(userEmail); MdResultT1 rstAt = new MdResultT1(false, ""); if (string.IsNullOrWhiteSpace(_AccessToken)) { rstAt.message = "M365 권한획득에 실패하였습니다."; ComHelper.WriteErrorLog(rstAt.message, request, bodyData, "E_R056"); return rstAt; } string tmpPoint = $"https://graph.microsoft.com/v1.0/users/{userEmail}/events/{Uri.EscapeDataString(evtID)}" + " ?$select=id,subject,body,attendees,organizer,start,end,location,importance,sensitivity"; try { using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _AccessToken); client.DefaultRequestHeaders.Add("Prefer", $"outlook.timezone=\"{timeZone}\""); var response = await client.GetAsync(tmpPoint); string result = await response.Content.ReadAsStringAsync(); MdCldData rstData = new MdCldData(); if (response.IsSuccessStatusCode) { JObject item = JObject.Parse(result); string appName = ComHelper.NullToEmpty(item["organizer"]?["emailAddress"]?["name"]); string appEmail = ComHelper.NullToEmpty(item["organizer"]?["emailAddress"]?["address"]); rstData.schKey = ComHelper.NullToEmpty(item["id"]); rstData.userName = appName; rstData.writeName = appName; rstData.userEmail = appEmail; rstData.writeEmail = appEmail; rstData.subject = ComHelper.NullToEmpty(item["subject"]); rstData.startDate = DateTime.Parse(item["start"]?["dateTime"]?.ToString()); rstData.startZone = ComHelper.NullToEmpty(item["start"]?["timeZone"]?.ToString()); rstData.endDate = DateTime.Parse(item["end"]?["dateTime"]?.ToString()); rstData.endZone = ComHelper.NullToEmpty(item["end"]?["timeZone"]?.ToString()); rstData.location = ComHelper.NullToEmpty(item["location"]?["displayName"]?.ToString()); // 장소 rstData.Importance = ComHelper.NullToEmpty(item["importance"]); rstData.contentType = ComHelper.NullToEmpty(item["body"]?["contentType"]?.ToString()); rstData.content = ComHelper.NullToEmpty(item["body"]?["content"]?.ToString()); rstData.sensitivity = ComHelper.NullToEmpty(item["sensitivity"]?.ToString()); rstData.repeatedYn = "N"; string resourceName = string.Empty; /* 속성 불러오기 var extProps = item["singleValueExtendedProperties"] as JArray; if (extProps != null && extProps.Count > 0) { resourceName = ComHelper.NullToEmpty(extProps[0]?["value"]?.ToString()); } */ rstData.recName = resourceName; var attendeesArray = item["attendees"] as JArray; if (attendeesArray != null) { foreach (var attendee in attendeesArray) { string tmpEmail = ComHelper.NullToEmpty(attendee["emailAddress"]?["address"]?.ToString()); string tmpName = ComHelper.NullToEmpty(attendee["emailAddress"]?["name"]?.ToString()); MdCldAttendee mdAttendees = new MdCldAttendee { attedEmail = tmpEmail, attedName = tmpName, attedType = ComHelper.NullToEmpty(attendee["type"]) }; rstData.attendees.Add(mdAttendees); if (userEmail == tmpEmail) { rstData.userName = tmpName; rstData.userEmail = tmpEmail; } } } } rstAt.success = response.IsSuccessStatusCode; rstAt.data = rstData; } } catch (Exception ex) { rstAt.message = "M365 일정조회에 실패하였습니다.";; rstAt.exmsg = ex.Message; ComHelper.WriteErrorLog(ex, request, bodyData, "E_R057"); } return rstAt; } #endregion #region 메일 발송 public async Task SendMailAsync(string userEmail, string senderEmail, List<(string Name, string Email)> toRct, List<(string Name, string Email)> ccRct, string subject, string body, string bodyType) { MdResultBase rBase = new MdResultBase(false, ""); await GetAccessTokenAsync(userEmail); if (string.IsNullOrWhiteSpace(_AccessToken)) { rBase.SetRst(false, "M365 권한획득에 실패하였습니다."); ComHelper.WriteErrorLog(new Exception(), senderEmail, "E_R058"); return rBase; } string sUrl = $"https://graph.microsoft.com/v1.0/users/{userEmail}/sendMail"; try { using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _AccessToken); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); //List<(string address, string name)> toList = new List<(string address, string name)>(); var message = new { message = new { subject = subject, body = new { contentType = bodyType, content = body }, toRecipients = toRct, ccRecipients = ccRct }, saveToSentItems = false }; var json = JsonConvert.SerializeObject(message); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await client.PostAsync(sUrl, content); if (response.IsSuccessStatusCode) { rBase.SetRst(true, "M365 메일발송완료."); } else { var error = await response.Content.ReadAsStringAsync(); ComHelper.WriteErrorLog(new Exception(), senderEmail, "E_R059"); } } } catch (Exception ex) { rBase.SetRst(false, "M365 메일발송에 실패하였습니다.", "", ex.Message); ComHelper.WriteErrorLog(ex, senderEmail, "E_R060"); } return rBase; } public async Task SendMailAsync(string userEmail, string senderEmail, List toRct, List ccRct, string subject, string body, string bodyType) { MdResultBase rBase = new MdResultBase(false, ""); await GetAccessTokenAsync(userEmail); if (string.IsNullOrWhiteSpace(_AccessToken)) { rBase.SetRst(false, "M365 권한획득에 실패하였습니다."); ComHelper.WriteErrorLog(new Exception(), senderEmail, "E_R061"); return rBase; } string sUrl = $"https://graph.microsoft.com/v1.0/users/{userEmail}/sendMail"; try { using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _AccessToken); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); //List<(string address, string name)> toList = new List<(string address, string name)>(); // 받는 사람(To) var toList = toRct.Select(r => new { emailAddress = new{ address = r.mail, name = r.userNm} }).ToList(); var ccList = ccRct.Select(r => new { emailAddress = new { address = r.mail, name = r.userNm } }).ToList(); var message = new { message = new { subject = subject, body = new { contentType = bodyType, content = body }, toRecipients = toList, ccRecipients = ccList }, saveToSentItems = false }; var json = JsonConvert.SerializeObject(message); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await client.PostAsync(sUrl, content); if (response.IsSuccessStatusCode) { rBase.SetRst(true, "M365 메일발송완료."); } else { var error = await response.Content.ReadAsStringAsync(); ComHelper.WriteErrorLog(new Exception(error), senderEmail, "E_R062"); rBase.SetRst(false, "M365 메일발송에 실패하였습니다.", "", error); } } } catch (Exception ex) { rBase.SetRst(false, "M365 메일발송에 실패하였습니다.", "", ex.Message); ComHelper.WriteErrorLog(ex, senderEmail, "E_R063"); } return rBase; } #endregion } }

2025년 2월 5일 수요일

유튜브-변환

// 비주얼스튜디오 > 도구 > NuGet패키지관리자 > 솔류션용 NuGet패키지관리 > VideoLibrary 검색 설치 (프레임웍:4.7.2, VideoLibrary:3.1.9)

using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using VideoLibrary;
using System.Net;

namespace WindowsFormsApp3
{
    public partial class Form1 : Form
    {
        char[] invalidChars = System.IO.Path.GetInvalidFileNameChars(); // 허용하지 않는 파일명을 찾기위해
        public Form1()
        {
            InitializeComponent();
            //cbbDownType.Items.Clear();
            //cbbDownType.DisplayMember = "Text";
            //cbbDownType.ValueMember = "Value";
            //cbbDownType.Items.Add(new { Text = "MP4 And MP3", Value = "0" });
            //cbbDownType.Items.Add(new { Text = "Only MP4", Value = "1" });
            //cbbDownType.Items.Add(new { Text = "Only MP3", Value = "2" });
            //cbbDownType.SelectedIndex = 0;
        }

        private void btnFolderSelect_Click(object sender, EventArgs e)
        {
            //folderDialog.ShowDialog();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string strUrl = "https://youtu.be/s3MiZJy4PLg?feature=shared";
            //string strType = (cbbDownType.SelectedItem as dynamic).Value;

            //if (string.IsNullOrEmpty(folderDialog.SelectedPath))
            //{
            //    folderDialog.ShowDialog();
            //}

            //if (string.IsNullOrEmpty(folderDialog.SelectedPath) == false)
            //{
            //    Thread trd = new Thread(() => YouTobeDown(strUrl, folderDialog.SelectedPath, strType));
            //    trd.Start();

            //}

            Thread trd = new Thread(() => YouTobeDown(strUrl, @"C:\Users\saengyeol_chun2\Desktop", ""));
            trd.Start();
        }

        private async void YouTobeDown(string strUrl, string strSavePath, string strType)
        {
            try
            {
                WebRequest GetTitle = HttpWebRequest.Create(strUrl);
                YouTube youtube = YouTube.Default;
                YouTubeVideo video = await youtube.GetVideoAsync(strUrl);
                string fileNm = "1111";
                File.WriteAllBytes(strSavePath + @"\" + fileNm + ".mp4", await video.GetBytesAsync());
            }
            catch (Exception ex)
            {
                //throw;
            }


            //if (strType == "0" || strType == "2")
            //{
            //}

        }

    }
}

변환

// 비주얼스튜디오 > 도구 > NuGet패키지관리자 > 솔류션용 NuGet패키지관리 > HtmlAgilityPack 검색해서 설치

using HtmlAgilityPack;
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            imgBas64Test001();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            imgBas64Test002();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            string strUrl = "http://kiken.kolon.com/Upload_Approval10/LS/Doc/2025/323/00000000000031540323.mht";
            Uri url = new Uri(strUrl);
            System.Net.WebClient wClient = new System.Net.WebClient();
            string strMhtData = wClient.DownloadString(url);

            ClsResult cRslt = getBoundaryText(ref strMhtData);
            if (cRslt.bSucc)
            {
                ClsHtmlDoc htmlRslt = mhtToHtml(ref strMhtData, cRslt.sResult,true); // htmlDocument정보와 결과 정보를 가지고 있음
                if (htmlRslt.cRslt.bSucc)
                {
                    htmlRslt.htmlDoc.Save(@"C:\Users\saengyeol_chun2\Desktop\111.html");
                }
            }
            else
            {
                MessageBox.Show(cRslt.sResult);
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            string strFlNm = @"C:\Users\saengyeol_chun2\Desktop\새 폴더\00000000000021838552.mht";
            StreamReader RS = new StreamReader(strFlNm);
            string strMhtData = RS.ReadToEnd();
            RS.Close();

            ClsResult cRslt = getBoundaryText(ref strMhtData);
            if (cRslt.bSucc)
            {
                ClsHtmlDoc htmlRslt = mhtToHtml(ref strMhtData, cRslt.sResult, true);    // htmlDocument정보와 결과 정보를 가지고 있음
                if (htmlRslt.cRslt.bSucc)
                {
                    htmlRslt.htmlDoc.Save(@"C:\Users\saengyeol_chun2\Desktop\222.html");
                }
            }
            else
            {
                MessageBox.Show(cRslt.sResult);
            }
        }


        private ClsResult getBoundaryText(ref string strMht)
        {
            ClsResult cRslt = new ClsResult();

            int nStarPos = strMht.IndexOf("boundary=");
            if (nStarPos > 0)
            {
                int nEndPos = strMht.IndexOf("\"", nStarPos + 10);
                string strMsg = "--" + strMht.Substring(nStarPos + 10, nEndPos - nStarPos - 10);
                cRslt.setResult(true, strMsg, "");
            }
            else
            {
                cRslt.setResult(false, "기준점 boundary를 찾을수 없습니다.", "");
            }
            return cRslt;
        }

        private ClsHtmlDoc mhtToHtml(ref string strMht, string strBoundary, bool isUtf8)
        {
            string [] arrMht = strMht.Split(new string[] { strBoundary }, StringSplitOptions.None);
            string strErr = string.Empty;
            ClsHtmlDoc cHtmlRslt = new ClsHtmlDoc();

            HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();
            if (arrMht.Length < 2)
            {
                
                cHtmlRslt.setHtmlDoc("본문의 boundary를 찾을수 없습니다.", "");
                return cHtmlRslt;
            }
            else
            {
                try
                {
                    //arrMht[1].Split(new string[] { Environment.NewLine + Environment.NewLine }, StringSplitOptions.None)[1].Trim();
                    string tmpItm = arrMht[1].Split(new string[] { "\n\n" }, StringSplitOptions.None)[1].Trim();
                    int nCnt = tmpItm.IndexOf("\n") + 1;
                    string strItm = tmpItm.Substring(nCnt).Trim();
                    byte[] arrHtml = Convert.FromBase64String(strItm);
                    string strHtml = string.Empty;
                    if (isUtf8)
                    {
                        strHtml = System.Text.Encoding.UTF8.GetString(arrHtml);
                    }
                    else
                    {
                        strHtml = System.Text.Encoding.GetEncoding("ks_c_5601-1987").GetString(arrHtml);
                    }
                    htmlDoc.LoadHtml(strHtml);
                }
                catch (Exception)
                {
                    cHtmlRslt.setHtmlDoc("본문 변환에 실패하였습니다.", "");
                    return cHtmlRslt;
                }
            }

            int nIdx = 1;
            HtmlNodeCollection hnc = htmlDoc.DocumentNode.SelectNodes("//img");
            ClsResult cRslt = new ClsResult(true);  // 이미지가 하나도 없는 경우가 있어 성공에서 시작
            foreach (HtmlNode hNode in hnc)
            {
                cRslt = GetImage(hNode, ref arrMht, nIdx);
                
                if (cRslt.bSucc)
                {
                    hNode.SetAttributeValue("src", cRslt.sResult);
                }
                else
                {
                    break;
                }
                nIdx++;
            }

            cHtmlRslt.setHtmlDoc(htmlDoc, cRslt);

            return cHtmlRslt;
        }


        private ClsResult GetHtml(string strMht, string strBoundary, bool isUtf8)
        {
            ClsResult cRslt = new ClsResult();
            try
            {
                string strItm = strMht.Split(new string[] { "\n\n" }, StringSplitOptions.None)[1].Trim();
                byte[] arrHtml = Convert.FromBase64String(strItm);
                string strHtml = string.Empty;
                if (isUtf8)
                {
                    strHtml = System.Text.Encoding.UTF8.GetString(arrHtml);
                }
                else
                {
                    strHtml = System.Text.Encoding.GetEncoding("ks_c_5601-1987").GetString(arrHtml);
                }
                cRslt.setResult(true, strHtml, "");
            }
            catch (Exception ex)
            {
                cRslt.setResult(false, ex.Message, "");
            }
            return cRslt;
        }


        private ClsResult GetImage(HtmlNode hNode, ref string[] arrMht, int nCnt)
        {
            ClsResult cRslt = new ClsResult();
            string strAttr = hNode.GetAttributeValue("src", "");
            if (string.IsNullOrWhiteSpace(strAttr))
            {
                cRslt.setResult(false, string.Format("{0}번째 src 속성 값이 없습니다.", nCnt), "");
            }
            else
            {
                try
                {
                    for (int idx = 2; idx < arrMht.Length; idx++)
                    {
                        if (arrMht[idx].IndexOf(strAttr) > -1)
                        {
                            string strItm = "data:image/png;charset=utf-8;base64," + arrMht[idx].Trim().Split(new string[] { "\n\n" }, StringSplitOptions.None)[1].Trim();
                            cRslt.setResult(true, strItm, "");
                        }
                    }
                }
                catch (Exception ex)
                {
                    cRslt.setResult(false, string.Format("{0}번째 이미지 찾는중 오류 발생 : {1}", nCnt, ex.Message), "");
                }
            }

            if (cRslt.sResult == string.Empty)
            {
                cRslt.setResult(false, string.Format("{0}번째 src 속성[{1}] 동일한 이미지 찾기에 실패하였습니다.", nCnt, strAttr), "");
            }

            return cRslt;
        }


        
        private void imgBas64Test001()
        {
            string flNm = @"C:\Users\saengyeol_chun2\Desktop\처리모음\캡처.PNG";
            string strHtml = @"
"; WebBrowser browser = new WebBrowser(); browser.ScriptErrorsSuppressed = true; browser.DocumentText = strHtml; browser.Document.Write(strHtml); System.Windows.Forms.HtmlDocument htmlDoc = browser.Document; HtmlElementCollection imgRows = htmlDoc.GetElementsByTagName("img"); foreach (HtmlElement imgElement in imgRows) { string tmpSrc = imgElement.GetAttribute("src"); if (string.IsNullOrWhiteSpace(tmpSrc) == false) { string strSrc = "data:image/png;charset=utf-8;base64," + GetStringFromImageFile(flNm); // 이미지를 Base64로 변환 imgElement.SetAttribute("src", strSrc); } } string outHtml = htmlDoc.Body.InnerHtml; } private void imgBas64Test002() { string flNm = @"C:\Users\saengyeol_chun2\Desktop\처리모음\캡처.PNG"; string strHtml = @"
"; HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument(); htmlDoc.LoadHtml(strHtml); HtmlNodeCollection imgRows = htmlDoc.DocumentNode.SelectNodes("//img"); foreach (HtmlNode imgElement in imgRows) { string tmpSrc = imgElement.GetAttributeValue("src", ""); if (string.IsNullOrWhiteSpace(tmpSrc) == false) { string strSrc = "data:image/png;charset=utf-8;base64," + GetStringFromImageFile(flNm); // 이미지를 Base64로 변환 imgElement.SetAttributeValue("src", strSrc); } } string outHtml = htmlDoc.DocumentNode.InnerHtml; } private string GetStringFromImageFile(string filename) { string base64Image = ""; Image img = System.Drawing.Image.FromFile(filename); MemoryStream mstream = new MemoryStream(); img.Save(mstream, img.RawFormat); byte[] imgBytes = mstream.ToArray(); base64Image = Convert.ToBase64String(imgBytes); return base64Image; } } public class ClsHtmlDoc { public HtmlAgilityPack.HtmlDocument htmlDoc; public ClsResult cRslt; /// /// 성공시 호출 /// /// public void setHtmlDoc(HtmlAgilityPack.HtmlDocument htmlDocument) { cRslt = new ClsResult(); htmlDoc = htmlDocument; cRslt.bSucc = true; } /// /// 실패시 호출 /// /// /// public void setHtmlDoc(ClsResult clsRslt) { cRslt = clsRslt; } /// /// 실패시 호출 (메세지와, 코드는 직접 입력) /// /// /// public void setHtmlDoc(string strResult, string strCode) { cRslt = new ClsResult(); cRslt.sResult = strResult; cRslt.sCode = strCode; } public void setHtmlDoc(HtmlAgilityPack.HtmlDocument htmlDocument, ClsResult clsRslt) { htmlDoc = htmlDocument; cRslt = new ClsResult(); } } public class ClsResult { public bool bSucc = false; public string sResult = string.Empty; public string sCode = string.Empty; //public string sEtc1 = string.Empty; //public string sEtc2 = string.Empty; public ClsResult() { } public ClsResult(bool isSucc) { bSucc = isSucc; } public void setResult(bool isSucc, string strMsg, string strCode) { bSucc = isSucc; sResult = strMsg; sCode = strCode; } /* public void setResult(bool isSucc, string strMsg, string strCode, string strEtc1) { bSucc = isSucc; sResult = strMsg; sCode = strCode; sEtc1 = strEtc1; } public void setResult(bool isSucc, string strMsg, string strCode, string strEtc1, string strEtc2) { bSucc = isSucc; sResult = strMsg; sCode = strCode; sEtc1 = strEtc1; sEtc2 = strEtc2; } */ } }

2017년 11월 8일 수요일

C# 비밀번호 정규식

string msg = ""; string _Pattern = @"(?=^.{9,15})(?=.*[a-zA-Z])(?=.*[!@#$%^&*+=->

2017년 2월 13일 월요일

기본적인 유효성 검사 및 태그 틀 layerMsg 및 focus

---------------- 코드 실행 시작---------------------------------------------------------------
1-1항목 1-2항목 숫자만
2-1항목 2-2항목 숫자+콤마
3-1항목 3-2항목 숫자+장선(-)
4-1항목 4-2항목 숫자+점(.)
에러메세지....
---------------- 코드 실행 종료---------------------------------------------------------------
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<style type="text/css">
 .layer-main {
 z-index:2000;
 display:none;
 position: fixed;
 left: 0;
 right: 0;
 top: 0;
 bottom: 0;
 text-align: center;
 background-color: rgba(0, 0, 0, 0.5);
}
.layer-main:before {
 content: "";
 display: inline-block;
 height: 100%;
 vertical-align: middle;
 margin-right: -.25em;
}
.layer-popup {
 display: inline-block;
 vertical-align: middle;
 background-color: #fff;
 border: 1px solid #3571B5;
 z-index: 10;
 font-family:Tahoma;
}
.layer-popup .layer-container {
 padding-bottom: 20px;
}

.layer-title {
 overflow:hidden;
 margin:0px;
 padding:0px;
 background-color: #474747;
 height:36px;
}
.layerTitle {
 float:left;
 line-height:36px;
 margin:0px;
 padding:0px 0px 0px 10px;
 font-size:15px;
 color:#ffffff;
}

.layer-close {
 float:right;
 margin:0px;
 padding:8px 10px;
 cursor:pointer;
}

.layerConts {
 clear:both;
 margin-top:10px;
 padding: 10px;
}
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function (){
$("[d-valid='ValidNumber']").each(function (){
   // 익스 플로러 10버전 이상 및 (크롬 사파리 포함) 지원하면 keydown 이벤트를 input 이벤트로 데체 하면 좋음!!!!
$(this).on("keydown", function () {kdNumber(this);});
$(this).on("keyup", function () {kuNumber(this);});
});

$("[d-valid='ValidNumComma']").each(function (){
$(this).on("keydown", function () {kdNumComma(this);});
$(this).on("keyup", function () {kuNumComma(this);});
$(this).on("blur", function () {kbNumComma(this);});
});

$("[d-valid='ValidNumMinus']").each(function (){
$(this).on("keydown", function () {kdNumMinus(this);});
$(this).on("keyup", function () {kuNumMinus(this);});
});

$("[d-valid='ValidNumDot']").each(function (){
$(this).on("keydown", function () {kdNumDot(this);});
$(this).on("keyup", function () {kuNumDot(this);});
});
});

/* -- prototype -------------------------------------------------------------------------------------------------------------------*/
String.prototype.DelComma = function () {
return this.replace( /,/g, "");
}

String.prototype.AddComma = function () {
return this.replace(/[^0-9]/g , "").replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}


/* -- replace후 커서위치 유지 -------------------------------------------------------------------------------------------------------------------*/
function CurserSet(Obj, selLen) {
if (Obj.setSelectionRange) { Obj.focus(); Obj.setSelectionRange(selLen, selLen); } /* WebKit */
else if (Obj.createTextRange) { var range = Obj.createTextRange(); range.collapse(true); range.moveEnd('character', selLen); range.moveStart('character', selLen); range.select(); } /* IE */
else if (Obj.selectionStart) { Obj.selectionStart = selLen; Obj.selectionEnd = selLen; }
}

/* -- 숫자만 -------------------------------------------------------------------------------------------------------------------*/
function ValidNumber(Obj) {
var jObj = $(Obj);
var val = jObj.val();
var required = jObj.attr("d-required");
if(required.toLowerCase() != "true" && val == ""){
return true;
}

var msg = jObj.attr("d-msg");
if(jObj.attr("d-required") == "true" && val == "" ){
if(jObj.val() == ""){
msgShow(Obj, msg + " 항목 값을 입력해주세요.");
}
else{
msgShow(Obj, msg + " 항목 입력값을 확인하여 주세요.");
}
}
else{
var ext = /[^0-9]/g;
if(ext.test(val)){
msgShow(Obj, msg + " 항목 입력값을 확인하여 주세요.");
}
else{
return true;
}
}
jObj.focus();
return false;
}

function kuNumber(Obj) {
var selLen = Obj.selectionStart;
var oVal = $(Obj).val();
var tVal = oVal.replace(/[^0-9]/g , "");
if (oVal != tVal) {
$(Obj).val(tVal);
CurserSet(Obj, selLen + tVal.length - oVal.length);
}
}

function kdNumber(Obj) {
var eKey = event.keyCode;
if (((eKey >= 48 && eKey <= 57) || (eKey >= 96 && eKey <= 105) || eKey == 8 || eKey == 46 || eKey == 37 || eKey == 39 || eKey == 9) && (event.shiftKey == false)) {
}
else {
(event.preventDefault) ? event.preventDefault() : event.returnValue = false;
}
}

/* -- 숫자+콤마(,) -------------------------------------------------------------------------------------------------------------------*/
function ValidNumComma(Obj) {
var jObj = $(Obj);
var val = jObj.val();
var required = jObj.attr("d-required");
if(required.toLowerCase() != "true" && val == ""){
return true;
}

val = val.DelComma();
var msg = jObj.attr("d-msg");
if(jObj.attr("d-required") == "true" && val == "" ){
if(jObj.val() == ""){
msgShow(Obj, msg + " 항목 값을 입력해주세요.");
}
else{
msgShow(Obj, msg + " 항목 입력값을 확인하여 주세요.");
}
}
else{
var ext = /[^0-9]/g;
if(ext.test(val)){
msgShow(Obj, msg + " 항목 입력값을 확인하여 주세요.");
}
else{
return true;
}
}
jObj.focus();
return false;
}

function kuNumComma(Obj) {
var selLen = Obj.selectionStart;
var oVal = $(Obj).val();
var tVal = oVal.replace(/[^,0-9]/g , "");
if (oVal != tVal) {
$(Obj).val(tVal);
CurserSet(Obj, selLen + tVal.length - oVal.length);
}
}

function kdNumComma(Obj) {
var eKey = event.keyCode;
if (((eKey >= 48 && eKey <= 57) || (eKey >= 96 && eKey <= 105) || eKey == 8 || eKey == 188 || eKey == 46 || eKey == 37 || eKey == 39 || eKey == 9) && (event.shiftKey == false)) {
}
else {
(event.preventDefault) ? event.preventDefault() : event.returnValue = false;
}
}

function kbNumComma(Obj) {
var tval = $(Obj).val().AddComma();
$(Obj).val(tval);
}

function AddComma(Obj) {
$(Obj).val($(Obj).val().AddComma());
}

function DelComma(Obj) {
$(Obj).val($(Obj).val().DelComma());
}



/* -- 숫자+장선(-) -------------------------------------------------------------------------------------------------------------------*/
function ValidNumMinus(Obj) {
var jObj = $(Obj);
var val = jObj.val();
var required = jObj.attr("d-required");
if(required.toLowerCase() != "true" && val == ""){
return true;
}

val = val.replace( /-/g, "");
var msg = jObj.attr("d-msg");
if(jObj.attr("d-required") == "true" && val == "" ){
if(jObj.val() == ""){
msgShow(Obj, msg + " 항목 값을 입력해주세요.");
}
else{
msgShow(Obj, msg + " 항목 입력값을 확인하여 주세요.");
}
}
else{
var ext = /[^0-9]/g;
if(ext.test(val)){
msgShow(Obj, msg + " 항목 입력값을 확인하여 주세요.");
}
else{
return true;
}
}
jObj.focus();
return false;
}

function kuNumMinus(Obj) {
var selLen = Obj.selectionStart;
var oVal = $(Obj).val();
var tVal = oVal.replace(/[^-0-9]/g , "");
if (oVal != tVal) {
$(Obj).val(tVal);
CurserSet(Obj, selLen + tVal.length - oVal.length);
}
}

function kdNumMinus(Obj) {
var eKey = event.keyCode;
if (((eKey >= 48 && eKey <= 57) || (eKey >= 96 && eKey <= 105) || (eKey == 189 || eKey == 109) || eKey == 8 || eKey == 46 || eKey == 37 || eKey == 39 || eKey == 9) && (event.shiftKey == false)) {
}
else {
(event.preventDefault) ? event.preventDefault() : event.returnValue = false;
}
}

/* -- 숫자+점(.) -------------------------------------------------------------------------------------------------------------------*/
function ValidNumDot(Obj) {
var jObj = $(Obj);
var val = jObj.val();
var required = jObj.attr("d-required");
if(required.toLowerCase() != "true" && val == ""){
return true;
}

val = val.replace( /\./g, "")
var msg = jObj.attr("d-msg");
if(jObj.attr("d-required") == "true" && val == "" ){
if(jObj.val() == ""){
msgShow(Obj, msg + " 항목 값을 입력해주세요.");
}
else{
msgShow(Obj, msg + " 항목 입력값을 확인하여 주세요.");
}
}
else{
var ext = /[^0-9]/g;
if(ext.test(val)){
msgShow(Obj, msg + " 항목 입력값을 확인하여 주세요.");
}
else{
return true;
}
}
jObj.focus();
return false;
}

function kuNumDot(Obj) {
var selLen = Obj.selectionStart;
var oVal = $(Obj).val();
var tVal = oVal.replace(/[^.0-9]/g , "");
if (oVal != tVal) {
$(Obj).val(tVal);
CurserSet(Obj, selLen + tVal.length - oVal.length);
}
}

function kdNumDot(Obj) {
var eKey = event.keyCode;
if (((eKey >= 48 && eKey <= 57) || (eKey >= 96 && eKey <= 105) || eKey == 8 || eKey == 46 || eKey == 37 || eKey == 39 || eKey == 9 || eKey == 190 || eKey == 110) && (event.shiftKey == false)) {
}
else {
(event.preventDefault) ? event.preventDefault() : event.returnValue = false;
}
}

function IsValidate(){
var isSucc = true;
$("[d-valid]").each(function (){
if(isSucc){
var valid = $(this).attr("d-valid");
isSucc = eval(valid + "(this)");
}
});
if(isSucc){
alert("데이터 전송 로직을 작성하시면 됩니다.");
}
else{
return false;
}
}

var _focusObj;
function msgShow(Obj, Msg){
_focusObj = Obj;
AlertMsg(Msg);
}

function AlertMsg(Msg){
$(".layerConts").html(Msg);
$(".layer-main").show();
}

function closelayer(){
$(".layer-main").hide();
$(_focusObj).focus();
}
    </script>
</head>
<body>
<table style="width:600px;">
<colgroup>
<col style="width:60px;" />
<col style="width:140px;" />
<col style="width:60px;" />
<col style="width:140px;" />
<col style="width:200px;" />
</colgroup>
<tr>
<th>1-1항목</th>
<td>
<input type="text" style="width:100%;" d-valid="ValidNumber" d-required="true" d-msg="1-1항목" />
</td>
<th>1-2항목</th>
<td>
<input type="text" style="width:100%;" d-valid="ValidNumber" d-required="false" d-msg="1-2항목" />
</td>
<td>숫자만</td>
</tr>
<tr>
<th>2-1항목</th>
<td>
<input type="text" style="width:100%;" d-valid="ValidNumComma" d-required="true" d-msg="2-1항목" />
</td>
<th>2-2항목</th>
<td>
<input type="text" style="width:100%;" d-valid="ValidNumComma" d-required="false" d-msg="2-2항목" />
</td>
<td>숫자+콤마</td>
</tr>
<tr>
<th>3-1항목</th>
<td>
<input type="text" style="width:100%;" d-valid="ValidNumMinus" d-required="true" d-msg="3-1항목" />
</td>
<th>3-2항목</th>
<td>
<input type="text" style="width:100%;" d-valid="ValidNumMinus" d-required="false" d-msg="3-2항목" />
</td>
<td>숫자+장선(-)</td>
</tr>
<tr>
<th>4-1항목</th>
<td>
<input type="text" style="width:100%;" d-valid="ValidNumDot" d-required="true" d-msg="4-1항목" />
</td>
<th>4-2항목</th>
<td>
<input type="text" style="width:100%;" d-valid="ValidNumDot" d-required="false" d-msg="4-2항목" />
</td>
<td>숫자+점(.)</td>
</tr>
</table>
</body>
</html>
<input type="button" value="유효성체크"  onclick="IsValidate();"/>


<!----- 레이어 alert 메세지 ----->
<div class="layer-main">
    <div class="layer-popup">
        <div class="layer-container">
            <div class="layer-conts">
                <div class="layer-title">
                    <div class="layerTitle">
                        에러메세지....
                    </div>
                    <div class="layer-close" onclick="closelayer()">
                        <img src="/images/kr/cm/btn_popupClose.gif" alt="" />
                    </div>
                </div>
                <div class="layerConts">
                </div>
                <div>
                    <a href="javascript:closelayer();" class="">
                        닫기
                    </a>
                </div>
            </div>
        </div>
    </div>
</div>