Stay Hungry Stay Foolish

데브코스

[데브코스] 0129 영상 정리

dev스카이 2026. 2. 10. 18:06

OS 가상메모리

가상 메모리는 프로세스 전체가 메모리(RAM)에 올라오지 않더라도 실행이 가능하도록 하는 메모리 관리 기법입니다. 실제 물리적 메모리 크기보다 더 큰 프로그램을 실행할 수 있게 해주는 아주 똑똑한 기술이죠.

 

가상 메모리의 핵심 장점

  • 물리 메모리 제약 극복: 사용자 프로그램이 실제 RAM 용량에 신경 쓰지 않고 설계될 수 있습니다.
  • 동시 수행 능력 향상: 각 프로그램이 당장 필요한 부분만 메모리에 올리므로, 더 많은 프로그램을 동시에 올릴 수 있습니다.
  • 효율적인 I/O: 프로그램 전체를 한꺼번에 메모리에 올리고 내리는(Swap) 대신, 필요한 부분만 교체하므로 입출력 횟수가 최적화됩니다.

 

프로그램이 실행된다는 것의 의미 (컴퓨터 구조)

단순히 파일을 더블 클릭하는 것을 넘어, 컴퓨터 내부에서는 다음과 같은 일이 일어납니다.

  1. 주소 바인딩(Address Binding): 프로그램이 사용하는 가상 주소(논리 주소)를 실제 데이터가 담긴 물리 메모리 주소로 연결하는 과정입니다. CPU는 가상 주소를 보고 일하지만, 실제 데이터는 물리 주소에 있으므로 이 '번역'이 필수입니다.
  2. 스왑 영역(Swap Area): 물리 메모리(RAM)가 가득 찼을 때, 당장 쓰지 않는 데이터들을 잠시 보관해두는 하드디스크의 일부 영역입니다.
    • Swap-out: 메모리에서 디스크(스왑 영역)로 쫓겨남.
    • Swap-in: 디스크에서 다시 메모리로 불러옴.

 

가상 메모리의 작동 원리: 요구 페이징 (Demand Paging)

프로그램 실행 시 모든 페이지를 올리지 않고, '필요할 때(On-Demand)'만 메모리에 올리는 방식입니다.

  • 페이지 폴트(Page Fault): CPU가 필요한 데이터를 찾는데 메모리에 없을 때 발생하는 현상입니다. 이때 운영체제는 스왑 영역에서 해당 데이터를 찾아 메모리로 가져옵니다.

📌 핵심 요약: 메모리 관리의 예술

  • CPU: "가상 주소(Virtual Address) 100번지에 있는 거 가져와!"
  • MMU(메모리 관리 장치): "잠깐, 가상 주소 100번은 실제 RAM 주소 500번이야. (번역)"
  • OS: "만약 RAM에 없으면 내가 스왑 영역에서 가져올게, 조금만 기다려!"

개발 기본 용어

1. 디버깅 (Debugging) 이란?

"내 코드가 왜 안 돌아가지?"라는 질문에 답을 찾아가는 과정입니다.

 

  • 의미: 소프트웨어에 발생하는 문제(버그)의 원인을 찾아 해결하는 과정입니다.
  • 유래: 최초의 컴퓨터 중 하나인 '하버드 마크 II' 기계 안에 실제 나방이 들어가 오류를 일으켰던 것을 찾아서 제거한 일에서 유래되었습니다.
  • 버그가 발생하는 이유
    • 구문 오류 (Syntax Error): 오타나 문법 실수.
    • 논리적 오류 (Logic Error): 코드는 실행되지만 결과가 예상과 다른 경우.
    • 환경적 요인: 실행되는 기기나 운영체제와의 충돌.
  • 디버깅 방식: 코드 중간에 print()를 찍어 값을 확인하거나, 전문적인 디버깅 툴(Debugger)을 사용해 코드를 한 줄씩 실행하며 메모리 상태를 관찰합니다.

 

2. 컴파일러(Compiler) vs 인터프리터(Interpreter)

우리가 쓰는 언어(Java, Python 등)를 컴퓨터가 이해하는 0과 1로 번역하는 방식의 차이입니다.

구분 컴파일러 (Compiler)
인터프리터 (Interpreter)
작업 방식 코드 전체를 미리 한꺼번에 번역함
코드를 한 줄씩 읽으며 즉시 실행함
속도 실행 속도가 빠름 (번역본이 이미 있음)
실행 속도가 상대적으로 느림
수정 수정 후 다시 전체를 컴파일해야 함
수정 즉시 바로 확인 가능
언어 예시 C, C++, Java, Go
Python, JavaScript, Ruby

 

