올리브영 테크블로그 포스팅 10년 된 레거시를 현대화하다 - Part.3: 대고객 서비스로의 확장
Tech

10년 된 레거시를 현대화하다 - Part.3: 대고객 서비스로의 확장

DDD로 분리한 매장 도메인, 온·오프라인을 아우르는 대고객 서비스로의 확장 사례

2025.04.30

매장 서비스가 온·오프라인을 아우르는 대고객 서비스로 확장하기까지의 여정

안녕하세요! 올리브영에서 매장 도메인을 담당하고 있는 알렉스입니다 :)

이전 글에서 저희는 DDD의 전략적 설계와 전술적 설계를 통해 매장 도메인을 추출하고, 매장 서비스를 구축하는 과정을 알아보았습니다.
이번 글에서는 저희가 지난 1년간 걸어온 아래의 여정 중, Part.3 대고객 서비스로의 확장 사례를 소개하겠습니다 🚀

  1. 10년 된 레거시를 현대화하다 - Part.1: 도메인 분리의 첫걸음
  2. 10년 된 레거시를 현대화하다 - Part.2: 매장 도메인의 구현 여정
  3. 10년 된 레거시를 현대화하다 - Part.3: 대고객 서비스로의 확장

목차

  1. 온·오프라인 대고객 서비스
    • 오프라인 서비스 : 올리브영N 성수(혁신매장)
    • 온라인 서비스 : 올영매장 서비스
  2. 매장 관련 정보 제공
    • external / internal 환경별 성격 고려하기
    • Rest API HTTP API 설계하기
  3. 개발자 관점에서의 인프라 구성 (feat. TeamCity & AWS ECS)
    • 매장 서비스의 초기 구성
    • 앞으로의 고도화 방향
  4. 모니터링 구성 (feat. Datadog)
    • 대시보드 구축하기
    • 에러 알람 구축하기
  5. 마무리

온·오프라인 대고객 서비스

우선 매장 서비스가 구축되고, 가장 먼저 연계해야 했던 프로젝트를 소개해 보려고 합니다. 올리브영 첫 혁신매장인 올리브영N 성수와 온라인 전시 서비스인 올영매장, 각 프로젝트가 고객들에게 어떤 서비스를 제공하는지 같이 알아보시죠 🚀

오프라인 서비스 : 올리브영N 성수(혁신매장)

작년 말, 2024년 11월 22일에 새로 오픈한 5층 규모의 올리브영 매장에 대해서 알고 계신가요? 아래 기사들처럼 매우 많은 매스컴의 주목을 받았었는데요.


제가 개발리드로 참여한 올리브영N 성수는 각종 체험형 서비스를 제공하는 것이 기존 매장과 가장 큰 차이점입니다. 아래와 같이 여러 가지 체험형 콘텐츠들은 현장에서 바로 예약할 수도 있고, 온라인 전시 서비스인 올영매장에서 사전 예약으로 진행할 수도 있습니다.


기존 매장과의 또 다른 점은 층별 안내 서비스입니다. 올리브영N 성수는 5층 규모 및 약 1400평으로 조성된 매장이기 때문에, 어떤 상품들이 어디에 있고 어떤 행사들이 어디에서 진행하는지 찾기가 어렵습니다. 따라서 매장 1층, 2층, 3층에 안내 키오스크를 1대씩 배치하여, 고객들이 상품 및 행사 위치를 직관적으로 볼 수 있는 기능을 제공하였습니다.

[N성수 키오스크 사진]

[N성수 키오스크의 매장 시설·상품·행사 위치 층별 안내 서비스]


온라인 전시 서비스 : 올영매장 서비스

올영매장 서비스는 고객들에게 어떤 서비스를 제공할까요? 가장 많이 사용되고 있는 것은 상품 상세 페이지 내 구매 가능 올영매장 찾기 기능입니다.



