Skip to content

Instantly share code, notes, and snippets.

@YangSiJun528
Last active January 17, 2026 08:28
Show Gist options
  • Select an option

  • Save YangSiJun528/c78182fd3ff0996ba34de41321c08ae0 to your computer and use it in GitHub Desktop.

Select an option

Save YangSiJun528/c78182fd3ff0996ba34de41321c08ae0 to your computer and use it in GitHub Desktop.
[MIT] The Missing Semester of Your CS Education 강의 노트

소개

MIT에서 공개한 The Missing Semester of Your CS Education(여러분의 CS 교육에서 누락된 학기)에서 CS 교육에서 누락된 여러 내용을 다룬다. 내부 강의 자료이지만, 외부 사용자도 학습할 수 있도록 공개되어 있다.

주로 개발과 관련된 도구 활용에 대해서 다룬다. Shell, VIM, 가상 컨테이너, 백업, 로깅, 디버깅 등등 다양한 내용이 포함된다.

의도

여기에서 볼 수 있고, 비공식 한국어 번역본도 제공하지만 영상의 자막은 없다. (이중번역 확장 프로그램을 사용하자.)

현재 2019, 2020, 2026년도의 3가지 버전이 있으며, 학습을 시작한 2026년 1월 12일 기준 2026년도 자료는 미완성이므로 2020년을 주로 참고한다. 자료가 빠진 없는 경우 이전 년도의 자료를 참고하기도 할 것이다. (TODO: 이거 누락된거 없으면 이 문장 지워도 됨.) TODO: 2026년 2월쯤 되면 다 나왔을꺼라 그거 강의 보고 내용 보완하기

AI를 사용해서 쉽게 알 수 있는 정보들 or 별도 튜토리얼이 존재하는 경우에는 자세하게 정리하지 않고, 그냥 키워드나 느낀점 위주로 가볍게 정리할 것이다.

영상과 내용을 가볍게 보고 AI를 사용해 정리하고 직접 다듬는 식으로 정리하였다.

내 개인 메모이므로 확실히 알고 있는 내용은 스킵해서 정리했다.
또 개인적으로 궁금한 부분은 추가적으로 찾아서 정리했다.
(대신 확실한게 아니면 참고한 자료 링크 정도는 함께 제공하려고 함.)

TODO

https://missing.csail.mit.edu/2026/development-environment/ 를 보면 VIM 내용이 줄고 LSP나 IDE 설명이 늘었다. 최근 개발에서 VIM을 만질일이 적긴 하다고 생각해서 좋은 변화같음.

2026이 확실히 최신화가 많이 되서 AI 내용이나 컨테이너같은 내용이 추가되었고

VIM이나 쉘 스크립트 내용이 축소되는 등 실무 or 실제 개발에 더 적합하게 바뀐거 같은 느낌이 듦.

일던 정리 가볍게 다 할때쯤이면 2월 되었을테니 강의랑 자료 보고 보완 ㄱㄱ


이거 2020강의 찍은 사람 중 한 명이 Jon Gjengset라고 하는데, 내가 가지고 있는 SHENZHEN I/O 게임을 하는 유튜브 영상이 있길래 신기해서 찾아봄

보니까 AWS에서 일했고 Rust관련 영상도 많아서 참고하기 좋을듯? Rust로 BitTorrent 만드는 것도 있음.

https://github.com/jonhoo - https://www.youtube.com/@jonhoo/videos

01. Course Overview (수업 개요)

  • 문제: 컴퓨터는 반복 작업에 강하지만, 사용자는 이 특성을 컴퓨터 사용 방식에 충분히 활용하지 못하고 있다.
  • 목표: 명령어 암기나 복붙 중심의 사용에서 벗어나, 기존 도구를 제대로 활용하고 새로운 도구를 스스로 탐구하도록 돕는다.
  • 방법: 일반적인 컴퓨터공학 수업에서 다루지 않는 실무·생산성 중심의 도구와 활용 개념을 소개한다.

02. Introduction to the Shell (셸)

셸이란?

  • 운영체제와 사용자 사이의 인터페이스. 명령어를 해석하고 실행하는 프로그램
  • GUI와 달리 텍스트 기반으로 동작하며, 스크립팅과 자동화에 강점
  • 터미널(Terminal)은 셸을 표시하는 창. 셸 자체와는 별개
  • Shell 역시 /usr/bin에 저장되는 단순한 프로그램임.
    • 사용자마다 설정이나 사용하는 Shell 프로그램을 다르게 쓸 수 있음.
    • 셀 실행까지의 과정
      BIOS/UEFI
      → 부트로더 (GRUB)
          → 커널 로드
          → init/systemd (PID 1)
              → getty (TTY) 또는 sshd (SSH) 또는 display manager (GUI)
              → login 또는 인증 처리
                  → /etc/passwd에서 유저 셸 경로 읽음
                  → fork() + exec()로 셸 실행
                      → 셸이 프롬프트 표시하고 입력 대기
      
    • 참고: What is Login Shell in Linux?
  • Tutorial - Write a Shell in C • Stephen Brennan와 같은 간단한 구현을 찾아볼 수 있음.

주요 셸 종류

특징
sh (Bourne Shell) 최초의 유닉스 셸. POSIX 표준의 기반
bash (Bourne Again Shell) sh 호환 + 확장 기능. 대부분 리눅스 기본 셸. GNU Bash Manual
zsh bash 호환 + 강력한 자동완성, 플러그인 시스템. macOS 기본 셸. Zsh Manual
fish 사용자 친화적, 문법이 POSIX 비호환. Fish Shell

셸 프롬프트

  • 셸 프롬프트: 명령 입력을 기다리는 줄 (예: missing:~$)
  • 커스터마이징 가능 (사용자명, 경로, 시간 등 표시 설정)

명령어의 본질

  • echo, cat 등은 "명령어"라 부르지만 실제로는 시스템에 기본 설치된 프로그램
    • 플래그/옵션과 인자를 받아서 작업 수행
    • which [프로그램명]으로 프로그램 위치 확인 가능

변수와 플래그

  • $로 시작하면 환경 변수 (예: $PATH, $HOME)
  • - 또는 --로 시작하면 플래그/옵션
    • -h / --help: 도움말 표시
    • 단일 문자는 -, 단어는 -- 사용

권한 (ls -l)

  • ls -l 출력의 권한 부분을 설명
    • 소유자 / 소유 그룹 / 기타 사용자
    • r(읽기), w(쓰기), x(실행) 을 bit flags로 구분함
    • ls -al /usr/bin 을 하면 기타 사용자까지 실행 가능하게 되어있음.
      • /usr/bin은 실행 파일(binary)이 저장되는 표준 디렉터리.
    • 자세한건 Linux file permissions explained 같은 튜토리얼 보기
  • 자주 실수하는 주의점
    • 폴더와 디렉터리의 권한은 별개이며, 이 두 권한을 전부 필요로 하는 경우도 있다.
    • 예: 파일 삭제/이동, 디렉토리 진입

명령어 키워드 (복습용)

  • 강의에서 나온 명령어: pwd, cd, ls, mv, cp, rm, mkdir, rmdir, cat, touch, echo, which, man, tee

스트림과 리다이렉션

셸의 핵심 강점: 단일 프로그램들을 체이닝하여 연결 가능

  • 스트림

    • Input Stream: 프로그램 입력 (기본: 키보드)
    • Output Stream: 프로그램 출력 (기본: 터미널)
  • 리다이렉션 연산자

    • <: 파일에서 입력
    • >: 파일로 출력 (덮어쓰기)
    • >>: 파일로 출력 (추가)
    • |: 좌측 출력 → 우측 입력
  • cp를 리다이렉션으로 대체하는 예시: cat < hello.txt > hello2.txt

