30초~1분 내외 답변 가능한 핵심 내용 정리
| 프레임워크 | 특징 |
|---|---|
| React | • 컴포넌트 기반 아키텍처 • Virtual DOM 사용으로 성능 최적화 • 단방향 데이터 플로우 |
| Vue.js | • 점진적 프레임워크 • 러닝커브가 낮음 • 템플릿 기반 문법 |
| Angular | • 완전한 프레임워크 (Full Framework) • TypeScript 기반 • 의존성 주입, RxJS 내장 |
| 프레임워크 | 특징 |
|---|---|
| Next.js | • React 기반 SSR/SSG 프레임워크 • 파일 기반 라우팅 • 자동 코드 분할 |
| Nuxt.js | • Vue 기반 SSR/SSG 프레임워크 • 규약 기반 개발 • 자동 라우팅 생성 |
| SvelteKit | • Svelte 기반 풀스택 프레임워크 • 컴파일 타임 최적화 • 번들 크기 최소화 |
CSS Grid는 2차원 레이아웃 시스템으로 행과 열을 동시에 제어할 수 있습니다.
.grid-container {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
grid-template-rows: auto 1fr auto;
gap: 20px;
}
display: grid가 적용된 부모 요소fr 단위: 사용 가능한 공간의 비율 할당minmax() 함수: 최소/최대 크기 제한repeat() 함수: 반복 패턴 정의| 저장소 | 용량 | 지속성 | 서버 전송 | 특징 |
|---|---|---|---|---|
| 쿠키(Cookie) | 4KB | 만료일까지 | ✅ 자동 전송 | • HTTP 헤더에 포함 • 인증, 세션 관리용 |
| 로컬스토리지 | 5-10MB | 영구 저장 | ❌ 수동 전송 | • 같은 도메인 내 공유 • 사용자 설정 저장 |
| 세션스토리지 | 5-10MB | 탭 종료시 삭제 | ❌ 수동 전송 | • 탭 단위 저장 • 임시 데이터 저장 |
| 인덱스드DB | 용량 큼 | 영구 저장 | ❌ 수동 전송 | • NoSQL 데이터베이스 • 복잡한 데이터 저장 |
// 로컬스토리지
localStorage.setItem('theme', 'dark');
// 세션스토리지
sessionStorage.setItem('tempData', JSON.stringify(data));
// 쿠키
document.cookie = "username=john; expires=Thu, 18 Dec 2024 12:00:00 UTC; path=/";
웹팩은 모듈 번들러로, 여러 파일을 하나의 번들로 결합합니다.
// webpack.config.js
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' },
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
}
};
브라우저가 HTML, CSS, JavaScript를 화면에 픽셀로 변환하는 일련의 과정
graph LR
A[HTML 파싱] --> B[DOM 구성]
C[CSS 파싱] --> D[CSSOM 구성]
B --> E[Render Tree]
D --> E
E --> F[Layout 계산]
F --> G[Paint 실행]
| 단계 | 설명 | 특징 |
|---|---|---|
| 1. DOM 구성 | HTML 파싱 → DOM 트리 생성 | 점진적 처리 가능 |
| 2. CSSOM 구성 | CSS 파싱 → CSSOM 트리 생성 | 전체 CSS 처리 후 진행 |
| 3. Render Tree | DOM + CSSOM 결합 | 보이는 요소만 포함 |
| 4. Layout | 요소 위치/크기 계산 | Reflow 발생 지점 |
| 5. Paint | 화면에 픽셀 그리기 | Repaint 발생 지점 |
preload, prefetch, preconnect 활용sequenceDiagram
participant U as 사용자
participant B as 브라우저
participant D as DNS 서버
participant S as 웹 서버
U->>B: www.google.com 입력
B->>D: DNS 조회 요청
D->>B: IP 주소 반환
B->>S: TCP 연결 설정
B->>S: HTTP GET 요청
S->>B: HTML 응답
B->>B: 렌더링 시작
www.google.com → 172.217.175.4GET / HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0...
Accept: text/html,application/xhtml+xml...
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 12345
Set-Cookie: ...
<!DOCTYPE html>
<html>...
| 방식 | 렌더링 위치 | 초기 로딩 | SEO | 사용자 경험 |
|---|---|---|---|---|
| CSR | 클라이언트 | 느림 | 불리 | 빠른 페이지 전환 |
| SSR | 서버 | 빠름 | 유리 | 매번 서버 요청 |
| SSG | 빌드 타임 | 매우 빠름 | 매우 유리 | 빠름 + 캐싱 가능 |
<title>, <meta description> 등이 서버에서 생성되어 즉시 확인 가능Representational State Transfer API - 웹의 기본 원칙을 활용한 아키텍처 스타일
GET /users/123 # 사용자 조회
POST /users # 사용자 생성
PUT /users/123 # 사용자 수정
DELETE /users/123 # 사용자 삭제
// ❌ Bad: 세션 의존적
GET /api/next-page
Cookie: session=abc123
// ✅ Good: 자체 포함된 요청
GET /api/users?page=2&limit=10
| HTTP 메소드 | 목적 | 안전성 | 멱등성 | 캐시 가능 |
|---|---|---|---|---|
| GET | 조회 | O | O | O |
| POST | 생성 | X | X | X |
| PUT | 수정/생성 | X | O | X |
| PATCH | 부분 수정 | X | X | X |
| DELETE | 삭제 | X | O | X |
// Express.js 예시
const app = express();
// 리소스 중심의 URL 설계
app.get('/api/v1/users', getAllUsers);
app.get('/api/v1/users/:id', getUserById);
app.post('/api/v1/users', createUser);
app.put('/api/v1/users/:id', updateUser);
app.delete('/api/v1/users/:id', deleteUser);
// 중첩 리소스
app.get('/api/v1/users/:userId/posts', getUserPosts);
## API 요구사항 예시
- 사용자 관리 (CRUD)
- 인증/인가 기능
- 페이징 처리
- 검색 기능
GET /api/v1/users # 사용자 목록
GET /api/v1/users/123 # 특정 사용자
GET /api/v1/users/123/posts # 사용자의 게시글
GET /api/v1/users?page=1&limit=10 # 페이징
// 성공 응답
{
"success": true,
"data": {
"users": [...],
"pagination": {
"current_page": 1,
"total_pages": 10
}
}
}
// 에러 응답
{
"success": false,
"error": {
"code": "USER_NOT_FOUND",
"message": "해당 사용자를 찾을 수 없습니다."
}
}
통합 모델링 언어 - 소프트웨어 개발에서 시스템의 구조와 동작을 시각적으로 표현하는 표준 모델링 언어
┌─────────────────┐
│ User │
├─────────────────┤
│ - id: long │
│ - name: String │
│ - email: String │
├─────────────────┤
│ + getId() │
│ + setName() │
└─────────────────┘
│ 1:N
▼
┌─────────────────┐
│ Post │
├─────────────────┤
│ - id: long │
│ - title: String │
│ - content: Text │
└─────────────────┘
Client → Server → Database
│ │ │
│─login──→│ │
│ │─query───→│
│ │←result──│
│←token──│ │
| 관계 유형 | 표기법 | 의미 |
|---|---|---|
| 연관 | ──── | 클래스 간 참조 관계 |
| 집합 | ◇──── | 전체-부분 관계 |
| 합성 | ◆──── | 강한 소유 관계 |
| 상속 | △──── | 일반화/특수화 |
| 프로토콜 | 계층 | 연결성 | 신뢰성 | 속도 | 용도 |
|---|---|---|---|---|---|
| HTTP | 응용 계층 (7층) | 무상태 | 높음 | 보통 | 웹 통신 |
| TCP | 전송 계층 (4층) | 연결형 | 높음 | 느림 | 신뢰성 중요 |
| UDP | 전송 계층 (4층) | 비연결형 | 낮음 | 빠름 | 실시간 통신 |
| 구분 | 세션 쿠키 | JWT |
|---|---|---|
| 저장 위치 | 서버 (세션), 클라이언트 (쿠키) | 클라이언트만 |
| 상태 관리 | Stateful | Stateless |
| 확장성 | 서버 부하 증가 | 서버 부하 적음 |
| 보안성 | 서버에서 제어 가능 | 토큰 탈취 위험 |
| 크기 | 작음 (Session ID만) | 큼 (Payload 포함) |
// 로그인 성공 시
req.session.userId = user.id;
res.cookie('sessionId', req.session.id, {
httpOnly: true,
secure: true,
maxAge: 3600000 // 1시간
});
장점:
단점:
// JWT 생성
const token = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
// 헤더에 포함
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
구조: Header.Payload.Signature
장점:
단점:
서버가 클라이언트의 상태 정보를 저장하지 않는 것
// 세션 방식 (Stateful)
// 서버가 세션 정보를 메모리/DB에 저장
server.sessions = {
"session123": { userId: 1, loginTime: "2024-12-01" }
}
// JWT 방식 (Stateless)
// 모든 정보가 토큰 자체에 포함됨
const token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsImV4cCI6MTcwMzk5NzYwMH0.signature"
// 서버는 별도 저장소에 상태 정보를 보관하지 않음
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
// 대칭키 생성 (서버만 알고 있음)
const secretKey = 'your-256-bit-secret';
// JWT 생성 (서명)
const payload = {
userId: 123,
role: 'user',
exp: Math.floor(Date.now() / 1000) + (60 * 60) // 1시간
};
const token = jwt.sign(payload, secretKey, { algorithm: 'HS256' });
console.log('생성된 토큰:', token);
// JWT 검증
try {
const decoded = jwt.verify(token, secretKey);
console.log('검증 성공:', decoded);
// { userId: 123, role: 'user', exp: 1703997600, iat: 1703994000 }
} catch (error) {
console.log('검증 실패:', error.message);
}
// RSA 키 쌍 생성
const { generateKeyPairSync } = require('crypto');
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
// 개인키로 서명 (서버에서만 가능)
const token = jwt.sign(
{ userId: 123, role: 'admin' },
privateKey,
{ algorithm: 'RS256', expiresIn: '1h' }
);
// 공개키로 검증 (어디서든 가능)
try {
const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'] });
console.log('검증 성공:', decoded);
} catch (error) {
console.log('검증 실패:', error.message);
}
// Express.js 미들웨어 예시
const authenticateToken = (req, res, next) => {
// Authorization 헤더에서 토큰 추출
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({ error: '토큰이 없습니다' });
}
// 토큰 검증
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ error: '토큰이 만료되었습니다' });
} else if (err.name === 'JsonWebTokenError') {
return res.status(401).json({ error: '유효하지 않은 토큰입니다' });
}
return res.status(403).json({ error: '토큰 검증 실패' });
}
// 검증 성공 시 사용자 정보를 req 객체에 저장
req.user = decoded;
next();
});
};
// 보호된 라우트 사용
app.get('/protected', authenticateToken, (req, res) => {
res.json({
message: '인증된 사용자만 접근 가능',
user: req.user
});
});
| 구분 | 대칭키 (HMAC) | 비대칭키 (RSA) |
|---|---|---|
| 키 개수 | 1개 (공유 비밀키) | 2개 (공개키/개인키) |
| 서명 | 비밀키로 서명 | 개인키로 서명 |
| 검증 | 같은 비밀키로 검증 | 공개키로 검증 |
| 성능 | 빠름 | 느림 |
| 보안 | 키 노출 시 위험 | 개인키만 안전하게 보관 |
| 사용 사례 | 단일 서버 | 분산 시스템, 마이크로서비스 |
Header.Payload.Signature
// Header (Base64 인코딩)
{
"alg": "HS256", // 알고리즘
"typ": "JWT" // 토큰 타입
}
// Payload (Base64 인코딩)
{
"userId": 123,
"role": "user",
"iat": 1703994000, // 발급 시간
"exp": 1703997600 // 만료 시간
}
// Signature (서명)
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
graph LR
A[소스 코드] --> B[컴파일러]
B --> C[실행 파일]
C --> D[로더]
D --> E[메모리 적재]
E --> F[CPU 실행]
| 영역 | 용도 | 특징 | 관리 |
|---|---|---|---|
| 코드 영역 | 실행 코드 저장 | 읽기 전용 | OS |
| 데이터 영역 | 전역/정적 변수 | 프로그램 종료까지 | OS |
| 힙 영역 | 동적 할당 메모리 | 런타임 크기 결정 | 프로그래머 |
| 스택 영역 | 지역 변수, 함수 호출 | LIFO 구조 | 컴파일러 |
1. Fetch (인출): 메모리에서 명령어를 가져옴
2. Decode (해석): 명령어를 해석하여 제어 신호 생성
3. Execute (실행): ALU에서 연산 수행
4. Write Back (저장): 결과를 레지스터/메모리에 저장
// 스택 영역 (자동 관리)
int localVar = 10;
// 힙 영역 (수동 관리)
int* ptr = malloc(sizeof(int));
레지스터 (가장 빠름, 가장 작음)
↓
L1 캐시
↓
L2 캐시
↓
L3 캐시
↓
주 메모리 (RAM)
↓
보조 메모리 (SSD/HDD) (가장 느림, 가장 큼)
| 구분 | 기존 가상머신 | 도커 컨테이너 |
|---|---|---|
| 가상화 수준 | 하드웨어 수준 | OS 수준 |
| 게스트 OS | 필요 | 불필요 |
| 리소스 사용량 | 높음 | 낮음 |
| 부팅 시간 | 느림 (분 단위) | 빠름 (초 단위) |
| 격리 수준 | 완전 격리 | 프로세스 수준 격리 |
기존 VM:
Host OS → Hypervisor → Guest OS → App
Docker:
Host OS → Docker Engine → Container (App)
# 프로세스 격리
PID namespace: 프로세스 ID 격리
Network namespace: 네트워크 인터페이스 격리
Mount namespace: 파일 시스템 마운트 격리
User namespace: 사용자 ID 격리
FROM node:16
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
"내 컴퓨터에서는 되는데?" 문제 해결
# docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "3000:3000"
db:
image: postgres:13
environment:
POSTGRES_DB: myapp
| 구분 | 프로세스 | 스레드 |
|---|---|---|
| 정의 | 실행 중인 프로그램 | 프로세스 내 실행 단위 |
| 메모리 공유 | 독립적 | 일부 공유 |
| 생성 비용 | 높음 | 낮음 |
| 통신 방법 | IPC (복잡) | 메모리 공유 (간단) |
| 안정성 | 높음 (격리) | 낮음 (영향 받음) |
Process A Process B
┌─────────────┐ ┌─────────────┐
│ Stack │ │ Stack │
├─────────────┤ ├─────────────┤
│ Heap │ │ Heap │
├─────────────┤ ├─────────────┤
│ Data │ │ Data │
├─────────────┤ ├─────────────┤
│ Code │ │ Code │
└─────────────┘ └─────────────┘
독립적 메모리 독립적 메모리
Process
┌─────────────────────┐
│ Thread1 │ Thread2 │ ← Stack (독립)
│ Stack │ Stack │
├─────────────────────┤
│ Heap │ ← 공유
├─────────────────────┤
│ Data │ ← 공유
├─────────────────────┤
│ Code │ ← 공유
└─────────────────────┘
void function() {
int localVar = 10; // 각 스레드의 스택에 독립적으로 저장
// 스레드 간 서로 다른 localVar 값 가능
}
int* ptr = malloc(sizeof(int)); // 같은 프로세스 내 모든 스레드가 접근 가능
*ptr = 20; // 스레드 간 공유되는 데이터
#include <unistd.h>
int main() {
pid_t pid = fork(); // 새로운 프로세스 생성
if (pid == 0) {
// 자식 프로세스
printf("Child Process\n");
} else {
// 부모 프로세스
printf("Parent Process\n");
}
return 0;
}
#include <pthread.h>
void* thread_function(void* arg) {
printf("Thread running\n");
return NULL;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL); // 스레드 생성
pthread_join(thread, NULL); // 스레드 종료 대기
return 0;
}
// 전역 변수 공유
int shared_data = 0;
void* thread1(void* arg) {
shared_data = 100; // 공유 변수 수정
return NULL;
}
void* thread2(void* arg) {
printf("%d\n", shared_data); // 공유 변수 읽기
return NULL;
}
int counter = 0;
void* increment(void* arg) {
for (int i = 0; i < 1000000; i++) {
counter++; // 위험! 동시 접근 시 데이터 손실
}
return NULL;
}
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* safe_increment(void* arg) {
for (int i = 0; i < 1000000; i++) {
pthread_mutex_lock(&mutex);
counter++; // 안전한 접근
pthread_mutex_unlock(&mutex);
}
return NULL;
}
Thread - 프로세스 내에서 실행되는 여러 실행 흐름의 단위로, CPU 이용의 기본 단위
| 구분 | 프로세스 | 스레드 |
|---|---|---|
| 정의 | 실행 중인 프로그램 | 프로세스 내의 실행 단위 |
| 메모리 | 독립적 메모리 공간 | 프로세스 내 메모리 공유 |
| 생성 비용 | 높음 | 낮음 |
| 통신 방법 | IPC (파이프, 소켓) | 공유 메모리 |
| 독립성 | 완전 독립 | 부분 독립 |
┌─────────────────────────────────┐
│ 프로세스 메모리 │
├─────────────────────────────────┤
│ 코드 영역 (공유) │ ← 모든 스레드 공유
├─────────────────────────────────┤
│ 데이터 영역 (공유) │ ← 전역변수, 정적변수
├─────────────────────────────────┤
│ 힙 영역 (공유) │ ← 동적 할당 메모리
├─────────────────────────────────┤
│ 스택 영역 1 (스레드 1 전용) │ ← 각 스레드별 독립
├─────────────────────────────────┤
│ 스택 영역 2 (스레드 2 전용) │ ← 지역변수, 함수 호출
└─────────────────────────────────┘
// Thread 클래스 상속
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
}
}
// 사용 예시
public static void main(String[] args) {
MyThread thread1 = new MyThread();
thread1.start();
// Lambda 표현식
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Lambda Thread: " + i);
}
});
thread2.start();
}
| 구분 | C | C++ |
|---|---|---|
| 패러다임 | 절차지향 | 객체지향 + 절차지향 |
| 개발년도 | 1972년 | 1985년 |
| 확장자 | .c | .cpp, .cxx |
| 컴파일러 | gcc | g++, cl |
| 표준 라이브러리 | 작음 | 매우 큼 (STL 포함) |
// C 스타일 - 함수 중심
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int x, y;
} Point;
Point* createPoint(int x, int y) {
Point* p = malloc(sizeof(Point));
p->x = x;
p->y = y;
return p;
}
void printPoint(Point* p) {
printf("Point: (%d, %d)\n", p->x, p->y);
}
int main() {
Point* p = createPoint(10, 20);
printPoint(p);
free(p);
return 0;
}
// C++ 스타일 - 클래스 중심
#include <iostream>
class Point {
private:
int x, y;
public:
Point(int x, int y) : x(x), y(y) {} // 생성자
~Point() {} // 소멸자
void print() {
std::cout << "Point: (" << x << ", " << y << ")" << std::endl;
}
// 연산자 오버로딩
Point operator+(const Point& other) {
return Point(x + other.x, y + other.y);
}
};
int main() {
Point p1(10, 20);
Point p2(5, 15);
Point p3 = p1 + p2; // 연산자 오버로딩 사용
p3.print();
return 0;
}
// C - malloc/free
int* arr = malloc(sizeof(int) * 10);
free(arr);
// C++ - new/delete + 스마트 포인터
int* arr = new int[10];
delete[] arr;
// 스마트 포인터 (C++11)
std::unique_ptr<int[]> arr = std::make_unique<int[]>(10);
// 자동으로 메모리 해제
// C - printf/scanf
printf("Hello %s\n", name);
scanf("%d", &number);
// C++ - iostream
std::cout << "Hello " << name << std::endl;
std::cin >> number;
// C - char 배열
char str[100];
strcpy(str, "Hello");
strcat(str, " World");
// C++ - string 클래스
std::string str = "Hello";
str += " World";
template<typename T>
class Vector {
private:
T* data;
size_t size;
public:
Vector(size_t size) : size(size) {
data = new T[size];
}
T& operator[](size_t index) {
return data[index];
}
};
// 사용
Vector<int> intVector(10);
Vector<double> doubleVector(5);
#include <vector>
#include <map>
#include <algorithm>
std::vector<int> numbers = {5, 2, 8, 1, 9};
std::sort(numbers.begin(), numbers.end());
std::map<std::string, int> ages;
ages["Alice"] = 25;
ages["Bob"] = 30;
| 구분 | C | Java |
|---|---|---|
| 패러다임 | 절차지향 | 객체지향 |
| 플랫폼 | 플랫폼 의존적 | 플랫폼 독립적 (JVM) |
| 메모리 관리 | 수동 (malloc/free) | 자동 (GC) |
| 포인터 | 지원 | 미지원 |
| 컴파일 | 네이티브 코드 | 바이트코드 |
| 실행 속도 | 빠름 | 상대적으로 느림 |
#include <stdio.h>
#include <stdlib.h>
int main() {
// 동적 메모리 할당
int* arr = malloc(sizeof(int) * 10);
if (arr == NULL) {
printf("메모리 할당 실패\n");
return 1;
}
// 메모리 사용
for (int i = 0; i < 10; i++) {
arr[i] = i * i;
}
// 수동으로 메모리 해제 (필수!)
free(arr);
arr = NULL; // 댕글링 포인터 방지
return 0;
}
public class MemoryExample {
public static void main(String[] args) {
// 객체 생성 (힙에 할당)
int[] arr = new int[10];
// 메모리 사용
for (int i = 0; i < 10; i++) {
arr[i] = i * i;
}
// 메모리 해제 불필요 (GC가 자동 처리)
// arr = null; // 참조 제거 (선택사항)
}
}
// hello.c
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
// 컴파일 및 실행
// Windows: gcc hello.c -o hello.exe && hello.exe
// Linux: gcc hello.c -o hello && ./hello
// 각 플랫폼별로 다른 실행 파일 생성
// Hello.java
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
// 컴파일 및 실행 (모든 플랫폼에서 동일)
// javac Hello.java (바이트코드 생성)
// java Hello (JVM에서 실행)
// 구조체와 함수 분리
typedef struct {
int balance;
char owner[50];
} Account;
void deposit(Account* account, int amount) {
account->balance += amount;
}
void withdraw(Account* account, int amount) {
if (account->balance >= amount) {
account->balance -= amount;
}
}
int getBalance(Account* account) {
return account->balance;
}
// 클래스에 데이터와 메소드 통합
public class Account {
private int balance;
private String owner;
public Account(String owner) {
this.owner = owner;
this.balance = 0;
}
public void deposit(int amount) {
balance += amount;
}
public boolean withdraw(int amount) {
if (balance >= amount) {
balance -= amount;
return true;
}
return false;
}
public int getBalance() {
return balance;
}
}
#include <stdio.h>
int divide(int a, int b, int* result) {
if (b == 0) {
return -1; // 에러 코드 반환
}
*result = a / b;
return 0; // 성공
}
int main() {
int result;
if (divide(10, 0, &result) != 0) {
printf("나누기 에러: 0으로 나눌 수 없습니다.\n");
return 1;
}
printf("결과: %d\n", result);
return 0;
}
public class ErrorHandling {
public static int divide(int a, int b) throws ArithmeticException {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return a / b;
}
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println("결과: " + result);
} catch (ArithmeticException e) {
System.out.println("나누기 에러: " + e.getMessage());
}
}
}
전 세계적으로 연결된 컴퓨터 네트워크의 집합으로, 누구나 접근 가능한 공개 네트워크
특정 조직 내부에서만 사용하는 사설 네트워크로, 인터넷 기술을 활용하되 접근이 제한됨
| 구분 | 인터넷 | 인트라넷 |
|---|---|---|
| 접근성 | 전 세계 누구나 | 조직 구성원만 |
| 보안 수준 | 낮음 | 높음 |
| 네트워크 범위 | 전 세계 | 조직 내부 |
| 정보 범위 | 무제한 | 제한적 |
| 소유권 | 없음 (공공) | 조직 소유 |
| 비용 | 사용자 부담 | 조직 부담 |
| 속도 | 상대적으로 느림 | 빠름 |
[사용자 PC] ↔ [ISP] ↔ [인터넷 백본] ↔ [웹 서버]
↕
[다른 ISP들과 연결]
[직원 PC] ↔ [스위치] ↔ [방화벽] ↔ [사내 서버]
↕
[다른 부서 네트워크]
// 누구나 접근 가능한 웹사이트
<html>
<head><title>공개 웹사이트</title></head>
<body>
<h1>누구나 볼 수 있는 내용</h1>
<p>인터넷에 연결된 모든 사람이 접근 가능</p>
</body>
</html>
// 로그인이 필요한 사내 포털
const intranetAccess = {
authentication: "Active Directory",
authorization: "Role-based",
network: "192.168.1.0/24", // 사설 IP 대역
firewall: "Only internal access"
};
// 접근 절차
if (user.isEmployee && user.isOnCorporateNetwork) {
grantAccess();
} else {
denyAccess();
}
공인 IP 주소 예시:
- 8.8.8.8 (Google DNS)
- 1.1.1.1 (Cloudflare DNS)
- 203.248.252.2 (네이버)
사설 IP 주소 범위:
- A 클래스: 10.0.0.0 ~ 10.255.255.255
- B 클래스: 172.16.0.0 ~ 172.31.255.255
- C 클래스: 192.168.0.0 ~ 192.168.255.255
보안 위협:
✗ 해킹 시도
✗ 악성 코드
✗ 피싱 사이트
✗ DDoS 공격
✗ 개인정보 유출
보안 강화:
✓ 방화벽 보호
✓ 접근 통제
✓ 암호화 통신
✓ 사용자 인증
✓ 네트워크 모니터링
<!-- 전자상거래 사이트 -->
<!DOCTYPE html>
<html>
<head>
<title>온라인 쇼핑몰</title>
</head>
<body>
<header>전 세계 누구나 접근 가능</header>
<main>
<h1>상품 목록</h1>
<div class="products">
<!-- 공개 상품 정보 -->
</div>
</main>
</body>
</html>
<!-- 사내 포털 시스템 -->
<!DOCTYPE html>
<html>
<head>
<title>사내 포털</title>
</head>
<body>
<header>직원 전용 시스템</header>
<nav>
<ul>
<li>인사 관리</li>
<li>급여 조회</li>
<li>사내 공지</li>
<li>프로젝트 관리</li>
</ul>
</nav>
<main>
<!-- 기밀 회사 정보 -->
</main>
</body>
</html>
인트라넷을 특정 외부 사용자에게 부분적으로 개방한 네트워크
인트라넷 + 제한적 외부 접근 = 엑스트라넷
예시:
- 협력업체 포털
- 고객 지원 시스템
- 공급망 관리 시스템
[재택근무 직원] ↔ [VPN 터널] ↔ [회사 인트라넷]
보안 기능:
- 암호화된 연결
- 사용자 인증
- 트래픽 터널링
- IP 주소 마스킹
온프레미스 인트라넷 + 클라우드 서비스
↓
Microsoft 365, Google Workspace 등
- 인터넷을 통해 접근
- 하지만 조직별 격리된 환경
- 강력한 인증 및 권한 관리
AWS 클라우드에서 논리적으로 격리된 가상 네트워크로, 사용자가 정의한 가상 네트워크 환경
IP 주소와 라우팅을 효율적으로 관리하기 위한 주소 할당 방법
IP주소/서브넷마스크비트수
예: 192.168.1.0/24
192.168.1.0: 네트워크 주소
/24: 처음 24비트가 네트워크 부분
(나머지 8비트가 호스트 부분)
10.0.0.0/16 분석:
- 네트워크 부분: 10.0 (16비트)
- 호스트 부분: 0.0 (16비트)
- 사용 가능한 IP: 2^16 = 65,536개
- 범위: 10.0.0.0 ~ 10.0.255.255
10.0.0.0/24 분석:
- 네트워크 부분: 10.0.0 (24비트)
- 호스트 부분: 0 (8비트)
- 사용 가능한 IP: 2^8 = 256개
- 범위: 10.0.0.0 ~ 10.0.0.255
AWS 권장 사설 IP 대역:
- 10.0.0.0/8 (10.0.0.0 ~ 10.255.255.255)
- 172.16.0.0/12 (172.16.0.0 ~ 172.31.255.255)
- 192.168.0.0/16 (192.168.0.0 ~ 192.168.255.255)
AWS VPC CIDR 제약:
- 최소: /28 (16개 IP)
- 최대: /16 (65,536개 IP)
- 추천: /16 ~ /20 (대부분의 경우)
{
"VPC_CIDR": "10.0.0.0/24",
"총_IP_수": 256,
"서브넷_설계": {
"퍼블릭_서브넷": "10.0.0.0/26", // 64개 IP
"프라이빗_서브넷": "10.0.0.64/26", // 64개 IP
"데이터베이스_서브넷": "10.0.0.128/26" // 64개 IP
}
}
{
"VPC_CIDR": "10.0.0.0/16",
"총_IP_수": 65536,
"가용영역별_서브넷": {
"AZ_A": {
"퍼블릭": "10.0.1.0/24", // 256개
"프라이빗": "10.0.2.0/24", // 256개
"데이터베이스": "10.0.3.0/24" // 256개
},
"AZ_B": {
"퍼블릭": "10.0.4.0/24", // 256개
"프라이빗": "10.0.5.0/24", // 256개
"데이터베이스": "10.0.6.0/24" // 256개
}
}
}
VPC: 10.0.0.0/16
Tier 1 - 웹 계층 (Public Subnet):
├── AZ-a: 10.0.1.0/24
└── AZ-b: 10.0.2.0/24
Tier 2 - 애플리케이션 계층 (Private Subnet):
├── AZ-a: 10.0.11.0/24
└── AZ-b: 10.0.12.0/24
Tier 3 - 데이터베이스 계층 (DB Subnet):
├── AZ-a: 10.0.21.0/24
└── AZ-b: 10.0.22.0/24
VPC: 10.0.0.0/16
개발 환경 (Dev):
├── 10.0.1.0/24 ~ 10.0.10.0/24
스테이징 환경 (Staging):
├── 10.0.11.0/24 ~ 10.0.20.0/24
프로덕션 환경 (Production):
├── 10.0.21.0/24 ~ 10.0.50.0/24
서브넷 CIDR: 10.0.1.0/24 (10.0.1.0 ~ 10.0.1.255)
예약된 IP:
- 10.0.1.0: 네트워크 주소
- 10.0.1.1: VPC 라우터용 (AWS 예약)
- 10.0.1.2: DNS 서버용 (AWS 예약)
- 10.0.1.3: 향후 사용 (AWS 예약)
- 10.0.1.255: 브로드캐스트 주소
실제 사용 가능: 10.0.1.4 ~ 10.0.1.254 (251개)
# AWS CLI로 보조 CIDR 추가
aws ec2 associate-vpc-cidr-block \
--vpc-id vpc-12345678 \
--cidr-block 10.1.0.0/16
# 결과: VPC가 두 개의 CIDR 블록 보유
# 기본: 10.0.0.0/16
# 보조: 10.1.0.0/16
VPC-A (Production): 10.0.0.0/16
VPC-B (Development): 10.1.0.0/16
VPC-C (Staging): 10.2.0.0/16
주의사항:
✓ CIDR 블록이 겹치면 안됨
✓ 향후 확장 가능성 고려
✓ 온프레미스와의 연결 고려
온프레미스: 192.168.0.0/16
AWS VPC: 10.0.0.0/16
라우팅 테이블:
- 온프레미스 → AWS: 10.0.0.0/16 via VPN
- AWS → 온프레미스: 192.168.0.0/16 via VPN
충돌 방지:
✗ 같은 대역 사용 금지
✓ 서로 다른 사설 IP 대역 사용
입력: 10.0.0.0/20
출력:
- 네트워크: 10.0.0.0
- 마스크: 255.255.240.0
- 범위: 10.0.0.0 ~ 10.0.15.255
- 호스트 수: 4,096개
- 서브넷 분할 옵션 제공
현재 필요: 100개 인스턴스
권장 CIDR: /20 (4,096개 IP)
이유: 향후 확장성 고려
서브넷 명명 예시:
- public-subnet-1a (10.0.1.0/24)
- private-subnet-1a (10.0.11.0/24)
- db-subnet-1a (10.0.21.0/24)
# VPC 설계 문서
VPC_Design:
VPC_CIDR: "10.0.0.0/16"
Region: "ap-northeast-2"
Availability_Zones:
- name: "ap-northeast-2a"
subnets:
public: "10.0.1.0/24"
private: "10.0.11.0/24"
database: "10.0.21.0/24"
- name: "ap-northeast-2c"
subnets:
public: "10.0.2.0/24"
private: "10.0.12.0/24"
database: "10.0.22.0/24"
알고리즘이 실행되는 데 걸리는 시간을 입력 크기에 대한 함수로 표현한 것
| 표기법 | 이름 | 예시 알고리즘 | 설명 |
|---|---|---|---|
| O(1) | 상수 시간 | 배열 인덱스 접근 | 입력 크기와 무관하게 일정 |
| O(log n) | 로그 시간 | 이진 탐색 | 입력이 절반씩 줄어듦 |
| O(n) | 선형 시간 | 선형 탐색 | 입력 크기에 비례 |
| O(n log n) | 선형 로그 시간 | 합병 정렬 | 효율적인 정렬 알고리즘 |
| O(n²) | 이차 시간 | 버블 정렬 | 중첩 반복문 |
| O(2ⁿ) | 지수 시간 | 피보나치 재귀 | 매우 비효율적 |
def linear_search(arr, target):
for i in range(len(arr)): # n번 반복
if arr[i] == target: # 비교 연산 (기본 연산)
return i
return -1
분석: 비교 연산이 최대 n번 → O(n)
def bubble_sort(arr):
n = len(arr)
for i in range(n): # n번 반복
for j in range(n-1): # n-1번 반복
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
분석: n × (n-1) ≈ n² → O(n²)
def binary_search(arr, target, left, right):
if left > right:
return -1
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] > target:
return binary_search(arr, target, left, mid-1) # 절반으로 줄어듦
else:
return binary_search(arr, target, mid+1, right)
분석: 매번 절반씩 줄어듦 → O(log n)
graph LR
A[O(1)] --> B[1번]
C[O(log n)] --> D[20번]
E[O(n)] --> F[100만번]
G[O(n log n)] --> H[2000만번]
I[O(n²)] --> J[1조번]
def get_first_element(arr):
return arr[0] # 배열 크기와 무관하게 항상 1번
def find_max(arr):
max_val = arr[0]
for num in arr: # n번 반복
if num > max_val:
max_val = num
return max_val
def print_pairs(arr):
for i in arr: # n번
for j in arr: # n번
print(i, j) # n × n = n²번
def power(base, exp):
if exp == 0:
return 1
if exp % 2 == 0:
half = power(base, exp // 2) # 지수가 절반으로
return half * half
else:
return base * power(base, exp - 1)
알고리즘이 실행되는 데 필요한 메모리 공간을 입력 크기에 대한 함수로 표현
# O(1) 공간 복잡도
def sum_array(arr):
total = 0 # 상수 공간
for num in arr:
total += num
return total
# O(n) 공간 복잡도
def reverse_array(arr):
return arr[::-1] # 새로운 배열 생성 (n만큼 공간 필요)
분산 데이터베이스 시스템에서 동시에 보장할 수 있는 3가지 속성 중 최대 2가지만 선택 가능하다는 이론
| 속성 | 영어명 | 의미 | 예시 |
|---|---|---|---|
| 일관성 (C) | Consistency | 모든 노드가 동시에 동일한 데이터를 봄 | 은행 잔고가 모든 ATM에서 동일 |
| 가용성 (A) | Availability | 시스템이 항상 응답을 반환함 | 서버 일부 장애 시에도 서비스 이용 가능 |
| 분할 허용성 (P) | Partition Tolerance | 네트워크 분할에도 시스템이 계속 동작 | 노드 간 통신 장애 시에도 각 노드가 동작 |
-- 네트워크 분할 시
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 모든 노드 동기화 확인 후 COMMIT
-- 동기화 실패 시 ROLLBACK
COMMIT;
// 네트워크 분할 시에도 포스팅 허용
async function createPost(userId, content) {
try {
// 로컬 노드에 저장
await localDB.insert({ userId, content, timestamp: now() });
// 백그라운드에서 다른 노드에 복제 (최종 일관성)
syncToOtherNodes({ userId, content });
return { success: true };
} catch (error) {
return { success: false };
}
}
Not Only SQL - 관계형 데이터베이스가 아닌 다양한 형태의 데이터 저장 기술
| 구분 | NoSQL | RDBMS |
|---|---|---|
| 데이터 모델 | 문서, 키-값, 컬럼, 그래프 | 테이블(행, 열) |
| 스키마 | 유연함 (Schema-less) | 고정적 (Schema-strict) |
| 확장성 | 수평 확장 용이 | 수직 확장 중심 |
| ACID | 완전 지원 어려움 | 완전 지원 |
| 쿼리 | API 호출, 단순 쿼리 | SQL (복잡한 쿼리 가능) |
| 트랜잭션 | 제한적 | 완전 지원 |
// MongoDB 문서 예시
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"name": "홍길동",
"age": 30,
"address": {
"city": "서울",
"district": "강남구"
},
"hobbies": ["독서", "영화감상", "프로그래밍"]
}
// 다양한 구조의 문서들이 같은 컬렉션에 저장 가능
{ "name": "김철수", "age": 25 }
{ "name": "이영희", "age": 28, "email": "lee@example.com" }
{ "name": "박민수", "phone": "010-1234-5678", "married": true }
// 샤딩 설정 예시
sh.enableSharding("myDatabase")
sh.shardCollection("myDatabase.users", { "userId": 1 })
// Create (생성)
db.users.insertOne({ name: "홍길동", age: 30 })
// Read (조회)
db.users.find({ age: { $gte: 25 } })
// Update (수정)
db.users.updateOne(
{ name: "홍길동" },
{ $set: { age: 31 } }
)
// Delete (삭제)
db.users.deleteOne({ name: "홍길동" })
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$customerId", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } }
])
| 구분 | MongoDB | MySQL |
|---|---|---|
| 데이터 모델 | 문서형 (BSON) | 관계형 (테이블) |
| 스키마 | 동적 | 정적 |
| 쿼리 언어 | MQL (MongoDB Query Language) | SQL |
| 인덱스 | 단일, 복합, 텍스트, 지리공간 | B-Tree, Hash, Full-text |
| 트랜잭션 | 4.0+ 지원 (제한적) | 완전 지원 |
| 조인 | $lookup (제한적) | 다양한 JOIN 지원 |
두 개 이상의 테이블을 연결하여 데이터를 조회하는 SQL 연산
SELECT 컬럼명
FROM 왼쪽테이블 A
LEFT JOIN 오른쪽테이블 B ON A.조인키 = B.조인키;
-- 고객 테이블 (customers)
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
customer_name VARCHAR(50),
city VARCHAR(50)
);
-- 주문 테이블 (orders)
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
order_date DATE,
amount DECIMAL(10,2)
);
-- 고객 데이터
INSERT INTO customers VALUES
(1, '홍길동', '서울'),
(2, '김철수', '부산'),
(3, '이영희', '대구'),
(4, '박민수', '인천');
-- 주문 데이터
INSERT INTO orders VALUES
(101, 1, '2024-01-15', 50000),
(102, 2, '2024-01-16', 75000),
(103, 1, '2024-01-17', 30000);
-- 박민수(customer_id=4)는 주문이 없음
SELECT
c.customer_id,
c.customer_name,
c.city,
o.order_id,
o.order_date,
o.amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
ORDER BY c.customer_id;
| customer_id | customer_name | city | order_id | order_date | amount |
|---|---|---|---|---|---|
| 1 | 홍길동 | 서울 | 101 | 2024-01-15 | 50000 |
| 1 | 홍길동 | 서울 | 103 | 2024-01-17 | 30000 |
| 2 | 김철수 | 부산 | 102 | 2024-01-16 | 75000 |
| 3 | 이영희 | 대구 | NULL | NULL | NULL |
| 4 | 박민수 | 인천 | NULL | NULL | NULL |
-- 양쪽 테이블에 모두 존재하는 데이터만 반환
SELECT c.customer_name, o.order_id
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id;
-- 결과: 홍길동(2건), 김철수(1건)만 반환
-- 왼쪽 테이블 모든 데이터 + 오른쪽 테이블 일치 데이터
SELECT c.customer_name, o.order_id
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id;
-- 결과: 모든 고객 포함, 주문 없으면 NULL
-- 오른쪽 테이블 모든 데이터 + 왼쪽 테이블 일치 데이터
SELECT c.customer_name, o.order_id
FROM customers c
RIGHT JOIN orders o ON c.customer_id = o.customer_id;
-- 결과: 모든 주문 포함, 고객 정보 없으면 NULL
-- 양쪽 테이블의 모든 데이터 반환
SELECT c.customer_name, o.order_id
FROM customers c
FULL OUTER JOIN orders o ON c.customer_id = o.customer_id;
-- 결과: 모든 고객과 모든 주문 포함
SELECT c.customer_name
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE o.customer_id IS NULL;
-- 결과: 이영희, 박민수
SELECT
c.customer_name,
COALESCE(SUM(o.amount), 0) as total_amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name
ORDER BY total_amount DESC;
SELECT
c.customer_name,
o.order_id,
p.product_name
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
LEFT JOIN order_details od ON o.order_id = od.order_id
LEFT JOIN products p ON od.product_id = p.product_id;
행과 행 간의 관계를 정의하여 연산을 수행하는 함수 (SQL 표준 ANSI)
함수명() OVER (
[PARTITION BY 컬럼]
[ORDER BY 컬럼]
[ROWS/RANGE 절]
)
-- 고유한 순위 부여 (동일 값도 다른 순위)
SELECT
employee_id,
name,
salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) as row_num
FROM employees;
-- 동일 값에 같은 순위, 다음 순위는 건너뜀
SELECT
name,
salary,
RANK() OVER (ORDER BY salary DESC) as rank_num
FROM employees;
-- 결과: 1, 2, 2, 4, 5 (3번 건너뜀)
-- 동일 값에 같은 순위, 다음 순위는 연속
SELECT
name,
salary,
DENSE_RANK() OVER (ORDER BY salary DESC) as dense_rank_num
FROM employees;
-- 결과: 1, 2, 2, 3, 4 (연속된 순위)
-- 이전/다음 행의 값 가져오기
SELECT
name,
salary,
LAG(salary, 1, 0) OVER (ORDER BY hire_date) as prev_salary,
LEAD(salary, 1, 0) OVER (ORDER BY hire_date) as next_salary
FROM employees;
-- 윈도우 내 첫 번째/마지막 값
SELECT
name,
salary,
FIRST_VALUE(salary) OVER (
PARTITION BY department_id
ORDER BY salary DESC
ROWS UNBOUNDED PRECEDING
) as highest_salary_in_dept
FROM employees;
-- 누적 합계
SELECT
name,
salary,
SUM(salary) OVER (
ORDER BY hire_date
ROWS UNBOUNDED PRECEDING
) as running_total
FROM employees;
-- 이동 평균
SELECT
name,
salary,
AVG(salary) OVER (
ORDER BY hire_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) as moving_avg_3
FROM employees;
| 구분 | PARTITION BY | GROUP BY |
|---|---|---|
| 결과 행 수 | 원본과 동일 | 그룹 수만큼 |
| 용도 | 분석용 | 집계용 |
| SELECT 절 | 모든 컬럼 가능 | 그룹키 + 집계함수만 |
-- GROUP BY (부서별 평균 급여)
SELECT department_id, AVG(salary)
FROM employees
GROUP BY department_id;
-- 결과: 부서 수만큼 행
-- PARTITION BY (각 직원과 부서 평균 급여)
SELECT
name,
department_id,
salary,
AVG(salary) OVER (PARTITION BY department_id) as dept_avg
FROM employees;
-- 결과: 전체 직원 수만큼 행
SELECT
department_id,
name,
salary,
RANK() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) as dept_rank
FROM employees
WHERE RANK() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) <= 3; -- 각 부서 상위 3명
SELECT
year_month,
sales,
LAG(sales, 12) OVER (ORDER BY year_month) as prev_year_sales,
ROUND(
(sales - LAG(sales, 12) OVER (ORDER BY year_month))
/ LAG(sales, 12) OVER (ORDER BY year_month) * 100, 2
) as growth_rate
FROM monthly_sales;
SELECT
name,
salary,
NTILE(4) OVER (ORDER BY salary) as quartile,
PERCENT_RANK() OVER (ORDER BY salary) as percentile
FROM employees;
-- 물리적 행 기준
SELECT
name,
salary,
AVG(salary) OVER (
ORDER BY hire_date
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) as avg_3_rows
FROM employees;
-- 논리적 값 기준
SELECT
name,
salary,
COUNT(*) OVER (
ORDER BY salary
RANGE BETWEEN 1000 PRECEDING AND 1000 FOLLOWING
) as similar_salary_count
FROM employees;
다른 SQL 문 안에 포함된 SELECT 문
-- 평균 급여보다 높은 직원 조회
SELECT name, salary
FROM employees
WHERE salary > (
SELECT AVG(salary) FROM employees -- 1. 서브쿼리 먼저 실행
);
-- 실행 순서: 서브쿼리 → 메인쿼리
실행 과정:
1. SELECT AVG(salary) FROM employees 실행 → 결과: 5000
2. WHERE salary > 5000 조건으로 메인쿼리 실행
-- 각 부서별 평균보다 급여가 높은 직원
SELECT e1.name, e1.salary, e1.department_id
FROM employees e1
WHERE e1.salary > (
SELECT AVG(e2.salary)
FROM employees e2
WHERE e2.department_id = e1.department_id -- 외부 테이블 참조
);
실행 과정:
1. 메인쿼리의 첫 번째 행 처리
2. 해당 행의 department_id로 서브쿼리 실행
3. 서브쿼리 결과와 비교하여 조건 확인
4. 메인쿼리의 다음 행으로 반복
-- 주문이 있는 고객만 조회
SELECT c.customer_name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);
실행 과정:
1. 메인쿼리의 각 행에 대해
2. 서브쿼리에서 일치하는 행 존재 여부 확인
3. 존재하면 TRUE, 없으면 FALSE
4. TRUE인 행만 결과에 포함
-- 서브쿼리가 한 번만 실행됨
SELECT name FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE location = 'Seoul'
);
-- 실행 횟수: 서브쿼리 1회 + 메인쿼리 1회
-- 메인쿼리 행 수만큼 서브쿼리 실행
SELECT name FROM employees e1
WHERE salary > (
SELECT AVG(salary)
FROM employees e2
WHERE e2.department_id = e1.department_id
);
-- 실행 횟수: 서브쿼리 N회 (N = 메인쿼리 행 수)
-- 상관 서브쿼리 (느림)
SELECT e1.name
FROM employees e1
WHERE e1.salary > (
SELECT AVG(e2.salary)
FROM employees e2
WHERE e2.department_id = e1.department_id
);
-- JOIN으로 최적화 (빠름)
SELECT e1.name
FROM employees e1
JOIN (
SELECT department_id, AVG(salary) as avg_salary
FROM employees
GROUP BY department_id
) dept_avg ON e1.department_id = dept_avg.department_id
WHERE e1.salary > dept_avg.avg_salary;
-- EXISTS (대용량 데이터에서 유리)
SELECT customer_name
FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id
);
-- IN (소용량 데이터에서 유리)
SELECT customer_name
FROM customers
WHERE customer_id IN (
SELECT DISTINCT customer_id FROM orders
);
-- Oracle
EXPLAIN PLAN FOR
SELECT name FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- MySQL
EXPLAIN
SELECT name FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
| 유형 | 실행 방식 | 성능 | 사용 시기 |
|---|---|---|---|
| 단순 서브쿼리 | 서브쿼리 → 메인쿼리 | 우수 | 고정값 조건 |
| 상관 서브쿼리 | 메인쿼리 행마다 서브쿼리 | 주의 | 행별 다른 조건 |
| EXISTS | 존재 여부만 확인 | 양호 | 존재 여부 확인 |
| IN | 값 목록과 비교 | 양호 | 값 목록 매칭 |
| 구분 | Oracle | MySQL |
|---|---|---|
| 개발사 | Oracle Corporation | Oracle Corporation (오픈소스) |
| 라이선스 | 상용 (유료) | 오픈소스 (GPL) + 상용 |
| 플랫폼 | 멀티 플랫폼 | 멀티 플랫폼 |
| 아키텍처 | 멀티 프로세스 | 멀티 스레드 |
| 주요 용도 | 대기업, 복잡한 시스템 | 웹 애플리케이션, 스타트업 |
-- Oracle
SELECT first_name || ' ' || last_name AS full_name
FROM employees;
-- MySQL
SELECT CONCAT(first_name, ' ', last_name) AS full_name
FROM employees;
-- Oracle (12c 이전)
SELECT * FROM (
SELECT * FROM employees ORDER BY salary DESC
) WHERE ROWNUM <= 5;
-- Oracle (12c 이후)
SELECT * FROM employees
ORDER BY salary DESC
FETCH FIRST 5 ROWS ONLY;
-- MySQL
SELECT * FROM employees
ORDER BY salary DESC
LIMIT 5;
-- Oracle
SELECT SYSDATE FROM DUAL;
SELECT SYSTIMESTAMP FROM DUAL;
-- MySQL
SELECT NOW();
SELECT CURRENT_TIMESTAMP();
-- Oracle
SELECT SUBSTR(name, 1, 3) FROM employees;
-- MySQL
SELECT SUBSTRING(name, 1, 3) FROM employees;
-- 또는
SELECT LEFT(name, 3) FROM employees;
| 용도 | Oracle | MySQL |
|---|---|---|
| 정수 | NUMBER(10) | INT, BIGINT |
| 실수 | NUMBER(10,2) | DECIMAL(10,2), FLOAT |
| 문자열 | VARCHAR2(4000) | VARCHAR(65535) |
| 긴 문자열 | CLOB | TEXT, LONGTEXT |
| 날짜 | DATE | DATE, DATETIME |
| 시간 | TIMESTAMP | TIME, TIMESTAMP |
| 불린 | NUMBER(1) | BOOLEAN (TINYINT) |
-- 시퀀스 생성
CREATE SEQUENCE emp_seq
START WITH 1
INCREMENT BY 1
NOCACHE;
-- 사용
INSERT INTO employees (id, name)
VALUES (emp_seq.NEXTVAL, '홍길동');
-- 현재 값 조회
SELECT emp_seq.CURRVAL FROM DUAL;
-- 테이블 생성 시
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100)
);
-- 사용 (자동으로 증가)
INSERT INTO employees (name) VALUES ('홍길동');
-- 마지막 값 조회
SELECT LAST_INSERT_ID();
| 엔진 | 특징 | 용도 |
|---|---|---|
| InnoDB | 트랜잭션 지원, 외래키 | 일반적인 애플리케이션 |
| MyISAM | 빠른 읽기, 트랜잭션 미지원 | 로그, 통계 데이터 |
| Memory | 메모리에 저장 | 임시 데이터, 캐시 |
-- Oracle (오래전부터 지원)
SELECT name, salary,
RANK() OVER (ORDER BY salary DESC) as rank
FROM employees;
-- MySQL (8.0부터 지원)
SELECT name, salary,
RANK() OVER (ORDER BY salary DESC) as rank
FROM employees;
-- Oracle
WITH tree(id, name, parent_id, level) AS (
SELECT id, name, parent_id, 1
FROM categories WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id, t.level + 1
FROM categories c
JOIN tree t ON c.parent_id = t.id
)
SELECT * FROM tree;
-- MySQL (8.0부터 지원)
WITH RECURSIVE tree AS (
SELECT id, name, parent_id, 1 as level
FROM categories WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id, t.level + 1
FROM categories c
JOIN tree t ON c.parent_id = t.id
)
SELECT * FROM tree;
데이터베이스에서 하나의 논리적 작업 단위를 구성하는 연산들의 집합
트랜잭션의 모든 연산이 성공하거나 모두 실패해야 함 (All or Nothing)
BEGIN TRANSACTION;
-- 계좌 A에서 10만원 출금
UPDATE accounts
SET balance = balance - 100000
WHERE account_id = 'A';
-- 계좌 B에 10만원 입금
UPDATE accounts
SET balance = balance + 100000
WHERE account_id = 'B';
-- 둘 다 성공하면 COMMIT, 하나라도 실패하면 ROLLBACK
COMMIT;
위반 예시:
-- 출금은 성공했지만 입금이 실패한 경우
-- → 돈이 사라지는 문제 발생 (원자성 위반)
트랜잭션 실행 전후에 데이터베이스가 일관된 상태를 유지해야 함
-- 제약 조건 예시
ALTER TABLE accounts
ADD CONSTRAINT chk_balance
CHECK (balance >= 0);
-- 일관성 유지 예시
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 50000 WHERE account_id = 'A';
-- 만약 balance가 음수가 되면 제약 조건 위반으로 자동 ROLLBACK
COMMIT;
위반 예시:
-- 잔고가 음수가 되는 경우
-- 외래키 제약 조건을 위반하는 경우
동시에 실행되는 트랜잭션들이 서로 영향을 주지 않아야 함
| 수준 | 설명 | Dirty Read | Non-Repeatable Read | Phantom Read |
|---|---|---|---|---|
| READ UNCOMMITTED | 커밋되지 않은 데이터도 읽기 | O | O | O |
| READ COMMITTED | 커밋된 데이터만 읽기 | X | O | O |
| REPEATABLE READ | 같은 행은 항상 같은 결과 | X | X | O |
| SERIALIZABLE | 완전한 격리 | X | X | X |
-- 격리 수준 설정
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 트랜잭션 1
BEGIN TRANSACTION;
SELECT balance FROM accounts WHERE account_id = 'A'; -- 100,000 원
-- ... 다른 작업 수행 중
-- 트랜잭션 2 (동시 실행)
BEGIN TRANSACTION;
UPDATE accounts SET balance = 50000 WHERE account_id = 'A';
COMMIT;
-- 트랜잭션 1 계속
SELECT balance FROM accounts WHERE account_id = 'A'; -- 50,000 원 (변경됨)
COMMIT;
커밋된 트랜잭션의 결과는 영구적으로 보존되어야 함
BEGIN TRANSACTION;
INSERT INTO orders (customer_id, amount) VALUES (1, 50000);
COMMIT; -- 이 시점부터 시스템 장애가 발생해도 데이터는 보존됨
보장 방법:
-- 트랜잭션 A
UPDATE accounts SET balance = 50000 WHERE id = 1;
-- 아직 COMMIT 하지 않음
-- 트랜잭션 B
SELECT balance FROM accounts WHERE id = 1; -- 50000 읽음 (문제!)
-- 트랜잭션 A가 ROLLBACK 하면 잘못된 데이터를 읽은 것
-- 트랜잭션 A
SELECT balance FROM accounts WHERE id = 1; -- 100000
-- 트랜잭션 B
UPDATE accounts SET balance = 50000 WHERE id = 1;
COMMIT;
-- 트랜잭션 A
SELECT balance FROM accounts WHERE id = 1; -- 50000 (다른 값!)
-- 트랜잭션 A
SELECT COUNT(*) FROM accounts WHERE balance > 10000; -- 5개
-- 트랜잭션 B
INSERT INTO accounts (balance) VALUES (20000);
COMMIT;
-- 트랜잭션 A
SELECT COUNT(*) FROM accounts WHERE balance > 10000; -- 6개 (새로운 행!)
graph LR
A[Active] --> B[Partially Committed]
B --> C[Committed]
B --> D[Failed]
A --> D
D --> E[Aborted]
-- 읽기 락 - 다른 트랜잭션도 읽기 가능, 쓰기 불가
SELECT * FROM accounts WHERE id = 1 LOCK IN SHARE MODE;
-- 쓰기 락 - 다른 트랜잭션의 읽기/쓰기 모두 불가
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- 트랜잭션 A
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE id = 1; -- 1번 락
UPDATE accounts SET balance = balance + 1000 WHERE id = 2; -- 2번 락 대기
-- 트랜잭션 B (동시 실행)
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE id = 2; -- 2번 락
UPDATE accounts SET balance = balance + 1000 WHERE id = 1; -- 1번 락 대기
-- 결과: 서로 락을 기다리는 교착상태 발생
-- 해결: DBMS가 자동으로 한 트랜잭션을 롤백
@Service
@Transactional
public class AccountService {
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
// 출금
accountRepository.withdraw(fromAccount, amount);
// 입금
accountRepository.deposit(toAccount, amount);
// 예외 발생 시 자동 롤백
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("금액은 0보다 커야 합니다");
}
}
}
현실 세계의 데이터를 컴퓨터가 이해할 수 있도록 개념적으로 구조화하는 과정
| 단계 | 목적 | 결과물 | 특징 |
|---|---|---|---|
| 개념적 모델링 | 업무 요구사항 분석 | ERD | 개체, 속성, 관계 정의 |
| 논리적 모델링 | 시스템 설계 | 정규화된 테이블 | 데이터 타입, 제약조건 |
| 물리적 모델링 | 성능 최적화 | 실제 테이블 | 인덱스, 파티션 |
graph LR
A[웹 기반] --> B[무료]
B --> C[쉬운 사용]
C --> D[실시간 공유]
-- 자동 생성되는 DDL 예시
CREATE TABLE `users` (
`user_id` INT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL,
`email` VARCHAR(100) NOT NULL,
PRIMARY KEY (`user_id`),
UNIQUE INDEX `username_UNIQUE` (`username`),
UNIQUE INDEX `email_UNIQUE` (`email`)
);
추천 도구: draw.io, MySQL Workbench
- 빠른 시작
- 비용 절약
- 단순한 구조
추천 도구: DBeaver + draw.io
- 기존 DB 분석 가능
- 적절한 기능
- 팀 협업 가능
추천 도구: ERwin, PowerDesigner
- 고급 기능 필요
- 표준화 요구
- 복잡한 관계 관리
고객 ||--o{ 주문
- ||: 하나
- o: 0개 이상
- {: 여러 개
고객 ——————< 주문
- ——: 하나
- <: 여러 개
-- 고객 테이블
CREATE TABLE customers (
customer_id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE,
phone VARCHAR(20),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 상품 테이블
CREATE TABLE products (
product_id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(200) NOT NULL,
price DECIMAL(10,2) NOT NULL,
stock_quantity INT DEFAULT 0,
category_id INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 주문 테이블
CREATE TABLE orders (
order_id INT PRIMARY KEY AUTO_INCREMENT,
customer_id INT NOT NULL,
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
total_amount DECIMAL(10,2),
status ENUM('pending', 'confirmed', 'shipped', 'delivered'),
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
-- 주문 상세 테이블
CREATE TABLE order_items (
order_item_id INT PRIMARY KEY AUTO_INCREMENT,
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL,
price DECIMAL(10,2) NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(order_id),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
-- 테이블명: 복수형, 소문자, 언더스코어
users, order_items, product_categories
-- 컬럼명: 단수형, 의미있는 이름
user_id, first_name, created_at
-- 외래키: 참조테이블명_id
customer_id, product_id
-- 1차 정규화: 원자값만 저장
-- 나쁜 예
CREATE TABLE users (
id INT,
hobbies VARCHAR(200) -- "독서,영화,게임" (X)
);
-- 좋은 예
CREATE TABLE users (id INT, name VARCHAR(100));
CREATE TABLE user_hobbies (
user_id INT,
hobby VARCHAR(50),
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 자주 검색되는 컬럼에 인덱스
CREATE INDEX idx_user_email ON users(email);
CREATE INDEX idx_order_date ON orders(order_date);
CREATE INDEX idx_customer_orders ON orders(customer_id);
테이블의 검색 속도를 향상시키기 위해 별도로 생성하는 데이터 구조
[50]
/ \
[20,30] [70,90]
/ | \ / | \
[10][25][35][60][80][95]
-- 인덱스 없이: O(n) - 전체 스캔
SELECT * FROM users WHERE email = 'user@example.com';
-- 100만 건 중 1건 찾기: 최대 100만 번 비교
-- 인덱스 있음: O(log n) - 트리 탐색
CREATE INDEX idx_users_email ON users(email);
-- 100만 건 중 1건 찾기: 최대 20번 비교
-- 인덱스 없이: 정렬 알고리즘 수행
SELECT * FROM products ORDER BY price;
-- 인덱스 있음: 이미 정렬된 상태로 저장됨
CREATE INDEX idx_products_price ON products(price);
-- GROUP BY 절에서 인덱스 활용
SELECT category_id, COUNT(*)
FROM products
GROUP BY category_id;
-- category_id에 인덱스가 있으면 빠른 그룹핑
CREATE INDEX idx_products_category ON products(category_id);
-- 테이블 데이터가 인덱스 순서로 물리적 저장
CREATE TABLE users (
id INT PRIMARY KEY, -- 자동으로 클러스터형 인덱스
name VARCHAR(100)
);
-- 별도의 구조로 저장, 테이블 데이터를 가리킴
CREATE INDEX idx_users_name ON users(name);
-- 여러 컬럼을 조합한 인덱스
CREATE INDEX idx_orders_customer_date
ON orders(customer_id, order_date);
-- 효과적인 쿼리들
SELECT * FROM orders WHERE customer_id = 123; -- ✓
SELECT * FROM orders WHERE customer_id = 123 AND order_date = '2024-01-01'; -- ✓
SELECT * FROM orders WHERE order_date = '2024-01-01'; -- △ (두 번째 컬럼만 사용)
-- 중복 값 방지 + 성능 향상
CREATE UNIQUE INDEX idx_users_email ON users(email);
-- 테이블 크기: 1GB
-- 인덱스 추가 시: 테이블 1GB + 인덱스 200MB = 1.2GB
-- INSERT 시
INSERT INTO users (name, email) VALUES ('홍길동', 'hong@email.com');
-- 1. 테이블에 데이터 삽입
-- 2. 모든 인덱스 업데이트 (추가 시간 소요)
-- UPDATE 시
UPDATE users SET email = 'new@email.com' WHERE id = 1;
-- 1. 테이블 데이터 수정
-- 2. email 인덱스 재구성 (기존 삭제 + 새로운 추가)
-- DELETE 시
DELETE FROM users WHERE id = 1;
-- 1. 테이블에서 데이터 삭제
-- 2. 모든 인덱스에서 해당 항목 삭제
-- 시간이 지나면서 인덱스 성능 저하
-- 정기적인 재구성 필요
ALTER INDEX idx_users_email REBUILD; -- Oracle
OPTIMIZE TABLE users; -- MySQL
-- 좋은 인덱스 후보 (높은 선택도)
CREATE INDEX idx_users_email ON users(email); -- 99% 유니크
CREATE INDEX idx_users_ssn ON users(ssn); -- 100% 유니크
-- 나쁜 인덱스 후보 (낮은 선택도)
CREATE INDEX idx_users_gender ON users(gender); -- 50% (남/여)
CREATE INDEX idx_users_status ON users(status); -- 10% (활성/비활성/대기 등)
-- 카디널리티가 높은 컬럼부터 복합 인덱스 구성
CREATE INDEX idx_orders_date_customer
ON orders(order_date, customer_id); -- order_date가 더 다양함
-- 잘못된 순서
CREATE INDEX idx_orders_customer_date
ON orders(customer_id, order_date); -- customer_id 값이 적음
-- 자주 사용되는 WHERE 절 분석
SELECT * FROM products
WHERE category_id = 1 AND price BETWEEN 10000 AND 50000;
-- 적절한 인덱스 생성
CREATE INDEX idx_products_category_price
ON products(category_id, price);
-- MySQL
SELECT
table_schema,
table_name,
index_name,
cardinality
FROM information_schema.statistics
WHERE table_schema = 'mydb';
-- Oracle
SELECT
index_name,
table_name,
uniqueness,
status
FROM user_indexes;
-- MySQL
EXPLAIN SELECT * FROM users WHERE email = 'test@email.com';
-- Oracle
EXPLAIN PLAN FOR
SELECT * FROM users WHERE email = 'test@email.com';
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Thread - 프로세스 내에서 실행되는 여러 실행 흐름의 단위로, CPU 이용의 기본 단위
| 구분 | 프로세스 | 스레드 |
|---|---|---|
| 정의 | 실행 중인 프로그램 | 프로세스 내의 실행 단위 |
| 메모리 | 독립적 메모리 공간 | 프로세스 내 메모리 공유 |
| 생성 비용 | 높음 | 낮음 |
| 통신 방법 | IPC (파이프, 소켓) | 공유 메모리 |
| 독립성 | 완전 독립 | 부분 독립 |
┌─────────────────────────────────┐
│ 프로세스 메모리 │
├─────────────────────────────────┤
│ 코드 영역 (공유) │ ← 모든 스레드 공유
├─────────────────────────────────┤
│ 데이터 영역 (공유) │ ← 전역변수, 정적변수
├─────────────────────────────────┤
│ 힙 영역 (공유) │ ← 동적 할당 메모리
├─────────────────────────────────┤
│ 스택 영역 1 (스레드 1 전용) │ ← 각 스레드별 독립
├─────────────────────────────────┤
│ 스택 영역 2 (스레드 2 전용) │ ← 지역변수, 함수 호출
└─────────────────────────────────┘
// Thread 클래스 상속
class MyThread extends Thread {
|------|---|-----|
| **패러다임** | 절차지향 | 객체지향 + 절차지향 |
| **개발년도** | 1972년 | 1985년 |
| **확장자** | .c | .cpp, .cxx |
| **컴파일러** | gcc | g++, cl |
| **표준 라이브러리** | 작음 | 매우 큼 (STL 포함) |
### 프로그래밍 패러다임
#### C - 절차지향
```c
// C 스타일 - 함수 중심
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int x, y;
} Point;
Point* createPoint(int x, int y) {
Point* p = malloc(sizeof(Point));
p->x = x;
p->y = y;
return p;
}
void printPoint(Point* p) {
printf("Point: (%d, %d)\n", p->x, p->y);
}
int main() {
Point* p = createPoint(10, 20);
printPoint(p);
free(p);
return 0;
}
// C++ 스타일 - 클래스 중심
#include <iostream>
class Point {
private:
int x, y;
public:
Point(int x, int y) : x(x), y(y) {} // 생성자
~Point() {} // 소멸자
void print() {
std::cout << "Point: (" << x << ", " << y << ")" << std::endl;
}
// 연산자 오버로딩
Point operator+(const Point& other) {
return Point(x + other.x, y + other.y);
}
};
int main() {
Point p1(10, 20);
Point p2(5, 15);
Point p3 = p1 + p2; // 연산자 오버로딩 사용
p3.print();
return 0;
}
// C - malloc/free
int* arr = malloc(sizeof(int) * 10);
free(arr);
// C++ - new/delete + 스마트 포인터
int* arr = new int[10];
delete[] arr;
// 스마트 포인터 (C++11)
std::unique_ptr<int[]> arr = std::make_unique<int[]>(10);
// 자동으로 메모리 해제
// C - printf/scanf
printf("Hello %s\n", name);
scanf("%d", &number);
// C++ - iostream
std::cout << "Hello " << name << std::endl;
std::cin >> number;
// C - char 배열
char str[100];
strcpy(str, "Hello");
strcat(str, " World");
// C++ - string 클래스
std::string str = "Hello";
str += " World";
template<typename T>
class Vector {
private:
T* data;
size_t size;
public:
Vector(size_t size) : size(size) {
data = new T[size];
}
T& operator[](size_t index) {
return data[index];
}
};
// 사용
Vector<int> intVector(10);
Vector<double> doubleVector(5);
#include <vector>
#include <map>
#include <algorithm>
std::vector<int> numbers = {5, 2, 8, 1, 9};
std::sort(numbers.begin(), numbers.end());
std::map<std::string, int> ages;
ages["Alice"] = 25;
ages["Bob"] = 30;
| 구분 | C | Java |
|---|---|---|
| 패러다임 | 절차지향 | 객체지향 |
| 플랫폼 | 플랫폼 의존적 | 플랫폼 독립적 (JVM) |
| 메모리 관리 | 수동 (malloc/free) | 자동 (GC) |
| 포인터 | 지원 | 미지원 |
| 컴파일 | 네이티브 코드 | 바이트코드 |
| 실행 속도 | 빠름 | 상대적으로 느림 |
#include <stdio.h>
#include <stdlib.h>
int main() {
// 동적 메모리 할당
int* arr = malloc(sizeof(int) * 10);
if (arr == NULL) {
printf("메모리 할당 실패\n");
return 1;
}
// 메모리 사용
for (int i = 0; i < 10; i++) {
arr[i] = i * i;
}
// 수동으로 메모리 해제 (필수!)
free(arr);
arr = NULL; // 댕글링 포인터 방지
return 0;
}
public class MemoryExample {
public static void main(String[] args) {
// 객체 생성 (힙에 할당)
int[] arr = new int[10];
// 메모리 사용
for (int i = 0; i < 10; i++) {
arr[i] = i * i;
}
// 메모리 해제 불필요 (GC가 자동 처리)
// arr = null; // 참조 제거 (선택사항)
}
}
// hello.c
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
// 컴파일 및 실행
// Windows: gcc hello.c -o hello.exe && hello.exe
// Linux: gcc hello.c -o hello && ./hello
// 각 플랫폼별로 다른 실행 파일 생성
// Hello.java
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
// 컴파일 및 실행 (모든 플랫폼에서 동일)
// javac Hello.java (바이트코드 생성)
// java Hello (JVM에서 실행)
// 구조체와 함수 분리
typedef struct {
int balance;
char owner[50];
} Account;
void deposit(Account* account, int amount) {
account->balance += amount;
}
void withdraw(Account* account, int amount) {
if (account->balance >= amount) {
account->balance -= amount;
}
}
int getBalance(Account* account) {
return account->balance;
}
// 클래스에 데이터와 메소드 통합
public class Account {
private int balance;
private String owner;
public Account(String owner) {
this.owner = owner;
this.balance = 0;
}
public void deposit(int amount) {
balance += amount;
}
public boolean withdraw(int amount) {
if (balance >= amount) {
balance -= amount;
return true;
}
return false;
}
public int getBalance() {
return balance;
}
}
#include <stdio.h>
int divide(int a, int b, int* result) {
if (b == 0) {
return -1; // 에러 코드 반환
}
*result = a / b;
return 0; // 성공
}
int main() {
int result;
if (divide(10, 0, &result) != 0) {
printf("나누기 에러: 0으로 나눌 수 없습니다.\n");
return 1;
}
printf("결과: %d\n", result);
return 0;
}
public class ErrorHandling {
public static int divide(int a, int b) throws ArithmeticException {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return a / b;
}
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println("결과: " + result);
} catch (ArithmeticException e) {
System.out.println("나누기 에러: " + e.getMessage());
}
}
}
전 세계적으로 연결된 컴퓨터 네트워크의 집합으로, 누구나 접근 가능한 공개 네트워크
특정 조직 내부에서만 사용하는 사설 네트워크로, 인터넷 기술을 활용하되 접근이 제한됨
| 구분 | 인터넷 | 인트라넷 |
|---|---|---|
| 접근성 | 전 세계 누구나 | 조직 구성원만 |
| 보안 수준 | 낮음 | 높음 |
| 네트워크 범위 | 전 세계 | 조직 내부 |
| 정보 범위 | 무제한 | 제한적 |
| 소유권 | 없음 (공공) | 조직 소유 |
| 비용 | 사용자 부담 | 조직 부담 |
| 속도 | 상대적으로 느림 | 빠름 |
[사용자 PC] ↔ [ISP] ↔ [인터넷 백본] ↔ [웹 서버]
↕
[다른 ISP들과 연결]
[직원 PC] ↔ [스위치] ↔ [방화벽] ↔ [사내 서버]
↕
[다른 부서 네트워크]
// 누구나 접근 가능한 웹사이트
<html>
<head><title>공개 웹사이트</title></head>
<body>
<h1>누구나 볼 수 있는 내용</h1>
<p>인터넷에 연결된 모든 사람이 접근 가능</p>
</body>
</html>
// 로그인이 필요한 사내 포털
const intranetAccess = {
authentication: "Active Directory",
authorization: "Role-based",
network: "192.168.1.0/24", // 사설 IP 대역
firewall: "Only internal access"
};
// 접근 절차
if (user.isEmployee && user.isOnCorporateNetwork) {
grantAccess();
} else {
denyAccess();
}
공인 IP 주소 예시:
- 8.8.8.8 (Google DNS)
- 1.1.1.1 (Cloudflare DNS)
- 203.248.252.2 (네이버)
사설 IP 주소 범위:
- A 클래스: 10.0.0.0 ~ 10.255.255.255
- B 클래스: 172.16.0.0 ~ 172.31.255.255
- C 클래스: 192.168.0.0 ~ 192.168.255.255
보안 위협:
✗ 해킹 시도
✗ 악성 코드
✗ 피싱 사이트
✗ DDoS 공격
✗ 개인정보 유출
보안 강화:
✓ 방화벽 보호
✓ 접근 통제
✓ 암호화 통신
✓ 사용자 인증
✓ 네트워크 모니터링
<!-- 전자상거래 사이트 -->
<!DOCTYPE html>
<html>
<head>
<title>온라인 쇼핑몰</title>
</head>
<body>
<header>전 세계 누구나 접근 가능</header>
<main>
<h1>상품 목록</h1>
<div class="products">
<!-- 공개 상품 정보 -->
</div>
</main>
</body>
</html>
<!-- 사내 포털 시스템 -->
<!DOCTYPE html>
<html>
<head>
<title>사내 포털</title>
</head>
<body>
<header>직원 전용 시스템</header>
<nav>
<ul>
<li>인사 관리</li>
<li>급여 조회</li>
<li>사내 공지</li>
<li>프로젝트 관리</li>
</ul>
</nav>
<main>
<!-- 기밀 회사 정보 -->
</main>
</body>
</html>
인트라넷을 특정 외부 사용자에게 부분적으로 개방한 네트워크
인트라넷 + 제한적 외부 접근 = 엑스트라넷
예시:
- 협력업체 포털
- 고객 지원 시스템
- 공급망 관리 시스템
[재택근무 직원] ↔ [VPN 터널] ↔ [회사 인트라넷]
보안 기능:
- 암호화된 연결
- 사용자 인증
- 트래픽 터널링
- IP 주소 마스킹
온프레미스 인트라넷 + 클라우드 서비스
↓
Microsoft 365, Google Workspace 등
- 인터넷을 통해 접근
- 하지만 조직별 격리된 환경
- 강력한 인증 및 권한 관리
AWS 클라우드에서 논리적으로 격리된 가상 네트워크로, 사용자가 정의한 가상 네트워크 환경
IP 주소와 라우팅을 효율적으로 관리하기 위한 주소 할당 방법
IP주소/서브넷마스크비트수
예: 192.168.1.0/24
192.168.1.0: 네트워크 주소
/24: 처음 24비트가 네트워크 부분
(나머지 8비트가 호스트 부분)
10.0.0.0/16 분석:
- 네트워크 부분: 10.0 (16비트)
- 호스트 부분: 0.0 (16비트)
- 사용 가능한 IP: 2^16 = 65,536개
- 범위: 10.0.0.0 ~ 10.0.255.255
10.0.0.0/24 분석:
- 네트워크 부분: 10.0.0 (24비트)
- 호스트 부분: 0 (8비트)
- 사용 가능한 IP: 2^8 = 256개
- 범위: 10.0.0.0 ~ 10.0.0.255
AWS 권장 사설 IP 대역:
- 10.0.0.0/8 (10.0.0.0 ~ 10.255.255.255)
- 172.16.0.0/12 (172.16.0.0 ~ 172.31.255.255)
- 192.168.0.0/16 (192.168.0.0 ~ 192.168.255.255)
AWS VPC CIDR 제약:
- 최소: /28 (16개 IP)
- 최대: /16 (65,536개 IP)
- 추천: /16 ~ /20 (대부분의 경우)
{
"VPC_CIDR": "10.0.0.0/24",
"총_IP_수": 256,
"서브넷_설계": {
"퍼블릭_서브넷": "10.0.0.0/26", // 64개 IP
"프라이빗_서브넷": "10.0.0.64/26", // 64개 IP
"데이터베이스_서브넷": "10.0.0.128/26" // 64개 IP
}
}
{
"VPC_CIDR": "10.0.0.0/16",
"총_IP_수": 65536,
"가용영역별_서브넷": {
"AZ_A": {
"퍼블릭": "10.0.1.0/24", // 256개
"프라이빗": "10.0.2.0/24", // 256개
"데이터베이스": "10.0.3.0/24" // 256개
},
"AZ_B": {
"퍼블릭": "10.0.4.0/24", // 256개
"프라이빗": "10.0.5.0/24", // 256개
"데이터베이스": "10.0.6.0/24" // 256개
}
}
}
VPC: 10.0.0.0/16
Tier 1 - 웹 계층 (Public Subnet):
├── AZ-a: 10.0.1.0/24
└── AZ-b: 10.0.2.0/24
Tier 2 - 애플리케이션 계층 (Private Subnet):
├── AZ-a: 10.0.11.0/24
└── AZ-b: 10.0.12.0/24
Tier 3 - 데이터베이스 계층 (DB Subnet):
├── AZ-a: 10.0.21.0/24
└── AZ-b: 10.0.22.0/24
VPC: 10.0.0.0/16
개발 환경 (Dev):
├── 10.0.1.0/24 ~ 10.0.10.0/24
스테이징 환경 (Staging):
├── 10.0.11.0/24 ~ 10.0.20.0/24
프로덕션 환경 (Production):
├── 10.0.21.0/24 ~ 10.0.50.0/24
서브넷 CIDR: 10.0.1.0/24 (10.0.1.0 ~ 10.0.1.255)
예약된 IP:
- 10.0.1.0: 네트워크 주소
- 10.0.1.1: VPC 라우터용 (AWS 예약)
- 10.0.1.2: DNS 서버용 (AWS 예약)
- 10.0.1.3: 향후 사용 (AWS 예약)
- 10.0.1.255: 브로드캐스트 주소
실제 사용 가능: 10.0.1.4 ~ 10.0.1.254 (251개)
# AWS CLI로 보조 CIDR 추가
aws ec2 associate-vpc-cidr-block \
--vpc-id vpc-12345678 \
--cidr-block 10.1.0.0/16
# 결과: VPC가 두 개의 CIDR 블록 보유
# 기본: 10.0.0.0/16
# 보조: 10.1.0.0/16
VPC-A (Production): 10.0.0.0/16
VPC-B (Development): 10.1.0.0/16
VPC-C (Staging): 10.2.0.0/16
주의사항:
✓ CIDR 블록이 겹치면 안됨
✓ 향후 확장 가능성 고려
✓ 온프레미스와의 연결 고려
온프레미스: 192.168.0.0/16
AWS VPC: 10.0.0.0/16
라우팅 테이블:
- 온프레미스 → AWS: 10.0.0.0/16 via VPN
- AWS → 온프레미스: 192.168.0.0/16 via VPN
충돌 방지:
✗ 같은 대역 사용 금지
✓ 서로 다른 사설 IP 대역 사용
입력: 10.0.0.0/20
출력:
- 네트워크: 10.0.0.0
- 마스크: 255.255.240.0
- 범위: 10.0.0.0 ~ 10.0.15.255
- 호스트 수: 4,096개
- 서브넷 분할 옵션 제공
현재 필요: 100개 인스턴스
권장 CIDR: /20 (4,096개 IP)
이유: 향후 확장성 고려
서브넷 명명 예시:
- public-subnet-1a (10.0.1.0/24)
- private-subnet-1a (10.0.11.0/24)
- db-subnet-1a (10.0.21.0/24)
# VPC 설계 문서
VPC_Design:
VPC_CIDR: "10.0.0.0/16"
Region: "ap-northeast-2"
Availability_Zones:
- name: "ap-northeast-2a"
subnets:
public: "10.0.1.0/24"
private: "10.0.11.0/24"
database: "10.0.21.0/24"
- name: "ap-northeast-2c"
subnets:
public: "10.0.2.0/24"
private: "10.0.12.0/24"
database: "10.0.22.0/24"
최종 업데이트: 2024년 12월