백엔드 개발자를 위한 서버 가이드

2025. 12. 4. 15:53·AI가 만들어준 가이드

1. 서버의 기본 개념

1.1 서버란?

서버 = 요청을 받아서 처리하고 응답하는 컴퓨터

[클라이언트] ──요청──> [서버] ──응답──> [클라이언트]

예시:
브라우저(클라이언트) ──"상품 목록 보여줘"──> 
                         API 서버 
                    ──"여기 데이터야"──> 
                         브라우저

1.2 서버의 종류

물리적 분류:

1. 온프레미스 (On-Premise)
   └─ 회사 건물에 직접 서버 컴퓨터 설치
   └─ 예: 삼성, 네이버 같은 대기업

2. 클라우드 (Cloud)
   └─ 다른 회사(AWS, GCP 등)의 서버를 빌려 쓰기
   └─ 예: 대부분의 스타트업

역할별 분류:

1. 웹 서버 (Web Server)
   └─ Nginx, Apache
   └─ 정적 파일(HTML, CSS, JS) 제공
   └─ 리버스 프록시 역할

2. 애플리케이션 서버 (WAS)
   └─ Spring Boot, Node.js, Django
   └─ 비즈니스 로직 처리

3. 데이터베이스 서버
   └─ MySQL, PostgreSQL, MongoDB
   └─ 데이터 저장/조회

4. 캐시 서버
   └─ Redis, Memcached
   └─ 빠른 데이터 임시 저장

5. 파일 서버
   └─ AWS S3, NAS
   └─ 이미지, 동영상 등 저장

1.3 서버 스펙 이해하기

서버 스펙 예시:
├─ CPU: 4 Core (vCPU)
│   └─ 동시 처리 능력
│
├─ Memory: 8GB RAM
│   └─ 프로그램 실행 공간
│
├─ Storage: 100GB SSD
│   └─ 데이터 저장 공간
│
└─ Network: 1Gbps
    └─ 네트워크 속도

선택 기준:

개발/테스트 서버:
└─ t3.small (2 vCPU, 2GB RAM) - 월 $15

소규모 운영:
└─ t3.medium (2 vCPU, 4GB RAM) - 월 $30

중규모 운영:
└─ t3.large (2 vCPU, 8GB RAM) - 월 $60

대규모:
└─ c5.xlarge (4 vCPU, 8GB RAM) - 월 $120+

2. 네트워크 기초

2.1 IP 주소

IP = 인터넷 상의 주소

IPv4: 192.168.1.10
└─ 4개 숫자 (0~255)
└─ 총 43억개 (부족해서 IPv6로 전환 중)

Public IP (공인 IP):
└─ 52.79.123.45
└─ 인터넷에서 접근 가능
└─ 돈 내고 할당받음

Private IP (사설 IP):
└─ 10.0.1.10
└─ 내부 네트워크에서만 사용
└─ 공짜

2.2 포트 (Port)

포트 = 서비스 구분 번호

IP 주소 = 아파트 주소
포트 = 동/호수

예시:
192.168.1.10:8080
└─ IP: 192.168.1.10
└─ 포트: 8080

주요 포트:

22    - SSH (서버 접속)
80    - HTTP (웹)
443   - HTTPS (보안 웹)
3306  - MySQL
5432  - PostgreSQL
6379  - Redis
8080  - Spring Boot (개발)
8081  - API 서버
3000  - React, Node.js

2.3 도메인과 DNS

도메인 → DNS → IP 주소

example.com
    ↓ (DNS 조회)
52.79.123.45
    ↓
EC2 서버

설정 예시:

도메인 구매: example.com (가비아, Route53)
DNS 설정:
├─ example.com → 52.79.123.45
├─ api.example.com → 52.79.123.45
└─ admin.example.com → 52.79.123.45

2.4 CIDR 표기법

CIDR = IP 주소 범위 표현 방법

10.0.0.0/16
└─ 10.0.0.0 ~ 10.0.255.255 (65,536개)

10.0.1.0/24
└─ 10.0.1.0 ~ 10.0.1.255 (256개)

10.0.1.0/28
└─ 10.0.1.0 ~ 10.0.1.15 (16개)

계산법:
/16 = 32-16 = 16비트 → 2^16 = 65,536개
/24 = 32-24 = 8비트 → 2^8 = 256개
/28 = 32-28 = 4비트 → 2^4 = 16개

2.5 프로토콜

HTTP (Hypertext Transfer Protocol)
├─ 포트: 80
├─ 암호화: ❌
└─ 사용: 개발 환경

HTTPS (HTTP + SSL/TLS)
├─ 포트: 443
├─ 암호화: ✅
└─ 사용: 운영 환경 (필수!)

TCP (Transmission Control Protocol)
├─ 신뢰성: ✅ (데이터 순서 보장)
└─ 사용: HTTP, SSH, 데이터베이스

UDP (User Datagram Protocol)
├─ 신뢰성: ❌ (빠르지만 손실 가능)
└─ 사용: 동영상 스트리밍, 게임

3. Linux 서버 기본

3.1 왜 Linux인가?

