리포트용 SQL 빌더 하나를 믿을 수 있게 만들기까지

들어가며

안녕하세요. 초SSS급 개발자가 되고 싶은 이승재입니다. 이번 글은 광고 리포트를 만드는 SQL 빌더 하나를, 믿을 수 있게 만들기까지의 기록입니다.

자연어가 리포트가 되기까지

제가 다루는 서비스에는 SQL을 이용해 리포트를 만드는 빌더가 있습니다. 사용자가 “지난달 캠페인별 전환수 보여줘”처럼 자연어로 요청하면, AI 에이전트가 그 문장을 해석해 차원·지표·기간·필터를 파라미터로 뽑아냅니다. 빌더는 그 파라미터를 받아 Athena에서 돌릴 SQL을 절 단위로 조립하고, 실행 결과를 리포트로 돌려줍니다. 자연어 한 문장이 SQL이 되어, 곧바로 광고주가 보는 숫자가 되는 셈입니다.

자연어 질문 차원·지표·기간·필터 AI 에이전트 파라미터 추출·정규화 SQL 빌더 절 단위 조립 쿼리를 절 단위로 따로 만들어 조립 SELECT GROUP BY WHERE HAVING ORDER BY LIMIT Athena 실행·캐싱 리포트 숫자 반환
자연어 요청이 AI 에이전트를 거쳐 SQL로 조립되고, Athena에서 실행되어 리포트가 된다

문제는, 이런 코드는 틀려도 티가 안 난다는 점입니다. SQL 문법이 깨지는 게 아니라면 쿼리는 멀쩡히 돌고, 표도 예쁘게 채워집니다. 매출이 중복 집계돼 두 배로 부풀어도 아무도 눈치채지 못하고 숫자만 조용히 틀립니다.

리포트 결과 쿼리 성공 ✓ 항목 매출 실제 매출 ₩1,000,000 리포트 매출 ₩2,000,000 중복 집계 ×2 SQL 문법은 멀쩡 — 값만 조용히 틀림 리포트 매출이 왜 실제랑 일치하지 않지? 광고주
SQL은 성공했지만 매출이 두 배로 — 문법이 아니라 값이 조용히 틀린다

그래서 저는 이 빌더를 믿을 수 있게 만드는 테스트를 고민했습니다. 이 글은 그 고민의 기록입니다.

테스트에 대한 고찰

제가 생각하는 테스트는 크게 둘로 나뉩니다. 유닛 테스트와 통합 테스트입니다. 둘 중 뭐가 더 중요하냐고 묻는다면, 저는 둘 다 중요하다고 답하겠습니다. 목적이 서로 다르기 때문입니다.

유닛 테스트는 계약에 대한 검증입니다. ‘이 입력을 주면 이 출력을 준다’는 계약, 곧 스펙을 확인합니다. 실제 인프라를 끌어오지 않아 비용 부담이 적고 빠릅니다만 그 환경은 실제와 다릅니다. 프로덕션에서는 인프라를 호출하고, 모듈끼리 협응하고, 네트워크를 타고 밖으로 나갑니다. 하지만 유닛 테스트는 그렇지 않습니다. 게다가 유닛 테스트는 대부분 성공하기 마련입니다. 애초에 스펙을 확인하려고 성공 케이스와 실패 케이스를 나눠 그렇게 mock으로 만들어 두니까요. 그래서 유닛 테스트의 역할은 스펙 검증과, 코드 수정 시 변경의 전파를 잡는 데 있습니다.

반면 통합 테스트는 정도의 차이는 있지만 실제 프로덕션 환경과 최대한 유사하게 맞추려 합니다. 실제 동작을 테스트할 수 있지만 그만큼 무겁고, 경우에 따라 과금이 발생하기도 합니다.

실제 코드가 프로덕션으로 나가려면 통합 테스트가 반드시 필요합니다. 그리고 이 둘은 언제나 함께 개발되어야, 비로소 테스트 코드가 제값을 합니다.

유닛 테스트 계약 · 스펙 검증 · mock으로 성공·실패 분리 · 인프라 없이 싸게 · 빠르게 돌린다 변경의 전파를 잡는다 통합 테스트 프로덕션에 가까이 · 인프라 호출 · 모듈끼리 협응 · 네트워크 타고 밖으로 프로덕션 반출의 필수 조건 둘은 언제나 함께 개발되어야 테스트가 제값을 한다
유닛은 계약을 싸게 확인하고, 통합은 프로덕션의 협응을 확인한다 · 둘은 함께 간다

데이터가 없는데 어떻게 테스트해요

막상 테스트를 하려니 문제가 있었습니다. S3·Athena 같은 AWS 인프라는 갖춰져 있었지만, 정작 그 위에서 테스트에 쓸 데이터가 없었습니다.

그래서 mock 데이터로 시작했습니다. 작은 가짜 데이터를 만들어 넣고 그 값을 pandas로 검산해 둔 다음, 같은 입력을 빌더에 넣어 만들어진 SQL의 결과와 맞춰 봤습니다. 이렇게 빌더가 뽑은 숫자를 빌더로 확인하지 않고 독립된 계산과 대조하는 방식을 테스트 오라클 기법이라고 합니다. 두 경로는 완전히 독립적이며 셀 단위로 한 칸이라도 어긋나면 실패입니다.

빌더의 SQL 실 Athena 실행 원본 데이터 pandas 독립 재계산 결과 A 결과 B 셀 단위 대조 0 불일치 = 통과
정답을 아는 독립 계산이 따로 있어야 검증이 성립한다 · 두 경로가 셀 하나까지 같아야 통과

