AWS에서 미니 PC 홈서버로 이전한 실전 기록 (1)

ILJUN YUN·2026년 1월 25일
post-thumbnail

React + Spring Boot + RDS → 미니 PC 홈서버

CI/CD, Nginx, HTTPS, DNS까지 직접 옮기며 겪은 시행착오와 설계 정리


1. 왜 홈서버로 이전했나

기존 서비스는 AWS 기반으로 구성되어 있었다.

  • Frontend: React
  • Backend: Spring Boot
  • DB: RDS
  • CI/CD: GitHub Actions

구성 자체는 안정적이었지만, 개인 프로젝트를 운영하기엔 비용과 복잡도가 점점 부담이 됐다.

그래서 목표를 이렇게 잡았다.

  • 클라우드 의존도 최소화
  • 운영은 최대한 단순하게
  • 비용은 거의 0에 가깝게
  • 그래도 실서비스 수준의 구조는 유지

결론은 미니 PC 홈서버로 이전이었다.


2. 전체 목표 아키텍처

이전 후의 목표 구조는 아래와 같았다.

  • 미니 PC 1대
  • Nginx (Reverse Proxy)
  • React 정적 빌드 서빙
  • Spring Boot 백엔드 (systemd)
  • GitHub Actions self-hosted runner
  • HTTPS (Let’s Encrypt)

핵심은 “한 대지만, 운영 서버처럼 쓰자”였다.


3. 디렉터리 구조 설계

여러 프로젝트를 올릴 것을 고려해 루트 디렉터리부터 명확히 설계했다.

/dadamda
 ├─ _shared
 │   └─ nginx
 │       └─ conf.d
 │
 └─ dadamda-web
     ├─ frontend
     │   └─ build
     └─ backend
         └─ app.jar
  • /dadamda : 조직/서비스 루트
  • _shared : 여러 프로젝트 공통 자원
  • 프로젝트 단위로 완전히 분리

이 구조 덕분에:

  • 배포 경로 고정
  • CI/CD 단순화
  • 나중에 프로젝트 추가도 쉬워졌다.

4. Nginx 설정과 프록시 구조

Nginx는 두 가지 역할만 하도록 제한했다.

  1. React 정적 파일 서빙
  2. /api 요청을 Spring Boot로 프록시
location / {
    try_files $uri /index.html;
}

location /api/ {
    proxy_pass http://127.0.0.1:8080/;
}

프론트와 백엔드를 하나의 도메인으로 묶으면서도,
CORS나 인증 문제 없이 깔끔하게 동작했다.


5. Spring Boot 실행 방식

백엔드는 Docker 대신 systemd 서비스로 올렸다.

[Service]
User=godhkekf24
WorkingDirectory=/dadamda/dadamda-web/backend
ExecStart=/usr/bin/java -jar app.jar
Restart=always

이 방식을 선택한 이유는 단순하다.

  • 빠르게 이전 및 검증
  • 재부팅 시 자동 실행
  • 로그는 journald로 관리
  • 장애 시 자동 재시작

홈서버에서는 단순함이 곧 안정성이었다.


6. CI/CD 재구성

아직 작업하진 못했지만 기존 GitHub Actions를 그대로 유지하되,
self-hosted runner를 미니 PC에 설치하려고 한다.

이 선택의 장점은 명확했다.

  • GitHub Actions 비용 0원
  • 빌드/배포 모두 로컬 자원 사용
  • 외부 포트 오픈 불필요 (outbound only)

결과적으로 Jenkins 없이도 충분히 강력한 파이프라인을 만들 수 있을 것이다.


7. DNS: 가장 많이 막혔던 부분

이 구간이 이번 이전 작업에서 가장 시간을 많이 잡아먹은 부분이었다.

처음에는 단순히 DNS 전파 시간 문제라고 생각했다.

  • A 레코드는 정상적으로 추가했고
  • TTL도 낮게 설정했고
  • 포트포워딩과 Nginx도 모두 준비된 상태였다.

그런데 몇 시간이 지나도, 하루가 지나도 DNS가 잡히지 않았다.

