AI가 알고리즘을 다 풀어주는데도 우리는 왜 다시 배워야 할까

vx_developer·2026년 9월 1일

코테보다가

목록 보기
1/26
post-thumbnail

프로그래밍을 처음 배울 때 알고리즘은 보통 문제를 해결하기 위한 순서와 절차라고 배운다.

예를 들어 여러 쿠폰 중에서 특정 쿠폰을 찾는 문제는 다음과 같이 해결할 수 있다.

function findCoupon(coupons, couponId) {
  return coupons.find((coupon) => coupon.id === couponId);
}

앞에서부터 쿠폰을 하나씩 확인하다가 ID가 같은 쿠폰을 반환한다. 알고리즘의 기본적인 개념을 이해하기에는 충분한 예제다.

그런데 지금은 이런 코드도 AI가 몇 초 만에 작성한다.

코드를 설명해달라고 요청할 수도 있고, 더 빠른 방법으로 개선해달라고 할 수도 있다. 테스트 코드와 예외 처리까지 함께 만들어달라고 요청하는 것도 가능하다.

그렇다면 한 가지 질문이 생긴다.

AI가 알고리즘 문제를 풀고 코드까지 작성해주는데, 우리는 왜 알고리즘을 다시 배워야 할까?

실제 서비스를 개발할 때는 무엇을 작성할 수 있는지만으로 문제가 해결되지 않기 때문이다.

다음과 같은 판단이 남아 있다.

  • AI가 이해한 문제가 우리가 해결하려는 문제와 같은가?
  • 생성된 코드는 모든 입력에서 올바르게 동작하는가?
  • 사용자 10명일 때뿐 아니라 10만 명일 때도 사용할 수 있는가?
  • 동시에 여러 요청이 들어오면 데이터가 잘못 변경되지 않는가?
  • 더 빠른 방법을 선택하면서 무엇을 포기했는가?
  • 실패했을 때 서비스에는 어떤 결과가 남는가?

AI는 해결책을 빠르게 제안할 수 있다. 하지만 그 해결책을 이해하고 선택하고 검증하는 기준은 개발자에게 필요하다.

AI 시대에 알고리즘을 배운다는 것은 정답 코드를 외우는 일이 아니라, AI가 만든 해결 절차가 우리 서비스에 적합한지 판단하는 기준을 만드는 일이다.

AI가 빠르게 만드는 것은 코드이지 문제의 기준이 아니다

쿠폰 서비스에서 사용자가 쿠폰을 사용할 수 있는지 판단하는 기능을 만든다고 생각해보자.

AI에게 다음과 같이 요청할 수 있다.

쿠폰이 사용되었거나 만료되었는지 확인하는 함수를 만들어줘.

AI는 다음과 비슷한 코드를 만들어줄 수 있다.

function canRedeemCoupon(coupon) {
  return !coupon.isUsed && new Date(coupon.expiresAt) > new Date();
}

이 코드는 두 가지를 확인한다.

  1. 쿠폰이 아직 사용되지 않았는가?
  2. 현재 시간이 만료 시간보다 이전인가?

주어진 요구사항만 보면 자연스러운 해결책이다. 코드도 정상적으로 실행된다.

하지만 실제 쿠폰 서비스의 사용 가능 여부를 판단하기에는 아직 부족하다.

  • 이 쿠폰을 사용하려는 사람이 실제 수신자인가?
  • 쿠폰이 발행자에 의해 취소되지는 않았는가?
  • 한 번만 사용할 수 있는가, 여러 번 사용할 수 있는가?
  • expiresAt은 어느 지역의 시간을 기준으로 하는가?
  • 브라우저 시간과 서버 시간이 다르면 무엇을 기준으로 판단하는가?
  • 두 개의 사용 요청이 동시에 들어오면 어떻게 처리하는가?
  • 사용할 수 없다면 사용자에게 어떤 이유를 알려줘야 하는가?

AI가 코드를 잘못 작성했다고 단정할 수는 없다. 처음 요청에 이러한 조건이 포함되지 않았기 때문이다.

문제가 명확하지 않으면 AI는 빈 부분을 일반적인 가정으로 채운다. 그리고 그 가정이 실제 서비스의 정책과 다르면, 문법적으로 올바른 코드가 잘못된 결과를 만든다.

따라서 알고리즘을 설계하기 전에 다음 요소부터 분명하게 해야 한다.

구분쿠폰 사용에서 결정해야 할 내용
입력쿠폰 상태, 요청한 사용자, 서버 시간
출력사용 가능 여부, 실패 이유
규칙수신자만 사용 가능, 활성 상태, 잔여 횟수 존재
제약 조건동시 요청, 중복 요청, 만료 시각
반드시 지켜야 할 조건사용 횟수가 0보다 작아지지 않아야 함

