제약 조건은 문제 끝에 붙은 참고 사항이 아니다

vx_developer·2026년 9월 9일

코테보다가

목록 보기
7/26
post-thumbnail

알고리즘 문제를 처음 풀 때 제약 조건은 보통 입력 범위와 함께 주어진다.

쿠폰의 개수 N은 1 이상 100,000 이하이다.
제한 시간은 1초이고 메모리 제한은 256MB이다.

사용 가능한 쿠폰을 찾는 문제라면 다음과 같이 작성할 수 있다.

function getAvailableCoupons(coupons, now) {
  return coupons.filter(
    (coupon) =>
      coupon.status === "ACTIVE" &&
      coupon.remainingUses > 0 &&
      now < coupon.expiresAt
  );
}

이 코드는 쿠폰을 하나씩 확인하고 조건을 만족하는 쿠폰만 반환한다.

입력 크기와 제한 시간을 보고 현재 방법이 충분한지 판단하는 연습은 알고리즘의 비용을 이해하는 데 유용하다.

하지만 실제 서비스를 개발할 때는 제약 조건이 이렇게 정리되어 전달되지 않는다.

기획서에는 보통 다음과 같이 적혀 있다.

사용자가 보유한 쿠폰 중 지금 사용할 수 있는 쿠폰을 보여준다.

이 요구사항만으로는 적절한 해결 방법을 선택하기 어렵다.

  • 사용자 한 명이 보유한 쿠폰은 평균 몇 개인가?
  • 가장 많은 사용자는 몇 개까지 보유할 수 있는가?
  • 전체 쿠폰은 얼마나 빠르게 증가하는가?
  • 한 번에 모든 쿠폰을 보여줘야 하는가?
  • 화면은 몇 초 안에 표시되어야 하는가?
  • 쿠폰 상태는 얼마나 자주 변경되는가?
  • 사용 가능 여부는 어느 정도까지 최신이어야 하는가?
  • 같은 쿠폰을 여러 사용자가 동시에 사용할 수 있는가?
  • 서버 한 대가 처리해야 하는 요청은 초당 몇 건인가?
  • 데이터베이스 조회와 네트워크 전송에는 얼마의 비용이 드는가?

이 질문에 대한 답이 달라지면 같은 기능에도 다른 알고리즘과 자료구조가 필요하다.

실제 서비스에서 제약 조건은 구현 이후 성능을 확인하기 위한 참고 정보가 아니라, 어떤 데이터를 어디에서 어떻게 처리해야 하는지 결정하는 설계의 출발점이다.

같은 정답을 만드는 코드도 처리할 수 있는 범위가 다르다

크리스가 보유한 쿠폰 10개가 이미 브라우저에 있다고 생각해보자.

const availableCoupons = coupons.filter(
  (coupon) =>
    coupon.status === "ACTIVE" &&
    coupon.remainingUses > 0 &&
    serverNow < coupon.expiresAt
);

쿠폰이 10개라면 배열 전체를 한 번 확인하는 방식은 단순하고 충분히 빠르다.

그런데 서버가 전체 사용자에게 발행된 쿠폰 1,000만 개를 데이터베이스에서 가져온 뒤 같은 코드를 실행한다면 상황이 달라진다.

const coupons = await couponRepository.findAll();

const availableCoupons = coupons.filter(
  (coupon) =>
    coupon.recipientId === currentUser.id &&
    coupon.status === "ACTIVE"
);

결과는 만들 수 있지만 한 사용자의 쿠폰을 찾기 위해 전체 쿠폰을 서버 메모리로 가져온다.

문제는 filter 문법에 있지 않다. 처리 대상의 범위를 정하지 않은 데 있다.

필요한 데이터만 저장소에서 조회해야 한다.

SELECT id, title, expires_at, remaining_uses
FROM coupons
WHERE recipient_id = $1
  AND status = 'ACTIVE'
  AND remaining_uses > 0
  AND expires_at > $2
