Tech

ChatGPT는 FE개발자를 대체할 수 있을까? (AI로 개발하기)

ENTER TECH 2023. 4. 18. 09:00

 

 

안녕하세요!

카카오엔터테인먼트 FE개발자 클로토입니다.

최근 핫한 ChatGPT를 주제로 주니어 FE개발자의 관점에서 글을 작성해 보았습니다.

재밌게 봐주셨으면 좋겠습니다.

 

FE개발자 클로토!

❗️이 글은 ChatGTP 3.5 버전을 기준으로 작성된 점 참고 부탁드립니다. (글 작성 중에 ChatGTP 4.0이 발표되었네요!)

 

 


 

요즘 ChatGPT가 뜨거운 감자입니다. 출시일 5일 만에 100만 명의 사용자를 모으고 2달 만에 사용자 1억 명에 달했습니다. 블로거, 기자, 유튜버 등 누구 할 것 없이 ChatGPT에 관해 이야기하고 있습니다. 많은 사람들이 일자리를 잃게 될지도 모른다고 이야기하는 여론도 심심치 않습니다. 평소 인공지능에는 관심이 있던지라 ChatGPT가 FE개발자의 업무를 과연 어디까지 보조 또는 대체할 수 있을지 궁금해졌습니다. 그럼 한번 용의자인 ChatGPT에게 먼저 물어보도록 하죠. 물론 FE개발자를 대체할 리 없다고 대답하겠지만요.

 

Q
ChatGPT는 FE 개발자를 대체할 수 있을까?
A

용의자 ChatGPT의 진술

역시 예상대로 발뺌(?)하는군요. ChatGPT 말을 순순히 '그렇구나'하고 포기했다간 제 글이 여기서 끝나버립니다.

그럴 수는 없죠. 간단한 미니 프로젝트를 통해 gpt(ChatGPT와 GPT는 다르지만 여기서부터 gpt라 줄여서 표현하겠습니다)가 어디까지 활용될 수 있을지를 한번 확인해 보도록 하겠습니다. gpt에 대한 설명보다는 실제 문답을 통해 코드를 작성하는 과정으로 설명할 예정입니다.

 

미니 프로젝트로는 간단한 웹툰 서비스를 기획해 보았습니다. 기획하는 과정에서도 gpt에게 물어보며 주로 어떤 장르가 인기가 있는지, 장르에 쓰일 작품을 답변받아 추가했습니다. 이미지들은 stable diffusion webui를 사용해서 txt2img로 작품 키워드에 맞는 이미지를 생성하여 넣었고 더미 이미지는 pixabay를 사용했습니다. 그리고 간단한 API 연동을 위해 TMDB API를 활용한 페이지도 추가했습니다.

 

완성된 프로젝트는 https://next-gpt-test.vercel.app/ 에서 데모를 확인할 수 있습니다. (vercel hobby plan이라 약간의 제약이 있을 수 있습니다). 어떤 과정을 통해 만들게 되었는지는 아래 ChatGPT로 개발하기 부분에서 설명할 예정입니다. 우선 gpt가 어떤 일을 할 수 있을지 알아보기 전에 FE개발자가 어떤 업무들을 수행하고 있는지부터 알아보겠습니다.

 

https://next-gpt-test.vercel.app/

 

next-gpt-test.vercel.app

 

| FE개발자가 하는 일

FE개발자는 주로 어떤 일을 할까요? 간단하게 생각해 봤을 때는 다음과 같은 일들이 있습니다.

  • 프로젝트 환경설정
  • 요구사항에 맞는 html 작성
  • 디자인에 맞는 css 작성
  • 상태를 관리하여 동작 처리하기(react state, redux, recoil 등)
  • 서버에서 데이터 불러오기(axios, reat-query, graphql 등)
  • 반응형 디자인 대응(pc, mobile, tablet)

 

물론 이 과정에서 여러 사람들과 협업하기 위한 소통도 빠질 수 없겠죠. 또 아토믹 디자인/디자인 시스템과 같은 방법론을 적용해야 할 수 있고, 팀에 따라 BFF를 사용할 수도 있을 겁니다(저희 팀이 그렇습니다). 이 외에도 코드 리팩터링, 인증, SEO, 성능 최적화 등 나열하자면 꽤 긴 글이 될 것입니다. 여기서는 gpt를 활용하는 데 초점을 맞추고, 동작하는 서비스를 만드는 데 최소 요건이라고 생각되는 부분을 진행해 보도록 하겠습니다.

🔍 프론트엔드의 역사와 개발 범위에 대해 더 자세히 알고 싶다면 테오님이 작성하신 글이 정말 좋으니 한번 읽어보시길 바랍니다.

자, 그럼 gpt와 함께 간단한 웹툰 서비스를 만들어보며 위에서 나열한 FE개발자가 하는 일을 어디까지 대체할 수 있는지 알아보겠습니다.

 

 

| ChatGPT로 개발하기

gpt와의 질의응답 과정에서 내용이 너무 긴 부분은 중요한 부분만 따로 설명하고, 나머지는 shareGPT를 사용해 대화 내용을 확인할 수 있도록 링크를 첨부했습니다. shareGPT는 gpt와의 대화 내용을 공유할 수 있게 도와주는 크롬 플러그인입니다.

그럼 본격적으로 gpt를 활용해서 간단한 웹툰 서비스를 만들어보는 미니 프로젝트를 진행해 보겠습니다. 전체 코드에 관한 내용을 보고 싶으면 github repository를 참고 부탁드립니다.

프로젝트 환경설정

이 프로젝트는 next.js를 사용하고 스타일 작업에는 tailwind를 사용합니다. API 사용을 위한 라이브러리와 테스팅 도구도 설치합니다.

 

Q

다음과 같이 환경설정을 하고 싶어 방법을 알려줘
- next.js
- typescript
- eslint
- prettier
- tailwind
- react-query
- jest

 

A

Next.js, TypeScript, ESLint, Prettier, Tailwind, React Query 및 Jest로 환경을 설정하려면 다음 단계를 따르세요.

 

1. 다음 명령을 사용하여 새 Next.js 프로젝트를 만듭니다.

npx create-next-app --typescript

이렇게 하면 TypeScript를 지원하는 새로운 Next.js 프로젝트가 생성됩니다.

 

2. 다음 명령을 사용하여 필요한 종속성을 설치합니다.

npm install --save-dev eslint prettier eslint-config-prettier eslint-plugin-prettier @typescript-eslint/eslint-plugin @typescript-eslint/> parser tailwindcss postcss autoprefixer act-query jest @types/jest

