728x90
반응형

바쁜 하루하루를 살다가(?)

즐거운 휴가를 보내고 왔다.

놀고 왔으니 할 건 해야지


1. 사이트 파비콘 만들기

사이트 이름 옆에 존재하는 동그란 저 로고가 파비콘이다.

내가 브라우저를 막 열었을 때 사이트를 식별하는 것에 도움을 주고, 브랜드의 정체성을 보여주기도 한다.

당연 필수는 아니다.

하지만 없으면 밋밋하니
Figma와 일러스트레이터를 이용해 난 만든다 :)

Before
After

export const metadata = {
  title: "타로타르트",
  description: "달콤하게 맛보는 나의 운명: 타로타르트",
  icons: {
    icon: [
      { url: "/favicon.ico", type: "image/x-icon", sizes: "any" },
      { url: "/favicon-32x32.png", type: "image/png", sizes: "32x32" },
      { url: "/android-icon-192x192.png", type: "image/png", sizes: "192x192" },
    ],
    apple: { url: "/apple-icon-180x180.png", sizes: "180x180" },
    manifest: "/manifest.json",
  },
  other: {
    "google-adsense-account": "ca-pub-4184442303277144",
  },
};

metadata까지 수정하면 끝

 

2. Google Search Console

여기에 내 사이트를 등록해 보자

그럼 검색 시 내 사이트가 잘 나올 수 있다!

 

2-1. 필요한 파일 만들기

- public/robots.txt

User-agent: *
Allow: /
Sitemap: https://tarot-tart.vercel.app/sitemap.xml

- user-agent: * 설정으로 모든 검색엔진에 적용하겠다는 말이고

- Allow: / 설정으로 모든 경로 크롤링을 허용하겠다는 말이다.

- sitemap: your domain 설정으로 사이트맵 위치를 명시하여 검색 효율을 높인다.

 

- public/sitemap.xml

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://tarot-tart.vercel.app/</loc>
    <changefreq>weekly</changefreq>
    <priority>1.0</priority>
  </url>
</urlset>

- loc은 노출시키고 싶은 URL이고

- changefreq는 얼마나 자주 바뀌는지

- priority: 는 검색엔진에게 중요도를 표시한다.

 

2-2. 메타태그 설정

layout.tsx 에 메타태그를 야무지게 채워 넣는다.

export const metadata = {
  title: "타로타르트",
  description: "달콤하게 맛보는 나의 운명: 타로타르트",
  keywords: ["타로", " 타로카드", "타로점", "운세", "운명"],
  url: "https://tarot-tart.vercel.app/",
  siteName: "타로타르트",
  locale: "ko_KR",
  type: "website",
  icons: {
    icon: [
      { url: "/favicon.ico", type: "image/x-icon", sizes: "any" },
      { url: "/favicon-32x32.png", type: "image/png", sizes: "32x32" },
      { url: "/android-icon-192x192.png", type: "image/png", sizes: "192x192" },
    ],
    apple: { url: "/apple-icon-180x180.png", sizes: "180x180" },
    manifest: "/manifest.json",
  },
  openGraph: {
    title: "타로타르트",
    description: "달콤하게 맛보는 나의 운명: 타로타르트",
    url: "https://tarot-tart.vercel.app/",
    siteName: "타로타르트",
    images: [
      {
        url: "/banner.png",
        width: 1200,
        height: 630,
        alt: "타로타르트 미리보기 이미지",
      },
    ],
    locale: "ko_KR",
    type: "website",
  },
  twitter: {
    card: "summary_large_image",
    title: "타로타르트",
    description: "달콤하게 맛보는 나의 운명: 타로타르트",
    images: ["/banner.png"],
  },
  other: {
    "google-adsense-account": "ca-pub-4184442303277144",
    "google-site-verification": "KjRABD7__6IdKg9gv6RUmh1X2R1lFUEtT44FXGXxSr4",
  },
};

 

마지막 other를 보면 뭔가 있다.

첫 번째 꺼는 에드센스 등록을 위한 코드고

두 번째 거는 구글 서치 소유권 인증 코드이다.

(노출돼도 무방하다)

 

https://search.google.com/

 

Google

 

www.google.com

그리고 여기 가서 등록하자

