Archive: 2025

0

[JAVA] - Cold Start

목차 [JAVA] - 가비지 컬렉션 튜닝 [JAVA] - Garbage Collection(가비지 컬렉션) [JAVA] - JVM (자바 가상 머신) 🧊 Cold Start란?Cold Start는 JVM이 애플리케이션이 처음 실행될 때 초기화 과정 때문에 발생하는 지연(latency) 을 의미합니다. 마치 추운 겨울날 자동차 시동을 걸 때 엔진이 따뜻해질 때까지 시간이 걸리는 것과 비슷합니다. 일반적인 서버 환경에서는 애플리케이션이 한 번 시작되면 계속 실행되기 때문에 이 문제가 크게 부각되지 않았습니다. 하지만 MSA 는 인스턴스가 빈번히 생성 및 소멸되고 여러 서버가 긴밀히 통신하는 환경입니다. 이로 인해 특정 시스템이 재부팅되면 연결된 다른 시스템의 응답 속도까지 지연되는 문제가 발생했으며, 무중단 배포·잦은 배포·오토스케일링 등으로 인해 기존 아키텍처보다 성능 지연 현상이 더 빈번하게 나타나게 되었고, 전체적인 서비스 성능 이슈의 문제가 됐습니다. 🚦 Cold Start가 발생하는 이유Java 애플리케이션은 네이티브 코드가 아닌 JVM 위에서 바이트코드 실행 방식으로 동작하기 때문에, 첫 실행 시 다음과 같은 과정에서 오버헤드가 발생합니다. 1. JVM 초기화 과정의 복잡성Java 애플리케이션이 시작될 때 여러 단계의 초기화 과정을 거쳐야 합니다:

0

[Spring Boot] - 스프링 프로퍼티 암호화

스프링부트 민감정보 암호화 하기운영 중인 프로그램에서 DB 암호나 API 키와 같은 민감 정보를 설정 파일에 평문으로 저장할 경우, 프로그램이 유출되면 그대로 외부에 노출되는 심각한 보안 문제가 발생할 수 있습니다. 이를 방지하기 위해 프로퍼티 암호화를 적용하면, 설정값이 유출되더라도 평문이 아닌 암호화된 형태로만 노출되므로 보안성을 크게 강화할 수 있습니다. 자바 환경에서는 이러한 암·복호화를 손쉽게 지원하는 Jasypt 모듈을 활용할 수 있어, 운영 환경에서 안전하게 민감 정보를 관리할 수 있습니다. 이번 포스팅에서는 Spring boot 에 Jaspyt 적용해 프로퍼티를 암복호화 하는 내용을 작성했습니다 암복호화를 위한 라이브러리 추가// Jasypt for property encryptionimplementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5' 값 암호화 하기java -cp "build/libs/app.jar" com.ulisesbocchio.jasyptspringboot.cli.JasyptEncryptorCLI \ input="my-db-pass" \ password="${JASYPT_ENCRYPTOR_PASSWORD}" \ algorithm="PBEWITHHMACSHA512ANDAES_256" \ ivGeneratorClassName="org.jasypt.iv.RandomIvGenerator" Configuration 추가

0

[Java] - 가비지 컬렉션 히스토리

목차 [JAVA] - 가비지 컬렉션 튜닝 [JAVA] - Garbage Collection(가비지 컬렉션) [JAVA] - JVM (자바 가상 머신) 초기 GC들 (JDK 1.0~1.4)Serial GC 가장 오래된 GC 단일 스레드로 동작 작은 애플리케이션이나 클라이언트 환경에 적합 Stop-the-World 시간이 길어서 현재는 제한적으로 사용 Parallel GC (Throughput Collector) JDK 1.4에서 도입 멀티코어 환경에서 여러 스레드를 사용해 처리량을 개선 JDK 6~8에서 기본 GC 여전히 긴 정지 시간이 단점 저지연 목표 GC들 (JDK 1.4~)Concurrent Mark Sweep (CMS) GC

0

[Javascript] Axios 파일 업로드 및 다운로드

