Ⅰ. 오전 수업
A. 1교시
1. 지난 시간 복습
2. 라우터 쪼개기
B. 2교시
1. app.js에 esportRouter 등록하기
2. Database: MySQL
3. MySQL Workbench
C. 3교시
1. MySQL Workbench (cont.)
Ⅱ. 오후 수업
A. 4교시
1. LangSmith
B. 5교시
1. LangChain 평가 지표
2. ROUGE Score
C. 6교시
1. BLEU 지표
2. METEOR 지표
Ⅲ. CAREER UP
정보보호교육
req.query에 객체 형태로 자동 저장됨req.body에 자동 저장됨const bp = require("body-parser");
app.use(bp.urlencoded({extended:true}));
// POST 데이터 처리
app.post("/postLogin",(req,res)=>{
console.log(req.body);
});
const express = require("express");
const app = express();
const bp = require("body-parser");
app.use(express.static("public"));
app.use(bp.urlencoded({extended:true}));
// mainRouter.js
const express = require("express");
const router = express.Router(); // express의 여러 기능 중 Router만 가져가기
const path = require("path");
const filePath = path.join(__dirname,"../public");
router.get("/",(req,res)=>{
res.sendFile(filePath+"/main.html");
});
router.get("/baseball",(req,res)=>{
res.sendFile(filePath+"/baseball.html");
});
router.get("/soccer",(req,res)=>{
res.sendFile(filePath+"/soccer.html");
});
module.exports = router;
// app.js
const express = require("express");
const app = express();
const bp = require("body-parser"); // 내장모듈&설치모듈은 이름만 가지고 오면 됨
// 내가 제작한 모듈을 호출할 때는 반드시 경로를 확인하자!
const mainRouter = require("./routes/mainRouter"); // 내가 만든 모듈은 상대경로 써야 함
app.use(express.static("public"));
app.use(bp.urlencoded({extended:true}));
app.use("/",mainRouter); // ※ 사용등록 필수 ※
// 3000번 포트 뒤 경로 확인하고 "/"로 한번에 들어가는 구조라면 mainRouter 사용하겠다는 뜻
app.listen(3000);
→ app.get에서 router.get으로: 업무는 동일하지만 실행하는 주체가 바뀜
__dirname: 현재 실행 중인 파일을 포함하는 폴더까지가 기준const path = require("path");const filePath = path.join(__dirname,"../public"); → res.sendFile(filePath+"/main.html");
res.sendFile(filePath+"/esports.html");
문제점:
a. LOL 누르면 /esports/lol로 가는 게 아니라 /lol로 이동함 (두 번 들어가는 경로인데 한 번만 들어가는 걸로 나옴)
b. 이렇게 만들면 경로 그룹화가 안 됨(mainRouter.js가 다 하니까)
esports 그룹 만들기 → 경로에 esports 추가

라우터 쪼개기: esportsRouter.js 만들기

const express = require("express");
const router = express.Router();
const path = require("path");
const filePath = path.join(__dirname,"../public");
// esportsRouter는 app.js에서 한번 경로를 걸러서 들어온다 → /esports 경로가 사라진다.
router.get("/",(req,res)=>{
res.sendFile(filePath+"/esports.html");
});
router.get("/lol",(req,res)=>{
res.sendFile(filePath+"/lol.html");
});
router.get("/valorant",(req,res)=>{
res.sendFile(filePath+"/valorant.html");
});
module.exports = router;




큰 부서 안의 작은 하위부서, 혹은 대분류-소분류를 보는 느낌으로 이해하면 됨
Q. 하위 라우터를 위에 배치하라고 하던데 우리는 하위 라우터가 아래 있네요?
A. 우리가 짠 코드는 경로 중복이 없어서 순서 상관없음 (라우터 간 경로 중복이 있는 경우 순서 중요)
http://localhost:3000 → 뒤에 아무것도 없음)app.use("/",mainRouter);) → 메인 라우터 호출router.get("/",(req,res)=>{res.sendFile(filePath+"/main.html");}); 실행 → main.html 보여주기http://localhost:3000/baseball)app.use("/",mainRouter);) → 메인 라우터 호출router.get("/baseball",(req,res)=>{res.sendFile(filePath+"/baseball.html");}); 실행 → baseball.html 보여주기http://localhost:3000/esports)app.use("/esports",esportsRouter);router.get("/",(req,res)=>{res.sendFile(filePath+"/esports.html");}); 실행 → esports.html 보여주기http://localhost:3000/esports/lol)app.use("/esports",esportsRouter);router.get("/lol",(req,res)=>{res.sendFile(filePath+"/lol.html");}); 실행 → lol.html 보여주기이제 '회원 가입, 탈퇴, 수정, 삭제' 구현을 위해 데이터베이스를 배워봅시다!
《함께 알아두면 좋은 내용》
넌적스(Nunjucks)
쿠키와 세션









