08:08 입실
개별 프로젝트 기능 구현 완료하고 테스트 코드 공부하기
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { ConfigService } from '@nestjs/config';
import { Injectable } from '@nestjs/common';
import { LangchainDto } from './dto/langchain.dto';
@Injectable()
export class LangchainService {
constructor(private configService: ConfigService) {}
async post(langchainDto: LangchainDto) {
const chatModel = new ChatOpenAI({
openAIApiKey: this.configService.get<string>('OPENAI_API_KEY'),
modelName: 'gpt-3.5-turbo-1106',
temperature: 0,
});
const userMessage = langchainDto.messages;
const systemMessage = '당신은 CTO입니다.';
const prompt = ChatPromptTemplate.fromMessages([
['system', systemMessage],
['user', '{input}'],
]);
const chain = prompt.pipe(chatModel);
const res = await chain.invoke({
input: userMessage,
});
return res;
}
}
화면에 내용이 없어도 푸터를 최하단에 위치시키는 방법은 본문의 최소 높이를 화면 높이만큼 지정시키면 됨.
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ko">
<body className={`${inter.className} whitespace-pre-wrap`}>
<div className="container mx-auto min-h-screen">
<AppProvider>
<MainNav />
{children}
</AppProvider>
</div>
<Footer />
</body>
</html>
);
}
문제는 없으나 실행 흐름이 달라질 수 있으므로 유의.
내부적으로 Promise.resolve()로 처리됨.
코드를 통일하여 가독성 향상에 이점이 있음.
NestJS에서 Promise가 HTTP 응답으로 변환되어 클라이언트로 전송되는 과정은 NestJS의 내부 구조와 Node.js의 비동기 처리 메커니즘에 기반합니다. NestJS는 내부적으로 Express.js 또는 Fastify와 같은 Node.js 웹 프레임워크를 사용하며, 이러한 프레임워크들은 비동기적인 자바스크립트(Promise)를 지원합니다.
과정:
컨트롤러와 핸들러:
NestJS에서 HTTP 요청이 들어오면, 해당 요청을 처리하기 위한 특정 컨트롤러의 메소드(핸들러)가 호출됩니다. 이 핸들러 함수는 동기적일 수도 있고, Promise를 반환하는 비동기적인 함수일 수도 있습니다.
Promise 처리:
만약 핸들러 함수가 Promise를 반환한다면, NestJS는 이 Promise가 해결될 때까지 기다립니다. 이는 Node.js의 이벤트 루프와 비동기 처리 메커니즘을 활용하여 처리됩니다.
HTTP 응답 생성:
Promise가 해결되면, 그 결과값은 HTTP 응답의 본문(body)이 됩니다. NestJS는 이 값을 적절한 HTTP 응답으로 포맷팅하고 클라이언트에게 전송합니다.
오류 처리:
만약 Promise가 거부될 경우(예: 오류 발생), NestJS는 이를 잡아내고 해당 오류에 따라 적절한 HTTP 오류 응답(예: 500 Internal Server Error)을 생성하여 클라이언트에게 전송합니다.
응답 종료:
HTTP 응답이 클라이언트에게 전송되면, 요청-응답 사이클이 종료됩니다.
예시:
@Controller('items')
export class ItemsController {
constructor(private readonly itemsService: ItemsService) {}
@Get()
async findAll(): Promise<Item[]> {
return this.itemsService.findAll();
}
}
위 예시에서 findAll 메소드는 Promise<Item[]>를 반환합니다. 클라이언트가 이 경로에 요청을 보내면, itemsService.findAll() 메소드의 결과(Promise)가 HTTP 응답으로 변환되어 클라이언트에게 전송됩니다.
NestJS의 이러한 처리 방식은 개발자가 비동기적인 로직을 쉽게 구현할 수 있게 해주며, 동시에 효율적인 비동기 처리를 통해 높은 성능의 애플리케이션을 만들 수 있도록 지원합니다.
이거 참고하기
https://docs.nestjs.com/controllers
무한 스크롤 구현하는데 offset개념이 나온다.
이거 가상메모리 할 때 나온 거라서 바로 이해됨.
숙원의 무한 스크롤 구현 성공
offset이 중요함.
offset은 0으로 시작하는데 이 값이 바뀌면 useEffect를 이용해서 새로운 포스트를 받아와서 기존 포스트와 합쳐서 새로운 배열로 반환한 후에 렌더링 한다.
처음 로딩시 이벤트 리스너를 걸어주는데, 이 이벤트 리스너는 스크롤을 할 때마다 작동한다. 이때 스크롤이 최하단으로 가면 offset을 변경한다.
이렇게 offset이 바뀌면 useEffect를 통해 fetchPosts()가 실행되는 구조
"use client";
import { useEffect, useState } from "react";
import { PostListCard } from "@/components/post/postListCard";
export default function PostListPage() {
const [posts, setPosts] = useState([]);
const [offset, setOffset] = useState(0);
const limit = 20;
const fetchPosts = async () => {
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_SERVER_API}/posts?offset=${offset}&limit=${limit}`
);
const newPosts = await response.json();
setPosts((prevPosts) => [...prevPosts, ...newPosts]);
} catch (error) {
console.error(error);
}
};
const handleScroll = () => {
if (
window.innerHeight + document.documentElement.scrollTop !==
document.documentElement.offsetHeight
)
return;
setOffset(offset + limit);
};
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
useEffect(() => {
fetchPosts();
}, [offset]);
return (
<>
{posts.map((post) => (
<PostListCard
key={post.id}
id={post.id}
title={post.title}
content={post.content}
/>
))}
</>
);
}
이렇게 짰는데 fetchPosts를 두 번씩 호출한다?
똑같은 포스팅이 중복해서 렌더링된다.
이건 내일 해결해보자.