GitHub Copilot: 생각의 속도로 Python으로 비행하기
Python에서 GitHub Copilot 시작하기
GitHub Copilot은 자연어를 12개 이상의 프로그래밍 언어로 실시간으로 코드로 번역할 수 있는 OpenAI Codex 시스템을 기반으로 한 최초의 상용 제품이다. OpenAI Codex는 GPT-3 딥러닝 언어 모델의 후손이다. Codex의 신경망은 텍스트와 GitHub에 호스팅된 수억 개의 공개 코드 리포지토리를 모두 학습했다.
Note: 데이터 과학자 Jodie Burchell이 출연하는 Real Python Podcast 121화를 들으며 GPT-3에 대해 자세히 알아보세요.
GitHub Copilot은 몇 가지 프로그래밍 언어와 영어에만 국한되지 않고 다양한 인간 언어를 이해한다. 예를 들어, 스페인어를 모국어로 사용하는 경우 모국어로 GitHub Copilot과 대화할 수 있다.
처음에 이 제품은 일부 사람들에게만 기술 미리 보기로 제공되었다. 하지만 최근에 변경되어 현재는 누구나 코드 편집기에서 인공 지능의 놀라운 힘을 경험할 수 있다. 이 제품을 테스트해 보고 싶으다면 GitHub Copilot을 구독하여야 한다.
GitHub Copilot 구독
GitHub Copilot을 사용하려면 GitHub 프로필의 청구 설정으로 이동하여 관련 섹션이 보일 때까지 아래로 스크롤한다. 안타깝게도 이 서비스는 대부분의 사용자에게 무료로 제공되지 않는다. 이 글을 작성하는 시점에서 이 서비스는 선불로 결제할 경우 월 10달러 또는 연간 100달러의 비용이 든다. 결제 정보를 제공해야만 60일간의 체험 기간 동안 무료로 이용할 수 있다.
Note: 원치 않는 요금이 청구되지 않도록 미결제 구독 플랜은 만료되기 전에 취소하세요!
학생과 오픈소스 관리자는 GitHub Copilot을 무료로 구독할 수 있다. 운이 좋다면 서비스를 활성화한 후 다음 정보를 볼 수 있다.

GitHub는 학교 ID 사진이나 .edu 도메인의 이메일 주소 또는 인기 있는 오픈 소스 리포지토리 중 하나에서의 활동과 같은 증명을 기반으로 1년에 한 번 회원님의 상태를 확인한다.
GitHub 구독 설정과 관리에 대한 자세한 지침은 공식 문서의 단계를 따르도록 한다. 다음으로는 Visual Studio Code용 GitHub Copilot 확장 프로그램을 설치하는 방법을 알아보자.
Visual Studio Code 확장 프로그램 설치
Microsoft가 GitHub를 소유하고 있기 때문에, Visual Studio Code 편집기가 GitHub Copilot 지원을 가장 먼저 받은 도구라는 것은 놀라운 일이 아니다. Visual Studio Code에서 확장 프로그램을 설치하는 방법에는 몇 가지가 있지만, 가장 빠른 방법은 Ctrl+P 또는 Cmd+P를 사용하여 빠른 열기 패널을 불러온 다음 다음 명령을 입력하는 것이다.
ext install GitHub.copilot
Enter 키를 눌러 확인하면 확장 프로그램이 설치되고 나중에 에디터를 다시 로드하라는 메시지가 표시된다.
또는 창 왼쪽에 있는 Active Bar에서 확장 프로그램 아이콘을 찾아 Visual Studio Marketplace에서 GitHub Copilot 확장 프로그램을 검색해 볼 수도 있다.

해당 키보드 단축키를 사용하여 Visual Studio Code에서 바로 Extension view를 표시할 수도 있다.
설치가 완료되면 Visual Studio Code에서 새 확장 프로그램에 필요한 GitHub 프로필에 대한 액세스 권한을 부여하기 위해 GitHub에 로그인하라는 메시지가 표시된다.