💡 Java의 독특한 점: Java는 컴파일을 하면 기계어가 아닌 '바이트코드(.class)'를 만듭니다. 이 파일은 JVM(자바 가상 머신)이라는 통역사가 있는 곳이라면 어디서든 실행될 수 있어 "Write Once, Run Anywhere"라는 슬로건을 가집니다.

 

3. 빌드(Build)와 배포(Deploy)

코드가 내 컴퓨터를 떠나 실제 서비스가 되는 과정입니다.

  • 빌드(Build): 소스 코드를 실행 가능한 소프트웨어 산출물(exe, jar, apk 등)로 만드는 과정입니다. 이 과정에서 컴파일, 테스트, 압축, 난독화 등이 이루어집니다.
  • 배포(Deploy): 빌드가 완료된 결과물을 사용자들이 접속할 수 있는 서버에 올리는 것을 말합니다.

 

4. 환경변수 (Environment Variable)

환경변수는 "어떤 환경(내 컴퓨터냐, 실제 서버냐)이냐에 따라 달라지는 비밀 정보"라고 이해하면 됩니다.

 

📍 왜 환경마다 값을 다르게 쓸까요? (예시)

  1. 데이터베이스 연결 주소
    • 개발 중일 때: 내 컴퓨터에 저장된 연습용 데이터 주소를 사용.
    • 실제 서비스 중일 때: 실제 고객의 데이터가 들어있는 안전한 서버 주소를 사용.
  2. 보안 키 (API Key)
    • 개발 중일 때: 테스트용 무료 키를 사용.
    • 실제 서비스 중일 때: 결제가 발생하는 진짜 유료 키를 사용.
  3. 에러 메시지 노출 정도
    • 개발 중일 때: 어디서 에러가 났는지 상세하게 보여줌 (공부용).
    • 실제 서비스 중일 때: "잠시 후 다시 시도해주세요"라고 깔끔하게만 보여줌 (보안용).

 

5. 라이브러리와 프레임워크: 도구상자와 조립 키트

둘 다 남이 만든 코드를 빌려 쓰는 것이지만, 그 규모가 다릅니다.

  • 라이브러리(Library): 필요할 때 꺼내 쓰는 '공구'입니다. 내가 집을 짓다가 망치가 필요하면 망치(라이브러리)를 가져다 쓰는 것이죠. 주도권은 '나'에게 있습니다.
  • 프레임워크(Framework): 이미 뼈대가 다 잡혀있는 '밀키트'나 '조립 주택'입니다. 정해진 틀 안에서 재료만 내가 넣으면 요리가 완성됩니다. 주도권은 '프레임워크'가 쥐고 있습니다.
  • 참고: 리액트(React)는 라이브러리지만, 그 기능이 워낙 강력해서 프레임워크처럼 쓰이기도 합니다.

객체지향 프로그래밍 (OOP) 핵심 개념 

객체지향이란 프로그램을 단순한 명령어의 집합이 아니라, 서로 상호작용하는 '객체'들의 모임으로 파악하는 방법론입니다.

 

① 클래스(Class)와 객체(Object)

  • 클래스: 속성과 동작을 정의한 설계도입니다.
  • 객체: 그 설계도를 바탕으로 메모리에 실제 생성된 실체(Instance)입니다.

② 캡슐화 (Encapsulation)

데이터와 그 데이터를 처리하는 함수를 하나로 묶는 것입니다. 내부의 상세한 구현은 숨기고(은닉화) 필요한 기능만 밖으로 노출하여 보안성과 유지보수성을 높입니다.

 

③ 상속 (Inheritance)

이미 만들어진 클래스의 특성을 그대로 물려받아 새로운 클래스를 만드는 것입니다. 공통된 기능을 다시 만들 필요가 없어 코드의 재사용성이 극대화됩니다.

 

④ 다형성 (Polymorphism)

하나의 인터페이스나 메서드가 상황에 따라 서로 다른 방식으로 동작하는 성질입니다. 같은 명령을 내려도 각 객체가 자기 방식대로 반응할 수 있게 하여 유연한 설계를 가능하게 합니다.

 

💡 상속 vs 인터페이스 (결정적 차이)

  • 상속 (Inheritance): '부모-자식' 관계를 형성합니다. 클래스 간의 위계질서가 뚜렷하며 "A는 B의 일종이다(is-a)"라는 관계일 때 주로 사용합니다.
  • 인터페이스 (Interface): 클래스에 상관없이 공통으로 필요한 기능(행위)의 규격을 정의합니다. "~을 할 수 있다(can-do)"라는 관계에 적합하며, 다중 구현이 가능하다는 장점이 있습니다.

