์๋ธ๋๋ฉ์ธ ์ค์ (Subdomain Configuration)
๊ฐ์
์๋ธ๋๋ฉ์ธ์ด๋ ๋ฉ์ธ ๋๋ฉ์ธ ์์ ์ถ๊ฐ๋ก ๋ถ๋ ์ฃผ์ ์ฒด๊ณ๋ค. example.com์ด ๋ฉ์ธ ๋๋ฉ์ธ์ด๋ฉด, api.example.com, admin.example.com ๊ฐ์ ๊ฒ ์๋ธ๋๋ฉ์ธ์ด๋ค.
๋ง์น ์ํํธ ๊ฑด๋ฌผ์ฒ๋ผ ์๊ฐํด๋ณด์. example.com์ด ๊ฑด๋ฌผ ์ฃผ์๋ฉด, api.example.com์ "101ํธ", admin.example.com์ "202ํธ"๋ผ๊ณ ๋ณด๋ฉด ๋ผ. ๊ฐ์ ๊ฑด๋ฌผ์ด์ง๋ง ๊ฐ ํธ์ค๋ง๋ค ๋ค๋ฅธ ์ฉ๋๊ฐ ์๋ ๊ฑฐ์ผ!
์๋ธ๋๋ฉ์ธ์ ์ฐ๋ฉด:
โข
โข
โข
โข
์๋ธ๋๋ฉ์ธ ๊ตฌ์กฐ
graph TD
A["example.com<br/>(๋ฉ์ธ ๋๋ฉ์ธ)"] --> B["api.example.com<br/>(API ์๋ฒ)<br/>๐ Spring Boot"]
A --> C["admin.example.com<br/>(๊ด๋ฆฌ์ ํจ๋)<br/>โ๏ธ Admin UI"]
A --> D["cdn.example.com<br/>(์ ์ ์ฝํ
์ธ )<br/>๐ ์ด๋ฏธ์ง, ํ์ผ"]
A --> E["blog.example.com<br/>(๋ธ๋ก๊ทธ)<br/>๐ ๋ธ๋ก๊ทธ ์์ง"]
A --> F["mail.example.com<br/>(๋ฉ์ผ ์๋ฒ)<br/>๐ง ์ด๋ฉ์ผ"]
style A fill:#FFD700
style B fill:#87CEEB
style C fill:#90EE90
style D fill:#FFB366
style E fill:#DDA0DD
style F fill:#F08080Mermaid
๋ณต์ฌ
์๋ธ๋๋ฉ์ธ vs ๊ฒฝ๋ก
๊ตฌ๋ถ | ์๋ธ๋๋ฉ์ธ | ๊ฒฝ๋ก(Path) |
URL ํ์ | api.example.com | example.com/api |
์ฟ ํค ๊ณต์ | ||
SSL ์ธ์ฆ์ | ๊ฐ๊ฐ ํ์ OR ์์ผ๋์นด๋ | 1๊ฐ ์ธ์ฆ์ |
์ฑ๋ฅ | ||
DNS ์ค์ | ||
๋ณด์ ์ ์ฑ
| ||
์ฌ์ฉ ์ | ๋ง์ดํฌ๋ก์๋น์ค | ๋ชจ๋๋ฆฌ์ |
1๋จ๊ณ: DNS ๋ ์ฝ๋ ์ค์
๋ชจ๋ ์๋ธ๋๋ฉ์ธ์ด ๊ฐ์ ์๋ฒ IP๋ก ํฅํ๋๋ก ์ค์ ํด์ผ ํ๋ค.
graph LR
A["DNS ์ฟผ๋ฆฌ<br/>api.example.com?"] -->|"A ๋ ์ฝ๋"| B["203.0.113.10<br/>(Nginx ์๋ฒ)"]
C["DNS ์ฟผ๋ฆฌ<br/>admin.example.com?"] -->|"A ๋ ์ฝ๋"| B
D["DNS ์ฟผ๋ฆฌ<br/>cdn.example.com?"] -->|"A ๋ ์ฝ๋"| B
style B fill:#FFD700Mermaid
๋ณต์ฌ
ํธ์คํ ์ ์ฒด์์ ์ค์
ํธ์คํธ๋ช
| ํ์
| ๊ฐ |
example.com | A | 203.0.113.10 |
*.example.com | A | 203.0.113.10 |
์์ผ๋์นด๋ * ์๋ฏธ: *.example.com์ ๋ชจ๋ ์๋ธ๋๋ฉ์ธ(aaa.example.com, xyz.example.com ๋ฑ)์ด ๊ฐ์ IP๋ก ํฅํ๋ค๋ ๋ป์ด๋ค.
ํ์ธ ๋ฐฉ๋ฒ:
# ๋ฆฌ๋
์ค/๋งฅ
nslookup api.example.com
nslookup admin.example.com
# ๊ฒฐ๊ณผ ์์
Name: api.example.com
Address: 203.0.113.10
Bash
๋ณต์ฌ
2๋จ๊ณ: Nginx ์ค์
๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ค์
server {
listen 80;
server_name api.example.com; # ์ด ๋๋ฉ์ธ๋ง ์ฒ๋ฆฌ
location / {
proxy_pass <http://localhost:8080>; # Spring Boot
}
}
server {
listen 80;
server_name admin.example.com; # ๋ค๋ฅธ ๋๋ฉ์ธ
location / {
proxy_pass <http://localhost:3000>; # React Admin
}
}
Plain Text
๋ณต์ฌ
์ฌ๋ฌ ์๋ธ๋๋ฉ์ธ ํ ๋ฒ์ ์ฒ๋ฆฌ
server {
listen 80;
# ๋ชจ๋ ์๋ธ๋๋ฉ์ธ ์์ฉ
server_name ~^(?<subdomain>.+)\\.example\\.com$;
# ์๋ธ๋๋ฉ์ธ ์ด๋ฆ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ์ฒ๋ฆฌ
location / {
# subdomain ๋ณ์์ "api", "admin", "cdn" ๋ฑ์ด ๋ค์ด์ด
set $backend "<http://localhost:8000>";
if ($subdomain = "api") {
set $backend "<http://localhost:8080>";
}
if ($subdomain = "admin") {
set $backend "<http://localhost:3000>";
}
if ($subdomain = "cdn") {
set $backend "<http://localhost:8888>";
}
proxy_pass $backend;
}
}
Plain Text
๋ณต์ฌ
๊ถ์ฅ: ์๋ธ๋๋ฉ์ธ๋ณ ๊ฐ๋ณ ๋ธ๋ก (๊ฐ๋ ์ฑ ์ข์)
# ============================================
# API ์๋ฒ (Spring Boot)
# ============================================
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass <http://localhost:8080>;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# ============================================
# ๊ด๋ฆฌ์ ํจ๋ (React)
# ============================================
server {
listen 80;
server_name admin.example.com;
root /var/www/admin;
index index.html;
location / {
try_files $uri $uri/ /index.html; # SPA ๋ผ์ฐํ
}
}
# ============================================
# CDN (์ ์ ํ์ผ)
# ============================================
server {
listen 80;
server_name cdn.example.com;
root /var/www/cdn;
# ์ด๋ฏธ์ง, ๋์์ ์บ์ฑ
location ~* \\.(jpg|jpeg|png|gif|mp4|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# ============================================
# ๋ธ๋ก๊ทธ (๋ณ๋ ์์ง)
# ============================================
server {
listen 80;
server_name blog.example.com;
location / {
proxy_pass <http://localhost:9000>; # ๋ธ๋ก๊ทธ ์์ง
}
}
Plain Text
๋ณต์ฌ
HTTPS + SSL ์ธ์ฆ์ ์ค์
๋ฐฉ๋ฒ 1: ์์ผ๋์นด๋ ์ธ์ฆ์ (๊ฐ์ฅ ๊ฐ๋จ)
์ฅ์ : ๋ชจ๋ ์๋ธ๋๋ฉ์ธ ํ ๊ฐ ์ธ์ฆ์๋ก ์ปค๋ฒ
# ์์ผ๋์นด๋ ์ธ์ฆ์ ๋ฐ๊ธ
certbot certonly --standalone \\
-d example.com \\
-d "*.example.com"
Bash
๋ณต์ฌ
Nginx ์ค์ :
server {
listen 443 ssl http2;
server_name api.example.com admin.example.com cdn.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}
Plain Text
๋ณต์ฌ
๋ฐฉ๋ฒ 2: ๊ฐ๋ณ ์ธ์ฆ์
์ฅ์ : ๊ฐ ์๋ธ๋๋ฉ์ธ๋ณ ์ ์ฑ
๋
๋ฆฝ ๊ด๋ฆฌ
certbot certonly --standalone -d api.example.com
certbot certonly --standalone -d admin.example.com
Bash
๋ณต์ฌ
Nginx ์ค์ :
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
}
server {
listen 443 ssl http2;
server_name admin.example.com;
ssl_certificate /etc/letsencrypt/live/admin.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/admin.example.com/privkey.pem;
}
Plain Text
๋ณต์ฌ
HTTP โ HTTPS ๋ฆฌ๋ค์ด๋ ํธ
server {
listen 80;
server_name api.example.com;
# ๋ชจ๋ HTTP ์์ฒญ์ HTTPS๋ก ๋ฆฌ๋ค์ด๋ ํธ
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.example.com;
# ์ค์ ์ฒ๋ฆฌ
location / {
proxy_pass <http://localhost:8080>;
}
}
Plain Text
๋ณต์ฌ
์ค์ ์ฌ์ฉ ์๋๋ฆฌ์ค
sequenceDiagram
participant ์ฌ์ฉ์ as ๐ค ์ฌ์ฉ์
participant DNS as ๐ DNS<br/>nameserver
participant Nginx as ๐ฅ๏ธ Nginx
participant API as ๐ API<br/>8080
participant Admin as โ๏ธ Admin<br/>3000
์ฌ์ฉ์->>DNS: api.example.com์ IP?
DNS-->>์ฌ์ฉ์: 203.0.113.10
์ฌ์ฉ์->>Nginx: GET <https://api.example.com/users>
Note over Nginx: server_name ๋งค์นญ<br/>api.example.com ๋ธ๋ก ์ ํ
Nginx->>API: ์์ฒญ ์ ๋ฌ
API-->>Nginx: ์๋ต
Nginx-->>์ฌ์ฉ์: ์๋ต
์ฌ์ฉ์->>DNS: admin.example.com์ IP?
DNS-->>์ฌ์ฉ์: 203.0.113.10 (๊ฐ์ ์๋ฒ!)
์ฌ์ฉ์->>Nginx: GET <https://admin.example.com>
Note over Nginx: server_name ๋งค์นญ<br/>admin.example.com ๋ธ๋ก ์ ํ
Nginx->>Admin: ์์ฒญ ์ ๋ฌ
Admin-->>Nginx: ์๋ต
Nginx-->>์ฌ์ฉ์: ์๋ตMermaid
๋ณต์ฌ
์ฟ ํค์ ์ธ์
๊ด๋ฆฌ
์ค์ํ ์ : ์๋ธ๋๋ฉ์ธ์ ์ฟ ํค๊ฐ ๊ณต์ ๋์ง ์๋๋ค
graph LR
A["api.example.com<br/>(๋ก๊ทธ์ธ)"] -->|"Set-Cookie:<br/>sessionId=123"|B["๋ธ๋ผ์ฐ์ ์ ์ฅ์"]
B -->|"Cookie: sessionId=123<br/>(?)"|C["admin.example.com<br/>(๊ด๋ฆฌ์ ํจ๋)"]
D["โ ์ฟ ํค ๋ฏธ์ ๋ฌ"] -.-> C
style D fill:#FFB6C1Mermaid
๋ณต์ฌ
์ฟ ํค ๊ณต์ ๋ฐฉ๋ฒ
# Spring Boot ์ค์
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass <http://localhost:8080>;
# Set-Cookie ์๋ต์์ ๋๋ฉ์ธ ๋ช
์
proxy_cookie_path / /;
proxy_cookie_domain localhost .example.com; # ๐ ๋ชจ๋ ์๋ธ๋๋ฉ์ธ์์ ์ฌ์ฉ
}
}
Plain Text
๋ณต์ฌ
๋ฐฑ์๋ ์ฝ๋ (Spring Boot)
// application.properties
server.servlet.session.cookie.domain=.example.com # ๋ชจ๋ ์๋ธ๋๋ฉ์ธ์์ ์ ๊ทผ ๊ฐ๋ฅ
server.servlet.session.cookie.secure=true # HTTPS๋ง
server.servlet.session.cookie.http-only=true # JS ์ ๊ทผ ์ฐจ๋จ
server.servlet.session.cookie.same-site=Lax # CSRF ๋ฐฉ์ง
Java
๋ณต์ฌ
์ฑ๋ฅ ์ต์ ํ
ํญ๋ชฉ | ์ค์ | ํจ๊ณผ |
DNS ์บ์ฑ | TTL = 3600์ด | DNS ์กฐํ ๊ฐ์ |
Keep-Alive | Connection: keep-alive | ์ฐ๊ฒฐ ์ฌ์ฌ์ฉ |
CDN ์๋ธ๋๋ฉ์ธ | cdn.example.com | ๋ธ๋ผ์ฐ์ ๋ณ๋ ฌ ๋ค์ด๋ก๋ |
HTTP/2 Server Push | HTTPS + HTTP/2 | ์ ์ ์ ๋ฆฌ์์ค ์ ์ก |
์์ถ | gzip on | ์ ์ก ํฌ๊ธฐ ์ถ์ |
server {
listen 443 ssl http2;
server_name ~^(?<subdomain>.+)\\.example\\.com$ example.com;
# HTTP/2 ํ์ฑํ๋จ (listen 443 ssl http2)
# ์ ์ก ์์ถ
gzip on;
gzip_types application/javascript text/css application/json;
# Keep-Alive
keepalive_timeout 65;
keepalive_requests 100;
}
Plain Text
๋ณต์ฌ
์ฒดํฌ๋ฆฌ์คํธ
DNS A ๋ ์ฝ๋์ ์์ผ๋์นด๋(.example.com) ์ถ๊ฐํ๋?
Nginx ์ค์ ํ์ผ์์ server_name ์ ํํ ์
๋ ฅํ๋?
nginx -t๋ก ์ค์ ํ
์คํธ ์๋ฃ?
systemctl reload nginx ๋๋ systemctl restart nginx ์คํ?
๊ฐ ์๋ธ๋๋ฉ์ธ์ผ๋ก ์ ์ ํ
์คํธ ์๋ฃ? (curl -H "Host: api.example.com" <http://localhost>)
HTTPS ์ธ์ฆ์ ๋ฐ๊ธ ์๋ฃ? (์์ผ๋์นด๋ ๋๋ ๊ฐ๋ณ)
SSL ๋ฆฌ๋ค์ด๋ ํธ ์ค์ ์๋ฃ?
์๋ธ๋๋ฉ์ธ๋ณ ๋ก๊ทธ ํ์ธ(tail -f /var/log/nginx/access.log)?
์ฟ ํค/์ธ์
๊ณต์ ํ์์ ๋๋ฉ์ธ ์ค์ ์๋ฃ?
์๋ ๊ฐฑ์ (certbot renew) ๋ฑ๋ก ์๋ฃ?
ํต์ฌ ์ ๋ฆฌ
์๋ธ๋๋ฉ์ธ = sub.example.com ํํ๋ก ํ๋์ ๋๋ฉ์ธ์ผ๋ก ์ฌ๋ฌ ์๋น์ค ์ด์
DNS ์ค์ = ์์ผ๋์นด๋(*.example.com)๋ก ๋ชจ๋ ์๋ธ๋๋ฉ์ธ์ด ๊ฐ์ IP ํฅํ๊ฒ
Nginx server_name = ๋๋ฉ์ธ๋ณ๋ก ๋ค๋ฅธ ๋ธ๋ก ๋ง๋ค์ด ์ญํ ๋ถ๋ด
SSL ์ธ์ฆ์ = ์์ผ๋์นด๋ 1๊ฐ ๋๋ ๊ฐ๋ณ ๋ค์ค ์ธ์ฆ์ ์ ํ
์ฟ ํค ๋ฏธ๊ณต์ = ์๋ธ๋๋ฉ์ธ์ ์ฟ ํค/์ธ์
๋ถ๋ฆฌ (๊ณต์ ์ํ๋ฉด .example.com ์ค์ )
์ฅ์ = ์๋ฒ ๋ถ์ฐ, URL ๊น๋, ๋ณด์ ๊ฐํ, ์บ์ ๋
๋ฆฝ



