[nextjs] 스팀 최신글 보여주기

in #kr2 years ago

개요

  • nextjs 13 을 사용하여 빠르게 스팀 최신 글 목록 정보를 보여주도록 하는 것이 목표

image.png

참조링크 : https://nextjs.org/docs/getting-started

설치

nodejs 14.18 이상 설치 되지 않은 분들은 설치 후 아래 작업 수행 바립니다. ( 23.04.21 기준 18.16 이 LTS 버전 받는 것을 추천 드립니다. )

npx create-next-app@latest 커맨드를 입력하여 설치를 수행 합니다.

(테스트) 실행

설치 후 my-app (별도로 지정하지 않았다면...) 폴더로 이동 후

npm run dev 로 app 을 실행 후 http://localhost:3000 으로 접속하면 화면이 동작하는 것을 확인 하실 수 있습니다.

steem 최신글 목록보기 만들기

하려고 하는 기능을 요약 해보면

  • rpc20 으로 데이터 가져오기 => 화면에 보여주기

간단? 합니다.

데이터 가져오기

rpc20 형태의 데이터 구조를 만들고 이를 fetch 를 통해 정보를 가져오면 됩니다.

하지만... typescript 로 작업을 진행 하려 해보니 보내는 데이터 받는 데이터를 구조화 해보도록 하겠습니다. ( 전체가 아닌 필수요소만 우선적으로 만들어봄 )

보내는 데이터

// 보낼 때 사용하는 정보 
interface IBlogParam {
  tag: string;
  limit: number;
  truncate_body?: number;
}

받는 데이터

// 받을 때 사용하는 정보
interface IPostResult {
  post_id: number;
  author: string;
  permlink: string;
  category: string;
  parent_author: string;
  parent_permlink: string;
  title: string;
  body: string;
  json_metadata: string;
  last_update: string;
  created: string;
  url: string;
}

자 이제 rpc20 통신을 위한 함수를 만들어 보면

function rpc20(method: string, params: IBlogParam[], id: number = 1) {
  return {
    body: JSON.stringify({ jsonrpc: '2.0', method, params, id }),
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    method: 'POST',
  };
}

이제 화면이 렌더링 하기 전 props (값) 을 지정 해주면

export async function getStaticProps() {
  const API_URL: string = 'https://api.steemit.com';
  const method: string = 'condenser_api.get_discussions_by_created';
  const params: IBlogParam[] = [
    {
      tag: 'kr-dev', // 태그
      limit: 100, // 보여 줄 글 수 (최대 100)
      truncate_body: 0, // 본문 자르기 (0: 전체)
    },
  ];
  const OPTIONS = rpc20(method, params);
  const res = await fetch(API_URL, OPTIONS);
  const json = await res.json();
  const posts: IPostResult[] = json.result;

  return {
    props: {
      posts,
    },
  };
}

이제 대망의 화면 그리기

tailwind 가 기본적으로 내장 되어 있으니, tailwindcss-docs 를 참조하여 대충 만들어 봅니다.

날짜를 손쉽게 포맷팅 하기위해 moment 를 설치 해줬습니다.

import Link from 'next/link';
import moment from 'moment-timezone';

export default function Steem({ posts }: { posts: IPostResult[] }) {
  return (
    <div>
      {posts.map((post) => (
        <div
          key={post.post_id}
          className="rounded-lg border-8 border-transparent"
        >
          <div className="pl-0">
            <span>
              [
              {moment(post.created + '.000Z')
                .tz('Asia/Seoul')
                .format('YYYY-MM-DD HH:mm:ss')}
              ]
            </span>
            <span className="pl-2 text-green-300">{post.author}</span>
          </div>
          <div>
            <Link
              href={'https://steemit.com' + post.url}
              target="_blank"
              className="pl-10"
            >
              <p className="w-1/2 text-xl underline decoration-sky-500 text-ellipsis overflow-hidden">
                {post.title}
              </p>
            </Link>
          </div>
        </div>
      ))}
    </div>
  );
}

전체 소스

steem.tsx

import Link from 'next/link';
import moment from 'moment-timezone';

export default function Steem({ posts }: { posts: IPostResult[] }) {
  return (
    <div>
      {posts.map((post) => (
        <div
          key={post.post_id}
          className="rounded-lg border-8 border-transparent"
        >
          <div className="pl-0">
            <span>
              [
              {moment(post.created + '.000Z')
                .tz('Asia/Seoul')
                .format('YYYY-MM-DD HH:mm:ss')}
              ]
            </span>
            <span className="pl-2 text-green-300">{post.author}</span>
          </div>
          <div>
            <Link
              href={'https://steemit.com' + post.url}
              target="_blank"
              className="pl-10"
            >
              <p className="w-1/2 text-xl underline decoration-sky-500 text-ellipsis overflow-hidden">
                {post.title}
              </p>
            </Link>
          </div>
        </div>
      ))}
    </div>
  );
}

function rpc20(method: string, params: IBlogParam[], id: number = 1) {
  return {
    body: JSON.stringify({ jsonrpc: '2.0', method, params, id }),
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    method: 'POST',
  };
}

interface IBlogParam {
  tag: string;
  limit: number;
  filter_tags?: string[];
  select_authors?: string[];
  select_tags?: string[];
  truncate_body?: number;
}

interface IPostResult {
  post_id: number;
  author: string;
  permlink: string;
  category: string;
  parent_author: string;
  parent_permlink: string;
  title: string;
  body: string;
  json_metadata: string;
  last_update: string;
  created: string;
  url: string;
}

export async function getStaticProps() {
  const API_URL: string = 'https://api.steemit.com';
  const method: string = 'condenser_api.get_discussions_by_created';
  const params: IBlogParam[] = [
    {
      tag: 'kr-dev', // 태그
      limit: 100, // 보여 줄 글 수 (최대 100)
      // filter_tags: ['kr'],
      // select_authors: ['wonsama'],
      // select_tags: ['kr'],
      truncate_body: 100, // 본문 자르기 (0: 전체)
    },
  ];
  const OPTIONS = rpc20(method, params);
  const res = await fetch(API_URL, OPTIONS);
  const json = await res.json();
  const posts: IPostResult[] = json.result;

  return {
    props: {
      posts,
    },
  };
}

맺음말

참 쉽죠 ?

Sort:  

[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.

Upvoted! Thank you for supporting witness @jswit.

역시 능력자 원사마님. 제 눈에는 안쉬워보이는데요.ㅋㅋ

!sdbot wonsama

used prompt : wonsama
created by @anpigon
[이벤트] sdbot 활성화를 위한 이벤트 - 4월 말까지 진행

이제야 찾아 보네요. 스팀에서 자료를 찾은 것이 어렵다 보니 그런가 봅니다. 저에게 필요한 자료였군요. 아직 typescript를 공부하지 않아서 이것을 이해하려면 또 시간이 필요한 것 같습니다.
그러나 꼭 공부해서 제대로 이해하도록 하겠습니다. 이렇게 자료를 남겨주시니 고맙습니다. 미리 알았더라면 그동안 고생도 좀 덜했을 것 같습니다.