코드를 생성하는 것보다 먼저 해야 하는 일은 무엇을 올바른 결과로 볼 것인지 결정하는 것이다.

실행되는 코드와 올바른 절차 사이에는 운영 조건이 있다

앞의 조건을 반영해 쿠폰 사용 가능 여부를 조금 더 구체적으로 표현해보자.

function evaluateCouponRedemption({ coupon, requesterId, now }) {
  if (coupon.recipientId !== requesterId) {
    return { allowed: false, reason: "NOT_RECIPIENT" };
  }

  if (coupon.status !== "ACTIVE") {
    return { allowed: false, reason: "NOT_ACTIVE" };
  }

  if (coupon.remainingUses <= 0) {
    return { allowed: false, reason: "NO_REMAINING_USES" };
  }

  if (now.getTime() >= coupon.expiresAt.getTime()) {
    return { allowed: false, reason: "EXPIRED" };
  }

  return { allowed: true };
}

이제 함수가 확인하는 서비스 규칙이 코드에 명확하게 드러난다.

단순히 true나 false만 반환하지 않고, 사용할 수 없는 이유도 함께 반환한다. 현재 시간도 함수 내부에서 직접 생성하지 않고 외부에서 전달받는다. 서버 시간을 사용할 수 있고, 테스트에서는 원하는 시간을 넣어 만료 조건을 검증할 수 있다.

이전 코드보다 실제 서비스에 가까워졌다.

하지만 이 함수가 allowed: true를 반환했다고 해서 쿠폰 사용이 안전하게 완료된 것은 아니다.

쿠폰의 남은 사용 횟수가 1회라고 생각해보자.

  1. 첫 번째 요청이 remainingUses를 조회한다.
  2. 두 번째 요청도 같은 값을 조회한다.
  3. 두 요청 모두 남은 횟수가 1이라고 판단한다.
  4. 두 요청이 각각 쿠폰을 사용 처리한다.

각 요청의 알고리즘만 보면 올바르게 동작했다. 하지만 두 요청을 함께 보면 1회용 쿠폰이 두 번 사용되었다.

이 문제는 조건문 하나를 더 추가한다고 해결되지 않는다. 확인과 변경을 하나의 안전한 작업으로 처리해야 한다.

예를 들어 데이터베이스에서 다음과 같은 조건부 변경을 수행할 수 있다.

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

이 쿼리는 사용 조건을 만족하는 쿠폰만 변경한다.

두 요청이 동시에 들어오더라도 remaining_uses > 0이라는 조건을 만족한 요청만 성공해야 한다. 반환된 행이 없다면 쿠폰을 찾지 못했거나, 만료되었거나, 이미 모두 사용된 것이다.

여기서 알고리즘은 더 이상 함수 안의 조건문만을 의미하지 않는다.

쿠폰을 조회하고, 조건을 확인하고, 상태를 변경하고, 성공 여부를 반환하는 전체 처리 순서가 알고리즘이 된다.

화면의 판단과 최종 사용 처리는 같은 책임이 아니다

프런트엔드에서도 쿠폰의 사용 가능 여부를 계산할 수 있다.

const isRedeemButtonDisabled =
  coupon.status !== "ACTIVE" ||
  coupon.remainingUses <= 0 ||
  Date.now() >= new Date(coupon.expiresAt).getTime();

이 값은 버튼을 비활성화하고 사용자에게 현재 상태를 빠르게 보여주는 데 유용하다.

그러나 브라우저에 있는 쿠폰 데이터는 이미 오래된 정보일 수 있다. 사용자는 요청 값을 변경할 수도 있고, 다른 기기에서 같은 쿠폰을 먼저 사용할 수도 있다.

따라서 화면의 계산 결과를 최종 판단으로 사용해서는 안 된다.

위치담당하는 판단
UI현재 데이터에 따른 예상 상태를 보여준다
서버사용자와 입력을 검증하고 비즈니스 규칙을 적용한다
데이터베이스최신 상태를 기준으로 변경 가능 여부를 결정한다
테스트규칙과 상태 전이가 계속 유지되는지 확인한다

쿠폰의 실제 사용 가능 횟수에 대한 Source of Truth는 데이터베이스에 있다. UI의 isRedeemButtonDisabled는 원본 데이터가 아니라 화면 표시를 위해 계산한 값이다.

알고리즘을 이해한다는 것은 로직의 순서만 이해하는 것이 아니다. 각 판단이 어디에서 이루어져야 하는지 구분하는 것까지 포함한다.

더 빠른 방법은 데이터의 크기와 사용 방식에 따라 달라진다

