3줄 요약
- 개인 홈 서버에서 GitHub 기반 MVP 플랫폼을 만들고 있다. 프로젝트 생성부터 고정 Preview URL 배포까지의 흐름을 구현하는 작업이다.
- 장애 범위를 좁히려고 책임을 나눴다. 코드의 책임 분리와 프로세스의 장애 격리를 구분하고, API와 워커는 별도 프로세스로 구성했다.
- 『주니어 백엔드 개발자가 반드시 알아야 할 실무지식』 7장을 읽고 IO 병목과 가상 스레드·논블로킹 IO의 선택 기준을 정리했다.
지난주 Try 점검
- 『주니어 백엔드 개발자가 반드시 알아야 할 실무지식』 7장 읽고 정리하기 → IO 대기 중 자원 활용 방식과 가상 스레드·논블로킹 IO의 선택 기준을 스터디 노트에 정리했다.
이번 주의 작업: MVP 플랫폼을 만들며 책임과 실행 단위를 나눴다
사내에서 MVP를 쉽게 검증할 수 있는 플랫폼을 구상하고 있다. 서버 구성과 공통 요소 연동은 AI에게 맡기고, 나는 공통 보안 가이드라인과 플랫폼 운영을 담당하는 구조다. 이번 주에는 첫 단계로 개인 홈 서버에서 사용할 플랫폼을 만들고 있다.
사용 흐름은 세 단계로 좁혔다.
- 프로젝트 생성: GitHub으로 로그인하고, 프로젝트 이름을 입력해 FastAPI 템플릿 기반의 비공개 저장소를 만든다.
- Preview 배포: main에 코드를 push하면 빌드와 테스트를 거쳐 프로젝트별 고정 Preview URL에 반영한다.
- 상태 확인: 포털에서 접근 권한이 있는 프로젝트와 배포 이력을 확인한다.
첫 범위는 FastAPI와 main 기준의 고정 Preview URL로 잡았다. PR별 Preview, Production 승격, 다른 언어의 템플릿은 후속 범위로 뒀다. 현재 구현 범위와 구조는 GitHub 기반 내부 MVP 플랫폼 구현 이슈에 정리하고 있다.
MVP 시연 영상
GitHub 로그인과 프로젝트 상세 화면, 배포 이벤트를 확인할 수 있다.
장애 범위를 좁히기 위해 책임을 나눴다
플랫폼 제어 계층은 프로젝트와 배포 상태를 관리하고, 배포 실행 계층의 워커는 빌드와 배포를 실행한다. 두 책임을 분리하면 워커가 중단돼도 사용자 요청 처리와 기존 Preview는 유지할 수 있다. 장애 원인을 조사하고 복구할 범위도 좁아진다.
책임은 네 가지 기준으로 나눴다.
- 입력: 사용자 요청, GitHub 이벤트, 주기 실행 중 무엇으로 시작하는가?
- 권한: GitHub 인증 정보나 Docker 접근 권한이 필요한가?
- 실행 시간: 짧은 API 요청인가, 오래 걸리는 빌드 작업인가?
- 실패 처리: 오류 응답, 재시도, 이전 버전 복구 중 무엇이 필요한가?
워커는 실행 시간이 길고 Docker 접근 권한이 필요해 API와 별도 프로세스로 구성했다. 빌드나 테스트가 실패하면 기존 Preview를 유지하고, 배포 후 Ready 확인이 실패하면 이전 정상 이미지로 복구를 시도한다.
| 계층 | 책임 |
|---|---|
| 접근 계층 | HTTPS와 호스트별 라우팅 |
| 사용자 포털 계층 | 로그인, 세션, 화면, 사용자 권한 확인 |
| 플랫폼 제어 계층 | 프로젝트 생성, 저장소 연동, 배포 요청과 상태 관리 |
| 변경 감지 계층 | Webhook·Poller로 변경을 확인하고 작업 등록 |
| 배포 실행 계층 | 작업 획득, 빌드, 테스트, 교체, 복구 |
| 서비스 실행 계층 | 생성된 MVP 실행과 프로젝트 데이터 유지 |
| 상태 저장 계층 | 프로젝트·배포·작업 이력 보존 |
프로젝트 폴더도 책임에 맞춰 구분하려 했다. 폴더 분리는 코드의 변경 범위를 줄이고, 프로세스 분리는 장애 영향을 줄인다. 두 경계가 반드시 같을 필요는 없다.
시스템 아키텍처
- 접근 계층: Nginx가 호스트 이름에 따라 사용자 포털·플랫폼 제어 계층의 API와 서비스 실행 계층의 Preview로 요청을 보낸다.
- 변경 감지 계층: Webhook으로 main 변경을 감지하고, Poller가 누락을 보완한다. 배포 작업은 상태 저장 계층의 PostgreSQL 큐에 저장한다.
- 배포 실행 계층: 워커가 작업을 가져와 해당 커밋을 빌드·테스트하고 서비스 실행 계층의 Preview에 배포한다. 단계별 결과는 상태 저장 계층에 기록한다.
현재 장애 격리 범위는 다음과 같다.
- API: 사용자 포털 계층과 플랫폼 제어 계층, 변경 감지 계층의 Webhook 수신이 같은 프로세스에서 실행된다. API가 중단되면 세 기능이 함께 영향을 받는다.
- 워커: 배포 실행 계층을 별도 프로세스로 실행한다. 작업은 상태 저장 계층의 PostgreSQL 큐에 저장하므로 워커가 중단돼도 남아 있다.
- 호스트: 모든 구성 요소가 같은 서버와 Docker 데몬을 공유한다. 호스트 장애는 전체 서비스에 영향을 준다.
이번 주의 공부 : 『주니어 백엔드 개발자가 반드시 알아야 할 실무지식』 7장
7장은 네트워크 IO와 자원 효율, 가상 스레드, 논블로킹 IO를 다룬다. 기억할 내용은 세 가지다.
- 블로킹 IO에서는 입출력이 끝날 때까지 스레드가 기다린다. 동시 요청을 처리하려고 플랫폼 스레드를 계속 늘리면 메모리와 스케줄링 비용도 고려해야 한다.
- 가상 스레드는 IO 대기가 많은 작업에서 동시성과 처리량을 높이는 수단이다. CPU 중심 작업의 실행 속도를 높여주는 것은 아니다. (OpenJDK JEP 444)
- 논블로킹 IO는 입출력이 끝날 때까지 스레드를 붙잡아 두지 않는 방식이다. 어떤 수단을 쓸지는 네트워크 IO가 실제 병목인지, 구현을 얼마나 바꿀 수 있는지부터 확인한다.
지난주 CDC 실험에서는 CPU 프로파일에 보이는 파싱 비용만으로 병목을 설명할 수 없었다. 대기 시간과 컨슈머 병렬성을 함께 확인했을 때 가장 큰 개선이 나왔다. 이번 장도 성능을 볼 때 실행 중인 코드뿐 아니라 기다리는 시간과 자원 사용 방식을 함께 살펴봐야 한다는 내용으로 이어졌다.
마무리하며
이번 주에는 개인 홈 서버에 MVP 플랫폼을 구현하며 책임과 장애 격리 범위를 정리했다. 장애 원인을 조사하고 복구할 범위가 명확한 구조를 만들고 싶었다.
다음에도 구성 요소를 나눌 때는 입력, 권한, 실행 시간, 실패 대응을 기준으로 본다. 폴더의 경계와 프로세스의 경계도 구분한다. 성능을 개선할 때는 IO가 실제 병목인지 확인한 뒤 자원 효율과 구현 변경 비용을 함께 따진다.