파일 업로드 및 다운로드📁 파일 업로드// 단일 파일 업로드async function uploadFile(file, onProgress) { const formData = new FormData(); formData.append('file', file); formData.append('category', 'documents'); formData.append('description', [file.name](http://file.name)); try { const response = await [axios.post](http://axios.post)('/api/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: (progressEvent) => { const percentCompleted = Math.round( (progressEvent.loaded * 100) / [progressEvent.total](http://progressEvent.total) ); console.log(`업로드 진행률: ${percentCompleted}%`); onProgress?.(percentCompleted); } }); return [response.data](http://response.data); } catch (error) { console.error('파일 업로드 실패:', error); throw error; }}// 다중 파일 업로드async function uploadMultipleFiles(files) { const formData = new FormData(); files.forEach((file, index) => { formData.append(`files`, file); }); const response = await [axios.post](http://axios.post)('/api/upload/multiple', formData, { headers: { 'Content-Type': 'multipart/form-data' } }); return [response.data](http://response.data);}// 사용 예시const fileInput = document.getElementById('fileInput');fileInput.addEventListener('change', async (event) => { const file = [event.target](http://event.target).files[0]; if (file) { try { const result = await uploadFile(file, (progress) => { console.log(`업로드 진행률: ${progress}%`); }); console.log('업로드 완료:', result); } catch (error) { console.error('업로드 에러:', error); } }}); 📥 파일 다운로드// 파일 다운로드async function downloadFile(fileId, filename) { try { const response = await axios.get(`/api/files/${fileId}/download`, { responseType: 'blob' }); // Blob을 사용하여 파일 다운로드 트리거 const url = window.URL.createObjectURL(new Blob([[response.data](http://response.data)])); const link = document.createElement('a'); link.href = url; link.setAttribute('download', filename); document.body.appendChild(link); [link.click](http://link.click)(); document.body.removeChild(link); // 메모리 정리 window.URL.revokeObjectURL(url); } catch (error) { console.error('파일 다운로드 실패:', error); }} 에러 처리 베스트 프랙티스🛡️ 포괄적인 에러 처리// 에러 타입별 처리 함수function handleApiError(error) { if (error.response) { // 서버가 응답했지만 상태 코드가 2xx 범위를 벗어남 const { status, data } = error.response; switch (status) { case 400: console.error('잘못된 요청:', data.message); break; case 401: console.error('인증 실패 - 로그인이 필요합니다'); // 로그인 페이지로 리다이렉트 window.location.href = '/login'; break; case 403: console.error('접근 권한이 없습니다'); break; case 404: console.error('요청한 리소스를 찾을 수 없습니다'); break; case 429: console.error('요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요'); break; case 500: console.error('서버 내부 오류가 발생했습니다'); break; default: console.error(`서버 오류 (${status}):`, data.message); } return { type: 'response', status, message: data.message }; } else if (error.request) { // 요청은 전송되었지만 응답을 받지 못함 (네트워크 오류) console.error('네트워크 오류: 서버에 연결할 수 없습니다'); return { type: 'network', message: '네트워크 연결을 확인해주세요' }; } else { // 요청 설정 중에 오류 발생 console.error('요청 설정 오류:', error.message); return { type: 'config', message: error.message }; }}// 재시도 로직이 포함된 API 호출async function apiCallWithRetry(requestFn, maxRetries = 3, delay = 1000) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await requestFn(); } catch (error) { const errorInfo = handleApiError(error); if (attempt === maxRetries || errorInfo.type === 'response') { throw error; // 최대 재시도 횟수 도달 또는 서버 응답 오류 } console.log(`재시도 ${attempt}/${maxRetries} - ${delay}ms 후 다시 시도...`); await new Promise(resolve => setTimeout(resolve, delay * attempt)); } }}// 사용 예시async function fetchUserData(userId) { return apiCallWithRetry( () => axios.get(`/api/users/${userId}`), 3, // 최대 3번 재시도 1000 // 1초 간격 );}

0

[Javascript] Axios 인터셉터