그러면 ESLint, Prettier, TypeScript ESLint 플러그인, Tailwind, PostCSS, React Query 및 Jest가 필요한 유형과 함께 설치됩니다.

 

3. 다음 내용으로 프로젝트의 루트에 `.eslintrc.js`라는 이름의 ESLint 구성 파일을 만듭니다.

module.exports = {
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint', 'prettier'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended',
  ],
  rules: {
    'prettier/prettier': ['error'],
  },
};

그러면 TypeScript 파서, 권장 TypeScript 규칙 및 Prettier 플러그인을 사용하도록 ESLint가 구성됩니다.

 

4. 다음 내용으로 프로젝트의 루트에 `.prettierrc`라는 Prettier 구성 파일을 만듭니다.

 

//.. 중략

 

8. 마지막으로 `package.json` 파일의 `scripts` 섹션을 업데이트하여 다음을 포함합니다.

"scripts": {
  "dev": "next dev",
  }

 

질문에 따라 gpt의 답변이 조금씩 달랐는데요. npm init을 하고 패키지를 쭉 설치하는 방법을 알려주기도 하고, create-next-app을 통해서 하는 방법을 알려주기도 합니다. 정해진 답을 알려주는 게 아니라 답변을 생성하는 언어 모델 답군요. 내용을 따라서 create-next-app으로 설치하게 되면 typescript뿐만 아니라 eslint도 설정할 수 있는 질문이 나옵니다.

 

next app

 

생성된 파일을 보면 기본적으로 eslint와 prettier 파일이 만들어져 이미 설정까지 되어 있음을 확인할 수 있습니다. 위에서 gpt가 알려준 방식은 따로 `.eslintrc.js`와 `.prettierrc`을 만들라고 했는데 조금 다르네요. 당황하지 않고 계속 진행해 보겠습니다.

 

이어서 설치를 진행하는데 오류가 발생했습니다. 원인을 찾아보니 최신 tailwind 공식 문서에서는 아래 명령을 실행하라고 되어 있지만 gpt 대답에는 이와 관련된 내용이 없었습니다.

npx tailwindcss init -p

tailwind.config.js 일부 내용이 추가가 안 되어 있어서 오류가 발생하고 있었고, jest 설치 시에는 typescript를 같이 쓰기 위한 설정이 미비하다든가 하는 문제들이 있었습니다. 물론 세팅하면서 발생하는 에러 코드를 gpt에게 다시 물어서 해결해 나갈 수 있었지만 아직은 공식 문서를 보고 프로젝트 환경 설정을 하는 것이 좋아 보입니다. 물론 프로젝트 설정의 전체적인 흐름을 보는 데는 좋을 수도 있을 것 같지만요.

 

gpt는 지금까지는(gpt-3.5 모델 기준) 2021년 이전의 인터넷 정보만을 가지고 학습된 모델이기 때문에 next.js 최신 버전이나 최신 라이브러리들을 설치하는데 적절하지 않은 가이드를 제공하고 있습니다.

[참고] ChatGPT 학습 데이터 세트

 

  • Common Crawl: Common Crawl은 웹 페이지의 대규모 데이터셋을 수집하는 프로젝트입니다. 이 데이터셋은 인터넷에서 수집한 텍스트 데이터의 대규모 샘플을 제공합니다.
  • Reddit: Reddit은 인기 있는 소셜 뉴스 사이트입니다. 이 데이터셋은 Reddit 게시물과 댓글의 대규모 샘플을 제공합니다.
  • WebText: WebText는 Common Crawl에서 수집된 HTML 페이지의 일부에서 추출한 텍스트 데이터의 샘플입니다. 이 데이터 세트는 웹에서 수집된 매우 다양한 유형의 문서를 포함합니다.
  • BooksCorpus: BooksCorpus는 수백만 개의 도서를 포함하는 대규모 책 데이터 세트입니다.
  • English Wikipedia: English Wikipedia는 영어 위키피디아의 전체 텍스트 데이터 세트입니다.

 

상단 배너 만들기

웹툰 서비스에는 일반적으로 작품의 홍보나 이벤트를 위해 상단에 carousel 형식의 배너가 들어갑니다. 그리고 next.js는 react 기반이기 때문에 배너와 같은 요소들을 보통 리액트 ‘컴포넌트’ 단위로 관리하죠. 이미지를 넘겨 볼 수 있는 배너를 만들기 위해 위에 설정한 프로젝트 환경대로 jsx, tailwind 형식으로 배너 컴포넌트를 만들어달라고 요청해 봤습니다.

 

Q

    웹툰 서비스 배너를 만들고 싶어. 최상단에서 이미지를 돌리면 넘어가는 형식이야. jsx, tailwind 문법으로 코드를 짜줘

A

 

알겠습니다! 다음은 JSX 및 Tailwind 구문을 사용하는 웹툰 서비스 배너의 예제 코드 스니펫입니다.

<div className="w-full h-24 md:h-36 flex items-center justify-center">
  <div className="w-full h-24 md:h-36 flex items-center justify-center overflow-hidden">
    <img className="w-auto h-full transform rotate-90" src="https://example.com/banner.jpg" alt="Webtoon Service Banner" />
  </div>
	<div className="absolute bottom-0 w-full h-6 bg-white"></div>
</div>

 

음..? 딱 봐도 뭔가 이상하죠? `transform roate-90`을 줘서 뭔가 이미지를 돌린다는 것 같은데요. 여러 장의 이미지를 넣을 수도 없고 뭔가 아직은 부족해 보입니다. 그래도 jsx와 tailwind를 사용해서 alt에 `Webtoon Service Banner`까지 넣어 주었네요! 아무래도 질문을 너무 성의 없이 해서 gpt 선생님도 답변을 대충 해주신 것 같습니다. 한번 요구사항을 제대로 넣어서 다시 질문을 해보도록 하겠습니다.

 

Q

웹툰 서비스 배너를 만들고 싶어. 최상단에서 이미지를 돌리면 넘어가는 carousel 형식이야. 처음에는 3개의 이미지가 보이고, 중앙의 이미지는 전부 다 보이지만 좌, 우에 있는 이미지는 절반만 보여야 해. 이미지를 좌우로 스와이프 하면 다음 이미지로 넘어가야 하고, 마지막 이미지에서 한 번 더 스와이프 하면 처음 이미지로 돌아와야 해. 
jsx, tailwind 문법으로 코드를 짜줘

 

