QnA

CAP 설계 면접에서 틀리는 질문 5가지 #shorts #SAP #QnA

▶ YouTube에서 보기

왜 CAP 아키텍트 면접은 코드 한 줄로 합격이 갈릴까?

SAP BTP 프로젝트가 늘어나면서 CAP(Cloud Application Programming Model) 아키텍트 포지션의 채용도 활발해졌습니다. 그런데 막상 면접장에 들어가 보면 "CDS 엔티티 어떻게 정의하나요" 같은 표면적 질문이 아니라, "왜 Composition을 썼나요" "Remote Service를 Projection으로 노출했을 때 발생할 수 있는 문제는 무엇인가요" 같은 설계 의사결정에 관한 질문이 쏟아집니다. 이 글에서는 실제 BTP 현장에서 아키텍트가 마주치는 6가지 핵심 면접 주제를 "잘못된 답변 vs 올바른 답변" 포맷으로 정리합니다. 단순 암기가 아니라 의사결정 근거를 면접관 앞에서 설명할 수 있도록 동작 원리까지 함께 파고듭니다.

이 글에서 점검할 6가지 영역

  • CDS 모델링: Composition vs Association, Managed vs Unmanaged
  • Remote Service 연동 패턴(Mashup / Delegation / Projection)
  • CAP Node.js와 Java 런타임 선택 기준
  • Multitenancy(MTX) 구현 전략
  • Event Mesh 기반 비동기 메시징 설계
  • 성능 튜닝 — eager loading과 N+1 문제

읽기 전에 갖춰두면 좋은 배경 지식

이 글은 CAP의 기본 문법(서비스 정의, CDS 엔티티, srv 폴더 구조)은 사용해 본 경험이 있다고 가정합니다. Node.js Express 또는 Java Spring Boot 중 한 가지 백엔드 경험, OData v4 프로토콜의 기본 개념($expand, $filter), 그리고 BTP Cockpit에서 서비스 인스턴스를 바인딩해 본 경험이 있으면 의사결정 사례를 더 빠르게 이해할 수 있습니다.

실습 환경 및 버전 정보

아래 코드는 CAP Node.js 7.x / CAP Java 2.x 기준으로 작성했습니다. SAP BTP Cloud Foundry 또는 Kyma 런타임에서 동작하며, HANA Cloud(또는 SQLite로 로컬 테스트), Destination Service, XSUAA, Event Mesh가 바인딩된 환경을 전제로 합니다. 로컬에서 빠르게 검증하려면 다음 도구를 준비합니다.

  • Node.js 20 LTS 또는 JDK 17
  • @sap/cds-dk 최신 버전 (npm i -g @sap/cds-dk)
  • Cloud MTA Build Tool (mbt)
  • cf CLI 8.x, MultiApps 플러그인
  • BTP Cockpit 접근 권한(서브계정 Admin 이상)

면접관이 진짜로 보고 싶어 하는 핵심 개념 지도

아키텍트 면접은 "지식의 양"이 아니라 "선택의 이유"를 봅니다. CAP을 사람의 몸에 비유하면 CDS는 골격, Service는 신경계, Event Mesh는 호르몬 분비계입니다. 각 구성요소가 어떤 상황에서 어떤 역할을 맡아야 하는지 정합성 있게 설명할 수 있어야 합니다.

CDS 모델은 단순한 테이블 정의가 아니라, 도메인 무결성을 코드보다 먼저 표현하는 계약서입니다. 따라서 Composition 선택 하나에도 "트랜잭션 경계가 어디까지인가"라는 설계 철학이 담깁니다.

특히 다음 다섯 가지 키워드는 면접 도중 반드시 등장합니다.

  1. Composition: 부모-자식 라이프사이클이 하나로 묶이는 강한 소유 관계. Deep Insert/Delete가 자동 처리됩니다.
  2. Association: 참조만 하는 약한 관계. 외래키 무결성은 보장되지만 cascade는 일어나지 않습니다.
  3. Managed Composition: CAP이 키와 외래키를 자동 생성하고 to-N에서 backlink를 추론합니다.
  4. Projection: 기존 엔티티(또는 Remote 엔티티)를 재사용해 서비스 레이어를 노출하는 방식.
  5. Delegation: srv.run을 활용해 Remote Service로 요청을 위임하고 결과만 가공해 돌려주는 패턴.

이 다섯 키워드의 조합이 곧 아키텍처 설계 전략입니다. 면접관은 "왜 Mashup 대신 Delegation을 골랐는가" 같은 질문을 통해 후보자의 트레이드오프 감각을 측정합니다. 일반적으로 응답 데이터 크기가 작고 사용자 인터랙션이 잦으면 Projection, 트랜잭션 무결성이 중요하면 Delegation, 여러 백엔드를 합쳐 보여줘야 하면 Mashup이 권장됩니다.

면접 질문 1단계 — CDS 모델링: Composition을 언제 써야 하나요?

