[C#/WPF]네이버 연관 검색어 프로그램 7(WPF UI : 결과)
네이버 API 및 11번가 Open API를 이용해 키워드 검색프로그램을 만들면서 개발의 방법 및 스킬을 늘리는게 목적입니다.
기본 베이스가 되는 프로그램 언어는 c#(WPF)로 진행 할 생각 입니다
1) WPF UI 프로젝트 생성
EEH.DLL,EEH.WPF.DLL 참조합니다.(이전 시간에 만들었던 공통 프로젝트를 사용하기 위해 참조)
UI 디자인을 위해 NuGet에서 ModernUI.CoreWPF 를 참조 합니다.
(1) App.xaml
<Application x:Class="EEH.WPF.UI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:EEH.WPF.UI"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.xaml" />
<ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Dark.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
ModernUI Theme 적용을 위에 App.xaml 파일에 Application.Resources 를 추가 합니다.
using FirstFloor.ModernUI.Presentation;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace EEH.WPF.UI
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
AppearanceManager.Current.ThemeSource = AppearanceManager.LightThemeSource;//ModernUI Theme 적용
}
}
}
(2) MainWindow.xaml
<mui:ModernWindow x:Class="EEH.WPF.UI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
xmlns:local="clr-namespace:EEH.WPF.UI"
mc:Ignorable="d"
ContentSource="/Keyword/Views/KeywordSearch.xaml"
Title="MainWindow" Height="800" Width="1400">
<mui:ModernWindow.MenuLinkGroups>
<mui:LinkGroup DisplayName="KEYWORD">
<mui:LinkGroup.Links>
<mui:Link DisplayName="SEARCH" Source="/Keyword/Views/KeywordSearch.xaml"></mui:Link>
<mui:Link DisplayName="API INFO" Source="/Keyword/Views/Settings.xaml"></mui:Link>
</mui:LinkGroup.Links>
</mui:LinkGroup>
</mui:ModernWindow.MenuLinkGroups>
<Grid>
</Grid>
</mui:ModernWindow>
using FirstFloor.ModernUI.Windows.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace EEH.WPF.UI
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : ModernWindow
{
public MainWindow()
{
InitializeComponent();
}
}
}
(3) KeyworkdSearch.xaml
<Page x:Class="EEH.WPF.UI.Keyword.Views.KeywordSearch"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:EEH.WPF.UI.Keyword.Views"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="KeywordSearch">
<Grid Style="{StaticResource ContentRoot}">
<Grid.RowDefinitions>
<RowDefinition Height="50px"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<StackPanel VerticalAlignment="Center" Orientation="Horizontal" >
<TextBox Text="{Binding SearchText}" Height="30" MinWidth="150" VerticalAlignment="Center" VerticalContentAlignment="Center"></TextBox>
<Button Command="{Binding OnSearchCommand}" Content="검색" Width="100" Height="30" Margin="10,0,0,0"></Button>
<Button Command="{Binding OnSearchEcommerceCommand}" Content="분석" Width="100" Height="30" Margin="10,0,0,0"></Button>
</StackPanel>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="2"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Results}" DisplayMemberPath="Keyword" SelectedItem="{Binding SelectedItem}" ></ListBox>
<GridSplitter Grid.Column="1"></GridSplitter>
<DataGrid Grid.Column="2" CanUserAddRows="False" CanUserDeleteRows="False"
IsReadOnly="True" AutoGenerateColumns="False" FrozenColumnCount="1"
ItemsSource="{Binding SelectedItem.KeywordList}">
<DataGrid.Columns>
<DataGridTextColumn Header="연관검색어" Binding="{Binding RelKeyword}" />
<DataGridTextColumn Header="경쟁정도" Binding="{Binding PlAvgDepth}" />
<DataGridTextColumn Header="월평균노출광고상태" Binding="{Binding CompIdx}" />
<DataGridTextColumn Header="월간검색수" Binding="{Binding MonthlyQcCnt,StringFormat=\{0:N0\}}" />
<DataGridTextColumn Header="월평균클릭수" Binding="{Binding MonthlyAveClkCnt}" />
<DataGridTextColumn Header="월평균클릭률" Binding="{Binding MonthlyAveCtr}" />
<DataGridTextColumn Header="네이버상품수" Binding="{Binding EcommerceNaverCnt,StringFormat=\{0:N0\}}" />
<DataGridTextColumn Header="11번가상품수" Binding="{Binding Ecommerce11stCnt,StringFormat=\{0:N0\}}" />
<DataGridTextColumn Header="인터파크상품수" Binding="{Binding EcommerceInterparkCnt,StringFormat=\{0:N0\}}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
<Border Grid.RowSpan="2" Background="Black" Opacity="0.8" Visibility="{Binding VisibilityProgressBar}">
<Grid>
<StackPanel VerticalAlignment="Center">
<ProgressBar Visibility="Visible" Margin="20" Height="20" IsIndeterminate="False" Minimum="0" Maximum="100" Value="{Binding ProgressValue}" ></ProgressBar>
<TextBlock Foreground="White" HorizontalAlignment="Center" Text="{Binding ProgressText}"></TextBlock>
<Button Content="분석종료" Width="100" Style="{StaticResource SystemButtonLink}" Command="{Binding OnStopCommand}" ></Button>
</StackPanel>
</Grid>
</Border>
</Grid>
</Page>
using EEH.WPF.UI.Keyword.ViewModels;
using System.Windows.Controls;
namespace EEH.WPF.UI.Keyword.Views
{
/// <summary>
/// KeywordSearch.xaml에 대한 상호 작용 논리
/// </summary>
public partial class KeywordSearch : Page
{
public KeywordSearch()
{
InitializeComponent();
this.DataContext = new KeywordSearchViewModel();
}
}
}
(4) KeyworkdSearchViewModel.cs
using EEH.Component.Ecommerce;
using EEH.Component.Keyword.Naver;
using EEH.Component.Keyword.Naver.Models;
using EEH.Utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace EEH.WPF.UI.Keyword.ViewModels
{
public class KeywordSearchViewModel : BaseViewModel
{
string progressText;
public string ProgressText
{
get { return progressText; }
set { progressText = value; OnPropertyCanged("ProgressText"); }
}
double progressValue;
public double ProgressValue
{
get { return progressValue; }
set { progressValue = value; OnPropertyCanged("ProgressValue"); }
}
Visibility visibilityProgressBar = Visibility.Hidden;
public Visibility VisibilityProgressBar
{
get { return visibilityProgressBar; }
set { visibilityProgressBar = value; OnPropertyCanged("VisibilityProgressBar"); }
}
string searchText;
public string SearchText
{
get { return searchText; }
set { searchText = value; OnPropertyCanged("SearchText"); }
}
ObservableCollection<KeywordsTool> results;
public ObservableCollection<KeywordsTool> Results
{
get { return results; }
set { results = value; OnPropertyCanged("Results"); }
}
KeywordsTool selectedItem;
public KeywordsTool SelectedItem
{
get { return selectedItem; }
set { selectedItem = value; OnPropertyCanged("selectedItem"); }
}
public BaseCommand OnSearchCommand { get; set; }
public BaseCommand OnSearchEcommerceCommand { get; set; }
public BaseCommand OnStopCommand { get; set; }
public BaseCommand OnSaveCommand { get; set; }
List<IOpenApi> ecommerceApis;
public KeywordSearchViewModel()
{
ecommerceApis = new List<IOpenApi>();
ecommerceApis.Add(new OpenApiNaver());
ecommerceApis.Add(new OpenApi11st());
ecommerceApis.Add(new SearchInterpark());
Results = new ObservableCollection<KeywordsTool>();
OnSearchCommand = new BaseCommand();
OnSearchCommand.ExecuteHandler = async (param) =>
{
NaverAPIClient client = new NaverAPIClient();
KeywordsTool result = await client.Search(SearchText);
Results.Add(result);
SearchText = string.Empty;
SelectedItem = result;
Save();
OnPropertyCanged("KeywordResult");
};
OnSearchEcommerceCommand = new BaseCommand();
OnSearchEcommerceCommand.ExecuteHandler = (param) =>
{
if (SelectedItem.ExNotNull() && ecommerceApis.ExNotNull())
{
VisibilityProgressBar = Visibility.Visible;
double totalCnt = 0;
double progressCnt = 0;
foreach (IOpenApi api in ecommerceApis)
{
foreach (KeywordModel keyword in SelectedItem.KeywordList)
{
totalCnt += 1;
long cnt = 0;
QueueAsync.Instance.AddTask(async () =>
{
cnt = await api.GetProductTotalCnt(keyword.RelKeyword);
}, (r, ErrorContext) =>
{
if (api.Type == EcommerceType.ETINTERPARK)
{
keyword.EcommerceInterparkCnt = cnt;
}
else if (api.Type == EcommerceType.ET11ST)
{
keyword.Ecommerce11stCnt = cnt;
}
else if (api.Type == EcommerceType.ETNAVER)
{
keyword.EcommerceNaverCnt = cnt;
}
progressCnt += 1;
ProgressValue = progressCnt * 100 / totalCnt;
ProgressText = string.Format("{0}/{1} {2}%", progressCnt, totalCnt, Math.Round(progressValue, 2));
if (totalCnt == progressCnt)
{
Save();
HideProgress();
}
});
}
}
}
};
OnStopCommand = new BaseCommand();
OnStopCommand.ExecuteHandler = (param) =>
{
QueueAsync.Instance.Stop();
Save();
HideProgress();
};
Load();
}
void HideProgress()
{
VisibilityProgressBar = Visibility.Collapsed;
ProgressValue = 0;
}
void Save()
{
string strJson = SelectedItem.ExJsonSerializeString();
strJson.ExSaveForDataFolder("KEYWORD", SelectedItem.Keyword);
}
void Load()
{
List<string> keywords = "KEYWORD".ExListForDataFolder();
foreach (string keyword in keywords)
{
string strJson = "KEYWORD".ExLoadForDataFolder(keyword);
KeywordsTool r = strJson.ExDeserializeObject<KeywordsTool>();
if (r.ExNotNull())
Results.Add(r);
}
if (Results.Count > 0)
{
SelectedItem = Results[0];
}
}
}
}
(5) Settings.xaml
<Page x:Class="EEH.WPF.UI.Keyword.Views.Settings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:EEH.WPF.UI.Keyword.Views"
xmlns:pwd="clr-namespace:EEH.WPF;assembly=EEH.WPF"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Style="{StaticResource ContentRoot}">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="30px"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer>
<StackPanel>
<TextBlock Text="네이버 광고 API INFO" Style="{StaticResource Heading2}" Margin="0,3,0,3" ></TextBlock>
<Grid Margin="0,3,0,3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Customer ID" VerticalAlignment="Center" />
<PasswordBox Grid.Column="1" VerticalAlignment="Center" pwd:PasswordHelper.Password="{Binding NaverCustomerID,Mode=TwoWay}" />
</Grid>
<Grid Margin="0,3,0,3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Api Key" VerticalAlignment="Center" />
<PasswordBox Grid.Column="1" VerticalAlignment="Center" pwd:PasswordHelper.Password="{Binding NaverApiKey,Mode=TwoWay}" />
</Grid>
<Grid Margin="0,3,0,3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Secret Key" VerticalAlignment="Center" />
<PasswordBox Grid.Column="1" VerticalAlignment="Center" pwd:PasswordHelper.Password="{Binding NaverSecretKey,Mode=TwoWay}" />
</Grid>
<TextBlock Text="네이버 OPEN API INFO" Style="{StaticResource Heading2}" Margin="0,3,0,3" ></TextBlock>
<Grid Margin="0,3,0,3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Client ID" VerticalAlignment="Center" />
<PasswordBox Grid.Column="1" VerticalAlignment="Center" pwd:PasswordHelper.Password="{Binding OpenApiNaverClientID,Mode=TwoWay}" />
</Grid>
<Grid Margin="0,3,0,3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Secret Key" VerticalAlignment="Center" />
<PasswordBox Grid.Column="1" VerticalAlignment="Center" pwd:PasswordHelper.Password="{Binding OpenApiNaverSecretKey,Mode=TwoWay}" />
</Grid>
<TextBlock Text="11번가 OPEN API INFO" Style="{StaticResource Heading2}" Margin="0,3,0,3" ></TextBlock>
<Grid Margin="0,3,0,3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Key" VerticalAlignment="Center" />
<PasswordBox Grid.Column="1" VerticalAlignment="Center" pwd:PasswordHelper.Password="{Binding OpenApi11stKey,Mode=TwoWay}" />
</Grid>
</StackPanel>
</ScrollViewer>
<Button Grid.Row="1" Style="{StaticResource BaseButtonStyle}" Command="{Binding SaveCommand}" Content="저장"></Button>
</Grid>
</Page>
using EEH.WPF.UI.Keyword.ViewModels;
using System.Windows.Controls;
namespace EEH.WPF.UI.Keyword.Views
{
/// <summary>
/// Settings.xaml에 대한 상호 작용 논리
/// </summary>
public partial class Settings : Page
{
public Settings()
{
InitializeComponent();
this.DataContext = new SettingsViewModel();
}
}
}
(6) Settings.xaml
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace EEH.WPF.UI.Keyword.ViewModels
{
public class SettingsViewModel : BaseViewModel
{
string naverCustomerID;
public string NaverCustomerID
{
get
{
return naverCustomerID;
}
set
{
naverCustomerID = value;
OnPropertyCanged("NaverCustomerID");
}
}
string naverApiKey;
public string NaverApiKey
{
get
{
return naverApiKey;
}
set
{
naverApiKey = value;
OnPropertyCanged("NaverApiKey");
}
}
string naverSecretKey;
public string NaverSecretKey
{
get
{
return naverSecretKey;
}
set
{
naverSecretKey = value;
OnPropertyCanged("NaverSecretKey");
}
}
string openApiNaverClientID;
public string OpenApiNaverClientID
{
get
{
return openApiNaverClientID;
}
set
{
openApiNaverClientID = value;
OnPropertyCanged("OpenApiNaverClientID");
}
}
string openApiNaverSecretKey;
public string OpenApiNaverSecretKey
{
get
{
return openApiNaverSecretKey;
}
set
{
openApiNaverSecretKey = value;
OnPropertyCanged("OpenApiNaverSecretKey");
}
}
string openApi11stKey;
public string OpenApi11stKey
{
get
{
return openApi11stKey;
}
set
{
openApi11stKey = value;
OnPropertyCanged("OpenApi11stKey");
}
}
public BaseCommand SaveCommand { get; set; }
public SettingsViewModel()
{
SaveCommand = new BaseCommand();
SaveCommand.ExecuteHandler = (param) =>
{
APIInfoSettings.Default.KeywordSearchNaverCustomerID = NaverCustomerID;
APIInfoSettings.Default.KeywordSearchNaverApiKey = NaverApiKey;
APIInfoSettings.Default.KeywordSearchNaverSecret = NaverSecretKey;
APIInfoSettings.Default.OpenApiNaverClientID = OpenApiNaverClientID;
APIInfoSettings.Default.OpenApiNaverSecret = OpenApiNaverSecretKey;
APIInfoSettings.Default.OpenApi11StKey = OpenApi11stKey;
APIInfoSettings.Default.Save();
};
Init();
}
public void Init()
{
NaverCustomerID = APIInfoSettings.Default.KeywordSearchNaverCustomerID;
NaverApiKey = APIInfoSettings.Default.KeywordSearchNaverApiKey;
NaverSecretKey = APIInfoSettings.Default.KeywordSearchNaverSecret;
OpenApiNaverClientID = APIInfoSettings.Default.OpenApiNaverClientID;
OpenApiNaverSecretKey = APIInfoSettings.Default.OpenApiNaverSecret;
OpenApi11stKey = APIInfoSettings.Default.OpenApi11StKey;
}
}
}
(7) 결과
EEH.WPF.UI 시작 프로젝트로 설정후 F5 클릭 하면 빌드 되면서 프로그램이 실행 됩니다.
API 정보를 입력후 저장
원하는 키워드 검색
분석 클릭하면 네이버,11번가,인터파크의 상품수 검색합니다
이상으로 키워드 검색 프로그램을 만들어 봤습니다.
코드 분석 후 기능 추가하면 개발에 큰도움이 될거 같습니다.
다음에 만들어볼 프로그램은 키움 증권의 API를 이용하여 조건식 자동 매수,매도 프로그램을 만들어 보겠습니다.
감사합니다.