인터셉터로 요청/응답 제어하기📤 요청 인터셉터// 모든 요청에 공통 로직 적용axios.interceptors.request.use( (config) => { // 요청 시작 로그 console.log(`🚀 API 요청: ${config.method?.toUpperCase()} ${config.url}`); // 인증 토큰 자동 추가 const token = localStorage.getItem('accessToken'); if (token) { config.headers.Authorization = `Bearer ${token}`; } // 요청 시간 기록 (성능 측정용) config.metadata = { startTime: new Date() }; return config; }, (error) => { console.error('❌ 요청 설정 오류:', error); return Promise.reject(error); }); 📥 응답 인터셉터// 모든 응답에 공통 로직 적용axios.interceptors.response.use( (response) => { // 응답 시간 계산 const endTime = new Date(); const duration = endTime - response.config.metadata.startTime; console.log(`✅ API 응답: ${response.config.url} (${duration}ms)`); return response; }, async (error) => { const originalRequest = error.config; // 401 에러: 토큰 갱신 처리 if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { const refreshToken = localStorage.getItem('refreshToken'); const response = await [axios.post](http://axios.post)('/auth/refresh', { refreshToken }); const newToken = [response.data](http://response.data).accessToken; localStorage.setItem('accessToken', newToken); // 원래 요청에 새 토큰 적용 후 재시도 originalRequest.headers.Authorization = `Bearer ${newToken}`; return axios(originalRequest); } catch (refreshError) { // 토큰 갱신 실패 시 로그인 페이지로 리다이렉트 localStorage.clear(); window.location.href = '/login'; } } // 에러 로깅 console.error('❌ API 에러:', { url: error.config?.url, status: error.response?.status, message: error.message }); return Promise.reject(error); }); 실전 예제: API 클래스 구현🏢 사용자 관리 API 클래스class UserService { constructor() { this.client = axios.create({ baseURL: process.env.REACT_APP_API_URL || 'https://api.example.com', timeout: 10000 }); this.setupInterceptors(); } setupInterceptors() { // 요청 인터셉터: 인증 토큰 자동 추가 this.client.interceptors.request.use(config => { const token = this.getAuthToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); // 응답 인터셉터: 에러 처리 this.client.interceptors.response.use( response => response, error => this.handleError(error) ); } getAuthToken() { return localStorage.getItem('authToken'); } handleError(error) { const message = error.response?.data?.message || error.message; throw new Error(`API 요청 실패: ${message}`); } // 사용자 목록 조회 async getUsers(options = {}) { const { page = 1, limit = 10, search = '' } = options; const response = await this.client.get('/users', { params: { page, limit, search } }); return { users: [response.data.data](http://response.data.data), totalCount: [response.data.total](http://response.data.total), currentPage: page }; } // 사용자 상세 조회 async getUserById(userId) { const response = await this.client.get(`/users/${userId}`); return [response.data](http://response.data); } // 사용자 생성 async createUser(userData) { const response = await [this.client.post](http://this.client.post)('/users', userData); return [response.data](http://response.data); } // 사용자 정보 수정 async updateUser(userId, userData) { const response = await this.client.put(`/users/${userId}`, userData); return [response.data](http://response.data); } // 사용자 삭제 async deleteUser(userId) { await this.client.delete(`/users/${userId}`); return { success: true, userId }; } // 사용자 검색 async searchUsers(query) { const response = await this.client.get('/users/search', { params: { q: query } }); return [response.data](http://response.data); }}// 싱글톤 패턴으로 사용export const userService = new UserService();// 사용 예시async function handleUserOperations() { try { // 사용자 목록 조회 const userList = await userService.getUsers({ page: 1, limit: 20, search: 'john' }); console.log('사용자 목록:', userList); // 새 사용자 생성 const newUser = await userService.createUser({ name: '김개발', email: '[kim.dev@example.com](mailto:kim.dev@example.com)', role: 'developer' }); console.log('생성된 사용자:', newUser); // 사용자 정보 수정 const updatedUser = await userService.updateUser([newUser.id](http://newUser.id), { name: '김시니어', role: 'senior-developer' }); console.log('수정된 사용자:', updatedUser); } catch (error) { console.error('사용자 작업 중 오류:', error.message); }}

0

[Javascript] Axios 사용하기 (고급편)

