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를 두 번씩 호출한다?
똑같은 포스팅이 중복해서 렌더링된다.
이건 내일 해결해보자.