January 13, 2024 • ☕️☕️☕️ 13 min read
fail()
함수를 호출하면 된다!
test('챗봇 정상 응답 시 도움이 되었는지 물어보는 응답을 반환한다,', async () => {
customRender(<ChatContent onShowNewMessage={jest.fn()} onFocusChatInput={jest.fn()} />);
userEvent.click(await screen.findByText('정산/부가세'));
const categoryFaqList = undefined;
if (!categoryFaqList) fail();});
Response
객체의 필드bodyUsed
- 응답에서 body가 사용되었는지 여부. response.json()
(또는 .text()
, .blob()
) 를 호출하면 비로소 값이 true로 변경된다.redirected
- 응답이 redirect의 응답인지 여부type
- 응답의 타입(ex. basic
, cors
)Ref https://developer.mozilla.org/en-US/docs/Web/API/Response
Fetch API의
.json()
함수가 Promise를 반환하는 이유
- 메인쓰레드를 블락하지 않기 위해서
qs
자바스크립트로 URL의 query string을 파싱하기 위해 사용한다.
문자열 형태의 쿼리 스트링을 객체의 형태로 변환할 때, qs의 parse()
함수를 사용한다.
const obj = qs.parse("mode=dark&active=true&nums=1&nums=2&nums=3");
console.log(obj);
// {
// mode: "dark",
// active: "true",
// nums: ["1", "2", "3"],
// }
반대로 자바스크립트 객체 형태의 쿼리 스트링을 문자열로 변환하고 싶을 때는 stringify()
함수를 사용한다.
const str = qs.stringify({
mode: "dark",
active: "true",
nums: ["1", "2", "3"],
});
console.log(str); // "mode=dark&active=true&nums%5B0%5D=1&nums%5B1%5D=2&nums%5B2%5D=3"
👩🏫
qs
라이브러리는 기본적으로 배열 값을파라미터명[인덱스]=값
의 형태로 변환한다.
웹 표준 방식으로 동일한 파라미터명이 반복되게 하고 싶다면, 두 번째 인자로 arrayFormat 옵션의 값을 “repeat”로 주면 된다.
const str = qs.stringify(
{
mode: "dark",
active: "true",
nums: ["1", "2", "3"],
},
{ arrayFormat: "repeat" }
);
// "mode=dark&active=true&nums=1&nums=2&nums=3"
qs의 `stringify()“ 함수는 복잡한 형태의 객체를 다룰 수 있다.
URLSearchParams
웹 표준 API에서 제공하는 URLSearchParams
를 사용하여 좀 더 안전하게 쿼리 스트링을 다룰 수 있게 되었다.
URLSearchParams
객체의 생성자는 여러 형태의 값을 인자로 받을 수 있다.
new URLSearchParams([
["mode", "dark"],
["page", 1],
["draft", false],
["sort", "email"],
["sort", "date"],
]);
쿼리 스트링을 문자열의 형태로 넘길 수도 있다.
new URLSearchParams("?mode=dark&page=1&draft=false&sort=email&sort=date");
size
속성으로 쿼리 스트링에 얼마나 많은 매개변수가 들어있는지 알 수 있다.
const searchParams = new URLSearchParams("mode=dark&page=1&draft=false");
searchParams.size; // 3
쿼리 객체들을 문자열로 변환할 때는 toString()
을 사용한다.
const searchParams = new URLSearchParams([
["mode", "dark"],
["page", 1],
["draft", false],
]);
searchParams.toString(); // 'mode=dark&page=1&draft=false'
append()
메서드를 이용하여 쿼리 스트링의 파라미터를 하나씩 추가할 수 있다.
const searchParams = new URLSearchParams();
searchParams.append("mode", "dark");
searchParams.append("page", 1);
searchParams.toString(); // 'mode=dark&page=1'
객체에 저장되어 있는 값을 읽을 때는 get()
과 getAll()
메서드를 사용한다.
searchParams.get("mode"); // 'dark
searchParams.getAll("mode"); // [ 'dark' ]
searchParams.getAll("page"); // [ '1' ]
URLSearchParams
객체에 저장되어 있는 파라미터는 for...of
문법을 사용하여 쉽게 순회할 수 있습니다.
for (const [key, value] of searchParams) {
console.log(`${key}: ${value}`);
}
URL 객체의 search 속성에는 쿼리 스트링이 문자열로 저장되어 있고, searchParams 속성에는 쿼리 스트링이 URLSearchParams 객체로 저장되어 있다.
const url = new URL("https://example.org:8080/foo/bar?q=baz#bang");
url.search; // '?q=baz'
const searchParams = url.searchParams; // URLSearchParams {size: 1}
searchParams.get("q"); // 'baz'
searchParams.set("q", "updated");
searchParams.append("r", 2);
searchParams.append("r", false);
url.toString(); // 'https://example.org:8080/foo/bar?q=updated&r=2&r=false#bang'
함께 사용하면 유용하다! 😉
Ref
const ConditionalWrapper = ({ condition, wrapper, children }) =>
condition ? wrapper(children) : children;
const ServiceCard = ({title, description, image, url}) => {
return (
<section>
<ConditionalWrapper
condition={url}
wrapper={children => <a href={url}>{children}</a>}
>
<>
<h2>{title}</h2>
<p>{description}</p>
<img src={image} alt={title} />
</>
</ConditionalWrapper>
</section>
)
})
Ref https://dev.to/dailydevtips1/conditional-wrapping-in-react-46o5
pnpm changeset pre enter {tag}
pnpm changeset pre exit
쉼표로 구분된 목록에서 값의 우선순위를 표현하는 방법 (일부 HTTP 헤더 및 HTML에서 사용)
활용 예시
Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5
부연설명
';q='
바로 뒤에 0에서 1사이의 값Safe Area란, (모바일 기기에서) 화면 구성요소를 표현하기에 안전한 영역을 의미한다. ex. 상태바, 노치영역, (아이폰의) 하단 내비게이션 바 영역 등등
안드로이드
특별히 설정을 하지 않으면 대부분의 화면이 상태바 하단으로 잡히기 때문에 크게 신경쓸 일이 없다. 내비게이션 바는 애초에 화면 표시영역이 아니다.
아이폰
Safe Area 영역을 파악하는 파악하는 법 (웹에서)
env(safe-area-inset-*)
으로 수치를 알 수 있다.
아이폰 safe area inset의 대략적인 수치
상단
하단
keydown
이벤트는 마우스클릭 뿐 아니라 tab할 때에도 트리거되기 때문에 조심해야 한다!React Fiber는 React 코어 알고리즘의 진행중인 작업이다. React Fiber의 목표는 애니메이션, 레이아웃, 제스처 등에서 사용성을 높이는 것이다. 그 주요 기능은 점진적 렌더링인데, 이는 렌더링 작업을 청크로 나누고 여러 프레임에 걸쳐 실행하는 것이다. 다른 주요 기능은 새로운 업데이트가 발생했을 때 작업을 일시중지, 중단 또는 재사용할 수 있게 하는 것이며, 서로 다른 타입의 업데이트 항목들에 대해 우선순위를 부여하고 새로운 동시성 모드를 지원하는 것이다.
더 이상 ‘virtual DOM’이라는 표현은 쓰지 않는다. 근본적인 컨셉은 비슷하지만, 이를 구현하는 React의 기술 코어가 지난 3년 사이에 조금 바뀐 것 같다!
Fiber는 컴포넌트의 입/출력 정보들을 가지고 있는 자바스크립트 객체다. 하나의 fiber는 스택 프레임에 해당하며, 동시에 Component의 인스턴스에 해당되기도 한다.
Fiber에 해당하는 중요한 항목들
type
과 key
- Fiber의 type과 key는 React 요소에 있어 그것과 같은 목적을 갖는다. (실제로 React 요소로부터 fiber가 생성될 때, 이 두 필드는 복사되어 전달된다). Fiber의 type은 그것이 상응하는 컴포넌트를 가리킨다. type과 함께, key는 fiber가 재사용될 수 있는지를 판별하기 위해 재조정 중에 사용된다.child
와 sibling
- 이 필드들은 다른 fiber를 대상으로 하며, fiber의 재귀적 트리 구조를 가리킨다. 자식 fiber(a child fiber)는 컴포넌트의 render 함수가 반환하는 값에 해당한다. Sibling field는 render 함수가 여러개의 자식을 반환할 때 설명된다return
- return fiber는 현재 항목을 수행한 후 프로그램이 반환해야 하는 정보를 담고 있는 fiber다.pendingProps
와 memoizedProps
- Fiber의 pendingProps는 함수의 실행 처음에 설정되고, memoizedProps는 실행 마지막에 설정된다. 들어오는 pendingProps가 memoizedProps와 같으면, 이걸 통해 fiber에게 이전 출력물이 재사용될 수 있음을 알리고, 불필요한 작업을 방지할 수 있다.pendingWorkPriority
- Fiber 작업의 우선 순위를 나타내는 숫자. ReactPriorityLevel (관련 설명 현재 삭제됨) 모듈은 다른 우선 순위 수준과 각각의 의미를 나열한다.alternate
output
Ref
react-router와 크게 다르지 않은 인터페이스를 제공하면서, 타입스크립트를 기본 지원하여 라우팅에도 타입 안정성을 제공한다.
RootRoute
- root route를 생성한다.Outlet
- 잠재적으로 매칭될 자식 route들을 렌더하기 위해 사용된다. (현재 URL과 매칭될 경우)Route
- 자식 route들은 getParentRoute
메서드로 부모로부터 타입 정보와 런타임 정보를 받는다. 이를 통해 route 정의부의 타입 안정성을 보장하고, 순환 참조나 인스턴스화되지 않은 변수 등의 문제를 예방할 수 있다.
let rootRoute = new RootRoute()
const indexRoute = new Route({ getParentRoute: () => rootRoute, path: '/' })
const blogRoute = new Route({ getParentRoute: () => rootRoute, path: 'blog' })
const postRoute = new Route({ getParentRoute: () => blogRoute, path: '$slug' })
rootRoute.addChildren([...])
과 route.addChildren([...])
으로 route tree를 생성할 수 있다.declare module '@tanstack/react-router' {
interface Register {
// This infers the type of our router and registers it across your entire project
router: typeof router
}
}
완성된 모습!
import { RootRoute, Route, Router } from '@tanstack/react-router'
let rootRoute = new RootRoute()
const indexRoute = new Route({ getParentRoute: () => rootRoute, path: '/' })
const blogRoute = new Route({ getParentRoute: () => rootRoute, path: 'blog' })
const postRoute = new Route({ getParentRoute: () => blogRoute, path: '$slug' })
const routeTree = rootRoute.addChildren([
indexRoute,
blogRoute.addChildren([postRoute]),
])
const router = new Router({ routeTree })
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}
Ref https://tanstack.com/router/v1/docs/overview
Ref https://www.frontoverflow.com/magazine/3/2023%20JavaScript%20Rising%20Stars
타 플랫폼에도 압력이 될 수도 있다고(?)
중국 한 기업에서, 100mW 출력의 수명 50년짜리 원자력 전지를 동전보다 작은 크기로 소형화 및 모듈화 하는 데에 성공했다고 주장 및 특허 출원 신청 중. (주장이 사실이라면 기존의 어지간한 모바일 기기는 거뜬하고, 현대 인공위성이나 가전 등에도 무리없이 사용 가능)
이르면 25년 제품화가 가능할 것이라고 주장.
세기의 도박이라던 전지 혁명은 어떻게 될 것인가…
Ref https://techrecipe.co.kr/posts/61854
12월 모든 약속이 1월로 밀려 완전 정신없는 한 주였다 🫠
주말엔 찜질방 가서 힐링도 하고, 입주 두 달 차 대청소도 하고
알차게 보내고 있는 1월이다