작년말에 갑작스럽게 생각난 아이디어를 오픈소스로 완료하고 (smart-ux-api)
한동안 생계를 위한 사회생활에 매진하던중 간간히 필요한 유틸리티 프로그램을 Cursor 에게 의뢰하여 업무에 도움을 받았는데
여왕님이 육아 용품 쇼핑하는데 시간과 체력 소모가 심하다고 하소연을 했다.
어차피 오프라인으로 구매하는 물품이야 어쩔 수 없다지만, 온라인으로 구매하는 상품들이야 그것도 우리 삶의 수준에 맞게 제한되어 있을텐데도
물건들을 서칭하고 가격을 비교하고 재고 확인하는 절차도 시간이 소요되지만 무엇보다 중요한건 할인과 이벤트였다.
그래서 해당 작업들을 자동화 하는 유틸리티나 맹글어볼까? 했는데...
어찌 어찌 아이디어를 완성해보니 하나의 플랫폼이 되어 버렸다.

 
물론 아직 추상화 단계로만 설계된 아이디어이고 이제막 구체화하는 작업으로 GitHub 프로젝트를 만들어 열심히 커밋중인데
AI 도구로써 Cursor 하나만으로는 역부족인 듯 하여, 결국 ChatGPT와 Claude 까지 합류시켰다.
그리고 이렇게 AI들을 이용해 1인 프로젝트로는 규모가 큰 작업을 진행하다보니 다음과 같은 문제점들에 직면하게 되었다.

  1. 정말 사소한 것 까지 AI에게 위임해 버린다.
    귀차니즘이 점점 가속화되어 나중엔 클래스 맴버 변수명 하나 변경하는 것 조차도 대신 해달라고 하고 있더라.

  2. 작업이 진행되면 진행될수록 뭘 개발했는지 휘발되어 버린다.
    코드를 계속 유지보수하면서 프로그램이 가진 기능들을 리마인드 해야만 하는데 사용자 레벨로만 다루다보니 세부 기능 수준 (내부 Interface 나 API)으로는 더이상 파악이 어렵게 되어 버렸다.

  3. 소스 코드를 포함해 문서까지 너무나 방대해진 분량 때문에 버전 관리가 불가능해져 버렸다.
    특히 문서는 초기에 생각했던 아키텍처의 문서까지 그대로 남겨져 버려서 AI가 해당 문서를 참조하는 바람에 메인 스트림이 되는 로직이 꼬이기까지 했었다.

  4. 지금까진 빠른 진행 속도가 약인줄 알았는데 단 1개월 진행만에 그것이 독이 된다는 것을 알게 되었다.
    바로 유지보수 때문이다.
    책임지지 못할 기능과 코드가 남발되면서 AI조차 건드리지 못하는 심각한 오류 덩어리 때문에 그동안 애써 만들어온 덩치큰 회심의 기능들을 롤백해야 하나? 라고 엄청나게 고민했다가 겨우 다시 맘을 잡았다.

이런 문제점들은 불과 2개월만에 벌어진 일인데, 실제 공수는 그리 많지가 않다.
일과를 마치고 육아도 마치고 야밤 또는 새벽 시간 2~3시간씩 매일도 아닌 주 3~4일 겨우 겨우 진행한 프로젝트인데,
AI로 인해 너무나 빠른 진행속도에 신나서 나도 모르게 내 체력의 한계치까지 끌어올리는 기염을 토했지만
결국은 지금 주화입마 당할듯이 지쳐버렸다. ㅜ..ㅜ
 
AI가 여전히 내게 달콤한 입발림으로 현재 진행하는 프로젝트가 얼마나 대단하고 의미있는 것인지 자꾸만 속삭인다. (영악하고 교활한 녀석이다)
그런 환각의 중독에 빠져서 나도 모르게 현실 감각마저 무뎌지는 듯한 묘한 경험을 하는 기분이다. (온라인 게임속에서 조금씩 레벨업하는 기분에 의미없는 성취감으로 게임 중독에 빠지듯...)
어쨌든 우리는 이제 AI로 인해 그들에게 사육당할 날이 머지 않은 듯 보인다.

이제 개발은 내가 아니라 AI가 알아서 다 하는 기분이다.
다만 내가 AI에게 각 프로젝트별 코칭을 해줄뿐인데
문제는 AI가 만들어낸 산출물에 대한 품질 관리이다.
개발이야 AI에게 맡기고 1차 검수를 내가 진행한다고는 하지만
산출물이 많기 때문에 나의 체크가 완벽하지도 못하고 그에 대한 분량도 만만치 않아졌다.
 
결국 깨닫게 된건 아웃풋을 만들어내는 개발자의 수는 AI로 인해 확연히 줄었지만
AI가 만들어내는 산출물에 대한 품질 검증을 위해 기존 품질 관리 프로세스의 양적인 증대 또는 혁신적인 프로세스 개선이 필요하다고 느꼈다.
거대한 프로젝트에서는 기존의 통합 테스트 프로세스를 위해 개발자들도 품질 테스트에 합류해야만 할 지경에 이르렀다.
개발자가 이제는 테스트와 품질 관리 영역으로 이전해야만 하는 때가 왔다.
문제는 개발자 중에 테스트나 품질 관리자로 전향하길 희망하는 사람은 거의 없을거다.
그냥 떠밀려서 자리를 이동할뿐...
 