등록은 전부 했다.

시간이 조금 걸린다고 하니

오늘은 여기까지!


 

동호회 어플 기능 추가해야 한다...

형님들이 극성이야!

728x90
반응형
728x90
반응형

오래간만에 글을 쓴다.

이제 백수가 된 나는 심심하던 차 장난감을 하나 만들기로 한다.

예전부터 기획만 했던 서비스였는데,

바로 '타로점' 프로젝트다.


1. 디자인 구체화

 

먼저, 머릿속에 있는 디자인/기획을 공책에 적고 GPT와 Figma를 이용해 구체화시켜준다.

(GPT 없었으면 이미지 못 만들 뻔)

 

2. 프로젝트 시작한다. 완성!!

https://tarot-tart.vercel.app/

 

타로타르트

타르트 시간세트 - 과거-현재-미래 -

tarot-tart.vercel.app

 

-끝-

 


[잘한 일]

1. supabase 

- 1인 개발이었기에 여러 가지 사용해 보는 것이 경험치 축적에 좋다고 생각했다.

- 타로서비스의 핵심은 '결과'였는데, 내가 생각하는 다양한 해석의 결과를 보여주려면 예전에 가벼운 사이드프로젝트 할 때 사용했던 json 파일 하나로는 부족하다고 생각했다.

- Firebase를 활용하자니 noSQL이라 구조관리가 번거로울 것 같았고, 예전에 백엔드 교육을 받을 때 SQL을 다뤄본 기억이 남아있어 PostgreSQL 기반 supabase을 쓰기로 했다.

- Next.js와 잘 어울린다는 이야기를 아는 개발자 지인에게 들었다. (형님 감사합니다!)

 

supabase 사용해 보기

- 사실 딱히 어려움은 없었다. (제대로 못써서 그런가..)

- 그냥 테이블 만들어서 데이터를 만들고 insert 했다.

- 실시간 이용자 집계는 좀 헷갈렸다. (sessionId를 만들고, 로그 기록 테이블에 insert 한다. 그걸 사용자 집계로 썼다.)

- 사실 실시간이라기보다 간단한 방문자 집계에 가까운 기능이었다.

export const handleCountUsers = async (page: string) => {
  await supabase.from(로그 기록 테이블).insert({
    session_id: getSessionId(),
  });
};

 

2. 셔플

- 셔플에는 대표적으로 Fisher-Yates 셔플이 있다.

function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1)); // 0 ≤ j ≤ i
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

- 이 무작위성에 추가로 사람의 손을 탄 셔플을 구현했다.

- 추가로 애니메이션까지 :)

 


[어려웠던 점]

 

1. supabase 활용하기

- 잘 쓰려면 일단 sql문에 익숙해져야 했다. 근데 아주 다행이었던 건 supabase의 인터페이스가 쉽게 테이블을 만들 수 있게 되어 있었다. 구글링을 통해 사용법을 간단하게 익혀준 다음 Table을 만들고, RSL 설정까지 적용해 봤다. 진입장벽이 그리 높지 않았기에 다행이었다.

 

2. 해석에 대한 정리

-  사람들은 왜 타로를 볼까?라는 생각을 깊게 해보지 못했었다. 그냥 인간이라면 당연하게 자신의 운명, 행운 등을 보고 싶을 것이라는 막연한 생각에 결과에 대한 템플릿만 생각했었다. 이를 본 우리 집 대장님의 왈

타로는 고민에 대한 조언을 얻고 그 속에서 공감을 받고 싶어서 보는 거야.!

 

그 말을 듣고 정신이 번쩍 들었고, 내가 너무 가볍게 생각했다는 것을 깨달았다.

때문에 몇 가지 룰을 설정했다.

1. 진짜 사람이 섞는 듯한 셔플 로직을 만들기

2. 인공지능을 사용할 순 없지만, 다양한 해석을 종합해 썩 괜찮은 조언을 만들자

 

... 이후 내용은 영업 비밀!
하지만 정말 많은 고민과 시행착오가 있었다.

 

이번 프로젝트는 어떤 결과가 나올까?

많은 사람들이 즐겁게 봐줬으면 좋겠다 :)

728x90
반응형
728x90
반응형