A

알겠습니다! 다음은 캐러셀 효과를 포함하는 JSX 및 Tailwind 구문을 사용하는 웹툰 서비스 배너의 예제 코드 스니펫입니다.

import React, { useState } from "react";

const WebtoonServiceBanner = () => {
  const [currentImageIndex, setCurrentImageIndex] = useState(0);

  const images = [
    "https://example.com/image1.jpg",
    "https://example.com/image2.jpg",
    "https://example.com/image3.jpg",
  ];

  const handleSwipe = (direction) => {
    if (direction === "left") {
      setCurrentImageIndex((currentImageIndex + 1) % images.length);
    } else if (direction === "right") {
      setCurrentImageIndex(
        (currentImageIndex - 1 + images.length) % images.length
      );
    }
  };

  return (
    <div className="w-full h-24 md:h-36 relative flex items-center justify-center">
      <div className="w-full h-24 md:h-36 flex items-center justify-center overflow-hidden">
        {images.map((image, index) => (
          <img
            key={index}
            className={`w-full h-full absolute transition duration-500 ${
              index === currentImageIndex
                ? "opacity-100"
                : "opacity-0 translate-x-full md:translate-y-full md:rotate-90"
            }`}
            src={image}
            alt={`Webtoon Service Banner ${index}`}
          />
        ))}
      </div>
      <div className="absolute bottom-0 w-full h-6 bg-white"></div>
      <div
        className="absolute inset-y-0 left-0 w-1/3 md:w-1/6 flex items-center justify-center z-10 cursor-pointer"
        onClick={() => handleSwipe("right")}
      >
        <span className="text-white text-4xl md:text-5xl">&lt;</span>
      </div>
      <div
        className="absolute inset-y-0 right-0 w-1/3 md:w-1/6 flex items-center justify-center z-10 cursor-pointer"
        onClick={() => handleSwipe("left")}
      >
        <span className="text-white text-4xl md:text-5xl">&gt;</span>
      </div>
    </div>
  );
};

export default WebtoonServiceBanner;

이 코드에서는 현재 이미지 인덱스를 추적하기 위한 상태를 포함하는 `WebtoonServiceBanner` 구성 요소를 만듭니다. 또한 이미지 URL 배열과 스와이프를 처리하는 함수를 정의하고 있습니다.

 

구성요소 내부에서 모바일에서는 높이가 24픽셀이고 큰 화면에서는 36픽셀인 컨테이너 div를 만듭니다(`md:h-36`). 해당 컨테이너 안에 이미지를 담을 또 다른 div를 만들고 있습니다. 우리는 Tailwind의 `overflow-hidden` 클래스를 사용하여 컨테이너 경계를 넘어 확장되는 이미지 부분을 숨깁니다.

 

또한 `images` 배열을 매핑하고 각각에 대해 `img` 요소를 생성합니다. 현재 이미지(`index === currentImageIndex`)의 경우 불투명도를 100으로 설정하여 표시하고 두 개의 인접한 이미지의 경우 불투명도를 0으로 설정하고 `translate-x- full md:translate-y-full md:rotate-90` 변환하여 숨기고 회전시킵니다.

 

마지막으로 배너 상단에 배치되고 사용자가 왼쪽이나 오른쪽으로 스와이프 하여 이미지를 변경할 수 있는 두 개의 클릭 가능한 div를 만듭니다. 우리는 Tailwind의 `cursor-pointer` 클래스를 사용하여 클릭할 수 있게 만들고 스와이프 방향을 나타내는 텍스트가 있는 화살표 아이콘도 추가하고 있습니다.

 

오 대단한데요? 이번에는 React 컴포넌트 구조로 만들어 주었습니다. 코드를 보니 direction이 left일 때 이미지 index를 더해주고, right일 때 빼주면서 이미지가 이동할 수 있게 만들어 주는 것 같군요! 거기다 코드 밑에 코드에 대한 설명까지 덧붙여주었습니다. 훌륭하네요! 자, 그럼 만들어 준 코드에 더미 이미지를 넣어서 한번 확인해 볼까요?

네.. 동작하기는 하는데 제가 상상했던 이미지와는 조금 다르네요. 심지어 모바일로 변경하면 이미지가 화면 일부분으로 잘려 나오고 제대로 동작하지 않습니다. 현재 보이지 않는 이미지의 opacity를 조정해서 안 보이게 하고 교체하는 방식은 괜찮을지 몰라도, rotate를 사용해서 그런지 동작이 우아해 보이지 않습니다. 물론 transition을 적절하게 수정해서 원하는 모습을 만들 수도 있겠지만 저는 조금 더 쉬운 길을 가기 위해 swiper.js 라이브러리를 사용하도록 gpt에게 의뢰해 보도록 하겠습니다.

 

Q

제대로 동작을 안 하는데 swiper.js를 사용해서 바꿀 수 있을까?

A

예, Swiper.js를 사용하여 스와이프 기능이 있는 웹툰 서비스 배너를 만들 수 있습니다. Swiper.js는 강력하고 사용하기 쉬운 JavaScript 라이브러리로 반응형 터치 지원 슬라이더 및 캐러셀을 만들 수 있습니다.

 

다음은 Swiper.js를 사용하여 웹툰 서비스 배너를 만드는 방법의 예입니다.

import React from "react";
import SwiperCore, { Navigation, Pagination } from "swiper";
import { Swiper, SwiperSlide } from "swiper/react";

// Import Swiper styles
import "swiper/swiper-bundle.css";

interface WebtoonServiceBannerProps {
  images: string[];
}

// Install Swiper modules
SwiperCore.use([Navigation, Pagination]);

const WebtoonServiceBanner: React.FC<WebtoonServiceBannerProps> = ({
  images,
}) => {
  return (
    <Swiper
      navigation
      pagination={{ clickable: true }}
      className="h-full"
      loop
    >
      {images.map((image, index) => (
        <SwiperSlide key={index}>
          <img src={image} alt={`Webtoon Service Banner ${index}`} />
        </SwiperSlide>
      ))}
    </Swiper>
  );
};

export default WebtoonServiceBanner;

이 코드에서는 Swiper.js의 `Swiper` 및 `SwiperSlide` 구성 요소를 사용하여 회전식 이미지를 만듭니다. 또한 'Navigation' 및 'Pagination' 모듈을 설치하여 캐러셀에 스와이프 탐색 및 페이지 매김을 추가했습니다.

 