UNIX 철학

  • 프로그램들은 서로에 대해 모름
  • 셸이 이들을 연결하여 강력한 기능 구현
    • 텍스트뿐 아니라 바이너리, 이미지, 비디오 스트리밍(크롬캐스트 등) 등
  • 자세한건 Basics of the Unix Philosophy 참고

Root와 sudo

  • Root: user ID 0, 시스템의 모든 작업 가능한 사용자
  • 기본적으로 일반 사용자로 동작하는 이유: 실수로 시스템 손상 방지
  • sudo [명령어]: 명령어를 root 권한으로 실행
  • sudo su: 셸 자체를 root로 전환 (프롬프트도 $#로 변경)
  • 리다이렉션 주의사항:
    • sudo echo 500 > brightness → 실패. >는 셸이 처리하므로 일반 권한으로 파일 열림
    • 해결: echo 500 | sudo tee brightness → tee가 root로 실행되어 파일 쓰기 가능

sysfs (/sys)

  • 커널 매개변수를 파일 형태로 접근/조작 가능
  • 기존 명령어(cat, echo 등)로 하드웨어 제어 가능
  • 예: 화면 밝기, LED, 배터리 상태 등
  • 참고: Everything is a file - UNIX의 핵심 설계 철학
    • 그래서 소켓이나 파일 I/O도 파일에 쓰는 방식으로 동작함

03. Shell Tools and Scripting (셸 도구 및 스크립팅)

솔직히 여기 내용은 나에게 크게 유용하지는 않다. 내가 아직까진 CLI를 자주 접하는 환경은 아니였어서.
Bash 문법은 정리하는것보다 원본을 보는게 나아서 스킵.
인상깊거나 몰랐던 부분만 추가로 정리함.

공백의 중요성

foo=bar   # 변수 할당 (O)
foo = bar # foo 프로그램에 =, bar를 인자로 전달 (X)
  • 공백이 인자 구분자로 사용됨
  • 파일명에 공백이 있으면 따옴표로 감싸야 함

글로빙 (Globbing)

셸이 패턴을 확장해주는 기능. 파일 다룰 때 유용함.

패턴 의미 예시
* 0개 이상의 임의 문자 *.sh → 모든 .sh 파일
? 정확히 1개의 임의 문자 project? → project1, project2
{a,b} a 또는 b로 확장 image.{png,jpg} → image.png image.jpg
{a..z} 범위 확장 {a..c} → a b c
# 카테시안 곱 (조합)
touch {foo,bar}/{a..c}   # foo/a, foo/b, foo/c, bar/a, bar/b, bar/c

# 글로빙 조합
mv *{.py,.sh} folder     # 모든 .py, .sh 파일 이동

shebang

  • #!로 시작하며 스크립트 첫 줄에 위치
  • 커널이 어떤 인터프리터로 실행할지 결정
  • 팁: #!/usr/bin/env [인터프리터]을 쓰면 빌트인 된 env 프로그램이 PATH에서 인터프리터를 찾아서 실행하므로 설치 위치에 의존하지 않아 이식성 높음.

/dev/null

  • 쓰면 바로 버려지는 특수 장치 파일
  • >: stdout 리다이렉션
  • 2>: stderr 리다이렉션
  • 출력을 무시하고 싶을 때 사용

함수 vs 스크립트

구분 함수 스크립트
언어 셸과 동일해야 함 아무 언어 (shebang 필요)
로딩 정의 시 1회 실행마다 로딩
실행 환경 현재 셸 (cd(디렉토리 이동) 영향 O) 별도 프로세스 (cd 영향 X)
변수 셸 변수 직접 수정 가능 export된 것만 값 복사로 전달

서브셸 (Subshell)

강의에 없는 자료. 내가 찾음. 링크|번역본

괄호 ()로 묶인 명령어는 자식 프로세스(서브셸)에서 실행됨.

( cd /tmp; echo $PWD )  # 서브셸에서 실행
echo $PWD               # 부모 셸 위치 그대로
  • 서브셸 내 변수 변경은 부모에 영향 없음
  • exit은 서브셸만 종료

서브셸이 생성되는 경우:

  • ( ) 괄호로 묶은 명령어
  • & 백그라운드 실행
  • 파이프라인의 각 명령어 (예: ls | grep foo에서 lsgrep 각각)
  • 외부 명령어 실행

외부 명령어 vs 내장 명령어:

  • 내장(builtin): 셸 자체에 포함, 현재 프로세스에서 실행 (cd, export, source), 빠름
  • 외부: 별도 실행 파일, fork로 자식 프로세스 생성 (ls, grep, cat)
  • type 명령어로 확인 가능

병렬 처리:

( task1 ) &
( task2 ) &
wait  # 모든 백그라운드 작업 대기

쉘 도구

쉘 도구는 다음과 같이 구분한다.

  • Shell builtin
    • 셸에 내장된 명령어
    • 외부 프로세스 없이 셸이 직접 실행
    • 예: cd, export, source, alias
  • GNU Coreutils
    • GNU에서 만든 프로그램들의 패키지
    • 대부분의 리눅스 배포판에 포함됨
    • /bin, /usr/bin에 위치
    • 예: ls, cat, cp, grep, find
  • 서드파티
    • 별도 설치 필요, 몇가지 도구는 리눅스 배포판에 따라 미리 추가해두기도 함
    • 예: fd, rg, fzf, tldr

추가
셸 도구 중 필수적인 몇 가지는 POSIX 표준에 정의되어 있다.
POSIX는 최소 동작만 규정한다. 리눅스는 GNU, MacOS는 BSD를 기반으로 하므로 구현에 약간 차이가 있다.
POSIX 표준 도구: https://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html


PATH 환경변수

명령어를 경로 없이 실행할 수 있는 이유.

PATH란:

echo $PATH
# /usr/local/bin:/usr/bin:/bin
  • 콜론(:)으로 구분된 디렉토리 목록
  • 명령어 입력 시 셸이 왼쪽부터 순차 탐색

실행 과정:

python --version
# 1. /usr/local/bin/python 있음? → 없음
# 2. /usr/bin/python 있음? → 있음 → 실행

같은 이름이 여러 경로에 있으면:

which python      # 실행되는 것
which -a python   # 모든 경로 표시

PATH에서 먼저 나오는 쪽이 실행됨.

저장 위치 (로딩 순서):

범위 파일 적용 대상
시스템 전역 /etc/environment 모든 사용자, 모든 셸
시스템 전역 /etc/profile 모든 사용자 (login shell)
사용자별 (bash) ~/.bashrc, ~/.bash_profile 해당 사용자
사용자별 (zsh) ~/.zshrc, ~/.zprofile 해당 사용자

PATH 수정:

# 임시 (현재 세션만)
export PATH="/custom/path:$PATH"

# 영구 (셸 설정 파일에 추가)
echo 'export PATH="/custom/path:$PATH"' >> ~/.bashrc

04. Editors (Vim)

스킵.
기본적인 이동, 문자열 작업, 종료 정도는 이미 알고 있고, 간단한 작업 외에 VIM을 사용하고 싶지는 않음.
CLI 환경에서 유용한 작업 도구인건 맞지만, 지금은 필요하지 않고, 알고싶지 않음.
이건 배경지식으로 쓰기에도 뭐해서 굳이 정리는 안함.

필요하면 원본 보기

05. Data Wrangling (데이터 정리)

이것도 알아두면 유용할거 같긴 한데, 마찬가지로 터미널 환경을 많이 접하지 않아서 쓸 일이 적음.
정규식도 개발할 떄 가끔 쓰지 자주 쓰는건 아니라...
간단하게 요약하고 필요하면 원본 보기. 원본도 소개 느낌이지 깊게 다루는건 아니라 정말 잘 다루려면 별도 학습이 필요할 듯.

데이터 랭글링이란?

  • 데이터를 한 형식에서 다른 형식으로 변환하는 작업
  • 파이프(|) 사용 자체가 이미 데이터 랭글링의 일종

정규표현식

  • 텍스트 패턴 매칭을 위한 표현식
  • 문법은 원본 참고
  • 캡처 그룹은 \1, \2로 참조
  • 복잡해질 수 있으므로 디버거(regex101 등) 활용 권장

유용한 도구들

  • awk: 컬럼 기반 텍스트 처리 일반 스크립트 언어. $1, $2로 컬럼 접근. 패턴 매칭과 BEGIN/END 블록 지원
  • sed, sort, uniq, paste, bc, wc

응용

  • xargs: 파이프 출력을 다른 프로그램의 인자로 전달
  • 바이너리 데이터: ffmpeg, convert 등으로 이미지/비디오도 파이프 처리 가능

  • ssh에서 ssh server 'cmd1 | cmd2'처럼 따옴표로 묶으면 서버에서 파이프 처리 후 결과만 전송됨. 큰 데이터를 처리해서 네트워크에서 가져오는 데 많은 시간이 소요될 때 유용함.

06. Command-line Environment (커맨드라인 환경)

은근 유용하게 써먹을만한거 많았음.
서브쉘 개념이나 ssh, nohup, & 같은거 그냥 따라 쓰기만 했는데 좀 이해한 느낌.
brewfile이나 dotfiles로 설정 관리하는건 꽤나 매력적임. 나중에 여유 시간 많으면 로컬 기반 파일 저장소 + 백업 시스템까지 해서 구축해볼까 생각 중...
쉘도 바꾸고 싶긴 한데 fish는 호환이랑 사용법 차이가 약간 있는거 같아서 https://blog.taehun.dev/omz-to-starship/ 처럼 starship + zsh로 가볍게 갈지도 고려하고 있음. 아마 당장은 omz + 가볍게 하는 플러그인 같은거 쓸 듯

Job Control

  • 셸은 시그널(signal)을 통해 프로세스와 통신함
  • 주요 시그널
    • SIGINT (Ctrl+C): 인터럽트. 핸들링/무시 가능
    • SIGQUIT (Ctrl+): 종료 + 코어 덤프. 핸들링/무시 가능
    • SIGTSTP (Ctrl+Z): 일시정지. 핸들링/무시 가능
    • SIGHUP: 터미널 종료 시 전송. 기본 동작은 프로세스 종료. 데몬들은 관례적으로 이를 설정 리로드 용도로 사용
    • SIGKILL: 강제 종료. 무시 불가
    • 더 자세한건 위키피디아 참고
  • 프로세스 제어 명령어
    • jobs: 현재 세션의 작업 목록
    • bg %N: N번 작업을 백그라운드에서 재개
    • fg %N: N번 작업을 포그라운드로 가져옴 (터미널 제어권 획득)
    • kill -[SIGNAL] %N: 특정 시그널 전송
    • &: 명령어 끝에 붙이면 백그라운드 실행
  • nohup [명령어] &: SIGHUP 무시하고 실행. 터미널 닫아도 유지
  • disown: 이미 실행 중인 프로세스를 job table에서 제거 (SIGHUP 대상에서 제외)

시그널은 하드웨어 인터럽트가 아니다.
커널이 유저 프로세스에 전달하는 제한적인 비동기 IPC(프로세스 간 통신) 메커니즘이며,
프로세스 상태 변화나 예외 상황을 통지하고 정해진 동작을 유도하기 위해 사용된다.
인터럽트와 유사한 “비동기 처리 패턴”을 가지지만 계층·의미·목적은 다르다.

Aliases

  • 긴 명령어를 짧게 매핑하는 기능
  • 문법: alias 별칭="명령어"
  • 활용 예시
    alias gs="git status"
    alias sl=ls              # 오타 방지
    alias mv="mv -i"         # 기본 플래그 추가
  • \명령어: alias 무시하고 원래 명령 실행
  • unalias 별칭: alias 해제
  • alias 별칭: 현재 매핑 확인
  • alias는 세션 종료 시 사라짐. 영구 저장하려면 dotfile에 추가

Dotfiles

  • 대부분의 프로그램은 .으로 시작하는 텍스트 파일로 설정됨
  • 주요 dotfiles
    • ~/.bashrc, ~/.bash_profile: bash 설정
    • ~/.zshrc: zsh 설정
    • ~/.vimrc: vim 설정
    • ~/.ssh/config: SSH 설정
    • ~/.tmux.conf: tmux 설정
    • ~/.gitconfig: git 설정
  • 관리 방법
    • dotfiles 전용 폴더 생성 후 버전 관리(git)
    • symlink로 원래 위치에 연결: ln -s ~/dotfiles/.bashrc ~/.bashrc
    • GNU Stow 같은 도구로 자동화 가능
  • 이식성 확보
    if [[ "$(uname)" == "Linux" ]]; then {do_something}; fi
    if [[ "$SHELL" == "zsh" ]]; then {do_something}; fi
  • GitHub에 공개된 타인의 dotfiles 참고 가능. 단, 맹목적 복사는 비추천

심링크(symlink) 뭔지 자세하게 찾아볼까 했는데, 그냥 바로가기 파일 같은 느낌임.
용량 복사되는거 아니고 그냥 원본 링크하는거고, 수정해도 원본이 수정되고, 원본 없어지면 깨진 파일 되고...
내부 구조 까볼 정도로 궁금하진 않음.
https://www.freecodecamp.org/korean/news/rinugseu-symlink-tyutorieol-simbolrig-ringkeu-symbolic-link-reul-saengseonghago-sagjehaneun-bangbeob/

SSH

  • ssh user@host: 원격 서버 접속
  • ssh user@host [명령어]: 원격에서 명령 실행 후 결과 반환. 파이프와 조합 가능
  • SSH가 안전한 이유
    • 공개키 암호화로 세션 키 교환 (키 자체는 네트워크로 전송 안 됨)
    • 세션마다 임시 키 생성 → 과거 세션 노출 안 됨 (Perfect Forward Secrecy)
    • 모든 트래픽 암호화 (Telnet은 평문 전송)
  • SSH 키 등록 과정
    • ssh-keygen -t ed25519: 키 쌍 생성
    • ~/.ssh/id_ed25519: 개인키 (절대 공유 금지)
    • ~/.ssh/id_ed25519.pub: 공개키
    • ssh-copy-id user@host: 공개키를 서버에 복사
    • 서버의 ~/.ssh/authorized_keys에 공개키 등록됨
  • SSH 키 추천
    • RSA 4096: 2048은 안전하지 않음, 많은 도구에서 지원
    • ed25519: RSA 4096 보다 안전하면서 효율적임
  • 파일 전송
    • scp 로컬파일 user@host:경로: 단순 복사
    • rsync -avP 로컬 user@host:경로: 변경분만 전송, 중단 시 재개 가능, 서드파티 프로그램
  • SSH config (~/.ssh/config)
    Host vm
        User jjgo
        HostName 192.168.1.100
        IdentityFile ~/.ssh/id_ed25519
        LocalForward 9999 localhost:8888
    Host *.mit.edu
        User student123
        IdentityFile ~/.ssh/id_school
    Host *
        ServerAliveInterval 60
    
    • 여러 서버는 블록 나열, *.mit.edu처럼 공통 설정 가능, *는 전역
    • 위에서 아래로 읽고, 먼저 매칭된 값 우선
    • 설정 후 ssh vm으로 간단히 접속
    • scp, rsync 등 다른 도구도 이 설정 인식 (alias보다 나은 점)
  • Port Forwarding
    • Local (-L): ssh -L 5433:localhost:5432 user@host
      • 로컬:5433 → 원격:5432. 외부 접속 막힌 원격 서비스 우회 접근.
    • Remote (-R): ssh -R 8888:localhost:3000 user@host
      • 외부:8888 → 로컬:3000. 로컬 서비스 외부 노출 (ngrok 유사).
    • Dynamic (-D): ssh -D 9090 user@host
      • 로컬:9090 → 원격 → 어디든. SOCKS 프록시. 목적지 동적. IP/방화벽 우회.
    • 여러 개: ssh -L 5433:db:5432 -L 8080:web:80 user@host
      • 로컬:5433 → db:5432, 로컬:8080 → web:80. 동시에 여러 포트 포워딩.
  • 관련 프로그램
    • Mosh: 연결 끊김에 강한 SSH 대안
    • sshfs: 원격 폴더를 로컬에 마운트

Shells & Frameworks

  • 주요 셸 종류
    • sh: 최초의 유닉스 셸. POSIX 표준 기반
    • bash: sh 호환 + 확장. 대부분 리눅스 기본
    • zsh: bash 호환 + 강력한 자동완성, 경로 확장. macOS 기본
    • fish: 사용자 친화적이나 POSIX 비호환
  • 프레임워크
    • oh-my-zsh, prezto: 플러그인/테마 관리
    • zsh-syntax-highlighting: 명령어 구문 강조
    • zsh-history-substring-search: 히스토리 부분 검색
  • 주의: 프레임워크가 셸 시작 속도를 느리게 할 수 있음
  • 셸 비교표: Unix Shells Comparison

07. Version Control (Git)

AI 기반으로 간단하게 정리함.
내부 구조 설명해주는 Git 소개 영상은 처음보는데, 생각보다 이해 잘 되고 재밌음. 유용한지는 모르곘지만.

데이터 모델

Git을 제대로 이해하려면 인터페이스(명령어)가 아니라 내부 구조부터 봐야 함.

TLDR: Git 저장소 = objects + references 이다.

Snapshot 구조

  • blob: 파일. 바이트 배열
  • tree: 디렉토리. 이름 → blob 또는 tree 매핑
  • commit: 스냅샷. tree + 메타데이터 + 부모 참조
<root> (tree)
├── foo (tree)
│   └── bar.txt (blob, "hello world")
└── baz.txt (blob, "git is wonderful")

의사코드

type blob = array<byte>                    # 파일 = 바이트 배열

type tree = map<string, tree | blob>       # 디렉토리 = 이름 → tree 또는 blob

type commit = struct {
    parents: array<commit>                 # 부모 commit들 (머지면 여러 개)
    author: string
    message: string
    snapshot: tree                         # 최상위 tree
}

히스토리: DAG

Git은 선형 히스토리가 아닌 방향성 비순환 그래프(DAG):

  • 각 commit은 부모(parents)를 가리킴
  • 브랜치 분기 → 병렬 개발, 머지 → 두 부모를 가진 commit
  • commit은 immutable (수정 = 새 commit 생성 + 참조 변경)
o <-- o <-- o <-- o <---- o  (merge commit)
            ^            /
             \          v
              --- o <-- o

Content-Addressing

모든 객체(blob, tree, commit)는 해시로 식별 (기본 SHA-1, SHA-256 리포지토리도 존재):

  • 객체끼리 참조할 때 해시로 포인터 역할
  • git cat-file -p <hash>로 객체 내용 확인 가능
type object = blob | tree | commit         # 세 타입 통합

objects = map<string, object>              # 해시 → 객체 저장소

def store(o):
    id = sha1(o)
    objects[id] = o

def load(id):
    return objects[id]

References

  • master / main: 메인 브랜치의 최신 commit
  • HEAD: 현재 위치
  • origin/master: 리모트 브랜치 상태

객체는 immutable, 참조는 mutable. 브랜치 = 특정 commit을 가리키는 참조.

references = map<string, string>           # 이름 → 해시

def update_reference(name, id):
    references[name] = id

def read_reference(name):
    return references[name]

def load_reference(name_or_id):
    if name_or_id in references:
        return load(references[name_or_id])
    else:
        return load(name_or_id)

객체 저장 방식

blob, tree, commit 모두 object로 통합되어 .git/objects/에 저장.

Loose Object (기본):

  • 각 객체가 개별 파일로 저장
  • 경로 = 해시 앞 2자리가 디렉토리, 나머지 38자리가 파일명 (예: ab/cdef1234...)
  • type + size + content 전체를 zlib 압축 (델타 압축 아님)

Packfile (gc, clone, fetch, pull, push 시):

  • 여러 객체를 하나의 .pack 파일로 합침
  • 델타 압축 적용 (비슷한 객체끼리 차이점만 저장)
  • 해시가 파일명이 아니므로 .idx 인덱스 파일 필요 (해시 → 오프셋 매핑)

참고: Pro Git - Packfile

Staging Area

작업 디렉토리 → staging → commit 순서로 동작.

왜 필요한가:

  • 여러 기능 중 일부만 commit
  • 디버깅 코드 제외하고 commit
  • git add -p로 파일 내 일부만 staging

명령어 요약

  • 기본: init, status, add, commit, log, diff, checkout
  • 브랜치/머지: branch, merge, mergetool, rebase
  • 리모트: remote, push, fetch, pull, clone
  • Undo: commit --amend, reset, checkout --
  • 고급: stash, blame, bisect, add -p, config
  • .gitignore: 추적 제외 패턴 지정

추가 학습 자료

AI가 강의 스크립트 기반으로 만든 Git 데이터 모델 실습. 강의 영상에서 하는 것과 크게 다르지 않음.

(마크다운 이름에 Z 들어가는 이유는 위치를 아래로 옮기기 위함.)

# ========================================
# Git 내부 구조 까보기 튜토리얼
# 복붙해서 따라하면 됨
# ========================================

# 1. 임시 폴더 만들고 이동
mkdir git-internals-demo && cd git-internals-demo

# 2. Git 저장소 초기화 (브랜치 이름 main으로)
git init -b main

# 3. 파일 몇 개 만들기
echo "hello world" > hello.txt
mkdir src
echo "print('hi')" > src/main.py

# 4. 커밋 생성
git add .
git commit -m "first commit"

# ========================================
# 여기서부터 내부 구조 탐색
# ========================================

# 5. main이 가리키는 commit 해시 확인
git rev-parse main
# 출력 예: 7a3b5c9d... (40자 해시)

# 6. commit 내용 까보기
git cat-file -p main
# 출력 예:
# tree 8f94139bf9...
# author ...
# committer ...
#
# first commit

# 7. commit 안의 tree 해시 추출해서 tree 내용 까보기
# (위 출력에서 tree 뒤의 해시 복사해서 사용)
git cat-file -p $(git rev-parse main^{tree})
# 출력 예:
# 100644 blob 3b18e512d...    hello.txt
# 040000 tree 9a8b7c6d5...    src

# 8. hello.txt blob 내용 까보기
# (위 출력에서 hello.txt 옆 해시 복사해서 사용)
git cat-file -p $(git rev-parse main:hello.txt)
# 출력: hello world

# 9. src 디렉토리(tree) 까보기
git cat-file -p $(git rev-parse main:src)
# 출력 예:
# 100644 blob 5d8a9c3b2...    main.py

# 10. src/main.py blob 내용 까보기
git cat-file -p $(git rev-parse main:src/main.py)
# 출력: print('hi')

# ========================================
# 객체 타입 확인해보기
# ========================================

git cat-file -t main                              # commit
git cat-file -t $(git rev-parse main^{tree})      # tree
git cat-file -t $(git rev-parse main:hello.txt)   # blob

# ========================================
# .git 폴더 구조 확인 (실제 저장 위치)
# ========================================

# objects 폴더에 해시 앞 2자리가 디렉토리, 나머지가 파일명
find .git/objects -type f | head -5

# ========================================
# 정리: 폴더 삭제
# ========================================

cd ..
rm -rf git-internals-demo

echo "완료!"

08. Debugging and Profiling

유튜브 영상에 이런 댓글이 있었는데, 이게 딱 공감가는 내용.

It's great to have an overview of what options exist, rather than discovering them one by one over the years.

아는 것도 있지만 모르는 것도 많다. 뭘 배워야 하는지 알게 해준다. 이런 걸 학생 때 알았어야 했는데...

디버깅 부분이 관심가서 찾아보았음.

디버깅

Printf 디버깅

가장 단순한 방법. 의심 지점에 print문 삽입. 설정 불필요하고 빠르지만 출력이 많아지면 파악 어려움.

로깅 (Logging)

printf 디버깅의 체계화된 버전. 심각도 레벨(DEBUG, INFO, WARN, ERROR 등)로 필터링 가능. 파일, 소켓, 원격 서버 등 다양한 출력 대상 지원. 특정 버그가 아닌 상시 모니터링 목적.

import logging
logging.basicConfig(level=logging.ERROR)  # ERROR 이상만 출력
logging.debug("디버그 메시지")   # 출력 안 됨
logging.error("에러 메시지")     # 출력됨

터미널 색상 코딩: ANSI escape code로 로그 레벨별 색상 지정하면 가독성 향상.

echo -e "\e[38;2;255;0;0mThis is red\e[0m"  # \e[38;2;R;G;B;m ... \e[0m

시스템 로그

유닉스 계열에서 로그는 일반적으로 /var/log/에 저장됨.
웹서버, DB 등 서드파티 프로그램도 여기에 로그를 남기므로(/var/log/nginx, /var/log/mysql 등) 클라이언트 에러만으로 부족할 때 확인하면 유용함.

최근에는 통합 시스템 로그 사용이 늘어남.
Linux(systemd)는 journalctl, macOS는 log show 명령어로 조회.

logger "Hello Logs"                        # 시스템 로그에 기록
journalctl --since "1m ago" | grep Hello   # Linux
log show --last 1m | grep Hello            # macOS

디버거

printf로 부족할 때 사용. 프로그램 실행을 제어하며 상태 검사.
브레이크포인트 설정, 한 줄씩 실행, 변수 값 검사, 크래시 시점 상태 확인 등 가능.

Python - pdb/ipdb:

python -m pdb script.py   # 디버거로 실행
python -m ipdb script.py  # ipdb (색상, 자동완성 지원)

주요 명령: l(코드 표시), s(step into), n(next), b 줄번호(브레이크포인트), p 변수(출력), c(continue), q(종료)

저수준 디버거: C/C++ 및 바이너리 디버깅에는 gdb, lldb 사용.

IDE 통합: 대부분 이런 CLI 기반의 디버깅 프로그램을 GUI로 보여주는 것. CLI를 쓸 줄 알면 GUI에서 지원 안하는 고급 기능을 쓸 수도 있다.

[추가] 디버거가 동작하는 방식

언어가 어떻게 실행되느냐에 따라 다르다.

네이티브 바이너리의 경우

프로그램이 실행되면 코드가 메모리의 코드 영역(.text 섹션)에 로드된다. 이 영역은 읽기/실행만 가능하고 쓰기 불가능하므로 일반적인 방법으로는 수정할 수 없다.

디버거는 ptrace 시스템 콜을 사용해 이 제약을 우회한다. ptrace로 대상 프로세스에 attach하면 커널 권한으로 해당 프로세스의 메모리를 읽고 쓸 수 있다. 브레이크포인트는 원본 명령어를 int 3 (0xCC)로 교체하는 방식으로 구현된다. CPU가 이 명령어를 만나면 SIGTRAP 시그널이 발생하고, 디버거가 이를 받아 처리한다.

즉, 네이티브 디버거는 ptrace 기반으로 동작하며, 대상 프로그램의 협조 없이 강제로 코드를 수정한다.

VM/인터프리터 언어의 경우

실행되는 프로그램은 VM 자체이고, 바이트코드는 코드 영역이 아닌 일반 데이터 영역(힙 등)에 존재한다. 따라서 쓰기 제한이 없어 자유롭게 수정 가능하다.

VM이 실행 흐름을 완전히 제어하므로 ptrace 같은 커널 개입 없이 VM 레벨에서 디버깅 기능을 구현한다. 인터프리터 루프에 브레이크포인트 체크를 넣거나, 바이트코드에 디버그용 opcode를 삽입하는 방식이다. 이를 위해 VM/인터프리터는 디버깅 API나 프로토콜(JDWP, Chrome DevTools Protocol 등)을 제공하고, 디버거는 이에 맞춰 구현된다.

디버깅 기능은 오버헤드가 있으므로 보통 별도 옵션으로 활성화한다.
네이티브는 ptrace가 프로그램 협조 없이 강제로 동작하므로, 빌드 옵션과 무관하게 디버깅 자체는 가능하다. 다만 디버그 심볼이 없으면 변수명이나 소스 코드 라인 정보가 없어서 어셈블리 레벨로 디버깅해야 한다.
VM/인터프리터는 프로그램의 협조가 필수적이다. VM이 디버깅 API를 제공해야만 디버거가 동작하므로, 실행 시점에 디버깅 모드를 명시적으로 활성화해야 한다. 비활성화하면 체크 로직이 빠져서 성능이 향상된다.

(당연하겠지만 VM/인터프리터 자체를 디버깅하려면 네이티브 바이너리용 디버거를 써야할 것이다. JVM이라면 C++으로 만들어졌으므로 이에 맞는 디버거가 필요하다.)

JIT 컴파일 환경의 경우

JIT으로 생성된 네이티브 코드는 실행 가능 메모리에 존재한다. 이 코드에 브레이크포인트를 걸려면 mprotect로 해당 메모리를 쓰기 가능하게 변경 후 수정하거나, 해당 코드를 deoptimize해서 인터프리터 모드로 되돌린 뒤 처리한다. (실제 구현은 확인 안 해봐서 어떤 식인지는 모름)

프로세스가 자기 자신의 코드 영역을 수정하려면 mprotect()로 권한을 바꿔야 한다. ptrace는 외부에서 다른 프로세스를 제어하는 데 쓴다.

참고 자료

시스템 콜 추적

프로그램을 블랙박스로 보고 커널 호출 추적. 파일 접근, 네트워크 연결 등 예상치 못한 동작 확인에 유용.

strace ls -l 2>&1 | grep open      # Linux
sudo dtruss ls -l 2>&1 | grep open # macOS

[추가] strace vs gdb:

  • strace: 블랙박스 분석. 소스/심볼 없어도 됨. "이 프로그램이 어떤 파일을 열고, 어떤 네트워크 연결을 하는가?"
  • gdb: 화이트박스 분석. 내부 변수, 콜스택, 메모리 상태. "이 변수가 왜 이 값인가?"

strace 동작 원리: 역시 ptrace 시스템 콜 사용. 대상 프로세스가 시스템 콜을 호출할 때마다 가로채서 기록. 공식문서 피셜

네트워크 분석

네트워크 패킷 분석이 필요할 때 tcpdump나 Wireshark를 사용하면 패킷 내용을 확인하고 필터링할 수 있다.

정적 분석

코드를 실행하지 않고 분석. 오타, 타입 오류, 미사용 변수 등 탐지. 대부분의 IDE/에디터가 통합하여 실시간 표시(linting).

pyflakes script.py  # 미정의 변수, 재정의 등
mypy script.py      # 타입 체크
shellcheck script.sh # 쉘 스크립트

프로파일링

타이밍

time 명령어로 real(벽시계 시간), user(유저 코드 CPU 시간), sys(커널 코드 CPU 시간) 측정.

$ time curl https://example.com
real    0m2.561s  # 총 경과
user    0m0.015s  # CPU 유저 시간
sys     0m0.012s  # CPU 커널 시간

real >> user + sys면 I/O나 네트워크 대기 중이라는 의미.

CPU 프로파일러

트레이싱 프로파일러: 모든 함수 호출 기록. 정확하지만 오버헤드 큼.
샘플링 프로파일러: 주기적으로 스택 확인. 통계적이지만 오버헤드 작음.

cProfile (함수별): python -m cProfile -s tottime script.py 서드파티 라이브러리 함수까지 다 나와서 복잡할 수 있음.

line_profiler (라인별): 함수에 @profile 데코레이터 추가 후 kernprof -l -v script.py 어느 라인이 병목인지 직관적으로 파악 가능.

메모리 프로파일러

라인별 메모리 증감 확인. Python은 memory_profiler, C/C++은 Valgrind.

python -m memory_profiler script.py

이벤트 프로파일링 (perf)

CPU 사이클, 캐시 미스, 페이지 폴트 등 하드웨어 수준 분석. Linux 전용.

perf stat ./program   # 이벤트 통계
perf record ./program # 기록 후 perf report로 분석

시각화

Flame Graph: Y축은 콜 스택 깊이, X축은 시간 비율. 넓을수록 시간 많이 소비.
Call Graph: 함수 간 호출 관계를 그래프로 표시. Python은 pycallgraph.

리소스 모니터링

일반: htop, glances, dool - CPU, 메모리, 프로세스 종합 모니터링 I/O: iotop - 디스크 읽기/쓰기 모니터링 디스크: df(파티션별), du(파일별), ncdu(인터랙티브) - 디스크 용량 확인 메모리: free - 메모리 사용량 확인 프로세스: lsof - 특정 파일/포트를 사용 중인 프로세스 확인 네트워크: ss, ip(연결/설정), nethogs, iftop(사용량) - 네트워크 모니터링 벤치마킹: hyperfine - 명령어 성능 비교 부하 테스트: stress - 인위적 시스템 부하 생성

lsof -i :4444  # 포트 사용 프로세스 찾기
hyperfine 'fd -e jpg' 'find . -iname "*.jpg"'  # 성능 비교

브라우저 개발자 도구: 웹 개발 시 HTML/CSS/JS 검사, 네트워크 요청, 성능 프로파일링 등 광범위한 도구 세트.

09. Metaprogramming (build systems, dependency management, testing, CI)

make가 생각보다 쓸만함. 아주 간단한 경우엔 언젠가 쓸 일이 있을 수도?
나머진 대충 알던 것 들

이름이 메타프로그래밍 이유: 코드 작성 자체가 아닌, 코드를 둘러싼 과정(프로세스)에 관한 내용. 빌드 시스템, 의존성 관리, 테스트, CI 등을 다룸.

빌드 시스템 (Build Systems)

프로젝트 빌드 명령어들을 자동화하는 도구. 타겟(결과물), 의존성(필요한 것), 규칙(생성 명령어)을 정의하면 변경된 파일만 재빌드해줌.

Make

거의 모든 UNIX 시스템에 설치됨. 단순~중간 복잡도에 적합.

기본 문법:

타겟: 의존성
	명령어
  • 타겟: 생성할 파일명 또는 작업 이름 (all, clean 등)
  • 의존성: 타겟 생성 전에 필요한 파일 또는 먼저 실행할 타겟
  • 명령어: 타겟 생성을 위한 셸 명령어. 반드시 으로 시작 (스페이스 불가)

예시:

paper.pdf: paper.tex plot-data.png
	pdflatex paper.tex

plot-%.png: %.dat plot.py
	./plot.py -i $*.dat -o $@

.PHONY: clean
clean:
	rm -f *.pdf *.png
  • %: 와일드카드 패턴
  • $@: 타겟명, $*: 패턴 매칭된 문자열
  • 인자 없이 make 실행 시 첫 번째 타겟(기본 목표) 빌드
  • .PHONY: 실제 파일이 아닌 작업 이름임을 명시. 같은 이름의 파일이 있어도 항상 실행됨

간단하게 설명 잘해주는 글: https://makefiletutorial.com/

다른 빌드 시스템: CMake(C/C++), Maven/Ant(Java), Bazel(대규모 다중언어), Cargo(Rust), npm(Node.js)

의존성 관리

저장소 (Repository)

패키지가 모여있는 곳. 주로 생태계에 따라서 각자 다양하게 관리됨.
예: PyPI(Python), npm(Node.js), apt(Ubuntu), crates.io(Rust) 등.

관리형 vs 개방형: 개방형(예: Arch User Repository)은 보안 보장이 약할 수 있음.

시맨틱 버전 (Semantic Versioning)

major.minor.patch (예: 8.1.7)
  • patch 증가: 버그 수정 (API 변경 없음)
  • minor 증가: 기능 추가 (하위 호환)
  • major 증가: 하위 호환 깨지는 변경

semver.org

같은 major이고 minor가 같거나 높으면 patch는 뭐든 상관없어야 함.

(내가 만드는 프로그램을 불특정 다수가 써야하는 경우, 언어나 라이브러리에서) 사용하는 의존성 버전 요구사항은 가능한 낮게 유지하는 것이 좋음

락 파일 & 벤더링

  • 락 파일: 설치된 정확한 버전 기록. 재현 가능한 빌드, 자동 업데이트 방지.
  • 벤더링: 의존성 코드를 프로젝트에 직접 복사. 완전한 통제(코드를 소유함) 가능하나 업데이트는 수동.

버전 충돌

A→C 4.0, B→C 3.0 인 경우: 도구마다 다름

  • 하나로 통합 시도: 대부분의 도구 (버전 범위가 겹치면 하나로 해결)
  • 통합 불가 시 에러: pip 등
  • 통합 불가 시 둘 다 빌드: Cargo(Rust), npm (단, 가능하면 통합 우선)

둘 다 빌드하는 경우에도, 라이브러리가 전역 상태나 싱글톤을 사용하면 런타임에 문제가 생길 수 있음.
이론적으로는 파일/캐시 같은 공유 자원의 포맷 충돌도 가능. 이런 이유로 대부분의 도구는 가능한 한 하나의 버전으로 통합하려고 시도함.

CI (Continuous Integration)

코드 변경 시 자동 실행되는 작업. Travis CI, GitHub Actions 등.

저장소에 설정 파일 추가 → 이벤트(push, PR 등) 발생 시 가상머신에서 실행.

활용: 테스트 실행, 린터, 문서 배포, PyPI 릴리즈, 의존성 알림(Dependabot)

GitHub Pages: 마크다운 → Jekyll → 웹사이트 자동 빌드/배포. 클래스 웹사이트가 이 방식.

테스트

  • Test Suite: 특정 기능/목적에 맞게 그룹화된 테스트 케이스들의 모음
  • Unit Test: 단일 기능의 작은 테스트
  • Integration Test: 여러 컴포넌트 상호작용 테스트
  • Regression Test: 과거 버그 재발 방지 테스트
  • Mocking: 시스템 일부를 가짜로 대체 (예: 네트워크 mock으로 실제 연결 없이 테스트)

10. Security and Cryptography (보안 및 암호학)

알고있는것들이긴 했지만 유용한거 많음.

기존 도구들(Git, SSH 등)을 이해하기 위한 암호학 개념, 비 전문적.

보안 업무는 반드시 정규 교육을 받은 후에, 암호화 구현은 전문가가 아니면 직접 만들지 말 것 (다른 프로토콜 등 분야도 동일).

엔트로피 (Entropy)

무작위성의 척도. 비밀번호 강도를 측정할 때 유용함.

엔트로피 = log₂(가능한 경우의 수)

예시 경우의 수 엔트로피
동전 던지기 2 1비트
주사위 굴리기 6 ~2.58비트
2000개 사전에서 단어 4개 2000⁴ ~44비트

필요한 엔트로피: 온라인 공격 방어 ~40비트, 오프라인 브루트포스 방어 ~80비트 이상

인간은 무작위 선택을 잘 못함. 물리적/수학적으로 구한 무작위 값 권장.

해시 함수 (Hash Functions)

가변 길이 입력 → 고정 길이 출력. 결정적이지만 역산 불가.

속성: 결정적, 비가역성, 충돌 저항성 (같은 해시를 가진 두 입력 찾기 어려움)

활용:

  • Git: 모든 객체를 SHA-1 해시로 식별 (무결성 보장)
  • 파일 검증: 미러에서 다운로드 후 공식 해시와 비교
  • 커밋먼트 스킴(commitment scheme): 값을 숨기고 나중에 공개 (예: 공정한 동전 던지기 문제, 블록체인 등에서 사용)

SHA-1은 더 이상 강력한 암호학적 해시 함수로 간주되지 않음. SHA-256, SHA-512 등 대안 사용.
Git은 무결성 보장이 목적이라 변경이 급하진 않음. 단, 신규 프로젝트에선 SHA-256도 지원함.

키 파생 함수 (Key Derivation Function, KDF)

해시와 유사하나 의도적으로 느림. 브루트포스 공격 속도 저하 목적.

예시: PBKDF2, bcrypt, scrypt, Argon2

활용:

  • 패스프레이즈 → 암호화 키 생성
  • 비밀번호 저장: salt + KDF(password + salt) 형태로 저장

Salt: 사용자별 랜덤 문자열. 레인보우 테이블 공격 방어 (같은 비밀번호라도 해시값이 다름).

대칭키 암호화 (Symmetric Cryptography)

하나의 키로 암호화/복호화. 문 자물쇠와 유사 (같은 키로 열고 잠금).

예시: AES-256

  • 암호화: openssl aes-256-cbc -salt -in file.txt -out file.enc
  • 복호화: openssl aes-256-cbc -d -in file.enc -out file.dec

활용: 클라우드 업로드 전 파일 암호화 (서비스 제공자도 내용 못 봄, 키 소유자만 복구 가능)

비대칭키 암호화 (Asymmetric Cryptography)

공개키/개인키 쌍 사용. 자물쇠(padlock)와 유사 (열린 자물쇠는 주고, 키는 보관).

  • 암호화: 공개키로 암호화 → 개인키로만 복호화
  • 서명: 개인키로 서명 → 공개키로 검증

활용:

  • 이메일 암호화 (PGP)
  • 비공개 메시징 (Signal, Keybase)
  • 소프트웨어/Git 커밋 서명

하이브리드 암호화: 실제로는 대칭키(빠름)로 데이터 암호화, 비대칭키(느림)로 대칭키만 암호화하여 조합 사용.

공격 방어 기법

키 분배 문제

대칭키: 양쪽이 같은 키를 공유해야 함 → 키를 어떻게 안전하게 전달?
비대칭키: 공개키가 실제로 그 사람 것인지 어떻게 확인?

방식 설명
대역 외 교환 직접 만나서 교환
Certificate Authority (CA) 신뢰하는 인증 기관이 공개키 보증 (HTTPS)
Trust on First Use 첫 연결 시 저장, 변경 시 경고 (Signal)
안전 번호 검증 QR 코드 상호 검증 (Signal)
Web of Trust 친구의 친구를 신뢰 (PGP)
Social Proof 여러 SNS로 신원 증명 (Keybase)

오프라인 공격 방어 (패스프레이즈 + KDF)

"패스프레이즈 + KDF → 대칭키 → 민감 데이터 암호화" 패턴.

기기 분실, 파일 유출, 디스크 탈취 등 오프라인 공격 방어 목적. 파일이 유출되어도 패스프레이즈 없이 복호화 불가. KDF가 느려서 브루트포스 비용도 높음.

서비스 보호 대상
SSH, GPG 개인키 파일
비밀번호 관리자 저장된 모든 비밀번호 (볼트)
디스크 암호화 (LUKS 등) 디스크 전체
암호화폐 지갑 개인키
Signal 로컬 백업 메시지 백업

네트워크 트래픽 수집 공격 방어 (PFS)

공격 시나리오:

  1. 공격자가 암호화된 트래픽을 수년간 저장
  2. 나중에 서버 개인키 탈취 (또는 양자컴퓨터 등장)
  3. 저장해둔 과거 트래픽 전부 복호화

방어: Perfect Forward Secrecy (PFS)

매 세션마다 임시 키 쌍 생성 → 세션 키 교환 → 세션 종료 후 임시 키 폐기

서버 개인키가 유출되어도 과거 세션은 복호화 불가 (임시 키는 이미 삭제됨). TLS 1.3, Signal 등에서 사용.

11. Potpourri (잡동사니)

개별 강의로 다루기엔 작지만 알아두면 유용한 주제들 모음. 각 주제가 독립적이라 필요한 부분만 참고하면 됨.

데몬 (Daemons)

백그라운드에서 항상 실행되는 프로세스. 이름이 d로 끝나는 경우가 많음.

예시

  • sshd: SSH 연결 대기 및 인증 처리
  • crond: 예약 작업 실행
  • 네트워크 관리자, DNS 리졸버, GUI 관리자 등

Init System (초기화 시스템)

시스템 부팅 시 데몬들을 시작하고 관리하는 시스템.

OS Init System 명령어
Linux systemd systemctl
macOS launchd launchctl
# Linux (systemd)
systemctl status          # 실행 중인 데몬 목록
systemctl start/stop      # 일회성 시작/중지
systemctl enable/disable  # 부팅 시 자동 실행 설정

# macOS (launchd)
launchctl list            # 실행 중인 서비스 목록
launchctl load/unload     # 서비스 로드/언로드

커스텀 서비스도 등록 가능. Linux는 /etc/systemd/system/.service 파일, macOS는 ~/Library/LaunchAgents/.plist 파일 작성.

: 주기적 실행만 필요하면 데몬 대신 cron 사용.

FUSE (Filesystem in Userspace)

커널 모듈 없이 사용자 공간에서 파일시스템을 구현할 수 있게 해주는 인터페이스.

일반적으로 파일시스템 작업은 커널 모듈만 수행할 수 있다.
이는 커널이 VFS(Virtual File System)를 통해 파일시스템을 추상화하여 특정 저장 기술에 의존하지 않도록 설계되어 있기 때문이다.
그러나 이 구조로 인해 파일 작업을 처리하는 코드는 반드시 커널 공간에서 실행되어야 한다는 제약이 생긴다.

FUSE는 이 제약을 우회하여 사용자 공간에서 작성한 코드가 파일 작업을 직접 처리할 수 있게 한다.

동작 원리

VFS가 FUSE 커널 모듈에 파일 작업 요청을 전달하면, FUSE는 이 요청을 사용자 공간의 데몬 프로세스로 전달한다.
데몬이 요청을 처리한 후 결과를 다시 FUSE를 통해 VFS로 반환한다.

활용 예시

도구 기능
sshfs 원격 파일시스템을 SSH를 통해 로컬에 마운트
rclone Dropbox, Google Drive, S3 등 클라우드 스토리지 마운트
gocryptfs 투명한 암호화 파일시스템 (쓰기 시 암호화, 읽기 시 복호화)
kbfs Keybase 분산 암호화 파일시스템
borgbackup 백업 아카이브를 파일시스템으로 마운트하여 탐색

커맨드라인 공통 패턴

이건 유용한데 그냥 가서 보는게 나아서 설명은 스킵

기타 다루는 주제

  • 키보드 리매핑: Caps Lock → Ctrl/Esc, 키 조합으로 액션 실행, 탭 vs 홀드 구분
  • 백업: 동기화 ≠ 백업, 버전 관리/중복 제거/오프사이트 보관의 중요성
  • API: curl + jq로 웹 API 활용, OAuth 토큰 관리
  • 윈도우 매니저: 플로팅 vs 타일링
  • VPN: ISP → VPN 제공자로 신뢰 대상만 바뀜, HTTPS면 이미 암호화
  • Markdown: 가벼운 마크업 언어, 거의 모든 곳에서 지원
  • Hammerspoon: macOS Lua 스크립트 자동화
  • 부팅 + Live USB: BIOS/UEFI 설정, 복구용 부팅 미디어
  • 가상화: VM, Vagrant, Docker, 클라우드(AWS, GCP 등)
  • 노트북 프로그래밍: Jupyter Notebook 셀 단위 실행
  • GitHub: Issue로 버그 리포트, Pull Request로 코드 기여

12. Q&A (잡다한 이야기)

뭐 관심 가질만한 질문이 있긴 한데, 그나마 메모해둘만한건 몇 개 안되는듯?

셸 스크립트 실행 방식 차이

source script.sh는 현재 셸 세션에서 실행하므로 환경 변경이 유지된다. ./script.sh는 새 프로세스에서 실행하므로 환경 변경이 부모 세션에 영향을 주지 않는다.

리눅스 디렉토리 구조

  • /bin: 필수 명령어
  • /sbin: 루트용 시스템 명령어
  • /etc: 설정 파일
  • /home: 사용자 홈
  • /lib: 공용 라이브러리
  • /tmp: 임시 파일
  • /usr/bin: 비필수 명령어
  • /usr/local/bin: 사용자 컴파일 프로그램
  • /var: 로그/캐시

전체 목록은 Filesystem Hierarchy Standard 참고. 프로그램 위치는 PATH에서 찾으며 which나 type으로 확인한다.

시스템 패키지 매니저 vs 언어별 패키지 매니저

예시로 apt-get은 시스템 전역 설치, pip은 파이썬 환경 설치다.

언어별 패키지 매니저가 더 최신이고 패키지가 많다. 버전 충돌 방지를 위해 virtualenv 같은 가상 환경을 쓴다.

시스템 패키지 매니저가 유용한 경우는 라즈베리 파이 같은 ARM 컴퓨터에서 미리 컴파일된 바이너리를 제공해서 직접 컴파일할 필요가 없다는 장점이 있고, 배포판에서 테스트된 버전이라 시스템 호환성이 보장된다.

둘을 섞으면 충돌로 문제가 발생할 가능성이 있으므로 일반적으론 언어별 패키지 매니저와 가상 환경 조합을 권장한다.

강의 내용중에 알면 좋을 개인 보안 행동 지식들 있어서 그것들만 따로 정리


개인 보안 설정 가이드

1. 비밀번호 관리

1.1 비밀번호 관리자 선택

모든 사이트에 고유한 고엔트로피 비밀번호를 사용하고 마스터 비밀번호 하나만 기억한다.

도구 특징 권장 용도
1Password 유료, 편의성 우수 일반 사용자
BitWarden 클라우드 동기화, 오픈소스 크로스 플랫폼 필요 시
KeePassXC 로컬 저장, 오픈소스 클라우드 불신 시
pass CLI 기반, GPG 암호화 개발자/고급 사용자

1.2 마스터 비밀번호 생성

권장 방식: Diceware (물리적 주사위 + 4개 이상 무작위 단어 조합)

예시: correcthorsebatterystaple

공격 유형 필요 엔트로피 비고
온라인 공격 ~40 bits 시도 횟수 제한 있음
오프라인 공격 80+ bits 무제한 시도 가능

2. 2단계 인증 (2FA)

비밀번호(지식) + 인증기(소유)로 피싱 및 비밀번호 유출 방어

2.1 인증 방식 (권장순)

방식 피싱 방어 비고
FIDO/U2F 하드웨어 키 O YubiKey 등, 가장 안전
TOTP 앱 X Google Authenticator, Duo
SMS X 최후 수단, SIM 스와핑 취약

2.2 백업 키 관리

방법 설명
물리적 보관 출력 후 금고 보관, 디지털 저장 안 함
비밀번호 관리자 편의성 우선, 관리자 탈취 시 노출 감수
여분 하드웨어 키 별도 장소에 보관

3. 비공개 메시징

3.1 권장 앱

평가 비고
Signal 최선 완전한 종단간 암호화
Wire 양호 기업용 옵션 있음
WhatsApp 수용 가능 메타데이터 수집 우려

3.2 피해야 할 것

  • Telegram: 기본 설정에서 종단간 암호화 아님
  • 데스크톱 메신저: Electron 기반은 공격 표면이 큼
  • 이메일 (PGP 포함): Forward secrecy 없음, 키 배포 문제

4. 파일 보안

4.1 오프라인 공격 방어 (전체 디스크 암호화)

꺼진 노트북 도난 시나리오 대응

OS 도구
Linux cryptsetup + LUKS
Windows BitLocker
macOS FileVault

4.2 온라인 공격 방어 (파일 단위 암호화)

켜진 상태에서 탈취 시나리오 대응

방식 도구 설명
암호화 파일시스템 gocryptfs, eCryptFS 마운트/언마운트로 접근 제어
개별 파일 암호화 gpg -c 대칭 암호화
암호화 백업 Tarsnap, Borgbase 원격 백업 보호

5. 백업 전략

5.1 백업이 아닌 것들

방식 문제점
같은 디스크에 복사 디스크 고장 시 전부 손실
집의 외장 드라이브 화재/도난 시 함께 손실
Dropbox/Google Drive 삭제/손상이 그대로 동기화
RAID 미러링 삭제/랜섬웨어가 복제됨

핵심: 동기화는 백업이 아니다. 실수나 악의적 변경이 전파된다.

5.2 좋은 백업 조건

  • 오프사이트(Off-site): 물리적으로 다른 장소에 저장
  • 버전 관리: 변경 이력 보존, 특정 시점 복구 가능
  • 중복 제거: 변경된 부분만 저장해 용량 절약
  • 보안: 데이터 읽기/삭제 권한 분리

5.3 클라우드 데이터 백업 대상

로컬 파일 외에도 백업 필요: 웹메일, SNS 사진, 스트리밍 플레이리스트, 클라우드 문서

계정 해킹/정책 위반 판정 시 접근 불가하므로 오프라인 사본 필요


6. 인터넷 보안

6.1 공개 WiFi

  • 사용 후 저장된 네트워크 삭제 (자동 재연결 방지)
  • 신뢰할 수 없는 네트워크에서 VPN 사용

6.2 VPN

VPN이 하는 것:

  • ISP 대신 VPN 제공자를 통해 인터넷 접속
  • 실제 위치 대신 VPN 서버 위치로 보임
  • 로컬 네트워크에서 트래픽 암호화

VPN이 안 하는 것:

  • 신뢰 대상만 바꿈 (ISP에서 VPN 제공자로)
  • 이미 HTTPS로 암호화된 트래픽에 추가 보안 없음
상황 VPN 필요
신뢰할 수 없는 공용 WiFi O
지역 제한 콘텐츠 우회 O
집에서 신뢰할 수 있는 ISP X
이미 HTTPS 사이트 접속 X

주의: 무료 VPN 사용 금지. 직접 설정 권장 (WireGuard).

6.3 DNS 보안

DNS 쿼리는 기본적으로 평문이므로 어떤 사이트 접속하는지 노출

프로토콜 설명
DNS over TLS (DoT) DNS 쿼리를 TLS로 암호화
DNS over HTTPS (DoH) DNS 쿼리를 HTTPS로 암호화

6.4 USB 보안

공공 충전소에서 USB 데이터 차단기 사용

USB 데이터 차단기는 전원 핀만 연결하고 데이터 핀을 물리적으로 차단하는 어댑터


7. 웹 브라우저 보안

7.1 필수 확장 프로그램

확장 역할
HTTPS Everywhere HTTP 대신 HTTPS 강제
uBlock Origin 광고, 서드파티 통신, 인라인 스크립트 차단

uBlock Origin: medium/hard 모드로 설정 시 일부 사이트 작동 안 할 수 있으나 보안 향상

7.2 컨테이너/프로필 분리

쿠키와 상태를 격리하여 사이트 간 추적 방지

브라우저 방법
Firefox Multi-Account Containers로 SNS/뱅킹/쇼핑 분리
Chrome 별도 프로필 사용
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment