notes

2023. 6. 30.
CSS container queries

tl;dr

container queries illustrated

How to

  1. container query로 "볼" element에 container-type으로 "containment context"를 지정한다.
  2. container-type은 아래와 같은 옵션들이 있다.
    • size: 'containment context로 지정된 element의 "inline", "block" layout 정보를 다 쓰겠다.'
    • inline-size: '"inline" layout 정보만 쓰겠다.'
    • normal: 'layout 정보는 안쓰겠다.'
  3. container-name으로 naming도 할 수 있다. (grid-area랑 비슷한 머시기라고 생각하자.)
  4. 위 둘을 줄여서 container: {name} / {type} 로 쓸 수 있다.
  5. @container {name} ({condition}) { ... }로 스타일을 정의한다.
  6. container queries용 unit도 따로 있다.
    • cqw: containment context element width의 1%
    • cqh: height의 1%
    • cqi: inline size의 1%
    • cqb: block size의 1%
    • cqmin: cqi와 cqb 중 작은 값
    • cqmax: 큰 값

"width랑 inline size랑 같은 거 아니에요?"

writing mode가 vertical일 경우를 생각해보시면...

Refs

비고

2023. 6. 26.
Callback Ref

callback refs?

ref는 보통 useRef hook과 함께 쓰지만 ref 에 함수를 넘길 수도 있는데, 요런 패턴을 callback refs 라고 하고, 대부분 DOM node를 액세스 하는 경우에 쓰인다.

How?

놀랍게도 ref엔 함수를 넘길 수 있는데, 요 경우 아래와 같이 node를 arg로 받게 된다.

const ref = (node) => {
  // access the dom node
}
const ref = (node) => {
  // access the dom node
}

사실 useRef가 넘기는 ref 도 걍 아래와 같다.

<div
  ref={(node) => {
    ref.current = node;
  }}
/>
<div
  ref={(node) => {
    ref.current = node;
  }}
/>

But why?

기존의 ref 는 보통 effect 내부에서 마운트가 된 이후에 액세스하게 된다. 근데 해당 dom node에 조건부 렌더가 걸려있다면 동작하지 않는 케이스가 생길 수 있고, node가 다이나믹하게 변경될 수 있는 케이스에 대응이 어렵다거나 등등.

When?

아래와 같은 걸 할 수 있다.

const ref = (node) => {
  node?.focus()
}

// ...

return <input ref={ref} type="text" />
const ref = (node) => {
  node?.focus()
}

// ...

return <input ref={ref} type="text" />

useCallback

근데 위와 같이 하면 매 렌더시마다 포커스가 될 것이므로, 거의 대부분의 경우 useCallback이랑 쓰게 될 것이다.

const ref = useCallback(() => {
  node?.focus()
}, [])
const ref = useCallback(() => {
  node?.focus()
}, [])

Further Readings

2023. 6. 25.
next.js에서 route가 변경되었을 경우 이전 route를 죽지 않게 하려면

til이 딱히 til이 아닌 것 같고 github을 cms로 쓰는 것도 좀 번거로운 감이 있어서 걍 .mdx 파일을 작성하는 걸로 하고 route 이름도 notes로 바꾸었는데, 바꾸고 보니 이전 링크를 preserve 해야 되겠다 싶어서 알아보았다. 브로큰 링크를 만들지 않아보겠다! 가 얼마 전에 목표가 되었기 때문에.

하려는 것

  1. (아마도 없을 것 같긴 하지만) 이전 링크를 어딘가 저장해 둔 누군가가 있을 수도 있다.
  2. 그 저장된 링크는 /til/:id 일 것
  3. 바뀐 링크는 /notes/:id 이지만,
  4. 2.의 링크로도 액세스가 가능해야 함

그러니까 redirects 설정을 하면 되는 것인데,

next.config.js

아래와 같이 하면 된다.

/** @type {import('next').NextConfig} */
const nextConfig = {
	// ...
	async redirects() {
		return [
			// ...
			{
				source: "/til/:path*",
				destination: "/notes/:path*",
				permanent: false,
			},
		]
	},
}
/** @type {import('next').NextConfig} */
const nextConfig = {
	// ...
	async redirects() {
		return [
			// ...
			{
				source: "/til/:path*",
				destination: "/notes/:path*",
				permanent: false,
			},
		]
	},
}

대략 "/til/*" 하면 되지 않을까 했는데 역시 아니었고 "foo/:path*" 라구 해야 함. permanent: false인 것에도 유의.