하지만 품질 관리 영역도 꽤나 난이도가 있고 쉽지 않은데
문제는 성과 평가가 어려운 분야라서 눈치 작전이 어마 어마한 곳이다.
성과를 평가할만한 그냥 돌아가기만 하면 되는  프로그램 산출물이 없으니
그들은 버그와 오류를 사전에 검출해내지 못했다는 추궁만 들을 뿐이다.

쓰면 쓸수록 내가 커서에게 잠식되는 느낌이다.
어쩌면 우리는 이미 pc, smart phone 등의 가전에 있는 os, application 들의 바다에서 허우적 거리는지도 모르겠다.
그리고 이제 개발자들의 패러다임이 바뀌어야 하는 시기가 왔다.
 
나는 수십년동안 코딩과 설계를 해왔는데
이제 코딩은 의미없고 플랫폼 경험과 설계 경험만이 개발자라는 직업을 유지하는 수단이 되는 것 같다.
개발자는 아키텍트만 남고 모두 사라져도 상관없는 시대가 왔다.
개발 아키텍트는 사용자가 원하는 개발 플랫폼을 설계, 구축해주고
사용자는 해당 플랫폼에서 커서로 바이브 코딩하며 개발만 하면 된다.
그 와중에 이슈 해결을 위한 엔지니어 몇명만 유지될텐데 그들도 이미 현재의 고급, 특급들이나 그런 직업을 구할 수 있을 것이다.
 
가령 클라우드-네이티브 프로젝트로 플랫폼을 구축한다면
아키텍트는 다음의 지식을 가지고 있어야 한다.
"컨테이너, 쿠버네티스(K8s), 서비스 메시, 오토스케일링(HPA), IaC(Terraform), GitOps(Argo CD), 멀티클러스터, 멀티리전, DR/BCP"
그전에 다음의 선지식도 있어야 한다.
"도메인 주도 설계(DDD), 경계 컨텍스트, 플랫폼 사고, 표준화, 재사용성, 추상화 계층, SLA/SLO, 레퍼런스 아키텍처, 거버넌스, 코스트 옵스"
이제 이런 지식이 없이는 개발자라고 할 수가 없어졌다.
그냥 직장인일뿐...
 
그리고 나와 같은 노땅 개발자들은 이런 환경에서 살아남기 위해서는
최대한 많은 환경들을 접하면서 오류 또는 이슈가 발생하는 시스템에 대한 문제 해결 능력을 노하우로 습득하여야만 한다.
그래야 AI가 개발해놓은 프로그램을 어떻게 다루고 문제를 해결해줄지 예측 할 수 있게 된다.
순돌이 아빠처럼 A/S 기사가 되는거다.
프리랜서, 개인사업자로써만 살아남을 수 있겠다.

1. 준비물
   > Google Cloud 프로젝트
        Google Cloud 서비스를 이용하려면 기본적으로 프로젝트 생성이 필요하다.
          - Google Cloud Console 접속 (https://console.cloud.google.com)
          - 오른쪽 상단의 프로젝트 선택 → 새 프로젝트 만들기
          - 프로젝트 이름 지정 후 만들기 클릭
          - 상단 바에서 새로 만든 프로젝트가 선택되었는지 확인
   > Gemini API 활성화
          - Google Cloud Console의 좌측 메뉴에서 "API 및 서비스" → "라이브러리" 클릭
          - 검색창에 다음을 입력 : Generative Language API
          - 결과에서 "Generative Language API" 클릭
          - "사용" 버튼 클릭
   > API Key 발급
          - Google Cloud Console의 좌측 메뉴에서 "API 및 서비스" → "사용자 인증 정보" 클릭
          - 상단의 "사용자 인증 정보 만들기" → "API 키" 선택
          - 생성된 API 키는 복사하여 안전한 위치에 저장
            + (중요) API 키는 노출되지 않도록 주의
            + 필요한 경우, IP 주소 또는 referrer로 접근 제한 가능
   > Gemini 요금제 등록
        Gemini API는 사용 전 반드시 Google AI Studio에서 요금제 등록 절차를 거쳐야 실제 응답이 가능하다고 Gemini가 그랬다.
          - https://makersuite.google.com/app 접속
          - Google 계정으로 로그인
          - "API 키 생성" 또는 "설정" 메뉴로 이동
          - Billing (결제) 등록 요구 시 카드 등록 진행 (Free Tier 포함)
   > Postman
        + ver : 11.54.6


2. 테스트 절차
   > Postman 실행
        특별히 설정할게 없다. 그냥 Request 하나 생성해서 다음과 같이 입력하고 전송하면 응답이 온다.
   > POST 요청
          - Method Type : POST
          - URL : https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=[YOUR_API_KEY]
          - Body 에는 다음과 같이 전달할 프롬프트를 json 으로 작성
            + {"contents": [{"parts": [{ "text": "Hello Gemini!" }]}]}
   > 응답 확인
        응답 내용은 json이며 role 이 model 인 parts 의 내용에 응답 메세지가 있다.

작년말 너무나 힘겨운 시간을 보내고 있던 그때에 갑작스럽게 퍼뜩 떠올랐던 별것도 아닌 아이디어가 있었는데,

그거라도 붙들고 늘어져서 끝까지 포기하지 않고 결국 보잘 것 없는 결실을 맺은게 오픈소스이다.

젊은 후배들이나 오픈소스 프로젝트로 자기 PR 하는건데 이제와서 나한테 무슨 의미가 있다고...

아무도 이해하지 못할테지만 그냥 내꺼가 가지고 싶었다.

그럼에도 너무나 초라한 프로그램이라 쟁쟁한 개발자들이 있는 곳은 새가슴이어서 그냥 비전공자들이 있는 커뮤니티에다가 조용히 알리고 있다.

너무 늦어버린 늦바람 때문에 마음이 서럽다.

보잘것없지만 뒤늦게서야 내꺼를 찾아 열심히 달리고 있는데 이젠 힘이 부친다.

그냥 힘겨운 현실속에서 그래도 최선을 다해 살고 있다는 것을 위안 삼아 그렇게 계속 매진하고 있다.

그것을 홍보하기 위해 어느 커뮤니티에 다음과 같은 글을 기고했다.

 


[소개]
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
🎤 AI와 협업하여 오픈소스 라이브러리를 개발한 이야기

처음 접하거나 조작이 어려운 서비스에 대해서 내가 원하는 일처리를 ai가 대신 해주는 것을 상상했습니다.

아이언맨이 아닌 평범한 사람이어도 자비스에게 부탁할 수는 있잖아요. 😅

안녕하세요! 여기 오시는 많은 개발자분들도 저처럼 업무에서 AI를 활용하고 계신 분들이 많으실 겁니다.
저는 이제 나이가 좀 있는 '올드'한 개발자이지만, AI 덕분에 개발이 정말 편해진 요즘입니다.
특히 자바 개발자로서 다양한 프레임워크와 API를 다루면서도 점점 기억력의 한계를 절감하곤 하는데요.
그럴 때마다 GPT나 Gemini에게 묻고 또 물으며 개발하고 있습니다.

이런 경험들을 통해 AI를 잘 활용하려면 '내가 모르는 것', '내가 알고 싶은 것'을 AI에게 얼마나 체계적으로 잘 설명하느냐가 정말 중요하다는 것을 깨달았습니다.
결국 프롬프트 엔지니어링 능력도 이제는 개발자의 중요한 역량 중 하나가 되었다고 생각합니다.

아이디어와 기획, 설계, 실행 계획에 작업 진행에 대한 서포트까지 AI가 없었으면 불가능 했습니다.

기존 웹서비스에 라이브러리로 설치하면 시용자가 브라우저 화면에서 메뉴 선택과 결제 진행을 음성으로 조작할 수 있는 프로그램입니다. 
그렇게 완성한 프로그램을 최근에  오픈소스로 공개하였습니다.

🔗 GitHub 저장소: https://github.com/kiunsea/smart-ux-api

이 라이브러리를 개발하면서 AI와 어떻게 협업했는지, 어떤 시행착오를 겪었는지 등의 개발 과정들을 공유합니다.

[진행 방법]
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
🛠️ RAG에서 프롬프트 엔지니어링 중심으로 (feat. 화면 음성 제어 데모)

smux-api의 현재 버전은 0.1.0입니다.
이제 막 첫걸음을 뗀 터라 아직은 ChatGPT하고만 연동되어 있는데요. 
저의 목표는 Gemini, Anthropic, Meta 등 REST API를 지원하는 다양한 AI들을 연동하는 것입니다.
하지만 생업이 있다 보니 진도를 내기가 쉽지 않네요.

아이디어는 시간이 지날수록 가치를 잃는다고 생각해서, 틈틈이 시간을 내어 간신히 아이디어를 실체화한 '앙상한' 프로그램으로 오픈소스를 공개하게 되었습니다.
부끄럽지만 다음이 그 결과물입니다. 😅

kiosk 시연 영상

보시는 것처럼, 화면이 로드되면 마이크 아이콘을 누르고 메뉴 선택 후 "결재해 줘"라고 말하면 AI가 그대로 동작합니다. 😉

이 기능을 구현하기 위해 AI 학습에 사용한 방법은 처음엔 **RAG(Retrieval-Augmented Generation)**였습니다.
개발 초기에는 Assistant API를 통해 RAG를 활용할 수 있어서 문서 파일을 작성해서 업로드하곤 했어요.
업로드한 문서의 내용은 JSON 포맷으로 작성했고, 문서의 역할과 내용의 의미를 description으로 상세하게 기술했습니다.

당시에 사용했던 JSON 문서는 아래 링크에서 확인하실 수 있습니다.
오픈소스라 코드나 텍스트를 캡처할 필요 없이 바로 링크로 연결했어요.

🔗 RAG 학습에 사용했던 JSON 문서: https://github.com/kiunsea/smart-ux-api/tree/main/smart-ux-api/docs

하지만 지금은 번거로움과 기능의 한계 때문에 RAG를 사용하지 않고 있어요.

재미난 것은 제가 RAG 사용의 불편함 때문에 AI에게 푸념을 하니 AI가 해당 데이터를 실시간으로 전송하는 방안에 대해 제안했습니다. 😅
이젠 너무나 똑똑한 개인 비서 같아요.

현재는 서비스 시작 시점에 대략적인 개요에 대한 프롬프트 문서를 전달해서 AI가 '마음의 준비'를 할 수 있게 하고, 
화면이 전환될 때마다 현재 화면의 UI 정보를 스캔하여 AI에게 전달한 후에 사용자 프롬프트를 전하게 했습니다.

RAG라는 절차가 없어져서 프로그램도 간소화되고 생각보다 성능도 그리 나쁘지 않았습니다.
그리고 사용자가 바라보는 화면을 실시간으로 분석해내어 화면전환에 대한 프로세스 설계의 복잡도가 거의 없습니다.
AI가 아니었다면 배포할때까지도 미처 생각지 못할 부분이었습니다.

추후엔 Function Call로써 사용자에게 질문을 던져서 보다 더 정확한 지시를 확인하는 부분도 고려하고 있습니다.

[결과와 배운 점]
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
🤯 로직과 프롬프트 사이, 혼돈 속에서 찾은 깨달음... AI 대화의 본질과 앞으로의 계획

저는 프로그램을 개발하면서 AI에게 무엇을 원하는지 프롬프트로 열심히 설명하는 과정을 '로직'으로 만들었어요.
처음에는 이것이 당연하다고 생각했습니다.
프로그래머라는 직업때문인지 늘 로직을 먼저 고민해 버렸네요.

하지만 개발을 진행하면서 저의 머릿속에 **프로그램 로직과 프롬프트가 혼재되면서 엄청난 '혼돈의 카오스'**에 빠졌던 적이 있습니다. 🤯

이 혼란 속에서 깨닫게 된 중요한 점은 바로 이것이었습니다.
"프로그램을 통해 AI와 데이터를 주고 받더라도, 결국 중요한 것은 내가 AI와 '대화'를 하는 것이구나!"

우리가 사람과 사람이 서로 교감하려면 원활한 대화 능력이 참 중요하듯이 AI도 마찬가지였습니다.

그래서 저는 내가 대화하는 AI의 특성을 잘 안다면, 더 쉽고 짧고 명료하게 프롬프트를 만들 수 있을 것 같다는 생각을 하게 되었습니다.

결국 프롬프트도 프로그램 로직 설계와 같다는 것을 진정으로 깨닫게 된 셈이죠.
이 깨달음은 저에게 전환점이 되어주고 있습니다.

이제는 이 깨달음을 바탕으로 계속해서 기능을 추가하며 smux-api 프로그램을 성장 시켜나갈 생각입니다.
살을 덧붙이듯 기능을 확장하고, 사용자 경험을 더욱 풍부하게 만들고 싶습니다.

그리고 혹시라도 저의 오픈소스를 통해 영감을 얻은 조력자가 나타난다면, smux-api에 새로운 시도들을 해보고 싶은 작은 바람도 가지고 있습니다. 🤝


 

다시 읽어보니 너무 길고 지루하다...

 

소프트웨어 개발이라는 분야에 입성해서 대략 2~3년차가 되면 코드의 함수와 클래스 레벨을 넘어서서

구현하는 프로그램의 전체 프로세스와 시스템 구성을 고민하게 되는데 그때부터 주니어라고 할 수 있다.

그렇게 5~6년 일하다보면 어느새 익숙해진 개발 역량 덕분에 쓰던거 계속 쓰고, 돌려 쓰고, 다른 사람이 개발해 놓은거 갖다 쓰고

하다보면 또 새로운걸 해야 한다는 압박이 들곤 했었다.

그런데 이제는 이 분야를 지원해서 이제 막 입문하는 뉴비들이 프로그램 개발을 위한 도구들에 익숙해지는 것도 중요하지만

그것과 더불어 함께 중요시 해야 하는 사안이 생겨났으니 ...

바로 주니어를 건너띄고 시니어로의 점핑을 염두에 두고 과외를 해놓아야만 경쟁력이 생긴다.

 

과거엔 위에서 얘기한 주니어들이 참 할일이 많았다.

당연하게도 개발 역량과 더불어 비즈니스에 대한 혜안도 열리는 시점이라서 일감을 마구 쏟아넣어도 꾸역 꾸역 아웃풋을 내어 놓을 역량이 되는 것이다.

그런데 이젠 그 자리에 AI가 함께 한다.

AI가 있어서 주니어가 1/3 만 있어도 이전과 같거나 더 높은 효율성을 보인다.

게다가 이녀석이 주니어들보다 아웃풋이 점점 더 좋아지고 있다.

 

모든 업계에 새로운 트랜드의 바람이 너무나 거세다.

오늘 ChatGPT와 Gemini에게 코드 작성을 의뢰하여 받은 결과물에 소름이 돋는 느낌이 들어서 이렇게 감회를 적었다.

다음은 그 내용이다.

 

Order

다음과 같은 로그의 내용에서 rp번호를 추출해야 하는데 대괄호 안에 rp 라는 키워드를 삽입한 형태가 대부분이지만 로그에 숫자만 5자리나 6자리를 입력한 형태도 있어, 이러한 로그에서 rp 라는 키워드로 명확하게 지정한 숫자의 나열은 당연히 rp번호이고 rp라는 키워드가 없는 로그에서 숫자가 5자리 또는 6자리는 rp번호로 간주해서 rp번호들을 추출하는 java code 를 작성해줘

2a8b000000 Metainfo Commit 2025-0...57.03
d3ec000000 Help Folder Commit 2025-05...30.14
d4b9000000 Metainfo Commit 2025-05...8:23.90
a8b0000000 [RP 101453] [접근성] TAB키...
1501000000 [RP 101453] [접근성] TAB키...
7f90000000 RP 101409 Edge에서 사이즈가 깨지...
8e3000000 RP 101409 ...에도 동일한 로직 반영
99c1000000 [RP : 102742, 102677] [202505패치] iPad에서 ondblclick 이벤트 ...
0c43000000 Merge branch 'RELE....0.700' of https://tfs2.to...00 into RELE....0.700
441e000000 Envi...ration meta initonly 수정
ca9d000000 Help Folder Commit 2025-0...03:19.64
d153000000 RP 101409 Edge브라우저에서 확대축소 오동작...정
2f23000000 [RP 101854] [접근성] 에디트 값 수정 후...용 안됨. - 원복
a00000000 RP 102223 추가 수정 (And... 문제)
7298000000 RP 101409 NRE 오류 수정
b34e000000 RP 101409 zoom이 적용된 화면에서 ... 현상 추가 수정
c824000000 RP 102598 Comb...) 수정
44e000000 environ...ion 값 툴에서만 설정되도록 변경