고급 설정 및 인스턴스⚙️ 상세한 요청 설정const requestConfig = { method: 'get', url: '/api/users', baseURL: 'https://api.example.com', // 헤더 설정 headers: { 'Authorization': 'Bearer token123', 'Content-Type': 'application/json', 'Accept': 'application/json' }, // URL 파라미터 params: { page: 1, size: 20 }, // 타임아웃 (밀리초) timeout: 10000, // 응답 타입 responseType: 'json', // 'json', 'text', 'blob', 'stream' 등 // 요청 바디 (POST, PUT, PATCH) data: { name: '홍길동', email: '[hong@example.com](mailto:hong@example.com)' }};const response = await axios(requestConfig); 🏗️ Axios 인스턴스 생성// API별 인스턴스 생성const apiClient = axios.create({ baseURL: 'https://api.myservice.com/v1', timeout: 15000, headers: { 'Content-Type': 'application/json' }});// 인증이 필요한 API용 인스턴스const authApiClient = axios.create({ baseURL: 'https://api.myservice.com/v1', timeout: 10000, headers: { 'Authorization': `Bearer ${getAuthToken()}` }});// 인스턴스 사용const users = await apiClient.get('/users');const profile = await authApiClient.get('/profile'); 인터셉터로 요청/응답 제어하기웹 개발을 하다 보면 서버와 통신할 때 반복적으로 처리해야 하는 공통 로직들이 생깁니다.예를 들어 JWT 토큰을 모든 요청 헤더에 붙인다거나, 에러 응답을 일관되게 처리한다거나, 응답 데이터 구조를 통일한다거나 하는 일들이죠. 이때 유용하게 사용할 수 있는 기능이 바로 Axios Interceptor입니다. 📤 요청 인터셉터

0

[Javascript] Axios 사용하기

웹 개발에서 API 통신은 필수불가결한 요소입니다. 그 중에서도 Axios는 가장 인기 있고 강력한 JavaScript HTTP 클라이언트 라이브러리로 자리잡고 있습니다. 이 글에서는 Axios의 기본 사용법부터 고급 기능까지 실무에서 바로 활용할 수 있는 예제와 함께 상세히 알아보겠습니다. Axios란 무엇인가?Axios는 Promise 기반의 HTTP 클라이언트 라이브러리로, 브라우저와 Node.js 환경에서 모두 사용할 수 있습니다. ✨ 주요 특징 Promise 기반: async/await와 완벽 호환 요청/응답 인터셉터: 공통 로직 처리 가능 자동 JSON 변환: 별도 변환 과정 불필요 요청/응답 변환: 데이터를 원하는 형태로 가공 요청 취소: 불필요한 요청 중단 가능 광범위한 브라우저 지원: IE11까지 지원 설치 및 기본 설정📦 설치# npm 사용시npm install axios# yarn 사용시 yarn add axios# pnpm 사용시pnpm add axios

0

네트워크 트래픽의 3가지 패턴 - Ingress, Egress, East-West Traffic

현대 IT 인프라를 이해하려면 데이터가 어떻게 흐르는지 파악하는 것이 필수입니다. 특히 클라우드 환경과 마이크로서비스 아키텍처가 보편화되고 트래픽의 움직임이 클라우드 서비스의 과금 요소중에 하나가 됨으로써 네트워크 트래픽 패턴을 정확히 이해하는 것은 더욱 중요해졌습니다. 오늘은 인프라를 운영하면서 알아두면 좋을 3가지 트래픽 패턴에 대해 알아보겠습니다. 📌 1. Ingress: 외부에서 내부로 들어오는 트래픽 Ingress는 클러스터 외부 → 내부로 들어오는 요청입니다. Ingress Traffic은 외부에서 우리 네트워크나 시스템으로 들어오는 모든 트래픽을 의미합니다. 대표적인 예시는 인터넷 사용자가 웹 애플리케이션에 접속하는 경우입니다. 실무 예시🌐 사용자 브라우저 → 웹 서버 (HTTP/HTTPS 요청)📱 모바일 앱 → API 게이트웨이 (REST API 호출)🔗 외부 시스템 → 내부 서비스 (Webhook, 데이터 동기화) 📌 2. Egress: 내부에서 외부로 나가는 트래픽 Egress는 클러스터 내부 → 외부로 나가는 요청 Egress Traffic은 우리 시스템에서 외부로 나가는 모든 트래픽입니다. 예를 들어 응답 데이터, 외부 API 호출, 백업 데이터 전송등을 할때 발생합니다.

0

[Django] - Model 이란?

Model이란?Django에서 Model은 데이터베이스와 상호작용하기 위한 핵심 개념입니다. 쉽게 말해, 데이터베이스 테이블을 파이썬 클래스 형태로 정의한 것이라고 보면 됩니다. 하나의 모델 클래스 → 하나의 테이블 클래스의 속성 → 테이블의 컬럼 인스턴스 → 테이블의 행(Row) 즉, 모델을 정의하면 SQL을 직접 작성하지 않고도 Django의 ORM(Object Relational Mapping)을 통해 데이터베이스를 제어할 수 있습니다. Model 생성제목, 내용, 작성자, 생성시간, 수정시간을 가진 게시판 데이터를 저장하기 위해서는 다음과 같이 모델을 정의할 수 있습니다. from django.db import modelsclass Board(models.Model): title = models.CharField(max_length=200) # 문자열 컬럼 content = models.TextField() # 긴 텍스트 author = models.ForeignKey('auth.User', on_delete=models.CASCADE) # 외래키 created_at = models.DateTimeField(auto_now_add=True) # 생성 시간 updated_at = models.DateTimeField(auto_now=True) # 수정 시간 class Meta: ordering = ['-created_at'] # 기본 정렬 def __str__(self): return self.title 주요 요소 설명필드(Field)

0

[Spring Boot] - Spring Interceptor 에서 Response 조작이 가능한가?

참고 Spring interceptor에서 Response 수정하기 “HttpServletResponse is committed” mean? 📝 후처리에서 메뉴 데이터를 Response 객체에 데이터를 넣어주세요개발 요구사항 중 페이지 접근시 전처리에서 접근 권한을 확인하고 후처리로 Response 객체에 사용자가 접근할 수 있는 메뉴를 같이 내려달라는 요구를 받았습니다. 해당 요구사항을 Spring 에서 제공하는 Interceptor 로 가능한지 검토해 달라는 요청을 받았습니다. 🤔 Interceptor 에서 데이터 변경이 가능할까?스프링에서 제공하는 Interceptor 는 아래 코드와 같습니다. 요청을 처리하는 Controller 전에 preHandler 를 거치기 때문에 사용자 접근을 제어해달라는 요구사항은 어렵지 않았습니다. 마찬가지로 요청이 끝난 후에는 postHandler 를 거쳐 나가는데 그때 응답 객체를 수정할 수 있지 않을까? 마침, HttpServletRequest 객체를 매게변수로 받아서 조작이 가능할 것 같은 그런 기분이 들지만, 결론은 안됩니다. (두둥탁 🥁) HandlerInterceptorpublic interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { }} 🧐 그럼 왜 인터셉터 에서 응답 객체 조작이 불가능한 것인가?

