October 8, 2022 • ☕️☕️ 10 min read
아무래도 술을 좋아해
리액트에서 배열이나 객체를 업데이트 해야 할 때는 직접 수정을 하면 안되고 불변성을 지켜주면서 업데이트를 해줘야 한다.
이때 Immer를 사용하면 React 컴포넌트의 깊은 상태(nested/deep state)를 쉽게 업데이트 할 수 있다.
as-is
const nextState = {
...state,
posts: state.posts.map((post) =>
post.id === 1
? {
...post,
comments: post.comments.concat({
id: 3,
text: "새로운 댓글",
}),
}
: post
),
};
to-be
import produce from "immer";
const nextState = produce(state, (draft) => {
const post = draft.posts.find((post) => post.id === 1);
post.comments.push({
id: 3,
text: "와 정말 쉽다!",
});
});
immer는 어떤 원리를 이용했길래 불변성을 유지해 주는 것일까?
immer의 핵심 원리는 Copy-on-write
(이하 기록 중 복사)와 Proxy
(이하 프록시)에 있다. 기록 중 복사란 자원을 공유하다가도 수정해야 할 경우가 발생하면 자원의 복사본을 쓰게 하는 개념이다. immer는 프록시 객체를 이용해서 원본 객체인 상태 객체 대신 프록시 객체를 대신 조작(변경)하는 것이다.
immer를 이용하면 상태 객체에서 실제로 변경할 부분만 골라서 변경이 되고, 다른 부분은 기존 상태 객체와 동일한 것을 확인할 수 있다.
lodash/cloneDeep
vs immer
Ref https://immerjs.github.io/immer/
기존의 nest는 decorator 및 annotation을 사용하는데, 이 방법을 사용하기 싫다면 직접 클래스를 만들어 멤버변수로 넣어서 사용할 수 있다. 그런데 이렇게 하면 멤버 변수를 모킹하기가 어려워지기 때문에 테스트가 어려워진다.
nest의 주된 철학인 Provider를 Module 단위로 만들어서 재사용 가능하고, DI와 헐리웃 원칙을 지켜서 모킹할 수 있도록 해야한다. 이러한 모듈로, 모노레포 전체에서 활용하는 Mocking Module이나, Health Check Module이나, 환경변수를 주입하는 모듈 등을 만들 수 있다.
이는 @nestjs/config
을 이용하면 더 쉽게 구성할 수 있으며, 여러 레포지토리에서 공통적으로 사용하는 configuration을 구성할 수 있다.
nest에서 Serialization된 데이터의 validating을 위해
class-transformer
를 사용할 수 있다. 깃헙 스타 수도 많으며, nest에서 공식으로 사용하는 방법이기도 하다.
const data = { foo: 42, bar: 1337 };
보다 const data = JSON.parse('{"foo":42,"bar":1337}');
이 최대 70%까지 빠르다고 한다. 😯
JSON 문자열은 한번만 평가되기 때문에, JSON.parse
가 자바스크립트 객체 리터럴보다 더 빠르다고 한다. 벤치마크 측정 결과도 존재한다. 용량이 클수록 더욱 효과가 좋으며, 10kb 이상의 객체에 사용하면 좋다.
Ref https://v8.dev/blog/cost-of-javascript-2019
Speedy Web Compiler의 약자로, next.js의 빌드 툴이다.
SWC는 Rust로 제작되었는데, 그래서 자바스크립트와 달리 병렬 처리가 가능하다. 즉 의존성이 없는 파일들을 동시에 변환할 수 있어서 빌드 속도가 빠르다. (하지만 싱글 스레드 환경에서도 SWC가 babel보다 빠르다)
babel(트랜스파일링)과 Terser(코드 경량화)를 SWC 하나로 대체할 수 있어서, SWC 기반 컴파일러를 이용하면 빌드 타임이 최대 5배 향상된다고 한다.
Ref https://fe-developers.kakaoent.com/2022/220217-learn-babel-terser-swc/
리소스를 중심으로 API 디자인 구성 즉, 웹 API가 표시하는 비즈니스 엔터티에 집중해야 한다. 리소스 URI는 동사(리소스에 대한 작업)가 아닌 명사(리소스)를 기반으로 해야 한다.
https://adventure-works.com/orders // Good
https://adventure-works.com/create-order // Avoid
REST의 목적은 엔터티 및 해당 엔터티에서 애플리케이션이 수행할 수 있는 작업을 모델링하는 것이다. 클라이언트는 내부 구현에 노출되면 안 된다.
URI에 일관적인 명명 규칙을 적용하는데, 일반적으로 컬렉션을 참조하는 URI에 대해 복수 명사를 사용한다. 컬렉션 및 항목에 대한 URI는 계층 구조로 구성하는 것이 좋다.
https://adventure-works.com/cusomters // 고객 컬렉션의 경로
https://adventure-works.com/cusomters/5 // ID가 5인 고객의 경로
이때 리소스 URI를 컬렉션/항목/컬렉션보다 더 복잡하게 요구하지 않는 것이 좋다.
Web API를 데이터베이스의 추상화라고 생각하자. 그러면 클라이언트 애플리케이션이 기본 데이터베이스 스키마의 변경 내용으로부터 격리된다.
HTTP 메서드 측면에서 API 작업 정의 HTTP 프로토콜은 요청에 의미가 드러나는 다양한 메서드를 정의한다.
리소스 | POST | GET | PUT | DELETE |
---|---|---|---|---|
/customers | 새 고객 만들기 | 모든 고객 검색 | 고객 대량 업데이트 | 모든 고객 제거 |
/customers/1 | Error | 고객 1에 대한 세부 정보 검색 | 고객 1이 있는 경우 고객 1의 세부 정보 업데이트 | 고객 1 제거 |
/customers/1/orders | 고객 1에 대한 새 주문 만들기 | 고객 1에 대한 모든 주문 검색 | 고객 1의 주문 대량 업데이트 | 고객 1의 모든 주문 제거 |
HTTP 의미 체계 준수
Content-Type
헤더로 표현 형식을 지정한다.데이터 필터링 및 페이지 매기기 단일 요청에서 반환하는 데이터의 양이 제한되도록 Web API를 디자인할 수 있다.
/orders?limit=25&offset=50
대용량 이진 리소스에 대한 부분 응답 지원
리소스에 파일 또는 이미지 같은 대용량 이진 필드가 포함된 경우, 이러한 리소스를 청크로 검색할 수 있게 하는 방안이 있다. Accept-Ranges
헤더를 사용하여, GET 작업이 부분 요청을 지원한다는 것을 표시한다.
응답 메시지는 HTTP 상태 코드 206을 반환하여 부분 응답임을 나타낸다.
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 2500
Content-Range: bytes 0-2499/4580
[...]
HATEOAS를 사용하여 관련 리소스 탐색 REST를 실행하는 기본적인 동기 중 하나는 URI 체계에 대해 미리 알고 있지 않아도 전체 리소스 집합을 탐색할 수 있어야 한다. 각 HTTP GET 요청은 응답에 포함된 하이퍼링크를 통해 요청된 개체와 직접 관련된 리소스를 찾는 데 필요한 정보를 반환해야 하며, 이러한 각 리소스에 대해 사용할 수 있는 작업을 설명하는 정보도 제공되어야 한다. 이 원칙을 HATEOAS(Hypertext as the Engine of Application State)라고 한다.
RESTful 웹 API 버전 관리
버전 관리 없음
URI 버전 관리
https://adventure-works.com/v2/customers/3
쿼리 문자열 버전 관리
https://adventure-works.com/customers/3?version=2
헤더 버전 관리
GET https://adventure-works.com/customers/3 HTTP/1.1
Custom-Header: api-version=1
미디어 형식 버전관리
Accept
헤더를 사용하여 클라이언트 애플리케이션이 예상하는 리소스의 버전을 나타낼 수 있도록 하는 정보를 포함한 사용자 지정 미디어 형식을 정의할 수 있다.
GET https://adventure-works.com/customers/3 HTTP/1.1
Accept: application/vnd.adventure-works.v1+json
const obj = {
one: true,
two: false,
three: true,
};
Object.keys(obj).forEach((key) => {
obj[key] = false;
});
// 👇️ {one: false, two: false, three: false}
console.log(obj);
const obj = {
one: true,
two: false,
three: true,
};
const newObj = Object.keys(obj).reduce((accumulator, key) => {
return { ...accumulator, [key]: false };
}, {});
// 👇️ {one: false, two: false, three: false}
console.log(newObj);
Ref https://bobbyhadz.com/blog/javascript-set-all-object-properties-to-false
box-shadow
를 한 방향만 살리고 싶다면, clip-path
를 이용할 수 있다. (Ref)React의 단방향 데이터 바인딩, 그리고 ‘선언적 컴포넌트’ 작성 방식은, 데이터 동기화와 성능 문제에 있어서 혁신적으로 문제를 해결하는 듯 보였다…
그러나 저자는 다음같은 이유들로 React에게 상처입은 듯하다.
useContext
를 사용한다면 불필요한 리렌더링을 피하기 위해 컨텍스트를 수도 없이 분리해야 한다.ref
를 사용할 때, ref
를 사용하는 하위 컴포넌트까지 모두 전달해야 한다. 이때 forwardRef
를 사용하는데, forwardRef
는 타입스크립트 제네릭을 붙인 컴포넌트에서 사용할 수 없다.useEffect
는, 의존성 배열에 수많은 값을 가지게 되었다. 개발자는 직접 의존성 지옥을 관리해야 한다. useEffect
의 구멍을 메우기 위해 다른 hook들이 등장했지만, 이는 복잡성을 더 키울 뿐이다.환승연애보다 더한 절절한 사랑 이야기…
그리고 이에 대한 Dan abramov의 반격(?)도 있으니 궁금하다면 읽어보자.
Ref
사람들이 ‘클린’코드를 위해 노력하지만, ‘클린’ 이란건 유용한 척도가 아니다. 코드는 여러 가지 이유로 훌륭할 수 있지만, 이러한 이유들은 어떤 면에서는 서로 상충된다. ex) 읽기 쉽다, 이해하기 쉽다, 간단하다, 성능이 좋다, 안전하다…
코드가 훌륭하다면, 우린 ‘왜 그런지’를 이야기해야 한다. 그냥 ‘클린하다’는 표현 대신, ‘디커플링되어 있고, 이해하기 쉽고, 테스트하기 좋고…‘. 정확한 용어를 사용하여, 팀 전체가 공통적인 이해를 가지게 해야 한다.
아직도 v1도 아니었다니..~~ 그러네.
Ref https://github.com/axios/axios/releases/tag/v1.0.0
어쩌다보니 이번주는 거의 맨날 술이네 🤷♀️ 그것도 금주기간이 끝나니 막 퍼붓고… 그래… 회사 분들과 너무너무 즐거운 술자리였다. 재밌게 마시니 컨디션도 괜찮은 것 같다.
석촌호수 러버덕은 정말정말 크고 귀엽고 움직인다! “움직인다!” ㅋㅋㅋ 바람 불면 조금씩 움직인다. 사진 찍을 때 나무에 가리지 않도록 주의해야 한다.
환승연애2에 푹 빠져버렸다… 해은헝거 못잃어… 규민 잃어… 근데 나언진솔도 못잃어…
fe conf는 완전 좋았다! 세션도 재밌었고, 많은 사람들도 만나고, 여러 회사들에서 굿즈도 받았다. 근데 토스에서 또 당첨돼서 에어팟 맥스를 받았다! 미쳤다… 🎧
fe conf가 끝난 뒤에는 부랴부랴 달려가서 수많은 인파 속에서 불꽃축제도 보고, 또 술 마시고, 다음 날도…
굉장한 연휴를 보내버렸다. 🤩