AI에게 특정 쿠폰을 빠르게 찾는 방법을 요청하면 해시 테이블이나 Map을 사용하라는 답을 받을 수 있다.

기존 코드는 배열을 앞에서부터 탐색한다.

const coupon = coupons.find(
  (coupon) => coupon.id === targetCouponId
);

쿠폰이 많아질수록 확인해야 하는 데이터도 늘어난다.

반복해서 쿠폰을 조회한다면 ID를 기준으로 Map을 만들 수 있다.

const couponsById = new Map(
  coupons.map((coupon) => [coupon.id, coupon])
);

const coupon = couponsById.get(targetCouponId);

처음에 Map을 만드는 비용이 들지만, 이후에는 ID로 쿠폰을 반복해서 찾기 쉬워진다.

그렇다고 두 번째 코드가 항상 더 좋은 것은 아니다.

  • 쿠폰이 10개이고 한 번만 조회한다면 배열 탐색이 더 단순하다.
  • 수천 개의 쿠폰을 메모리에서 반복 조회한다면 Map이 유리할 수 있다.
  • 수백만 개의 쿠폰이 데이터베이스에 있다면 모든 쿠폰으로 Map을 만드는 것은 적절하지 않다.
  • 쿠폰 상태가 자주 변경된다면 메모리에 복사한 데이터가 최신 상태와 달라질 수 있다.
  • 데이터베이스에서 ID로 조회한다면 애플리케이션의 Map보다 적절한 인덱스가 중요하다.
상황고려할 수 있는 방법
작은 목록에서 한 번 조회배열의 선형 탐색
메모리 목록에서 반복 조회Map으로 조회 구조 생성
데이터베이스의 대량 데이터 조회ID 인덱스를 사용한 쿼리
상태가 자주 변경되는 데이터최신 원본 데이터와 일관성 우선

AI는 여러 구현 방법을 제안할 수 있다. 알고리즘 지식은 그중 가장 어려운 방법을 선택하게 만드는 지식이 아니다.

알고리즘 지식은 데이터의 크기, 조회 횟수, 변경 빈도와 저장 위치를 기준으로 충분히 적절한 방법을 선택하게 하는 지식이다.

테스트를 생성하기 전에 무엇을 지켜야 하는지 정의해야 한다

AI는 구현 코드를 바탕으로 테스트 코드도 생성할 수 있다.

하지만 코드에 잘못된 가정이 들어 있다면, AI가 만든 테스트도 그 가정을 그대로 확인할 수 있다. 테스트가 통과한다는 사실은 작성된 코드와 테스트가 서로 일치한다는 뜻일 뿐, 서비스의 요구사항까지 올바르다는 뜻은 아니다.

쿠폰 사용 기능에서 먼저 정의해야 할 것은 테스트 코드가 아니라 다음과 같은 조건이다.

  • 남은 사용 횟수는 절대로 0보다 작아지지 않는다.
  • 성공한 사용 요청 하나는 사용 횟수를 정확히 1만큼 줄인다.
  • 쿠폰 수신자가 아닌 사용자는 상태를 변경할 수 없다.
  • 만료된 쿠폰 요청은 데이터에 아무런 변경도 남기지 않는다.
  • 동시에 여러 요청이 들어와도 허용된 횟수보다 많이 사용할 수 없다.
  • 동일한 요청이 재전송되더라도 같은 사용 결과가 중복 반영되지 않는다.

이처럼 처리 전후에 반드시 유지되어야 하는 조건을 불변 조건이라고 한다.

동시 요청에 관한 테스트는 다음과 같은 모습이 될 수 있다.

it("1회용 쿠폰에 두 요청이 동시에 들어와도 한 번만 성공한다", async () => {
  const results = await Promise.all([
    redeemCoupon(couponId, currentUser),
    redeemCoupon(couponId, currentUser),
  ]);

  const successCount = results.filter(
    (result) => result.success
  ).length;

  expect(successCount).toBe(1);
});

이 테스트는 함수가 어떤 조건문을 사용했는지 확인하지 않는다. 대신 1회용 쿠폰은 한 번만 사용되어야 한다는 서비스의 약속을 검증한다.

개발자가 불변 조건과 반례를 정의하면 AI는 테스트 구현을 빠르게 도울 수 있다. 그러나 무엇을 검증해야 하는지 정하지 못하면 많은 테스트가 있어도 중요한 오류를 놓칠 수 있다.

AI에게 맡길수록 개발자의 판단 기준은 더 구체적이어야 한다