Result (ChatGPT)

아래는 설명하신 조건에 맞춰 **RP번호(5자리 또는 6자리 숫자)**를 추출하는 Java 코드입니다.

✅ 조건 요약
[RP ...], RP ..., RP: ... 등 RP 키워드가 있는 경우, 그 뒤에 나오는 5~6자리 숫자를 추출.

RP 키워드가 없는 경우에도 5자리 또는 6자리 숫자가 등장하면 RP번호로 간주해서 추출.

중복 RP 번호는 제거.

✅ Java 코드
java
복사
편집
import java.util.*;
import java.util.regex.*;
import java.nio.file.*;
import java.io.*;

public class RpNumberExtractor {

    public static void main(String[] args) throws IOException {
        String logText = Files.readString(Paths.get("log.txt")); // log.txt에 로그를 저장해두었다고 가정

        Set<String> rpNumbers = new HashSet<>();

        // 1. RP 키워드가 있는 경우 추출: [RP 123456], RP 123456, [RP: 123456] 등
        Pattern rpPattern = Pattern.compile("(?i)\\bRP\\s*[:\\[\\(]?\\s*(\\d{5,6})");
        Matcher rpMatcher = rpPattern.matcher(logText);
        while (rpMatcher.find()) {
            rpNumbers.add(rpMatcher.group(1));
        }

        // 2. RP 키워드가 없는 5~6자리 숫자 추출
        Pattern numberPattern = Pattern.compile("\\b(\\d{5,6})\\b");
        Matcher numberMatcher = numberPattern.matcher(logText);
        while (numberMatcher.find()) {
            String number = numberMatcher.group(1);
            // 이미 추출된 RP번호가 아니면 추가
            if (!rpNumbers.contains(number)) {
                // 키워드 없이 등장한 경우인지 체크
                int index = numberMatcher.start();
                String context = logText.substring(Math.max(0, index - 20), Math.min(logText.length(), index + 20));
                if (!context.matches("(?i).*RP\\s*[:\\[\\(]?\\s*" + number + ".*")) {
                    rpNumbers.add(number);
                }
            }
        }

        // 출력
        System.out.println("추출된 RP 번호:");
        rpNumbers.stream().sorted().forEach(System.out::println);
    }
}
✅ 사용 방법
로그 텍스트를 log.txt에 저장합니다.