React 앱에서 Swiper.js를 사용하려면 먼저 npm 또는 yarn과 같은 패키지 관리자를 사용하여 설치해야 합니다. Swiper.js를 설치하면 위와 같이 코드에서 해당 모듈과 구성 요소를 가져올 수 있습니다.

 

Swiper.js를 사용하면 스와이프 기능으로 보다 견고하고 유연한 웹툰 서비스 배너를 제작할 수 있으며, 모바일에서 이미지가 잘리는 문제를 피할 수 있습니다.

 

이전에 준 코드에 문제가 있는 걸 알았던 걸까요? 모바일에서 이미지가 잘리는 문제를 피할 수 있다고까지 설명해 줬네요. (근데 왜 안 고쳐줬어..) 드디어 제가 원하는 배너의 기능을 하는 컴포넌트가 만들어졌습니다. 여러 이미지를 넣을 수 있고, 터치 스와이프도 지원하고, 마지막 이미지에서 첫 이미지로 가는 루프 기능까지 잘 완성이 되었습니다. 저는 요구사항에 넣지도 않았는데 페이지네이션 기능까지 알아서 추가해 줬네요.

 

디자인까지는 완벽하지 않아서 몇 가지 질문을 더하고 디자인을 수정해 주었습니다. 배너 질문의 전체 문답은 shareGPT를 사용한 링크에서 확인할 수 있습니다.

 

작품 섹션 만들기

자, 상단 배너 영역은 완성했습니다. 이제 gpt와 함께 기획한 훌륭한 작품들을 보여줄 영역이 필요하겠죠? 작품의 썸네일과 제목, 작가명을 나타낼 수 있는 컴포넌트를 먼저 만들어보겠습니다.

 
Q

웹툰 작품을 표현하는 컴포넌트를 만들고 싶어. 작품의 썸네일이 들어가고 그 아래에 작품 이름과, 그 아래에 작가명이 들어갔으면 좋겠어. 썸네일은 너무 네모이면 별로니까 약간 라운드가 들어갔으면 좋겠고. jsx, tailwind, typescript 문법으로 코드를 짜줘

A

물론이죠, 다음은 수행할 수 있는 작업의 예입니다.

import React from 'react';
import Image from 'next/image';
import { Work } from 'types';

interface Props {
  work: Work;
}

const WorkComponent: React.FC<Props> = ({ work }) => {
  return (
    <div className="flex flex-col items-center">
      <div className="rounded-full overflow-hidden w-24 h-24 mb-2">
        <Image
          src={work.thumbnail}
          alt={work.name}
          layout="responsive"
          width={300}
          height={300}
        />
      </div>
      <h3 className="text-lg font-bold text-center">{work.name}</h3>
      <p className="text-sm text-gray-600">{work.artist}</p>
    </div>
  );
};

export default WorkComponent;

이 코드는 어딘가에 다음과 같은 `Work` 유형이 정의되어 있다고 가정합니다.

export interface Work {
  name: string;
  artist: string;
  thumbnail: string;
}

이 구성 요소는 `flex-col` 및 `items-center` 클래스와 함께 `flex` 레이아웃을 사용하여 콘텐츠를 수직 및 수평으로 중앙에 배치합니다.

 

축소판은 원형으로 만들기 위해 `rounded-full` 클래스와 함께 `div`로 래핑됩니다. `overflow-hidden` 클래스는 이미지가 원의 경계까지 잘리도록 합니다.

 

Next.js의 `Image` 구성 요소는 썸네일 이미지를 렌더링 하는 데 사용됩니다. `layout="responsive"` prop은 원본 이미지의 종횡비를 기반으로 이미지의 너비와 높이를 자동으로 계산하도록 Next.js에 지시합니다. `width` 및 `height` 소품은 이미지의 최대 크기를 설정하는 데 사용됩니다.

 

`name` 및 `artist`는 각각 `h3` 및 `p` 태그를 사용하여 표시됩니다. `text-lg`, `text-sm` 및 `text-gray-600` 클래스는 텍스트 스타일을 지정하는 데 사용됩니다.

 

짜잔~ 작품을 표현할 수 있는 컴포넌트가 만들어졌습니다! 약간 round로 요청했는데 rounded-full로 원형으로 만든 건 조금 아쉽네요. 그리고 웹툰 작품을 표현하는 건데 Work라고 하는 건 적절하지 않아 보입니다. WorkOfArt 도 아니고 그냥 Work라고 하면 다른 사람이 코드를 봤을 때 헷갈릴 것이 분명합니다.

 

 

Q

위에 말한 작품은 웹툰 작품이라서 이름을 좀 다르게 했으면 좋겠어.

A

물론이죠. 작업에 다른 이름을 사용하도록 구성 요소를 변경할 수 있습니다. 예를 들어 웹툰이라는 사실을 더 잘 반영하기 위해 "작품" 대신 "에피소드"를 사용할 수 있습니다. 업데이트된 코드는 다음과 같습니다.

import React from 'react';
import Image from 'next/image';
import { Episode } from 'types';

interface Props {
  episode: Episode;
}

const EpisodeComponent: React.FC<Props> = ({ episode }) => {
  return (
    <div className="flex flex-col items-center">
      <div className="rounded-full overflow-hidden w-24 h-24 mb-2">
        <Image
          src={episode.thumbnail}
          alt={episode.name}
          layout="responsive"
          width={300}
          height={300}
        />
      </div>
      <h3 className="text-lg font-bold text-center">{episode.name}</h3>
      <p className="text-sm text-gray-600">{episode.artist}</p>
    </div>
  );
};

export default EpisodeComponent;

이전과 마찬가지로 구성요소는 어딘가에 정의된 `Episode` 유형이 있다고 가정합니다. 이는 다음과 같습니다.

export interface Episode {
  name: string;
  artist: string;
  thumbnail: string;
}

업데이트된 구성 요소는 이전 구성 요소와 유사하지만 변수 및 소품 이름과 주석이 있는 경우 "work" 대신 "episode"를 사용합니다.

 

대단하군요. 이전에 말했던 코드에서 Work를 Episode로 변환한 코드를 반환해 줬습니다. 매번 새로운 세션에서 대답하는 게 아니기 때문에 이렇게 기존에 말하던 내용을 이어서 진행할 수 있다는 점이 정말 강력한 것 같네요. 자, 이제 gpt가 알려준 대로 코드를 작성하고 더미 이미지와 텍스트도 넣어봅시다.

 