AI가 잘할 수 있는 일은 분명히 존재한다.

  • 해결 방법 후보를 빠르게 제안한다.
  • 반복적인 구현 코드를 작성한다.
  • 낯선 알고리즘을 설명한다.
  • 테스트 코드의 초안을 만든다.
  • 두 구현의 시간 및 공간 비용을 비교한다.
  • 코드에서 놓치기 쉬운 예외 상황을 제안한다.

그러나 AI가 서비스의 모든 맥락을 자동으로 알고 있는 것은 아니다.

작업AI가 도울 수 있는 부분개발자가 책임질 부분
문제 정의질문과 누락 조건 제안해결해야 할 실제 문제 결정
방법 탐색여러 알고리즘 후보 생성서비스 제약에 맞는 방법 선택
코드 작성구현과 리팩터링코드가 정책을 정확히 반영하는지 확인
테스트테스트 사례와 코드 생성불변 조건과 중요한 반례 정의
성능 개선복잡도 분석과 대안 제안실제 병목 측정과 비용 판단
운영오류 원인 분석 지원사용자와 데이터에 미치는 결과 책임

AI에게 코드를 맡긴다고 해서 개발자의 일이 사라지는 것은 아니다. 개발자의 역할이 코드 입력에서 판단과 검증으로 이동한다.

오히려 코드 생성 비용이 낮아질수록 더 많은 해결책이 빠르게 만들어진다. 그중 무엇을 사용할지 결정하는 능력이 이전보다 중요해진다.

AI가 만든 알고리즘을 사용하기 전에 물어봐야 할 질문

AI가 생성한 코드를 바로 서비스에 적용하기 전에 다음 질문을 확인할 수 있다.

  1. 이 코드는 우리가 해결하려는 문제를 정확히 풀고 있는가?
  2. 입력값은 어디에서 오며, 검증하기 전에도 신뢰할 수 있는가?
  3. 성공뿐 아니라 가능한 실패 결과도 정의되어 있는가?
  4. 항상 유지되어야 하는 서비스의 불변 조건은 무엇인가?
  5. 빈 데이터, 잘못된 데이터, 중복 요청과 동시 요청을 고려했는가?
  6. 데이터가 10배 또는 100배 증가하면 처리 비용은 어떻게 달라지는가?
  7. 속도를 높이기 위해 추가 메모리, 저장 공간이나 최신성을 포기하고 있지는 않은가?
  8. 최종 판단과 상태 변경은 신뢰할 수 있는 위치에서 이루어지는가?
  9. 요구사항이 변경되면 어느 부분을 수정해야 하는가?
  10. 이 방법이 적절한 이유를 다른 개발자에게 설명할 수 있는가?

마지막 질문에 답하기 어렵다면 코드는 동작하더라도 아직 충분히 이해한 해결책이라고 보기 어렵다.

코드 작성이 쉬워질수록 알고리즘을 판단하는 능력이 중요해진다

AI가 없던 시기에는 알고리즘을 직접 구현하는 능력이 중요했다. 지금은 알고리즘의 구현뿐 아니라 설명, 변환, 테스트까지 AI의 도움을 받을 수 있다.

그렇다고 알고리즘 학습의 가치가 사라진 것은 아니다.

알고리즘을 모르면 AI가 만든 코드가 그럴듯한지 확인할 수는 있어도, 그것이 정확한지 판단하기 어렵다. 더 빠르다는 설명을 들어도 어떤 비용과 조건에서 빨라지는지 알기 어렵다. 테스트가 통과해도 어떤 반례가 빠졌는지 발견하기 어렵다.

반대로 알고리즘의 원리를 이해하면 AI를 코드 자동완성 도구보다 더 넓게 활용할 수 있다.

  • 문제의 누락된 조건을 찾게 할 수 있다.
  • 여러 해결책과 각각의 비용을 비교하게 할 수 있다.
  • 불변 조건을 기준으로 반례를 만들게 할 수 있다.
  • 데이터 규모에 따른 성능 변화를 분석하게 할 수 있다.
  • 선택한 방법을 실제 서비스 조건에 맞게 수정하게 할 수 있다.

결국 AI 시대에 필요한 것은 AI보다 빠르게 코드를 작성하는 능력이 아니다.

AI 시대에 알고리즘을 배운다는 것은 정답 코드를 손으로 재현하는 훈련이 아니라, 문제를 명확히 정의하고 생성된 해결책의 정확성과 비용을 판단하며 그 결과를 책임질 기준을 만드는 일이다.

AI는 답을 빠르게 만들 수 있다.

하지만 어떤 질문을 해야 하는지, 무엇을 올바른 답으로 볼 것인지, 그 답을 실제 서비스에 사용해도 되는지는 여전히 개발자가 결정해야 한다.

profile
Vision eXperience Developer

0개의 댓글