티스토리 뷰

프로그래밍

C#으로 문자 보내기

야라바 2025. 8. 29. 19:06
728x90

프로그램으로 문자를 보내는 방법은 어떤 서비스 업체를 선택하는가로 시작한다. 네이버와 같은 대형 포털 업체를 사용할 수도 있고 중소 전문 업체를 이용할 수도 있다. 필자의 경우에는 깃허브에 C# 예제 코드와 라이브러리를 공개하고 있는 CoolSMS를 이용하기로 했다. 

 

회원 가입 시점에서 개인 아이디로 가입할지 사업자 계정을 만들지를 묻는다. 당연히 사업자 계정인 경우에는 대량 발송이나 멤버 관리가 필요할 것으로 보인다. 

 

회원 가입이 끝나면 자동으로 위의 그림처럼 300원을 충전해 주므로 건당 20원씩 생각하면 개발 중 15건의 테스트가 가능하다. 개발을 위해서 꼭 필요한 것은 API Key로 [새로운 API KEY] 버튼을 눌러서 키를 발급받는다. 발급 과정에서 IP 대역을 지정하여 특정한 IP대역에서만 접근할 수 있도록 하여 보안성을 높일 수 있는데 애매하면 모든 IP에서 접근 가능하도록 키를 발급하면 된다. 발급이 끝나면 API KEY와 API SECRET을 복사해서 사용하면 된다.

 

https://github.com/coolsms/coolsms-csharp/tree/main

 

CoolSMS 서비스를 C#으로 사용할 수 있는 코드는 위의 깃허브 주소에서 확인할 수 있다. 일반적인 방법은 Config-dist.cs을  Config.cs로 변경하여 API KEY와 API SECRET을 적용하고 MessagingLib.cs 파일을 프로젝트에 포함시켜 사용하는 간단한 방법이다. 

 

필자의 경우 사용 환경에 따라서 API Key가 바뀔 수도 있어서 사용자에게 입력받은 내용을 동적으로 적용할 수 있도록 했고, 닷넷 4.8 환경이고 언어 버전이 낮아서 깃허브에서 배포하고 있는 코드가 호환되지 않는 부분이 있었다. 그리고 SMS 전용이고 LMS를 사용하지 않는 방식으로 MessagingLib.cs 코드를 조금 수정하고 Config.cs도 하나의 코드로 병합시켰다.

 

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

static class Config
{
    public static string apiKey = "";
    public static string apiSecret = "";
    public static string domain = "api.coolsms.co.kr";
    public static string protocol = "https";
    public static string prefix = "";
}

static class MessagingLib
{
    public class Agent
    {
        public string osPlatform;
        public string sdkVersion;
        
        public Agent()
        {
            osPlatform = Environment.OSVersion.Platform + " | " + Environment.Version;
            sdkVersion = "C#/1.0.2";
        }
    }
    
    public class Message
    {
        public string type;
        public string to;
        public string from;
        public string subject;
        public string text;
        public string imageId;
        public KakaoOptions kakaoOptions;
    }

    public class Messages
    {
        public List<Message> messages = new List<Message>();

        public void Add(Message message)
        {
            messages.Add(message);
        }
    }

    class GroupInfo
    {
        public string appId;
        public bool strict;
        public string sdkVersion;
        public string osPlatform;
    }

    class Image
    {
        public string type;
        public string file;
        public string name;
        public string link;
    }

    public class KakaoButton
    {
        public string buttonType;
        public string buttonName;
        public string linkMo;
        public string linkPc;
        public string linkAnd;
        public string linkIos;
    }
    public class KakaoOptions
    {
        public string pfId;
        public string templateId;
        public bool disableSms;
        public string imageId;
        public KakaoButton[] buttons;
    }

    public class Response
    {
        public System.Net.HttpStatusCode StatusCode;
        public string ErrorCode;
        public string ErrorMessage;
        public JObject Data;
    }

    private static JsonSerializerSettings JsonSettings = new JsonSerializerSettings()
    {
        NullValueHandling = NullValueHandling.Ignore
    };