Ref

2023. 6. 24.
app router에서 `fetch`를 쓰지 않을 경우 cache 동작

tl;dr

되긴 됨

Detail

fetch를 쓰지 않을 경우 Default Caching Behavior를 따르거나 Segment Cache Configuration를 따르게 됨.

Default Caching Behavior

If the segment is static (default), the output of the request will be cached and revalidated (if configured) alongside the rest of the segment. If the segment is dynamic, the output of the request will not be cached and will be re-fetched on every request when the segment is rendered.

Segment Cache Config

As a temporary solution, until the caching behavior of third-party queries can be configured, you can use segment configuration to customize the cache behavior of the entire segment.

like:

// app/page.tsx

import prisma from './lib/prisma'
 
export const revalidate = 3600 // revalidate every hour
 
async function getPosts() {
  const posts = await prisma.post.findMany()
  return posts
}
 
export default async function Page() {
  const posts = await getPosts()
  // ...
}
// app/page.tsx

import prisma from './lib/prisma'
 
export const revalidate = 3600 // revalidate every hour
 
async function getPosts() {
  const posts = await prisma.post.findMany()
  return posts
}
 
export default async function Page() {
  const posts = await getPosts()
  // ...
}

Ref

Data Fetching without fetch()

2023. 6. 20.
tailwind 👍

panda-css를 시험삼아 함 써보던 중에 쓰면서는 인식하지 못하고 있던 tailwind의 장점을 알게 됐다.

tailwind로 작성되어 있던 코드를 바꿔봤는데: FzEqR1KaEAAxoFf 오른쪽의 형태가 되는 순간 어 이건 좀... 싶어지면서 아래의 두가지를 비로소 인식하게 되었다.

  1. styled-components/emotion 처럼 css syntax를 쓰던지 stitches 처럼 js object syntax를 쓰던지 상관없이 스타일링 관련 코드는 무조건 세로 방향/2차원으로 코드 면적이 늘어나게 된다는 점.
  2. key: value의 형태 때문에 반복하게 되는 타이핑량 + 시각적 스트레스가 있다는 점.

그러나 tailwind는:

  1. 코드가 가로/1차원으로만 늘어나고,
  2. value만 줄줄이 넣는 식.

이라 1,2로 알게 모르게 느끼고 있던 스트레스가 해결되고 있었다는 걸 알게 되었고 결론은 tailwind가 선택지에 있으면 나는 tailwind를 쓸 것 같다는 뭐 그런 얘기.


추가:

  1. 이게 코드량/면적의 문제도 있지만 스타일링 관련 코드가 다른 코드랑 '생긴게' 아예 달라진다는 점.
  2. 스타일링 관련 코드가 className="..."에 '격리' 된다는 점.

을 또 들 수 있을 것 같다.

위에 나열한 요소들이 중요하게 느껴지는 건 아무래도 시각적으로 인지 부하가 줄어들기 때문인 것 같고.

2023. 6. 18.
tailwind에서 pseudo 머시기 쓰기

csstailwind(modifier)
:first-childfirst:
:first-of-typefirst-of-type:
:only-childonly:
:nth-child(odd)odd:
:afterafter:

group, peer

추가로 지원하고 있는 기능이 있는데,

group

parent의 상태를 child가 쓸 수 있도록 함.

예)

<a href="#" class="group">
              <!-- ^^^^^ parent에서 `group`을 선언하고 나서 -->
  <div class="flex items-center space-x-3">
    <svg class="group-hover:stroke-white" fill="none" viewBox="0 0 24 24"><!-- ... --></svg>
          <!-- ^^^^^^^^^^^^^^^^^^^^^^^^ parent의 상태를 child 스타일링에 쓸 수 잇음 -->
  </div>
</a>
<a href="#" class="group">
              <!-- ^^^^^ parent에서 `group`을 선언하고 나서 -->
  <div class="flex items-center space-x-3">
    <svg class="group-hover:stroke-white" fill="none" viewBox="0 0 24 24"><!-- ... --></svg>
          <!-- ^^^^^^^^^^^^^^^^^^^^^^^^ parent의 상태를 child 스타일링에 쓸 수 잇음 -->
  </div>
</a>

peer

자신의 상태를 sibling이 쓸 수 있도록 함.

