Salmonholic

개발, 인프라, AI에 대한 실용적인 기술 블로그입니다.

서버 정전 대비 자동 복구 설정하기

사무실 서버를 운영하면 정전을 피할 수 없다. 주말에 건물 전기 점검으로 전원이 나간 적이 있는데, 월요일 아침에 출근해보니 서버가 꺼져 있었다. 서비스 전부 다운. 원격으로 켤 수도 없어서 직접 전원 버튼을 눌러야 했다. 그 뒤로 자동 복구를 세팅해뒀고, 이후에는 정전이 와도 전기 복구 후 알아서 서버가 켜지고 서비스까지 올라온다. BIOS 설정: 전원 복구 시 자동 부팅 서버나 PC의 BIOS에 “AC Power Recovery” 또는 “After Power Loss” 같은 옵션이 있다. 보통 세 가지 중 하나를 선택할 수 있다: ...

2025년 5월 10일 · 3 분 · Salmonholic

GitLab CI/CD 파이프라인 구축하기

수동 배포를 하다 보면 실수가 생긴다. 빌드를 까먹거나, 테스트를 안 돌리거나, 잘못된 브랜치를 배포하거나. GitLab CI/CD를 쓰면 코드를 push할 때 자동으로 테스트 → 빌드 → 배포까지 돌릴 수 있다. .gitlab-ci.yml 파일 하나면 된다. 기본 구조 프로젝트 루트에 .gitlab-ci.yml을 만든다: stages: - test - build - deploy variables: NODE_VERSION: "20" test: stage: test image: node:${NODE_VERSION}-alpine script: - npm ci - npm run lint - npm test cache: key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/ build: stage: build image: node:${NODE_VERSION}-alpine script: - npm ci - npm run build artifacts: paths: - dist/ expire_in: 1 hour only: - main - develop deploy: stage: deploy image: alpine:latest script: - apk add --no-cache rsync openssh-client - mkdir -p ~/.ssh - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - ssh-keyscan -H $DEPLOY_HOST >> ~/.ssh/known_hosts - rsync -avz --delete dist/ $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/ only: - main when: manual stages에서 순서를 정의하고, 각 job이 어떤 stage에 속하는지 지정한다. 같은 stage의 job은 병렬로 실행된다. ...

2025년 5월 3일 · 3 분 · Salmonholic

React Vite 프로젝트 Cloudflare Pages에 배포하기

사이드 프로젝트를 빠르게 배포하고 싶을 때 Cloudflare Pages가 좋다. 무료 플랜으로도 충분하고, 글로벌 CDN이 기본이라 속도도 빠르다. Vercel이나 Netlify도 좋지만, 이미 Cloudflare를 쓰고 있다면 한 곳에서 관리하는 게 편하다. Git 연동 없이 CLI로 바로 배포하는 방법을 정리한다. 프로젝트 생성 Vite로 React 프로젝트를 만든다: npm create vite@latest my-app -- --template react cd my-app npm install TypeScript를 쓰고 싶으면 --template react-ts로 바꾸면 된다. 개발 서버를 띄워서 확인: npm run dev http://localhost:5173에서 기본 화면이 뜨면 정상이다. ...

2025년 4월 25일 · 2 분 · Salmonholic

Elasticsearch + Kibana 모니터링 스택 구축하기

서버 로그를 grep으로 뒤지는 건 한두 대일 때나 가능하다. 서비스가 늘어나면 로그를 한 곳에 모아서 검색하고 시각화할 수 있는 환경이 필요하다. ELK 스택(Elasticsearch + Logstash + Kibana)이 업계 표준인데, 작은 규모에서는 Logstash 대신 Filebeat만 써도 충분하다. Docker로 띄우면 설치 자체는 30분이면 끝난다. Docker Compose 설정 version: "3.8" services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0 environment: - discovery.type=single-node - xpack.security.enabled=false - "ES_JAVA_OPTS=-Xms512m -Xmx512m" volumes: - es-data:/usr/share/elasticsearch/data ports: - "9200:9200" restart: unless-stopped healthcheck: test: curl -s http://localhost:9200 >/dev/null || exit 1 interval: 30s timeout: 10s retries: 5 kibana: image: docker.elastic.co/kibana/kibana:8.12.0 environment: - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 ports: - "5601:5601" depends_on: elasticsearch: condition: service_healthy restart: unless-stopped volumes: es-data: xpack.security.enabled=false는 개발/내부용으로 쓸 때 편하려고 끈 거다. 외부에 노출한다면 반드시 켜야 한다. ...

2025년 4월 18일 · 2 분 · Salmonholic

JavaScript로 드래그 앤 드롭 구현하기

칸반 보드 같은 걸 만들 때 드래그 앤 드롭은 필수다. 라이브러리를 쓰면 편하긴 한데, 간단한 기능이면 직접 구현하는 게 번들 사이즈도 줄고 커스터마이징도 자유롭다. HTML5 Drag and Drop API가 있긴 하지만 모바일에서 제대로 작동하지 않아서, 마우스/터치 이벤트를 직접 다루는 방식으로 구현했다. HTML 구조 <div class="container"> <div class="column" id="todo"> <h2>할 일</h2> <div class="card" draggable="true">카드 1</div> <div class="card" draggable="true">카드 2</div> </div> <div class="column" id="doing"> <h2>진행 중</h2> <div class="card" draggable="true">카드 3</div> </div> <div class="column" id="done"> <h2>완료</h2> </div> </div> CSS 드래그 중인 요소에 시각적 피드백을 주는 게 중요하다: ...

