Skip to main content

시스템 설계 입문

Views 7약 10분생성형 AI 활용

작은 프로그램을 만들 때 우리는 보통 기능에 집중한다. 로그인만 되면 되고, 게시글만 저장되면 되고, 검색만 되면 된다. 이 단계에서는 코드가 잘 돌아가는지가 가장 중요하다. 그런데 사용자가 늘어나는 순간, 질문이 달라진다.

이제는 "동작하는가?"만으로는 부족하다. "많은 사람이 동시에 써도 버티는가?", "서버 하나가 죽어도 계속 서비스되는가?", "응답이 느려지지 않는가?", "비용이 감당 가능한가?" 같은 질문이 앞에 나온다. 이 지점부터 개발은 기능 구현의 싸움이 아니라 구조 설계의 싸움이 된다.

시스템 설계는 거창한 건축 설계도가 아니다. 오히려 현실적인 기술 판단의 연속에 가깝다. 어디에 병목이 생길지 예상하고, 어떤 고장이 일어날지 미리 생각하고, 제한된 예산 안에서 어느 정도의 성능과 안정성을 얻을지 결정하는 일이다. 큰 서비스는 특별한 마법으로 만들어지지 않는다. 작은 프로그램이 감당하지 못하는 문제를 하나씩 분리하고, 완충하고, 대체 가능하게 만드는 과정에서 탄생한다.

시스템 설계 입문

작은 프로그램과 큰 서비스의 차이

작은 프로그램은 대개 한 대의 컴퓨터, 한 명의 사용자, 짧은 실행 시간이라는 전제를 깔고 있다. 프로그램을 켜고, 입력을 받고, 결과를 보여주면 끝난다. 상태도 단순하다. 메모리에만 잠깐 존재하거나, 파일 하나에 저장해도 충분하다. 실패하더라도 다시 실행하면 끝나는 경우가 많다.

하지만 큰 서비스는 살아 있는 도시와 비슷하다. 수많은 사용자가 동시에 들어오고, 데이터는 계속 바뀌며, 일부 서버는 언제든 고장 날 수 있다. 프로그램은 한 번 실행하고 끝나는 것이 아니라 24시간 계속 돌아가야 한다. 데이터는 메모리가 아니라 데이터베이스, 캐시, 메시지 큐, 로그 시스템 등 여러 장소에 흩어져 저장된다. 한 부분의 장애가 다른 부분으로 번지지 않도록 막아야 하고, 새 버전을 배포하는 동안에도 서비스는 멈추지 않아야 한다.

가장 먼저 달라지는 것은 상태다. 작은 프로그램에서는 상태를 한 프로세스 안에서 다루면 되지만, 큰 서비스에서는 상태가 여러 서버와 저장소에 나뉜다. 예를 들어 장바구니 정보가 웹 서버 메모리에만 있으면, 서버를 두 대로 늘리는 순간 사용자는 요청할 때마다 다른 장바구니를 보게 될 수 있다. 그래서 상태를 어디에 두고 어떻게 공유할지부터 설계해야 한다.

다음으로 달라지는 것은 트래픽이다. 사용자가 적을 때는 요청 하나하나가 독립적으로 보이지만, 사용자가 몰리면 요청은 파도처럼 밀려온다. 어느 순간부터는 "한 번 처리하는 코드"보다 "한꺼번에 많이 들어올 때도 무너지지 않는 구조"가 더 중요해진다. 느린 쿼리 하나, 큰 파일 다운로드 하나, 외부 API 호출 하나가 전체 시스템을 흔들 수 있다.

장애도 성격이 달라진다. 작은 프로그램에서는 에러가 나면 로그를 보고 고치면 되지만, 큰 서비스에서는 장애가 예외가 아니라 일상이다. 디스크는 고장 나고, 네트워크는 느려지고, 프로세스는 죽고, 배포는 꼬인다. 그래서 시스템 설계는 "고장이 없게 만드는 법"이 아니라 "고장이 나도 전체가 무너지지 않게 만드는 법"에 가깝다.

운영 복잡도 역시 무시할 수 없다. 서버가 한 대일 때는 직접 접속해 재시작하면 되지만, 수십 대가 되면 모니터링, 로그 수집, 배포 자동화, 알림, 롤백 같은 운영 시스템이 필요해진다. 큰 서비스는 코드를 잘 짜는 것만으로 유지되지 않는다. 관측 가능성, 즉 시스템 내부를 들여다볼 수 있는 능력도 함께 설계해야 한다.

