목차
Ⅰ. 오전 수업
A. 1교시
1. 지난 시간 복습
2. 게시판 구현
B. 2교시
1. mainRouter.js 설정
2. 메인 페이지
3. 게시글 상세 페이지
C. 3교시
1. 게시글 상세 페이지(cont.)
Ⅱ. 오후 수업
A. 4교시
1. 지난 시간 복습
2. Agent: Tools
B. 5교시
1. Agent: Tools (cont.)
C. 6교시
배운 내용 복습
Ⅲ. CAREER UP
AWS 특강
Ⅳ. 하루 돌아보기
Ⅰ. 오전 수업
A. 1교시
1. 지난 시간 복습
- 회원 관리 플랫폼: 로그인 기능
- 서버 세팅 시 처음에 app.js 잘 세팅해 두면 더 건들 부분 없음 → 핵심은 router!
- 사용자에게 입력 받은 id, pw → 로그인 구현
- 기존에 사용한 방식:
- 로그인한 정보를 가지고 와서 그 정보를 그대로 추출:
let {id,pw}=req.body
- sql문에 넣어 바로 검색 → 전달 받은 값을 가지고 바로 렌더링: 메인 페이지를 만듦과 동시에 user라는 키에 사용자의 닉네임 값을 저장하는 함수 작성
- 다른 페이지로 가면 정보 증발하는 문제 → 해당 데이터를 유지해 다른 페이지에서 사용할 수 없었음 (값을 넘긴 페이지에서만 사용 가능)
- session 활용:
- 사용자의 정보를 유지시켜야 하거나 기록해야 하는 경우 공용 공간인 session에 저장
- 지금 쓰고 있는 브라우저 세션 외에도 다양한 세션이 있음
- session 사용 시 주의 사항
- 세션 활용 & 저장 방법은 매우 다양: 서버 세션, DB 세션, 브라우저 세션 등
- 자바스크립트 로컬 스토리지 개념도 있음
- 자바스크립트 로컬 스토리지는 웹 브라우저의 내장 기능으로, 사용자의 로컬 환경(브라우저)에 키-값 쌍 형태의 데이터를 영구적으로 저장하는 기술
- app.js 설정
- express-session 불러오기
- 사용 등록하기
- session 파라미터 설정: secret, resave, saveUnintalized, cookie
- cookie 파라미터 설정: httpOnly, secure, maxAge
- 넘어온 세션값 전역 변수로 등록 ★★★

→ null 처리(초깃값 등록) 안 하면 오류남: undefined / It's not defined
→ next(); 키워드 반드시 등록해야 함! (그래야 다음 코드로 넘어갈 수 있음)
- mainRouter.js
- React, Vue와 관련 있는 개념과 유사한 부분이 있음 → '값'을 전달한다: Props

- 레이아웃 쪼개기: header, footer → include()
- cf. 리엑트는 컴포넌트(component) 단위로 다 쪼갬
- JAVA base의 JSP, Servet 등의 SSR 기법에도 동일한 개념이 들어 있음
- 로그아웃
- 현재 기록하고 있던 로그인 정보를 세션에서 지운다는 뜻

2. 게시판 구현
보통 회사에서는 게시판을 직접 구현하지는 않는다고 함 → 게시판 API 사용 & AI
"데이터의 흐름", "통신"을 이해하기 위한 실습!
- 새 폴더 만들고 모듈 설치
npm i express mysql2 ejs express-session

-
폴더 & 파일 구성

-
app.js
- 서버 구성 뼈대 작성

- 설정: POST 통신, EJS

- router 설정은 mainRouter.js 만들고 난 후 추가하기
-
DB에 게시판용 테이블 생성
- 테이블 이름: board

- 컬럼 생성: id, title, content, writer, hit
- id → sequence: 고유 식별자 → PK, NN, AI(자동 증가: 시퀀싱 기능) 체크

- hit: 초깃값 설정 필요 (아무도 안 본 상태를 0으로 지정해야 함) → Default/Expression에 0 입력
- 초기 데이터 설정

- 테이블 확인

- config 폴더 db.js 생성
- routes 폴더 mainRouter.js 생성
- 기본 구조 세팅