nslookup dadamda.site

결과는 계속 NXDOMAIN.


문제의 진짜 원인: DNS 권한(Authoritative) 불일치

원인은 "시간"이 아니라 "권한" 문제였다.

도메인의 네임서버(NS)는 이미 AWS Route53로 위임된 상태였는데,
실제 A 레코드 설정은 가비아 DNS 관리 화면에서 하고 있었던 것이다.

즉 구조는 이랬다.

도메인 등록: 가비아
네임서버(NS): Route53
DNS 레코드 설정: 가비아 ❌

DNS 동작 원리를 기준으로 보면 이건 명확한 오류다.

네임서버가 Route53를 가리키고 있으면,
DNS 레코드는 반드시 Route53 Hosted Zone에 존재해야 한다.

가비아에서 아무리 A 레코드를 추가해도,
전 세계 DNS 서버는 Route53에 질의하기 때문에
결과는 계속 "존재하지 않는 도메인"이 된다.


그래서 왜 하루가 지나도 안 됐나

이 상황에서는:

  • DNS 전파를 아무리 기다려도 ❌
  • TTL을 줄여도 ❌
  • 포트포워딩을 다시 해도 ❌

절대 해결되지 않는다.

왜냐하면 DNS는 "기다리면 퍼지는 값"이 아니라,
"권한 있는 서버에 존재해야만 조회되는 값" 이기 때문이다.


해결 방법

해결은 두 가지 중 하나였다.

  1. 네임서버를 가비아 DNS로 되돌리거나
  2. Route53에 Hosted Zone을 생성하고
    A / CNAME 레코드를 Route53에서 직접 관리

나는 두 번째 방법을 선택했다.

  • Route53 Public Hosted Zone 생성
  • dadamda.site에 대한 A 레코드 추가
  • 네임서버는 그대로 유지

그 순간부터:

nslookup dadamda.site 8.8.8.8

에서 즉시 IP가 반환되었고,
Let’s Encrypt 인증서도 문제없이 발급됐다.


이 경험에서 얻은 교훈

DNS 문제의 절반은 "설정"이고,
나머지 절반은 "어디가 권한을 가지고 있는지"다.

"기다리면 된다"는 말은
권한이 올바를 때만 성립한다.

DNS가 안 잡힐 때는 항상 먼저 이 질문을 해야 한다.

  • 지금 이 도메인의 authoritative DNS는 누구인가?
  • 내가 레코드를 추가한 곳이, 정말 그 서버인가?


8. 공유기 포트포워딩과 관리자 페이지 이슈

80/443 포트포워딩을 설정한 뒤,
공인 IP로 접속하니 공유기 페이지 대신 서비스 화면이 나왔다.

처음엔 당황했지만, 이는 정상적인 동작이었다.

  • 외부 요청은 미니 PC로 전달
  • 공유기 관리자 페이지는 내부 IP + 별도 포트

오히려 보안적으로 더 안전한 상태였다.


9. 결과: 지금의 상태

현재 상태는 다음과 같다.

  • 미니 PC 홈서버 운영 중
  • React + Spring Boot 정상 서비스
  • HTTPS 적용 완료

해야하는 작업

  • CI/CD 자동 배포
  • DB 이전

무엇보다 구조를 전부 이해하고 운영한다는 안정감이 가장 큰 수확이었다.


10. 마치며

이번 이전 작업은 단순한 서버 이동이 아니라,

  • 네트워크
  • DNS
  • Linux
  • 배포
  • 운영

을 한 번에 복습하는 경험이었다.

클라우드가 편한 건 사실이지만,
직접 굴려본 홈서버 경험은 다른 차원의 이해를 준다.

비슷한 고민을 하고 있다면,
작은 프로젝트 하나라도 홈서버로 옮겨보는 걸 추천한다.


다음 글에서는

  • CI/CD 자동 배포

  • DB 이전

    도 정리해볼 예정이다.

profile
한정된 자원으로 더 많은 가치를 제공하려고 노력하는 백엔드 엔지니어입니다.

0개의 댓글