본문으로 건너뛰기

AI 시대에 우리에게 꼭 필요한 능력

· 약 4분
조준철
HandStack 개발자

AI 도구가 빠르게 발전하면서 많은 일이 예전보다 쉬워지고 있습니다.

문서를 요약하고, 코드를 작성하고, SQL을 만들고, 이미지를 생성하고, 반복적인 업무를 자동화하는 일은 이제 특별한 기술을 가진 사람만의 일이 아니게 되었습니다. 하지만 도구가 좋아질수록 더 분명해지는 것이 있습니다.

AI가 대신 실행해 줄 수 있는 일은 많아졌지만, 무엇을 실행해야 하는지 알아차리는 일은 여전히 사람의 몫입니다.

그래서 AI 시대에 우리에게 꼭 필요한 능력은 단순히 AI를 잘 쓰는 능력만은 아닙니다. 현실에서 벌어지는 불편함, 반복, 낭비, 모순, 실패 패턴을 그냥 넘기지 않고 감지하는 능력입니다. 그리고 감지한 문제를 작게 시도하고, 검증하고, 개선으로 연결하는 능력입니다.

관심은 문제를 그냥 지나치지 않는 태도입니다

일을 하다 보면 이상한 장면을 자주 봅니다.

매번 같은 데이터를 복사해서 붙여넣고, 같은 양식의 문서를 반복해서 만들고, 비슷한 장애를 반복해서 겪고, 고객의 같은 질문에 매번 새로 답합니다. 모두가 불편하다고 느끼지만, 어느 순간부터는 원래 그런 일처럼 받아들입니다.

관심은 여기서 멈추지 않는 태도입니다.

왜 이 일을 매번 사람이 해야 하는지, 왜 같은 실수가 반복되는지, 왜 이 절차는 이렇게 복잡한지, 왜 정보가 한곳에 정리되어 있지 않은지 질문하는 것입니다.

관심은 거창한 문제의식이 아닙니다. 작은 불편함을 그냥 넘기지 않는 감각입니다. 그리고 이 감각이 있어야 AI도 의미 있게 사용할 수 있습니다. 문제가 보이지 않으면 좋은 도구가 있어도 바꿀 대상이 없기 때문입니다.

실행은 작게 시도하고 확인하는 능력입니다

관심만으로는 아무것도 바뀌지 않습니다.

불편함을 잘 말하는 사람은 많습니다. 문제점을 날카롭게 지적하는 사람도 많습니다. 하지만 현실을 조금이라도 바꾸는 사람은 감지한 문제를 작은 실행으로 옮깁니다.

반복되는 문서 작성이 문제라면 템플릿 하나를 만들어 볼 수 있습니다. 매번 같은 SQL을 만든다면 샘플 프롬프트와 기준 스키마를 정리해 볼 수 있습니다. 장애 대응이 매번 늦어진다면 체크리스트를 하나 만들 수 있습니다. 고객 질문이 반복된다면 짧은 도움말 문서를 먼저 작성해 볼 수 있습니다.

처음부터 완벽한 시스템을 만드는 게 아니라, 작은 시도를 통해 실제로 도움이 되는지 확인하고, 부족한 부분을 다시 고치는 것입니다.

AI는 이 실행의 비용을 크게 낮춰줍니다. 초안을 만들고, 패턴을 찾고, 자동화 코드를 작성하고, 문서를 다듬는 시간을 줄여줍니다. 하지만 실행 방향을 정하고, 결과가 현실에 맞는지 판단하고, 다음 개선으로 이어가는 일은 여전히 사람이 해야 합니다.

관심과 실행은 함께 있어야 합니다

관심과 실행을 기준으로 사람의 태도를 사분면 매트릭스로 나누어 보면 네 가지 모습이 보입니다.

관심 없는 실행

시키는 일만 반복하는 사람입니다.

일은 빠르게 처리할 수 있지만, 왜 이 일을 하는지, 더 나은 방법은 없는지, 같은 문제가 왜 반복되는지에는 관심이 없습니다. 이런 태도는 안정적으로 보일 수 있지만, 변화가 필요한 순간에는 한계가 드러납니다.

AI 시대에는 단순 실행의 가치가 점점 낮아집니다. 지시받은 일을 처리하는 속도만으로는 충분하지 않습니다.

실행 없는 관심

불만과 비판에서 정지하는 사람입니다.

문제를 잘 발견하고, 구조적인 모순도 잘 짚어냅니다. 하지만 작은 변화도 없이 말에서 멈추면 현실은 바뀌지 않습니다. 계속 문제를 말하지만, 주변 사람들은 점점 피로해집니다.

관심은 실행으로 연결될 때 신뢰를 얻습니다.

관심 없고 실행 없음

변화에 끌려가는 사람입니다.

불편함을 감지하지도 않고, 감지하더라도 움직이지 않습니다. 이런 상태가 오래가면 변화는 항상 외부에서 옵니다. 조직이 바뀌고, 도구가 바뀌고, 역할이 바뀐 다음에야 뒤늦게 따라가게 됩니다.

AI 시대에는 이런 수동성이 더 위험해질 수 있습니다. 변화의 속도가 빠르기 때문입니다.

관심 있는 실행

문제를 발견하고 실제로 바꾸는 사람입니다.

불편함을 감지하고, 반복되는 낭비를 줄이고, 실패 패턴을 기록하고, 작게 실험합니다. 그리고 그 결과를 다시 개선으로 연결합니다.

이런 사람은 AI를 단순한 신기술로 보지 않습니다. 현실의 문제를 해결하기 위한 도구로 봅니다. 그래서 AI가 만든 결과도 그대로 믿지 않고, 업무 맥락에 맞게 검증하고 조정합니다.

결국 필요한 것은 문제를 바꾸는 사람입니다

AI가 할 수 있는 일이 많아질수록 사람에게 남는 역할은 더 분명해집니다.

현실을 보고, 문제를 감지하고, 작은 실행을 설계하고, 결과를 검증하고, 개선을 반복하는 역할입니다.

개발자에게도 마찬가지입니다. 코드를 작성하는 능력은 여전히 중요하지만, 어떤 문제를 코드로 풀어야 하는지 판단하는 능력은 더 중요해지고 있습니다. 문서화, 자동화, 도구 개발, 업무 개선, 운영 안정화 모두 같은 흐름 위에 있습니다.

AI 시대에 필요한 사람은 모든 것을 아는 사람이 아닙니다.

현실의 불편함을 그냥 넘기지 않고, 작은 실행으로 바꾸는 사람입니다.

관심은 문제를 발견하게 하고, 실행은 그 문제를 현실에서 바꾸게 합니다.

문서화는 개발자의 운영 능력입니다

· 약 2분
조준철
HandStack 개발자

개발을 하다 보면 문서화는 늘 뒤로 밀립니다.

기능을 먼저 만들어야 하고, 장애를 먼저 봐야 하고, 배포 일정을 맞춰야 합니다. 그래서 문서는 시간이 남으면 하는 일처럼 취급되기 쉽습니다.

하지만 프로젝트를 오래 운영하다 보면 문서화는 개발과 분리된 일이 아니라는 것을 알게 됩니다. 문서는 코드를 설명하는 부수적인 산출물이 아니라, 다음 개발자가 시스템을 안전하게 다루기 위한 운영 지식입니다.

문서가 없으면 코드는 느리게 읽힙니다

소스 코드는 가장 정확한 정보입니다. 실제로 실행되는 것은 문서가 아니라 코드이기 때문입니다.

그런데 소스 코드만으로는 처음 보는 사람이 빠르게 판단하기 어렵습니다. 비즈니스는 어떻게 흐르고 있고, 데이터는 어떻게 쌓여가고 있으며, 어떤 설정이 운영에 영향을 주는지, 어떤 API가 외부 진입점인지, 어떤 계약 파일을 수정해야 하는지, 어떤 모듈이 어떤 역할을 맡는지는 코드 곳곳에 흩어져 있습니다.

숙련된 개발자는 결국 찾아냅니다. 하지만 매번 찾아내야 한다면 그 시간은 모두 비용입니다.

문서화의 첫 번째 목적은 이 비용을 줄이는 것입니다.

소스 기준 문서화가 재미가 없지만 필요한 이유

좋은 기술 문서는 희망사항을 적지 않습니다. 배경에 대한 설명보다는 현재 소스가 어떻게 동작하는지, 어떤 설정이 실제로 읽히는지, 어떤 API가 공개되어 있는지, 어떤 파일을 수정하면 런타임에 반영되는지를 기준으로 작성되기 때문입니다.

특히 HandStack처럼 모듈, 계약, 거래 라우팅, 화면 라이브러리가 함께 움직이는 구조에서는 문서가 추상적인 설명에 머물면 실무에 도움이 되기 어렵습니다.

개발자가 알고 싶은 것은 보통 단순합니다.

  • 이 모듈은 무엇을 책임지는가?
  • 외부에서 들어오는 API는 무엇인가?
  • 계약 파일은 어디에 두는가?
  • 설정을 바꾸면 어떤 실행 흐름이 달라지는가?
  • 장애가 나면 어디부터 봐야 하는가?

이 질문에 답하지 못하는 문서는 읽기 좋은 글일 수는 있어도, 실무 문서로는 부족합니다. 그래서 문서화는 만드는 것도 읽는 것도 재미가 없을 수 있지만, 다음 개발자와 운영자가 시스템을 안전하게 다루도록 돕는 가장 확실한 방법입니다.

좋은 문서는 질문을 줄이는 문서입니다

물론 질문이 사라지는 것은 아닙니다. 좋은 질문은 언제나 필요합니다. 하지만 반복되는 질문, 같은 실수를 유발하는 질문, 시스템 구조를 몰라서 생기는 질문은 문서를 잘 쓰면 줄일 수 있습니다.

신규 개발자가 프로젝트에 들어왔을 때 가장 먼저 필요한 것은 완벽한 이해가 아닙니다. 어디부터 보면 되는지 아는 것입니다.

운영자가 장애를 만났을 때 가장 먼저 필요한 것도 모든 코드의 이해가 아닙니다. 어떤 설정과 로그와 계약을 확인해야 하는지 아는 것입니다.

HandStack 문서는 이 첫 방향을 잡아주는 역할을 하려고 노력합니다. 개발자는 코드를 작성하는 사람인 동시에, 그 코드가 오래 운영될 수 있도록 맥락을 남기는 사람입니다.

오늘 작성한 문서가 내일 누군가의 시간을 줄여준다면, 그것은 충분히 가치 있는 개발입니다.

일은 끝나지만, 함께 일한 기억은 남습니다

