AI 코드 리뷰어 개발 및 배포 여정 [1]
작성일: 2025.12.09
1. 프로젝트 개요: LLM과 배포 여정의 교차로에서
💡 프로젝트 탄생 배경 및 동기
이번 프로젝트는 단순히 코드를 짜는 것을 넘어, 제가 경험했던 기술적 아쉬움과 새로운 기술에 대한 호기심이 결합된 결과물입니다.
-
LLM 실전 경험 : 최근 가장 핫한 기술인 LLM을 가지고 '진짜 작동하는' 토이 프로젝트를 만들어보고 싶었습니다.
-
미완의 서비스에 대한 아쉬움 : 과거 GitHub API를 써서 포트폴리오 서비스를 만들다가 중단했던 아쉬움이 남아 이번에 다시 MVP라도 GitHub API를 활용해 그 경험을 완성하고자 했습니다.
-
클라우드 배포 최적화 학습 : 단순 코딩을 넘어, Vercel, NCP, SSL/ALB 구성을 결합해서 배포 환경을 구축하는 것을 핵심 목표로 삼았습니다.
🤖 Why Gemini? (Ollama -> Gemini)


과거에 저는 한 기술 블로그에서 Ollama를 접하고 흥미를 느꼈었고 언젠가 꼭 사용해보겠다고 생각했었습니다.
그래서 이번에 저비용으로 경량 LLM 모델을 직접 호스팅 해보자는 생각에 Ollama 기반의 온프레미스 구성을 첫 번째 선택지로 고려했습니다.
| 항목 | Ollama (Self-Hosted) | Gemini 2.5 Flash API |
|---|---|---|
| Resource 의존도 | 매우 높음 (GPU 필수) | 낮음 (API 서버에서 처리) |
| 운영 비용 | VM(GPU) 시간당 과금 (매우 비쌈) | 토큰 사용량 기반 과금 (저렴하고 명확함) |
| 속도 | 느림 (VM 성능에 의존) | 빠름 (API 응답 속도 우수) |
하지만 테스트 결과, Ollama는 GPU 의존도가 너무 높고 무거워서 제가 비용상 사용할 NCP VM 인스턴스(저코스트)에서 안정적인 서비스 속도를 보장할 수 없었습니다. GPU 옵션을 추가하면 VM 비용이 시간당 몇 천 원으로 급증하는 문제도 있었습니다.
결국, 속도가 빠르고 GPU 의존도가 없으며, 비용 구조가 토큰 기반으로 명확한 Gemini 2.5 Flash API를 사용하는 것이 최적의 선택이라 판단하고 변경했습니다.
☁️ Why NCP?
지금까지의 배포 경험은 AWS에만 집중되어 있었기 때문에 이번 기회에 타 클라우드 환경을 경험해보고 싶다는 생각이 컸습니다.
-
AWS 프리티어 만료 : 기존에 사용하던 AWS 프리티어가 만료되면서 적지 않을 서버 유지 비용에 대한 부담이 생겼습니다.
-
NCP 크레딧 활용 : 마침 NCP에서 신규 가입 시 제공하는 10만 원 상당의 크레딧이 있었고 유효기간 내에 새로운 클라우드 환경을 경험하기 위해서 선택했습니다.
💻 주요 기술 스택
이 프로젝트는 아래 기술 스택을 기반으로 성능과 개발 효율성을 모두 고려하여 구축되었습니다.
| 구분 | 기술 스택 | 설명 |
|---|---|---|
| Frontend | React, Vite | 빠른 UI 및 GitHub OAuth 2.0 흐름 제어 |
| Backend | JDK 21, Spring Boot 4.0, Spring Security | GitHub OAuth 처리, Gemini API 호출 및 비즈니스 로직 |
| AI Service | Google Gemini 2.5 Flash API | 코드 리뷰, 채팅 및 분석 리포트 생성 |
| Hosting 비용 | VM 시간당 약 330원, ALB 월 약 2.5만원, Domain 550원 | 비용을 최소화하며 인프라를 구축 |
🏗️ 아키텍쳐
🚀 왜 Vercel과 NCP를 함께 썼을까?
저의 백엔드 서버가 NCP에 있음에도 프론트엔드를 Vercel에 배포한데는 몇가지 이유가 있습니다. 바로 개발 경험과 사용자 경험 극대화입니다.
- 극대화된 개발 생산성: Vercel은 GitHub와 연동만 해두면 코드를 푸시할 때마다 자동 빌드 및 배포를 해줍니다(자동사냥 CI/CD).
- 글로벌 CDN 성능: Vercel의 글로벌 CDN 덕분에 UI 파일 로딩 속도가 빠릅니다.
- 자동 SSL: Vercel이 프론트엔드 도메인에 대해 자동으로 HTTPS 인증서를 발급해 주어 보안의 기본을 잡아주었습니다.
2. 실습 배포 과정: NCP와 도메인 연결하기
2.1 NCP 네트워크 환경 구성 (VPC, Subnet, ACG)
NCP에 서버를 띄우기 전 VPC 기반의 네트워크 환경을 먼저 구축했습니다. VPC와 Subnet 생성 자체는 과금이 되지 않기 때문에 부담 없이 설정했습니다.
- VPC 생성:
git-code-reviewer-vpc이름으로 가상 네트워크를 정의했습니다. - Subnet 생성: 당연히 외부 통신이 가능한 Public Subnet을 선택했고
public-subnet-1을 생성했습니다. - ACG 설정:
code-reviewer-acg를 생성하여 SSH(22) 및 SpringBoot 포트(8080) 접근을 위한 인바운드 규칙을 마련했습니다.
2.2 VM 인스턴스 스펙 및 공인 IP 할당
VPC와 Subnet을 기반으로 Spring Boot 서버를 호스팅할 VM을 생성했습니다.
| 항목 | 상세 정보 | 비고 |
|---|---|---|
| 서버 스펙 | s2-g3a (vCPU 2EA, Memory 8GB) | Spring Boot 구동에 안정적인 사양 (시간당 330원) |
| OS | Ubuntu 22.04 base | 익숙한 리눅스 환경 선택 |
| 내부 IP | 10.0.1.6 | Subnet에 의해 자동 할당되는 사설 주소 |
| 공인 IP | 175.xx.xx.xx | VM 생성 시 할당하여 ALB 설정 전까지 직접 접속 및 테스트용으로 사용했습니다. |
2.3 백엔드 빌드 및 실행 (NCP)
NCP 인스턴스에 Spring Boot JAR 파일을 올리고 실행하는 과정입니다. 보안을 위해 .env.properties 파일을 생성해 API 키와 프롬프트 같은 민감 정보를 분리했습니다.
- 빌드: 로컬에서 Gradle로 JAR 파일을 만듭니다.
./gradlew clean build -x test - 파일 전송:
scp명령어로 JAR 파일과.env.properties를 NCP 서버의/root디렉터리로 전송합니다.scp git-code-reviewer-0.0.1-SNAPSHOT.jar root@[서버 공인 IP]:/root/ - 애플리케이션 실행:
.env.properties파일을 명시적으로 로드하면서nohup으로 백그라운드 실행합니다.# NCP 서버 접속 후 실행:
nohup java -jar git-code-reviewer-0.0.1-SNAPSHOT.jar --spring.config.location=file:./.env.properties > nohup.out 2>&1 &
2.4 SSL 주입: Mixed Content 해결 여정
Vercel(HTTPS)에서 NCP(HTTP 8080)로 API를 호출하자마자 당연히 Mixed Content Error가 발생했고 저는 필수로 HTTPS 환경을 구축했고 ALB를 생성했습니다.
-
도메인 구매: 도메인 비용도 최소로 하기 위하여 이쁘진 않지만 가비아에서
code-reviewer.store도메인을 구매했습니다 (단돈 550원). -
SSL 인증서 발급: NCP Certificate Manager에서 SSL 인증서 발급을 요청하고 확인 후 가비아 DNS 설정에 CNAME 레코드를 입력하여 소유권을 검증했습니다.
-
Application Load Balancer (ALB) 설정:
- 리스너 설정: 외부의 HTTPS 443 요청을 받도록 설정하고, SSL 인증서를 연결했습니다.
- 타겟 그룹 생성: Spring Boot 서버의 내부 IP(
10.0.1.6)와 HTTP 8080 포트를 연결하는 타겟 그룹을 생성했습니다.
-
DNS A 레코드 최종 설정:
- ALB가 할당된 공인 IP를 확인했습니다.
- 가비아 DNS로 돌아가,
api.code-reviewer.store도메인의 A 레코드 값을 이 ALB의 공인 IP로 변경했습니다.
2.5 프론트엔드 수정 및 최종 배포
-
Frontend API URL 수정:
Dashboard.js파일에서 API 호출 주소를 새로운 HTTPS 도메인으로 변경했습니다.// 변경 전: '[http://175.45.204.50:8080/api](http://175.45.204.50:8080/api)'
// 변경 후: '[https://api.code-reviewer.store/api](https://api.code-reviewer.store/api)'
const api = axios.create({ baseURL: '[https://api.code-reviewer.store/api](https://api.code-reviewer.store/api)', ... }); -
물론 기존 localhost로 되어있던 Github Callback URL도 맞춰서 다시 수정해줬습니다!
- Vercel 배포: 코드를 푸시하여 Vercel을 재배포하는 것으로 마침내 HTTPS 통신 환경이 완성되었습니다.
2.5 NCP 인프라 구축 핵심 과정 요약
1. VPC & Subnet 생성
- 주요 작업 내용:
git-code-reviewer-vpc와public-subnet-1을 생성합니다.- 목적: 외부 인터넷과 격리된 가상 네트워크 공간을 정의하고, 서버를 배치할 구체적인 IP 대역을 마련하는 기초 공사 단계입니다.
2. Server (VM) 생성
- 주요 작업 내용:
my-server-reviewer(Ubuntu 22.04, s2-g3a) 인스턴스를 생성합니다.- 목적: Spring Boot 애플리케이션을 호스팅할 컴퓨팅 자원을 확보합니다.
3. 초기 접속 경로 확보: Public IP 할당
- 주요 작업 내용: VM에 공인 IP(
175.45.204.50)를 할당합니다.- 목적: ALB 설정 전, SSH 접속을 하거나 애플리케이션 테스트를 진행하기 위한 임시 경로를 확보합니다.
4. 방화벽 설정: ACG & UFW 구성
- 주요 작업 내용: NCP ACG와 서버 내부 UFW에 8080 포트 인바운드 허용 규칙을 설정합니다.
- 목적: 서버로 들어오는 트래픽에 대한 방화벽을 설정하여 보안을 강화합니다.
5. Certificate Manager
- 주요 작업 내용: 도메인(
code-reviewer.store)을 등록하여 SSL 인증서를 발급받습니다.- 목적: HTTPS 접속을 위한 보안 인증서를 확보하여, Mixed Content 에러를 해결할 준비를 합니다.
6. Application Load Balancer 생성
- 주요 작업 내용: ALB 인스턴스를 생성하고 공인 IP를 할당합니다.
- 목적: 서버 부하를 분산하고, 가장 중요하게는 SSL/HTTPS 종료를 담당하는 트래픽 관리 계층을 구축합니다.
7. 최종 라우팅 정의: ALB Listener & Target Group 연결
- 주요 작업 내용: Listener(443/HTTPS)를 Target Group(8080/HTTP)과 연결합니다.
- 목적: 외부의 HTTPS 요청을 ALB에서 받아 암호 해제 후, 내부 서버의 HTTP로 안전하게 중계하도록 라우팅을 최종 설정합니다.
3. 트러블슈팅
해결
| 문제 상황 | 해결책 | 교훈 |
|---|---|---|
| Mixed Content Error | 도메인 구매 후 NCP ALB에 SSL 인증서를 설정하고, API 호출 주소를 https://[도메인]으로 변경. | 돈이 들더라도 SSL Termination은 로드 밸런서에게 맡기는 것이 가장 안정적인 실무 공식입니다. |
| GitHub Callback URL 오류 | GitHub OAuth App 설정을 최종 주소인 https://api.code-reviewer.store/callback 형태로 업데이트하여 보안 표준을 준수했습니다. | OAuth 표준에서는 인증 과정에 IP 주소나 HTTP를 사용하지 않습니다. |
| Vercel "Not Found" | 프로젝트 루트에 vercel.json 파일을 생성하여 /auth/callback 같은 클라이언트 라우팅 경로를 index.html로 리다이렉트하는 설정을 추가. | React Router 같은 SPA를 정적 호스팅 플랫폼에 배포할 때는 라우팅 폴백 설정이 필수입니다. |
임시 방편 및 리팩토링 필요 항목
이 항목은 임시 조치이며, 추후 반드시 보안 표준에 맞게 수정해야 합니다.
| 문제점 | 일시적 해결책 | 실무적 개선 방향 (필수 리팩토링) |
|---|---|---|
| HTTP/8080 포트 공개 | NCP ACG에서 TCP 8080 포트를 0.0.0.0/0 (전체 외부 접근)에 대해 인바운드 허용. | ALB만 허용: ACG 규칙을 수정하여 8080 포트는 ALB의 Private IP 대역에서만 접근 가능하도록 제한해야 서버로의 직접 접근을 막아 보안성을 높입니다. |
4. 회고 및 다음 단계
Spring Boot 코드를 짜는 시간보다(어차피 Open API 사용이 대부분이었기에...) NCP 콘솔에서 서버 올리고 ACG와 DNS 레코드를 확인하는 시간이 훨씬 길었던 여정이었지만 꽤 재밌었습니다.
동시에 몇가지 배운것도 많은데
-
SSL Termination의 필요성: Vercel에서 HTTP 백엔드를 호출할 수 없다는 문제 하나가 ALB 도입이라는 월 2.5만 원짜리 결정으로 이어지는 과정을 보면서, 서비스의 안정성과 보안은 곧 비용으로 직결된다는 현실을 배웠습니다.
-
클라우드 플랫폼의 복잡성: AWS와 달리 NCP의 VPC, Subnet, ALB를 처음부터 설계하고 연결하는 과정은 복잡했지만, 덕분에 클라우드 네트워크에 대한 이해도가 한 단계 깊어졌습니다.
다음 단계에서는 현재 휘발되는 AI 리뷰 결과를 저장하고 관리하기 위해 DB를 도입하여 서비스를 더욱 고도화하고 기능도 몇가지 더 추가할 계획입니다! 계속 확인해주세요 🤗