0

[Spring Boot] - Github Package 에 Spring 프로젝트를 배포하기

참고 GitHub Packages - Gradle 레지스트리 작업 🧑‍💻 무료로 Artifact 저장소를 사용하고 싶다!회사가 아닌 개인 프로젝트를 할때, 라이브러리를 개발해서 다른 프로젝트에서 사용하고 싶은 경우, 라이브러리 저장을 위한 Nexus 저장소를 별도로 구축하기가 쉽지 않습니다. 지속적이고 무료로 패키지 저장이 가능한 서비스로 JitPack 과 Github 패키지 저장소를 찾아봤습니다. 처음에 알아본 JitPack 은 패키지를 무료로 올릴 수 있는 서비스지만, Private 저장소 대상으로는 유료입니다. 만약 내가 Private 저장소를 이용해 패키지를 만든다고 하면 적합한 대상이 아닙니다. 추가적으로 알아본게 Github 인데, Github 가 코드 저장 뿐만 아니라 Docker 이미지나 Artifact 들을 올릴 수 있는 기능 또한 제공을 하고 있었습니다. 코드와 함께 이번에는 Github 를 패키지 저장소로 사용해보려고 합니다. 🌱 Spring Boot 프로젝트를 Github 에 배포하기✅ Gradle 플러그인 추가 설정 추가build.gradle 에 maven-publish 플러그인을 추가합니다

0

[Docker] 이미지 history

목차 [Docker] 이미지 history [Docker] 이미지 레이어 [Docker] 이미지 Build 참고 Docker History 명령어 공식문서 📋 이미지 historyDocker 이미지를 받아서 사용하다보면 해당 이미지가 어떻게 생성 됐는지, 어떤 명령어를 사용했는지 확인해봐야 할때가 있습니다. Docker 에서는 history 명령어를 이용해 해당 이미지가 어떤 명령어를 이용해 이미지가 생성 됐는지 확인할 수 있는 기능을 제공합니다. 저 같은 경우는 제대로 작동되지 않는 빌드된 이미지에 대한 디버깅용으로 사용을 많이 했었습니다. ✅ docker history 명령어기본 명령어

