[크래프톤 정글 3기] 1/5(금) TIL

ClassBinu·2024년 1월 5일
0

크래프톤 정글 3기 TIL

목록 보기
80/120

08:08 입실
개별 프로젝트 기능 구현 완료하고 테스트 코드 공부하기

LangChain

템플릿 체인 연결

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;
  }
}

next.js

화면에 내용이 없어도 푸터를 최하단에 위치시키는 방법은 본문의 최소 높이를 화면 높이만큼 지정시키면 됨.

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>
  );
}

async, await

문제는 없으나 실행 흐름이 달라질 수 있으므로 유의.
내부적으로 Promise.resolve()로 처리됨.
코드를 통일하여 가독성 향상에 이점이 있음.

promise -> response

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

0개의 댓글