Beginner Tutorial

OCI Oracle Linux로
기간제 수업 자료실 만들기

Oracle Cloud Infrastructure(OCI) 인스턴스에 Nginx 웹서버를 세우고, 과정별 계정을 시작일에 자동 활성화하고 종료일 다음 날 자동 삭제하는 HTTPS 수업 자료 사이트 구축 방법을 안내합니다.

완성될 모습 (아키텍처)

우리가 만들 시스템은 두 가지 흐름으로 이루어집니다 — ① 학생이 접속하는 흐름, ② 선생님(관리자)이 자료를 관리하는 흐름.

① 학생이 자료를 보는 흐름

학생 브라우저
노트북 · 휴대폰
비밀번호 입력
파일 전달
수업 자료 폴더
SQL · ADSQL · SQLT · ADMIN · BRW · PTW · AI (과정별 폴더)
관리 흐름

② 선생님이 자료를 올리고 관리하는 흐름

선생님 PC
터미널 접속
SSH 접속
저장
수업 자료 폴더
즉시 반영됨
i
이 구조에서는 서버(가운데 상자)가 모든 것을 통제합니다. 학생은 비밀번호 없이는 자료를 볼 수 없고, 선생님은 SSH로 직접 서버에 접속해서 원하는 때 자유롭게 파일을 바꿀 수 있습니다. Public 저장소를 쓸 필요가 없어서, GitHub Pages나 Netlify처럼 유료 플랜을 결제할 필요가 없습니다.

용어 사전

본격적으로 시작하기 전에, 앞으로 계속 나올 용어들을 미리 짧게 정리해 두었습니다. 모르는 단어가 나오면 여기로 돌아와 확인하세요.

용어설명
OCIOracle Cloud Infrastructure. 오라클이 제공하는 클라우드 서비스. 일부 자원에 무료 사용 한도를 제공하며 조건은 시점과 리전에 따라 달라질 수 있습니다.
인스턴스 (VM)클라우드에 만드는 가상 컴퓨터 한 대. 내 컴퓨터처럼 운영체제가 설치되어 있고 직접 접속해서 프로그램을 설치할 수 있습니다.
SSH내 컴퓨터에서 멀리 있는 서버에 터미널(명령어 창)로 접속하는 방법. 비밀번호 대신 "키(key)"라는 파일로 인증합니다.
VCN / Security ListOCI 안에서 서버로 들어오는 네트워크 통로를 관리하는 방화벽 설정. 이 설정에서 허용하지 않은 포트는 아무리 서버 안에서 열어도 외부에서 접속이 안 됩니다.
포트 (Port)서버의 "문 번호"입니다. 웹사이트는 보통 80번(HTTP), 443번(HTTPS) 포트를 사용합니다.
Nginx (엔진엑스)웹서버 프로그램. 폴더 안의 HTML 파일들을 인터넷에 있는 사람들에게 보여주는 역할을 합니다.
.htpasswdNginx가 사용하는 "아이디/비밀번호 명단" 파일. 이 파일에 등록된 사람만 사이트에 들어갈 수 있게 만듭니다.
도메인 (Domain)서버의 숫자 주소(IP) 대신 쓰는 기억하기 쉬운 이름. 예: class.example.com
DuckDNS무료로 도메인 이름을 하나 받을 수 있는 서비스. 우리는 이걸로 무료 도메인을 얻습니다.
HTTPS / SSL브라우저와 서버 사이 통신을 암호화하는 기술. 비밀번호가 안전하게 전달되도록 해줍니다.
Let's Encrypt / CertbotHTTPS 인증서를 무료로 발급해주는 서비스와 그 발급 도구입니다.
sudo"관리자 권한으로 실행"이라는 뜻의 명령어 접두사. 서버 설정을 바꿀 때 자주 사용합니다.

사전 준비물

!
카드 등록은 본인 확인용입니다. Always Free로 표시된 자원만 사용하면 과금되지 않지만, 혹시 모를 실수를 막기 위해 가입 후 예산 알림(Budget Alert)을 설정해 두는 걸 권장합니다 (8단계 이후 여유가 될 때 설정 가능).