ORDER BY expires_at ASC
LIMIT 20;

이 쿼리는 현재 사용자의 활성 쿠폰 중 사용할 수 있는 데이터 20개만 반환한다.

같은 “사용 가능한 쿠폰 찾기”이지만 처리하는 위치와 데이터 양이 달라졌다.

상황처리 범위적절한 접근
화면에 이미 있는 쿠폰 10개매우 작음배열에서 직접 필터링
사용자별 쿠폰 수백 개작거나 중간서버 또는 데이터베이스에서 조건 검색
전체 쿠폰 수백만 개큼사용자와 상태 기준 인덱스 활용
한 화면에 20개만 필요출력이 제한됨정렬과 LIMIT으로 필요한 만큼만 조회
다음 페이지도 계속 조회결과가 여러 구간으로 나뉨안정적인 페이지네이션 적용

알고리즘을 선택하려면 코드가 아니라 데이터의 위치와 크기부터 확인해야 한다.

평균 데이터만 보면 가장 중요한 사용자를 놓칠 수 있다

서비스 기획자가 다음과 같이 설명할 수 있다.

사용자는 평균적으로 쿠폰을 12개 정도 보유한다.

이 정보만 보면 모든 쿠폰을 확인하는 방식으로 충분해 보인다.

하지만 평균은 데이터의 분포를 모두 보여주지 않는다.

  • 신규 사용자는 쿠폰이 0개일 수 있다.
  • 일반 사용자는 5개에서 20개를 보유할 수 있다.
  • 이벤트에 자주 참여한 사용자는 500개를 보유할 수 있다.
  • 테스트 계정이나 기업 고객은 수만 개를 보유할 수 있다.

평균이 12개여도 특정 사용자의 쿠폰 화면은 매우 느릴 수 있다.

function getAvailableCoupons(coupons, now) {
  return coupons
    .filter((coupon) => isAvailable(coupon, now))
    .sort(
      (first, second) =>
        first.expiresAt - second.expiresAt
    );
}

이 코드는 모든 쿠폰을 필터링한 뒤 남은 쿠폰 전체를 정렬한다.

대부분의 사용자에게는 문제가 없더라도 쿠폰을 수만 개 가진 사용자에게는 계산량과 메모리 사용량이 크게 증가한다.

따라서 다음 값을 함께 확인해야 한다.

  • 평균 보유 개수
  • 상위 1% 사용자의 보유 개수
  • 현재 최댓값
  • 앞으로 허용할 최댓값
  • 일정 기간 동안의 증가 속도
  • 비정상적으로 큰 데이터가 생길 가능성

실제 서비스에서는 평균적인 경우뿐 아니라 시스템이 정상적으로 지원하기로 한 최대 범위를 정의해야 한다.

제약 조건은 “현재 대부분의 데이터가 얼마나 작은가”가 아니라 어디까지 정상적인 사용으로 책임질 것인가를 정하는 기준이다.

필요한 출력이 작으면 전체 문제를 해결할 필요가 없다

쿠폰 화면에 모든 결과가 필요한지 확인해보자.

기획 요구사항이 다음과 같을 수 있다.

홈 화면에 가장 먼저 만료되는 쿠폰 3개를 보여준다.

전체 쿠폰을 정렬한 뒤 앞의 3개를 선택할 수 있다.

function findExpiringCoupons(coupons, now) {
  return coupons
    .filter((coupon) => isAvailable(coupon, now))
    .sort(
      (first, second) =>
        first.expiresAt - second.expiresAt
    )
    .slice(0, 3);
}

결과는 올바르다.

하지만 출력이 3개뿐인데 모든 사용 가능한 쿠폰의 순서를 결정한다.

데이터가 이미 애플리케이션 메모리에 있고 개수가 많다면 가장 가까운 후보 3개만 유지하는 방법을 고려할 수 있다.

