Skip to content

Commit 16a6bb0

Browse files
발표자료 AI 정리본 업로드
Signed-off-by: jonghoonpark <dev@jonghoonpark.com>
1 parent dcb3d17 commit 16a6bb0

File tree

2 files changed

+526
-0
lines changed

2 files changed

+526
-0
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
---
2+
layout: "post"
3+
title: "컨트리뷰트로 배운 Spring AI"
4+
description:
5+
"KSUG Spring AI Meetup 에서 발표한 '컨트리뷰트로 배운 Spring AI' 내용을 정리한 글입니다.\
6+
\ Spring AI에 기여하게 된 계기부터 Ollama, Elasticsearch Vector Store 기여 사례,\
7+
\ 그리고 기여하면서 느낀 AI 업계와 커리어에 대한 인사이트를 공유합니다."
8+
categories:
9+
- "오픈소스"
10+
tags:
11+
- "Spring AI"
12+
- "오픈소스"
13+
- "기여"
14+
- "Ollama"
15+
- "Elasticsearch"
16+
- "Vector Store"
17+
- "RAG"
18+
date: "2025-10-26 00:00:00 +0900"
19+
toc: true
20+
---
21+
22+
발표 했던 내용을 정리해보면 좋겠다 싶어 AI로 내용을 정리하여 기록으로 남깁니다. AI로 정리하여 일부 내용은 발표했던 내용과 다를 수 있고, 오류가 있을 수 있습니다.
23+
24+
---
25+
26+
이 글은 [KSUG Spring AI Meetup](https://event-us.kr/ksug/event/114010) 에서 발표한 "컨트리뷰트로 배운 Spring AI" 발표 내용을 AI로 정리한 글입니다.
27+
28+
발표 당시 Spring AI 상위 기여자이자 한국인 중 1위였습니다.
29+
30+
왜 Spring AI에 기여를 하게 되었고, 어떻게 기여하였는지 사례를 소개합니다.
31+
이 글을 읽고 '나도 기여해볼 수 있겠다'라는 생각을 가지실 수 있기를 기대합니다.
32+
33+
# 왜 Spring AI에 기여를 하게 되었나
34+
35+
## AI 시대를 맞이한 개발자의 고민
36+
37+
AI 시대를 맞이하면서 백엔드 개발자로서 고민이 많았다. "나... AI 시대에 살아남을 수 있을까?" 라는 생각도 했고, 지금이라도 AI를 공부해야 하는 건 아닌지 고민했다.
38+
39+
백엔드 개발자로서, 어떻게 AI를 서비스에 통합할 수 있을지 이해하고 있으면 좋을 것이라고 생각했다. 그런데 현재 회사에서 하는 업무는 AI와 직접적인 관련이 없었다.
40+
41+
멘토님께서 이런 조언을 해주셨다.
42+
43+
> 회사에서 하지 않는다면, 직접 구축해서 학습해보면 되지 않겠나.
44+
45+
그래서 관련 프로젝트에 직접 기여를 해보기로 했다. 이슈를 해결해가면서 간접적으로 느껴보자는 마음으로 Spring AI에 기여를 시작했다.
46+
47+
# Spring AI 소개
48+
49+
Spring AI는 다양한 AI Provider를 쉽게 통합할 수 있도록 돕는 프로젝트이다. 다양한 프로바이더를 통해 AI Model을 제공받을 수 있는데, Spring AI는 이들을 하나의 일관된 인터페이스로 묶어준다.
50+
51+
# 오픈소스에 기여하는 법
52+
53+
기여는 생각보다 어렵지 않다. 다음 5단계로 이루어진다.
54+
55+
1. **이슈 생성 또는 선정** - 해결할 이슈를 찾거나 직접 생성한다.
56+
2. **구현 방향 논의** - 메인테이너와 어떻게 해결할지 논의한다.
57+
3. **구현 (코드 작성)** - 실제 코드를 작성한다.
58+
4. **PR 생성 및 코드 리뷰** - Pull Request를 올리고 리뷰를 받는다.
59+
5. **반영 (Merge)** - 코드가 머지된다.
60+
61+
# 기여 현황
62+
63+
M6부터 RC를 거쳐 정식 릴리즈(1.0.x)까지의 여정에 함께하였다. 약 40개의 PR을 남겼고, 기여 분류는 다음과 같다.
64+
65+
| 분류 | 비율 |
66+
| ------------ | ----- |
67+
| Model | 36.6% |
68+
| Vector Store | 26.8% |
69+
| Doc | 24.4% |
70+
| Testing | 9.8% |
71+
| MCP | 2.4% |
72+
73+
이 글에서는 Model과 Vector Store 기여 사례를 소개한다.
74+
75+
# Model 기여 사례
76+
77+
## AI Model 이란
78+
79+
AI Model은 방대한 정보 데이터 세트로 학습된 컴퓨터 프로그램 또는 알고리즘이다. 다양한 종류의 AI Model들이 있으며, 다양한 프로바이더를 통해 제공받을 수 있다.
80+
81+
## Ollama
82+
83+
Ollama는 로컬에 배포되는 AI 모델 실행기로, 사용자가 직접 대규모 언어 모델(LLM)을 다운로드하고 실행할 수 있도록 설계되었다.
84+
85+
### Ollama #1: keep-alive 음수값 버그 수정
86+
87+
**"왜 Keep-Alive를 -1로 넣으면 죽어요?"**
88+
89+
keep-alive 옵션은 모델이 메모리에 올라온 뒤, 얼마나 오랫동안 계속 유지될지를 정하는 설정이다. -1을 넣으면 모델이 계속 메모리에 유지되어야 하는데, 오류가 발생하고 있었다.
90+
91+
원인은 정규식에 있었다. 기존 정규식은 `\\d+`로 되어 있어 0 이상의 숫자만 입력 가능했다. 음수 값을 허용하지 않았던 것이다.
92+
93+
수정은 간단했다. `-?\\d+`로 변경하여 음수도 허용되도록 수정했다.
94+
95+
### Ollama #2: Builder 패턴 적용
96+
97+
Spring AI의 Model은 일정한 구조로 구성되어 있다. 어느 날 OpenAI 쪽에 Builder 패턴을 적용하는 PR이 올라왔다.
98+
99+
"이거 다른 곳에도 적용하면 좋을 것 같은데?" 라는 생각이 들었다.
100+
101+
빠르게 작업하여 OllamaApi에도 Builder 패턴을 적용하는 PR을 등록했다.
102+
103+
**AS-IS**: 각 상황별로 생성자를 직접 구현하는 방식이었다.
104+
105+
**TO-BE**: Builder 패턴을 사용하여 유연하게 설정할 수 있도록 변경했다.
106+
107+
### Ollama #3: 오래된 PR 머지로 인한 파일 경로 문제
108+
109+
"이 파일은 왜 혼자 덩그라니 있지...?"
110+
111+
6개월 동안 묵혀진 오래된 PR이 머지되면서 파일의 이전 경로에서 업데이트가 된 것이었다. 프로젝트 구조가 변경되면서 파일 경로가 바뀌었는데, 오래된 PR은 이전 경로 기준으로 작성되어 있었던 것이다.
112+
113+
| 이전 경로 | 새 경로 |
114+
| ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
115+
| `spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/ollama/` | `auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/` |
116+
117+
약 10일 동안 PR 작성자도, 메인테이너도 이 문제를 인지하지 못하고 있었다. CI 테스트에서는 코드에 영향이 없기 때문에 그냥 넘어간 것이다.
118+
119+
이동된 경로에서 변경 사항을 반영하는 PR을 생성했다. 코드 자체는 짧았으나, 업데이트한 내용으로 인해 문제가 없는지 테스트를 수행하는 데 많은 시간이 들었다.
120+
121+
#### Testcontainer 이야기
122+
123+
Spring AI에서는 Testcontainer를 사용한 통합 테스트를 적극적으로 활용한다. 일반적인 경우에는 크게 문제 없겠지만, Ollama의 경우 테스트할 때마다 새로운 Container가 띄워지면 매번 모델을 다시 다운로드해야 한다. 테스트는 얼마 안 걸리는데 모델을 다운로드하는 데 한세월 걸린다.
124+
125+
이를 방지하기 위해 CI 시에는 Testcontainer가 아닌 별도의 Ollama 컨테이너를 활용한다.
126+
127+
# Vector Store 기여 사례
128+
129+
## Vector Store 란
130+
131+
"우리 회사에서 정산과 관련된 파트의 담당자는 누구인가요?" 라는 질문을 AI에게 한다고 가정해보자.
132+
133+
이런 내부 정보는 AI 모델이 학습하지 않은 데이터이다. 이때 필요한 것이 바로 **검색 증강 생성(RAG, Retrieval-Augmented Generation)** 이다. RAG는 대규모 언어 모델의 출력을 최적화하여, 응답을 생성하기 전에 외부의 신뢰할 수 있는 기술 자료를 참조하도록 하는 프로세스이다.
134+
135+
예를 들어 사내 문서에 아래와 같은 정보가 있다고 하자.
136+
137+
> [업무 담당자 정리]
138+
> 개발 담당자 : 박종훈
139+
> 정산 담당자 : 김OO
140+
141+
이 문서를 벡터화하여 Vector Store에 저장해두면, "정산 담당자가 누구인가요?"라는 질문이 들어왔을 때 관련 문서를 검색하여 "정산과 관련된 파트의 담당자는 김OO입니다."라고 답변할 수 있게 된다.
142+
143+
Vector Store는 이러한 RAG에서 사용되는 벡터를 저장하는 역할을 한다. Spring AI는 다양한 Vector Store를 지원하고 있다.
144+
145+
## Elasticsearch: embedding field 하드코딩 문제
146+
147+
**"왜 embedding field가 하드코딩 되어있는 거에요?"**
148+
149+
Elasticsearch Vector Store의 코드를 살펴보니, 읽어올 때(doSimilaritySearch)와 기록할 때(doAdd) 모두 embedding 필드명이 하드코딩되어 있었다. 이 필드명을 dynamic하게 처리할 필요가 있었다.
150+
151+
### 메인테이너와의 논의
152+
153+
"record 말고 Map을 이용해서 key를 dynamic하게 처리하는 건 어떨까요?" 라고 제안했다.
154+
155+
메인테이너의 반응:
156+
157+
> 우리는 Map을 사용하는 것보다 구체적인 타입을 사용하는 방향을 선호해요. Record에서 Map으로 변경하려는 이유가 있을까요?
158+
159+
embedding 필드의 이름을 동적으로 받아야 한다고 설명하고, 더 좋은 방법이 있는지 물었다. 결국 메인테이너도 Map을 쓰면 쉽게 해결할 수 있다는 것에 동의했고, PR이 머지되었다.
160+
161+
### 이후 발생한 이슈: Document 구조 변경
162+
163+
PR이 머지된 후 어느 날 메일이 한 통 온다.
164+
165+
> ElasticSearch doSimilaritySearch broken after recent changes in Document
166+
167+
"너가 수정한 내용 때문에 toDocument 기능이 더 복잡해졌어 :-("
168+
169+
내 코드 때문에 문제가 발생한다면 어떻게 해야 할까? 우선 record로 처리하는 방식으로 PR을 새로 생성하고 물었다. "그럼 만족해?" 이슈 제기자는 "응, 도움이 될 것 같아"라고 했지만, 이어서 "그런데 문제는 끝나지 않았어"라고 했다.
170+
171+
"...? 그럼 이 분은 뭘 원하고 계신거지?"
172+
173+
### 문제 다시 파악하기
174+
175+
이슈 내용을 다시 정리해보니, serialize/deserialize 과정에서 에러가 발생 중이었다. 이슈 제기자에게 확인차 물어보았다.
176+
177+
> 옛날 버전에서 저장한 데이터를 새 버전에서 불러오려는 거야? 아니면 새 버전에서 저장한 데이터를 새 버전에서 불러왔을 때도 문제가 있다는 거야?
178+
179+
> 두 케이스 모두 에러가 발생되고 있어. 저장할 때는 "content" 프로퍼티로 저장되어 있는데 불러올 때는 "text" 프로퍼티로 불러와지고 있어서 그래.
180+
181+
기존에는 `content`라는 필드명을 사용하고 있었는데, 미디어에 대한 임베딩 처리가 요구되면서 `text` 필드와 `media` 필드로 나눠지게 된 것이 원인이었다. 기존 코드와의 호환성을 위해 데이터 매핑 시에는 `content`라는 필드를 유지하고 있었지만, 실제로는 필드 불일치가 발생하고 있었던 것이다.
182+
183+
확인해보니 이슈 제기자는 과거 버전(M5 이전)을 사용 중이셨던 것으로 보였고, 최신 버전으로 마이그레이션 시 에러가 발생하고 있었다.
184+
185+
두 케이스 모두 확인한 후 답변했다.
186+
187+
> 첫 번째 케이스는 필요하다면 데이터 마이그레이션을 하면 될 것 같고, 두 번째 케이스는 정상적으로 데이터가 저장되고 불러오도록 처리되어 있어. 혹시 문제가 되는 코드를 제공해줄 수 있어?
188+
189+
최종적으로 이슈 제기자도 "너의 말이 맞아, 데이터 마이그레이션이 필요하겠다"라며 동의하면서 해결되었다.
190+
191+
# 기여하면서 느낀 것
192+
193+
## AI 업계에 대해서
194+
195+
- **빠른 변화**: 많은 기술들이 나오지만 살아남는 게 쉽지 않다. 얼마 지나지 않아 또 새로운 기능이 나온다.
196+
- **대세의 중요성**: 하나의 확실한 대세가 생기고 나면 그 후에 다른 것들은 쉽지 않다. 대부분의 Model API에서 OpenAI 호환 API를 제공하고 있으며, 작은 규모의 프로젝트에서는 이 전략이 맞을지도 모른다. Gemini도 결국 OpenAI 호환 API를 제공하게 되었다. MCP 출시 후 A2A도 나왔지만 여전히 주도권에서는 다소 뒤처진 모습이다.
197+
- **돈이 많으면 간단하게 갈 수 있다**: 리소스가 충분하다면 선택지가 넓어진다.
198+
- **RAG는 생각보다 잘 되지 않는다**: 다양한 방법을 연구해야 한다.
199+
- **핫한 것 vs 실제 사용**: 핫한 것과 실제 사용되는 것은 다른 이야기이다. "MCP는 쓰는 사람보다 만드는 사람이 많은 유일한 기술일 것이다."
200+
201+
## 커리어 관점에서
202+
203+
- **글로벌 협업 경험**: 다른 회사는 어떻게 일할까? 빅테크, 글로벌 IT 기업은 어떻게 일할까? 오픈소스 기여를 통해 실제로 경험해볼 수 있는 기회가 된다. "이 사람들도 사람이구나"라고 느낄 수도 있다.
204+
- **프로젝트 방향성 체감**: 해당 프로젝트의 방향성을 직접 느껴볼 수 있다. 기여를 하다 보면 또 다른 기여와 연결되고, 프로젝트에 참여하면서 나의 지식도 확장된다. 프로젝트의 깊은 부분까지 알아볼 수 있으며, 내 의견을 직접 어필할 수 있는 기회가 된다.
205+
- **도움을 주는 입장으로 전환**: 도움만 받는 입장에서 도움을 주는 입장으로 전환된다. 기여한 코드로 다른 개발자들의 편의성을 올려줄 수 있고, 리뷰어로 다른 기여자들에게 도움을 드리기도 하며, 직접 이슈를 남기기도 한다.
206+
207+
# 마무리
208+
209+
왜 Spring AI에 기여를 하게 되었고, 어떻게 기여하였는지 사례를 소개하였다.
210+
211+
'나도 기여해볼 수 있겠다'라고 생각이 드셨다면, 여러분들도 Spring AI에 기여해보시길 기대한다. [오픈소스 기여모임](https://litt.ly/opensource)에 참여해보시는 것도 좋은 방법이다. 다양한 프로젝트에 함께 기여하고 있다.

0 commit comments

Comments
 (0)