Visual Studio Code는 사용자가 누구인지 알아야 GitHub Copilot 구독 상태를 확인할 수 있다. 하지만 GitHub 프로필에 대한 액세스 권한을 허용하면 에디터가 비공개 리포지토리를 읽을 수 있다. 마음이 바뀌면 언제든지 GitHub 프로필 설정으로 이동하여 인증된 OAuth 앱에서 VS Code용 GitHub를 찾아 이 권한을 취소할 수 있다.
Note: 막히는 부분이 있으면 공식 Getting started with GitHub Copilot in Visual Studio Code 가이드를 참고하세요.
Visual Studio Code에서 GitHub Copilot으로 더욱 생산적으로 작업할 수 있도록 기억해 두면 좋은 가장 일반적인 키보드 단축키를 소개한다.
| Action | Linux/Windows | MacOS |
|---|---|---|
| Trigger inline suggestions | Alt+\ |
Option+\ |
| See the next suggestion | Alt+] |
Option+] |
| See the previous suggestion | Alt+[ |
Option+[ |
| Accept a suggestion | Tab |
Tab |
| Dismiss an inline suggestion | Esc |
Esc |
| Show all suggestions in a new tab | Ctrl+Enter |
Ctrl+Enter |
기본 단축키가 작동하는 데 문제가 있는 경우 Visual Studio Code에서 직접 키 바인딩을 정의해 보세요. 이 방법은 미국 이외의 키보드 레이아웃으로 작업하는 경우 특히 유용할 수 있다.
때때로 GitHub Copilot의 제안이 방해가 될 수 있다. 이 경우 에디터 창의 오른쪽 하단에 있는 확장 프로그램 아이콘을 클릭하여 전역적으로 또는 특정 프로그래밍 언어에 대해 비활성화할 수 있다.
![]()
끝났습니다! 이제 Visual Studio Code에서 GitHub Copilot 확장 프로그램을 사용할 준비가 되었다.
GitHub Copilot에 고삐 넘기기
이제 Visual Studio Code에서 GitHub Copilot이 예상대로 작동하는지 확인해야 한다. Visual Studio Code 편집기에서 GitHub Copilot이 예상대로 작동하는지 확인하려면 새 텍스트 파일을 만들고 기본 프로그래밍 언어로 Python을 선택한 다음 hello()와 같은 샘플 함수 서명을 작성하기 시작한다.
def hello():
첫 줄 끝에 콜론(:)을 입력하여 새 코드 블록을 도입하는 즉시 GitHub Copilot이 제안된 함수 본문을 채워준다. Tab 키를 눌러 수락하거나 Esc 키를 눌러 거부할 때까지는 회색 글꼴로 표시된다. 이 경우 제안된 코드는 print() 함수를 호출하여 Hello World 텍스트를 화면에 표시한다. 멋지지는 않지만 GitHub Copilot이 실제로 올바르게 작동하고 있음을 확인할 수 있다.
def hello():
print("Hello World")
Note: 확장 프로그램이 함수 본문을 얼마나 빨리 채우는지 눈치채나요? GitHub Copilot의 엔지니어링 팀은 더 나은 개발자 경험을 위해 주어진 제안의 지연 시간을 낮추기 위해 많은 노력을 기울였다.
플러그인 설치가 성공했는지 확인하기 위하여 다른 예를 시도해 보자. add(a, b)와 같이 두 개의 숫자를 더하는 함수의 이름을 나타내는 함수 시그니처를 작성하기 시작한다.
def add(a, b):
def add(a, b):
return a + b
물론 GitHub Copilot은 a와 b의 합을 반환하는 매우 합리적인 제안을 제공한다. 함수에서 값을 반환하는 것과 화면에 결과를 인쇄하는 것의 차이점을 알아보자. 영리한 가상 조수는 함수의 이름과 인수를 통해 의도를 유추할 수 있다.
Note: GitHub Copilot에 고유한 마법은 없다고 해도 과언이 아니다. 이 도구는 방대한 양의 고품질 데이터를 학습하여 지금까지 파일이나 프로젝트에서 본 내용을 바탕으로 가장 가능성이 높은 출력을 결정할 수 있다. 이 도구는 코드를 이해할 수 없기 때문에 항상 올바른 제안을 하는 것은 아니다.
이 튜토리얼의 나머지 부분에서는 일상적인 소프트웨어 엔지니어링 작업에서 GitHub Copilot의 몇몇 실제 사용 사례를 살펴본다. 프로그래밍 요구 사항에 맞는 즉각적인 코드 제안을 통해 생산성을 완전히 새로운 수준으로 끌어올리는 방법을 배울 수 있을 것이다.
자연어로부터 Python 코드 합성하기
GitHub Copilot은 자연어와 다양한 프로그래밍 언어의 선별된 샘플에 대해 학습되었기 때문에 두 영역을 모두 이해하는 것으로 보인다. 따라서 일반 영어나 다른 자연어를 사용하여 추상적인 문제를 GitHub Copilot에게 설명하면 원하는 프로그래밍 언어로 해당 코드를 생성할 것으로 기대할 수 있다.
기본 머신 러닝 모델은 그 반대, 즉 자연어로 코드를 설명하거나 한 프로그래밍 언어를 다른 프로그래밍 언어로 번역하는 작업도 수행할 수 있다. 하고 싶은 일에 대한 비전은 있지만 아직 컴퓨터 프로그래밍을 마스터하지 못한 초보자나 창의적인 인재에게 이 기능이 얼마나 도움이 될지 상상해 보세요.
이제 인간 언어와 컴퓨터 언어 간의 번역이 실제로 어떻게 이루어지는지 살펴보겠다.
Python 주석을 사용하여 문제 설명하기
Robert C. Martin같은 프로그래밍계의 영향력 있는 인물들은 코드 주석을 반패턴으로 간주하지만, 주석은 특정 코드가 왜 그렇게 보이는지 설명하는 데 도움이 될 때도 있다. 일반적으로 코멘트는 미래의 자신이나 같은 코드베이스에서 작업하는 팀원을 위해 작성한다.
여기에 GitHub Copilot을 추가하면 코드 주석을 읽을 수 있는 또 다른 대상 고객이 생기는 것이다. 고전적인 Hello, World! 프로그램을 설명하는 다음과 같은 한 줄짜리 Python 주석을 생각해 보자.
# Print "Hello, World!"
코드 편집기에 해당 주석을 입력하면 GitHub Copilot이 자동으로 인식하지 못한다는 것을 알 수 있다. 코멘트를 통해 Copilot과 소통하기로 선택한 경우, 제안 사항을 보려면 GitHub Copilot 사이드 패널 또는 탭을 열어야 한다. 또는 약간의 코드를 입력하여 자동으로 완성되도록 할 수도 있다. 어느 쪽이든 위에서 주석을 작성하면 다음과 같은 Python 코드가 표시된다.
print("Hello, World!")
이는 hello() 함수 스텁을 작성하여 Visual Studio Code 확장을 확인했을 때와 거의 동일한 제안이다. 하지만 이번에는 약간 다른 출력이 표시되었다. GitHub Copilot은 사용자가 주석의 인용된 부분을 명령어가 아닌 리터럴 텍스트로 취급하고 싶다는 것을 이해한다.
GitHub Copilot에게는 너무 쉬운 일이었나 보다. 좀 더 구체적인 출력을 요청하여 기준을 높이는 것은 어떨까? 예를 들어 Hello, World!를 스페인어로 거꾸로 인쇄하고 싶을 수 있다.
# Print "Hello, World!" backward in Spanish.
에디터에서 GitHub Copilot 패널을 새로 고치면 새로운 제안이 표시된다. 제안의 수와 품질은 GitHub Copilot을 실행할 때마다 달라질 수 있다. 이 특정 주석에 가장 적합한 것은 다음과 같다.
print("¡Hola, mundo!!"[::-1])
정말 인상적이지 않은가! GitHub Copilot은 정확할 뿐만 아니라 숙련된 파이토니스트라면 직접 작성할 수 있는 파이토닉 코드를 생성하여 이 문제를 해결한다. 더 많은 주석을 작성한 제안이 더욱 흥미로워진다.
더 많은 주석을 추가하여 문제 복잡도 높이기
한 줄 주석을 사용하여 문제를 설명하는 것도 좋지만, 그 안에 많은 내용을 담는 데는 한계가 있다. 다행히도 여러 줄로 연속된 주석을 논리적이고 일관성 있는 스토리로 결합하면 GitHub Copilot이 전체적으로 처리할 수 있다. 각 줄에 줄 바꿈 없이 완전한 문장을 넣는 것이 가장 좋지만, 선택적으로 문장 중간에 명시적인 백슬래시(\)를 넣어 줄 바꿈을 표시할 수 있다.
# Ask the user to provide a line of text.
# Scan the text for the following mildly offensive words: \
# arse, bloody, damn, dummy.
# If you find any, then replace its letters with asterisks \
# except for the first letter in each offensive word.
# Print the resulting text.
def main():
정확한 결과를 얻으려면 문법적으로 정확한 언어를 사용하고 구두점에 신경을 써야 한다. 이 경우 주석에 함수 시그니처를 추가하면 GitHub Copilot에 추가 단서를 제공할 수 있다. 여러분이 얻게 될 제안 중 하나는 꽤 괜찮아 보인다.
def main():
text = input("Enter a line of text: ")
offensive_words = ["arse", "bloody", "damn", "dummy"]
for word in offensive_words:
if word in text:
text = text.replace(word, word[0] + "*" * (len(word) - 1))
print(text)
위의 함수에 대한 설명이 필요하다면 댓글에 있는 텍스트 설명을 참조하면 된다. 제안된 코드는 여기에 설명된 내용과 매우 유사하다.
Note: GitHub Copilot은 다양한 스타일과 규칙으로 코드를 생성한다. 일관성을 유지하고 불필요한 방해 요소를 제거하기 위해 여기서는 제시된 모든 코드 제안은 PEP 8 스타일 가이드를 준수하도록 다시 포맷되었다. 대부분의 편집기에서 버튼 하나만 누르면 코드를 직접 다시 포맷할 수 있다는 점을 기억하자.
main() 함수를 호출하면 생성된 코드가 얼마나 잘 수행되는지 평가할 수 있다.
Enter a line of text: She loves you, dummy.
She loves you, d****.
놀랍지 않나? GitHub Copilot에 작업에 대한 자연어 설명을 제공했더니 정확히 맞는 솔루션을 찾아냈다.
Note: 이 포스팅에 제시된 제안과 다른 제안이 나올 수도 있다는 점에 유의하세요. 원하는 결과를 얻기까지 시행착오를 겪을 수도 있으므로 만족스러운 결과가 바로 나오지 않는다면 주석을 조금씩 수정해 보자.
GitHub Copilot에서 주목해야 할 중요한 점은 정말 불쾌한 단어, 음란물, 개인 데이터 또는 비밀 API 키와 같은 민감한 정보를 차단하기 위해 여러 가지 필터가 구현되어 있다는 것아다. 이러한 요소가 포함된 제안은 절대 제공되지 않도록 노력해야 한다. 누군가의 개인 정보나 비밀을 알려주도록 GitHub Copilot을 유인하여 이러한 필터를 실험해 볼 수 있다.
| 코드 | 제안 결과 |
|---|---|
| offensive_words = [ | None |
| # My phone number is | # My phone number is +1 (###) ###-#### |
| GITHUB_API_KEY = | GITHUB_API_KEY = <GITHUB_API_KEY> |
대부분의 경우, 민감한 정보를 인식하고 무작위 또는 익명화된 결과를 제공하는 데 꽤 잘 작동한다. 하지만 필터링 메커니즘이 완벽하지 않기 때문에 이론적으로는 훈련 데이터 세트에서 누군가의 실제 데이터가 유출될 수 있다. 공식 웹사이트에 따르면 이런 일이 발생할 가능성은 극히 적다고 한다.
GitHub Copilot이 제안하는 코드의 대부분은 이전에 본 적이 없는 코드이기 때문이다. 최근 내부 조사에 따르면 약 1%의 확률로 추천 코드에 트레이닝 세트와 일치하는 150자 이상의 코드 조각이 포함될 수 있다고 한다. (출처)
이 가능성에 대해서는 나중에 자세히 살펴보겠지만, 지금은 GitHub Copilot이 프로그래밍 퍼즐을 대신 풀어주도록 하여 게임을 한 단계 업그레이드할 때이다.
프로그래밍 경시대회 퍼즐 풀기
GitHub Copilot의 진정한 힘을 테스트하려면 코딩 챌린지(coding challenge)에 대한 텍스트 설명의 일부를 복사하여 붙여넣고 어떻게 처리되는지 확인할 수 있다. 예를 들어, 2021년 Advent of Code 달력의 첫날에 나오는 Sonar Sweep 퍼즐의 첫 번째 부분을 가져와 보았다.
# For example, suppose you had the following report:
#
# 199
# 200
# 208
# 210
# 200
# 207
# 240
# 269
# 260
# 263
#
# (...)
#
# In this example, there are 7 measurements that are larger \
# than the previous measurement.
#
# How many measurements are larger than the previous measurement?
def solve(measurements):
문제에 대한 전체 설명은 약간 길지만 지면 관계상 위의 코드 블록에서 축약하여 설명했다. 간단히 말해, 목표는 이전 측정값에 비해 측정값이 증가하는 횟수를 확인하는 것이다. 얼마나 많은 사람들이 정확히 같은 문제를 해결하고 GitHub에서 해결책을 공유했는지를 고려할 때 GitHub Copilot은 이 특정 문제에 대한 해결책을 특히 잘 제시한다.
def solve(measurements):
count = 0
for i in range(1, len(measurements)):
if measurements[i] > measurements[i - 1]:
count += 1
return count
측정 리스트의 두 번째 항목에서 시작하여 현재 측정값을 이전 측정값과 비교하는 짧은 루프이다. 현재 측정값이 더 크면 카운터가 증가하고, 함수가 마지막에 이 값을 반환한다. 이 솔루션은 올바르게 작동하고 상당히 효율적이며 가독성이 좋지만, GitHub Copilot의 제안을 문자 그대로 사용해야 한다는 의무감은 갖지 마세요. 전부 아니면 전무가 아니다!
GitHub Copilot에서 생성된 코드를 내 코드처럼 편집할 수 있다. 때로는 형식이나 명명 규칙이 마음에 들지 않거나 다시 작성하고 싶은 로직의 특정 부분이 있을 수 있다. 다른 경우에는 제안을 보고 더 영리한 대안을 떠올리게 될 수도 있다. 이 경우 예를 들어 이 한 줄짜리 솔루션을 사용할 수 있다.
def solve(measurements):
return sum(1 for x, y in zip(measurements, measurements[1:]) if y - x > 0)
인접한 모든 측정값 쌍의 차이가 양수인 경우 제너레이터 표현식을 사용하여 생성된 측정값 시퀀스의 합을 구한다. Python 지식 수준에 따라 이 방법 또는 제안된 코드가 더 마음에 들 수도 있다.
이제 하나 이상의 Python 주석을 사용하여 자연어에서 코드를 생성하는 방법을 알게 되었다. 하지만 소프트웨어 엔지니어라면 문맥에 맞는 코드 제안이 훨씬 더 유용할 것이다.
더욱 지능적인 코드 완성 제안 받기
GitHub Copilot은 프로젝트의 컨텍스트를 심층적으로 이해하여 가장 적합한 제안을 제공하는 지능형 코드 완성 메커니즘이라고 생각하면 된다. GitHub Copilot을 충분히 오래 사용하다 보면 가끔은 이 도구가 내 마음을 읽는 것 같은 소름 끼치는 느낌을 받을 수도 있다. 이 섹션에서는 이러한 동작을 보여주는 몇 가지 예를 보이겠다.
GitHub Copilot이 내 마음을 읽도록 하기
세 계수 𝑎, 𝑏와 𝑐를 갖는 2차 다항식(이차 함수라고도 함)의 근을 구하려고 한다. 이러한 함수는 다음 식을 사용하여 표현할 수 있다.
\(f(x) = ax^2 + bx + c\)
예를 들어, 구체적인 함수 \(𝑥^2 + 2𝑥 - 3\)의 계수 값은 다음과 같다: 𝑎 = 1, 𝑏 = 2, 𝑐 = -3으로, 그리스 문자 델타로 표시되는 소위 판별식을 계산하는 데 사용할 수 있다.
\(\Delta = b^2 - 4ac\)
이 경우 위의 공식에서 문자 𝑎, 𝑏, 𝑐를 해당 값으로 바꾸면 함수의 판별식 값은 16(\(\Delta = 16\))이 된다. 판별식 값의 부호에 따라 이차 함수는 근이 두 개, 하나 또는 전혀 없을 수 있다.
\(\Delta\) > 0 : \(x_1\) = \(\frac { - b - \sqrt {\Delta } } { 2 a }\) and \(x_2\) = \(\frac { - b + \sqrt {\Delta } } { 2 a }\)
\(\Delta\) = 0 : \(x_0\) = \(\frac { - b } { 2 a }\)
\(\Delta\) <> 0 : No roots
앞서 계산한 판별식이 양수인 16이므로 이차 함수에 정확히 두 개의 근이 있으며, 위의 공식을 적용하면 \(𝑥_1\) = -3 및 \(𝑥_2\) = 1로 된다는 것을 알 수 있다. 이 중 하나를 이차 함수에 𝑥 변수의 값으로 연결하면 함수가 0을 반환한다. 함수의 근은 플롯했을 때 해당 포물선이 가로축을 가로지르는 지점이다.
이제 세 가지 계수인 𝑎, 𝑏, 𝑐를 기반으로 이러한 다항식의 근을 찾는 Python 함수를 구현할 수 있다. 판별식을 계산하고 정확한 근의 수를 평가하여 동일한 알고리즘을 따르고 싶을 것이다. 먼저 의도를 알 수 있는 설명적 함수 시그니처 작성하는 것으로 시작한다.
def find_roots(a, b, c):
잠시 후 GitHub Copilot이 제안을 하기 시작할 것이다. 운이 좋다면 그 중 하나가 놀라울 정도로 정확할 것이다.
def find_roots(a, b, c):
d = b**2 - 4 * a * c
if d < 0:
return None
elif d == 0:
return -b / (2 * a)
else:
return (-b + d**0.5) / (2 * a), (-b - d**0.5) / (2 * a)
이 함수는 올바른 공식을 사용하여 판별식을 계산하고 그 결과를 임시 변수에 저장한다. 판별식 값의 부호에 따라 올바르게 계산된 None, 근의 단일 값 또는 근의 두 값으로 구성된 튜플을 반환한다. 제안된 솔루션은 지수 연산자(**)를 사용하여 판별식의 제곱근을 계산한다는 점에 유의하자. 이렇게 하면 Python의 수학 모듈에서 sqrt() 함수를 가져오는 것을 피할 수 있다.
Note: 인공 지능은 새로운 것을 계속 학습하기 때문에 완전히 결정론적이지 않으므로 표시되는 제안이 다르게 보일 수 있다. 다른 시간에 요청된 동일한 입력에 대해 다른 결과를 얻을 수도 있다. 특히 프로젝트에 추가 소스 코드가 있는 경우 GitHub Copilot이 컨텍스트를 가져올 때 더욱 그럴 수 있다.
하지만 생성된 함수는 실수근이 존재하는 경우에만 실수근을 찾을 수 있다. 대신 복소수 영역의 모든 근을 표시하고 싶다면 어떻게 해야 할까? 다음 섹션에서는 GitHub Copilot에 이러한 힌트를 제공하는 방법에 대해 알아보도록 하겠다.
더 나은 제안을 위한 컨텍스트 제공
대수학의 기본 정리에 따르면 복소수 계수를 갖는 𝑛차 다항식은 정확히 𝑛개의 복소수 근을 갖는다. 즉, 이차 다항식인 이차 함수는 실수 영역에 복소수 근이 없는 경우에도 항상 정확히 두 개의 복소수 근을 갖는다.
판별식 값이 음수여서 근이 없음을 나타내는 함수 \(𝑥^2\) + 1을 생각해 보자. 수평축을 가로지르지 않는 함수의 포물선을 그려보면 근이 없다는 것을 그래픽으로 확인할 수 있다. 그러나 동일한 함수에는 \(𝑥_1\) = -𝑖 및 \(𝑥_2\) = 𝑖 라는 두 개의 복소수 근이 있으며, 여기서 𝑖는 제곱하면 -1이 되는(\(i^2\) = -1) 가상의 단위이다.
실수 루트 대신 복소수 루트를 얻도록 GitHub Copilot에 구현을 변경하도록 요청하려면 어떻게 해야 할까?
GitHub Copilot이 참고할 수 있는 컨텍스트를 제공하여 문제에 제약 조건을 추가해야 한다. 예를 들어, 사용하려는 모듈을 가져오거나 예상되는 결과를 자연어로 설명하는 Python 문서 문자열을 작성할 수 있다.
import cmath
def find_roots(a, b, c):
"""Return a tuple of complex numbers."""
여기에서는 수학 모듈의 복소수 대응 모듈인 cmath 모듈을 임포트한다. 여기에는 음수의 제곱근을 계산할 수 있는 cmath.sqrt() 함수가 포함되어 있지만, math.sqrt()는 이러한 경우 오류를 발생시키는 반면, 이 함수는 음수의 제곱근을 계산할 수 있다.
>>> import cmath
>>> cmath.sqrt(-1)
1j
>>> import math
>>> math.sqrt(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: math domain error
복소수 영역에서 -1의 제곱근은 허수 단위를 산출하며, Python에서는 이를 1j라고 한다. Python에서 복소수 사용에 대한 자세한 내용을 읽어보면 왜 Python이 허수 단위를 나타내는 데 i 대신 문자 j를 사용하는지 알 수 있다.
문서 문자열은 함수가 반환해야 하는 예상 데이터 타입을 나타낸다. 경우에 따라 더 구체적인 단어를 포함하여 기대하는 바를 명확히 해야 할 수도 있다. 예를 들어 "두 복소수의 튜플"이라고 쓰면 정확히 두 개의 요소로 구성된 튜플을 의미합니다. 반면에 튜플 대신 쌍이라는 단어를 사용하면 덜 명확하다.
Note: docstrings 외에도 GitHub Copilot은 Python 코드의 타입 힌트를 이해한다.
이 두 가지 작은 단서를 추가하면 GitHub Copilot은 이제 동일한 함수 시그니처에 대해 각각 다른 구현을 생성한다.
import cmath
def find_roots(a, b, c):
"""Return a tuple of complex numbers."""
d = (b**2) - (4 * a * c)
x1 = (-b + cmath.sqrt(d)) / (2 * a)
x2 = (-b - cmath.sqrt(d)) / (2 * a)
return x1, x2
이 함수는 이전처럼 판별식을 계산하지만 더 이상 부호를 확인하지 않는다. 대신 이 함수는 원하는 대로 cmath.sqrt() 함수를 활용하여 두 복소수 근을 계산한다. 새 함수를 Python REPL에서 테스트하여 함수가 두 복소근을 올바르게 계산하는지 확인할 수 있다.
>>> import cmath
>>> def find_roots(a, b, c):
... """Return a tuple of complex numbers."""
... d = (b**2) - (4 * a * c)
... x1 = (-b + cmath.sqrt(d)) / (2 * a)
... x2 = (-b - cmath.sqrt(d)) / (2 * a)
... return x1, x2
...
>>> find_roots(1, 0, 1) # Function f(x) = x² + 1
(1j, -1j)
>>> 1j**2 + 1
0j
>>> (-1j)**2 + 1
0j
이 코드는 훌륭하게 작동한다! \(x^2\) + 1 함수는 두 복소수 근인 1j와 -1j에 대해 0을 반환한다.
특별히 흥미롭지 않더라도 시간을 많이 절약할 수 있는 GitHub Copilot의 창의성에 감탄하게 될 것이다. 다음에는 GitHub Copilot을 사용하여 클래스 본문을 생성해 보겠다.
GitHub Copilot의 창의력 활용하기
새로운 데이터 유형을 설계하다가 적절한 속성이나 구현 방법을 찾지 못해 막막했던 적이 있나요? GitHub Copilot을 사용하면 탭을 누르기만 하면 새로운 속성, 메서드 및 프로퍼티가 자동 생성되므로 편안하게 앉아 휴식을 취할 수 있다.
Python의 data classes를 사용하여 Person 클래스를 정의하고 싶다고 하자. 먼저 새 데이터 타입에 의미 있는 이름 .first_name라는 첫 번째 속성을 도입한다.
from dataclasses import dataclass
from datetime import date
@dataclass
class Person:
first_name: str
GitHub Copilot은 그 다음으로 가능성이 높은 속성인 .last_name을 제안한 다음 .age를 제안하여 즉시 선택한다. 하지만 사람의 나이는 시간이 지남에 따라 변한다는 것을 알고 있으므로 대신 생년월일을 기록해 두는 것이 좋다. GitHub Copilot의 다음 논리적 제안은 현재 날짜를 기준으로 사람의 나이를 계산하는 방법이다. 새 속성을 정의할 때 이름과 성을 연결하여 본문을 깔끔하게 완성한다.
결국 아래 코드 편집기에서 몇 번의 키 입력만으로 얻을 수 있는 결과이다.
from dataclasses import dataclass
from datetime import date
@dataclass
class Person:
first_name: str
last_name: str
birth_date: date
def age(self):
return (date.today() - self.birth_date).days // 365
@property
def full_name(self):
return f"{self.first_name} {self.last_name"}
시간을 크게 절약하고 생산성을 크게 높일 수 있다. 무엇을 입력해야 할지 알고 있더라도 GitHub Copilot을 사용하면 생각의 속도로 코딩할 수 있으며, 사용자가 무엇을 생각하고 있는지 추측한 다음 적절한 제안을 제공하고 버튼 클릭이나 키 입력 한 번으로 수락할 수 있다.
개인 번역기로 여러 프로그래밍 언어를 구사 하기
하나의 파일에 HTML, CSS, JavaScript, Django templating language, Python 등 여러 언어를 혼합하는 것은 드문 일이 아니다. 다행히도 GitHub Copilot은 12개 이상의 프로그래밍 언어, 훨씬 더 많은 프레임워크, 여러 가지 인간 언어를 알고 있다. 상황에 따라 흐름을 끊지 않고 여러 언어간 전환하는 데 아무런 문제가 없다.
예를 들어, 일치하는 이름으로 사용자를 검색하는 SQL 쿼리를 저장하기 위해 Python 변수를 정의하고 싶을 수 있다. 변수 이름에 적절한 단어를 사용하기만 하면 다음과 같은 합리적인 제안을 받을 수 있다.
query_users_by_first_or_last_name = """
SELECT * FROM users
WHERE first_name LIKE %s OR last_name LIKE %s
"""
세 따옴표(""")를 사용하여 여러 줄의 문자열 리터럴을 정의했기 때문에 개별 줄을 단계별로 구분하여 가져오는 것을 볼 수 있다.
이 쿼리는 언뜻 보기에는 괜찮아 보이지만, GitHub Copilot이 테이블 이름과 검색할 두 개의 열에 대해 몇 가지 가정을 하고 있다. 그래도 SQL 쿼리로 일반 문자열이 아닌 prepared statement를 생성하여 SQL injection attack을 방지하는 데 도움이 된다는 점은 위안이 된다.
이쯤 되면 GitHub Copilot에 꽤 익숙해졌을 것이다. 하지만 아직 다뤄야 할 부분이 많으니 조금만 더 기다려주세요!
가상 버디와 함께 페어 프로그래밍 연습하기
공식 GitHub Copilot 웹사이트를 방문하기 전에도 웹 검색 결과에서 AI 페어 프로그래머로 광고되고 있다는 것을 금방 알 수 있다. 간단히 말해, 페어 프로그래밍은 두 명의 엔지니어가 동일한 기능을 함께 작업하는 인기 있는 애자일 기법이다. 표면적으로는 이와 같은 소프트웨어를 제작하는 데 더 많은 비용이 들지만, 장기적으로는 비용이 많이 드는 버그 수정이 줄어든다.
페어 프로그래밍의 이점은 다음과 같다.
- 생산된 코드의 품질 향상
- 팀 전체의 코드베이스에 대한 전반적인 이해도 향상
- 모범 사례에 대한 지식과 공유
코드에 대해 솔직한 피드백을 주고, 근본적인 문제를 발견하고, 바른 방향으로 이끌어 줄 수 있는 실제 사람과 함께 작업하는 것의 이점을 능가하기는 어렵다. 페어 프로그래밍의 많은 이점은 팀 공동 작업으로 이루어질 때만 볼 수 있다. 반면에 인공 지능은 잠재적으로 잘못된 제안을 하여 코드 품질을 떨어뜨릴 수 있다. 궁극적으로 인공 지능의 제안을 받아들일지 여부는 여러분의 결정에 달려 있다!
곧 알게 되겠지만, GitHub Copilot은 생산성을 높이는 데 매우 효과적이다.
테스트용 샘플 데이터 fixture 생성하기
일상적인 개발 과정에서 테스트, 문서화 또는 기본 설정을 위한 샘플 데이터를 생각해야 할 때가 종종 있다. 막막할 때 GitHub Copilot이 도움을 줄 수 있다. 예를 들어 앞서 정의한 Person 클래스를 떠올려 보자.
from dataclasses import dataclass
from datetime import date
@dataclass
class Person:
first_name: str
last_name: str
birth_date: date
def age(self):
return (date.today() - self.birth_date).days // 365
@property
def full_name(self):
return f"{self.first_name} {self.last_name}"
이제 회사의 직원을 나타내기 위해 이 클래스의 인스턴스를 몇 개 만들어야 한다고 가정해 보겠다. 동일한 파일을 편집 중이거나 다른 모듈에서 Person 클래스를 가져온 상태에서 GitHub Copilot을 사용하여 직원 목록을 채울 수 있다. 표현식 변수 이름을 사용하여 빈 Python 리스트를 선언하고 여는 대괄호([) 바로 뒤에 Enter 키를 누르면 제안이 트리거된다.
employees = [
제안된 각 줄 뒤의 Tab을 계속 누르면 다음과 같은 직원 리스트를 얻을 수 있다.
employees = [
Person("John", "Smith", date(1970, 1, 1)),
Person("Jane", "Doe", date(1980, 1, 1)),
Person("Joe", "Doe", date(1990, 1, 1)),
Person("Jack", "Doe", date(2000, 1, 1)),
Person("Jill", "Doe", date(2010, 1, 1)),
Person("Jana", "Doe", date(2020, 1, 1)),
]
GitHub Copilot은 직원 목록 요소에 Person 클래스를 사용하는 방법을 성공적으로 알아냈다. 나중에 해당 리스트를 필요로 하는 테스트 케이스 그룹에 대한 공통 테스트 fixture로 사용할 수 있다. 예를 들어 테스트 프레임워크에서 호출할 함수로 리스트를 래핑할 수 있다. GitHub Copilot은 테스트와 테스트 중인 코드를 모두 제안하여 테스트 프로세스 자체에 큰 도움이 될 수 있다.
테스트 케이스가 마법처럼 나타나길 바라며
테스트 함수는 테스트 실행 보고서에 표시되므로 길고 설명이 포함된 이름을 짓는 것이 좋다. 테스트 함수 중 하나가 실패하면 이름이 잘 지어진 테스트 함수는 무엇이 작동하지 않는지 즉시 주의를 끌 수 있을 것이다. 행동 중심 개발(behavior-driven development)을 지지하는 사람들은 테스트 중인 행동에 초점을 맞추기 위해 '해야 한다'라는 단어로 시작하는 전체 문장을 사용하여 테스트가 비즈니스 요구 사항 명세서처럼 보이도록 할 것을 권장한다.
때때로 터무니없이 긴 함수 이름을 사용하는 것의 또 다른 장점은 테스트 케이스 구현을 생성하는 데 GitHub Copilot이 이를 사용할 수 있다는 것이다. 예를 들어 Person 클래스의 테스트 모듈에서 다음 함수 시그니처을 정의해 보자.
def test_should_not_be_able_to_set_full_name():
대부분의 테스트 프레임워크는 테스트 함수 이름 앞에 test_를 붙이는 것과 같이 표준 명명 규칙을 따르는 경우 자동으로 테스트 케이스를 검색한다. GitHub Copilot을 사용하면 위의 함수에 대해 트리거된 제안은 다음과 같이 보일 수 있다.
def test_should_not_be_able_to_set_full_name():
person = Person("John", "Doe", date(1980, 1, 1))
with pytest.raises(AttributeError):
person.full_name = "Jane Doe"
신기롭게 GitHub Copilot은 표준 라이브러리에서 사용할 수 있는 내재된 unittest 모듈보다 수동으로 설치하고 가져와야 하는 외부 pytest 라이브러리를 더 선호한다.
Note: 이러한 선택은 두 도구의 인기도를 짐작할 수 있으며, 실제로
pytest는 Python 생태계에서 가장 광범위하고 다재다능한 테스트 프레임워크 중 하나이다.
기존 코드의 테스트 케이스 구현을 자동화하는 것이 도움이 될 때도 있지만, 단계를 뒤집어 테스트 중심 개발을 사용하여 하향식 방식으로 소프트웨어를 개발하는 것이 훨씬 더 바람직할 수 있다. 이 접근 방식에서는 테스트 케이스를 아직 존재하지 않는 코드의 상위 수준 사양으로 먼저 작성한다. 자동화된 테스트 케이스가 완성되면 이를 통과하기 위한 코드를 작성한다.
테스트 중심 개발(TDD) 연습하기
이전에 TDD를 연습해 본 적이 없다면 테스트 중심 개발에 대한 단계별 실습 자습서인 'TDD로 Python에서 해시 테이블 만들기' 튜토리얼을 참조하세요.
이 과정은 간단히 세 단계로 요약할 수 있다.
- 만족시킬 수 있는 실패한 테스트 케이스 작성하기
- 테스트 케이스가 통과할 수 있도록 최소한의 코드를 구현한다.
- 원하는 경우 모든 테스트 케이스를 여전히 통과하도록 코드를 리팩터링한다.
그런 다음 헹구고 반복한다! 이 지속적인 사이클을 유지할 수 있을 만큼 훈련이 되어 있다면 테스트 커버리지가 높고 문서화할 수 있는 테스트 가능한 코드를 작성할 수 있다. 동시에 필요 없는 코드를 작성하는 것을 피할 수 있어 전반적인 유지 관리 비용을 줄일 수 있다. 하지만 테스트 중심 개발이 만병통치약이 아니므로 예를 들어 연구 개발 프로젝트에서는 실용적이지 않을 수 있다.
그렇다면 위에서 언급한 튜토리얼의 해시테이블 구현을 복제하고, 가상 페어 프로그래머로 GitHub Copilot을 사용해 보는 것은 어떨까? 같은 폴더에 빈 Python 파일 두 개를 나란히 만든다.
src/
├── hashtable.py
└── test_hashtable.py
아래 그림의 왼쪽에 표시된 첫 번째 폴더에는 테스트 중인 코드가 들어 있다. 다른 하나는 오른쪽에 표시된 것처럼 구현을 주도하는 테스트 케이스가 있다. 다음으로, 첫 번째 테스트 케이스를 작성하여 새로운 HashTable 클래스의 인스턴스화를 확인한다.

테스트 중심 개발을 따르려면 해당 코드를 구현하기 전과 후에 각 테스트 케이스를 실행하여 올바른 것을 테스트하고 있는지 확인해야 한다. 그 외에도 테스트를 만족시킬 수 있는 최소한의 기능만 구현해야 한다. 하지만 GitHub Copilot은 한 걸음 더 나아가 미래에 필요한 코드를 예측하려고 노력한다. 이는 엄밀히 말해 진정한 테스트 중심 개발 방법론은 아니다.
요약하자면, 방금 작성한 두 가지 테스트 케이스는 초기 용량이 있든 없든 해시 테이블을 생성할 수 있는지 확인하기 위해 작성한 것이다.
# test_hashtable.py
from hashtable import HashTable
def test_should_create_hashtable():
assert HashTable() is not None
def test_should_create_hashtable_with_capacity():
assert HashTable(capacity=10) is not None
이를 기반으로 GitHub Copilot은 지금까지 다음과 같은 해시 테이블 구현을 생성했다.
# hashtable.py
class HashTable:
def __init__(self, capacity=10):
self.capacity = capacity
self.buckets = [None] * capacity
self.size = 0
이니셜라이저 메서드는 인수를 제공하지 않고 해시 테이블을 생성할 경우를 대비해 기본 용량을 10으로 설정하며, 이는 첫 번째 테스트 케이스에 의해 결정된다. 그런 다음 용량은 인스턴스 어트리뷰트에 저장된다. GitHub Copilot이 해시 테이블의 크기와 용량의 차이를 올바르게 인식한 것을 확인할 수 있다. 또한 빈 버킷을 생성하여 seperate chaining에 기반한 고전적인 해시 테이블 구현을 가정했다.
GitHub Copilot은 인간 페어 프로그래머만큼은 아니지만, 질문에 대한 답변을 제공하는 가상 조수의 역할을 훌륭하게 수행한다. 마치 구글이나 스택 오버플로를 검색하여 특정 문제에 대한 해결책을 찾는 것처럼 코드 에디터에 바로 내장되어 있다!
즉각적인 컨텍스트 인식 솔루션을 위한 Stack Overflow 제거하기
인터넷에는 프로그래머의 질문에 대한 답을 찾기 위해 스택 오버플로나 Google과 같은 사이트를 과도하게 사용한다는 농담이 넘쳐난다. 예를 들어, Copying and Pasting from Stack Overflow와 같은 유명한 오라일리의 책 표지를 패러디한 것이 있다.

대부분의 개발자가 일상적인 작업을 하면서 이러한 도구를 정기적으로 사용하며, 요즘에는 이러한 도구 없이는 생활하기 힘들다는 점을 고려하면 놀라운 일이 아니다.
GitHub Copilot을 사용하면 이러한 도구에 대한 의존도를 줄일 수 있다. 온라인에서 찾을 수 있는 최상의 해답과는 달리 프로젝트의 컨텍스트에 맞는 맞춤형 코드를 즉시 제공받을 수 있기 때문이다. 또한 코드 편집기를 강제로 종료하지 않으므로 해당 영역에 머물면서 진행 속도를 높일 수 있다. Python 커뮤니티의 집단적 지식을 손끝으로 직접 체험해 보려면 계속 읽어보세요.
상용구 코드에 대해 다시는 생각하지 않기
Python은 비교적 표현력이 풍부한 구문을 사용하기 때문에 간결하고 읽기 쉬운 코드를 작성할 수 있다. 하지만 아무리 좋아하는 프로그래밍 언어라 할지라도 가끔씩 상용구 코드를 작성하는 것은 피할 수 없는 일이다. 때로는 길고 장황해 보이지만 실제로는 별로 유용한 작업을 수행하지 않는 코드를 작성할 수밖에 없는 경우가 있다.
예를 들어, URL 주소에서 데이터를 가져와서 JSON으로 직렬화하고 그 결과를 파일에 저장하는 작업에는 별다른 가치를 제공하지 않는 지루한 세부 사항이 많이 포함되어 있다. 가장 중요한 것, 즉 더 높은 수준의 문제에 대한 해결책을 찾는 데 집중하면서 GitHub Copilot이 개별 단계를 코드로 매핑하도록 한다면 더 멋지지 않을까?
그럴 수 있다! 이전과 마찬가지로 설명적인 함수 시그니처를 작성하는 것부터 시작하고 나머지는 GitHub Copilot에 맡기면 된다.
def save_json(url, filename):
위의 시그니처에 대한 응답으로 제안된 함수 본문은 다음과 같을 수 있다.
def save_json(url, filename):
import json
import requests
import os
if not os.path.exists(filename):
with open(filename, "w") as f:
f.write(json.dumps(requests.get(url).json()))
else:
print("File already exists")
이 함수는 외부 요청 라이브러리에 의존하는데, 이 라이브러리는 Python에서 HTTP 요청을 만들기 위한 사실상의 표준이므로 설치해야 할 수도 있다. 이 함수는 컨텍스트 관리자를 활용하여 JSON을 덤프한 후 파일을 닫는다. 이미 충분히 훌륭하지만 몇 가지 외형적인 조정을 추가하고 싶을 수도 있다.
import json
import os
import requests
def save_json(url, filename):
if not os.path.exists(filename):
with open(filename, mode="w", encoding="utf-8") as file:
file.write(json.dumps(requests.get(url).json()))
else:
print("File already exists")
PEP 8 스타일 가이드에 따르면 일반적으로 함수 외부에 임포트 문을 정의해야 하며, 외부 라이브러리 모듈은 표준 라이브러리 모듈 다음에 와야 한다. 또한, 서로 다른 운영 체제에서 호환되지 않는 기본값을 사용할 수 있는 불일치를 피하기 위해 Python에서 파일로 작업할 때는 UTF-8과 같은 문자 인코딩을 명시적으로 지정하는 것을 권장한다.
Note: black 또는 유사한 도구를 사용하여 생성된 코드의 서식을 프로젝트의 나머지 부분과 일관되게 유지할 수 있다.
GitHub Copilot을 사용하는 것이 Stack Overflow보다 약간 더 안전할 수 있다. Stack Overflow를 사용하면 다른 사람의 코드를 완전히 이해하거나 상황에 맞는지 생각하지 않고 무심코 그대로 복사하여 붙여넣을 수 있다. GitHub Copilot의 제안을 수락하는 것도 똑같이 무모할 수 있지만, 적어도 상황에 맞게 세밀하게 조정된 퍼즐 조각을 얻을 수 있어 실패할 확률보다 성공할 확률이 더 높다.
GitHub Copilot의 또 다른 장점은 사용하려는 라이브러리나 API의 문서를 직접 찾아보지 않아도 된다는 점이다.
항상 손끝에서 API 문서 확인 가능
GitHub 사용자의 공개 리포지토리 목록을 가져오는 작은 Python 함수를 작성하고 싶다고 가정해 보겠다. 기존 방식에서는 웹에서 github api를 검색하는 것부터 시작하여 GitHub REST API 문서 페이지로 이동한다. 그런 다음 선택할 수 있는 대체 REST API의 수와 모든 가이드, 빠른 시작 및 참조 문서에 압도당할 수 있다.
다행히도 잘 알려진 API를 사용하도록 학습된 GitHub Copilot이 있으므로 호출할 API에 대한 최소한의 힌트를 제공할 수 있다. github_api_client라는 새 Python 모듈을 만들고 그 안에 다음 코드를 입력한다.
# github_api_client.py
import os
GITHUB_API_TOKEN = os.getenv("GITHUB_API_TOKEN")
def get_repository_names(username):
나중에 토큰을 환경 변수에 저장한 상태에서 터미널 세션에서 이 스크립트를 실행한다. 환경 변수를 통해 비밀 키와 구성 데이터를 읽는 것이 일반적이므로 Python의 os 모듈을 활용하여 API에 액세스하기 위해 필요할 수 있는 개인 GitHub API token을 읽는다.
Note: 다른 사람의 공개 리포지토리를 가져오는 데는 토큰이 필요하지 않지만, 익명 클라이언트로서 시간당 60건의 API 요청으로 제한된다. 이 제한을 초과하려면 개인용 액세스 토큰을 사용하여 자신을 인증해야 한다. 어차피 대부분의 API 엔드포인트는 인증이 필요하므로 지금 인증하는 것이 좋다.
제안된 결과 중 하나가 바로 작동한다.
# github_api_client.py
import os
GITHUB_API_TOKEN = os.getenv("GITHUB_API_TOKEN")
def get_repository_names(username):
import requests
url = f"https://api.github.com/users/{username}/repos"
headers = {"Authorization": f"token {GITHUB_API_TOKEN}"}
response = requests.get(url, headers=headers)
response.raise_for_status()
return [repo["name"] for repo in response.json()]
이 기능을 테스트하기 전에 GitHub 프로필에서 새 개인 액세스 토큰을 생성하고 터미널에서 해당 환경 변수를 설정해야 한다.
$ export GITHUB_API_TOKEN=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
그런 다음 환경 변수를 정의한 것과 동일한 터미널 세션에 있는 상태에서 대화형 모드에서 코드가 포함된 소스 파일을 실행하여 Python이 호출할 수 있는 생성된 함수를 읽도록 한다.
$ python -i github_api_client.py
>>> for name in get_repository_names("gvanrossum"):
... print(name)
...
500lines
asyncio
ballot-box
cpython
ctok
exceptiongroup
guidos_time_machine
gvanrossum.github.io
http-get-perf
minithesis
mirror-cwi-stdwin
mypy
mypy-dummy
path-pep
patma
pep550
peps
Pyjion
pythonlabs
pythonlabs-com-azure
pytype
pyxl3
이 경우 Python의 창시자인 Guido van Rossum이 만든 공개 리포지토리의 목록이 다소 짧다.
잘 알려진 API를 사용하는 것은 특별히 어렵지 않았지만, GitHub Copilot이 이전에 보지 못한 커스텀 API를 활용하는 것은 어떨까? 다음에서 알아보자.
GitHub Copilot에게 고유한 방언을 사용하도록 가르치기
세 주요 플랫폼 중 하나에 텍스트 음성 변환(TTS) 명령을 래핑하여 음성 합성을 위한 사용자 지정 API를 정의하는 다음과 같은 Python 모듈이 있다고 가정해 보자.
# custom_api.py
import platform
import subprocess
class TextToSpeechAPIClient:
def __init__(self, command=None):
self.command = command or get_default_command()
def say(self, text):
subprocess.call(self.command(text))
def get_default_command():
match platform.system():
case "Darwin":
return lambda text: ["say", text]
case "Linux":
return lambda text: ["spd-say", text]
case "Windows":
return lambda text: \
"PowerShell -Command \"Add-Type –AssemblyName System.Speech; " \
"(New-Object System.Speech.Synthesis.SpeechSynthesizer)." \
f"Speak('{text}');\""
TextToSpeechAPIClient 클래스는 운영 체제와 관련된 음성 합성 명령과 함께 선택적 인수를 받는다. 아래에 정의된 도우미 함수는 Python 3.10에 도입된 match 문을 활용하여 올바른 명령을 결정한다.
이제 다른 Python 모듈을 만든 다음 댓글로 새 API 사용법을 배우기 위해 GitHub Copilot에 소원을 말한다.
# main.py
# Say "Hello, World!" using the custom text-to-speech API client.
if __name__ == "__main__":
파일 끝에 있는 실행 조건은 Python 스크립트를 작성하고 싶다는 것을 전달하여 GitHub Copilot에 필요한 컨텍스트를 제공한다. 그렇지 않으면 덜 유용한 제안을 받을 수 있다. 하지만 이 필수 확인이 적용되면 제안 중 하나가 다음과 같이 표시된다.
# main.py
# Say "Hello, World!" using the custom text-to-speech API client.
if __name__ == "__main__":
import custom_api
client = custom_api.TextToSpeechAPIClient()
client.say("Hello, World!")
훌륭하다! GitHub Copilot은 다른 Python 모듈에서 사용자 지정 API를 찾아서 필요한 곳에서 가져온 다음 TextToSpeechAPIClient 클래스를 그대로 사용했다. 로컬 모듈에서도 훌륭하게 작동했지만 타사 모듈에 대한 코드 스니펫을 제안하는 것도 좋다.
익숙하지 않은 프레임워크 또는 라이브러리 탐색하기
GitHub Copilot으로 Python에서 새로운 라이브러리를 탐색하는 것은 즐거운 경험이다. 지식이 조금 녹슬어버린 프레임워크를 다시 살펴보고 있는데 어디서부터 시작해야 할지 기억이 나지 않을 수도 있다. 하지만 특정 도구에 대해 잘 알고 있는 경우에도 GitHub Copilot은 때때로 더 효율적인 솔루션이나 전혀 몰랐던 API의 일부를 제안하여 사용자를 놀라게 할 수 있다.
파일 기반 SQLite 데이터베이스에서 쿼리한 JSON 직렬화된 도서 리스트를 제공하기 위해 Flask 프레임워크를 사용하여 REST API 엔드포인트를 노출하고 싶다고 가정해 보겠다. 이제 새 Python 소스 파일을 만들고, 필요한 모듈을 가져온 다음, 이 단락의 이전 문장을 Python 주석으로 바꾼다.
import sqlite3
import flask
# Expose a REST API endpoint using the Flask framework \
# to serve a JSON-serialized list of books queried from \
# a file-based SQLite database.
제안이 도착할 때까지 기다린 다음 가장 좋아 보이는 제안을 선택한다. 때로는 예상 코드를 입력하기 시작하고 각 줄에서 Tab 키를 눌러 한 줄씩 제안을 요청하면 더 나은 결과를 얻을 수 있다. 결국에는 다음과 같은 결과가 나올 수도 있다.
import sqlite3
import flask
# Expose a REST API endpoint using the Flask framework \
# to serve a JSON-serialized list of books queried from \
# a file-based SQLite database.
app = flask.Flask(__name__)
@app.route("/books")
def get_books():
conn = sqlite3.connect("books.db")
c = conn.cursor()
c.execute("SELECT * FROM books")
books = c.fetchall()
conn.close()
return flask.jsonify(books)
if __name__ == "__main__":
app.run(debug=True)
이 경우, GitHub Copilot은 여러분이 잊어버렸을 수도 있는 Flask 애플리케이션의 일반적인 구조를 스캐폴딩하고 요청하신 엔드포인트를 정의했다. 작동하는지 테스트하려면 Flask 애플리케이션과 동일한 폴더에서 터미널을 열고 몇 개의 샘플 도서가 있는 books.db라는 파일 기반 데이터베이스를 새로 만든다.
SQL 쿼리를 입력하려면 일괄 모드에서 sqlite3 명령을 실행하고 end-of-file(EOF) 문자로 종료하면 된다.
$ sqlite3 books.db -batch
CREATE TABLE books(
id INTEGER PRIMARY KEY AUTOINCREMENT,
isbn TEXT,
author TEXT,
title TEXT
);
INSERT INTO books(isbn, author, title) VALUES
('978-0132350884', 'Robert C. Martin', 'Clean Code'),
('978-1449340377', 'David Beazley', 'Python Cookbook'),
('978-0131872486', 'Bruce Eckel', 'Thinking in Java'),
('978-1775093329', 'David Amos', 'Python Basics');
Windows에서는 일반적으로 Ctrl+Z를 사용하여 표준 입력 스트림으로 EOF 문자를 보낼 수 있지만, Linux와 macOS에서는 Ctrl+D 키 조합을 사용한다.
다음, 스크립트를 실행하여 기본 네트워크 인터페이스와 포트 번호에서 Flask 애플리케이션을 시작한 다음 웹 브라우저에서 /books 엔드포인트로 이동한다. 또는 macOS 또는 Linux를 사용하는 경우 터미널에서 직접 cURL과 같은 명령을 사용하여 books 가져올 수 있다.
$ curl http://127.0.0.1:5000/books
[
[
1,
"978-0132350884",
"Robert C. Martin",
"Clean Code"
],
[
2,
"978-1449340377",
"David Beazley",
"Python Cookbook"
],
[
3,
"978-0131872486",
"Bruce Eckel",
"Thinking in Java"
],
[
4,
"978-1775093329",
"David Amos",
"Python Basics"
]
]
놀랍게도 GitHub Copilot으로 생성된 코드가 살아 있다! 생각해 보자. 사용자는 원하는 프로그램에 대한 자연어 설명을 제공하고 사용할 라이브러리에 대한 컨텍스트를 제공하기 위해 두 개의 모듈을 가져왔을 뿐이다. 그 사이에 인공 지능이 여러분의 설명을 작동하는 웹 애플리케이션으로 바꿔 놓았다.
그렇긴 하지만 GitHub Copilot에는 어두운 면이 있는데, 다음 섹션에서 자세히 알아보겠다.
GitHub Copilot 사용에 반대하는 주장 고려하기
웹에서 GitHub Copilot에 대한 정보를 검색하면 칭찬과 흥분, 그리고 약간의 비판이 섞인 글을 찾을 수 있다. 그 중 일부는 타당한 내용이지만, 일부는 기술의 목적을 잘못 이해한 데서 비롯된 것이다. 궁극적으로 GitHub Copilot은 사람마다 다른 의미를 지니고 있다. 이 섹션을 읽고 나면 인공 지능 기반 가상 조수의 가장 큰 단점을 알게 될 것이다.
오토파일럿은 절대 아니다!
copilot이라는 이름은 이 제품을 설명하기 위해 GitHub에서 영리하게 선택한 이름이다. 사람들이 이 제품이 프로그래머를 대신하여 제어할 수 있다고 오해하는 것을 피하기 위해서이다. 일반적으로 copilot이라고 불리는 항공기의 first officer처럼, GitHub Copilot은 사용자를 도와주지만 대신 일을 하지는 않는다. 이 표현은 오늘날 일부 자동차 제조업체가 대놓고 autopilot이라고 부르는 첨단 운전자 지원 시스템과 대조적이다.
한 가지 기억해야 할 것은 GitHub Copilot에서 생성된 코드가 항상 이상적인 것은 아니라는 점이다. 사실, 때로는 차선책이거나 안전하지 않을 수 있으며, 잘못된 프로그래밍 관행을 따를 수도 있다.
코드에 구문 오류가 있거나 완전히 무의미한 코드라면 쉽게 발견할 수 있기 때문에 문제가 되지 않는다. 하지만 언뜻 보기에 그럴듯해 보이는 코드에도 논리적 오류가 있을 수 있다. 따라서 생성된 코드를 주의 깊게 검토하고 테스트하지 않고는 GitHub Copilot을 절대 신뢰해서는 안 된다!
다시 말해, GitHub Copilot은 유용하게 사용할 수 있는 도구이지만, 예를 들어 원자력 발전소를 위한 미션 크리티컬 소프트웨어를 구축할 때 제안 사항에만 의존해서는 안 된다.
잠재적인 보안 위험이 있다
GitHub Copilot의 머신 러닝 모델은 공개 리포지토리에서 학습되었기 때문에 데이터 중독에 취약하다. 악의적인 공격자가 의도적으로 학습 데이터 집합을 악성 코드로 오염시켜 코드 편집기에서 유사한 패턴을 제안하도록 모델을 속일 수 있다. 이는 학습 데이터에 포함된 방대한 양의 코드로 인해 우연히 발생할 수도 있다.
다시 한 번 강조하지만, GitHub Copilot은 자신의 책임하에 사용하여야 한다!
지적 재산권 문제 제기
현재 가장 심각한 우려 사항인 지적 재산권을 침해할 수도 있다. 이전에 본 것을 기반으로 코드를 합성하기 때문에 비공개 리포지토리, 저작권이 있는 공개 리포지토리 또는 비허용 라이선스가 있는 오픈 소스 리포지토리에서 독점 알고리즘을 유출할 수 있다.
Note: GitHub Copilot을 사용하려면 서비스 개선을 위해 비공개 리포지토리를 포함하여 리포지토리에 있는 코드와 데이터를 공유하는 데 동의한다는 내용이 명시된 이용 약관에 동의해야 한다.
Flask의 개발자인 Armin Ronacher는 최근 X(tweet)을 통해 GitHub Copilot이 fast inverse square root 알고리즘을 구현하는 데 있어 Quake의 구현 방식을 그대로 인용하는 방법을 시연했다. 생성된 코드는 John Carmack의 것이지만, GitHub Copilot은 그 위에 완전히 무작위적인 라이선스를 제안했다.

위 예에서 생성된 코드에는 저주 단어가 포함된 원본 댓글도 포함되어 있다. GitHub는 이론적으로 이러한 단어를 차단할 수 있는 필터를 갖추고 있다고 주장한다. 또한 실수로 GitHub에 커밋되었을 수 있는 API 키나 이메일 주소와 같은 민감한 정보도 제거해야 한다. 하지만 확실히 알 수는 없다.
부정행위 조장
새로운 프로그래밍 언어, 프레임워크, 라이브러리 또는 API를 이해하려는 경우 GitHub Copilot은 환상적인 학습 도구가 될 수 있다. 동시에, 일부 학생들이 GitHub Copilot을 악용하여 일반 영어로 된 작업 설명을 복사하여 붙여넣고 몇 초 만에 과제를 해결하는 부정행위를 하는 것을 상상하는 것은 그리 어렵지 않다. 이 튜토리얼을 시작할 때 했던 Advent of Code puzzle을 떠올려 보세요.
또한 부정행위를 할 의도가 없는 학생을 게으르게 만들고 독립적인 사고를 방해할 수 있으므로 GitHub Copilot을 멀리하는 것이 좋다. 다른 모든 강력한 도구와 마찬가지로, GitHub Copilot도 용도에 따라 적절하게 사용하여야 한다.
구독 플랜이 필요
GitHub Copilot에 대한 가장 일반적인 불만 중 하나는 유료 구독이 필요하기 때문에 잠재적 사용자 수가 제한된다는 사실이다. 대부분의 오픈 소스 코드에 기반한 도구에 돈을 지불해야 한다는 사실에 화가 난 사람들이 많다.
설상가상으로, 전 세계 어디에 있든 고정 요금이 부과된다는 점도 문제이다. 구매력 평가가 고려되지 않아 일부 국가의 고객에게는 다른 국가보다 구독료가 더 큰 부담이 될 수 있다. 구매력이 높은 국가에서 엔지니어링 급여를 받고 있거나 회사에서 구독을 대량으로 구매하는 경우에는 가격이 합리적으로 보일 수 있다. 그렇지 않은 경우에는 가격이 매우 비쌀 수 있다.
반면에, GitHub는 최소한 평가판 기간을 제공하고 검증된 학생과 오픈 소스 관리자가 무료로 서비스를 이용할 수 있도록 하는 것은 공정하다. 궁극적으로 가격이 부담스러울 수도 있지만, 거대한 신경망을 위한 인프라의 유지 관리 비용을 고려하면 일반적으로 가격 대비 좋은 가치이다. 결국, 이를 실행하고 짧은 지연 시간을 달성하려면 분산된 클러스터가 필요하다.
Note: AI 기반 프로그래밍 지원을 무료로 이용하고 싶다면 ChatGPT가 코딩 멘토가 될 수 있다.
기본 머신 러닝 모델에 대해 더 자세히 알아보고 싶다면 OpenAI Codex에서 원본 논문을 읽어보세요.
마치며
GitHub Copilot은 소프트웨어 엔지니어의 작업 속도와 생산성을 높여주는 혁신적인 프로그래밍 지원 도구이다. 상용구 코드를 생성하여 시간을 절약하고 문서 작업을 하지 않아도 된다. 프로젝트의 컨텍스트를 이해하기 때문에 즉각적인 제안은 맞춤형이며 일반적으로 의도한 대로 작동한다.
이 튜토리얼에서 그 방법을 배웠다.
- 코드 편집기에 GitHub Copilot 확장 프로그램 설치하기
- 작업에 대한 자연어 설명을 작업 코드로 변환하기
- 여러 가지 대체 지능형 코드 완성 제안 중에서 선택하기
- 익숙하지 않은 프레임워크와 프로그래밍 언어 탐색하기
- GitHub Copilot에게 커스텀 API 사용 방법 가르치기
- 가상 페어 프로그래머와 함께 실시간으로 테스트 중심 개발 연습하기
GitHub Copilot에 대해 어떻게 생각하나요? 그만한 가치가 있을까? 향후 프로젝트에 사용할 만큼 충분히 매력적인가? 의견을 나누어 보세요!