가장 자주 등장하는 워밍업 질문입니다. 시나리오는 "주문(Order)과 주문 항목(OrderItem)을 어떻게 모델링하시겠습니까?"입니다.

잘못된 답변은 무조건 Association으로 묶고 외래키를 수동 관리하는 경우입니다.

// 잘못된 예: 라이프사이클을 분리해 cascade 처리를 직접 구현해야 함
namespace my.ordermgmt;

entity Orders {
  key ID    : UUID;
  customer  : String;
  items     : Association to many OrderItems on items.order = $self;
}

entity OrderItems {
  key ID   : UUID;
  order    : Association to Orders;
  product  : String;
  qty      : Integer;
}

이 모델은 Orders 삭제 시 OrderItems가 고아 레코드로 남습니다. 또한 OData Deep Insert가 자동 동작하지 않아 클라이언트에서 두 번의 요청을 보내야 합니다. 올바른 답변은 Composition으로 라이프사이클을 일치시키는 것입니다.

namespace my.ordermgmt;
using { cuid, managed } from '@sap/cds/common';

entity Orders : cuid, managed {
  customer : String(100) not null;
  status   : String(20) default 'OPEN';
  items    : Composition of many OrderItems on items.parent = $self;
}

entity OrderItems : cuid {
  parent  : Association to Orders;
  product : String(40);
  qty     : Integer default 1;
  price   : Decimal(11,2);
}

이렇게 정의하면 DELETE /Orders(ID) 한 번으로 자식까지 정리되고, POST 한 번으로 Deep Insert가 가능합니다. 면접에서는 "Composition은 트랜잭션 경계와 도메인 Aggregate Root를 표현한다"고 답하면 좋은 인상을 줍니다.

면접 질문 2단계 — Remote Service: S/4HANA Business Partner를 어떻게 노출하나요?

실무에서 가장 자주 마주치는 패턴은 S/4HANA Cloud에서 ABusiness Partner API를 끌어와 자사 앱과 합치는 경우입니다. 잘못된 답변은 axios로 직접 HTTP 요청을 보내고 토큰을 코드로 관리하는 방식입니다.

// 잘못된 예: Destination Service와 토큰 관리를 우회
const axios = require('axios');
module.exports = async (srv) => {
  srv.on('READ', 'Partners', async () => {
    const res = await axios.get('https://my-s4.com/API_BUSINESS_PARTNER/A_BusinessPartner', {
      headers: { Authorization: 'Basic ' + Buffer.from('user:pass').toString('base64') }
    });
    return res.data.d.results;
  });
};

이 코드는 Destination Service를 우회해 자격증명이 평문으로 노출되고, Principal Propagation도 불가능합니다. 올바른 답변은 CDS Import + cds.connect.to + 위임 패턴입니다.

// srv/partner-service.js — Delegation 패턴
const cds = require('@sap/cds');

module.exports = class PartnerService extends cds.ApplicationService {
  async init() {
    const s4 = await cds.connect.to('API_BUSINESS_PARTNER');
    const { Partners } = this.entities;

    this.on('READ', Partners, async (req) => {
      // CAP이 Destination + JWT를 자동 처리
      return s4.run(req.query);
    });

    this.on('READ', Partners, async (req, next) => {
      const rows = await next();
      // 자사 도메인 필드 보강
      return rows.map(r => ({ ...r, displayName: `${r.firstName} ${r.lastName}` }));
    });

    return super.init();
  }
};

package.json의 cds.requires에서 destination 이름과 인증 방식을 선언하면, BTP Destination에 등록된 자격증명을 자동으로 가져옵니다. Principal Propagation도 user-token 옵션 한 줄로 처리됩니다.

{
  "cds": {
    "requires": {
      "API_BUSINESS_PARTNER": {
        "kind": "odata-v2",
        "model": "srv/external/API_BUSINESS_PARTNER",
        "credentials": { "destination": "S4HC_BP", "path": "/sap/opu/odata/sap/API_BUSINESS_PARTNER" }
      }
    }
  }
}

면접 질문 3단계 — Node.js와 Java 중 무엇을 고르겠습니까?

이 질문에서 "그냥 익숙한 거 쓴다"고 답하면 탈락입니다. 의사결정 기준을 구조화해 답해야 합니다. 일반적으로 다음과 같은 기준이 권장됩니다.

기준CAP Node.jsCAP Java
개발 속도매우 빠름(핫리로드, 적은 보일러플레이트)Spring Boot 기반, 컴파일 단계 존재
장기 실행 트랜잭션이벤트 루프 특성상 부적합스레드 모델로 안정적
레거시 Java 자산 연계제한적JCA, JMS 등 풍부
메모리 사용량적음JVM 워밍업 비용 있음
OData v2 Adapterplug-in 형태기본 내장

"OData v2 어댑터가 필수이고 Spring 생태계와 통합이 필요하면 Java, 빠른 프로토타입과 SaaS 멀티테넌시 확장은 Node.js"라고 답하는 것이 일반적으로 권장됩니다.

면접 질문 4단계 — Multitenancy를 어떻게 설계할 건가요?

