반응형
반응형
📝 오늘 배운 것: Node.js Express 앱에 Gemini AI 연동하기 학습 기록
오늘은 웹 브라우저에서 질문을 입력하면 Gemini AI가 답변을 해주는 간단한 웹 애플리케이션을 만들고 실행하는 과정을 배우셨습니다. 이 과정을 통해 웹 개발의 기초와 AI API 연동의 핵심 개념을 익히셨습니다.
1. 💻 Express.js 프로젝트 생성하기
웹 애플리케이션을 만들기 위한 기본적인 뼈대(구조)를 만드는 작업입니다.
- 개념:
Express.js는 Node.js 환경에서 웹 애플리케이션을 더 쉽고 빠르게 만들 수 있도록 도와주는프레임워크(Framework)입니다. 미리 정해진 규칙과 도구들을 제공해서 복잡한 웹 서버 코드를 직접 다 작성할 필요 없이 효율적으로 개발할 수 있게 해줍니다. - 사용 도구:
Express 제너레이터(Express Generator)를 사용했습니다. 이것은 Express 프로젝트의 기본 폴더와 파일들을 자동으로 만들어주는 도구입니다. - 명령어:
npx express --view=ejs express-gemini-appnpx:Node Package Execute의 줄임말로, 로컬에 설치되지 않은 Node.js 패키지(여기서는 Express 제너레이터)도 바로 실행할 수 있게 해주는 편리한 도구입니다.express: Express 제너레이터를 호출하는 명령어입니다.--view=ejs: 웹 페이지의 디자인을 담당하는템플릿 엔진으로EJS (Embedded JavaScript)를 사용하겠다고 지정하는 옵션입니다. EJS는 HTML 코드 안에 자바스크립트 코드를 넣어 동적인 웹 페이지를 만들 수 있게 해줍니다.express-gemini-app: 여러분이 만들 웹 애플리케이션 프로젝트의 이름입니다. 이 이름으로 새로운 폴더가 생성되고, 그 안에 모든 프로젝트 파일들이 들어갑니다.
- 오늘 배운 점 (오류 해결):
- [cite_start]
npm error could not determine executable to run: 이 오류는npx가express-generator를 찾지 못할 때 발생할 수 있습니다. [cite: 5]- 해결책:
npm install -g express-generator명령어로 Express 제너레이터를 시스템에글로벌(-g)하게 미리 설치해주면 해결됩니다.
[cite_start]
package.json파일 누락: 프로젝트 생성 명령이 제대로 완료되지 않으면package.json파일이 없을 수 있습니다. [cite: 5] 이 파일은 프로젝트의 설정과 필요한 부품(패키지) 목록을 담고 있습니다.- 해결책: 기존에 불완전하게 생성된 프로젝트 폴더를 삭제하고,
npx express명령을 다시 실행하여 프로젝트를 깔끔하게 재설정합니다.
[cite_start]
'Express\express-gemini-app' 인수를 허용하는 위치 매개 변수를 찾을 수 없습니다.:cd명령어로 폴더 이동 시, 폴더 이름에 공백이 포함되어 있으면 발생하는 오류입니다. [cite: 5]
[cite_start]
- 해결책:
cd "C:\Users\김광수\Desktop\파이썬\1.4 Express\express-gemini-app"와 같이 경로 전체를 큰따옴표로 묶어주면 공백이 포함된 경로도 정확히 인식합니다. [cite: 5]
2. 📦 프로젝트에 필요한 부품(패키지) 설치하기
만들어진 웹 애플리케이션이 동작하기 위해 필요한 추가적인 부품들을 설치하는 작업입니다.
- 개념: Node.js 프로젝트는 다양한 기능을 제공하는
패키지(Package)들을 사용합니다. 이 패키지들은npm(Node Package Manager)이라는 도구를 통해 설치하고 관리합니다.package.json파일에는 우리 프로젝트가 어떤 패키지들을 필요로 하는지 목록이 적혀 있습니다. - 명령어:
npm install- 이 명령어는
package.json파일을 읽어서 프로젝트에 필요한 모든 패키지들을node_modules라는 폴더 안에 자동으로 다운로드하여 설치해줍니다.
- 이 명령어는
- 오늘 배운 점 (오류 해결):
- [cite_start]
npm error code EJSONPARSE: 이 오류는package.json파일의JSON문법이 잘못되었을 때 발생합니다. [cite: 1] [cite_start]특히JSON파일 안에는 주석(//또는/* */)을 사용할 수 없습니다. [cite: 1]- 해결책:
package.json파일 안에 있는 모든 주석을 제거해야 합니다. [cite_start]예를 들어,"@google/generative-ai": "^0.1.3" // 이 부분을 추가하거나 변경합니다.와 같은 줄이 있다면// 이 부분을 추가하거나 변경합니다.를 지워야 합니다. [cite: 1]
[cite_start]
Error: Cannot find module 'dotenv':dotenv모듈을 찾을 수 없다는 오류입니다. [cite: 2]dotenv는.env파일(환경 변수 파일을 만드는 용도)에 있는 중요한 정보(API 키 등)를 우리 프로그램이 읽을 수 있게 해주는 패키지입니다.
[cite_start]
- 해결책:
npm install dotenv명령어로dotenv패키지를 설치해주면 됩니다. [cite: 2]
3. ⚙️ .env 파일로 중요 정보(API 키) 관리하기
우리 프로그램에서 사용할 중요한 정보인 Gemini API 키를 안전하게 보관하고 불러오는 방법입니다.
- 개념:
환경 변수(Environment Variable)는 프로그램이 실행되는 환경에 따라 값이 달라질 수 있는 변수입니다. API 키와 같은 민감한 정보는 코드 안에 직접 넣는 대신,.env파일이라는 별도의 파일에 저장하고 환경 변수로 불러와 사용하는 것이 일반적입니다. 이렇게 하면 코드를 다른 사람과 공유해도 API 키가 노출되지 않아 안전합니다. - 작업:
- 프로젝트의 가장 바깥 폴더(
express-gemini-app) 안에.env라는 이름의 파일을 만듭니다. (파일 탐색기에서 직접 만들면 됩니다.) .env파일을 열고 다음과 같이 Gemini API 키를 저장합니다.GOOGLE_API_KEY=여러분이_발급받은_실제_Gemini_API_키=양쪽에 공백이 없도록 주의해야 합니다.여러분이_발급받은_실제_Gemini_API_키는 구글 AI 스튜디오 등에서 직접 발급받은 문자열입니다.
- 프로젝트의 가장 바깥 폴더(
4. 🚀 웹 애플리케이션 실행하고 확인하기
우리가 만든 웹 애플리케이션이 웹 서버로서 제대로 동작하는지 확인하는 작업입니다.
- 명령어:
npm start- 이 명령어를 실행하면 Node.js 환경에서 Express 웹 서버가 시작됩니다. 서버는 기본적으로
3000번 포트에서 클라이언트의 요청을 기다립니다.
- 이 명령어를 실행하면 Node.js 환경에서 Express 웹 서버가 시작됩니다. 서버는 기본적으로
- 확인:
- 웹 브라우저를 열고 주소창에
http://localhost:3000을 입력하여 접속합니다.
[cite_start]
Gemini Chat App이라는 제목과 함께 프롬프트를 입력할 수 있는 공간(Prompt:)과Submit버튼이 보이면 성공입니다. [cite: 3]
- 웹 브라우저를 열고 주소창에
- 오늘 배운 점 (오류 해결):
- [cite_start]
ERR_CONNECTION_REFUSED(사이트에 연결할 수 없음): 웹 서버가 실행되지 않았거나, 방화벽 등의 이유로 연결이 거부될 때 나타납니다. [cite: 4]- 해결책:
- 터미널에
npm start실행 후 오류 메시지가 없는지 확인: 서버가 제대로 시작되었는지 먼저 확인합니다.
[cite_start]
- 방화벽 해제 또는 규칙 추가: Windows
방화벽이3000번 포트로의 접속을 막고 있을 가능성이 큽니다. [cite: 4]- [cite_start]
- 임시 해제 후 테스트: Windows 방화벽을 임시로
사용 안 함으로 설정한 후 접속을 시도하여 문제가 방화벽 때문인지 확인합니다. [cite: 4] - 인바운드 규칙 추가: 테스트 후 방화벽을 다시 활성화하고,
인바운드 규칙을 추가하여TCP 포트 3000에 대한 연결을허용하도록 설정합니다.
- 임시 해제 후 테스트: Windows 방화벽을 임시로
- 터미널에
5. 🤖 Gemini AI 연동 코드 수정하기 (routes/index.js)
이제 웹 애플리케이션의 핵심인 AI 기능을 추가하는 작업입니다. 사용자로부터 받은 질문을 Gemini AI에 보내고, 답변을 받아 웹 페이지에 표시하는 코드를 작성합니다.
- 개념: Express 앱에서는
라우터(Router)라는 것을 사용하여 특정 웹 주소(GET /,POST /등)로 들어오는 요청을 처리합니다. 우리는routes폴더 안의index.js파일에서 이러한 요청들을 처리하는 코드를 작성했습니다. - 주요 코드 (핵심!):
var express = require('express'); var router = express.Router(); // Gemini API를 위한 Google Generative AI SDK import const { GoogleGenerativeAI } = require('@google/generative-ai'); // dotenv 라이브러리를 사용하여 .env 파일의 환경 변수를 로드 require('dotenv').config(); // .env 파일에서 Gemini API 키 가져오기 const GEMINI_API_KEY = process.env.GOOGLE_API_KEY; // GoogleGenerativeAI 인스턴스 초기화 // 사용 가능한 모델 이름(예: gemini-pro, gemini-2.0-flash 등)을 확인하여 지정 const genAI = new GoogleGenerativeAI(GEMINI_API_KEY); const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" }); // 사용 가능한 모델로 변경 /* GET home page. */ router.get('/', function(req, res, next) { res.render('index', { title: 'Gemini Chat App', question: null, result: null }); }); /* POST request from form. */ router.post('/', async function(req, res, next) { const prompt = req.body["prompt"]; let resultText = null; if (prompt) { try { resultText = await accessGeminiAPI(prompt); } catch (error) { // 여기에서 상세 오류 로깅을 수행합니다. console.error("Gemini API 호출 중 상세 오류:", error); if (error.response) { // error.response 객체가 존재하는지 확인 console.error("HTTP 상태 코드:", error.response.status); console.error("오류 응답 데이터:", error.response.data); if (error.response.data && error.response.data.error && error.response.data.error.message) { console.error("Gemini API 오류 메시지:", error.response.data.error.message); } } else if (error.message) { // 일반적인 JavaScript 에러 메시지 console.error("일반 오류 메시지:", error.message); } resultText = "죄송합니다. AI 응답을 가져오는 중 오류가 발생했습니다. 터미널을 확인하세요."; } } res.render('index', { title: 'Gemini Chat App', question: prompt, result: resultText }); }); // Gemini API 접근 함수 async function accessGeminiAPI(userPrompt) { const chat = model.startChat({ history: [], // 여기서는 이전 대화 없이 단일 요청으로 처리합니다. generationConfig: { maxOutputTokens: 200, // 최대 응답 토큰 수 설정 }, }); const result = await chat.sendMessage(userPrompt); const response = await result.response; const text = response.text(); return text; } module.exports = router; - 오늘 배운 점 (오류 해결):
- [cite_start]
title is not defined:index.ejs템플릿에title변수를 넘겨주지 않아서 발생하는 오류입니다. [cite: 5]- 해결책:
res.render('index', { title: 'Gemini Chat App', ... });와 같이title값을index.js에서 명시적으로 전달합니다. API key not valid. [cite_start]Please pass a valid API key.: Gemini API 키가 유효하지 않거나 잘못 입력되었을 때 발생합니다. [cite: 6]- 해결책: Google AI Studio에서 API 키를 다시 발급받거나,
.env파일에 정확하게 복사되었는지 꼼꼼히 확인합니다.
[cite_start]
Error fetching from ... [400 Bad Request]: API 호출이 유효하지 않거나, 사용하려는 모델(gemini-pro등)이 현재 API 키로 접근할 수 없는 모델일 수 있습니다. [cite: 6]- 해결책: Google AI Studio에서 현재 사용 가능한 모델 이름을 확인하고,
index.js파일의genAI.getGenerativeModel({ model: "사용_가능한_모델명" });부분을 정확히 수정합니다.
6. 🎨 웹 페이지 디자인 파일 수정하기 (views/index.ejs)
사용자가 질문을 입력하고 답변을 볼 수 있는 웹 페이지의 모습을 만드는 작업입니다.
- 개념:
EJS 템플릿 파일(.ejs)은 HTML 코드와 자바스크립트 코드를 함께 사용하여 동적인 웹 페이지를 만듭니다. 서버(index.js)에서 전달받은title,question,result같은 값들을 이 파일에서 받아서 웹 페이지에 표시합니다. - 주요 코드:
<!DOCTYPE html> <html lang="ko"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title><%= title %></title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous"> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body class="container"> <h1 class="display-6 py-2"><%= title %></h1> <form method="POST" action="/"> <div> <label for="prompt">Prompt:</label> <textarea id="prompt" name="prompt" class="form-control"></textarea> </div> <center class="py-3"> <input type="submit" value="Submit" class="btn btn-primary"> </center> </form> <% if (question != null) { %> <p class="border border-2 p-3 h6 mt-3"><%= question %></p> <% } %> <% if (result != null) { %> <p class="border border-2 p-3 h6 mt-3"><%= result %></p> <% } %> </body> </html> - 오늘 배운 점:
<form>태그: 사용자가 데이터를 입력하고 서버로 전송할 수 있는 양식을 만듭니다.method="POST"와action="/"을 통해index.js의router.post부분으로 데이터가 전송되도록 합니다.<textarea id="prompt" name="prompt">: 여러 줄의 텍스트를 입력할 수 있는 공간입니다.name="prompt"속성은 서버에서 이 입력값을req.body.prompt로 가져올 수 있게 해줍니다.<input type="submit" value="Submit">: 양식에 입력된 데이터를 서버로 제출하는 버튼입니다.<%= 변수명 %>: EJS에서 서버(index.js)로부터 전달받은변수명의 값을 웹 페이지에 표시할 때 사용하는 문법입니다.<% if (조건) { %> ... <% } %>: EJS에서 특정조건이 참일 때만 해당 HTML 코드를 표시하도록 하는 제어문입니다. 질문과 답변이 있을 때만 화면에 나타나게 하는 용도로 사용되었습니다.
오늘의 학습 목표를 달성하셨습니다! 이제 이 정리 내용을 티스토리에 잘 기록해 두시면 훌륭한 학습 자료가 될 것입니다. 코딩 여정을 계속 응원하겠습니다!
반응형
'dev' 카테고리의 다른 글
| AI와 함께 보고용 PPT 제작기: Gemini와 skywork.ai 활용법 (0) | 2025.07.09 |
|---|---|
| 🎾 테니스 NTRP 레벨 테스트 (0) | 2025.07.06 |
| 🎾 누구나 따라 할 수 있는 테니스 NTRP 레벨 테스트 웹 앱 만들기 가이드 (1) | 2025.07.06 |
| Gemini API를 활용한 슬랙 챗봇 제작 가이드 (7) | 2025.07.06 |
| 파이썬 Flask와 Google Gemini API 연동 개발 가이드: 초보자도 쉽게 따라하기 (0) | 2025.07.06 |