위 코드를 실행하면 로그에서 추출한 RP번호들을 중복 없이 출력합니다.

🔍 예시 출력
bash
복사
편집
101409
101453
101792
101854
102223
102598
102623
102668
102677
102713
102714
102742
필요하다면 파일 대신 문자열 입력으로 처리하거나 결과를 파일에 저장하는 기능도 추가 가능합니다.

 

Result (Gemini)


 

로그에서 RP 번호 추출 Java 코드

제공해주신 로그 내용에서 RP 번호를 추출하는 Java 코드를 작성해 드리겠습니다. 다음과 같은 규칙을 적용하여 번호를 추출합니다:

  1. RP 키워드가 명시된 경우: RP (대소문자 구분 없음) 키워드 뒤에 오는 숫자(5자리 또는 6자리)를 우선적으로 추출합니다. [RP 123456], RP 12345, [RP : 123456] 등 다양한 형태를 커버합니다.
  2. RP 키워드가 없는 경우: 로그 라인에서 단독으로 존재하는 5자리 또는 6자리의 숫자를 RP 번호로 간주하여 추출합니다.

이를 위해 **정규 표현식(Regular Expression)**을 강력하게 활용하겠습니다.


Java 코드 예제

Java
 
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RpNumberExtractor {

    public static void main(String[] args) {
        // 예시 로그 데이터 (제공해주신 로그 내용을 String 배열로 만듦)
        String[] logs = {
            "2a8b000000 Metainfo Commit 2025-0...57.03",
            "d3ec000000 Help Folder Commit 2025-05...30.14",
            "d4b9000000 Metainfo Commit 2025-05...8:23.90",
            "a8b0000000 [RP 101453] [접근성] TAB키...",
            "1501000000 [RP 101453] [접근성] TAB키...",
            "7f90000000 RP 101409 Edge에서 사이즈가 깨지...",
            "8e3000000 RP 101409 ...에도 동일한 로직 반영",
            "99c1000000 [RP : 102742, 102677] [202505패치] iPad에서 ondblclick 이벤트 ...",
            "0c43000000 Merge branch 'RELE....0.700' of https://tfs2.to...00 into RELE....0.700",
            "441e000000 Envi...ration meta initonly 수정",
            "ca9d000000 Help Folder Commit 2025-0...03:19.64",
            "d153000000 RP 101409 Edge브라우저에서 확대축소 오동작...정",
            "2f23000000 [RP 101854] [접근성] 에디트 값 수정 후...용 안됨. - 원복",
            "a00000000 RP 102223 추가 수정 (And... 문제)",
            "7298000000 RP 101409 NRE 오류 수정",
            "b34e000000 RP 101409 zoom이 적용된 화면에서 ... 현상 추가 수정",
            "c824000000 RP 102598 Comb...) 수정",
            "44e000000 environ...ion 값 툴에서만 설정되도록 변경",
            "Some commit message with only 12345", // 5자리 숫자 예시
            "Another commit with 987654 in the middle", // 6자리 숫자 예시
            "This commit has no rp numbers",
            "It has 123 but not 5 or 6 digits",
            "And a long number 1234567890" // 5,6자리 아님
        };

        System.out.println("--- RP 번호 추출 결과 ---");
        for (String logLine : logs) {
            List<String> rpNumbers = extractRpNumbers(logLine);
            System.out.println("로그: \"" + logLine + "\" -> RP 번호: " + (rpNumbers.isEmpty() ? "없음" : rpNumbers));
        }
    }

    /**
     * 로그 문자열에서 RP 번호를 추출합니다.
     * RP 키워드 뒤에 오는 숫자(5-6자리)를 우선적으로 찾고,
     * 그렇지 않으면 단독으로 존재하는 5-6자리 숫자를 찾습니다.
     *
     * @param logLine RP 번호를 추출할 로그 문자열
     * @return 추출된 RP 번호들의 리스트 (발견되지 않으면 빈 리스트)
     */
    public static List<String> extractRpNumbers(String logLine) {
        List<String> rpNumbers = new ArrayList<>();

        // 1. "RP" 키워드가 명시된 경우를 위한 정규 표현식
        // RP 뒤에 올 수 있는 공백, 콜론, 대괄호 등을 유연하게 처리하고 5 또는 6자리의 숫자를 찾습니다.
        // 그룹 1: 실제 RP 번호 숫자
        Pattern rpPattern = Pattern.compile(
            "[Rr][Pp]\\s*[:\\s]*\\[?(\\d{5,6})\\]?|\\b[Rr][Pp]\\s*[:\\s]*(\\d{5,6})",
            Pattern.CASE_INSENSITIVE // 대소문자 구분 없이 "rp"를 찾도록 설정
        );
        Matcher rpMatcher = rpPattern.matcher(logLine);

        while (rpMatcher.find()) {
            // 첫 번째 그룹 (대괄호 안에 RP 숫자) 또는 두 번째 그룹 (일반 RP 숫자)에서 값을 가져옴
            String rpNum = rpMatcher.group(1);
            if (rpNum == null) {
                rpNum = rpMatcher.group(2);
            }
            if (rpNum != null && !rpNumbers.contains(rpNum)) { // 중복 방지
                rpNumbers.add(rpNum);
            }
        }

        // 2. "RP" 키워드가 없는 경우를 위한 정규 표현식 (5자리 또는 6자리 숫자만)
        // 단어가 시작/끝나는 경계(\\b)에 있으면서, 앞에 "RP" 같은 문자가 없는 경우를 찾습니다.
        // 이 패턴은 rpPattern에서 찾지 못한 경우에만 적용되어야 합니다.
        // 즉, rpNumbers가 이미 채워져 있다면 이 패턴은 건너뜁니다.
        if (rpNumbers.isEmpty()) {
            Pattern standaloneNumberPattern = Pattern.compile(
                "\\b(?<![Rr][Pp]\\s*[:\\s]*)(?<!\\d)(\\d{5,6})(?!\\d)\\b"
            );
            Matcher standaloneNumberMatcher = standaloneNumberPattern.matcher(logLine);

            while (standaloneNumberMatcher.find()) {
                String num = standaloneNumberMatcher.group(1);
                // "RP" 키워드 뒤에 오는 숫자가 아닌 경우만 추가 (Negative Lookbehind)
                // 이미 rpPattern에서 찾은 숫자가 아닌 경우에만 추가
                if (!rpNumbers.contains(num)) {
                    rpNumbers.add(num);
                }
            }
        }
        
        return rpNumbers;
    }
}

