C# 알아가기 (6-1. C# 네이버 뉴스 Windows Form으로 보여주기)

min seung moon·2021년 8월 16일
0

C#알아가기

목록 보기
10/10
post-custom-banner

1. 네이버 뉴스 Windows Form으로 보여주기

01. 결과

  • 이전에 코드를 거의 수정 없이 사용했구요!
  • 이번에는 윈도우 폼이다 보니 이벤트나 컨트롤이 추가가 되었고 OriginalLink 또는 Link를 클릭하면 크롬 브라우저로 열어주게 할거에요!
  • 한번 천천히 볼까요!

02. 패키지 설치

01.CefSharp

  • CefSharp을 검색해서 WinForms를 선택해주세요!
    • 만약에 나는 다르다 하면 각자에게 맞는 패키지 설치해주세요!
    • 설치하고 나면 뭐 이런 저런 파일이 많이 나오는데 그건 내가 이 패키지를 활용할거면 꼼꼼히 읽어 보세요!
  • 디버그 설정을 해주셔야 해요!
    • 저는 이미 설정해서 x64가 보이지만 없다면 구성관리자에서 추가해주셔야 해요!
    • 있으면 그냥 선택해주세요! 그리고 옆에 실행 프로젝트 확인 잘 해주시구요!



02. Newtonsoft.Json

  • JSON을 사용하기 위한 패키지입니다!

03. Windows Form 구성

-1. NaverNews.cs(Form)

  • TableLayoutPanel, Panel, Label, TextBox(titleTxt), Button(searchBtn), DataGridView(newScriptGv)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CSharpBasic
{
    public partial class NaverNews : Form
    {
        private DataTable dt = null;
        public NaverNews()
        {
            InitializeComponent();

            // DataGridView 초기화(선택사항)
            dt = new DataTable();
            dt.Columns.Add("Title", typeof(String));
            dt.Columns.Add("OriginalLink", typeof(String));
            dt.Columns.Add("Link", typeof(String));
            dt.Columns.Add("Description", typeof(String));
            dt.Columns.Add("Pubdate", typeof(DateTime));
            newScriptGv.DataSource = dt;
        }        

        // 검색 버튼 이벤트
        private void searchBtn_Click(object sender, EventArgs e)
        {
            SearchNews(titleTxt.Text);
        }

        // 텍스트박스 키업 이벤트
        private void titleTxt_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                SearchNews(titleTxt.Text);
            }
        }

        // 검색 메서드
        private void SearchNews(string searchTitle)
        {
            // naver api id & scret key
            string id = "";
            string secret = "";
            NaverNewsDao nn = new NaverNewsDao(id, secret);

            // JSON
            List<NaverNewsDto> news = nn.FindNewsJson(1, 20, searchTitle);
            dt = LinqQueryToDataTable(news);

            // XML
            //int total = nn.Find(searchTitle);
            //List<NaverNewsDto> nc = nn.FindNewsToXML(1, 20);
            //dt = LinqQueryToDataTable(nc);

            newScriptGv.DataSource = dt;
        }

        // List -> DataTable
        private DataTable LinqQueryToDataTable(IEnumerable<dynamic> query)
        {
            // 시퀀스의 첫 번째 요소를 반환하거나, 요소가 없으면 기본값을 반환합니다.
            var firstRecord = query.FirstOrDefault();
            if (firstRecord == null) return null;

            // 속성의 특성을 검색하고 속성 메타데이터에 대한 액세스를 제공합니다.
            PropertyInfo[] infos = firstRecord.GetType().GetProperties();

            // 반환할 데이터테이블
            DataTable table = new DataTable();

            // 데이터 테이블에 컬럼 추가
            foreach (var info in infos)
            {
                Type propType = info.PropertyType;

                // Nullable types should be handled too
                // IsGenericType, 현재 형식이 제네릭 형식인지를 나타내는 값을 가져옵니
                // GetGenericTypeDefinition, 현재 제네릭 형식을 생성할 수 있는 제네릭 형식 정의를 나타내는 Type 개체를 반환합니다.
                if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    // 지정된 nullable 형식의 내부 형식 인수를 반환합니다.
                    table.Columns.Add(info.Name, Nullable.GetUnderlyingType(propType));
                }
                else
                {
                    table.Columns.Add(info.Name, info.PropertyType);
                }
            }

            DataRow row;

            // 데이터 테이블에 데이터 추가
            foreach (var record in query)
            {
                row = table.NewRow();
                for (int i = 0; i < table.Columns.Count; i++)
                {
                    row[i] = infos[i].GetValue(record) != null ? infos[i].GetValue(record) : DBNull.Value;
                }
                table.Rows.Add(row);
            }
            // 마지막으로 호출된 이후 이 테이블에서 변경된 내용을 모두 커밋
            table.AcceptChanges();

            return table;
        }

        // 크롬 브라우저 보여주기
        private void newScriptGv_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            DataGridView gv = (DataGridView)sender;

            if(e.ColumnIndex != 1 && e.ColumnIndex != 2 || e.RowIndex < 0)
            {
                MessageBox.Show("링크를 클릭하면 해당 페이지가 열립니다");
                return;
            }

            string url = gv.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
            if(string.IsNullOrEmpty(url))
            {
                MessageBox.Show("검색 후 클릭해주세요.");
                return;
            }

            (new NaverNewsBrowser(url)).ShowDialog();
        }
    }
}