이 단계에서는 데이터의 정합성도 살폈지만, 주로 확인하려던 건 스펙이었습니다. SQL이 깨지지는 않는지, 빌더가 조건에 따라 분기를 제대로 타는지, 테이블을 옳게 고르는지. 빌더 로직이 뽑아내는 SQL의 모양을 검증하려 했습니다.

실데이터가 적재되는 순간에도 새로운 테스트 오라클을 만들면 됐습니다. 다행히 실데이터 기반으로도 잘 검증해 주었지만 이내 다른 문제를 맞닥뜨리게 됩니다. 요구사항은 언제나 변하고, 버그는 발견됩니다. 그때마다 코드를 리팩토링하고, 테스트를 다시 정비하는 과정은 너무 반복적이고 지루하다는 겁니다.

어떻게 매번 사람이 해요

테스트를 설계하며 세 가지를 중요하게 뒀습니다. 첫째, 언제 돌려도 재현될 것. 둘째, 무엇을 왜 고쳤는지 추적될 것. 셋째, 이 둘을 사람 손이 아니라 자동으로 굴릴 것.

01 재현 고정 데이터셋 회귀 테스트 스크립트 영구화 언제 돌려도 같은 결과 02 추적 서사 문서 테스트셋 문서 append only 무엇을 왜 고쳤는지 남는다 03 자동화 테스트 에이전트 스킬 같은 절차 반복 사람 손 없이 굴러간다
재현 · 추적 · 자동화 — 테스트 설계에서 붙든 세 가지

첫 번째 원칙, 재현. 검증에 쓴 데이터셋을 고정해 두고, 그 위에서 케이스 전부를 매번 다시 돌립니다. 빌더 로직을 건드릴 때마다 같은 데이터에 같은 케이스를 던져, 예전에 맞던 것이 지금도 맞는지 확인합니다. 일종의 회귀 테스트입니다. 검증 스크립트도 저장소 안에 영구화해, 파일 이름만으로 그때 그 라운드를 그대로 다시 돌릴 수 있게 했습니다.

두 번째 원칙, 추적. 테스트를 돌릴 때마다 검증 문서를 남겼습니다. 하나는 서사입니다. 목표, 데이터 출처, 산식, 라운드별 결과, 실패했을 때의 원인과 고친 과정. 다른 하나는 테스트셋입니다. 여태 돌린 SQL 전문과 각 결과, 재현 방법. 둘 다 지우지 않고 append만 합니다.

검증 문서 · 서사 목표 · 데이터 출처 · 산식 라운드별 결과와 판정 실패 원인 · 스스로 고친 과정 append only SQL 문서 · 테스트셋 돌린 SQL 전문 각 결과 재현 방법 append only R1 R2 R3 R9 라운드가 쌓일수록 문서도 함께 쌓인다
서사와 테스트셋을 지우지 않고 쌓는다 · 파일 이름만으로 그때 그 라운드를 다시 돌릴 수 있게

세 번째 원칙, 자동화. 여기까지를 매번 사람이 손으로 할 수는 없습니다. 그래서 SQL 빌더 전용 테스트 에이전트와 스킬을 만들었습니다. 에이전트가 케이스를 펼치고, 스킬이 검증 절차를 매번 같은 순서로 밟습니다. 재현과 추적이 사람 의지가 아니라 도구의 기본 동작이 됩니다.

① 오라클 재계산 S3 원본 → pandas 독립 재계산 verify_core.py · 코드 무의존 ② 산출물 실행 sql_builder → Athena 실행 결과 재사용 캐시 ③ 셀 단위 대조 키 1:1 · 3축 값 · CASE 파싱 · 컬럼 순서 ④ 회귀 스위트 QA마다 전용 라운드 추가 전체 스위트 재실행 ⑤ append 문서 검증.md 서사 · SQL.md 테스트셋 · QA 트래커 sql_builder를 바꿀 때마다 전체 스위트를 반복 · 스위트는 QA마다 늘어난다
검증 스킬 구조 — 독립 오라클과 빌더 실행을 셀 단위로 대조하고, QA마다 전용 라운드를 더해 전체를 회귀로 재실행하며 결과를 문서에 append한다

마치며

테스트를 쓰는 일은 구현보다 언제나 더 어려운 것 같습니다. 잘 쓰인 테스트는 그것만 봐도 본래 코드를 유추할 수 있고, 그 자체로 실행 가능한 스펙이 됩니다.

굳이 개발자의 경지를 나눈다면, 유의미한 테스트 코드를 잘 쓰는 개발자야말로 앞서 말한 초SSS급 개발자가 아닐까 싶습니다.

그렇기 때문에 요즘은 어떻게 테스트할 것인가, 어떻게 검증할 것인가에 대해 많은 고민을 하고 있습니다.

읽어주셔서 감사합니다.

참조

이 글은 https://codestudy.org/how-i-tested-a-sql-builder에 먼저 올린 글입니다.

프롬프트 엔지니어링에서 하네스 엔지니어링까지, 그리고 제 생각

들어가며

안녕하세요. NHN AD 플랫폼서비스랩의 이승재입니다. 이번 글에서는 전통적인 개발에서 하네스 엔지니어링까지의 변화와, 그에 대한 제 생각을 나눠보려 합니다.

손으로 벽돌 쌓기

제 첫 코딩은 2018년 대학에서 시작됐습니다. 소프트웨어 융합대학 소속이라 C언어와 파이썬을 필수로 배워야 했는데, 이중 for문 별찍기와 포인터를 만나기 전까지는 ‘나는 코딩 천재 아닐까?’ 하는 착각도 했습니다. 그 시절의 코딩이란 구글링과 선배들을 통해 알음알음 배워가는, 벽돌을 한 장 한 장 쌓아 프로그램을 올리는 일이었습니다.