<form>
  <label class="block">
    <input type="email" class="peer ..."/>
                          <!-- ^^^^ 여기서 선언하면 -->
    <p class="mt-2 invisible peer-invalid:visible text-pink-600 text-sm">
                        <!-- ^^^^^^^^^^^^^^^^^^^^ 이렇게 sibling이 쓸 수 있음 -->
      Please provide a valid email address.
    </p>
  </label>
</form>
<form>
  <label class="block">
    <input type="email" class="peer ..."/>
                          <!-- ^^^^ 여기서 선언하면 -->
    <p class="mt-2 invisible peer-invalid:visible text-pink-600 text-sm">
                        <!-- ^^^^^^^^^^^^^^^^^^^^ 이렇게 sibling이 쓸 수 있음 -->
      Please provide a valid email address.
    </p>
  </label>
</form>

주의) peer-{modifier}peerprev 가 아니라 next sibling이 쓸 수 있음

Ref

Quick reference

2023. 6. 16.
Motivation

  • 신경과학적으로 'Liking'에 연관된 기관과 'Wanting'에 연관된 기관은 따로라고 한다.
  • 어떤 행위를 했을 때 좋다고 느낀다고 그걸 하고 싶어지는 것은 아닌 것이 그 때문.
  • 예) 어쩌다 간만에 운동을 하고 '아 역시 좋군' 한다고 다음날 아침에 '와! 어서 피트니스 센터에 가고 싶군!' 이 되지 않음
  • 'Wanting' 관련 기관은 21세기의 도파민 폭탄들 때문에 맛이 가있을 확률이 높고, 그래서 현대인은 대부분 Liking/Wanting 싱크가 더 안맞고 있을 확률이 ⬆️
  • 그리고 뇌는 어떤 행위를 하기 전에 그것에 드는 인풋과 돌아올 보상의 수지 타산을 늘 계산하는데, 우선 순위가 저인풋고보상>저인풋저보상>고인풋고보상>고인풋저보상 이라고.
  • 대부분의 의미 있는 일은 고인풋 고보상이라 늘 우선 순위에서 밀리게 마련이고 그래서 의욕이 생기지 않는 것이라고.
  • 그러나 '왜 나는 의욕이 없지?', '보조제를 먹어야 되나?', '스터디 모임을 만들어야 되나?' 등과 같이 '집나간 의욕을 어떻게 찾아올까'는 실패할 확률이 더 높다.
  • 그보다 성공률이 높은 건 의욕 유무를 실행 조건에서 빼는 것.
  • '해야 되는데 하고 싶지 않으니 의욕을 만들어서 한다'가 아니라 '해야 되니까 그냥 한다'가 훨씬 효과적이라고.

Refs

2023. 6. 15.
JavaScript 1.2 Reference

  1. 나는 분명히 rsc 글을 쓰던 중이었는데
  2. 어쩌다보니 초창기 js 코드는 어떻게 생겼었을까 궁금해져서
  3. 검색도 해보고 gpt한테 물어도 보고 했는데 적절한 결과를 얻을 수 없었는데
  4. 그러다 아 초기 문서를 찾아보면 되겠구나 하는 생각이 들어 찾아보니
  5. JavaScript 1.2 Reference 가 있었다

image

"10/31/97" ☜ 간지 난다...

wayback machine에 아카이빙된 오리지널 링크도 있었다.

2023. 6. 15.
`for (; amount--; )`

react-scrollbars-custom예제를 보다가 충격적인 걸 봐갖고

export function renderAmountOfParagraphs(amount = 5, paragraphsProps) {
  const result = [];

  for (; amount--; ) { // ??
    result.push(
      <p {...paragraphsProps} className="ExampleCard-Paragraph" key={`paragraph_${amount}`}>
        {getRandomParagraphText()}
      </p>
    );
  }

  return result;
}
export function renderAmountOfParagraphs(amount = 5, paragraphsProps) {
  const result = [];

  for (; amount--; ) { // ??
    result.push(
      <p {...paragraphsProps} className="ExampleCard-Paragraph" key={`paragraph_${amount}`}>
        {getRandomParagraphText()}
      </p>
    );
  }

  return result;
}

?

  for (; amount--; ) {
  for (; amount--; ) {

?? 🤯

mdn의 for 관련 문서를 보니

All three expressions in the head of the for loop are optional. For example, it is not required to use the initialization block to initialize variables

🤯🤯

이 외에도 충격적인 게 더 있었는데:

Using for without a body

🤯🤯🤯

Using for with two iterating variables

🤯🤯🤯🤯