2023. 6. 1.`aria-current`
현재 페이지/단계/위치/시간/등등을 나타낼 수 있는 어트리뷰트이고 대략 이런 느낌이다.
<ul>
<!-- ... -->
<li aria-current="page">
<a href="">link</a>
</li>
<!-- ... -->
</ul>
<ul>
<!-- ... -->
<li aria-current="page">
<a href="">link</a>
</li>
<!-- ... -->
</ul>
다양한 걸 다룰 수 있음:
page
: Represents the current page within a set of pages such as the link to the current document in a breadcrumb.step
: Represents the current step within a process such as the current step in an enumerated multi step checkout flow .location
: Represents the current location within an environment or context such as the image that is visually highlighted as the current component of a flow chart.date
: Represents the current date within a collection of dates such as the current date within a calendar.time
: Represents the current time within a set of times such as the current time within a timetable.true
: Represents the current item within a set.false
(default): Does not represent the current item within a set.
2023. 5. 29.MDX에 무심코 <3을 쓰면 터진다
<3
은 emoji가 없던 시절 하트를 표시하던 방법이다. 좀 전에 graphite cheatsheet til을 쓰면서 무심코 하트를 <3
으로 했었는데 로컬 빌드가 터져서 음? 하고 보니 당연한 얘기였다.
MDX에게 <
란 jsx
의 시작이기 때문에...
추가:
<>
도 터짐<-
도 터짐
2023. 5. 29.Graphite cheatsheet
https://graphite.dev/docs/graphite-cheatsheet
어제부터 본격적으루다가 써보는 중. cli 베이스인 것도 맘에 들고 플로우도 맘에 들고 이래저래 편하고 좋당🖤
커밋하기
gt bc -am '<message>'
gt bc -am '<message>'
브랜치를 만들지 않고 변경+커밋하면서 브랜치까지 만드는 플로우. 브랜치 이름도 알아서 만들어줌.
생성되는 브랜치명의 기본 템플릿은 아래와 같음.
mm-dd-commit_message
mm-dd-commit_message
변경 추가
gt add -A && gt ca
gt add -A && gt ca
기본적으로 git commit --amend
와 같음. 그냥 커밋 추가도 가능하긴 한데(gt cc
) 브랜치당 하나의 커밋이 스타일이므로 (변경의 영역/성격 등이 다른데 관련이 있다면 일단 PR을 보내고 거기다 다시 PR을 보내라는 스타일) 이렇게 하라고 되어있음.
PR 보내기
gt ss
gt ss
Merge
graphite 웹앱에서 할 수도 있고 걍 깃헙에서 해도 되고.
마무리
gt rs -r
gt rs -r
로 싱크를 맞춤. main
은 pull
해주고 머지된 작업 브랜치를 자동으로 지워줌. 꼬리 물기 PR 싱크도 요 단계에서 처리.
2023. 5. 28.TailwindCSS: You can not pass arbitrary values as variables
const A = `after:content-['░']`
const B = `after:content-['${variable}']`
const A = `after:content-['░']`
const B = `after:content-['${variable}']`
A
works, but B
doesn't.
2023. 5. 26.ms clarity
ms의 analytics 툴 clarity의 설치 스크립트는 이렇게 생겼다.
<script type="text/javascript">
(function(c,l,a,r,i,t,y){
// ^^^^^^^^^^^^^ 귀엽...
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
// ...
</script>
2023. 5. 25.Extracting Image Dimensions from Remote Sources
Before
- til을 위해 repo issues를 cms로 쓰고 있었고,
- issue body render를 위해 github api reponse 중
bodyHTML
을dangerously...
에 넣어주고 있었음. - 근데 이러면 next가 해주는 이것저것이 아까우므로,
- api response 중
body
(markdown string)을 mdx renderer에 넘겨서 써야겠다고 생각.
Issues
- next-mdx-remote를 쓰면 되고 딱히 별 문제는 없음(rsc도 지원).
- 한가지, 이미지 최적화 기능을 위해선
next/image
를 써야하는데, - static import일 경우엔 알아서 해주지만,
- remote url일 경우엔
width
/height
정보를 넘겨줘야함. - 근데 이걸 어떠케 알어요...
How?
- probe-image-size라는 라이브러리가 있고,
- mdx renderer option의
component
설정시<img />
-><Image />
로 replace 하면서 요걸 사용해서width
/height
정보를 넘겨주면 됨. - 코드는 대충 이런식:
import probe from "probe-image-size" // ... const components: MDXRemoteProps["components"] = { // ... // @ts-expect-error <- ts는 아직 async 컴포넌트를 모르지만 우리는 rsc 세계로 넘어왔으므로 ok. img: async ({ src, alt }) => { if (!src) return null const { width, height } = await probe(src ?? "") // ^^^^^ rsc ftw... if (!width || !height) return null return <Image src={src} alt={alt ?? ""} width={width} height={height} /> }, // ... } // ...
import probe from "probe-image-size" // ... const components: MDXRemoteProps["components"] = { // ... // @ts-expect-error <- ts는 아직 async 컴포넌트를 모르지만 우리는 rsc 세계로 넘어왔으므로 ok. img: async ({ src, alt }) => { if (!src) return null const { width, height } = await probe(src ?? "") // ^^^^^ rsc ftw... if (!width || !height) return null return <Image src={src} alt={alt ?? ""} width={width} height={height} /> }, // ... } // ...
Result
최종 렌더된 이미지의 url을 보면
next가 잘 처리하고 있음을 알 수 있음.
Conclusion
RSC는 대박이다...
2023. 5. 24.`generateStaticParams`를 사용하는 페이지에서 서버 액션을 호출하면 `405` 에러 (2023-05-24 현재)
좋아요 버튼을 달려고 @vercel/kv 랑 요렇게 저렇게 해보고 있었는데 아래 에러가 무한히 발생했다.
Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.
at AppContainer (/Users/sehyunchung/personal/sehyunchung.dev/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/next/dist/server/render.js:337:29)
at AppContainerWithIsomorphicFiberStructure (/Users/sehyunchung/personal/sehyunchung.dev/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/next/dist/server/render.js:373:57)
at div
at Body (/Users/sehyunchung/personal/sehyunchung.dev/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/next/dist/server/render.js:673:21)
Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.
..
검색을 해봐도 뭐가 안나와서 'use server'
를 파일에 넣었다 함수에 넣었다 이케저케 해봐도 안됐는데 어쩌다 브라우저 콘솔을 열어보니
네트워크 탭을 확인해보니
요걸로 검색해보니 아래 이슈가 나왔다.
[NEXT-1167] Server Actions 405 "Method Not Allowed" when using generateStaticParams #49408
생각해보면 말이 되는 것 같기도... 근데 안되면 안되는데?
2023. 5. 24.`@vercel/og` Cheatsheet (?)
- @vercel/og
- Edge Runtime 환경 기반으로 동작함.
- 이미지 렌더에 satori를 사용하는데,
- react element를
svg
로 변환하는 라이브러리이고, - React Native의 Flexbox layout engine을 사용하므로, css의 모든 기능을 사용할 순 없다.
- e.g.
z-index
,calc()
등 사용 불가능.
- e.g.
- react element를
- app router 사용시
app/og/route.tsx
혹은app/og.tsx
등등으로 파일을 만들면 됨..ts
도 사용할 수 있으나 고러면jsx
를 사용할 수 없겟쥬
- 커스텀 폰트를 사용할 수 있으나
next/font
외에 별도로 로컬에서 폰트 파일을 가져와야 함. (리모트는 아직 안해봄).ttf
,.woff
사용 가능 (.woff2
는 안됨)
- 대략의 api는 아래와 같음.
new ImageResponse( element: ReactElement, options: { width?: number = 1200 height?: number = 630 emoji?: 'twemoji' | 'blobmoji' | 'noto' | 'openmoji' = 'twemoji', // emoji render에 어떤 lib을 사용할 것인지 fonts?: { name: string, data: ArrayBuffer, // 폰트 파일 데이터. fetch(URL).then(res => res.imageBuffer())로 가져오면 된다. weight: number, style: 'normal' | 'italic' }[] debug?: boolean = false // true 일 경우 각 element의 border, line-height 등이 표시됨. status?: number = 200 statusText?: string headers?: Record<string, string> }, )
new ImageResponse( element: ReactElement, options: { width?: number = 1200 height?: number = 630 emoji?: 'twemoji' | 'blobmoji' | 'noto' | 'openmoji' = 'twemoji', // emoji render에 어떤 lib을 사용할 것인지 fonts?: { name: string, data: ArrayBuffer, // 폰트 파일 데이터. fetch(URL).then(res => res.imageBuffer())로 가져오면 된다. weight: number, style: 'normal' | 'italic' }[] debug?: boolean = false // true 일 경우 각 element의 border, line-height 등이 표시됨. status?: number = 200 statusText?: string headers?: Record<string, string> }, )
- 대략의 사용례는 아래와 같음.
// app/og/route.tsx import { ImageResponse } from 'next/server'; // app router 사용시 @verce/og가 포함되어 있음 export const runtime = 'edge'; const font = fetch(new URL('../path/to/font/Font.woff', import.meta.url)).then( (res) => res.arrayBuffer(), ); export async function GET(request: Request) { const fontData = await font; // query param으로 이런 저런 텍스트를 동적으로 넣을 수 있음. const url = new URL(request.url) const searchParams = url.searchParams const title = searchParams.has("title") ? searchParams.get("title") : null return new ImageResponse( ( <div style={{ backgroundColor: 'white', height: '100%', width: '100%', fontSize: 100, fontFamily: '"Font"', paddingTop: '100px', paddingLeft: '50px', }} > {title ? title : 'Hello World!'} </div> ), { width: 1200, height: 630, fonts: [ { name: 'Font', data: fontData, style: 'normal', }, ], }, ); }
// app/og/route.tsx import { ImageResponse } from 'next/server'; // app router 사용시 @verce/og가 포함되어 있음 export const runtime = 'edge'; const font = fetch(new URL('../path/to/font/Font.woff', import.meta.url)).then( (res) => res.arrayBuffer(), ); export async function GET(request: Request) { const fontData = await font; // query param으로 이런 저런 텍스트를 동적으로 넣을 수 있음. const url = new URL(request.url) const searchParams = url.searchParams const title = searchParams.has("title") ? searchParams.get("title") : null return new ImageResponse( ( <div style={{ backgroundColor: 'white', height: '100%', width: '100%', fontSize: 100, fontFamily: '"Font"', paddingTop: '100px', paddingLeft: '50px', }} > {title ? title : 'Hello World!'} </div> ), { width: 1200, height: 630, fonts: [ { name: 'Font', data: fontData, style: 'normal', }, ], }, ); }
- tailwind 사용이 가능한데 아직
experimental
이 붙어있고className
말고tw
를 사용하도록 되어있음. - Hobby plan일 경우 단일 function당 1MB 제한이 있어 한글 커스텀 폰트를 추가하긴 쉽지 않았음.
- sehyunchung.dev에 적용해본 결과 -> https://sehyunchung.dev/og?title=암온더넧렙을&description=절대적룰을지켜
2023. 5. 23.Urge Surfing
"...general principle is that if we look at the Mind urges or Cravings arise but if we're able to tolerate that for some amount of time if were able to surf on top of the urge instead of give in to it over time that urge will actually go away."
The Real Reason You Never Follow Through (And How to Fix It)
2023. 5. 22.Vercel Edge Function size limit
@vercel/og
로 og image generation을 하는 김에 폰트도 맞춰보려고 일케절케 하다보니
Error: The Edge Function "api/og" size is X MB and your plan size limit is 1 MB. Learn More: https://vercel.link/edge-function-size
라면서 빌드가 터짐.
왜냐?
- og image generation에 커스텀 폰트를 사용하려면
.ttf
혹은.woff
(.woff2
는 안됨)를fetch
로 불러와서arrayBuffer
로 만들어야 함 - 영어 폰트의 자수는 72자
- 근데 한글 폰트는 서브셋 처리를 해도 2,350자
- 그래서 한글 폰트를 일단 불러오면 1MB 안엔 안들어감
- 돈을 더 내거나 더 줄인 서브셋을 만들어야 함
- 돈을 더 낼 순 없다 왜냐면 암것도 모르는 영어권 놈들(...)이 책정한 가격이니까
- 자수를 더 줄인 서브셋 만들기는 아직 방법도 모르므로 이후로 미룸
- 일단 한글 커스텀 폰트를 뺐더니
- 빌드에 성공함
- 영어!!!! ㅠㅠㅠ