서버 애플리케이션 옆에는 거의 늘 관계형 DB, 인메모리 캐시, 메시지 브로커(message broker), 검색 엔진이 붙는다. 하나로 다 하지 않는 이유는 데이터의 성격(영구성·속도·전달·검색)마다 잘하는 도구가 다르기 때문이다.
| 관계형 DB (PostgreSQL) | 캐시 (Redis) | 브로커 (Kafka) | 검색 (OpenSearch) | |
|---|---|---|---|---|
| 저장 위치 | 디스크(영구) | 메모리(휘발) | 디스크(로그) | 디스크(색인) |
| 잘하는 것 | 정확한 저장·트랜잭션 | 초고속 읽기·임시값 | 서비스 간 비동기 전달 | 텍스트 검색·관련도 |
| 잃으면 | 치명적 | 다시 채우면 됨 | 재처리 가능 | DB에서 재색인 |
| 비유 | 공식 장부 | 포스트잇 | 사내 우편함 | 도서관 색인 카드 |
- 관계형 DB: 주문·회원처럼 잃으면 안 되는 데이터의 원본. ACID 트랜잭션으로 정확성을 지킨다. 읽기가 많아지면 읽기 복제본을 둔다
- 캐시: 자주 읽는 값, 세션, 요청 카운터를 메모리에 둔다(Redis (인메모리 저장소), 읽기 캐시 전략)
- 브로커: "주문 생성됨" 같은 이벤트를 던지고 끝내면 재고·알림·통계가 각자 받아 처리한다. 서비스끼리 직접 알 필요가 없다(Kafka)
- 검색: DB 데이터를 복사해 역색인(inverted index)을 만들어 두고 검색만 맡긴다(역색인과 검색 엔진)
설정은 환경변수로 주입
DB 주소·캐시 주소·비밀키처럼 환경마다 달라지는 값은 코드나 이미지에 넣지 않고 환경변수로 바깥에서 주입한다. 그러면 같은 이미지를 로컬·개발·운영에 그대로 쓰고 연결할 저장소만 바꿀 수 있다. 12-Factor App(The Twelve-Factor App)이 "Config"와 "Backing services" 원칙으로 정리한 내용이다. 스프링 부트는 SPRING_DATASOURCE_URL 같은 환경변수를 설정 파일보다 우선해 프로퍼티(spring.datasource.url)에 묶어 주므로, 이미지 안의 값을 코드 수정 없이 덮을 수 있다(스프링 설정 외부화와 프로파일). 컨테이너 안에서 주소를 어떻게 적는지는 컨테이너 네트워킹를 본다.
대가
원본(DB) 외의 저장소는 대부분 복사본이다. 복사본이 생기는 순간 둘을 맞추는 일이 생긴다. 그래서 이 구조에는 CDC (변경 데이터 캡처), 트랜잭셔널 아웃박스 패턴, 최종 일관성 같은 동기화 문제가 따라온다. "읽기를 빠르게 하는 거의 모든 기법은 쓰기 비용이나 정합성으로 값을 치른다"는 DB 인덱스와 트레이드오프의 법칙이 시스템 수준에서 다시 나타나는 것이다.
규모가 커지며 이 구성요소가 하나씩 추가되는 과정은 사용자 수에 따른 규모 확장에서 볼 수 있다.
참고: The Twelve-Factor App — Config · Backing services · Spring Boot — Binding From Environment Variables