반응형

✅ 가비아 DNS 그대로 사용하는 방식

장점

  • 설정이 간단: EC2 퍼블릭 IP만 입력하면 끝
  • 추가 비용 없음: Route 53을 쓰지 않으니 AWS 요금 없음
  • 관리 분산: 도메인 만료/갱신은 가비아에서, 서버 관리는 AWS에서

단점

  • DNS 기능 제한적: 가비아 DNS는 단순 A/CNAME 정도만 제공 → 세밀한 트래픽 라우팅, 복잡한 레코드 관리 불편
  • 전파 속도 조금 느림: 가비아 네임서버 특성상 DNS 전파가 AWS보다 느릴 수 있음
  • AWS 서비스 연계 어려움: CloudFront, ALB, S3 웹호스팅 등을 붙일 때 Route 53보다 설정이 번거로움

✅ Route 53 사용하는 방식

장점

  • AWS 서비스와 강력한 연동:
    • CloudFront, ALB(로드밸런서), S3 정적 웹호스팅 등 쉽게 연결
    • 헬스체크, 지리적 라우팅(서울은 서울 서버, 미국은 미국 서버 등) 가능
  • 빠른 DNS 응답 속도: AWS 글로벌 인프라 기반으로 성능 우수
  • 고급 기능 제공: 가중치 라우팅, 페일오버, 지연시간 기반 라우팅 등 엔터프라이즈 기능

단점

  • 추가 비용 발생:
    • 호스팅 존 유지비 약 $0.50 / 월
    • 쿼리당 소량 비용 (트래픽 많을 때만 체감)
  • 초기 설정 복잡: 가비아에서 네임서버 변경해야 함
  • 관리 이원화: 도메인 자체는 가비아에서 갱신, DNS는 AWS에서 관리

👉 정리

  • 개인 블로그/소규모 프로젝트 → 가비아 DNS 직접 사용 (간단, 무료)
  • 서비스 규모가 크거나 AWS 서비스 적극 활용 예정 → Route 53 권장 (확장성, 성능, 유연성)
반응형
반응형