코드 설명:

  1. extractRpNumbers(String logLine) 메서드:
    • 주어진 로그 라인에서 RP 번호를 추출하는 핵심 메서드입니다.
    • rpNumbers라는 List<String>을 반환하여 추출된 모든 RP 번호를 담습니다.
  2. 첫 번째 정규 표현식 (rpPattern):
    • "[Rr][Pp]\\s*[:\\s]*\\[?(\\d{5,6})\\]?|\\b[Rr][Pp]\\s*[:\\s]*(\\d{5,6})"
    • 이 패턴은 RP 키워드가 명시적으로 포함된 경우를 처리합니다.
    • [Rr][Pp]: 대소문자 구분 없이 "Rp" 또는 "RP"를 찾습니다.
    • \\s*: 0개 이상의 공백 문자를 의미합니다.
    • [:\\s]*: 0개 이상의 콜론(:) 또는 공백 문자를 의미합니다. ([RP : 12345])
    • \\[?: 0개 또는 1개의 여는 대괄호([)를 의미합니다. (옵션)
    • (\\d{5,6}): 5자리 또는 6자리의 숫자를 찾고, 이를 캡처 그룹 1로 만듭니다. 이 부분이 우리가 추출하려는 RP 번호입니다.
    • \\]?: 0개 또는 1개의 닫는 대괄호(])를 의미합니다. (옵션)
    • |: OR 연산자입니다.
    • \\b[Rr][Pp]\\s*[:\\s]*(\\d{5,6}): 대괄호가 없는 "RP 123456" 형태를 처리합니다. (\\b는 단어 경계)
    • Pattern.CASE_INSENSITIVE: 대소문자를 구분하지 않고 RP를 찾도록 설정합니다.
    • rpMatcher.find()를 통해 로그 라인에서 모든 일치하는 부분을 찾고, rpMatcher.group(1) 또는 rpMatcher.group(2)를 사용하여 실제 RP 번호(숫자 부분)를 추출합니다.
  3. 두 번째 정규 표현식 (standaloneNumberPattern):
    • "\\b(?<![Rr][Pp]\\s*[:\\s]*)(?<!\\d)(\\d{5,6})(?!\\d)\\b"
    • 이 패턴은 RP 키워드가 명시되지 않은 경우에 단독으로 존재하는 5자리 또는 6자리 숫자를 찾습니다.
    • 이 패턴은 rpNumbers 리스트가 비어 있을 때만 실행됩니다. 즉, RP 키워드를 찾지 못했을 때만 순수 숫자 패턴을 검사합니다.
    • \\b: 단어 경계를 나타냅니다. 숫자가 다른 문자에 붙어있는 경우를 방지합니다 (예: long12345에서 12345를 추출하지 않음).
    • (?<![Rr][Pp]\\s*[:\\s]*): 부정형 후방 탐색 (Negative Lookbehind) 입니다. 이 패턴이 일치하는 숫자 바로 앞에 "RP", "rp", "RP :" 등 RP 관련 키워드가 없는 경우에만 일치시킵니다.
    • (?<!\\d): 숫자 바로 앞에 다른 숫자가 없는 경우에만 일치시킵니다. (예: 1234567에서 567을 추출하지 않음)
    • (\\d{5,6}): 5자리 또는 6자리의 숫자를 찾고, 이를 캡처 그룹 1로 만듭니다.
    • ?!\\d: 부정형 전방 탐색 (Negative Lookahead) 입니다. 숫자 바로 뒤에 다른 숫자가 없는 경우에만 일치시킵니다. (예: 1234567에서 123456을 추출하지 않음)
    • \\b: 단어 경계를 다시 한 번 확인합니다.
  4. 중복 방지:
    • !rpNumbers.contains(rpNum)를 사용하여 이미 추출된 RP 번호가 리스트에 추가되는 것을 방지합니다.