show databases;
create database nodejs;
show databases;로 다시 확인


use nodejs;




select * from member;select id from member;select id,pw from member;

insert into member values ("test","1234","nickname");
update member set nick="hello" where id="test" and pw="1234";use 명령어 먼저 쓰기 (안 그러면 오류남)
delete from member where id="test" and pw="1234";LangChain Products 알아보기
: LangChain, LangSmith, LangGraph, LangFlow
- LangChain: LLM 애플리케이션 개발을 위한 기본 프레임워크
- LLM을 활용한 애플리케이션을 보다 쉽고 체계적으로 구축하기
- GPT-4로 초안을 작성한 후, Llama 3가 이를 보완하고, 필요에 따라 외부 데이터를 가져오도록 하는 시스템을 손쉽게 구축 가능
- 주요 기능
- LLM 지원: GPT-4, Llama 3 등 다양한 LLM을 지원하며, API 키만 입력하면 바로 사용할 수 있습니다.
- 프롬프트 템플릿: 하드코딩 없이 유동적으로 프롬프트를 구성할 수 있습니다.
- 체인(Chains): 여러 작업을 연결하여 하나의 워크플로우로 실행할 수 있습니다.
- 인덱스(Indexes): 문서 로더 및 벡터 데이터베이스를 통해 외부 데이터를 활용할 수 있습니다.
- 메모리: 애플리케이션이 이전 대화 내용을 기억할 수 있도록 지원합니다.
- 에이전트(Agents): LLM을 활용해 다음 작업을 자동으로 결정할 수 있습니다.
- LangChain을 사용하면 LLM을 기반으로 한 애플리케이션을 효율적으로 구성하고 확장할 수 있음
- LangSmith: 성능 모니터링 및 평가
- 테스트 & 모니터링 → LLM 기반 애플리케이션의 성능을 관리하고 평가하는 도구
- 주요 기능
- 테스트 및 디버깅: 에이전트와 LLM 호출이 예상대로 동작하는지 확인할 수 있습니다.
- 모니터링 및 비용 분석: 토큰 사용량, API 호출 횟수, 오류 발생률 등을 추적할 수 있습니다.
- 다양한 프레임워크와 호환: LangChain과 LangGraph뿐만 아니라, 다른 LLM 프레임워크와도 함께 사용할 수 있습니다.
- AI 애플리케이션의 성능을 지속적으로 모니터링하고 최적화할 수 있음
- LangGraph: 다중 에이전트 시스템 관리
- 다중 에이전트를 활용한 복잡한 워크플로우를 관리하는 데 초점
- 여러 에이전트가 협력해야 하는 연구 도우미나 자동화된 업무 프로세스를 개발할 때 유용
- 핵심 개념
- State (상태): 애플리케이션의 현재 상태를 유지하며, 사용자 입력과 에이전트의 결과 등을 저장합니다.
- Nodes (노드): 각각의 작업을 수행하는 단위로, 특정 함수 실행, LLM 호출, 외부 도구와 상호 작용할 수 있습니다.
- Edges (엣지): 노드 간의 데이터 흐름을 정의하며, 유연한 경로 설정이 가능합니다.
- 순환적인 인터랙션(cyclical interaction)이 필요한 경우 적합하며, 여러 에이전트가 협업해야 하는 애플리케이션을 개발할 때 유용함
- LangFlow: 시각적 인터페이스를 통한 프로토타이핑
- LangChain을 기반으로 하는 비주얼 개발 도구로, 코드를 작성하지 않고도 드래그 앤 드롭 방식으로 AI 워크플로우를 구축
- 코드 없이 LLM 애플리케이션을 시각적으로 설계
- 특징
- 사용이 간편한 UI: 다양한 LLM, 데이터 처리 도구, 프롬프트 등을 연결하여 AI 워크플로우를 손쉽게 설계할 수 있습니다.
- 빠른 프로토타이핑: LangFlow는 주로 MVP(최소 기능 제품) 개발 및 프로토타이핑 용도로 사용됩니다.
- 클라우드 및 로컬 설치 지원: DataStax 같은 클라우드 서비스를 통해 사용할 수도 있고, 로컬 서버에 직접 설치할 수도 있습니다.
| 도구 | 주요 기능 | 사용 목적 |
|---|---|---|
| LangChain | LLM 기반 애플리케이션 개발 프레임워크 | LLM과 외부 데이터 연동, 프롬프트 체이닝 |
| LangGraph | 다중 에이전트 워크플로우 관리 | 복잡한 에이전트 시스템 구축 |
| LangFlow | 비주얼 노코드 개발 도구 | 빠른 프로토타이핑, AI 워크플로우 설계 |
| LangSmith | 성능 모니터링 및 평가 도구 | LLM 애플리케이션 테스트 및 디버깅 |
→ 각 도구는 서로 보완적인 역할을 하기 때문에, 개발하려는 애플리케이션의 특성에 따라 적절히 선택하여 활용하는 것이 중요