서버 디스크 공간 문제를 해결하기 위해 Docker 관련 정리 명령

  해결 방법:

  1. 사용하지 않는 Docker 이미지 정리:
  docker system prune -a --volumes

  2. Docker 빌드 캐시 정리:
  docker builder prune -a

  3. 디스크 사용량 확인:
  df -h
  du -sh /var/lib/docker/*

  4. APT 캐시 정리:
  sudo apt-get clean
  sudo apt-get autoremove

  5. 로그 파일 정리:
  sudo journalctl --vacuum-time=3d

  이런 정리 작업 후에 Docker 빌드를 다시 시도해보시기 바랍니다.

반응형
반응형

N+1 문제와 1+N 문제는 사실상 같은 문제를 다르게 표현한 것입니다.

데이터베이스 성능 최적화에서 가장 흔히 발생하는 문제 중 하나입니다.

 

N+1 문제란?

하나의 메인 쿼리 + N개의 추가 쿼리가 실행되는 문제입니다.

구체적인 예시

시나리오: 사용자 10명과 각자의 게시글을 조회

 
 
sql
-- 1. 첫 번째 쿼리: 사용자 목록 조회
SELECT * FROM users LIMIT 10;

-- 2. 각 사용자마다 개별 쿼리 실행 (N번)
SELECT * FROM posts WHERE user_id = 1;
SELECT * FROM posts WHERE user_id = 2;
SELECT * FROM posts WHERE user_id = 3;
...
SELECT * FROM posts WHERE user_id = 10;

결과: 총 11개의 쿼리 (1 + 10)

코드에서 발생하는 예시

 
 
python
# 문제가 있는 코드
users = User.objects.all()[:10]  # 1번째 쿼리

for user in users:
    posts = user.posts.all()     # 각 사용자마다 추가 쿼리
    print(f"{user.name}: {len(posts)} posts")
 
 
javascript
// JavaScript/Node.js 예시
const users = await User.findAll({ limit: 10 });  // 1번째 쿼리

for (const user of users) {
    const posts = await Post.findAll({             // N번의 추가 쿼리
        where: { userId: user.id }
    });
    console.log(`${user.name}: ${posts.length} posts`);
}

성능에 미치는 영향

 
 
사용자 10명: 11개 쿼리
사용자 100명: 101개 쿼리  
사용자 1000명: 1001개 쿼리

데이터가 증가할수록 쿼리 수가 선형적으로 증가하여 성능이 급격히 저하됩니다.

해결 방법들

1. JOIN 사용 (가장 기본적)

 
 
sql
-- 하나의 쿼리로 해결
SELECT u.*, p.* 
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
WHERE u.id <= 10;

2. Eager Loading

 
 
python
# Django ORM
users = User.objects.prefetch_related('posts')[:10]

# SQLAlchemy
users = session.query(User).options(joinedload(User.posts)).limit(10)
 
 
javascript
// Sequelize
const users = await User.findAll({
    limit: 10,
    include: [{ model: Post, as: 'posts' }]
});

3. 배치 조회

 
 
python
# 두 번의 쿼리로 해결
users = User.objects.all()[:10]
user_ids = [user.id for user in users]
posts = Post.objects.filter(user_id__in=user_ids)

# 메모리에서 매핑
posts_by_user = {}
for post in posts:
    if post.user_id not in posts_by_user:
        posts_by_user[post.user_id] = []
    posts_by_user[post.user_id].append(post)

4. DataLoader 패턴 (GraphQL에서 주로 사용)

 
 
javascript
const DataLoader = require('dataloader');

const postLoader = new DataLoader(async (userIds) => {
    const posts = await Post.findAll({
        where: { userId: userIds }
    });
    
    // userIds 순서대로 정렬하여 반환
    return userIds.map(id => 
        posts.filter(post => post.userId === id)
    );
});

1+N과 N+1의 차이점

표현 방식의 차이일 뿐 본질적으로는 동일합니다:

  • N+1: N개의 데이터를 위해 1개의 메인 쿼리 + N개의 추가 쿼리
  • 1+N: 1개의 메인 쿼리 + N개의 추가 쿼리

둘 다 같은 문제를 설명하며, N+1이 더 일반적으로 사용되는 표현입니다.

실무에서 주의할 점

숨겨진 N+1 문제

 
 
python
# 언뜻 문제없어 보이지만...
users = User.objects.select_related('profile').all()

for user in users:
    # 여기서 추가 쿼리 발생!
    recent_posts = user.posts.filter(created_at__gte=last_week)

ORM의 지연 로딩

대부분의 ORM은 기본적으로 지연 로딩(Lazy Loading)을 사용하므로, 의도치 않게 N+1 문제가 발생할 수 있습니다.

N+1 문제는 애플리케이션 성능에 치명적인 영향을 미칠 수 있으므로, 개발 단계에서부터 쿼리 최적화를 고려하고 데이터베이스 쿼리 로그를 모니터링하는 것이 중요합니다.

반응형
반응형

docker-compose

docker-compose -f docker-compose.prod.yml down 
docker-compose -f docker-compose.prod.yml build --no-cache
docker-compose -f docker-compose.prod.yml up -d 

docker logs

docker logs name-nginx --tail 100 -f
docker logs name-fastapi --tail 100 -f

docker-compose down 후 up 만으로 수정사항 반영되는 경우는 어떤 경우이며, docker-compose build 까지 해야 하는 경우는 어떤 경우인가요? fastAPI, nginx를 사용중입니다.

Docker Compose에서 down/up만으로 충분한 경우와 build가 필요한 경우를 구분하는 핵심은 이미지 재빌드 필요 여부입니다.

down/up 만으로 충분한 경우
설정 파일 변경
docker-compose.yml 수정
docker-compose.prod.yml 수정
환경변수 변경 (.env 파일)
포트 매핑 변경
볼륨 마운트 경로 변경
네트워크 설정 변경
볼륨 마운트된 파일 수정
yaml

이런 설정에서 호스트 파일 수정

volumes:

  • ./nginx.conf:/etc/nginx/nginx.conf
  • ./templates:/app/templates
    호스트의 nginx.conf나 templates 폴더 내용을 수정하면 down/up만으로 반영됩니다.

외부 이미지 사용 시
yaml
services:
nginx:
image: nginx:latest # 외부 이미지 사용
build가 필요한 경우
소스 코드 변경
Python 파일 수정 (FastAPI 코드)
Dockerfile 수정
requirements.txt 변경
새로운 패키지 설치
이미지 내부 파일 변경
dockerfile

Dockerfile에서 COPY되는 파일들

COPY ./app /app
COPY requirements.txt .
RUN pip install -r requirements.txt
빌드 시점에 설치되는 요소들
apt 패키지 설치
pip 패키지 설치
이미지 내부 설정 파일 변경
실제 시나리오 예시
down/up으로 충분
bash

nginx 설정 파일 수정

vim nginx.conf

환경변수 변경

vim .env

포트 변경

vim docker-compose.yml

적용

docker-compose down
docker-compose up -d
build 필요
bash

FastAPI 코드 수정

vim app/main.py

새 패키지 추가

echo "requests==2.28.0" >> requirements.txt

Dockerfile 수정

vim Dockerfile

적용

docker-compose down
docker-compose build
docker-compose up -d
판단 기준
핵심 질문: "컨테이너 내부의 파일이 변경되었는가?"

YES → build 필요
NO → down/up만 충분
추가 고려사항:

볼륨 마운트된 파일은 호스트에서 직접 수정되므로 빌드 불필요
이미지 태그가 latest인 경우 docker-compose pull로 최신 이미지 가져올 수 있음
캐시된 레이어가 있어도 코드 변경 시에는 해당 레이어부터 재빌드됨
개발 효율성을 위해 가능한 한 볼륨 마운트를 활용하여 down/up만으로 변경사항을 반영할 수 있도록 설정하는 것이 좋습니다.

반응형
반응형

Nginx의 역할과 기능

  1. Reverse Proxy (리버스 프록시)

  [브라우저] → [Nginx:80] → [FastAPI:8000]

  역할:
  - 클라이언트의 요청을 FastAPI 서버로 전달
  - FastAPI 서버의 응답을 클라이언트로 전달
  - 내부 서버(FastAPI)를 외부에 직접 노출하지 않음

  설정 예시:
  upstream fastapi {
      server fastapi:8000;  # Docker 컨테이너 이름
  }

  location / {
      proxy_pass http://fastapi;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
  }

  2. Load Balancer (로드 밸런서)

  여러 FastAPI 인스턴스가 있을 때 요청을 분산:

  upstream fastapi {
      server fastapi1:8000;
      server fastapi2:8000;
      server fastapi3:8000;
  }

  3. Static File Server (정적 파일 서버)

  location /static/ {
      alias /usr/share/nginx/html/static/;
      expires 30d;
      add_header Cache-Control "public, immutable";
  }

  효과:
  - CSS, JS, 이미지 파일을 Nginx가 직접 서빙
  - FastAPI 서버 부하 감소
  - 빠른 정적 파일 전달

  4. Security Headers (보안 헤더)

  add_header X-Frame-Options "SAMEORIGIN" always;
  add_header X-Content-Type-Options "nosniff" always;
  add_header X-XSS-Protection "1; mode=block" always;
  add_header Referrer-Policy "strict-origin-when-cross-origin" always;

  보안 기능:
  - XSS 공격 방어
  - 클릭재킹 방어
  - MIME 타입 스니핑 방지

  5. Rate Limiting (속도 제한)

  # 존 정의
  limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
  limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m;

  # 적용
  location ~ ^/(login|signup|logout) {
      limit_req zone=auth burst=2 nodelay;  # 5회/분 제한
  }

  location / {
      limit_req zone=general burst=20 nodelay;  # 10회/초 제한
  }

  효과:
  - DDoS 공격 방어
  - 무차별 로그인 시도 방지
  - 서버 리소스 보호

  6. Compression (압축)

  gzip on;
  gzip_vary on;
  gzip_min_length 1024;
  gzip_types text/plain text/css text/xml text/javascript application/json;

  효과:
  - 네트워크 대역폭 절약
  - 페이지 로딩 속도 향상
  - 사용자 경험 개선

  7. SSL/TLS Termination (SSL 종료)

  server {
      listen 443 ssl http2;
      ssl_certificate /path/to/certificate.pem;
      ssl_certificate_key /path/to/private.key;

      location / {
          proxy_pass http://fastapi;  # 내부는 HTTP 통신
      }
  }

  역할:
  - HTTPS 암호화/복호화 처리
  - FastAPI는 HTTP만 처리하면 됨
  - SSL 인증서 관리

  8. Caching (캐싱)

  location /static/ {
      expires 30d;  # 30일 캐시
  }

  location /api/ {
      proxy_cache my_cache;
      proxy_cache_valid 200 1h;  # 1시간 캐시
  }

  9. Health Check & Failover

  upstream fastapi {
      server fastapi1:8000 max_fails=3 fail_timeout=30s;
      server fastapi2:8000 backup;  # 백업 서버
  }

  내 프로젝트에서의 Nginx 역할

  현재 설정 분석:

  1. 포트 80에서 요청 받음
  2. 정적 파일 (/static/) 직접 서빙
  3. 인증 엔드포인트 특별 관리 (Rate Limiting)
  4. 모든 요청을 FastAPI:8000으로 프록시
  5. 보안 헤더 자동 추가
  6. Gzip 압축 적용

  장점:

  - 성능: 정적 파일을 Nginx가 직접 처리
  - 보안: Rate Limiting, 보안 헤더
  - 확장성: 여러 FastAPI 인스턴스로 확장 가능
  - 운영: 로그 관리, 모니터링

  FastAPI만 사용했을 때와의 차이:

  FastAPI만:
  [브라우저] → [FastAPI:8000]

  Nginx + FastAPI:
  [브라우저] → [Nginx:80] → [FastAPI:8000]

  Nginx가 웹 서버의 역할을, FastAPI가 애플리케이션 서버의 역할을 각각 담당하여 웹을 통해 보안토큰이 전송될 필요없이 효율적인 구조를 가집니다.

반응형

+ Recent posts