시스템 설계에서 먼저 보는 질문

시스템 설계를 처음 접하면 많은 사람이 "정답 구조"를 찾으려 한다. 하지만 실제 설계는 정답 찾기보다 질문 정리가 더 중요하다. 같은 게시판 서비스라도 목표가 다르면 구조가 완전히 달라질 수 있기 때문이다.

예를 들어 사내 공지 시스템은 사용자가 많지 않아도 데이터 정확성이 매우 중요할 수 있다. 반면 실시간 인기 댓글 서비스는 약간의 지연이나 일시적 불일치를 감수하더라도 빠른 응답이 더 중요할 수 있다. 결제 시스템은 쓰기 성공의 정확성과 복구 가능성이 핵심이고, 뉴스 사이트는 읽기 성능이 핵심이다.

그래서 설계는 먼저 다음 질문들로 시작한다.

  • 이 시스템은 읽기가 많은가, 쓰기가 많은가?

  • 응답이 느려지면 치명적인가, 잠깐 늦어도 괜찮은가?

  • 일부 데이터가 잠시 늦게 반영되어도 되는가, 항상 즉시 같아야 하는가?

  • 서버 한 대가 죽었을 때 서비스가 멈춰도 되는가?

  • 얼마의 인프라 비용을 감당할 수 있는가?

이 질문에 대한 답이 시스템의 모양을 결정한다. 설계는 기술 자랑이 아니라 우선순위 선택이다.

시스템 설계의 다섯 가지 목표

큰 서비스를 설계할 때 반복해서 등장하는 핵심 목표가 있다. 확장성, 가용성, 일관성, 지연 시간, 비용이다. 이 다섯 가지는 서로 협력하기도 하지만, 자주 충돌한다. 좋은 설계자는 이 개념을 따로 외우는 사람이 아니라, 서로의 긴장을 읽는 사람이다.

확장성

확장성은 사용자가 늘거나 데이터가 커져도 시스템이 계속 감당할 수 있는 능력이다. 서버 한 대로 1천 명을 처리하던 구조가 10만 명이 오면 무너진다면 확장성이 낮은 것이다.

확장에는 두 방향이 있다. 하나는 더 좋은 장비로 바꾸는 수직 확장이고, 다른 하나는 서버 수를 늘리는 수평 확장이다. 작은 단계에서는 수직 확장이 빠르고 단순하다. 하지만 어느 순간부터는 한 대의 성능 향상보다 여러 대로 나누는 편이 현실적이다. 이때부터 로드밸런서, 분산 캐시, 데이터 샤딩 같은 개념이 등장한다.

가용성

가용성은 시스템이 얼마나 자주, 얼마나 오래 살아 있는지를 뜻한다. 사용자는 내부 구조를 모른다. 버튼을 눌렀는데 동작하면 살아 있는 것이고, 안 되면 죽은 것이다.

가용성을 높이려면 중복이 필요하다. 웹 서버를 여러 대 두고, 데이터베이스도 복제하고, 특정 서버가 죽어도 다른 서버가 이어받게 만들어야 한다. 하지만 중복은 비용을 늘리고, 데이터 동기화 문제를 가져온다. "안 죽는 시스템"은 보통 "더 복잡한 시스템"이기도 하다.

일관성

일관성은 여러 곳에 저장된 데이터가 같은 시점에 같은 내용을 보여주는 성질이다. 계좌 이체처럼 숫자 하나가 틀리면 안 되는 시스템에서는 강한 일관성이 중요하다. 반면 좋아요 수나 조회수처럼 몇 초 늦게 반영되어도 큰 문제가 없는 시스템도 있다.

일관성을 강하게 요구할수록 처리 속도와 가용성에 제약이 생길 수 있다. 반대로 성능과 유연성을 위해 일관성을 조금 양보하면, 사용자는 잠깐 이상한 화면을 볼 수 있다. 예를 들어 방금 올린 게시글이 일부 서버에서는 보이고 다른 서버에서는 아직 안 보일 수 있다.

지연 시간

