01. 결과
- 이전에 코드를 거의 수정 없이 사용했구요!
- 이번에는 윈도우 폼이다 보니 이벤트나 컨트롤이 추가가 되었고 OriginalLink 또는 Link를 클릭하면 크롬 브라우저로 열어주게 할거에요!
- 한번 천천히 볼까요!
02. 패키지 설치
01.CefSharp
- CefSharp을 검색해서 WinForms를 선택해주세요!
- 만약에 나는 다르다 하면 각자에게 맞는 패키지 설치해주세요!
- 설치하고 나면 뭐 이런 저런 파일이 많이 나오는데 그건 내가 이 패키지를 활용할거면 꼼꼼히 읽어 보세요!
- 디버그 설정을 해주셔야 해요!
- 저는 이미 설정해서 x64가 보이지만 없다면 구성관리자에서 추가해주셔야 해요!
- 있으면 그냥 선택해주세요! 그리고 옆에 실행 프로젝트 확인 잘 해주시구요!
02. Newtonsoft.Json
- JSON을 사용하기 위한 패키지입니다!
- 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();
}
}
}
- 어차피 크롬 띄울거라 아무것도 추가 안했어요!
- 조건을 줘서 이전에 생성한 적이 있는지 확인 필!
- 안해주면 또 만들게 되서 충돌로 인한 에러가 발생!
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 충돌이 일어나서 왠지 싶었더니 계속 생성하면 안되더라구요! 좋은 공부를 했습니다!
- 이전 파일 복퉅이 전부라 손 될건 없었던것 같아요!
- 무한 스크롤을 만드는게 좋을까 생각도 했는데 어떻게 할지 고민이 되더라구요! 다음 버튼을 눌러서 해야되나 싶고 추가적으로 생각해서 업데이트 하면 올리겠습니다.