-2. NaverNewsBrowser.cs(Form)

  • 어차피 크롬 띄울거라 아무것도 추가 안했어요!
  • 조건을 줘서 이전에 생성한 적이 있는지 확인 필!
    • 안해주면 또 만들게 되서 충돌로 인한 에러가 발생!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;

namespace CSharpBasic
{
    public partial class NaverNewsBrowser : Form
    {
        ChromiumWebBrowser chrome = null;

        public NaverNewsBrowser(string url)
        {
            InitializeComponent();
            InitializeChromeBrowser(url);

        }

        private void InitializeChromeBrowser(string url)
        {
            if (!Cef.IsInitialized) // 만약 초기화를 안해줬다면 생성
            {
                CefSettings cefSettings = new CefSettings();
                Cef.Initialize(cefSettings);
            }
            
            chrome = new ChromiumWebBrowser(url);
            chrome.Dock = DockStyle.Fill;
            this.Controls.Add(chrome);
        }
    }
}

04. DTO & DAO

-1. NaverNewsDto.cs

  • 이전 News.cs 파일 입니다!
    • 바뀐건 Make() 메서드를 오버로딩 해주었습니다!
      • XML과 JSON에서 사용하는 내부 메서드가 달라서 오버로딩 해주었습니다!
      • Make() 메서드는 태그 없애주는 Strip()메서드로 태그를 없앤 내용을 Dto개체로 만들어서 반환해줍니다!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace CSharpBasic
{
    class NaverNewsDto
    {
        public string Title { get; set; }
        public string OriginalLink { get; set; }
        public string Link { get; set; }
        public string Description { get; set; }
        public DateTime Pubdate { get; set; }

        public NaverNewsDto(string title, string originalLink, string link, string description, DateTime pubdate)
        {
            Title = title;
            OriginalLink = originalLink;
            Link = link;
            Description = description;
            Pubdate = pubdate;
        }

        // XML 형식으로 데이터를 받아올 때
        public static NaverNewsDto Make(XmlNode xn)
        {
            try
            {
                string title = Strip(xn.SelectSingleNode("title").InnerText);
                string olink = Strip(xn.SelectSingleNode("originallink").InnerText);
                string link = Strip(xn.SelectSingleNode("link").InnerText);
                string description = Strip(xn.SelectSingleNode("description").InnerText);
                DateTime pubdate = DateTime.Parse(xn.SelectSingleNode("pubDate").InnerText);
                return new NaverNewsDto(title, olink, link, description, pubdate);
            }
            catch
            {
                return null;
            }
        }
        // JSON
        public static NaverNewsDto Make(NaverNewsDto dto)
        {
            try
            {
                string title = Strip(dto.Title);
                string olink = Strip(dto.OriginalLink);
                string link = Strip(dto.Link);
                string description = Strip(dto.Description);
                DateTime pubdate = DateTime.Parse(dto.Pubdate.ToString());
                return new NaverNewsDto(title, olink, link, description, pubdate);
            }
            catch
            {
                return null;
            }
        }

        private static string Strip(string innerText)
        {
            int s = innerText.IndexOf("<");
            int e = innerText.IndexOf(">");
            while (s < e)
            {
                string b = innerText.Substring(0, s);
                string a = innerText.Substring(e + 1);
                innerText = b + a;
                s = innerText.IndexOf("<");
                e = innerText.IndexOf(">");
            }
            return innerText;
        }

        public override string ToString()
        {
            return Title;
        }
    }
}