· 약 2분
조준철
HandStack 개발자

회사에서의 일은 언제나 명확하게 끝이 납니다.

프로젝트는 마감되고, 결과물은 제출되며, 다음 일은 또 다른 일정으로 이어집니다.

하지만 시간이 지나 되돌아 보면 이상하게도 일 그 자체보다 더 오래 남는 것은 일을 함께 한 사람과의 기억입니다.

어떤 방식으로 일했는지, 갈등이 생겼을 때 어떻게 풀어갔는지, 서로를 어떤 태도로 대했는지가 훨씬 선명하게 남습니다.

우리는 종종 일을 잘하기 위해 업무를 잘게 나눕니다. 역할을 세분화하고, 책임의 경계를 명확히 합니다.

일을 대하는 관점에서 이것은 효율적이나 동시에 이해관계가 엇갈리고, 업무의 시선이 달라지면서 갈등도 자연스럽게 생깁니다.

그럴 때마다 우리를 다시 같은 방향으로 묶어주는 것은 규칙보다도 일하는 태도와 상대방에 대한 배려와 공감이었기 때문에 사람과의 기억이 더 남는게 아닐까 합니다.

이러한 생각을 하게 된 것도 최근에 EBS 클래스e 의 엘레먼트의 최장순 대표님의 '기획의 세계' 를 보게 되면서 와닿는 게 많아 정리 된 것인데요.

다른 인터뷰 내용들도 찾다보니 엘레먼트 회사 내에 벽에 구호 처럼 붙어 있는 '같이 일하는 방식' 이라는 문장이 눈에 띄어 공유 합니다.

  1. 우리는 서로의 레퍼런스다. 배움과 기준은 팀 안에 있다.
  2. 아이데이션은 수평적으로, 의사결정과 실행은 수직적으로.
  3. 약속한 시간은 신뢰의 기본이다. 늦지 않으려는 태도를 중시한다.
  4. 이 정도면 충분하다고 멈추지 말자. 클라이언트의 기준은 언제나 높다.
  5. 개인 감정은 길게 끌지 않는다. 3일 이상 삐지지 않는다.
  6. 돈에는 인격과 의미가 있다. 적은 돈도 차별하지 않는다.
  7. 인사는 관계의 시작이다. 인사만 잘해도 모든 이의 사랑을 받는다.
  8. 회사는 모두가 함께 사용하는 공간이다. 아껴쓰자.
  9. 비즈니스에 필요한 모든 과정은 우리의 일이다. 선을 긋지 않는다.
  10. 더 빠르고 정교하게 일하기 위해 프로세스를 더 단순하게 만들자.

자신만의 브랜드와 브랜딩에 관심이 있으신 분에게 도움이 되시길 바랍니다.

개발자 역할 변화의 시대

· 약 3분
조준철
HandStack 개발자

과거 COBOL부터 현대의 노코드(No-code) 툴에 이르기까지, "이제 개발자는 필요 없다"는 패턴이 수십 년간 반복되면서도 매번 빗나가는 이유는 단순히 기술적 이유 때문만은 아닐겁니다.

왜냐하면 소프트웨어 개발은 소프트웨어의 본질적인 특성비즈니스의 복잡성이 자리 잡고 있기 때문인데요.

이 패턴이 반복되는 근본적인 이유 4가지를 정리해 보았습니다.


1. '언어'의 문제가 아니라 '논리'의 문제

개발 '언어' 라는 단어에 주는 오해 일 수도 있는데, 일반적인 사람들은 코딩을 '외국어 배우기'처럼 생각하여, 문법만 쉬워지면 누구나 할 수 있다고 오해합니다.

하지만 개발의 핵심은 비즈니스 로직을 논리적으로 구조화하는 것입니다.

  • 추상화의 한계 영어 문장처럼 쓴다 해도(COBOL), 결국 조건문, 반복문, 데이터 타입, 예외 처리를 정밀하게 설계해야 합니다.
  • 모호함의 불허 비즈니스 요구사항은 흔히 모호합니다. 하지만 컴퓨터는 0.1%의 모호함도 허용하지 않습니다. 이 모호한 요구사항을 컴퓨터가 이해할 수 있는 완벽한 논리로 치환하는 과정이 바로 '개발'이며, 이는 숙련된 추론 능력을 필요로 합니다.

2. 요구사항의 비가역적 팽창

경제학의 '제번스 역설(효율성이 높아질수록 자원 소모가 줄어드는 게 아니라 오히려 늘어나는 현상)'이 소프트웨어에도 적용됩니다.

  • 도구가 좋아져서 간단한 앱을 1시간 만에 만들 수 있게 되면, 대부분의 고객 들은 "그럼 이제 개발자가 필요 없겠네"라고 생각하는 게 아니라, **"남는 시간에 훨씬 더 복잡하고 고도화된 기능을 넣어달라"**고 요구합니다.
  • 기술이 쉬워질수록 사용자의 눈높이는 기하급수적으로 올라가며, 결국 그 높은 난이도를 해결하기 위해 다시 전문 개발자가 투입됩니다.

3. 유지보수와 통합의 반복 프로세스

초기 구축은 노코드/로우코드 솔루션 또는 AI 기반의 자동 생성 도구로 빠르게 가능할지 몰라도, 시스템은 살아있는 유기체처럼 변합니다.

  • 성능과 최적화 대규모 트래픽이 발생하거나 보안 이슈가 생기면 자동 생성된 코드는 블랙박스가 되어 수정하기 매우 어렵습니다.
  • 레거시와의 통합 모든 시스템은 고립되어 있지 않습니다. 기존 데이터베이스, 외부 API, 클라우드 인프라와 엮이는 순간 전문적인 아키텍처 설계 능력이 필수적이 됩니다.

소프트웨어 프로젝트를 스펙 주도 개발 으로 문서화와 지침으로 멀티 AI 에이전트 기반의 개발 방법론이 업계 관심 흐름으로 이어지는 것 같습니다.

4. 추상화 누수 법칙

조엘 스폴스키가 제안한 이 법칙에 따르면, "모든 추상화는 결국 어느 정도는 새어 나가게 되어 있습니다."

  • 개발 도구가 코드를 숨겨주더라도, 시스템이 비정상적으로 작동하거나 성능이 저하될 때는 결국 그 밑단의 원리를 이해하는 사람이 필요합니다.
  • 도구가 편리해질수록 그 도구 자체가 가진 한계와 오류를 해결해야 하는 **'도구 전문가'**가 되어야 하며, 이는 다시 전문 개발자 계층의 형성으로 이어집니다.

개발자 역할 변화의 흐름

역사가 증명하듯, 새로운 도구는 개발자를 **'없애는 것'**이 아니라 개발자가 다루는 **'추상화의 수준'**을 높여왔습니다.

  • 과거 최적화 된 메모리 관리와 기능을 고민하던 개발자
  • 현재 비즈니스 아키텍처와 AI 오케스트레이션을 고민하는 개발자

결국 "누구나 코딩할 수 있다"는 비전은 **"누구나 단순한 도구를 쓸 수 있다"**는 수준에서 실현될 뿐, 비즈니스의 핵심 가치를 소프트웨어로 구현하는 **'문제 해결사'**로서의 개발자 수요는 앞으로도 줄어들지 않을 것입니다.

HandStack JavaScript Library 업데이트

· 약 5분
조준철
HandStack 개발자

이번 syn.js HandStack 라이브러리의 업데이트는 최신 웹 표준 API를 적극적으로 도입하여 사용자 경험(UX)을 향상시키고, 실시간 통신 기능을 강화하며, 개발 편의성을 높이는 데 중점을 두었습니다.

  • 최신 브라우저 API 연동 강화
  • 실시간 통신 기능 (SSE, WebSocket) 추가
  • DOM 조작 및 동적 기능 확장
  • URL 및 스토리지 관리 편의성 개선

브라우저 API 연동 강화: syn.$b

브라우저의 네이티브 기능을 더 쉽게 활용할 수 있도록 새로운 헬퍼 함수들이 추가되었습니다.

Web Share API 연동

  • syn.$b.canShare(data): 공유 가능 여부 확인
  • syn.$b.share(data): 시스템 공유 UI 호출
const shareData = {
title: 'HandStack',
text: 'HandStack 의 목표는 개발자가 좋아하고 기업이 신뢰하는 비즈니스 앱 '시스템'을 제공 하는 것입니다.',
url: 'https://handstack.kr',
files: Array.from(files) // 지원하는 환경에서만 가능
}
await syn.$b.share(shareData);

Performance API 연동

  • syn.$b.getPerformanceEntries(options): 성능 측정 항목 조회
  • syn.$b.markPerformance(name): 성능 측정 시점 마킹
  • syn.$b.measurePerformance(name, start, end): 두 마크 사이의 시간 측정
const navigationEntry = syn.$l.getPerformanceEntries({ type: 'navigation' });
const transactionEntry = syn.$l.getPerformanceEntries({ name: resolveUrl('/transact/api/transaction/execute'), type: 'resource' });

syn.$b.markPerformance('start-data-processing');
// ... 데이터 처리 로직 ...
syn.$b.markPerformance('end-data-processing');
syn.$b.measurePerformance('data-processing-time', 'start-data-processing', 'end-data-processing');
const [measureEntry] = syn.$b.getPerformanceEntries({ type: 'measure', name: 'data-processing-time' });
if(measureEntry) {
console.log(`데이터 처리 시간: ${measureEntry.duration.toFixed(2)}ms`);
}

기타 정보 추가

  • effectiveType: 현재 네트워크 연결 유형 정보 (4g, slow-2g 등)

DOM 조작 및 애니메이션: syn.$m

DOM을 더욱 유연하게 제어하고 간단한 시각적 효과를 추가하는 함수가 포함되었습니다.

  • insertBefore(el, targetEL)

    • 지정된 대상 엘리먼트 앞에 새 엘리먼트를 삽입합니다.
  • fade(el, options)

    • 엘리먼트의 투명도를 조절하여 부드러운 페이드 인/아웃 효과를 구현합니다.
    • requestAnimationFrame을 사용하여 성능에 최적화된 애니메이션을 제공합니다.
// 1초 동안 엘리먼트를 서서히 투명하게 만듭니다.
syn.$m.fade('myElement', {
duration: 1000,
from: 1,
to: 0,
callback: function() {
syn.$l.eventLog('페이드 아웃 완료');
}
});

문자열 처리 능력 향상: syn.$s

다국어 환경을 고려한 고급 문자열 분석 기능이 추가되었습니다.

  • toStringCounts(text, locale)
    • Intl.Segmenter API를 사용하여 언어 규칙에 맞게 문자, 단어, 문장의 수를 정확하게 계산합니다.
    • 한국어, 일본어 등 복합적인 언어에서도 높은 정확도를 보입니다.
