Search

์„œ๋ธŒ ๋„๋ฉ”์ธ

์„œ๋ธŒ๋„๋ฉ”์ธ ์„ค์ • (Subdomain Configuration)

๊ฐœ์š”

์„œ๋ธŒ๋„๋ฉ”์ธ์ด๋ž€ ๋ฉ”์ธ ๋„๋ฉ”์ธ ์•ž์— ์ถ”๊ฐ€๋กœ ๋ถ™๋Š” ์ฃผ์†Œ ์ฒด๊ณ„๋‹ค. example.com์ด ๋ฉ”์ธ ๋„๋ฉ”์ธ์ด๋ฉด, api.example.com, admin.example.com ๊ฐ™์€ ๊ฒŒ ์„œ๋ธŒ๋„๋ฉ”์ธ์ด๋‹ค.
๋งˆ์น˜ ์•„ํŒŒํŠธ ๊ฑด๋ฌผ์ฒ˜๋Ÿผ ์ƒ๊ฐํ•ด๋ณด์ž. example.com์ด ๊ฑด๋ฌผ ์ฃผ์†Œ๋ฉด, api.example.com์€ "101ํ˜ธ", admin.example.com์€ "202ํ˜ธ"๋ผ๊ณ  ๋ณด๋ฉด ๋ผ. ๊ฐ™์€ ๊ฑด๋ฌผ์ด์ง€๋งŒ ๊ฐ ํ˜ธ์‹ค๋งˆ๋‹ค ๋‹ค๋ฅธ ์šฉ๋„๊ฐ€ ์žˆ๋Š” ๊ฑฐ์•ผ!
์„œ๋ธŒ๋„๋ฉ”์ธ์„ ์“ฐ๋ฉด:
โ€ข
ํ•˜๋‚˜์˜ ๋ฉ”์ธ ๋„๋ฉ”์ธ์œผ๋กœ ์—ฌ๋Ÿฌ ์„œ๋น„์Šค ๊ตฌ๋ถ„
โ€ข
ํฌํŠธ๋ฒˆํ˜ธ ์—†์ด ๊น”๋”ํ•œ URL
โ€ข
SSL ์ธ์ฆ์„œ ๊ณต์œ  ๊ฐ€๋Šฅ (์™€์ผ๋“œ์นด๋“œ)
โ€ข
์บ์‹œ ๋…๋ฆฝ ๊ด€๋ฆฌ (์ฟ ํ‚ค, ์„ธ์…˜ ๋ถ„๋ฆฌ)

์„œ๋ธŒ๋„๋ฉ”์ธ ๊ตฌ์กฐ

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:#F08080
Mermaid
๋ณต์‚ฌ

์„œ๋ธŒ๋„๋ฉ”์ธ 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:#FFD700
Mermaid
๋ณต์‚ฌ

ํ˜ธ์ŠคํŒ… ์—…์ฒด์—์„œ ์„ค์ •

ํ˜ธ์ŠคํŠธ๋ช…
ํƒ€์ž…
๊ฐ’
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:#FFB6C1
Mermaid
๋ณต์‚ฌ

์ฟ ํ‚ค ๊ณต์œ  ๋ฐฉ๋ฒ•

# 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 ๊น”๋”, ๋ณด์•ˆ ๊ฐ•ํ™”, ์บ์‹œ ๋…๋ฆฝ