-2. NaverNewsDao.cs

  • 이전 NaverNews.cs 파일 입니다!
    • 추가된건 XMLUrl, JsonUrl 거슬려서 따로 변수로 만들었어요!
    • 그리고 FindNewsJson() 메서드 아랫부분에 NaverNewsDto.Make(ns)를 추가해줬어요!
      • JSON으로 받아왔는데 태그가 보여서 없애주는 용도에요!
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace CSharpBasic
{
    class NaverNewsDao
    {
        private string SRC { get; set; } // search title
        private string ID { get; set; } // naver api id
        private string Secret { get; set; } // naver api secret

        private string XMLUrl = "https://openapi.naver.com/v1/search/news.xml";
        private string JsonUrl = "https://openapi.naver.com/v1/search/news.json";
        public NaverNewsDao(string iD, string secret)
        {
            this.ID = iD;
            this.Secret = secret;
        }

        // XML 데이터를 받아올 때
        public int Find(string src)
        {
            this.SRC = src;
            Stream stream;
            string url = $"{XMLUrl}?query={SRC}&sort=date";
            XmlDocument xdoc = MakeXMLDocument(url, out stream);
            XmlNode node = xdoc.SelectSingleNode("rss");
            XmlNode n = node.SelectSingleNode("channel");
            int total = int.Parse(n.SelectSingleNode("total").InnerText);
            stream.Close();
            return total;
        }

        private XmlDocument MakeXMLDocument(string url, out Stream stream)
        {
            WebRequest request = null;
            request = WebRequest.Create(url);
            request.Headers.Add("X-Naver-Client-Id", ID);
            request.Headers.Add("X-Naver-Client-Secret", Secret);

            WebResponse response = request.GetResponse();
            stream = response.GetResponseStream();
            XmlDocument xdoc = new XmlDocument();
            xdoc.Load(stream);
            return xdoc;
        }

        public List<NaverNewsDto> FindNewsToXML(int start, int display)
        {
            Stream stream;
            string url = $"{XMLUrl}?query={SRC}&display={display}&start={start}&sort=date";

            XmlDocument xdoc = MakeXMLDocument(url, out stream);
            XmlNode node = xdoc.SelectSingleNode("rss");
            XmlNode n = node.SelectSingleNode("channel");
            XmlNodeList xnl = n.SelectNodes("item");
            List<NaverNewsDto> nc = new List<NaverNewsDto>();
            NaverNewsDto news;
            foreach (XmlNode xn in xnl)
            {
                news = NaverNewsDto.Make(xn);
                if (news == null)
                {
                    break;
                }
                nc.Add(news);
            }
            stream.Close();
            return nc;
        }

         // JSON 형식으로 받아올 때
        private string MakeJSONDocument(string url)
        {
            string responseFromServer = string.Empty;

            try
            {
                WebRequest request = WebRequest.Create(url);

                request.Method = "GET";
                request.ContentType = "application/json";
                request.Headers["X-Naver-Client-Id"] = ID;
                request.Headers["X-Naver-Client-Secret"] = Secret;

                using (WebResponse response = request.GetResponse())
                using (Stream dataStream = response.GetResponseStream())
                using (StreamReader reader = new StreamReader(dataStream))
                    responseFromServer = reader.ReadToEnd();
            }
            catch
            {
                return null;
            }

            return responseFromServer;
        }

        public List<NaverNewsDto> FindNewsJson(int start, int display, string src)
        {
            this.SRC = src;
            string url = $"{JsonUrl}?query={SRC}&display={display}&start={start}&sort=date";

            var parseJson = JObject.Parse(MakeJSONDocument(url));

            // var QueryResultCount = Convert.ToInt32(parseJson["display"]);
            // var TotalResultCount = Convert.ToInt32(parseJson["total"]);

            List<NaverNewsDto> news = JsonConvert.DeserializeObject<List<NaverNewsDto>>(parseJson["items"].ToString());

            NaverNewsDto dto;
            List<NaverNewsDto> nc = new List<NaverNewsDto>();

            foreach (NaverNewsDto ns in news)
            {
                dto = NaverNewsDto.Make(ns);
                if (dto == null)
                {
                    break;
                }
                nc.Add(dto);
            }
            return nc;
        }
    }
}

05. 후기

  • 원래는 CefSharp가 아니라 WebBrowser Controller를 사용할려고 했지만 뭔가 계속 스크립트 창이랑 떠서 그냥 크롬 브라우저가 갖고 올수 있는게 있나 검색하다가 찾았습니다! 처음에는 링크를 한번 누르고 또 누르면 계속 Thread 충돌이 일어나서 왠지 싶었더니 계속 생성하면 안되더라구요! 좋은 공부를 했습니다!
  • 이전 파일 복퉅이 전부라 손 될건 없었던것 같아요!
  • 무한 스크롤을 만드는게 좋을까 생각도 했는데 어떻게 할지 고민이 되더라구요! 다음 버튼을 눌러서 해야되나 싶고 추가적으로 생각해서 업데이트 하면 올리겠습니다.
profile
아직까지는 코린이!
post-custom-banner

0개의 댓글