# LangSmith API 키 읽어오기
with open("./key/.langsmith_api_key", 'r') as f:
api_key = f.read().strip()
os.environ["LANGCHAIN_API_KEY"] = api_key
# 추적 여부 ("true" → LangChain의 실행 결과가 LangSmith로 전달이 진행)
os.environ["LANGCHAIN_TRACING_V2"] = "true"
# OpenAI API 키 읽어오기
import os
with open('./key/.openai_api_key','r') as f:
api_key = f.read().strip()
# 환경변수 설정 (딕셔너리형태)
os.environ['OPENAI_API_KEY'] = api_key
!pip install -qU openai langchain-openai langchain langchain_community
!pip install -qU langchain-teddynote huggingface_hub
!pip install -qU tiktoken pypdf pdfplumber chromadb faiss-gpu
from langchain_openai import ChatOpenAI
from langchain_teddynote import logging
# 누적할 프로젝트 이름을 작성 → '특정 프로젝트에 기록하세요'라는 의미
logging.langsmith("Project-1",set_enable=True)
llm = ChatOpenAI()
llm.invoke("Hello world!")
LangSmith 추적을 시작합니다.
[프로젝트명]
Project-1
AIMessage(content='Hello there! How can I assist you today?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 10, 'total_tokens': 20, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'id': 'chatcmpl-CDNuNXVStd4UTz3n3zg8uTGJIFoG2', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--fa885dc8-cb11-4ab3-9611-a7811cf15313-0', usage_metadata={'input_tokens': 10, 'output_tokens': 10, 'total_tokens': 20, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})
logging.langsmith("특정 프로젝트명",set_enable=True)os.environ["LANGCHAIN_PROJECT"] = "Project-2"