`rounded-full`로 넣어 주었던 부분은 제가 디자인을 살짝 수정하여 `rounded-[10px]`로 넣어 주었습니다. 작품 제목이 더 큰 폰트로 들어가면 좋을 것 같다는 걸 잘 알고 있었군요. 제가 딱 생각하던 작품을 표현할 수 있는 컴포넌트가 만들어졌네요. 신기하지 않나요? (슬슬 gpt에게 위기감이 느껴지기는 하는데 뭐 저도 이 정도는 금방 만드니까.. 하하.. 하..)

 

작품을 하나만 보여줄 수는 없으니까 여러 작품이 나올 섹션이 필요하겠죠. 섹션에는 장르별로 작품들을 노출하려고 하는데요. gpt가 추천해 준 인기 있는 웹툰 장르 TOP5 로맨스, 판타지, 액션, 코미디, 드라마 중에 저는 로맨스, 판타지만 선택했고, 각 장르 섹션에는 작품을 한 5개 정도 넣으려고 합니다. 물론 더 많은 작품이 들어갈 수 있으니 스크롤 될 수 있는 형태로 만들어보겠습니다.

 

 

Q

위에서 만든 EpisodeComponent를 담는 섹션을 만들 거야. 섹션에는 제목이 들어가야 하고, 제목 아래에 EpisodeComponent들이 가로로 나열되었으면 좋겠어. 나열된 EpisodeComponent들은 좌우로 스크롤, 스와이프가 가능해야 하고 화면 밖에 나가면 안 보여야 해. jsx, tailwind, typescript 문법으로 코드를 짜줘

A

물론입니다. 설명하신 가로 스크롤 동작으로 섹션을 만드는 방법의 예는 다음과 같습니다.

import React from 'react';
import EpisodeComponent from './EpisodeComponent';
import { Episode } from 'types';

interface Props {
  title: string;
  episodes: Episode[];
}

const EpisodeSection: React.FC<Props> = ({ title, episodes }) => {
  return (
    <div className="flex flex-col">
      <h2 className="text-2xl font-bold mb-4">{title}</h2>
      <div className="flex overflow-x-auto">
        {episodes.map((episode) => (
          <div className="flex-none" key={episode.name}>
            <EpisodeComponent episode={episode} />
          </div>
        ))}
      </div>
    </div>
  );
};

export default EpisodeSection;

이 코드는 `episode`라는 소품으로 전달되는 `Episode` 객체의 배열이 있다고 가정합니다. `title` 소품은 섹션의 제목을 표시하는 데 사용됩니다.

 

`flex` 및 `flex-col` 클래스는 자식을 세로로 쌓는 플렉스 컨테이너를 만드는 데 사용됩니다. `text-2xl` 및 `font-bold` 클래스는 제목의 스타일을 지정하는 데 사용됩니다.

 

`flex` 및 `overflow-x-auto` 클래스는 에피소드의 가로 스크롤 컨테이너를 만드는 데 사용됩니다. `overflow-x-auto` 클래스는 에피소드가 컨테이너를 넘칠 때 가로 스크롤 막대를 표시하도록 브라우저에 지시합니다.

 

`episodes.map` 함수는 각 에피소드를 `EpisodeComponent`로 렌더링하는 데 사용됩니다. `flex-none` 클래스는 각 `EpisodeComponent`가 가로로 나열될 수 있도록 최소 너비를 차지하도록 하는 데 사용됩니다.

 

`key` 소품은 각 `EpisodeComponent`가 React에 의해 고유하게 식별되도록 각 에피소드의 `name` 속성으로 설정됩니다.

 

설명은 gpt가 충분히 해주었으니 바로 결과물을 보시죠!

 

 

만들어 주기는 했는데 조금 아쉽네요. 작품들이 너무 달라붙어 있고, overflow-x-auto를 사용해서 영역 밖에 나가면 스크롤이 되지만 PC에서는 스크롤, 스와이프도 되지 않는 것 같습니다. 하지만 저희는 추가적인 질문을 통해 gpt와 함께 고쳐 나갈 수 있음을 알고 있습니다. 점점 제 말투가 인공지능스러워지는 건 착각이겠죠?

 

 

Q

PC에서는 스크롤과 스와이프가 안 되는데 방법이 없을까? 그리고 작품 간에 마진도 조금 있었으면 좋겠어. 지금은 작품끼리 붙어있어서 보기가 안 좋네.

A

예, PC를 사용하는 경우 마우스의 스크롤 휠을 사용하여 에피소드 섹션에서 가로로 스크롤 할 수 있습니다. 또는 Shift 키를 누른 상태에서 스크롤 휠을 사용하여 수평으로 스크롤 할 수 있습니다.

 

`EpisodeComponent` 사이에 여백을 추가하려면 `mr`(오른쪽 여백) 클래스를 사용하여 각 구성 요소의 오른쪽에 약간의 여백을 추가할 수 있습니다. 업데이트된 코드는 다음과 같습니다.

 

//코드 생략

 

코드는 `mr`(오른쪽 여백) 부분만 수정되어 생략했습니다. 하지만 위에서 답변된 shift키를 누르고 휠로 스크롤 하는 건 제가 생각하던 기능이 아닙니다. 작품을 보기 위해 유저가 키보드와 마우스를 함께 사용해서 스크롤 해야 한다니 상상도 하기 싫네요. react-swipeable과 같은 라이브러리를 사용해서 구현해 달라고 추가로 요청했지만, 만들어 준 코드가 PC에서는 동작하지 않는군요. 아무래도 PC버전에서 스크롤에 스와이프를 통해 작품을 탐색하는 기능은 제 욕심이었던 것 같네요. (물론 카카오 페이지에서는 잘 구현되어 있습니다.) 다른 웹툰 서비스들과 유사하게 좌, 우에 버튼을 만들어서 버튼이 눌리면 스크롤이 일어날 수 있도록 요구사항을 수정해 보겠습니다.

 

Q

react-swipeable을 사용하는 방법이 PC에서는 잘 동작하지 않는 것 같아 PC에서 드래그로 스크롤 하는 게 힘들다면 버튼을 눌렀을 때 작품이 스크롤 되도록 좌우에 버튼을 만들어줘.

A
// ...기존 코드 생략
const handleScroll = (amount: number) => {
    if (containerRef.current) {
      const newPosition = scrollPosition + amount;
      setScrollPosition(newPosition);
      containerRef.current.scrollTo({
        left: newPosition,
        behavior: 'smooth'
      });
    }
  };