2025년 4월 10일 · 3 분 · Salmonholic

Let's Encrypt SSL 인증서 자동 갱신 설정하기

요즘은 HTTPS가 선택이 아니라 필수다. 브라우저에서 “안전하지 않음” 경고를 띄우니까 사용자 입장에서도 신뢰가 안 간다. 예전에는 SSL 인증서가 비쌌는데, Let’s Encrypt 덕분에 무료로 쓸 수 있게 됐다. 다만 90일마다 갱신해야 하는데, 자동화해두면 한 번 설정하고 까먹어도 된다. certbot 설치 Ubuntu 기준: sudo apt update sudo apt install certbot python3-certbot-nginx python3-certbot-nginx 플러그인이 Nginx 설정까지 자동으로 건드려주니까 같이 설치하는 게 편하다. 인증서 발급 Nginx가 이미 돌아가고 있고 도메인이 서버 IP로 연결되어 있다면: ...

2025년 4월 2일 · 2 분 · Salmonholic

Redis를 Docker로 빠르게 구축하기

API 응답이 느려서 캐시를 붙여야 하는 상황이 왔다. Redis가 가장 무난한 선택이었는데, 서버에 직접 설치하면 버전 관리도 귀찮고 나중에 지울 때도 깔끔하지 않다. Docker로 띄우면 한 줄이면 되고, 안 쓸 때 컨테이너만 지우면 끝이라 편하다. 기본 실행 가장 간단한 방법: docker run -d --name redis \ -p 6379:6379 \ redis:7-alpine 끝이다. 이것만으로 Redis 서버가 돌아간다. 접속 테스트: docker exec -it redis redis-cli 127.0.0.1:6379> ping PONG 127.0.0.1:6379> set hello "world" OK 127.0.0.1:6379> get hello "world" 근데 이렇게 쓰면 컨테이너가 죽을 때 데이터가 다 날아간다. 캐시 용도라면 상관없지만, 세션 스토어 같은 걸로 쓸 때는 데이터를 보존해야 한다. ...

2025년 3월 25일 · 2 분 · Salmonholic

GitHub Pages로 무료 웹사이트 호스팅하기

포트폴리오 사이트나 기술 블로그를 만들고 싶은데 호스팅 비용이 부담스럽다면 GitHub Pages가 답이다. 무료에다 HTTPS도 지원하고, Git push만 하면 자동으로 배포된다. 나도 처음 기술 블로그를 GitHub Pages로 시작했는데, 트래픽이 꽤 나올 때까지 문제없이 잘 버텨줬다. 저장소 만들기 GitHub에서 새 저장소를 만든다. 두 가지 방식이 있다: 사용자 사이트: 저장소 이름을 username.github.io로 만들면 https://username.github.io로 접속 프로젝트 사이트: 아무 이름으로 만들면 https://username.github.io/repo-name으로 접속 개인 블로그라면 사용자 사이트가 깔끔하다. mkdir my-site && cd my-site git init git remote add origin https://github.com/username/username.github.io.git 기본 페이지 만들기 간단한 index.html부터 시작하자: ...

2025년 3월 18일 · 2 분 · Salmonholic

Nginx 리버스 프록시로 Basic Auth 걸기

사내에서 Grafana, Jenkins, Portainer 같은 관리 도구를 띄워놓으면 편한데, 이걸 그냥 열어두면 불안하다. VPN을 구축하면 가장 좋겠지만 규모가 작은 팀이라 그건 좀 오버스펙이었다. 그래서 Nginx 리버스 프록시 앞에 Basic Auth를 걸어서 최소한의 보호막을 만들었다. htpasswd 파일 생성 Basic Auth에 사용할 사용자/비밀번호 파일을 먼저 만든다. apache2-utils 패키지에 htpasswd 명령이 들어있다: sudo apt install apache2-utils # 파일 새로 생성 + 사용자 추가 sudo htpasswd -c /etc/nginx/.htpasswd admin 비밀번호를 물어보니까 입력하면 된다. 사용자를 추가할 때는 -c 옵션을 빼야 한다 (파일을 덮어쓰니까): ...

2025년 3월 12일 · 3 분 · Salmonholic

Docker Compose로 사내 서버 관리하기

회사에서 자체 서버를 운영하다 보면 서비스가 하나둘 늘어난다. 처음에는 docker run 으로 하나씩 띄우다가, 어느 순간 컨테이너가 10개를 넘기면서 관리가 안 되기 시작했다. 어떤 컨테이너가 어떤 포트를 쓰는지, 볼륨은 어디에 마운트했는지, 환경변수는 뭘 넣었는지… 결국 Docker Compose로 전부 옮기고 나서야 숨통이 트였다. 기본 구조 잡기 프로젝트 루트에 docker-compose.yml 하나면 된다. 우리 팀은 보통 이런 식으로 구성한다: /srv/ ├── docker-compose.yml ├── .env ├── nginx/ │ └── conf.d/ ├── data/ │ ├── postgres/ │ ├── redis/ │ └── minio/ └── logs/ docker-compose.yml 기본 뼈대는 이렇다: ...

2025년 3월 5일 · 3 분 · Salmonholic