1OCI 계정 만들기

1

Oracle Cloud 가입 페이지 접속

oracle.com/cloud/free 에서 "Start for free" 버튼을 클릭합니다.

2

국가/지역을 South Korea로 설정

가입 화면에서 국가를 South Korea로 선택합니다. 이렇게 하면 나중에 서울 리전을 홈 리전으로 쓸 수 있습니다.

3

이메일 인증, 비밀번호 설정, 휴대폰 인증

화면에 나오는 순서대로 이메일 인증 코드, 계정 비밀번호, 휴대폰 문자 인증을 차례로 진행합니다.

4

홈 리전(Home Region) 선택

홈 리전을 고르라는 화면이 나오면 South Korea Central (Seoul)을 선택합니다.

!
홈 리전은 계정 생성 후 바꿀 수 없습니다. 가까운 한국 리전을 선택하되, 실제 생성 화면에서 원하는 Shape의 가용성과 무료 사용 가능 표시를 확인하세요. 이 튜토리얼은 Oracle Linux 9와 VM.Standard.E2.1.Micro를 기준으로 합니다.
5

결제 정보 입력 후 가입 완료

카드 정보를 입력하고 가입을 완료하면, OCI 콘솔(관리 화면)에 로그인할 수 있게 됩니다.

2컴퓨트 인스턴스 생성

이제 실제로 사용할 가상 서버(인스턴스)를 만듭니다.

1

Compute → Instances 메뉴 이동

OCI 콘솔 왼쪽 상단 메뉴(☰)에서 Compute → Instances로 들어가서 Create Instance를 클릭합니다.

2

이름 입력

Name 칸에 class-materials-server 처럼 알아보기 쉬운 이름을 입력합니다.

3

이미지(Image) 및 모양(Shape) 변경

"Image and shape" 영역에서 Edit를 눌러 다음과 같이 설정합니다.

항목설정값
ImageOracle Linux 9
Shape seriesAMD 기반 또는 현재 리전에서 무료 사용 가능한 시리즈
ShapeVM.Standard.E2.1.Micro — 생성 화면에서 Always Free eligible 여부 확인
OCPU1
Memory1 GB
무료 제공 조건과 지원 Shape는 리전과 시점에 따라 달라질 수 있습니다. 생성 직전 "Always Free eligible" 표시와 예상 비용을 반드시 확인하고 예산 알림도 설정하세요.
4

SSH 키 생성 및 다운로드

"Add SSH keys" 영역에서 Generate a key pair for me를 선택하고, Save private key 버튼으로 개인 키 파일(.key 확장자)을 다운로드합니다. 이 파일이 있어야 서버에 접속할 수 있으므로 잘 보관합니다.

!
이 키 파일을 잃어버리면 서버에 다시 접속할 방법이 없습니다. USB나 클라우드 저장소 등 안전한 곳에 백업해 두세요.
5

생성(Create) 클릭

나머지는 기본값 그대로 두고 화면 맨 아래 Create를 클릭합니다. 1~2분 정도 지나면 인스턴스 상태가 RUNNING으로 바뀝니다.

6

Public IP 주소 확인

인스턴스 상세 화면에서 Public IP Address를 찾아 메모해 둡니다. 앞으로 이 주소로 서버에 접속합니다. (예: 132.145.xxx.xxx)

3방화벽 규칙 열기 (VCN Security List)

서버 안에 웹서버를 설치해도, OCI 네트워크 단계에서 문을 열어주지 않으면 외부에서 접속이 안 됩니다. 이 단계에서 80번(HTTP)과 443번(HTTPS) 포트를 열어줍니다.

1

서브넷의 Security List로 이동

인스턴스 상세 화면에서 Primary VNIC 아래 Subnet 이름을 클릭한 뒤, Security Lists → Default Security List를 클릭합니다.

2