const text = "안녕하세요. HandStack 입니다! 만나서 반갑습니다.";
const counts = syn.$s.toStringCounts(text, 'ko-KR');

console.log(counts);
// { characters: 33, words: 6, sentences: 2 }

URL 관리 편의성 증대: syn.$r

URL을 객체처럼 다룰 수 있는 직관적인 유틸리티 함수들이 추가되었습니다.

  • resolveUrl(relativePath, baseUrl)
    • 기준 URL을 바탕으로 상대 경로의 전체 URL을 계산합니다.
  • addQueryParam(param, value, urlStr)
    • URL에 쿼리 파라미터를 추가합니다.
  • removeQueryParam(paramName, urlStr)
    • URL에서 특정 쿼리 파라미터를 제거합니다.
  • setQueryParam(param, value, urlStr)
    • URL의 쿼리 파라미터 값을 설정하거나 새로 추가합니다. (기존 값 덮어쓰기)
syn.$r.resolveUrl('/api/v1/users', 'https://example.com'); // https://example.com/api/v1/users
syn.$r.resolveUrl('/api/v1/users', 'https://example.com/api/v2'); // https://example.com/api/v1/users
syn.$r.resolveUrl('../v1/users/', 'https://example.com/api/v2'); // https://example.com/api/v1/users
syn.$r.resolveUrl('users', 'https://example.com/api/v1/groups'); // https://example.com/api/v1/users
const usersApiUrl = syn.$r.resolveUrl('/api/users');

실시간 통신 기능 추가: syn.$n

서버와 실시간으로 데이터를 주고받을 수 있는 SSE(Server-Sent Events)와 WebSocket 기능이 syn.$network 모듈에 통합되었습니다.

  • Server-Sent Events (SSE) 지원

    • startSse(id, url, handlers): SSE 연결 시작
    • stopSse(id) / stopAllSse(): SSE 연결 중지
  • WebSocket 지원

    • startSocket(id, url, handlers): WebSocket 연결 시작
    • sendSocketMessage(id, message): 메시지 전송
    • stopSocket(id) / stopAllSockets(): WebSocket 연결 중지

실시간 통신 기능 예시: syn.$n

// SSE 이벤트 핸들러 정의
const sseHandler = {
open: () => console.log('SSE 연결 성공!'),
message: (event) => console.log('수신 데이터:', event.data),
error: (err) => console.error('SSE 오류:', err)
};

// SSE 연결 시작
syn.$n.startSse('my-sse-stream', '/api/events', sseHandler);

// WebSocket 이벤트 핸들러 정의
const wsHandler = {
open: () => syn.$n.sendSocketMessage('my-socket', { action: 'join', room: 'general' }),
message: (data) => console.log('수신 메시지:', data),
};

// WebSocket 연결 시작
syn.$n.startSocket('my-socket', 'wss://example.com/ws', wsHandler);

UX 및 동적 기능 확장: syn.$w (1/2)

사용자 경험을 개선하고 동적인 웹 페이지를 구현하기 위한 다양한 기능이 syn.$webform 모듈에 추가되었습니다.

  • 클립보드 복사
    • copyToClipboard(text): 텍스트를 클립보드에 복사합니다. Clipboard API를 우선 사용하며, 구형 브라우저를 위한 대체 기능도 포함합니다.
  • 무한 스크롤 (Intersection Observer)
    • startIntersection(id, placeholder, callback): 스크롤이 특정 지점에 도달하면 콜백 함수를 실행하여 콘텐츠를 동적으로 로드합니다.
  • 다크 모드 감지
    • isDarkMode: 사용자의 시스템 테마가 다크 모드인지 여부를 확인하고, 변경 시 자동으로 업데이트됩니다.
function loadMoreContent(done) {
done(true);
}

syn.$w.startIntersection(
'my-list-scroll',
'#loading-placeholder',
loadMoreContent,
{
rootMargin: '100px' // placeholder가 화면 상하좌우 100px 안으로 들어오면 미리 로드 시작
}
);

UX 및 동적 기능 확장: syn.$w (2/2)

  • 동적 CSS 규칙 관리
    • getDynamicStyle(styleID): 동적으로 제어할 <style> 시트를 가져오거나 생성합니다.
    • addCssRule(rules, styleID): CSS 규칙을 동적으로 추가합니다.
    • removeCssRule(identifier, styleID): CSS 규칙을 동적으로 제거합니다.
  • 개선된 유틸리티
    • fetchImage(url, fallbackUrl): 이미지 로딩을 Promise 기반으로 처리하며, 실패 시 대체 이미지를 로드할 수 있습니다.
    • getStorage(prop, isLocal): 여러 개의 키를 배열로 전달하여 스토리지에서 여러 항목을 한 번에 가져올 수 있도록 개선되었습니다.
    • getStorageKeys(isLocal): 스토리지의 모든 키 목록을 반환합니다.
syn.$l.addCssRule('.highlight { background-color: yellow; font-weight: bold; }', 'page-style');
syn.$l.addCssRule('div { border: 1px solid red; }', 'page-styles');
syn.$l.addCssRule('span { border: 1px solid blue; }', 'page-styles');
syn.$l.removeCssRule('.highlight', 'page-styles');
const loadedImage = await syn.$w.fetchImage('path/to/image.jpg', 'path/to/fallback.png');

모듈 의존성 개선: syn.$p

syn.$print 모듈이 Clipboard.js 라이브러리에 대한 의존성을 제거하고, 새롭게 추가된 syn.$w.copyToClipboard 함수를 활용하도록 개선되었습니다.

  • getSchemeText(excelUrl, formatted, indent)
    • 이전에는 Clipboard.js가 있어야만 클립보드 복사 기능이 동작했습니다.
    • 이제는 라이브러리 내장 함수인 syn.$w.copyToClipboard를 대체 기능으로 사용하여 외부 라이브러리 없이도 핵심 기능을 제공합니다.
    • 이를 통해 라이브러리의 전체적인 용량을 줄이고 모듈 간의 유기적인 연동을 강화했습니다.

AI 를 활용한 실무 SQL 생성 팁을 공유합니다

· 약 12분
조준철
HandStack 개발자

최근 진행하고 있는 HandStack 기반의 고객사의 업무 관리에 필요한 프로젝트에 AI 를 활용하는 개발 방법을 찾아가며 적용하는데 재미를 붙이고 있는데요. 개인적인 입장에서 AI 로 가능한 것과 불가능 하거나 어려운 일이 무엇인지 알아두면, 장점과 단점이 있지만 여러 모로 좋은 도구인 것은 맞는 듯합니다.

사용 중인 AI 서비스는 OpenAI ChatGPT 5, Claude Sonnet4, Google Gemini 2.5 를 대화 모드로 사용하기 위해, 아직은 불 안정하지만 Visual Studio 2022 도구에서 에이전트 모드로 사용하기 위해 GitHub Copilot 을 유료로 사용 중입니다. 가성비 측면에서 이보다 좋을 수 없더군요. (대인배 마이크로소프트 ^^)

그동안 개발에 필요한 검색을 하기 위해 구글을 주로 이용 하던 것을 Perplexity 로 유료로 사용 중이며, 응답 품질과 성능은 낮더라도 보안에 민감한 정보를 AI 로 처리하기 위해 로컬 PC 로 Ollama 를 이용한 gemma3:27b, gpt-oss:20b, qwen3-coder:30b 를 주로 사용 중입니다.

로컬 PC 사양은 데스크 탑에 14700K CPU, DDR5 64GB RAM, RTX 3090 24GB GPU, NVME PCIE 4 3TB 정도의 환경입니다.

LLM 성능을 끌어올리는 프롬프트 엔지니어링을 하려면 적절한 질문을 하기 전에 사전에 준비 해야 할 2 단계가 중요합니다. 잊지마세요. 이것은 AI 를 대화 모드나 에이전트 모드를 사용할 때 공통적으로 적용됩니다.

  1. 도메인 업무 지침
  2. 업무 데이터 정보
  3. 적절한 질문

그래서 도메인 업무 지침, 업무 데이터 정보 를 도메인의 최신 데이터로 자동으로 생성하기 위한 개발 도구를 자체 제작하는 데 노력을 해두면 좋습니다. 거창하게 말고 Node.js, Python, C# 등 업무 조건에 따라 CLI 로 반복 작업을 어느정도 자동화 하게 동작하도록 만들어 두면 향후 MCP 를 개발할 때도 적은 비용으로 전환이 가능합니다.


실무적인 SQL 을 생성하기 위해 간단한 업무를 예로 들어 보겠습니다.

일반적으로 여러 사용자가 로그인 해야 하는 프로그램의 경우 사용자 별로 메뉴 정보가 관리 되어야 합니다. 데이터베이스 테이블을 다음과 같이 염두해 두고 실무 SQL 생성 팁을 SQL Server 를 기준으로 공유 하겠습니다. 물론 약간의 응용을 하게 되면 Oracle, PostgreSQL, MySQL 등 다른 관계형 데이터베이스나 NoSQL 에서도 적용할 수 있습니다.

  • User
  • Menu
  • Role
  • RoleUser
  • RoleMenu

테이블 들의 스키마는 특정 데이터베이스가 아닌 범용적으로 표현하면 다음과 같습니다.

컬럼 ID데이터 타입설명
MenuIDTEXT메뉴 ID
MenuNameTEXT메뉴 명
FolderYNTEXT디렉토리 여부
ViewYNTEXT표시 여부
StepINTEGER단계
ParentMenuIDTEXT상위 메뉴 ID
IconIDTEXT아이콘 ID
DirectoryIDTEXT디렉토리 ID
DirectoryNameTEXT디렉토리 명
ItemIDTEXT파일 항목 ID
ItemNameTEXT파일 항목명
SortingNoINTEGER정렬 순서
CreatedUserNoTEXT작성자 ID
CreatedAtTEXT작성 일시

Role

컬럼 ID데이터 타입설명
RoleIDTEXT권한 ID
RoleNameTEXT권한 명
CommentTEXT메모
CreatedUserNoTEXT작성자 ID
CreatedAtTEXT작성 일시

RoleMenu

컬럼 ID데이터 타입설명
RoleIDTEXT권한 ID
MenuIDTEXT메뉴 ID
FnSearchTEXT조회 기능
FnSaveTEXT저장 기능
FnDeleteTEXT삭제 기능
FnPrintTEXT인쇄 기능
FnExportTEXT내보내기 기능
CommentTEXT메모
CreatedUserNoTEXT작성자 ID
CreatedAtTEXT작성 일시