- 미션 1: 사용자가 메인에 방문하면 DB에 있는 전체 게시글의 정보를 가져와서 메인 페이지에 출력하는 구조 만들기
- 전체 게시글 조회하는 sql문 작성
- conn을 통해서 쿼리문을 실행 (※ 넘겨줄 값은 없음)
- 메인 페이지를 렌더링 + 넘겨 받은 모든 데이터 posts 키로 넣어주기
B. 2교시
1. mainRouter.js 설정
- 미션 1: 사용자가 메인에 방문하면 DB에 있는 전체 게시글의 정보를 가져와서 메인 페이지에 출력
- 전체 게시글 조회하는 sql문 작성
- conn을 통해서 쿼리문을 실행 (넘겨줄 값은 없음)
- 리스트는 쿼리문에 넘겨줄 값이 있을 때만 사용
- 메인 페이지를 렌더링 + 넘겨 받은 모든 데이터 posts 키로 넣어주기

- 항상 콘솔창 먼저 출력해보는 습관 들이기

- 실행 결과

2. 메인 페이지

- 미션 2: 넘겨 받은 posts 키에서 값을 추출
- 문법: 반복문
- 꺼낼 값: id, title, writer, hit
- 반복 생성: tr 태그, td 태그 4개, td 안의 내용을 posts에서 꺼내오기
- 풀이
- 반복문 쓰기

- 반복 생성할 내용 넣기

- EJS 문법으로 감싸주기

3. 게시글 상세 페이지
- title을 a 태그로 감싸기

- href 넣기: board로 보내면 될까?

- 이렇게 하면 게시글이 여러 개일 때에도 다 같은 경로로 이동하는 문제가 있음 → 게시글 고유 번호와 연결하기: 파라미터 기법

- 미션 3: 특정 게시글을 조회해서 상세 페이지를 제작
- 핵심: 게시글의 번호(id)를 받아와서 해당 게시글만 조회
- mainRouter.js

- 파라미터를 관리할 때는
: 기호를 사용 → 문자가 아닌 보내준 값을 받아 유동적으로 처리하는 변수로 인식하게 됨!
- get의 쿼리스트링도 아니고 post 통신도 아님 → 파라미터 값 꺼내오는 게 따로 있음:
param

- 데이터가 잘 들어오는지 확인: console.log()

C. 3교시
1. 게시글 상세 페이지(cont.)
- 현재 내용은 '조회'에 해당
- 넘겨 받은 id에 해당하는 게시글 정보를 DB에서 수집
- SQL문 작성하고 DB에 연결해 값이 잘 넘어오는지 확인하기

- 특정 게시글 하나만 볼 거니까 넘길 때부터 정확하게 넘기기

- 넘길 때 디테일하고 정확하게 넘겨야 꺼내 쓸 때 편함!
views 폴더에 detail.ejs 생성

- 실행 결과

응용:

게시글 목록으로 돌아가기
조회 수 1 증가하는 로직 작성
-
쿼리문 ‘UPDATE' 활용

-
주의 사항
- 쿼리문 순서가 중요: update 먼저 하고 조회해야 페이지 들어갔을 때 조회 수 1 증가한 게 반경됨
게시판의 핵심은 '파라미터'라는 고유값임
Ⅱ. 오후 수업
A. 4교시
1. 지난 시간 복습
- LangGraph를 활용한 챗봇 구축
- State(상태) 정의
- add_messages: 리듀서 함수(reducer) → 데이터를 누적할 때 사용
- State 내에서 특정 키(messages)를 업데이트 할 때 기본 설정인 ‘덮어쓰기’ 대신 ‘리스트 형태로 이어 붙이기’로 처리하기 위함
- Node(노드) 정의
- 그래프 객체 생성 및 노드 추가
- Edge(엣지) 추가 → 노드들끼리 어떻게 연결되는지 지정
add_edge(START, "chatbot") vs.set_entry_point("retrieve")
- 그래프 컴파일
- 그래프 시각화
- 그래프 실행
- 앞선 설정으로 인해 messages가 계속 누적되기 때문에 최종 답변만 출력하기 위해 인덱싱(-1) 사용해서 출력
- LangGraph를 활용한 Agent 구축
- Agent? 조건을 스스로 생각!
- LLM에 입력된 요청에 따라 스스로 판단하여 필요 시 바인딩된 도구에서 웹 검색 도구(tools)를 호출
- 도구 바인딩:
.bind_tools()
- 주의: 도구도 노드가 필요함 → 도구 노드 정의 및 추가 필요
- 조건부 엣지
- 마지막 AI 메시지에 "도구를 호출하라"라는 의도(키워드)를 확인하면 → tools로 이동
- "도구를 호출하라"라는 의도가 없으면 알아서 END로 보냄 → END로 가는 edge 따로 정의하지 않아도 됨
- tools_condition이 tools → chatbot 및 chatbot → END 엣지를 처리하기 때문에 chatbot → tools 엣지와 START → chatbot만 정의하면 됨
- Agent에 메모리(memory) 추가하기
- checkpointer 객체(MemorySaver) 만들고 compile 시 매칭시켜주기
- RunnableConfig 설정 이유
- LLM이 스스로 생각 → 무한 루프에 빠질 수 있음 → 무한 루프를 막기 위해 recursioin_limit 지정
- thread_id 설정을 통해 사용자 구분 → 메모리를 나눠 기억 가능
- 다른 사람(다른 thread_id)으로 들어오면 대화 내용 기억 못함
- snapshot
- Agent는 LLM이 필요할 때 어떤 도구를 써야하는지를 스스로 결정하는 도구
- 이미 만들어져 있는 다양한 도구(검색 툴, 뉴스 데이터 가져오기, 파이썬 코드 생성 등)를 가져와서 쓸 수 있음
- @tool
- LangChain이 외부 기능을 호출할 수 있도록 인식하게 만드는 표시
- 함수가 아니라 도구라고 인식하게 만듦
- GoogleNews tool을 활용하여 뉴스 검색 후 답변을 하는 LangGraph 설계 후 사용
- 상태 정의
class State(TypedDict):
messages: Annotated[list, add_messages]
dummy_data: Annotated[str, "dummy"]
- 도구 정의 및 바인딩
- 이미 만들어져 있는 도구를 가져다 사용: search_by_keyword()