const scrollLeft = () => handleScroll(-containerRef.current!.clientWidth);
const scrollRight = () => handleScroll(containerRef.current!.clientWidth);

return (
// ...생략
	<button
	  className="px-2 py-1 rounded-md bg-gray-200 hover:bg-gray-300 mr-2"
	  disabled={scrollPosition === 0}
	  onClick={scrollLeft}
	>
	  &lt;
	</button>
// ... episodes 그리는 부분 생략
	<button
	  className="px-2 py-1 rounded-md bg-gray-200 hover:bg-gray-300 ml-2"
	  disabled={containerRef.current && containerRef.current.clientWidth + scrollPosition >= containerRef.current.scrollWidth}
	  onClick={scrollRight}
	>
	  &gt;
	</button>
// ...생략

export default EpisodeSection;

드디어 쓸만한 섹션 컴포넌트가 만들어졌습니다. 디자인이 조금 아쉽긴 하지만 제가 정확하게 알려주지 않은 부분이니 제가 한번 고쳐보았습니다.

자세한 코드 변경은 레포나 sharegpt를 참고해 주세요. 레포의 commit 내역 중 [gpt]라고 앞에 붙인 부분은 gpt에게 물어보고 답변된 코드를 그대로 붙여 놓은 것이고(동작하지 않는 부분만 살짝 수정했습니다.) 커밋 중 [gpt]를 안 붙인 부분은 디자인이나 간단한 수정을 위해 제가 작성한 코드 입니다. 전체적인 문답은 마찬가지로 shareGPT에서 확인하실 수 있습니다.

 

작품 홈을 반응형 디자인으로 만들고 상태 관리하기

배너도 만들었고, 섹션도 만들었으니 메인 홈 부분은 얼추 완성되었군요! 벌써 글이 꽤 길어지고 있는데 잘 따라오고 계시죠? 이제 어느 정도 gpt와 대화하는 법에 대해 감이 오셨을 테니 컴포넌트를 만드는 것 부분은 과감히 스킵하도록 하겠습니다.

 

다음으로는 메인 홈에서 작품을 클릭했을 때 작품에 대한 설명과 회차 정보를 볼 수 있는 작품 홈페이지가 있었으면 좋겠는데요. 작품의 정보를 표현할 수 있는 Webtoon 컴포넌트, 회차 정보를 표현할 수 있는 ChapterList 컴포넌트를 gpt에게 부탁해 만들었습니다. 한 화면에서 작품에 대한 설명과 회차 정보 모두를 다 보여주긴 힘들기 때문에 반응형 디자인이 필요할 것 같습니다. 화면이 넓은 PC에서는 두 컴포넌트를 좌우로 배치해 한 번에 볼 수 있게 하고, 화면이 작아 정보를 한눈에 보기 힘든 모바일의 경우에는 상하로 배치하여 볼 수 있게 질문해 봤습니다.

 

Q

tailwind responsive design md 기준으로 크면 webtoon이 왼쪽에 오고 chapterlist가 오른쪽에 오게 해 주고, 작으면 webtoon이 위에 오고 chapterlist가 아래로 오게 페이지를 만들어줘

A
// ...생략
	<div className="flex flex-col md:flex-row ">
	  {/* Webtoon component on top (if screen is smaller than md) */}
	  <div className="w-full md:w-1/2  md:hidden">
	    <Webtoon episode={testEpisode} likes={0} onLike={() => {}} />
	  </div>
	
	  {/* ChaptersList component on bottom (if screen is smaller than md) */}
	  <div className="w-full md:w-1/2 mt-4 md:mt-0 md:hidden">
	    <ChaptersList chapters={dummyChapters} />
	  </div>
	
	  {/* Webtoon component on left (if screen is larger than md) */}
	  <div className="hidden md:block w-1/2">
	    <Webtoon episode={testEpisode} likes={0} onLike={() => {}} />
	  </div>
	
	  {/* ChaptersList component on right (if screen is larger than md) */}
	  <div className="hidden md:block w-1/2 ml-4">
	    <ChaptersList chapters={dummyChapters} />
	  </div>
	</div>
);

export default WebtoonPage;

 

간단한 질문만으로 화면 해상도에 맞게 분기하는 반응형 디자인을 만들 수 있었습니다. 코드를 간단히 살펴보면 md(768px)를 기준으로 가로로 `w-1/2`씩 차지할 수 있게 그려주고 아닐 때는 `w-full`로 화면에 가득 차게 그려지는 것을 볼 수 있습니다. 물론 웹툰 정보와 회차 정보가 너비를 1:1로 가져가면 보기에 안 좋겠죠. 디자인을 조금은 자연스럽게 수정해 주었습니다.

 

눈치 빠른 분은 이미 Webtoon 컴포넌트에 likes와 onLike가 존재하는 것을 확인하셨을 겁니다. 위에서 웹툰 컴포넌트를 만들 때 '좋아요' 개수를 표시하기 위해 만들어 주었는데요. 이를 통해 '좋아요' 버튼을 눌렀을 때 반영될 수 있게 이벤트 처리까지 해보도록 하겠습니다.

 

'좋아요' 상태를 변경할 수 있게 만들어달라고 질문하니 React State를 사용해서 '좋아요'를 구현해 주었습니다.

// ...생략
const [likes, setLikes] = useState(0); // Initialize the state of likes to 0

const handleLike = () => {
  setLikes(likes + 1); // Update the state of likes by incrementing it by 1
};
// ...생략
export default WebtoonPage;

 

하지만 이렇게 상태를 구현하면 작품 홈에서 이탈했을 때, 즉 WebtoonPage가 unmount 되었을 때 상태는 날아가게 됩니다. 서비스가 떠 있는 동안은 상태가 살아있을 수 있도록 전역상태관리 라이브러리 redux를 활용해 보도록 하겠습니다. next.js에서 조금 더 편리하게 사용하기 위해서 redux toolkit로 설정해 봅니다. 그리고 모든 작품이 동일한 '좋아요' 상태를 공유하면 안 되니 작품마다 '좋아요' 상태를 가질 수 있도록 조건도 추가해 보겠습니다.

 

 

Q

WebtoonPage에서 좋아요 state를 사용한 부분을 redux-toolkit 사용하는 방법으로 변경해 주고, episode.id에 따라 좋아요 숫자를 반영할 수 있게 코드를 작성해 줘

A

좋아요 수에 로컬 상태 대신 Redux Toolkit을 사용하고 에피소드 ID를 기반으로 좋아요 수를 반영하도록 하려면 코드를 다음과 같이 변경할 수 있습니다.

 

