◆완성될 모습 (아키텍처)
우리가 만들 시스템은 두 가지 흐름으로 이루어집니다 — ① 학생이 접속하는 흐름, ② 선생님(관리자)이 자료를 관리하는 흐름.
① 학생이 자료를 보는 흐름
② 선생님이 자료를 올리고 관리하는 흐름
◆용어 사전
본격적으로 시작하기 전에, 앞으로 계속 나올 용어들을 미리 짧게 정리해 두었습니다. 모르는 단어가 나오면 여기로 돌아와 확인하세요.
| 용어 | 설명 |
|---|---|
| OCI | Oracle Cloud Infrastructure. 오라클이 제공하는 클라우드 서비스. 일부 자원에 무료 사용 한도를 제공하며 조건은 시점과 리전에 따라 달라질 수 있습니다. |
| 인스턴스 (VM) | 클라우드에 만드는 가상 컴퓨터 한 대. 내 컴퓨터처럼 운영체제가 설치되어 있고 직접 접속해서 프로그램을 설치할 수 있습니다. |
| SSH | 내 컴퓨터에서 멀리 있는 서버에 터미널(명령어 창)로 접속하는 방법. 비밀번호 대신 "키(key)"라는 파일로 인증합니다. |
| VCN / Security List | OCI 안에서 서버로 들어오는 네트워크 통로를 관리하는 방화벽 설정. 이 설정에서 허용하지 않은 포트는 아무리 서버 안에서 열어도 외부에서 접속이 안 됩니다. |
| 포트 (Port) | 서버의 "문 번호"입니다. 웹사이트는 보통 80번(HTTP), 443번(HTTPS) 포트를 사용합니다. |
| Nginx (엔진엑스) | 웹서버 프로그램. 폴더 안의 HTML 파일들을 인터넷에 있는 사람들에게 보여주는 역할을 합니다. |
| .htpasswd | Nginx가 사용하는 "아이디/비밀번호 명단" 파일. 이 파일에 등록된 사람만 사이트에 들어갈 수 있게 만듭니다. |
| 도메인 (Domain) | 서버의 숫자 주소(IP) 대신 쓰는 기억하기 쉬운 이름. 예: class.example.com |
| DuckDNS | 무료로 도메인 이름을 하나 받을 수 있는 서비스. 우리는 이걸로 무료 도메인을 얻습니다. |
| HTTPS / SSL | 브라우저와 서버 사이 통신을 암호화하는 기술. 비밀번호가 안전하게 전달되도록 해줍니다. |
| Let's Encrypt / Certbot | HTTPS 인증서를 무료로 발급해주는 서비스와 그 발급 도구입니다. |
| sudo | "관리자 권한으로 실행"이라는 뜻의 명령어 접두사. 서버 설정을 바꿀 때 자주 사용합니다. |
◆사전 준비물
- 이메일 주소 (OCI 계정 가입용)
- 본인 인증용 신용/체크카드 (가입 확인용이며, Always Free 자원만 쓰면 요금이 청구되지 않습니다)
- 휴대폰 (본인 인증 문자 수신용)
- 노트북 또는 PC (터미널 사용 가능한 환경 — Windows/Mac 모두 가능)
- 지금까지 만든 수업 자료 HTML 파일들 (SQL / ADSQL / SQLT / ADMIN / BRW / PTW / AI 7개 과정 폴더로 정리된 것)
1OCI 계정 만들기
Oracle Cloud 가입 페이지 접속
oracle.com/cloud/free 에서 "Start for free" 버튼을 클릭합니다.
국가/지역을 South Korea로 설정
가입 화면에서 국가를 South Korea로 선택합니다. 이렇게 하면 나중에 서울 리전을 홈 리전으로 쓸 수 있습니다.
이메일 인증, 비밀번호 설정, 휴대폰 인증
화면에 나오는 순서대로 이메일 인증 코드, 계정 비밀번호, 휴대폰 문자 인증을 차례로 진행합니다.
홈 리전(Home Region) 선택
홈 리전을 고르라는 화면이 나오면 South Korea Central (Seoul)을 선택합니다.
결제 정보 입력 후 가입 완료
카드 정보를 입력하고 가입을 완료하면, OCI 콘솔(관리 화면)에 로그인할 수 있게 됩니다.
2컴퓨트 인스턴스 생성
이제 실제로 사용할 가상 서버(인스턴스)를 만듭니다.
Compute → Instances 메뉴 이동
OCI 콘솔 왼쪽 상단 메뉴(☰)에서 Compute → Instances로 들어가서 Create Instance를 클릭합니다.
이름 입력
Name 칸에 class-materials-server 처럼 알아보기 쉬운 이름을 입력합니다.
이미지(Image) 및 모양(Shape) 변경
"Image and shape" 영역에서 Edit를 눌러 다음과 같이 설정합니다.
| 항목 | 설정값 |
|---|---|
| Image | Oracle Linux 9 |
| Shape series | AMD 기반 또는 현재 리전에서 무료 사용 가능한 시리즈 |
| Shape | VM.Standard.E2.1.Micro — 생성 화면에서 Always Free eligible 여부 확인 |
| OCPU | 1 |
| Memory | 1 GB |
SSH 키 생성 및 다운로드
"Add SSH keys" 영역에서 Generate a key pair for me를 선택하고, Save private key 버튼으로 개인 키 파일(.key 확장자)을 다운로드합니다. 이 파일이 있어야 서버에 접속할 수 있으므로 잘 보관합니다.
생성(Create) 클릭
나머지는 기본값 그대로 두고 화면 맨 아래 Create를 클릭합니다. 1~2분 정도 지나면 인스턴스 상태가 RUNNING으로 바뀝니다.
Public IP 주소 확인
인스턴스 상세 화면에서 Public IP Address를 찾아 메모해 둡니다. 앞으로 이 주소로 서버에 접속합니다. (예: 132.145.xxx.xxx)
3방화벽 규칙 열기 (VCN Security List)
서버 안에 웹서버를 설치해도, OCI 네트워크 단계에서 문을 열어주지 않으면 외부에서 접속이 안 됩니다. 이 단계에서 80번(HTTP)과 443번(HTTPS) 포트를 열어줍니다.
서브넷의 Security List로 이동
인스턴스 상세 화면에서 Primary VNIC 아래 Subnet 이름을 클릭한 뒤, Security Lists → Default Security List를 클릭합니다.
Ingress Rule 추가 (포트 80)
Add Ingress Rules를 클릭하고 아래와 같이 입력합니다.
| 항목 | 설정값 |
|---|---|
| Source Type | CIDR |
| Source CIDR | 0.0.0.0/0 |
| IP Protocol | TCP |
| Destination Port Range | 80 |
Ingress Rule 추가 (포트 443)
같은 방식으로 규칙을 한 번 더 추가하되, Destination Port Range만 443으로 바꿔서 저장합니다. (SSH용 22번 포트는 기본값으로 이미 열려 있습니다.)
4SSH로 서버 접속
키 파일 권한 설정 (Mac / Linux)
터미널을 열고 다운로드한 키 파일이 있는 폴더로 이동한 뒤 아래 명령을 실행합니다.
# 키 파일 권한을 본인만 읽을 수 있게 제한
chmod 400 ssh-key-오늘날짜.key
서버 접속
ssh -i ssh-key-오늘날짜.key opc@서버의_Public_IP
처음 접속하면 "계속 연결하시겠습니까? (yes/no)" 라고 물어보는데 yes를 입력하고 엔터를 누릅니다.
ssh 명령이 그대로 작동합니다. 별도 프로그램 설치가 필요 없습니다.접속 성공 확인
프롬프트가 [opc@class-materials-server ~]$ 형태로 바뀌면 접속에 성공한 것입니다. 이제부터 나오는 모든 명령어는 이 화면(서버 안)에서 입력합니다.
5서버 내부 방화벽 설정
Oracle Linux 9는 서버 내부에서 firewalld를 사용합니다. OCI Security List와 서버 내부 방화벽을 모두 열어야 외부 접속이 가능합니다.
80번, 443번 포트 허용 규칙 추가
sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload
설정 영구 저장
sudo firewall-cmd --list-all
services에 http https ssh가 보이는지 확인합니다. --permanent로 추가했으므로 재부팅 후에도 유지됩니다.
6Nginx 웹서버 설치
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로 확인하세요.
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
nice/ionice로 우선순위를 낮추면 안정적입니다.설치 확인
sudo systemctl enable --now nginx sudo nginx -t
브라우저에서 http://서버의_Public_IP로 접속해 기본 화면이 보이면 정상입니다.
7무료 도메인 연결 (DuckDNS)
숫자로 된 IP 주소 대신 기억하기 쉬운 이름을 쓰고, 나중에 HTTPS(암호화)를 적용하려면 도메인이 하나 있어야 합니다. DuckDNS는 완전 무료로 서브도메인을 제공합니다. (이 단계는 선택이지만 강력히 추천합니다.)
DuckDNS 가입 및 도메인 생성
duckdns.org에 접속해 Google/GitHub 계정으로 로그인합니다. 먼저 주황색 reCaptcha 버튼의 인증을 완료해야 domain 영역이 나타납니다. dataclass만 입력하고 add domain을 누르면 dataclass.duckdns.org가 만들어집니다.
도메인에 서버 IP 연결
DuckDNS 페이지의 도메인 옆 IP 입력칸에 2단계에서 메모한 Public IP를 입력하고 update ip를 클릭합니다.
연결 확인
브라우저에서 http://dataclass.duckdns.org로 접속했을 때 Nginx 화면이 보이면 정상입니다. 등록 직후 이름을 찾지 못하면 잠시 기다리거나 Windows에서 ipconfig /flushdns를 실행합니다.
8수업 자료 파일 업로드
지금까지 정리한 7개 과정(SQL / ADSQL / SQLT / ADMIN / BRW / PTW / AI) 폴더 구조를 서버로 옮깁니다. 과정마다 폴더를 나누어 두어야 다음 단계에서 과정별로 다른 비밀번호를 걸 수 있습니다.
폴더 구조 최종 확인 (내 컴퓨터에서)
업로드하기 전에 내 컴퓨터의 자료 폴더가 아래처럼 정리되어 있는지 확인합니다. 7개 과정 폴더가 최상위에 나란히 있어야 합니다.
SQL/과 sql/은 서로 다른 폴더로 취급됩니다. 아래 단계들과 폴더명을 정확히 맞춰주세요.scp로 서버에 통째로 전송
내 컴퓨터의 터미널(서버 접속 전 화면)에서 아래 명령을 실행합니다. -i 뒤에는 SSH 키 파일 경로를, IP 자리에는 서버 주소를 넣습니다.
scp -i ssh-key-오늘날짜.key -r ./class-materials opc@서버의_Public_IP:/home/opc/
웹서버 폴더로 이동 (서버 안에서, 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입니다.
동기화 스크립트 작성
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
계정 생성 스크립트 작성
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
매분 자동 동기화 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
과정 계정 생성
sudo /root/create-course-access
과정 번호, 20260720 형식의 시작일·종료일, 아이디, 비밀번호와 비밀번호 확인을 입력합니다. 같은 과정/아이디를 다시 등록하면 기존 일정과 비밀번호가 교체됩니다.
10Nginx 설정 적용
이제 과정 폴더마다 알맞은 비밀번호 파일을 연결합니다. 허브 페이지(첫 화면)는 누구나 볼 수 있게 두고, 각 과정 폴더에 들어갈 때만 그 과정의 비밀번호를 요구하도록 설정합니다.
설정 파일 만들기
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+O → Enter (저장) → Ctrl+X (닫기)로 빠져나옵니다.
index.html)는 location /에만 걸려 있어서 비밀번호 없이 누구나 열립니다. 카드를 클릭해서 실제 과정 폴더(예: /SQL/)로 들어가는 순간부터 그 과정의 비밀번호를 요구합니다.인증 파일 초기화
sudo /usr/local/sbin/sync-course-access sudo ls -l /etc/nginx/course-auth
아직 활성 계정이 없다면 7개 파일이 크기 0으로 생성되고 과목 페이지는 401로 차단됩니다.
문법 검사 후 재시작
sudo nginx -t sudo systemctl enable --now nginx sudo systemctl reload nginx
syntax is ok, test is successful 문구가 보이면 정상입니다.
11HTTPS(SSL) 적용
비밀번호가 암호화되지 않은 채로 전달되지 않도록, 무료 인증서로 HTTPS를 적용합니다. (7단계에서 도메인을 만든 경우에만 가능합니다.)
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
인증서 발급 및 자동 적용
sudo certbot --nginx -d dataclass.duckdns.org sudo systemctl enable --now certbot-renew.timer sudo certbot certificates
이메일 입력과 약관 동의를 거치면 인증서를 발급받아 Nginx에 적용합니다. 이후 https://dataclass.duckdns.org로 접속할 수 있습니다.
12최종 접속 테스트
시크릿 모드에서 접속
브라우저 시크릿(비공개) 창을 열고 https://dataclass.duckdns.org로 접속합니다.
허브 페이지가 비밀번호 없이 뜨는지 확인
이번 설정에서는 허브 페이지(index.html) 자체는 비밀번호 없이 누구나 볼 수 있습니다. 아이디/비밀번호 창 없이 7개 과정 카드가 바로 보이면 정상입니다.
각 과정 폴더 클릭 테스트
SQL / ADSQL / SQLT / ADMIN / BRW / PTW / AI 카드를 하나씩 클릭해봅니다. 이때마다 아이디/비밀번호 창이 뜹니다. 9단계에서 만든 그 과정의 계정으로만 로그인이 되는지, 다른 과정 계정으로 로그인을 시도하면 실패하는지 확인합니다. 로그인 후 각 페이지와 이미지가 정상적으로 열리는지도 함께 확인합니다.
◆유지보수: 자료 추가/교체하기
이제부터는 새 자료가 생기면 아래 두 줄만 반복하면 됩니다.
내 컴퓨터에서 서버로 전송
scp -i ssh-key-오늘날짜.key ./새파일.html opc@서버IP:/home/opc/
웹서버 폴더로 옮기고 권한 정리 (서버 안에서)
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)에 새 카드를 추가하는 것도 같은 방식으로 파일만 교체하면 됩니다.
◆운영·보안·백업 권장사항
중요 설정 백업
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 개인키는 일반 자료 백업과 분리해 보호하세요.
상태와 로그 확인
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
계정 정보와 수동 동기화
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 해시만 저장됩니다. 비밀번호 원문은 복구할 수 없으므로 잊었다면 같은 과정/아이디로 다시 등록해 교체합니다.
Public IP와 비용 보호
Ephemeral Public IP는 Stop/Start 뒤 바뀔 수 있습니다. IP가 바뀌면 DuckDNS에서 새 IP로 update하거나 OCI Reserved Public IP 사용을 검토하세요. 예산 알림을 설정하고 부트 볼륨·백업·트래픽 등 무료 한도 밖 자원이 생기지 않았는지 정기적으로 확인합니다.
◆문제 해결 표
| 증상 | 원인 / 해결 방법 |
|---|---|
| 브라우저에서 사이트가 전혀 안 열림 | 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를 갱신합니다. |
◆최종 체크리스트
- OCI 계정 가입 완료, 홈 리전 = Seoul
- Oracle Linux 9 인스턴스 생성, 무료 사용 가능 여부와 예상 비용 확인
- SSH 키 파일 안전하게 백업
- VCN Security List에서 80/443 포트 개방
- SSH로 서버 접속 성공
- 서버 내부 firewalld에서 http/https 서비스 영구 허용
- Nginx 설치 및 기본 화면 확인
- DuckDNS 도메인 생성 및 IP 연결 (선택)
- SQL / ADSQL / SQLT / ADMIN / BRW / PTW / AI 7개 과정 폴더 업로드 및 권한 설정
- create-course-access와 sync-course-access 설치, 기간제 계정 생성 테스트
- course-access-sync.timer 활성화 및 자동 만료 테스트
- Nginx 설정 파일에 7개 과정별 location 블록 작성 및 활성화
- Certbot으로 HTTPS 적용 (선택이지만 추천)
- 시크릿 창에서 허브 페이지(비밀번호 없이 접속) 및 각 과정 폴더가 다른 계정으로만 열리는지 테스트