function findExpiringCoupons(coupons, now) {
  const nearest = [];

  for (const coupon of coupons) {
    if (!isAvailable(coupon, now)) {
      continue;
    }

    nearest.push(coupon);
    nearest.sort(
      (first, second) =>
        first.expiresAt - second.expiresAt
    );

    if (nearest.length > 3) {
      nearest.pop();
    }
  }

  return nearest;
}

이 코드는 현재까지 확인한 쿠폰 중 만료일이 가장 가까운 3개만 유지한다.

다만 데이터가 데이터베이스에 있다면 서버로 모두 가져오는 것보다 저장소가 정렬과 제한을 처리하도록 하는 편이 자연스럽다.

SELECT id, title, expires_at
FROM coupons
WHERE recipient_id = $1
  AND status = 'ACTIVE'
  AND expires_at > $2
ORDER BY expires_at ASC
LIMIT 3;

어떤 방법이 적절한지는 입력의 크기만으로 결정되지 않는다.

출력이 전체 목록인지, 일부 목록인지, 하나의 값인지도 처리 비용을 바꾼다.

제약 조건은 입력의 최대 크기뿐 아니라 실제로 필요한 출력의 크기도 포함한다.

한 번 실행하는 기능과 계속 반복되는 기능은 다르게 설계해야 한다

크리스가 쿠폰 화면을 한 번 열 때 사용 가능한 쿠폰을 계산하는 비용은 작을 수 있다.

하지만 같은 계산이 매 요청마다 반복된다면 전체 비용은 커진다.

for (const coupon of coupons) {
  const campaign =
    await campaignRepository.findById(
      coupon.campaignId
    );

  evaluateCoupon(coupon, campaign);
}

쿠폰이 20개라면 캠페인 정보를 20번 별도로 조회한다.

사용자 한 명의 요청만 보면 많지 않아 보이지만, 동시에 1,000명이 화면을 열면 짧은 시간에 수만 번의 데이터베이스 조회가 발생할 수 있다.

필요한 캠페인을 한 번에 조회하고 ID로 찾을 수 있는 구조를 만들 수 있다.

const campaignIds = [
  ...new Set(
    coupons.map((coupon) => coupon.campaignId)
  ),
];

const campaigns =
  await campaignRepository.findByIds(campaignIds);

const campaignById = new Map(
  campaigns.map((campaign) => [
    campaign.id,
    campaign,
  ])
);

이 코드는 필요한 캠페인의 ID를 모아 한 번에 조회한 뒤, 반복적으로 찾기 쉽도록 Map에 저장한다.

한 번의 실행에서 빠른지만 보면 첫 번째 코드도 동작한다. 그러나 요청 빈도를 제약 조건에 포함하면 반복적인 데이터베이스 접근이 더 큰 문제라는 사실을 알 수 있다.

서비스에서는 다음 두 비용을 함께 봐야 한다.

요청 한 건의 비용 × 일정 시간 동안의 요청 수

개별 요청이 50밀리초에 끝나더라도 동시에 너무 많은 데이터베이스 연결을 사용하면 다른 기능까지 느려질 수 있다.

알고리즘의 비용은 함수 한 번의 실행 횟수가 아니라 서비스 전체에서 얼마나 자주 반복되는지까지 포함한다.

응답 시간 제약은 처리 위치를 결정한다

쿠폰 목록을 1초 안에 보여줘야 한다고 생각해보자.

이때 “1초”는 서버 함수의 실행 시간만 의미하지 않는다.

사용자가 화면을 보기까지는 여러 비용이 더해진다.

구간발생하는 비용
브라우저요청 생성과 응답 처리
네트워크요청과 응답 전송
서버인증, 검증과 비즈니스 로직
데이터베이스데이터 검색과 정렬
브라우저목록 렌더링과 이미지 표시

