사내 VM IP 관리 시스템 만들기 - React 대시보드 + Docker Compose 배포 (4편)

React 대시보드 대시보드는 탭 2개로 구성된다. VM 목록 — 전체 VM 현황, 상태별 필터, ONLINE/OFFLINE/UNKNOWN 배지 이벤트 로그 — IP 충돌, IP 변경, 오프라인 이벤트 타임라인 WebSocket이 아닌 30초 폴링으로 구현했다. VM 상태가 초 단위로 바뀌지 않고, 운영 대시보드 특성상 약간의 지연은 허용된다. 심플하게 가는 게 낫다고 판단했다. 30초 폴링 훅 // usePolling.js import { useEffect, useRef } from 'react'; export function usePolling(callback, intervalMs = 30_000) { const callbackRef = useRef(callback); useEffect(() => { callbackRef.current = callback; }); useEffect(() => { callbackRef.current(); // 마운트 시 즉시 1회 실행 const id = setInterval(() => callbackRef.current(), intervalMs); return () => clearInterval(id); }, [intervalMs]); } // App.jsx const [vms, setVms] = useState([]); const [loading, setLoading] = useState(true); usePolling(async () => { const data = await fetch('/api/vms').then(r => r.json()); setVms(data); setLoading(false); }, 30_000); callbackRef를 쓰는 이유는 setInterval 클로저가 최초 등록 시점의 callback을 계속 참조하는 문제를 피하기 위해서다. useRef로 항상 최신 콜백을 가리키도록 한다. ...

2026년 4월 15일 · 5 min · 958 words · Chanyeol

사내 인사정보 관리 시스템 만들기 - Docker Compose 멀티 서비스 배포 + 트러블슈팅 (3편)

Docker Compose 멀티 서비스 구성 세 서비스가 올바른 순서로 떠야 한다. db (PostgreSQL) → server (Express + Prisma) → client (React + nginx) server가 DB보다 먼저 뜨면 Prisma 연결이 실패하고, client가 server보다 먼저 뜨면 API 프록시가 동작하지 않는다. docker-compose.yml services: db: image: postgres:16-alpine container_name: insa-db environment: POSTGRES_DB: unipost_insa POSTGRES_USER: insa POSTGRES_PASSWORD: insa1234 volumes: - ./data/postgres:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U insa -d unipost_insa"] interval: 5s timeout: 5s retries: 10 server: build: ./server container_name: insa-server ports: - "4000:4000" env_file: - ./server/.env environment: DATABASE_URL: postgresql://insa:insa1234@db:5432/unipost_insa depends_on: db: condition: service_healthy healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://localhost:4000/health', r => process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))"] interval: 5s timeout: 5s retries: 10 restart: unless-stopped client: build: ./client container_name: insa-client ports: - "3002:80" depends_on: server: condition: service_healthy restart: unless-stopped depends_on에 condition: service_healthy를 쓰면 단순히 컨테이너가 “시작됐는지"가 아니라 “준비됐는지"를 확인하고 다음 서비스를 시작한다. ...

2026년 4월 11일 · 5 min · 862 words · Chanyeol

노트북으로 홈서버 구축하기 - certbot --expand로 SSL 서브도메인 추가하기 (9편)

문제 상황 처음 SSL 인증서를 발급할 때 메인 도메인만 포함해서 발급했다. certbot certonly --nginx -d yourdomain.com 이후 서비스가 하나씩 늘어나면서 서브도메인이 추가됐는데, 브라우저에서 photo.yourdomain.com에 접속하면 아래 에러가 발생했다. NET::ERR_CERT_COMMON_NAME_INVALID 연결이 비공개로 설정되어 있지 않습니다. 인증서에 photo.yourdomain.com이 포함돼 있지 않아서 생기는 문제였다. 해결: –expand 옵션 기존 인증서에 서브도메인을 추가할 때는 --expand 플래그를 써야 한다. --expand 없이 서브도메인을 추가하려고 하면 아래 에러가 난다. Missing command line flag or config entry for this setting: You have an existing certificate that contains a portion of the domains you requested. It contains these names: yourdomain.com You requested these names for the new certificate: yourdomain.com, photo.yourdomain.com Do you want to expand and replace this existing certificate with the new certificate? (You can set this with the --expand flag) certbot이 친절하게 --expand 쓰라고 안내해주긴 한다. ...

