컴파일 언어와 인터프리터 언어
프로그래밍 언어는 코드를 실행하는 방식에 따라 컴파일 언어와 인터프리터 언어로 나눌 수 있다. JavaScript(JS)와 같은 인터프리터 언어를 사용하는 개발자일지라도, 컴파일 언어의 동작 원리를 이해하는 것은 매우 중요하다. 특히, 현대 웹 개발에서 널리 사용되는 TypeScript(TS)가 컴파일(정확히는 트랜스파일) 방식을 채택하고 있기 때문이다.
인터프리터(Interpreter) 언어
인터프리터 언어는 소스 코드를 한 줄씩 읽어 내려가며 즉시 기계어로 번역하고 실행하는 방식이다. 대표적으로 JavaScript, Python이 여기에 속한다.
- 장점: 별도의 컴파일 과정이 없어 개발 중 빠른 코드 수정과 테스트가 가능하며, 디버깅이 용이하다.
- 단점: 런타임(실행 시점)에 매번 코드를 번역해야 하므로, 동일한 코드가 반복 실행될 경우 컴파일 언어에 비해 성능이 저하될 수 있다.
컴파일(Compiler) 언어
컴파일 언어는 실행하기 전에 소스 코드 전체를 컴파일러라는 번역기를 통해 기계어나 중간 언어(Bytecode 등)로 변환하는 과정을 거친다. C, C++, Java, 그리고 TypeScript가 이 방식을 사용한다.
- 장점:
- 사전 오류 검출: 컴파일 과정에서 코드 전체의 문법 오류, 타입 불일치 등을 미리 발견하여 런타임 에러를 줄일 수 있다.
- 실행 속도: 이미 번역된 코드를 실행하므로 런타임에서의 실행 속도가 빠르며, 최적화된 코드를 생성할 수 있다.
- 단점: 코드를 수정할 때마다 다시 컴파일해야 하므로 개발 과정에서 번거로움이 있을 수 있다.
TypeScript와 컴파일
TypeScript는 엄밀히 말해 기계어로 컴파일되는 것이 아니라, 브라우저가 이해할 수 있는 JavaScript 코드로 트랜스파일(Transpile)된다. 이 트랜스파일 과정이 컴파일과 유사한 역할을 수행하며, 코드 전체의 타입을 검사하여 JavaScript의 동적 타입으로 인해 발생할 수 있는 잠재적 버그를 사전에 차단하는 강력한 이점을 제공한다.
** 메모리 구조와 데이터 관리**
프로그램이 실행될 때, 운영체제는 해당 프로그램이 사용할 메모리 공간을 할당한다. 이 메모리 공간은 단순히 하나의 큰 덩어리로 관리되지 않고, 목적에 따라 코드(Code), 데이터(Data), 힙(Heap), 스택(Stack)의 네 가지 주요 영역으로 나뉜다.
** 메모리 구조**
- 코드(Code) 영역: 실행할 프로그램의 소스 코드가 기계어 형태로 저장되는 공간이다.
- 데이터(Data) 영역: 전역 변수와 정적 변수가 저장된다.
- 힙(Heap) 영역: 프로그래머가 동적으로 할당하고 해제하는 메모리 공간이다.
- 스택(Stack) 영역: 함수의 호출과 관련된 지역 변수, 매개변수 등이 저장된다.
** 메모리 영역을 나누는 이유**
메모리를 각기 다른 특성을 가진 영역으로 나누어 관리하는 이유는 효율성, 안정성, 그리고 예측 가능성을 극대화하기 위함이다. 각 영역은 특정 목적에 최적화된 관리 방식을 사용한다.
- 효율적인 메모리 관리: 데이터의 생명주기(Lifecycle)와 특성이 다르기 때문에, 이를 분리하여 관리하는 것이 효율적이다.
- 스택(Stack): 함수 호출처럼 생명주기가 짧고 명확하며, 크기가 고정된 데이터(지역 변수 등)를 저장한다. 스택 포인터를 이동시키는 단순하고 빠른 방식으로 메모리를 할당하고 해제할 수 있어 속도가 매우 빠르다.
- 힙(Heap): 언제 생성되고 해제될지 예측하기 어렵고, 크기가 동적으로 변하는 데이터(객체, 배열 등)를 저장한다. 다소 복잡한 알고리즘을 통해 메모리를 관리하지만, 프로그램의 유연성을 보장한다.
- 이처럼 데이터의 성격에 맞는 영역에 저장함으로써, 한 가지 방식으로 모든 것을 처리할 때 발생하는 비효율을 줄일 수 있다.
- 프로그램의 안정성 확보: 메모리 영역을 분리함으로써 서로 다른 영역이 침범하는 것을 막아 프로그램의 안정성을 높인다.
- 코드 영역의 보호: 코드 영역을 읽기 전용(Read-Only)으로 설정하여, 프로그램 실행 중에 명령어 자체가 변경되는 것을 방지한다. 만약 데이터나 힙 영역의 값이 코드 영역을 침범할 수 있다면, 악의적인 코드 주입이나 심각한 버그로 이어질 수 있다.
- 스택 오버플로우 방지: 스택 영역은 크기가 제한되어 있다. 만약 힙처럼 무한히 커질 수 있는 데이터를 스택에 저장한다면, 할당된 공간을 넘어 다른 메모리 영역을 침범하는 스택 오버플로우(Stack Overflow)가 발생하여 프로그램이 비정상적으로 종료될 수 있다. 영역을 분리함으로써 이러한 문제를 최소화한다.
- 자원의 예측 가능성과 최적화: 운영체제와 컴파일러는 각 영역의 특성을 미리 알고 있기 때문에 프로그램 실행을 최적화할 수 있다.
- 데이터 영역: 프로그램 실행 내내 유지될 전역/정적 변수들은 미리 공간을 할당받아 예측 가능하게 관리된다.
- 코드 영역: 프로그램의 실행 코드는 정적이므로, 메모리에 한 번 로드된 후 효율적으로 재사용될 수 있다.
결론적으로, 메모리 영역을 나누는 것은 자동차의 엔진, 좌석, 트렁크가 각기 다른 목적을 위해 분리된 공간에 배치되는 것과 같다. 각 공간의 역할을 명확히 하여 전체 시스템이 안정적이고 효율적으로 동작하도록 만드는 핵심적인 설계 원리이다.
메모리 구조 사용 예시
아래 JavaScript 코드를 통해 각 메모리 영역이 어떻게 사용되는지 이해할 수 있다.
const globalName = "John"; // 데이터(Data) 영역에 저장
function createGreeting(name) { // createGreeting 함수 자체는 코드(Code) 영역에 저장
const prefix = "Hello, "; // 스택(Stack) 영역에 저장되는 지역 변수
return prefix + name;
}
function introduce() {
const person = { // 'person' 변수는 스택(Stack)에, 실제 {name, age} 객체는 힙(Heap)에 저장
name: "Jane",
age: 30
};
const greeting = createGreeting(person.name); // 스택(Stack) 영역에 저장
console.log(greeting);
}
introduce(); // 함수 호출 시 스택 프레임 생성globalName은 전역 변수이므로 데이터 영역에 저장된다.createGreeting과introduce함수 코드는 코드 영역에 저장된다.introduce함수가 호출되면, 이 함수의 실행 정보를 담은 스택 프레임이 스택 영역에 생성된다.person객체는 복합 데이터이므로 실제 값{name: "Jane", age: 30}은 힙 영역에 할당되고,person변수에는 이 힙 주소가 저장된다.person변수 자체는introduce함수의 지역 변수이므로 스택 영역에 존재한다.greeting과prefix같은 지역 변수들도 스택 영역에 직접 값을 저장한다.introduce함수 실행이 끝나면, 스택에 할당되었던person,greeting등의 지역 변수들은 모두 사라진다.##
변수와 자료형
원시 타입(Primitive Type)과 참조 타입(Reference Type)
변수에 저장되는 데이터의 종류는 메모리 저장 방식에 따라 원시 타입과 참조 타입으로 나뉜다.
- 원시 타입(Primitive Type):
Number,String,Boolean,null,undefined,Symbol등이 있다. 변수에 실제 값(Value)이 직접 저장된다. - 참조 타입(Reference Type):
Object,Array,Function등이 있다. 실제 데이터는 힙 영역에 저장되고, 변수에는 해당 데이터가 저장된 힙의 메모리 주소(Reference)가 저장된다.
원시 타입과 참조 타입의 저장 위치와 그 이유
사용자의 이해대로, 데이터의 크기 예측 가능성과 변경 가능성이 저장 위치를 결정하는 핵심적인 이유이다.
1) 원시 타입은 왜 스택(Stack)에 저장되는가?
원시 타입의 값들은 고정된 크기(Fixed Size)를 갖는다. 예를 들어, number 타입은 8바이트, boolean 타입은 1바이트와 같이 컴파일 시점에 해당 데이터가 얼마만큼의 메모리를 차지할지 명확하게 예측할 수 있다.
스택 메모리는 매우 구조적이고 빠른 속도로 동작하도록 설계된 공간이다. 운영체제는 컴파일 시점에 함수의 지역 변수들이 필요로 하는 총 메모리 크기를 계산하고, 함수가 호출될 때 그만큼의 공간(스택 프레임)을 미리 할당한다. 데이터의 크기가 고정되어 있기 때문에, 스택 포인터를 이동하는 단순한 작업만으로 데이터를 빠르고 효율적으로 할당하고 해제할 수 있다.
따라서 크기가 정해져 있고 예측 가능한 원시 타입은, 이처럼 빠르고 단순한 스택 영역에 저장하는 것이 가장 효율적이다. 사용자가 언급한 "변화가 없다"는 것은, 데이터의 값 자체가 불변(Immutable)이라는 특성과 함께, 데이터 타입의 크기가 고정되어 있다는 의미로 해석할 수 있다.
2) 참조 타입은 왜 힙(Heap)에 저장되는가?
참조 타입의 데이터, 예를 들어 객체나 배열은 동적인 크기(Dynamic Size)를 갖는다. 프로그램 실행 중에 프로퍼티가 추가되거나 배열의 길이가 늘어나는 등 데이터의 크기가 얼마든지 변할 수 있다.
만약 이렇게 크기가 유동적인 데이터를 스택에 직접 저장한다면, 데이터의 크기가 커졌을 때 이미 할당된 다른 변수의 메모리 공간을 침범하는 심각한 문제가 발생할 것이다. 스택은 이처럼 크기가 변하는 데이터를 처리하도록 설계되지 않았다.
이 문제를 해결하기 위해 다음과 같은 방식을 사용한다.
- 크기가 동적인 실제 데이터는 힙(Heap) 영역이라는 넓고 유연한 메모리 공간에 저장한다. 힙은 필요에 따라 메모리를 할당하고 해제하는 데 특화되어 있다.
- 그리고 이 데이터가 저장된 힙의 메모리 주소(주소값)를 가져온다. 이 주소값 자체는 고정된 크기를 갖는다.
- 이 고정된 크기의 주소값을 변수에 할당하여 스택 영역에 저장한다.
결론적으로, 참조 타입은 데이터의 크기가 동적으로 변하기 때문에 스택에 직접 저장할 수 없다. 대신, 유연한 힙 영역에 실제 데이터를 저장하고, 그 데이터에 접근할 수 있는 고정 크기의 주소값만을 스택 영역에 저장하여 효율성과 유연성을 모두 확보하는 것이다.
가비지 컬렉터(Garbage Collector)
가비지 컬렉터는 힙 영역에서 더 이상 참조되지 않는 메모리(객체, 배열 등), 즉 "쓰레기(Garbage)"를 자동으로 찾아내어 해제하는 역할을 한다. 이를 통해 개발자는 메모리 누수(Memory Leak) 걱정 없이 프로그래밍에 집중할 수 있다. JavaScript, Java, Python 등 대부분의 고수준 언어는 가비지 컬렉터를 내장하고 있다.
변수와 상수
- 변수(Variable): 데이터를 저장하기 위해 할당받은 메모리 공간에 붙인 이름이다. 변수에 저장된 값은 프로그램 실행 중에 변경될 수 있다. (예:
let,var) - 상수(Constant): 변수와 유사하지만, 한 번 값을 할당하면 이후에는 변경할 수 없는 데이터이다. 프로그램의 안정성을 높이고 의도치 않은 값의 변경을 방지한다. (예:
const)
사용자로부터 데이터 입력받기 (C언어의 scanf 예시)
C언어에서 scanf 함수를 사용할 때는 변수 이름 앞에 & 기호를 붙인다.
int age;
scanf("%d", &age);이는 age 변수에 값을 직접 할당하는 것이 아니라, age 변수가 할당된 메모리의 주소를 scanf 함수에 전달하여, 해당 주소에 직접 사용자 입력 값을 저장하도록 하는 원리이다. 즉, "값에 의한 호출(Call by Value)"이 아닌 "주소에 의한 참조(Call by Reference)" 방식을 사용하는 것이다.
다른 고수준 언어에서는 &와 같은 주소 연산자를 명시적으로 사용하지 않더라도, 내부적으로는 비슷한 원리로 동작하여 변수가 할당된 메모리 공간에 값을 업데이트한다. 이처럼 메모리 주소의 개념을 이해하면 데이터가 어떻게 저장되고 관리되는지에 대한 깊이 있는 통찰을 얻을 수 있다.
'Programmers' 카테고리의 다른 글
| [41일차]프로그래밍 패러다임과 객체 지향 개념의 TS에서의 활용 (0) | 2025.11.06 |
|---|---|
| [40일차] C언어와 JavaScript의 메모리 관리 및 포인터 (0) | 2025.11.05 |
| [37일차]프론트엔드 커리큘럼의 시작, JS 기초 (0) | 2025.11.02 |
| [36일차]Book Market API 인증 모듈 및 주요 기능 분석 (0) | 2025.10.30 |
| [과제]Express Book Market API: JWT 인증 기능 테스트 시나리오 (0) | 2025.10.29 |