[소고] AI 주도 개발

@codemaru · June 28, 2022 · 15 min read

최근 Github Copilot 출시 소식을 접했다. 소개 페이지를 보면 월 10불, 년 100불에 Python, JavaScript, TypeScript, Ruby, Go, C#, or C++ 코딩을 도와주는 도구라고 나와있다. 알파고가 등장하기 전이었다면 쳐다보지도 않았겠지만 알파고의 위력을 알았기에 설치를 해보았다. 결과는 실로 놀라웠다. 처음 3분간 테스트 해 본 코드를 첨부하면 다음과 같다. ai+라고 주석이 달린 부분은 모두 인공지능이 작성해서 추가한 부분이다. 살펴보면 대체로 함수 원형만 작성하면 나머지는 모두 코파일럿이 작성했다는 것을 알 수 있다. 사실 여기까지만 보고도 많이 놀랐다.

int plus(int a, int b) {
	return a + b; // ai+
}

int IsPrime(int n){
	if (n <= 1) { // ai+
		return 0; // ai+
	} // ai+
	for (int i = 2; i < n; i++) { // ai+
		if (n % i == 0) { // ai+
			return 0; // ai+
		} // ai+
	} // ai+
	return 1; // ai+
}


int GetFactorial(int n)
{
	if (n == 0) // ai+
		return 1; // ai+
	else // ai+
		return n * GetFactorial(n - 1); // ai+

}

int GetFactorialWithoutRecursion(int n)
{
	int result = 1; // ai+
	for (int i = 1; i <= n; i++) // ai+
	{ // ai+
		result *= i; // ai+
	} // ai+
	return result; // ai+
}

int GetFibonacci(int n)
{
	if (n == 0) // ai+
		return 0; // ai+
	else if (n == 1) // ai+
		return 1; // ai+
	else // ai+
		return GetFibonacci(n - 1) + GetFibonacci(n - 2); // ai+
}

int GetFibonacciWithoutRecursion(int n)
{
	int a = 0; // ai+
	int b = 1; // ai+
	int c = 0; // ai+
	for (int i = 0; i < n; i++) // ai+
	{ // ai+
		c = a + b; // ai+
		a = b; // ai+
		b = c; // ai+
	} // ai+
	return c; // ai+
}

bool IsPalindromeString(const char* s){
	int len = strlen(s); // ai+
	for (int i = 0; i < len / 2; i++) { // ai+
		if (s[i] != s[len - i - 1]) { // ai+
			return false; // ai+
		} // ai+
	} // ai+
	return true; // ai+
}

int GetMaxValue(int* a, size_t cnt)
{
	int max = a[0]; // ai+
	for (int i = 1; i < cnt; i++) { // ai+
		if (a[i] > max) { // ai+
			max = a[i]; // ai+
		} // ai+
	} // ai+
	return max; // ai+
}

void SortData(int *data, size_t cnt)
{
	for (int i = 0; i < cnt; i++) { // ai+
		for (int j = i + 1; j < cnt; j++) { // ai+
			if (data[i] > data[j]) { // ai+
				int tmp = data[i]; // ai+
				data[i] = data[j]; // ai+
				data[j] = tmp; // ai+
			} // ai+
		} // ai+
	} // ai+
}

typedef struct _STUDENT
{
	std::string name;
	int score;
} STUDENT, *PSTUDENT;

int FindStudentScore(PSTUDENT students, int cnt, std::string name){
	for (int i = 0; i < cnt; i++) { // ai+
		if (students[i].name == name) { // ai+
			return students[i].score; // ai+
		} // ai+
	} // ai+
	return -1; // ai+
}

void QuickSortData(int* data, size_t cnt){
	if (cnt <= 1) // ai+
		return; // ai+
	int pivot = data[cnt / 2]; // ai+
	size_t i = 0; // ai+
	size_t j = cnt - 1; // ai+
	while (i <= j) // ai+
	{ // ai+
		while (data[i] < pivot) // ai+
			i++; // ai+
		while (data[j] > pivot) // ai+
			j--; // ai+
		if (i <= j) // ai+
		{ // ai+
			int tmp = data[i]; // ai+
			data[i] = data[j]; // ai+
			data[j] = tmp; // ai+
			i++; // ai+
			j--; // ai+
		} // ai+
	} // ai+
	QuickSortData(data, j + 1); // ai+
	QuickSortData(data + i, cnt - i); // ai+
}