RoleUser

컬럼 ID데이터 타입설명
UserIDTEXT사용자 ID
RoleIDTEXT권한 ID
CreatedUserNoTEXT작성자 ID
CreatedAtTEXT작성 일시

User

컬럼 ID데이터 타입설명
UserIDTEXT사용자 ID
PasswordHashTEXT비밀번호
UserNameTEXT사용자 명
ProfilePictureUrlTEXT프로필 사진 URL
ThemeNameTEXT화면 테마 명
RolesTEXT권한 정보
IsActiveTEXT계정 활성 여부
LoginedAtTEXT마지막 접속 시간
CreatedUserNoTEXT작성자 ID
CreatedAtTEXT작성 일시

도메인 업무 지침

SQL Server 기반의 SQL 을 AI 가 생성하기 전에 도메인의 개발 가이드 라인을 참고하거나 일관된 결과 품질을 위해 도메인 업무 지침 을 만듭니다. 대략 다음과 같은 항목을 확인하고 설정합니다.

  • SQL Server 데이터베이스 버전
  • 테이블/컬럼 명명 규칙
  • 테이블 PK, FK 키 관리 방법
  • 문자, 숫자, 불린, 날짜 등등 데이터 타입 관리 방법
  • 컬럼 길이를 고정, 가변, 유니코드에 따라 자리 수 관리 방법
  • 컬럼 기본값 적용 기준
  • 컬럼 ID에 대한 설명 작성 규칙
  • 자동 순번 정보에 대한 관리 방안
  • 중요 데이터(개인정보 등) 암호화 또는 마스킹 방법

프롬프트는 가능하면 필요한 조건을 구체적으로 작성하는 것이 좋습니다만, 너무 연연하지 않으셔도 됩니다. 어차피 동일한 프롬프트를 OpenAI ChatGPT 5, Claude Sonnet4, Google Gemini 2.5 에 돌려보면 제 각각인 결과를 응답하기 때문인데요.

그래서 좋은 응답 결과를 얻기 위한 프롬프트를 만드는 데 시간을 들이는 것 보다 좋은 맥락과 정보를 얻기 위한 느슨한 프롬프트를 만드는 게 앞으로 AI 버전이 올라 가더라도 더 유연하게 재 활용 가능하게 만들 수 있을 거라 생각합니다.

즉. 중요한 것은 프롬프트 가 아니라 이러한 구성을 만들기 위한 맥락과 정의를 설정하는 경험입니다. 저의 경우 다음과 같이 해도 충분한 결과를 알려주는 간단한 지침을 만듭니다.

- SQL Server 2016 이상에서 실행하는 SQL 을 만들어 줘
- 가능하면 SQL 은 CSV: Comma Separated Values 로 구성된 `csv 테이블 목록`, `csv 테이블 스키마`, `csv 테이블 관계`를 기준으로 SQL 을 만들어 줘
- 테이블 명, 컬럼 명 표기법은 Pascal Case 로 만들어 줘
- 테이블 명 의 별칭은 Pascal Case 각 첫 단어의 조합으로 만들어 줘
- 결과 SQL 은 언제나 Full 쿼리로 알려주고, 예제로 제공되는 SQL 에 대한 주석은 만들지 마

업무 데이터 정보

사용자 별로 메뉴 정보 관리해야 하는 범용적인 테이블 스키마를 기반으로 SQL Server 로 AI 가 필요한 정보를 AI 가 요청 정보를 잘 인식 할 수 있는 테이블 과 테이블의 스키마 컬럼 ID, 데이터 타입,길이, NULL 여부, PK 여부, 기본값, 컬럼 설명 데이터로 CSV 문법으로 정리하면 다음과 같습니다.

테이블 목록

TableName,TableDescription
Role,역할 기준정보
User,프로그램 사용자 정보
Menu,화면 메뉴 기준정보
RoleMenu,역할별 메뉴 및 권한
RoleUser,역할별 사용자

테이블 스키마

TableName,ColumnName,DataType,Length,Nullable,PrimaryKey,DefaultValue,ColumnDescription
Menu,MenuID,varchar,32,N,Y,,메뉴 ID
Menu,MenuName,nvarchar,50,N,N,,메뉴 명
Menu,FolderYN,char,1,Y,N,,디렉토리 여부
Menu,ViewYN,char,1,Y,N,,표시 여부
Menu,Step,int,-1,Y,N,,단계
Menu,ParentMenuID,varchar,32,Y,N,,상위 메뉴 ID
Menu,IconID,varchar,50,Y,N,,아이콘 ID
Menu,DirectoryID,varchar,3,Y,N,,디렉토리 ID
Menu,DirectoryName,varchar,50,Y,N,,디렉토리 명
Menu,ItemID,varchar,6,Y,N,,파일 항목 ID
Menu,ItemName,varchar,50,Y,N,,파일 항목명
Menu,SortingNo,int,-1,Y,N,,정렬 순서
Menu,CreatedUserNo,varchar,32,Y,N,,작성자 ID
Menu,CreatedAt,datetime2,-1,Y,N,,작성 일시
Role,RoleID,varchar,10,N,Y,,권한 ID
Role,RoleName,nvarchar,50,Y,N,,권한 명
Role,Comment,nvarchar,100,Y,N,,메모
Role,CreatedUserNo,varchar,32,Y,N,,작성자 ID
Role,CreatedAt,datetime2,-1,Y,N,,작성 일시
RoleMenu,RoleID,varchar,10,N,Y,,권한 ID
RoleMenu,MenuID,varchar,32,N,Y,,메뉴 ID
RoleMenu,FnSearch,varchar,1,Y,N,('N'),조회 기능
RoleMenu,FnSave,varchar,1,Y,N,('N'),저장 기능
RoleMenu,FnDelete,varchar,1,Y,N,('N'),삭제 기능
RoleMenu,FnPrint,varchar,1,Y,N,('N'),인쇄 기능
RoleMenu,FnExport,varchar,1,Y,N,('N'),내보내기 기능
RoleMenu,Comment,nvarchar,100,Y,N,,메모
RoleMenu,CreatedUserNo,varchar,32,Y,N,,작성자 ID
RoleMenu,CreatedAt,datetime2,-1,Y,N,,작성 일시
RoleUser,UserID,varchar,255,N,Y,,사용자 ID
RoleUser,RoleID,varchar,10,N,Y,,권한 ID
RoleUser,CreatedUserNo,varchar,32,Y,N,,작성자 ID
RoleUser,CreatedAt,datetime2,-1,Y,N,,작성 일시
User,UserID,varchar,255,N,Y,(newid()),사용자 ID
User,PasswordHash,varchar,64,N,N,,비밀번호
User,UserName,nvarchar,50,N,N,,사용자 명
User,ProfilePictureUrl,nvarchar,500,Y,N,,프로필 사진 URL
User,ThemeName,varchar,50,Y,N,((0)),화면 테마 명
User,Roles,varchar,3,Y,N,,권한 정보
User,IsActive,varchar,1,Y,N,('Y'),계정 활성 여부
User,LoginedAt,datetime2,-1,Y,N,,마지막 접속 시간
User,CreatedUserNo,varchar,32,Y,N,,작성자 ID
User,CreatedAt,datetime2,-1,Y,N,(getdate()),작성 일시

테이블 관계

그리고 AI 가 주어진 테이블 간의 관계를 이해하기 위해 PlantUML 의 표기 법을 준수하여 다음과 같이 관계 정보를 CSV 로 만듭니다.