1. 필요한 Redux Toolkit 기능을 가져오고 좋아요 수를 저장할 조각을 만듭니다.

import { createSlice } from '@reduxjs/toolkit';

const webtoonSlice = createSlice({
  name: 'webtoon',
  initialState: {},
  reducers: {
    incrementLikes(state, action) {
      const { episodeId } = action.payload;
      if (!state[episodeId]) {
        state[episodeId] = { likes: 0 };
      }
      state[episodeId].likes += 1;
    }
  }
});

export const { incrementLikes } = webtoonSlice.actions;
export default webtoonSlice.reducer;

2. WebtoonPage 구성 요소에서 useState 후크를 useSelector로 교체하여 Redux 스토어에서 좋아요 수를 검색하고 useDispatch를 사용하여 incrementLikes 작업을 발송합니다.

import { useSelector, useDispatch } from 'react-redux';
import { incrementLikes } from '../src/webtoonSlice';

const WebtoonPage = ({ episode }: { episode: Episode }) => {
  const likes = useSelector((state) => state.webtoon[episode.id]?.likes || 0);
  const dispatch = useDispatch();

  const handleLike = () => {
    dispatch(incrementLikes({ episodeId: episode.id }));
  };

  // rest of the code
};

3. Webtoon 구성 요소에서 로컬 상태 대신 Redux 스토어의 좋아요 수를 사용하도록 likes 소품을 업데이트합니다.

const Webtoon = ({ episode, likes, onLike }: WebtoonProps) => {
  return (
    <div className="flex flex-col items-center">
      {/* rest of the code */}
      <div className="text-center">
        <button onClick={onLike} className="p-2 border rounded-md">
          {likes} likes
        </button>
      </div>
    </div>
  );
};

 

이러한 변경으로 좋아요 수는 Redux 스토어에 저장되고 에피소드 ID를 기반으로 업데이트됩니다. WebtoonPage 구성 요소는 useSelector 및 useDispatch를 사용하여 각각 좋아요 수를 검색하고 업데이트합니다. Webtoon 구성 요소는 업데이트된 좋아요 수를 Redux 스토어에서 소품으로 받습니다.

 

역시 redux-toolkit 도 프로젝트에 세팅하는 작업이 필요해서 그런지 한 번에 깔끔하게 되지는 않았습니다. store가 필요한데 만들어지지 않아서 추가하고, _app에 Provider로 연결하는 부분이 빠져 있어서 연결하며 일부 수정하게 되었습니다. 공식 문서와 next.js에 redux-toolkit을 설정하는 방법을 찾아보며 설정을 반영했습니다. gpt가 알려준 코드는 완벽하지는 않지만, setState를 리덕스 액션으로 잘 변경하고 컴포넌트와 연결하는 방법을 알려 준 것 같네요. 이 코드로 저희는 작품별로 '좋아요'를 처리하는 기능까지 완성하게 되었습니다!

 

TMDB API 연동하기

마지막으로 API 연동하는 부분에 대해 진행해 볼까 합니다. copilot나 gpt를 활용해서 ‘오늘 날씨를 알려주는 API를 연결해 줘~’와 같은 기능에 맞는 Open API를 가져와 구현하는 예시는 많이 보았는데요. 과연 명세가 정확히 정해진 API에 대해서도 잘 동작할지 확인해 보고자 합니다. 저는 영화 정보를 가져올 수 있는 The Moview Database(TMDB) API 활용해보려고 합니다.

특정 영화의 정보를 받아올 수 있는 GET movie/{movie_id} API를 활용해보겠습니다. TMDB API 사이트에 들어가 보면 어떤 값을 넣어서 보내야 하는지, 어떤 응답으로 오고 스키마가 어떻게 되어 있는지 잘 명시되어 있습니다. 저는 응답 json을 가지고 react-query를 통해 데이터를 받아올 수 있는 코드를 만들어보겠습니다.

 

 