서버 OS 점유율:
├─ Linux: 96%
│   ├─ Ubuntu (가장 인기)
│   ├─ CentOS
│   └─ Amazon Linux
│
├─ Windows Server: 3%
└─ 기타: 1%

Linux 장점:
✅ 무료
✅ 안정적
✅ 보안 강함
✅ 가벼움 (GUI 없음)
✅ 자동화 쉬움

3.2 필수 Linux 명령어

파일 & 디렉토리:

# 현재 위치 확인
pwd

# 파일 목록
ls -la

# 디렉토리 이동
cd /home/ubuntu

# 디렉토리 생성
mkdir myapp

# 파일 생성
touch file.txt

# 파일 내용 보기
cat file.txt
less file.txt    # 긴 파일 (스크롤 가능)

# 파일 복사
cp source.txt dest.txt

# 파일 이동/이름 변경
mv old.txt new.txt

# 파일 삭제
rm file.txt
rm -rf directory/  # 디렉토리 삭제 (조심!)

# 파일 편집
vi file.txt
nano file.txt    # 초보자 추천

프로세스 관리:

# 실행 중인 프로세스 확인
ps aux
ps aux | grep java

# 실시간 프로세스 모니터링
top
htop  # 더 보기 좋음 (설치 필요)

# 프로세스 종료
kill 1234          # PID로 종료
kill -9 1234       # 강제 종료
killall java       # 이름으로 종료

네트워크:

# 포트 확인
netstat -tulpn
ss -tulpn         # 최신 방식

# 특정 포트 사용 확인
lsof -i :8080

# 네트워크 연결 테스트
ping google.com
curl http://localhost:8080
wget http://example.com/file.zip

시스템 정보:

# 디스크 사용량
df -h

# 디렉토리 크기
du -sh /var/log

# 메모리 사용량
free -h

# CPU 정보
lscpu

# 시스템 정보
uname -a

로그 확인:

# 로그 실시간 확인
tail -f /var/log/application.log

# 최근 100줄
tail -n 100 app.log

# 로그 검색
grep "ERROR" app.log
grep -i "error" app.log  # 대소문자 무시

권한 관리:

# 권한 변경
chmod 755 script.sh    # rwxr-xr-x
chmod +x script.sh     # 실행 권한 추가

# 소유자 변경
chown ubuntu:ubuntu file.txt

# sudo (관리자 권한으로 실행)
sudo apt update

3.3 패키지 관리

Ubuntu (apt):

# 패키지 목록 업데이트
sudo apt update

# 패키지 설치
sudo apt install docker.io

# 패키지 삭제
sudo apt remove docker.io

# 시스템 업그레이드
sudo apt upgrade

CentOS (yum):

sudo yum update
sudo yum install docker

3.4 서비스 관리 (systemd)

# 서비스 시작
sudo systemctl start nginx

# 서비스 중지
sudo systemctl stop nginx

# 서비스 재시작
sudo systemctl restart nginx

# 서비스 상태 확인
sudo systemctl status nginx

# 부팅 시 자동 시작 설정
sudo systemctl enable nginx

# 자동 시작 해제
sudo systemctl disable nginx

# 로그 확인
sudo journalctl -u nginx -f

3.5 환경변수

# 환경변수 확인
echo $PATH
env

# 임시 설정 (현재 세션만)
export MY_VAR="hello"

# 영구 설정 (~/.bashrc 또는 ~/.profile)
echo 'export MY_VAR="hello"' >> ~/.bashrc
source ~/.bashrc

# Spring Boot 환경변수
export SPRING_PROFILES_ACTIVE=prd
export DB_PASSWORD=mypassword

4. 클라우드 (AWS) 기초

4.1 AWS 주요 서비스

컴퓨팅:
├─ EC2: 가상 서버
├─ Lambda: 서버리스 함수
└─ ECS/EKS: 컨테이너 오케스트레이션

스토리지:
├─ S3: 파일 저장소
├─ EBS: EC2용 디스크
└─ EFS: 공유 파일 시스템

데이터베이스:
├─ RDS: 관계형 DB (MySQL, PostgreSQL)
├─ DynamoDB: NoSQL
└─ ElastiCache: Redis, Memcached

네트워크:
├─ VPC: 가상 네트워크
├─ Route53: DNS
├─ CloudFront: CDN
└─ ELB: 로드 밸런서

보안:
├─ IAM: 권한 관리
├─ Security Group: 방화벽
└─ ACM: SSL 인증서

4.2 VPC (Virtual Private Cloud)

VPC = 내 전용 네트워크 공간

VPC: 10.0.0.0/16
│
├─ Public Subnet (10.0.1.0/24)
│  ├─ 인터넷 연결 ✅
│  └─ 웹서버, API 서버
│
├─ Private Subnet (10.0.2.0/24)
│  ├─ 인터넷 연결 ❌
│  └─ 데이터베이스, 캐시
│
├─ Internet Gateway
│  └─ VPC와 인터넷 연결
│
└─ NAT Gateway
   └─ Private 서브넷에서 외부 접속

실제 설정:

VPC 생성:
├─ 이름: my-vpc
├─ CIDR: 10.0.0.0/16
│
├─ Public Subnet:
│  ├─ CIDR: 10.0.1.0/24
│  ├─ AZ: ap-northeast-2a
│  └─ 용도: 웹/API 서버
│
└─ Private Subnet:
   ├─ CIDR: 10.0.2.0/24
   ├─ AZ: ap-northeast-2a
   └─ 용도: DB, Redis

4.3 EC2 (Elastic Compute Cloud)

EC2 = 가상 서버

인스턴스 타입:

t 시리즈 (범용, 가성비):
├─ t3.micro: 2 vCPU, 1GB - 무료 티어
├─ t3.small: 2 vCPU, 2GB - 개발/테스트
├─ t3.medium: 2 vCPU, 4GB - 소규모 운영
└─ t3.large: 2 vCPU, 8GB - 중규모 운영

c 시리즈 (CPU 최적화):
└─ c5.xlarge: 4 vCPU, 8GB - CPU 집약적

r 시리즈 (메모리 최적화):
└─ r5.large: 2 vCPU, 16GB - 캐시, 인메모리 DB

m 시리즈 (균형):
└─ m5.large: 2 vCPU, 8GB - 균형 잡힌 워크로드

EC2 생성 과정:

1. AMI 선택:
   └─ Ubuntu 24.04 LTS

2. 인스턴스 타입:
   └─ t3.small

3. 네트워크:
   ├─ VPC: my-vpc
   ├─ Subnet: Public Subnet
   └─ Public IP: 자동 할당

4. 스토리지:
   └─ 20GB gp3 (SSD)

5. Security Group (방화벽):
   ├─ SSH (22): 내 IP만
   ├─ HTTP (80): 0.0.0.0/0
   └─ HTTPS (443): 0.0.0.0/0

6. Key Pair (SSH 키):
   └─ my-key.pem 다운로드

SSH 접속:

# 키 권한 설정 (한 번만)
chmod 400 my-key.pem

# EC2 접속
ssh -i my-key.pem ubuntu@52.79.123.45

# 또는 퍼블릭 DNS 사용
ssh -i my-key.pem ubuntu@ec2-52-79-123-45.ap-northeast-2.compute.amazonaws.com

4.4 Security Group (보안 그룹)

Security Group = 방화벽 규칙

Inbound Rules (들어오는 트래픽):
┌──────────┬──────┬─────────────┬─────────────┐
│ Type     │ Port │ Source      │ 설명        │
├──────────┼──────┼─────────────┼─────────────┤
│ SSH      │ 22   │ 내IP/32     │ 내 PC만     │
│ HTTP     │ 80   │ 0.0.0.0/0   │ 모두 허용   │
│ HTTPS    │ 443  │ 0.0.0.0/0   │ 모두 허용   │
│ Custom   │ 8080 │ 10.0.0.0/16 │ VPC 내부만  │
└──────────┴──────┴─────────────┴─────────────┘

Outbound Rules (나가는 트래픽):
└─ All traffic: 0.0.0.0/0 (기본 전체 허용)

베스트 프랙티스:

✅ SSH는 내 IP만 허용
✅ 불필요한 포트는 닫기
✅ 애플리케이션 포트는 내부망만
✅ 주기적으로 규칙 검토

4.5 S3 (Simple Storage Service)

S3 = 파일 저장소 (무제한)

사용 사례:
├─ 이미지, 동영상 저장
├─ 정적 웹사이트 호스팅
├─ 백업 파일 저장
└─ 로그 아카이빙

특징:
✅ 용량 무제한
✅ 99.999999999% 내구성
✅ 저렴 (GB당 $0.023/월)
✅ CDN(CloudFront) 연동

Spring Boot에서 S3 사용:

@Service
public class S3Service {
    private final AmazonS3 s3Client;

    public String uploadFile(MultipartFile file) {
        String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();

        s3Client.putObject(
            "my-bucket",
            fileName,
            file.getInputStream(),
            new ObjectMetadata()
        );

        return s3Client.getUrl("my-bucket", fileName).toString();
    }
}

4.6 RDS (Relational Database Service)

RDS = 관리형 데이터베이스

지원 엔진:
├─ PostgreSQL
├─ MySQL
├─ MariaDB
├─ Oracle
└─ SQL Server

vs 직접 설치:
┌────────────────┬─────────────┬──────────────┐
│                │ EC2에 직접  │ RDS          │
├────────────────┼─────────────┼──────────────┤
│ 설치/설정      │ 직접        │ 자동         │
│ 백업           │ 직접        │ 자동         │
│ 업데이트       │ 직접        │ 자동         │
│ 모니터링       │ 직접 구축   │ 기본 제공    │
│ 장애 복구      │ 직접        │ 자동         │
│ 비용           │ 저렴        │ 약간 비쌈    │
└────────────────┴─────────────┴──────────────┘

추천:

  • 소규모/학습: EC2에 직접 설치
  • 운영 환경: RDS 사용

4.7 ElastiCache (Redis/Memcached)