검색 자바 이중 for문 별 찍기 Main.java 12345 타이핑… 검색으로 배우고, 한 줄 한 줄 손으로 — 벽돌을 쌓듯
구글링으로 방법을 찾고, 코드를 직접 타이핑하던 시절

하지만 지금은 어떤가요? 개발자들은 더 이상 구글링하지 않고, 코드를 한 줄씩 쓰지도 않습니다. 모든 일의 시작과 끝에 AI가 있고, 개발자의 역할도 ‘코드를 작성하는 사람’에서 ‘문제를 정의하고 해결하는 사람’으로 바뀌었습니다. 실제로 저는 NHN AD 입사 이후 코드를 단 한 줄도 직접 쓴 적이 없습니다.

코드를 직접 쓰지 않는다면, 개발자는 무엇을 해야 할까요?

벽돌을 더 잘 쌓는 법

제 AI 사용은 2023년, NHN Academy에서 공부하던 무렵 시작됐습니다. 유튜브와 인스타그램만 켜도 이력서 프롬프트, 글쓰기 프롬프트, 개발 프롬프트가 넘쳐나던 시기였죠. 저도 그 흐름을 따라, 늘 이런 지시문부터 적었습니다.

ChatGPT 너는 10년 차 시니어 자바 개발자야. SQL과 클린코드에 깊은 지식을 가진 전문가로, 코드를 작성하고 피드백을 줘… 물론입니다. 어떤 코드를 봐드릴까요?
Prompt Engineering — 모델에게 ‘어떤 말’을 할지가 전부였다

이런 방법론을 Prompt Engineering이라 부릅니다. 주된 관심사는 단 하나, ‘모델에게 어떤 말을 해야 하나’였습니다. 역할을 부여하고, 상황과 원하는 바를 최대한 자세히 설명하는 방식이죠. 직관적이고 효과도 좋아서, NHN AD 서비스에도 Few-Shot과 ReAct 프롬프팅이 적용되어 있습니다.

Instruction 역할·규칙·예시 OpenAPI Schema 도구 정의 에이전트 해석 → 도구 선택 도구 호출 응답 생성
Instruction과 스키마가 에이전트의 행동을 규정한다 · 서비스 특화 내용은 제외한 일반 구조

저는 오랜 기간 이 방식에 머물렀습니다. 대부분의 코드는 손으로 쓰고, 막히거나 어려운 개념이 나오면 그때 AI에게 물어보는 식이었죠.

벽돌 쌓는 시대의 종말

그러던 2025년, 저를 바꾼 사건이 있습니다. 바로 Claude Code입니다. GUI에서 CLI로, 그리고 Prompt Engineering에서 Context Engineering으로의 전환이었습니다.

예전에는 필요한 코드와 파일을 제가 직접 채워 넣고 프롬프트로 모델을 제어했습니다. 하지만 Claude Code 이후로는 AI가 파일시스템과 도구에 직접 접근해, 스스로 파일을 읽고 코드를 고치고 테스트를 돌립니다. 관심사도 ‘어떤 말을 할까’에서 ‘어떤 정보를 넣을까’로 옮겨갔습니다.

claude code — CLI docs/spec.md 읽고 구조 파악해줘 Read docs/spec.md src/UserService.java 의 validate() 함수 고쳐줘 Edit src/UserService.java validate() Bash ./gradlew test ✓ passed
경로와 파일·함수만 가리키면, 에이전트가 직접 읽고 고치고 검증한다

핵심 사항과 제약, 메모리를 파일에 나눠 적어두고 MCP로 도구를 연결해, 필요할 때 필요한 만큼만 가져오게 했습니다. 그 무렵 제 머릿속엔 “개발자 필요 없겠는데, 취업 어떡하지?”“이제 진짜 빠르게 만들 수 있겠는데?”가 동시에 맴돌았습니다. 이때쯤부터 코드를 직접 치는 빈도가 줄었고, 면접에서도 Claude Code, 에이전트와 스킬, 컨텍스트 윈도우를 묻기 시작하던, Context Engineering의 시대였습니다.

면접관 Q Claude Code 써보셨나요? Q 서브에이전트와 스킬은 어떻게 쓰시나요? Q 컨텍스트 윈도우는 어떻게 관리하세요? 음, 그게…
Claude Code, 에이전트와 스킬, 컨텍스트 윈도우

죽은 벽돌, 살아있는 설계

2026년 현재는 하네스 엔지니어링의 시대로 보입니다. 뼈대는 Context Engineering과 비슷하지만, 한층 세련된 방식으로 시스템 자체를 설계합니다. 시스템 프롬프트를 정의하고, 서브 에이전트를 쓰며, 결정론적인 부분과 비결정론적인 부분을 나눠 관리합니다. 에이전트가 엇나가지 않게 Rule로 통제하고, 가이드를 무시하더라도 린트·타입체크·컴파일이 기계적으로 잡아내도록 강제합니다.

아래는 제가 업무에서 사용하는 구조입니다. 작업을 역할별 에이전트에 나눠 맡기고, 검증에서 나온 실수는 그때그때 고치는 데 그치지 않고 스킬·메모리·Rule에 되먹여 다시 일어나지 않게 합니다.

