ABOUT

성능과 운영 안정성을 함께 끌어올리는 개발자입니다.

92% Positional Error Reduction
79% p95 Latency Improvement
90%+ Long Tasks Reduction

2022.02 · 한국장학재단

우수 멘티

한국장학재단 사회 리더 대학생 멘토링 IT

2022.10 · 동작구청

우수 인재상

동작구청 우수 SW 인재

2025.05 · (주) 그랩

프로그래밍 우수상

(주) 그랩 우수 프로그램 개발

2025.05 · AWSKRUG

AWS한국사용자모임 발표

AI agent 스크립트 튜닝 관련 발표

ComputerScience

Development

Engineering

Trouble Shooting

GUESTBOOK

첫 마음부터
함께 나누는 온기

방명록 작성하러 가기

SUBSCRIBE

최신소식을
편하게 만나보세요.

확장성을 고려한 개발

도입
“지금은 작게 만들되, 내일 커져도 버틸 수 있게 설계하는 것”입니다.

신입 때는 “기능이 동작하면 끝”으로 보이지만, 실무에서는 기능은 금방 늘고 요구사항은 계속 바뀝니다.

초반에 확장성을 염두에 두지 않으면, 일정이 촉박할수록 코드가 꼬이고 장애/성능 이슈가 누적됩니다.

여기서 말하는 확장성은 “처음부터 거대한 아키텍처”가 아닙니다. 변화가 와도 수정 범위를 최소화하고, 트래픽이 늘어도 대응 가능한 구조를 만드는 능력에 가깝습니다.

정의
부하(트래픽/데이터/기능)가 증가해도 성능, 안정성, 개발 속도가 급격히 무너지지 않도록 설계하는 접근입니다.

확장성은 크게 2가지로 나눠서 보는 게 좋습니다.

(1) 시스템 확장성: 트래픽/데이터가 늘어도 서비스가 버티는가 (성능/가용성/장애 대응)
(2) 설계 확장성: 기능/요구사항이 늘어도 개발이 버티는가 (유지보수/변경 비용/테스트 용이성)

핵심 메시지

“확장성은 성능 튜닝만이 아니라,
변경 비용을 제어하는 설계까지 포함한다.”

- 커지는 건 트래픽만이 아니다 -
백엔드에서 확장성이 깨지는 전형적인 순간
“처음엔 되는데, 어느 순간부터 지옥이 시작되는” 지점이 있습니다.
증상 원인 백엔드 관점 해석
응답 지연 증가 DB I/O 병목 인덱스/쿼리/커넥션 풀/캐시 전략이 없으면 급격히 느려짐
장애 전파 연동 실패 처리 부재 타임아웃/재시도/서킷브레이커가 없으면 전체가 같이 죽음
배포가 무서움 강결합/사이드 이펙트 코드 변경이 어디에 영향을 주는지 추적 불가, 롤백도 어려움
기능 추가 속도 저하 규칙 분산/중복 도메인 규칙이 여러 곳에 흩어져 수정 비용이 폭발
확장성을 만드는 핵심 축
실무에서는 아래 4가지를 “세트”로 봅니다.
1
확장 가능한 데이터 흐름(저장/조회) 인덱스, 쿼리 설계, 캐시, 커넥션 풀, 읽기/쓰기 분리 고려
2
실패를 전제로 한 연동 설계 Timeout/Retry/Backoff, Circuit Breaker, Bulkhead, Idempotency
3
확장 가능한 코드 구조(변경 비용 제어) 경계/계약(추상화), 단일 책임, 의존성 역전, 모듈화
4
관측 가능성(Observability) 로그/메트릭/트레이싱, SLO/알람, 장애 원인 추적 가능

💡 TIP / 초보가 가장 빨리 체감하는 포인트

“확장성”을 처음 체감하는 순간은 보통 에러/지연/장애가 발생했을 때입니다.
그래서 처음부터 거대한 구조를 만들기보다, 타임아웃/재시도/로그/지표 같은 “기본 체력”을 먼저 깔아두는 것이 실전에서 가장 효율이 좋습니다.

 

실전 시나리오
“결제 연동”이 확장성의 시험대가 되는 이유

결제는 트래픽이 늘수록 실패도 같이 늘고(네트워크 지연/타임아웃), 장애가 나면 매출에 직접 영향이 갑니다. 그래서 결제 연동은 확장성 설계의 종합 문제처럼 다뤄집니다.

확장성을 고려한 구현은 보통 “추상화 + 실패 처리 + 멱등성 + 관측”이 같이 들어갑니다.

// 예시: 결제 연동의 확장성 포인트(구현은 생략, 구조만 보기)

interface PaymentGateway {
    PaymentResult pay(String idempotencyKey, Money amount);
}

class PaymentService {
    private final PaymentGateway gateway;
    private final PaymentRepository repo;

    public PaymentService(PaymentGateway gateway, PaymentRepository repo) {
        this.gateway = gateway;
        this.repo = repo;
    }

    public PaymentResult requestPayment(String orderId, Money amount) {
        // 1) 멱등성 키(중복 요청 방지): "같은 요청이면 같은 결과"를 보장
        String key = "pay:" + orderId;

        // 2) 중복 처리 방지(저장소 기반): 이미 결제 결과가 있으면 그대로 반환
        PaymentResult cached = repo.findResultByKey(key);
        if (cached != null) return cached;

        // 3) 외부 연동 (여기서 timeout/retry/circuit breaker 같은 전략이 붙는다)
        PaymentResult result = gateway.pay(key, amount);

        // 4) 결과 저장: 재시도/중복 호출에서도 결과 일관성을 유지
        repo.saveResult(key, result);
        return result;
    }
}
마지막 체크
확장성은 “미리 다 하는 것”이 아니라, 확장 가능한 방향으로 결정하는 것입니다.

👍 GOOD (확장성 있는 결정)

  • 변경 가능성이 큰 지점을 경계/계약으로 분리
  • 실패를 전제로 타임아웃/재시도/관측을 기본 탑재
  • 핵심 경로의 병목(DB/외부 I/O)을 먼저 측정
  • 테스트 가능한 구조로 만들어 배포 위험을 낮춤

👎 BAD (확장성 깨지는 결정)

  • 구현 디테일(DB/벤더)이 서비스 전역에 퍼짐
  • 실패 처리가 없고 “일단 성공”만 가정
  • 로그/지표가 부족해 장애 원인 파악이 느림
  • 핵심 로직이 테스트 없이 배포에 의존

✅ 핵심 요약

  • ✔️ 확장성은 트래픽뿐 아니라 기능 증가까지 포함해 성능/안정성/개발 속도가 무너지지 않게 하는 설계다.
  • ✔️ 실무 핵심 축은 데이터 흐름, 실패 처리, 코드 구조, 관측 가능성 4가지다.
  • ✔️ 처음부터 거대하게 만들기보다, 확장 가능한 방향으로 결정하고 병목과 실패를 먼저 관리하는 게 현실적인 접근이다.
728x90