Q
{
  "adult": false,
  "backdrop_path": "/fCayJrkfRaCRCTh8GqN30f8oyQF.jpg",
  "belongs_to_collection": null,
  "budget": 63000000,
  "genres": [
    {
      "id": 18,
      "name": "Drama"
    }
	// ...생략 https://developers.themoviedb.org/3/movies/get-movie-details 에 있는 example 입니다.
	"title": "Fight Club",
  "video": false,
  "vote_average": 7.8,
  "vote_count": 3439
}

이런 json 응답을 가지는 api 요청을 react-query와 typescript로 만들어줘. endpoin는 https://api.themoviedb.org/3/movie/550?api_key= 이거야

A

지정된 엔드포인트에 대해 TypeScript를 사용하여 React Query API 요청을 생성하려면 다음 코드를 사용할 수 있습니다.

import { useQuery } from 'react-query';

interface Movie {
  adult: boolean;
  backdrop_path: string | null;
  belongs_to_collection: unknown;
  budget: number;
  genres: { id: number; name: string }[];
  //...생략
  title: string;
  video: boolean;
  vote_average: number;
  vote_count: number;
}

const fetchMovie = async (): Promise<Movie> => {
  const response = await fetch(
    'https://api.themoviedb.org/3/movie/550?api_key=YOUR_API_KEY'
  );
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

const useMovieQuery = () => {
  return useQuery<Movie>('movie', fetchMovie);
};

클라이언트 측에서 API 요청을 하려면 React Query에서 제공하는 `useQuery` 후크를 사용할 수 있습니다. 다음은 React 구성 요소에서 사용하는 방법의 예입니다.

import { useMovieQuery } from 'react-query';

const MovieComponent = () => {
  const { data, isLoading, isError, error } = useMovieQuery();

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div>An error occurred: {error.message}</div>;
  }

  if (!data) {
    return <div>No data available</div>;
  }

  return (
    <div>
      <h1>{data.title}</h1>
      <p>{data.overview}</p>
      <img src={`https://image.tmdb.org/t/p/w500${data.poster_path}`} alt={data.title} />
    </div>
  );
};

다시 말하지만 `YOUR_API_KEY`를 The Movie Database API의 고유한 API 키로 바꾸는 것을 잊지 마십시오. 또한 이 코드를 사용하기 전에 React Query(`react-query` 및 `react-query-devtools`)에 필요한 종속 항목을 설치해야 합니다.

 

아주 간단하게 TMDB API를 활용하는 코드가 완성되었습니다. 직접 구현했다면 26개나 되는 필드의 json 응답을 읽고 파악해서 타입을 만들고 변환하는 데 한참 시간이 걸렸을 텐데 순식간에 만들어졌습니다.

 

mock 데이터를 만들기 위해 faker.js를 활용하기도 하는데, gpt를 활용하면 mock 데이터도 손쉽게 만들 수 있습니다. 위에서 작업했던 ChapterList에 들어가 있던 dummyChapters 들은 모두 gpt를 통해 만들어진 mock 데이터들입니다.

 

위 영화 정보를 그릴 수 있는 적절한 컴포넌트를 만들어달라고도 요청하였는데 Movie 인터페이스에 맞게 정보들을 표현하는 MovieDetails 컴포넌트를 만들어 주었습니다. 역시나 아직 gpt의 대답은 완벽하게 동작함을 보장하지 않기 때문에 적절하게 조립하고 디자인을 가다듬어 주었습니다.

 

| 마무리

드디어 끝이 났습니다. jsx와 tailwind 형식으로 컴포넌트를 만들고, readux-toolkit으로 상태를 관리해 보며 react-query를 통해 API를 연동하는 작업까지 간단하게 진행해 보았습니다. 복잡한 비즈니스 로직을 구현하지는 않았지만, 작품을 보여주는 간단한 웹툰 서비스를 구현해 낸 것 같네요. 프로젝트 진행은 뽀모도로 기법을 사용하여 진행했는데 개발하는 시간만 계산했을 때 총 10 뽀모도로(약 5시간)가 소요되었습니다.

 

gpt는 ‘이 세상에 없는 획기적인 웹툰 서비스 코드를 만들어줘!’와 같은 터무니없는 질문을 해결해 줄 수는 없습니다. 하지만 작은 코드 스니펫을 만드는 데는 훌륭하게 동작합니다. 저는 gpt에게 질문을 가볍게 던졌지만, 프롬프트를 잘 활용하면 더 좋은 결과를 얻을 수 있을 것 같습니다. (글을 적는 중에 gpt-4가 공개되어 앞으로 더욱 기대되네요.) 하지만 openAI에서도 한계점으로 말하고 있는 hallucinates(환각) 증상이 있어서 때때로 잘못된 코드를 알려줍니다. 답변된 코드가 제대로 된 코드인지 오류는 없는지 파악하는 게 중요하긴 합니다만 검증할 수 있는 작은 조각을 만들어서 잘 이어 붙인다면 거대한 프로젝트 까지도 만들 수 있지 않을까 싶습니다. 특히나 프론트엔드라는 영역은 눈으로 확인이 가능하여 코드의 신뢰성(reliability)을 확인하기까지의 과정이 쉬우므로 더욱 높은 활용이 기대됩니다.

 

FE개발자란 무엇일까요? 저희는 모두 FE개발자가 그저 적혀있는 명세를 화면으로 구현만 하는 사람이 아니라는 것을 알고 있습니다. 더 좋은 제품을 만들기 위해 기획, 디자인과 협의하고 때로는 제안하고, 시시각각 변하는 요구사항을 빠르게 대응합니다. 그리고 앞으로 어떤 요구사항이 추가될지를 생각해서 유지보수를 위해 구조를 설계하고 지속해서 개선합니다. 미래를 내다볼 수 있는 사업가나 기획자와 일할 수 있어서, 미래에도 바뀌지 않는 마법의 명세서를 얻을 수 있다면 gpt만으로 개발할 수 있을지도 모르겠지만요!

 

마지막으로 AI 시대에 FE개발자는 어떤 역량을 강화해야 좋을지 gpt에게 한번 물어보겠습니다.

 

 

Q

AI가 발전하면서 점차 개발자가 할 수 있는 영역을 대체할 수 있을 것 같아. AI 시대에 FE개발자는 어떤 역량을 강화하면 좋을까?

A

기본이 중요하다

 

앞으로 언어 모델뿐만 아닌 많은 다른 AI 기술이 발전하면서 사업적으로도 그렇고 개발 방법론적으로도 많은 변화가 있을 것으로 예상됩니다. openAI에서 폐쇄적인 정책이 아닌 공개 API로 ChatGPT 사용을 열었기 때문에 이에 따라 많은 서비스가 생겨날 것이고, 오히려 이런 서비스들을 유저에게 전달하는 FE개발자에게는 또 새로운 기회로 거듭나지 않을까 싶습니다.

 

gpt의 원리에 대해서도 정리해서 넣고 싶었는데 넣었다간 정말 1시간 분량의 글이 될 것 같아 여기서 마무리하도록 하겠습니다. 시간 내서 긴 글을 읽어 주셔서 감사합니다. 개발자로서 AI 시대에 어떤 방향으로 준비해야 할지, 어떤 부분이 대체되고 어떤 부분이 더욱 중요해질지 한 번쯤 생각해 보는 시간이 되었다면 더할 나위 없이 기쁠 것 같습니다.

감사합니다!

 

 

진짜 마지막으로 사용했던 툴들과 도움이 되었던 자료에 대해 혹시나 궁금할 분들이 있으실까 싶어 정리해 보았습니다.

 

◼️ 프로젝트 데모 페이지 🔗
◼️ 프로젝트 git repo 🔗
◼️ ChatGPT 🔗
◼️ 프롬프트 지니 : ChatGPT에 질문할 때 한국어를 자동으로 영어로 번역해 질문하고, 영어로 답변된 내용을 다시 한국어로 번역해 주는 크롬 플러그인 🔗
◼️ shareGPT : ChatGPT에게 질문한 결과를 공유할 수 있게 해주는 크롬 플러그인 🔗
◼️ stable diffusion WebUI : stable diffusion 모델 기반으로 이미지를 생성할 수 있게 하는 프로그램 🔗
◼️ TMDB API : 영화 산업 관련 데이터를 사용할 수 있게 한 RESTful API 🔗
◼️ 테오의 프론트엔드 역사와 잘하는 프론트 엔드 개발자란? 🔗
◼️ 그림으로 설명하는 GPT-2 🔗
◼️ OpenAI GPT-3: Everything You Need to Know 🔗
◼️ 블로그 글을 쓰는 도중에 ChatGPT-4가 나왔습니다! 🔗
◼️ 한국어 임베딩 : 임베딩의 개념과 NLP의 발전에 대해 알 수 있는 도서 🔗

 

 


 

 

더 많은 FE 지식을 나누고 싶다면?! 카카오엔터테인먼트 FE기술블로그 [바로가기]