diff --git a/docs/blog/posts/20230612-cloudfail-1/20230612.md b/docs/blog/posts/20230612-cloudfail-1/20230612.md
index 7c61dd4..d5ad96f 100644
--- a/docs/blog/posts/20230612-cloudfail-1/20230612.md
+++ b/docs/blog/posts/20230612-cloudfail-1/20230612.md
@@ -1,5 +1,5 @@
---
-title: 무료로 클라우드 개발환경을 구성하기 위한 몸부림 (1편)
+title: '[Cloud] 무료로 클라우드 개발환경을 구성하기 위한 몸부림 (1편)'
description: 클라우드 뉴비의 다사다난했던 클라우드 환경 구축기
authors:
- bnbong
diff --git a/docs/blog/posts/20230808-android-chatapp/20230808.md b/docs/blog/posts/20230808-android-chatapp/20230808.md
index 057eb3a..6d8e122 100644
--- a/docs/blog/posts/20230808-android-chatapp/20230808.md
+++ b/docs/blog/posts/20230808-android-chatapp/20230808.md
@@ -1,5 +1,5 @@
---
-title: 나와 가장 완벽한 궁합을 가진 친구와 채팅하는 앱
+title: '[Project] 나와 가장 완벽한 궁합을 가진 친구와 채팅하는 앱'
description: Android Studio를 사용하여 개발한 친구와 채팅하는 앱
authors:
- bnbong
@@ -7,7 +7,7 @@ date:
created: 2023-08-08
updated: 2023-08-08
categories:
- - Development
+ - Project
tags:
- Android Studio
- Mobile Application
@@ -43,8 +43,6 @@ comments: true
그래서 친구의 단점이라는 주제에 대해서 보편적으로 느낄 수 있는 단점이 무엇이 있을까 생각을 해보았고,
정리해본 결과로는 다음과 같은 문제(단점)가 있을 것 같다고 생각했다.
-
-
!!! question
1. 친구와의 성향 차이 때문에 자주 다투게 된다.
@@ -57,6 +55,8 @@ comments: true
그래서 생각을 한 것이 바로 AI 였다.
+
+
## Development
ChatGPT를 써본 사람이라면 알겠지만 이녀석, 아는 것도 엄청 많고 심지어 내가 모르는 것에 대해서 물어보면 정말 친절하게 알려준다.
diff --git a/docs/blog/posts/20230814-cloudfail-2/20230814.md b/docs/blog/posts/20230814-cloudfail-2/20230814.md
new file mode 100644
index 0000000..ac83823
--- /dev/null
+++ b/docs/blog/posts/20230814-cloudfail-2/20230814.md
@@ -0,0 +1,202 @@
+---
+title: '[Cloud] 무료로 클라우드 개발환경을 구성하기 위한 몸부림 (2편)'
+description: 클라우드 뉴비의 다사다난했던 클라우드 환경 구축기
+authors:
+ - bnbong
+date:
+ created: 2023-08-14
+ updated: 2023-08-14
+categories:
+ - Cloud
+tags:
+ - CloudInfra
+ - IaaS
+ - Vultr
+ - CodeServer
+comments: true
+---
+
+저번 글 이후로 오랜만에 다시 작성하는 클라우드 관련 포스팅ㅎ
+
+이전 글 링크는 다음과 같다.
+
+[무료로 클라우드 개발환경을 구성하기 위한 몸부림 (1편)](https://bnbong.github.io/blog/2023/06/12/%EB%AC%B4%EB%A3%8C%EB%A1%9C-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD%EC%9D%84-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EB%AA%B8%EB%B6%80%EB%A6%BC-1%ED%8E%B8/)
+
+---
+
+## 클라우드 잼민이에게 가뭄의 단비같이 찾아온 Vultr
+
+![vultr](vultr.png)
+
+Vultr는 클라우드 컴퓨팅, 단독 서버 등의 가상 클라우드 서버를 주력으로 서비스하고 있는 서비스이다. 한국어는 '벌쳐'로 대부분 읽는다.
+
+![vulture](vulture.png)
+/// caption
+Somethin' on your mind?
+///
+
+벌쳐하면 자연스럽게 생각나는 그분..
+
+벌쳐는 가격 대비 성능이 상당히 뛰어난 호스팅 업체 중 하나이며, 클라우드에 대해 잘 모르는 사람도 굉장히 친숙하게 사용할 수 있다.
+
+
+
+우선 홈페이지를 들어가서 회원가입을 하면 바로 대쉬보드 페이지로 넘어가는데,
+
+![ui1](ui1.png)
+
+정말정말 직관적인 UI가 우릴 반겨준다.
+
+
+
+좌측 shortcut과 상단 탭에서 바로바로 관련 페이지로 이동할 수 있다.
+
+![ui2](ui2.png)
+
+Billing 페이지에서 현재 계정과 연결된 카드 및 계좌를 확인할 수 있고, Support 탭에서 내가 어려움을 겪거나 서비스와 관련하여 궁금한 점에 대한 도움 및 답변을 받을 수 있는 페이지가 존재한다.
+
+군생활을 하면서부터 1년 넘게 Vultr 서비스를 사용하면서 이 페이지에서 많은 도움을 받았었다.
+
+![ui3](ui3.png)
+
+공식 문서 및 블로그 또한 나름 잘 정리되어 있는 편이었다.
+
+
+
+일단 전반적으로 Vultr이라는 클라우드 서비스의 장점은 =='직관적'== 이라는 것이다.
+
+클라우드 서비스를 처음 사용해보거나 이전 서비스의 어려운 UX 혹은 부실한 레퍼런스 및 문서등으로 인해 고통을 받았던 사람들의 경우에는 Vultr 서비스는 훌륭한 대안이 될 수 있다.
+
+포스팅을 작성하고 있는 지금은 Vultr 인스턴스들을 모두 빼버리고 다시 AWS로 돌아왔지만 빠르게 간단한 인스턴스를 배포하거나 타 클라우드 서비스의 UX에 어려움이 있어 아키텍처를 손수 구축하기에는 힘들다 싶으면은 Vultr 서비스로 입문을 해보는 것도 나쁘지 않다고 생각한다.
+
+
+
+단점이라고 생각하는 것이 있다면 가격이다. 클라우드 시장에서 가장 점유율이 높은 서비스인 AWS, Azure의 경우에는 두 서비스 모두 1년 정도 사용할 수 있는 무료티어를 제공하고 있으나, Vultr의 경우에는 내가 해당 서비스를 사용하던 2021, 2022년에는 무료 티어가 없었는데
+
+2023년 3월 7일에 Vultr가 전 세계 개발자들에게 더 나은 경제적 클라우드 컴퓨팅 모델을 제공하고, 클라우드 비용을 줄이기 위한 목적으로 무료 플랜을 발표했다. 현재 무료 플랜은 일부 신청자에 한 해 얼리 액세스로 제공되고 있다. 얼리 액세스 신청은 누구든지 할 수 있으나, 가중치 점수(Weighted Score)라는 것을 종합하여 무작위 선정된다.
+
+그러나 앞서 언급했듯이 모든 사용자가 무료 플랜을 사용할 수 있는 것은 아니고 무작위 선정이라는 점, 인스턴스 OS 선택시 Ubuntu같은 대중적인 리눅스 OS를 선택하면 무료 플랜이 해제되고 데이터 센터 또한 미국, 독일 두 센터 밖에 없다. 또한 Vultr 무료 플랜의 인스턴스 사양은 다음과 같은데,
+
+!!! note ""
+ - 1v CPU
+ - 512MB RAM
+ - 10GB SSD
+ - IPv4 주소 제공
+
+
+프로젝트 배포용으로는 그다지 적합한 사양이 아니다. 테스트 용으로 굴리기에는 손색이 없지만 웹앱 서버 배포용으로 사용하기에는 램 용량이 너무 아쉽다. 그래도 대역폭은 2TB로 굉장히 넉넉한 것이 그나마 위안인 셈.
+
+따라서 결국에는 울며 겨자먹기로 유로플랜을 선택할 수 밖에 없는데
+
+![plan](plan.png)
+
+배포를 생각해서 2기가 램 딸려 있고 저장 용량, 대역폭도 적당히 배포 환경에서 굴릴만한 사양으로 선택하면 월에 14달러 정도 청구되는 서비스를 사용하게 될 수 밖에 없다.
+
+물론 그렇게 비싼 가격이 아니라고 느껴질 수 있지만 다른 서비스에서는 비슷하거나 이보다 살짝 떨어지는 사양의 인스턴스를 무료로 사용할 수 있는 반면, Vultr에서는 유로 플랜을 선택해야 사용할 수 있다는 점이 아쉬울 따름이다.
+
+
+
+그럼에도 나는 1년이 넘는 시간 동안 Vultr 서비스를 굉장히 만족하면서 사용했다.
+
+![myux](myux.png)
+
+사용 용도로는 인스턴스에 Visual Studio Code-Server를 올려 내 Workspace를 원격으로 구축하기 위함이었다.
+
+
+
+Code-Server는 웹 페이지에서 Visual Studio Code를 실행할 수 있는 서비스이다.
+
+코드 서버를 사용했던 이유는 군대라는 환경의 특성 때문이었다.
+
+군대 사이버지식정보방은 내가 사용하던 컴퓨터에서 로그아웃을 해버리면 내가 작업했던 모든 환경이 날아갔기 때문에 내 작업환경을 보존시킬 솔루션이 필요했다.
+
+그 대안으로 생각한 것이 Code-Server이였고, 언제 어디서든 접속 가능한 클라우드 환경에서 단순히 웹 페이지 URL로 접속만 한다면 모바일이든, PC환경이든 접속이 가능한 당시 내 상황에 정말 적절한 솔루션이었다.
+
+![code-server](codeserver.png)
+
+Code-Server가 올라가 있는 인스턴스에 내가 기존에 작업했던 프로젝트나 1일 1커밋을 빡세게 수행하기 위해 알고리즘 문제를 풀기 위한 Workspace를 구축해놓았고 덕분에 군대에서도 코딩 공부를 할 수 있었다.
+
+물론 편하게 웹 페이지 접속을 하기 위해 DNS 설정도 추가로 해주었다.
+
+
+그러나 3개월마다 한 번 씩 일어나는 알 수 없는 심각한 문제로 인해 1년 넘게 잘 사용하던 Vultr 서비스에 발을 때게 되었는데,
+
+
+### 1. 이상현상?
+
+현재는 해당 현상을 재현할 방법이 없으나, 겪었던 이상현상에 대해서 서술해보자면
+
+우선 짧게는 3개월, 길게는 6개월에 한번 씩 분명히 하루 전까지만 해도 잘만 들어가졌던 인스턴스가 갑자기 외부 접속이 안되는 것이었다.
+
+![vultrconsole](vultrconsole.png)
+
+ssh든, 내 코드서버 인스턴스로 향하는 그 어떠한 외부 접속이 불가한 상황이었다.
+
+그래서 vultr 서비스에서 제공해주는 내부 console을 열어서 네트워크 인터페이스들을 확인해보았다.
+
+![error](error.png)
+/// caption
+도커 네트워크를 제외한 외부 네트워크 연결을 구축하는 핵심 인터페이스가 다 날아가있는 상황
+///
+
+그때 당시 머리를 꽁꽁싸매며 고민했던 시절에 해당 현상을 캡쳐한 사진이 위 사진밖에 없다.
+
+외부에서 내부 인스턴스로 접속하는 것과 ping 명령어로 내부에서 외부 사이트에 연결을 시도하는 것도 불가능했었다.
+
+그래서 'ip route show', 'ifconfig' 명령어 등으로 네트워크 상태를 확인해보니
+
+en0번 같이 외부 연결에 핵심 연결을 구축하는 이더넷 포트들의 정보가 싸그리 날아가 있었다.
+
+프로젝트용으로 구축했던 도커 네트워크(172.17.0.X로 되어 있는)만 남아 있고 다른 네트워크 인터페이스들이 다 날아갔다.
+
+연결을 구축할 수 있는 인터페이스 자체가 날아가다 보니 문제를 해결할 수 있는 새로운 외부 패키지를 받을 수 있는 수단 조차 사용할 수 없는 상황이었다.
+
+
+
+도저히 방법이 없어 백업 이미지로 인스턴스 복구를 했던 것이 1년동안 vultr 서비스를 사용하면서 여러번 있었다.
+
+vultr가 백업용 인스턴스 이미지를 1주일 정도의 주기로 자동 생성해주었으나 그 몇일 사이에 있던 작업 내용이 모조리 날아가는 것은 뼈아픈 손실이었다.
+
+포스팅을 작성하는 현재도 이 현상이 어떤 이유로 일어나고, 무슨 현상인지 그 이름조차 알지 못한다. 혹시나 이 현상에 대해 감이 오는 분이 계시다면 공유좀 부탁드립니다ㅠㅠ
+
+
+### 2. 에이 짜증나
+
+전역을 하고 나서도 5월에 잠깐 Vultr 서비스를 사용하던 때가 있었는데 그 때도 위 현상이 생겨서
+
+도저히 짜증나서 Vultr 서비스를 사용하지 않기로 한다.
+
+위 현상에 대해서는 Vultr 공식문서에도 찾을 수 없었다.
+
+FAQ를 통해 Vultr 팀과 상담을 할 방안도 생각을 했었지만 그럴 바에야 이 기회에 다른 클라우드 서비스를 사용해보면 어떨까? 하고 생각을 하게 되었다.
+
+
+### 3. 결론
+
+서버 공부는 해야하는데 돈이 좀 덜 드는 다른 클라우드 서비스가 뭐가 있을까 찾아보던 와중,
+
+차라리 우리 집에 안쓰는 머신을 사용해보는건 어떨까? 찾아보다가 발견한 것이 동생이 코딩 학원에서 사용하다가 이제는 안쓰게된 라즈베리파이3 머신을 발견하게 되는데...(3편에서 계속)
+
+---
+
+## Vultr에 대한 클라우드 입문자 시각에서의 평가
+
+### 장점
+
+1. ^^==기본적으로 배포 환경에 굴려야하는 인스턴스가 모두 구비되어 있다.==^^ AWS나 Azure같이 메이저한 클라우드서비스와 비교하면 수가 적긴 하지만 로드밸런서, 데이터베이스 서버, 컴퓨팅 머신, VPC 네트워크, 쿠버네티스 클러스터 등 기본적으로 있을만한 머신들은 다 존재한다.
+2. ^^==편의성이 좋다==^^. 클라우드를 처음 접해보는 사람들도 쉽게 인스턴스를 생성해서 배포할 수 있게 할 수 있도록 UI/UX가 굉장히 직관적으로 제공된다. 클라우드 입문자의 경우에는 보통 공식 문서가 아니라 외부 레퍼런스를 따라해보면서 배포를 진행하겠지만 Vultr의 경우에는 공식 문서 및 블로그를 참고하면서 혼자서 만져봐도 문제 없이 배포를 진행할 수 있다. 이 점은 지금까지도 다른 서비스에서는 쉽게 찾을 수 없는 Vultr만의 강점이라고 생각한다.
+
+### 단점
+
+1. ^^==과금 요소가 다른 메이저 서비스에 비해 센편이다.==^^ AWS, Azure는 모든 사람이 무료 플랜을 접할 수 있지만 Vultr의 경우에는 그렇지 못하며 쓸만한 인스턴스를 사용하기 위해서라면 과금을 피할 수 없다. AWS는 프리티어 하나로 배포환경에서도 문제없이 사용 가능할만한 아키텍처를 구축할 수 있는 것에 비해 Vultr는 일정 수준의 과금을 해야 비슷한 아키텍처를 구축할 수 있다. 컴퓨팅 자원이 늘어날 수록 과금 부담 또한 크게 늘어난다.
+2. 인스턴스에 알 수 없는 현상으로 인해 불편함이 있을 수 있다. 이 점은 앞서 언급 했듯이, 나만 겪었던 문제일 수도 있으나 인스턴스에 대한 불안정성이 존재한다. 보안 관련된 문제인지 아니면 Vultr의 정책에 내가 몰랐던 어떠한 점이 걸렸기에 인스턴스 사용에 문제가 생긴 것인지는 알 수 없으나, 컴퓨팅 자원에 대한 안정성 및 신뢰성에 의심이 든다.
+
+
+
+결론을 이야기 하자면은 Vultr는 클라우드를 처음 접해보는 사람이 사용하기에 적합한 서비스이며, 테스팅 목적으로 양질의 인스턴스를 구축해보고 싶다면 충분히 접해볼 가치가 있는 서비스이다. 다만, 본격적인 아키텍처 구축과 프로젝트 배포용으로 사용하기에는 비용적인 측면에서 부담이 큰 편이므로 다른 서비스를 고려해야 할 것이다.
+
+
+
+내게 처음으로 클라우드라는 것에 접할 수 있도록 해준 고마운 서비스이다. 나는 지금도 내 주변에서 클라우드를 입문하고 싶지만 복잡한 인터페이스때문에 고통을 받아 주저하는 사람들에게 Vultr 서비스를 추천해주고 있다.
+
+포스팅 제목이 '무료'로 클라우드 개발환경을 구성하기 위한 몸부림이라 이미 과금이 필수인 Vultr 서비스는 에러가 아닐까 싶지만...ㅋㅋ
diff --git a/docs/blog/posts/20230814-cloudfail-2/codeserver.png b/docs/blog/posts/20230814-cloudfail-2/codeserver.png
new file mode 100644
index 0000000..d8fafd9
Binary files /dev/null and b/docs/blog/posts/20230814-cloudfail-2/codeserver.png differ
diff --git a/docs/blog/posts/20230814-cloudfail-2/error.png b/docs/blog/posts/20230814-cloudfail-2/error.png
new file mode 100644
index 0000000..1a48bee
Binary files /dev/null and b/docs/blog/posts/20230814-cloudfail-2/error.png differ
diff --git a/docs/blog/posts/20230814-cloudfail-2/myux.png b/docs/blog/posts/20230814-cloudfail-2/myux.png
new file mode 100644
index 0000000..0e403ed
Binary files /dev/null and b/docs/blog/posts/20230814-cloudfail-2/myux.png differ
diff --git a/docs/blog/posts/20230814-cloudfail-2/plan.png b/docs/blog/posts/20230814-cloudfail-2/plan.png
new file mode 100644
index 0000000..8c52a5e
Binary files /dev/null and b/docs/blog/posts/20230814-cloudfail-2/plan.png differ
diff --git a/docs/blog/posts/20230814-cloudfail-2/ui1.png b/docs/blog/posts/20230814-cloudfail-2/ui1.png
new file mode 100644
index 0000000..beca48c
Binary files /dev/null and b/docs/blog/posts/20230814-cloudfail-2/ui1.png differ
diff --git a/docs/blog/posts/20230814-cloudfail-2/ui2.png b/docs/blog/posts/20230814-cloudfail-2/ui2.png
new file mode 100644
index 0000000..0590c51
Binary files /dev/null and b/docs/blog/posts/20230814-cloudfail-2/ui2.png differ
diff --git a/docs/blog/posts/20230814-cloudfail-2/ui3.png b/docs/blog/posts/20230814-cloudfail-2/ui3.png
new file mode 100644
index 0000000..afd696a
Binary files /dev/null and b/docs/blog/posts/20230814-cloudfail-2/ui3.png differ
diff --git a/docs/blog/posts/20230814-cloudfail-2/vultr.png b/docs/blog/posts/20230814-cloudfail-2/vultr.png
new file mode 100644
index 0000000..85c4d9d
Binary files /dev/null and b/docs/blog/posts/20230814-cloudfail-2/vultr.png differ
diff --git a/docs/blog/posts/20230814-cloudfail-2/vultrconsole.png b/docs/blog/posts/20230814-cloudfail-2/vultrconsole.png
new file mode 100644
index 0000000..13e6267
Binary files /dev/null and b/docs/blog/posts/20230814-cloudfail-2/vultrconsole.png differ
diff --git a/docs/blog/posts/20230814-cloudfail-2/vulture.png b/docs/blog/posts/20230814-cloudfail-2/vulture.png
new file mode 100644
index 0000000..d181781
Binary files /dev/null and b/docs/blog/posts/20230814-cloudfail-2/vulture.png differ
diff --git a/docs/blog/posts/20230817-cspell/20230817.md b/docs/blog/posts/20230817-cspell/20230817.md
new file mode 100644
index 0000000..e287842
--- /dev/null
+++ b/docs/blog/posts/20230817-cspell/20230817.md
@@ -0,0 +1,287 @@
+---
+title: '[CSpell] 이 코드 외않되 / 이 코드 왜 안 돼 중에 뭐가 맞는 거예용?'
+description: cspell 적용 방법
+authors:
+ - bnbong
+date:
+ created: 2023-08-17
+ updated: 2023-08-17
+categories:
+ - OpenSource
+tags:
+ - cspell
+ - spellcheck
+ - open-source
+comments: true
+---
+
+## 개요
+
+친구와 채팅으로 대화를 할 때, 중요 문서를 작성을 할 때 등등 우리는 글을 쓰면서 가끔씩 내가 쓰는 단어가 제대로 된 맞춤법을 지킨건가? 싶을 때가 있다.
+
+이때 가장 좋은 확인 방법은 인터넷에 검색을 해보거나 사전에 검색을 해보거나 아니면 문법잘알 누군가에게 물어보는 것이 적절하다.
+
+그러나 이렇게 일일이 모르는 단어가 생길때마다 검색해보는 것은 여간 귀찮은 일이 아닐 수 없다.
+
+그래서 요즘 타이핑 도구들(한컴의 한글, 구글 Word 등)에서는 자동으로 맞춤법을 교정해주는 기능이 탑재되어 있고 문서 도구들이 나날이 발전해나가면서 그 기능 또한 점점 좋아지고 있다.
+
+프로그래밍 또한 수많은 타이핑이 들어가는 작업이다. 코딩을 할 때에도 변수 이름을 정하거나, 이 코드를 읽는 누군가가 내가 쓴 코드를 더 잘 이해할 수 있게 주석을 달아주는 등 기계가 아닌 사람이 읽을 수 있는 글을 쓰는 때가 많다. 프로젝트 규모가 커질수록 human-readable을 위한 글을 쓰는 시간과 그 규모가 덩달아 커진다.
+
+그러나 코딩을 하는 사람들은 개발자이지 국립국어원이나 국제 언어 문법 관련 직종에 종사하는 사람들이 아니기 때문에 종종 문법 관련 실수를 저지르곤 한다.
+
+그런데 그 많은 단어들의 오탈자를 하나하나 확인하는 것은 모래사장에 바늘찾기만큼 어려운 작업이다. 때문에 똑똑한 형님들이 다른 문서 작업 툴에 있는 문법 교정 도구처럼 코드에서도 문법 관련 오류를 찾아 report해주는 라이브러리를 만들었다.
+
+바로 이번 포스팅에서 소개할 'CSpell' 이라는 라이브러리이다.
+
+
+
+---
+
+## Installation
+
+!!! question "CSpell이란"
+ Q. CSpell은 무엇 인가요?
+
+ A. CSpell은 철자 검사 코드를 위한 명령줄 도구 및 라이브러리입니다.
+
+CSpell 공식 홈페이지는 다음과 같다.
+
+
+
+앞서 언급 했듯이, CSpell은 라이브러리에 해당되는 도구이다.
+
+공식 문서에서 언급하는 CSpell의 기능은 다음과 같다.
+
+>1. 맞춤법 검사 코드 – 사전을 확인하기 전에 단어로 구문 분석하여 맞춤법 검사 코드를 입력할 수 있습니다.
+>2. CamelCase, snake_case 및 복합어 명명 스타일을 지원합니다.
+>3. 자체 저장 – Hunspell이나 aspell과 같은 OS 라이브러리에 의존하지 않습니다. 온라인 서비스에도 의존하지 않습니다.
+>4. 빠른 속도 – 1000개의 코드 라인을 몇 초 안에 확인합니다.
+>5. 프로그래밍 언어별 사전 – 다음을 위한 전용 지원: 자바스크립트, TypeScript, Python, PHP, C#, C++, LaTex, Go, HTML, CSS 등.
+>6. 사용자 정의 가능 – 사용자 정의 사전 및 단어 목록을 지원합니다.
+>7. 지속적인 통합 지원 – Travis-CI에 대한 설명으로 쉽게 추가할 수 있습니다.
+
+다중 언어를 지원하는 라이브러리이기 때문에 프로젝트 제약이 거의 없는 편이다. 머신에 node가 깔려 있으면 CSpell을 설치할 수 있다.
+
+다음 명령어로 Node 설치 확인 및 CSpell 라이브러리를 설치할 수 있다.
+
+### 설치되어 있는 Node Version 확인
+
+```bash
+$ Node -v
+```
+
+### CSpell 설치
+
+```bash
+$ npm install -g cspell
+```
+
+만약 Node 설치가 되어 있지 않다면 (Node -v 명령어를 입력했을 때 Node 명령어를 찾을 수 없다는 등의 오류가 났다면) 머신에 Node를 설치해야한다.
+
+다음 명령어로 Node를 설치할 수 있다.
+
+```bash
+# Windows 환경
+#https://nodejs.org/ko/download 사이트에서 직접 윈도우 인스톨러를 받거나,
+#Powershell 등에 Chocolatey 패키지 관리자가 설치되어 있다면:
+$ choco install nodejs
+
+
+# MacOS 환경 (homebrew)
+$ brew update
+$ brew install node
+
+# MacOS & Linux 환경 (NVM)
+$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
+$ nvm install node
+
+
+# Linux 환경 (Debian / Ubuntu 계열)
+$ sudo apt-get update
+$ sudo apt-get install nodejs npm
+
+# Linux 환경 (CentOS / RHEL 계열)
+$ sudo yum install nodejs npm
+
+# Linux 환경 (Fedora 계열)
+$ sudo dnf install nodejs npm
+
+
+# 정상 설치 확인 방법
+$ node -v 혹은 npm -v // 9.6.7 혹은 v20.3.1 같은 출력이 나오면 성공적으로 설치된 것이다.
+```
+
+CSpell 설치를 완료하면 다음 명령어를 통해 코드 구문 오류를 확인할 수 있다.
+
+```bash
+$ cspell "소스코드 경로"
+# 또는
+$ cspell lint "소스코드 경로"
+
+# 전체 파일 문법 검사
+$ cspell "**"
+```
+
+---
+
+## Configuration
+
+특정 프로젝트의 경우, spelling checking을 해야하는 파일이 있고 그렇지 않은 파일이 있을 수 있다.
+
+그럴때마다 매번 특정 파일의 이름을 하나하나 쳐가면서 체크를 하면 굉장히 비효율적이고 또 팀 프로젝트나 누구나 기여를 할 수 있는 오픈소스 프로젝트의 경우에는 협업 능력이 매우 저하될 수 있다.
+
+또는 문법 상 틀린 단어라도 팀 변수 네이밍 규칙에 따라 그대로 사용하는 문법도 있을 수도 있다.
+
+그럴때 사용하는 것이 바로 CSpell Configuration이다.
+
+우선 'cspell'이라는 제목의 파일을, 확장자는 하단 목록을 참고하여 생성한다.
+
+다음은 CSpell 공식 문서에서 제공하는 CSpell이 읽을 수 있는 configuration 파일명 목록이다.
+!!! note ""
+ .cspell.json
+ cspell.json
+ .cSpell.json
+ cSpell.json
+ cspell.config.js
+ cspell.config.cjs
+ cspell.config.json
+ cspell.config.yaml
+ cspell.config.yml
+ cspell.yaml
+ cspell.yml
+ package.json
+
+위의 파일 명 중 하나를 선택하여 프로젝트 내부에 만들고 해당 파일 내부에 다음과 같은 형식으로 configuration을 설정할 수 있다.
+
+```json
+// in CSpell configuration file, ex) cspell.json
+// CSpell Settings
+{
+ // Version of the setting file. Always 0.2
+ "version": "0.2",
+ // language - current active spelling language
+ "language": "en",
+ // words - list of words to be always considered correct
+ "words": [
+ "mkdirp",
+ "tsmerge",
+ "githubusercontent",
+ "streetsidesoftware",
+ "vsmarketplacebadge",
+ "visualstudio"
+ ],
+ // flagWords - list of words to be always considered incorrect
+ // This is useful for offensive words and common spelling errors.
+ // For example "hte" should be "the"
+ "flagWords": [
+ "hte"
+ ],
+ // ignorePaths - this folder or files will be ignored when checking typos
+ "ignorePaths": ["node_modules/**"]
+}
+```
+
+공식 문서에서 더 자세한 Configuration 설정 방법을 확인할 수 있다.
+
+
+
+---
+
+## Example Of Use
+
+나는 CSpell이라는 라이브러리를 오픈소스 컨트리뷰션 아카데미에서 처음 접했고,
+아카데미 멘토, 멘티분들과 차근차근 가이드라인을 따라 사용해보면서 Azure SDK for python 공식 Repository에 실제로 기여를 해보았다.
+
+![contribution](contribution.png)
+
+Azure SDK for python Repository의 경우, 굉장히 거대한 규모의 오픈소스 프로젝트이다.
+
+프로젝트를 로컬 작업 환경에 받아오는 것부터 상당히 오래걸리고 터미널도 다른 프로젝트에 비해 켜지는 시간이 긴편이다.
+
+때문에 sdk 내부 기능별로 정말 다양한 분량의 코드와 주석이 적혀있다. 그러나 세계적인 기업인 Microsoft의 개발자 분들이더라도 결국에는 사람인지라 타이핑을 하다가 오타를 하는 등의 실수가 있기 마련이다.
+
+이를 Microsoft에서도 인지를 하고 있는지 해당 Repository에 CSpell을 사용해서 오탈자 해결을 위한 이슈를 상단의 사진처럼 각 SDK 기능 별로 여러개 열어놓았다.
+
+Open되어 있는 CSpell 관련 Issue에는 중간에 What to do 탭을 통해 어떻게 CSpell checking을 위한 설정을 하고 typo checking을 하는지 소개하고 있다.
+
+내가 원하는 SDK 기능의 CSpell 관련 issue를 직접 찾아가서 저 이슈 탭을 보면서 작업을 해도 되지만 Azure SDK Korean 공식 github.io 문서에 CSpell 관련 Contribution을 하는 방법이 정의되어 있다.
+
+
+
+Issue의 What to do 탭을 기계번역한지라 읽을 때 어색함이 느껴질 수 있지만 큰 어려움 없이 가이드라인을 따라할 수 있다.
+
+윈도우 사용자라면 PowerShell이 기본으로 설치되어 있을 것이기 때문에 바로 가이드라인을 따라해도 좋지만,
+
+자신이 MacOS사용자라면 머신에 따로 PowerShell을 깔아주어야한다.
+
+Azure SDK for python프로젝트의 CSpell checking 스크립트는 PowerShell script로 짜여 있기 때문이다.
+
+
+
+상단의 가이드라인대로 MacOS에 PowerShell을 설치했다면
+
+```bash
+$ pwsh
+```
+
+터미널에 상단의 명령어를 입력하여 PowerShell을 열 수 있다.
+
+
+
+기본 설정을 마친 나는 PowerShell을 열고 Azure SDK Korean github.io 가이드라인을 쭉 따라하면서 내가 원하는 SDK 소스코드에 typo checking을 수행했다.
+
+내가 체크했던 SDK 기능은 Azure에서 제공하는 ==Azure Storage blob== 기능의 소스코드였다.
+
+!!! question "Azure Storage Blob(Blob Storage)는 뭔가요?"
+ Q. Azure Storage Blob(Blob Storage)는 뭔가요?
+
+ A. Azure Blob Storage는 클라우드를 위한 Microsoft의 개체 스토리지 솔루션입니다. Blob Storage는 대량의 비정형 데이터를 저장하는 데 최적화되어 있습니다. 비정형 데이터는 텍스트 또는 이진 데이터와 같은 특정 데이터 모델이나 정의를 따르지 않는 데이터입니다.
+ 출처: [Microsoft Learn 공식 문서](https://learn.microsoft.com/ko-kr/azure/storage/blobs/storage-blobs-overview)
+
+Azure Storage Blob은 요약하자면 AWS의 S3의 Azure 버전이라고 생각하면 쉽다. 러닝 서버에 저장하기에는 부담이 되는 크기를 가진 미디어파일, 바이너리 파일 등을 보관하기 위한 스토리지 서버이다.
+
+
+
+![practice1](practice1.png)
+
+상단 사진의 vscode/cspell.json 파일의 84번째 줄 =="sdk/storage/azure-storage-blob/**"== 이 부분을 지우고 typo 체크를 진행했다.
+
+typo checking 결과를 이 포스팅에도 적어놓고 싶었으나 길이가 너무너무 길어서 패스...
+
+typo checking 결과는 다음 형식으로 출력된다.
+
+```bash
+...
+./sdk/storage/azure-storage-blob/samples/blob_samples_hello_world.py:173:70 - Unknown word (myappendcontainersync)
+./sdk/storage/azure-storage-blob/samples/blob_samples_hello_world.py:180:61 - Unknown word (myappendblob)
+./sdk/storage/azure-storage-blob/samples/blob_samples_proxy_configuration.py:50:61 - Unknown word (configuation)
+...
+```
+
+어찌됐든 typo checking을 해보니 Azure SDK Storage Blob 소스코드에 오타가 있는 것을 확인했다.
+
+포스팅을 작성하는 지금은 이미 내가 오타수정을 마쳐 PR을 달아놓은 상태이므로 git diff 명령어를 통해 오타가 존재하던 상태와 현재 상태를 비교했다.
+
+![practice2](practice2.png)
+
+상단의 사진에서 볼 수 있듯이, 대부분의 typo는 주석부분에 작성되어 있었으며, transferred로 적혀 있어야하는 단어가 transfered로 적혀있는 등 주석에 적혀있는 사소한 오타들을 발견했다.
+
+fork한 내 repository에 오타 수정을 하기 위한 branch를 새로파서 오타 수정을 진행했고 Azure SDK for python repository에 Pull Request를 보냈다.
+
+
+
+![contributed](contributed.png)
+
+총 4개의 단어에 오탈자가 있는 것을 확인하여 수정했고 PR을 보냈으며, 아직 Microsoft 관계자 분들이 리뷰를 진행하지 않은 상태이기에 Merge를 기다리고 있다.
+
+## Conclusion
+
+내가 소속되어 있던 OSSCA의 Azure SDK팀에서 실제 Microsoft에서 근무를 하고 계시는 멘토님께 CSpell 관련 기여를 여쭤보니,
+
+CSpell 관련 이슈는 웬만하면 주석의 오탈자를 수정하는 컨트리뷰션이 거의 대부분이기에 Merge가 빠르게 되는 편이라고 하셨다.
+
+한 5일 정도 기다리면 관계자가 코드리뷰를 진행할 것이라고 말씀하셨다 ㅎㅎ
+
+
+
+이번 기회에 새로운 유용한 라이브러리를 알게되어 기쁘다. 앞으로 내가, 혹은 다른 누군가와 진행할 프로젝트의 질을 좀 더 높일 수 있는 무기를 또 하나 얻게 되었다.
diff --git a/docs/blog/posts/20230817-cspell/contributed.png b/docs/blog/posts/20230817-cspell/contributed.png
new file mode 100644
index 0000000..3bfab9f
Binary files /dev/null and b/docs/blog/posts/20230817-cspell/contributed.png differ
diff --git a/docs/blog/posts/20230817-cspell/contribution.png b/docs/blog/posts/20230817-cspell/contribution.png
new file mode 100644
index 0000000..d141086
Binary files /dev/null and b/docs/blog/posts/20230817-cspell/contribution.png differ
diff --git a/docs/blog/posts/20230817-cspell/practice1.png b/docs/blog/posts/20230817-cspell/practice1.png
new file mode 100644
index 0000000..5736023
Binary files /dev/null and b/docs/blog/posts/20230817-cspell/practice1.png differ
diff --git a/docs/blog/posts/20230817-cspell/practice2.png b/docs/blog/posts/20230817-cspell/practice2.png
new file mode 100644
index 0000000..b4ea995
Binary files /dev/null and b/docs/blog/posts/20230817-cspell/practice2.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/20230921.md b/docs/blog/posts/20230921-project-pinned/20230921.md
new file mode 100644
index 0000000..c6adff8
--- /dev/null
+++ b/docs/blog/posts/20230921-project-pinned/20230921.md
@@ -0,0 +1,398 @@
+---
+title: '[Project] Project-Pinned 프로젝트 회고'
+description: Project-Pinned 프로젝트를 수행하면서 겪었던 문제들과 해결 방법에 대해 서술합니다.
+authors:
+ - bnbong
+date:
+ created: 2023-09-21
+ updated: 2023-09-21
+categories:
+ - Project
+tags:
+ - CICD
+ - PostgreSQL
+ - Docker
+ - Django
+ - JIRA
+ - Github
+ - Development
+ - PM
+ - Backend
+ - Frontend
+ - Github Actions
+comments: true
+---
+
+공모전 출품을 위해 준비했던 웹 SNS 프로젝트.
+
+프로젝트 팀장을 맡았고, 프로젝트 설계 및 개발 방법론 디자인, API document를 비롯한 문서화, 주요 Backend 기능 개발, CI/CD 아키텍처 구축을 맡았다.
+
+!!! quote "Contribution info"
+ 개발 기간: 2023/06/13 ~ 2023/09/06
+ 담당 역할: 팀장, Project Managing, Backend 개발, 문서화, CI/CD 구축, 아키텍처 설계 및 인프라 구축. 디자인 초안 작성
+ 프로젝트 주제: 지역 랜드마크와 관련된 내 추억을 공유하는 지도 기반 웹 SNS 서비스
+ 출품한 공모전: 제 11회 문화데이터 활용 경진대회, 2023년 공개SW 개발자대회
+ 팀 규모: 4명
+
+모든 소스는 Open Source로 공개를 했고, 다음 링크에서 확인할 수 있다.
+
+
+
+---
+
+## 프로젝트 개요
+
+한창 공모전을 알아보던 5월, 단순히 아이디어톤 같은 것도 괜찮았지만 뭔가 개발 프로젝트를 해보고 싶은 마음에 여기저기 찾아다니다가
+
+다음 공모전을 발견했다.
+
+
+
+문화 빅데이터를 활용해서 서비스를 기획하거나 개발, 혹은 데이터 분석을 해서 출전을 하는 공모전이었다.
+
+이 3개의 분야 중 제품*서비스 개발 분야에 출전하기로 결정했고, 바로 프로젝트에 대한 초안을 그려봤다.
+
+자주 가는 카페에서 다음과 같은 표정으로 30분동안 앉아서 고민을 했고, 패드로 간단하게 UIUX 다이어그램도 그려봤다.
+
+![thinking](thinking.png)
+/// caption
+음... 뭐 만들지
+///
+
+그렇게 고민을 끝내고 프로젝트 초안을 다음과 같이 완성했다.
+
+![idea](idea.png)
+/// caption
+프로젝트 브레인스토밍 초안 및 UI/UX 다이어그램 일부
+///
+
+피그마 UI를 다룰줄 알고 디자인에 대한 기본 지식이 있었다면 이렇게 비효율적으로 손으로 삐뚤삐뚤하게 그린 다이어그램을 사용하지 않았을 거지만 내겐 그런 능력이 없었기에 이정도로 만족했다..
+
+다만 이번 프로젝트를 끝내고 나서 피그마는 꼭 배워놔야겠다고 생각했다. 추후 얘기를 다시 한번 하겠지만 팀에 디자이너가 없었기에 깔끔한 UI를 뽑기가 너무 어려웠다. 프로젝트를 진행하면서 차라리 내가 이걸 할 줄 알았다면 결과물이 엄청 달랐을텐데... 하는 아쉬움이 너무 컸다.
+
+여튼 아이디어 초고를 간단하게 정리하고 나니 이 프로젝트는 혼자서 모든 것을 개발하기에는 많이 어려울 것 같아서 같이 공모전을 나갈 팀원을 구했다. 기존에 아이디어톤 같은 공모전을 나간 경험이 있는 동기들과 팀을 꾸렸고, 팀장인 내가 팀원들의 개발 분야를 나눴다.
+
+총 4명으로 구성된 팀에서 나를 포함한 2명은 Backend 개발을, 나머지 2명은 디자인 및 Frontend 개발을 맡았고, 추가로 나는 프로젝트 스프린트 구성, 아키텍처 설계 및 구현, 문서화를 맡았다.
+
+개발 Stack은 다음과 같다.
+
+!!! quote "Stack"
+ Framework: Backend - Django, Python / Frontend - Next.js, tailwind.css
+ DB: PostgreSQL
+ Cache DB: Redis
+ Notification Server: Firebase
+ Proxy: Nginx
+ Container: Docker
+ CI/CD: Github Action
+ Infrastructure: AWS
+
+
+
+---
+
+## 문서화
+
+나는 Notion, JIRA Confluence 등 여러 해외 문서화 툴을 사용해본 경험이 있었다. 해외 문서화 툴은 모두 Markdown을 지원해서 자연스럽게 나도 Markdown 문법으로 문서화를 하는 것에 익숙해졌다.
+
+그 중 요즘 다른 오픈소스도 잘 사용하지 않는 Github의 제공 기능 중 하나인 Github wiki 페이지에 눈이 갔다.
+
+이전 협업 프로젝트도 Github Wiki 페이지를 잠깐 이용해본적이 있었는데 당연히 Markdown은 완벽 호환이 되면서 버전 관리 툴과 문서화 툴을 합칠 수 있다는 큰 장점 덕분에 이번 프로젝트도 Github Wiki 페이지를 활용하여 문서를 정리했다.
+
+![apidocs](apidocs.png)
+/// caption
+API Document 초안
+///
+
+가장 먼저 작성한 문서는 API endpoint에 대한 문서였다.
+
+백엔드와 프론트엔드는 API를 통해 통신하기 때문에 Frontend와의 협업을 위해 정형화된 API는 매우 중요하다.
+
+API endpoint 문서가 세밀할수록 위험부담을 크게 줄일 수 있다. 그 중요성을 알고 있기에 프로젝트 초반 문서화 작업을 한창 할 때 가장 신경써서 문서화를 진행했다.
+
+프로토타입도 개발이 안된 상태 임에도 불구하고 이 문서를 바탕으로 Backend, Frontend 모두 각자 기능개발을 원활하게 진행할 수 있었다.
+
+API document가 완성된 시점에서는 협업을 위한 가이드라인을 작성했다. 두 번째로 가장 공들인 문서이다.
+
+![contributedocs](contributedocs.png)
+/// caption
+Contribution Guide 문서
+///
+
+해당 문서에서는 git branching 전략, version tag를 붙이는 전략 및 JIRA Issue와 연동하기 위해 commit message 양식을 통일시키는 전략등을 기재했고, 프로젝트를 진행하면서 Frontend, Backend가 서로 test를 돌리기 위해 참고해야할 설정 및 빌드 명령어 등을 기재했다.
+
+협업에 필요한 가장 중요한 문서는 아마 이 문서가 아닐까 싶다. 나 혼자 개발하는 프로젝트가 아니기 때문에 Code Conflict를 일으키지 않고 개발을 진행하기 위해 필요한 모든 정보를 이 문서에 자세하게 풀어내려고 노력을 했다. 예를 들어, 프로젝트 Backend code의 경우에는 formatting을 하기 위해 Python의 black 이라는 라이브러리를 사용했는데 black formatter로 code format을 통일시켜 커밋하는 방법을 이 문서에 정의했다.
+
+Project-Pinned의 모든 문서는 다음 링크에서 확인할 수 있다.
+
+
+---
+
+## 협업을 위한 도구 사용
+
+개발 프로세스를 관리하기 위해 이슈관리 및 스프린트를 관리하는 외부 툴의 사용이 거의 불가피했다. Github도 꾸준히 Github 사이트 내에서 모든 개발 과정을 매니징 할 수 있게 여러 기능을 도입해나가고 있지만 그래도 JIRA, Jenkins같은 기존부터 꾸준히 강력한 기능과 편의성을 제공하는 third party tools의 점유율을 쉽게 뺏기는 어려운 실정이다.
+
+학부 팀프로젝트에서 JIRA로 프로젝트 매니징을 경험해본적이 있는 나는 이번 프로젝트에 JIRA를 도입하여 스프린트 매니징을 진행했다.
+
+JIRA가 제공하는 기본 Template 중 Agile Template를 활용해서 프로젝트탭을 만들었다.
+
+![jira](jira.png)
+/// caption
+JIRA backboard
+///
+
+스프린트를 구성하기 위해 개발해야하는 가장 큰 범주를 Epic Issue로 구분하여 설정했고, 각 Epic Issue의 개발 기한을 설정했다. Epic Issue의 life time이 설정되면 사진과 같이 바 그래프 형태로 마감 기한을 볼 수 있다.
+
+Epic Issue를 설정하고 나서 각 Epic Issue별로 개발해야하는 세부 기능들을 하위 Issue로 생성했다.
+
+![issues](issues.png)
+
+Epic Issue도 포함하여 하위 Issue들도 생성되는 순간부터 'PP-X'로 시작하는 Issue Tag가 붙는다. JIRA page와 Github가 연동이 되어 있다면 Git commit message에 해당 JIRA Issue Tag를 붙여서 커밋하면 JIRA Issue page에서 현재 이슈와 관련있는 commit, branch를 한눈에 볼 수 있다.
+
+![timelines](timelines.png)
+/// caption
+JIRA이슈 카테고리에 속하는 모든 contributions
+///
+
+JIRA 덕분에 프로젝트 매니징을 편하게 할 수 있었다.
+
+스프린트 계획까지 모두 마친 후 6월 중순부터 개발에 들어갔다.
+
+---
+
+## Backend 개발
+
+본격적으로 프로젝트를 개발하기 시작하기 전에 Backend Stack을 고민했다.
+
+일단 나는 Python이 굉장히 익숙했기 때문에 Python Framework로 Backend를 구축하고자 했다.
+
+다만, 언어를 고르면서 Python의 single thread가 마음에 걸렸다. 기획한 프로젝트는 다수의 사용자가 동시 접속, Data 실시간 반영이 적용되어야하는 대규모 사용자를 고려한 프로젝트였기 때문에 특정 시점에 하나의 python code만 실행하게 되어 있는 Global Interpreter Lock(GIL)이 병목현상으로 작용할까 걱정이 되었다.
+
+그러나 요즘 나오는 Python Web framework들은 이런 파이썬의 단점을 해소하기 위해 WSGI (Web Server Gateway Interface)를 도입하여 multi-threaded 또는 multi-process 방식으로 웹앱 서버를 띄울 수 있게 나온다. WSGI 덕분에 single thread에서만 작동하는 Python 코드도 실제 프로덕션 환경에서는 thread 병목 현상을 크게 신경쓸 필요 없이 Backend를 구축할 수 있는 것이다.
+
+또한 Python threading 라이브러리같이 multi-thread를 구현할 수 있는 라이브러리가 존재하기 때문에 문제 없이 Python 언어를 선택했다.
+
+언어 선택을 하고 나서 다음으로 Framework를 고민했다.
+
+Flask, FastAPI, Django를 모두 사용해본적이 있는 나는 어떤 프레임워크를 고르든 괜찮았다. 그러나 이 프로젝트에서 나는 Django를 선택했다.
+
+
+
+Django를 선택한 이유는 다음과 같다.
+
+>1. 방대한 양의 레퍼런스와 세밀하고 정리가 매우 잘되어 있는 공식 문서
+>2. 강력한 기본제공 기능: 인증, URL 라우팅, 관리자 인터페이스, ORM, 템플릿 엔진, Admin 페이지
+>3. Python-base라는 엄청난 접근성 및 방대한 커뮤니티
+>4. 확장성
+
+Django는 현재 우리 팀의 상황을 모두 적절하게 충족시킬 수 있었다. 나와 같이 Backend를 담당한 팀원은 Backend 개발이 처음이었기 때문에 쉽게 개발에 접근이 가능하면서도 learning curve가 매우 완만한 Backend Framework가 필요했고, 또 해당 Framework를 학습할만한 레퍼런스의 양과 질이 모두 좋아야 했다.
+
+2005년 부터 꾸준히 업데이트가 이루어지고 있는 Django는 약 20년 가까이 되어 가는 User-Reference와 꾸준히 업데이트 되면서도 초보자도 읽기 쉽게 제공되는 좋은 공식 문서가 존재한다. 때문에 Backend 개발을 처음 해보는 팀원이 어렵지 않게 Django에 뛰어들 수 있었다.
+
+또한 Django는 기본적으로 제공해주는 기능들도 매우 많았다. 개발 기한이 널널한 편이 아니었던 우리 프로젝트는 빠르게 MVP를 만들고 꾸준히 테스팅을 통해 성능 검증 및 개선 작업을 했어야 했다. 따라서 MVP 개발에 소요되는 시간이 길면 길수록 프로젝트가 쉽게 늘어질 수 있었다. 그래서 Django를 선택했다.
+
+이 점에 대해서는 FastAPI도 선택지에 포함이 될 수 있었다. 그러나 FastAPI를 체택하게 된다면 ORM, Admin page 등을 모두 일일이 개발을 해야했다. 거의 Go언어로 개발된 서버만큼이나 빠르고 최적화된 성능을 자랑하는 FastAPI이지만 Backend를 처음 접하는 사람에게는 learning curve가 가파른 편이었다. 초보자가 참고할만한 레퍼런스도 많지 않은 편이었다.
+
+Backend 개발에 필요한 거의 모든 기능을 기본적으로 제공해주는 Django의 강력함 덕분에 개발 기한 내로 원하는 요구사항을 모두 충족시킬 수 있는 Backend를 구축할 수 있었다. Django를 선택한 것은 정말 잘한 선택이었다.
+
+개발 후반에는 API document를 실제 개발되어 있는 API endpoint의 양식에 맞게 자동화를 시키기 위해 swagger library를 도입했다. 이런 소소한 확장도 가능하다는 것은 Python-base Framework의 강력한 장점이었다.
+
+![dirtree](dirtree.png)
+/// caption
+최종 Backend Directory Tree
+///
+
+Django는 오로지 Backend 구축을 위해 체택한 Framework라서 정적 파일들을 제공할 필요가 없었다고 생각했다. 정적 파일 Serving은 Nginx를 통해서 제공되기 때문이다. 그러나, Admin 페이지는 Django가 auto-create해주는 static 파일들이 다수 존재했기 때문에 Django가 제공하는 static 파일들을 모두 collect해서 'static/' 이라는 폴더에 따로 저장했고, Nginx가 이 static 파일들을 참조하게 했다.
+
+![stacks](stacks.png)
+/// caption
+주요 컨테이너
+///
+
+인스턴스를 구성하는 핵심 기능들은 모두 Docker를 활용하여 Container화 시켰다.
+
+인스턴스의 Forwarding을 담당하는 Nginx 컨테이너가 외부로의 모든 접속 및 입출력을 담당하고 Frontend, Backend Container에게 Proxy를 쏴서 컨테이너 간 통신을 이루도록 구성했다.
+
+HTTPs certification을 구축하기 위해 certbot을 도입해서 nginx 내부 컨테이너에 인증서와 인증 관련된 로직을 때려 박으려고 했으나 여러 시행착오를 겪었고 답이 없다는 결론을 내서 그냥 EC2 instance 내부에 인증서를 직접 발급하여 저장했다. 이 부분은 https 인증을 자동화 할 수 있는 다른 좋은 방법이 있을 것 같은데 내 능력 부족으로 인해 차후 학습을 통해 개선 방법을 찾고자 한다.
+
+서비스를 모두 컨테이너화를 시킨 덕분에 실제 Deployment로 올라갈 환경과 거의 유사한 환경에서 testing을 진행할 수 있었고 어려움 없이 바로 deploy가 가능했다.
+
+컨테이너 이미지는 모두 DockerHub에 업로드 하여, CI/CD 자동화를 구축하는 과정에서 observer가 자동으로 DockerHub 이미지를 참조해서 컨테이너를 빌드하도록 설정했다.
+
+데이터베이스로는 유저 데이터, 빅데이터를 가공한 데이터 등 무결성이 중요한 데이터를 저장하기 위해 RDBMS를 체택했고 PostgreSQL을 체택했다.
+
+
+
+MySQL도 체택할 수 있었지만 그렇지 않았던 이유는 우리 서비스가 위치 및 지리정보를 포함한 빅데이터를 가공하여 사용하기 때문에 PostGIS와의 연동성을 고려한 선택이었다. 현재 버전에서는 PostGIS기능까지 요구하여 지리 탐색을 요하는 알고리즘이 포함되어 있지는 않다. 빅데이터에서 이미 지역 랜드마크에 대한 위치 정보를 모두 제공해주고 있기 때문에 빅데이터 정보만으로도 위치 정보에 대한 문제를 해결할 수 있었지만 추후 새로운 랜드마크가 생겨 업데이트를 해야하는 경우를 고려한 것이다.
+
+유저 피드같이 실시간으로 데이터 수정이 이루어지고 caching이 요구되는 데이터는 NoSQL을 체택하여 데이터를 저장하기로 했고, 직관적으로 Key-Value로 값의 입출력을 할 수 있는 Redis를 체택했다.
+
+
+
+---
+
+## CI/CD 아키텍처 구축
+
+개발에는 큰 어려움이 없었다. 이 것을 배포하는 과정이 조금 난관이 있었다.
+
+지금은 인프라로 AWS를 사용하고 있지만 처음에는 Azure를 사용했다. 그 당시 작성했던 아키텍처 초안이 조금 날아가 있는 상태지만 초반 아키텍처는 다음과 같이 설계했었다.
+
+>1. Docker images registry - Azure Container Registry
+>2. CI/CD - Github Action
+>3. Machine - Azure Container Instance
+>4. 로그 수집 - ELK(Elasticsearch: indexing & storage, Logstash: log aggregation & processing, Kibana: Analysis & Visualization)
+>5. 고가용성을 위한 DB 이중화 및 샤딩 - PostgreSQL용 Azure Cosmos DB
+
+실제로 설계를 마치고 나서 인프라 구축까지도 완료했으나 일주일 만에 모든 아키텍처를 버리고 AWS에서 처음부터 구축을 하게 되었는데, Azure에서 AWS로 넘어가게 된 이유는 다음과 같다.
+
+개인적으로 느꼈던 Azure의 장점은 직관적이면서도 가시화된 인프라 상태를 한눈에 확인할 수 있는 좋은 UI였다. 대시보드 창에서 현재 내가 생성한 인스턴스들을 구독 별로, 인스턴스 그룹 별로 정말 직관적으로 확인이 가능했다. 또한 기본 제공 기능 중 하나로 가시화된 자료로 현재 구성되어 있는 인스턴스 설정을 바탕으로 인스턴스의 관계를 볼 수 있는 자료를 만들어 주는 탭도 존재했다. 해당 탭을 바탕으로 내가 인스턴스 간에 어떤 구성을 빠뜨렸는지 확인할 수 있었다.
+
+그러나 초보자가 참조할만한 공식 문서에 대한 질이 그리 좋은 편이 아니었다. AWS의 경우에는 기업이 아니라 개인도 정말 많이 사용하는 인프라이기 때문에 개인 블로그 같은 외부 레퍼런스도 굉장히 정리가 잘 되어 있는 편이라 그냥 그 문서 그대로 따라만 해도 100% functional한 인프라를 구축할 수 있지만 Azure의 경우에는 그렇지 않다. Outdated되어 있는 문서도 꽤나 존재하는 편이고 가독성 또한 떨어지는 문서가 많다. 이를 MS에서 인지하고 있는지 Microsoft Learn 같은 무료 레퍼런스를 제공하면서 Azure 사용법에 대한 지식을 학습 할 수 있는 세션을 운영중이지만, 해당 세션들을 모두 수강할만한 시간이 많지도 않았을 뿐더러 AWS로 갈아타게된 가장 큰 계기는 그냥 어느날 갑자기 Azure Computing Instance가 접속 불가(....)가 되어버려서 였다.
+
+그냥 어느날 갑자기 ssh든 컨테이너 내부 접속이든 모두 먹통이 되어서 써먹을 수 없는 상황이었다. 또한 Region 문제도 발목을 잡았었는데 한국 region으로 인스턴스 생성은 가능했으나 각 region 별로 인스턴스 제공 기능에 차이가 있었고 이를 알아차린건 인스턴스를 만들고 나서 한참 뒤였다...
+
+![plans](plans.png)
+/// caption
+ACI 생성 가능 region 중 일본 지역 일부는 가용성 영역을 지원하지만 한국은 그렇지 않다.
+///
+
+실제로 한국 region으로 사용해보면서 인스턴스에게 할당할 수 있는 최대 CPU, Memory의 할당량도 공식문서에 기재되어 있는 설명과 차이가 있었다.
+
+결국 진짜 오랜만에 AWS로 돌아가서 인프라를 구축하게 되었다.
+
+외부 레퍼런스를 참고하면서 구축했고 성공적으로 인프라를 구축했다. 현재까지도 작동하면서 이슈가 생긴적 단 한번도 없이 잘 작동한다. 완성된 인프라 아키텍처는 다음과 같다.
+
+![infra](infra.png)
+/// caption
+인프라 구성
+///
+
+이제 인프라를 구축했으니 계속 버전 업데이트가 이루어지는 코드를 실제 프로덕션 환경에 반영할 CI/CD 파이프라인 자동화 작업이 남았다.
+
+사용해봤던 CI/CD 툴로는 Jenkins가 있었지만, 이번에 나는 Github Actions를 사용해보기로 했다.
+
+
+
+Jenkins가 아니라 Github Actions를 선택한 이유는 다음과 같다.
+
+Jenkins는 따로 인프라 어딘가에 직접 Jenkins를 설치하고 초기 설정까지 하나하나 다 해줘야 한다. 이는 유지 관리 측면에서 보안 측면이라던지, 파이프라인 구축 전반에 대한 이해를 요하기 때문에 CI/CD 구축에 더 많은 시간이 소요될 수도 있겠다는 생각이 들었다. 가장 많이 사용되면서도 Github Actions보다 상대적으로 생긴지 오래된 툴이기 때문에 다수의 플러그인이 존재한다는 장점이 있으나 딱히 필요성을 못느꼈기 때문에 체택하지 않았다.
+
+Github Actions의 경험은 정말 만족스러웠다. GitHub 저장소와 직접 통합되어 있기 때문에 별도의 CI 서비스를 설정할 필요가 없었고 YAML 파일 기반의 워크플로우를 사용하여 CI/CD 파이프라인을 손쉽게 정의할 수 있었다. 또한 도커 컨테이너와도 유연한 연결을 보여줘서 빌드 중 다른 앱과의 Conflict 없이 배포가 가능했다. 공식 레퍼런스도 자세하고 다양하게 제공되어 있는 편이라 참고도 쉬웠다.
+
+완성된 CI/CD 파이프라인은 다음과 같다.
+
+![infracomplete](infracomplete.png)
+/// caption
+CI/CD
+///
+
+---
+
+## 좋았던 점
+
+이번 프로젝트에서 좋았던 점은 다음과 같다고 생각한다.
+
+!!! success
+ 1. 구성한 프로젝트 스프린트 대로 알파 빌드 별 태그를 부여한 버저닝
+ 2. Git branch를 적극 활용한 버전 관리
+ 3. Github wiki 페이지를 활용한 문서 정리
+ 4. Github actions를 활용한 문제 없이 작동하는 CI/CD의 성공적인 구축
+
+프로젝트 초기에 내가 정의한 개발 방법론 및 가이드라인을 최대한 준용하여 개발을 진행하였고, 정형화된 가이드라인을 정의한 덕분에 협업에 대한 모니터링을 수월하게 할 수 있었다.
+
+브랜칭, 버전 태그 부여로 인해 각 버전의 스프린트가 끝나면 개발을 더 진행했더라도 해당 버전의 코드를 토대로 스프린트 회고를 할 수 있었다.
+
+Git branch의 장점을 그대로 녹인 개발 방법론 덕분에 협업을 성공적으로 진행할 수 있었고 개발 마감까지 프로젝트를 완성할 수 있었다.
+
+또한 제대로된 CI/CD를 온전히 내가 구현한 적은 이번이 처음이었는데 문제 없이 파이프라인을 구축하여 배포 자동화를 할 수 있었던 것이 굉장히 뿌듯했다. 처음 써보는 인프라와 아키텍처, 툴이 적지 않았음에도 불구하고 다양한 레퍼런스를 참고하여 개발을 완료할 수 있었다.
+
+그리고 가장 좋았던 것은 내 아이디어가 협업을 통해 생각했던 기능 거의 그대로 개발이 되어 나온 것이었다. 몇달 전까지만 해도 그냥 끄적이면서 내 머릿속에 생각했던 아이템이 실제로 그대로 구현되어 나왔다는 것이 신기했다. 비록 공모전 출품용으로 세상에 나오게된 아이템이지만 이번 경험을 토대로 앞으로의 프로젝트들을 더욱 완벽하게 완성할 수 있을 것 같다.
+
+---
+
+## 어려웠던 점
+
+개발 과정중에서는 크게 어려운점이 없었다. 하나 있었다면 뉴스피드 알고리즘을 구현하는 것이 어려웠다. 처음 해보는 뉴스피드 개발이기도 하고 뉴스피드에서 현재 여러가지의 알고리즘이 개발되어 있다는 것을 이번 프로젝트를 통해서 처음 알게 되었다. 뉴스피드 알고리즘을 아키텍처에 적용시켜 최적화 하는 작업이 어려웠다.
+
+그러나 레퍼런스를 통해 알고리즘 구현에 대한 힌트를 얻을 수 있었고 이를 통해 뉴스피드 알고리즘 최적화 및 뉴스피드 구현을 할 수 있었다.
+
+처음 난관을 겪었던 부분은 Docker Compose를 활용해 빌드한 컨테이너 이미지를 실제 프로덕션에서 배포하는 과정중에 겪었던 문제였다.
+내가 작업하는 머신은 MacBook, 즉 OS 환경이 Linux도 아니고 Windows도 아니고 MacOS였다. 당연히 로컬에서 작업하는 머신에서 컨테이너 이미지를 빌드하고 있었고 amd64 환경이 아니라 M1 아키텍처에 맞는 이미지가 빌드가 되었다.
+
+이 이미지를 그대로 DockerHub에 올렸다. 그러나 CD Observer가 이미지를 DockerHub에서 다운 받아 컨테이너로 빌드하는 과정이 자꾸 실패했다.
+
+![cdfail](cdfail.png)
+/// caption
+수 많은 CD failures
+///
+
+머신 아키텍처 문제라고는 전혀 생각을 못했기 때문에 이 문제에 많은 시간을 쏟아서 해결하려고 노력했고, 다행이 원인을 찾을 수 있었다.
+
+이미지 빌드 환경을 m1 아키텍처가 아닌 linux/amd64환경으로 설정하고 빌드해서 해결할 수 있었다.
+
+![cmd](cmd.png)
+/// caption
+명령어의 추가로 CI/CD를 성공적으로 할 수 있었다.
+///
+
+빌드 명령어를 위와 같이 수정함으로써 프로덕션에 성공적으로 새로운 버전을 올릴 수 있었다.
+
+
+
+그리고 가장 어려웠고 가장 많은 시간을 잡아 먹었던 것은 버전 1.0 배포 단계에 있었던 트러블 슈팅이었다.
+
+![troubleshoot](troubleshot.png)
+/// caption
+Issue Error 내용
+///
+
+![trouble](trouble.jpeg)
+/// caption
+이슈가 나는 페이지
+///
+
+버전 1.0 배포를 눈앞에 두고 있던 날, 즉 프로젝트 배포 마감일 하루 전에 다같이 모여서 Frontend Page를 로컬에서 빌드해보면서 이슈가 나는 페이지는 없는지 확인을 하던 중이었다.
+
+그러다가 피드에 게시물 렌더링은 문제 없이 되지만 게시물을 클릭해서 게시물 세부 내용을 보는 페이지가 작동이 되지 않는 것을 확인했다.
+
+Frontend code 로직 상으로 post_title부분에 undefined 토큰이 넘어갈 이유가 없음에도 불구하고 거의 7시간 넘게 왜 자꾸 post_title이 undefined가 뜨는지 찾지를 못했다.
+
+![talk](talk.png)
+/// caption
+고생한 흔적..
+///
+
+결국 배포 1시간 전까지 이유를 찾지 못했고 일단 시간이 없으므로 이 버전을 배포하기로 했다.
+
+![output](output.png)
+
+그리고 놀랍게도 배포를 했더니 작동이 문제 없이 잘 되는 것이었다(....)
+
+이 이슈를 트러블 슈팅하느라 이날 밤을 샜는데 이렇게 허무하게 해결이 된 것이 너무 허탈했다.
+
+뭐, 결론적으로 작동이 되는 코드가 프로덕션에 올라가서 다행이지만 아직도 찜찜함이 남아있다...
+
+---
+
+## 아쉬웠던 점
+
+프로젝트를 완성을 하긴 했지만 회고를 해보니 아쉬운점이 여럿 있었다.
+
+일단 팀에 PM이 없어서 팀장인 내가 PM의 역할까지도 맡았는데, 공모전 개발이 아니라 다른 여러 일 때문에 일정이 바빠져서 버전 1.0 배포를 마치고 나서 1.1을 배포할 때 까지 팀원들의 개발 프로세스 모니터링 및 리뷰를 할 시간이 별로 없었다.
+
+시간이 부족해지면서 자연스럽게 JIRA 페이지에 대한 신경을 쓰지 못하게 되었고 백로그 관리가 조금 소홀해졌다.
+
+1달동안 좀 더 노력해서 계속 이슈를 해결하고 개발 프로세스를 더 검토하면서 기능 발전을 이뤄내고 버전 업을 꾸준히 이루었어야 했는데 그러지 못하고 버전 1.1로 최종 빌드를 마무리해서 제출한 것이 아쉬웠다.
+
+그리고 Github repo설계부분이 조금 아쉬웠다. Github organizations를 만들어서 팀 repo를 꾸려 backend, frontend, 환경 설정용 repo를 따로따로 분류하면 이슈 관리 및 backend, frontend application 버전 관리를 좀 더 깔끔하게 할 수 있었을 것 같았는데 프로젝트 구성 초기에 이를 좀 더 깊게 고려하지 못하고 저장소 하나에서 해결하려고 했던 것이 아쉬웠다.
+
+그리고 가장 뼈져리게 아쉬웠던 것은 역량 부족으로 인해 UIUX 디자인을 좀 더 깔끔하고 세련되게 수정하지 못한 점이었다. 디자이너의 부재로 frontend 팀과 스켈레톤 디자인을 개발했던 내가 디자인까지 설계를 했었는데 이 때문에 UI 페이지 결과물이 조잡한 수준으로 뽑혀서 너무 아쉬웠다. 디자이너가 한명이라도 팀에 있었다면 더 나은 UIUX 설계를 통해 좀 더 예쁘고 인터렉티브한 디자인을 뽑을 수 있었을 텐데 말이다. 결국 출전했던 두 공모전 모두 수상을 하진 못했던 것이 아마 이 부분 때문이 아닐까 싶기도 하다.
+
+문서화 단계에서도 아쉬운 점이 있었다. 나 혼자 개발하는 프로젝트가 아니었기에 코딩 컨벤션 구성에 신경을 써서 문서화를 진행했다고는 하지만, 나는 Frontend 개발자가 아니었기 때문에 Frontend 컨벤션은 Backend만큼 자세하게 풀진 못했다.. Frontend 관련 지식이 부족했기 때문에 Backend와는 달리 Frontend 컨벤션은 그리 세밀하지 못했다. Frontend도 컨벤션을 Backend만큼이나마 자세하게 풀었다면 리펙토링도 하기 쉬웠을거라고 생각한다.
+
+---
+
+## 마무리
+
+거의 처음으로 교내 프로젝트가 아니라 공모전을 위한 프로젝트, 심지어 아이디어 공모전도 아니라 아이템 개발 프로젝트를 처음으로 개발 전반을 내가 주도해서 완성시킨 프로젝트이다.
+
+아쉬운 점도 많았던 프로젝트지만 그보다 더 뿌듯했던 점이 많았던 프로젝트였다. 약 3달동안의 경험으로 정말 많은 것을 얻어갈 수 있었던 프로젝트였다. 회고를 통해 아쉽다고 느껴진 점들은 모두 다음 프로젝트에서 개선해서 더 발전된 아이템으로 만들어나갈 것이다.
diff --git a/docs/blog/posts/20230921-project-pinned/apidocs.png b/docs/blog/posts/20230921-project-pinned/apidocs.png
new file mode 100644
index 0000000..c1eb9f6
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/apidocs.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/cdfail.png b/docs/blog/posts/20230921-project-pinned/cdfail.png
new file mode 100644
index 0000000..d8de647
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/cdfail.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/cmd.png b/docs/blog/posts/20230921-project-pinned/cmd.png
new file mode 100644
index 0000000..8f86601
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/cmd.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/contributedocs.png b/docs/blog/posts/20230921-project-pinned/contributedocs.png
new file mode 100644
index 0000000..b366ef6
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/contributedocs.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/dirtree.png b/docs/blog/posts/20230921-project-pinned/dirtree.png
new file mode 100644
index 0000000..ce874c1
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/dirtree.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/idea.png b/docs/blog/posts/20230921-project-pinned/idea.png
new file mode 100644
index 0000000..79b512b
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/idea.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/infra.png b/docs/blog/posts/20230921-project-pinned/infra.png
new file mode 100644
index 0000000..42524b8
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/infra.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/infracomplete.png b/docs/blog/posts/20230921-project-pinned/infracomplete.png
new file mode 100644
index 0000000..72383e7
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/infracomplete.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/issues.png b/docs/blog/posts/20230921-project-pinned/issues.png
new file mode 100644
index 0000000..8f97569
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/issues.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/jira.png b/docs/blog/posts/20230921-project-pinned/jira.png
new file mode 100644
index 0000000..6576f1f
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/jira.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/output.png b/docs/blog/posts/20230921-project-pinned/output.png
new file mode 100644
index 0000000..61f282c
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/output.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/plans.png b/docs/blog/posts/20230921-project-pinned/plans.png
new file mode 100644
index 0000000..e319a46
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/plans.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/stacks.png b/docs/blog/posts/20230921-project-pinned/stacks.png
new file mode 100644
index 0000000..a277c31
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/stacks.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/talk.png b/docs/blog/posts/20230921-project-pinned/talk.png
new file mode 100644
index 0000000..72089fa
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/talk.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/thinking.png b/docs/blog/posts/20230921-project-pinned/thinking.png
new file mode 100644
index 0000000..4c6c184
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/thinking.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/timelines.png b/docs/blog/posts/20230921-project-pinned/timelines.png
new file mode 100644
index 0000000..14e6768
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/timelines.png differ
diff --git a/docs/blog/posts/20230921-project-pinned/trouble.jpeg b/docs/blog/posts/20230921-project-pinned/trouble.jpeg
new file mode 100644
index 0000000..c1fda5e
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/trouble.jpeg differ
diff --git a/docs/blog/posts/20230921-project-pinned/troubleshot.png b/docs/blog/posts/20230921-project-pinned/troubleshot.png
new file mode 100644
index 0000000..63aa541
Binary files /dev/null and b/docs/blog/posts/20230921-project-pinned/troubleshot.png differ
diff --git a/docs/blog/posts/20231002-drf-yasg/20231002.md b/docs/blog/posts/20231002-drf-yasg/20231002.md
new file mode 100644
index 0000000..3c8d6e9
--- /dev/null
+++ b/docs/blog/posts/20231002-drf-yasg/20231002.md
@@ -0,0 +1,485 @@
+---
+title: '[API Documentation] Django backend에 swagger로 API endpoint 문서화 하기(drf-yasg)'
+description: DRF YASG 적용 과정에서 겪었던 문제들과 해결 방법에 대해 서술합니다.
+authors:
+ - bnbong
+date:
+ created: 2023-10-02
+ updated: 2023-10-02
+categories:
+ - Backend
+tags:
+ - DRF
+ - Django REST Framework
+ - Swagger
+ - API Documentation
+comments: true
+---
+
+## API와 API 문서?
+
+개발을 하다 보면, API 라는 단어를 적어도 한번 혹은 정말 질리도록 마주칠 것이다. 그런데 이 단어를 처음 접해서 인터넷에 검색을 해보면 당황을 하는 사람이 더러 있을 것이다.
+
+Application Programming Interface, 줄여서 ==API는 다양한 소프트웨어나 애플리케이션 간의 상호작용을 가능하게 해주는 인터페이스==라고 설명이 되어 있는데 이렇게 설명만 듣고 보면 무슨 말인지 감이 잘 안올 것이다. 나도 최근에 공모전 등의 협업 프로젝트를 진행하면서 같은 SW학부생인데도 불구하고 API라는 것이 어렵고, 또 잘 와닿지 못해 하는 친구들을 더러 봤다.
+
+API라는 단어가 주는 압박감, 이해하기 힘든 의미 때문에 API가 어렵게 느껴질 수 있지만 이 것은 정말로 어렵게 생각할 것이 전혀 아니다.
+
+
+
+API를 예시를 들어 설명하자면 다음과 같다.
+
+여러분 앞에 데스크톱 본체가 있다고 하자. 이 데스크톱을 모니터에 연결해서 컴퓨터 화면을 볼 수 있고, 또 유선키보드나 유선 마우스를 연결해서 인터넷 검색을 하거나 유튜브를 볼 수 있다.
+
+우리는 유선 마우스를 주문해서 마우스 클릭질을 하면서 유튜브를 보고 싶다. 노트북이라면 따로 마우스를 구매하지 않아도 되었겠지만 데스크톱 본체만 달랑 있다면 우리는 아무것도 할 수 없기에 마우스를 구매하기로 했다. 얼마 후, 마우스가 배송이 되어서 왔고 드디어 우리는 컴퓨터 화면에서 클릭을 하면서 유튜브 동영상을 볼 수 있게 되었다.
+
+그런데 가만히 생각해보면 데스크톱 본체와 마우스는 제조사가 다르다. 그럼에도 불구하고 우리는 이들을 문제없이 연결해서 쓸 수 있다. 우리가 유선 마우스를 택배로 받았을 때, 데스크톱 본체와 다른 제조사에서 다른 날짜에 배송된 물건을 받았음에도 유선마우스를 연결할 수 있는 동일한 USB 포트가 데스크톱에 포함되어 있어 컴퓨터에 연결해서 마우스 클릭 액션을 할 수 있다. 이게 어떻게 가능할까?
+
+이 것은 데스크톱 본체와 마우스를 만드는 회사가 같은 포트 '규격'을 보고 제품을 만들었기 때문이다. USB라는 표준 규격을 참고하여 두 회사가 제품을 만들었기 때문에 마우스와 데스크톱 본체를 문제 없이 연결을 할 수 있고 마우스 클릭, 휠 스크롤 등의 동작의 결과를 컴퓨터 화면에서 볼 수 있는 것이다. 만약 마우스 회사가 자기들만의 독자적인 포트 규격을 사용해서 제품을 만들었다고 하면 그 마우스는 우리 집에 있는 데스크톱과 연결할 수 없을 것이다.
+
+이 예시 중 API의 의미에 대해서 우리가 주목을 해야햐는 부분은 'USB 포트'이다. 컴퓨터 없이 마우스만 달랑 있다고 해서 우리가 아무리 클릭을 한다 한들, 유튜브 화면을 띄워서 동영상을 클릭할 수 없을 것이고 데스크톱 본체만 달랑 있다고 해서 우리가 아무리 보고 싶은 동영상이 있어도 클릭해서 그 영상의 내용을 볼 수 없다. ==컴퓨터와 마우스를 연결해서 우리가 '클릭' 이라는 마우스 액션을 데스크톱 본체로 전달을 해주는 것이 이 'USB 포트'라고 생각할 수 있다.== 즉, USB 포트는 컴퓨터와 마우스가 상호작용을 할 수 있도록 해주는 일종의 징검다리 역할을 해주는 것이다. 컴퓨터를 맨 처음 발명한 회사는 이 징검다리를 ^^USB라는 '규격'^^으로 설명해놨기에 이 행동이 가능한 것이다.
+
+API도 USB 포트의 역할과 동일하다. ==다양한 애플리케이션, 서비스, 시스템들이 서로 상호작용을 할 수 있도록 통신할 수 있는 ^^징검다리^^ 역할을 해주는 것이 API==다. 방금전 예시로 들었던 USB 규격의 설명 처럼 이 API라는 것을 개발자가 써먹을 수 있게 설명을 해놓은 설명서가 API 문서이다.
+
+API 문서는 어떤 프로그램이나 앱이 아니라 말 그대로 글로 적혀 있는 메뉴얼이다. 어떤 소프트웨어 제품을 개발하려고 할 때, 개발자는 이 API 문서를 오랫동안 들여다보면서 서로 다른 분야에 있는 개발자라고 하더라도 하나하나 다 물어보면서 서로를 귀찮게하거나 내가 알고 있는 분야가 아닌 처음 보는 분야를 공부하는 등의 비효율적인 작업 필요 없이 굉장히 효율적인 협업을 할 수 있다.
+
+API 문서를 잘 작성하는 것은 API 서비스를 성공적으로 만드는 역할 뿐만 아니라 성공적인 협업으로의 결정적인 역할을 할 수 있다. Backend 개발의 경우에는 Backend 개발자가 서비스를 만듦과 동시에 API 문서를 작성하게 되는데, 기존에는 API 문서를 하나하나 손으로 작성해야했기에 비효율적이었고, 작성했다 하더라도 실제 API 서비스와 문서에 적혀있는 기능이 다른 경우가 많았다. 이를 해결하기 위해 따로 문서를 작성할 필요 없이 API 문서를 자동으로 만들어주는 라이브러리 및 규격들이 만들어졌다. 이번 포스팅에 소개할 Swagger가 그 중 하나이다.
+
+
+
+---
+
+## Swagger
+
+
+
+==Swagger는 API 설계, 구축, 문서화, 그리고 사용에 관한 도구 및 규약의 모음==이다. Swagger는 OpenAPI Specification (OAS)의 초기 이름으로, 현재는 OpenAPI로 더 잘 알려져 있다. OpenAPI 규격이라고 하면 Swagger에서 제공하는 규격이라고 생각해도 문제 없다.
+
+Swagger는 어느 특정 언어나 프레임워크에 제한되어 있지 않기 때문에 내가 어떤 언어로 개발을 하더라도 Swagger를 적용시킬 수 있다. Swagger가 API 문서 작성을 자동화해주는 덕분에 API 문서에 투자하는 시간을 확실하게 줄일 수 있다. 또한 API 서비스에 변화가 생기더라도 즉시, 자동으로 반영해주기 때문에 수고를 많이 덜 수 있다.
+
+Swagger/OpenAPI 규격은 여러개가 나와있고, 이 Swagger 규격을 Python의 Django framework에서도 적용시킬 수 있다. 바로 **drf-yasg**라는 라이브러리를 통해 할 수 있다.
+
+## drf-yasg
+
+### 1. 설치
+
+
+
+다음 명령어로 drf-yasg 라이브러리를 프로젝트에 설치할 수 있다.
+
+```bash
+$ pip install drf-yasg
+```
+
+drf-yasg는 라이브러리 이름 답게 Django Rest Framework(DRF)와의 유연한 연결을 할 수 있다. DRF가 제공하는 [camel-case renderer인 djangorestframework-camel-case](https://github.com/vbabiy/djangorestframework-camel-case)와의 통합도 기본적으로 지원하고 다른 drf관련 기능 통합도 기본적으로 지원하는 기능이 많다. 자세한 방법은 다음 drf-yasg 공식 문서에서 확인할 수 있다.
+
+
+
+### 2. 초기 설정
+
+drf-yasg를 django 프로젝트에 설치를 했다면 다음 코드를 settings.py에 추가해줘야한다.
+
+```python
+# in settings.py
+
+INSTALLED_APPS = [
+ ...
+ 'django.contrib.staticfiles', # required for serving swagger ui's css/js files
+ 'drf_yasg', # 추가
+ ...
+]
+```
+
+urls.py에도 다음 코드를 추가해주면 여러분의 django 프로젝트에 swagger를 적용시킬 준비가 끝난다.
+
+```python
+# in urls.py
+
+...
+from django.urls import re_path
+from rest_framework import permissions
+from drf_yasg.views import get_schema_view
+from drf_yasg import openapi
+
+...
+
+schema_view = get_schema_view(
+ openapi.Info(
+ title="Snippets API", # API 문서의 제목
+ default_version='v1', # API 문서의 버전
+ description="Test description", # API 문서의 설명
+ terms_of_service="https://www.google.com/policies/terms/",
+ contact=openapi.Contact(email="contact@snippets.local"), # 개발자 연락처
+ license=openapi.License(name="BSD License"), # API 라이선스 내용(생략 가능)
+ ),
+ public=True,
+ permission_classes=(permissions.AllowAny,),
+)
+
+urlpatterns = [ # 하단의 urlpatterns로 접속을 하면 Swagger가 자동생성해주는 API 문서를 볼 수 있다.
+ path('swagger/', schema_view.without_ui(cache_timeout=0), name='schema-json'),
+ path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
+ path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
+ ...
+]
+```
+
+urlpatterns에 추가해준 내용을 바탕으로 여러분의 django 프로젝트는 다음 4개의 엔드포인트에서 swagger 문서를 볼 수 있다.
+
+!!! note ""
+ /swagger.json
+ /swagger.yaml
+ /swagger/
+ /redoc/
+
+내가 작성한 예제에서는 별다른 조건 없이 urlpatterns에다가 swagger endpoint를 그대로 연결해놓고 있지만 실제 배포 환경에서 API 문서를 public에 노출시키는 것은 내가 만들 API가 private인 경우에는 곤란해질 수 있다. 따라서 다음 내용 처럼 DEBUG 환경에서만 API 문서를 랜더하도록 조건을 걸 수 있다.
+
+```python
+# in urls.py
+
+if settings.DEBUG:
+ urlpatterns = [ # 하단의 urlpatterns로 접속을 하면 Swagger가 자동생성해주는 API 문서를 볼 수 있다.
+ path('swagger/', schema_view.without_ui(cache_timeout=0), name='schema-json'),
+ path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
+ path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
+ ...
+ ]
+```
+
+Django 공식 문서에서도 프로덕션 빌드가 아닌 이상 DEBUG 옵션을 DEBUG=True로 설명을 하고 있기에 위 코드가 문제 없이 실행될 수 있다.
+
+### 3. 추가 환경 설정
+
+settings.py에 SWAGGER_SETTINGS 문단을 추가해서 drf-yasg가 읽어들이는 swagger/redoc settings를 기재할 수 있다. 예시는 다음과 같다.
+
+```python
+# in settings.py
+
+SWAGGER_SETTINGS = {
+ 'SECURITY_DEFINITIONS': {
+ 'basic': {
+ 'type': 'basic'
+ }
+ },
+ ...
+}
+
+REDOC_SETTINGS = {
+ 'LAZY_RENDERING': False,
+ ...
+}
+```
+
+다음 공식 문서에서 확인할 수 있다. 내용이 많은 편이다.
+
+
+
+### 4. 내 API에 적용
+
+drf-yasg 설정을 끝내면 이제 내가 작성한 Django application의 views.py와 연결된 엔드포인트에 대해 Swagger 문서에 자동화시켜 띄울 수 있다. 이번 포스팅에서는 DRF의 generic views class를 바탕으로 설명을 할 것이다.
+
+
+
+다음과 같이 어떤 Django Application에 CRUD 기능을 하는 view를 정의해놓았다고 하자.
+
+```python
+# in someapp/views.py
+
+from rest_framework import generics, mixins
+
+from .serializer import SomeAppSerializer
+
+
+class SomeAPI(
+ mixins.RetrieveModelMixin, # get methods
+ mixins.CreateModelMixin, # post methods
+ mixins.UpdateModelMixin, # put methods
+ mixins.DestroyModelMixin, # delete methods
+ generics.GenericAPIView,
+):
+ queryset = SomeModel.objects.all()
+ serializer_class = SomeAppSerializer
+ lookup_field = "id"
+ lookup_url_kwarg = "some_id"
+ permission_classes = AllowAny
+
+ def get(self, request, *args, **kwargs):
+ return self.retrieve(request, *args, **kwargs)
+
+ def post(self, request, *args, **kwargs):
+ return self.create(request, *args, **kwargs)
+
+ def put(self, request, *args, **kwargs):
+ return self.update(request, *args, **kwargs, partial=True) # 부분 put method 기능 지원.
+
+ def delete(self, request, *args, **kwargs):
+ return self.destroy(request, *args, **kwargs)
+```
+
+이 앱의 view 로직을 연결하는 router는 다음과 같이 someapp폴더의 urls.py에 정의했다.
+
+```python
+# in someapp/urls.py
+
+from . import views
+
+from django.urls import path
+
+urlpatterns = [
+ # Read, Delete, Update operations
+ path("/", views.SomeAPI.as_view(), name="some_routers"),
+]
+```
+
+이 앱의 엔드포인트로 request, response가 오고 가는 데이터를 serializing해주는 serializer는 다음과 같다.
+
+```python
+# in someapp/serializer.py
+
+from .models import SomeApp
+
+class SomeAppSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = SomeApp
+ fields = '__all__'
+```
+
+이 API endpoint는 SomeApp의 models.py에서 정의해놓은 Model 객체 형식대로 data를 serializer하여 서버에 CRUD 작업을 수행할 것이다.
+
+이렇게만 해주어도 Swagger 문서는 자동으로 SomeAPI class에 정의해놓은 serializer, permission class 등을 읽어서 문서를 생성해준다.
+
+이 상태로 python manage.py runserver 명령어를 입력해 localhost:8080에 서버를 띄운 후, localhost:8080/swagger/ 로 들어가 보면 다음과 유사한 페이지가 보일 것이다.
+
+![swaggeruiexample](swaggeruiexample.png)
+/// caption
+drf-yasg 공식 문서 제공
+///
+
+자동화된 API 문서에 실제 API 서비스와 유사한 request, response parameter를 랜더하기 위해서는 내가 만든 views.py에 있는 serializer_class같은 _class 설정 요소를 잘 정의해두는 것이 중요하다. 이 값을 바탕으로 drf-yasg가 문서를 만들어주기 때문이다.
+
+serializer 뿐만 아니라 API endpoint에서 데이터를 필터링하는 query parameter 또한 API 문서에 정의해둘 수 있다. 다음과 같이 View class 내부에 filterset_class를 정의해두면 된다.
+
+```python
+# in someapp/views.py
+
+from rest_framework import generics, mixins
+
+from utils.filters import SomeFilter
+
+from .serializer import SomeAppSerializer
+
+
+class SomeAPI(
+ mixins.RetrieveModelMixin, # get methods
+ mixins.CreateModelMixin, # post methods
+ mixins.UpdateModelMixin, # put methods
+ mixins.DestroyModelMixin, # delete methods
+ generics.GenericAPIView,
+):
+ queryset = SomeModel.objects.all()
+ serializer_class = SomeAppSerializer
+ lookup_field = "id"
+ lookup_url_kwarg = "some_id"
+ permission_classes = AllowAny
+ filter_backends = (DjangoFilterBackend,). # 추가
+ filterset_class = SomeFilter # 추가
+
+ def get(self, request, *args, **kwargs):
+ return self.retrieve(request, *args, **kwargs)
+
+ def post(self, request, *args, **kwargs):
+ return self.create(request, *args, **kwargs)
+
+ def put(self, request, *args, **kwargs):
+ return self.update(request, *args, **kwargs, partial=True) # 부분 put method 기능 지원.
+
+ def delete(self, request, *args, **kwargs):
+ return self.destroy(request, *args, **kwargs)
+```
+
+이렇게 filter을 적용시킬 query parameter의 class를 정의해두었다면 API 문서에서는 다음과 같이 랜더링된다.
+
+![rendered](rendered.png)
+
+이제 API 기능에 대한 자동화를 마쳤다면, 이 API endpoint에 대한 설명을 기재해야한다.
+
+이때, ==@swagger_auto_schema==를 사용하면 각 GET, POST, PUT, DELETE function 별로 설명을 넣거나 request body, response body paramter를 custom 할 수 있다.
+
+예시는 다음과 같다.
+
+```python
+# in someapp/views.py
+
+from rest_framework import generics, mixins
+
+from utils.filters import SomeFilter
+
+from .serializer import SomeAppSerializer
+
+from drf_yasg import openapi
+from somewhere.serializer import AnotherSerializer
+
+
+class SomeAPI(
+ mixins.RetrieveModelMixin, # get methods
+ mixins.CreateModelMixin, # post methods
+ mixins.UpdateModelMixin, # put methods
+ mixins.DestroyModelMixin, # delete methods
+ generics.GenericAPIView,
+):
+ queryset = SomeModel.objects.all()
+ serializer_class = SomeAppSerializer
+ lookup_field = "id"
+ lookup_url_kwarg = "some_id"
+ permission_classes = AllowAny
+ filter_backends = (DjangoFilterBackend,). # 추가
+ filterset_class = SomeFilter # 추가
+
+ @swagger_auto_schema(operation_description="GET Method 입니다.") # 문서에 GET method에 대한 설명 추가
+ def get(self, request, *args, **kwargs):
+ return self.retrieve(request, *args, **kwargs)
+
+ @swagger_auto_schema(operation_description="POST Method 입니다.", responses={201: '성공적으로 생성'}) # 문서에 POST method에 대한 설명과 Response message 추가
+ def post(self, request, *args, **kwargs):
+ return self.create(request, *args, **kwargs)
+
+ @swagger_auto_schema(request_body=AnotherSerializer) # PUT method 문서에서 request body를 다른 파라미터 형식으로 추가
+ def put(self, request, *args, **kwargs):
+ return self.update(request, *args, **kwargs, partial=True) # 부분 put method 기능 지원.
+
+ @swagger_auto_schema(manual_parameters=[openapi.Parameter(
+ name="uid",
+ in_=openapi.IN_HEADER,
+ type=openapi.TYPE_STRING,
+ description="유저의 UID 입니다.",
+ ),]) # Header로 받는 파라미터에 대한 설명을 API 문서에 추가.
+ def delete(self, request, *args, **kwargs):
+ return self.destroy(request, *args, **kwargs)
+```
+
+@swagger_auto_schema에 대한 자세한 설명은 공식 문서에서 확인할 수 있다.
+
+
+
+@swagger_auto_schema 데코레이터를 통한 endpoint 설명이 아니라 주석 형식으로 엔드포인트 설명을 달고 싶다면 다음과 같은 주석 형식을 사용하면 된다.
+
+```python
+# in someapp/views.py
+
+from rest_framework import generics, mixins
+
+from utils.filters import SomeFilter
+
+from .serializer import SomeAppSerializer
+
+
+class SomeAPI(
+ mixins.RetrieveModelMixin, # get methods
+ mixins.CreateModelMixin, # post methods
+ mixins.UpdateModelMixin, # put methods
+ mixins.DestroyModelMixin, # delete methods
+ generics.GenericAPIView,
+):
+ """
+ 다수 Event를 조회
+
+ ---
+ RBAC - 1(게스트) 이상
+ """
+ queryset = SomeModel.objects.all()
+ serializer_class = SomeAppSerializer
+ lookup_field = "id"
+ lookup_url_kwarg = "some_id"
+ permission_classes = AllowAny
+ filter_backends = (DjangoFilterBackend,). # 추가
+ filterset_class = SomeFilter # 추가
+
+ def get(self, request, *args, **kwargs):
+ return self.retrieve(request, *args, **kwargs)
+
+ def post(self, request, *args, **kwargs):
+ return self.create(request, *args, **kwargs)
+
+ def put(self, request, *args, **kwargs):
+ return self.update(request, *args, **kwargs, partial=True) # 부분 put method 기능 지원.
+
+ def delete(self, request, *args, **kwargs):
+ return self.destroy(request, *args, **kwargs)
+```
+
+상단의 내용 처럼 API View class 하단에 큰 따옴표 3개 붙어있는 주석을 작성하게 되면 다음과 같이 API 문서 중 각 endpoint에 대한 내용이 다음과 같이 적용된다.
+
+![example](example.png)
+
+'---' 기호 상단에 있는 내용이 각 endpoint에 대한 제목이고, 하단에 있는 내용은 설명을 펼쳐 나오는 세부 설명으로 렌더링이 된다.
+
+만약 Django application을 여러개 정의해놓아서 각 application 별로 공통 description이 필요하다면 다음과 같이 코드를 추가해서 설정해줄 수 있다.
+
+```python
+from drf_yasg.generators import OpenAPISchemaGenerator
+
+
+class CustomOpenAPISchemaGenerator(OpenAPISchemaGenerator):
+ def get_schema(self, request=None, public=False):
+ """Generate a :class:`.Swagger` object with custom tags"""
+
+ swagger = super().get_schema(request, public)
+ swagger.tags = [
+ {"name": "someapp", "description": "어떤 django application endpoint에 대한 설명입니다."},
+ {"name": "anotherapp", "description": "다른 django application endpoint에 대한 설명입니다."},
+ ...
+ ]
+
+ return swagger
+```
+
+drf-yasg는 각 Django application 별로 tag를 부여해서 문서를 만드는데, 이 태그에 대한 description을 dictionary로 넣어주는 로직이다.
+위 class를 정의해놓은 뒤, urls.py로 넘어가서 다음과 같이 설정을 추가해주면 된다.
+
+```python
+# in urls.py
+
+...
+from django.urls import re_path
+from rest_framework import permissions
+from drf_yasg.views import get_schema_view
+from drf_yasg import openapi
+
+from path_your_custom_api_schema_generator import CustomOpenAPISchemaGenerator
+
+...
+
+schema_view = get_schema_view(
+ openapi.Info(
+ title="Snippets API", # API 문서의 제목
+ default_version='v1', # API 문서의 버전
+ description="Test description", # API 문서에 설명
+ terms_of_service="https://www.google.com/policies/terms/",
+ contact=openapi.Contact(email="contact@snippets.local"), # 개발자 연락처
+ license=openapi.License(name="BSD License"), # API 라이선스 내용(생략 가능)
+ ),
+ public=True,
+ permission_classes=(permissions.AllowAny,),
+ generator_class=CustomOpenAPISchemaGenerator, # 추가
+)
+
+urlpatterns = [ # 하단의 urlpatterns로 접속을 하면 Swagger가 자동생성해주는 API 문서를 볼 수 있다.
+ path('swagger/', schema_view.without_ui(cache_timeout=0), name='schema-json'),
+ path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
+ path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
+ ...
+]
+```
+
+이렇게 정의를 해두었다면 다음과 같이 application 별로 description이 붙여서 문서가 생성된다.
+
+![practice2](practice2.png)
+
+이 외의 다른 내용들은 다음 공식 문서 링크에서 확인할 수 있다.
+
+
+
+## 마무리
+
+개발에서의 문서화는 코드를 작성하는 것 만큼이나 매우 중요한 작업이다. 코드를 작성하는 시간보다 문서를 다듬고 모니터링하는 과정이 더 많은 시간을 잡아먹을 수 있는 만큼 문서는 개발에서 큰 비중을 차지한다.
+
+이 작업에 대해 대단한 편의성을 제공해주시는 멋지고 대단한 개발자분들 항상 존경합니다 (_ _)
diff --git a/docs/blog/posts/20231002-drf-yasg/example.png b/docs/blog/posts/20231002-drf-yasg/example.png
new file mode 100644
index 0000000..c903896
Binary files /dev/null and b/docs/blog/posts/20231002-drf-yasg/example.png differ
diff --git a/docs/blog/posts/20231002-drf-yasg/practice2.png b/docs/blog/posts/20231002-drf-yasg/practice2.png
new file mode 100644
index 0000000..a407a08
Binary files /dev/null and b/docs/blog/posts/20231002-drf-yasg/practice2.png differ
diff --git a/docs/blog/posts/20231002-drf-yasg/rendered.png b/docs/blog/posts/20231002-drf-yasg/rendered.png
new file mode 100644
index 0000000..5626332
Binary files /dev/null and b/docs/blog/posts/20231002-drf-yasg/rendered.png differ
diff --git a/docs/blog/posts/20231002-drf-yasg/swaggeruiexample.png b/docs/blog/posts/20231002-drf-yasg/swaggeruiexample.png
new file mode 100644
index 0000000..9521956
Binary files /dev/null and b/docs/blog/posts/20231002-drf-yasg/swaggeruiexample.png differ
diff --git a/docs/blog/posts/20231211-pydantic-2/20231211.md b/docs/blog/posts/20231211-pydantic-2/20231211.md
index 8890eea..43819bd 100644
--- a/docs/blog/posts/20231211-pydantic-2/20231211.md
+++ b/docs/blog/posts/20231211-pydantic-2/20231211.md
@@ -1,5 +1,5 @@
---
-title: Pydantic 2.0 적용하기
+title: '[FastAPI] Pydantic 2.0 적용하기'
description: Pydantic 2.0 버전 적용 과정에서 겪었던 문제들과 해결 방법에 대해 서술합니다.
authors:
- bnbong
@@ -35,7 +35,11 @@ num = 1 # type: int
def duplicate(string, number = 2):
# type: (str, int) -> list
return [string] * number
+```
+
typing 라이브러리를 사용하면 다음과 같이 메서드의 type annotation을 적용할 수 있다.
+
+```python
num: int = 1
def duplicate(string: str, number: int = 2) -> list:
diff --git a/docs/blog/posts/20240816-cloudflare-dns/20240816.md b/docs/blog/posts/20240816-cloudflare-dns/20240816.md
index bd9ecf2..74db8f0 100644
--- a/docs/blog/posts/20240816-cloudflare-dns/20240816.md
+++ b/docs/blog/posts/20240816-cloudflare-dns/20240816.md
@@ -1,5 +1,5 @@
---
-title: CloudFlare DNS 레코드 프록시 기능...다 숨겨줬잖아
+title: '[Computer Network] CloudFlare DNS 레코드 프록시 기능...다 숨겨줬잖아'
description: CloudFlare DNS 레코드 프록시 기능을 통해 내 인스턴스의 Public IP를 숨길 수 있는 방법에 대해 서술합니다.
authors:
- bnbong
diff --git a/docs/blog/posts/20241230-computer-network-1/20241230.md b/docs/blog/posts/20241230-computer-network-1/20241230.md
new file mode 100644
index 0000000..4b42fde
--- /dev/null
+++ b/docs/blog/posts/20241230-computer-network-1/20241230.md
@@ -0,0 +1,168 @@
+---
+title: '[Computer Network] 컴퓨터 네트워크 개요'
+description: 모바일 컴퓨팅 강의 내용 정리 1
+authors:
+ - bnbong
+date:
+ created: 2024-12-30
+ updated: 2024-12-30
+categories:
+ - Network
+tags:
+ - TCP/IP
+ - OSI
+ - Protocol
+comments: true
+---
+
+## 네트워크 구조 이해: 네트워크 프로토콜과 계층 구조
+
+네트워크는 현대 사회의 필수 기술 중 하나다. 이 글에서는 2024-2학기 모바일 컴퓨팅 수업 자료를 바탕으로, 학습했던 네트워크의 계층 구조와 프로토콜의 기본 개념을 정리한다. 특히 OSI 모델과 TCP/IP 스택을 중심으로, 프로토콜이 데이터를 어떻게 전달하고 통신을 가능하게 하는지 설명한다.
+
+---
+
+### 1. 네트워크 계층의 중요성: 계층 구조와 추상화
+
+#### **계층 구조란?**
+네트워크 계층 구조는 복잡한 네트워크 작업을 단계별로 나누어 처리하기 위해 설계된 체계다. 각 계층은 특정 역할과 기능을 담당하며, 상호 독립적으로 작동한다.
+
+예를 들어:
+- **데이터 링크 계층**은 오류 검출 및 수정, 프레임 전송을 담당한다.
+- **네트워크 계층**은 패킷 전달 및 라우팅 역할을 한다.
+- **전송 계층**은 데이터의 신뢰성을 보장하고 흐름 제어를 수행한다.
+
+현대에서는 정말 다양한 네트워크 장비가 존재한다. 각 네트워크 장비들은 제조사가 다르고, 모델이 다르고, 심지어 같은 뿌리의 네트워크 장비라고 하더라도 버전이 다를 수 있다. 이러한 네트워크 장비들이 서로 통신하기 위해서는 각 장비들이 서로의 프로토콜을 알고 있어야 한다.
+
+그러나 정말 다양한 환경의 네트워크 장비들이 서로의 상황을 완벽하게 이해하기란 불가능에 가까우므로 이를 프로토콜을 사용하여 추상화하여 각 장비들이 서로의 상황을 이해하도록 한다.
+
+
+
+#### **추상화의 장점**
+계층 구조는 복잡한 네트워크 프로세스를 단순화하고, 특정 계층의 변화가 다른 계층에 영향을 미치지 않도록 설계된다. 이는 시스템 확장성과 상호 운용성을 크게 향상시킨다.
+
+---
+
+### 2. OSI 모델과 TCP/IP 프로토콜 스택
+
+#### **OSI 모델**
+OSI (Open Systems Interconnection) 모델은 7개의 계층으로 구성된다:
+1. **물리 계층**: 하드웨어 전송 기술.
+2. **데이터 링크 계층**: 프레임 생성, MAC 주소.
+3. **네트워크 계층**: 라우팅 및 IP 주소.
+4. **전송 계층**: TCP, UDP.
+5. **세션 계층**: 통신 관리.
+6. **표현 계층**: 데이터 변환 (암호화 등).
+7. **응용 계층**: HTTP, FTP 등 응용 프로그램 프로토콜.
+
+#### **TCP/IP 프로토콜 스택**
+실제 네트워크에서는 TCP/IP 스택이 주로 사용된다. TCP/IP는 4계층 구조로, OSI 모델과 비교하면 다음과 같다:
+1. **링크 계층**: 이더넷, PPP(Point-to-Point Protocol).
+2. **네트워크 계층**: IP, ARP.
+3. **전송 계층**: TCP, UDP.
+4. **응용 계층**: HTTP, DNS.
+
+TCP/IP 프로토콜 스택과 OSI 계층은 다음과 같이 서로 대응이 된다.
+
+| OSI 계층 | TCP/IP 계층 |
+| --------- | ------------ |
+| 물리 계층 (Layer 1) | 링크 계층(Layer 1) |
+| 데이터 링크 계층 (Layer 2) | 링크 계층(Layer 1) |
+| 네트워크 계층 (Layer 3) | 네트워크 계층(Layer 2) |
+| 전송 계층(Layer 4) | 전송 계층(Layer 3) |
+| 세션 계층(Layer 5) | 응용 계층(Layer 4) |
+| 표현 계층(Layer 6) | 응용 계층(Layer 4) |
+| 응용 계층(Layer 7) | 응용 계층(Layer 4) |
+
+각 네트워크 계층의 프로토콜들을 정리하여 그림으로 나타내면 다음과 같다.
+
+![layerprotocol](layerprotocol.png)
+
+---
+
+### 3. 프로토콜의 역할과 상호작용
+
+#### **프로토콜이란?**
+프로토콜은 통신 규칙을 정의한 표준이다. 데이터를 전송하는 방법, 오류를 검출하는 방식 등을 규정한다. 주요 프로토콜은 다음과 같다:
+- **TCP**는 신뢰성 있는 데이터 전송을 보장한다.
+- **UDP**는 빠르지만 신뢰성이 낮은 전송 방식을 제공한다.
+- **IP**는 패킷을 목적지로 전달하는 역할을 한다.
+
+#### **프로토콜 계층 간의 상호작용**
+프로토콜은 계층 간 상호작용을 통해 데이터를 처리한다:
+- 상위 계층에서 요청한 데이터는 하위 계층에서 캡슐화(encapsulation)된다.
+- 반대로, 수신된 데이터는 하위 계층부터 상위 계층으로 전달되며 디캡슐화(decapsulation)된다.
+
+통신을 수행하는 각 네트워크 장비는 서로의 프로토콜 계층에 데이터 통신을 주고 받는다.
+
+![peercommunicate](peercommunicate.png)
+
+즉, A 네트워크 장비가 B 네트워크 장비와 통신을 수행하고 있는 상황이라고 하면 A 네트워크 장비의 IP 계층에서 캡슐화된 데이터 패킷이 B 네트워크 장비에 도달하여 B 네트워크 장비의 IP 계층에서 디캡슐화되어 데이터 패킷이 다뤄지는 것이다.
+
+---
+
+### 4. 데이터 전송과 PDU
+
+#### **PDU (Protocol Data Unit)**
+각 계층에서 처리되는 데이터 단위를 PDU라고 한다. PDU는 각 계층마다 다르게 정의된다:
+- 데이터 링크 계층: **프레임(Frame)**.
+- 네트워크 계층: **패킷(Packet)**.
+- 전송 계층: **세그먼트(Segment)**.
+
+#### **캡슐화(Encapsulation)와 디캡슐화(Decapsulation)**
+- 캡슐화: 데이터가 각 계층에서 헤더를 추가하며 전송 준비를 한다.
+- 디캡슐화: 수신된 데이터에서 헤더를 제거하며 원래 데이터를 복원한다.
+
+캡슐화는 내가 상대방에게 보내고자 하는 데이터를 보내기 위해 각 계층에서 헤더를 추가하는 것이고, 디캡슐화는 상대방에게서 받은 데이터를 원래 데이터로 복원하는 것이다.
+
+기본적으로 네트워크 장비의 각 계층은 다른 계층의 데이터 구조를 전혀 건들이지 않는 것이 원칙이다. 따라서 원본 데이터 구조를 변경하는 작업을 하지 않고 단순히 헤더를 붙여서 필요한 정보만을 헤더에 기록하도록 한다.
+
+즉, 각 TCP 계층들에서 캡슐화가 수행되면 다음 사진과 같이 각 계층 별 헤더가 추가된다.
+
+![encapsule](encapsule.png)
+
+이렇게 캡슐화된 데이터는 상대방 네트워크 장비의 각 계층의 헤더를 제거하며 디캡슐화가 수행된다. 캡슐화와 마찬가지로 디캡슐화를 수행하는 계층이 아닌 다른 계층의 헤더를 제거하는 것은 불가능하도록 설계되어 있다.
+
+
+
+TCP 링크계층까지 캡슐화가 완료되어 외부로 나가는 데이터 프레임의 전체적인 구조는 다음과 같이 파싱된다.
+
+![dataframe](dataframe.png)
+
+상단의 사진은 인간이 이해하기 쉽도록 표현된 것이고, 실제로는 헤더를 포함한 모든 데이터가 컴퓨터가 읽을 수 있는 형태로 변환된다.
+
+![packettrace](packettrace.png)
+/// caption
+Wireshark 패킷 캡처 예시
+///
+
+---
+
+### 5. 데이터 링크 계층과 CRC
+
+#### **데이터 링크 계층의 역할**
+데이터 링크 계층은 프레임을 통해 데이터를 전달하고, 오류 검출 기능을 제공한다. 대표적으로 **CRC (Cyclic Redundancy Check)**가 사용된다.
+
+#### **CRC의 기능**
+CRC는 데이터 오류를 검출하기 위한 방법이다. 송신자가 CRC 값을 계산하여 프레임에 추가하고, 수신자는 이를 다시 계산해 비교함으로써 데이터 손상을 확인한다.
+
+---
+
+### 6. 멀티캐스트와 브로드캐스트
+
+#### **브로드캐스트**
+브로드캐스트는 네트워크의 모든 노드에 데이터를 전달하는 방식이다. 예를 들어 ARP 요청은 브로드캐스트로 MAC 주소를 탐색한다.
+
+#### **멀티캐스트**
+멀티캐스트는 특정 그룹에 속한 노드에만 데이터를 전달한다. IP TV와 같은 서비스에서 주로 사용된다.
+
+---
+
+### 7. 프로토콜과 상호 운용성의 미래
+
+네트워크 기술은 계속 발전하고 있으며, TCP/IP 프로토콜 스택과 OSI 모델은 여전히 중요한 역할을 하고 있다. 향후 IPv6, SDN(소프트웨어 정의 네트워크) 등 차세대 기술이 도입되더라도, 계층 구조의 핵심 원칙은 계속 유지될 것이다.
+
+---
+
+### 결론
+
+네트워크 계층 구조와 프로토콜은 데이터 통신의 근본적인 토대다. OSI 모델과 TCP/IP 프로토콜 스택을 통해 네트워크의 동작 방식을 이해하면, 더 나은 네트워크 설계와 문제 해결 능력을 갖출 수 있다.
diff --git a/docs/blog/posts/20241230-computer-network-1/dataframe.png b/docs/blog/posts/20241230-computer-network-1/dataframe.png
new file mode 100644
index 0000000..9c5dea0
Binary files /dev/null and b/docs/blog/posts/20241230-computer-network-1/dataframe.png differ
diff --git a/docs/blog/posts/20241230-computer-network-1/encapsule.png b/docs/blog/posts/20241230-computer-network-1/encapsule.png
new file mode 100644
index 0000000..bdc5a14
Binary files /dev/null and b/docs/blog/posts/20241230-computer-network-1/encapsule.png differ
diff --git a/docs/blog/posts/20241230-computer-network-1/layerprotocol.png b/docs/blog/posts/20241230-computer-network-1/layerprotocol.png
new file mode 100644
index 0000000..a19a6b4
Binary files /dev/null and b/docs/blog/posts/20241230-computer-network-1/layerprotocol.png differ
diff --git a/docs/blog/posts/20241230-computer-network-1/packettrace.png b/docs/blog/posts/20241230-computer-network-1/packettrace.png
new file mode 100644
index 0000000..1095d5f
Binary files /dev/null and b/docs/blog/posts/20241230-computer-network-1/packettrace.png differ
diff --git a/docs/blog/posts/20241230-computer-network-1/peercommunicate.png b/docs/blog/posts/20241230-computer-network-1/peercommunicate.png
new file mode 100644
index 0000000..13d6423
Binary files /dev/null and b/docs/blog/posts/20241230-computer-network-1/peercommunicate.png differ
diff --git a/mkdocs.yml b/mkdocs.yml
index d310aa6..187c74f 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -141,6 +141,7 @@ markdown_extensions:
- pymdownx.keys
- pymdownx.mark
- pymdownx.tilde
+ - pymdownx.blocks.caption
- meta
- def_list
- admonition