[개인개발]'하고 싶은 게 없다'는 인터넷 서비스를 조금 해결했습니다.

개요


트위터 APIv2'드림워터카'를 활용한 트위터 시장조사 서비스를 제작했다.
https://ys.7oh.dev

개발의 계기


여러분 지금 하고 싶은 거 있어요?
이어 "조합과 개인 개발을 만들고 싶지만 하고 싶은 게 없다"며 "프로그래밍 기본을 마치면 뭘 해야 할지 모르겠다"는 이런 엔지니어를 종종 볼 수 있다.
나는 프로그래밍을 시작할 때도 이런 고민이 있었기 때문에 매우 공감했다.
마찬가지로 트위터에서도 많은 비엔지니어들이'이런 앱이 있었으면 좋겠다','이런 서비스가 있었으면 좋겠다'고 희망한다.
이 두 개가 잘 맞으면 어떤 고민도 해결할 수 있죠?개발의 계기가 된 것 같습니다.

콘셉트


응용 프로그램과 서비스 등 제품은 일부 문제와 문제를 해결하기 위해 존재한다.
시스템 개발에서 먼저 해결해야 할 과제를 설정한 뒤 그 고려 기능을 해결하기 위해서다.
과제에 앞서 기능을 먼저 했다면'무엇을 위한 기능인가','누구를 위한 기능인가'를 모를 수 있다.
어렵게 만들면 쓸만한 걸 만들고 싶어요.
해결해야 할 과제는 이 세상 곳곳에 있지만 한 사람이 알아차리기는 어렵다.
과제를 발견하는 가장 좋은 방법은 서로 다른 입장의 사람들의 고민과 생각을 경청하는 것이다.
다양한 입장을 접한 사람들의 고민과 아이디어에 가장 적합한 도구는 트위터 등 SNS다.
특히 트위터는 2020년까지 일본인 약 4명이 사용하고 있다.( 총무성 조사 )
최근 인수로 더욱 눈길을 끌고 있다.
실제로 트위터를 검색해보니 매일 많은 요구가 있었다='있었으면 좋겠다'.
"몽색수차"는 "이런 게 있었으면 좋겠다", "이런 게 있었으면 좋겠다"는 희망 트윗만 트위터에 일람하는 인터넷 서비스다.
구상에 지장을 주지 않기 위해 무관한 트윗은 극력 배제했다.
모든 트위터는 적용, 서비스, 게임, 이벤트 등으로 표시돼 목적에 따라 필터링할 수 있다.
과제 발견, 시장조사, 구상 훈련 등에 꼭 활용하세요.

제품 이름 정보


[몽색수차]
나는 트위터를 강에 비유해 에너지를 만드는 수차를 연상했다.
최근 로마자 표기의 서비스명은 주류지만 일부러 한자를 차별화 목표로 삼고 있다.