프로세스와 스레드

  • 프로세스(Process): 운영체제로부터 자원을 할당받는 작업의 단위입니다. 실행 중인 프로그램 그 자체를 의미하며, 독립된 메모리 영역(Code, Data, Stack, Heap)을 가집니다.
  • 스레드(Thread): 프로세스 내에서 실행되는 흐름의 단위입니다. 프로세스가 할당받은 자원을 공유하며, 별도의 Stack 영역만 따로 가집니다.

동시성(Concurrency)과 병렬성(Parallelism)

  • 동시성: 싱글 코어에서 여러 작업을 번갈아 가며 실행하여, 마치 동시에 실행되는 것처럼 보이게 하는 것입니다.(논리적인 개념)
  • 병렬성: 멀티 코어에서 여러 작업을 물리적으로 동시에 실제로 실행하는 것입니다.(물리적인 개념)

👉 멀티 스레드를 활용해 싱글 코어에서는 동시성을 구현하고, 멀티 코어에서는 병렬성을 실현하여 프로그램의 효율을 극대화할 수 있습니다.

 

컨텍스트 스위칭 (Context Switching)

CPU가 한 프로세스(또는 스레드)에서 다른 프로세스로 전환할 때, 현재 상태를 저장하고 새로운 상태를 불러오는 과정을 말합니다.

  • 비용 발생: 스위칭이 일어나는 동안 CPU는 아무런 일을 할 수 없으므로, 너무 잦은 스위칭은 오버헤드를 발생시켜 성능을 떨어뜨립니다.

👉 컨텍스트 스위칭은 동시성을 실현하기 위한 구체적인 메커니즘이며, 프로세스보다 스레드를 사용할 때 훨씬 효율적으로 일어납니다.

프로세스와 스레드의 장단점 비교

구분 장점 단점
프로세스 하나의 프로세스가 죽어도
다른 프로세스에 영향을 주지 않아 안전함
컨텍스트 스위칭 비용이 높고 자원 공유가 복잡함
(IPC 필요)
스레드 자원을 공유하므로 응답 속도가 빠르고 효율적임 하나가 예외를 일으키면 프로세스 전체가 죽을 수 있고,
동기화 문제가 발생함

 

💡 자바에서 스레드를 사용하는 방법

자바에서는 크게 두 가지 방법을 사용합니다.

1️⃣ Thread 클래스 상속

class MyThread extends Thread {
    public void run() { /* 작업 내용 */ }
}
new MyThread().start();

 

2️⃣ Runnable 인터페이스 구현 (권장)

class MyRunnable implements Runnable {
    public void run() { /* 작업 내용 */ }
}
new Thread(new MyRunnable()).start();

 

 

 

synchronized 블록과 Thread.sleep()

  • synchronized 블록: 여러 스레드가 동시에 같은 자원에 접근할 때 발생하는 데이터 오염을 막기 위한 장치입니다. 해당 블록에 진입하는 스레드는 Lock을 획득하며, 작업이 끝날 때까지 다른 스레드의 접근을 차단합니다.
synchronized(this) {
    // 공유 자원 수정 로직
}
  • Thread.sleep(ms): 현재 실행 중인 스레드를 지정된 시간 동안 일시 정지(Timed Waiting) 상태로 만듭니다. 시간이 지나면 다시 실행 대기 상태가 됩니다.

👉 이 둘은 멀티스레드 환경에서 발생할 수 있는 '충돌'을 막거나 '속도'를 조절하는 역할을 한다.


📌 정리

 

  1. 동시성을 위해 CPU는 컨텍스트 스위칭을 하며 스레드를 번갈아 실행한다.
  2. 이때 여러 스레드가 동시에 같은 자원을 건드리면 위험하므로 synchronized로 순서를 지켜준다.
  3. 필요에 따라 Thread.sleep()으로 스레드의 실행 흐름을 미세하게 제어한다.

🧹 가비지 컬렉터 (Garbage Collector, GC)

 

프로그램이 동적으로 할당했던 메모리 영역(Heap) 중에서 더 이상 사용하지 않는 객체를 찾아 자동으로 해제해주는 엔진입니다.

 

왜 필요한가? (메모리 누수 방지)

  • 메모리 누수(Memory Leak): 사용이 끝난 메모리를 해제하지 않고 계속 들고 있으면, 시스템의 메모리가 고갈되어 프로그램이 멈추거나 느려집니다.
  • 가비지 컬렉터는 개발자가 실수로 메모리 해제를 빠뜨려도 대신 처리해주어 프로그램의 안정성을 높여줍니다.