해당 기능을 통해 고객은 사고 싶은 상품에 대한 주변 매장의 재고 정보를 한눈에 확인할 수 있습니다. 또한, 해당 매장의 위치·전화번호·영업시간·휴무일 등의 정보도 같이 볼 수 있습니다.



매장 관련 정보 제공

올리브영N 성수(혁신매장)와 온라인 올영매장 서비스에서 고객에게 제공하고 있는 매장 관련 정보는 모두 매장 서비스 에서 제공합니다. 또한, 대고객 서비스뿐만 아니라 내부 시스템 간의 연동·통신에서도 매장 서비스를 통해 정보를 받아 갑니다.

여기서 저희는 아래와 같은 고민을 하였습니다.

"대고객 서비스(external)와 내부 서비스(internal)에서 모두 같은 정보가 필요할까?"


external / internal 환경별 성격 고려하기

저희는 매장 API를 통해 매장 기본정보를 제공해 주어야 했습니다. 이때 필요한 DB 테이블은 매장 정보 엔티티에 매핑되어 있는 테이블일 것입니다.

Part.2: 매장 도메인의 구현 여정에서 소개한 ERD에서 매장 정보 엔티티의 컬럼 일부를 살펴보겠습니다.


온라인 서비스에서 고객들에게 필요한 정보는 매장명, 매장 전화번호, 매장 주소 등이 있을 것입니다. 그렇다면 예상 일일 매출금액BEP 금액 정보는 어떨까요? 고객 입장에서 과연 필요한 정보일까요? 궁금한 고객도 물론 있겠지만, 회사의 대외비 정보를 함부로 노출시킬 수 없습니다.
따라서 매장의 기본정보 API더라도 해당 정보를 사용하는 클라이언트에 따라서 제공하는 데이터가 다를 수 있습니다.

또 한가지의 다른 점은 데이터 양입니다. 내부 서비스(internal) 간 연동은 보통 많은 컬럼과 데이터를 제공받길 원합니다. 하지만 온라인 서비스(external) 환경에서도 많은 컬럼과 데이터를 한 번에 내려받는다면 어떨까요? 불필요한 컬럼들을 포함한 대형 JSON 페이로드를 한번에 내려받을 경우, HTTP 응답 크기가 불필요하게 커져 네트워크 전송 시간이 길어질 것입니다. 이는 곧 페이지 렌더링 지연이나 API Latency 지연으로 이어지고, 사용자에게 불편함을 초래할 수 있습니다.


REST API HTTP API 설계하기

그렇다면 내부 서비스(internal)와 온라인 서비스(external)는 각각 어떻게 HTTP API를 설계해야 할까요? 흔히 "REST API"라고도 많이 부르지만, 사실 일반적으로는 Uniform interface의 제약조건 중 Self-descriptive와 HATEOAS를 잘 따르기가 어렵기 때문에 해당 포스팅에서는 "HTTP API" 로 부르겠습니다.

  • REST의 자세한 내용이 궁금하다면 Wikipedia를 참고해 보시길 추천드립니다 😁

우선 API 설계에 앞서 HTTP API의 주요 개념에 대해 간단하게 짚고 넘어가겠습니다.


흔히 볼 수 있는 URL은 위와 같은 요소들로 구성되어 있습니다.
여기서 저희는 보통 API를 설계할 때 패스(path), 쿼리 파라미터(query parameter), 요청/응답 본문(request/response body) 등을 주로 설계합니다. 이 때 잘 고려해야 할 부분은 리소스(자원)행위(메서드) 입니다. 위의 예시처럼 매장의 정보가 필요할 경우 "매장(shops)"이 리소스가 되고, "조회(GET)"가 행위가 될 것입니다.

주로 사용되는 HTTP 메서드(행위) 5개에 대해 핵심 내용만 표로 간단하게 소개하겠습니다.

추가로 잘 고려해야 할 부분 중 하나는 멱등성(Idempotent)입니다. 1번 호출하든 100번 호출하든 작업 결과가 항상 동일한지를 의미하는 이 멱등성은 추후 여러 이유로 API 요청이 실패하였을 때 재처리 전략에 용이하게 사용됩니다.
따라서 각 HTTP 메서드에 따라 규약에 맞게 멱등성을 잘 보장하도록 설계해야 합니다.