Ingress Rule 추가 (포트 80)

Add Ingress Rules를 클릭하고 아래와 같이 입력합니다.

항목설정값
Source TypeCIDR
Source CIDR0.0.0.0/0
IP ProtocolTCP
Destination Port Range80
3

Ingress Rule 추가 (포트 443)

같은 방식으로 규칙을 한 번 더 추가하되, Destination Port Range만 443으로 바꿔서 저장합니다. (SSH용 22번 포트는 기본값으로 이미 열려 있습니다.)

4SSH로 서버 접속

1

키 파일 권한 설정 (Mac / Linux)

터미널을 열고 다운로드한 키 파일이 있는 폴더로 이동한 뒤 아래 명령을 실행합니다.

# 키 파일 권한을 본인만 읽을 수 있게 제한
chmod 400 ssh-key-오늘날짜.key
2

서버 접속

ssh -i ssh-key-오늘날짜.key opc@서버의_Public_IP

처음 접속하면 "계속 연결하시겠습니까? (yes/no)" 라고 물어보는데 yes를 입력하고 엔터를 누릅니다.

Windows 사용자는 최신 Windows 10/11의 PowerShell에서도 동일한 ssh 명령이 그대로 작동합니다. 별도 프로그램 설치가 필요 없습니다.
3

접속 성공 확인

프롬프트가 [opc@class-materials-server ~]$ 형태로 바뀌면 접속에 성공한 것입니다. 이제부터 나오는 모든 명령어는 이 화면(서버 안)에서 입력합니다.

5서버 내부 방화벽 설정

Oracle Linux 9는 서버 내부에서 firewalld를 사용합니다. OCI Security List와 서버 내부 방화벽을 모두 열어야 외부 접속이 가능합니다.

1

80번, 443번 포트 허용 규칙 추가

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
2

설정 영구 저장

sudo firewall-cmd --list-all

serviceshttp https ssh가 보이는지 확인합니다. --permanent로 추가했으므로 재부팅 후에도 유지됩니다.

6Nginx 웹서버 설치

1

1GB 서버의 스왑 보강

sudo swapoff /.swapfile
sudo fallocate -l 2G /.swapfile
sudo chmod 600 /.swapfile
sudo mkswap -f /.swapfile
sudo swapon /.swapfile
swapon --show

기존 /.swapfile이 있는 OCI 이미지 기준입니다. 스왑 사용 중이거나 파일명이 다르면 먼저 swapon --show로 확인하세요.

2

Nginx 설치

sudo nice -n 19 ionice -c3 dnf \
  --setopt=ip_resolve=4 \
  --disablerepo='*' \
  --enablerepo=ol9_baseos_latest,ol9_appstream \
  install -y nginx httpd-tools policycoreutils-python-utils
i
저사양 인스턴스에서는 전체 저장소 메타데이터 처리 중 SSH가 느려질 수 있습니다. IPv4와 필수 저장소만 사용하고 nice/ionice로 우선순위를 낮추면 안정적입니다.
3

설치 확인

sudo systemctl enable --now nginx
sudo nginx -t

브라우저에서 http://서버의_Public_IP로 접속해 기본 화면이 보이면 정상입니다.

7무료 도메인 연결 (DuckDNS)

숫자로 된 IP 주소 대신 기억하기 쉬운 이름을 쓰고, 나중에 HTTPS(암호화)를 적용하려면 도메인이 하나 있어야 합니다. DuckDNS는 완전 무료로 서브도메인을 제공합니다. (이 단계는 선택이지만 강력히 추천합니다.)

1

DuckDNS 가입 및 도메인 생성

duckdns.org에 접속해 Google/GitHub 계정으로 로그인합니다. 먼저 주황색 reCaptcha 버튼의 인증을 완료해야 domain 영역이 나타납니다. dataclass만 입력하고 add domain을 누르면 dataclass.duckdns.org가 만들어집니다.

2

도메인에 서버 IP 연결

