AI가 코딩하는 시대 도래.? AI를 기반으로 개발자에게 코드를 제안해주는 GitHub Copilot!!

개발자가 작성한 주석이나 함수명을 분석해 해당 기능을 완성하기 위한 코드를 제공해주는 GitHub Copilot에 대한 간단한 소개와 GitHub Copilot Technical Preview의 사용 후기에 대한 글입니다.
2022.03.27

안녕하세요, 클래스메서드 MAD 사업부의 정현재입니다.

얼마 전 신청하였던 GitHub Copilot Technical Preview 사용이 승인이 되서 여러 가지 만져보았는데요, 이번 블로그에서는 GitHub Copilot에 대한 간단한 소개와 사용해본 후기에 대해서 블로그를 쓰려고 합니다.

GitHub Copilot이란?

GitHub Copilot이란 AI 관련 스타트업인 OpenAI의 Codex로 구동이되는 프로그램으로, AI를 기반으로 하여 개발자가 작성한 주석이나 코드, 함수명 등을 분석해 해당 기능을 완성하기 위한 코드를 제안하는 프로그램입니다. 수많은 GitHub의 사용자들이 푸시한 방대한 양의 오픈되어 있는 소스들을 이용해 AI가 학습을 하고, 그것을 바탕으로 사용자에게 코드를 제안해주는 방식입니다. 이 때문에, 여러 가지 저작권에 관련된 문제들에 휩싸이고 있기도 합니다만, 개인적으로는 AI가 개발자를 대신하여 코드를 작성해준다는 것이 그저 놀라울 따름입니다...

GitHub Copilot은 Visual Studio Code Extension 형태로 제공을 하고 있어, 굉장히 쉽게 설치하고 사용할 수 있습니다. 아직 정식 출시는 되지 않아 Technical Preview만 가능한 상태이고, Technical Preview도 신청을 하면 순차적으로 승인을 해주는 방식입니다. 그러면 신청, 설치 방법, 사용해본 후기에 대해 말씀드리도록 하겠습니다.

GitHub Copilot Technical Preview 신청 및 승인

GitHub Copilot Technical Preview의 신청 방법은 아주 간단합니다. GitHub Copilot 홈페이지에 들어가 GitHub 계정으로 로그인 후, Join the waitlist 버튼을 누르면 신청 완료입니다!(저는 이미 해버려서 캡처 화면을 준비 못했네요..)

신청까지는 굉장히 간단히 할 수 있지만, 승인이 날 때까지는 꽤나 오랜 시간이 걸립니다. 현재에는 얼마나 걸리는지 정확히 모르지만(새로운 계정으로 신청해본 결과 1주일정도 걸렸습니다.), 저의 경우에는 6달정도 걸렸던 것 같습니다. 그렇게 신청을 했다는 사실을 까맣게 잊고 있다 보면 어느 순간 GitHub에서 아래와 같이 GitHub Copilot Technical Preview 를 사용할 수 있다는 메일이 도착합니다. 메일의 Install GitHub Copilot 버튼을 클릭하면 GitHub Copilot을 설치하는 방법과 간단한 사용법이 적혀있는 GitHub 리포지토리로 이동합니다.

GitHub Copilot 설치

GitHub Copilot의 설치도 신청 방법 만큼이나 굉장히 간단합니다. GitHub Copilot 은 Visual Studio Code Extension 형태로 제공하고 있어, Visual Studio Code에서 위의 확장 프로그램을 설치하면 90프로는 설치한 것입니다. 확장 프로그램 설치 후, GibHub 계정으로 로그인을 하게 되면 사용할 준비 완료입니다.

사용해보자!

Visual Studio Code에서 원하는 프로그래밍 언어의 파일을 만듭니다. 저의 경우에는 Python 파일을 하나 만들었습니다.

우선 간단히 def helloWorld 라고만 적어보도록 하겠습니다.

