Featured Post
작성자:
Iros
- 공유 링크 만들기
- X
- 이메일
- 기타 앱

요즘 제가 개발하면서 제일 많이 하는 말이 있어요. “이거 AI한테 한번 시켜볼까?” 예전 같으면 반나절은 잡고 만들었을 작은 API나 테스트 코드, 관리자 화면 초안 같은 것들이 ChatGPT나 Copilot 덕분에 정말 빠르게 튀어나오거든요. 솔직히 편합니다. 편한 건 인정해야죠. 그런데 한편으로는 묘하게 불안해요. 여행 가서 블로그 후기만 믿고 식당 들어갔다가 “아, 여긴 아니었네…” 싶은 순간 있잖아요. AI가 짜준 코드도 딱 그런 느낌일 때가 있어요. 겉보기엔 멀쩡한데, 안쪽을 들여다보면 보안 구멍이나 이상한 습관이 숨어 있는 경우가 꽤 있거든요.
그래서 제가 요즘 AI 바이브 코딩할 때 거의 습관처럼 붙여 쓰는 도구가 있습니다. 바로 Semgrep이에요. 정적 분석 도구인데, 말이 좀 딱딱하죠. 쉽게 말하면 “코드를 실행하기 전에 위험한 냄새가 나는 부분을 먼저 찾아주는 친구” 정도로 생각하면 됩니다. 오늘은 제가 실제로 AI가 만든 코드를 Semgrep으로 걸러보면서 겪었던 일들, 그리고 실무에서 써먹기 좋은 작은 팁들을 편하게 풀어볼게요.
AI 코드가 빨라진 만큼, 검증 루틴도 같이 빨라져야 하더라
저도 처음에는 AI가 짜준 코드를 꽤 순진하게 믿었어요. “오, 잘 돌아가네?” 싶으면 약간 손보고 바로 붙이기도 했고요. 특히 개인 프로젝트나 사내에서 빠르게 프로토타입 만들 때는 속도가 워낙 중요하니까요. 그런데 어느 날, AI가 만들어준 검색 API를 보다가 등골이 살짝 서늘해진 적이 있습니다.
FastAPI로 간단한 블로그 검색 기능을 만들고 있었어요. 제가 AI에게 “게시글 제목으로 검색하는 endpoint 만들어줘”라고 했더니 이런 코드를 내줬습니다.
// app.py (AI가 생성한 코드)
@app.get("/search")
async def search(q: str):
query = f"SELECT * FROM posts WHERE title LIKE '%{q}%'"
result = await db.execute(query)
return result.fetchall()
처음 보면 뭐, 그럴듯합니다. endpoint도 있고, query parameter도 받고, DB 조회도 하죠. 그런데 이건 실무 개발자라면 바로 눈치챌 수밖에 없는 코드예요. SQL injection 위험이 그대로 열려 있거든요. 사용자가 넘긴 q 값을 그대로 SQL 문자열에 꽂아 넣고 있으니까요.
사실 이런 코드는 주니어가 작성했다면 코드 리뷰에서 바로 잡아줄 수 있어요. 그런데 AI가 만든 코드는 이상하게 사람이 방심하게 됩니다. “얘가 알아서 잘했겠지” 하는 마음이 생겨요. 저는 이게 제일 위험하다고 봐요. AI가 코드를 빨리 만들어주는 건 좋지만, 검증까지 대신해주는 건 아니거든요.
그래서 저는 Semgrep을 AI 코드의 안전벨트처럼 씁니다
정적 분석 도구는 꽤 많아요. SonarQube, ESLint, Pylint, CodeQL도 있고요. 저도 이것저것 다 써봤습니다. 20년 가까이 개발하면서 느낀 건, 도구는 아무리 좋아도 팀에서 꾸준히 안 쓰면 그냥 장식품이 된다는 거예요.
Semgrep이 마음에 들었던 건 딱 그 지점이었어요. 가볍고, 빠르고, 규칙 만들기가 생각보다 쉽습니다. 특히 AI가 만들어낸 코드를 빠르게 훑어보는 용도로는 꽤 잘 맞아요.
| 도구 | 제가 느낀 장점 | 조금 아쉬웠던 점 | AI 코드 검사용 체감 |
|---|---|---|---|
ESLint |
JavaScript, TypeScript 프로젝트에서는 거의 기본값처럼 쓰기 좋음 | 보안 패턴이나 여러 언어를 한 번에 보기엔 한계가 있음 | 프론트엔드 코드 품질 체크용으로 좋음 |
SonarQube |
대시보드와 품질 지표가 잘 정리됨 | 초기 설정과 운영 부담이 조금 있음 | 조직 단위 품질 관리에 적합 |
CodeQL |
깊이 있는 보안 분석에 강함 | 분석 시간이 길게 느껴질 때가 있음 | 중요한 서비스의 정밀 검사에 좋음 |
Semgrep |
빠르고, 규칙 작성이 쉽고, CI/CD에 붙이기 편함 | 너무 복잡한 데이터 흐름 분석은 별도 고민이 필요함 | AI가 만든 코드 빠르게 거르기에 딱 좋음 |
예전에 어떤 프로젝트에서 CodeQL을 붙였을 때 빌드 시간이 5분 넘게 늘어난 적이 있었어요. 물론 CodeQL이 나쁘다는 이야기는 아닙니다. 깊게 보는 도구니까 그만한 비용이 드는 거죠. 그런데 팀원들 반응은 냉정합니다. “커밋 한 번 할 때마다 너무 오래 걸리는데요?” 이 말이 나오면 도구는 점점 우회되기 시작해요.
반면 Semgrep은 제가 쓰는 규모의 프로젝트에서는 보통 10초에서 20초 안팎으로 끝나는 경우가 많았습니다. 이게 별것 아닌 것 같지만, 실무에서는 엄청 커요. 개발자가 기다릴 수 있는 시간 안에 끝나면 도구는 습관이 됩니다. 기다리다가 커피 타러 가야 하면, 어느 순간 꺼버리게 되고요.
실제로 AI가 만든 FastAPI 코드를 Semgrep으로 잡아본 이야기
아까 보여드린 위험한 SQL 코드를 Semgrep으로 돌려봤습니다. 저는 처음에는 간단하게 아래처럼 실행했어요.
semgrep --config auto
그러면 Semgrep이 프로젝트 언어와 구조를 보고 기본적으로 쓸 만한 규칙들을 적용해줍니다. 완벽하진 않아도, 처음 돌려보기에는 꽤 괜찮아요. 그때 잡힌 규칙이 이런 종류였습니다.
| Semgrep Rule ID | 잡아내는 패턴 | 왜 위험한가 |
|---|---|---|
python.lang.security.audit.raw-sql-query |
execute(f"...") 또는 문자열 연결로 만든 SQL |
사용자 입력값이 SQL에 직접 들어가면 SQL injection 위험이 생김 |
generic.secrets.security.detected-hardcoded-secret |
코드 안에 박혀 있는 secret, password, token | 저장소에 올라가는 순간 유출 사고로 이어질 수 있음 |
python.lang.security.audit.dangerous-subprocess-use |
검증되지 않은 입력값을 이용한 subprocess 호출 | 명령어 주입 취약점으로 이어질 수 있음 |
터미널에 경고가 뜨는 순간, 뭐랄까. 누가 옆에서 “형, 이거 그냥 배포하면 큰일 나요” 하고 말해주는 느낌이었습니다.
그래서 저는 다시 AI에게 이렇게 요청했어요.
이 코드는 SQL injection 위험이 있어.
SQLAlchemy ORM 방식으로 바꾸고,
사용자 입력값이 직접 SQL 문자열에 들어가지 않게 수정해줘.
그랬더니 AI가 훨씬 나은 방향으로 코드를 바꿔줬습니다.
// app.py (수정 후 예시)
@app.get("/search")
async def search(q: str, session: AsyncSession = Depends(get_session)):
stmt = select(Post).where(Post.title.ilike(f"%{q}%"))
result = await session.execute(stmt)
return result.scalars().all()
물론 이 코드도 프로젝트 상황에 따라 더 다듬어야 합니다. 검색어 길이 제한도 넣어야 하고, 페이지네이션도 필요하고, 인덱스 설계도 봐야 하죠. 그래도 최소한 사용자가 입력한 값을 그대로 raw SQL 문자열에 꽂아 넣는 방식에서는 벗어났습니다. 이 정도만 해도 사고 가능성은 확 줄어요.
AI가 은근히 자주 만드는 위험한 코드 패턴들
제가 AI 바이브 코딩을 하면서 자주 본 위험 패턴이 몇 가지 있어요. 웃긴 건, AI가 이런 코드를 굉장히 자연스럽게 써준다는 겁니다. 마치 “이 정도는 괜찮겠죠?” 하는 얼굴로요. 하지만 운영 환경에서는 괜찮지 않은 경우가 많습니다.
| AI가 자주 만드는 코드 | 문제점 | 제가 보통 고치는 방향 |
|---|---|---|
SECRET_KEY = "supersecret" |
secret이 코드에 하드코딩됨 | 환경변수 또는 secret manager로 분리 |
eval(user_input) |
사용자 입력값이 코드로 실행될 수 있음 | eval 제거, 허용된 값만 매핑 처리 |
os.system(f"rm {filename}") |
command injection 위험 | 안전한 API 사용, 입력값 검증, shell 제거 |
pickle.load(file) |
신뢰할 수 없는 pickle 파일 역직렬화 위험 | JSON 등 안전한 포맷 사용 |
verify=False |
TLS 인증서 검증 비활성화 | 인증서 검증 유지, 개발용 코드 분리 |
특히 하드코딩된 secret은 정말 자주 나옵니다. 제가 최근에 사내 도구 하나를 만들면서 AI에게 “JWT 인증 샘플 코드도 넣어줘”라고 했더니, 아주 당당하게 이런 코드를 넣어줬어요.
SECRET_KEY = "my-super-secret-key"
ALGORITHM = "HS256"
샘플 코드니까 그럴 수 있죠. 그런데 문제는 우리가 바쁠 때 이걸 그대로 복사해서 쓰는 순간입니다. “나중에 바꿔야지” 하고 넘어가면, 그 나중은 생각보다 잘 안 옵니다. 개발자라면 다 알잖아요. 나중에 하자는 말은 대체로 안 하겠다는 말과 비슷합니다.
이런 부분에서 Semgrep이 꽤 고마워요. 사람이 놓친 걸 기계적으로 잡아줍니다. 감정도 없고, 눈치도 안 봅니다. 그냥 “여기 위험해요” 하고 말해줘요. 저는 이런 도구가 팀에 하나쯤은 꼭 있어야 한다고 생각합니다.
제가 쓰는 Semgrep 기본 사용 흐름
제 작업 방식은 복잡하지 않습니다. 오히려 단순해야 오래 가더라고요. AI에게 코드를 만들게 하고, 제가 한 번 읽고, Semgrep을 돌리고, 다시 AI에게 수정 요청을 하는 식입니다. 사람과 AI와 도구가 서로 역할을 나눠 갖는 느낌이에요.
대략 이런 흐름입니다.
- AI에게 기능 단위로 코드를 요청합니다. 너무 큰 범위를 한 번에 맡기면 이상한 코드가 섞일 확률이 높아져요.
- 제가 먼저 코드 흐름을 눈으로 봅니다. 인증, 권한, 입력값 검증, 예외 처리 쪽을 특히 봐요.
- Semgrep을 로컬에서 한 번 돌립니다. 빠르게 위험 패턴을 확인합니다.
- 경고가 뜨면 AI에게 구체적으로 다시 요청합니다. “이 경고를 해결해줘”라고만 하지 않고, 왜 위험한지도 같이 말해줍니다.
- 수정된 코드를 다시 Semgrep으로 확인합니다. 이걸 귀찮아하면 결국 똑같은 문제가 남습니다.
명령어는 처음엔 이 정도면 충분합니다.
semgrep --config auto .
조금 익숙해지면 특정 규칙 묶음을 지정해서 돌릴 수도 있어요.
semgrep --config p/security-audit .
semgrep --config p/secrets .
semgrep --config .semgrep/rules .
저는 보통 개인 프로젝트에서는 auto로 시작하고, 회사 프로젝트에서는 팀 규칙을 별도로 둡니다. 회사마다 금지해야 하는 패턴이 다르거든요. 어떤 팀은 raw SQL을 완전히 금지할 수도 있고, 어떤 팀은 특정 모듈에서만 허용할 수도 있습니다. 이런 건 범용 규칙만으로는 애매해요. 결국 팀만의 규칙이 필요합니다.
Custom Rule을 만들기 시작하면 Semgrep이 훨씬 재밌어집니다
Semgrep의 진짜 매력은 Custom Rule이라고 생각해요. YAML로 규칙을 만들 수 있는데, 처음엔 “또 YAML이야?” 싶다가도 몇 번 해보면 은근히 손에 붙습니다.
예를 들어 우리 팀에서 eval() 사용을 금지하고 싶다고 해볼게요. 이런 규칙을 만들 수 있습니다.
rules:
- id: no-eval-in-python
patterns:
- pattern: eval(...)
message: eval 사용은 위험합니다. 허용된 값 매핑 방식으로 바꿔주세요.
languages:
- python
severity: ERROR
단순하죠. 그런데 효과는 꽤 큽니다. 코드 리뷰에서 사람이 매번 “eval 쓰지 마세요”라고 말하는 대신, 도구가 먼저 막아줍니다. 사람은 더 중요한 설계나 맥락을 보게 되고요. 저는 이게 좋은 코드 리뷰 문화라고 생각합니다. 사람이 기계처럼 체크할 일은 도구에게 넘기고, 사람은 사람답게 판단하는 거죠.
저는 개인적으로 .semgrep/rules/ 폴더를 만들어서 팀 규칙을 모아두는 걸 좋아합니다.
project-root/
├── app/
│ ├── api/
│ ├── models/
│ └── services/
├── tests/
├── .semgrep/
│ └── rules/
│ ├── ai-vibe-checks.yml
│ ├── no-raw-sql.yml
│ └── no-hardcoded-secret.yml
├── .pre-commit-config.yaml
└── pyproject.toml
이렇게 해두면 새로 합류한 개발자도 “아, 이 팀은 이런 패턴을 싫어하는구나” 하고 금방 감을 잡습니다. 문서로만 적어두면 잘 안 읽히는데, 규칙으로 만들어두면 코드 작성 중에 바로 만나게 되거든요.
제가 실제로 쓰는 AI 코딩 전용 체크 규칙
저는 ai-vibe-checks.yml이라는 이름으로 AI가 자주 만드는 실수를 모아둔 규칙 파일을 하나 만들어 씁니다. 이름이 좀 장난스럽죠. 그런데 꽤 유용합니다. AI가 만들어준 코드를 붙이기 전에 이걸 한 번 돌리면 마음이 조금 편해져요.
| 체크 항목 | 왜 넣었나 | 실무에서 본 상황 |
|---|---|---|
eval() 사용 |
입력값 실행 위험 | 사용자 입력 수식 계산 기능을 만들 때 AI가 자주 제안함 |
pickle.load() |
역직렬화 취약점 위험 | 캐시 파일 읽기 예제로 종종 등장함 |
os.system() |
command injection 위험 | 파일 처리 자동화 코드에서 쉽게 나옴 |
verify=False |
인증서 검증 비활성화 | 외부 API 연동 테스트 코드에 자주 섞임 |
| 하드코딩된 password, token | 저장소 유출 위험 | 샘플 인증 코드 만들 때 거의 단골처럼 나옴 |
재밌는 건, 이런 규칙을 만들어두면 AI에게 프롬프트를 줄 때도 더 구체적으로 말하게 된다는 거예요. 예전에는 “로그인 API 만들어줘”라고 했다면, 요즘은 이렇게 말합니다.
FastAPI로 로그인 API를 만들어줘.
비밀번호는 bcrypt로 검증하고,
SECRET_KEY는 환경변수에서 읽어와.
raw SQL은 쓰지 말고 SQLAlchemy ORM을 사용해.
eval, pickle, os.system 같은 위험한 함수는 사용하지 마.
이렇게 요청하면 처음부터 더 나은 코드가 나올 확률이 올라갑니다. 물론 그래도 검사는 해야 합니다. AI는 가끔 자신감 있게 이상한 코드를 줍니다. 사람이랑 비슷해요. 자신감과 정확도는 늘 같이 가지 않습니다.
Pre-commit에 붙이면 귀찮음이 확 줄어듭니다
제가 강하게 추천하는 방식은 Pre-commit에 Semgrep을 붙이는 겁니다. 사람이 기억해서 매번 명령어를 치는 건 한계가 있어요. 저도 바쁘면 까먹습니다. 회의 들어가기 전에 급하게 커밋하다 보면 “아 맞다, 검사 안 했네”가 너무 자연스럽게 나오거든요.
.pre-commit-config.yaml에 이런 식으로 넣어둘 수 있습니다.
repos:
- repo: https://github.com/semgrep/pre-commit
rev: v1.78.0
hooks:
- id: semgrep
args:
- --config
- auto
설정한 뒤에는 아래처럼 설치하면 됩니다.
pre-commit install
이제 커밋할 때마다 자동으로 Semgrep이 돌게 됩니다. 물론 처음에는 귀찮게 느껴질 수 있어요. 그런데 며칠만 지나면 이게 오히려 마음을 편하게 해줍니다. 여행 갈 때 현관문 잠갔는지 다시 확인하는 습관 같은 거예요. 한 번 확인하고 나면 발걸음이 가벼워지죠.
Semgrep Playground는 규칙 만들 때 꽤 쓸 만합니다
Custom Rule을 처음 만들 때 로컬에서 계속 돌려보면 조금 번거롭습니다. 그럴 땐 Semgrep Playground를 써보면 좋아요. 브라우저에서 코드와 규칙을 같이 넣고 바로 테스트할 수 있습니다.
저는 보통 이런 식으로 씁니다.
- 잡고 싶은 나쁜 코드 예시를 Playground에 넣습니다.
- 규칙을 대충 작성해봅니다.
- 정말 잡히는지 확인합니다.
- 잡히면 안 되는 정상 코드도 넣어봅니다.
- 오탐이 줄어들 때까지 규칙을 조금씩 다듬습니다.
이 과정이 중요해요. 규칙은 너무 느슨해도 문제고, 너무 빡빡해도 문제입니다. 너무 느슨하면 위험한 코드를 놓치고, 너무 빡빡하면 개발자들이 짜증을 냅니다. 결국 도구는 신뢰가 생명이라서, “이 경고는 볼 만하다”는 느낌을 줘야 오래 씁니다.
Semgrep을 쓴다고 코드 리뷰가 필요 없어지는 건 아닙니다
이건 꼭 말하고 싶어요. Semgrep이 좋다고 해서 코드 리뷰가 사라지는 건 아닙니다. AI가 코드를 잘 만든다고 개발자가 필요 없어지는 것도 아니고요. 저는 오히려 반대라고 봅니다. AI와 정적 분석 도구를 잘 쓸수록 개발자의 판단력이 더 중요해집니다.
Semgrep은 패턴을 잘 잡습니다. 하지만 이 기능이 비즈니스 요구사항에 맞는지, 장애 상황에서 어떤 식으로 복구될지, 운영자가 로그를 보고 원인을 파악할 수 있을지, 이런 건 사람이 봐야 합니다. 보안도 마찬가지예요. 도구가 잡아주는 건 기본적인 위험 신호고, 진짜 설계 판단은 여전히 사람 몫입니다.
제가 좋아하는 조합은 이렇습니다.
| 역할 | 맡기면 좋은 일 | 맡기면 안 되는 일 |
|---|---|---|
| AI | 초안 작성, 반복 코드 생성, 테스트 케이스 뼈대 만들기 | 보안 책임, 아키텍처 최종 결정, 운영 정책 판단 |
| Semgrep | 위험 패턴 탐지, 팀 규칙 강제, 반복적인 리뷰 포인트 자동화 | 비즈니스 맥락 이해, 복잡한 설계 의도 판단 |
| 개발자 | 요구사항 해석, 설계 결정, 예외 상황 판단, 운영 품질 책임 | 기계적으로 반복되는 단순 패턴 검사에 시간 낭비하기 |
이렇게 역할을 나누면 꽤 편합니다. AI에게는 빠른 손을 빌리고, Semgrep에게는 깐깐한 눈을 빌리고, 개발자는 전체 방향을 잡는 거죠. 저는 이 방식이 요즘 개발자에게 꽤 현실적인 생존 전략이라고 생각합니다.
AI 바이브 코딩을 한다면, 최소한의 안전장치는 꼭 두세요
솔직히 말하면, 저는 이제 AI 없이 예전 방식으로만 개발하라고 하면 좀 답답할 것 같아요. 그만큼 생산성 차이가 큽니다. 하지만 AI가 만들어준 코드를 아무 검증 없이 붙이는 건 반대입니다. 빠르다고 다 좋은 건 아니니까요. 고속도로를 달릴수록 안전벨트가 더 중요해지는 것처럼, AI 바이브 코딩을 할수록 Semgrep 같은 정적 분석 도구가 더 필요해진다고 봅니다.
제가 추천하는 최소 세팅은 이 정도예요.
- 로컬에서
semgrep --config auto를 돌려보기 - secret 탐지 규칙은 꼭 켜두기
- 팀에서 금지하는 패턴은 Custom Rule로 만들기
- Pre-commit이나 CI/CD에 붙여서 자동화하기
- AI에게 코드 수정 요청할 때 Semgrep 경고 내용을 같이 알려주기
이 정도만 해도 체감이 꽤 큽니다. “어? 이거 위험했네” 싶은 순간을 몇 번 만나게 되거든요. 그리고 그 몇 번이 실제 장애나 보안 사고를 막아줬을 가능성도 있습니다.
이런 분들에게 특히 추천하고 싶습니다
이 글은 AI 코딩 도구를 이미 쓰고 있거나, 이제 팀에 도입해보려는 분들에게 특히 도움이 될 거예요. 혼자 사이드 프로젝트를 만드는 개발자에게도 좋고, 팀 코드 품질을 챙겨야 하는 리더에게도 꽤 현실적인 선택지라고 생각합니다.
- ChatGPT, Copilot 같은 AI 코딩 어시스턴트를 자주 쓰는 개발자
- AI가 만든 코드를 어디까지 믿어야 할지 고민되는 분
- 보안 취약점을 배포 전에 최대한 걸러내고 싶은 실무자
- 팀만의 코딩 규칙을 자동으로 강제하고 싶은 리더
- “일단 돌아가는 코드”보다 “운영해도 덜 불안한 코드”를 좋아하는 개발자
저는 Semgrep이 모든 문제를 해결해준다고 생각하진 않습니다. 그런 도구는 없어요. 그래도 AI가 만든 코드를 한 번 걸러주는 안전망으로는 정말 쓸 만합니다. 빠르게 만들고, 빠르게 검사하고, 다시 고치고. 이 리듬이 잡히면 AI 코딩이 훨씬 덜 불안해집니다.
AI가 짠 코드, 그냥 믿지는 마세요. 대신 잘 활용하면 됩니다. 저는 그 중간 지점에 Semgrep을 두고 있습니다. 너무 과하지도 않고, 너무 느슨하지도 않은 안전장치. 요즘 제 개발 작업대 위에 늘 올려두는 작은 공구 같은 녀석입니다.
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
댓글
댓글 쓰기