Show GN: lx: 완전한 개발자의 통제하의 바이브 코딩 도구
(github.com/chebread)서론
안녕하세요. 컴퓨터 공학에 흥미가 있는 학생입니다. 이번에 lx 라는 프로그램을 개발하게 되어 글을 읽기만 하던 GeekNews에 처음으로 포스팅을 해보고자 합니다.
근래에 AI에게 자연어로 명령하면 코드를 알아서 다 짜주는 바이브 코딩이 대세입니다.
저는 이러한 바이브 코딩이 두렵습니다.
단순하게 실직에 대한 공포가 아니라, "코드를 작성하는 즐거움(Wrangling code) (출처: 켄트 벡 - Augmented Coding: Beyond the Vibes)" 그리고 "개발자의 통제권"을 박탈당하고 있다는 프로그래밍의 상실감 때문입니다.
혹자는 이번 변화를 천공 카드에서 기계어, 어셈블리어, C언어로 이어지는 프로그래밍의 자연스러운 진화라고 말합니다. 하지만 저는 이러한 비유가 틀렸다고 생각합니다.
과거의 추상화는 개발자에게 '더 좋은 망치'를 쥐여주는 과정이었습니다.
도구는 계속 발전했지만 망치를 휘두르는 주체는 여전히 인간이었고, 결과는 온전히 개발자의 통제하에 있었습니다.
하지만 작금의 AI 코딩은 다릅니다.
이제는 로봇이 망치를 대신 휘두르고, 개발자는 그저 구경하거나 기껏해야 로봇을 조금 타일러보는 처지가 되었습니다.
우리가 망치를 휘두를 수 없다면, 그것은 더 이상 프로그래밍이라 할 수 없다고 생각합니다.
왜냐하면, 그것은 전적으로 우리의 통제하에 있지 않기 때문입니다.
그래서 저는 lx를 만들었습니다.
lx는 로봇에게서 망치를 뺏어와 다시 개발자의 손에 쥐여주는 도구입니다.
lx는 AI를 철저히 통제 가능한 도구로 다룰 수 있도록 해줍니다.
본론
lx는 "인터페이스는 인간이, 로직은 AI"라는 철학을 가집니다.
개발자는 함수의 입출력 및 함수가 하는 일을 정의하여 '계약'을 맺고, AI는 그 함수의 내부 구현만 담당합니다.
이러한 접근은 개발의 연속성을 보장합니다.
함수의 입출력을 작성하는 순간 해당 로직은 이미 완성된 것으로 간주됩니다.
프로그래머는 세부 구현에 매몰되지 않고 즉시 상위 로직을 작성하며 개발 흐름을 끊김 없이 유지할 수 있습니다.
또한 lx는 단순한 텍스트 치환이 아닙니다. github.com/tree-sitter/go-tree-sitter 패키지를 활용하여 AST(추상 구문 트리)를 기반으로 소스 코드를 파싱합니다. 따라서 파일 내의 다른 코드, 주석, 들여 쓰기를 오염시키지 않으며 오직 지정된 스코프의 로직만 안전하게 교체합니다.
기본 사용법
lx를 사용하는 기본적인 형태는 다음과 같습니다.
package main
import (
"fmt"
lx "github.com/chebread/lxgo"
)
func main() {
var year string = "2025-01-02"
// 개발자는 함수 호출부와 흐름을 통제합니다.
result1 := LX_GetYear(year)
var age = 30
result2 := LX_GetAge(age)
fmt.Println(result1, result2)
}
func LX_GetYear(year string) (result string) {
// AI에게 전달할 프롬프트
lx.Generate("yyyy-dd-mm 형식을 한국식 날짜로 변환")
return
}
func LX_GetAge(year int) (result string) {
// 프로그래밍 언어에 따른 lx 라이브러리를 별도 설치하기 귀찮은 경우를 위해, 아래 처럼 lx() 주석 마커 형태도 지원합니다.
// lx("한국식 나이를 만 나이로 변환")
return
}
위 코드에서 LX_GetYear 함수는 개발자가 정의한 계약입니다.
lx 도구를 실행하면, 이 도구는 lx.Generate(...) 혹은 // lx(...) 마커를 인식하여 LLM에 프롬프트를 전송하고, 해당 함수의 본문을 실제 작동하는 코드로 덮어씌웁니다.
이때 토큰 최적화가 적용됩니다. 파일 전체를 보내는 것이 아니라, 해당 함수의 시그니처와 프롬프트만 LLM에 전송하여 비용을 절감하고 보안을 강화합니다.
2. 개발자의 통제
lx 함수 내부의 로직은 AI가 작성하지만, 그 함수를 사용하는 주체는 개발자이어야 합니다.
그러나,lx 함수 내부에는 사용자 정의 로직을 섞으면 무시되므로, 아래와 같이 래핑 함수를 통해 통제할 수 있습니다.
package test
import (
"fmt"
lx "github.com/chebread/lxgo"
)
func main() {
var year string = "2025-01-02"
result1 := ParseYear(year) // 래퍼 함수 호출
fmt.Println(result1)
}
// 개발자가 제어하는 비즈니스 로직
func ParseYear(year string) string {
// AI가 생성한 로직을 부품처럼 사용
res := LX_GetYear(year)
// 결과에 대한 추가 가공은 개발자의 몫
foo := fmt.Sprintf("오늘은 %v 입니다!", res)
return foo
}
func LX_GetYear(year string) (result string) {
lx.Generate("yyyy-dd-mm 형식을 한국식 날짜로 변환")
return
}
3. 안전한 의존성 관리와 투명성
lx는 단일 책임 원칙(SRP)을 지향합니다.
코드를 생성할 뿐, 프로그램을 빌드하거나 실행하지 않습니다.
또한 AI가 생성한 코드가 외부 라이브러리를 필요로 할 경우, lx는 임의로 패키지를 설치하지 않습니다.
-
Code: 생성된 코드 상단에
// lx-dep: ...주석 명시 -
Output: CLI 표준 출력으로 설치 필요 목록 리포트
대신, 이렇게 두 가지 방식으로 개발자에게 보고합니다.
개발자는 이를 확인하고 직접 의존성을 설치할지 결정하면 됩니다.
4. 설정
lx를 사용하기 위해서는 LLM 설정이 필요합니다. 홈 디렉토리(~/) 또는 프로젝트 루트(./)에 lx-config.yaml을 생성하면 됩니다. 만약 두 경로 모두 파일을 위치한다면, 로컬 설정 파일이 우선적으로 적용되므로 각각의 프로젝트마다 lx 설정을 다르게 관리할 수 있습니다.
# lx-config.yaml
provider: "gemini"
api_key: "foo"
model: "bar"
5. 설치 및 실행
Mac 사용자는 Homebrew를 통해 설치할 수 있으며, 타 OS는 lx의 GitHub Releases에서 바이너리를 다운로드하여 설치할 수 있습니다.
brew tap chebread/lx
brew install lx
설치 후 프로젝트 경로에서 lx 명령어를 실행하면 실제 코드가 생성됩니다.
lx에는 스마트 생성 기능이 있어, 이미 코드가 생성된 함수는 다시 LLM을 호출하지 않으므로 안심하고 lx 명령어를 반복 실행할 수 있습니다.
참고: lx는 생성된 코드의 포맷팅을 위해 각 언어별 도구(Go: goimports, Python: ruff, JS: prettier)를 사용합니다. 해당 도구들은 미리 설치되어 있어야 합니다.
6. 라이센스
lx는 AGPL-3.0 License 하에 배포됩니다.
lx가 오픈소스 생태계에 기여하되, 이 도구가 폐쇄적으로 사유화되는 것을 방지하기 위함입니다.
결론
소프트웨어는 인간의 부단한 노력으로 직조된 결정체입니다. AI 시대에도 프로그래머는 여전히 코드의 주인이어야 합니다.
lx는 귀찮은 정규식이나 데이터 파싱 같은 "지루한 구현"은 AI에게 맡기되, 프로그램의 구조와 흐름은 인간이 온전히 소유할 수 있게 해줍니다.
코드를 작성하는 즐거움(Wrangling code)과 통제권을 잃고 싶지 않은 개발자분들께 이 도구를 추천드립니다!