언어별 메모리 관리 체계

  • Managed Language(자바, 파이썬 등): 가비지 컬렉터가 메모리 관리를 대신 해줍니다. 개발은 편하지만 GC가 작동할 때 시스템이 잠시 멈추는(Stop-the-world) 현상이 발생할 수 있습니다.
  • Unmanaged Language(C, C++ 등): 개발자가 직접 malloc(), free() 등을 통해 메모리를 할당하고 해제해야 합니다. 제어권이 높지만 실수할 경우 메모리 누수가 발생하기 쉽습니다.

가비지 컬렉터의 한계

  • 성능 부하: GC가 언제 실행될지 예측하기 어렵고, 실행되는 동안 CPU 자원을 소모합니다.
  • 실시간성 저하: 앞서 언급한 'Stop-the-world' 현상 때문에 아주 미세한 응답 속도가 중요한 실시간 서비스(금융, 게임 등)에서는 단점이 될 수 있습니다.
  • 완벽하지 않음: 객체가 논리적으로는 쓸모없지만, 어딘가에서 참조(Reference)되고 있다면 GC는 이를 지우지 못합니다. (이게 바로 Managed 언어의 메모리 누수 원인입니다!)

 

💡 "결국 메모리 관리를 잘해야 한다!" GC가 있다고 해서 메모리 관리에 손을 떼도 된다는 뜻은 아닙니다. 

 

✅ 메모리 관리 꿀팁

  • 참조 끊기: 사용이 끝난 대형 객체는 null 처리를 하거나, 컬렉션(List, Map 등)에서 명시적으로 제거하여 GC의 대상이 되도록 합니다.
  • 객체 재사용: 무분별한 new 연산자로 짧은 수명의 객체를 남발하지 말고, 가능하다면 객체 풀(Object Pool)을 사용하세요.
  • 변수의 범위(Scope) 최소화: 변수를 최대한 좁은 지역 변수로 사용하면, 메서드 실행이 끝남과 동시에 GC 후보가 되어 관리가 수월해집니다.
  • 정적(static) 변수 남용 금지: static으로 선언된 객체는 프로그램이 종료될 때까지 메모리에 남습니다. 꼭 필요한 경우에만 사용하세요.

🌳 깃(Git)과 깃허브(Github) : 버전 관리의 모든 것

이 둘은 '프로그램''저장소'라는 명확한 차이가 있습니다.

 

깃(Git): 내 컴퓨터의 "타임머신"이자 "CCTV"

  • 핵심 역할: 파일을 항상 감시하며 모든 변화를 기록하고 추적합니다.
  • 버전 관리: 같은 파일이라도 시간대별로, 혹은 기능별로 각기 다른 버전을 관리할 수 있습니다.
  • 협업의 기초: 여러 명이 동시에 같은 파일을 수정해도, 누구의 코드가 어떻게 바뀌었는지 추적하여 충돌 없이 합칠 수 있게 도와줍니다.

깃허브(GitHub): 소셜 "클라우드 저장소"

  • 핵심 역할: 내 컴퓨터(Local)에 있는 Git 기록들을 올려두는 온라인 클라우드 서비스입니다.
  • 공유와 협업: 깃허브에 코드를 올리면 전 세계 어디서든 내 프로젝트에 접근할 수 있고, 다른 개발자와 협업하기가 매우 쉬워집니다.
  • 대안 서비스: GitLab, Bitbucket 등이 있지만, 커뮤니티와 생태계 규모 면에서 GitHub가 가장 독보적입니다.

깃(Git)이 동작하는 원리 (3가지 공간)

깃이 파일을 추적할 때 내부적으로는 크게 3가지 단계를 거칩니다. 이 흐름을 이해하면 add, commit 명령어가 왜 필요한지 알 수 있습니다.

  1. Working Directory (작업 디렉토리): 내가 실제로 파일을 수정하고 있는 현재 폴더입니다.
  2. Staging Area (준비 영역): "이 변화들을 기록할 거야!"라고 선택한 파일들이 대기하는 곳입니다. (git add 단계)
  3. Local Repository (로컬 저장소): 선택한 파일들의 상태를 확정해서 사진을 찍듯(Snapshot) 저장하는 곳입니다. (git commit 단계)

 

'데브코스' 카테고리의 다른 글

[CSS] Flexbox 속성 한눈에 정리하기  (0) 2026.02.12
[데브코스] 0128 영상 정리  (0) 2026.02.09