서버에서 배열을 정렬하는 데 5밀리초밖에 걸리지 않아도 데이터베이스 조회가 800밀리초 걸릴 수 있다. 응답에 불필요한 이미지 데이터가 포함되어 네트워크 전송이 오래 걸릴 수도 있다.

따라서 다음처럼 목표를 구체적으로 나눌 수 있다.

항목목표 예시
전체 화면 표시1초 이내
서버 응답400밀리초 이내
데이터베이스 조회150밀리초 이내
반환할 쿠폰 수최대 20개
응답 본문 크기최대 100KB

이 숫자는 모든 서비스에 동일하게 적용되는 기준이 아니다. 서비스의 사용자 경험과 운영 환경에 따라 정해야 하는 예시다.

중요한 점은 “빨라야 한다”를 측정할 수 있는 제약으로 바꾸는 것이다.

측정 가능한 제약이 있어야 어느 구간을 개선해야 하는지 판단할 수 있다.

메모리 제약은 무엇을 미리 저장할지 결정한다

조회 속도를 높이기 위해 모든 활성 쿠폰을 서버 메모리에 저장할 수도 있다.

const activeCoupons =
  await couponRepository.findAllActive();

cache.set("active-coupons", activeCoupons);

이후에는 데이터베이스를 조회하지 않고 메모리에서 쿠폰을 찾을 수 있다.

하지만 전체 쿠폰이 계속 증가한다면 메모리 사용량도 함께 증가한다. 서버가 여러 대라면 각 서버가 같은 데이터를 중복 저장할 수도 있다.

반대로 아무것도 저장하지 않으면 모든 요청이 데이터베이스에 집중된다.

여기에서 시간과 공간 사이의 선택이 생긴다.

선택얻는 것부담하는 것
매번 데이터베이스 조회최신 데이터와 단순한 구조반복되는 조회 비용
전체 데이터를 메모리에 저장빠른 읽기큰 메모리 사용량과 갱신 비용
자주 조회되는 데이터만 캐시비용과 메모리의 절충어떤 데이터를 유지할지 결정해야 함
계산 결과를 미리 저장빠른 응답원본 변경 시 다시 계산해야 함
인덱스 추가검색 범위 감소저장 공간과 쓰기 비용 증가

공간 제약은 단순히 “메모리를 적게 써야 한다”는 의미가 아니다.

검색 속도를 높이기 위해 어떤 정보를 중복 저장할 수 있는지, 그 데이터를 최신 상태로 유지하는 비용을 감당할 수 있는지 결정하는 조건이다.

최신성 제약은 캐시 사용 가능 여부를 바꾼다

쿠폰 목록은 자주 조회되므로 5분 동안 캐시하기로 했다고 생각해보자.

const cachedCoupons = cache.get(userId);

if (cachedCoupons) {
  return cachedCoupons;
}

읽기 성능은 좋아질 수 있다.

하지만 캐시를 만든 직후 쿠폰이 사용되거나 취소되면 최대 5분 동안 오래된 상태를 보여줄 수 있다.

여기에서 필요한 질문은 “캐시가 빠른가?”가 아니다.

이 기능은 얼마나 오래된 데이터를 허용할 수 있는가?

쿠폰 목록 미리보기에서는 몇 초 정도의 차이를 허용할 수 있다. 그러나 결제 확정 단계에서 쿠폰 사용 가능 여부를 판단할 때는 오래된 값을 사용할 수 없다.

const previewCoupons =
  await couponCache.findByUserId(userId);

이 결과는 화면에 예상 가능한 쿠폰을 보여주는 데 사용할 수 있다.

최종 사용 단계에서는 데이터베이스의 최신 상태를 확인해야 한다.

const redeemedCoupon =
  await couponRepository.redeemIfAvailable({
    couponId,
    userId,
    redeemedAt: serverNow,
  });

화면의 쿠폰 목록은 미리보기 데이터이고, 최종 쿠폰 상태의 Source of Truth는 데이터베이스다.

같은 데이터라도 기능에 따라 허용하는 최신성이 다르다.