지연 시간은 요청을 보낸 뒤 응답이 돌아오기까지 걸리는 시간이다. 처리량이 아무리 높아도 사용자가 느리다고 느끼면 좋은 서비스가 아니다. 검색창에 글자를 쳤는데 결과가 3초 뒤에 나타나면, 시스템은 살아 있어도 경험은 망가진다.

지연 시간은 체인처럼 늘어난다. 브라우저에서 서버까지, 로드밸런서를 거치고, 애플리케이션 서버에서 캐시를 보고, 데이터베이스를 조회하고, 외부 API를 호출하는 과정이 모두 더해진다. 그래서 큰 시스템에서는 "각 부품이 조금씩 느린 것"이 합쳐져 전체를 느리게 만든다.

비용

비용은 가장 현실적인 제약이다. 서버를 무한정 늘리면 성능과 가용성을 높일 수 있을 것 같지만, 실제 서비스는 예산 안에서 살아야 한다. 캐시를 크게 두면 빠르지만 비싸고, 여러 지역에 서버를 두면 가용성은 좋아지지만 운영 부담도 커진다.

좋은 설계는 가장 비싼 기술을 쓰는 설계가 아니다. 필요한 품질을, 감당 가능한 비용으로 달성하는 설계다. 스타트업의 시스템과 은행의 시스템이 다른 이유는 기술 수준보다 요구 조건과 비용 구조가 다르기 때문이다.

큰 서비스를 떠받치는 기본 부품들

대규모 서비스는 완전히 새로운 재료로 만들어지지 않는다. 오히려 몇 가지 기본 부품을 적절한 위치에 배치해 만든다. 캐시, 큐, 로드밸런서, CDN, 비동기 처리는 그중에서도 가장 자주 등장하는 핵심 부품이다.

로드밸런서: 요청을 나누는 교통정리자

로드밸런서는 들어오는 요청을 여러 서버에 적절히 분산한다. 서버가 한 대뿐이라면 필요 없지만, 두 대 이상이 되는 순간 사실상 필수에 가깝다. 한 서버에만 요청이 몰리면 다른 서버가 놀고 있어도 전체 서비스는 느려진다.

로드밸런서는 단순히 트래픽을 나누는 것만 하지 않는다. 죽은 서버를 제외하고, 건강한 서버로 우회시키며, 특정 지역이나 조건에 따라 다른 서버군으로 보내기도 한다. 즉, 큰 시스템의 입구에서 첫 번째 방어선을 담당한다.

캐시: 자주 찾는 답을 가까이에 두기

캐시는 자주 사용하는 데이터를 더 빠른 저장소에 잠깐 보관해 두는 장치다. 예를 들어 인기 게시글 목록을 매번 데이터베이스에서 계산하면 비싸고 느리다. 대신 결과를 메모리 캐시에 저장해 두면 훨씬 빠르게 응답할 수 있다.

캐시는 읽기 부하를 줄이는 데 매우 강력하다. 특히 같은 데이터를 여러 사용자가 반복해서 요청하는 서비스에서는 효과가 크다. 하지만 캐시는 새로운 문제도 만든다. 원본 데이터가 바뀌었는데 캐시는 예전 값을 들고 있을 수 있기 때문이다. 그래서 캐시는 빠름을 주는 대신, "언제 비울 것인가"라는 관리 문제를 함께 가져온다.

큐: 밀려드는 일을 줄 세우는 완충 장치

큐는 작업을 즉시 처리하지 않고 줄 세워 두는 구조다. 예를 들어 사용자가 회원가입을 했다고 해서 환영 메일 발송, 추천 초기화, 통계 적재, 알림 전송을 모두 한 요청 안에서 처리하면 응답이 느려진다. 대신 가입 자체만 먼저 끝내고, 나머지 작업은 큐에 넣어 뒤에서 처리할 수 있다.

큐의 진짜 가치는 완충 작용에 있다. 트래픽이 갑자기 폭증해도 일단 큐가 작업을 받아 주면, 뒤쪽 소비자 시스템은 자신의 속도에 맞춰 처리할 수 있다. 즉, 큐는 성능 도구이면서 동시에 장애 전파를 늦추는 안전장치다.

CDN: 사용자 가까운 곳에서 전달하기