저희는 우선 조회성 기능을 먼저 제공해야 했기 때문에, 아래와 같이 HTTP API 설계를 진행하였습니다.


예시를 보면 매장 관련 정보에서 매장 기본 정보매장 요약 정보가 나누어져 있습니다.


매장 기본 정보는 매장에 관련된 모든 마스터성 컬럼을 포함한 기본 정보를 반환해 주는 API입니다. 반면 매장 요약 정보는 필요한 컬럼들만 요약해서 반환해 주고, 여러 가지 컬럼을 조합하여 고유의 비즈니스 로직을 통해 특정 상태를 나타내주는 컬럼까지 포함되어 있습니다.
예시로 Part.1 에서 잠깐 소개했던 매장 영업 상태 값은 아래의 컬럼들을 조합하여 비즈니스 로직을 통해 판단하는 컬럼입니다.

  • 매장 운영 상태 컬럼 : 해당 매장이 정상적으로 개점한 매장인지? 아니면 아직 공사 중인 매장이거나 폐점한 매장인지?
  • 매장 휴무일 컬럼 : 오늘이 매장 휴무일인지?
  • 영업시간 컬럼 : 오늘의 영업시간은 몇 시부터 몇 시인지?

따라서 매장의 필수 요약 정보들만 실시간으로 빠르게 볼 수 있는 API는 external, 매장의 모든 마스터성 기본 정보를 방대하게 받을 수 있는 API는 internal로 제공될 것입니다. 실제로 코드상에서도 Presentation 영역인 Controller도 아래와 같이 internal과 external 패키지에 각각 분리되어 있습니다.
아래와 같이 구성되어 있다면 추후 external 환경과 internal 환경을 완전히 분리할 때, 패키지 단위로 모듈을 쪼개기만 하면 간단하게 분리 작업을 완료할 수 있습니다.


개발자 관점에서의 인프라 구성 (feat. TeamCity & AWS ECS)

초기 인프라는 어떻게 구성했을까요? 우선 올리브영은 기본적으로 인프라에 대한 구성 및 관리를 플랫폼엔지니어링 조직이 담당하고 있습니다. 따라서 인프라 구성에 대한 자세한 내용보다는, 개발자 관점에서 설계한 초기 구성도와 CI/CD 설정 등을 간단하게 소개해보겠습니다.


매장 서비스의 초기 구성

우선 올리브영은 기본적으로 AWS ECS Fargate를 통해 애플리케이션을 구동합니다. 그리고 각각의 역할에 맞는 로드밸런서(LB)를 앞단에 배치합니다.
아래는 초기 매장 서비스의 인프라 구성입니다. 초반부터 external 환경과 internal 환경을 분리하기에는 요구사항이 명확하지도 않을뿐더러 너무 이르다고 판단했기 때문에, ECS 클러스터 내부에 하나의 서비스로만 구성되어 있는 모습입니다.


그렇다면 초기 CI/CD 환경은 어떻게 구성했을까요? 올리브영은 현재 JetBrains사의 TeamCity를 CI/CD 파이프라인 도구로 사용하고 있습니다.
각 프로젝트별로 Build 단계와 Deploy 단계를 분리하여 운영 중인데, Build를 수행하면 Docker 이미지를 생성하여 ECR로 업로드(Push)를 진행하고, Deploy를 수행하면 ECR에 저장된 최신 이미지를 가져와 CodeDeploy를 통해 Blue/Green 배포 과정을 거칩니다.



앞으로의 고도화 방향

현재 구성에서 매장 서비스를 사용하는 클라이언트가 많아진다면 어떻게 할까요? 실제로 올리브영에서 "매장"이라는 정보는 거의 모든 영역에서 필요로 합니다. 위의 초기 구성도에서 볼 수 있듯이 초반부터 5개의 클라이언트가 연계되어 있고, 앞으로 점차 늘어날 예정이므로 자연스럽게 매장 API로 들어오는 트래픽 또한 증가할 것입니다.

