February 4, 2023 • ☕️☕️ 8 min read
코드리뷰는 재미써
job과 job 간에 파일을 공유하려면 job artifacts - dependencies에 가져올 다른 job artifact를 넣어주어야 한다.
gitlab ci의 파이프라인은 두 가지 종류가 있다
$CI_PIPELINE_SOURCE == 'merge_request_event'
를 명시해주어야 한다.Ref
테스트의 준비-실행-검증 구절에서 보통 실행 구절은 한 줄이어야 한다. 실행 구절이 여러 줄이면, 단일 작업을 수행하는 데 여러 개의 메서드 호출이 필요하다는 것이다. 이는 API에 문제가 있는 것이며, 클라이언트에서 모순이 발생할 수 있다.
jest에서는 describe.each()
메서드로 테스트를 매개변수화할 수 있다
describe.each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
])(".add(%i, %i)", (a, b, expected) => {
test(`returns ${expected}`, () => {
expect(a + b).toBe(expected);
});
test(`returned value not be greater than ${expected}`, () => {
expect(a + b).not.toBeGreaterThan(expected);
});
});
Ref https://jestjs.io/docs/api#describeeachtablename-fn-timeout
SSR에서 react-query를 사용해주기 위해서, 서버에서 query를 prefetch하고 클라이언트에서 hydration해주는 방법을 선택했었다. query hydration을 사용하기 위해서는 react-query 공식 문서에서 QueryClient
를 리액트의 상태로 관리하여 데이터가 서로 다른 유저와 요청 사이에 공유되지 않도록 작성하는 방법을 권장하고 있었다.
// _app.jsx
import {
Hydrate,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
export default function MyApp({ Component, pageProps }) {
const [queryClient] = React.useState(() => new QueryClient())
return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
</QueryClientProvider>
)
그런데… useQuery를 호출하는 컴포넌트들에서 무한 API 호출이 발생하는 버그가 터졌다 😇
이유와 해결책을 살펴보자.
react-query의 queryClient는 일종의 옵저버 패턴으로, 내부에 query 데이터에 대한 캐시값이나 클라이언트 내의 정보를 담고 있다. API를 호출하게 되면 queryClient에 캐시 정보와 여러 정보를 업데이트 하게 된다. 그리고 client의 값이 변경될 때마다 컴포넌트는 새로 mount한다.
위 코드에서 queryClient는 useState
로 관리되기 때문에 상태를 변경할 수 없는 상태이므로, useQuery
로 호출한 상태의 queryClient를 불러오지 못한다. react-query 내부적으로 제대로 mount가 안되고, API를 호출했으나 응답이 오지 않으니 지속적으로 API를 호출하게 된다.
어느 순간 애플리케이션에서 SSR hydration을 사용하지 않게 되었는데, QueryClient
는 여전히 useState
로 관리되고 있어서 발생한 문제였다.
그래서 다음과 같이 queryClient를 React 컴포넌트 밖으로 빼버렸다.
const [queryClient] = new QueryClient({
defaultOptions: {
queries: {
suspense: true,
useErrorBoundary: true,
retry: false,
},
},
})
const App = ({ Component, pageProps }: AppProps) => {
return ...
Nest.js는 앱 전체에서 처리되지 않은 예외처리들을 담당하는 exceptions layer가 있다. 이 레이어는 처리되지 않은 애플리케이션 코드를 잡아 사용자 친화적인 응답으로 바꿔준다.
이 동작은 HttpException 타입의 예외를 처리하는 global exception filter로 수행된다. 알 수 없는 형태의 에러라면, 빌트인 exception filter는 500 Internal server error를 JSON 형식으로 반환한다.
다음처럼 기본으로 제공하는 HttpException
을 사용할 수도 있고,
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
HttpException
을 상속하여 커스텀 Exception을 사용할 수도 있다.
export class ForbiddenException extends HttpException {
constructor() {
super("Forbidden", HttpStatus.FORBIDDEN);
}
}
HttpException
뿐만 아니라 exception layer 전반을 제어하기 위해 Exception filter를 사용할 수도 있다. Exception filter를 이용하면 에러 발생 시 로깅 등의 추가적인 작업을 할 수 있다.
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
컨트롤러에서 사용할 땐 이렇게
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
또는 앱 전역에서 globalFilter로 사용할 수 있다
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
Ref https://docs.nestjs.com/exception-filters
Nest는 앱 부트스트랩 및 여러 다른 예외 처리의 상황에서 문자열 기반의 로거를 제공한다. 이 기능은 nest에 내장된 Logger
class를 사용한다.
cf) 더 향상된 로깅을 위해 Node.js의 로깅 패키지인 Winston을 사용할 수도 있다.
nest에서 제공하는 LoggerService
를 상속하여 커스텀 로거를 만들 수도 있다.
import { LoggerService } from '@nestjs/common';
export class MyLogger implements LoggerService {
log(message: any, ...optionalParams: any[]) {}
error(message: any, ...optionalParams: any[]) {}
warn(message: any, ...optionalParams: any[]) {}
debug?(message: any, ...optionalParams: any[]) {}
verbose?(message: any, ...optionalParams: any[]) {}
}
Ref https://docs.nestjs.com/techniques/logger
Vanilla Extract는 타입스크립트를 지원하는 zero-runtime 스타일시트로, 타입스크립트를 전처리기로 사용하여 빌드 타임에 타입 안정적인 정적 CSS 파일을 생성한다.
파일 확장자명이 .css.ts
로 끝나는 건 신기하다.
import { style } from "@vanilla-extract/css";
export const className = style({
display: "flex",
flexDirection: "column",
selectors: {
"&:nth-child(2n)": {
background: "aliceblue",
},
},
"@media": {
"screen and (min-width: 768px)": {
flexDirection: "row",
},
},
});
Ref https://vanilla-extract.style/
Fetch API를 사용하지 못하는 환경이 있다.
만약 sdk나 라이브러리를 만들 때 Fetch API가 필요하다면, fetch polyfill을 굳이 구현할 필요는 없다. 사용하는 쪽에서 fetch를 사용하지 않을 수도 있기 때문에, 필요 시 라이브러리를 사용하는 쪽에서 구현하는 것이 더 낫다.
또한 만약 라이브러리에서 특정 polyfill(node-fetch)들을 디펜던시로 가지고있다면 node가 아닌 deno, bun 등의 다른 런타임에서 문제를 일으킬 수 있다.
DPR, Device Pixel Ratio 기기 픽셀 비율
Alpha Compositing 알파 채널 기법
Raster graphics 래스터 이미지
Art direction
npm run build
npm pack
npm i {tgz파일경로}
MouseEvent.shiftKey
영어가 약 60%, 한국어가 1% 남짓으로 나름 많다.
천재만재 인도 개발자들이 많을 줄 알았는데, Hindi는 0.1%밖에 안 된다. (근데 인도어가 ‘Indian’이 아니고 ‘Hindi’라는 것도 처음 알았다.) 유튜브만 많이 하시나보다.
Ref https://w3techs.com/technologies/overview/content_language
이번주도 신규 프로젝트 코드리뷰 열심히 하고 😎 주말에 알차게 놀고 뻗었다
사실 안 뻗었다 절거운 인생 또 힘찬 월요일~!