계획 PM 에이전트 task.md · spec 구현 백엔드·프론트 ·인프라 에이전트 검증 QA·리뷰어 린트·테스트·CI 통합 Git 에이전트 커밋 · PR 정리 GC 에이전트 레거시 이관 실수를 시스템에 되먹임 스킬 · 메모리 · Rule · 에이전트 역할 갱신 실수 발견 되먹임
역할별 에이전트가 계획→구현→검증→통합→정리를 나눠 맡고, 실수는 스킬·메모리·Rule로 되먹인다 · 사내 구현은 제외한 일반 구조
repo/
├─ .claude/
│   ├─ commands/   new-feature · new-bugfix · new-hotfix …
│   ├─ agents/     pm · 백엔드 · 프론트 · 인프라 · qa · 리뷰어 · git · 정리
│   └─ skills/     반복 작업·노하우를 박제
├─ ai/          에이전트 · 람다 · 배치
├─ app/
├─ domains/     도메인 로직
├─ modules/     api-client · db-client · storage-client
├─ infra/       IaC · 배포
├─ docs/        spec · 설계 기록
├─ scripts/
└─ .legacy/     오래된 산출물 이관
폴더 예시 — 커맨드·에이전트·스킬과 작업 산출물이 한 저장소 안에서 함께 관리된다 · 사내 내용은 제외한 일반 구조

이제 저는 코드를 한 줄씩 쓰지도, 매번 긴 프롬프트를 새로 적지도 않습니다. 해야 할 일을 정의해 두면 에이전트가 스스로 필요한 파일을 읽고, 코드를 고치고, 테스트를 돌려 결과를 확인합니다. 제 역할은 무엇을 만들지 정의하고, 그 결과를 검증하는 일로 좁혀졌습니다.

앞으로의 여정

저는 지금 공모전 하나를 자동으로 진행하는 실험을 돌리고 있습니다. 매일 정해진 시각이 되면 제 노트북의 LaunchAgent가 헤드리스 Claude를 깨우고, 그날 할 일을 스스로 정리해 작업을 수행한 뒤, 자기 결과를 다시 검토하고, 하루치 리포트를 남기고 잠듭니다. 제가 손대지 않아도 다음 날 또 같은 일이 반복됩니다.

LaunchAgent 매일 정해진 시각 헤드리스 Claude 할 일 정리 작업 수행 읽기·수정·실행 자가 검토 결과 다시 확인 리포트 · 종료 하루치 기록 남김 다음 날, 손대지 않아도 같은 일이 반복
매일 스스로 깨어나 할 일을 정리하고, 수행하고, 검토하고, 리포트를 남기는 하루 단위 자동 루프
공모전 자동 진행 저장소 — 매일 task 생성·실행·자가 검증·리포트 누적
매일 task를 생성하고 실행한 뒤, 스스로 검증하고 리포트를 누적한다

그 결과로, 제가 직접 커밋하지 않은 날에도 저장소에는 날짜별 작업 기록이 차곡차곡 쌓여 있습니다.

매일 누적된 일자별 커밋 — 사람과 에이전트가 함께 author로 기록된다
사람과 에이전트가 함께 author로 남은, 일자별로 누적된 커밋

앞으로는 이 구조를 다듬어, 전자정부 표준프레임워크에 기여하는 시스템을 구축해보려 합니다. 잘 설계된 시스템이라면, 남이 만든 실제 코드베이스에 스스로 기여를 남기는 데까지 갈 수 있을 거라 생각합니다. 전자정부프레임워크 오픈 커뮤니티에서 그 여정을 이어가 보려 합니다.

읽어주셔서 감사합니다.

이 글은 https://codestudy.org/prompt-to-harness-engineering에 먼저 올린 글입니다.

들어가며

안녕하세요, 오랜만입니다. 3개월 만에 글을 작성하네요.
글 작성이 늦어진 이유는 회사에서의 바쁜 일정도 있지만, 글을 작성하는 관점이 달라져서도 있는 것 같습니다.

이전에는 글을 쓰는 행위 자체를 중요하게 생각하였는데, 이제는 하고 싶은 말이 무엇인지가 가장 중요한 것 같습니다.
하고 싶은 말이 없다면 구태여 무언가를 적어내릴 필요가 있나라는 생각입니다.

 

주 7일제를 원하는 개발자


주 5일은 개발자로서 서비스를 만들고, 주말에는 코딩 강사나 개발 콘텐츠를 만드는 일을 하고 싶습니다.
서비스를 만드는 일은 여전히 재밌고, 배울 것이 많으며 그 자체로 동기를 부여해 줍니다.
하지만 동시에 요구사항을 읽고, 문서화하고, Agent를 사용하고, 검증을 하며, 코드를 병합하는 같은 프로세스의 반복이기도 합니다.
그래서 다른 방향으로 자극을 받고, 경험의 한계를 넓히고 싶다는 생각을 하게 되네요.

당장 사이드 잡을 찾는 것은 어려우니, 인프런에 무료 강의라도 열어볼까 혹은 AI 쇼츠 자동화 서비스를 만들어서 직접 운영해 볼까 등 여러 가지 생각은 많은데 언제나 그렇듯 구체화하는 것은 쉽지 않은 것 같습니다.

제 블로그 구독자분이나, 깃허브 친구를 맺어주시는 분들과 프로젝트나 스터디를 해도 재밌겠다는 생각도 합니다.

 

생각보다 기술 블로그


이건 놀라운 사실인데요, 블로그 조회수 TOP 5안에는 항상 기술 글이 포함돼 있습니다.
시기마다 순서나 기술 글의 비중은 달라지지만, 최소 1개 이상은 포함돼 있단 건 변함없습니다.