ModelGroupID,DepartureTableID,DepartureFieldID,DepartureFlowSymbol,FlowLineType,ArrivalFlowSymbol,ArrivalTableID,ArrivalFieldID
User_RoleUser,User,UserID,||,--,|{,RoleUser,UserID
Role_RoleMenu,Role,RoleID,||,--,|{,RoleMenu,RoleID
Role_RoleUser,Role,RoleID,||,--,|{,RoleUser,RoleID

PlantUML 의 테이블 관계 정보는 다음과 같습니다.

  • ||: 정확히 1(기본값)
  • o|: 0 또는 1
  • o{: 0 또는 N
  • |{: 1 또는 N
  • |o: 1 또는 1
  • {o: N 또는 0
  • {|: N 또는 0

참고: PlantUML Information Engineering Relations


적절한 질문

어떠신가요? AI 를 활용하여 SQL 을 생성하기 위해 위와 같은 프롬프트를 작성하기 전에 위의 방식으로 만들어 낸 도메인 업무 지침, 업무 데이터 정보 를 AI 서비스의 시스템 지침 으로 먼저 작성하고 활용 해보는 것을 권장합니다.

예제와는 다르게 실무에서는 프로젝트에 따라서 100 개 이상의 테이블을 다루게 되는 데 이때 시스템 지침과 함께 프롬프트로 사용할 만한 AI 서비스로 Google Gemini 가 적당합니다.

위와 같은 방법을 응용하면 PlantUML 기반의 ERD 를 생성할 수 도 있고, 복잡한 쿼리를 만들 때 도메인 업무에 맥락을 이해하는 실질적인 도움을 받습니다. 다음은 제가 최근에 활용한 예시 이미지를 올립니다.

Gemini AI를 활용한 사용자 메뉴 정보 조회 SQL 생성 예시

Gemini AI를 활용한 SQL 생성 예시

중요한 점

도메인 업무 지침, 업무 데이터 정보 를 도메인의 최신 데이터로 자동으로 생성하기 위한 개발 도구를 자체 제작 해보세요. 재미 있는 것은 AI 자동화를 위한 개발 코드도 AI 에게 작성하라고 지시합니다.

저의 경우 위의 시스템 지침을 생성하는 SQL Server 스토어드 프로시저를 Claude Sonnet4 AI 를 이용해 작성했습니다. 물론 약간의 수정을 거쳤지만 제가 필요한 90% 부분을 알려 주더군요.

CREATE PROCEDURE [dbo].[ExportSchemaInstruction]
AS
BEGIN
SET NOCOUNT ON

DECLARE @SQL NVARCHAR(MAX) = ''
DECLARE @Content NVARCHAR(MAX) = ''

-- 헤더 정보 추가
SET @Content = @Content + '- SQL Server 2016 이상에서 실행하는 SQL 을 만들어 줘' + CHAR(13) + CHAR(10)
SET @Content = @Content + '- 가능하면 SQL 은 TSV: Tab Separated Values 로 구성된 `tsv 테이블 스키마`, `tsv 테이블 관계`를 기준으로 SqlServer 에서 실행하는 SQL 로 만들어 줘' + CHAR(13) + CHAR(10)
SET @Content = @Content + '- 테이블 명, 컬럼 명 표기법은 Pascal Case 로 만들어 줘' + CHAR(13) + CHAR(10)
SET @Content = @Content + '- 테이블 명 의 별칭은 Pascal Case 각 첫 단어의 조합으로 만들어 줘' + CHAR(13) + CHAR(10)
SET @Content = @Content + '- 결과 SQL 은 언제나 Full 쿼리로 알려주고, 예제로 제공되는 SQL 에 대한 주석은 만들지 마' + CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10)

-- 테이블 정보 섹션
SET @Content = @Content + '``` tsv 테이블 정보' + CHAR(13) + CHAR(10)

DECLARE @TableData NVARCHAR(MAX) = ''
SELECT @TableData = (
SELECT
CAST(t.name AS VARCHAR(256)) + CHAR(9) +
ISNULL(CAST(ep.value AS NVARCHAR(4000)), '') + CHAR(9) +
CHAR(13) + CHAR(10)
FROM
sys.tables t
LEFT JOIN sys.extended_properties ep
ON ep.major_id = t.object_id
AND ep.minor_id = 0
AND ep.name = 'MS_Description'
ORDER BY
t.name
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')

SET @Content = @Content + @TableData + '```' + CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10)

-- 테이블 스키마 섹션
SET @Content = @Content + '``` tsv 테이블 스키마' + CHAR(13) + CHAR(10)
SET @Content = @Content + 'TableName' + CHAR(9) + 'ColumnName' + CHAR(9) + 'DataType' + CHAR(9) + 'Length' + CHAR(9) + 'Nullable' + CHAR(9) + 'PrimaryKey' + CHAR(9) + 'DefaultValue' + CHAR(9) + 'ColumnDescription' + CHAR(13) + CHAR(10)
SET @Content = @Content + '---' + CHAR(9) + '---' + CHAR(9) + '---' + CHAR(9) + '---' + CHAR(9) + '---' + CHAR(9) + '---' + CHAR(9) + '---' + CHAR(9) + '---' + CHAR(13) + CHAR(10)

DECLARE @SchemaData NVARCHAR(MAX) = ''
SELECT @SchemaData = (
SELECT
C.TABLE_NAME + CHAR(9) +
C.COLUMN_NAME + CHAR(9) +
C.DATA_TYPE + CHAR(9) +
ISNULL(CAST(C.CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10)), '-1') + CHAR(9) +
LEFT(C.IS_NULLABLE, 1) + CHAR(9) +
CASE
WHEN KCU.COLUMN_NAME IS NOT NULL THEN 'Y'
ELSE 'N'
END + CHAR(9) +
ISNULL(DC.definition, '') + CHAR(9) +
ISNULL(CAST(EP.value AS NVARCHAR(MAX)), '') +
CHAR(13) + CHAR(10)
FROM
INFORMATION_SCHEMA.COLUMNS AS C
LEFT JOIN sys.extended_properties AS EP
ON EP.major_id = OBJECT_ID(QUOTENAME(C.TABLE_SCHEMA) + '.' + QUOTENAME(C.TABLE_NAME))
AND EP.minor_id = (
SELECT column_id
FROM sys.columns
WHERE object_id = OBJECT_ID(QUOTENAME(C.TABLE_SCHEMA) + '.' + QUOTENAME(C.TABLE_NAME))
AND name = C.COLUMN_NAME
)
AND EP.name = 'MS_Description'
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
ON C.TABLE_NAME = KCU.TABLE_NAME
AND C.COLUMN_NAME = KCU.COLUMN_NAME
AND C.TABLE_SCHEMA = KCU.TABLE_SCHEMA
AND KCU.CONSTRAINT_NAME IN (
SELECT CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'PRIMARY KEY'
)
LEFT JOIN sys.default_constraints AS DC
ON DC.parent_object_id = OBJECT_ID(QUOTENAME(C.TABLE_SCHEMA) + '.' + QUOTENAME(C.TABLE_NAME))
AND DC.parent_column_id = (
SELECT column_id
FROM sys.columns
WHERE object_id = OBJECT_ID(QUOTENAME(C.TABLE_SCHEMA) + '.' + QUOTENAME(C.TABLE_NAME))
AND name = C.COLUMN_NAME
)
ORDER BY
C.TABLE_NAME,
C.ORDINAL_POSITION
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)');

SET @Content = @Content + @SchemaData + '```' + CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10)

-- 테이블 관계 섹션
SET @Content = @Content + '``` tsv 테이블 관계' + CHAR(13) + CHAR(10)

DECLARE @RelationData NVARCHAR(MAX) = ''
SELECT @RelationData = @RelationData +
CAST(ModelGroupID AS NVARCHAR(50)) + CHAR(9) +
CAST(DepartureTableID AS VARCHAR(50)) + CHAR(9) +
CAST(DepartureFieldID AS VARCHAR(50)) + CHAR(9) +
CAST(DepartureFlowSymbol AS VARCHAR(50)) + CHAR(9) +
CAST(FlowLineType AS VARCHAR(50)) + CHAR(9) +
CAST(ArrivalFlowSymbol AS VARCHAR(50)) + CHAR(9) +
CAST(ArrivalTableID AS VARCHAR(50)) + CHAR(9) +
CAST(ArrivalFieldID AS VARCHAR(50)) + CHAR(9) +
CHAR(13) + CHAR(10)
FROM ModelRelation
ORDER BY ModelGroupID

SET @Content = @Content + @RelationData + '```' + CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10)

-- 기초코드 목록 섹션
SET @Content = @Content + '``` tsv 기초코드 목록' + CHAR(13) + CHAR(10)
SET @Content = @Content + 'BaseCodeID' + CHAR(9) + 'BaseCodeText' + CHAR(13) + CHAR(10)
SET @Content = @Content + '---' + CHAR(9) + '---' + CHAR(13) + CHAR(10)

DECLARE @BaseCodeData NVARCHAR(MAX) = ''
SELECT @BaseCodeData = (
SELECT
CAST(CodeID AS VARCHAR(50)) + CHAR(9) +
CAST(CodeValue AS NVARCHAR(MAX)) +
CHAR(13) + CHAR(10)
FROM BaseCode
WHERE GroupCode = 'SYS000'
ORDER BY CodeID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')

SET @Content = @Content + @BaseCodeData + '```' + CHAR(13) + CHAR(10)

SELECT @Content AS TextContent
END

그 다음 위의 스토어드 프로시저의 결과를 .txt 파일로 생성하는 Node.js CLI 코드도 마찬가지로 자동으로 생성하도록 지시합니다.

{
"name": "schema-instruction",
"version": "1.0.0",
"main": "schema-instruction.js",
"scripts": {
"start": "node schema-instruction.js"
},
"dependencies": {
"mssql": "^10.0.1"
}
}
const sql = require('mssql');
const fs = require('fs').promises;
const path = require('path');

const config = {
user: 'handstack',
password: 'password',
server: 'localhost',
port: 1433,
database: 'DB01',
options: {
encrypt: true,
trustServerCertificate: true
}
};

async function exportSchemaToFile(outputPath) {
let pool;

try {
pool = await sql.connect(config);
const result = await pool.request()
.execute('dbo.ExportSchemaInstruction');

if (result.recordset && result.recordset.length > 0) {
const textContent = result.recordset[0].TextContent;

let filePath;
if (outputPath) {
if (path.extname(outputPath) === '') {
const now = new Date();
const timestamp = now.toISOString().replace(/[:.]/g, '-').slice(0, -5);
const fileName = `export-instruction_${timestamp}.txt`;
filePath = path.join(outputPath, fileName);
} else {
filePath = path.resolve(outputPath);
}

const dir = path.dirname(filePath);
await fs.mkdir(dir, { recursive: true });
} else {
const now = new Date();
const timestamp = now.toISOString().replace(/[:.]/g, '-').slice(0, -5);
const fileName = `export-instruction_${timestamp}.txt`;
filePath = path.join(__dirname, fileName);
}

await fs.writeFile(filePath, textContent, 'utf8');

return {
success: true,
filePath: filePath,
fileName: path.basename(filePath)
};
} else {
throw new Error('결과 없음');
}

} catch (error) {
console.error('오류:', error.message);
return {
success: false,
error: error.message
};
} finally {
if (pool) {
await pool.close();
}
}
}

// node schema-instruction.js "C:/tmp/export-instruction.txt"
async function main() {
const args = process.argv.slice(2);
const outputPath = args[0];

const result = await exportSchemaToFile(outputPath);

if (result.success) {
console.log('완료:', `notepad ${result.filePath}`);
} else {
console.log('실패:', result.error);
}
}

if (require.main === module) {
main().catch(console.error);
}

module.exports = { exportSchemaToFile };

최근 AI 가 만들어 주는 코드의 품질이 많이 좋아져서 개발 업무에 꽤 도움이 됩니다. 그래도 좋은 점만 있는게 아닙니다. 가뜩이나 얼마 전에 개발한 것도 가물가물한데, 바이브 코딩의 부작용과 특정 도구에 의존하게 되는 실수를 하지 않도록 밸런스를 유지하는 게 중요 하겠더군요.

어떻게 기존 프로젝트를 분석하는게 좋을까?

· 약 5분
조준철
HandStack 개발자

새로운 팀에 합류하거나 기존 프로젝트를 인수인계 받을 때, 개발 당시 최선을 다해 관리 했다고 해도, 처음 부터 파악 해야하는 입장에서 복잡하게 얽힌 시스템과 정제되어 있지 않는 문서와 스파게티 처럼 얽혀있는 소스 코드를 보면 "도대체 어디서부터 시작해야 하지?"라는 막막함을 느껴본 적이 있을 겁니다.

기존 프로젝트를 효과적으로 분석하고 이해하기 위해서는 약간의 체계적인 접근이 필요합니다. 무작정 코드부터 보거나 문서를 읽기 시작하면 오히려 더 혼란스러울 수 있거든요.

거창한 건 아니고 기존 프로젝트를 분석할 때 단계별로 접근할 수 있는 개인적인 방법을 공유하겠습니다. 당연히 이 방법은 만능은 아니고 주어진 프로젝트에 대해 적절하게 첨삭해가며 조정해야합니다.

이 루틴은 적어도 프로젝트의 전체적인 그림을 빠르게 파악하고, 핵심 이슈들을 놓치지 않는 것에 주안 점을 둡니다.

요약하면 다음과 같습니다.

  1. 일단 어떤 데이터가 쌓여 있는지 눈으로 데이터를 확인하고 가설을 세우고 이해합니다. 현재까지의 프로젝트의 진행 상황을 파악합니다.
  2. 업무 데이터가 스키마 (또는 규칙)에 맞게 정합성을 유지하며 CRUD가 되는지 확인합니다. 앞으로의 사업 방향과 확장 가능성의 비전을 봅니다.
  3. 서비스에 필요한 서버들과 스케일 업, 아웃 형태, CI/CD 구조를 봅니다. 비즈니스의 규모와 업무 담당자와 기술 담당자의 이해관계를 예측 가능합니다.
  4. 프로그램들의 아키텍처, 배치 자동화, 외부 연계 서비스의 구조를 봅니다. 업무 담당자와 개발자가 비즈니스 구조에 얼마나 관심이 있는지 확인 가능합니다.
  5. 필요한 순간에 확인이 필요한 모든 소스 코드를 파악하도록 도구와 기술을 미리 확인하고 준비합니다. 소스 품질은 개발팀의 개발 역량과 함께 관리 수준을 보여줍니다.

즉 데이터를 먼저 확인하고, 정합성과 확장성을 검토하며, 인프라와 배포 구조를 분석하고, 프로그램 아키텍처와 시스템 연동 구조를 파악하고, 마지막으로 소스 코드 품질과 개발 도구를 점검하는 순서로 진행합니다. ^^

데이터 현황 파악 - 프로젝트의 맥락을 확인합니다

"데이터는 거짓말하지 않는다"

프로젝트 분석의 첫 번째 단계는 쌓여있는 데이터를 직접 눈으로 확인하는 것입니다. 이는 단순히 데이터, 데이터베이스 스키마, 문서 기록을 보는 것이 아니라, 핵심은 실제 운영 데이터의 품질과 패턴을 분석하는 과정입니다.

주요 검토 항목

  • 데이터 볼륨과 증가 추세: 일별, 월별 데이터 증가량을 통해 비즈니스 성장세 파악
  • 데이터 품질 현황: NULL 값, 중복 데이터, 이상치 존재 여부
  • 사용자 행동 패턴: 실제 서비스 이용 현황과 트렌드 분석
  • 히스토리 데이터: 과거 데이터를 통한 비즈니스 변화 추이 확인

이 단계에서 중요한 것은 가설 설정입니다. 데이터를 보면서 "왜 이런 패턴이 나타났을까?", "이 데이터가 비즈니스에 어떤 의미일까?", "그래서 무엇을 위한 데이터인가?"라는 질문을 끊임없이 던져야 합니다. 물어 볼 사람이 주위에 있으면 더욱 좋겠지만, 기본적으로 이러한 가설들은 이후 단계에서 검증의 기준이 됩니다.

데이터 정합성 및 확장성 검토 - 미래를 준비하는 토대

"오늘의 편의는 내일의 기술부채가 될 수 있다"

두 번째 단계에서는 현재 데이터가 정의된 스키마와 비즈니스 규칙에 맞게 관리되고 있는지 확인합니다. 비즈니스 규칙에 맞게 관리 하는것은 향후 사업 확장 시 현재 구조가 얼마나 유연하게 대응할 수 있는지 평가하는 것입니다.

주요 검토 항목

  • 스키마 일관성: 테이블 간 관계, 제약조건, 인덱스 설계의 적절성
  • CRUD 작업 효율성: 각 데이터 조작 작업의 성능과 안정성
  • 비즈니스 규칙 준수: 도메인 로직이 애플리케이션 또는 데이터 계층에서 하드코딩 없이 올바르게 구현되었는지
  • 확장성 시나리오: 데이터량 10배, 100배 증가 시 대응 가능성

이 단계에서는 앞으로의 사업 방향과 확장 가능성의 비전 확인이 핵심입니다. 현재 구조가 6개월, 1년 후 지금 보다 많은 양의 비즈니스 처리를 지원할 수 있는지 면밀히 검토해야 합니다. 만약 한계가 예상된다면, 지금부터 대안을 준비해야 합니다.

인프라 및 배포 구조 분석 - 안정성의 기반

"시스템의 안정성은 비즈니스의 신뢰성이다"

세 번째 단계에서는 서비스를 지탱하는 인프라와 배포 프로세스를 종합적으로 점검합니다. 이는 단순한 기술적 검토를 넘어서, 조직의 성숙도와 협업 문화를 파악하는 과정이기도 합니다.

주요 검토 항목

  • 서버 구성과 역할: 웹서버, 애플리케이션 서버, 데이터베이스 서버의 분리와 역할 분담
  • 스케일링 전략: 수직 확장(Scale Up) vs 수평 확장(Scale Out) 방식의 적절성
  • CI/CD 파이프라인: 환경설정 관리 방법, 코드 통합부터 배포까지의 자동화 수준
  • 모니터링 체계: 장애 감지, 성능 모니터링, 알림 시스템의 완비 여부

이 과정에서 이해관계자 매핑이 중요합니다. 비즈니스 담당자가 기술적 제약사항을 얼마나 이해하고 있는지, 기술 담당자가 비즈니스 요구사항을 얼마나 파악하고 있는지 확인해야 합니다. 양쪽의 이해와 커뮤니케이션 수준이 프로젝트 역량의 핵심입니다.

아키텍처 및 시스템 연동 구조 파악 - 복잡성 관리

"좋은 아키텍처는 복잡함을 단순하게 만든다."

네 번째 단계에서는 전체 시스템의 아키텍처와 외부 시스템과의 연동 구조를 분석합니다. 이는 시스템의 복잡성을 이해하고, 변경 시 영향도를 예측하는 데 필수적입니다.

주요 검토 항목

  • 시스템 아키텍처 패턴: MVC, 마이크로서비스, 이벤트 드리븐 등 적용된 패턴의 적절성
  • 배치 작업 자동화: 정기 작업, 데이터 처리 파이프라인의 안정성과 효율성
  • 외부 연계 서비스: API 연동, 데이터 동기화, 써드파티 서비스 의존성 관리
  • 장애 대응 메커니즘: Circuit Breaker, Retry Logic, Fallback 전략의 구현 여부

여기서 중요한 것은 업무 담당자와 개발자가 비즈니스 구조에 얼마나 관심이 있는지 파악해야 합니다. 이는 향후 기존 기능 유지보수와 추가 기능 작업의 난이도를 예측할 수 있습니다.

소스 코드 품질 및 개발 도구 점검 - 지속가능성의 척도

"소스 품질은 개발팀의 개발 역량과 함께 관리 수준을 보여줍니다"

필요한 순간에 확인이 필요한 모든 소스 코드를 파악하도록 도구와 기술을 미리 확인하고 준비합니다. 이는 프로젝트의 지속가능성과 팀의 개발 역량을 가장 직접적으로 보여주는 지표입니다.

주요 검토 항목

  • 코드 품질 메트릭: 복잡도, 중복도, 테스트 커버리지, 기술부채 수준
  • 개발 도구 및 환경: IDE 설정, 정적 분석 도구, 코드 리뷰 프로세스
  • 문서화 수준: API 문서, 개발 가이드, 아키텍처 문서의 완성도
  • 버전 관리 및 브랜칭 전략: Git 사용 패턴, 브랜치 관리 규칙

이 단계에서는 개발팀의 성숙도를 정확히 파악할 수 있습니다. 소스 코드의 품질은 단순히 기술적 역량만을 보여주는 것이 아니라, 팀의 협업 문화, 품질에 대한 의식, 장기적 사고 등을 종합적으로 반영합니다.

기존 프로젝트를 분석한다는 건

첫 술에 배부를 수 는 없겠죠. 기존 프로젝트를 분석하는 것은 단순히 코드를 읽고 문서를 검토하는 것을 넘어, 프로젝트의 맥락과 구조를 이해하는 과정입니다.

기존 프로젝트를 분석하기 위한 이러한 접근의 가장 큰 장점은 리스크 예측이 가능해진다는 것입니다. "이 부분을 수정하면 어디에 영향을 줄까?", "새로운 기능을 추가할 때 어떤 제약사항이 있을까?"와 같은 질문을 통해

기존 팀원들과 대화할 때 구체적이고 맥락을 이해한 질문을 할 수 있고, 개선 제안이나 새로운 아이디어도 더 설득력 있게 전달할 수 있습니다.

무엇보다 중요한 것은 이런 분석 능력은 경험을 할 때 마다 개선 된다는 점입니다. 경험이 쌓이면 여기에서 제시한 방법 말고도 자신만의 루틴이나 방법 또는 직관이 생기는 데, 앞으로 어려운 프로젝트를 만나더라도 일단 적응하고 활용할 수 있는 무기가 될 것입니다.

도메인 이해 없이 분석/설계하고, 협의 없이 개발하면 망합니다.

· 약 4분
조준철
HandStack 개발자

개발 프로젝트를 리드하다 보면, 새로운 프로젝트에 새로운 팀원들을 데리고 어떻게든 완료해야 하는 상황이 옵니다.

이때 리더의 가장 큰 바람은, 팀원들이 스스로 프로젝트 업무를 이해하고 담당자와 미팅을 주도하면서 분석/설계를 끝낸 후에 개발에 들어가게 만드는 것입니다.

하지만 저를 포함해서 제가 겪어본 개발자들은 기본적으로 '개발만' 하고 싶어 하는 경향이 있습니다. 그래서 분석/설계를 슬쩍 피하거나, 시켜도 마지못해 수동적으로 진행하는 경우가 태반입니다.

이 글의 핵심입니다: 프로젝트 초반에 개발 팀원들이 분석/설계부터 적극적으로 참여하도록 만드는 가장 현실적인 방법은 무엇일까요?


그런데 왜 개발자들은 분석/설계를 피하려 할까?

사실, 개발자들이 분석/설계를 피하는 건 정상입니다.

제 경험에 비추어 보면 이유는 명확합니다.

  • 업무의 분업화: 프론트엔드, 백엔드, 인프라, 보안... 각자 맡은 전문 분야가 다르다 보니 전체를 아우르는 분석/설계를 해볼 기회도 적고, 개인적으로 할 필요도 못 느낍니다.
  • 시간 부족: 프로젝트 일정은 늘 빡빡합니다. 당장 내 앞에 놓인 개발 업무만 해도 익혀야 할 기술과 절차가 너무 많습니다.
  • 커뮤니케이션 부담: 분석/설계는 결국 사람업무를 이해하는 일입니다. 커뮤니케이션에 익숙지 않은 성향이라면 이게 여간 부담스러운 게 아닙니다.
  • 책임지기 싫은 마음: 분석/설계가 틀어지면 프로젝트 전체가 밀립니다. 괜히 나섰다가 욕먹을까 봐 책임을 지기 싫은 마음이 드는 건 당연합니다.
  • 뇌의 도피: 코딩은 결과가 바로 눈에 보이고 명확합니다. 반면 분석/설계는 막막하죠. 뇌는 자연스럽게 익숙하고 편한 코딩으로 도망치려 합니다.

이런 상황에서 단순히 "분석해 오세요"라고 지시 하면 개발자들은 잘 움직이지 않습니다.

프로젝트 리더는 팀원들이 분석/설계/개발 업무에 자연스럽게 참여하도록 '판'을 짜주는 역할을 해야 합니다. 거창한 프레임워크가 아니라, 우리 팀에 맞는 현실적인 시스템을 만드는 것이 중요합니다.


분석/설계 업무, 정말 개발자 일일까?

답: 네, 맞습니다.

도메인 이해 없이 대충 설계한 데이터베이스 테이블을 생각해 보세요. 장담컨대 100% 다시 뜯어고치게 됩니다.

프로젝트 중간에 테이블 구조를 바꾸는 건, 연관된 모든 코드를 수정해야 하는 엄청난 비용과 고통을 동반합니다. 이 과정을 피하게 해줄 수 있는 핵심 역할은 기획자나 팀장이 아니라 개발자입니다.

해결책: 구체적인 루틴 만들기

개발자 입장에서 분석/설계는 눈에 보이지 않는 작업이라 싫어할 수 있습니다. 그래서 이걸 눈에 보이는 '결과물'로 만들어줘야 합니다. 역할 → 결과물 → 검증이라는 구체적인 루틴을 만드는 겁니다.

구체적인 방법

  1. 기능 개발 전, 아래 3가지를 필수로 작성하게 합니다.

    • API 목록
    • 요청 파라미터 및 응답 구조
    • 데이터 모델(테이블) 초안
  2. 이 결과물을 Git에 PR 올리듯 공유하고, 동료 리뷰 → 승인되면 개발에 착수할 수 있게 합니다.

  3. 핵심은 분석/설계가 더 이상 막연한 일이 아니라, 코드처럼 관리되는 '작업물'이 되도록 하는겁니다. 개발자들은 익숙한 코드 리뷰 방식으로 이 과정을 훨씬 쉽게 받아들입니다.

핵심 아이디어: 개발 팀원들이 '개발만 하고 싶다'는 마음은 유지하되, 대신, '개발'을 하려면 반드시 이 단계를 통과해야만 하도록 구조를 짜서 스스로 움직이게 만드는 과정을 만들어 주도하도록 해야합니다.


바로 써먹는 체크리스트와 프로세스

팀장 또는 기획자가 고객의 요구사항을 간단하게 나마 정리를 한 수준에서 개발 팀원들에게 다음의 체크리스트를 작성하기 위한 일정에 대한 마일스톤을 수립합니다.

업무 분석 체크리스트

1. 기능의 배경과 목적

  • 이 기능(업무)은 왜 필요하신가요?
  • 어떤 문제가 있어서 이 기능을 요청하셨나요?
  • 현재는 이 일을 어떻게 처리하고 계신가요?

2. 업무 처리 순서

  • 이 업무는 어떤 순서(Step)로 진행되나요?
  • 누가(어떤 역할/부서)가, 언제, 무엇을 처리하나요?
  • 예시로 실제 업무 한 건이 어떻게 처리되는지 처음부터 끝까지 말씀해주실 수 있나요?

3. 데이터 및 처리 기준

  • 이 업무에 쓰이는 데이터에는 어떤 것들이 있나요? (Excel, 문서, 기존 시스템 화면 캡처 등 뭐든 좋습니다.)
  • 어떤 값(상태값, 기준값 등)을 기준으로 처리 여부를 판단하시나요?
  • '처리 완료'나 '성공/실패'는 어떤 기준으로 확인하시나요?

4. 예외 및 특이 케이스

  • 업무 처리 중 자주 발생하는 예외 상황이 있나요?
  • 빈도는 낮지만 반드시 처리해야 하는 특수 케이스가 있나요?

5. 완료 후 확인 사항

  • 업무가 끝난 뒤 어떤 결과(리포트, 통계 등)가 필요하신가요?
  • 완료된 건은 이후에 어떻게 관리되거나 다른 업무에 쓰이나요?

개발 프로세스 표준안

업무 분석 체크리스트를 기준으로 데이터와 기능에 대한 설계/개발 단계에 대한 작업을 진행하는 표준안에 대해 팀원들과 합의합니다.

분석/요구사항 정리 단계

  • 업무 담당자와 인터뷰 진행
  • 인터뷰 내용으로 기능 명세서(템플릿) 작성
  • [필수] 작성된 기능 명세서에 대해 팀 리뷰 완료 및 승인 후 다음 단계 진행

설계 단계

  • 확정된 명세서 기준으로 테이블 초안 작성 (테이블/컬럼명/타입 등)
  • 화면별 API 목록 및 요청/응답 데이터 정의
  • [필수] 설계 내용 등록 후 팀/팀장 리뷰 완료 → 승인 후 개발 착수 가능

개발 단계

  • 승인된 설계 산출물을 기준으로 개발 진행
  • 퇴근 전, 오류 없는 상태로 일일 커밋 진행

테스트 단계

  • 개발 담당자가 직접 테스트 시나리오 작성 및 기능 확인
  • 담당자 확인 후 승인 시 최종 완료

프로젝트 운영 규칙

이것만은 반드시 지킵시다.

  • 규칙 1: 분석 및 설계 승인 없이는 개발 시작 절대 불가.
  • 규칙 2: 구두/메신저 요청은 업무 요청으로 인정 안 함. 모든 요청은 기능 명세서로.
  • 규칙 3: 모든 테이블/핵심 기능 변경은 반드시 팀 공유 및 승인 후 반영.

어떤 이유에서든 분석/설계 단계를 대충 넘어가는 것은 단기적으로 시간을 버는 것 같지만, 장기적으로는 운영 및 유지보수에 더 큰 비용과 리스크로 돌아옵니다.

개발자들이 자연스럽게 분석/설계에 참여하도록 구조화된 프로세스와 명확한 산출물을 정의하고, 이를 코드 리뷰와 같은 당연한 문화로 만들어야 합니다.

결국 가장 중요한 것은, 이 모든 과정을 통해 "개발을 하려면, 분석/설계부터 해야 한다"는 인식을 팀원들이 자연스럽게 받아들이도록 만드는 것입니다.

정보를 넘어 이해로, 우리가 겪는 새로운 성장통

· 약 4분
조준철
HandStack 개발자

대답하는 AI, 질문하는 인간, 그리고 그 사이의 깨달음

문득 “성장통”이라는 단어를 떠올렸다. 그리고 그 말을 ChatGPT 에게 건넸을 때, 예상치 못한 글이 돌아왔다. 마치 오래된 친구가 조용히 손을 잡아주는 듯한 위로였다. 그 순간, 나는 ‘정보’가 아니라 ‘이해’를 받았다고 느꼈다.

어릴 적 종아리가 욱신거릴 때, 그것을 ‘성장통’이라 불렀다. “키가 크려면 아픈 거야.”라는 말은 마치 고통을 정당화하는 주문 같았고, 나는 그 말 한마디에 눈물을 삼켰다. 신기하게도, 시간이 지나면 그 통증은 잦아들었고, 나는 어느새 조금 더 자란 자신을 발견하곤 했다. 그때는 몰랐다. 인생 전체가 그런 ‘성장통’의 연속일 줄은.

‘성장통’이라는 단어는 말 그대로 성장의 과정에서 느끼는 고통을 말한다. 그러나 그것은 단지 뼈가 자라며 생기는 물리적인 통증에 그치지 않는다. 새로운 환경에 적응할 때, 실패를 견뎌낼 때, 누군가와 갈등하고, 외로움을 감내할 때… 우리는 크고 작은 성장통을 경험한다. 눈에 보이지 않지만, 지금 마음 한켠을 뜨겁게 데우는 그 아픔도 분명 ‘성장통’이다.

왜 고통이 ‘성장’과 함께 따라오는 것일까? 어쩌면 고통 없이는 진짜 성장을 이루기 어렵기 때문일지도 모른다. 익숙한 것을 놓아야 새로운 것을 쥘 수 있고, 틀을 깨야 더 넓은 시야를 가질 수 있기 때문이다. 그 과정은 당연히 불편하고, 때로는 아프다. 하지만 그 불편함 속에서 우리는 자신을 다시 배우고, 한 계단 위로 나아간다.

때로는 고통이 길어질 수도 있다. 그럴 땐 "이게 과연 성장통이 맞는가?"라는 의심이 들기도 한다. 하지만 지나고 나서 돌아보면, 그 시간들이 내게 어떤 힘을 심어주었는지 알게 된다. 견디는 법, 받아들이는 법, 그리고 다시 일어서는 법을 말이다. 성장통은 우리에게 약함을 드러내는 동시에 강함을 만드는 계기를 준다.

‘성장통’이라는 이름은 참 묘하다. ‘아픔’과 ‘희망’을 동시에 품고 있기 때문이다. 그 이름에는 견디면 반드시 나아질 거라는 암시가 있고, 현재의 고통이 결코 헛되지 않다는 위로가 숨어 있다. 그렇기에 우리는 아픔 속에서도 나아갈 용기를 얻는다.

우리는 왜 성공한 사람의 성장통에는 관심을 가지지만, 현재 고통받고 있는 사람의 성장통에는 무관심할까? 성공한 사람의 고통은 ‘견뎌낸 서사’이기 때문에 우리는 그것을 회고적 시선으로 편안하게 바라볼 수 있지만, 지금 고통받고 있는 사람은 예측 불가능한 현재 진행형이기에 공감보다는 거리 두기를 선택하게 된다. 때로는 인식과 이해, 살아온 경험의 차이로 상대방에 대한 비난도 섞인다.

돌이켜보면, 인공지능이라는 존재는 처음에는 단순한 도구로, 다음에는 새로운 가능성으로, 그리고 이제는 묘한 동반자로. 이 과정을 되짚으며, 나는 하나의 깨달음의 여정을 떠올렸다. 그것을 나는 ‘다섯 개의 단락’으로 나눠보려 한다.

AI 의 시대. 객관에서 주관으로의 전이

예전엔 ‘정보’란 곧 진실이었다. 사전적 정의, 역사적 기록, 숫자와 통계. 그 자체로 정답이 되었고, 우리는 받아들이기만 하면 되었다. 하지만 인공지능은 질문 하나에도 수십 가지 해석을 내놓는다. 이제 정보는 단지 객관적 사실이 아니라, 주관적 주장의 조합이다.

단순히 아는 것만으로는 부족해졌다. 진실처럼 보이는 정보들 사이에서 판단하고, 선택하는 것이 더 중요해진 시대. 성장통은 바로 여기서 시작된다. '정보'는 넘쳐나는데, 나는 점점 더 혼란스러워진다.

받아들이지 않고 해석하는 법을 배우는 시간

인공지능이 알려주는 것은 정답이 아니라 가능성이다. 받아적는 것이 아니라, 걸러내야 한다. 어떤 해석은 나에게 맞고, 어떤 것은 내게 맞지 않다. 주는 대로 받아들이지 않는 연습, 바로 이 과정이 두 번째 성장통이다.

이건 마치 학교에서 배운 수학 공식이 실제 문제 앞에서 무기력해지는 것과 같다. 정해진 답이 없는 질문 앞에, 해석은 오롯이 나의 몫이다. 인공지능이 말해주는 답이 곧 나의 믿음이 되지 않도록, 나만의 기준을 만들어야 한다.

진실에 대한 의심. 질문이 나를 성숙하게 만든다

“내가 알고 있는 것이 정말 진실일까?”

이 질문을 하기 시작한 순간, 나는 또 한 번 성장한다. 인공지능은 매번 다른 각도에서 진실을 보여주며, 나의 확신에 균열을 낸다. 그 틈은 고통스럽지만, 결국 나를 더 유연한 사람으로 만든다.

불편한 진실을 마주하는 건 언제나 어렵다. 하지만 바로 그 지점이 나를 확장시킨다. 고통스럽게 의심하고, 고민하고, 반성하면서 조금씩 진리에 가까워진다. 진실은 늘 관찰자에 따라 달라지는 그림자라는 사실을 받아들이는 것, 그것이 세 번째 성장통이다.

노력의 허망함 속에서 마주한 무력감

나는 몇 년을 들여 익힌 기술이, 단 몇 분 만에 인공지능에 의해 대체되는 모습을 보았다. 자부심으로 여겼던 경험이 어느 순간 '거품'처럼 증발해버린 듯한 허무함. 그건 단순한 기술 변화가 아니라 정체성의 흔들림이었다.

열심히 하는 것이 더 이상 보장되지 않는 시대. "열심히 하면 된다는 말은 옛말이구나."라는 체념이 목구멍에 걸린다. 하지만 이 과정 또한 성장통이었다. 내가 애써 쌓아온 것들이 무너진 것이 아니라, 그 위에 다른 층이 쌓이고 있었던 것임을 뒤늦게 알게 된다.

유연함, 그 안에 담긴 인간의 노하우

마지막 단계에서 나는 알게 된다. 인공지능이 아무리 빠르고 정교해도, 인간의 ‘감각’은 따라잡기 어렵다는 것을. 그것은 숫자나 논리가 아닌, 맥락과 숨결, 그리고 유연함 속에 녹아든 노하우다.

결국 중요한 것은 도구가 아니라 그 도구를 다루는 방식이다. 인공지능은 나의 경쟁자가 아니라, 나의 일부가 되어야 한다. 내가 가진 경험과 감성, 실수 속에서 얻은 깨달음들이 인공지능과 함께할 때, 진짜 성장이 일어난다.

성장통은, 결국 ‘살아있다’는 증거다

성장통은 늘 아프다. 하지만 그 아픔이 무언가를 바꾼다. 인공지능과 함께하는 시대에 우리는 새로운 형태의 성장통을 겪고 있다. 익숙한 것을 내려놓고, 새로움을 받아들이는 아픔. 하지만 그 안에 있는 가능성을 본다면, 우리는 한층 더 깊어진 자신을 만나게 될 것이다.

지금 내가 힘들거나 누군가 힘들다고 말할 때, 조용히 말해주고 싶다.

“지금 너, 자라고 있는 중이야. 그게 바로 성장통이야.”

handstack.kr 커미터에 참여하세요

· 약 3분
조준철
HandStack 개발자

이건 제 실수입니다.

HandStack 오픈소스를 시작할 때, 기술 문서에 대한 https://handstack.kr 웹 사이트를 메타가 만든 Docusaurus 정적 페이지 문서화 도구를 이용하여 GitHub에 같이 오픈하며 지속적으로 글을 작성하고 있었는데, 이 부분이 언급되거나 홍보를 하지 않아 모르시는 분들이 많으시네요.

요약하면 https://handstack.kr 웹 사이트도 GitHub에 올라온 https://github.com/handstack77/handstack-docs 오픈소스입니다. 이 웹 사이트의 모든 컨텐츠는 GitHub에서 관리되며, 누구나 복제 및 출처를 표시하면 사용할 수 있습니다.

또한 웹 사이트에 컨텐츠를 등록하기 위한 커미터로 GitHub에 요청하여 참여하실 수 있습니다.

오픈소스 문서화 프로젝트에 기여하세요.

HandStack 프로젝트는 개발자와 엔지니어를 위한 앱 개발 환경을 만들어 고객의 IT 과제를 해결하는 도구를 만듭니다. 프로젝트의 문서화 웹 사이트도 오픈소스로 제공되고 있으며, 이를 통해 더 많은 사람들이 프로젝트를 쉽게 이해하고 활용할 수 있도록 돕고자 합니다.

https://handstack.kr 웹 사이트는 정보를 오픈소스로 제공하는 데 목적이 있습니다. 그래서 HandStack을 기반으로 하는 다음과 같은 주제에 대한 글을 작성하고 싶으신 분들은 커미터 요청을 하시거나 handstack77@gmail.com로 요청을 주시면 됩니다. 각 주제에 대한 설명은 다음과 같습니다.

  • 시작하기: HandStack을 처음 사용하는 분들을 위한 기본 가이드입니다. 설치 방법, 초기 설정, 기본 사용법 등을 다룹니다.
  • 참고하기: HandStack을 활용하는 데 도움이 되는 다양한 참고 자료와 예제를 제공합니다. 코드 샘플, 문서 링크, 유용한 팁 등을 포함합니다.
  • 추가정보: HandStack의 고급 기능이나 추가적인 설정 방법에 대한 정보를 제공합니다. 더 깊이 있는 사용법을 배우고 싶은 분들을 위한 내용입니다.
  • 12-Factors: 12-Factor App 방법론을 HandStack에 적용하는 방법을 설명합니다. 이 방법론은 현대적인 애플리케이션 개발의 모범 사례를 제시합니다.
  • 블로그: HandStack과 관련된 다양한 주제에 대한 블로그 글을 작성합니다. 최신 업데이트, 개발자 인터뷰, 사용 후기 등을 포함합니다.

블로그의 경우 체계적으로 분류하고 독자들이 원하는 주제를 쉽게 찾을 수 있도록 태그를 제시합니다. 블로그 커미터 요청을 하시면 컨텐츠에 태그를 다음 중 하나 이상 (tags: [계획, 챌린지]) 입력해주세요.

  • 아이디어: 새로운 프로젝트나 기능에 대한 창의적인 아이디어를 공유합니다.
  • 인사이트: 특정 주제나 기술에 대한 깊이 있는 통찰과 분석을 제공합니다.
  • MVP: 최소 기능 제품(MVP) 개발 과정과 결과를 소개합니다.
  • 성과: 프로젝트나 작업에서 달성한 주요 성과와 성공 사례를 공유합니다.
  • 업데이트: 프로젝트나 제품의 최신 업데이트와 변경 사항을 알립니다.
  • 런칭: 새로운 제품이나 기능의 출시 소식을 전합니다.
  • 배운점: 프로젝트나 작업을 통해 얻은 교훈과 배운 점을 나눕니다.
  • 고민: 현재 직면한 문제나 고민에 대해 이야기합니다.
  • 푸념: 작업 중 겪은 어려움이나 불만을 솔직하게 털어놓습니다.
  • 회고: 프로젝트나 작업을 마친 후의 회고와 반성을 기록합니다.
  • 생각정리: 복잡한 생각이나 아이디어를 정리하여 공유합니다.
  • 의사결정: 중요한 의사결정 과정과 그 이유를 설명합니다.
  • 질문: 독자들에게 묻고 싶은 질문이나 의견을 구합니다.
  • 계획: 앞으로의 계획과 목표를 공유합니다.
  • 챌린지: 도전 과제와 그 해결 과정을 기록합니다.
  • 자기소개: 블로그 작성자에 대한 소개와 개인적인 이야기를 전합니다.

오픈소스 프로젝트에 참여함으로써 얻을 수 있는 보상

  • 개인 브랜드 강화: 오픈소스 기여를 통해 자신의 이름을 알리고, 개인 브랜드를 강화할 수 있습니다.
  • 포트폴리오 강화: 오픈소스 프로젝트에 기여한 기록은 본인의 자산이며, 포트폴리오에 추가할 수 있고, 이는 취업이나 프리랜서 활동에 큰 도움이 됩니다.
  • 경험과 기술 향상: 실제 프로젝트에 얻은 실무 경험을 쌓고, 새로운 기술을 배우며 자신의 역량을 공유할 수 있습니다.
  • 네트워킹 기회: 다양한 개발자들과 협업하면서 네트워크를 확장하고, 업계 전문가들과의 인맥을 쌓을 수 있습니다.
  • 인정과 감사: 프로젝트의 공식 문서나 웹사이트에 기여자로 이름을 올리거나, 기여자 페이지에 소개됩니다.
  • 학습 기회: 저와 직접 피드백을 주고 받으면서 프로젝트를 진행하면서 발생하는 이슈나 장애에 대한 지원 및 학습할 수 있는 기회를 제공합니다.

어떻게 기여할 수 있나요?

  • 문서 개선: 기존 문서를 읽고, 오타나 잘못되거나 오래된 정보를 수정해 주세요.
  • 새로운 가이드 작성: 프로젝트 사용법, 설치 방법, 팁 등을 포함한 새로운 가이드를 작성해 주세요.
  • 번역: 문서를 다양한 언어로 번역하여 더 많은 사람들이 접근할 수 있도록 도와주세요.
  • 피드백 제공: 문서를 읽고 이해하는 데 어려움이 있었다면, 피드백을 제공해 주세요.

참여 방법

  • GitHub 저장소를 포크하고, 변경 사항을 커밋합니다.
  • Pull Request를 생성하여 변경 사항을 제출합니다.
  • 리뷰어가 변경 사항을 검토하고, 필요한 경우 피드백을 제공합니다.

작은 기여가 큰 변화를 만듭니다. 함께 더 나은 오픈소스 프로젝트를 만들어 나가요.