
요즘 개발 쪽 분위기 보면, 정말 하루만 한눈팔아도 새로운 AI 코딩 도구가 하나씩 튀어나오는 느낌이잖아요. 저도 20년 넘게 개발자로 밥 먹고 살았지만, 솔직히 가끔은 “아, 이제 공부할 게 너무 많다” 싶을 때가 있어요. 그런데 또 막상 써보면 재밌습니다. 예전에 여행지에서 우연히 들어간 작은 식당이 생각보다 너무 괜찮았던 것처럼요. 이번에 제가 꽤 진지하게 써본 도구가 바로 MorphLLM인데, 생각보다 실무에 붙이는 맛이 있더라고요. 그래서 오늘은 거창한 이론 말고, 제가 실제 프로젝트에 붙이면서 겪은 이야기랑 삽질했던 포인트를 편하게 풀어보려고 해요.
내가 왜 갑자기 MorphLLM을 써보게 됐냐면
사실 저는 이미 GitHub Copilot이나 ChatGPT 같은 도구를 꽤 오래 써왔어요. 처음엔 “와, 이거 진짜 신기하다” 했다가, 어느 순간부터는 좀 냉정하게 보게 되더라고요. 신기한 거랑 실무에 바로 도움 되는 건 살짝 다른 문제니까요.
특히 저희 팀에서 새로 만들던 B2B 서비스가 하나 있었는데, 거기에 자연어로 API 로직을 생성하는 기능을 작게 붙여보자는 이야기가 나왔어요. 예를 들면 이런 거죠. 사용자가 “회원 목록 조회 API 만들어줘”라고 입력하면, 백엔드 기본 코드 초안을 만들어주는 식이에요. 말만 들으면 간단해 보이는데, 막상 붙여보려니 고민할 게 많았습니다.
기존 LLM들은 모델이 너무 무겁거나, 응답 시간이 애매하거나, 커스터마이징하려고 하면 갑자기 일이 커졌어요. 뭐랄까, 간단히 국수 한 그릇 먹으러 갔는데 코스 요리를 주문해야 하는 느낌이랄까요. 그러다 알게 된 게 MorphLLM이었어요. 오픈소스 기반으로 접근하기 괜찮고, 파인튜닝도 비교적 단순하고, 메모리 사용량도 생각보다 착하다는 이야기를 들었습니다.
그 말을 듣고 그냥 넘어갈 수가 없더라고요. 그래서 주말에 노트북 펴놓고 커피 한 잔 내려서 바로 붙여봤습니다. 물론 예상대로 한 번에 되진 않았고요. 개발이 늘 그렇죠. 문서는 친절해 보이는데, 내 환경에서는 꼭 다른 일이 생깁니다.
Flask API에 MorphLLM을 붙여본 실제 예제
처음부터 거창하게 가면 망할 것 같아서, 아주 단순한 기능부터 시작했어요. 기존 Flask 앱에 REST API 엔드포인트를 하나 만들고, 사용자가 프롬프트를 던지면 MorphLLM이 코드를 생성해서 반환하는 구조였습니다.
예를 들면 사용자가 이런 문장을 입력하는 거예요.
GET /users 에서 모든 사용자 목록을 JSON으로 반환하는 Flask 함수를 만들어줘
그러면 MorphLLM이 Flask 라우트 코드를 만들어서 내려주는 방식이죠. 처음 테스트할 때 썼던 코드는 대략 이런 형태였습니다.
from morphllm import MorphLLM
from flask import Flask, jsonify, request
app = Flask(__name__)
llm = MorphLLM(model="morph-small", api_key="sk-xxxx")
@app.route("/generate-code", methods=["POST"])
def generate_code():
prompt = request.json.get("prompt")
result = llm.generate(
prompt,
max_tokens=200,
temperature=0.2
)
return jsonify({"code": result.text})
코드만 보면 별거 없어 보이죠. 저도 처음엔 “오, 이 정도면 금방 끝나겠는데?” 싶었어요. 그런데 여기서 바로 첫 번째 함정이 나왔습니다.
temperature 값을 기본값인 1.0 근처로 놔뒀더니, 같은 프롬프트를 넣어도 코드가 매번 조금씩 달라졌어요. 물론 창의적인 결과가 필요한 작업이면 그게 장점일 수도 있죠. 그런데 API 코드를 만드는 상황에서는 이야기가 달라집니다. 어제는 잘 되던 코드가 오늘은 import가 빠져 있고, 또 어떤 때는 괄호 하나가 어긋나고, 가끔은 존재하지 않는 함수명을 만들어내더라고요.
그때 살짝 웃음이 나왔습니다. “그래, 너도 신입 개발자처럼 컨디션 타는구나” 싶어서요. 그래서 temperature를 0.2로 낮췄습니다. 그랬더니 확실히 결과가 안정적으로 바뀌었어요. 재미는 조금 줄었지만, 실무에서는 재미보다 예측 가능성이 더 중요하니까요.
그리고 max_tokens도 은근히 중요했습니다. 처음에 너무 낮게 잡았더니 코드가 중간에서 뚝 잘리는 경우가 있었어요. 특히 예외 처리나 응답 포맷까지 포함해달라고 하면 100 토큰 정도로는 부족하더라고요. 제 기준으로는 간단한 Flask 함수도 최소 200 이상, 조금 복잡한 로직이면 400에서 600 정도는 잡아야 마음이 편했습니다.
직접 작성한 코드와 MorphLLM 생성 코드를 비교해봤어요
제가 직접 짠 코드와 MorphLLM이 생성한 코드를 같은 조건에서 비교해봤습니다. 물론 완전히 공정한 비교는 아닐 수 있어요. 저는 이미 프로젝트 구조를 알고 있고, MorphLLM은 프롬프트만 보고 만들었으니까요. 그래도 실무 감각으로 보면 꽤 참고할 만했습니다.
| 비교 항목 | 제가 직접 작성한 코드 | MorphLLM이 생성한 코드 |
|---|---|---|
| 코드 길이 | 약 55줄, 주석과 예외 처리 포함 | 약 22줄, 핵심 로직 위주 |
| 에러 핸들링 | try-except, HTTP 상태 코드, 사용자 메시지 포함 | 기본 예외 처리만 있거나 생략되는 경우 있음 |
| 프로젝트 스타일 반영 | 기존 폴더 구조와 네이밍 규칙 반영 | 프롬프트에 명시하지 않으면 일반적인 형태로 생성 |
| 리팩토링 필요 여부 | 거의 없음 | 함수 분리, 타입 힌트, 예외 메시지 보강 필요 |
| 작업 시간 | 약 15분, 생각하는 시간 포함 | 약 1.2초, 모델 응답 기준 |
이 표만 보면 MorphLLM이 무조건 좋아 보일 수도 있는데, 저는 그렇게 보진 않아요. MorphLLM은 속도가 정말 빠르고 초안을 잘 뽑아줍니다. 그런데 바로 배포할 수준이냐고 물으면, 음, 제 대답은 “아직은 사람이 꼭 봐야 한다” 쪽이에요.
특히 에러 핸들링이나 도메인 규칙은 AI가 완벽하게 알기 어렵습니다. 예를 들어 저희 서비스에서는 특정 고객사의 데이터는 별도 권한 체크를 거쳐야 하는데, 이런 내부 규칙은 프롬프트에 쓰지 않으면 당연히 반영되지 않아요. 그래서 저는 MorphLLM이 만든 코드를 그대로 쓰기보다, 초안으로 받아서 제가 리뷰하고 손보는 방식을 택했습니다. 이게 마음도 편하고, 사고도 덜 납니다.
실제로 겪었던 문제와 고친 방식
도구 소개 글을 보면 보통 좋은 이야기만 많잖아요. “설치하면 바로 생산성 3배!” 이런 식으로요. 그런데 실무에서는 꼭 어딘가에서 삐끗합니다. 저도 몇 번 제대로 삐끗했어요.
문제 하나, request import를 빼먹은 코드
처음 생성된 코드 중에 이런 게 있었습니다.
@app.route("/users", methods=["POST"])
def create_user():
data = request.get_json()
return jsonify({"email": data["email"]})
겉으로 보면 멀쩡해 보이는데, 위쪽에 request import가 빠져 있었어요. 실행하면 바로 이런 에러가 납니다.
NameError: name 'request' is not defined
이건 뭐 사람이 봐도 금방 고치긴 합니다.
from flask import Flask, jsonify, request
그런데 이런 사소한 문제가 반복되면 결국 사람이 피곤해져요. 그래서 프롬프트에 아예 이렇게 조건을 넣기 시작했습니다.
필요한 import 문을 모두 포함해서 완성된 코드로 작성해줘. 코드 일부만 보여주지 말고 실행 가능한 Flask 예제로 작성해줘.
이 한 줄 차이가 생각보다 컸습니다. 개발도 결국 말 잘하는 사람이 이기는 건가 싶더라고요. AI에게도 일을 시킬 땐 업무 지시를 또렷하게 해야 합니다.
문제 둘, 우리 팀 코드 스타일과 달랐던 결과
저희 팀은 API 응답을 항상 이런 형태로 맞추고 있어요.
{
"success": true,
"data": {},
"message": ""
}
그런데 MorphLLM은 별도 지시가 없으면 그냥 이런 식으로 반환하더라고요.
return jsonify(users)
틀린 코드는 아닙니다. 다만 우리 프로젝트에서는 안 맞는 코드죠. 이 차이가 은근히 중요해요. AI 코딩 도구를 쓸 때 많은 분들이 “왜 이렇게 엉뚱하게 만들지?”라고 하는데, 사실 AI 입장에서는 엉뚱한 게 아닐 수 있어요. 우리가 프로젝트 맥락을 충분히 안 준 거죠.
그래서 저는 프로젝트마다 기본 프롬프트를 따로 만들었습니다. 대충 이런 식이에요.
우리 프로젝트의 API 응답 형식은 항상 아래 구조를 따른다.
{
"success": true,
"data": {},
"message": ""
}
생성하는 모든 Flask API 코드는 이 응답 형식을 사용해야 한다.
에러 발생 시 success는 false로 반환하고, message에 사용자 친화적인 오류 메시지를 넣어라.
이렇게 해두니까 결과물이 확실히 팀 스타일에 가까워졌습니다. 이건 정말 별것 아닌데, 안 해두면 계속 손으로 고치게 됩니다. 손으로 고치는 시간이 쌓이면 결국 “AI 써도 별로 안 빠른데?”라는 말이 나오고요.
MorphLLM을 쓰면서 몸으로 배운 실전 팁들
한 달 정도 MorphLLM 코드 생성을 이것저것 붙여보면서 느낀 게 있어요. 이 도구는 그냥 “코드 만들어줘”라고 던지는 것보다, 약간의 틀을 잡아주면 훨씬 쓸 만해집니다. 마치 후배 개발자에게 일을 줄 때 “알아서 해봐”보다 “이 구조 안에서 이 기준으로 해줘”라고 말하면 결과가 좋아지는 것처럼요.
프롬프트에는 예제를 꼭 넣는 게 좋습니다
처음에는 저도 아주 짧게 말했어요.
SQLAlchemy User 모델을 만들어줘.
그러면 모델은 만들어줍니다. 그런데 컬럼 타입이 제가 원하는 것과 다르거나, created_at이 빠지거나, unique 조건이 누락되는 일이 생겼어요. 그래서 프롬프트를 이렇게 바꿨습니다.
SQLAlchemy User 모델을 만들어줘. 필드는 아래와 같아. id: Integer, primary_key=True email: String(255), unique=True, nullable=False name: String(100), nullable=False created_at: DateTime, 기본값은 현재 시간 타입 힌트와 __repr__ 메서드도 포함해줘.
이렇게 구체적으로 주니까 결과가 훨씬 좋아졌어요. 제 체감으로는 정확도가 70점에서 90점 정도로 올라간 느낌이었습니다. 특히 예제 데이터를 한두 줄 넣어주면 더 안정적이에요.
예시 데이터: email = "test@example.com" name = "홍길동"
뭐랄까, AI도 빈 종이를 주면 헤매고, 참고 자료를 주면 훨씬 잘합니다. 사람하고 크게 다르지 않더라고요.
CI/CD 검증은 귀찮아도 꼭 넣어야 합니다
이건 제 기준에서는 선택이 아니라 필수에 가까워요. MorphLLM이 만든 코드를 사람이 대충 보고 “괜찮네” 하고 main 브랜치에 넣으면 언젠가는 사고 납니다. 정말요. 코드는 눈으로 봐서 멀쩡해도 실행하면 다른 이야기를 할 때가 많거든요.
저는 생성된 코드가 들어간 브랜치에 대해 자동으로 pylint와 pytest를 돌리게 했습니다. 테스트에 실패하면 PR에 코멘트가 달리도록 했고요.
name: AI Code Validation
on:
pull_request:
branches:
- main
- uses: actions/checkout@v3
- name: Set up Python
- name: Install dependencies
- name: Run lint
- name: Run tests
처음엔 조금 번거로웠는데, 이걸 넣고 나니까 마음이 훨씬 편해졌습니다. AI가 코드를 빠르게 만들어주는 건 좋은데, 검증까지 빠지면 그냥 빠르게 위험해지는 거예요. 저는 이 부분은 좀 단호한 편입니다. AI 코딩 도구를 쓴다면 자동 테스트는 꼭 같이 가야 합니다.
반복 프롬프트는 Redis로 캐싱했습니다
개발하다 보면 같은 프롬프트를 여러 번 날리게 됩니다. “회원 조회 API 만들어줘”, “상품 조회 API 만들어줘”처럼 비슷한 요청도 많고요. 그런데 매번 MorphLLM API를 호출하면 비용도 비용이고, 응답 시간도 아깝습니다.
그래서 저는 Redis에 프롬프트 해시를 키로 저장했습니다. TTL은 1시간으로 잡았고요. 간단히 말하면 같은 요청이 들어오면 굳이 MorphLLM까지 가지 않고 캐시된 결과를 바로 반환하는 구조입니다.
import hashlib
import redis
cache = redis.Redis(host="localhost", port=6379, db=0)
def get_prompt_hash(prompt: str) -> str:
return hashlib.sha256(prompt.encode("utf-8")).hexdigest()
def generate_with_cache(prompt: str):
key = f"morphllm:{get_prompt_hash(prompt)}"
cached = cache.get(key)
if cached:
return cached.decode("utf-8")
result = llm.generate(prompt, max_tokens=400, temperature=0.2)
cache.setex(key, 3600, result.text)
return result.text
이걸 넣고 나니 응답 시간이 꽤 줄었습니다. 실제로 간단한 프롬프트 기준으로 평균 1.2초 정도 걸리던 게, 캐시 히트 시에는 0.02초 근처까지 떨어졌어요. 사용자는 이런 차이를 바로 느낍니다. 개발자도 느끼고요. 괜히 기분 좋아져요.
| 구분 | 평균 응답 시간 | 체감 |
|---|---|---|
| MorphLLM 직접 호출 | 약 1.2초 | 조금 기다리는 느낌 있음 |
| Redis 캐시 사용 | 약 0.02초 | 거의 즉시 반환 |
System prompt를 대충 쓰면 결과도 대충 나옵니다
제가 초반에 가장 가볍게 봤던 게 System prompt였어요. 그냥 사용자 프롬프트만 잘 쓰면 되겠지 싶었거든요. 그런데 아니었습니다. System prompt는 생각보다 중요해요. AI에게 “너는 어떤 기준으로 일해야 하는 사람인지” 알려주는 역할을 하니까요.
제가 실제로 효과를 봤던 System prompt는 이런 식이었습니다.
당신은 10년 경력의 Python 백엔드 개발자입니다. 생성하는 코드는 PEP8을 따릅니다. 모든 함수에는 타입 힌트를 포함합니다. Flask API 응답은 success, data, message 구조를 사용합니다. 예외 상황은 try-except로 처리하고, 사용자에게 노출할 메시지는 친절하게 작성합니다.
이걸 넣기 전에는 변수명이 제각각이고, 어떤 때는 snake_case였다가 어떤 때는 camelCase가 섞이는 일도 있었어요. 그런데 System prompt를 정리한 뒤로는 코드 톤이 꽤 안정됐습니다. 사람도 회사 컨벤션 문서 보고 일하잖아요. AI도 마찬가지더라고요.
실패한 프롬프트는 그냥 버리지 말고 모아두세요
이건 제가 제일 강하게 추천하고 싶은 부분이에요. 실패한 프롬프트를 그냥 “아, 이상하네” 하고 버리면 너무 아깝습니다. 실패한 프롬프트야말로 진짜 데이터거든요.
저는 운영 테스트 중에 프롬프트와 응답을 따로 저장해두고, 사용자 피드백을 붙였습니다. 예를 들면 이런 식이에요.
| 저장 항목 | 내용 |
|---|---|
| prompt | 사용자가 입력한 자연어 요청 |
| response | MorphLLM이 생성한 코드 |
| feedback | 좋아요, 싫어요, 수정 필요 여부 |
| reason | 실패 사유 또는 수정한 이유 |
이렇게 모아둔 데이터를 나중에 파인튜닝이나 프롬프트 개선에 썼습니다. 처음에는 체감 정확도가 70% 정도였는데, 한 달 정도 지나니까 90% 가까이 올라갔어요. 물론 이 숫자가 논문처럼 엄밀한 건 아닙니다. 그냥 실무에서 “아, 이제 손볼 게 훨씬 줄었네” 하고 느낀 정도예요. 그래도 개발자 입장에서는 이 체감이 꽤 큽니다.
내가 정한 MorphLLM 사용 기준
한동안 써보고 나니 제 나름의 기준이 생겼습니다. MorphLLM을 모든 개발에 다 쓰는 건 별로예요. 오히려 그렇게 쓰면 더 피곤해질 수 있습니다. 대신 잘 맞는 영역이 분명히 있어요.
- 반복적인 CRUD API 초안 만들기에는 꽤 좋습니다. 비슷한 패턴이 많은 곳에서 속도가 확 납니다.
- 테스트 코드 초안 생성에도 잘 맞습니다. 다만 assertion은 꼭 사람이 다시 봐야 합니다.
- DTO, schema, model 같은 보일러플레이트 코드를 만들 때 시간 절약 효과가 큽니다.
- 복잡한 비즈니스 로직은 아직 사람이 설계하는 게 맞다고 봅니다. 특히 돈, 권한, 정산, 개인정보가 얽힌 로직은 조심해야 해요.
- 기존 레거시 코드 리팩토링은 작은 단위로 쪼개서 맡기는 게 좋습니다. 한 번에 큰 파일을 던지면 결과가 흔들립니다.
저는 요즘 이렇게 씁니다. 설계는 제가 하고, 반복 구현은 MorphLLM에게 초안을 맡기고, 검증과 마무리는 다시 제가 합니다. 이 조합이 제일 편했어요. 마치 운전할 때 내비게이션을 보긴 보지만, 최종 판단은 운전자가 하는 것과 비슷합니다. 내비가 골목길로 안내한다고 무조건 들어가면 안 되잖아요.
실무에서 쓰기 전에 확인하면 좋은 체크리스트
혹시 팀에 AI 코딩 도구를 도입하려고 한다면, 저는 아래 항목은 꼭 먼저 확인해보라고 이야기하고 싶어요. 이거 안 보고 들어가면 나중에 “생각보다 관리할 게 많네” 하게 됩니다.
- 생성된 코드가 기존 코드 컨벤션을 따르는지 확인해야 합니다.
- API Key나 개인정보 같은 민감 정보가 프롬프트에 섞이지 않도록 필터링이 필요합니다.
- 자동 테스트와 정적 분석 도구를 반드시 붙여야 합니다.
- 프롬프트와 응답 로그를 어디까지 저장할지 정책을 정해야 합니다.
- AI가 만든 코드의 책임 주체를 팀 안에서 명확히 해야 합니다. 결국 리뷰한 사람이 책임지는 구조가 안전합니다.
- 캐싱 전략을 세우면 비용과 속도 면에서 꽤 이득을 봅니다.
이런 이야기를 하면 가끔 너무 조심스러운 것 아니냐고 묻는 분들도 있어요. 그런데 저는 개발 경력이 길어질수록 더 조심스러워지는 것 같습니다. 예전엔 빨리 만드는 게 제일 멋있어 보였는데, 지금은 오래 버티는 코드가 더 멋있어 보여요. 특히 회사 서비스라면 더 그렇고요.
MorphLLM을 써본 뒤 제 생각은 이렇습니다
솔직히 말하면 MorphLLM이 마법 같은 도구는 아닙니다. 복잡한 요구사항을 던지면 아직도 엉뚱한 코드를 만들 때가 있고, 프로젝트 맥락을 충분히 알려주지 않으면 일반적인 예제 코드에 머무르는 경우도 많아요. 그런데 잘 쓰면 분명히 시간을 아껴줍니다.
저는 특히 이런 분들에게 추천하고 싶어요. AI 코딩 도구를 팀에 한번 도입해보고 싶은 개발 리더, 반복적인 API 작업이 많은 백엔드 개발자, 주말에 빠르게 토이 프로젝트를 만들고 싶은 분들요. 그리고 저처럼 40대가 되어서도 새로운 도구를 보면 괜히 만져보고 싶은 분들께도 꽤 재미있는 장난감이자 실무 도구가 될 수 있습니다.
다만 너무 믿고 맡기진 않았으면 해요. MorphLLM은 좋은 조수에 가깝습니다. 방향을 잡아주는 건 여전히 개발자의 몫이에요. 저는 이 균형이 마음에 들었습니다. 반복 작업은 덜어내고, 사람은 구조와 판단에 더 집중하는 것. 앞으로 개발 일이 이런 식으로 조금씩 바뀌지 않을까 싶어요.
커피 한 잔 옆에 두고, 작은 API 하나부터 붙여보세요. 생각보다 재밌습니다. 물론 생성된 코드에 에러 처리를 덧붙이는 일은 아직 우리 몫이고요. 그 정도 수고는 뭐, 개발자답게 웃으면서 해볼 만하지 않겠어요.
댓글
댓글 쓰기