CDN은 이미지, 동영상, 정적 파일 같은 콘텐츠를 사용자와 가까운 위치에서 전달하는 네트워크다. 서울 사용자가 요청한 이미지가 매번 해외 원본 서버까지 다녀오면 느릴 수밖에 없다. CDN은 여러 지역에 복사본을 두고 가까운 지점에서 응답해 지연 시간을 줄인다.

특히 정적 자원이 많은 웹 서비스에서는 CDN이 원본 서버의 부담을 크게 덜어 준다. 원본 서버는 더 중요한 동적 요청 처리에 집중하고, 반복되는 파일 전송은 CDN이 맡는 식이다.

비동기 처리: 기다리지 않고 다음으로 넘어가기

비동기 처리는 결과가 바로 필요하지 않은 일을 나중으로 미루는 방식이다. 사용자가 사진을 올릴 때 원본 저장, 썸네일 생성, AI 분석, 검색 인덱싱을 한 번에 끝내려 하면 업로드가 너무 오래 걸린다. 사용자는 업로드 버튼을 눌렀을 뿐인데, 내부에서는 긴 공장이 돌고 있는 셈이다.

이럴 때 업로드 성공만 먼저 돌려주고, 나머지는 백그라운드 작업으로 넘기면 사용자 경험이 훨씬 좋아진다. 비동기 처리는 시스템 응답성을 높이는 핵심 도구이지만, 작업이 나중에 실패할 수 있다는 점까지 고려해야 한다. 그래서 재시도, 중복 처리 방지, 실패 기록 같은 장치가 함께 필요하다.

읽기 부하와 쓰기 부하는 다르게 다뤄야 한다

많은 초보 설계에서 놓치기 쉬운 점이 하나 있다. 시스템은 단순히 "요청이 많다"로 설명되지 않는다. 읽기가 많은지, 쓰기가 많은지에 따라 병목 위치와 해결책이 완전히 달라진다.

읽기 부하가 큰 시스템의 대표 예는 뉴스, 커뮤니티 메인 화면, 동영상 조회, 상품 상세 페이지다. 같은 데이터를 수많은 사용자가 반복해서 본다. 이 경우 캐시, CDN, 읽기 복제본 같은 도구가 특히 효과적이다. 데이터를 한 번 만들어 여러 번 나눠 주는 편이 유리하기 때문이다.

반면 쓰기 부하가 큰 시스템은 더 까다롭다. 채팅, 결제, 로그 수집, 실시간 이벤트 처리처럼 데이터가 계속 들어오면 저장 계층이 병목이 되기 쉽다. 쓰기는 단순히 저장 공간만의 문제가 아니다. 중복 기록을 막아야 하고, 순서가 중요할 수 있고, 여러 저장소에 동시에 반영해야 할 수도 있다.

예를 들어 조회수 1억 번은 캐시로 잘 막을 수 있지만, 초당 수십만 건의 클릭 이벤트를 저장하는 일은 다른 종류의 설계를 요구한다. 이때는 배치 쓰기, 로그 기반 적재, 파티셔닝, 큐 기반 흡수 같은 방법이 중요해진다.

핫스팟도 자주 등장한다. 핫스팟은 특정 키, 특정 사용자, 특정 데이터 조각에 요청이 비정상적으로 몰리는 현상이다. 모두가 같은 인기 게시글을 보거나, 한 연예인의 프로필만 집중적으로 조회하거나, 특정 시간에 같은 상품 재고를 동시에 구매하는 경우가 그렇다. 시스템 전체 평균은 괜찮아 보여도, 한 지점이 과열되면 그 부분이 전체를 끌어내린다.

그래서 설계에서는 평균보다 최악의 지점을 봐야 한다. 한 데이터베이스 노드에만 유독 요청이 몰리지 않는지, 특정 캐시 키 하나가 병목이 되지 않는지, 같은 락을 너무 많은 요청이 기다리지 않는지 살펴야 한다. 큰 시스템은 대체로 전체가 고르게 느린 것이 아니라, 한 부분이 너무 뜨거워져서 망가진다.

백프레셔와 장애 격리: 무너질 때 덜 무너지게 하기

서비스는 언제나 최고 성능 상태로만 동작하지 않는다. 트래픽 급증, 외부 API 지연, 데이터베이스 부하, 버그, 배포 실수 등으로 언제든 압박을 받는다. 이때 중요한 것은 모든 요청을 무리하게 받는 것이 아니라, 감당 가능한 만큼만 받아서 전체 붕괴를 막는 것이다. 이 개념이 바로 백프레셔다.