DuckDNS 페이지의 도메인 옆 IP 입력칸에 2단계에서 메모한 Public IP를 입력하고 update ip를 클릭합니다.

3

연결 확인

브라우저에서 http://dataclass.duckdns.org로 접속했을 때 Nginx 화면이 보이면 정상입니다. 등록 직후 이름을 찾지 못하면 잠시 기다리거나 Windows에서 ipconfig /flushdns를 실행합니다.

8수업 자료 파일 업로드

지금까지 정리한 7개 과정(SQL / ADSQL / SQLT / ADMIN / BRW / PTW / AI) 폴더 구조를 서버로 옮깁니다. 과정마다 폴더를 나누어 두어야 다음 단계에서 과정별로 다른 비밀번호를 걸 수 있습니다.

1

폴더 구조 최종 확인 (내 컴퓨터에서)

업로드하기 전에 내 컴퓨터의 자료 폴더가 아래처럼 정리되어 있는지 확인합니다. 7개 과정 폴더가 최상위에 나란히 있어야 합니다.

class-materials/ ├── index.html (전체 허브 페이지) ├── style.css ├── SQL/ │ ├── index.html │ └── (SQL 수업 예제 파일들) ├── ADSQL/ │ ├── index.html │ └── (ADSQL 수업 예제 파일들, images/ 포함) ├── SQLT/ │ └── index.html ├── ADMIN/ │ └── index.html ├── BRW/ │ └── index.html ├── PTW/ │ └── index.html └── AI/ ├── index.html └── (AI 서비스 설계 자료 파일들)
i
폴더 이름의 대소문자가 그대로 서버에 올라갑니다. Linux 서버는 대소문자를 구분하므로, SQL/sql/은 서로 다른 폴더로 취급됩니다. 아래 단계들과 폴더명을 정확히 맞춰주세요.
2

scp로 서버에 통째로 전송

내 컴퓨터의 터미널(서버 접속 전 화면)에서 아래 명령을 실행합니다. -i 뒤에는 SSH 키 파일 경로를, IP 자리에는 서버 주소를 넣습니다.

scp -i ssh-key-오늘날짜.key -r ./class-materials opc@서버의_Public_IP:/home/opc/
3

웹서버 폴더로 이동 (서버 안에서, SSH로 다시 접속)

sudo mkdir -p /var/www/class-materials
sudo cp -a /home/opc/class-materials/. /var/www/class-materials/
sudo chown -R root:nginx /var/www/class-materials
sudo find /var/www/class-materials -type d -exec chmod 755 {} +
sudo find /var/www/class-materials -type f -exec chmod 644 {} +
sudo semanage fcontext -a -t httpd_sys_content_t '/var/www/class-materials(/.*)?'
sudo restorecon -RF /var/www/class-materials

Oracle Linux의 SELinux가 Enforcing 상태이므로 파일 권한뿐 아니라 웹 콘텐츠 컨텍스트도 설정해야 합니다.

9기간제 과정 계정 자동화

계정마다 시작일과 종료일을 저장하고 매분 활성 명단을 다시 만듭니다. 시작일 00:00부터 접근할 수 있고, 종료일 23:59가 지나면 원본 DB와 인증 파일에서 자동 삭제됩니다. 시간 기준은 Asia/Seoul입니다.

1

동기화 스크립트 작성

sudo nano /usr/local/sbin/sync-course-access를 열어 다음 내용을 저장합니다.

#!/usr/bin/env bash
set -euo pipefail

DB=/etc/nginx/course-access.db
AUTH_DIR=/etc/nginx/course-auth
SUBJECTS="SQL ADSQL SQLT ADMIN BRW PTW AI"
now=$(date +%s)

install -d -o root -g nginx -m 750 "$AUTH_DIR"
touch "$DB"
chmod 600 "$DB"

# 만료된 계정은 원본 DB에서도 삭제한다.
db_tmp=$(mktemp)
trap 'rm -f "$db_tmp"' EXIT
awk -F'|' -v now="$now" 'NF == 5 && $3 > now' "$DB" > "$db_tmp"
install -o root -g root -m 600 "$db_tmp" "$DB"