from langchain_teddynote.tools import GoogleNews
from typing import List, Dict
from langchain.tools import tool
@tool
def search_keyword (query: str) -> List[Dict[str,str]]:
"""
키워드로 구글 뉴스 검색
"""
tool_googleNews = GoogleNews()
return tool_googleNews.search_by_keyword(query,k=1)
tools = [search_keyword]
llm = ChatOpenAI(model="gpt-4o-mini")
llm_with_tools = llm.bind_tools(tools)
- return 형태 예시
- 가정: GoogleNews().search_by_keyword("삼성전자", k=2) 실행 결과
[
{
"title": "삼성전자, 3분기 실적 발표…영업이익 6조원"
, "link": "https://news.example.com/article/12345"
, "date": "2025-09-15"
}
, {
"title": "엔비디아 HBM4 성능 상향 요구에 마이크론 진땀, 삼성전자 내년 HBM4 입지 더 커지나"
, "link": "https://news.example.com/article/67890"
, "date": "2025-09-15"
}
]
- 독스트링(Docstrings)
- 함수나 클래스, 모듈에 첨부할 수 있는, 큰 따옴표 세 개 (""") 혹은 작은 따옴표 세 개 (''')로 둘러싸여진 문자열
@tool 데코레이터는 LangChain에서 함수에 '도구(tool)'로서의 속성과 메타데이터(이름, 설명, 인자 타입 등)를 부여해, 에이전트 또는 LLM(Chat Model)이 해당 함수를 호출 가능한 ‘도구’로 인식하도록 만드는 역할을 합니다.
@tool의 역할 및 활용
- 정의: 일반적인 Python 함수를 LangChain이 도구로 인식할 수 있게 변환하는 데코레이터입니다.
- 형식: 함수 위에 데코레이터를 붙이는 것을 "데코레이터 패턴" 또는 "함수 데코레이터"라 합니다.
- 사용 맥락: 도구로 사용될 함수에 @tool을 붙이면, 자동으로 함수명·설명(docstring)·타입 힌트 정보가 추출되어 에이전트, LLM 등에 목록으로 전달할 때 사용할 수 있습니다. 즉, 에이전트가 모델 추론 과정에서 프로그램적으로 함수(도구) 호출이 가능해집니다.
List[Dict[str, str]]의 의미
- 형태: Python의 ‘제네릭 타입 힌트’로,
List[Dict[str,str]]는 “각 요소가 {Key: Value} 쌍(모두 문자열)인 딕셔너리이고 이 딕셔너리들의 리스트”라는 뜻입니다.
- 예:
[{"title": "기사1", "url": "주소1"}, {"title": "기사2", "url": "주소2"}] 형태입니다.
- 이유: 여러 개의 뉴스 기사(딕셔너리로 표현됨) 결과를 하나의 리스트로 반환하기 때문에 대괄호를 사용합니다. 대괄호는 리스트(List), 중괄호는 딕셔너리(Dict)를 표현합니다.
- 활용: 반환 타입을 명확하게 하여 호출 주체나 코드 자동완성 도구가 결과 구조를 해석할 수 있도록 도와줍니다.
- 정리
- @tool: 함수가 자연어 모델과 연동되는 ‘호출 가능한 도구’임을 선언하는 LangChain용 “데코레이터 패턴”이며, 함수/메서드 상단에 @으로 표기합니다. LangChain 에이전트나 LLM이 함수 호출을 통한 복잡한 작업을 실행할 때 사용합니다.
List[Dict[str,str]]: “문자열 key와 value로 이루어진 딕셔너리”를 요소로 하는 리스트를 뜻하며, 여러 뉴스 기사 정보 등 복수 결과를 반환할 때 쓰입니다.
- 노드 정의 & 그래프 정의
def chatbot(state:State):
return {
"messages": [llm_with_tools.invoke(state["messages"])]
, "dummy_data": "[chatbot] 호출, dummy_data"
}
tools = ToolNode(tools=tools)
graph_builder4 = StateGraph(State)
graph_builder4.add_node("chatbot", chatbot)
graph_builder4.add_node("tools", tools)
B. 5교시
- 엣지 추가
graph_builder4.add_conditional_edges("chatbot", tools_condition)
graph_builder4.add_edge(START, "chatbot")
graph_builder4.add_edge("tools", "chatbot")
- 그래프 컴파일
graph4 = graph_builder4.compile()
- 그래프 시각화
visualize_graph(graph4)

- 그래프 실행
- dummy_data 사용: 출력해서 어떤 노드를 지나갔는지 확인
- LangChain에서 제공하는 다양한 도구 알아보기
- 평가는 어떻게? 평가 node 만들어서!
- 평가지표 활용
- 평가용 LLM 사용
- 프롬프트를 아래와 같은 형태로 작성:
prompt = f"""너는 답변 품질 평가자다. 아래 답변이 '질문에 충분히 답했는지'만 판단해라. 출력은 오직 GOOD 또는 BAD 중 하나.
C. 6교시
배운 내용 복습 & 보충
Tavily와 GoogleNews 툴 등록 방식 차이
- Tavily
- LangChain이나 LangGraph에서 이미 제공되는 공식 통합 형태로 많이 사용
- 별도 커스텀 함수 없이 인스턴스 생성 후 바로 등록하는 방식이 일반적
- TavilySearch Tool은 공식 통합 or 별도의 툴 클래스 제공으로 바로 등록 가능
- 커스텀 함수, 데코레이터 없이 사용해도 툴로 취급
- GoogleNews와 같은 커스텀 툴
- 일반적인 파이썬 함수를 직접 정의하고 이를 LangChain/LangGraph의 툴 인터페이스에 맞게 연결해야 해서 데코레이터를 반드시 붙여야 함
- 커스텀 웹/API 함수를 직접 툴로 노출하고 싶을 때는 LangChain/LangGraph가 요구하는 툴 인식 및 명세를 위해 @tool 데코레이터를 붙여야 모델이 올바르게 처리 가능
- @tool 데코레이터는 함수 시그니처 파싱, 설명/Schema 등록 등 자동화를 지원
- 왜 데코레이터가 필요한가?
- 인공지능 에이전트 워크플로우에서 ‘사용자 정의 함수’를 툴로 등록할 때 내부적으로 LangChain/LangGraph가 Tool Registry에 올바른 명세(name, args, description 등)를 남기기 위해 데코레이터(@tool 등)가 필요
- TavilySearch는 이미 툴 객체를 제공하거나 “tool_node" 등 공식 예시와 통합에 맞춰 구현되어 별도의 데코레이터 없이 바로 사용 가능
| 툴 종류 | 데코레이터 필요 여부 | 방식 |
|---|
| Tavily | 필요 없음(공식 통합 제공) | 툴 인스턴스 바로 등록, 자동 명세 적용 |
| GoogleNews | 필요(커스텀 등) | 사용자 함수에 데코레이터 필요, 명세 자동화 |
직접 만든 함수 혹은 외부 API Wrapper는 반드시 데코레이터를 통해 툴 schema화해야 LangGraph가 모델 호출과 툴 사용을 정상적으로 연결할 수 있습니다.
Ⅲ. CAREER UP
배운 내용 복습
하루 돌아보기
👍 잘한 점
👎 아쉬웠던 점
🔬 개선점