따라서 많은 클라이언트를 효율적으로 관리하는 방법의 하나는, External API와 Internal API 환경을 분리하는 것입니다. 환경을 2개로 분리한다면, N성수 키오스크와의 통신을 위해 별도로 사용하던 NLB도 ALB로 통일시킬 수 있습니다.


하지만 서비스 환경을 2개로 분리한다면 관리 포인트가 늘어나 유지보수성이 떨어질 수 있습니다. 이 때 도움을 줄 수 있는게 멀티모듈 아키텍처와 AWS ECS 구성입니다.

Part.2: 매장 도메인의 구현 여정에서 소개했듯이 현재 멀티모듈 아키텍처 구성은 api 모듈, service 모듈, domain 모듈로 각각 분리되어 있습니다. 그리고 api 모듈에는 external 패키지와 internal 패키지로 나누어져 있습니다. 여기서 우선 api 모듈을 external-api 모듈internal-api 모듈로 분리합니다. 그리고 아래 예시처럼 각 환경에 필요한 모듈이 조립돼서 배포될 수 있도록 Build 스크립트와 TeamCity 설정을 진행합니다.



그다음 작업으로 매장 서비스 ECS 클러스터 내부에 External API 서비스와 Internal API 서비스를 각각 생성합니다. 그리고 TeamCity CI/CD 설정을 환경별로 구성하고 Build 스크립트를 통해 각 환경에 필요한 모듈을 조립하여 배포한다면, 클라이언트 성격에 따라 매장 API의 유입지점을 분리시킬 수 있어 관리하기 수월해집니다.
개발자 입장에서는 소스코드를 하나의 Repository로 관리하며, Presentation 영역(api)만 분리하고 나머지 모듈은 재사용하면 되므로 유지보수성도 챙겨갈 수 있습니다.
전반적인 인프라 구성은 아래와 같이 될 것입니다.


모니터링 구성 (feat. Datadog)

마지막은 서비스 운영에서 가장 중요한 모니터링입니다. 저희 올리브영은 기본적으로 Datadog을 사용해서 모니터링을 진행하고 있습니다. 최근 Datadog 본사에서 주최하는 DASH 2024 컨퍼런스에서 저희 올리브영의 Datadog 사용 사례를 소개했듯이, 올리브영은 Datadog을 굉장히 유용하게 사용하고 있습니다. DASH 2024 후기가 궁금하다면 아래의 글을 참고해 보시길 추천드립니다 😁


올리브영이 다양한 메트릭을 어떻게 비즈니스에 잘 녹여내고 있는지는 해당 발표 영상에서 상세하게 설명하고 있습니다. 따라서 이번 글에서는 매장 서비스의 모니터링 대시보드와 에러 알람을 어떻게 구축했는지 간단하게 소개해 보겠습니다.


대시보드 구축하기

먼저 대시보드입니다. 대고객 서비스 오픈을 앞두고 어떤 지표들이 필요할까요?
우선 서비스가 잘 살아있는지를 확인해야합니다. 서비스 인스턴스가 몇개가 구동되고 있는지, CPU와 메모리는 얼마나 사용하고 있는지, JVM Heap 메모리와 스레드 개수는 충분히 여유로운지 등을 확인할 수 있어야 합니다.
또한, API 요청이 잘 이루어지고 있는지 확인해야 합니다. 현재 TPS가 얼마나 되는지, 총 요청 수는 어느정도인지, Latency 지연이 발생하고 있지는 않은지, 에러가 발생하고 있는지 등을 확인할 수 있어야 합니다.

확인해야 할 요소들을 모두 대시보드에 넣는다면, 아래와 같이 구성됩니다.


에러 알람 구축하기

대시보드 구축을 완료하고, 저는 또 한 번의 고민에 빠졌습니다.

