Compare commits

...

15 Commits

Author SHA1 Message Date
dihwang a851025e43 노트추가 및 서버 이전 2026-05-12 14:59:58 +09:00
dihwang 722d5dbfaf 오랜만에 커밋 2026-05-04 10:30:04 +09:00
dihwang c35c042afa 스크립트 잘못되있어서 /home/dihwang 경로 다 날라감
방어스크립트로 변경
2026-04-10 11:48:51 +09:00
dihwang d3de1706be /home/dihwang -> /var/www 로 경로 변경 2026-04-10 11:21:08 +09:00
dihwang bf1c660ed9 공부내용 몇개추가 + root 경로 변경 2026-04-10 09:57:02 +09:00
dihwang 3087d9cc11 Revert "볼륨 삭제"
This reverts commit 007b2747fb.
2026-04-09 11:34:46 +09:00
dihwang a403850ca2 Revert "filterFn 그지같네"
This reverts commit b10a88d5d2.
2026-04-09 11:23:14 +09:00
dihwang b10a88d5d2 filterFn 그지같네 2026-04-09 11:20:08 +09:00
dihwang ae069f460f 커밋메시지 적기도 귀찮다 2026-04-09 11:04:11 +09:00
dihwang 2e101e6d9d 이젠 진짜 좀 되라 2026-04-09 11:00:44 +09:00
dihwang f03c11f53a config, layout 커스터마이징 2026-04-09 10:57:36 +09:00
dihwang 2476953a4b Revert "쉘프 삭제"
This reverts commit edca620072.
2026-04-09 10:48:25 +09:00
dihwang d24254fffc Revert "노트 삭제"
This reverts commit 536c8d9197.
2026-04-09 10:48:09 +09:00
dihwang 4f2c886825 Revert "엑스트라 삭제"
This reverts commit 494c03687b.
2026-04-09 10:47:55 +09:00
dihwang 4510784946 Revert "Reapply "archive 삭제""
This reverts commit aec9ae8c43.
2026-04-09 10:45:31 +09:00
463 changed files with 10688 additions and 16 deletions
+12 -4
View File
@@ -34,7 +34,7 @@ jobs:
username: ${{ secrets.GCE_USERNAME }}
key: ${{ secrets.GCE_SSH_KEY }}
source: "deploy.tar.gz"
target: "/home/dihwang/white-smith-blog"
target: "/var/www/white-smith-blog"
- name: Extract Archive on GCE
uses: appleboy/ssh-action@master
@@ -43,9 +43,17 @@ jobs:
username: ${{ secrets.GCE_USERNAME }}
key: ${{ secrets.GCE_SSH_KEY }}
script: |
cd /home/dihwang/white-smith-blog/public
# 3. 서버에 남아있는 유령 파일들 싹 제거 (매우 중요)
# 1. 대상 폴더가 없으면 미리 생성 (cd 실패 방지)
mkdir -p /var/www/white-smith-blog/public
# 2. 폴더 이동에 성공했을 때만(&&) 내부 파일 삭제 및 압축 해제 진행
cd /var/www/white-smith-blog/public && {
# 현재 폴더(public) 내의 모든 파일/폴더 삭제 (점 파일 포함)
find . -mindepth 1 -delete
# 4. 압축 해제
# 상위 폴더에 전송된 압축 파일 해제
tar -xzf ../deploy.tar.gz
# 작업 완료 후 압축 파일 삭제
rm ../deploy.tar.gz
}
@@ -0,0 +1,13 @@
[[꼭 알아둬야 할 것들]]
[[AWS Architecture로의 전환이 필요한 이유]] (As-Is)
[[온프레미스에서 EC2 기반으로]]
[[컨테이너 기반의 아키텍처]] (To-Be)
[[ECS와 EKS]]
[[EKS Cluster]]
[[AWS ECS 구성하기]]
@@ -0,0 +1,27 @@
---
id: "CMMS 마이그레이션 20260319"
created: "2026-03-19 12:58"
tags:
aliases:
---
마이그레이션: 신규 고객의 셋업작업 진행 시 필요한 데이터를 DB로 밀어넣는 과정
엑셀파일로 된 마이그레이션 필요 문서 작성 (담당자가 따로있나봄)
(원본엑셀 -> 마이그레이션 할 데이터 엑셀 )
/auth/fullmigration
( CMMS 엔터프라이즈형은 /auth/mig )
마이그레이션 페이지가 따로 만들어져있음.
여기서 엑셀파일들을 전부 다 execute migration 진행해줘야 함
너무 많은 데이터를 한번에 시도하면 문제가 될 수 있어서
분리해서 진행해야함.
파일 마이그레이션은 암호화(확장자없는) 파일을 먼저 만들고 (엑셀파일부터 작성)
...? 그다음에 뭘 또 해야한다는데 이건 이해 못함
shared-service: 마이그레이션, open API (우리는 일단 신경x) 등등의 기능들을 처리해주는 서비스
View File
+2
View File
@@ -0,0 +1,2 @@
[[VPN으로 외부 DB에 접근]]
+2
View File
@@ -0,0 +1,2 @@
[[마력과 토크, RPM]]
@@ -0,0 +1,9 @@
[[Gitea & Quartz 배포 자동화 트러블슈팅]]
[[gitea, qurtz 설치]]
[[kui-vault]]
[[WebHook]]
+1
View File
@@ -0,0 +1 @@
[[안드로이드 스튜디오 무선디버깅]]
+2
View File
@@ -0,0 +1,2 @@
[[전표처리]]
+3
View File
@@ -0,0 +1,3 @@
---
title: 1. 프로젝트
---
+4
View File
@@ -0,0 +1,4 @@
### Coding Agent
[[Aider 사용기]]
[[Aider 프로젝트 생성]]
+2
View File
@@ -0,0 +1,2 @@
[[Functional Domain Modeling]]
+2
View File
@@ -0,0 +1,2 @@
[[파이썬(python) 통찰]]
+5
View File
@@ -0,0 +1,5 @@
[[VPN으로 외부 DB에 접근]]
[[Pc끼리 Lan선으로 Direct 연결]]
[[RS-232와 이더넷 통신]]
+6
View File
@@ -0,0 +1,6 @@
[[리눅스(Linux)]]
[[리눅스의 파일시스템]]
[[유닉스(Unix)]]
[[유닉스 철학 (The Unix Philosophy)]]
+2
View File
@@ -0,0 +1,2 @@
### Server
[[어플리케이션(서비스) 실행 시 계정 구분하는 이유]]
@@ -0,0 +1,15 @@
1. [[단순함의 노하우(Introduction)]]
2. [[80대20 원칙(The Pareto Principle)]]
3. [[최소 기능 제품 (MVP)]]
4. [[코딩 원칙 (YAGNI, KISS, DRY)]]
5. [[유닉스 철학 (The Unix Philosophy)]]
6. [[설계와 추상화]]
7. [[디버깅과 테스트]]
8. [[협업과 지속 가능한 개발]]
+22
View File
@@ -0,0 +1,22 @@
---
id: "Aider 단축키 20260413"
created: "2026-04-13 13:35"
tags:
---
| **명령어** | **설명** | **비고** |
| --------------- | --------------------------------- | ----------------------------------- |
| **/undo** | **AI가 방금 수정한 코드를 취소하고 직전 상태로 복구** | 가장 자주 쓰임 |
| **/add <파일>** | 수정하거나 참고할 파일을 채팅창에 추가 | 파일명 일부만 쳐도 자동완성 |
| **/drop <파일>** | 채팅창에서 특정 파일 제외 | 실제 파일이 삭제되지는 않음 |
| **/ls** | 현재 채팅창에 포함된 파일 목록 확인 | |
| **/diff** | 마지막 수정 사항의 변경 전/후 차이점 표시 | |
| **/test <명령어>** | 테스트 명령을 실행하고 실패 시 AI가 자동 수정 | 예: `/test pytest` |
| **/model <모델>** | 사용 중인 AI 모델을 즉시 변경 | 예: `/model gemini/gemini-2.5-flash` |
| **/tokens** | 현재 세션의 토큰 사용량과 비용 정보 확인 | |
| **/run <명령어>** | 에이더를 나가지 않고 터미널 명령어 실행 | 예: `/run ls -al` |
| **/exit** | 에이더 종료 | `/quit`과 동일 |
| **/help** | 모든 명령어 목록과 상세 도움말 확인 | |
| **명령어** | **용도** | **특징** |
| **/ask <질문>** | 코드에 대해 질문하기 | 코드를 **직접 수정하지 않고** 설명만 해줍니다. |
| **/chat <메시지>** | 일반적인 대화/토론 | 파일 수정 없이 구현 방법이나 구조에 대해 논의할 때 씁니다. |
| **/code <명령>** | 코드 수정 요청 | 기본 모드입니다. 지시한 대로 파일을 직접 수정합니다. |
+76
View File
@@ -0,0 +1,76 @@
---
id: Aider 20260409
created: 2026-04-09 15:19
tags:
---
시작은 단순했다.
> [!question]
> Q. ai agent가 뭐야
>
> A. **AI 에이전트(AI Agent)**는 단순히 질문에 답하는 챗봇을 넘어, 사용자가 설정한 **목표(Goal)**를 달성하기 위해 스스로 계획을 세우고, 도구를 사용하며, 실행까지 완료하는 **자율적 소프트웨어**를 의미합니다.
난 이정도까지 바란건 아니었는데
> [!question]
> Q. 내가원하는건 너랑 대화하듯이 cli 같은 환경을 통해 대화하는데 ai가 내가 원하는 방향대로 실제 내 파일을 수정해주는거야 내가 복사 붙여넣기 해야하는게 아니고
>
> A. 지금 말씀하신 방식은 단순한 채팅을 넘어선 **코딩 에이전트(Coding Agent)** 혹은 **엔지니어링 에이전트**의 전형적인 모습이네요. 복사 붙여넣기 없이 AI가 직접 파일을 수정하고 실행하려면, AI에게 내 PC의 파일 시스템에 접근할 수 있는 **권한(Capability)**과 **도구(Tools)**가 있어야 합니다.
>
> 현재 이런 경험을 제공하는 대표적인 도구들과 작동 원리를 정리해 드릴게요.
## 1. 지금 바로 사용할 수 있는 대표적인 도구
### **Cursor (커서)**
가장 대중적이고 완성도가 높습니다. VS Code를 포크(Fork)해서 만든 에디터라 사용법이 익숙합니다.
- **작동 방식:** 채팅창에서 `Command + K``L`을 눌러 수정 요청을 하면, AI가 코드를 직접 작성하고 사용자가 **[Accept]** 버튼만 누르면 파일에 바로 반영됩니다.
- **장점:** 에디터와 일체화되어 있어 흐름이 매우 매끄럽습니다.
### **Aider (에이더)**
질문하신 **CLI 환경**에 가장 부합하는 강력한 도구입니다. 터미널(Terminal)에서 직접 실행합니다.
- **작동 방식:** 터미널에서 `aider`를 실행하고 대화하면, AI가 로컬 파일을 읽고 직접 수정(Edit)한 뒤 자동으로 **Git Commit**까지 남겨줍니다.
- **장점:** 복사 붙여넣기가 전혀 필요 없고, 터미널 기반이라 개발 환경에 통합하기 좋습니다.
### **Cline (구 Claude Dev)**
VS Code의 확장 프로그램(Extension)으로, 최근 가장 핫한 오픈소스 에이전트입니다.
- **작동 방식:** 파일 읽기/쓰기, 터미널 명령 실행, 브라우저 실행 권한을 AI에게 부여합니다. "이 프로젝트 빌드해보고 에러 나면 고쳐줘"라고 하면 스스로 파일을 수정하고 터미널에서 테스트까지 돌려봅니다.
## 추천하는 시작 방법
==만약 **CLI 환경**을 선호하신다면 **Aider**를 먼저 써보시는 걸 강력 추천합니다.==
Aider를 써보기로 함
이거 설치방법같은건 못적겠다.
변수도 너무 많고 내용도 너무 길고 그냥 ai한테 물어보면 알아서 잘 설명해주는데..
> [!warning] 파이썬은 3.12.10 을 설치하자
처음에 3.14가 깔려있었는데 에이더가 제대로 설치가 안되고 에러가 났었음.
Stable 버전인 3.13으로 다시 설치했는데 이것도 에러났음
3.12로 설치하니까 에러가 발생되지 않았음.
파이썬 버전은 3.12.13까지 있지만 3.12.10까지만 설치 installer가 제공되니까 3.12.10을 설치하자.
(이게 마음이 편할거 같음)
```cmd
C:\Users\Administrator\MyFolder\test\aider
```
이 경로에 설치했음 나는
```bash
Administrator@DESKTOP-UUOTCE2 MINGW64 ~/MyFolder/test/aider (master)
$ aider --version
aider 0.86.2
```
진짜 힘들게설치함;
+689
View File
@@ -0,0 +1,689 @@
---
id: "Aider 최초 사용 20260413"
created: "2026-04-13 14:01"
tags:
---
Gemini를 채찍질해서 아래의 명세를 가지는 VaultKeeper 어플리케이션 구상을 함.
( kui-veil 서버의 auth 관리 어플리케이션임 )
```markdown
## 🏗️ VaultKeeper 프로젝트 상세 설계도 (Ver 2.0)
### 1. 개요
- **목적**: Nginx의 `.htpasswd` 파일을 웹 GUI를 통해 관리하는 초경량 도구.
- **타겟 환경**: Google Cloud e2-micro (저사양 서버).
- **주요 언어**: **Go** (백엔드), **Vanilla JS** (프론트엔드).
### 2. 보안 전략 (핵심)
- **인증 분리**: 백엔드 자체에 로그인 로직을 구현하지 않고, Nginx의 `auth_basic` 모듈에 인증을 위임함.
- **접근 권한**: 서버 관리자가 터미널에서 직접 생성한 계정으로만 웹 UI에 접근 가능.
- **입력값 검증**: `os/exec` 실행 시 쉘 인젝션을 방지하기 위해 사용자 이름에 특수문자 입력을 제한함.
### 3. 구성 요소
- **Backend (`main.go`)**:
- 환경 변수 `HTPASSWD_PATH`를 통해 대상 파일 경로 지정.
- 표준 패키지 `net/http`를 사용하여 메모리 점유 최소화.
- `htpasswd` 시스템 명령어를 서브프로세스로 호출하여 계정 추가/삭제.
- **Frontend (`index.html`)**:
- 단일 파일 SPA 구조.
- Tailwind CSS(CDN)를 활용한 직관적인 대시보드.
```
```markdown
## 🤖 에이더(Aider)를 위한 최종 지시서
아래 내용을 에이더 채팅창에 입력하시면 됩니다.
**프로젝트: VaultKeeper (Nginx .htpasswd 관리 도구)**
**1. 백엔드 구현 (`main.go`)**
- **언어**: Go (표준 라이브러리 위주로 매우 가볍게).
- **기능**:
- `GET /api/users`: `.htpasswd` 파일을 읽어 유저 목록을 JSON으로 반환.
- `POST /api/users`: `username`, `password`를 받아 `htpasswd -b` 실행.
- `DELETE /api/users/{username}`: `htpasswd -D` 실행.
- **보안**: 사용자 이름 입력값에서 쉘 인젝션이 발생하지 않도록 유효성 검사 로직 포함.
- **설정**: `.htpasswd` 경로를 환경 변수 `HTPASSWD_PATH`에서 읽어오도록 설정.
**2. 프론트엔드 구현 (`index.html`)**
- 단일 파일 SPA로 작성하고 Tailwind CSS를 사용하여 깔끔한 UI 제공.
- 유저 목록 표시, 추가 폼, 삭제 버튼 구현.
**3. 배포 가이드 (`deployment.md`) 생성**
- `go build`를 이용한 경량 바이너리 빌드 방법 (`-s -w` 옵션 포함).
- 터미널에서 최초 관리자 계정을 생성하는 명령어 안내.
- 이 서비스를 Nginx `auth_basic` 뒤에 배치하기 위한 `location` 블록 설정 예시. (8000번 포트 기준)
**주의**: 모든 코드와 문서에서 특정 개인의 이름이나 개인정보를 절대 포함하지 말고 `Admin` 또는 `User`와 같은 공용 용어만 사용해줘.
```
이걸 aider에게 먹이고 (add)
```bash
────────────────────────────────────────────────────────────────────────────────
> /add "doc/vault-keeper 상세 설계도.txt" "doc/지시서.txt"
Added doc\vault-keeper 상세 설계도.txt to the chat
Added doc\지시서.txt to the chat
────────────────────────────────────────────────────────────────────────────────
```
진행해보라고 했다.
```bash
doc\vault-keeper 상세 설계도.txt doc\지시서.txt
> 내가 추가한 상세 설계도와 지시서를 바탕으로 어플리케이션을 만들어봐
```
과연.. 결과는?
어.. 뭔가 만들어지긴 했다..
아래는 콘솔에 찍힌 내용 전체
요청하신 내용에 따라 애플리케이션을 만들겠습니다. main.go, index.html, deployment.md 파일을 새로 생성합니다.
다음은 필요한 SEARCH/REPLACE 블록입니다:
main.go
<<<<<<< SEARCH
=======
package main
import (
"bufio"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"regexp"
"strings"
"sync"
)
// User는 .htpasswd 파일의 사용자 정보를 나타냅니다.
type User struct {
Username string `json:"username"`
}
// AddUserRequest는 새 사용자 추가 요청에 사용됩니다.
type AddUserRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
var (
htpasswdPath string
htpasswdMutex sync.Mutex // htpasswd 명령 실행 시 동시성 문제를 방지하기 위한 뮤텍스
usernameRegex *regexp.Regexp // 사용자 이름 유효성 검사를 위한 정규식
)
func init() {
// HTPASSWD_PATH 환경 변수 설정
htpasswdPath = os.Getenv("HTPASSWD_PATH")
if htpasswdPath == "" {
log.Fatal("환경 변수 HTPASSWD_PATH가 설정되지 않았습니다.")
}
// 사용자 이름 유효성 검사 정규식 초기화: 영문, 숫자, 하이픈, 언더스코어만 허용
usernameRegex = regexp.MustCompile("^[a-zA-Z0-9_-]+$")
}
func main() {
mux := http.NewServeMux()
// 정적 파일(index.html) 제공
mux.HandleFunc("/", serveFrontend)
// API 엔드포인트
mux.HandleFunc("/api/users", handleUsers)
mux.HandleFunc("/api/users/", handleDeleteUser) // DELETE 요청 처리
log.Println("VaultKeeper 서버가 8000 포트에서 시작됩니다.")
log.Printf("HTPASSWD_PATH: %s\n", htpasswdPath)
log.Fatal(http.ListenAndServe(":8000", mux))
}
// serveFrontend는 index.html 파일을 제공합니다.
func serveFrontend(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" || r.URL.Path == "/index.html" {
http.ServeFile(w, r, "index.html")
return
}
http.NotFound(w, r)
}
// handleUsers는 GET /api/users 및 POST /api/users 요청을 처리합니다.
func handleUsers(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
getUsers(w, r)
case http.MethodPost:
addUser(w, r)
default:
http.Error(w, "허용되지 않는 메소드입니다.", http.StatusMethodNotAllowed)
}
}
// getUsers는 .htpasswd 파일에서 사용자 목록을 읽어 JSON으로 반환합니다.
func getUsers(w http.ResponseWriter, r *http.Request) {
htpasswdMutex.Lock()
defer htpasswdMutex.Unlock()
file, err := os.Open(htpasswdPath)
if os.IsNotExist(err) {
// 파일이 없으면 빈 목록 반환 (처음 생성 시)
json.NewEncoder(w).Encode([]User{})
return
}
if err != nil {
log.Printf("Error opening .htpasswd file: %v", err)
http.Error(w, "사용자 파일을 읽을 수 없습니다.", http.StatusInternalServerError)
return
}
defer file.Close()
var users []User
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if len(line) > 0 && !strings.HasPrefix(line, "#") { // 주석 및 빈 줄 무시
parts := strings.SplitN(line, ":", 2)
if len(parts) > 0 {
users = append(users, User{Username: parts[0]})
}
}
}
if err := scanner.Err(); err != nil {
log.Printf("Error scanning .htpasswd file: %v", err)
http.Error(w, "사용자 파일을 스캔할 수 없습니다.", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
}
// addUser는 새 사용자를 .htpasswd 파일에 추가합니다.
func addUser(w http.ResponseWriter, r *http.Request) {
var req AddUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "유효하지 않은 요청 본문입니다.", http.StatusBadRequest)
return
}
// 사용자 이름 유효성 검사
if !usernameRegex.MatchString(req.Username) {
http.Error(w, "사용자 이름은 영문, 숫자, 하이픈, 언더스코어만 포함할 수 있습니다.", http.StatusBadRequest)
return
}
if req.Username == "" || req.Password == "" {
http.Error(w, "사용자 이름과 비밀번호는 필수입니다.", http.StatusBadRequest)
return
}
htpasswdMutex.Lock()
defer htpasswdMutex.Unlock()
// htpasswd -b <file> <username> <password>
// 파일이 없으면 -c 옵션이 필요하지만, 첫 생성은 터미널에서 하도록 가이드하므로 여기서는 -b만 사용.
// -b는 파일이 없으면 생성하고, 있으면 추가/수정합니다.
cmd := exec.Command("htpasswd", "-b", htpasswdPath, req.Username, req.Password)
output, err := cmd.CombinedOutput()
if err != nil {
log.Printf("Error executing htpasswd -b: %v, Output: %s", err, output)
http.Error(w, fmt.Sprintf("사용자 추가 실패: %s", strings.TrimSpace(string(output))), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{"message": "사용자가 성공적으로 추가되었습니다."})
}
// handleDeleteUser는 DELETE /api/users/{username} 요청을 처리합니다.
func handleDeleteUser(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodDelete {
http.Error(w, "허용되지 않는 메소드입니다.", http.StatusMethodNotAllowed)
return
}
username := strings.TrimPrefix(r.URL.Path, "/api/users/")
if username == "" {
http.Error(w, "사용자 이름을 지정해야 합니다.", http.StatusBadRequest)
return
}
// 사용자 이름 유효성 검사 (삭제도 동일하게 적용)
if !usernameRegex.MatchString(username) {
http.Error(w, "유효하지 않은 사용자 이름 형식입니다.", http.StatusBadRequest)
return
}
htpasswdMutex.Lock()
defer htpasswdMutex.Unlock()
// htpasswd -D <file> <username>
cmd := exec.Command("htpasswd", "-D", htpasswdPath, username)
output, err := cmd.CombinedOutput()
if err != nil {
// htpasswd -D는 사용자가 존재하지 않을 경우에도 에러를 반환합니다.
// 그러나 사용자 삭제 요청 자체는 성공했다고 간주할 수 있습니다 (해당 사용자가 없는 상태이므로).
// 실제 htpasswd의 에러 메시지를 확인하여 사용자 부재인지 다른 에러인지 구분할 수 있으나,
// 간단하게 사용자 부재 메시지가 아니라면 서버 에러로 처리합니다.
if strings.Contains(strings.ToLower(string(output)), "not found") {
http.Error(w, fmt.Sprintf("사용자 '%s'를 찾을 수 없습니다.", username), http.StatusNotFound)
return
}
log.Printf("Error executing htpasswd -D: %v, Output: %s", err, output)
http.Error(w, fmt.Sprintf("사용자 삭제 실패: %s", strings.TrimSpace(string(output))), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(map[string]string{"message": fmt.Sprintf("사용자 '%s'가 성공적으로 삭제되었습니다.", username)})
}
>>>>>>> REPLACE
index.html
<<<<<<< SEARCH
=======
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VaultKeeper - Nginx .htpasswd 관리</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<style>
body {
background-color: #f3f4f6;
}
</style>
</head>
<body class="font-sans antialiased text-gray-900">
<div class="min-h-screen flex flex-col items-center justify-center p-4">
<div class="w-full max-w-2xl bg-white rounded-lg shadow-xl p-8 space-y-8">
<h1 class="text-3xl font-bold text-center text-gray-800">VaultKeeper</h1>
<p class="text-center text-gray-600">Nginx .htpasswd 파일 관리</p>
<!-- 사용자 추가 폼 -->
<div class="border-t border-gray-200 pt-8">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">새 사용자 추가</h2>
<form id="addUserForm" class="space-y-4">
<div>
<label for="username" class="block text-sm font-medium text-gray-700">사용자 이름</label>
<input type="text" id="username" name="username" required
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none
focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-700">비밀번호</label>
<input type="password" id="password" name="password" required
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none
focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<button type="submit"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm
font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
사용자 추가
</button>
</form>
<div id="addMessage" class="mt-3 text-center font-medium"></div>
</div>
<!-- 사용자 목록 -->
<div class="border-t border-gray-200 pt-8">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">현재 사용자 목록</h2>
<div id="userListContainer">
<ul id="userList" class="divide-y divide-gray-200 bg-white rounded-md shadow-sm">
<!-- 사용자는 여기에 동적으로 로드됩니다. -->
</ul>
<p id="noUsersMessage" class="text-center text-gray-500 mt-4 hidden">등록된 사용자가 없습니다.</p>
</div>
<div id="listMessage" class="mt-3 text-center font-medium"></div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const addUserForm = document.getElementById('addUserForm');
const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password');
const addMessageDiv = document.getElementById('addMessage');
const userList = document.getElementById('userList');
const noUsersMessage = document.getElementById('noUsersMessage');
const listMessageDiv = document.getElementById('listMessage');
async function fetchUsers() {
listMessageDiv.textContent = '';
userList.innerHTML = '';
try {
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const users = await response.json();
if (users.length === 0) {
noUsersMessage.classList.remove('hidden');
} else {
noUsersMessage.classList.add('hidden');
users.forEach(user => {
const li = document.createElement('li');
li.className = 'flex items-center justify-between p-4 hover:bg-gray-50';
li.innerHTML = `
<span class="text-lg font-medium">${user.username}</span>
<button data-username="${user.username}"
class="delete-btn bg-red-500 hover:bg-red-600 text-white font-bold py-1 px-3 rounded text-sm">
삭제
</button>
`;
userList.appendChild(li);
});
addDeleteEventListeners();
}
} catch (error) {
console.error('Error fetching users:', error);
listMessageDiv.textContent = `사용자 목록을 불러오는 데 실패했습니다: ${error.message}`;
listMessageDiv.className = 'mt-3 text-center font-medium text-red-600';
}
}
async function addUser(event) {
event.preventDefault();
addMessageDiv.textContent = '';
const username = usernameInput.value;
const password = passwordInput.value;
try {
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.message || '사용자 추가 실패');
}
addMessageDiv.textContent = data.message;
addMessageDiv.className = 'mt-3 text-center font-medium text-green-600';
usernameInput.value = '';
passwordInput.value = '';
fetchUsers(); // 사용자 목록 새로 고침
} catch (error) {
console.error('Error adding user:', error);
addMessageDiv.textContent = `사용자 추가 실패: ${error.message}`;
addMessageDiv.className = 'mt-3 text-center font-medium text-red-600';
}
}
async function deleteUser(username) {
listMessageDiv.textContent = '';
if (!confirm(`사용자 '${username}'을(를) 정말 삭제하시겠습니까?`)) {
return;
}
try {
const response = await fetch(`/api/users/${username}`, {
method: 'DELETE',
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.message || '사용자 삭제 실패');
}
listMessageDiv.textContent = data.message;
listMessageDiv.className = 'mt-3 text-center font-medium text-green-600';
fetchUsers(); // 사용자 목록 새로 고침
} catch (error) {
console.error('Error deleting user:', error);
listMessageDiv.textContent = `사용자 삭제 실패: ${error.message}`;
listMessageDiv.className = 'mt-3 text-center font-medium text-red-600';
}
}
function addDeleteEventListeners() {
document.querySelectorAll('.delete-btn').forEach(button => {
button.onclick = () => deleteUser(button.dataset.username);
});
}
addUserForm.addEventListener('submit', addUser);
// 페이지 로드 시 사용자 목록 가져오기
fetchUsers();
});
</script>
</body>
</html>
>>>>>>> REPLACE
deployment.md
<<<<<<< SEARCH
=======
# VaultKeeper 배포 가이드
이 문서는 VaultKeeper 애플리케이션의 빌드, 초기 설정 및 Nginx 연동 방법을 안내합니다.
## 1. 백엔드 빌드
Go 바이너리를 가볍게 빌드하려면 다음 명령어를 사용합니다. 이 명령어는 디버그 정보를 제거하고 심볼 테이블을 생략하여 최종 바이너리
크기를 줄입니다.
```bash
go build -o vaultkeeper -ldflags "-s -w" main.go
빌드가 완료되면 현재 디렉토리에 vaultkeeper 실행 파일이 생성됩니다.
2. .htpasswd 파일 경로 설정
VaultKeeper는 .htpasswd 파일의 경로를 환경 변수 HTPASSWD_PATH를 통해 설정합니다. 애플리케이션을 실행하기 전에 이 환경 변수를 설정해야
합니다.
예시:
export HTPASSWD_PATH="/etc/nginx/.htpasswd"
또는
export HTPASSWD_PATH="/usr/local/etc/htpasswd"
주의: 지정된 경로에 Nginx가 실제로 사용하는 .htpasswd 파일이 있어야 합니다. 쓰기 권한도 필요합니다.
3. 최초 관리자 계정 생성 (터미널)
VaultKeeper는 자체 로그인 로직을 포함하지 않으며, Nginx auth_basic 모듈에 인증을 위임합니다. 웹 UI에 접근하려면 Nginx를 통해 인증할 수
있는 계정이 필요합니다. 최초 관리자 계정은 VaultKeeper가 실행되기 전에 터미널에서 htpasswd 명령어를 사용하여 생성해야 합니다.
.htpasswd 파일이 아직 없는 경우:
htpasswd -c /etc/nginx/.htpasswd adminuser
adminuser를 원하는 관리자 사용자 이름으로 바꾸세요. 이 명령어는 /etc/nginx/.htpasswd 파일을 생성하고 adminuser를 추가합니다.
.htpasswd 파일이 이미 있는 경우 (새 사용자를 추가하거나 기존 사용자의 비밀번호를 변경):
htpasswd /etc/nginx/.htpasswd anotheruser
-c 옵션을 사용하면 기존 파일이 덮어쓰여지므로 절대 이미 존재하는 파일에 -c 옵션을 다시 사용하지 마세요.
4. VaultKeeper 실행
환경 변수 설정 및 .htpasswd 파일 준비가 완료되면, 빌드된 VaultKeeper 바이너리를 실행합니다.
./vaultkeeper
기본적으로 VaultKeeper는 8000번 포트에서 수신 대기합니다.
5. Nginx 설정 예시
Nginx의 auth_basic 모듈 뒤에서 VaultKeeper를 실행하여 접근을 제한할 수 있습니다. 다음은 Nginx 설정 파일 (/etc/nginx/nginx.conf 또는
conf.d 내부 파일)에 추가할 수 있는 location 블록 예시입니다.
이 설정은 /vaultkeeper/ 경로로 들어오는 요청을 VaultKeeper 백엔드(8000번 포트)로 프록시하고, auth_basic을 통해 .htpasswd 파일로 인증을
요구합니다.
server {
listen 80;
server_name your_domain.com; # 도메인 또는 IP 주소로 변경
location /vaultkeeper/ {
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd; # .htpasswd 파일 경로를 정확히 지정
proxy_pass http://127.0.0.1:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
# 다른 Nginx 설정...
}
Nginx 설정을 변경한 후에는 반드시 Nginx를 재로드하거나 재시작하여 변경사항을 적용해야 합니다.
sudo nginx -t # 설정 파일 유효성 검사
sudo systemctl reload nginx # Nginx 재로드
이제 http://your_domain.com/vaultkeeper/ (또는 http://your_server_ip/vaultkeeper/)로 접근하여 VaultKeeper 웹 UI를 사용할 수 있습니다.
접근 시 Nginx가 사용자 이름과 비밀번호를 요청할 것입니다.
▌ ▌ ▌ ▌ ▌ ▌ ▌ REPLACE
새로 생성된 파일을 확인하고 싶으시다면, 다음 명령어를 실행하여 바이너리를 빌드하고 서버를 시작할 수 있습니다:
```bash
go build -o vaultkeeper -ldflags "-s -w" main.go
export HTPASSWD_PATH="/tmp/.htpasswd" # 테스트용 임시 경로, 실제 경로로 변경 필요
htpasswd -c /tmp/.htpasswd adminuser # 최초 관리자 계정 생성
./vaultkeeper
index.html을 브라우저에서 열거나, Nginx를 설정한 후 http://localhost:8000 (또는 Nginx 프록시 설정에 따른 URL)으로 접속하여 프론트엔드를
확인할 수 있습니다.
Tokens: 3.3k sent, 5.0k received. Cost: $0.01 message, $0.01 session.
main.go
Create new file? (Y)es/(N)o [Yes]: y
index.html
Create new file? (Y)es/(N)o [Yes]: y
deployment.md
Create new file? (Y)es/(N)o [Yes]: y
Applied edit to main.go
Applied edit to index.html
Applied edit to deployment.md
Commit bd414ad feat: 새로운 htpasswd 관리 애플리케이션 VaultKeeper 구현
go build -o vaultkeeper -ldflags "-s -w" main.go
export HTPASSWD_PATH="/tmp/.htpasswd" # 테스트용 임시 경로, 실제 경로로 변경 필요
htpasswd -c /tmp/.htpasswd adminuser # 최초 관리자 계정 생성
./vaultkeeper
Run shell commands? (Y)es/(N)o/(D)on't ask again [Yes]: y
Running go build -o vaultkeeper -ldflags "-s -w" main.go
Running export HTPASSWD_PATH="/tmp/.htpasswd" # 테스트용 임시 경로, 실제 경로로 변경 필요
'export'() Ǵ ܺ , ִ α׷, Ǵ
ġ ƴմϴ.
Running htpasswd -c /tmp/.htpasswd adminuser # 최초 관리자 계정 생성
'htpasswd'() Ǵ ܺ , ִ α׷, Ǵ
ġ ƴմϴ.
Running ./vaultkeeper
'.'() Ǵ ܺ , ִ α׷, Ǵ
ġ ƴմϴ.
Add command output to the chat? (Y)es/(N)o/(D)on't ask again [Yes]: y
Added 11 lines of output to the chat.
You can use /undo to undo and discard each aider commit.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.md doc\vault-keeper 상세 설계도.txt doc\지시서.txt index.html main.go
> /ls
Files in chat:
deployment.md
index.html
main.go
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.md doc\vault-keeper 상세 설계도.txt doc\지시서.txt index.html main.go
>
@@ -0,0 +1,69 @@
---
id: "Aider 프로젝트 생성 20260413"
created: "2026-04-13 13:31"
tags:
---
새로운 프로젝트를 만든다고 가정
만들고싶은 디렉토리에서
### 1단계: 프로젝트 폴더 생성 및 이동
먼저 프로젝트별로 방을 나눠야 합니다.
```bash
mkdir my-new-project # 원하는 프로젝트 이름으로 생성
cd my-new-project
```
### 2단계: Git 초기화 (매우 중요)
에이더는 작업 내용을 자동으로 기록하고 커밋하기 위해 **Git**을 기반으로 움직입니다. 폴더를 만들자마자 가장 먼저 해야 할 일입니다.
```bash
git init
```
- 이 과정을 거쳐야 나중에 에이더가 사고를 쳐도 `git checkout` 등으로 안전하게 코드를 복구할 수 있습니다.
### 3단계: API 키 및 기본 설정 확인
매번 키를 입력하기 귀찮다면, 아까 논의했던 대로 **환경 변수**나 **설정 파일**이 준비되어 있는지 확인합니다.
- 이미 윈도우 환경 변수에 `GEMINI_API_KEY`를 등록하셨다면 이 단계는 건너뛰셔도 됩니다.
- 만약 특정 모델을 고정하고 싶다면 이 폴더에 `.aider.conf.yml` 파일을 만드세요.
### 4단계: 에이더(Aider) 실행
이제 준비가 끝났습니다. 에이더를 깨웁니다.
```bash
aider
```
- 만약 별칭(alias)이나 설정 파일을 만들지 않았다면 직접 모델명을 적어줍니다: `aider --model gemini/gemini-1.5-pro`
- pro는 비싸니까 그냥 flash로 하는게 정신건강과 지갑건강상 좋음
### 5단계: 첫 번째 파일 생성 요청
에이더가 실행되면 AI에게 첫 작업을 시켜보세요.
- 예: `Hello! 파이썬으로 간단한 계산기 로직이 담긴 calculator.py 파일을 만들어줘.`
### 최종 결과물 구조
이렇게 순서대로 진행하면 폴더 안이 아래와 같이 예쁘게 정렬됩니다.
```cmd
workspaces/
└── my-new-project/
├── .git/ (2단계에서 생성)
├── .aider.chat.history.md (4단계 실행 후 생성)
├── .aider.tags.cache.v4/ (4단계 실행 후 생성)
└── calculator.py (5단계에서 생성됨)
```
**꿀팁:** 작업을 하다가 마음에 안 들면 에이더 채팅창 안에서 `/undo`를 입력하세요. 그러면 에이더가 방금 한 커밋을 취소하고 코드를 직전 상태로 돌려놓습니다.
@@ -0,0 +1,12 @@
As-Is
![[Pasted image 20260227093328.png]]https://wk.wecmms.com
https://innox.wecmms.com
### EC2기반의 아키텍처에는 다음의 한계들을 가짐
![[EC2기반 아키텍처의 한계점]]
### 한계를 극복하기 위해 나아가야 할 방향
[[서버리스(Serverless)]] 구성
: AWS가 제공해주는 [[파게이트(Fargate)]]로 구성한다.
@@ -0,0 +1,28 @@
---
id: "AWS ECS 구성하기 20260305"
created: "2026-03-05 13:29"
tags:
---
### ECS
![[ECS와 EKS#ECS(Elastic Container Service)]]
[[태스크(Task)]] 구성
1) 고객이 완벽한 물리적인 격리를 원할 경우: 3. ECS 클러스터 만들기부터 진행
2) 고객이 별도의 물리적인 격리를 원하지 않을 경우: 4. ECS 서비스 만들기부터 진행
## 🔢 목록
#### 1. [[ECS와 EKS]]
#### 2. [[ECS의 구성]]
#### 3. [[ECS 클러스터 만들기]]
#### 4. [[ECS 서비스 만들기]]
만약 [[ECR(Elastic Container Registry)]]에 등록된 컨테이너 이미지가 없다면?
이미지부터 만들어야 합니다. (도커에 대한 이해가 필요)
#### 5. [[테넌시(Tenancy)]]
- [[테넌시 구성 가이드]]
@@ -0,0 +1 @@
VPC
@@ -0,0 +1 @@
## 1.
@@ -0,0 +1,64 @@
---
id: 서비스 만들기 20260305
created: 2026-03-05 08:38
tags:
---
> [!abstract]
> [[클러스터(Cluster)]] 내부에서 동작하는 서비스를 만듭니다.
> ECS 서비스는 **"데이터 플레인 위에서 태스크가 잘 돌아가게 감시하는 역할"**을 합니다.
> [!check]
> - **개수 유지:** "태스크 3개를 항상 띄워줘"라고 설정하면, 하나가 죽었을 때 데이터 플레인(서버) 위에 새로운 태스크를 다시 올립니다.
>
> - **로드 밸런싱:** 들어오는 트래픽을 여러 태스크에 골고루 나눠줍니다.
>
> - **배포 관리:** 새로운 버전의 태스크 정의가 나오면, 기존 태스크를 하나씩 끄고 새 버전으로 교체합니다.
>
## 🔢 목록
#### 1. 서비스의 [[태스크(Task)]] 정의
![[Pasted image 20260305084812.png]]
만약 정의한 [[태스크(Task)]]가 없다면 태스크부터 정의해야 합니다.
[[ECS 태스크 만들기]]
태스크를 기반으로 ECS의 서비스가 가동되어집니다.
> [!question]
> [[태스크 정의 패밀리]]가 뭐지?
태스크 정의 패밀리를 선택하면 그 패밀리의 최신 버전이 자동으로 태스크 정의 개정에 등록됩니다.
서비스 이름을 마저 지정합니다.
#### 2. 컴퓨팅 구성 선택
![[Pasted image 20260305102304.png]]
클러스터명은 변경할 수 없어서 비활성화 되어있는 것
[[시작 유형(Launch Type)과 용량 공급자 전략(Capacity Provider Strategy)의 차이]]
참고해서 설정하면 됩니다.
#### 3. 문제 해결 구성 설정
> [!info] 개발(Dev)이나 스테이징(Staging) 환경에서는 무조건 켜두시고, 운영(Prod) 환경에서는 보안 승인을 받은 사람만 쓸 수 있게 [[IAM(Identity and Access Management)]]으로 꽉 잠가두고 켜두시는 것을 추천
![[Pasted image 20260305104853.png]]
#### 4. 배포 구성은 그대로 놔둡니다.
정확한 사용법을 확인하지 못했음.
#### 5. 네트워킹
[[VPC(Virtual Private Cloud)]]와 [[서브넷(Subnet)]], [[보안 그룹(Security Group)]]을 설정합니다.
필요한대로, 원하는대로 설정해줍니다.
#### 6. 로드밸런싱 선택
태스크로의 로드밸런싱이 필요한 경우 체크하고 사용합니다.
여기서 추가해주지 않으면 중간에 추가할 수 없어서 태스크를 새로 만들어야 합니다.
![[Pasted image 20260305110136.png]]
**자동 등록 (Target Group):** 새로운 태스크가 실행되면 ECS 서비스가 알아서 로드 밸런서의 **'대상 그룹(Target Group)'**에 해당 태스크의 IP를 등록해줍니다.**
로드 밸런싱 사용을 체크하지 않을 경우 자동 등록이 되지 않습니다.
서비스를 만들고 나면 서비스 태스크가 정상적으로 실행되기를 기다려야합니다.
![[Pasted image 20260305110258.png]]
만약 태스크 실행이 실패하였다면 태스크정보에서 로그 탭을 확인해야 합니다.
![[Pasted image 20260305111038.png]]
@@ -0,0 +1,24 @@
---
id: 클러스터 만들기 20260305
created: 2026-03-05 08:30
tags:
- cluster
- ecs
---
> [[!]]
## 🔢 목록
#### 1. [[클러스터(Cluster)]] 이름을 정한다.
![[Pasted image 20260305083155.png]]
#### 2. 인프라를 선택해준다.
![[Pasted image 20260305083442.png]]
> [!note]
> 진정한 [[서버리스(Serverless)]] 환경구축을 위해서는 [[파게이트(Fargate)]]전용으로 해준다.
>
#### 3. 나머지는 일단 그대로 둔다.
모니터링, 암호화, 태그는 우선 그대로 둡니다. (추후 필요할 경우 확인 후 설정 필요)
클러스터를 생성하고 서비스를 생성합니다.
[[ECS 서비스 만들기]]
@@ -0,0 +1,46 @@
---
id: ECS 태스크 vs k8s 파드 20260304
created: 2026-03-04 17:22
tags:
- ecs
- container
---
## 💡 생각
결국 태스크와 파드는 같다고 보면된다. 태스크는 ECS에서 쓰고 파드는 EKS에서 쓴다.
## 🤝 ECS 태스크 vs K8s 포드: 공통점
1. **컨테이너의 집합체:** 두 개념 모두 하나 이상의 컨테이너를 포함할 수 있습니다. (예: 메인 앱 + 로그 수집기)
2. **네트워크 공유:** 태스크/포드 내의 컨테이너들은 동일한 IP 주소를 공유하며 `localhost`를 통해 서로 통신합니다.
3. **스토리지 공유:** 태스크/포드 수준에서 정의된 볼륨을 내부 컨테이너들이 함께 마운트해서 쓸 수 있습니다.
4. **생명 주기:** 태스크나 포드가 죽으면 그 안의 컨테이너들도 운명을 같이 합니다.
## 🧐 굳이 차이점을 찾자면? (한 끗 차이)
개념은 같지만, 그것을 **관리하는 방식**에서 이름표가 달라집니다.
- **ECS ([[파게이트(Fargate)]] 기준):** AWS가 서버를 대신 관리해주기 때문에, 사용자가 "난 이만큼의 자원이 필요해"라고 **태스크 단위로 주문서**를 써야 합니다. 그래야 AWS가 그에 딱 맞는 가상 머신을 뒤에서 빌려줄 수 있으니까요.
- **[[쿠버네티스(Kubernetes)]]:** 보통 이미 떠 있는 **노드(Node/서버) 덩어리**가 있고, 파드는 그 안에서 자원을 조금씩 나눠 쓰는 '세입자' 같은 느낌입니다. 그래서 리소스 설정이 '실행 기준'이라기보다 '최대/최소 가이드라인'처럼 느껴질 수 있습니다.
### ECS 태스크 (Task)
ECS는 특히 **Fargate** 모드를 쓸 때, 태스크 전체가 사용할 CPU와 메모리를 **태스크 수준**에서 딱딱하게 결정해야 합니다.
- 예: "이 태스크는 무조건 0.5 vCPU와 1GB 메모리를 점유한다."
- 이 기준이 틀리면 아예 실행이 안 되거나 설정 오류가 납니다. 그래서 질문자님 말씀처럼 "태스크 = 리소스 기준이 명확히 박힌 단위"라는 인상이 강합니다.
### 쿠버네티스 파드 (Pod)
파드도 YAML 설정(Spec) 안에 각 컨테이너가 사용할 `requests`(최소 보장)와 `limits`(최대 제한)를 적습니다.
- 예: "이 컨테이너는 최소 256MB, 최대 512MB 메모리를 쓴다."
- **차이점:** 쿠버네티스는 여러 컨테이너의 합산 리소스를 보고 노드(서버)의 남는 자리에 파드를 '스케줄링'하는 데 집중합니다. ECS보다 설정이 좀 더 유연하고 복잡하게 느껴질 수 있습니다.
@@ -0,0 +1,63 @@
---
id: ECS 태스크 만들기 20260305
created: 2026-03-05 08:49
tags:
---
## 💡 생각
ECS 클러스터의 서비스가 [[태스크(Task)]]를 실행해서 서비스가 구동될 수 있습니다.
## 🔢 목록
#### 1. 태스크 정의하기
태스크는 Amazon **E**lastic **C**ontainer **S**ervice의 태스크 정의 탭에서 정의 가능합니다.
![[Pasted image 20260305085256.png]]
#### 2. [[태스크 정의 패밀리]] 이름 지정
![[Pasted image 20260305085241.png]]
#### 3. 인프라 요구 사항 지정
![[Pasted image 20260305091259.png]]
진정한 [[서버리스(Serverless)]] 환경 구축을 위해서는 AWS [[파게이트(Fargate)]]를 선택합니다.
물론 [[EC2(Elastic Compute Cloud)]]로 지정할수도 있습니다.
#### 4. OS, 아키텍처, 네트워크 모드 지정
[[ECS 태스크 정의]]
![[ECS 태스크 정의#📑 개념]]
![[Pasted image 20260305091749.png]]
여기서는 기본값으로 설정하고 진행했습니다.
#### 5. 태스크 역할 - 조건부 지정
[[태스크 역할과 태스크 실행 역할]]
![[Pasted image 20260305093459.png]]
> [!note] ecsTaskExecutionRole ?
> 사실상 default 역할인 **AmazonECSTaskExecutionRolePolicy**가 연결되어있음
![[Pasted image 20260305094500.png]]
작업 배치 - (선택 사항)
결함 주입 - 선택 사항
는 일단 지정하지 않았습니다. (아직 뭔지 모름)
#### 6. 컨테이너 생성 (1)
컨테이너의 이름과 도커 이미지의 위치를 지정합니다.
![[Pasted image 20260305094813.png]]
도커 이미지는 [[ECR(Elastic Container Registry)]]에 저장되어 있는 것을 가져다가 사용할 수 있고 (추천)
프라이빗 공간의 이미지를 가져다가 쓸 수도 있습니다 (만 추가적인 설정이 필요해 보입니다.)
#### 7. 컨테이너 생성(2)
컨테이너 포트정보를 입력합니다. [[파게이트(Fargate)]]의 경우 컨테이너 포트가 호스트 포트와 반드시 동일해야합니다. (별도로 맞춰줄 필요 없이 컨테이너 포트만 입력하면 알아서 맞춰집니다.)
![[Pasted image 20260305100339.png]]
읽기 전용 루트 파일 시스템
리소스 할당 제한 - 조건부
두가지는 일단 설정하지 않습니다. (아직 잘 모름)
#### 8. 환경 변수 설정
필요한 환경 변수가 있을 경우 추가해줍니다.
![[Pasted image 20260305100917.png]]
추가예시
![[Pasted image 20260305101005.png]]
#### 9. 로그 수집 설정
![[Pasted image 20260305101051.png]]
별도의 로그 수집 정책이 있을 경우 다른 것으로 지정 가능
@@ -0,0 +1,61 @@
---
id: ECS 태스크 정의 20260305
created: 2026-03-05 09:20
tags:
---
---
## 📑 개념
> [!abstract]
> **"실제로 어떤 하드웨어 환경에서 돌아갈지"**와 **"비용 청구서가 어떻게 나올지"**를 결정하는 핵심 데이터
## 1. 운영 체제 및 아키텍처 (OS & Architecture)
여기서 가장 중요한 건 **ARM64(AWS Graviton)**를 선택하느냐 아니냐입니다.
- **설정 내용:** `X86_64` (인텔/AMD 계열) vs `ARM64` (AWS Graviton 계열)
- **무엇이 달라지나:** * **호환성:** 내 도커 이미지를 어떤 CPU용으로 빌드했느냐에 따라 선택해야 합니다. (잘못 선택하면 실행 시 `exec format error`가 나며 죽습니다.)
- **성능:** ARM64(Graviton) 기반 인스턴스는 최신 세대 가상 머신을 사용하여 가성비가 매우 좋습니다.
- **💰 비용 차이:** **매우 중요합니다.** 일반적으로 **ARM64 아키텍처를 선택하면 x86보다 약 20% 정도 저렴**합니다. 똑같은 태스크를 돌려도 아키텍처 설정만 바꾸면 돈을 아낄 수 있습니다. (단, 이미지 빌드도 ARM용으로 해야 함)
---
## 2. 네트워크 모드 (Network Mode)
[[파게이트(Fargate)]]를 사용하신다면 사실상 `awsvpc` 모드로 고정되지만, [[EC2(Elastic Compute Cloud)]] 기반에서는 선택지가 나뉩니다.
- **awsvpc 모드 (Fargate 기본):** * **특징:** 각 태스크가 자신만의 **고유한 Private IP**를 가집니다. 마치 VPC 안에 EC2 인스턴스가 하나 더 들어온 것처럼 행동합니다.
- **장점:** 보안 그룹(Security Group)을 태스크 단위로 촘촘하게 걸 수 있습니다. (A 태스크는 DB 접근 가능, B 태스크는 불가 등)
- **Bridge / Host 모드 (EC2 전용):** * **특징:** 호스트 EC2의 IP와 포트를 공유해서 씁니다.
- **💰 비용 차이:** 네트워크 모드 자체로 돈을 더 받지는 않지만, `awsvpc` 모드 사용 시 발생하는 **데이터 전송 비용(Data Transfer Out)**이나 가용 영역(AZ) 간 통신 비용이 실질적인 비용 차이를 만듭니다.
---
## 3. 리소스 할당 (CPU & Memory)
[[파게이트(Fargate)]] 비용의 핵심입니다. "사용량만큼 낸다"는 말의 정확한 의미는 **"내가 설정한 CPU/메모리 사양만큼 예약해서 낸다"**입니다.
- **착각하기 쉬운 점:** 태스크가 실제로 CPU를 1%만 쓰고 있어도, 설정(예약)을 1 vCPU로 했다면 **1 vCPU만큼의 비용이 꼬박꼬박 나갑니다.** * **차이:** * **설정값:** 0.25 vCPU / 0.5 GB 로 설정하면 가장 저렴합니다.
- **비용 구조:** `(vCPU 가격 * 시간) + (GB당 메모리 가격 * 시간)`.
- 따라서 실제 사용량보다 너무 과하게 설정하면 서버리스인데도 돈이 많이 나올 수 있습니다.
## 📝 노트
> [!note]
> 무엇을 선택해야 유리할까?
| **설정 항목** | **추천 선택지** | **비용/효율 영향** |
| ----------- | ---------- | ------------------------------ |
| **아키텍처** | **ARM64** | **약 20% 저렴**하며 성능도 우수 (강력 추천) |
| **네트워크 모드** | **awsvpc** | 보안 관리가 쉬워지나, ENI 할당 등 관리 요소 발생 |
| **CPU/메모리** | **최소 필요량** | 설정한 수치에 비례해서 초 단위로 과금됨 |
---
+19
View File
@@ -0,0 +1,19 @@
### ECS(Elastic Container Service)
: AWS가 제공해주는 [[컨테이너 오케스트레이션]] 서비스입니다.
### EKS(Elastic kubernetes Service)
: [[쿠버네티스(Kubernetes)]]를 AWS의 다른 서비스들과 연동해서 사용할 수 있게 해주는 AWS 서비스입니다.
> [!note]
>
> - ECS와 EKS는 둘 다 [[컨테이너 오케스트레이션]]을 지원해주는 서비스입니다.
> - 컴퓨팅 자원으로 [[파게이트(Fargate)]]를 지정할 수 있습니다.
> - 표준화된 [[배포 파이프라인(Deployment Pipeline)]]을 사용할 수 있습니다.
ECS와 EKS의 핵심 목표는 ==‘컨테이너 기술의 안정적 운영’==임.
![[ECS와 EKS의 차이점]]
ECS는 AWS기반에서는 비교적 쉽고 빠르게 사용 가능 하지만 다른 환경으로의 이식이 어렵고,
EKS는 러닝커브가 가파르고 복잡도가 높지만 이식성이 매우 높음.
ECS와 EKS는 모두 [[클러스터(Cluster)]] 형태로 구현됨.
+32
View File
@@ -0,0 +1,32 @@
---
id: ECS의 구성 20260304
created: 2026-03-04 16:57
tags:
---
![[ECS와 EKS#ECS(Elastic Container Service)]]
### ECS는 [[클러스터(Cluster)]] 형태로 구성되어집니다.
![[Pasted image 20260304165810.png]]
#### [[클러스터(Cluster)]]
![[클러스터(Cluster)#📑 개념]]
### ECS Cluster는
> [!note]
> 하나의 서버에서 구동중인 [[쿠버네티스(Kubernetes)]] 엔진을 사용하는 것 같은 느낌을 주기위해서 만듭니다.
### Cluster에서 서비스가 동작됩니다.
![[Pasted image 20260304170925.png]]
### 서비스에는 [[태스크(Task)]]가 동작합니다.
![[Pasted image 20260304171641.png]]
> [!note]
> ECS의 태스크에는 태스크가 실행되기 위해 필요한 정보들, 어떤 컨테이너들을 실행해야하는지 등이 정의되어있음.
![[Pasted image 20260304171851.png]]
위와같이 태스크가 어떤 구성으로 동작해야하는지가 기록되어있고
![[Pasted image 20260304171939.png]]
어떤 컨테이너들이 실행되어야 하는지 기재되어있다.
## 💡 생각
즉, 태스크는 쿠버네티스에서 말하는 Pod를 실체화시킨 것
[[ECS 태스크 vs k8s 파드]]
+29
View File
@@ -0,0 +1,29 @@
## 📑 개념
> [!abstract]
> AWS에서 사용할 수 있는 [[컨테이너 오케스트레이션]] 엔진
> 클러스터 형태로 구현해놓았고 그걸 EKS라고 함.
## 📝 노트
> [!note]
>
>EKS Cluster의 구조는 크게 Control Plane과 Data Plane 둘로 나눠집니다.
> Control Plane은 전체적인 지휘소 역할을 하며 오케스트레이션에 필요한 모듈들이 위치하고, Data Plane은 실제 서비스가 돌아가는 자원들이 위치합니다.
>
> 이미지를 보면 5개의 Node가 보이는데 각 노드들을 컨테이너를 담고 있는 가상서버이며 그 위에 실제 서비스 단위인 컨테이너가 구동됩니다.
>
> 각 노드들에는 core-운, swing-cmms, load-balancer 컨테이너들이 구동되고 있습니다.
>
> 로드밸런서는 앞서봤던 Elastic Load Balancer를 가리키는 포인터 역할의 서비스이며 CoreDNS는 클러스터 내부의 통신을 위해 필요한 필수 서비스입니다
>
> Aws에 구현해놓은 cmms 서비스는 현재 이 다섯개의 컨테이너들로 구성되어서 실행되고 있습니다.
>
![[Pasted image 20260227162406.png]]
## EKS Cluster 기반의 아키텍처 구조도
![[Pasted image 20260227163046.png]]
## VPC 범위까지 확대
![[Pasted image 20260227163118.png]]
## 🔗 지식 연결
- **태그:** #cluster #container #kubernetes
- 관련 문서: [[ECS와 EKS의 차이점]] [[쿠버네티스(Kubernetes)]]
+22
View File
@@ -0,0 +1,22 @@
### CMMS의 SaaS 플랫폼화를 해야합니다.
##### 플랫폼화를 할 경우 장점
> [!check]
> - **운영 효율 극대화 및 비용 최적화**
>
> : 기존 EC2 기반의 1:1 관리 방식에서 벗어나 하나의 클러스터에서 여러 테넌트(고객사)를 관리함으로써 운영 공수를 획기적으로 줄입니다.
>
> - **시장 대응 속도(Time-to-Market) 가속화**
>
> : 새로운 고객사가 추가될 때마다 서버를 새로 구축할 필요가 없습니다. 미리 구성된 멀티 테넌시 환경과 커스텀 라우팅을 통해 클릭 몇 번만으로 신규 고객 전용 환경을 즉시 제공(On-boarding)할 수 있어, 비즈니스 기회를 놓치지 않고 빠르게 시장을 확대할 수 있습니다.
>
> - **서비스 안정성 및 보안성 고도화**
>
> : 파드 자동 복구기능을 통해 장애 발생 상황 대응에 인력의 투입이 필요 없어지도록 해줍니다. (관리 포인트가 줄어듦)
> [!note]
>
> - CMMS의 경우 SaaS 플랫폼화가 이미 진행되어있는 상태
> - Enterprize 요금제를 요구하는 고객을 위한 '설치형 구독형' 모델 구축을 위한 Cloud Architecture 화를 진행한 것임
> - 기본형의 SaaS CMMS는 이미 존재함
@@ -0,0 +1,8 @@
## [[EC2(Elastic Compute Cloud)]]
![[EC2(Elastic Compute Cloud)#📑 개념]]
## [[컨테이너(Container)]]
![[컨테이너(Container)#📑 개념]]
## [[파게이트(Fargate)]]
![[파게이트(Fargate)#📑 개념]]
@@ -0,0 +1,33 @@
---
id: 시작 유형(Launch Type)과 용량 공급자 전략(Capacity Provider Strategy)의 차이 20260305
created: 2026-03-05 10:24
tags:
- ecs
- fargate
---
## 💡 생각
그냥 용량 공급자 전략을 써야한다고만 기억하면 됨. (기본값도 용량 공급자 전략으로 되어있음)
> [!note]
> ECS 서비스에서 **시작 유형(Launch Type)**과 **용량 공급자 전략(Capacity Provider Strategy)**은 "내 컨테이너를 어디에 띄울 것인가"를 결정하는 두 가지 방식입니다.
>
> 과거에는 '시작 유형'만 있었지만, 현재는 더 유연한 관리를 위해 '용량 공급자 전략'을 권장하는 추세입니다. 두 방식의 핵심적인 차이를 정리해 드릴게요.
## 1. 시작 유형 (Launch Type) : "단순한 선택"
가장 직관적인 방식입니다. 태스크를 띄울 인프라를 **딱 하나만** 고정해서 선택하는 것입니다.
- **방식:** "나는 무조건 **Fargate**만 쓸 거야" 혹은 "무조건 **EC2**만 쓸 거야"라고 선언합니다.
- **특징:** 설정이 매우 간단하지만, 여러 종류의 인프라를 섞어서 쓰는 것이 불가능합니다.
- **비유:** 식당에 가서 "무조건 소고기 메뉴만 주세요"라고 고정 주문을 하는 것과 같습니다.
## 2. [[용량 공급자 전략 (Capacity Provider Strategy)]] : "유연한 배분"
> [!note] 하나 이상의 인프라(용량 공급자)를 **어떤 비율로 섞어서 쓸지** 결정하는 고도화된 방식입니다.
![[Pasted image 20260305103056.png]]
![[용량 공급자 전략 (Capacity Provider Strategy)#💡 왜 '용량 공급자 전략'을 써야 할까요? (핵심 이유)]]
@@ -0,0 +1,11 @@
순서
## 1. [[VPC(Virtual Private Cloud)]] 생성
## 2. VPC에 [[서브넷(Subnet)]] 생성
: 필요에 따라 퍼블릭,프라이빗 서브넷을 생성하면 됨.
[[Pod를 Private subnet에 두는 이유]]
## 3. 라우팅 테이블 생성
## 4. 인터넷 게이트웨이 생성
## 5. EC2 생성
@@ -0,0 +1,29 @@
To-Be
![[Pasted image 20260227094154.png]]
EC2기반의 아키텍처를 클라우드 컴퓨팅 환경에 맞게 변경한 아키텍처입니다.
다른 부분은 모두 동일하고 [[파게이트(Fargate)]]라고 적혀있는 이 부분만 달라진 것을 확인할 수 있습니다.
[[파게이트(Fargate)의 장점]]
컨테이너 기반의 아키텍처로 변경했을떄의 장점
[[가용성(Availability)]] [[보안성(Security)]]이 확보되고 프로젝트의 [[생산성(Productivity)]]이 증가되고, 프로젝트의 [[유연성(Flexibility)]]과 [[확장성(Scalability)]]확보에 유리해진다.
각각을 현재 상황에맞게 풀어서 설명해보면
[[가용성(Availability)]]: 초기설정을 잘 해 놓으면 [[컨테이너 오케스트레이션]]에 의해 서비스의 자가복구가 가능하다.
[[유연성(Flexibility)]]: 로드밸런서가 컨테이너 단위로의 접근이 가능해서 동일한 내부 포트(예: 80)를 사용하는 여러 서비스를 띄워도, 각 컨테이너가 고유 IP를 가지므로 충돌 없이 동적 라우팅이 가능하다.
[[보안성(Security)]]: [[파게이트(Fargate)]] 이용 시 OS 업데이트, 보안패치 등의 OS/인프라 레벨의 보안을 AWS가 책임져준다.
[[생산성(Productivity)]]: 서버관리 포인트가 줄어들어서 인프라 관리 등의 인프라 운영에 들어가는 반복적인 공수(Heavy Lifting)를 제거하여, 비즈니스 로직 개발 및 서비스 고도화에 인력을 집중 투입할 수 있다.
## 📝 노트
> [!note]
EC2는 가상 컴퓨팅 환경이기 때문에 장애 복구에 많은 시간이 필요하지만 fargate는 컨테이너이므로 수초내에 복구할 수 있습니다.
>
> 컨테이너 자체는 자가복구의 기능이 없지만 뒤에서 설명드릴 ECS나 EKS에서는 가능합니다.
>
> 그리고 로드밸런서가 EC2같은 서버가 아닌 컨테이너를 가리킬 수 있습니다. 그로인해 자주 사용되는 80포트등의 포트들이 중복사용으로 충돌을 일으킬 가능성이 없습니다.
>
> A컨테이너의 80포트, B컨테이너의 80포트 이런식으로 상세하게 설정할 수 있기 떄문입니다.
>
> 그리고 AWS가 보안과 서버인프라 문제를 모두 책임져주기 때문에 생산성 향상이 될 수 있습니다.
@@ -0,0 +1,18 @@
---
id: "태스크 역할과 태스크 실행 역할 20260305"
created: "2026-03-05 09:38"
tags:
---
[[태스크 역할(Task Role)]] vs [[태스크 실행 역할(Task Execution Role)]]
|**구분**|**태스크 실행 역할 (Execution Role)**|**태스크 역할 (Task Role)**|
|---|---|---|
|**누가 쓰는가?**|**ECS 에이전트 / 인프라**|**내 앱 소스 코드 (Container)**|
|**언제 쓰는가?**|태스크를 **준비하고 띄울 때**|태스크가 **실행 중일 때**|
|**필수 여부**|대부분 필수 (이미지 풀, 로그 때문)|선택 (AWS 서비스를 안 쓰면 필요 없음)|
|**예시 정책**|`AmazonECSTaskExecutionRolePolicy`|`AmazonS3FullAccess`, `AmazonDynamoDBReadOnly` 등|
### 💡 실무에서는 어떻게 설정하나용?
1. **실행 역할(Execution Role):** AWS에서 기본으로 제공하는 `AmazonECSTaskExecutionRolePolicy`를 연결해 주는 것이 국룰입니다. (이거 안 하면 로그가 안 남거나 이미지를 못 가져와서 태스크가 무한 재시작됩니다.)
2. **태스크 역할(Task Role):** 기본값은 비어있습니다. 내 앱이 S3나 DynamoDB를 써야 할 일이 생길 때만 필요한 권한을 콕 집어서 추가해 주면 됩니다.
@@ -0,0 +1,47 @@
---
id: 테넌시 선택 20260305
created: 2026-03-05 13:18
tags:
- tenancy
- cluster
- ecs
- ecr
- saas
---
[[싱글 테넌시(Single-tenancy)]] vs [[멀티 테넌시(Multi-tenancy)]]
: 결정적인 차이는 **물리적으로 격리되어있는지** 여부
### [[멀티 테넌시(Multi-tenancy)]]
멀티 테넌시를 선택할 때 가장 중요한 것은 **인프라를 공유한다**는 것입니다.
순수 멀티 테넌시는 하나의 어플리케이션 인스턴스와 하나의 DB를 여러 고객들이 공유해서 사용합니다.
> [!note] 설정값, 환경변수 등의 차이만으로 고객간의 구분을 하고 모든 인프라는 하나를 공유해서 사용
고객간의 요구사항 편차가 적을 경우에는 효율이 좋음
편차가 클 경우 대응이 매우 어렵거나 거의 불가능에 가깝습니다. (고객 수가 적으면 되긴 함)
**SaaS 솔루션:** 완전한 멀티 테넌시 (공통 기능 + DB 설정 기반 커스텀)
즉 SaaS 솔루션은 모든 고객들이 하나의 인프라를 공유해서 사용하는 경우를 의미합니다.
> [!warning] 이렇게 되면 고객간의 요구사항 편차가 클 경우 대응이 불가능함
이 문제를 해결하기 위해 하이브리드 방식을 채택합니다.
**"인프라는 공유하되 애플리케이션은 격리하는 방식"** 같은 경우입니다.
이를 **가상 싱글 테넌시(Virtual Single Tenancy)** 또는 **격리형 멀티 테넌시**라고 부르기도 합니다.
격리형 테넌시 형태로 엔터프라이즈 솔루션 형태로 구성을 해야함.
**엔터프라이즈 솔루션:** 고객별 컨테이너/네임스페이스 분리 + 이미지 커스텀
테넌시별로 별도의 컨테이너 이미지를 확보해야함
코드레벨에서 독립되어지기 때문에 클라우드형 싱글테넌시 구조와 유사한 형태의 서비스가 가능
### 엔터프라이즈 솔루션의 구조도
(고객사별 독립된 어플리케이션 인스턴스 사용 + 하나의 DB 인프라 사용)
![[Pasted image 20260305140832.png]]
VPC레벨까지도 격리를 원하는 고객사의 경우에는 위의 이미지대로 별도의 ECS Cluster를 구성
그정도까지 아니면 하나의 공용 ECS Cluster에 [[태스크(Task)]]만 별도로 추가
## 🔗 관련 노트
- [[클러스터(Cluster)]]
- [[ECR(Elastic Container Registry)]]
@@ -0,0 +1,35 @@
---
id: "마력과 토크, RPM 20260504"
created: "2026-05-04 14:12"
tags:
---
[[마력]]은 엔진이 일정시간동안 얼마나 많은 일을 할 수 있는지에 대한 지표임.
즉 마력이 높다 = 엔진이 많은 일을 할 수 있다(혹은 했다) 가 된다.
'근데 엔진이 많은 일을 했다'는 이 엔진이 장착된 차량의 속도가 빠르다는 말과 같다고 볼 순 없다.
일을 많이 할 수 있는 건 맞지만 엔진이 일을 많이 했다고 해서 반드시 차량의 속도가 빠르다고 볼 순 없기 때문
슈퍼카나 스포츠카들의 경우 엔진의 마력이 높은걸로 표시가 되어있는 이것만 보고 '아, 마력이 높으면 차가 빠른거구나?' 라고 하면 안된단 거임.
마력의 정의에서 적혀있듯 마력은 엔진이 할 수 있는 일의 양을 의미함.
이 일을 차량의 가속에 최대한 많이 사용하면 차가 빠르게 달리는거고
일을 차량이 큰 힘을 내는데 사용하면 차가 빠른건 아니지만 힘이 좋아서 무거운걸 나른다던가 할 수 있는 것임.
즉, **마력은 차량의 속도와 직접적인 관계가 있는 것은 아니다.**
차가 빠르게 달리기 위해서는 현재 속도에서 가속이 되기 위한 최적의 기어를 물리고 힘을 주는 것임.
최적의 기어를 선택해주는게 바로 [[변속기(Transmission)]]의 역할이다.
엔진이 낼 수 있는 힘은 정해져 있는데 이 정해진 힘을 적절하게 사용해서 자동차를 효율적으로 움직일 수 있도록 한 것.
엔진이 회전하면서 힘을내는 과정([[연료가 연소되면서 회전력이 발생되는 원리]]) 에서 필연적으로 엔진은 빠르게 회전하게 되는데 이 때 회전속도를 [[RPM]]이라는 수치로 표현한다.
엔진에는 회전할 수 있는 회전속도의 한계가 있는데 이걸 [[최대 RPM]]이라고 표현한다.
마력이나 토크가 높으려면 필연적으로 엔진이 빠르게 회전해야 한다.
[[출력을 높이는 방법]] 참고
스포츠카나 스포츠드라이빙의 경우 자세히보면 굉장히 상당히 고RPM (4,000~7,000RPM 사이)에서 주행하는것을 볼 수 있는데 여기서 착각하면 안되는게 차량이 큰 토크를 내거나(힘이 쌔거나) 속도가 빠르기 위해서 RPM이 높아야한다. 이건 잘못된 생각이다.
RPM이 높아서 힘이 쌘게 아니고 큰 힘을 내려고 엔진이 빠르게 회전했고 그로인해 RPM이 높은것임.
엔진이 큰 힘을 내거나 빠른 속도를 내기 위해서는 엔진의 회전보다 [[기어비(Gear Ratio)]]가 더 중요함.
상황에 맞는 기어를 물리고 엔진이 강하게 일을해서 큰 힘을 내는 것이고 그럴려다보니 RPM이 올라간 것 뿐임.
큰 토크(강한 힘)를 내기 위해서는 구동측 기어는 작은 것(저단 기어)를 쓰고 힘을 주는것이 좋음.
@@ -0,0 +1,50 @@
---
id: 80대20 원칙(The Pareto Principle) 20260318
created: 2026-03-18 10:08
tags:
- 클린코드
aliases:
---
## 💡 생각
코드를 단순화하는 것은 어려운 일이기 때문에 모든 코드를 단순화하는 것은 엄청난 낭비가 될 수 있다.
그렇기 때문에 전체 가치의 80%를 창출해내는 20%의 자주 쓰이는 코드를 단순화하는 것에 집중해야 한다.
## 🔗 관련 노트
- [[단순한 코드]], [[파레토의 법칙]](80:20의 법칙)
---
## 저자가 80:20의 법칙을 언급하는 이유
### 1. 노력 대 결과의 불균형
프로그래밍에서 80%의 가치는 전체 노력의 20%만 들여도 달성할 수 있다는 점을 강조합니다.
- **핵심 기능 우선:** 사용자에게 진짜 필요한 핵심 기능(20%)을 먼저 완벽하게 만드는 것이, 나머지 잔기능(80%)에 매달리는 것보다 훨씬 가치 있습니다.
- **복잡성의 함정:** 나머지 20%의 가치(예외 케이스, 아주 희귀한 최적화 등)를 채우기 위해 전체 노력의 80%를 쏟아붓는 순간, 코드는 급격히 복잡해지고 유지보수가 불가능해집니다.
### 2. '충분히 좋은(Good Enough)' 코드
저자는 **완벽함은 단순함의 적**이라고 말합니다.
- [[파레토의 법칙]]에 따르면, 100% 완벽한 코드를 만들려는 노력은 비효율적입니다.
- 대신 **80%의 성능과 안정성**을 보장하는 [[단순한 코드]]를 빠르게 작성하고, 실제 문제가 발생하는 지점만 나중에 정밀하게 타격(최적화)하는 것이 훨씬 똑똑한 전략입니다.
### 3. 기능 구현에서의 80/20
새로운 기능을 추가할 때도 이 법칙이 적용됩니다.
- [[YAGNI(You Ain't Gonna Need It)]]와의 연결: "혹시 필요할지 모르는" 기능들을 다 넣으려 하지 마세요. 실제 사용자는 제공된 기능의 20%만 주로 사용합니다.
- 그 20%의 기능을 **가장 단순하고 견고하게** 만드는 데 집중하는 것이 이 장의 핵심 레슨입니다.
---
> [!note] 왜 자꾸 20%의 코드에 집중해야한다고 강조하느냐면 단순한 코드를 작성하는 것은 쉬운일이 아니기 떄문입니다.
- "모든 곳을 단순하게 만들려고 애쓰는 것 자체가 복잡함의 시작이다. 진짜 중요한 20%만 단순하게 유지하고 나머지는 최소한의 노력만 들여라."
- "단순함은 기술(Skill)이고, 파레토 법칙은 그 기술을 사용할 전략(Strategy)이다. 전략 없는 기술은 개발자를 지치게 만든다."
- "단순함은 복잡함보다 어렵다. 생각을 명확히 해서 단순하게 만들려면 정말 열심히 노력해야 한다." - 스티브잡스
@@ -0,0 +1,54 @@
---
id: 단순함의 노하우 20260317
created: 2026-03-17 10:53
tags:
- 클린코드
---
## 💡 생각
> [!note] 코드는 무조건 단순해야 한다. 단순한 코드란 읽기 쉽고 명확한(가독성이 좋은) 코드다.
[[단순한 코드]] ?
[[파레토의 법칙]]을 프로그래밍에 응용
##### - 모든 코드를 100%의 노력으로 완벽하고 좋은 성능으로 만들려고 하면 많은 리소스 (인력이든 시간이든 뭐든..)를 필요로 하게 됨.
프로그램 실행 시간의 80%는 단 20%의 코드(핵심 알고리즘, 반복문 등)에서 소비됩니다. 따라서 나머지 80%의 코드는 성능을 위해 복잡하게 짤 필요 없이, **최대한 단순하고 읽기 쉽게** 유지하는 것이 이득입니다.
##### - 80% 의 자주 쓰지 않는 코드에 집중하고 자원을 낭비하지 말라.
훌륭한 구조를 가지고 좋은 성능을 보이는 코드라고 하더라도 **자주 쓰이지 않고** **코드 작성에 너무 많은 시간을 들였다면** 그것은 과도한 낭비가 될 가능성이 높다.
[[YAGNI(You Ain't Gonna Need It)]]
- 지금 필요 없는 건 하지 마라.
### 선택과 집중이 필요하다.
- **불필요한 최적화 거부:** 병목 지점이 아닌 나머지 80%의 코드를 최적화하느라 코드를 꼬아놓지 마세요. 그 부분은 그냥 **단순함** 그 자체로 두는 것이 유지보수에 훨씬 유리합니다.
- **복잡성 격리:** 정말 성능이 중요해서 복잡한 로직이 들어가야 한다면, 그 20%의 영역만 따로 분리(격리)하고 나머지는 깨끗하게 유지하세요.
> [!note] "80%의 효과를 내는 20%의 단순한 로직을 먼저 작성하라."
### 결국 저자가 하고 싶은 말은
1. 불필요한 단계를 최대한 제거하고
2. 사람이 읽기 좋은 형태로 바꿔주고
3. 알고리즘 최적화하는것
저자인 크리스티안 마이어가 경계하라고 강조하는 것
> [!warning] "0.001초를 줄이려고 코드 10줄을 50줄로 늘리며 가독성을 해치는 행위"
이런 경우는 최적화가 단순화를 **방해**하는 상황이 됩니다. 반대로 **복잡한 계산식을 수학적으로 정리해 한 줄로 줄이는 것**은 최적화이자 동시에 완벽한 단순화가 되는 것이죠.
## 사람이 읽기 좋은 코드...? [[코드의 가독성]]
> [!question]
> Q. 사람이 읽기 좋은 형태의 코드라는건 어떤걸 의미하는거야?
> A. 사람이 읽기 좋은 코드, 즉 **가독성이 높은 코드**는 단순히 "예쁜 코드"를 넘어 **코드를 읽는 사람의 뇌가 에너지를 최소한으로 쓰게 만드는 코드**를 의미합니다.
## 그리고 데이터가 늘어나도 잘 버티는 코드
데이터가 늘어나도 잘 버티는 코드는 보통 **확장성(Scalability)** 이 좋은 코드라고 부릅니다.
데이터가 10건일 때는 0.001초 만에 끝나던 로직이, 데이터가 100만 건으로 늘어났을 때도 서비스가 죽지 않고 합리적인 시간 내에 결과를 내놓는 것을 의미합니다.
> [!question]
> Q. 일단은 단순하게 만들고 나중에 문제가 생길경우에 집중해서 개선하라고 앞에서 계속 그러다가 갑자기 업스케일링을 염두해둬야한다는 말을 하면 앞뒤가 잘 안맞는거 아니야? 업스케일링에 유리한 코드를 미리 설계하고 개발하는거 자체도 일일텐데?
> A. 정말 날카롭고 합리적인 지적입니다! "일단 단순하게 만들라"는 말과 "나중에 커질 것을 대비(확장성/업스케일링)하라"는 말이 얼핏 들으면 서로 충돌하는 것처럼 느껴질 수 있습니다.
>
> 하지만 이 책과 클린 코드의 철학이 말하는 핵심은 [[유연한 단순함]]에 있습니다.
"지금 당장 필요하지 않은 기능(Over-engineering)은 넣지 않되, 나중에 그 기능을 넣고 싶을 때 코드 전체를 부수지 않아도 되게 끔 '문(Interface/Module)'만 잘 만들어 두는 것"
@@ -0,0 +1,74 @@
---
id: 유닉스 철학 (The Unix Philosophy) 20260330
created: 2026-03-30 15:20
tags:
---
# 개념
[[유닉스(Unix)]] 개발자들 사이에서 내려오는 유명한 원칙이 있습니다. 바로 **작고 단순한 것이 아름답다**는 점입니다.
1. **각 프로그램은 한 가지 일만 잘해야 한다.**
2. **여러 프로그램을 조합해서 복잡한 문제를 해결한다.** (이때 사용하는 것이 **파이프(|)** 기능입니다.)
3. **모든 것은 파일이다.** (텍스트 파일로 설정을 관리하고 데이터를 주고받습니다.)
> [!note] 단순히 운영체제를 만드는 기술적 규칙이 아니라, 소프트웨어를 설계하고 문제를 해결하는 일종의 **미니멀리즘 예술**에 가깝습니다.
: 1970년대 벨 연구소의 더글러스 매킬로이(Douglas McIlroy)가 정립한 이 철학의 핵심은 **작고 단순하며, 서로 협력하는 도구**를 만드는 것입니다.
## 1. 핵심 3원칙
더글러스 매킬로이는 유닉스 철학을 다음 세 문장으로 요약했습니다.
- **한 가지만 하되, 아주 잘하라 (Do One Thing and Do It Well):** 하나의 프로그램은 오직 하나의 기능에만 집중해야 합니다. 기능이 많아지면 복잡해지고 버그가 생기기 쉽기 때문입니다.
- **함께 작동하도록 만들어라 (Expect the output of every program to become the input to another):** 프로그램은 서로 연결될 것을 예상하고 만들어야 합니다.
- **텍스트 스트림을 표준 인터페이스로 사용하라:** 데이터는 가공하기 가장 쉬운 **텍스트** 형태로 주고받아야 합니다. 그래야 서로 다른 프로그램끼리 쉽게 대화할 수 있습니다.
## 2. 파이프(Pipe)와 필터
유닉스 철학을 시각적으로 가장 잘 보여주는 것이 바로 **파이프(|)** 기호입니다. 작은 도구들을 파이프로 연결하여 복잡한 일을 수행하는 방식이죠.
> **예시:** `ls | grep "test" | sort`
>
> 1. `ls`: 파일 목록을 출력한다. (한 가지 일)
>
> 2. `grep`: 그중 "test"가 포함된 것만 골라낸다. (한 가지 일)
>
> 3. `sort`: 결과를 알파벳 순으로 정렬한다. (한 가지 일)
>
각각은 단순한 도구일 뿐이지만, 연결하면 강력한 기능을 수행합니다. 이는 마치 레고 블록을 조립하는 것과 비슷합니다.
## 3. 에릭 레이먼드의 룰 (Rule of...)
오픈소스 전도사인 에릭 레이먼드는 그의 저서에서 유닉스 철학을 더 구체적인 규칙으로 확장했습니다.
- **단순함의 법칙 (Rule of Simplicity):** 복잡해지기 전까지 최대한 단순하게 설계하라.
- **명확함의 법칙 (Rule of Clarity):** 코드는 컴퓨터가 읽기 쉬운 것보다 사람이 읽기 쉬운 것이 더 중요하다.
위의 두 법칙을 지킴으로써 코드가 [[KISS (Keep It Simple, Stupid)]]해진다.
- **조합의 법칙 (Rule of Composition):** 프로그램이 다른 프로그램과 쉽게 연결될 수 있도록 설계하라.
조합의 법칙을 지키다 보면 코드가 [[DRY (Don't Repeat Yourself)]]해진다.
- **침묵의 법칙 (Rule of Silence):** 프로그램이 정말로 할 말이 없을 때는 아무것도 출력하지 마라. (그래야 다른 프로그램이 출력을 가공하기 좋습니다.)
유닉스 철학을 구체화 한 규칙들을 지킴으로써 [[클린코드의 기술]]들을 지키게 된다.
---
## 4. 왜 유닉스 철학이 중요한가요?
오늘날 현대적인 소프트웨어 개발 방법론인 **마이크로서비스 아키텍처(MSA)** 나 [[함수형 프로그래밍(Functional Programming)]]의 뿌리도 이 유닉스 철학에 닿아 있습니다.
- **유지보수 용이:** 작고 단순한 코드는 고치기 쉽습니다.
- **재사용성:** 잘 만들어진 작은 도구는 여기저기서 다시 쓰일 수 있습니다.
- **확장성:** 새로운 기능을 추가할 때 기존 프로그램을 수정하는 대신, 새로운 도구를 만들어 연결하면 됩니다.
@@ -0,0 +1,69 @@
---
id: 최소 기능 제품 (MVP) 20260320
created: 2026-03-20 14:00
tags:
---
**M**inimum **V**iable **P**roduct , 최소 기능 제품
![[MVP(Minimum Viable Product)#📑 개념]]
### MVP의 핵심: "완벽함보다 실행"
저자는 개발자가 빠지기 쉬운 가장 큰 함정이 **모든 기능을 다 갖춰야 출시할 수 있다**는 생각이라고 지적합니다.
- **MVP의 정의:** 고객에게 가치를 전달할 수 있는 **최소한의 핵심 기능**만 담은 제품입니다.
- **개발자 버전 MVP:** 복잡한 아키텍처나 부가 기능(로깅, 화려한 UI, 상세 설정 등)을 다 붙이기 전에, **비즈니스 로직의 핵심**이 돌아가는 가장 단순한 형태의 코드를 먼저 완성하는 것입니다.
### 왜 MVP가 클린 코드와 연결될까요?
단순히 빨리 만드는 게 목적이 아닙니다. MVP 방식으로 개발하면 다음과 같은 이점이 있습니다.
- **복잡성 제어:** 처음부터 거대한 시스템을 설계하면 복잡도가 기하급수적으로 늘어납니다. 작은 단위(MVP)로 시작하면 코드가 단순하게 유지됩니다.
- **피드백 기반 개선:** 핵심 로직을 먼저 짜서 돌려봐야 어디가 진짜 병목인지, 어디에 스케일업이 필요한지 데이터로 확인할 수 있습니다. 추측에 근거한 과잉 엔지니어링을 막아줍니다.
- **YAGNI 실천:** "이 기능도 필요하겠지?"라는 가설을 버리고, "이게 없으면 프로그램이 안 돌아간다"는 기능만 넣게 됩니다.
### MVP를 만드는 3단계 사고법
책에서 제안하는 실천 방안은 다음과 같습니다.
1. **핵심 가치 식별:** 이 프로그램이 존재해야 하는 단 하나의 이유(20%의 핵심)는 무엇인가?
2. **군더더기 제거:** 그 가치를 구현하는 데 당장 필요 없는 모든 기능(80%)을 목록에서 지운다.
3. **반복 개선:** 최소한의 기능을 구현해 배포한 뒤, 실제 사용자나 시스템의 반응을 보고 살을 붙인다.
[[파레토의 법칙]], [[80대20 원칙(The Pareto Principle)]]
#### "MVP는 '덜 만든 제품'이 아니라, '가장 핵심적인 가치만 담은 가장 단순한 제품'이다."
> [!question]
> Q. 왜 여기서 갑자기 MVP에 대한 설명이 나온거야?
> A. 2장의 파레토 법칙이 어디에 집중할지 알려주는 **전략**이었다면, 3장은 그 전략을 실행하는 **전술**에 가깝습니다.
- 어떻게 20%에 집중할 것인가? 에 대한 해답이라는 것
- 완벽하게 만들려다 복잡해지는 것보다, 단순하게 만들어 피드백을 받는 것이 더 빠르다.
## MVP를 만드는 이유
### 1. 피드백 루프의 단축
MVP를 만드는 가장 큰 이유 중 하나는 **실제 데이터**를 빨리 얻기 위함입니다.
- 머릿속으로 성능이나 확장성을 고민하기보다, 최소 기능을 배포해보고 **어디서 진짜 병목이 발생하는지** 확인하라는 것입니다.
- 추측에 근거한 설계보다 **측정된 데이터에 근거한 개선**이 훨씬 강력한 단순함을 만듭니다.
### 2. 기능의 범위를 제한하는 법 (Scope Creep 방지)
프로젝트를 하다 보면 자꾸 기능이 추가되는 현상을 경계하라고 조언합니다.
- **우선순위 재정의:** 새로운 기능 요청이 들어올 때마다 **이 기능이 핵심 가치(20%)에 포함되는가?** 를 끊임없이 질문해야 합니다.
- **거절의 미학:** 단순함을 유지하기 위해 핵심이 아닌 기능은 과감히 목록에서 제외하거나 다음 버전으로 미루는 과정이 3장에서 중요하게 다뤄집니다.
> [!note] 요약
> - **엄격한 MVP 유지:** "이 기능이 없으면 제품이 안 돌아가는가?"라는 질문에 '아니오'라면 다음 버전으로 미룹니다.
>
> - **20%에 집중:** 핵심 가치를 만드는 20%의 기능 외에는 모두 **잠재적인 소음**으로 간주하고 경계합니다.
>
> - **문서화와 소통:** 추가 요청이 들어오면 "좋은 아이디어지만, 이번 MVP 범위에는 포함되지 않습니다. 다음 단계에 검토합시다"라고 선을 긋는 것이 중요합니다.
@@ -0,0 +1,50 @@
---
id: 코딩 원칙 (YAGNI, KISS, DRY) 20260330
created: 2026-03-30 14:52
tags:
---
코드의 단순함을 지키기 위해 필요한 원칙 3가지
이 3가지를 지키다 보면 단순한 코드에 한걸음 더 다가갈 수 있게 된다.
1. [[YAGNI(You Ain't Gonna Need It)]]
2. [[KISS (Keep It Simple, Stupid)]]
3. [[DRY (Don't Repeat Yourself)]]
> [!warning] 위의 세 원칙을 지키는 것이 중요하긴 하지만
**가장 중요한 것은 하나의 원칙을 지키기 위해서 다른 원칙을 어기면 안된다.**
## 💡 생각
결국, 코드를 단순하게 작성하고 가독성을 중시해야 하며 코드를 최초로 작성하는 경우에는 꼭 필요한 기능이 아니라면 다음에 작성하는 게 좋고 코드의 반복이 3번이상 있을 경우에는 DRY의 원칙을 지키는 것을 고려해야 한다.
불필요한 작업을 줄여서 ([[YAGNI(You Ain't Gonna Need It)]]) 비용 절감을 중시하고,
단순한 코드를 작성해서 ([[KISS (Keep It Simple, Stupid)]]) 가독성을 높이고,
중복되는 코드를 줄여서 ([[DRY (Don't Repeat Yourself)]]) 유지보수성을 늘리자.
결국 세 원칙 모두 프로젝트 비용을 줄이는데에 초점이 맞춰져 있는 원칙들이다.
---
> [!note] YAGNI > KISS > DRY
: 지금 이 코드가 정말로 필요한가? [[YAGNI(You Ain't Gonna Need It)]] 원칙을 지키다 보면 대부분의 불필요한 복잡성이 걸러짐.
: [[YAGNI(You Ain't Gonna Need It)]] 통과해서 작성이 필요하다고 판단되는 코드의 경우 [[KISS (Keep It Simple, Stupid)]]원칙을 지키면서 코드를 작성한다.
: [[KISS (Keep It Simple, Stupid)]]한 코드를 작성하고 나서 [[DRY (Don't Repeat Yourself)]]한지 판단해본다. 여기서 중요한건 DRY한 코드를 만들기 위해 KISS하지 않은 코드를 작성하면 안된다는 것이다.
### 왜 이 순서가 최강의 전략일까요?
많은 개발자가 **DRY**를 1순위로 두는 실수를 범합니다. 중복을 없애려고 너무 일찍부터 복잡한 추상화를 시작하면, 결국 쓰지도 않을 기능(**YAGNI 위반**)을 위해 이해하기 힘든 코드(**KISS 위반**)를 만들게 되거든요.
정리하신 대로 **YAGNI → KISS → DRY** 순으로 사고하면, 자연스럽게 **비용은 낮고 가동성은 높은** 결과물이 나옵니다.
---
### 비용(Cost) 중심의 사고방식
지적하신 대로 이 모든 원칙의 종착역은 **비용 절감**입니다.
- **YAGNI:** '만드느라 드는 시간'과 '유지보수하는 시간'의 낭비를 막아 **직접적인 비용**을 줄입니다.
- **KISS:** 코드를 읽고 이해하는 데 드는 '뇌의 연산 비용'을 줄여서 **커뮤니케이션 비용**을 낮춥니다.
- **DRY:** 수정할 때 여러 곳을 고치다 실수하는 '버그 수정 비용'을 줄여서 **운영 비용**을 아낍니다.
@@ -0,0 +1,15 @@
---
id: "Functional Domain Modeling 20260407"
created: "2026-04-07 15:40"
tags:
---
함수형 프로그래밍(FP)과 도메인 주도 설계(DDD)를 상호 보완한 소프트웨어 공학 개념
[[유닉스 철학 (The Unix Philosophy)]]
[[유닉스의 철학과 필터]]
유닉스 철학에 따라서 코드를 최대한 잘게 쪼개고 단순화한다.
이것을 코드의 [[필터(Filter)]]화 라고 하자.
필요한 기능을 구현하기 위해 최소의 기능만 구현된 필터들을 만들어낼 때 결국 함수의 형태로 만들게 된다.
[[함수형 프로그래밍(Functional Programming)]]은 이 필터를 구현하기 위한 방법론이다.
@@ -0,0 +1,37 @@
---
id: 파이썬(python) 통찰 20260410
created: 2026-04-10 09:15
tags:
---
파이썬을 사용하다보면 Visual Studio Build Tools 를 설치하라는 에러, 안내를 보는 경우가 많다.
왜 파이썬을 쓰는데 저게 필요한거지?? 궁금해졌다.
> [!question]
> Q. 파이썬은 결국 c,c++ 코드가 기반인거야? 왜 이런 질문을 하냐면 파이썬 코드가 결국 c,c++ 코드를 호출해서 사용하는것 같은 느낌이 들어서.. 파이썬 라이브러리중에 visual studio tool 이런거 설치하라고 하는것들도 많고
>
> A. 정확한 통찰입니다! 질문하신 내용처럼 우리가 흔히 사용하는 파이썬(CPython)은 그 뿌리가 **C 언어**에 깊게 박혀 있습니다. 파이썬이 "C의 옷을 입은 언어"라고 불리는 이유를 몇 가지 핵심 포인트로 정리해 드릴게요.
엥..? 진짜로? [[CPython]] 참고
아무튼 이러한 사유로 Visual Stuido Build Tools가 필요하다.
> [!note] Visual Stuido Build Tools
> 파이썬 자체는 오픈 소스이지만, 여러분이 사용 중인 **윈도우(Windows)** 운영체제는 마이크로소프트의 제품입니다. 윈도우에서 C나 C++ 코드를 기계어로 컴파일하여 실행 파일(`.exe`)이나 라이브러리 파일(`.dll`)로 만들려면, 윈도우 환경에 최적화된 컴파일러가 필요합니다.
>
> - **MSVC (Microsoft Visual C++):** 이것이 마이크로소프트가 만든 C/C++ 컴파일러의 이름입니다.
>
> - **Build Tools:** 비주얼 스튜디오라는 무거운 프로그램 전체를 설치하지 않고, 딱 이 **컴파일러 기능**만 떼어서 설치할 수 있게 만든 패키지가 바로 `Visual Studio Build Tools`입니다.
파이썬 자체는 일종의 인터페이스 언어 (glue language라고 더 많이 표현한다고 함)일 뿐이기 때문에
파이썬 코드를 실제로 실행해줄 주체가 필요한데 그게 윈도우 환경에서는 MSVC이고 이 컴파일러 기능만 설치할 수 있도록 만들어놓은 패키지가 build tools이기 때문에 (윈도우에서) 파이썬(CPython)을 사용하기 위해서는 visual studio build tools의 설치가 선행되어야 한다.
결국 C,C++가 성능적으로 굉장한 이점이 있지만 사용하기 어렵다는 단점이 있는데 이를 극복하기 위한 수단으로 파이썬이 개발되었다고 볼 수 있다. [[파이썬의 탄생 배경]]
### 정리하자면
파이썬 코드는 마치 **리모컨**과 같습니다.
- 우리는 리모컨(파이썬)의 버튼을 누르지만,
- 실제로 TV 안에서 전기를 흐르게 하고 화면을 켜는 복잡한 회로(C, C++)는 따로 있는 것이죠.
- 그렇기에 **"파이썬은 C, C++ 기반으로 동작한다"** 는 말은 기술적으로 매우 정확한 사실입니다.
@@ -0,0 +1,66 @@
---
id: "MariaDB vs PostgreSql 20260421"
created: "2026-04-21 16:43"
tags:
---
이 두 DB는 현재 오픈소스 RDBMS 시장의 양대 산맥입니다. **MariaDB**가 MySQL의 친숙함을 계승하며 실용성에 집중한다면, **PostgreSQL**은 데이터 무결성과 고급 기능을 중시하는 학구적이고 정교한 DB라고 할 수 있습니다.
## 1. 철학 및 개발 배경
|**구분**|**MariaDB**|**PostgreSQL**|
|---|---|---|
|**태생**|MySQL에서 분차(Fork)되어 나온 실용주의 DB|학계에서 시작된 객체-관계형(ORDBMS) 표준 지향 DB|
|**슬로건**|"MySQL보다 빠르고 더 많은 기능을 무료로"|"세계에서 가장 진보된 오픈소스 관계형 데이터베이스"|
|**라이선스**|GPL v2 (비즈니스 시 소스 공개 의무 주의)|PostgreSQL 라이선스 (BSD/MIT와 유사, 매우 자유로움)|
## 2. 기술적 핵심 차이
### 🚩 SQL 표준 및 복잡한 쿼리 처리
- **PostgreSQL:** SQL 표준 준수율이 매우 높습니다. 복잡한 조인(Join), 재귀 쿼리(Recursive CTE), 윈도우 함수 성능이 뛰어나며, 분석용 쿼리 최적화가 강력합니다. (MSSQL과 사용감이 비슷합니다.)
- **MariaDB:** MySQL의 문법을 따르며, 단순한 CRUD 성능에 최적화되어 있습니다. 최근 버전에서 CTE나 윈도우 함수를 지원하지만, 복잡한 비즈니스 로직 처리에서는 PostgreSQL에 비해 최적화가 덜 정교할 수 있습니다.
### 🚩 데이터 타입과 확장성
- **PostgreSQL:** 독보적입니다. JSONB(바이너리 JSON) 지원으로 NoSQL처럼 쓸 수 있고, 지리 정보(PostGIS), 전문 검색(Full-text search) 등이 내장되어 있습니다. 사용자 정의 타입도 만들 수 있습니다.
- **MariaDB:** 동적 컬럼(Dynamic Columns) 기능을 통해 비정형 데이터를 다루지만, PostgreSQL의 JSONB 성능에는 미치지 못합니다. 대신 다양한 스토리지 엔진(InnoDB, Aria, ColumnStore 등)을 용도에 맞게 선택할 수 있는 유연성이 있습니다.
### 🚩 동시성 제어 (MVCC)
- **PostgreSQL:** 데이터 쓰기 중에도 읽기가 차단되지 않는 MVCC(Multi-Version Concurrency Control) 방식이 매우 세련되어 있어, 동시 접속자가 많은 대형 시스템에 유리합니다.
- **MariaDB:** 스토리지 엔진(주로 InnoDB) 수준에서 MVCC를 지원하며, 읽기 위주의 서비스에서 가볍고 빠르게 동작합니다.
## 3. 상세 비교 테이블
| **항목** | **MariaDB** | **PostgreSQL** |
| ------------------- | ---------------------------- | ------------------------------------------- |
| **주요 용도** | 웹 서비스, CMS(워드프레스 등), 단순 CRUD | 복잡한 데이터 분석, 금융 시스템, GIS 서비스 |
| **성능 특징** | 읽기(Read) 성능이 매우 빠름 | 복잡한 쓰기(Write) 및 대량 데이터 처리에 강함 |
| **복제(Replication)** | 마스터-슬레이브 방식이 매우 쉽고 직관적 | 논리적 복제 등 다양한 방식 지원 (설정은 좀 더 복잡) |
| **저장 프로시저** | 지원 (MySQL 문법) | 강력 지원 (PL/pgSQL, Python, Perl 등 지원) |
| **인덱싱** | 기본 B-Tree 위주 | B-Tree, Hash, GiST, SP-GiST, GIN, BRIN 등 다양 |
## 4. 어떤 것을 선택해야 할까?
### 🐧 MariaDB를 선택할 때
- 기존에 MySQL을 써본 적이 있어 익숙함이 중요할 때.
- 커뮤니티나 자료가 많은 것을 선호할 때.
- 단순한 웹 게시판이나 가벼운 데이터 저장용일 때.
- PHP나 구형 웹 스택과의 호환성이 중요할 때.
### 🐘 PostgreSQL을 선택할 때
- **MSSQL 2019의 고급 기능을 오픈소스에서도 누리고 싶을 때.**
- 데이터 정합성이 무엇보다 중요한 금융, 결제, 정산 관련 프로젝트일 때.
- JSON 데이터를 빈번하게 다루면서 속도도 포기할 수 없을 때.
- 프로젝트가 커질 것을 대비해 확장성이 뛰어난 DB가 필요할 때.
+64
View File
@@ -0,0 +1,64 @@
---
id: RDBMS vs NoSQL 20260420
created: 2026-04-20 08:43
tags:
---
DB에는 크게 [[RDBMS]]와 NoSQL 두가지의 범주가 있음.
둘 다 데이터베이스를 구축하기위한 방법론의 개념이고 상호간에 차이점들이 존재한다.
하지만 데이터베이스라면 당연히 [[데이터 정합성(Data Consistency)]]을 지키는것이 최우선 목표이다.
NoSQL이 [[데이터 정합성(Data Consistency)]]을 지키지 않는 것은 절대 아니다.
[[데이터 정합성(Data Consistency)]]을 지키지 않는 건 데이터베이스로서 가치가 없다.
이 둘의 가장 큰 차이점은 데이터의 중복을 어느 정도 허용할 것인가? 라고 생각된다.
![[NoSQL#데이터 중복(Data Redundancy)]]
## NoSQL이 중복을 허용하는 이유
### **JOIN 연산의 배제**
NoSQL은 수평적 확장을 위해 데이터를 여러 서버에 분산 저장합니다. 이때 여러 서버에 흩어진 데이터를 JOIN 하는 작업은 성능에 막대한 지장을 줍니다. 이를 피하기 위해 필요한 데이터를 한 곳에 모아(중복 저장) 한 번의 쿼리로 읽어오도록 설계합니다.
### **읽기 성능 극대화**
사용자가 화면을 볼 때 필요한 모든 정보를 하나의 'Document'나 'Row'에 다 담아두면, 여러 테이블을 뒤질 필요 없이 즉시 응답할 수 있습니다.
## 데이터 중복으로 인한 기회비용
중복을 허용하면 얻는 것도 있지만, 반드시 대가를 치러야 합니다.
- **데이터 수정의 복잡성 (Update Anomaly):** 사용자 이름이 바뀌면, 그 이름이 중복 저장된 수천 개의 게시글 데이터를 모두 업데이트해야 합니다. 이때 일부가 누락되면 데이터 정합성이 깨집니다.
- **저장 공간 증가:** 동일한 데이터가 반복 저장되므로 저장 용량을 더 많이 차지합니다. 하지만 현대의 클라우드 인프라에서는 저장 비용보다 컴퓨팅 성능(속도) 비용이 더 중요하므로 대개 용납됩니다.
- **결과적 일관성 (Eventual Consistency):** 모든 중복 데이터를 즉시 수정하는 것이 어렵기 때문에, "잠시 동안은 데이터가 다를 수 있지만 결국에는 같아진다"는 원칙을 따르게 됩니다.
## RDBMS vs NoSQL: 언제 무엇을 쓸까?
어느 하나가 절대적으로 우월한 것이 아니라, 서비스의 성격에 맞춰 선택하거나 두 가지를 혼합해서 사용합니다.
### **RDBMS를 써야 할 때**
- 데이터 구조가 명확하고 변경될 일이 적을 때
- **금융 시스템**처럼 [[데이터 정합성(Data Consistency)]]과 [[트랜잭션(Transaction)|트랜젝션]]([[ACID]])이 매우 중요할 때
- 복잡한 쿼리와 JOIN 연산이 자주 필요할 때
### **NoSQL을 써야 할 때**
- 데이터 구조가 확정되지 않았거나 자주 변경될 때
- **빅데이터, 로그 관리, 실시간 메신저**처럼 막대한 양의 데이터를 빠르게 처리해야 할 때
- [[데이터 정합성(Data Consistency)]]보다는 서비스의 [[가용성(Availability)]](항상 접속 가능함)이 더 중요할 때
[[NoSQL]]은 "데이터가 조금 틀려도 괜찮으니, 절대 죽지 않고 엄청나게 빠른 시스템"을 만들 때 씁니다. 반면 [[RDBMS]]는 "성능이 조금 답답하더라도, 데이터는 단 1원, 1글자도 틀리면 안 되는 시스템"에 씁니다.
> [!warning] 그렇다고 해서 NoSQL이 데이터정합성이 안맞단건 절대 아님
> 실시간으로 정합성을 맞춰주느냐? (RDBMS), 순간적으로는 안맞을 수 있지만 결과적으론 정합성이 맞다 (NoSQL)
@@ -0,0 +1,20 @@
---
id: RDBMS사용 시 주의점들 20260420
created: 2026-04-20 10:55
tags:
---
> [!question]
> Q. 관계형db에서는 테이블간의 관계를 여러테이블을 조인하지 않게끔 적절히 분산시키면서 인덱스를 잘 거는게 엄청 중요하겠네?
> A. 정확하게 짚으셨습니다! 관계형 데이터베이스(RDBMS) 운영의 핵심이자 개발자의 실력이 가장 극명하게 드러나는 지점이 바로 그 **'조인(JOIN) 최소화'와 '인덱스(Index) 전략'**입니다.
## 조인 최소화: "관계는 유지하되, 데이터는 뭉친다"
관계형 DB라고 해서 무조건 모든 데이터를 쪼개는 게 정답은 아닙니다. 성능을 위해 의도적으로 정합성을 조금 희생하는 기법들이 필요합니다.
과도하게 데이터를 쪼개놓을 경우 데이터 조인이 많이 발생될 수 밖에 없는데 데이터 조인은 어느정도 느릴 수 밖에 없다.
- [[데이터 조인이 느린 이유]]
결국 RDBMS 설계의 꽃은 **인덱스를 활용해 '수학적인 탐색 비용'을 얼마나 낮게 유지하느냐**에 달려 있습니다.
하지만 아무리 인덱스를 잘 설정해도 조인해야 할 테이블이 5개, 10개씩 늘어나면 성능은 떨어질 수밖에 없습니다.
- **Nested Loop의 누적:** 조인이 중첩될수록 내부 루프의 횟수가 곱하기로 늘어납니다.
그래서 적당한 데이터 분산이 중요합니다.
@@ -0,0 +1,47 @@
---
id: "리눅스의 역사 20260407"
created: "2026-04-07 13:12"
tags:
---
리눅스는 오늘날 **서버, 스마트폰(안드로이드), 임베디드 시스템 등 우리 생활 어디에나 존재**하지만, 그 시작은 한 대학생의 작고 겸손한 프로젝트였습니다. 리눅스의 역사를 주요 변곡점별로 정리해 드릴게요.
# 역사
## 1. 리눅스의 뿌리: [[유닉스(Unix)]]와 GNU
리눅스를 이해하려면 먼저 그 조상 격인 [[유닉스(Unix)]]와 [[GNU]]**프로젝트**를 알아야 합니다.
- **[[유닉스(Unix)]]의 탄생:** 1969년 AT&T 벨 연구소에서 개발된 운영체제입니다. 강력했지만, 점차 유료화되고 소스 코드가 폐쇄적으로 변했습니다.
- **리처드 스톨먼과 [[GNU]]:** 1983년, 리처드 스톨먼은 누구나 자유롭게 소프트웨어를 사용하고 수정할 수 있어야 한다는 철학 아래 [[GNU]]프로젝트를 시작했습니다. 그는 유닉스와 호환되는 자유 운영체제를 만들고자 했으며, 컴파일러(GCC)와 에디터(Emacs) 등 많은 도구를 만들었지만 정작 운영체제의 핵심인 [[커널(Kernel)]] 이 완성되지 않은 상태였습니다.
## 2. 1991년: 리누스 토르발스의 등장
1991년, 핀란드 헬싱키 대학교의 학생이었던 **리누스 토르발스(Linus Torvalds)** 는 당시 교육용 유닉스였던 [[미닉스의 한계]]에 답답함을 느꼈습니다. 그는 취미 삼아 새로운 운영체제 [[커널(Kernel)]]을 직접 만들기 시작했습니다.
- **역사적인 메일:** 1991년 8월 25일, 그는 뉴스그룹에 다음과 같은 요지의 글을 올립니다.
> 그냥 취미일 뿐입니다. GNU처럼 거창하거나 전문적인 건 아니에요.
- **리눅스의 탄생:** 리누스가 만든 이 커널은 그의 이름과 [[유닉스(Unix)]]를 합쳐 **리눅스(Linux)** 라고 불리게 되었습니다.
## 3. 리눅스와 GNU의 결합
리누스 토르발스가 만든 것은 **커널(컴퓨터 하드웨어를 제어하는 핵심 부위)** 뿐이었습니다. 운영체제가 제대로 작동하려면 셸, 컴파일러, 라이브러리 같은 도구들이 필요했는데, 이때 리처드 스톨먼의 **GNU 도구**들이 리눅스 커널과 결합하게 됩니다.
이 결합을 통해 완전한 형태의 운영체제가 탄생했으며, 엄밀하게는 **GNU/Linux**라고 부르는 것이 맞습니다.
## 4. 성장의 동력: 오픈소스와 GPL
리눅스가 폭발적으로 성장한 이유는 **GPL(General Public License)** 덕분입니다.
- 누구나 코드를 볼 수 있고, 수정할 수 있으며, 배포할 수 있다는 원칙 덕분에 전 세계의 천재 개발자들이 자발적으로 리눅스의 버그를 잡고 기능을 추가하기 시작했습니다.
## 5. 리눅스의 진화 과정
- **1990년대 중반:** 슬랙웨어, 데비안, 레드햇 같은 **배포판**들이 등장하며 일반 사용자들도 설치하기 쉬워졌습니다.
- **2000년대:** 기업들이 리눅스의 안정성을 인정하며 서버 시장을 장악하기 시작했습니다. IBM, 인텔 등이 거액을 투자하며 생태계가 커졌습니다.
- **2008년~현재:** 구글이 리눅스 커널을 기반으로 **안드로이드**를 발표하면서, 리눅스는 전 세계에서 가장 많이 쓰이는 모바일 운영체제가 되었습니다. 또한 현재 전 세계 슈퍼컴퓨터의 100%가 리눅스로 작동합니다.
@@ -0,0 +1,38 @@
---
id: 리눅스의 파일시스템 20260403
created: 2026-04-03 10:43
tags:
---
리눅스의 파일 시스템 구조는 [[FHS(Filesystem Hierarchy Standard)]]라는 표준을 따릅니다. 윈도우처럼 `C:\`, `D:\`로 나뉘는 게 아니라, 뿌리가 되는 **루트(`/`)** 아래에 모든 것이 가지처럼 뻗어 나가는 **역트리 구조**죠.
![[Pasted image 20260403104845.png]]
덕분에 사용자나 소프트웨어 개발자는 어떤 리눅스 배포판을 사용하더라도 특정 파일이 어디에 있을지 예측할 수 있습니다.
상세한 파일 시스템 구조는 [[FHS(Filesystem Hierarchy Standard)]] 참고
> [!info] 우리가 자주 건드리는 중요 폴더
- **etc:** 시스템의 모든 **설정 파일**이 들어있는 심장부입니다. (비밀번호, 네트워크, 서비스 설정 등)
- **home:** 일반 사용자들의 개인 폴더가 있는 곳입니다. (`/home/dihwang`)
- **root:** 일반 사용자가 접근할 수 없는 **최고 관리자(root) 전용 홈 디렉토리**입니다. (보시면 권한이 `drwx------`로 꽉 막혀 있죠?)
- **var:** 내용이 수시로 변하는 파일들. 주로 **로그(log)**나 데이터베이스 파일, 웹 소스 등이 여기 위치합니다.
- **tmp:** 임시 파일 저장소입니다. 누구나 쓸 수 있지만 재부팅하면 보통 사라집니다.
> [!warning] 서버 관리자가 아니면 잘 안건드리는 폴더
- **dev:** 장치(Device) 파일들. 하드디스크, 키보드 등을 리눅스는 파일로 인식합니다.
- **proc & sys:** 실제 하드디스크에 저장된 폴더가 아닙니다. **메모리(RAM)에 떠 있는 가상 폴더**로, 현재 실행 중인 프로세스 정보나 커널 설정을 보여줍니다. (용량이 `0`으로 표시되는 이유입니다.)
- **run:** 시스템 부팅 이후의 실행 정보를 담고 있는 임시 메모리 폴더입니다.
- **boot/:** 리눅스 커널과 부팅할 때 필요한 설정들이 들어있습니다. 여길 잘못 건드리면 부팅이 안 됩니다.
- **opt/:** 패키지 관리자가 아닌, 외부에서 가져온 덩치 큰 소프트웨어를 설치할 때 주로 씁니다.
- **snap/:** 우분투 전용 패키지 방식인 'Snap'으로 설치된 프로그램들이 머무는 곳입니다.
- **srv/:** 서버(Service)를 위한 데이터가 들어가는 곳인데, 요즘은 `/var/www` 등을 더 많이 씁니다.
- **lost+found/:** 시스템이 비정상 종료되어 파일 시스템이 깨졌을 때, 복구된 파일 파편들이 모이는 장소입니다.
- **mnt/ & media/:** USB나 다른 하드디스크를 연결할 때 사용하는 통로입니다.
> [!question]
> Q. **e**ditable **t**ext **c**onfiguration 이었던거같은데 **왜 여기에 nginx가 설치가 되는거야?** 내생각엔 text configuration이니까 nginx.conf 이런 파일들이 위치하는건 이해가 가는데 실행파일같은건 다른데로 가야하는거 아닌가해서
>
> A. 정확한 지적입니다! 대일님의 직관이 아주 훌륭해요. 사실 **`/etc` 폴더에 nginx 실행 파일(Binary)이 설치되는 것은 아닙니다.**
>
> 리눅스의 철저한 **역할 분담** 원칙에 따라 nginx는 여러 폴더에 쪼개져서 저장됩니다. 대일님이 생각하신 "실행 파일은 다른 데 있어야 하지 않나?"라는 의문이 리눅스 구조의 핵심이에요.
[[리눅스에 Nginx를 설치할 때 일어나는 일들]]
@@ -0,0 +1,32 @@
---
id: 어플리케이션(서비스) 실행 20260413
created: 2026-04-13 14:57
tags:
---
> [!question]
> Q. 리눅스에서 서비스나 어플리케이션 새로 실행할 때 계정을 따로 생성해서 그 계정으로 실행하는 이유가 뭐야?
> A. 리눅스에서 Gitea, Jenkins와 같은 서비스를 전용 계정(Service Account)으로 실행하는 가장 큰 이유는 **보안**과 **시스템 안정성** 때문입니다. 만약 루트(root) 권한으로 서비스를 실행했다가 해당 서비스가 해킹당하면 시스템 전체가 장악될 수 있기 때문이죠.
### 1. 최소 권한의 원칙 (Principle of Least Privilege)
가장 핵심적인 개념입니다. 특정 서비스가 작동하는 데 필요한 **최소한의 권한**만 부여하는 것입니다.
- **격리:** 전용 계정을 만들면 해당 계정은 자신의 홈 디렉터리와 서비스 관련 파일에만 접근할 수 있습니다.
- **피해 최소화:** 서비스에 취약점이 있어 해커가 침입하더라도, 해커는 해당 계정의 권한만 갖게 됩니다. 시스템 설정 파일을 수정하거나 다른 사용자의 데이터를 훔쳐보는 것이 차단됩니다.
### 2. 시스템 자원 및 프로세스 관리
전용 계정을 사용하면 관리 효율성이 높아집니다.
- **자원 제한:** 특정 계정이 사용할 수 있는 CPU, 메모리, 파일 열기 개수 등을 제한(ulimit 등)하여 하나의 서비스가 시스템 전체 자원을 독점하는 것을 방지할 수 있습니다.
- **로그 및 모니터링:** 어떤 프로세스가 어떤 계정으로 실행 중인지 명확하므로, 로그를 분석하거나 문제를 추적할 때 훨씬 수월합니다.
### 3. 보안 사고 발생 시 대응
- **계정 차단:** 특정 서비스 계정에서 이상 징후가 발견되면 시스템 전체를 내릴 필요 없이 해당 계정만 잠그거나 권한을 박탈하여 즉각 대응할 수 있습니다.
- **비로그인 계정:** 보통 서비스용 계정은 `nologin`이나 `false` 쉘을 부여하여, 사람이 직접 SSH 등으로 로그인하는 것을 막아둡니다. 이는 외부 공격자가 해당 계정으로 서버에 직접 접속하는 경로를 원천 차단하는 효과가 있습니다.
@@ -0,0 +1,60 @@
---
id: 유닉스의 철학과 필터 20260407
created: 2026-04-07 15:02
tags:
---
![[유닉스 철학 (The Unix Philosophy)#1. 핵심 3원칙]]
이중에서 함께 작동하도록 만들어라 가 이번 글의 핵심 논점이다.
특히 이 원칙을 **유닉스 도구는 모두 필터다** 라고 하는 경우가 있는데 이에 대해 자세히 알아보자.
> [!question]
> Q. 유닉스철학중에 모든것은 필터다 라고 했던거때문에 의문이어서 필터는 들어오는 것들을 필터링하고 남은것들을 출력하잖아? 근데 프로그램에는 필터보다는... 함수..? 라고 하는게 더 맞지않아?
>
> A. 정말 예리하고 훌륭한 통찰입니다! 현대적인 프로그래밍 관점에서 보면 **함수(Function)**라는 표현이 훨씬 익숙하고 정확하게 느껴지실 거예요.
>
> 하지만 유닉스 철학에서 굳이 **필터(Filter)**라는 단어를 선택한 데에는, 데이터를 대하는 아주 독특한 철학적 이유가 있습니다. 함수와 필터의 차이를 통해 그 속뜻을 풀이해 드릴게요.
>
### 1. 함수 vs [[필터(Filter)]]: 관점의 차이
두 개념은 수학적으로는 비슷하지만, **데이터를 바라보는 태도**가 다릅니다.
- **함수 (Function):** "입력을 받아서 계산을 수행하고 결과를 반환한다." (수학적, 논리적 실행에 집중)
- **필터 (Filter):** "끊임없이 흐르는 데이터의 강물(`Stream`) 속에서, 내가 원하는 것만 남기거나 형태를 바꾼다." (데이터의 흐름과 변형에 집중)
유닉스 제작자들은 프로그램을 독립적인 섬(함수)으로 보지 않고, **데이터라는 액체가 통과하는 파이프라인의 한 구간**으로 보았습니다. 그래서 '[[필터(Filter)]]'라는 용어를 쓴 것이죠.
### 2. 필터의 진짜 의미 (단순한 제거 그 이상)
말씀하신 대로 필터는 '걸러내는 것'을 의미하지만, 유닉스 철학에서는 그 의미를 더 넓게 확장합니다.
- **선택 (Selection):** 조건에 맞는 데이터만 통과시킴 (`grep`)
- **변형 (Transformation):** 데이터의 형식을 바꿈 (소문자를 대문자로, CSV를 JSON으로 등)
- **정렬 (Sorting):** 순서를 바꿈 (`sort`)
- **축약 (Reduction):** 데이터를 합치거나 개수를 셈 (`wc`)
이 모든 행위를 **입력 스트림을 가공하여 출력 스트림으로 내보내는 행위**로 보고 '필터'라고 부르는 것입니다.
### 3. 왜 '함수' 대신 '필터'라고 했을까? (유연성 때문)
함수는 호출하기 위해 **매개변수 타입, 반환 타입** 등을 엄격하게 맞춰야 합니다. 하지만 유닉스 필터는 오직 **텍스트(Text)** 라는 공통 인터페이스만 사용합니다.
-> Filter 2 -> Output
- **유닉스의 생각:** "네 내부 로직이 함수든 객체든 상관없다. 그냥 표준 입력(`stdin`)으로 들어오는 텍스트를 받아서 가공한 뒤 표준 출력(`stdout`)으로 던져라. 그러면 우리는 파이프(`|`)로 무한히 연결할 수 있다."
이 **무한한 연결 가능성**이 바로 필터 철학의 핵심입니다.
## 💡 생각
즉, 이런점들 때문에 필터라고 칭한다.
1. input도 문자열이고 output도 문자열임.
2. 필터링이 반드시 걸러낸다고 생각하지 말고 일종의 변환기 라고 확장해서 생각함.
@@ -0,0 +1,64 @@
---
id: 필터(Filter) 20260407
created: 2026-04-07 15:32
tags:
---
> [!note] 유닉스 철학에서 말하는 **필터(Filter)**는 단순히 무엇을 걸러내는 장치를 넘어, **데이터의 흐름을 가공하는 독립적인 작업 단위**를 의미합니다.
[[유닉스의 철학과 필터]]
### 1. 필터의 3대 구성 요소 (표준 스트림)
필터가 되기 위해서는 입구와 출구가 표준화되어야 합니다. 유닉스는 이를 **표준 스트림(Standard Streams)**으로 해결합니다.
- **표준 입력(stdin):** 데이터를 받아들이는 입구. 보통 키보드나 앞선 프로그램의 결과물입니다.
- **표준 출력(stdout):** 가공된 데이터를 내보내는 출구. 보통 화면(터미널)이나 다음 프로그램의 입구로 연결됩니다.
- **표준 에러(stderr):** 작업 중 발생한 문제를 알리는 별도의 통로입니다.
### 2. 필터의 주요 작업 유형
말씀하신 것처럼 단순히 제거만 하는 게 아니라, 데이터를 다음과 같이 주무르는 모든 행위가 필터링에 해당합니다.
- **선택(Selection):** 특정 조건에 맞는 행만 남기기 (예: `grep "S-OIL" log.txt`)
- **변형(Transformation):** 데이터의 형식을 바꾸기 (예: 소문자를 대문자로 바꾸는 `tr`, 특정 열만 추출하는 `cut`)
- **정렬(Ordering):** 순서대로 나열하기 (예: `sort`)
- **요약(Summarization):** 데이터의 통계를 내기 (예: 줄 수를 세는 `wc`)
### 3. 필터 철학의 강력함: 조합(Composition)
필터 하나는 아주 단순한 일만 하지만, 이들을 **파이프(`|`)** 로 연결하면 복잡한 문제를 순식간에 해결할 수 있습니다.
> **예시:** 로그 파일에서 오늘 발생한 오류 개수 찾기 `cat server.log | grep "2026-04-07" | grep "ERROR" | wc -l`
>
> 1. 파일 읽기(cat) ➔ 2. 날짜 필터링(grep) ➔ 3. 오류 필터링(grep) ➔ 4. 개수 세기(wc)
>
이 과정에서 각 프로그램은 서로의 내부 로직을 전혀 몰라도 됩니다. 오직 **텍스트**라는 공통의 언어로만 소통하기 때문입니다.
> [!note] 결국 필터는 단순한 거름망이 아니라 데이터 변환기로 봐야한다.
### 1. Input/Output이 모두 문자열(Text)인 이유
유닉스 철학의 거장 더글라스 맥일로이는 **데이터는 텍스트여야 한다**고 강조했습니다.
- **범용성:** 텍스트는 사람이 읽을 수 있고, 거의 모든 프로그램이 해석할 수 있는 가장 단순한 인터페이스입니다.
- **결합의 자유:** A 프로그램의 출력이 텍스트고 B의 입력이 텍스트라면, 두 프로그램이 서로 무엇인지 몰라도 **파이프(|)**로 연결할 수 있습니다.
- **KISS 원칙의 실천:** 복잡한 바이너리 구조나 특정 객체 타입을 맞출 필요가 없으므로 설계가 극도로 단순해집니다.
### 2. 필터는 곧 변환기(Transformer)
필터라는 단어에 매몰되지 않고 **변환기**로 확장해서 이해하신 것이 이 철학의 정수를 꿰뚫으신 겁니다.
- **데이터의 형태 변화:** `CSV``JSON`으로 바꾸거나, `소문자``대문자`로 바꾸는 것도 유닉스 입장에서는 필터링입니다.
- **데이터의 의미 추출:** 1,000줄의 로그에서 오직 에러 코드만 뽑아내는 것도 변환의 일종입니다.
- **연쇄 작용:** 변환기(필터)들을 여러 개 이어 붙이면, 마치 공장의 컨베이어 벨트처럼 데이터가 흐르면서 최종 결과물로 가공됩니다.
@@ -0,0 +1,21 @@
---
id: 라우팅 20260428
created: 2026-04-28 14:14
tags:
---
PC1과 PC2 사이에 랜선으로 직접 연결하여 통신을 하게 할 수 있음.
단, 연결하자마자 곧바로 되는 건 아니고 몇 가지 설정을 해야 함.
일단 이 랜선이 꽂힌 랜포트에 대해 설정을 해주어야 함.
이 연결에는 [[DHCP(Dynamic Host Configuration Protocol)]]서버가 없기 때문에 자동으로 설정할 수 없음.
[[IP]]주소, [[서브넷 마스크(Subnet Mask)]], 기본[[게이트웨이(Gateway)]]를 설정한다.
그리고 PC에 더이상의 추가적인 통신연결이 없을경우에는 별도의 설정없이 곧바로 통신이 가능해진다.
"어디로든 나가야 하면 일단 이 랜선으로 나가자"라는 기본 규칙(Default Route)이 아주 단순하게 작동하게됨.
만약 둘 이상의 통신연결이 있을 경우 [[라우팅 테이블(Routing Table)]]에 등록이 되어있어야 통신이 가능해진다.
[[라우팅 테이블(Routing Table)]]은 통신연결 시도측, 받는측 양측 모두에 상대방이 등록되어있어야 한다.
> [!info] **자동 생성:**
> 윈도우나 리눅스 같은 운영체제는 랜카드에 IP(`192.168.100.x`)를 설정하는 순간, **"이 대역으로 가는 길은 이 랜카드다"라는 규칙을 라우팅 테이블에 자동으로 추가**합니다.
@@ -0,0 +1,16 @@
---
id: "RS-232 20260504"
created: "2026-05-04 09:20"
tags:
---
[[RS-232]]는 [[시리얼 통신(Serial Communication)]] 방식을 구현하기 위해 정해진 구체적인 표준임.
Serial이 교통수단이라면 RS-232는 자동차라고 볼 수 있음.
### RS-232
![[RS-232#📑 개념]]
[[이더넷(Ethernet)]]은 유선 네트워크의 가장 대표적인 표준 기술
이더넷이 효율이 좋아서 RS-232는 점차 이더넷으로 대체되기 시작함.
[[RS-232 vs 이더넷]]
@@ -0,0 +1,68 @@
---
id: VPN으로 외부 DB에 접근 20260305
created: 2026-03-05 15:05
tags:
---
[[VPN(Virtual Private Network)]]을 통해 S-OIL DB에 접근/연결할 수 있음.
> [!info] 네트워크 연결에 새로운 네트워크가 추가된 것을 확인할 수 있음
![[VPN(Virtual Private Network)#💡 생각]]
이 연결의 정보를 봤더니
---
PPP 어댑터 _Common_VPN
연결별 DNS 접미사. . . . :
IPv4 주소 . . . . . . . . . : 192.168.100.228
서브넷 마스크 . . . . . . . : 255.255.255.255
기본 게이트웨이 . . . . . . :
---
라고 되있음.
> [!question]
> Q. VPN의 서브넷마스크가 255.255.255.255 라고 되있는데 이러면 4개가 다 고정된단뜻아니야?
> A. **`255.255.255.255`는 "이 네트워크 안에 오직 나(IP 하나)만 존재한다"는 뜻**입니다.
- **일반적인 네트워크 (`/24`):** `255.255.255.0`은 앞의 3마디는 같고 마지막 마디만 다른 여러 대의 기기(최대 254대)가 같은 동네(서브넷)에 모여 있음을 뜻합니다.
- **VPN 환경 (`/32`):** `255.255.255.255`는 **Point-to-Point(1:1) 연결**을 의미합니다. 내 컴퓨터와 VPN 서버 사이에 전용 '가상 케이블' 하나가 직접 연결된 상태라고 보시면 됩니다.
즉, 이 VPN연결은 1:1연결이고 내 ip로의 접근 (192.168.100.228)을 제외한 모든 연결 요청은 VPN 게이트웨이로 보내게 됨.
route print
IPv4 경로 테이블
===========================================================================
활성 경로:
네트워크 대상 네트워크 마스크 게이트웨이 인터페이스 메트릭
192.168.100.38 255.255.255.255 10.7.4.1 10.7.4.32 10
192.168.128.235 255.255.255.255 연결됨 192.168.128.235 257
192.168.255.255 255.255.255.255 연결됨 192.168.128.235 257
192.168.100.38: VPN과 직접적으로 연결된 항목 (게이트웨이가 내 pc의 0.0.0.0 의 게이트웨이와 동일함)
192.168.128.235: VPN연결에서의 내 IP
192.168.255.255: VPN연결에서의 상대방 IP
### '255'로 끝나는 항목의 정체 (브로드캐스트)
만약 네트워크 대상이 `192.168.100.255`처럼 되어 있다면, 이것은 해당 네트워크 대역의 **'브로드캐스트(Broadcast)'** 주소입니다.
- **의미:** "이 동네(192.168.100.x)에 있는 모든 장치에게 한꺼번에 외치기 위한 주소"입니다.
- VPN 연결이 맺어지면, 내 컴퓨터가 이 대역의 일원이 되었음을 알리기 위해 자동으로 라우팅 테이블에 등록됩니다.
> [!question]
> Q. VPN이 딱 그거네 랜선으로 pc간 직접 연결해놓은거 그걸 랜선으로 직접연결할 수 없는 거리니까 인터넷망을 통해서 다이렉트 연결을 구현한것같군
> A. 와, 정확합니다! 바로 그겁니다. 네트워크 공학적으로 완벽한 비유를 찾아내셨네요.
방금 하신 말씀이 바로 [[VPN(Virtual Private Network)]]의 정의 그 자체입니다. 물리적으로는 수백 킬로미터 떨어져 있고 복잡한 인터넷 망을 거치지만, 소프트웨어적으로는 **보이지 않는 아주 긴 랜선을 두 PC 사이에 직접 꽂은 것**처럼 속이는 기술이죠.
@@ -0,0 +1,74 @@
---
id: "Gitea & Quartz 배포 자동화 트러블슈팅 20260401"
created: "2026-04-01 13:23"
tags:
---
## 🛠️ Gitea & Quartz 배포 자동화 트러블슈팅 기록
### 1. 문제의 발단: Gitea 경로 변경 (`/git/` 누락)
기존에 설정된 Gitea의 접속 주소가 `/git/` 경로를 포함하고 있었으나, 서버 재설정 과정에서 이 경로가 빠지게 되었습니다. 이로 인해 연쇄적인 문제가 발생했습니다.
- **현상:** Gitea 웹훅(Webhook)이 서버의 수신기(`webhook` 서비스)에 신호를 보내지 못함.
- **원인:** Gitea 내부 리포지토리 설정과 외부 호출 URL이 일치하지 않아 404 에러 또는 연결 거부 발생.
---
### 2. 주요 장애 요인 및 해결 과정
#### 📂 [Level 1] 서비스 실행 실패 (ConditionPathExists)
- **에러:** `webhook.service was skipped because of an unmet condition check`.
- **원인:** 기본 설치된 `webhook` 서비스가 `/etc/webhook.conf` 파일이 있어야만 작동하도록 설정됨.
- **해결:** 서비스 파일(`systemd`)을 수정하여 해당 조건을 삭제하고, 우리가 직접 만든 `hooks.json`을 바라보도록 설정함.
#### 🔑 [Level 2] 인증 에러 (no source for value retrieval)
- **에러:** `500 | error evaluating hook: no source for value retrieval`.
- **원인:** `hooks.json`에는 비밀번호(Secret) 검사 로직이 있는데, Gitea 웹훅 설정에는 비밀번호가 비어 있거나 헤더 이름이 일치하지 않음.
- **해결:** 보안을 위해 Gitea와 서버 양쪽에 동일한 비밀번호를 설정하거나, 테스트를 위해 `trigger-rule`을 삭제하여 문턱을 낮춤.
#### 🚀 [Level 3] 스크립트 실행 환경 문제 (npx & node)
- **에러:** `npx: command not found` 또는 `node: No such file or directory`.
- **원인:** `systemd` 서비스는 사용자 터미널 환경변수(`PATH`)를 읽지 못해 NVM에 설치된 Node.js를 찾지 못함.
- **해결:** `deploy.sh` 상단에 Node.js 실행 파일이 있는 **절대 경로**를 `export PATH`로 직접 주입함.
#### 📉 [Level 4] 자원 부족 및 서버 경직 (kswapd0)
- **현상:** 빌드 시작 시 서버가 급격히 느려지며 접속이 끊김.
- **원인:** Quartz 빌드는 CPU와 메모리를 많이 사용하는데, 512MB~1GB 수준의 낮은 메모리로 인해 스왑(Swap) 현상이 심화됨.
- **해결:** 1. **Swap 확장:** 기존 스왑 파일을 **2GB**로 대폭 확장하여 물리 메모리 부족에 대비함.
2. **우선순위 조정:** 빌드 명령어 앞에 **`nice -n 19`**를 붙여 다른 서비스(Nginx, Gitea)에 영향을 주지 않도록 최적화함.
---
### 3. 최종 자동화 아키텍처 (The "Tight" Setup)
|**구성 요소**|**역할**|**최적화 포인트**|
|---|---|---|
|**Gitea**|소스 관리 및 트리거|`/git/` 경로를 제외한 클린 URL 적용|
|**Webhook**|신호 수신기 (Port 29292)|전용 서비스 등록 및 자동 시작 설정|
|**deploy.sh**|배포 스크립트|**절대 경로 사용**, **로그 파일 기록**, **우선순위 최저 설정**|
|**Swap File**|가상 메모리 (2GB)|`/etc/fstab` 등록을 통한 영구 적용|
---
### 📝 교훈
> **경로(Path)와 환경(Environment)은 언제나 명시적이어야 한다.** > 공짜 서버일수록 수동으로 자원을 배분하고 우선순위를 관리하는 **빡빡한 최적화**가 시스템의 생명줄이 된다.
+38
View File
@@ -0,0 +1,38 @@
![[Pasted image 20260407165750.png]]
/home/dihwang/webhook/deploy.sh
```bash
#!/bin/bash
if [ -f /tmp/quartz.lock ]; then exit; fi
# 1. Node와 npx가 있는 경로를 강제로 주입 (v22.22.2 기준)
export PATH=$PATH:/home/dihwang/.nvm/versions/node/v22.22.2/bin
# 2. 작업 디렉토리로 이동
cd /home/dihwang/white-smith-blog/content || exit
# 3. 최신 데이터 동기화
git pull origin master
cd ..
# 4. Quartz 빌드 (이제 npx가 node를 잘 찾을 겁니다)
nice -n 19 npx quartz build
echo "Quartz Build Completed at $(date)"
```
/home/dihwang/webhook/hook.json
```json
[
{
"id": "quartz-deploy",
"execute-command": "/home/dihwang/webhook/deploy.sh",
"command-working-directory": "/home/dihwang/white-smith-blog",
"response-message": "Deploying Quartz...",
}
]
```
@@ -0,0 +1,239 @@
---
id: "gitea, qurtz 설치 20260331"
created: "2026-03-31 10:41"
tags:
---
# 🌐 개인 서버(GCP) 기반 Gitea & HTTPS 구축 가이드
본 문서는 Ubuntu 25.xx 환경에서 가벼운 Git 서비스인 **Gitea**를 설치하고, **Nginx**와 **DuckDNS**를 이용해 **HTTPS** 보안 접속을 구현한 과정을 정리합니다.
## 1. 서버 기본 환경 및 Gitea 설치
가장 가벼운 바이너리 실행 방식으로 Gitea를 설치하고 전용 사용자를 생성했습니다.
- **OS:** Ubuntu 25.xx (GCP Instance)
- **사용자 생성:** `git` 시스템 사용자 생성 (`/home/git`)
- **DB:** SQLite3 (경량화를 위해 선택)
- **바이너리 경로:** `/usr/local/bin/gitea`
- **데이터 경로:** `/var/lib/gitea`
---
## 2. 도메인 및 네트워크 설정
외부 접속을 위해 무료 DDNS 주소를 확보하고 클라우드 방화벽을 조정했습니다.
- **도메인:** `white-smith.duckdns.org` (GCP 외부 IP 연결 완료)
- **포트 개방 (GCP & UFW):**
- `22/tcp`: SSH 접속 (PuTTY, SFTP)
- `80/tcp`: HTTP (인증서 발급 및 리다이렉트용)
- `443/tcp`: HTTPS (실제 서비스용)
- **보안 조치:** 불필요한 포트(20, 21, 3389 등) 차단 및 Gitea 직접 포트(3000)는 Nginx를 거치도록 외부 차단 권장.
---
## 3. HTTPS & Nginx 리버스 프록시
Nginx를 앞단에 세워 보안을 강화하고 도메인 기반 접속을 구현했습니다.
### SSL 인증서 발급
`Certbot`을 이용해 **Let's Encrypt** 무료 SSL 인증서를 발급받았습니다.
Bash
```
sudo certbot --nginx -d white-smith.duckdns.org
```
### Nginx 최종 설정 (`/etc/nginx/sites-available/gitea`)
IP 접속 시 도메인으로 자동 전환되도록 설정했습니다.
Nginx
```
# 1. HTTP -> HTTPS 리다이렉트 (IP 포함)
server {
listen 80;
server_name 34.19.79.94 white-smith.duckdns.org;
return 301 https://white-smith.duckdns.org$request_uri;
}
# 2. HTTPS 서비스 블록
server {
listen 443 ssl;
server_name white-smith.duckdns.org;
# SSL 인증서 경로 (Certbot 관리)
ssl_certificate /etc/letsencrypt/live/white-smith.duckdns.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/white-smith.duckdns.org/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
---
## 4. Gitea 내부 설정 업데이트
HTTPS 환경에 맞춰 `/etc/gitea/app.ini` 파일을 `vi`로 수정했습니다.
- **ROOT_URL:** `https://white-smith.duckdns.org/`
- **DOMAIN:** `white-smith.duckdns.org`
- **SSH_DOMAIN:** `white-smith.duckdns.org`
---
## 5. 현재 상태 요약
1. **HTTPS 접속:** `https://white-smith.duckdns.org`로 안전하게 접속 가능.
2. **IP 리다이렉트:** `34.19.79.94` 입력 시 자동으로 도메인 주소로 전환됨.
3. **파일 전송:** 포트 22번을 통해 SFTP 방식으로 안전하게 파일 관리 중.
# 🚀 Gitea & Quartz 도메인 통합 및 서버 설정 가이드
본 가이드는 단일 메인 도메인(`white-smith.duckdns.org`)에서 **Quartz(블로그)**와 **Gitea(Git 서비스)**를 충돌 없이 운영하기 위한 설정법을 다룹니다.
---
## 1. 서비스 주소 설계
동일 도메인 점유 문제를 해결하기 위해 **하위 경로(Path)** 방식으로 서비스를 분리했습니다.
- **Quartz (메인 블로그):** `https://white-smith.duckdns.org/`
- **Gitea (코드 저장소):** `https://white-smith.duckdns.org/git/`
---
## 2. Gitea 설정 수정 (`app.ini`)
Gitea가 `/git/` 경로를 인식하도록 내부 설정을 변경해야 합니다.
- **파일 위치:** `/var/lib/gitea/custom/conf/app.ini` (또는 설치 경로 내 `custom/conf/app.ini`)
- **수정 내용:**
Ini, TOML
```
[server]
ROOT_URL = https://white-smith.duckdns.org/git/
LOCAL_ROOT_URL = http://127.0.0.1:3000/
HTTP_ADDR = 127.0.0.1
HTTP_PORT = 3000
```
> **Tip:** `LOCAL_ROOT_URL`을 추가해야 내부 리다이렉션 시 404 오류를 방지할 수 있습니다.
---
## 3. Nginx 역방향 프록시 설정
`/etc/nginx/sites-available/gitea` 파일을 수정하여 두 서비스를 통합합니다.
Nginx
```
server {
listen 80;
server_name 34.19.79.94 white-smith.duckdns.org;
return 301 https://white-smith.duckdns.org$request_uri;
}
server {
listen 443 ssl;
server_name white-smith.duckdns.org;
ssl_certificate /etc/letsencrypt/live/white-smith.duckdns.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/white-smith.duckdns.org/privkey.pem;
# [우선순위 1] Gitea 설정
location /git/ {
proxy_pass http://127.0.0.1:3000/; # 끝에 '/' 필수
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# [우선순위 2] Quartz 설정 (메인)
location / {
proxy_pass http://127.0.0.1:8080; # Quartz 실행 포트
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
---
## 4. 권한 관리 및 접근 (etc 경로)
시스템 설정 파일을 편리하게 수정하기 위해 권한을 조정하거나 도구를 활용합니다.
1. **권한 변경 (추천):**
Bash
```
sudo chown -R root:$(whoami) /etc/nginx
sudo chmod -R 775 /etc/nginx
```
2. **포트 확인 명령어:**
Bash
```
sudo ss -tulpn | grep node # Quartz 포트 확인
```
3. **서비스 재시작:**
Bash
```
sudo nginx -t && sudo systemctl restart nginx
sudo systemctl restart gitea
```
---
## 5. 핵심 주의사항
- **Nginx 경로 슬래시:** `proxy_pass http://127.0.0.1:3000/;` 처럼 끝에 `/`가 있어야 `/git/` 경로가 중복되지 않고 올바르게 전달됩니다.
- **Quartz 실행:** Quartz를 `npx quartz build --serve`로 띄운 경우 해당 포트(보통 8080)가 Nginx 설정과 일치해야 합니다.
- **보안:** `/etc` 전체 권한을 `777`로 바꾸지 마세요. 필요한 폴더만 그룹 권한을 부여하는 것이 안전합니다.
+51
View File
@@ -0,0 +1,51 @@
---
id: "kui-vault 20260401"
created: "2026-04-01 14:59"
tags:
---
**Google Cloud Platform(GCP)** 를 사용해서 구축함.
### 컴퓨팅 (Compute)
사용자가 코드를 실행할 공간을 제공합니다.
- **Compute Engine (GCE):** 가상 머신(VM)을 직접 생성하고 관리하는 IaaS 서비스입니다. (AWS의 EC2와 유사)
GCE를 한대 빌려서 구축하였음
![[Pasted image 20260401150036.png]]
원래는 퍼블릭하게 공개되면 안되는 자료들을 블러처리해주는 서버를 만들려고 (kui-veil) 확보한 서버였음
![[Pasted image 20260401163033.png]]
## 🖥️ Server Information: **kui-veil**
#### 1. 기본 호스트 정보
| **항목** | **내용** |
| ---------------------- | ---------------------------------- |
| **Static Hostname** | (unset) |
| **Transient Hostname** | **kui-veil** |
| **Machine ID** | `b723eb615fc74ff3ac0de806f770d293` |
| **Boot ID** | `2ce0c86e6d704281a6c28054cc4eba0e` |
#### 2. 운영체제 및 커널
|**항목**|**내용**|
|---|---|
|**Operating System**|**Ubuntu 25.10**|
|**Kernel Version**|**Linux 6.17.0-1007-gcp**|
|**Architecture**|**x86-64** (64비트)|
#### 3. 하드웨어 및 가상화 (GCP)
|**항목**|**내용**|
|---|---|
|**Virtualization**|**google** (Google Compute Engine)|
|**Hardware Vendor**|**Google**|
|**Chassis**|**vm** 🖴|
|**Firmware Date**|2026-02-12 (약 1.5개월 전)|
### 💡 주요 특징 메모
- **최신 버전 사용 중:** 현재 **Ubuntu 25.10**을 사용 중이시네요. 이는 매우 최신 배포판으로, 최신 커널 기능과 패키지들을 지원합니다.
- **GCP 최적화:** 커널 이름에 `-gcp`가 붙어 있어, 구글 클라우드 인프라에 최적화된 환경에서 동작하고 있음을 알 수 있습니다.
- **상태 요약:** 펌웨어 날짜가 2026년 2월인 것으로 보아, 시스템이 최근에 업데이트되었거나 생성된 아주 따끈따끈한 환경입니다!
@@ -0,0 +1,63 @@
---
id: "worker server spec 20260414"
created: "2026-04-14 13:28"
tags:
---
ubuntu@instance-20260414-0951:~$ **lscpu**
Architecture: aarch64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Vendor ID: ARM
Model name: Neoverse-N1
Model: 1
Thread(s) per core: 1
Core(s) per socket: 4
Socket(s): 1
Stepping: r3p1
BogoMIPS: 50.00
Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics
fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
NUMA:
NUMA node(s): 1
NUMA node0 CPU(s): 0-3
Vulnerabilities:
Gather data sampling: Not affected
Ghostwrite: Not affected
Indirect target selection: Not affected
Itlb multihit: Not affected
L1tf: Not affected
Mds: Not affected
Meltdown: Not affected
Mmio stale data: Not affected
Old microcode: Not affected
Reg file data sampling: Not affected
Retbleed: Not affected
Spec rstack overflow: Not affected
Spec store bypass: Mitigation; Speculative Store Bypass disabled via p
rctl
Spectre v1: Mitigation; __user pointer sanitization
Spectre v2: Mitigation; CSV2, BHB
Srbds: Not affected
Tsa: Not affected
Tsx async abort: Not affected
Vmscape: Not affected
ubuntu@instance-20260414-0951:~$ **free -h**
total used free shared buff/cache available
Mem: 23Gi 691Mi 21Gi 5.4Mi 1.9Gi 22Gi
Swap: 0B 0B 0B
ubuntu@instance-20260414-0951:~$ **df -h**
Filesystem Size Used Avail Use% Mounted on
tmpfs 2.4G 1.5M 2.4G 1% /run
efivarfs 256K 14K 243K 6% /sys/firmware/efi/efivars
/dev/sda1 193G 3.1G 190G 2% /
tmpfs 12G 0 12G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/sda16 891M 94M 735M 12% /boot
/dev/sda15 98M 6.4M 92M 7% /boot/efi
tmpfs 2.4G 12K 2.4G 1% /run/user/1001
+3
View File
@@ -0,0 +1,3 @@
---
title: volume
---
+52
View File
@@ -0,0 +1,52 @@
---
id: 전표처리 20260413
created: 2026-04-13 08:42
tags:
---
![[전표#📑 개념]]
회사 법인카드 사용하고나서 [[분개전표]] 처리를 해야한다. ([[전표]])
돈을 얼마나 썼는지, 왜 썼는지에 대한 상세사항정리 정도로 생각하면 됨.
> [!warning]
**분개전표 입력전에 법인카드사용내역회계전표생성(개인) 부터 먼저해야함.**
여기에 등록하고 승인나면 그때 자동으로 입력된 전표를 처리해야 함.
![[Pasted image 20260427095731.png]]
위 화면에선 특별히 추가할 거 없이 결재상신 하면 됨
1. 30만원 이하  PM -> 팀장 -> 사업부장, 합의 문새별 -> 김성민 입니다. (PM이 없는 경우나 PM이 팀장인 경우는 PM 생략)
결제 상세는 [[법인카드 결제상신 경로]] 참고
> [!note] 부가세 여부
[[부가세여부]] 참고
업무용 택시비, 소모품비, 숙소비는 부가세여부 O
일반차량 유류비, 톨게이트비 부가세여부 X
> [!warning] 법인카드사용내역회계전표생성(개인) 부터 먼저 결제하지 않고 진행하면...
전표 유형이 법인카드사용내역전표(개인) 이 아니고 일반분개전표 로 진행이 되어버림..
![[Pasted image 20260427133315.png]]
ERP에서 ESS -> 회계 -> 전표입력 으로 가서 전표입력 하면 됨.
![[분개(分介)#예시 이미지]]
위와같은 형태로 입력한다.
0002 대변금액은 직접 적는게 아니고 0001 차변금액을 아래로 드래그해서 추가하면 됨.
여기서 전표를 추가하고 그룹웨어에서 결제 진행하면 됨.
> [!info] 법인카드를 실수로 잘못 긁었을 떄
잘못결제한 건 취소하고 결제건 + 취소건 두개 다 erp에서 중단처리 한다.
![[Pasted image 20260507155619.png]]
+26
View File
@@ -0,0 +1,26 @@
---
id: ACID 20260420
created: 2026-04-20 08:44
tags:
aliases:
---
---
## 📑 개념
DB에서 하나의 논리적 작업 단위를 '트랜잭션'이라고 합니다. RDBMS는 이 트랜잭션이 안전하게 처리됨을 보장하기 위해 ACID 특성을 가집니다.
## 📌 상세
- **Atomicity (원자성):** 전부 성공하거나, 전부 실패하거나. - "All or Nothing"
트랜잭션 내의 모든 연산은 반드시 한꺼번에 완료되어야 하며, 그렇지 못할 경우 모두 취소되어야 합니다.
**예:** 계좌 이체 시 '내 계좌 출금'은 성공했는데 '상대 계좌 입금'에서 오류가 나면, 내 계좌의 출금 내역도 취소되어야 합니다.
- **Consistency (일관성):** 완료 후 DB 상태가 규칙을 위반하지 않음.
트랜잭션 수행 전후의 데이터베이스 상태는 항상 논리적으로 올바른 상태여야 합니다.
**예:** 모든 계좌의 잔액은 0원 이상이어야 한다는 제약 조건이 있다면, 트랜잭션 후 잔액이 마이너스가 되는 결과는 허용되지 않습니다.
- **Isolation (고립성):** 동시에 실행되는 트랜잭션들이 서로 방해하지 않음.
동시에 여러 트랜잭션이 실행될 때, 각 트랜잭션은 서로의 작업에 간섭할 수 없습니다. 하나의 트랜잭션이 끝나기 전까지는 다른 트랜잭션에서 그 중간 결과를 볼 수 없습니다.
- **Durability (지속성):** 성공한 결과는 영구적으로 저장됨.
성공적으로 완료된 트랜잭션의 결과는 시스템 장애가 발생하더라도 영구적으로 기록되어야 합니다. 로그 파일 등을 통해 복구가 가능한 상태를 말합니다.
---
+52
View File
@@ -0,0 +1,52 @@
---
id: CPython 20260410
created: 2026-04-10 09:16
tags:
- python
aliases:
---
## 💡 생각
Python은 일종의 문법이고 이 문법을 cpu가 알아먹을 수 있게 변환해주는 엔진이 CPython이다.
> [!question]
> Q. Python은 일종의 문법이고 이 문법을 cpu가 알아먹을 수 있게 변환해주는 엔진이 CPython이다.
> 라고 보면되는거야?
>
> A. 정확한 요약입니다! 비유하자면 **Python은 언어(설계도)**이고, **CPython은 그 언어를 실행 가능한 기계어로 번역해주는 번역가(구현체)** 라고 볼 수 있습니다.
그래서 CPython 을 사용할 때 Visual Studio Build Tools 가 필요한가보다.
기반이 C니까..
---
## 📑 개념
CPython은 Python 프로그래밍 언어의 **기본 구현체**이자, 전 세계에서 가장 널리 사용되는 파이썬 실행기입니다. 파이썬 언어 자체는 '명세(Specification)'이고, 이를 실제로 컴퓨터에서 돌아가게 만든 '소프트웨어'가 바로 CPython입니다.
## 📌 파이썬의 표준 구현체: CPython
우리가 터미널에 `python`이라고 입력해서 실행하는 프로그램의 정식 명칭은 **CPython**입니다. 이는 파이썬이라는 언어의 설계도(명세)를 **C 언어**로 구현한 결과물입니다.
- **해석기(Interpreter):** 파이썬 코드를 읽어서 컴퓨터가 이해할 수 있는 바이트코드로 바꾸고 실행하는 엔진 자체가 C로 작성되어 있습니다.
- **자료형:** 파이썬의 숫자(`int`), 리스트(`list`), 딕셔너리(`dict`) 같은 기본 자료형들도 내부적으로는 C의 구조체(struct)로 정의되어 있습니다.
## 주요 특징
- **C 언어로 작성:** 이름에서 알 수 있듯이 C 언어로 개발되었습니다. 파이썬 코드를 실행하기 위해 C 언어의 성능과 시스템 접근 능력을 활용합니다.
- **인터프리터이자 컴파일러:** 파이썬 코드를 바로 실행하는 것처럼 보이지만, 내부적으로는 파이썬 코드를 **바이트코드(Bytecode)** 로 먼저 컴파일한 후, 이를 CPython 가상 머신(VM)에서 해석하여 실행합니다.
- **표준 구현체:** 파이썬 재단(PSF)에서 관리하며, 새로운 파이썬 기능이 추가될 때 가장 먼저 반영되는 기준점이 됩니다.
## 📝 파이썬을 실행하는 도구는 CPython 외에도 여러 가지가 있습니다.
> [!note]
> |**종류**|**특징**|
|---|---|
|**PyPy**|JIT(Just-In-Time) 컴파일러를 사용하여 CPython보다 실행 속도가 훨씬 빠름.|
|**Jython**|자바 가상 머신(JVM) 위에서 동작하며 자바 라이브러리 활용 가능.|
|**IronPython**|.NET 프레임워크 기반으로 동작.|
---
하지만 Python 이라고 하면 그냥 CPython 이라고 보면 됨.
하지만 **CPython**이 가장 널리 쓰이기 때문에, 보통 우리가 파이썬이라고 부르는 것은 이 CPython 엔진을 뜻하는 경우가 99%입니다.
라고 함.
@@ -0,0 +1,49 @@
---
id: "DHCP(Dynamic Host Configuration Protocol) 20260428"
created: "2026-04-28 14:21"
tags:
aliases:
---
## 💡 생각
이곳에 하나의 생각 또는 아이디어를 작성합니다.
---
## 📑 개념
네트워크 안에 있는 장치들에게 IP 주소를 자동으로 할당해 주는 통신 규약입니다.
매번 사람이 직접 장치마다 IP를 입력하는 번거로움을 없애고, IP 충돌 같은 네트워크 사고를 방지하기 위해 사용됩니다.
## DHCP가 하는 일
네트워크에 새로운 기기(노트북, 스마트폰 등)가 연결되면, DHCP 서버는 다음과 같은 정보를 자동으로 제공합니다.
- **IP 주소:** 기기의 고유 식별 주소
- **서브넷 마스크:** 네트워크 범위를 결정하는 값
- **기본 게이트웨이:** 외부 인터넷으로 나가는 통로 주소
- **DNS 서버 주소:** 도메인 이름을 IP로 변환해 주는 서버 주소
## DHCP 사용의 장점
- **관리의 편리함:** 관리자가 일일이 IP를 할당하지 않아도 서버가 알아서 빈 주소를 찾아줍니다.
- **효율적인 IP 자원 관리:** 사용 중이지 않은 IP는 회수하여 다른 기기에 줄 수 있으므로 한정된 IP 자원을 아낄 수 있습니다. (이를 **임대 시간/Lease Time**이라고 합니다.)
- **설정 오류 방지:** 오타나 중복된 IP 입력으로 인한 네트워크 연결 오류를 방지할 수 있습니다.
## 임대(Lease)와 갱신(Renewal)
DHCP는 IP를 영구적으로 주는 것이 아니라, 특정 시간 동안 **빌려주는(Lease)** 개념입니다.
- **임대 시간:** IP를 사용할 수 있는 유효 기간입니다.
- **갱신:** 보통 임대 시간의 50%가 지나면 클라이언트는 서버에 "이 IP를 계속 써도 될까요?"라고 물어보고 사용 시간을 연장합니다. 만약 기기가 네트워크를 떠나면 임대 시간이 만료된 후 해당 IP는 다시 서버로 회수되어 다른 기기가 쓸 수 있게 됩니다.
> [!note] 우리가 흔히 쓰는 공유기가 바로 이 DHCP 서버 역할을 수행하여 집 안의 여러 기기에 IP를 나눠주고 있습니다.
>
---
@@ -0,0 +1,25 @@
---
id: "DRY (Don't Repeat Yourself) 20260330"
created: "2026-03-30 15:05"
tags:
aliases:
---
## 💡 생각
코드의 반복을 줄여서 유지보수를 용이하게 하자.
단, 코드의 반복을 무조건적으로 줄이는 것은 좋지 않은 결과를 낳을 수 있다.
[[DRY (Don't Repeat Yourself)]] << [[KISS (Keep It Simple, Stupid)]]
DRY한 코드보다 KISS한 코드를 만드는 게 더 중요하다.
---
## 📑 개념
**똑같은 일을 반복하지 마라**라는 뜻입니다.
동일한 코드의 중복을 피하라는 의미.
## 📌 상세
1. 코드의 정보나 로직은 시스템 내에서 **단 한 곳**에만 존재해야 합니다.
2. 똑같은 로직이 여기저기 복사되어 있으면, 나중에 수정할 때 모든 곳을 다 찾아내서 고쳐야 합니다. 하나라도 놓치면 바로 버그로 이어집니다.
3. 중복되는 코드가 보이면 함수나 클래스로 추출하여 공통화하세요.
4. 3번 이상 반복되는 코드에 적용하는 것이 좋음.
( 2번 이하로 반복되는 코드를 DRY하게 하다 보면 오히려 코드의 복잡성이 증가해서 [[KISS (Keep It Simple, Stupid)]] 원칙을 깨는 경우가 발생될 수 있음.)
---
@@ -0,0 +1,7 @@
| **비교 항목** | **EC2 기반 컨테이너 (ECS/EKS)** | **Fargate 기반 컨테이너 (ECS/EKS)** |
| ----------------- | -------------------------- | ----------------------------- |
| **인프라 관리** | 사용자가 EC2 인스턴스 관리 (OS 패치 등) | **AWS가 인프라 완전 관리** |
| **확장성 (Scaling)** | EC2 인벤토리와 컨테이너 개수 동시 고려 | **컨테이너(Task/Pod) 개수만 조절** |
| **격리 수준** | 인스턴스 내 커널 공유 가능성 있음 | **Task/Pod 단위의 강력한 커널 격리** |
| **과금 방식** | 인스턴스 크기 및 가동 시간 기준 | **할당된 CPU 및 메모리 리소스 사용량 기준** |
@@ -0,0 +1,27 @@
- 작성 **날짜:** 2026-02-27
## 📑 개념
> [!abstract]
> 클라우드에서 제공하는 **가상 서버**, EC2는 AWS 클라우드에서 확장 가능한 컴퓨팅 용량을 제공합니다. 하드웨어를 직접 구매할 필요 없이 가상 서버를 구축하고, 보안 및 네트워킹을 구성하며 저장소를 관리할 수 있습니다.
- **Elastic (탄력적인):** 수요에 따라 서버 대수를 자유롭게 늘리거나 줄일 수 있습니다.
- **Compute (컴퓨팅):** CPU, 메모리 등 계산 능력을 제공합니다.
- **Cloud (클라우드):** 인터넷을 통해 어디서든 접근 가능한 가상 환경입니다.
## 📌 상세
> [!check]
> |**요소**|**명칭**|**설명**|
> |---|---|---|
> |**운영체제**|**AMI** (Amazon Machine Image)|Windows, Linux 등 서버에 설치될 OS와 기본 설정이 담긴 이미지입니다.|
> |**하드웨어 사양**|**인스턴스 유형**|CPU, RAM, 네트워크 성능에 따른 규격입니다. (예: t3.medium, c5.large)|
> |**저장 장치**|**EBS** (Elastic Block Store)|서버의 하드디스크 역할을 하는 가상 디스크입니다.|
> |**보안/네트워크**|**보안 그룹** (Security Group)|가상 방화벽으로, 어떤 포트(80, 443, 22 등)를 열어줄지 설정합니다.|
> |**접속 인증**|**키 페어** (Key Pair)|서버 접속 시 사용하는 비밀번호 대신의 암호화 키 파일입니다.|
## 📝 노트
> [!note]
>
> - AWS(혹은 클라우드 서비스 제공자)로부터 가상의 서버를 임대해서 사용하는 것
> - 원하는 사양으로 요청하면 대여해서 사용할 수 있음
>
## 🔗 지식 연결
- **태그:** #zettelkasten #knowledge
@@ -0,0 +1,8 @@
| **구분** | **주요 한계점 및 단점** | **세부 설명** |
| ----------------------- | ------------------------------- | -------------------------------------------------------------------------- |
| **운영 관리 부담 (Overhead)** | **OS 및 소프트웨어 관리** | OS 패치, 보안 업데이트, 런타임 설치 및 관리를 사용자가 직접 수행해야 함 (Shared Responsibility Model). |
| **확장성 (Scalability)** | **느린 오토스케일링 속도** | 새로운 인스턴스를 띄우고 OS 부팅, 애플리케이션 실행까지 수 분의 시간이 소요되어 급격한 트래픽 변화에 대응이 늦음. |
| **자원 효율성** | **낮은 집적도 및 낭비** | 특정 프로세스가 CPU/RAM을 적게 쓰더라도 인스턴스 전체 비용이 발생함. 컨테이너 대비 자원 격리 및 배분 효율이 낮음. |
| **배포 및 일관성** | **"It works on my machine" 문제** | 개발 환경과 운영 서버 OS 설정이 다를 경우 장애 발생 가능성이 높음 (컨테이너 대비 환경 일관성 부족). |
| **고가용성 (HA)** | **복잡한 복구 프로세스** | 인스턴스 장애 시 단순히 프로세스를 재시작하는 것보다 교체 및 복구 과정이 더 무겁고 복잡함. |
| **비용 (Cost)** | **유휴 자원 비용 발생** | 트래픽이 없는 시간대에도 최소 유지 인스턴스 비용이 계속 발생하며, 세밀한 과금 단위 설정이 어려움. |
@@ -0,0 +1,52 @@
---
id: "ECR 20260305"
created: "2026-03-05 09:49"
tags:
---
## 💡 생각
도커 이미지 허브 (유료)
---
## 📑 개념
> [!abstract]
> AWS에서 제공하는 **완전관리형 컨테이너 이미지 레지스트리 서비스**
## 📌 상세
> [!check]
> 1. 개발자들이 만든 도커(Docker) 이미지를 안전하게 보관하고 필요할 때 꺼내 쓸 수 있는 **'클라우드 보관함'**이라고 생각하시면 됩니다.
> 2. 가장 유명한 'Docker Hub'의 AWS 버전이라고 이해하면 쉽습니다.
### 1. 주요 특징 및 기능
- **완전관리형 서비스:** 서버를 직접 관리하거나 스토리지 용량을 걱정할 필요가 없습니다. AWS가 인프라 운영과 확장을 알아서 처리합니다.
- **보안 및 통합:** AWS [[IAM(Identity and Access Management)]]과 통합되어, 특정 사용자나 EC2 인스턴스만 이미지에 접근할 수 있도록 세밀하게 권한을 제어할 수 있습니다.
- **이미지 스캔:** 업로드한 이미지에 보안 취약점이 있는지 자동으로 스캔하여 알려주는 기능을 제공합니다.
- **수명 주기 정책(Lifecycle Policy):** 오래된 이미지나 태그가 없는 이미지를 자동으로 삭제하도록 설정하여 저장 비용을 절감할 수 있습니다.
- **고가용성:** 이미지가 S3에 저장되므로 데이터 내구성이 매우 높으며, 여러 가용 영역(AZ)에 걸쳐 안정적으로 배포됩니다.
### 2. 작동 방식 (워크플로우)
1. **빌드(Build):** 로컬 PC나 CI/CD 환경에서 Docker 이미지를 생성합니다.
2. **인증(Authenticate):** AWS CLI를 통해 ECR에 로그인합니다.
3. **푸시(Push):** 생성된 이미지를 ECR 리포지토리(Repository)에 업로드합니다.
4. **풀(Pull):** Amazon ECS, EKS, 또는 Lambda 같은 서비스에서 실행 시점에 해당 이미지를 내려받아 컨테이너를 구동합니다.
---
#### ECR의 장점
|**구분**|**설명**|
|---|---|
|**속도**|AWS 내부 네트워크를 사용하므로 ECS나 EKS로 이미지를 배포할 때 속도가 매우 빠릅니다.|
|**비용**|사용한 스토리지 용량과 데이터 전송량에 대해서만 비용을 지불하며, AWS 내부 서비스 간의 데이터 전송은 무료인 경우가 많습니다.|
|**신뢰성**|퍼블릭 레지스트리(Docker Hub 등)의 장애나 속도 저하로부터 독립된 안정적인 환경을 구축할 수 있습니다.|
@@ -0,0 +1,9 @@
| **구분** | **Amazon ECS (Elastic Container Service)** | **Amazon EKS (Elastic Kubernetes Service)** |
| ----------- | ------------------------------------------ | ------------------------------------------- |
| **개념** | AWS 전용 컨테이너 관리 서비스 | AWS에서 제공하는 **관리형 쿠버네티스** |
| **복잡도** | **낮음** (설정과 관리가 매우 간결함) | **높음** (쿠버네티스 숙련도 필수) |
| **유연성/제어권** | AWS 환경에 최적화, 제어권은 제한적 | 매우 높음 (오픈 소스 기반의 무한한 커스텀) |
| **이식성** | AWS 전용 기술로 타 클라우드 이동이 어려움 | **매우 높음** (어디서든 동일한 K8s 환경 구동) |
| **관리 단위** | **Task** (태스크) | **Pod** (포드) |
| **학습 곡선** | 완만함 (비교적 빨리 배울 수 있음) | 매우 가파름 (개념과 리소스 종류가 방대함) |
| **적합한 상황** | AWS 서비스 위주의 빠르고 간결한 운영 | 복잡한 마이크로서비스, 표준 기술 스택 지향 |
@@ -0,0 +1,78 @@
---
id: FHS(Filesystem Hierarchy Standard) 20260403
created: 2026-04-03 11:04
tags:
aliases:
---
## 💡 생각
파일구조에 자유도를 억제해서 어느정도 규격화를 해놓은 것,
너무 자유로우면 무슨 파일이 어디에 있는지 확인하기가 매우 어려울텐데
어떤 종류의 파일은 어디에 있어야 하고 이런 규칙을 정의해서
어떤 리눅스를 사용하든 어느정도의 규칙이 존재하기 때문에 원하는 파일을 어느정도 쉽게 찾아갈 수 있게끔 함
---
## 📑 개념
뿌리가 되는 **루트(`/`)** 아래에 모든 것이 가지처럼 뻗어 나가는 **역트리 구조**
리눅스나 유닉스 계열 운영체제를 사용하다 보면 폴더 구조가 왜 이렇게 복잡한지 궁금할 때가 있죠.
**FHS(Filesystem Hierarchy Standard)**는 바로 이런 혼란을 방지하기 위해 파일과 디렉터리의 위치를 정의한 표준 규격입니다.
덕분에 사용자나 소프트웨어 개발자는 어떤 리눅스 배포판을 사용하더라도 특정 파일이 어디에 있을지 예측할 수 있습니다.
## 📌 상세
- **/ (root)**: 모든 파일과 디렉터리의 시작점
: 모든 디렉토리의 시작점입니다. 윈도우의 `C:\`와 비슷하지만, 리눅스에서는 모든 장치(하드디스크, USB 등)가 이 아래의 특정 폴더에 연결(마운트)됩니다.
--- 📂 주요 디렉토리
- **/bin**: 필수적인 사용자 명령 파일 (Binaries)
: 모든 사용자가 쓸 수 있는 기본 명령어들이 들어 있습니다.
- **/sbin**: 시스템 관리용 실행 파일 (System Binaries)
: 시스템 관리자(Root)가 사용하는 명령어들이 모여 있습니다.
- **/etc**: 시스템 설정 파일 (Editable Text Configurations)
: **가장 중요한 폴더 중 하나입니다.** 시스템의 모든 **설정 파일**이 들어 있습니다.
: 사용자 정보(`/etc/passwd`), 네트워크 설정, 설치된 프로그램의 환경 설정 등이 모두 여기에 텍스트 파일로 저장됩니다.
- **/home**: 일반 사용자들의 홈 디렉터리
: 일반 사용자들의 개인 공간, 예를 들어 계정명이 `dihwang`이라면 `/home/dihwang` 폴더가 생기고, 그 안에 바탕화면, 다운로드, 개인 설정 등이 저장됩니다.
: 관리자(root)를 제외한 모든 사용자는 자신의 홈 디렉토리 밖에서는 파일을 맘대로 만들거나 지울 수 없습니다.
- **/root**: 시스템 관리자(root)의 홈 디렉터리
: 시스템 최고 관리자인 **root 사용자의 홈 디렉토리**입니다. 일반 사용자 홈 디렉토리(`/home`)와는 별도로 관리됩니다.
---
--- ⚙️ 시스템 운영을 위한 폴더들
- **/usr**: 사용자 관련 모든 프로그램과 데이터 (User System Resources)
: 사용자가 설치한 프로그램과 데이터들이 모이는 곳입니다. 윈도우의 `C:\Program Files`와 가장 비슷합니다.
: `/usr/bin`: 나중에 설치한 응용 프로그램들의 실행 파일.
: `/usr/lib`: 프로그램 실행에 필요한 공유 라이브러리들.
- **/var**: 가변적인 데이터 저장소 (Variable)
: **내용이 수시로 변하는 데이터**가 저장됩니다.
: `/var/log`: 시스템 로그가 쌓이는 곳입니다. 서버가 왜 죽었는지 확인할 때 필수입니다.
: `/var/www`: 웹 서버(Apache, Nginx)의 소스 파일들이 기본적으로 위치하는 곳입니다.
- **/tmp**: 임시 파일 저장소 (Temporary)
: **임시 파일** 저장소입니다. 부팅 시나 일정 시간이 지나면 자동으로 삭제되는 파일들이 머뭅니다. 누구나 파일을 쓰고 지울 수 있습니다.
- **/dev**: 장치 파일 (Devices)
: 리눅스는 **모든 것을 파일로 관리한다**는 철학이 있습니다. 마우스, 키보드, 하드디스크 같은 물리적인 장치들도 이 폴더 안에 파일 형태로 존재합니다.
- **/mnt**: 수동으로 마운트하는 임시 지점 (Mount)
: 관리자가 수동으로 하드디스크 등을 연결할 때 쓰는 임시 장소.
- **/media**: 외부 장치 자동 마운트 지점 (USB, CD-ROM 등)
: USB나 CD-ROM을 꽂으면 자동으로 연결되는 곳.
- **/opt**: 추가 패키지 소프트웨어 설치 (Optional)
- **/boot**: 부팅 관련 핵심 파일
- **/srv**: 시스템이 제공하는 서비스 데이터 (Service)
: 요즘은 `/var/www` 등을 더 많이 씁니다.
- **/tmp**: 임시 파일 저장소 (Temporary)
- **/lib**: 공유 라이브러리 및 커널 모듈 (Libraries)
---
+38
View File
@@ -0,0 +1,38 @@
---
id: "GNU 20260407"
created: "2026-04-07 14:17"
tags:
aliases:
---
## 💡 생각
이곳에 하나의 생각 또는 아이디어를 작성합니다.
---
## 📑 개념
**GNU(그누)** 는 리처드 스톨먼이 1983년에 시작한 **자유 소프트웨어 프로젝트**이자, 그 결과로 만들어진 **운영체제**의 이름입니다.
GNU라는 이름은 **GNU is Not Unix**(GNU는 유닉스가 아니다)의 약자로, 자기 자신을 이름 안에 포함해 정의하는 재귀적 약어입니다.
## 1. GNU 프로젝트의 목적
당시에는 소프트웨어 소스 코드를 기업이 독점하고 유료로 판매하는 것이 당연시되었습니다. 리처드 스톨먼은 이에 반대하며 **누구나 소프트웨어를 실행하고, 복제하고, 수정하고, 배포할 수 있는 자유**를 누려야 한다고 주장했습니다. 이를 실현하기 위해 완전히 자유로운 운영체제를 밑바닥부터 만드는 것이 프로젝트의 목표였습니다.
## 2. 주요 구성 요소
운영체제는 단순히 커널(알맹이)만 있다고 작동하지 않습니다. GNU 프로젝트는 운영체제에 필요한 거의 모든 도구를 직접 만들었습니다.
- **GCC (GNU Compiler Collection):** 프로그램을 만드는 컴파일러
- **Glibc:** 시스템 자원을 사용하기 위한 기본 라이브러리
- **Bash:** 사용자의 명령을 입력받는 셸
- **Emacs:** 강력한 기능을 가진 텍스트 에디터
이러한 도구들은 현재 리눅스 환경에서도 표준처럼 사용되고 있습니다.
## 3. GNU와 리눅스의 관계 (중요!)
GNU 프로젝트는 운영체제의 거의 모든 구성 요소를 완성했지만, 정작 시스템의 두뇌 역할을 하는 **커널(Hurd)** 개발이 늦어지고 있었습니다.
이때 1991년, 리누스 토르발스가 **리눅스 커널**을 공개했습니다. GNU 프로젝트가 만든 수많은 소프트웨어와 리눅스 커널이 결합하면서 비로소 완벽하게 작동하는 자유 운영체제가 탄생했습니다. 우리가 흔히 부르는 리눅스는 정확히 말하면 **GNU/리눅스**인 셈입니다.
@@ -0,0 +1,60 @@
---
id: "IAM(Identity and Access Management) 20260316"
created: "2026-03-16 13:35"
tags:
---
## 💡 생각
사용자(User)가 가지는 권한이 무엇인지를 나열하고 필요한 사용자 혹은 그룹에 필요한 권한들을 원하는대로 부여할 수 있도록 구성해놓은 웹 서비스
AWS의 고유개념이 아니고 정보 보안 분야에서는 오랫동안 존재해온 표준적인 개념이자 기술 프레임워크.
---
## 📑 개념
"누가(인증)", "어떤 리소스에(권한)" 접근할 수 있는지를 중앙에서 관리를 해서 리소스에 대한 접근을 안전하게 제어할 수 있게 해주는 웹 서비스입니다.
## 📌 IAM의 핵심 구성 요소
- **사용자 (User):** 실제 사람이나 서비스(애플리케이션)를 나타냅니다. 고유한 보안 자격 증명(ID/PW 또는 Access Key)을 가집니다.
- **그룹 (Group):** 사용자들의 집합입니다. 그룹에 권한을 부여하면 그 그룹에 속한 모든 사용자가 동일한 권한을 상속받습니다. (예: `Developers` 그룹, `Admins` 그룹)
- **역할 (Role):** 특정 사용자에게 귀속되지 않고, **필요할 때만 잠시 빌려 쓰는 권한**입니다. 주로 EC2 서버나 Lambda 함수 같은 AWS 서비스가 다른 서비스에 접근할 때 사용합니다.
- **정책 (Policy):** "무엇을 할 수 있는지"를 정의한 **JSON 문서**입니다. 사용자, 그룹, 역할에 이 정책을 연결(Attach)하여 권한을 부여합니다.
### 주요 기능과 특징
| **기능** | **설명** |
| ------------------------- | ------------------------------------------------------------------ |
| **세분화된 권한 제어** | 특정 S3 버킷의 파일만 읽게 하거나, 특정 시간대에만 접속을 허용하는 등 매우 정밀한 설정이 가능합니다. |
| **다요소 인증 (MFA)** | 아이디/비번 외에 OTP(Google Authenticator 등)를 추가하여 보안을 한층 강화할 수 있습니다. |
| **자격 증명 연동 (Federation)** | 기업의 기존 계정(Active Directory 등)이나 구글/페이스북 계정으로 AWS에 로그인할 수 있게 지원합니다. |
| **비용 무료** | IAM 서비스 자체 사용에 따른 추가 비용은 없습니다. |
## 📝 노트
> [!note]
>
> ## IAM 설계의 황금률: "최소 권한의 원칙"
>
> 보안 사고를 막기 위해 IAM을 운영할 때 가장 중요한 원칙은 **최소 권한의 원칙 (Least Privilege)**입니다.
>
> - 사용자에게 업무에 **꼭 필요한 권한만** 부여합니다.
>
> - 루트(Root) 계정은 가급적 사용하지 않고, 일상 업무용 IAM 사용자를 따로 만들어 사용합니다.
>
---
![[Pasted image 20260316134417.png]]
사용자가 하나 있고 역할은 21개가 있고 정책은 1개가 생성되어있음.
![[Pasted image 20260316134502.png]]
![[Pasted image 20260316134525.png]]
사용자는 dihwang 하나가 있는데 AdministratorAccess 라는 이름의 정책을 가지고있다.
![[Pasted image 20260316134705.png]]
이 AdministratorAccess 정책에는 총 464개의 권한이 있다.
즉, dihwang 사용자는 464개의 기능을 사용할 권한이 있다는 뜻이 된다.
@@ -0,0 +1,36 @@
---
id: KISS (Keep It Simple, Stupid) 20260330
created: 2026-03-30 14:53
tags:
aliases:
---
## 💡 생각
[[단순한 코드]]를 만들어야 한다. 가독성을 항상 최우선시 하자.
단순한 형태로 쉽게쉽게 읽히는 코드를 만드는 게 중요하다.
코드의 유연성 확보는 그 다음 문제다.
---
## 📑 개념
**단순하게 유지해, 이 바보야**라는 뜻입니다. (여기서 바보는 나 자신이나 동료를 뜻하는 애칭 섞인 농담입니다.)
해결책은 항상 **가장 단순한 형태**여야 합니다.
## 📌 상세
코드가 똑똑해 보이고 화려할수록, 나중에 버그가 생겼을 때 고치기가 수십 배 더 힘들어집니다. 진짜 실력자는 어려운 문제를 어렵게 푸는 사람이 아니라, **어려운 문제를 누구나 이해할 수 있게 단순하게 푸는 사람**입니다.
복잡한 디자인 패턴이나 최신 기술을 억지로 끼워 넣지 마세요. `if-else`로 충분하다면 그렇게 짜는 것이 가장 좋습니다.
> [!question]
> Q. kiss의 경우 나도 많이 궁금했고 어려웠던건데 디자인 패턴이나 MVC패턴같은 개발 패턴들을 굳이 쓰지말고 일단은 단순하게 if-else 로 만들어란 뜻으로 해석해야 하는거야?
> A. 네, 정확하게 짚으셨습니다! **KISS** 원칙의 핵심은 **정답을 미리 정해놓고 끼워 맞추는 것이 아니라, 문제에 대한 가장 직관적인 해결책부터 시작하라**는 것입니다.
단순 if-else 로 구성된 코드가 무조건 잘못된 게 아님.
미래에 내가 다시 보거나 내가 아닌 다른사람이 내 코드를 읽었을 때 코드의 의도가 잘 이해되면 잘 만든 코드임.
디자인 패턴은 패턴의 이해가 필요하고 곧장 읽히지는 않기 때문에 디자인 패턴을 적용한 코드가 항상 가독성이 좋은 코드일수는 없음.
일단은 if-else 등의 단순한 형태로 작동되는 코드를 만드는 것이 중요함.
## 📝 노트
> [!note]
>
> - 코드의 중복이 발생되고 코드의 유연성이 떨어진다고 판단될 때 디자인 패턴을 적용하면 됨
>
---
@@ -0,0 +1,16 @@
---
id: "MVP(Minimum Viable Product) 20260320"
created: "2026-03-20 14:00"
tags:
aliases:
---
## 💡 생각
당장 꼭 필요한 기능만 포함된 최소한의 제품을 최대한 빠르게 만들어내는 게 핵심 가치
미완성본을 말하는 것이 아닌 최소한의 기능은 정상적으로 동작해야함.
---
## 📑 개념
직역하면 **생존 가능한 최소한의 제품**을 의미합니다.
단순히 '대충 만든 제품'이나 '미완성본'이 아니라, **고객에게 가치를 제공할 수 있는 핵심 기능만 담은 가장 작은 단위의 결과물**입니다.
---
+45
View File
@@ -0,0 +1,45 @@
---
id: "NoSQL 20260420"
created: "2026-04-20 10:07"
tags:
aliases:
---
## 💡 생각
결국 NoSQL은 빠른속도로 데이터베이스를 구축해서 사용하는 경우에 유용하다.
---
## 📑 개념
'Not Only SQL'의 약자로, 전통적인 [[관계형 데이터베이스(RDBMS)의 한계]]를 극복하기 위해 만들어진 **비관계형 데이터베이스**를 의미합니다.
표(Table) 형태의 엄격한 스키마를 따르지 않고, 데이터의 형태에 따라 유연하게 저장 구조를 선택할 수 있다는 것이 가장 큰 특징입니다.
## NoSQL의 특징
- **유연한 스키마 (Schema-less):** 고정된 테이블 구조가 없습니다. 데이터의 필드를 언제든지 추가하거나 변경할 수 있어, 형태가 일정하지 않은 로그 데이터나 SNS 포스트 등을 저장하기에 유리합니다.
- **수평적 확장성 (Scalability):** RDBMS가 주로 서버의 사양을 높이는 수직 확장(Scale-up)에 집중한다면, NoSQL은 여러 대의 저렴한 서버를 클러스터로 묶어 데이터를 분산 저장하는 **수평 확장(Scale-out)** 에 최적화되어 있습니다.
- **고성능:** 단순한 데이터 모델을 사용하여 읽기/쓰기 성능이 매우 빠릅니다. 복잡한 JOIN 연산을 지양하기 때문에 대규모 데이터를 처리할 때 유리합니다.
---
## NoSQL의 4가지 주요 유형
| **유형** | **특징** | **대표적인 DB** |
| ----------------- | --------------------------- | ---------------------- |
| **Key-Value** | 가장 단순한 형태. 키와 값의 쌍으로 저장 | Redis, Amazon DynamoDB |
| **Document** | JSON이나 XML 같은 문서 형식으로 저장 | **MongoDB**, CouchDB |
| **Column-family** | 행이 아닌 열(Column) 단위로 데이터를 저장 | Cassandra, HBase |
| **Graph** | 데이터 간의 관계를 노드와 간선으로 표현 | Neo4j |
## NoSQL의 트랜잭션 (BASE)
RDBMS의 [[ACID]]와 대조적으로, NoSQL은 보통 **BASE** 원칙을 따릅니다.
- **Basically Available (기본적 가용성):** 언제든 접근 가능해야 함.
- **Soft State (소프트 상태):** 외부의 개입 없어도 정보가 변경될 수 있음 (일시적 불일치 허용).
- **Eventually Consistent (결과적 일관성):** 일시적으로 정합성이 깨질 수 있지만, 시간이 지나면 결국 모든 노드의 데이터가 일치하게 됨.
## 데이터 중복(Data Redundancy)
NoSQL에서 **데이터 중복(Data Redundancy)** 은 실수가 아니라 **성능을 위해 의도적으로 선택하는 핵심 전략**입니다.
RDBMS에서는 데이터 중복을 죄악시하며 정규화(Normalization)를 통해 중복을 제거하지만, NoSQL은 그 반대인 **비정규화(Denormalization)** 를 지향합니다.
@@ -0,0 +1,34 @@
### 1. 공격 표면(Attack Surface)의 최소화
파드가 퍼블릭 서브넷에 있고 공인 IP(Public IP)를 직접 가지고 있다면, 전 세계 누구나 해당 파드에 직접 접속을 시도할 수 있습니다.
- **위험성:** 애플리케이션 코드의 작은 취약점이나 설정 오류만으로도 서버 전체가 해킹당할 수 있습니다.
- **해결:** 프라이빗 서브넷에 두면 외부에서는 아예 길 자체가 없어서 직접 접근이 불가능해집니다.
### 2. 단일 진입점 강제 (로드 밸런서 활용)
사용자는 파드에 직접 붙는 것이 아니라, **로드 밸런서(ALB/NLB)**라는 검증된 관문을 통해서만 들어와야 합니다.
- 로드 밸런서는 퍼블릭 서브넷에서 외부 요청을 안전하게 걸러서 받고, 내부 네트워크를 통해 프라이빗 서브넷에 있는 파드에게 전달합니다.
- 이 구조를 통해 트래픽 제어, SSL 인증서 관리, WAF(웹 방화벽) 적용이 훨씬 쉬워집니다.
### 3. IP 주소 자원 관리
퍼블릭 IP는 전 세계적으로 한정된 자원이며 비용이 발생합니다.
- 쿠버네티스 특성상 파드는 수십, 수백 개가 생성되었다가 사라집니다. 이때마다 퍼블릭 IP를 할당하는 것은 매우 비효율적이고 비용 낭비가 심합니다.
- 내부망(Private IP)을 사용하면 주소 부족 걱정 없이 자유롭게 스케일링할 수 있습니다.
### 4. 아키텍처의 논리적 분리 (Tiering)
현대적인 3-Tier 아키텍처 원칙을 따르기 위함입니다.
- **Public Tier:** 로드 밸런서, 배스천 호스트 (외부 소통 창구)
- **Private Tier:** 애플리케이션 서버, 데이터베이스 (실제 핵심 데이터와 로직)
+50
View File
@@ -0,0 +1,50 @@
---
id: RDBMS 20260420
created: 2026-04-20 08:43
tags:
aliases:
---
## 💡 생각
데이터간의 관계를 고민하고 관계를 이용해서 중복된 데이터가 들어가는것을 최소화하는 DB를 일컫는다고 보면 된다. 왜냐하면 데이터 정합성은 사실 RDBMS든 NoSQL이든 무조건 지켜주는게 좋은 부분이니까 이걸 RDBMS와 NoSQL의 차이다 라고 하기에는 좀 과하다고 생각된다.
---
## 📑 개념
관계형 DB의 가장 큰 특징은 데이터를 **Table(표)** 형태로 관리하고, 테이블 간의 **Relation(관계)** 을 정의한다는 점입니다.
## 📌 상세
- **스키마(Schema):** 데이터의 구조와 제약 조건을 정의한 설계도입니다.
- **기본키(PK)와 외래키(FK):** 테이블 간의 연결 고리이자 데이터의 유일성을 보장하는 핵심 장치입니다.
- **무결성(Integrity):** 데이터가 정확하고 일관되게 유지되도록 하는 규칙들입니다.
[[ACID]] [[트랜잭션(Transaction)|트렌젝션]]을 지킴으로 인해 DB에서 가장 중요한 덕목인 [[데이터 정합성(Data Consistency)]] 지키는데 특화됨.
![[ACID#📌 상세]]
### 성능 최적화: 인덱스와 정규화
실무에서 가장 많이 고민하게 되는 부분입니다.
- **정규화(Normalization):** 데이터 중복을 최소화하고 구조를 체계화하는 과정입니다. (1NF, 2NF, 3NF 등)
- **인덱스(Index):** 책의 색인처럼 데이터를 빠르게 찾기 위한 구조입니다. B-Tree 구조가 대표적이죠.
- **실행 계획(Execution Plan):** 작성한 SQL이 내부적으로 어떻게 실행되는지 분석하여 병목을 찾아내는 도구입니다.
---
## 주의
> [!warning] 하지만 데이터 정합성의 경우 RDBMS의 고유한 특징이라고 하기엔 무리가 있다.
왜냐하면 DB라면 데이터 정합성을 지켜야하는게 당연하기 때문이다.
( 얼마나 정합성을 강하게 지켜서 성능에서 손해를 보느냐, 약간 덜 엄격하게 지키고 성능에서 이점을 얻느냐가 차라리 더 중요하다. )
## RDBMS의 본질
> [!note] RDBMS의 본질: "구조적 설계를 통한 중복 제어"
- **정규화(Normalization):** 데이터 간의 관계를 분석해 원자 단위로 쪼개고, 중복을 원천 차단하는 프로세스입니다.
- **무결성 제약 조건:** DB 엔진 차원에서 외래키(FK), 유니크(Unique) 제약 등을 통해 "중복되거나 잘못된 관계가 맺어지는 것"을 물리적으로 막습니다.
- **저장 효율성:** 중복을 최소화하기 때문에 저장 공간을 효율적으로 쓰고, 데이터 수정 시 한 곳만 고치면 되는 구조를 만듭니다.
+72
View File
@@ -0,0 +1,72 @@
---
id: RS-232 vs 이더넷 20260504
created: 2026-05-04 09:58
tags:
aliases:
---
## 💡 생각
결과적으로 RS-232와 이더넷사이에는 약 8,000배의 속도차이가 발생된다고 한다.
설치비용도 이더넷이 훨씬 더 저렴하다고 한다.
이래도 RS-232를 쓴다고?
[[RS-232가 이더넷을 따라잡을 수 없는 이유]]
---
> [!question]
> Q. 왜 rs-232랑 이더넷은 속도의 차이가 나는거야? 어차피 둘다 똑같이 전기적인 신호의 전달인거 아니야?
> A. 정말 좋은 질문입니다! 본질적으로는 둘 다 구리선 안에서 전압의 변화(전기 신호)를 이용하는 것은 맞지만, 그 전기를 **얼마나 정교하고 빠르게 제어하느냐**에서 결정적인 차이가 발생합니다.
---
### 1. 전압의 폭과 물리적 부담 (관성)
전압을 바꾸는 것은 마치 수도꼭지를 틀고 잠그는 것과 비슷합니다.
- **[[RS-232]]:** 전압의 진폭이 큽니다(보통 -15V에서 +15V 사이). 전압을 이만큼 높였다 낮췄다 하려면 시간이 걸립니다. 즉, 신호를 한 번 바꿀 때마다 물리적인 '관성'이 큽니다.
- **이더넷:** 아주 낮은 전압(약 1V~2.5V 내외)을 사용합니다. 전압 폭이 작기 때문에 아주 미세하고 빠르게 전압을 튀길 수 있습니다. 덕분에 1초에 훨씬 많은 신호를 보낼 수 있는 것이죠.
***** 즉, 수도꼭지를 아주 조금만 틀고잠궈도 되는 이더넷이 많이 틀고 많이 잠궈야하는 RS-232보다 물리적인 부담이 훨씬 적다.
### 2. 노이즈를 처리하는 방식 (차동 신호)
멀리, 그리고 빠르게 신호를 보내려면 주변의 전기적 잡음(노이즈)을 이겨내야 합니다.
- **[[RS-232]] (싱글 엔디드):** 선 하나에 전기를 흘리고 그 전압 값만 측정합니다. 주변에서 노이즈가 끼어들면 신호가 쉽게 변조됩니다. 그래서 속도를 높이면 에러가 너무 많이 납니다.
- **이더넷 ([[차동 신호(Differential Signaling)]]):** 선 두 개를 꼬아서 사용합니다. 한쪽 선에 `+`를 보내면 다른 쪽엔 `-`를 보냅니다. 노이즈가 발생해도 두 선에 동시에 영향을 주므로, 두 신호의 '차이'를 계산하면 노이즈만 쏙 제거됩니다. 이 기술 덕분에 노이즈 걱정 없이 엄청난 속도로 신호를 쏠 수 있습니다.
***** 노이즈 발생 걱정 없이 데이터를 송,수신 할 수 있다.
### 3. 신호를 만드는 '언어'의 정교함
전압을 단순히 `높다(1)`, `낮다(0)`로만 구분하는지, 아니면 더 복잡하게 약속하는지의 차이입니다.
- **RS-232:** 단순합니다. 전압이 높으면 0, 낮으면 1(혹은 그 반대) 식으로 아주 기초적인 규칙만 씁니다.
- **이더넷:** 전압의 변화뿐만 아니라 위상(Phase) 등을 조합한 고도의 인코딩 방식을 씁니다. 한 번의 전기 신호 변화에 여러 비트의 데이터를 담는 '압축 기술'이 들어가 있어 같은 전기 신호라도 실어나르는 데이터 양이 훨씬 많습니다.
> [!question]
> Q. 신호를 만드는 '언어'의 정교함 이거는 만약에 rs-232도 고도로 발달한 인코딩 방식을 개발해서 사용했다면 어느정도는 따라갈 수 있는 부분이었을까?
> A. 매우 날카로운 질문입니다! 결론부터 말씀드리면, **인코딩 기술을 도입하면 RS-232도 지금보다는 훨씬 빨라질 수 있습니다.** 하지만 근본적으로 이더넷을 **'완벽히'** 따라잡기는 어렵습니다.
> [!note]
결국 **똑같은 전기**를 쓰더라도, 더 적은 힘으로(저전압), 더 영리하게 노이즈를 방어하면서(차동 신호), 더 정교한 규칙(인코딩)으로 소통하기 때문에 이더넷이 압도적으로 빠른 것입니다.
>
### 왜 RS-232는 차동 신호가 안 될까요?
RS-232는 선 하나($A$)와 접지(Ground, $0V$)를 비교합니다.
- **보낼 때:** $A$선에 $10V$를 보냅니다. (기준은 $0V$니까 차이는 $10V$)
- **노이즈 발생:** $A$선에만 $+5V$ 노이즈가 낍니다. ($15V$가 됨)
- **받을 때:** 받는 쪽은 여전히 $0V$를 기준으로 측정하므로, 데이터가 $15V$로 변질된 채 들어옵니다. 노이즈를 걸러낼 방법이 없습니다.
+57
View File
@@ -0,0 +1,57 @@
---
id: "RS-232 20260504"
created: "2026-05-04 09:37"
tags:
aliases:
---
## 💡 생각
SECS 통신에서 사용하는 그것을 말함.
아직도 RS-232를 쓰는곳이 종종 있음.
---
## 📑 개념
컴퓨터와 주변 장치(모뎀, 마우스, 산업용 장비 등) 간에 데이터를 주고받기 위해 만들어진 **직렬(Serial) 통신 표준**입니다. 1960년대에 처음 등장했음에도 불구하고, 신뢰성이 높고 구조가 간단하여 오늘날까지도 산업 현장에서 널리 사용되고 있습니다.
---
## 📌 주요 특징
- **직렬 통신:** 데이터를 한 번에 1비트씩 순차적으로 전송합니다. 병렬 통신에 비해 배선이 단순하다는 장점이 있습니다.
- **전압 기반 신호:** 논리 상태를 전압의 차이로 구분합니다.
- **논리 0 (Space):** $+3\text{V} \sim +15\text{V}$
- **논리 1 (Mark):** $-3\text{V} \sim -15\text{V}$ (휴지 상태)
- **전송 거리:** 표준상으로는 약 15m(50피트) 정도까지 권장되지만, 통신 속도를 낮추면 더 먼 거리도 가능합니다.
- **커넥터 형태:** 과거에는 25핀(DB-25)이 쓰였으나, 현재는 9핀(DE-9 또는 DB-9) 형태가 가장 흔합니다.
## 통신 방식 (프레임 구조)
RS-232는 **비동기식(Asynchronous)** 통신을 주로 사용합니다. 즉, 송신측과 수신측이 공통된 클록(Clock)을 공유하지 않고, 약속된 속도(Baud Rate)에 맞춰 데이터를 해석합니다.
(Baud Rate: 송신자와 수신자가 미리 **초당 몇 비트를 보낼지** 약속한 것)
1. **Start Bit:** 통신의 시작을 알리는 1비트 (Low 전압).
2. **Data Bits:** 실제 데이터 (보통 7~8비트).
3. **Parity Bit:** 오류 검출을 위한 옵션 비트.
4. **Stop Bit:** 통신의 종료를 알리는 비트 (1~2비트, High 전압).
RS-232는 보통 **전이중(Full-Duplex)** 방식입니다. 즉, 보내는 선(TX)과 받는 선(RX)이 분리되어 있습니다.
- 상대방이 데이터를 다 보낼 때까지 기다렸다가 내가 보내는 방식(Half-Duplex)도 가능하지만,
- 하드웨어적으로 선이 따로 있기 때문에 **상대방이 데이터를 보내는 중에도 내가 보낼 데이터가 있다면 동시에 Start Bit를 던지고 데이터를 보낼 수 있습니다.**
## 요약
- **Start Bit:** 수신기의 타이머를 가동시키는 스위치.
- **데이터 읽기:** 약속된 보레이트의 '절반' 지점마다 전압을 체크해서 0인지 1인지 결정.
- **Stop Bit:** 다음 데이터의 Start Bit를 제대로 인식할 수 있도록 선로를 대기 상태(High)로 복귀시키는 최소한의 여유 시간.
![[Pasted image 20260504100456.png]]
@@ -0,0 +1,49 @@
---
id: "RS-232가 이더넷을 따라잡을 수 없는 이유 20260504"
created: "2026-05-04 10:16"
tags:
aliases:
---
## 💡 생각
가성비를 위해 고안된 RS-232에 이더넷에서 사용하는 고도의 인코딩기술 등을 적용하려면 RS-232의 가장 큰 장점이었던 가성비가 사라지게 된다.
기술적으로도 싱글 엔디드(선 하나로 측정)방식에서 어떻게 할 수 없는 노이즈 현상이 RS-232의 고도화에 가장 큰 걸림돌이기도 하다.
---
### 1. 인코딩으로 얻을 수 있는 이득 (언어의 효율성)
RS-232도 현대적인 인코딩(예: QAM 등)을 사용한다면, 한 번의 전기 변화에 1비트가 아니라 4비트, 8비트를 담을 수 있습니다. 실제로 과거 전화선을 쓰던 **다이얼업 모뎀**이 이런 방식으로 속도를 높여왔습니다.
- 하지만 인코딩을 고도화할수록 **신호가 아주 미세한 차이**를 구분해야 합니다.
- 예를 들어, 전압을 0V/5V 두 개로 나누던 걸 0.1V 단위로 쪼개서 50개의 신호를 만든다고 가정해 보세요.
### 2. 발목을 잡는 '물리적 노이즈' (도로의 상태)
여기서 앞서 공부한 **노이즈** 문제가 다시 등장합니다.
- 고도의 인코딩은 '미세한 전압 차이'를 읽어내는 기술입니다.
- 그런데 RS-232는 노이즈에 취약한 **싱글 엔디드(선 하나로 측정)** 방식이죠.
- 주변에서 노이즈가 조금만 튀어도 0.1V 단위로 쪼개놓은 정교한 신호들이 순식간에 뭉개져 버립니다.
즉, **아무리 고급 언어(인코딩)를 개발해도, 전달 매체(전선)가 너무 시끄러우면(노이즈) 상대방이 알아들을 수가 없는 것**입니다.
### 3. '차동 신호'라는 인프라의 차이
이더넷이 고도의 인코딩을 마음껏 쓸 수 있는 이유는 [[차동 신호(Differential Signaling)]] 덕분에 바닥에 깔린 노이즈를 싹 제거하여 **깨끗한 도화지** 상태를 만들었기 때문입니다.
- **이더넷:** 도화지가 깨끗하니 아주 얇은 펜으로 정밀하게 글씨(인코딩)를 써도 잘 읽힙니다.
- **[[RS-232]]:** 도화지에 이미 연필 자국(노이즈)이 가득해서, 굵은 매직으로 크게 써야만 겨우 읽을 수 있는 상태입니다.
### 4. 하드웨어 설계의 단순성 (경제성)
RS-232는 애초에 "싸고 단순하게 만들자"가 목적이었습니다.
- 복잡한 인코딩을 처리하려면 CPU나 전용 칩셋이 신호를 실시간으로 계산해서 복원해야 합니다.
- RS-232 장치에 그런 비싼 칩을 넣을 바에는, 차라리 그 돈으로 **이더넷 칩**을 하나 다는 게 훨씬 싸고 빠릅니다. 이미 이더넷은 대량 생산으로 가격이 매우 낮아졌으니까요.
@@ -0,0 +1,27 @@
- 작성 **날짜:** 2026-02-27
## 📑 개념
> [!abstract]
> "AWS 클라우드 안에서 완벽하게 격리된, 사용자가 직접 정의하는 **가상 네트워크 환경**"
> AWS가 만든 개념인데 현재는 클라우드 표준처럼 사용됨
## 📌 VPC의 핵심 구성 요소
> [!check]
|**구성 요소**|**명칭**|**설명**|**비유**|
|---|---|---|---|
|**CIDR 블록**|**IP 주소 범위**|VPC에서 사용할 IP 주소의 범위를 정의 (예: `10.0.0.0/16`)|내 구역의 **전체 주소지**|
|**서브넷 (Subnet)**|**망 분할**|VPC를 더 작은 단위로 쪼갠 네트워크. 퍼블릭과 프라이빗으로 나뉨|큰 건물을 **여러 개의 방**으로 나눔|
|**라우팅 테이블**|**길잡이**|네트워크 트래픽이 어디로 가야 할지 알려주는 이정표|건물 내의 **복도와 안내판**|
|**인터넷 게이트웨이**|**IGW**|VPC와 인터넷 사이의 통로. 이게 있어야 외부 통신 가능|건물의 **정문 (바깥 세상으로 나가는 문)**|
|**NAT 게이트웨이**|**NAT**|프라이빗 서브넷의 서버가 보안을 유지하며 외부로만 나갈 때 사용|안에서는 밖을 보지만, **밖에서는 안을 못 보는 창문**|
## 📝 노트
> [!note]
>
> - 관련 사례나 반대되는 개념이 있다면 여기에 기록하세요.
>
> - 본인의 언어로 풀어서 쓰는 것이 제텔카스텔의 핵심입니다.
>
## 🔗 지식 연결
- **태그:** #zettelkasten #knowledge
@@ -0,0 +1,33 @@
---
id: "VPN(Virtual Private Network) 20260305"
created: "2026-03-05 15:00"
tags:
---
## 💡 생각
또다른 네트워크 연결 통로라고 생각하면 됨. 실제로 VPN 연결 시 네트워크 연결에 새로운 네트워크 연결이 추가됨.
![[Pasted image 20260305150408.png]]
---
## 📑 개념
가상 사설망, **인터넷상에 나만의 안전한 비밀 통로를 만드는 기술**이라고 생각하면 됨.
## 📌 상세
VPN 사용 시 주요 장점
| **장점** | **설명** |
| -------------- | -------------------------------------------------- |
| **보안 강화** | 공공장소(카페, 공항)의 무료 와이파이를 쓸 때 해킹 위험으로부터 데이터를 보호합니다. |
| **IP 주소 우회** | 나의 실제 위치(IP 주소)를 숨기고 VPN 서버가 있는 국가의 IP로 바꿀 수 있습니다. |
| **검열 및 차단 해제** | 특정 국가에서 접속이 막힌 사이트나 콘텐츠(해외 넷플릭스 등)에 접근할 수 있습니다. |
| **익명성 보장** | 방문하는 웹사이트나 서비스 제공자가 나의 실제 신원을 파악하기 어렵게 만듭니다. |
VPN 사용 시 주의할 점
- **속도 저하:** 데이터를 암호화하고 먼 거리에 있는 서버를 거치기 때문에 인터넷 속도가 평소보다 느려질 수 있습니다.
- **무료 VPN의 위험성:** "공짜" VPN 중 일부는 사용자의 활동 로그를 수집해 광고주에게 팔거나 보안이 취약할 수 있습니다. 가급적 신뢰할 수 있는 유료 서비스를 권장합니다.
- **완벽한 익명성은 없음:** VPN 업체는 내가 어떤 데이터를 주고받는지 알 수 있습니다. 따라서 '노 로그(No-log, 활동 기록을 남기지 않음)' 정책을 가진 업체를 선택하는 것이 중요합니다.
---
+45
View File
@@ -0,0 +1,45 @@
---
id: "Woodpecker 20260320"
created: "2026-03-20 16:56"
tags:
aliases:
---
## 💡 생각
이거 아주 괜찮을거같음
아주 경량화되어 사용하기 쉽고 빠릿빠릿한 젠킨스
(물론 계속 써봐야겠지만..)
---
## 📑 개념
Woodpecker CI는 오픈 소스 기반의 경량 CI/CD 엔진으로, 과거 유명했던 Drone CI에서 포크(Fork)되어 발전한 도구입니다. 복잡한 설정보다는 **간결함**과 **컨테이너 기반의 확장성**을 중시하는 팀에게 특히 매력적인 선택지입니다.
## 📌 상세
## 1. Woodpecker의 핵심 철학
Woodpecker는 모든 파이프라인 단계를 **도커(Docker) 컨테이너** 내에서 실행합니다. 이로 인해 환경 격리가 확실하며, 필요한 도구를 설치하기 위해 에이전트를 더럽힐 필요가 없습니다.
- **설정의 간소화:** `.woodpecker.yml`이라는 YAML 파일 하나로 전체 파이프라인을 정의합니다.
- **오픈 소스 정신:** 완전히 무료이며, 커뮤니티에 의해 유지보수됩니다. (Drone CI가 기업화되면서 라이선스 제약이 생긴 것에 반발하여 나온 프로젝트입니다.)
- **경량화:** 리소스 소모가 매우 적어 개인 서버나 사양이 낮은 VPS에서도 원활하게 돌아갑니다.
---
## 2. 작동 방식 (Architecture)
Woodpecker는 크게 두 가지 컴포넌트로 구성됩니다.
- **Server:** 웹 UI를 제공하고, GitHub, GitLab, Gitea와 같은 소스 코드 관리 도구(Forge)와 연동하여 웹훅(Webhook)을 처리합니다.
- **Agent:** 실제로 파이프라인 작업을 수행하는 주체입니다. 서버로부터 할당받은 작업을 컨테이너를 띄워 실행합니다.
## 주요 장점
- **멀티 플랫폼 지원:** x86_64는 물론이고 ARM64(라즈베리 파이 등)에서도 완벽하게 작동합니다.
- **다양한 Backend:** 기본적으로 Docker를 사용하지만, 최근에는 로컬 프로세스 실행이나 [[쿠버네티스(Kubernetes)]] 기반의 실행도 지원하기 시작했습니다.
- **플러그인 생태계:** Docker 이미지를 플러그인처럼 사용할 수 있습니다. 예를 들어, 빌드가 끝나고 Slack 메시지를 보내고 싶다면 이미 만들어진 Slack 플러그인 이미지를 파이프라인에 추가하기만 하면 됩니다.
@@ -0,0 +1,41 @@
---
id: "YAGNI(You Ain't Gonna Need It) 20260317"
created: "2026-03-17 16:54"
tags:
aliases:
---
## 💡 생각
지금 당장 꼭 필요한 것만 만들어라. 미리 먼저 만들지 마라
---
## 📑 개념
소프트웨어 개발 원칙 중 하나로 직역하면 **그거 필요 없을걸요** 또는 **미리 만들지 마세요**라는 뜻을 담고 있습니다.
개발자가 미래에 필요할 것으로 예상되는 기능을 미리 구현하지 말고, **실제로 그 기능이 필요한 시점이 되었을 때 구현하라**는 핵심 메시지를 전달합니다.
## 📌 상세
### 1. YAGNI가 필요한 이유
많은 개발자가 나중에 이 기능이 추가될 때를 대비해 미리 구조를 잡거나 범용적인 코드를 작성하려는 유혹에 빠지곤 합니다. 하지만 이런 **추측에 기반한 개발(Speculative Development)**은 다음과 같은 부작용을 낳습니다.
- **시간 낭비:** 결국 쓰이지 않게 될 기능을 만드느라 현재 꼭 필요한 작업에 집중하지 못하게 됩니다.
- **코드 복잡성 증가:** 당장 필요 없는 코드가 들어가면 전체적인 시스템 구조가 복잡해지고, 나중에 코드를 읽거나 수정하기 더 어려워집니다.
- **유지보수 비용 발생:** 사용되지 않는 기능이라도 버그가 생기면 수정해야 하고, 의존성 업데이트나 리팩토링 시 고려 대상이 되어 짐이 됩니다.
- **잘못된 예측:** 미래의 요구사항은 변하기 마련입니다. 미리 만들어둔 기능이 나중에 정작 필요한 형태와 달라서 결국 다시 만들어야 하는 경우가 많습니다.
### 2. YAGNI를 실천하는 방법
- **현재의 요구사항에 집중:** 오늘 해결해야 할 문제와 기능에만 충실하게 코드를 작성합니다.
- **확장성보다는 단순성:** 나중에 확장될 것을 대비해 복잡한 추상화 계층을 미리 만들기보다는, 지금 당장 이해하기 쉽고 단순한 구조를 유지합니다.
- **리팩토링의 힘:** 나중에 정말로 기능 확장이 필요해졌을 때, 그때 가서 코드를 리팩토링하여 기능을 추가하는 것이 훨씬 효율적입니다.
## 📝 노트
> [!note]
>
> - 작업의 가치는 그것이 실제로 사용될 때 발생합니다.
---
+3
View File
@@ -0,0 +1,3 @@
---
title: note
---
@@ -0,0 +1,6 @@
| **비교 항목** | **가상 머신 (EC2 등 VM)** | **컨테이너 (Docker 등)** |
| --------- | -------------------------------------- | --------------------------------- |
| **구조** | OS 위에 가상 하이퍼바이저와 **별도의 Guest OS**가 필요함 | 호스트의 **OS 커널을 공유**하며 프로세스 단위로 격리됨 |
| **무게** | GB 단위 (매우 무겁고 부팅이 느림) | **MB 단위** (매우 가볍고 즉시 실행됨) |
| **효율성** | 리소스 낭비가 큼 (OS마다 자원을 잡아먹음) | 필요한 자원만 사용하므로 집적도가 높음 |
| **이식성** | 환경 설정에 따라 작동 여부가 달라짐 | **"어디서든 동일하게 실행"**됨을 보장함 |
@@ -0,0 +1,20 @@
- 작성 **날짜:** 2026-02-27
## 📑 개념
> [!abstract]
> 시스템이 장애 없이 정상적으로 서비스를 제공할 수 있는 상태
> 언제든지 이상없이 잘 사용할 수 있는것도 가용성으로 봄
## 📝 상세 설명
> [!note]
>
> -
>
$$가용성(\%) = \frac{업타임(정상 운영 시간)}{업타임 + 다운타임(장애 시간)} \times 100$$
## 🔗 지식 연결
- **태그:** #zettelkasten #knowledge
@@ -0,0 +1,58 @@
---
id: 게이트웨이(Gateway) 20260428
created: 2026-04-28 14:25
tags:
- network
aliases:
---
## 💡 생각
서로 다른 네트워크간의 통신은 무조건 게이트웨이를 통해 진행된다.
내부를 먼저 확인하고 내부에 그 ip에 해당되는 녀석이 없으면 게이트웨이를 통해 외부로 나아간다.
이 때 가장 먼저 확인하는게 [[라우팅 테이블(Routing Table)]]이다.
---
## 📑 개념
서로 다른 네트워크를 연결해 주는 '출입구' 역할을 하는 장치나 소프트웨어를 말합니다.
컴퓨터 네트워크는 서로 다른 통신 프로토콜(규약)을 사용하거나 구조가 다른 경우가 많은데, 게이트웨이는 이들 사이에서 데이터를 주고받을 수 있도록 **통역사** 역할을 수행합니다.
## ## 1. 게이트웨이의 핵심 역할
네트워크의 경계에 위치하여 다음과 같은 기능을 수행합니다.
- **프로토콜 변환:** 서로 다른 방식의 네트워크가 소통할 수 있도록 신호를 변환합니다.
- **데이터 전달:** 내부 네트워크에서 외부(인터넷)로 나가는 모든 데이터는 반드시 게이트웨이를 통과해야 합니다.
- **보안:** 외부로부터 들어오는 허가되지 않은 접속을 차단하는 벽의 역할을 하기도 합니다.
---
## 2. 실생활 예시: 아파트 현관문
게이트웨이를 가장 쉽게 이해하는 방법은 **아파트의 현관문**에 비유하는 것입니다.
- **내부 네트워크:** 우리 집 안 (가족끼리 자유롭게 대화하고 이동 가능)
- **외부 네트워크:** 집 밖 (복도, 엘리베이터, 거리)
- **게이트웨이:** 현관문 (집 밖으로 나가거나 외부인이 들어올 때 반드시 거쳐야 하는 유일한 통로)
컴퓨터가 인터넷상의 웹사이트에 접속하려고 할 때, 먼저 자신의 네트워크 안에 해당 주소가 있는지 확인합니다. 없다면 일단 기본 게이트웨이(Default Gateway)로 데이터를 보낸 뒤 외부 세계로 나가게 됩니다.
## 3. 기본 게이트웨이 (Default Gateway)
우리가 사용하는 PC나 스마트폰 설정에서 흔히 볼 수 있는 용어입니다.
- 보통 가정에서는 유무선 공유기(루터)의 IP 주소가 기본 게이트웨이 주소가 됩니다.
- 내부망에서 해결할 수 없는 목적지(예: 구글, 네이버 등)로 가는 데이터는 무조건 이 기본 게이트웨이 주소로 전달됩니다.
보통 0.0.0.0에 위치하며 보통은 [[라우터(Router)]]를 가리킨다.
앞서봤듯 통신연결 시도가 발생되면 가장먼저 내부의 같은 네트워크망에 그 ip주소에 해당되는 요소가 있는지 확인하고 없으면 라우팅 테이블을 확인해본다. 거기도 없을 경우 기본 게이트웨이로 가게 되는데 보통 기본 게이트웨이는 라우터로 설정되어 있다.
[[라우터(Router)]]를 통해 연결시도 한 ip로 접근이 되는지 확인해보고 최대한 통신 연결을 시도해보다가 성공,실패하게 된다.
---
@@ -0,0 +1,54 @@
---
id: "고차 함수(Higher-Order Function) 20260407"
created: "2026-04-07 15:55"
tags:
aliases:
---
## 💡 생각
이곳에 하나의 생각 또는 아이디어를 작성합니다.
---
## 📑 개념
함수를 다루는 함수라고 생각하면 쉬워요.
함수가 다음 중 **하나 이상**을 수행한다면 고차 함수라고 부릅니다.
## 고차 함수의 두 가지 조건
### 1. 함수를 인자로 전달받음
다른 함수를 매개변수(Parameter)로 넘겨받아 실행하는 경우입니다. 이때 인자로 전달되는 함수를 보통 **콜백 함수(Callback Function)** 라고 부릅니다.
- **예시:** `Array.prototype.map()`, `filter()`, `forEach()`
``` javascript
const numbers = [1, 2, 3];
// 여기서 map은 고차 함수이고, (n => n * 2)는 콜백 함수입니다.
const doubled = numbers.map(n => n * 2);
```
### 2. 함수를 결과로 반환함
함수 실행의 결과물로 새로운 함수를 만들어 내보내는 경우입니다. 이 방식은 **클로저(Closure)** 나 **커링(Currying)** 기법을 구현할 때 자주 쓰입니다.
- **예시:**
``` javascript
function makeMultiplier(multiplier) {
// 함수 자체를 반환합니다.
return function(value) {
return value * multiplier;
};
}
const triple = makeMultiplier(3);
console.log(triple(10)); // 30
```
---
## 왜 고차 함수를 쓰나요?
고차 함수를 사용하면 코드의 **추상화 수준**을 높일 수 있습니다.
1. **코드의 재사용성:** 복잡한 로직(반복문, 조건문 등)은 고차 함수 내부에 숨기고, 실제 수행할 구체적인 작업만 함수로 갈아 끼울 수 있습니다.
2. **가독성 향상:** `for` 문을 돌리며 배열을 수정하는 대신, `filter`나 `map` 같은 명칭을 사용함으로써 코드가 무엇을 하려는지 의도를 명확히 드러낼 수 있습니다.
3. **함수형 프로그래밍:** 데이터를 직접 변경하지 않고 새로운 데이터를 생성하는 방식(불변성 유지)을 구현하기에 최적입니다.
@@ -0,0 +1,42 @@
---
id: 관계형 데이터베이스(RDBMS)의 한계 20260420
created: 2026-04-20 10:08
tags:
aliases:
---
## 💡 생각
서버의 수평적확장이 어려움 + 관계가 복잡해지면 조인연산이 느려질 수 있음
---
## 1. 유연하지 못한 스키마 (Rigid Schema)
RDBMS는 데이터를 저장하기 전에 미리 테이블 구조(Schema)를 정의해야 합니다.
- **변경의 어려움:** 서비스 운영 중에 데이터 구조를 변경하려면 `ALTER TABLE`과 같은 작업을 수행해야 하는데, 데이터 양이 많을 경우 시스템 부하가 크고 서비스 중단이 발생할 수 있습니다.
- **비정형 데이터 처리:** SNS 포스트, 로그 파일, 이미지 메타데이터 등 구조가 일정하지 않은 데이터를 담기에는 부적합합니다.
## 2. 수평적 확장(Scaling-out)의 어려움
RDBMS는 기본적으로 단일 서버의 성능을 높이는 **수직적 확장(Scale-up)** 에 최적화되어 있습니다.
- **분산 저정의 복잡성:** 여러 대의 서버에 데이터를 나누어 저장(Sharding)할 수 있지만, 이 경우 데이터 간의 일관성을 유지하고 조인(Join) 연산을 수행하는 것이 매우 까다로워집니다.
- **비용 문제:** 고성능의 단일 서버를 구축하는 비용은 서버를 여러 대 늘리는 비용보다 기하급수적으로 비싸집니다.
## 3. 복잡한 관계와 조인(Join) 성능 저하
데이터 간의 관계가 복잡해질수록 여러 테이블을 연결하는 **조인 연산**이 많아집니다.
- **성능 병목:** 수백만 건 이상의 데이터가 포함된 테이블들을 빈번하게 조인하면 쿼리 실행 속도가 급격히 느려집니다.
- **객체-관계 불일치 (Impedance Mismatch):** 객체지향 프로그래밍 언어의 구조와 RDBMS의 테이블 구조가 서로 달라, 이를 변환하는 과정(ORM 등)에서 복잡성이 증가하고 성능 손실이 발생합니다.
## 4. 대용량 데이터 처리 속도
초당 수만 건 이상의 쓰기 작업이 발생하는 실시간 서비스나 테라바이트(TB) 단위의 데이터를 분석하는 환경에서는 RDBMS의 ACID 트랜잭션 보장 기능이 오히려 독이 될 수 있습니다.
- **잠금(Locking) 현상:** 데이터의 일관성을 위해 특정 행이나 테이블을 잠그는 과정에서 대기 시간이 발생하여 전체적인 처리량이 떨어집니다.

Some files were not shown because too many files have changed in this diff Show More