Merge pull request '[] Blog 페이지 생성' (#8) from seonkyu.kim/Blazor:main into main

Reviewed-on: https://git.ipstein.myds.me/Study/Blazor/pulls/8
This commit is contained in:
김선규 2024-10-12 07:05:45 +00:00
commit 59aae5eb52
26 changed files with 358 additions and 14 deletions

View File

@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABoolean_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fabfadfcf168b17dd25ad62a3a2b89c89b9a3587ade1ccf7d80e4740274ef_003FBoolean_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AComponentBase_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F0691f2c61b694470b2385ca13b1109b2ddc00_003Ffb_003F9d185c09_003FComponentBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AComponentBase_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F0691f2c61b694470b2385ca13b1109b2ddc00_003Ffb_003F9d185c09_003FComponentBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATask_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F35f3a54f5acb408a3e219b2de039f1a39557b7e4515f11238cba07b60c0ce_003FTask_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATask_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F35f3a54f5acb408a3e219b2de039f1a39557b7e4515f11238cba07b60c0ce_003FTask_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F6780d13016c376c4491c5618b257d84da7eacf747ed2719783e775546b79b_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F6780d13016c376c4491c5618b257d84da7eacf747ed2719783e775546b79b_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>

View File

@ -37,5 +37,12 @@
<span class="oi oi-list-rich" aria-hidden="true"></span> Validation <span class="oi oi-list-rich" aria-hidden="true"></span> Validation
</NavLink> </NavLink>
</div> </div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="blog">
<span class="oi oi-list-rich" aria-hidden="true"></span> Blog
</NavLink>
</div>
</nav> </nav>
</div> </div>

View File

@ -0,0 +1,56 @@
@page "/blog"
@rendermode InteractiveServer
<h3>Blog</h3>
@*
Dependency Injection (DI)를 사용하는 블로그 앱
목표: Blazor의 DI 패턴을 이해하고 데이터 관리
포인트
• @inject 사용
• 서비스 라이프사이클 관리 (싱글턴, 트랜지언트, 스코프드)
설명: 블로그 포스트를 추가, 삭제, 수정할 수 있는 간단한 블로그 앱을 만들어보세요. 데이터를 관리하는 서비스 클래스를 DI로 주입해 데이터를 처리합니다.
*@
@if (BlogService.Instance.ReadPosts() == null || BlogService.Instance.ReadPosts().Count == 0)
{
<p>No posts available.</p>
}
else
{
<ul>
@foreach (var post in BlogService.Instance.ReadPosts())
{
<li>
<h4>@post.Title</h4>
<p>@post.Content</p>
<button disabled="@(isEdit)" @onclick="() => EditPost(post.Id)">Edit</button>
<button disabled="@(isEdit)" @onclick="() => RemovePost(post.Id)">Delete</button>
</li>
}
</ul>
}
<hr />
@* *@
<h3>@(isEdit ? "Edit Post" : "Add New Post")</h3>
@* *@
<div class="form-group">
<label>Title:</label>
<InputText @bind-Value="title" />
</div>
@* *@
<div class="form-group">
<label>Content:</label>
<InputTextArea @bind-Value="content" />
</div>
@* *@
<button @onclick = "SavePost">@(isEdit ? "Done" : "Add")</button>

View File

@ -0,0 +1,102 @@
using BlazorApp.PrefixCSharp;
using Microsoft.AspNetCore.Components;
namespace BlazorApp.Components.Pages.BlogPage;
public class PostItem
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
public partial class Blog : ComponentBase
{
// BlogService BS = BlogService.Instance;
// private PostItem post = new PostItem();
private string title = string.Empty;
private string content = string.Empty;
private int id = 0;
private bool isEdit = false;
public void SavePost()
{
if (isEdit)
{
Console.WriteLine(this.id);
EditPost(this.id);
}
else
{
PostItem post = new PostItem
{
Title = this.title,
Content = this.content
};
CreatePost(post);
}
title = string.Empty;
content = string.Empty;
}
public void CreatePost(PostItem post)
{
BlogService.Instance.CreatePost(post);
BlogService.Instance.ReadPosts();
}
public void RemovePost(int id)
{
BlogService.Instance.DeletePost(id);
}
public void EditPost(int id)
{
isEdit = Prefix.Toggle(isEdit);
if (isEdit)
{
PostItem post = BlogService.Instance.ReadPost(id);
title = post.Title;
content = post.Content;
this.id = post.Id;
}
else
{
BlogService.Instance.UpdatePost(BlogService.Instance.ReadPost(id),this.title, this.content);
}
}
}
public class Singleton
{
// Lazy 선언으로 사용시에만 객체를 생성하게 하면서 외부에서 접근할 수 없는 단일 인스턴스를 저장한다.
private static Lazy<Singleton> _instance = new Lazy<Singleton>(() => new Singleton());
// 외부에서 해당 인스턴스에 접근하려면 이 속성을 통해서만 접근이 가능
public static Singleton Instance => _instance.Value;
private int value = 0;
// 싱글턴이니까 외부에서 인스턴스 생성 불가하게
private Singleton()
{
}
public int DoSomething(int value)
{
Console.WriteLine("DO SINGLETON");
this.value++;
return this.value + value;
}
public void checkValue()
{
Console.WriteLine($"CHECK VALUE:{this.value}");
}
}

View File

@ -0,0 +1,14 @@
.form-group {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10px;
}
.form-group label {
width: 100px;
text-align: right;
margin-right: 10px;
padding-right: 4px;
}

View File

@ -0,0 +1,54 @@
namespace BlazorApp.Components.Pages.BlogPage;
public class BlogService
{
private static Lazy<BlogService> _instance = new Lazy<BlogService>(() => new BlogService());
public static BlogService Instance = _instance.Value;
private List<PostItem> posts = new List<PostItem>();
private BlogService() {
}
public void CreatePost(PostItem post)
{
if(posts.Count > 0)
post.Id = posts[^1].Id + 1;
else
post.Id = 1;
posts.Add(post);
}
public List<PostItem> ReadPosts()
{
foreach (var post in posts)
Console.WriteLine($"READ {post.Id} : {post.Title} = {post.Content}");
return this.posts;
}
public PostItem ReadPost(int id)
{
foreach (var post in posts)
{
if (post.Id == id)
return post;
}
return null;
}
public void UpdatePost(PostItem post,string title, string content)
{
post.Title = title;
post.Content = content;
}
public void DeletePost(int id)
{
PostItem post = ReadPost(id);
posts.Remove(post);
}
}

View File

@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Components;
namespace BlazorApp.Components.Pages.TodoPage namespace BlazorApp.Components.Pages.TodoPage
{ {
public partial class Todo : ComponentBase public partial class Todo : ComponentBase
{ {
private List<TodoItem> todos = new(); private List<TodoItem> todos = new();
@ -37,6 +38,7 @@ namespace BlazorApp.Components.Pages.TodoPage
private void ToggleTodo(TodoItem todo, int index) private void ToggleTodo(TodoItem todo, int index)
{ {
if (todo.IsDone) if (todo.IsDone)
{ {
todo.SuccessDate = string.Empty; todo.SuccessDate = string.Empty;

View File

@ -6,7 +6,6 @@
margin-bottom: 10px; margin-bottom: 10px;
} }
.form-group label { .form-group label {
width: 100px; width: 100px;
text-align: right; text-align: right;
@ -21,8 +20,10 @@
.form-actions { .form-actions {
display: flex; display: flex;
justify-content: center; flex-grow: 1;
/*justify-content: flex-end;*/
margin-top: 10px; margin-top: 10px;
width: 100%;
} }
.success-message { .success-message {

View File

@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("BlazorApp")] [assembly: System.Reflection.AssemblyCompanyAttribute("BlazorApp")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+4781a17b00b8e21414f02e947102acf72c5c6178")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+22a9ed453132bd81e3b047b70a2c7c8fecf41cf5")]
[assembly: System.Reflection.AssemblyProductAttribute("BlazorApp")] [assembly: System.Reflection.AssemblyProductAttribute("BlazorApp")]
[assembly: System.Reflection.AssemblyTitleAttribute("BlazorApp")] [assembly: System.Reflection.AssemblyTitleAttribute("BlazorApp")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@ -1 +1 @@
d6262563cb144b1b24689bcb852f66331e78b742afea34cc7526f77806d8fefb 0de9e1463f1e76d9de8902e22e93a1f278b71d472a502e3acfe4ed33a0f2e63e

View File

@ -54,6 +54,10 @@ build_metadata.AdditionalFiles.CssScope = b-fekawvbbds
build_metadata.AdditionalFiles.TargetPath = Q29tcG9uZW50cy9MYXlvdXQvTmF2TWVudS5yYXpvcg== build_metadata.AdditionalFiles.TargetPath = Q29tcG9uZW50cy9MYXlvdXQvTmF2TWVudS5yYXpvcg==
build_metadata.AdditionalFiles.CssScope = b-zswk0q6kaa build_metadata.AdditionalFiles.CssScope = b-zswk0q6kaa
[/Users/seankim/1.Program/Project(ASP)/BlazorApp/BlazorApp/Components/Pages/BlogPage/Blog.razor]
build_metadata.AdditionalFiles.TargetPath = Q29tcG9uZW50cy9QYWdlcy9CbG9nUGFnZS9CbG9nLnJhem9y
build_metadata.AdditionalFiles.CssScope = b-yzv2llfl6l
[/Users/seankim/1.Program/Project(ASP)/BlazorApp/BlazorApp/Components/Pages/TodoPage/Todo.razor] [/Users/seankim/1.Program/Project(ASP)/BlazorApp/BlazorApp/Components/Pages/TodoPage/Todo.razor]
build_metadata.AdditionalFiles.TargetPath = Q29tcG9uZW50cy9QYWdlcy9Ub2RvUGFnZS9Ub2RvLnJhem9y build_metadata.AdditionalFiles.TargetPath = Q29tcG9uZW50cy9QYWdlcy9Ub2RvUGFnZS9Ub2RvLnJhem9y
build_metadata.AdditionalFiles.CssScope = b-n64y2ur3f8 build_metadata.AdditionalFiles.CssScope = b-n64y2ur3f8

View File

@ -1 +1 @@
50edfd86d0e1da31fa31883ae82e1f3520bbce7f4aabb52fd74698655be3fe0f ef301e20d32eb1a6f2108ec68675e157089016ae7ab00873f44aef4cee05680c

View File

@ -33,3 +33,4 @@
/Users/seankim/1.Program/Project(ASP)/BlazorApp/BlazorApp/obj/Debug/net8.0/ref/BlazorApp.dll /Users/seankim/1.Program/Project(ASP)/BlazorApp/BlazorApp/obj/Debug/net8.0/ref/BlazorApp.dll
/Users/seankim/1.Program/Project(ASP)/BlazorApp/BlazorApp/obj/Debug/net8.0/scopedcss/Components/Pages/TodoPage/Todo.razor.rz.scp.css /Users/seankim/1.Program/Project(ASP)/BlazorApp/BlazorApp/obj/Debug/net8.0/scopedcss/Components/Pages/TodoPage/Todo.razor.rz.scp.css
/Users/seankim/1.Program/Project(ASP)/BlazorApp/BlazorApp/obj/Debug/net8.0/scopedcss/Components/Pages/ValidationPage/Validation.razor.rz.scp.css /Users/seankim/1.Program/Project(ASP)/BlazorApp/BlazorApp/obj/Debug/net8.0/scopedcss/Components/Pages/ValidationPage/Validation.razor.rz.scp.css
/Users/seankim/1.Program/Project(ASP)/BlazorApp/BlazorApp/obj/Debug/net8.0/scopedcss/Components/Pages/BlogPage/Blog.razor.rz.scp.css

View File

@ -0,0 +1,14 @@
.form-group[b-yzv2llfl6l] {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10px;
}
.form-group label[b-yzv2llfl6l] {
width: 100px;
text-align: right;
margin-right: 10px;
padding-right: 4px;
}

View File

@ -1,5 +1,4 @@
/* todo 용 CSS */
.list-item[b-n64y2ur3f8] { .list-item[b-n64y2ur3f8] {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -6,7 +6,6 @@
margin-bottom: 10px; margin-bottom: 10px;
} }
.form-group label[b-z8q1q1jnjv] { .form-group label[b-z8q1q1jnjv] {
width: 100px; width: 100px;
text-align: right; text-align: right;
@ -21,8 +20,10 @@
.form-actions[b-z8q1q1jnjv] { .form-actions[b-z8q1q1jnjv] {
display: flex; display: flex;
justify-content: center; flex-grow: 1;
/*justify-content: flex-end;*/
margin-top: 10px; margin-top: 10px;
width: 100%;
} }
.success-message[b-z8q1q1jnjv] { .success-message[b-z8q1q1jnjv] {

View File

@ -206,6 +206,21 @@ main[b-fekawvbbds] {
overflow-y: auto; overflow-y: auto;
} }
} }
/* _content/BlazorApp/Components/Pages/BlogPage/Blog.razor.rz.scp.css */
.form-group[b-yzv2llfl6l] {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10px;
}
.form-group label[b-yzv2llfl6l] {
width: 100px;
text-align: right;
margin-right: 10px;
padding-right: 4px;
}
/* _content/BlazorApp/Components/Pages/TodoPage/Todo.razor.rz.scp.css */ /* _content/BlazorApp/Components/Pages/TodoPage/Todo.razor.rz.scp.css */
.list-item[b-n64y2ur3f8] { .list-item[b-n64y2ur3f8] {
@ -266,7 +281,6 @@ main[b-fekawvbbds] {
margin-bottom: 10px; margin-bottom: 10px;
} }
.form-group label[b-z8q1q1jnjv] { .form-group label[b-z8q1q1jnjv] {
width: 100px; width: 100px;
text-align: right; text-align: right;
@ -281,8 +295,10 @@ main[b-fekawvbbds] {
.form-actions[b-z8q1q1jnjv] { .form-actions[b-z8q1q1jnjv] {
display: flex; display: flex;
justify-content: center; flex-grow: 1;
/*justify-content: flex-end;*/
margin-top: 10px; margin-top: 10px;
width: 100%;
} }
.success-message[b-z8q1q1jnjv] { .success-message[b-z8q1q1jnjv] {

View File

@ -206,6 +206,21 @@ main[b-fekawvbbds] {
overflow-y: auto; overflow-y: auto;
} }
} }
/* _content/BlazorApp/Components/Pages/BlogPage/Blog.razor.rz.scp.css */
.form-group[b-yzv2llfl6l] {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10px;
}
.form-group label[b-yzv2llfl6l] {
width: 100px;
text-align: right;
margin-right: 10px;
padding-right: 4px;
}
/* _content/BlazorApp/Components/Pages/TodoPage/Todo.razor.rz.scp.css */ /* _content/BlazorApp/Components/Pages/TodoPage/Todo.razor.rz.scp.css */
.list-item[b-n64y2ur3f8] { .list-item[b-n64y2ur3f8] {
@ -266,7 +281,6 @@ main[b-fekawvbbds] {
margin-bottom: 10px; margin-bottom: 10px;
} }
.form-group label[b-z8q1q1jnjv] { .form-group label[b-z8q1q1jnjv] {
width: 100px; width: 100px;
text-align: right; text-align: right;
@ -281,8 +295,10 @@ main[b-fekawvbbds] {
.form-actions[b-z8q1q1jnjv] { .form-actions[b-z8q1q1jnjv] {
display: flex; display: flex;
justify-content: center; flex-grow: 1;
/*justify-content: flex-end;*/
margin-top: 10px; margin-top: 10px;
width: 100%;
} }
.success-message[b-z8q1q1jnjv] { .success-message[b-z8q1q1jnjv] {

View File

@ -190,4 +190,8 @@ WebAssembly의 개념을 이해하고, Blazor를 사용하여 클라이언트
## **결론** ## **결론**
이미 다양한 개발 경험과 기본기를 갖추고 계신 만큼, .NET Core와 Blazor, EF6를 체계적으로 학습하신다면 빠르게 실력을 향상시킬 수 있을 것입니다. 제안드린 단계별 학습 방법을 따라가며, 실습과 프로젝트를 통해 이론을 실제로 적용해보세요. 학습 과정에서 궁금한 점이나 어려운 부분이 생기면 언제든지 질문해 주세요. 성공적인 학습과 개발 여정을 응원합니다! 화이팅하세요! 이미 다양한 개발 경험과 기본기를 갖추고 계신 만큼, .NET Core와 Blazor, EF6를 체계적으로 학습하신다면 빠르게 실력을 향상시킬 수 있을 것입니다. 제안드린 단계별 학습 방법을 따라가며, 실습과 프로젝트를 통해 이론을 실제로 적용해보세요. 학습 과정에서 궁금한 점이나 어려운 부분이 생기면 언제든지 질문해 주세요. 성공적인 학습과 개발 여정을 응원합니다! 화이팅하세요!
ex, 경로탐색 알고리즘, 충돌회피 알고리즘, 교통 트래픽 분산 알고리즘 트래픽 잼 탈출 알고리즘 등

View File

@ -23,4 +23,56 @@
- 변수나 클래스, 메서드 위에 [ ]를 사용해 속성을 추가 - 변수나 클래스, 메서드 위에 [ ]를 사용해 속성을 추가
- 코드에서 어떤 동작이나 정보를 제공하거나, 특정 조건에 따라 처리되는 방식을 지정할 수 있다. - 코드에서 어떤 동작이나 정보를 제공하거나, 특정 조건에 따라 처리되는 방식을 지정할 수 있다.
- 이미 존재하는(Required, Range, Obsolte, 등) 속성들도 있지만, 사용자가 직접 정의해서 사용할 수 있다. - 이미 존재하는(Required, Range, Obsolte, 등) 속성들도 있지만, 사용자가 직접 정의해서 사용할 수 있다.
- 2의 EditForm과 엮으면 좋음 - 2의 EditForm과 엮으면 좋음
## 4. Dependency Injection (DI)
- 의존성 관리를 쉽게 하기 위한 설계 패턴으로 객체가 필요로 하는 의존성을 직접 생성하지 않고,<br>
외부에서 주입해주는 방식으로 객체 간 결합도를 줄이고 테스트 가능성과 유연성을 높이는 역할을 한다.
- 의존성(Dependency)란?
- 간단하게 한 객체가 다른 객체의 기능, 데이터를 필요로 할 때 이를 의존성이라고 한다.
- A 클래스가 B 클래스의 기능을 사용하면 A는 B에 의존한다고 보면 된다.
- 핵심 개념
- 제어 역전(inversion of Control) : DI도 결국 이 개념의 한 형태로서<br>
전통적으로 객체는 스스로 필요한 의존성을 직접 생성하지만 DI에서는 외부에서 의존성 주입해 객체간 결합도 줄임
- 느슨한 결합 : DI는 클래스 간 결합도 낮추는 방법으로 객체가 다른 객체의 구체적 구현에 의존하지 않고 인터페이스나 추상 클래스에 의존하게 한다.<br>
그로 인해 시스템의 유지보수성과 확장성을 높인다.
## 5. 싱글턴 패턴
- 가장 유명하고 자주 쓰이면서 남발하면 절대 안되는 패턴
```csharp
public class Singleton
{
// Lazy 선언으로 사용시에만 객체를 생성하게 하면서 외부에서 접근할 수 없는 단일 인스턴스를 저장한다.
private static Lazy<Singleton> _instance = new Lazy<Singleton>(() => new Singleton());
// 외부에서 해당 인스턴스에 접근하려면 이 속성을 통해서만 접근이 가능
public static Singleton Instance => _instance.Value;
private int value = 0;
// 싱글턴이니까 외부에서 인스턴스 생성 불가하게
private Singleton()
{
// 초기화
}
public int DoSomething(int value)
{
// 그냥 아무 동작이나
Console.WriteLine("DO SINGLETON");
this.value++;
return this.value + value;
}
}
public class Base
{
private void CheckSingle()
{
Singleton ton1 = Singleton.Instance;
Singleton ton2 = Singleton.Instance;
if (ton1 == ton2)
Console.WriteLine("Same");
}
}
```