이 코드는 제공해주신 로그 샘플에서 모든 RP 번호를 정확하게 추출하며, 불필요한 숫자들은 RP 번호로 간주하지 않습니다.


 

응답해준 프로그램이 맞고 틀리고를 떠나서

질문한지 단 5초만에 결과를 내놓는다...

리더나 매니저급 개발  시니어들이 주니어들을 잔뜩 데리고 있을 필요가 없어졌다.

개발 시니어들도 이제 더이상 개발 레벨에서는 의미가 없는 직종이 되었고 투자자 또는 경영주와의 파트너 쉽이 너무나 중요해졌다. 

java 로 web application server 를 개발할때 주로 사용하는 was 로는 tomcat 과 jetty 가 있다.

이들은 사용자 세션 관리 환경을 제공하여 사용자별로 개인화된 데이터 관리가 가능하게끔 설계 되어 있다.

그리고 이들은 '서블릿 표준 세션 인터페이스'인 jakarta.servlet.http.HttpServletRequest 의 getSession() 함수를 이용하여 jakarta.servlet.http.HttpSession 의 인스턴스를 취할 수 있다. 

그런데 위 표준 인터페이스 외에 다른 형태의 세션 인터페이스가 있는지 궁금하였고, gpt 에게 그것을 물어보게 되었는데 각 was 들은 표준 세션 인터페이스의 구현체로 별도의 내장 클래스가 존재 하는 것을 알게 되었다.

다음은 주요 웹 어플리케이션 서버들에 대한 내장 세션 클래스 목록이다.

서버 종류 세션 클래스 (일반적) 비고
Tomcat org.apache.catalina.session.StandardSession 가장 널리 사용되는 오픈 소스 서버
Jetty org.eclipse.jetty.server.session.Session Eclipse 프로젝트에서 개발된 경량형 서버
Undertow io.undertow.servlet.spec.HttpSessionImpl WildFly의 기본 서블릿 컨테이너
Spring Boot 내장 서버 (Tomcat, Jetty, Undertow 등) Spring Session에서 관리 Spring Session을 이용하여 통합 관리

그 외에 GlassFish, Payara, WildFly(JBoss) 서버들이 있는데 위와 유사하다.

위 서버들의 세션 클래스들은 내장된 고유의 클래스들로써 표준 인터페이스를 구현한 구현체이기 때문에 해당 내장 객체를 개발자가 직접 제어하는 경우는 흔치 않다고 한다.