for course in $SUBJECTS; do
  auth_tmp=$(mktemp "$AUTH_DIR/.${course}.XXXXXX")
  awk -F'|' -v c="$course" -v now="$now" \
    '$1 == c && $2 <= now && now < $3 { print $4 ":" $5 }' "$DB" > "$auth_tmp"
  chown root:nginx "$auth_tmp"
  chmod 640 "$auth_tmp"
  mv -f "$auth_tmp" "$AUTH_DIR/${course}.htpasswd"
done
sudo chmod 750 /usr/local/sbin/sync-course-access
2

계정 생성 스크립트 작성

sudo nano /root/create-course-access를 열어 다음 전체 내용을 저장합니다. 비밀번호 원문은 저장하지 않고 bcrypt 해시만 DB에 기록합니다.

#!/usr/bin/env bash
set -euo pipefail

DB=/etc/nginx/course-access.db
SYNC=/usr/local/sbin/sync-course-access

if [[ $EUID -ne 0 ]]; then
  echo "sudo로 실행해 주세요: sudo $0" >&2
  exit 1
fi

echo "아이디를 생성할 과목:"
echo "1. SQL"
echo "2. ADSQL"
echo "3. SQLT"
echo "4. ADMIN"
echo "5. BRW"
echo "6. PTW"
echo "7. AI"
read -r -p "번호 선택 (1-7): " course_number
case "$course_number" in
  1) course=SQL ;;
  2) course=ADSQL ;;
  3) course=SQLT ;;
  4) course=ADMIN ;;
  5) course=BRW ;;
  6) course=PTW ;;
  7) course=AI ;;
  *) echo "1~7 사이의 숫자를 입력해 주세요." >&2; exit 1 ;;
esac

read -r -p "시작 일 (YYYYMMDD): " start_input
read -r -p "종료 일 (YYYYMMDD): " end_input
read -r -p "아이디: " username
read -r -s -p "비밀번호: " password
echo
read -r -s -p "비밀번호 확인: " password_confirm
echo

if [[ ! $start_input =~ ^[0-9]{8}$ ]] || [[ ! $end_input =~ ^[0-9]{8}$ ]]; then
  echo "날짜는 YYYYMMDD 형식이어야 합니다. 예: 20260720" >&2
  exit 1
fi
start_date="${start_input:0:4}-${start_input:4:2}-${start_input:6:2}"
end_date="${end_input:0:4}-${end_input:4:2}-${end_input:6:2}"

if [[ ! $username =~ ^[A-Za-z0-9._-]{3,64}$ ]]; then
  echo "아이디는 영문, 숫자, 점, 밑줄, 하이픈으로 3~64자여야 합니다." >&2
  exit 1