0

[Docker] 이미지 레이어

목차 [Docker] 이미지 레이어 [Docker] 이미지 Build 참고 Docker 공식 문서 - 이미지 레이어 이해하기 Docker 공식 문서 - Storage drivers 🔍 이미지 레이어란?Docker 이미지의 핵심적인 특징 중 하나는 레이어 기반 구조 입니다. Docker 이미지는 빌드시 한번에 만들어 지는 것이 아닌 기존 이미지에 레이어를 하나씩 쌓는 방식으로 이미지를 만듭니다. 이미지 레이어는 Dockerfile 에 있는 명령어를 실행할때 각 명령어별로 생성됩니다.. 각 레이어들은 읽기 전용 (read-only) 속성을 가지며, 순서대로 쌓여서 하나의 완전한 이미지를 구성합니다. 최종적으로 생성되는 이미지는 여러개의 레이어들이 쌓여서 만들어진 결과물이라 생각하면 됩니다. ✅ 레이어의 특징

0

[Docker] 이미지 Build

🔍 Docker Build 란?Docker Build 는 서비스를 격리된 환경에서 실행할 수 있도록 운영체제 및 필요한 의존성과 설정, 실행 파일등을 하나의 이미지로 패키징하는 것을 의미합니다. 이미지 Build 는 Dockerfile 을 기반으로 수행되며, Dockerfile 에는 이미지 생성에 필요한 명령어들이 있으며, 순서대로 실행됩니다. ✅ Build 명령어# 현재 디렉토리 위치에 Dockerfile 가 있고 현재 위치에서 이미지를 생성할 경우docker build -t <만들 이미지 이름>:<만들 이미지 태그> .# Dockerfile 이 다른 경로에 있을 경우docker build -t <만들 이미지 이름>:<만들 이미지 태그> -f <파일 경로> <빌드 컨텍스트 경로># Dockerfile 명이 다를 경우우docker build -t <만들 이미지 이름>:<만들 이미지 태그> -f <파일 경로/파일 이름> <빌드 컨텍스트 경로> ✅ 이미지지 레이어Docker 는 Build 시 Dockerfile 의 베이스가 되는 이미지부터 시작해 각 명령어를 실행할 때마다 새로운 이미지를 생성합니다. 이것을 이미지 레이어를 생성한다고 이야기 합니다. 때문에 초기 명령어들이 같고 중간 단계 부터 달라지는 Dockerfile 의 경우 빌드시 각각 이미지 레이어를 생성하는게 아닌 기존에 만들어진 이미지 레이어를 바탕으로 새로운 이미지를 생성해 빌드시간과 저장공간을 절약할 수 있습니다. ✅ Dockerfile

0

[Docker] Network - 컨테이너간 통신

목차 [Docker] Volume - 컨테이너 데이터 관리 [Docker] Network - 컨테이너간 통신 [Docker] 📌 Docker Network 란?Docker Network는 컨테이너 간 통신을 가능하게 해주는 가상 네트워크입니다. 컨테이너는 독립적인 환경에서 실행되지만, 애플리케이션을 만들다 보면 서로 데이터를 주고받아야 하거나 외부 요청을 받아야 할 일이 많습니다. Docker 네트워크는 이러한 컨테이너간 통신을 가능하게 해주는 역할을 합니다. 🔍 Docker Network 의 종류Docker는 기본적으로 다음과 같은 네트워크 드라이버들을 제공합니다 드라이버 설명 bridge 기본 드라이버. 컨테이너 간 통신이 가능하며, 같은 호스트에서 작동합니다. host 컨테이너가 호스트의 네트워크를 그대로 사용합니다. 격리는 없지만 속도는 빠릅니다. none 네트워크를 사용하지 않도록 설정합니다. 완전한 격리가 필요할 때 사용합니다. overlay 여러 호스트 간 컨테이너를 연결할 수 있습니다. Docker Swarm에서 유용합니다. macvlan 컨테이너에 고유한 MAC 주소를 부여해 물리 네트워크처럼 동작시킵니다. ✅ Docker 네트워크 생성