개인적으로 글이란 작성 당시의 생각과 철학을 반영하고 있어야 생각해, 수정을 하지 않는 편입니다.
그래서 틀린 내용이나, 엄밀하지 않은 부분도 그냥 내버려두는 편인데 조회 수가 많이 나오는 글들은 리뉴얼을 한번 해야 할까 싶기도 합니다.

제 블로그가 회고 모음집이 아닌 기술 블로그 역할을 한다는 것이 매우 기쁘네요.

 

마무리하며


분명하고 싶은 말이 있어 글을 작성하였는데, 막상 다 쓰고 나니 뭘 말하고 싶었던 건지 모르겠습니다.

일을 잘한다는 것, 개발의 총량과 값싸진 코드, 채용과 주니어 개발자 등 무언가 작성한 것은 많은데 결국에는 다 쓰지 못하고 회고 글 비슷하게 작성이 되었네요.

그냥 손 때가 묻어있는 글을 쓰고 싶었던 것 같습니다.
근데 막상 쓰고나서 보니까 LLM Generated 된 느낌이 나네요 LLM이 작성하는 문서를 많이 읽어서 그런건가?

오늘은 퇴근하는데 여름이 성큼 다가와 바로 곁에 있음이 느껴졌습니다, 습식 사우나에 있는 기분이었거든요.
더운 여름, 각자의 자리에서 뜨겁게 보내시길 바랍니다.

2025년 회고

설 연휴가 끝나고 2026년이 정식으로 시작되었습니다.
작년에도 그랬듯이 이번에도 같은 방식으로 회고를 작성해보려 합니다.


첫 직장

2025년의 시작은 더없이 기분 좋았습니다.
졸업하자마자 가비아라는 회사에 전환형 인턴으로 합류하게 되었고, 취업이 힘든 시기에 좋은 기회를 잡아 하루하루 행복했던 기억이 선명합니다.

하지만 최종적으로는 정규직으로 전환되지 못하였고, 새롭게 취업 준비를 시작하게 됩니다.
전환 발표 당일 압박감에 헛구역질이 날 정도로 긴장했던 것이 아직도 생생하네요.

스스로에게 가장 아쉬운 부분은, 꽤나 긴 시간 동안 이 경험을 실패로만 받아들였다는 것입니다.
전환되지 못한 것에 대해 스스로가 부끄러웠고, 실패한 경험일 뿐 얻은 것이 너무나 부족하다는 생각을 했었습니다.
지금은 스스로의 부족함을 절실히 깨닫는 계기가 되었고, 성장할 수 있게 만들어준 값진 경험이라 생각합니다.


남을 가르치는 일

가비아에서의 근무가 끝나고, 5월 말부터 새롭게 취업 준비를 해야 하는 상황이었습니다.
이즈음부터 쉬었음 청년이라는 단어가 뉴스에 나오고, IMF 때보다 취업이 어렵다는 말이 나올 정도로 채용 한파가 심해진 기억이 납니다.

이때에는 가비아에서의 실패를 딛고 스스로가 잘할 수 있는 사람임을 증명하고 싶었습니다.
그렇게 여러 가지 일을 찾던 중 코딩을 가르쳐보는 것은 어떨까라는 생각을 하게 되었고, 이것이 또 다른 시작이 되었습니다.

떨리는 마음으로 강사 지원서를 쓰고 최종 합격을 받기까지 일주일정도 걸렸으며, 수업 시연에서는 Spring의 이름이 왜 Spring인지에 대해 설명했던 것이 떠오릅니다. (여러분은 그 이유를 아시나요? 알아보면 나름의 재미가 있습니다.)
그렇게 6월 5일부터 코딩을 가르치는 강사로 일하게 되었고, 이 글을 쓰는 시점(2026/02/21)에 마지막 강의를 마치게 되었습니다.

매주 토요일마다 강의를 하였는데, 공채의 경우 코딩테스트가 토요일에 몰려있어 눈물을 머금으며 아까운 기회를 날리기도 하고, 어떻게든 양해를 구해 코딩테스트에 참석했던 기억도 납니다.

약 9개월간 더없이 좋은 분들과 함께할 수 있었고, 꾸준히 채용을 진행해 조직이 커가는 모습을 보면서 감회가 새롭기도 했습니다.
제가 가르친 학생이 원하는 고등학교에 진학하고, 자격증을 취득하며 개발자로서의 진로를 꿈꾸게 되는 경험은 앞으로도 잊지 못할 것 같습니다.


탈락의 연속

7월부터 12월까지는 꾸준히 취업 준비를 하였습니다.
운 좋게도 카카오게임즈, 네이버웹툰, Daum 모두 임원 면접까지 가게 되었는데

카카오게임즈는 첫 임원 면접에 충분히 준비하지 못하였고
네이버웹툰에서는 가비아 때의 경험을 단순한 실패로 생각했던 것에 대가를 치르게 됐으며
Daum은 가진 역량을 최대한 쏟아부었지만 결국 합격하지 못했습니다.

Daum의 경우 임원 면접 내용도 좋았고, 기술 면접을 지원자 중 가장 잘 봤다는 코멘트도 있어 합격을 예상했었는데 불합격을 받게 되었습니다.
저 셋 모두 신입 공고가 아닌 2년차 내지는 경력 무관 공고여서 더 어려웠던 것 같아요

가비아에 이어 계속해서 최종 탈락을 하게 되니 이때가 가장 힘든 시기였습니다.
친구와 나눈 카카오톡 대화인데요, 표현이 다소 직설적이지만 제 마음이 정말 그랬습니다.


이 친구와는 지금도 이런저런 대화를 나누는데, 서로 잘됐으면 좋겠습니다.


하고 싶은 일