    public static string GetSignature(string apiKey, string data, string apiSecret)
    {
        System.Security.Cryptography.HMACSHA256 sha256 = new System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes(apiSecret));
        byte[] hashValue = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(data));
        string hash = BitConverter.ToString(hashValue).Replace("-", "");
        return hash.ToLower();
    }

    public static string GetSalt(int len = 32)
    {
        string s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random r = new Random();
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        for (int i = 1; i <= len; i++)
        {
            int idx = r.Next(0, 35);
            sb.Append(s.Substring(idx, 1));
        }
        return sb.ToString();
    }
    public static string GetAuth(string apiKey, string apiSecret)
    {
        string salt = GetSalt();
        string dateStr = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
        string data = dateStr + salt;

        return "HMAC-SHA256 apiKey=" + apiKey + ", date=" + dateStr + ", salt=" + salt + ", signature=" + GetSignature(apiKey, data, apiSecret);
    }

    public static string GetUrl(object path)
    {
        string url = Config.protocol + "://" + Config.domain;
        if (!string.IsNullOrEmpty(Config.prefix))
        {
            url += Config.prefix;
        }
        url += path;
        return url;
    }

    public static Response Request(string path, string method, string data = null)
    {
        string auth = GetAuth(Config.apiKey, Config.apiSecret);

        try
        {
            System.Net.WebRequest req = System.Net.WebRequest.Create(GetUrl(path));
            req.Method = method;
            req.Headers.Add("Authorization", auth);
            req.ContentType = "application/json; charset=utf-8"; // .NetFrameWork 호환성으로 인한 오류 발생 시 이 구문 사용. 아래 구문은 주석처리 
            //req.Headers.Add("Content-type", "application/json; charset=utf-8");

            if (!string.IsNullOrEmpty(data))
            {
                using (var writer = new System.IO.StreamWriter(req.GetRequestStream()))
                {
                    writer.Write(data);
                    writer.Close();
                }
            }

            using (System.Net.WebResponse response = req.GetResponse())
            {
                using (System.IO.StreamReader streamReader = new System.IO.StreamReader(response.GetResponseStream()))
                {
                    var jsonResponseText = streamReader.ReadToEnd();
                    JObject jsonObj = JObject.Parse(jsonResponseText);
                    return new Response()
                    {
                        StatusCode = System.Net.HttpStatusCode.OK,
                        Data = jsonObj,
                        ErrorCode = null,
                        ErrorMessage = null
                    };
                }
            }
        }
        catch (System.Net.WebException ex)
        {
            using (System.IO.StreamReader streamReader = new System.IO.StreamReader(ex.Response.GetResponseStream()))
            {
                var jsonResponseText = streamReader.ReadToEnd();
                JObject jsonObj = JObject.Parse(jsonResponseText);
                string ErrorCode = jsonObj.SelectToken("errorCode").ToString();
                string ErrorMessage = jsonObj.SelectToken("errorMessage").ToString();
                System.Net.HttpWebResponse httpResp = (System.Net.HttpWebResponse)ex.Response;
                return new Response()
                {
                    StatusCode = httpResp.StatusCode,
                    Data = jsonObj,
                    ErrorCode = ErrorCode,
                    ErrorMessage = ErrorMessage
                };
            }
        }
        catch (Exception ex)
        {
            string ErrorCode = "Unknown Exception";
            string ErrorMessage = ex.Message;

            return new Response()
            {
                StatusCode = System.Net.HttpStatusCode.BadRequest,
                Data = null,
                ErrorCode = ErrorCode,
                ErrorMessage = ErrorMessage
            };
        }
    }

    class Group
    {
        private string groupId;

        public Group()
        {
        }


        public Group(string groupId)
        {
            this.groupId = groupId;
        }

        public Response AddMessages(Messages msgs)
        {
            if (string.IsNullOrEmpty(groupId))
                throw new System.Exception("그룹아이디가 설정되지 않았습니다.");
            return Request("/messages/v4/groups/" + groupId + "/messages", "POST", JsonConvert.SerializeObject(msgs, Formatting.None, JsonSettings));
        }

        public Response Create()
        {
            GroupInfo groupInfo = new GroupInfo()
            {
                osPlatform = Environment.OSVersion.VersionString + " | " + Environment.Version,
                sdkVersion = "C#/1.0.1"
            };
            return Request("/messages/v4/groups", "POST", JsonConvert.SerializeObject(groupInfo, Formatting.None, JsonSettings));
        }

        public Response GetList()
        {
            return Request("/messages/v4/groups", "GET");
        }
    }


    public static Response SendMessages(Messages messages)
    {
        return Request("/messages/v4/send-many", "POST", JsonConvert.SerializeObject(messages, Formatting.None, JsonSettings));
    }
}

비주얼스튜디오 2019, 닷넷 프레임워크 4.8 환경에서 SMS 전용으로 사용할 수 있도록 약간 수정한 MessagingLib.cs이다. 프로젝트 내에 포함시키면 별도의 도구 설치는 필요 없다.

 

MessagingLib.Messages messages = new MessagingLib.Messages();
messages.Add(new MessagingLib.Message()
{
	to = sms_recv.Text,
	from = sms_send.Text,
	text = "테스트 문자입니다."
});

Config.apiKey = sms_apikey.Text;
Config.apiSecret = sms_apipass.Text;
bool send_ok = false;
try
{
	MessagingLib.Response response = MessagingLib.SendMessages(messages);
	if (response.StatusCode == System.Net.HttpStatusCode.OK)
	{
		send_ok = true;
	}
	else
	{
		send_ok = false;
	}
}
catch (Exception)
{
	send_ok = false;
}

위의 코드는 프로젝트에 추가한 MessagingLib.cs를 바탕으로 WinForm을 통해 입력받은 내용을 기반으로 문자 전송 테스트를 수행하는 코드이다. messages.Add()를 통해 한번에 여러 건을 문자를 보낼 수도 있다. API KEY와 API SECRET은 글로벌 변수를 통해서 전달한다.

 

728x90
댓글
최근에 올라온 글
최근에 달린 댓글
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함