홈서버 구축 (미니PC)

개인 서버 구축(9) - nginx 설정하고 SSL 인증서로 HTTPS 받기 [마지막]

남건욱 2025. 7. 12. 19:38

목차

    반응형

    1. Nginx 설치

    sudo apt update
    sudo apt install -y nginx
    sudo systemctl status nginx

    - sudo apt update로 패키지 목록을 업데이트해준다.

    - sudo apt install -y nginx로 Nginx를 설치해 준다.

    - sudo systemctl status nginx로 서비스 상태를 조회한다.

     

    초록색으로 활성화가 떴다고 조회되면 올바르게 설치된 것이다.

     

    sudo rm /etc/nginx/sites-enabled/default
    
    기본으로 들어있는 /etc/nginx/sites-enabled/default 파일을 지워준다.

    default 설정을 지우는 이유는 어차피 사용하지 않을 것이고, 기본값이 내가 따로 설정할 값과 충돌할 수 있기 때문에 지워줬다.

     

    upstream react_app {
        server 127.0.0.1:3000;
    }
    upstream springboot {
        server 127.0.0.1:8080;
    }
    
    # ─────────────────────────────────
    # 1) HTTP 전용: 80번 포트에서 HTTPS로만 리다이렉트
    # ─────────────────────────────────
    server {
        listen      80;
        server_name tripmark.co.kr www.tripmark.co.kr;
    
        # HTTP로 들어오는 모든 요청을 HTTPS로
        return 301 https://$host$request_uri;
    }
    
    # ─────────────────────────────────
    # 2) HTTPS 전용: 443번 포트에서 React/API 프록시
    # ─────────────────────────────────
    server {
        listen              443 ssl http2;
        server_name         tripmark.co.kr www.tripmark.co.kr;
    
        # SSL 인증서 경로 (certbot이 발급해 준 경로)
        ssl_certificate     /etc/letsencrypt/live/tripmark.co.kr/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/tripmark.co.kr/privkey.pem;
    
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 10m;
        ssl_protocols       TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers on;
    
        # 공통 프록시 헤더
        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;
    
        # /api 요청은 스프링부트 컨테이너로
        location ^~ /api/ {
            proxy_pass http://springboot;
            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;
            
            # SSE 를 위한 버퍼링 해제
            proxy_buffering     off;
            proxy_cache         off;
            add_header          Cache-Control    "no-cache";
            proxy_http_version  1.1;
        }
    
        # ──────────────────────────────────────────
        # WebSocket (SockJS) 엔드포인트 프록시
        # ──────────────────────────────────────────
        location /ws-chat {
            proxy_pass          http://springboot/ws-chat;
            proxy_http_version  1.1;
            proxy_set_header    Upgrade           $http_upgrade;
            proxy_set_header    Connection        "Upgrade";
            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;
            # optionally disable buffering for WS
            proxy_buffering     off;
        }
    
        location /service/images/ {
            alias           {이미지 경로};
            access_log      off;
            expires         30d;
            add_header      Cache-Control "public";
            # 정적 파일(디렉터리) 조회
            try_files       $uri $uri/ =404;
        }
    
        # 그 외는 React 컨테이너로
        location / {
            proxy_pass http://react_app;
        }
    }

    /etc/nginx/sites-available/ 경로에서 스프링부트+리액트를 합친 .conf 파일 생성을 해준다.

    나는 서버+화면을 한 번에 nginx 설정에 넣었다.

     

    sudo ln -s /etc/nginx/sites-available/footprint.conf  /etc/nginx/sites-enabled/

    설정 파일을 sites-enabled로 심볼릭 링크해 nginx 설정을 활성화해 준다.

     

    sudo nginx -t
    sudo systemctl reload nginx

    nginx -t로 오류 유무를 파악해 준 뒤 systemctl reload nginx로 방금 만든 설정을 불러와준다.

     

     

    2. docker-compose.yml 설정

    version: "3.9"
    
    services:
      # ────────────────────────────────────────────────────────────
      # Spring Boot Backend
      # ────────────────────────────────────────────────────────────
      backend:
        image: footprint-backend:latest
        container_name: footprint-backend
        restart: always
        ports:
          - "8080:8080"
        environment:
          # Spring Boot 프로퍼티 오버라이드
          SPRING_PROFILES_ACTIVE: prod
          SPRING_DATASOURCE_URL: jdbc:mariadb://mariadb:{db포트}/footprint
          SPRING_DATASOURCE_USERNAME: {db아이디}
          SPRING_DATASOURCE_PASSWORD: {db비밀번호}
          SPRING_REDIS_HOST: redis
          SPRING_REDIS_PORT: "{redis포트}"
          SPRING_DATA_MONGODB_URI: mongodb://mongodb:{mongodb포트}/{db명}
          MAIL_USERNAME: {smtp아이디}
          MAIL_PASSWORD: {smtp비밀번호}
        depends_on:
          - mariadb
          - redis
          - mongodb
        volumes:
          - /home/gunwook/app/service/images:/home/gunwook/app/service/images
    
      # ────────────────────────────────────────────────────────────
      # React + Nginx Frontend
      # ────────────────────────────────────────────────────────────
      frontend:
        image: footprint-frontend:latest
        container_name: footprint-frontend
        restart: always
        ports:
          - "3000:80"
        depends_on:
          - backend
    
      # ────────────────────────────────────────────────────────────
      # MariaDB
      # ────────────────────────────────────────────────────────────
      mariadb:
        image: mariadb:10.6
        container_name: mariadb
        restart: always
        environment:
          MYSQL_ROOT_PASSWORD: {db아이디}
          MYSQL_DATABASE: {db비밀번호}
        ports:
          - "{db포트}:{db포트}"
        volumes:
          - mariadb_data:/var/lib/mysql

    이제 jenkins를 설정하기 전에 docker-compose.yml 파일을 만들어준다.

     

     

    3. Jenkins 설정 (서버, 화면)

    이제 젠킨스에서 서버 Pipeline 설정부터 해준다. 

     

    pipeline {
      agent any
    
      // GitHub Webhook 쓰려면 triggers { githubPush() } 또는 위 체크박스만으로 충분
      triggers {
        githubPush()
      }
    
      environment {
        // 도커 이미지 이름
        IMAGE_NAME = "footprint-backend"
        // docker-compose.yml 위치
        SERVICE_DIR = "/home/gunwook/app/service"
      }
    
      stages {
        stage('Checkout') {
          steps {
            // private repo 접근용 credentialsId 는 'tripmark'
            git credentialsId: 'tripmark',
                url: 'https://github.com/gunwooknam2023/footprint.git',
                branch: 'main'
          }
        }
        
        stage('Grant Exec Permission') {
          steps {
            sh 'chmod +x gradlew'
          }
        }
    
        stage('Build JAR') {
          steps {
            // 테스트 제외하고 빠르게 빌드
            sh './gradlew clean bootJar -x test'
          }
        }
    
        stage('Build Docker Image') {
          steps {
            // 컨테이너용 이미지 빌드
            sh "docker build -t ${IMAGE_NAME}:latest ."
          }
        }
    
        stage('Deploy via Docker Compose') {
          steps {
            sh '''
              cd /home/gunwook/app/service
              docker compose pull || true
              docker compose up -d
            '''
          }
        }
      }
    
      post {
        success {
          echo "✅ Deployment successful: ${IMAGE_NAME}:latest"
        }
        failure {
          echo "❌ Deployment failed!"
        }
      }
    }

     

     

     

    그다음 화면 Pipeline를 설정해준다. 

     

    pipeline {
      agent any
    
      triggers {
        githubPush()
      }
    
      environment {
        IMAGE_NAME  = "footprint-frontend"
        SERVICE_DIR = "/home/gunwook/app/service"
      }
    
      stages {
        stage('Checkout') {
          steps {
            git credentialsId: 'tripmark',
                url: 'https://github.com/gunwooknam2023/footprintfront.git',
                branch: 'main'
          }
        }
    
        stage('Build Docker Image') {
          steps {
            // Dockerfile 및 default.conf가 워크스페이스에 있으므로 바로 빌드
            sh "docker build -t ${IMAGE_NAME}:latest ."
          }
        }
    
        stage('Deploy via Docker Compose') {
          steps {
            sh """
              cd ${SERVICE_DIR}
              docker compose pull || true
              docker compose up -d
            """
          }
        }
      }
    
      post {
        success {
          echo "✅ Frontend deployed: ${IMAGE_NAME}:latest"
        }
        failure {
          echo "❌ Frontend deployment failed."
        }
      }
    }

     

     

     

     

    4. SSL을 사용한 인증서 생성 + https 적용

    sudo apt update
    sudo apt install -y certbot python3-certbot-nginx

    sudo apt update로 패키지 목록을 최신 상태로 갱신해 준다.

    sudo apt install -y certbot python3-certbot-nginx로 certbot이 nginx 설정을 자동으로 찾아서 SSL 인증서를 설치, 갱신해 주도록 한다. -y옵션으로 설치 중 묻는 옵션에 yes로 자동응답 하도록 설정했다.

     

    sudo certbot --nginx \     # nginx 플러그인을 사용해 인증서 발급 후 nginx 설정을 자동으로 수정
      -d tripmark.co.kr \      # 인증서를 적용할 도메인을 지정
      -d www.tripmark.co.kr \  #                           ""
      --agree-tos \            # 서비스 약관에 자동 동의
      --redirect \             # HTTP 요청을 HTTPS로 자동 리다이렉트하도록 nginx 설정 추가
      --no-eff-email \         # 뉴스레터 수신 동의 및 이메일 공유를 건너뜀
      -m ngwdevelop@gmail.com  # 인증서 만료 알림 등을 받을 관리자 이메일 지정

    그 뒤 SSL 인증서를 발급하고 리다이렉트 설정까지 해주도록 했다.

     

    Congratulations! You have successfully enabled HTTPS on https://tripmark.co.kr and https://www.tripmark.co.kr

    위처럼 떴다면 이제 도메인에 HTTPS가 활성화 됐을 것이다.

     

    잘 적용된 모습이다. 

     

     

    이로써 홈서버 구축부터 파이프라인 및 Jenkins 연동, 도메인 설정, SSL 기반 HTTPS 구성까지 모두 완료하여 기본 환경 구성을 마무리했다.

    반응형
    프로필사진

    남건욱's 공부기록