2026년 3월 31일 · 2 min · 363 words · Chanyeol

노트북으로 홈서버 구축하기 - 외장 SSD 마운트 + Filebrowser 원격 파일 관리 (3편)

외장 SSD 마운트 집에 1TB SSD가 남아있어서 홈서버 스토리지로 활용하기로 했다. 기존에 Windows에서 쓰던 드라이브라 NTFS 포맷이다. 디스크 확인 lsblk 어떤 디바이스명으로 잡혔는지 확인한다. 파티션 포맷 확인: sudo blkid /dev/nvme1n1p2 NTFS로 확인됐으니 마운트를 진행한다. 마운트 sudo apt install ntfs-3g -y sudo mkdir /mnt/data sudo mount /dev/nvme1n1p2 /mnt/data 마운트 후 확인해보니 Could not mount read-write, trying read-only 메시지가 떴다. 읽기 전용으로 마운트된 것이다. Windows에서 쓰던 드라이브라 더티 플래그가 남아있어서 발생하는 문제다. ...

2026년 3월 25일 · 3 min · 500 words · Chanyeol

서버 3대장(Nginx, Apache, Tomcat) logrotate 완벽 가이드: 디스크 풀 방지

서론 운영 중인 서버가 갑자기 멈췄을 때, 원인을 찾아보면 로그 파일이 디스크 용량을 모두 차지해버린 경우가 의외로 많습니다. 특히 트래픽이 몰리는 서비스라면 로그 파일 크기는 순식간에 수십 GB를 넘어섭니다. 이번 포스팅에서는 리눅스 표준 도구인 logrotate를 사용하여 주요 서버들(Nginx, Apache, Tomcat)의 로그를 서비스 중단 없이 관리하는 방법을 정리합니다. logrotate란? logrotate는 리눅스 시스템에서 로그 파일을 주기적으로 순환(Rotation), 압축(Compression), 삭제(Removal)해주는 시스템 유틸리티입니다. 이를 통해 오래된 로그를 보관하면서도 현재 디스크 사용량을 일정하게 유지할 수 있습니다. ...

2026년 2월 23일 · 2 min · 280 words · Chanyeol

Nginx 리버스 프록시와 로드 밸런싱 설정 가이드: WAS 성능 최적화

서론 현대 웹 아키텍처에서 백엔드 WAS(Tomcat, Spring Boot 등)를 외부에 직접 노출하는 것은 보안과 성능 면에서 권장되지 않습니다. Nginx를 앞단에 두어 **리버스 프록시(Reverse Proxy)**로 활용하면 보안 강화는 물론, **로드 밸런싱(Load Balancing)**을 통해 시스템의 가용성을 획기적으로 높일 수 있습니다. 리버스 프록시 설정 (Reverse Proxy) 리버스 프록시는 클라이언트의 요청을 대신 받아 백엔드 서버로 전달하는 역할을 합니다. 이를 통해 백엔드 서버의 IP를 숨기고 SSL 종단점(SSL Termination) 역할을 수행할 수 있습니다. server { listen 80; server_name example.com; location / { proxy_pass http://backend_servers; # 로드 밸런서 그룹 지정 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } 로드 밸런싱 설정 (Load Balancing) upstream 블록을 사용하여 여러 대의 백엔드 서버로 부하를 분산할 수 있습니다. ...

2026년 2월 23일 · 2 min · 238 words · Chanyeol
1