위와 같이 메서드명만 적었는데도 불구하고, Hello World를 출력하는 내용을 제안해주고 있습니다. 여기서 Tab 버튼을 누르면 코드가 제안된 내용으로 입력이 됩니다. 만약 원하는 코드가 제안되지 않았을 경우 아래의 단축키를 이용해 다른 제안을 볼 수도 있습니다.

  • macOS : Option or Alt + ] or Option or Alt + [
  • Windows or Linux : Alt + ] or Alt + [

메서드명 외에도 위와 같이 주석을 작성하여 코드를 제안 받을 수 있습니다.

더 복잡한 코드도 제안을 해줄 수 있을지 실험해보기 위해, 대학 시절 꽤 열심히 공부했던 유전 알고리즘을 GitHub Copilot을 이용해 구현해보기로 하였습니다. 아래의 코드는 오로지 GitHub Copilot만을 이용하여 작성한 유전 알고리즘의 코딩입니다.

def genetic_algorithm(population, fitness_func, mutation_rate, generations):
    """
    Genetic algorithm to find the best solution
    :param population: initial population
    :param fitness_func: fitness function
    :param mutation_rate: mutation rate
    :param generations: number of generations
    :return: best solution
    """
    # Initialize the population
    population = initialize_population(population, fitness_func)

    # Start the evolution
    for i in range(generations):
        # Select the best parents in the population
        parents = select_mating_pool(population, fitness_func)

        # Generate the new population based on the parents
        population = generate_new_population(population, parents, fitness_func, mutation_rate)

    # Return the best solution from the final population
    return population[0]

def initialize_population(population, fitness_func):
    """
    Initialize the population
    :param population: initial population
    :param fitness_func: fitness function
    :return: initialized population
    """
    # Loop and create individuals
    for _ in range(len(population)):
        population.append(create_individual(fitness_func))

    return population

def create_individual(fitness_func):
    """
    Create an individual
    :param fitness_func: fitness function
    :return: individual
    """
    # Create a random individual
    individual = []
    for _ in range(len(fitness_func)):
        individual.append(random.randint(0, 1))

    return individual

def select_mating_pool(population, fitness_func):
    """
    Select individuals for mating pool
    :param population: population of individuals
    :param fitness_func: fitness function
    :return: list of individuals
    """
    # Calculate the fitness of each individual
    fitness = []
    for individual in population:
        fitness.append(fitness_func(individual))

    # Create a roulette wheel
    wheel = []
    for i in range(len(population)):
        wheel.append(fitness[i] / sum(fitness))

    # Create the mating pool
    mating_pool = []
    for _ in range(len(population)):
        # Select a random value
        r = random.random()

        # Spin the wheel
        for i in range(len(wheel)):
            if r <= sum(wheel[:i + 1]):
                mating_pool.append(population[i])
                break

    return mating_pool

def generate_new_population(population, parents, fitness_func, mutation_rate):
    """
    Generate next generation of individuals
    :param population: current population
    :param parents: parents
    :param fitness_func: fitness function
    :param mutation_rate: mutation rate
    :return: next population
    """
    # Create the new population
    new_population = []

    # Reproduce until the population is filled
    while len(new_population) < len(population):
        # Select parents
        parent1 = random.choice(parents)
        parent2 = random.choice(parents)

        # Apply crossover
        child = crossover(parent1, parent2)

        # Apply mutation
        child = mutate(child, mutation_rate)

        # Add the child to the new population
        new_population.append(child)

    return new_population

def crossover(parent1, parent2):
    """
    Crossover parents to create children
    :param parent1: parent 1
    :param parent2: parent 2
    :return: child
    """
    # Create a child
    child = []

    # Loop through genes
    for i in range(len(parent1)):
        # 50% chance of using either parent
        if random.random() < 0.5:
            child.append(parent1[i])
        else:
            child.append(parent2[i])

    return child

def mutate(individual, mutation_rate):
    """
    Mutate an individual
    :param individual: individual
    :param mutation_rate: mutation rate
    :return: individual
    """
    # Loop through genes
    for i in range(len(individual)):
        # Apply mutation
        if random.random() < mutation_rate:
            individual[i] = 1 if individual[i] == 0 else 0

    return individual

def fitness_function(individual):
    """
    Fitness function
    :param individual: individual
    :return: fitness
    """
    # Initialize the fitness
    fitness = 0

    # Loop through genes
    for i in range(len(individual)):
        # Add 1 to the fitness for every 1 found
        if individual[i] == 1:
            fitness += 1

    return fitness

def main():
    # Create the initial population
    population = []

    # Create the fitness function
    fitness_func = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

    # Create the mutation rate
    mutation_rate = 0.01

    # Create the number of generations
    generations = 1000

    # Evolve the population
    population = genetic_algorithm(population, fitness_func, mutation_rate, generations)

    # Print the generation
    print(f"Generation: {generations}")

    # Print the individual
    print(f"Individual: {population}")

    # Print the fitness
    print(f"Fitness: {fitness_func(population)}")

코드를 작성할 때 제가 한 것은 처음에 def genetic_algorithm 까지 입력을 하여 제안받은 코드를 Tab을 통해 수락한 후, 그 후부터는 def 입력, Tab 클릭을 반복했을 뿐입니다. 그 결과, 유전 알고리즘이 30초만에 그것도 꽤나 높은 완성도로 구현이 되었습니다. 제가 대학생 시절, 몇 날 몇 일동안 머리 싸매며 구현했던 기억을 떠올리면 굉장히 허무하네요..

GitHub Copilot은 프레임 워크의 개발에도 사용 가능합니다.

위와 같이 // Express server on port 3000 라고 주석을 작성하는 것으로 Express 서버를 생성하고 기동하는 코드를 제안 받을 수 있습니다.

사용해본 후기

GitHub Copilot을 사용해본 후기로서 우선 정말 신기해서 시간 가는 줄 모르고 계속 만지게 되었습니다.설마 이런 것도 되겠어..? 라는 생각으로 주석을 작성해도 어느 정도 틀이 잡힌 코드를 제안 받을 수 있었습니다. 아래의 링크와 같이 Cloudformation 템플릿을 작성하는 것도 가능하다고 합니다.

하지만, 코드의 완성도, 기존 코드와의 연동 등 여러 가지 요인을 따져본다면 실제 어플리케이션의 개발이라던가 프로젝트에서는 아직은 그대로 사용하기에는 조금 무리가 있다고 느꼈습니다. 그렇기 때문에 Copilot에서 제안받은 코드를 100퍼센트 이용하여 개발을 한다기 보다, 어떠한 기능을 개발하고 싶은데 어떻게 시작해야 할지 모르겠을 때 Copilot에서 제안받은 코드 내용을 토대로 수정하면서 개발하는 방식으로 한다면 꽤나 도움이 될 것 같다고 생각하였습니다.

GitHub Copilot Technical Preview 신청을 아직 안하셨다면 신청을 하여 경험해보는 것도 좋을 것 같다고 생각합니다. 그럼, 이번 블로그는 이상으로 마치도록 하겠습니다!