from langchain.prompts.chat import ChatPromptTemplate
# 누적할 프로젝트 이름을 작성 → 새로운 이름을 쓰면 새로운 프로젝트가 생성됨
# 주의 → 새로운 프로젝트에 누적을 위하여 세션 재시작 필요!
logging.langsmith("Project-2",set_enable=True)
# os.environ["LANGCHAIN_PROJECT"] = "Project-2"
template = """너는 쉼표로 구분된 목록을 생성하는 유용한 조수이다.
사용자가 카테고리를 전달하면 해당 카테고리에 쉼표로 구분된 목록으로 5개의 개체를 생성해야 한다.
쉼표로 구분된 목록만 반환하며 그 이상은 반환하지 않는다."""
prompt = ChatPromptTemplate.from_messages([
("system", template)
, ("human", "{text}")
])
chain = prompt | llm
chain.invoke("비유클리드 기하학의 특징")
LangSmith 추적을 시작합니다.
[프로젝트명]
Project-2
AIMessage(content='1. 평행선이 없다\n2. 평행선 위의 점을 포함하지 않는 점을 지나는 직선은 한 개이다\n3. 각도의 합이 180도가 아닌 삼각형들이 있다\n4. 원의 중심이 중앙이 아닌 삼각형들이 있다\n5. 직선이 무한대로 뻗어간다', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 118, 'prompt_tokens': 132, 'total_tokens': 250, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'id': 'chatcmpl-CDOLXfny6fZ2FgLGM9vJ6LTTxDKL7', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--2a273eff-b430-4e23-9d49-86e6d09bef11-0', usage_metadata={'input_tokens': 132, 'output_tokens': 118, 'total_tokens': 250, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})
| 지표 | 특징 | 장점 | 단점 | 점수 범위 | 해석 | 좋은 점수 기준 | 주의사항 |
|---|---|---|---|---|---|---|---|
| BLEU | 기계 번역 평가용, n-그램 일치율 기반 | 계산 간단, 전통적으로 많이 사용 | 의미 같아도 표현 다르면 낮음 | 0 ~ 1 | 참조와 단어 수준 유사도 | ≥ 0.5: 꽤 유사, ≥ 0.7: 매우 유사 | 어휘 다양성 높은 경우 과소평가 가능 |
| ROUGE | 요약 평가에 특화, n-그램/구절/최장 공통 부분 수열 | 요약 핵심어 커버 잘 측정 | 단어 겹침 위주, 의미는 반영 약함 | 0 ~ 1 | 참조 요약과 단어·구절 겹침 정도 | ≥ 0.5: 주요 내용 커버, ≥ 0.7: 매우 좋음 | 의미 다르더라도 단어 겹치면 점수 높을 수 있음 |
| METEOR | BLEU 보완, 동의어·어간·형태 고려 | 인간 평가와 상관도 높음 | 계산 복잡, 언어 자원 필요 | 0 ~ 1 | 참조와 어휘적·의미적 유사도 | ≥ 0.6~0.7: 꽤 좋은 결과 | 지원 언어 한정, 속도 느릴 수 있음 |
| SemScore | 임베딩 기반 의미 유사도 | 표현 달라도 의미 같으면 높음 | 임베딩 모델 품질에 의존 | -1 ~ 1 (보통 0~1) | 의미적 유사도 (코사인 유사도) | ≥ 0.7: 유사, ≥ 0.85: 거의 동일 | 모델 성능·도메인에 따라 점수 신뢰도 달라짐 |
→ 언어 자원 필요: 동의어 사전 등
다양한 평가 지표를 사용하여 평가하는 것이 가장 좋음!
!pip install -qU openai langchain-openai langchain langchain_community
!pip install -qU langchain-teddynote huggingface_hub
!pip install -qU tiktoken pypdf pdfplumber chromadb faiss-gpu
!pip install -q rouge_score
sent1 = "안녕하세요. 반갑습니다. 내 이름은 최영화입니다."
sent2 = "안녕하세용 반갑습니다~^^ 내 이름은 최영화입니다!!"
sent3 = "내 이름은 최영화입니다. 안녕하세요. 반갑습니다."
# 토큰화 → 토크나이저
# 주의: 단순 Kiwi 토크나이저를 사용했을 때 현재 평가지표에 바로 사용하기는 어렵다 → TeddyNote KiwiTokenizer 사용!
# TeddyNote KiwiTokenizer: 평가 라이브러리에 바로 쓸 수 있도록 토크나이저 형태를 맞춰놓음
from langchain_teddynote.community.kiwi_tokenizer import KiwiTokenizer
from rouge_score import rouge_scorer
# 토크나이저 객체 생성
tokenizer = KiwiTokenizer()
# ROUGE 스코어
# rouge_scorer.RougeScorer(# 평가지표 종류, 토크나이저)
scorer = rouge_scorer.RougeScorer(
rouge_types=["rouge1", "rouge2", "rougeL"] # 평가지표 종류
, tokenizer=tokenizer # 토크나이저
, use_stemmer=False
)
| 지표 | 기준 | 민감한 요소 | 잘 잡아내는 차이 | 약점 |
|---|---|---|---|---|
| ROUGE-1 | 단어 단위(n=1) 겹침 | 단어 존재 여부 | 어휘가 얼마나 겹치는지 | 순서 무시 → 단어만 같으면 1.0 |
| ROUGE-2 | 연속된 두 단어(n=2) 겹침 | 단어 순서와 연속성 | 어휘가 같아도 문맥(연속성) 차이 감지 | 표현 조금만 달라져도 점수 크게 하락 |
| ROUGE-L | 최장 공통 부분 수열(LCS) | 전체 문장 구조·어순 | 단어 배열·문장 구조 유사성 | 단어 같아도 순서 다르면 점수 낮음 |
# sent1과 sent2의 비교
print(f"[sent1] {sent1}\n[sent2] {sent2} \
\n[rouge1] {scorer.score(sent1, sent2)['rouge1'].fmeasure:.5f} \
\n[rouge2] {scorer.score(sent1, sent2)['rouge2'].fmeasure:.5f} \
\n[rougeL] {scorer.score(sent1, sent2)['rougeL'].fmeasure:.5f}")
print("===" * 20)
# sent1과 sent3의 비교
print(f"[sent1] {sent1}\n[sent3] {sent3} \
\n[rouge1] {scorer.score(sent1, sent3)['rouge1'].fmeasure:.5f} \
\n[rouge2] {scorer.score(sent1, sent3)['rouge2'].fmeasure:.5f} \
\n[rougeL] {scorer.score(sent1, sent3)['rougeL'].fmeasure:.5f}")
[sent1] 안녕하세요. 반갑습니다. 내 이름은 최영화입니다.
[sent2] 안녕하세용 반갑습니다~^^ 내 이름은 최영화입니다!!
[rouge1] 0.78788
[rouge2] 0.64516
[rougeL] 0.78788
============================================================
[sent1] 안녕하세요. 반갑습니다. 내 이름은 최영화입니다.
[sent3] 내 이름은 최영화입니다. 안녕하세요. 반갑습니다.
[rouge1] 1.00000
[rouge2] 0.93333
[rougeL] 0.56250
| 비교 | ROUGE-1 (단어 단위 겹침) | ROUGE-2 (연속 단어 겹침) | ROUGE-L (최장 공통 부분 수열) | 종합 해석 |
|---|---|---|---|---|
| sent1 vs sent2 안녕하세요 vs 안녕하세용, 특수문자 차이 | 0.79 → 대부분 단어 같음 | 0.65 → 어휘 일부 변형으로 연속성 깨짐 | 0.79 → 문장 구조는 비슷 | 표현은 유사하지만 표기·특수문자 차이로 완벽하지 않음 |
| sent1 vs sent3 단어 동일, 순서만 다름 | 1.00 → 단어 100% 동일 | 0.93 → 대부분 연속 단어 동일, 순서 차이로 약간 감점 | 0.56 → 순서 크게 달라 공통 부분 수열 짧음 | 단어는 같지만 구조(순서) 차이 큼 |
from nltk.translate.bleu_score import sentence_bleu
sent1 = "안녕하세요. 반갑습니다. 내 이름은 최영화입니다."
sent2 = "안녕하세용 반갑습니다~^^ 내 이름은 최영화입니다!!"
sent3 = "내 이름은 최영화입니다. 안녕하세요. 반갑습니다."
# sent1과 sent2의 비교
bleu_score = sentence_bleu(
[tokenizer.tokenize(sent1, type="sentence")],
tokenizer.tokenize(sent2, type="sentence")
)
print(f"[sent1] {sent1}\n[sent2] {sent2}\n[score] {bleu_score:.5f}")
print("===" * 20)
# sent1과 sent3의 비교
bleu_score = sentence_bleu(
[tokenizer.tokenize(sent1, type="sentence")],
tokenizer.tokenize(sent3, type="sentence")
)
print(f"[sent1] {sent1}\n[sent3] {sent3}\n[score] {bleu_score:.5f}")
[sent1] 안녕하세요. 반갑습니다. 내 이름은 최영화입니다.
[sent2] 안녕하세용 반갑습니다~^^ 내 이름은 최영화입니다!!
[score] 0.76087
============================================================
[sent1] 안녕하세요. 반갑습니다. 내 이름은 최영화입니다.
[sent3] 내 이름은 최영화입니다. 안녕하세요. 반갑습니다.
[score] 0.95969
[ˈmiːtiər] 지표# 언어자원 제공
# METEOR 평가 지표는 동의어, 어간 매칭 지원 → 언어사전이 필요하다~
import nltk
from nltk.corpus import wordnet as wn
nltk.download("wordnet")
wn.ensure_loaded()
# 단어사전을 활용하여, 동의어, 어간, 형태소변화(run vs running) 등을 매칭
from langchain_teddynote.community.kiwi_tokenizer import KiwiTokenizer
from nltk.translate import meteor_score
kiwi_tokenizer = KiwiTokenizer()
sent1 = "안녕하세요. 반갑습니다. 내 이름은 최영화입니다."
sent2 = "안녕하세용 반갑습니다~^^ 내 이름은 최영화입니다!!"
sent3 = "내 이름은 최영화입니다. 안녕하세요. 반갑습니다."
# meteor_score 함수 안에서 자동으로 WordNet을 활용
meteor = meteor_score.meteor_score(
[kiwi_tokenizer.tokenize(sent1, type="list")],
kiwi_tokenizer.tokenize(sent2, type="list"),
)
print(f"[sent1] {sent1}\n[sent2] {sent2}\n[score] {meteor:.5f}")
print("===" * 20)
meteor = meteor_score.meteor_score(
[kiwi_tokenizer.tokenize(sent1, type="list")],
kiwi_tokenizer.tokenize(sent3, type="list"),
)
print(f"[sent1] {sent1}\n[sent3] {sent3}\n[score] {meteor:.5f}")
[sent1] 안녕하세요. 반갑습니다. 내 이름은 최영화입니다.
[sent2] 안녕하세용 반갑습니다~^^ 내 이름은 최영화입니다!!
[score] 0.80249
============================================================
[sent1] 안녕하세요. 반갑습니다. 내 이름은 최영화입니다.
[sent3] 내 이름은 최영화입니다. 안녕하세요. 반갑습니다.
[score] 0.97363
# SentenceTransformer: 사전학습된 문장 임베딩 모델 로드용
# util: 코사인 유사도 계산 같은 유틸 함수 제공
from sentence_transformers import SentenceTransformer, util
import warnings
warnings.filterwarnings("ignore", category=FutureWarning)
sent1 = "안녕하세요. 반갑습니다. 내 이름은 최영화입니다."
sent2 = "안녕하세용 반갑습니다~^^ 내 이름은 최영화입니다!!"
sent3 = "내 이름은 최영화입니다. 안녕하세요. 반갑습니다."
# 임베딩 모델 불러와서 사용하기 → huggingface에서 제공하는 모델 로드
model = SentenceTransformer("all-mpnet-base-v2") # all-mpnet-base-v2: 다국어 지원하는 임베딩 모델
# 문장 인코딩 / PyTorch 텐서 형태로 변환.
sent1_encoded = model.encode(sent1, convert_to_tensor=True)
sent2_encoded = model.encode(sent2, convert_to_tensor=True)
sent3_encoded = model.encode(sent3, convert_to_tensor=True)
# sent1과 sent2 사이의 코사인 유사도 계산
cosine_similarity = util.pytorch_cos_sim(sent1_encoded, sent2_encoded).item()
print(f"[sent1] {sent1}\n[sent2] {sent2}\n[score] {cosine_similarity:.5f}")
print("===" * 20)
# sent1과 sent3 사이의 코사인 유사도 계산
cosine_similarity = util.pytorch_cos_sim(sent1_encoded, sent3_encoded).item() # .item() → 파이토치 텐서를 파이썬 숫자로 변환
print(f"[sent1] {sent1}\n[sent3] {sent3}\n[score] {cosine_similarity:.5f}")
[sent1] 안녕하세요. 반갑습니다. 내 이름은 최영화입니다.
[sent2] 안녕하세용 반갑습니다~^^ 내 이름은 최영화입니다!!
[score] 0.87863
============================================================
[sent1] 안녕하세요. 반갑습니다. 내 이름은 최영화입니다.
[sent3] 내 이름은 최영화입니다. 안녕하세요. 반갑습니다.
[score] 0.98596
《목차》
1. 클라이언트 통신 방식
2. 웹 통신 구조(HTTP Packet Message)
3. 주요 취약점 진단 항목
4. AI의 확산과 보안의 변화
5. AI 악용에 따른 보안 위협
6. AI와 보안 전문가 협업: 적용사례 & 기대효과
7. 실습
7-1. 취약점 진단 도구 활용: BurpSuite
7-2. 취약점 진단 도구 활용: Fiddler
7-3. ChatGPT를 활용한 SQLi 공격 시도 과정
8. Q&A










취약점 진단 도구 활용: Burp Suite

web proxy 도구







Chrome 확장 프로그램 설치




ProxyOmega 추가 설정