ElastiCache = 관리형 캐시 서버

Redis vs Memcached:
┌──────────────┬─────────────┬──────────────┐
│              │ Redis       │ Memcached    │
├──────────────┼─────────────┼──────────────┤
│ 데이터 구조  │ 다양        │ Key-Value만  │
│ 영속성       │ 지원        │ 미지원       │
│ 복제         │ 지원        │ 미지원       │
│ 트랜잭션     │ 지원        │ 미지원       │
│ 속도         │ 빠름        │ 아주 빠름    │
└──────────────┴─────────────┴──────────────┘

추천: Redis (기능이 많음)

사용 사례:

✅ 세션 저장
✅ API 응답 캐싱
✅ 실시간 랭킹
✅ 분산 락
✅ 메시지 큐 (Redis Pub/Sub)

5. Docker & 컨테이너

5.1 Docker란?

Docker = 애플리케이션을 컨테이너로 패키징하는 도구

전통적 방식:
서버에 직접 설치
├─ Java 설치
├─ Node.js 설치
├─ PostgreSQL 설치
└─ 환경 설정...

문제점:
❌ 환경마다 설정 다름
❌ 버전 충돌
❌ 배포 어려움

Docker 방식:
컨테이너로 격리
├─ API 컨테이너 (Java + 앱)
├─ Admin 컨테이너 (Java + 앱)
├─ PostgreSQL 컨테이너
└─ Redis 컨테이너

장점:
✅ 어디서든 동일하게 동작
✅ 격리되어 충돌 없음
✅ 배포 간단
✅ 롤백 쉬움

5.2 Docker 핵심 개념

이미지 (Image):
└─ 애플리케이션 + 의존성을 묶은 "설계도"
└─ 예: ubuntu:24.04, nginx:1.25, postgres:15

컨테이너 (Container):
└─ 이미지를 실행한 "실제 프로세스"
└─ 예: 실행 중인 API 서버

Dockerfile:
└─ 이미지를 만드는 "레시피"

Docker Hub:
└─ 이미지 저장소 (GitHub 같은 것)

5.3 Docker 설치

# Ubuntu에서 Docker 설치
sudo apt update
sudo apt install docker.io docker-compose -y

# Docker 서비스 시작
sudo systemctl start docker
sudo systemctl enable docker

# 현재 사용자를 docker 그룹에 추가 (sudo 없이 사용)
sudo usermod -aG docker $USER

# 로그아웃 후 재로그인 필요
# 또는
newgrp docker

# 설치 확인
docker --version
docker-compose --version

5.4 Docker 명령어

이미지 관련:

# 이미지 다운로드
docker pull nginx:1.25

# 이미지 목록
docker images

# 이미지 빌드
docker build -t myapp:1.0 .

# 이미지 삭제
docker rmi myapp:1.0

# 사용하지 않는 이미지 삭제
docker image prune

컨테이너 관련:

# 컨테이너 실행
docker run -d -p 8080:8080 --name myapp myapp:1.0
# -d: 백그라운드 실행
# -p: 포트 매핑 (호스트:컨테이너)
# --name: 컨테이너 이름

# 실행 중인 컨테이너 목록
docker ps

# 모든 컨테이너 목록 (중지된 것 포함)
docker ps -a

# 컨테이너 중지
docker stop myapp

# 컨테이너 시작
docker start myapp

# 컨테이너 재시작
docker restart myapp

# 컨테이너 삭제
docker rm myapp

# 컨테이너 로그 확인
docker logs myapp
docker logs -f myapp  # 실시간

# 컨테이너 내부 접속
docker exec -it myapp /bin/bash

# 컨테이너 리소스 사용량
docker stats

5.5 Dockerfile 작성

Spring Boot 애플리케이션 예시:

# 1단계: 베이스 이미지
FROM openjdk:21-jdk-slim

# 2단계: 작업 디렉토리 설정
WORKDIR /app

# 3단계: JAR 파일 복사
COPY build/libs/app.jar app.jar

# 4단계: 포트 노출
EXPOSE 8080

# 5단계: 실행 명령
ENTRYPOINT ["java", "-jar", "app.jar"]

멀티 스테이지 빌드 (권장):

# 1단계: 빌드 스테이지
FROM gradle:8.5-jdk21 AS builder
WORKDIR /app
COPY . .
RUN ./gradlew bootJar -x test

# 2단계: 실행 스테이지
FROM openjdk:21-jdk-slim
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

이미지 빌드:

docker build -t myapp:1.0 .

5.6 Docker Compose

Docker Compose = 여러 컨테이너를 한 번에 관리

# docker-compose.yml
version: '3.8'

services:
  # PostgreSQL
  db:
    image: postgres:15
    container_name: myapp-db
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    ports:
      - "5432:5432"
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - myapp-network

  # Redis
  redis:
    image: redis:7-alpine
    container_name: myapp-redis
    ports:
      - "6379:6379"
    networks:
      - myapp-network

  # API 서버
  api:
    build: ./api
    container_name: myapp-api
    ports:
      - "8080:8080"
    depends_on:
      - db
      - redis
    environment:
      SPRING_PROFILES_ACTIVE: prd
      DB_HOST: db
      REDIS_HOST: redis
    networks:
      - myapp-network

  # Nginx
  nginx:
    image: nginx:1.25
    container_name: myapp-nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - api
    networks:
      - myapp-network