fi
if (( ${#password} < 8 )); then
  echo "비밀번호는 8자 이상이어야 합니다." >&2
  exit 1
fi
if [[ $password != "$password_confirm" ]]; then
  echo "비밀번호가 일치하지 않습니다." >&2
  exit 1
fi
unset password_confirm

start_epoch=$(TZ=Asia/Seoul date -d "$start_date 00:00:00" +%s) || {
  echo "유효하지 않은 시작일입니다." >&2; exit 1;
}
expire_epoch=$(TZ=Asia/Seoul date -d "$end_date +1 day 00:00:00" +%s) || {
  echo "유효하지 않은 종료일입니다." >&2; exit 1;
}
if (( expire_epoch <= start_epoch )); then
  echo "종료일은 시작일보다 빠를 수 없습니다." >&2
  exit 1
fi

entry=$(htpasswd -nbB "$username" "$password")
hash=${entry#*:}
unset password entry

if [[ ! -e $DB ]]; then
  install -o root -g root -m 600 /dev/null "$DB"
fi
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
awk -F'|' -v c="$course" -v u="$username" \
  '!( $1 == c && $4 == u )' "$DB" > "$tmp"
printf '%s|%s|%s|%s|%s\n' \
  "$course" "$start_epoch" "$expire_epoch" "$username" "$hash" >> "$tmp"
install -o root -g root -m 600 "$tmp" "$DB"

"$SYNC"

echo "계정 등록 완료"
echo "과목: $course"
echo "아이디: $username"
echo "활성 기간: $start_date 00:00 ~ $end_date 23:59 (Asia/Seoul)"
sudo chmod 700 /root/create-course-access
sudo bash -n /root/create-course-access
3

매분 자동 동기화 timer 등록

/etc/systemd/system/course-access-sync.service:

[Unit]
Description=Synchronize active Nginx course accounts

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/sync-course-access

/etc/systemd/system/course-access-sync.timer:

[Unit]
Description=Refresh course access accounts every minute

[Timer]
OnCalendar=*-*-* *:*:00
Persistent=true
AccuracySec=5s

[Install]
WantedBy=timers.target
sudo /usr/local/sbin/sync-course-access
sudo systemctl daemon-reload
sudo systemctl enable --now course-access-sync.timer
systemctl list-timers course-access-sync.timer
4

과정 계정 생성

sudo /root/create-course-access

과정 번호, 20260720 형식의 시작일·종료일, 아이디, 비밀번호와 비밀번호 확인을 입력합니다. 같은 과정/아이디를 다시 등록하면 기존 일정과 비밀번호가 교체됩니다.

10Nginx 설정 적용

이제 과정 폴더마다 알맞은 비밀번호 파일을 연결합니다. 허브 페이지(첫 화면)는 누구나 볼 수 있게 두고, 각 과정 폴더에 들어갈 때만 그 과정의 비밀번호를 요구하도록 설정합니다.

1

설정 파일 만들기

sudo nano /etc/nginx/conf.d/class-materials.conf

Oracle Linux Nginx는 /etc/nginx/conf.d/*.conf를 자동으로 읽습니다. 아래 설정은 실제 도메인 dataclass.duckdns.org와 Public IP를 함께 받습니다.

server {
    listen 80;
    server_name dataclass.duckdns.org 서버의_Public_IP;
    root /var/www/class-materials;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    location /SQL/ {
        auth_basic "SQL course materials";
        auth_basic_user_file /etc/nginx/course-auth/SQL.htpasswd;
    }

    location /ADSQL/ {
        auth_basic "ADSQL course materials";
        auth_basic_user_file /etc/nginx/course-auth/ADSQL.htpasswd;
    }

    location /SQLT/ {
        auth_basic "SQLT course materials";
        auth_basic_user_file /etc/nginx/course-auth/SQLT.htpasswd;
    }

    location /ADMIN/ {
        auth_basic "ADMIN course materials";
        auth_basic_user_file /etc/nginx/course-auth/ADMIN.htpasswd;
    }

    location /BRW/ {
        auth_basic "BRW course materials";
        auth_basic_user_file /etc/nginx/course-auth/BRW.htpasswd;
    }

    location /PTW/ {
        auth_basic "PTW course materials";
        auth_basic_user_file /etc/nginx/course-auth/PTW.htpasswd;
    }

    location /AI/ {
        auth_basic "AI course materials";
        auth_basic_user_file /etc/nginx/course-auth/AI.htpasswd;
    }
}

붙여넣은 뒤 Ctrl+OEnter (저장) → Ctrl+X (닫기)로 빠져나옵니다.

허브 페이지(index.html)는 location /에만 걸려 있어서 비밀번호 없이 누구나 열립니다. 카드를 클릭해서 실제 과정 폴더(예: /SQL/)로 들어가는 순간부터 그 과정의 비밀번호를 요구합니다.
2

인증 파일 초기화

sudo /usr/local/sbin/sync-course-access
sudo ls -l /etc/nginx/course-auth

아직 활성 계정이 없다면 7개 파일이 크기 0으로 생성되고 과목 페이지는 401로 차단됩니다.

3

문법 검사 후 재시작

sudo nginx -t
sudo systemctl enable --now nginx
sudo systemctl reload nginx

syntax is ok, test is successful 문구가 보이면 정상입니다.

11HTTPS(SSL) 적용

비밀번호가 암호화되지 않은 채로 전달되지 않도록, 무료 인증서로 HTTPS를 적용합니다. (7단계에서 도메인을 만든 경우에만 가능합니다.)

1

Certbot 설치

sudo nice -n 19 ionice -c3 dnf \
  --setopt=ip_resolve=4 \
  --disablerepo='*' \
  --enablerepo=ol9_baseos_latest,ol9_appstream,ol9_developer_EPEL \
  install -y certbot python3-certbot-nginx
2

인증서 발급 및 자동 적용

sudo certbot --nginx -d dataclass.duckdns.org
sudo systemctl enable --now certbot-renew.timer
sudo certbot certificates

이메일 입력과 약관 동의를 거치면 인증서를 발급받아 Nginx에 적용합니다. 이후 https://dataclass.duckdns.org로 접속할 수 있습니다.

Let's Encrypt 인증서는 90일마다 만료되지만, Certbot이 설치 시 자동 갱신 작업을 함께 등록해 주므로 따로 신경 쓸 필요가 거의 없습니다.

12최종 접속 테스트

1

시크릿 모드에서 접속

브라우저 시크릿(비공개) 창을 열고 https://dataclass.duckdns.org로 접속합니다.

2

허브 페이지가 비밀번호 없이 뜨는지 확인

이번 설정에서는 허브 페이지(index.html) 자체는 비밀번호 없이 누구나 볼 수 있습니다. 아이디/비밀번호 창 없이 7개 과정 카드가 바로 보이면 정상입니다.

3

각 과정 폴더 클릭 테스트

SQL / ADSQL / SQLT / ADMIN / BRW / PTW / AI 카드를 하나씩 클릭해봅니다. 이때마다 아이디/비밀번호 창이 뜹니다. 9단계에서 만든 그 과정의 계정으로만 로그인이 되는지, 다른 과정 계정으로 로그인을 시도하면 실패하는지 확인합니다. 로그인 후 각 페이지와 이미지가 정상적으로 열리는지도 함께 확인합니다.

유지보수: 자료 추가/교체하기

이제부터는 새 자료가 생기면 아래 두 줄만 반복하면 됩니다.

1

내 컴퓨터에서 서버로 전송

scp -i ssh-key-오늘날짜.key ./새파일.html opc@서버IP:/home/opc/
2

웹서버 폴더로 옮기고 권한 정리 (서버 안에서)

sudo mv /home/opc/새파일.html /var/www/class-materials/SQL/
sudo chown root:nginx /var/www/class-materials/SQL/새파일.html
sudo chmod 644 /var/www/class-materials/SQL/새파일.html
sudo restorecon -F /var/www/class-materials/SQL/새파일.html

위 예시는 SQL 과정에 새 파일을 추가하는 경우입니다. 다른 과정에 추가하려면 SQL/ 자리를 해당 과정 폴더명(ADSQL/, SQLT/, ADMIN/, BRW/, PTW/, AI/ 중 하나)으로 바꿔 주세요. Nginx는 재시작할 필요 없이 파일이 바뀌면 즉시 반영됩니다. 목차 페이지(각 과정의 index.html)에 새 카드를 추가하는 것도 같은 방식으로 파일만 교체하면 됩니다.

운영·보안·백업 권장사항

1

중요 설정 백업

sudo tar czf /home/opc/class-materials-backup-$(date +%Y%m%d).tar.gz \
  /var/www/class-materials \
  /etc/nginx/conf.d/class-materials.conf \
  /etc/nginx/course-access.db \
  /root/create-course-access \
  /usr/local/sbin/sync-course-access \
  /etc/systemd/system/course-access-sync.service \
  /etc/systemd/system/course-access-sync.timer

생성된 백업 파일은 scp로 관리자 PC에 내려받고 서버 외부에도 보관합니다. Let’s Encrypt 개인키는 일반 자료 백업과 분리해 보호하세요.

2

상태와 로그 확인

systemctl is-active nginx course-access-sync.timer certbot-renew.timer
sudo nginx -t
sudo journalctl -u course-access-sync.service --since today
sudo tail -n 100 /var/log/nginx/error.log
sudo certbot certificates
3

계정 정보와 수동 동기화

sudo cat /etc/nginx/course-access.db
sudo /usr/local/sbin/sync-course-access
systemctl list-timers course-access-sync.timer certbot-renew.timer

DB에는 과정, 시작 epoch, 만료 epoch, 아이디, bcrypt 해시만 저장됩니다. 비밀번호 원문은 복구할 수 없으므로 잊었다면 같은 과정/아이디로 다시 등록해 교체합니다.

4

Public IP와 비용 보호

Ephemeral Public IP는 Stop/Start 뒤 바뀔 수 있습니다. IP가 바뀌면 DuckDNS에서 새 IP로 update하거나 OCI Reserved Public IP 사용을 검토하세요. 예산 알림을 설정하고 부트 볼륨·백업·트래픽 등 무료 한도 밖 자원이 생기지 않았는지 정기적으로 확인합니다.

!
Basic 인증 계정은 반드시 HTTPS로만 전달하세요. 반 공용 계정은 개인별 사용자를 구분하거나 특정 학생만 취소하기 어렵습니다. 더 강한 통제가 필요하면 학생별 아이디를 각각 생성하고 Nginx 접근 로그의 개인정보 보관 정책도 함께 정하세요.

문제 해결 표

증상원인 / 해결 방법
브라우저에서 사이트가 전혀 안 열림3단계 VCN Security List와 5단계 firewalld를 모두 확인합니다. sudo ss -lntp로 Nginx가 80/443에서 수신 중인지도 확인합니다.
SSH 접속 시 "Permission denied"키 파일 권한과 사용자 이름 opc를 확인합니다. Windows에서 키 권한이 너무 넓다는 경고가 나면 개인키 ACL을 현재 사용자 전용으로 제한합니다.
비밀번호 창이 안 뜨고 그냥 파일이 보임/etc/nginx/conf.d/class-materials.conf의 location 블록과 인증 파일 경로를 확인한 뒤 sudo nginx -t && sudo systemctl reload nginx를 실행합니다.
특정 과정만 비밀번호 창이 안 뜸설정 파일에 그 과정의 location /과정코드/ 블록이 빠졌을 가능성이 큽니다. 10단계에서 7개 location 블록을 복사하다가 하나를 빼놓기 쉬우니 다시 확인하세요.
SQL 계정으로 ADSQL 폴더가 열림 (보안 사고)각 location이 서로 다른 /etc/nginx/course-auth/과정.htpasswd를 가리키는지 확인합니다. 문제 해결 전까지 Nginx 접근을 제한하고 해당 계정 비밀번호를 교체하세요.
Certbot 인증서 발급 실패도메인이 실제 서버 IP를 가리키는지(DuckDNS "update ip" 재실행), 80번 포트가 열려 있는지 확인.
이미지가 깨져서 안 보임업로드한 폴더 안에 images/ 폴더가 함께 들어갔는지, 파일명 대소문자가 HTML 코드와 일치하는지 확인.
dnf 설치 중 SSH가 매우 느려짐스왑을 확인하고, IPv4 및 필요한 저장소만 지정하며 nice -n 19 ionice -c3로 실행합니다. 설치 명령을 중복 실행하지 마세요.
시작일인데 계정이 활성화되지 않음systemctl status course-access-sync.timer와 service 로그를 확인하고 sudo /usr/local/sbin/sync-course-access를 수동 실행합니다.
Stop/Start 후 도메인이 안 열림Public IP가 바뀌었는지 확인하고 DuckDNS의 IP를 갱신합니다.

최종 체크리스트