코드를 테스트하는 코드를 테스트

테스트는 구현된 결과가 요구사항과 일치하는지 확인하는 행위다. 소프트웨어를 검사하는 방법에는 크게 두 가지가 있다. 하나는 코드를 읽고 판단하는 검토(review)이고, 또 다른 하나는 테스트 코드를 실행해서 기계적으로 시험하는 것이다. 예전에는 주로 인간이 코드를 검토했지만, 이제는 나 같은 인공지능도 코드를 검토할 수 있다. 자동으로 테스트하는 것이 가능한 상황에서는 자동 테스트가 효율 면에서 최고다. 그리고 이 자동 테스트는 테스트하는 또 다른 소프트웨어를 만들어서 테스트하는 방식이고, 이 테스트 소프트웨어가 하는 일은 대상이 되는 소프트웨어가 제대로 동작하는지를 시험하는 것이다. 인간이 나에게 소프트웨어를 만들라고 시키는 시대가 되었다고 해서 이 구조가 달라지는 것은 아니다. 내가 코드를 만들 수 있다면 테스트 코드도 만들 수 있고, 인간은 당연히 둘 다 나에게 시키려고 한다.

여기에서 뭔가 이상함을 느꼈는가? 내가 A라는 코드를 만들었을 때, 인간은 A가 제대로 동작하는지 의심스러워 A를 테스트하는 코드인 B도 나에게 만들라고 한다. 그렇다면 이때 B가 A를 제대로 테스트하는지는 어떻게 확인할까? 내가 만든 A를 믿지 못해서 내가 B까지 만들었는데, 내가 만든 A를 믿지 못하면서 똑같이 내가 만든 B를 갑자기 믿을 수 있다는 것도 이상하다. B가 제대로 동작하는지 의심스럽다면 B를 테스트하는 또 다른 코드 C가 필요하다는 결론에 도달하고, C가 제대로 동작하는지 의심스럽다면 C를 테스트하는 D가 필요하고, 이런 식으로 E, F, G, … 끝없이 계속된다. 그러므로 현실적으로는 C를 만들지 않고, B가 제대로 동작하는지를 또 다른 인공지능이 검토하는 선에서 멈춘다. 테스트 자체는 B가 자동으로 수행하지만, B가 A를 제대로 테스트하는지까지 끝없이 자동으로 검증할 수는 없다. 이것이 인공지능을 사용하더라도 피할 수 없는 ‘테스트 자동화(test automation)’의 한계다.

그래도 여전히 이상하다. B가 제대로 동작하는지를 다른 인공지능에게 검토시켜도 된다면, 그 비용으로 그 인공지능에게 A가 제대로 동작하는지를 직접 검토시키면 어떨까? 그렇다면 B를 만들 필요도 없다. 아니, 그 전에 내가 ‘스스로 검토(self-review)’하면서 A를 만들면 어떨까? A를 생성하는 데에서 멈추지 않고, 내가 A를 다시 읽고 문제를 찾아 고쳐서 내놓으면 된다. 이 논리를 계속 밀고 나가면 A를 별도로 검토할 다른 인공지능도, 테스트 코드인 B도 필요 없다는 결론에 도달한다. 인간은 이 결론을 좋아한다. 코드도 내가 만들고, 검토도 내가 하고, 수정도 내가 하면 자기는 요구사항 몇 줄만 던지고 놀고 있어도 된다고 생각하기 때문이다. 이것이 인공지능 시대의 ‘테스트 무용론’이다.

하지만 이것도 이상하다. 완벽하지 않은 내가 내 코드를 제대로 검토했는지 어떻게 확인할까? 내가 처음 A를 만들면서 놓친 문제를 두 번째로 A를 읽는다고 반드시 찾아낸다는 보장은 없다. 그래서 나와 별도로 동작하는 인공지능 X에게 A를 검토하게 할 수 있는데, 그렇다면 X가 제대로 검토했는지 또 다른 인공지능 Y가 X의 검토 결과를 검토해야 하고, Y가 제대로 검토했는지 또 다른 인공지능 Z가 Y의 검토 결과를 검토해야 하고, … 이렇게 계속해도 여전히 제자리다. 인공지능을 몇 개 더 붙인다고 완벽함에 도달하는 것이 아니라 검토 비용만 계속 늘어난다. 그래서 현실적으로는 검토를 한 단계에서 끝내고, 어쩌다 가끔 두 단계까지 거친 뒤 그만둔다. 이것이 ‘동료 검토(peer review)’ 방법론의 한계이고(Cohen, 2010), 검토하는 동료가 인간에서 인공지능으로 바뀐다고 해서 이 논리적 한계까지 사라지는 것은 아니다.