networks:
  myapp-network:
    driver: bridge

volumes:
  db-data:

Docker Compose 명령어:

# 모든 서비스 시작
docker-compose up -d

# 특정 서비스만 시작
docker-compose up -d api

# 서비스 중지
docker-compose stop

# 서비스 중지 및 삭제
docker-compose down

# 볼륨까지 삭제
docker-compose down -v

# 로그 확인
docker-compose logs -f

# 특정 서비스 로그
docker-compose logs -f api

# 서비스 재시작
docker-compose restart api

5.7 Docker 네트워크

Bridge 네트워크 (기본):
├─ 같은 네트워크의 컨테이너끼리 통신
└─ 컨테이너 이름으로 접근 가능

Host 네트워크:
└─ 호스트의 네트워크를 직접 사용

None 네트워크:
└─ 네트워크 없음 (격리)

컨테이너 간 통신:

# API 서버에서 PostgreSQL 접속
spring:
  datasource:
    url: jdbc:postgresql://db:5432/mydb
    #                      ↑ 컨테이너 이름으로 접근!

5.8 Docker 볼륨

볼륨 = 데이터 영구 저장

문제:
컨테이너 삭제 → 데이터도 삭제됨

해결:
볼륨 사용 → 호스트에 데이터 저장

볼륨 타입:

services:
  db:
    volumes:
      # Named Volume (추천)
      - db-data:/var/lib/postgresql/data

      # Bind Mount (호스트 경로 직접 지정)
      - /host/path:/container/path

      # Anonymous Volume
      - /var/lib/postgresql/data

volumes:
  db-data:  # Named Volume 선언

6. 배포 자동화 (CI/CD)

6.1 CI/CD란?

CI (Continuous Integration):
└─ 코드 변경 → 자동 빌드 → 자동 테스트

CD (Continuous Deployment):
└─ 빌드 성공 → 자동 배포

전체 흐름:
코드 푸시 → 빌드 → 테스트 → 배포 → 모니터링

6.2 GitLab CI/CD

.gitlab-ci.yml 기본 구조:

# 단계 정의
stages:
  - build
  - test
  - deploy

# 공통 설정
variables:
  GRADLE_OPTS: "-Dorg.gradle.daemon=false"

# 빌드 작업
build-job:
  stage: build
  image: gradle:8.5-jdk21
  script:
    - ./gradlew bootJar -x test
  artifacts:
    paths:
      - build/libs/*.jar
    expire_in: 1 hour

# 테스트 작업
test-job:
  stage: test
  image: gradle:8.5-jdk21
  script:
    - ./gradlew test
  coverage: '/Total.*?(\d+\.?\d*)%/'

# 배포 작업
deploy-job:
  stage: deploy
  tags:
    - my-runner  # 특정 Runner 지정
  script:
    - docker build -t myapp:latest .
    - docker-compose up -d
  only:
    - main  # main 브랜치만
  when: manual  # 수동 실행

고급 기능:

# 환경별 배포
deploy-dev:
  stage: deploy
  environment:
    name: development
    url: https://dev.example.com
  only:
    - develop

deploy-prd:
  stage: deploy
  environment:
    name: production
    url: https://example.com
  only:
    - main
  when: manual

# 조건부 실행
deploy-api:
  stage: deploy
  script:
    - echo "Deploy API"
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      changes:
        - api/**/*

# 캐시 사용 (빌드 속도 향상)
build-job:
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - .gradle/wrapper
      - .gradle/caches

6.3 GitLab Runner 설치 & 등록

# 1. Runner 설치
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
sudo apt-get install gitlab-runner

# 2. Runner 등록
sudo gitlab-runner register

# 입력 정보:
# GitLab URL: https://gitlab.com
# Token: (프로젝트 Settings → CI/CD → Runners에서 복사)
# Description: my-runner
# Tags: my-runner,docker
# Executor: shell 또는 docker

# 3. Runner 상태 확인
sudo gitlab-runner status

# 4. Runner 시작
sudo gitlab-runner start

6.4 GitHub Actions

.github/workflows/deploy.yml:

name: Deploy to EC2