기능허용 가능한 최신성고려할 수 있는 방식
쿠폰 목록 미리보기짧은 지연을 허용할 수 있음제한적인 캐시 사용
쿠폰 상세 조회비교적 최신이어야 함짧은 캐시 또는 데이터베이스 조회
쿠폰 사용 가능 여부처리 순간의 최신 상태 필요데이터베이스에서 재검증
남은 횟수 차감오래된 값 허용 불가조건부 갱신과 트랜잭션
운영 통계일정 시간 지연 가능배치 집계 또는 미리 계산

성능 제약만 보면 캐시가 좋은 선택처럼 보인다. 정확성과 최신성 제약을 함께 보면 사용할 수 있는 위치가 제한된다.

정확성 제약은 더 빠른 방법을 선택하지 못하게 할 수 있다

크리스가 1회용 쿠폰을 두 기기에서 거의 동시에 사용했다고 생각해보자.

애플리케이션에서 먼저 확인하고 나중에 차감하면 두 요청이 모두 성공할 수 있다.

const coupon = await couponRepository.findById(
  couponId
);

if (coupon.remainingUses > 0) {
  await couponRepository.decreaseRemainingUses(
    couponId
  );
}

이 코드는 읽기와 변경이 분리되어 있다.

동시 요청이 들어오면 두 요청이 같은 remainingUses 값을 읽을 수 있다.

쿠폰은 남은 횟수가 있을 때만 차감되어야 한다는 정확성 제약이 있다. 따라서 확인과 변경을 하나의 작업으로 처리해야 한다.

UPDATE coupons
SET remaining_uses = remaining_uses - 1
WHERE id = $1
  AND recipient_id = $2
  AND remaining_uses > 0
  AND status = 'ACTIVE'
  AND expires_at > $3
RETURNING id, remaining_uses;

이 쿼리는 처리 시점의 데이터베이스 상태가 모든 조건을 만족할 때만 값을 변경한다.

애플리케이션 메모리에서 판단하는 방식보다 데이터베이스 작업이 더 무거울 수 있다. 그래도 1회용 쿠폰이 두 번 사용되어서는 안 된다는 제약이 우선한다.

가장 빠른 방법이 항상 적절한 것은 아니다.

  • 중복 사용을 허용할 수 있는가?
  • 일부 데이터가 잠시 오래되어도 되는가?
  • 작업 중 일부만 성공해도 되는가?
  • 실패하면 다시 시도할 수 있는가?
  • 금액이나 사용 횟수가 틀릴 가능성을 허용할 수 있는가?

정확성 요구가 높은 작업일수록 선택할 수 있는 알고리즘과 저장 방식이 제한된다.

외부 입력의 범위도 제약하지 않으면 비용을 예측할 수 없다

쿠폰 목록 API가 페이지 크기를 입력으로 받는다고 생각해보자.

const limit = Number(request.query.limit);

const coupons = await couponRepository.findByUserId({
  userId,
  limit,
});

정상적인 화면에서는 limit=20을 보낼 수 있다.

하지만 외부 사용자가 limit=1000000을 보내면 서버와 데이터베이스가 거대한 결과를 만들려고 할 수 있다.

숫자로 변환할 수 있다는 검증만으로는 충분하지 않다.

const requestedLimit = Number(request.query.limit);

if (!Number.isInteger(requestedLimit)) {
  throw new Error("조회 개수는 정수여야 한다.");
}

const limit = Math.min(
  Math.max(requestedLimit, 1),
  100
);

이 코드는 조회 개수를 1개 이상 100개 이하로 제한한다.

입력 범위를 제한하면 한 요청이 사용할 수 있는 데이터베이스 시간, 메모리와 네트워크 크기도 제한할 수 있다.

외부 입력에 최대값이 없다면 알고리즘의 최대 비용도 알 수 없다.