기술 스택

  • 프런트엔드: Next.js(SG), TypeScript, Tailwind CSS
  • 백엔드: Go, Echo
  • 데이터베이스: PostgreSQL
  • PaaS: Heroku, Cloudflare
  • SG를 도입한 이유


    이번 제품은 데이터를 실시간으로 업데이트할 필요가 없기 때문에 넥스트.js의 Static Generation(SG)이 적용됩니다.
    SG는 구축할 때 정적 HTML을 만들기 위해 CDN 캐시를 사용할 수 있습니다.
    데이터에 관해서는 구축할 때 SG 함수에서 백엔드 API를 호출할 수 있으며 이를 바탕으로 매 페이지의 경로와 Propos를 만들 수 있다.
    백엔드 API와 데이터베이스는 구축할 때만 액세스할 수 있으므로 미리 사용량을 추정할 수 있습니다.
    구역과 규격에 특별히 신경 쓸 필요가 없기 때문에 백엔드는 히로쿠의 무료 방안을 채택했다.

    동적 라우팅을 통해 페이지 자동 생성


    페이지 정보, Next.js의 동적 경로 기능을 통해 페이지를 자동으로 생성할 수 있습니다.
    Next.js의 페이지는 기본적으로 페이지 디렉터리 아래에 있습니다.jsx, .tsx 파일을 경로로 직접 식별합니다.
    예를 들어, "pages/about.tsx"파일은 "/about"을 통해 액세스할 수 있습니다.
    파일 이름이나 디렉토리 이름에서 [매개변수 이름]을 사용하면 경로가 패라메트릭화되고 뒤에 설명된 SG용 함수에서 매개변수에 숫자를 매핑할 수 있습니다.
    SG에서 사용하는 함수는 다음과 같습니다.
  • getStaticPaths(): 경로를 생성하고 매개변수에 매핑합니다.생성된 경로는 getStaticPropos() 함수에 전달됩니다.
  • getStaticPropos(): 경로 매개변수를 사용할 수 있습니다.페이지 어셈블리에 전달되는 Proops를 생성합니다.
  • 예를 들어'pages/[page].tsx'파일을 만들고 다음 코드를 기술하면 수치가 매개 변수에 비치며'/tag/1/page/2''/tag/1/page/2'처럼 접근할 수 있다.
    또한 getStaticPropos () 함수에서 tag와 페이지를 매개 변수로 수신할 수 있기 때문에 이 값을 사용하여 백엔드 API에 요청할 수 있습니다.
    제작된 프로포즈는 페이지 구성 요소에 전달되기 때문에 이후에는 평소와 같이 화면에만 표시됩니다.
    다음 코드는 동적 루트의 샘플입니다.
    pages/tag/[tag]/page/[page].tsx
    import TweetCard from 'components/TweetCard';
    import type { GetStaticPaths, GetStaticProps, GetStaticPropsContext, InferGetStaticPropsType, NextPage } from 'next'
    import { responseSymbol } from 'next/dist/server/web/spec-compliant/fetch-event';
    import { TweetIndexResponse } from 'types/Tweet';
    
    type Props = InferGetStaticPropsType<typeof getStaticProps>;
    
    export default function Index({ tweets }: Props) {
      return (
        <main>
          {tweets && tweets.map((tweet) => <TweetCard tweet={tweet} />)}
        </main>
      )
    }
    
    // SG時のパス生成
    export const getStaticPaths: GetStaticPaths = async () => {
      // サンプルのため直書きしているが、通常はここでバックエンドから総件数などのメタ情報を取得してpathsを生成する
      return {
        paths: [
          { params: { tag: '1', page: '1' } },
          { params: { tag: '1', page: '2' } },
          { params: { tag: '2', page: '1' } },
          { params: { tag: '2', page: '2' } },
        ],
        fallback: false,
      };
    };
    
    // SG時のProps生成
    export const getStaticProps = async (context: GetStaticPropsContext) => {
    
      // getStaticPaths()関数からパスのパラメータを受け取る
      const { params } = context;
    
      // パラメータを使用してバックエンドAPIへリクエストする
      const resp = await fetch(`https://****.com/tweet/index?tag_id=${params?.tag}&page=${params?.page}`);
      const tweets: TweetIndexResponse = await resp.json();
    
      // ページコンポーネントに渡されるPropsを生成する
      return {
        props: {
          tweets: tweets.items,
        },
      };
    }
    

    자동 구축 및 설계


    생성된 HTML은 디버깅 후Cloudflare CDN 고속 라우팅됩니다.
    Cloudflare Pages의 무료 방안은 매달 500회 만들 수 있다.그리고 요구와 대역폭에 제한이 없다는 것은 매우 어려운 일이다.
    Cloudflare Pages는 보통 Giithub Push에 대한 시간에 구축을 터치하지만 Deploy Hooks라는 디자인에 사용되는 Webhook을 사용하면 임의의 시간에 터치할 수 있다.
    Cloudflare Workers부터CRON 트리거 정기적으로 구축을 실행할 때 최신 데이터가 페이지에 반영됩니다.

    백엔드에서 트위터 가져오기


    트위터 검색에 관해서는 Go의 네트워크/http 패키지를 사용하여 트위터 검색 APIv2를 요청합니다.
    이때 since가 마지막으로 받은 트위터 IDid 매개 변수로 지정하여 새 트위터만 가져옵니다.
    		target := fmt.Sprintf("https://api.twitter.com/2/tweets/search/recent?tweet.fields=created_at&user.fields=profile_image_url&expansions=author_id&max_results=%d&query=%s", count, keywords)
    	if id != "" {
    		target += fmt.Sprintf("&since_id=%s", id)
    	}
    
    응답을 보면 사용자 정보의 처리가 이전 버전과 다르고 처음에는 혼란스러웠다.
    사용자 정보는 autohor입니다.id와 includes.users와 연결을 맺어야 하기 때문에 해당 구조의 실체를 만들었습니다.

    다른 신경 쓴 곳.

  • 작은 시작 확인과 함께 가능하면 설치를 추진했다.애초 공식적인 트윗 매몰을 논의했으나 성능 때문에 SG를 적용했다.
  • 트위터 APIv2를 활용한 제품을 예전부터 만들고 싶었어요.앱 단위라면 베어톡이 이전보다 더 쉽게 시작됐다.
  • 백엔드는 층 구조로 개발되었다.테스트 가능성이 높아 영향 범위를 식별하기 쉽다.차원 구조에 관하여 과거에 글을 쓴 적이 있다.→ [Go] 테스트까지 써서 이해하는 차원 구조.
  • CSS와 관련하여 Tailwind CSS3을 사용합니다.역시 React의 구성 요소와 상통하는 것이 좋다.버전 2의 설정과 다소 다르니 주의가 필요하다.Next.js+Tailwind CSS에 관해서는 과거에도 기사가 쓰여 있었다.→ 【TypeScript】Next.js+Tailwind CSS를 사용하여 SPA를 빠르게 만드는 방법
  • 모두 무료이기 때문에 운영비는 월 0엔이다.클라우드 플레이어는 상위 계획의 가격도 양심적이기 때문에
  • 총결산


    예전부터 어떤 콘셉트가 담긴 제품을 만들고 싶었던 터라 무사히 마칠 수 있어서 좋았다.
    이번처럼 일방적으로 적용하면 성능과 컨디션 관리 등 고민거리도 줄어든다.
    운용원가와 관련해서는 공짜 애플릿부터 시작할 수 있어 다행인 만큼 두려워하지 말고 끊임없이 개인 개발에 도전해 주시기 바랍니다.
    만약 이 보도가 다른 사람의 학습이 될 수 있다면 매우 좋겠다.
    여기까지 읽어주셔서 감사합니다!

    좋은 웹페이지 즐겨찾기