테스트 하면서 웃긴 장면이 하나 있었다. 다음 함수다. 중괄호 매치가 끝나지 않은 지점에 인공지능 코딩이 끝난 것이다. 사실 인간 입장에서 여기에서 코드가 종결된 것인지, 아니면 코드를 더 작성해야 하는지 알기가 쉽지 않겠다는 생각이 들었다.

void QuickSortDataWithoutRecursion(int* data, size_t cnt) {
	std::stack<int> stack; // ai+
	stack.push(0); // ai+
	stack.push(cnt - 1); // ai+
	while (!stack.empty()) { // ai+
		int right = stack.top(); // ai+
		stack.pop(); // ai+
		int left = stack.top(); // ai+
		stack.pop(); // ai+
		int pivot = data[(left + right) / 2]; // ai+
		int i = left - 1; // ai+
		int j = right + 1; // ai+
		while (true) { // ai+
			do { // ai+
				i++; // ai+
			} while (data[i] < pivot); // ai+

		// 이 시점에 인간 프로그래머의 심정
		// 1) 다 짠걸까? 2) 더 있는걸까? 3) 맞는걸까?

여기까지 샘플 코드를 살펴보면 저건 단순하고 많이 테스트되는 코드들이니까 인공지능이 잘 작성했다고 생각할 수 있다. 어쨌든 나는 여기에서 가능성을 보았다. 그리고 이후 몇일간 실질적으로 회사 코딩을 하는데 적용해서 함께(?!) 코딩을 해보았다. 결과는 실로 놀라웠다.

내가 테스트한 개발 방식은 총 3가지였다. 안드로이드 C++ 개발, 윈도우 C++ 개발, 윈도우 커널모드 C 개발. 안드로이드와 윈도우 C++ 개발은 굉장히 편리하게 사용할 수 있는 수준이었다. 윈도우 커널모드 C 개발의 경우 주석을 굉장히 디테일하게 작성해야 사용할 수 있는 코드가 도출되었다. 결론부터 총평하자면 무조건 써라. 회사에서 사주지 않으면 개인 비용으로라도 쓰길 추천한다.

써보면서 가장 편리했던 부분은 끊임없이 레퍼런스를 참고하면서 작업해야 하는 반복적인 코딩이었다. 예를들면 Java 호출 코드를 JNI로 컨버팅하는 류인데 이런 경우 안드로이드 문서와 JNI 스펙 문서를 찾아보면서 일일이 타입, 시그니처를 찾고 생성해서 테스트를 해야 한다. 그렇게 일일이 수작업으로 생성해도 세미콜론이나 달러 기호가 빠져서 정상 동작하지 않는 경우가 많다. 하지만 코파일럿을 사용할 경우 주석만 적당히 입력하면 마법처럼 완벽한 코드를 생성해 냈다. 이게 몇 개 쌓이자 나중에는 일일이 확인을 하지도 않았다. 그냥 얘가 생성한거는 똑바로 생성했겠거니 하는 생각이 들었다.

두번째로 놀란 부분은 주석이 없어도 인공지능이 문맥을 어느 정도 파악한다는 점이다. 예를들면 내가 특정 조건을 실패로 리턴하면 다음부터는 그 함수 내에서는 그런 류의 판단은 자기가 알아서 성공/실패를 결정한다. 이건 진짜 웃긴데 내가 함수를 작성하다 구조가 마음에 안 들어서 위에서 한두개를 바꾸면 나머지는 다시 알아서 재추천을 해준다. 진짜 얘가 나와 상호작용을 하고 있다는 생각이 절로 든다. 나는 페어 프로그래밍은 시간 낭비라 생각해 많이 해보진 않았지만 그간 일반적인 신입 프로그래머와 코딩 작업이나 코드 리뷰를 해 본 경험에 비추어보자면 코파일럿이 보통의 신입 프로그래머보다는 훌륭하다는 생각이 들었다.

세번째로 놀란 부분은 완벽하진 않지만 힌트를 제공하면 마법처럼 내 의도를 알아차린다는 점이었다. 예를들면 다음과 같은 코드였다.

void CombineValue(uint8_t* buf, size_t buf_size, uint64_t msb, uint64_t lsb)
{
	uint64_t value = (msb << 32) | lsb; // ai+
	for (int i = 0; i < buf_size; i++) // ai+
	{ // ai+
		buf[i] = (value >> (8 * i)) & 0xFF; // ai+
	} // ai+

이건 아니지라는 생각에 AI가 추가한 코드를 지우고 memcpy(까지 치고 기다리면 다음처럼 올바른 코드를 생성해 낸다.

void CombineValue(uint8_t* buf, size_t buf_size, uint64_t msb, uint64_t lsb)
{
	memcpy(buf, &msb, sizeof(msb)); // ai+
	memcpy(buf + sizeof(msb), &lsb, sizeof(lsb)); // ai+

함수가 리턴 값이 있어야 할 거 같아서 다음과 같이 bool 형으로 바꾸고 if까지 입력하고 대기하면 다음과 같은 코드를 생성해 준다. 물론 완벽하진 않지만 내가 뭘 체크하려고 if를 입력했는지를 캐치했다는 점은 놀랍다. 함께 코딩해보면 이런 적이 제법 많다. 실질적으로 내가 의도한 바는 buf_size < 16이다. 그럼에도 주석 한 줄 없이 이 정도 코드를 추천했다는 것은 놀랍다.

bool CombineValue(uint8_t* buf, size_t buf_size, uint64_t msb, uint64_t lsb)
{
	if(buf_size < 8) // ai+
		return false; // ai+

	memcpy(buf, &msb, sizeof(msb)); // ai+
	memcpy(buf + sizeof(msb), &lsb, sizeof(lsb)); // ai+

	return true; // ai+

회사 작업을 하면서 코파일럿이 추가한 코드는 여기에 올린 것처럼 ai+라는 주석을 달았다. 실질적으로 얼만큼 실용적인지를 보고 싶었다. 하지만 이제 더는 달지 않는다. 몇일 작업하면서 이미 코파일럿은 그 자체로 존재 가치를 충분히 증명했기 때문이다.

나는 코파일럿이 제공하는 프로그래밍 언어를 다 다룰 줄 안다. 다만 주력으로는 파이썬, C++이고 나머지는 서브로 사용하고 있다. 그래서 해당 언어의 라이브러리 코드 등을 세세하게 다 기억하지 못해서 구글을 통해서 검색해가면서 개발하는 경우가 다반다사. 이런 경우에도 코파일럿은 굉장히 큰 도움이 되었다. 왜냐하면 그런 언어로 하는 작업은 실질적으로 아주 간단한 샘플 코드인 경우가 많아서 코파일럿 추천을 받기가 훨씬 쉽기 때문이었다.

class Test
{
	public String GetFileText(String path){
		String text = ""; // ai+
		try { // ai+
			FileInputStream fis = new FileInputStream(path); // ai+
			BufferedReader br = new BufferedReader(new InputStreamReader(fis)); // ai+
			String line; // ai+
			while ((line = br.readLine()) != null) { // ai+
				text += line + "\n"; // ai+
			} // ai+
			br.close(); // ai+
		} catch (IOException e) { // ai+
			e.printStackTrace(); // ai+
		} // ai+
		return text; // ai+
	}

	public void RequestPermission(String permission){
		ActivityCompat.requestPermissions(this, new String[]{permission}, 1); // ai+
	}
}

예전에 나는 이런 도구에 대해서 조금은 부정적인 의견이 있었다. 이런 도구들이 발전할수록 개발자가 점점 멍청해지지 않을까라는 생각이 있었던 것이다. 그러나 이제는 생각이 좀 바뀌었다. 인공지능이 진화할수록 결국 나를 100% 대체할 것은 자명한 사실이다. 하지만 100% 대체가 되기 전까지는 어쨌든 인공지능의 도움을 받는 것이 다른 사람들과 경쟁을 하는데 도움이 된다는 생각이다. 그리고 코파일럿을 써 본 입장에서 생산성 차이는 엄청나다. 최신형 사이보그 의족을 달고 우샤인 볼트와 대결할 수 있게 된 것이다. 아직도 코파일럿을 안 쓰는 개발자도 많을 것이다. 그런 점에서 사용하는 사람은 비교우위에 설 수 있다는 게 내 생각이다.

그리고 조심스레 예측해 본다. 앞으로 코파일럿에 적응한 개발자가 늘어날수록 함수명이나 변수명, 주석 등을 코파일럿이 적절하게 추천할 수 있도록 작성하는 것이 능력이 될 것이란 점이다. 아직까지는 주는 사람이고, 보조가 인공지능이지만 인공지능이 주가 될 날도 머지 않았다는 생각이 든다. 인공지능이 추천한 코드를 사용하다, 인공지능에게 코드 리뷰를 받다가, 결국에는 인공지능이 모두 다 작성하게 될 세상. 그런 세상에서 아마도 우린 이런 얘기를 하겠지? 라떼는 사람이 직접 프로그램을 입력하는 시절도 있었다고 말이다.

진짜 정말 놀라운 세상이다.

충분히 발달한 과학 기술은 마법과 구별할 수 없다.

-- 아서 C. 클라크

@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중