제약 조건은 구현 방법을 고르는 기준인 동시에 사용자가 시스템에 요구할 수 있는 작업량을 통제하는 안전장치다.

제약 조건이 바뀌면 좋은 해결책도 바뀐다

초기 쿠폰 서비스에서는 다음과 같은 조건이 있을 수 있다.

  • 전체 사용자 1,000명
  • 사용자당 쿠폰 최대 20개
  • 초당 목록 요청 5건
  • 한 화면에 모든 쿠폰 표시
  • 운영자만 쿠폰 발행
  • 쿠폰 상태 변경 빈도가 낮음

이 단계에서는 단순한 데이터베이스 조회와 애플리케이션 필터링으로도 충분할 수 있다.

서비스가 성장한 뒤에는 조건이 바뀔 수 있다.

  • 전체 사용자 100만 명
  • 사용자당 쿠폰 최대 10,000개
  • 이벤트 시간에 초당 요청 수천 건
  • 한 화면에 20개씩 표시
  • 여러 제휴사가 실시간으로 쿠폰 발행
  • 사용과 취소가 빈번하게 발생

기능 이름은 여전히 “사용 가능한 쿠폰 목록”이다.

그러나 다음과 같은 설계가 필요할 수 있다.

  • 사용자와 상태를 기준으로 한 데이터베이스 인덱스
  • 전체 목록을 가져오지 않는 페이지네이션
  • 필요한 필드만 선택하는 조회
  • 반복 조회를 줄이기 위한 제한적인 캐시
  • 최종 사용 시점의 데이터베이스 재검증
  • 동시 사용을 막는 조건부 갱신
  • 트래픽 급증을 견디기 위한 요청 제한
  • 느린 조회를 발견하기 위한 로그와 측정 지표

알고리즘과 자료구조는 한 번 선택하고 끝나는 것이 아니다.

서비스의 제약이 바뀌면 이전에는 적절했던 선택도 다시 검토해야 한다.

제약 조건은 측정 가능한 문장으로 작성해야 한다

“데이터가 많다”, “요청이 자주 온다”, “빠르게 보여줘야 한다”라는 표현만으로는 설계 판단을 내리기 어렵다.

가능하면 다음처럼 관찰 가능한 기준으로 바꿔야 한다.

모호한 표현측정 가능한 제약
쿠폰이 많다사용자당 최대 10,000개를 지원한다
목록이 빨라야 한다대부분의 요청을 400밀리초 안에 응답한다
트래픽이 높다이벤트 시간에 초당 2,000건을 처리한다
최신 데이터가 필요하다사용 확정 시 처리 순간의 상태를 다시 확인한다
메모리를 많이 쓰면 안 된다서버 한 대의 캐시를 500MB 이하로 제한한다
너무 많은 데이터를 보내면 안 된다한 번에 최대 20개, 응답은 100KB 이하로 제한한다
중복 사용을 막아야 한다성공한 요청 하나당 사용 횟수를 정확히 한 번 차감한다
장애에 안전해야 한다재시도되어도 같은 요청은 한 번만 반영한다

모든 수치를 개발자가 혼자 결정해야 한다는 뜻은 아니다.

제품 담당자, 운영자와 함께 사용자 경험, 비용과 비즈니스 위험을 기준으로 합의해야 한다.

숫자가 아직 없다면 먼저 측정할 수 있는 로그를 추가하고 실제 사용량을 확인할 수 있다.

제약이 명확해야 구현 결과가 충분한지도 검증할 수 있다.

해결 방법을 선택하기 전에 물어봐야 할 제약 조건

