[nextjs] 스팀 최신글 보여주기
개요
- nextjs 13 을 사용하여 빠르게 스팀 최신 글 목록 정보를 보여주도록 하는 것이 목표
설치
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,
},
};
}
맺음말
참 쉽죠 ?
[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.
Upvoted! Thank you for supporting witness @jswit.
역시 능력자 원사마님. 제 눈에는 안쉬워보이는데요.ㅋㅋ
!sdbot wonsama
used prompt : wonsama
created by @anpigon
[이벤트] sdbot 활성화를 위한 이벤트 - 4월 말까지 진행
이제야 찾아 보네요. 스팀에서 자료를 찾은 것이 어렵다 보니 그런가 봅니다. 저에게 필요한 자료였군요. 아직 typescript를 공부하지 않아서 이것을 이해하려면 또 시간이 필요한 것 같습니다.
그러나 꼭 공부해서 제대로 이해하도록 하겠습니다. 이렇게 자료를 남겨주시니 고맙습니다. 미리 알았더라면 그동안 고생도 좀 덜했을 것 같습니다.