요즘 J-pop의 매력에 빠져 살고 있다.

밴드 음악이나 인디스러운 음악을 좋아해서 어쩌다 듣게 되었는데,

지금까지 푹 빠져있다..ㅎㅎ

 

J-pop을 매일 듣다보니 자연스럽게 일본어에 관심이 갔고,

최근에는 지인들과 도쿄에도 다녀왔다.

여행을 하면서 간간히 일본어가 들리고, 이해하게 되니까

공부에 대한 욕심이 확 생겨버렸다.

 

함께 간 형들이 일본어 공부 어플같은 거 있으면

잘 쓸 것같다고 해서 

행동파인 나는 바로 작업에 착수

 

이런 앱이 탄생하게 되었다.

 

1. 취지

실용적인 공부를 원했다.

목표는 진지하게 일본어에 대해 공부를 하고, 원어민 급 혹은 자격증 공부를 하기 위함이 아니다.

여행을 가면 무슨 말하는 지 알아는 들을 수 있고, 가벼운 대답 정도는 할 줄 알게 만들자!

가 목표였기 때문에 깊게 들어가고 싶지 않았다.

때문에 사용자가 이 서비스를 이용하고 '일본어란 이런거구나.', '첫 공부로 좋네' 정도의 생각을 가졌으면 하는 바람이다.

 

2. 학습 방법

단순하게 반복학습을 목표로 했다.

히라가나를 외우고 싶으면 히라가나를 무한 반복, 단어를 외우고 싶으면 단어를 무한 반복 ...

그리고 발음은 로마지라는 영어를 많이 사용하던데.. 

잊지말자! 한국어는 모든 발음이 가능하다든든함이 있다^_^

 

3. 앞으로의 계획

일단 해당 앱을 계속적으로 업데이트 할 예정이다. 처음에는 포트폴리오로 써야하니까 '가볍게 하고 말자' 했는데..?

생각보다 주변 지인들은 잘 사용해주고 있다. 피드백도 간간히 해주고 있고 ...

 

"실용적인 회화 패턴" 까지 끝내면 내 나름의 시즌 1 개발 은 끝!

 

그리고 앞으로의 계획은...

사실 누가 얼마나 이용할 지는 잘 모르겠다. 그래도 이용해주는 사람들을 위해 질릴때까지 할 생각이다.

 

p.s 혹시 이걸 보고 들어오시는 분들!

절대로 이상한거 아니니 많이 이용해주시고, 피드백 주시면 너무나 감사하겠습니다!~~

 

- 해당 서비스가 불법적으로 사용될 경우 바로 감옥갑니다!

 

 

 

https://study-japanese.vercel.app/

 

Study-Japanese

 

study-japanese.vercel.app

많관부~

728x90
반응형
728x90
반응형

여전히 사이드 프로젝트를 진행하고 있다.

 

이게 뭐냐고! 

코드를 확인해봤다.

난 분명히 잘 설정 했는데?!


 

1. 문제 해결

- Fragment 사용

{teamList
          .sort((a, b) => a.rate - b.rate)
          .map((item) => {
            return (
              <React.Fragment key={item.name}>
                <div
                  className=" flex flex-col w-full overflow-hidden relative"
                  style={item.rate === 1 ? { height: 230 } : null}
                >
                ...
                </div>
              </React.Fragment>
            );
          })}

 

- Fragment 삭제

{teamList
          .sort((a, b) => a.rate - b.rate)
          .map((item) => {
            return (
                <div
                  className=" flex flex-col w-full overflow-hidden relative"
                  style={item.rate === 1 ? { height: 230 } : null}
                  key={item.name}
                >
                ...
                </div>
            );
          })}

 

 

2. 원인

리스트 렌더링할 때, key를 설정해야한다는 사실을 알고 있었는데,

Fragment 를 사용했다고 없는 애 취급을 했ㄷr,... (미안해..)

Fragment는 실제 DOM에 렌더링되지 않는 단순한 래퍼 역할을 한다.
하지만, React 내부적으로는 Fragment도 컴포넌트 트리에서 노드로 취급된다.
따라서, 리스트 렌더링에서 Fragment도 Key를 가져야 한다!
728x90
반응형

+ Recent posts