잘못된 답변은 테넌트별로 앱을 별도 배포하는 방식입니다. 비용과 운영 부담이 폭증합니다. 올바른 답변은 @sap/cds-mtxs를 활용한 Shared App + Tenant DB 모델입니다.

// server.js
const cds = require('@sap/cds');
const cov2ap = require('@cap-js-community/odata-v2-adapter');

cds.on('bootstrap', (app) => app.use(cov2ap()));
module.exports = cds.server;
{
  "cds": {
    "requires": {
      "multitenancy": true,
      "db": { "kind": "hana-cloud" },
      "auth": { "kind": "xsuaa" }
    },
    "mtx": {
      "provisioning": { "autoUndeploy": true }
    }
  }
}

이 구성에서 테넌트 구독 시 mtxs가 HDI 컨테이너를 자동 생성하고, 구독 해지 시 정리합니다. SaaS Registry와 SaaS Approuter를 통해 서브도메인 라우팅이 이루어지며, 코드 변경은 거의 없습니다.

면접 질문 5단계 — Event Mesh와 CAP Messaging 설계

"주문이 생성되면 배송 시스템에 알려야 한다. 동기 호출 대신 어떻게 설계하시겠습니까?" 잘못된 답변은 HTTP 콜백을 직접 호출하는 것입니다. 올바른 답변은 CAP Messaging Outbox + Event Mesh 조합입니다.

module.exports = class OrderService extends cds.ApplicationService {
  async init() {
    const messaging = await cds.connect.to('messaging');
    const { Orders } = this.entities;

    this.after('CREATE', Orders, async (data, req) => {
      await messaging.emit('my.ordermgmt.OrderCreated', {
        orderID: data.ID,
        customer: data.customer,
        createdAt: new Date().toISOString()
      });
    });

    return super.init();
  }
};

cds.requires.messaging.kind를 enterprise-messaging-shared로 설정하면 CAP은 Outbox 패턴을 자동 적용합니다. 즉 DB 트랜잭션 커밋 후에만 이벤트가 발행되어 "DB는 롤백됐는데 이벤트는 나간" 사고를 막아 줍니다.

면접 질문 6단계 — N+1 문제와 Eager Loading 전략

잘못된 답변: 루프 안에서 cds.read를 호출하는 코드.

// 100개 주문이면 101번 쿼리 발생
const orders = await SELECT.from(Orders);
for (const o of orders) {
  o.items = await SELECT.from(OrderItems).where({ parent_ID: o.ID });
}

올바른 답변은 CQL의 expand 절(또는 OData $expand)을 활용해 한 번에 조인하는 것입니다.

const orders = await SELECT.from(Orders, o => {
  o.ID, o.customer, o.items(i => { i.product, i.qty, i.price })
});

또한 HANA Cloud에서는 인덱스와 calculation view를 활용해 집계 쿼리를 최적화하고, 자주 읽는 마스터 데이터는 cds.cache로 메모리 캐싱하는 전략도 함께 언급하면 가산점이 붙습니다.

면접에서 자주 빠지는 함정과 FAQ

Q1. Composition이면 무조건 cascade delete인가요? 아닙니다. Composition은 "부모 라이프사이클에 종속"을 의미하지만, DB 레벨 cascade는 어댑터 설정에 따라 다릅니다. CAP은 애플리케이션 레이어에서 처리하며, 직접 SQL DELETE를 날리면 정합성이 깨질 수 있습니다.

Q2. Remote Service Mashup과 Delegation은 뭐가 다른가요? Mashup은 여러 소스를 합쳐 새로운 응답을 만드는 패턴이고, Delegation은 단일 소스로 요청을 전달만 합니다. Mashup은 네트워크 비용이 누적되므로 캐싱이 필수입니다.

Q3. MTX에서 테넌트별 커스텀 필드를 어떻게 지원하나요? @sap/cds-mtxs의 extensibility 모듈을 사용하면 테넌트가 런타임에 필드를 추가할 수 있습니다. 단, 코어 엔티티에 @extensible 어노테이션이 선언되어 있어야 합니다.

Q4. Event Mesh 메시지가 중복 수신되면? Event Mesh는 at-least-once 보장이라 멱등성(idempotency) 처리는 컨슈머 책임입니다. messageId를 키로 한 dedup 테이블을 두는 것이 일반적입니다.

면접 합격 후 더 깊게 파야 할 주제

아키텍트 채용이 확정되면 곧바로 다음 주제로 학습을 확장하는 것이 좋습니다. CAP plugin 개발(@cap-js 생태계), Hybrid Testing(로컬에서 BTP 서비스 바인딩), Feature Toggle, MTA 기반 블루-그린 배포, 그리고 SAP Build Code와의 통합이 차기 1년의 핵심 주제입니다. 또한 CAP Java의 Reactive 모델(WebFlux 통합)도 대규모 트래픽 시나리오에서 검토 가치가 있습니다.

더 읽어보면 좋은 자료

댓글 0

아직 댓글이 없습니다.