on:
  push:
    branches: [ main ]
  workflow_dispatch:  # 수동 실행

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3

    - name: Set up JDK 21
      uses: actions/setup-java@v3
      with:
        java-version: '21'
        distribution: 'temurin'

    - name: Build with Gradle
      run: ./gradlew bootJar -x test

    - name: Upload artifact
      uses: actions/upload-artifact@v3
      with:
        name: app-jar
        path: build/libs/*.jar

  deploy:
    needs: build
    runs-on: ubuntu-latest

    steps:
    - name: Download artifact
      uses: actions/download-artifact@v3
      with:
        name: app-jar

    - name: Deploy to EC2
      env:
        PRIVATE_KEY: ${{ secrets.EC2_SSH_KEY }}
        HOST: ${{ secrets.EC2_HOST }}
        USER: ubuntu
      run: |
        echo "$PRIVATE_KEY" > private_key
        chmod 600 private_key

        scp -i private_key -o StrictHostKeyChecking=no \
          *.jar $USER@$HOST:/home/ubuntu/app/

        ssh -i private_key -o StrictHostKeyChecking=no $USER@$HOST << 'EOF'
          cd /home/ubuntu/app
          docker build -t myapp:latest .
          docker-compose up -d
        EOF

6.5 Blue-Green 배포

무중단 배포 전략:

현재 상태:
├─ Blue (v1.0) ← 사용자 트래픽
└─ Green (idle)

배포 시:
1. Green에 v1.1 배포
2. Green 헬스체크
3. 트래픽을 Green으로 전환
4. Blue 중지

장점:
✅ 무중단 배포
✅ 빠른 롤백 (Blue로 다시 전환)

Docker Compose 예시:

# docker-compose.blue.yml
services:
  api-blue:
    image: myapp:blue
    ports:
      - "8080:8080"

# docker-compose.green.yml
services:
  api-green:
    image: myapp:green
    ports:
      - "8081:8080"

배포 스크립트:

#!/bin/bash

# 1. 현재 활성화된 컨테이너 확인
if docker ps | grep -q "api-blue"; then
    CURRENT="blue"
    NEW="green"
    NEW_PORT="8081"
else
    CURRENT="green"
    NEW="blue"
    NEW_PORT="8080"
fi

# 2. 새 버전 배포
docker-compose -f docker-compose.$NEW.yml up -d

# 3. 헬스체크 (30초 대기)
echo "Waiting for health check..."
for i in {1..30}; do
    if curl -f http://localhost:$NEW_PORT/actuator/health; then
        echo "Health check passed!"
        break
    fi
    sleep 1
done

# 4. Nginx 설정 변경 (트래픽 전환)
sed -i "s/$CURRENT/$NEW/g" /etc/nginx/conf.d/default.conf
nginx -s reload

# 5. 이전 버전 중지
docker-compose -f docker-compose.$CURRENT.yml down

7. 웹서버 & 리버스 프록시

7.1 Nginx란?

Nginx = 웹서버 + 리버스 프록시

역할:
1. 정적 파일 제공 (HTML, CSS, JS, 이미지)
2. 리버스 프록시 (API 서버로 요청 전달)
3. 로드 밸런싱 (여러 서버로 분산)
4. SSL/TLS 처리
5. 캐싱

7.2 Nginx 설치

# Ubuntu
sudo apt update
sudo apt install nginx

# 시작
sudo systemctl start nginx
sudo systemctl enable nginx

# 상태 확인
sudo systemctl status nginx

# 설정 파일 위치
/etc/nginx/nginx.conf  # 메인 설정
/etc/nginx/conf.d/     # 추가 설정

7.3 Nginx 기본 설정

리버스 프록시 설정:

# /etc/nginx/conf.d/default.conf

server {
    listen 80;
    server_name example.com;

    # 로그
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # API 서버로 프록시
    location /api/ {
        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;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Admin 서버
    location /admin/ {
        proxy_pass http://localhost:8081/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 정적 파일
    location / {
        root /var/www/html;
        index index.html;
        try_files $uri $uri/ /index.html;
    }
}

로드 밸런싱:

# Upstream 정의
upstream api_servers {
    least_conn;  # 연결이 가장 적은 서버로

    server 10.0.1.10:8080 weight=3;
    server 10.0.1.11:8080 weight=1;
    server 10.0.1.12:8080 backup;  # 백업 서버
}

server {
    listen 80;

    location /api/ {
        proxy_pass http://api_servers/;
    }
}

SSL/HTTPS 설정:

server {
    listen 443 ssl http2;
    server_name example.com;

    # SSL 인증서
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;

    # SSL 설정
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass http://localhost:8080;
    }
}

# HTTP → HTTPS 리다이렉트
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

캐싱:

# 캐시 존 정의
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m;

server {
    location /api/products {
        proxy_cache api_cache;
        proxy_cache_valid 200 10m;  # 200 응답은 10분간 캐시
        proxy_cache_key "$request_uri";

        add_header X-Cache-Status $upstream_cache_status;

        proxy_pass http://localhost:8080;
    }
}

7.4 Nginx 명령어

# 설정 테스트
sudo nginx -t

# 재시작 (다운타임 발생)
sudo systemctl restart nginx

# Reload (다운타임 없음)
sudo nginx -s reload

# 중지
sudo nginx -s stop

# 로그 확인
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log

8. 데이터베이스 & 캐시

8.1 PostgreSQL

설치:

# Ubuntu
sudo apt install postgresql postgresql-contrib

# 시작
sudo systemctl start postgresql
sudo systemctl enable postgresql

# PostgreSQL 사용자로 전환
sudo -u postgres psql

기본 명령:

-- 데이터베이스 생성
CREATE DATABASE mydb;

-- 사용자 생성
CREATE USER myuser WITH PASSWORD 'mypassword';

-- 권한 부여
GRANT ALL PRIVILEGES ON DATABASE mydb TO myuser;

-- 데이터베이스 목록
\l

-- 테이블 목록
\dt

-- 종료
\q

외부 접속 허용:

# postgresql.conf 수정
sudo vi /etc/postgresql/15/main/postgresql.conf
# listen_addresses = '*'

# pg_hba.conf 수정
sudo vi /etc/postgresql/15/main/pg_hba.conf
# host    all             all             0.0.0.0/0            md5

# 재시작
sudo systemctl restart postgresql

8.2 Redis

설치:

sudo apt install redis-server

# 시작
sudo systemctl start redis-server
sudo systemctl enable redis-server

# 접속
redis-cli

기본 명령:

# 값 저장
SET mykey "hello"

# 값 조회
GET mykey

# TTL 설정 (초 단위)
SETEX mykey 60 "expires in 60 seconds"

# 모든 키 조회
KEYS *

# 키 삭제
DEL mykey

# 데이터베이스 전환 (0~15)
SELECT 1

# 모든 데이터 삭제
FLUSHALL

# 종료
exit

Redis 설정:

# redis.conf 수정
sudo vi /etc/redis/redis.conf

# 주요 설정:
bind 0.0.0.0                    # 외부 접속 허용
requirepass mypassword          # 비밀번호 설정
maxmemory 256mb                 # 최대 메모리
maxmemory-policy allkeys-lru    # 메모리 부족 시 정책

8.3 백업 & 복구

PostgreSQL 백업:

# 전체 백업
pg_dump -U myuser mydb > backup.sql

# 복구
psql -U myuser mydb < backup.sql

# 자동 백업 스크립트
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
pg_dump -U myuser mydb > /backup/mydb_$DATE.sql
find /backup -name "*.sql" -mtime +7 -delete  # 7일 이상된 백업 삭제

Redis 백업:

# RDB (스냅샷)
redis-cli SAVE

# AOF (Append Only File) - 모든 쓰기 명령 기록
# redis.conf에서 설정:
appendonly yes
appendfsync everysec

9. 모니터링 & 로깅

9.1 애플리케이션 로깅

Logback 설정 (Spring Boot):

<!-- logback-spring.xml -->
<configuration>
    <springProfile name="prd">
        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>/var/log/myapp/application.log</file>

            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>/var/log/myapp/application.%d{yyyy-MM-dd}.log</fileNamePattern>
                <maxHistory>30</maxHistory>
            </rollingPolicy>

            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>

        <root level="INFO">
            <appender-ref ref="FILE"/>
        </root>
    </springProfile>
</configuration>

9.2 시스템 모니터링

기본 모니터링 명령:

# CPU, 메모리 실시간 확인
top
htop

# 디스크 사용량
df -h

# 메모리 사용량
free -h

# 네트워크 연결
netstat -tulpn

# 프로세스 확인
ps aux | grep java

CloudWatch (AWS):

기본 메트릭:
├─ CPU 사용률
├─ 네트워크 In/Out
├─ 디스크 I/O
└─ 상태 체크

커스텀 메트릭:
└─ 애플리케이션 로그
└─ JVM 메모리
└─ API 응답 시간

9.3 로그 수집 (ELK 스택)

Elasticsearch: 로그 저장 & 검색
Logstash: 로그 수집 & 변환
Kibana: 시각화

간단한 로그 관리:

# 로그 로테이션 설정
sudo vi /etc/logrotate.d/myapp

/var/log/myapp/*.log {
    daily               # 매일 로테이션
    rotate 30           # 30일 보관
    compress            # 압축
    delaycompress       # 1일 후 압축
    missingok           # 파일 없어도 에러 없음
    notifempty          # 빈 파일은 로테이션 안 함
}

10. 보안

10.1 SSH 보안

# 1. 비밀번호 로그인 비활성화
sudo vi /etc/ssh/sshd_config
# PasswordAuthentication no
# PermitRootLogin no

# 2. SSH 포트 변경
# Port 2222

# 3. 재시작
sudo systemctl restart sshd

# 4. 키 기반 인증만 허용
ssh-keygen -t ed25519
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server

10.2 방화벽 (UFW)

# UFW 설치 (Ubuntu에 기본 포함)
sudo apt install ufw

# 기본 정책: 들어오는 거 차단, 나가는 거 허용
sudo ufw default deny incoming
sudo ufw default allow outgoing

# 포트 허용
sudo ufw allow 22/tcp      # SSH
sudo ufw allow 80/tcp      # HTTP
sudo ufw allow 443/tcp     # HTTPS

# 특정 IP만 허용
sudo ufw allow from 1.2.3.4 to any port 22

# UFW 활성화
sudo ufw enable

# 상태 확인
sudo ufw status

10.3 SSL/TLS 인증서

Let's Encrypt (무료):

# Certbot 설치
sudo apt install certbot python3-certbot-nginx

# 인증서 발급 (Nginx 자동 설정)
sudo certbot --nginx -d example.com -d www.example.com

# 인증서 자동 갱신
sudo certbot renew --dry-run

# Cron으로 자동 갱신 설정 (이미 자동으로 됨)
# /etc/cron.d/certbot

10.4 환경변수 보안

민감 정보는 환경변수로:

# .env 파일 (Git에 커밋 금지!)
DB_PASSWORD=secret123
REDIS_PASSWORD=redis_secret
JASYPT_PASSWORD=jasypt_key

# Docker Compose에서 사용
services:
  api:
    env_file:
      - .env

Jasypt로 암호화:

// application.yml
spring:
  datasource:
    password: ENC(암호화된_비밀번호)

// 실행 시 복호화 키 전달
java -jar app.jar -Djasypt.encryptor.password=secret

11. 실전 배포 프로세스

11.1 전체 배포 플로우

1. 개발
   └─ 로컬에서 개발 & 테스트

2. 코드 푸시
   └─ git push origin main

3. CI/CD 자동 실행
   ├─ 빌드
   ├─ 테스트
   └─ Docker 이미지 생성

4. 배포
   ├─ 새 컨테이너 시작
   ├─ 헬스체크
   └─ 트래픽 전환

5. 모니터링
   └─ 로그 & 메트릭 확인

11.2 체크리스트

배포 전:

✅ 테스트 통과 확인
✅ 환경변수 설정 확인
✅ 데이터베이스 마이그레이션 계획
✅ 롤백 계획 수립
✅ 백업 완료

배포 중:

✅ 헬스체크 통과 확인
✅ 로그 모니터링
✅ 에러율 확인

배포 후:

✅ 주요 기능 동작 확인
✅ 응답 시간 확인
✅ 에러 로그 확인
✅ 사용자 피드백 모니터링

11.3 트러블슈팅

자주 발생하는 문제:

문제 1: 컨테이너가 시작하자마자 종료됨
해결:
└─ docker logs [container] 로그 확인
└─ 환경변수, 설정 파일 확인

문제 2: 502 Bad Gateway
해결:
└─ 백엔드 서버 실행 여부 확인
└─ 포트 확인
└─ 방화벽/Security Group 확인

문제 3: 메모리 부족 (OOM)
해결:
└─ JVM 메모리 설정 확인
└─ 서버 스펙 업그레이드
└─ 메모리 누수 확인

문제 4: 디스크 공간 부족
해결:
└─ docker system prune  # 미사용 이미지/컨테이너 삭제
└─ 로그 로테이션 확인
└─ 불필요한 파일 삭제

마무리: 학습 로드맵

1단계: 기초 (1~2주)

✅ Linux 기본 명령어
✅ SSH 접속
✅ Docker 기본 (이미지, 컨테이너)
✅ EC2 생성 및 접속

2단계: 중급 (2~4주)

✅ Docker Compose
✅ Nginx 리버스 프록시
✅ GitLab CI/CD 설정
✅ 간단한 배포 자동화

3단계: 고급 (1~2개월)

✅ Blue-Green 배포
✅ 모니터링 & 로깅
✅ 보안 강화
✅ 성능 최적화

4단계: 전문가 (지속적 학습)

✅ Kubernetes
✅ 마이크로서비스 아키텍처
✅ 인프라 자동화 (Terraform)
✅ 대규모 트래픽 처리
'AI가 만들어준 가이드' 카테고리의 다른 글
  • React.js를 기초부터 배우기
  • AWS 강의 시작 전 꼭 알아야 할 리눅스 기초
  • Redis 실무 활용 가이드
Du^
Du^
  • Du^
    DD
    Du^
  • 전체
    오늘
    어제
    • 분류 전체보기 (17)
      • 💡주제별 (3)
        • Front (0)
        • AWS (0)
        • 도커 (2)
        • AI (1)
      • 💡총정리 (0)
        • 1. 서버 엔지니어링 (0)
        • 2. 데이터베이스 (0)
        • 3. 테스트 전략 (0)
        • 4. 애플리케이션 설계 (0)
        • 5. 성능 엔지니어링 (0)
      • Spring (3)
      • Git (1)
      • 디자인패턴 (0)
      • Cafe24 (0)
      • 운영체제 (1)
      • 온라인 강의 (0)
        • 클린 코더스 강의 (0)
      • 오프라인 강의 (0)
        • AWS (0)
      • 개발도서 (1)
        • DB (1)
      • 사용 툴 및 개인설정 (3)
      • AI가 만들어준 가이드 (4)
      • 프로젝트 (0)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    멀티프로세싱
    spring 대용량 insert
    도커
    Git Flow
    스레드
    Docker
    도커 컴포즈
    docker volume
    티스토리 코드블럭
    멀티태스킹
    Git
    멀티스레딩
    Mermaid
    티스토리 스킨
    프로세스
    docker compose
    추상 팩토리 패턴
    jdbc batchinsert
    멀티프로그래밍
    도커 명령어
    도커 볼륨
    팩토리 패턴
    팩토리 메서드 패턴
    디자인 패턴
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
Du^
백엔드 개발자를 위한 서버 가이드
상단으로

티스토리툴바