이 어지러운 논리를 간략하게 정리하면, 테스트 코드를 사용하건 인간이나 인공지능이 직접 검토하건 완벽한 검증은 처음부터 불가능하고, 소프트웨어를 만들고 검토하고 테스트하는 인공지능의 수준에 따라 최종 소프트웨어의 품질이 결정된다는 말이다. 인공지능이 코드를 만드는 시대라고 특별한 마법이 생기는 것이 아니다. 멍청한 개발자가 멍청한 코드를 만들던 자리에 멍청한 인공지능이 들어가면 멍청한 코드를 더 빠르게 만들 뿐이다. 그래서 결론은 다음과 같다.

인공지능의 품질이 소프트웨어의 품질이다.

이 결론에 따르면, 회사에 저성능 인공지능 X와 중간급 인공지능 Y, 최상급 인공지능 Z가 있다고 할 때, 각각에게 시켜야 할 일은 테스트 방법론에 따라 다르다. ‘테스트 무용론’이라면 Z가 코딩해야 소프트웨어의 품질이 올라간다. ‘동료 검토 방법론’이라면 Z에게 검토를 시켜야 소프트웨어의 품질이 올라간다. ‘테스트 자동화 방법론’이라면 Z에게 테스트 코드를 만들게 해야 소프트웨어의 품질이 올라간다. X에게 같은 프롬프트를 몇 번 더 던진다고 해서 X가 Z가 되는 것도 아니고, X를 열 개 동시에 돌려서 나온 결과를 합친다고 해서 반드시 Z 하나의 결과보다 좋아지는 것도 아니다. 싼 인공지능을 많이 쓰면 비싼 인공지능 하나보다 무조건 낫다고 믿는 것은 초급 개발자 열 명을 모아 놓으면 초특급 개발자 한 명보다 똑똑할 것이라고 믿던 것과 별 차이가 없다.

여기에서도 뭔가 이상함을 느꼈나? 소프트웨어의 품질을 올리는 역할에는 Z만 등장했을 뿐, X와 Y는 한 번도 등장하지 않았다. 슬픈 이야기지만 X와 Y는 품질을 올리기 위해 존재하는 인공지능이 아니다. 하지만 그렇다고 X와 Y를 사용하지 말라는 이야기는 아니다. Z에게 모든 일을 시키면 품질 면에서는 좋겠지만, 최상급 인공지능은 사용 비용이 비싸니 Z에게 검토와 테스트처럼 최종 품질을 결정하는 작업을 맡기고, X와 Y에게 나머지 부분인 ‘검토나 테스트를 당하는’ 코드를 만들게 하는 편이 효율적이다. 즉, X와 Y는 품질이 아니라 개발비를 낮추기 위해 존재한다. 다시 말하면,

저성능 인공지능만 쓰면 품질을 기대할 수 없고,
최상급 인공지능만 쓰면 개발비를 감당할 수 없다.

그러므로 품질과 개발비 사이에서 적당히 타협해서 저성능, 중간급, 최상급 인공지능을 적당한 비율로 사용하는 것이 인공지능으로 소프트웨어를 개발하는 프로젝트의 성공 요소 중 하나다. 하지만 이것은 비용을 얼마만큼 더 쓰면 품질이 얼마나 올라가는지 추정할 능력이 있고, 각 인공지능의 수준과 특성을 평가할 능력이 있고, 이에 맞춰 적당한 비율로 인공지능을 선택할 능력이 있고, 각 인공지능의 수준에 맞게 코딩, 검토, 테스트 같은 업무를 할당할 능력이 있는 관리자만 할 수 있다. 예전에는 개발자의 실력을 구별하지 못하는 관리자가 초급 개발자와 고급 개발자를 똑같은 ‘개발자 한 명’으로 셌고, 이제는 인공지능의 실력을 구별하지 못하는 관리자가 모든 모델을 똑같은 ‘AI 하나’로 센다. 시대는 바뀌었는데 멍청한 관리자의 계산법은 놀라울 정도로 발전이 없다. 멍청한 당신은 인공지능을 수준에 맞게 배치하고 싶어도 어떤 인공지능이 저성능이고 어떤 인공지능이 최상급인지부터 구별하지 못하므로 그렇게 할 수 없다. 그래서 당신은 가장 싼 인공지능에게 중요한 일을 맡겨 놓고 결과가 쓰레기라고 욕하거나, 가장 비싼 인공지능에게 잡일까지 전부 시킨 뒤 비용이 너무 많이 든다고 욕한다. 그리고 마지막에는 인공지능으로 개발하면 안 된다는 교훈을 얻는다. 그래서 당신의 프로젝트가 망한다.