You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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를 사용해 정리하고 직접 다듬는 식으로 정리하였다.
내 개인 메모이므로 확실히 알고 있는 내용은 스킵해서 정리했다.
또 개인적으로 궁금한 부분은 추가적으로 찾아서 정리했다.
(대신 확실한게 아니면 참고한 자료 링크 정도는 함께 제공하려고 함.)
BIOS/UEFI
→ 부트로더 (GRUB)
→ 커널 로드
→ init/systemd (PID 1)
→ getty (TTY) 또는 sshd (SSH) 또는 display manager (GUI)
→ login 또는 인증 처리
→ /etc/passwd에서 유저 셸 경로 읽음
→ fork() + exec()로 셸 실행
→ 셸이 프롬프트 표시하고 입력 대기
이것도 알아두면 유용할거 같긴 한데, 마찬가지로 터미널 환경을 많이 접하지 않아서 쓸 일이 적음.
정규식도 개발할 떄 가끔 쓰지 자주 쓰는건 아니라...
간단하게 요약하고 필요하면 원본 보기. 원본도 소개 느낌이지 깊게 다루는건 아니라 정말 잘 다루려면 별도 학습이 필요할 듯.
은근 유용하게 써먹을만한거 많았음.
서브쉘 개념이나 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: 터미널 종료 시 전송. 기본 동작은 프로세스 종료. 데몬들은 관례적으로 이를 설정 리로드 용도로 사용
disown: 이미 실행 중인 프로세스를 job table에서 제거 (SIGHUP 대상에서 제외)
시그널은 하드웨어 인터럽트가 아니다.
커널이 유저 프로세스에 전달하는 제한적인 비동기 IPC(프로세스 간 통신) 메커니즘이며,
프로세스 상태 변화나 예외 상황을 통지하고 정해진 동작을 유도하기 위해 사용된다.
인터럽트와 유사한 “비동기 처리 패턴”을 가지지만 계층·의미·목적은 다르다.
type blob=array<byte># 파일 = 바이트 배열typetree=map<string, tree|blob># 디렉토리 = 이름 → tree 또는 blobtypecommit=struct {
parents: array<commit># 부모 commit들 (머지면 여러 개)author: stringmessage: stringsnapshot: tree# 최상위 tree
}
히스토리: DAG
Git은 선형 히스토리가 아닌 방향성 비순환 그래프(DAG):
각 commit은 부모(parents)를 가리킴
브랜치 분기 → 병렬 개발, 머지 → 두 부모를 가진 commit
commit은 immutable (수정 = 새 commit 생성 + 참조 변경)
o <-- o <-- o <-- o <---- o (merge commit)
^ /
\ v
--- o <-- o
주요 명령: 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 검사, 네트워크 요청, 성능 프로파일링 등 광범위한 도구 세트.
개별 강의로 다루기엔 작지만 알아두면 유용한 주제들 모음. 각 주제가 독립적이라 필요한 부분만 참고하면 됨.
데몬 (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 홀드 구분