2025년 12월 말부터 2026년 1월까지, 무조건 취업을 하고 싶다는 마음에 지원서를 닥치는 대로 넣기 시작했습니다.
처음에는 가고 싶은 회사를 선별하고 나름의 기준을 갖고 지원했었는데, 계속해서 조급해지다 보니 말 그대로 무차별 지원을 하게 됐습니다.
감사하게도 여러 곳에서 면접 제의가 왔고, 정말 많은 면접을 보았습니다.

하지만 면접을 볼수록 제가 생각하고 원하는 환경과는 많이 다르다는 것을 느꼈습니다.
오히려 그럴수록 진짜 하고 싶은 일, 멋있다고 느끼는 일을 해야겠다는 생각이 더욱 강해졌습니다.

그러던 중 NHN AD 인턴과 SI 정규직 중 하나를 선택해야 하는 상황이 찾아왔습니다.
처우만 놓고 보면 SI 정규직이 분명 나은 조건이었지만, 훗날 후회가 남을 것 같다는 생각에 NHN AD를 선택했습니다.
인턴인 만큼 계약 기간이 끝난 후 다시 취업 준비를 해야 할 수도 있지만, 그럼에도 후회 없는 선택을 하고 싶었습니다.


인생은 바키처럼

여러분, 바키라는 만화를 아시나요? 대충 이런 내용의 격투 만화입니다.


최근에 드는 생각인데, 인생도 조금은 바보처럼 살아야 하는 것 같습니다.
현재의 우리는 너무나 많은 정보와 선택지 앞에 놓여 있으며, 그중에서 최고의 선택을 하지 못했음을 자책하고 후회하곤 합니다.
그렇기에 자신의 선택이 최고는 아닐지언정, 최선이었음을 믿고 뚝심 있게 밀고 나가는 자세가 필요한 것 같습니다.

삼전의 자세


마무리하며

2024년 회고에서는 한 해의 총평을 개발자스러운 것들이라고 하였는데, 2025년 회고에서는 다사다난한 성장이라고 하고 싶습니다. 

 

2024년 회고

조금 늦은 2024년 회고설 전까지는 2025 BETA 버전이기 때문에 정식 릴리즈된 지금 시점에 회고를 작성합니다.2024년은 아쉬운 부분도, 만족하는 부분도 공존하지만 나름의 성과를 이뤄냈던 시간인

masiljangajji-coding.tistory.com

2026년에는 또 다른 새로운 경험을 하게 될 텐데, 벌써부터 기대가 됩니다.
읽어주셔서 감사합니다. 

세계 최고의 출판사 길벗

길벗에서 책을 협찬받았습니다.
밥 마틴으로 유명한, 로버트 마틴의 우리, 프로그래머들이라는 책입니다.

 

우리, 프로그래머들 | 로버트 C. 마틴 - 교보문고

우리, 프로그래머들 | 소프트웨어 개발 업계의 선도적 인물인 로버트 C. 마틴이 이야기하는 ‘우리, 프로그래머들’의 이야기생성형 AI가 코드를 만들어 내는 시대, 그럴수록 우리는 자연스럽게

product.kyobobook.co.kr

사실 길벗 하면 개발자분들은 다들 아실 거라 믿습니다.
그런 출판사에서 협찬 제의가 오다니.. 블로그를 3년째 운영 중인데 참뜻깊네요

다 여러분들 덕분입니다.
현재 조회 수가 가장 잘 나오는 글은 '네이버 웹툰 탈락 후기'인데 책을 더 받으려면 또 탈락해야 하나 싶기도 합니다.

 

우리, 프로그래머들

"우리는 왜 존재하는가? 우리는 디테일을 사랑하는 사람"
"우리가 사랑하는 것은 디테일을 하나씩 조합해서 문제를 해결해 나가는 것"

가장 감명 깊게 읽은 구절입니다.
흔히들 개발자라는 직업을 '문제를 해결하는 사람'으로 정의하는 경우가 많은데 '디테일을 사랑하는 사람'과 일맥상통합니다.

개인적으로는 문제를 해결한다는 조금은 딱딱한 말보다, 디테일을 사랑한다는 감성 있는 문구가 더 마음에 듭니다.
앞으로 나도 써먹어야지.

 

AI 시대, 하지만 과거를 말한다

이 책의 표지에는 다음과 같은 글이 적혀있습니다.
"AI 시대에 잊혀가는 '프로그래머 정신'을 다시 깨우다" 문구만 본다면 AI가 책의 주제일 것 같지만, 실제로 펄쳐 읽어보면 이 책의 초점은 다른 곳에 있습니다.

튜링, 폰 노이만, 다익스트라, 앨런 등 다양한 거장들의 일대기와 또 다른 거장인 로버트 마틴 자신의 이야기를 서술하는, 일종의 역사서이자 자전적인 내용을 담은 책입니다.

책은 총 4부로 구성돼있으며 가장 많은 분량을 맡은 부분은 2부(거장)로 책의 절반 이상을 차지합니다.
다음으로 많은 분량을 맡은 부분은 3부(급격한 전환점)로 이 둘의 분량은 전체 내용의 80%가량을 차지합니다.

이렇듯 상당 부분을 컴퓨터라는 도구와 프로그래밍의 발전에 대해서 서술하며, 4부(미래) 부분에 AI가 언급되긴 하지만 책의 중심 주제가 되기에는 턱없이 부족합니다.

이렇게 과거에 집중하는 이유는 무엇일까요?

 

로버트 마틴은 할아버지