알고리즘이나 자료구조를 선택하기 전에 다음 질문을 확인할 수 있다.

  1. 현재 입력 데이터는 평균적으로 몇 개인가?
  2. 정상적인 사용으로 지원해야 할 최대 입력 크기는 얼마인가?
  3. 데이터는 어느 속도로 증가하는가?
  4. 전체 결과가 필요한가, 일부 목록이나 하나의 값만 필요한가?
  5. 한 번에 반환할 최대 결과 수는 얼마인가?
  6. 이 작업은 한 번 실행되는가, 요청마다 반복되는가?
  7. 평균 요청량과 최대 요청량은 각각 얼마인가?
  8. 사용자가 기다릴 수 있는 전체 응답 시간은 얼마인가?
  9. 데이터베이스, 서버와 네트워크에 허용할 시간은 각각 얼마인가?
  10. 사용할 수 있는 메모리와 저장 공간에는 어떤 제한이 있는가?
  11. 조회 속도를 위해 데이터를 중복 저장해도 되는가?
  12. 데이터는 어느 정도까지 오래되어도 되는가?
  13. 최종 판단의 Source of Truth는 어디에 있는가?
  14. 캐시를 사용한다면 언제 갱신하고 제거해야 하는가?
  15. 외부 사용자가 요청할 수 있는 데이터 크기를 제한했는가?
  16. 동시에 여러 요청이 들어오면 어떤 결과가 보장되어야 하는가?
  17. 같은 요청이 반복되면 한 번만 처리해야 하는가?
  18. 중간 실패 후 재시도할 수 있는 작업인가?
  19. 절대로 허용할 수 없는 잘못된 상태는 무엇인가?
  20. 현재 방법이 한계에 도달했다는 사실을 어떤 지표로 알 수 있는가?
  21. 서비스가 성장하면 가장 먼저 바뀔 제약은 무엇인가?
  22. 선택한 방법이 이 제약을 만족한다는 근거를 설명할 수 있는가?

이 질문에 모두 정확한 숫자로 답하지 못할 수도 있다.

그래도 모르는 조건을 드러내는 것 자체가 중요하다. 확인하지 않은 가정을 사실처럼 사용하지 않게 해주기 때문이다.

제약 조건은 해결 방법의 경계를 그린다

알고리즘 문제에서 제약 조건은 정답 코드가 제한 시간과 메모리 안에서 실행되는지 판단하게 한다.

실제 서비스에서도 역할은 같다. 다만 고려할 범위가 더 넓다.

  • 데이터베이스에 저장된 데이터의 크기
  • 한 사용자가 만들 수 있는 최대 데이터
  • 동시에 들어오는 요청 수
  • 사용자에게 허용되는 대기 시간
  • 서버와 네트워크의 처리 비용
  • 캐시가 허용할 수 있는 데이터 지연
  • 동시 처리에서 지켜야 하는 정확성
  • 장애와 재시도에서 유지되어야 하는 상태
  • 운영 비용과 시스템의 확장 가능성

이 조건을 모르면 어떤 해결 방법이 충분한지 판단할 수 없다.

쿠폰 10개를 한 번 필터링한다면 단순한 배열 탐색이 가장 읽기 쉽고 안전한 선택일 수 있다. 쿠폰 수백만 개에서 사용자별 결과 20개를 계속 조회한다면 인덱스와 페이지네이션이 필요하다. 결제 순간의 쿠폰 사용이라면 캐시보다 최신 상태와 동시성 제어가 중요하다.

제약 조건은 알고리즘이 풀어야 할 문제의 크기와 속도만 정하는 것이 아니다. 서비스가 허용할 수 있는 지연, 비용, 오래된 데이터와 실패의 범위를 정의하여 선택 가능한 해결 방법의 경계를 그린다.

따라서 제약 조건은 구현을 마친 뒤 확인하는 부가 정보가 아니다.

문제를 정의한 다음, 코드를 작성하기 전에 확인해야 할 핵심 요구사항이다. 제약을 먼저 이해해야 단순한 방법으로 충분한지, 탐색 범위를 줄여야 하는지, 계산 결과를 저장해야 하는지, 데이터베이스의 도움을 받아야 하는지 판단할 수 있다.

profile
Vision eXperience Developer

0개의 댓글