백프레셔는 말 그대로 뒤쪽 시스템이 힘들다는 신호를 앞쪽에 전달하는 것이다. 예를 들어 작업 큐가 너무 길어지면 생산 속도를 늦추거나, 서버 스레드 풀이 가득 차면 새 요청 일부를 빠르게 거절하거나, 업로드 처리량이 넘치면 일시적으로 제한을 거는 방식이 있다. 무조건 받아 놓고 뒤에서 터지는 것보다, 일부를 일찍 제한하는 편이 전체적으로는 더 건강하다.

많은 장애는 과부하 자체보다 과부하의 전파 때문에 커진다. 데이터베이스가 느려지면 애플리케이션 서버가 응답을 오래 기다리고, 기다리는 요청이 쌓여 스레드가 고갈되고, 결국 정상적인 요청까지 모두 실패한다. 여기에 외부 결제 API까지 느려지면 장애는 연쇄 반응처럼 퍼진다.

이런 상황을 막기 위해 필요한 것이 장애 격리다. 장애 격리는 한 부분의 문제가 다른 부분까지 번지지 않게 구획을 나누는 설계다. 예를 들어 추천 시스템이 느려져도 결제 시스템은 정상 동작하게 만들고, 이미지 처리 서버가 죽어도 로그인은 가능하게 해야 한다. 핵심 기능과 부가 기능을 분리하고, 자원 풀도 나누고, 타임아웃과 실패 처리 규칙을 다르게 주는 이유가 여기에 있다.

쉽게 말해 큰 서비스는 "절대 안 망하는 구조"보다 "망가져도 핵심은 남는 구조"가 중요하다. 모든 기능을 100점으로 살리려다 모두 죽는 것보다, 중요한 기능을 80점으로라도 지키는 편이 훨씬 낫다.

시스템 설계는 결국 트레이드오프를 고르는 일

시스템 설계 입문에서 가장 먼저 버려야 할 환상은 "완벽한 구조가 어딘가에 있다"는 생각이다. 실제로는 확장성을 높이면 복잡도가 늘고, 가용성을 높이면 비용이 커지며, 일관성을 강하게 잡으면 지연 시간이 늘어날 수 있다. 반대로 단순함을 유지하면 운영은 쉬워지지만 성장 한계가 빨리 온다.

그래서 좋은 설계자는 화려한 기술 이름을 많이 아는 사람이 아니다. 현재 서비스의 단계에서 무엇이 진짜 문제인지 구분하고, 너무 이른 최적화는 피하면서도 분명한 병목에는 정확히 대응하는 사람이다.

초기 서비스라면 모든 것을 분산 시스템처럼 만들 필요는 없다. 오히려 단순한 단일 구조가 더 낫다. 다만 언젠가 사용자가 늘었을 때 어디서 먼저 깨질지 예상할 수 있어야 한다. 세션 상태는 어디에 둘지, 데이터베이스는 얼마나 버틸지, 읽기 폭증이 오면 무엇을 캐시할지, 무거운 작업은 어디까지 비동기로 뺄지 같은 질문을 미리 던질 수 있어야 한다.

작은 프로그램은 코드를 잘 짜면 돌아간다. 큰 서비스는 코드를 잘 짜는 것만으로는 부족하다. 요청의 흐름, 데이터의 위치, 장애의 전파, 병목의 이동을 함께 봐야 한다. 기능이 건물의 방이라면, 시스템 설계는 그 방들을 지탱하는 기둥과 배관과 전기선에 가깝다. 평소에는 잘 보이지 않지만, 사용자가 몰리는 순간 진짜 실력이 드러나는 부분이다.

다음 단계로 넘어가면 우리는 이 기본 부품들이 여러 컴퓨터에 걸쳐 어떻게 협력하는지 보게 된다. 그때부터는 한 대의 서버를 잘 쓰는 문제가 아니라, 여러 대의 서버를 하나의 시스템처럼 다루는 문제가 시작된다.

책 목록

컴퓨터공학의 지도: 코드에서 운영체제, 네트워크, 데이터베이스, 보안, AI까지 한 권으로 꿰는 CS의 전체 구조

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기