앞서 말했듯 이 책은 무식하게 크고 원시적인 컴퓨터에서 현대로 오기까지의 수많은 이야기와 마틴 자신의 경험이 주를 이룹니다.
처음엔 그 방대한 역사적 배경에 압도되기도 했지만, 본문 전체와 집필 후기를 읽으면서 책의 정의를 내릴 수 있었습니다. 

이 책은 할아버지가 말해주는 옛날이야기였던 것입니다.

실제로 로버트 마틴은 1952년 생으로 Elder라는 단어가 어울리는 프로그래밍의 원로이자 할아버지입니다.
그렇기에 지나간 시절에 대한 향수를 가지면서도, 변하지 않는 본질을 전하려고 했던 것 같습니다.

컴퓨터 기술이 원시적이었던 시절부터 현대에 이르기까지, 수많은 거장들이 어떻게든 문제를 해결해나가고 기술을 발전시켜 나간 모습을 보여줌으로써, "아무리 도구가 바뀌어도, 본질적인 것은 남는다"는 메시지를 전하려는 듯이요.

저는 젊은데도 개그콘서트가 그립고 자주 듣는 노래는 다 15년 전 노래거든요.
시간은 지나고 새로운 것은 계속해서 생기지만 그때가 그리운 것은 변하지 않는 본질적인 무언가가 있다는 의미 아닐까요?

 

디테일을 사랑하는 사람들

컴퓨터라는 개념이 없던 시절부터 개발자라는 말이 보편화된 지금까지.
'디테일을 사랑하는 사람들'은 저마다의 지성과 열정을 보여주며 발전해 왔으며, 이제는 AI라는 괴물과의 동행을 생각해야 하는 때가 왔습니다.

AI
의 가파른 발전, 줄어드는 일자리.. 
어쩌면 개발자라는 단어 자체가 사라질 수도 있겠다는 생각도 듭니다.
하지만 
로버트 마틴이 강조한 것처럼, '디테일을 사랑하는 사람'으로서의 정체성은 남을 것 같습니다.

뭐든 하면서 먹고살지 않을까요?, 저는 그랬으면 좋겠는데..

책장에 꽂아두고, 방향을 잃을 때마다 펴 읽어야겠습니다.
할아버지의 옛날이야기 속에는, 변하지 않을 무언가가 담겨있는 것 같으니까요

들어가는 글

프로그래밍 강사로 아이들을 가르친 지도 어느덧 7개월이 됐습니다. 주로 파이썬과 C 언어를 가르치고 있는데, 언어를 다시 설명하는 과정이 의외로 신선하게 느껴집니다. 알고 있다고 생각했던 개념도 아이들에게 풀어서 설명하다 보면, 제대로 이해하고 있었는지 스스로를 점검하게 되는 것 같기도 합니다.

가르치는 직업이란

강사를 하다 보면, 아이들에게 영향력을 전한다는 것이 가치 있는 일이라는 생각을 자주 하게 됩니다. 누군가의 인생에서 갈림길을 함께 선택해 준다는 게 얼마나 큰 책임감을 동반하는지.

제가 처음으로 가르친 학생 중 한 명은 벌써 COSPro 자격증을 취득했고, 소프트웨어 고등학교 진학을 목표로 준비하고 있습니다. 또 어떤 제자는 제 수업을 들은 후 실제로 원하던 소프트웨어 고등학교에 진학하기도 했습니다.

코딩 학원의 특수성

대치동 학원가라고하면, 까다롭고 날 선 학부모님들, 문제푸는 기계 같은 학생들이 떠오릅니다. 그런데 막상 강의를 해보니 아이들은 생각보다 너무나 착하고, 어렵다고 숙제 내주지 말라는 모습들을 보면 한편으론 귀엽게 느껴지기도 합니다. 

아무래도 코딩 학원의 특성상 필수적인 학원이 아니라 정말 코딩을 하고싶은 친구들이 등록을 하기 때문에 더욱 이런 경향이 나타나는 것 같습니다. 국영수 학원은 또 모르겠네요

마무리하며

남을 가르치는 직업은 참 특별한 것 같습니다. 너무나 보람찬 일이고, 제 지식과 기술로 가치를 창출한다는 느낌을 받습니다. 취업을 준비하고 있는 취준생인 저에게 사회적 효용성을 느끼게 하는 이 일은 참으로 사랑스러운 일입니다.

이제는 현업에 들어가서 제가 배워온 지식을 활용하고 싶은 욕구도 있는데요, 부디 그렇게 됐으면 좋겠습니다.
읽어주셔서 감사합니다.

 

아케오 | AI 기반 교육 플랫폼

아이들의 배움을 아케오가 책임집니다

www.akeoedu.com

 

 

 

Is Redis Single-Threaded? How Does It Handle Millions of Requests Per Second?

Yes, Redis is primarily single-threaded, but it can still handle millions of requests per second due to its highly optimized design.

blog.stackademic.com

 

오랜만에 재밌는 글을 가져왔습니다.
Redis는 굉장히 자주 사용되며, 특유의 원자성과 빠른 처리량으로 사랑받는 In-Memory DB 입니다.

원자성을 보장받는 이유는 Singlne Thread이기 때문인데요 "아무리 RAM 에 올라간다 하더라도 어떻게 수백만건의 요청을 처리하지?" 라는 의문을 한번쯤은 가져봤을 것 입니다.

개인적으로 "I/O는 멀티스레드를 써서 빠르다더라" 정도만 알고있었는데요, 이 글이 딥하게 알아볼 수 있는 계기가 되었습니다.

 

글의 내용