"어떤 종류의 알람을 어떤 방식으로 구성해야 서비스 장애를 빠르게 인지할 수 있을까?"


가장 중요하게 생각했던 요소는 두 가지였습니다.

  1. 초기 버전은 조회성 API만 제공하므로, HTTP 응답 코드와 응답 속도 위주로 알람을 구성하자
  2. 어떤 문제가 무슨 API에서 일어나고 있는지 명확하게 볼 수 있도록 가독성을 챙기자

HTTP 응답 코드(Status Code)에서의 문제는 크게 4xx 에러5xx 에러로 나눌 수 있습니다. 5xx 에러는 서버와 관련있어 발생 즉시 관리자가 인지하는 것이 바람직합니다. 반면 4xx 에러는 클라이언트와 관련있어서 의도적인 공격처럼 짧은 시간에 비정상적으로 많이 발생하는 경우에만 인지해도 된다고 판단했습니다.

그리고 대고객 서비스에서는 API 요청에 대한 응답 속도가 지연되면 사용자에게 불편함을 줄 수 있으므로, 특정 기준 시간 이상으로 응답 속도가 느려질 경우 저희가 인지할 수 있어야 합니다.
따라서 아래 항목들을 초기 알람으로 설정하였습니다.


그리고 이런 알람이 Slack을 통해 발생하더라도, 에러 종류와 원인을 명확하게 표시하지 않는다면 빠르게 대응할 수 없을 것입니다. 따라서 저희는 어떤 API에서 어떤 알람이 발생했는지, 아래와 같이 알람 포맷을 설정하여 가독성을 높였습니다.


이처럼 알람 포맷에 어떤 환경에서 어떤 에러 알람이 발생했고, 현재 수치가 어느 정도이며 해당 이슈에 빠르게 대응하기 위한 관련 Dashboard, Trace, Logs 링크를 포함해 놓음으로써 보다 빠르게 문제를 인지하고 해결할 수 있도록 구축하였습니다.


마무리

올해 1월부터 포스팅한 10년 된 레거시를 현대화하다 시리즈는 그레이 영역인 매장 도메인이 중심이었습니다. 아무것도 준비되지 않았던 2024년부터 레거시를 개선하기 위해 고군분투했고, 0에서부터 차근차근 1을 만들어가는 과정을 담아냈습니다. 1로 만든 매장 서비스는 현재 많은 스쿼드에서 사용하며 100을 향해 달려 나가고 있습니다.

시리즈를 돌아보면 이벤트 스토밍을 통한 도메인 식별을 시작으로, 멀티모듈 스켈레톤 프로젝트 구축과 인프라 아키텍처 설계 및 모니터링 구축까지 소개해 보았는데요. 이외에도 성능 테스트 전략, PDCA 기반의 성능 테스트 결과 템플릿, Github Actions을 활용한 편의성 증대, SonarQube 설정을 통한 테스트 커버리지 측정 자동화 등 아직 소개하지 못한 경험도 많습니다.

하지만 아쉽게도 저의 이번 시리즈는 여기서 마무리해야 될 것 같습니다.
저는 이제 올리브영에서 매장 도메인 대신 "정산 도메인"을 담당하게 되었는데요. 😁

다음 포스팅에서는 CJ올리브영의 정산 영역을 어떻게 차근차근 개선해 나가는지 또 하나의 시리즈로 찾아뵙겠습니다.

이번 시리즈에 많은 관심을 가져주셔서 매우 감사드리며, 앞으로의 경험도 많은 기대와 응원 부탁드립니다! 🚀



알렉스 로그


N성수 & 올영매장AWS ECSDatadog
올리브영 테크 블로그 작성 10년 된 레거시를 현대화하다 - Part.3: 대고객 서비스로의 확장
🐯
알렉스(범) |
Back-end Engineer
소통을 좋아하고 꾸준함의 가치를 향해 달려가는 프로그래머 알렉스입니다 :)