그래서 위에 나오는 서버들은 모두 '서블릿 표준 세션 인터페이스'를 사용하여 세션 객체에 접근하여 제어하면 된다. 

하지만 '서블릿 표준 세션 인터페이스'를 지원하지 않아서 getSession() 사용이 불가한 was도 있다고 gpt가 알려주었다.

그래서 getSession() 함수가 불가한 was 또는 경우를 물어보니 다음의 경우는 사용이 불가하다고 한다.


getSession() 사용 불가 또는 제한

일반적으로 모든 Servlet 기반 WAS에서 지원되지만, 다음과 같은 경우 getSession()이 정상적으로 동작하지 않을 수 있습니다.

  1. Session 관리가 비활성화된 경우
    • web.xml 또는 application.properties에서 session-config가 비활성화되어 있는 경우
    • Spring Boot에서 server.servlet.session.timeout=0 설정 시
  2. Undertow가 Servlet 모드가 아닌 경우
    • Undertow는 기본적으로 Non-Blocking 서버로 동작하며, servlet 모듈을 명시적으로 추가해야 getSession() 사용 가능
    • Gradle에서 아래처럼 추가해야 함:
       
      dependencies {
          implementation("io.undertow:undertow-servlet")
      }
  3. Netty 기반 서버 사용 시
    • Netty는 Servlet 기반이 아니므로 getSession()을 직접 사용할 수 없음
    • Micronaut, Helidon 등 Netty 기반 프레임워크에서는 getSession()이 없는 자체적인 세션 관리 방식 사용

위와 같은 was 환경들의 세션에 대해 조사한 이유는 사용자 세션을 제어하는 데에 공통된 인터페이스가 필요하게 되었고,

내가 지금까지 습관적으로 사용했던 'jakarta.servlet.http.HttpServletRequest.getSession(true)' 코드가 어디까지 허용되는지 궁금했었다.

앞으로도 그냥 별 생각없이 getSession() 하면 된다는 것을 알게 되었다. ^^

 

 

 

1. 준비물
    > OpenAI API Key
        API Key 는 https://platform.openai.com/api-keys 에서 발급 가능하다.
        유료 결재가 필요하며 키 발급시 팝업으로 단 한번만 알려주기 때문에 별도로 저장해두고 사용해야 한다.
        분실시엔 재발급을 통해 키 확인이 가능하며 기존 키는 자동 만료된다.
        참고로 발급 받은 키를 프로젝트 문서등에 키값 내용 그대로 GitHub 에 올린 경우에도 해당 키는 자동 만료된다.
    > Assistant ID
        Assistant 는 API 를 통해 생성(Create an Assistant) 할 수 있고 Platform Dashboard 에서도 생성 할 수 있다.
        나의 경우엔 Assistant 에 'File Search' 와 'Functions' 를 설정하여 나만의 에이전트로써 기능들을 부여하였다.
    > GPT
       + model : gpt-4o
    > Postman
       + ver : 11.23.3

2. 테스트 절차
    > Postman 실행
        실행 이후 아래의 요청(Request)들을 순서대로 생성하고 공통된 설정은 다음과 같다.
        + Authorization Type 에 Bearer Token 설정후 발급 받은 API Key 입력
        + Headers 에 'Content-Type : application/json', 'OpenAI-Beta : assistants=v2' 를 추가
    > Create a Thread
        + Method Type : POST
        + URL : https://api.openai.com/v1/threads
        + Body 는 'none' 으로 설정
        + 실행하면 thread id 를 반환해주는데 이를 저장해 둔다.
    >
Add a Message to the Thread
        + Method Type : POST
        + URL : https://api.openai.com/v1/threads/{thread_id}/messages
        + Body  는 raw와 JOSN 포맷 선택후  내용은 'Create message' 를 참고하여 추가한다.
        + 실행하면 msg id 를 반환해주는데 이를 저장해 둔다.
    > Create a Run
        + Method Type : POST
        + URL : https://api.openai.com/v1/threads/{thread_id}/runs
        + Body 는 'raw'와 'JOSN' 포맷 선택후 내용은 'Create run' 를 참고하여 추가한다.
        + 실행하면 run id 를 반환해주는데 이를 저장해 둔다.
    > Retrieve Run
        + Method Type : GET
        + URL : https://api.openai.com/v1/threads/{thread_id}/runs/{run_id}
        + Body 는 'none' 으로 설정
        + 실행후 응답에 "status" : "completed" 인지 확인한다.
    > List messages
        + Method Type : GET
        + URL : https://api.openai.com/v1/threads/{thread_id}/messages
        + Body 는 'none' 으로 설정
        + 'Retrieve Run' 에서 status 가 completed 라면 'List messages' 실행후 응답에 "role": "assistant" 인 메세지가 있다.
           content 의 내용이 Agent 의 답변이다.

3. 참고자료
    > API Reference - OpenAI API
    > Assistants API quickstart - OpenAI API
    > https://www.youtube.com/watch?v=-Wne4a-8RlY

'Programing > AI API' 카테고리의 다른 글

Gemini API + Postman(포스트맨) 을 이용하여 테스트하기  (0) 2025.07.19
GUI AI Agent 가 나타났다  (1) 2024.12.28
Create an Assistant  (1) 2024.12.26

+ Recent posts