위 글에서 말하는 내용은 다음과 같습니다.

  1. Redis는 모든 것을 RAM에 저장하므로 느린 디스크 I/O 를 방지
  2. HashMaps, SortedSets, HyperLogLogs 등 최적화된 데이터 구조 사용
  3. 고성능 I/O Multiplexing 사용
  4. Redis 6.0 이상부터 I/O 처리가 멀티스레드 방식으로 변경

이 글을 읽으면서 햇갈렸던 개념은 I/O Multiplexing 인데요, "성능이 좋아진단건 알겠는데 그래서 그게 뭐지?" 라는 생각이 들어 자세히 찾아봤습니다.

 

Multiplexing (다중화)란 무엇인가?

 

Multiplexing은 간단히 말해 하나를 여러 개처럼 보이게 하는 기법이며, Redis는 버전과 상관없이 항상 I/O Multiplexing 기반으로 동작합니다.
운영체제 관점에서는, 하나의 프로세스가 여러 파일(혹은 소켓)을 동시에 관리할 수 있도록 하는 방법을 의미합니다.

리눅스에서 파일(File)은 프로세스가 커널에 접근할 수 있게 해주는 인터페이스이며, 네트워크 환경에서는 파일 = 소켓으로 대응됩니다. 즉, 하나의 서버가 여러 개의 소켓을 관리하여 동시에 많은 클라이언트를 처리할 수 있는 구조가 됩니다.

또 프로세스가 특정 파일에 접근할 때는 파일 디스크립터(File Descriptor, FD) 라는 정수 값을 사용하는데요
이 FD는 커널 내부의 파일 객체(소켓)를 가리키며, I/O Multiplexing은 FD의 상태 변화(readable/writable)를 효율적으로 감시하는 데 초점이 맞춰져 있습니다.

리눅스에서의 I/O Multiplexing: epoll

 

리눅스는 I/O Multiplexing을 위해 여러 메커니즘을 제공해왔습니다.
초기에는 select, poll을 사용했지만, 이들은 FD 개수에 비례하는 오버헤드가 있어 수천~수만 연결을 처리하기엔 비효율적이었습니다.

이를 해결한 것이 epoll입니다. epoll은 다음과 같이 동작합니다.

  1. 애플리케이션이 epoll_ctl()을 통해 소켓을 등록하고, “이 소켓이 읽기/쓰기 가능해지면 알려달라”고 요청
  2. 이벤트 루프 스레드는 epoll_wait()을 호출해 대기
  3. 커널은 수천 개 소켓 중 실제로 준비된 것만 골라 이벤트 루프에 전달

즉, 준비된 소켓 목록만 반환하기 때문에, Redis는 수만 개 연결 중 필요한 소켓만 빠르게 처리할 수 있습니다.

 

Redis의 이벤트 루프 처리 흐름


Redis는 다음과 같은 순서로 이벤트(사용자 요청)를 처리합니다.

  1. 소켓을 논블로킹 모드로 오픈
    • 일반적으로 소켓은 Blocking 모드
    • 소켓 수신 버퍼에 데이터가 없으면 -> 스레드가 멈춘 채 데이터 도착할 때까지 기다림
    • 송신 버퍼 가득 차면 -> 버퍼 여유 생길 때까지 기다림
    • Non Blocking 모드
    • 소켓에 데이터가 있으면 즉시 읽고 반환, 데이터가 없으면 에러코드 EAGAIN 반환
    • 송신 버퍼에 들어가는 만큼만 쓰고, 가득 차면 에러코드 EAGAIN 반환
    • 즉 준비된 만큼만 처리하고, 나머지는 다음 이벤트에서 처리
  2. I/O Multiplexing 등록
    • Redis 이벤트 루프는 서버 시작 시 OS별로 다중화 방식 선택, Linux(epoll)
    • 논블로킹 소켓의 준비됨(readable/writable) 상태를 커널이 모아 이벤트 루프 스레드에 알려줌
    • 이벤트 루프 = 스레드가 발생한 이벤트를 지속적으로 감지하고, 콜백을 실행하는구조 
    • 소켓에 읽을 게 있으면 REDABLE, 소켓에 쓸 게 있으면 WRITABLE
  3. 이벤트 루프 실행
    • 메인 스레드는 준비된 소켓 목록을 받음
    • 준비된 소켓마다 등록된 콜백을 실행 
      • READABLE - 요청 읽기 + 명령 실행
      • WRITABLE - 응답 전송

이 과정을 통해 단일 스레드 이벤트 루프만으로도 수천~수만 개 동시 연결을 처리할 수 있으며, 락(lock)도 필요하지 않습니다.

6.0 이전에는위 과정 전체(읽기, 명령 실행, 응답 전송)를 메인 이벤트 루프 단일 스레드가 처리했으며
6.0 이후에는 I/O 전용 스레드를 도입하여 read/write 작업을 여러 스레드로 분산 처리를 하도록 변경됐습니다. 
(명령 실행 자체는 여전히 단일 스레드에서 순차적으로 처리됩니다.)

 

Blocking vs Non-blocking 소켓

 

여기까지 정리한 후 한가지 의문이 생겼는데요 
“어차피 epoll이 준비된 소켓만 알려주면, 소켓을 블로킹 모드로 열어도 괜찮지 않을까?” 라는 생각이었습니다.

하지만 준비된 소켓이라고 해서 read/write가 항상 성공하는 것은 아닙니다.
예를 들어, 수신 버퍼 크기보다 더 많은 데이터를 read() 하거나, 송신 버퍼에 여유가 있음에도 write() 하려는 데이터가 너무 많다면, 버퍼가 다시 준비될 때까지 스레드가 블로킹될 수 있습니다.

따라서 소켓은 논블로킹 모드로 열어주는게 적절하겠네요. 

 

 

+ Recent posts