반응형

https://100dayscss.com/

 

css 고수가 되고 싶어서 자료를 찾던중

유용한 사이트를 하나 발견해서 공유해본다~

 

https://100dayscss.com/

 

1일부터 100일까지 챌린지가 준비되어 있는데

아래와 같은 애니메이션을

 

See the Pen Untitled by lodado (@lodado) on CodePen.

직접 만들어보면 된다

 

진짜 어려운데.. 다른사람의 코드를 참고할 수 있으니

많이 배울 수 있는것 같다

 

내 아이디

>> https://100dayscss.com/progress/lodado/

반응형

'Front-end > CSS' 카테고리의 다른 글

font-family 상속 시키기  (0) 2022.11.04
[CSS] box-sizing  (0) 2022.07.11
[CSS] Gap  (0) 2022.06.13
loading spinner  (2) 2022.02.13
CSS 애니메이션 성능 개선  (0) 2022.01.27
반응형

이전에 유저의 명령을 undo, redo 하기 위해서 유저가 실행한 커맨드 하나하나를 기록하여 

스택에 쌓는 작업을 했었다.

 

그런데 다양한 인터렉션을 처리하려고 하다보니 여러 원자적인 연산(create, delete, update)들을 조합해 하나의 커맨드로 기록할 필요성이 생기게 되었다.

 

예를 들어 다음 예시 화면과 같이

부모 노드안에 자식 노드를 추가하려면 한번 추가할때마다 각각 두번의 연산이 필요하다. 

 

1. update 연산 -> 부모 노드(시작 스탭)의 width, height가 커져야 한다. 

2. add 연산 ->  자식 노드를 nodes 배열에 추가해야한다.

 

(undo, redo할땐 두 연산을 하나로 합쳐서 작동해야한다.)

 

이것을 간단한 코드 예시로 보자면 다음과 같다.

	const [nodes, setNodes] = useRecoilState(flowNodesState); // 배열 형태
	const [edges, setEdges] = useRecoilState(flowEdgesState); 
	

	const addChildNode = () => {
		....

		// this should be updateNodes
		setNodes((nodes: Node[]) => 부모 노드 크기 변경)

		// this should be addNodes
		setNodes((nodes: Node[]) => 새로운 자식 추가)
	}

 

setState 함수에서 인자를 이용하면 비록 setState가 비동기라도

기존 state가 아닌 새로 갱신된 state를 인자로 받아 마치 차례대로 실행하는것처럼 사용할 수 있다.

 

식으로 비유하자면

A : 처음 node State

F(x) : update 연산

G(x) : add 연산

 

새로운 상태 : G(F(A))

 

 

문제점 

 

그래서 setState를 히스토리를 기록하기 위해서 command 함수들로 바꾸면

내부적으로는 setState를 커맨드 하나마다 각각 실행하니 기존과 동일하게 실행되지 않을까? 하는 가설을 세웠지만 실제로 실행해보니 기대처럼 작동하지는 않았다.

 

코드 예시

더보기

 

	const [nodes, setNodes] = useRecoilState(flowNodesState);
	const [edges, setEdges] = useRecoilState(flowEdgesState);
	

	const addChildNode = () => {
		....

		updateNodes(부모 노드 크기 변경) 
        
        	addNodes(새로운 자식 노드 추가)
        
	}

 

즉, 내가 원하던건

 

A -> B -> C -> D 연산처럼 A에서 시작해 상태를 순서대로 업데이트 하는것이였다면

 

command들 함수를 이용하면 각각 command 내부에 들어있는 setState들이 서로 연관이 없다고 리엑트가 판단하여 인자값으로 갱신되지 않은 기존의 state를 반환해 A -> B, A -> C,  A->D 처럼 서로 독립적으로 실행이 되어버리는 것이였다.

 

그래서 한가지 아이디어를 냈는데

 

우리가 원하는건 A -> B -> C -> D 연산중에서 최종적으로 히스토리에 필요한것은 

undo했을때 기존의 값으로 되돌릴 A 상태와

redo했을때 되돌릴 D 상태다. 

 

그럼 커맨드들이 node를 인자값으로 받아 node를 return값으로 반환하는 여러가지 함수들을 만들고,

그 함수들을 순서대로 실행하게 이으면 A->B->C->D 연산을 할 수 있지 않을까?

 

그리고 history stack에는 A와 D만 저장하는 식으로 구현하면 될것이다.

 

이를 위해서 함수형 패러다임을 끼워넣어 구현해보기로 해보았따.

 

각 add, remove, update 등 상태를 변경하는 atomic한 함수를 만들고, 

아래의 pipe 함수를 이용해 순서대로 실행하는 식으로 구조를 변경하고,

마지막 갱신된 state를 setState해주는 식으로 구현하면 될것이다 

const pipe = (...fns) => { 
  return (...args) => {
    return fns.reduce( // 입력 받은 fns의 순서를 뒤집는다
      (res, fn) => [fn.call(null, ...res)], // 순서가 뒤집어진 fns 를 오른쪽부터 실행
      args // 초기값으로 받은 파라미터
    )[0];
  }
};

p.s) 위 함수 동작원리에 대한건 https://velog.io/@nakta/FP-in-JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A1%9C-%EC%A0%91%ED%95%B4%EB%B3%B4%EB%8A%94-%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%95%A8%EC%88%98-%EC%BB%B4%ED%8F%AC%EC%A7%80%EC%85%98-%EC%BB%A4%EB%A7%81-s7k2z039vb

를 참고해주세요. 

 

 

이를 간단하게 요약한 코드 예시로 보면 다음과 같다.

const NodesPipeExecution =
		(...fns) => {
			const element: Node[] = nodes.slice();

			return (option?: OptionType) => {
				const options = { ...option, isAtomicCommand: false, doesHistoryBeRecorded: false };

				const { newElement } = pipe(fns)({newElement : element, option : options}); // 함수를 순서대로 실행

				triggerUpdate(newElement) // 실제 history를 기록하고 setState를 반영하는 함수
			};
		};

 

실제 사용 예시

 

NodesPipeExecution(
		updateNodesPipe(부모 노드 크기 업데이트), 
		addNodesPipe(새로운 노드),
)()

 

그런데 이와 같이 함수들을 직접 넘기는 식으로 구현하면 

모든 함수와 파라미터를 알아야 되니 불편하지 않을까? 라는 피드백을 받아서 

 

정해진 인터페이스에 알맞게 넘기면 내부적으로 처리하는 식으로 다시 리펙토링 했다.

 

export type QueryType<T> = ((element: T) => boolean) | { [key: string]: string }[];

export interface AddExecutionPipeType<T> {
	type: "add";
	item: (() => T[]) | T[];
}

export interface RemoveExecutionPipeType<T> {
	type: "remove";
	query: QueryType<T>;
}

export interface UpdateExecutionPipeType<T> {
	type: "update";
	query: QueryType<T>;
	onChange: (element: T) => T;
}

export type ExecutionPipeType<T> = AddExecutionPipeType<T> | RemoveExecutionPipeType<T> | UpdateExecutionPipeType<T>;


const onNodesChange = (functionArray: ExecutionPipeType<Node>[]) => {
		NodesPipeExecution(
			...functionArray.map((func: ExecutionPipeType<Node>) => {
				const { type, item, query, onChange } = func as AddExecutionPipeType<Node> &
					RemoveExecutionPipeType<Node> &
					UpdateExecutionPipeType<Node>;
				switch (type) {
					case "add":
						return addNodesPipe(item);
					case "remove":
						return removeNodesPipe(query);
					case "update":
						return updateNodesPipe(query, onChange);
					default:
						return new Error("developer type error");
				}
			})
		)();
	};

 

실제 사용 예시



onNodesChange([
			{
				type: "update",
				query: 부모 노드 선택,
				onChange: (element: Node) => {
					부모 노드 크기 변경
				},
			},
			{
				type: "add",
				item: 새로운 자식 노드 생성,
			},
		]);

 

함수형 패러다임을 맛만 본거긴 하지만 실제로 해보니 재밌었다.

 

 

reference

 

https://velog.io/@nakta/FP-in-JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A1%9C-%EC%A0%91%ED%95%B4%EB%B3%B4%EB%8A%94-%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%95%A8%EC%88%98-%EC%BB%B4%ED%8F%AC%EC%A7%80%EC%85%98-%EC%BB%A4%EB%A7%81-s7k2z039vb

 

FP in JS (자바스크립트로 접해보는 함수형 프로그래밍) - 함수 컴포지션, 커링

두 번째 글입니다. 함수형 프로그래밍에서는 함수의 조합으로 원하는 값을 만들어 냅니다. 함수의 조합인 함수 컴포지션에 대해서 살펴보도록 하겠습니다. 그리고 커링 기법을 이용해 함수 컴

velog.io

 

반응형

'Front-end > React' 카테고리의 다른 글

react Dialog tab trap 만들기  (1) 2023.05.01
프론트엔드 테스트 정리 요약  (1) 2022.11.30
react-flow에 undo, redo 구현  (0) 2022.09.03
Suspense를 위한 wrapper 만들기  (0) 2022.07.28
react 17(개인 공부용)  (1) 2022.06.09
반응형




일하면서 react-flow(https://github.com/wbkd/react-flow)를 써보던 중

Node와 edge를 기반으로 한 react 시각화 라이브러리



해당 오픈소스 라이브러리에는 유저의 실행 명령을 기록해서 undo, redo하는 기능이 없다는것을 알게 되었다.
그래서 기획 상 필요한 undo, Redo 기능을 어떻게 넣을까 고심하게 되었는데...

배경지식 설명


해당 라이브러리는 node라는 배열과 Edge라는 배열을 가지고

	const [nodes, setNodes] = useRecoilState(flowNodesState); // 배열 형태
	const [edges, setEdges] = useRecoilState(flowEdgesState); 
	

	const addChildNode = () => {
		setNodes((nodes: Node[]) => 새로운 자식 추가)
	}


새로운 UI를 그려줄때마다 리엑트의 setState를 부르는 식으로 구현이 되어있다.

아이디어


setState를 실행할때마다 한 프레임이라고 정의한다면
undo를 하면 이전 프레임으로 상태를 복귀시키고,
Redo하면 다시 undo 했던 명령을 취소해 다시 기존 프레임으로 돌아와야 할것이다.

각 프레임이 변경될때마다 전체 state(node, edge)를 프레임 별로 저장해놓는 것은 메모리 용량 이슈가 발생하는것 같아서
유저가 실행한 커맨드를 순서대로 기억해두고(stack) undo시 해당 실행 커맨드를 역산하고, redo시 다시 실행하자 라는 가설을 세우고 코드를 작성해보았다.

즉, command 단위로 명령어를 저장했다.

모든 명령어는 event라는 단위로 추상화가 가능하다.

이를 위해 유저의 명령을 event라는 하나의 단위로 추상화하고, 이벤트가 실행할때마다 커맨드를 기록하는 식으로 구현하했다.

예를 들어 create, create, create라는 명령이 3번 온다면

우선 undo stack에 [create, create, create] 라는 형식으로 3번 쌓이게 된다.

그리고 undo 명령을 실행하면 undo stack에서 순서대로 명령어를 하나씩 꺼내어 그 명령의 역 커맨드를(역산) 실행하는데

undo시 (역산)
- create 명령어는 delete,
- delete 명령어는 create,
- update 명령어는 rollback

명령어를 실행하고 redo stack에 해당 명령어를 쌓게 된다.

redo 명령시 redo stack에서 명령어를 꺼내서 역으로 실행된 커맨드를 역 실행하기 위해서(부정의 부정은 긍정!)
즉.. 기존 커맨드를 그냥 재실행 하게되는데

redo 시 (재실행)
- create 명령어는 create,
- delete 명령어는 delete
- update 명령어는 update

하게 된다.

매우 간단히 요약된 코드 예시로 보자면

undoStack = [];
redoStack = [];


// 커맨드 기록 
function record(command){
    undoStack.push(command);
}


//undo 실행 
function undo(){
    redoStack.push(undoStack.pop());
}


//redo 실행
function redo(){
    undoStack.push(redoStack.pop());
}


위 두가지 스택이 맞물리는 형태가 된다.

undo, redo 예시


직접 구현하기 까다로웠던 문제였는데 잘 작동하는것 같다.

반응형
반응형

 

 

복잡한 비동기 로직을 사용할때 Suspense를 사용하면 데이터를 사용하는 컴포넌트와 로딩 & 에러 상태를 핸들링하는 방식을 분리시켜 선언적으로 프로그래밍이 가능하다. 

 

react-query나 recoil에서 suspense를 사용하는 방식을 지원해주는데  라이브러리의 지원이 없이 직접 처리하는 방식이 궁금해서 해보았다. 

 

우선 suspense에 대해 인터넷에 찾아보니 나온 코드는 다음과 같다.

 

function wrapPromise(promise: AxiosPromise) {
	let status = "pending";
	let result = {};
	const suspender = promise.then(
		(successResult) => {
			status = "success";
			result = successResult;
		},
		(errorResult) => {
			status = "error";
			result = errorResult;
		}
	);

	return function read() {
		switch (status) {
			case "pending":
				throw suspender;
			case "error":
				throw result;
			case "success":
			default:
				return result;
		}
	};
}

function fetchDataVersion1(requestAPI: Promise) {
	return wrapPromise(requestAPI);
}

const Main = () => {
	return (
			<Suspense fallback={<div>loading...</div>}>
				<Dddd />
			</Suspense>
	);
};

let count = 0;

setInterval(() => {
	count += 1;
}, 1000);

const axiosRequestExample = new Promise((res) => {
	setTimeout(() => {
		res(`${count} is shown`);
	}, 6000);
});

const requestResource = fetchDataVersion1(axiosRequestExample);

const Dddd = () => {
	const data = requestResource();

	return <div>{JSON.stringify(data)}</div>;
};

export default Main;

 

로직을 간단히 설명하자면, 

 

function wrapPromise(promise: AxiosPromise) {
	let status = "pending";
	let result = {};
	const suspender = promise.then(
		(successResult) => {
			status = "success";
			result = successResult;
		},
		(errorResult) => {
			status = "error";
			result = errorResult;
		}
	);

	return function read() {
		switch (status) {
			case "pending":
				throw suspender;
			case "error":
				throw result;
			case "success":
			default:
				return result;
		}
	};
}

비동기 데이터를 처리중인 상태에는 상위 컴포넌트로 promise를 넘겨서 Suspense가 처리하게 만들고,
(동일하게 error도 상위 컴포넌트로 넘겨서 ErrorBoundary(직접 구현해야함)가 처리하게 함)

데이터가 도착하면 렌더링 하는식으로 

 

Suspense를 이용하면 데이터를 불러오는 event(what)과 어떻게 처리하는 로직(how)을 분리시켜 선언적으로 처리하기 쉽게 만들어주는 강력한 도구가 될 수 있다.

 

그런데 위 코드는 실제로 사용하려고 하니 다음과 같은 기능이 부족했다.

 

1. 캐싱처리로 동일한 데이터는 캐싱처리해서 불러오는 시간을 줄임

2. 필요시 캐싱 처리를 무효화하고 새로운 데이터를 받아올 수 있어야함.

3. 여러 컴포넌트에서 재사용할 수 있어야함. 

 

그래서 다음의 블로그를 참고해서 위 조건을 만족하게 새로 만들어보았다.

 

https://coffeeandcakeandnewjeong.tistory.com/56

 

최종 구현형태는 다음과 같다.

 

let count = 1;

const TEST_URL = () => {
	console.log(`https://jsonplaceholder.typicode.com/todos/${count}`);

	return `https://jsonplaceholder.typicode.com/todos/${count}`;
};

setInterval(() => {
	count += 1;
}, 1000);

const Main = () => {
	return (
		<ErrorBoundary fallback={<div>error</div>}>
			<Suspense fallback={<div>213</div>}>
				<Dddd />
			</Suspense>
		</ErrorBoundary>
	);
};

const Dddd = () => {
	const { data, refresh } = useSuspense("useSuspense", () => axios(TEST_URL()), [123, 456, 789, 1011]);

	return (
		<div>
			<button type="button" onClick={() => refresh()}>
				refresh
			</button>
			<Link to="/login">to login</Link>
			{JSON.stringify(data)}
		</div>
	);
};

 

그런데 실제 구현물을 보니 react-query와 비슷하게 만든거 같다;;

이렇게 되면 위 코드를 실제로 사용하기는 애매해진거 같은 기분이다.

 

top-down 방식으로 위 코드를 파해쳐보자. 

type depsType = number[] | string[];
type axiosType = () => Promise<any>;

function useForceUpdate(key: string) {
	const [value, setValue] = useState(0);
	return () => {
		asyncManager.delete(key);
		setValue(Date.now());
	};
}

const useSuspense = (key: string, getPromiseCandidate: axiosType, deps: depsType = []) => {
	const resetFunction = useForceUpdate(key);

	const refresh = () => {
		resetFunction();
	};

	useEffect(() => {
		return () => asyncManager.delete(key);
	}, []);

	const { nextValue, force } = depsMemorization(key, getPromiseCandidate, deps); //deps 비교
	const { status, suspender, result } = getPromiseData(key, nextValue, force); // Promise 처리 

	if (status === "pending") {
		throw suspender;
	}

	if (status === "error") {
		throw result;
	}

	return { data: result, refresh };
};

 

가장 먼저 useSuspense hook부터 살펴보자.

 

맨 처음 소개되었던 코드에 다음과 같은 기능이 추가됐다.

 

1. depsMemorization - 캐싱처리(key와 deps 비교를 통해)

2. getPromiseData - Promise 처리 및 key값에 따라 메모라이징 

3. refresh - 캐싱 무효화 & refetch 후 re-rendering

 

status에 따라 throw & return 하는 방식은 동일하다.

다만 asyncManager라는 새로운 객체를 추가했고 해당 객체로 캐싱처리 관련을 해줄 예정이고(crud 가능),

key가 동일하고 deps가 동일하다면 한 컴포넌트에서 fetch를 받아왔으면 캐싱처리해 여러 컴포넌트에서 동일하게 사용할 수 있도록 구현했다.

 

다음은 depsMemorization 코드이다.

 

const depsMemorization = (key: string, axiosRequest: axiosType, deps: depsType) => {
	if (!deps || !Array.isArray(deps) || deps.length === 0) return { nextValue: axiosRequest(), force: true };

	if (asyncManager.has(key)) {
		return asyncManager.update(axiosRequest, deps, key);
	}

	return asyncManager.mount(axiosRequest, deps, key);
};

사실 deps란 기능이 필요없다면 key의 유무만 판단해서 구현해줘도 된다.

그게 구현하기 더 심플한거 같기도 하다.

 

useEffect등의 deps 배열과 비슷한 역할을 하게 구현이 되어있다.

deps를 비교해 동일하다면 캐싱 값을 리턴하고 아니라면 key값을 기준으로 새로 캐싱해주는 형태로 구현이 되어있다.

 

다음은 Promise를 핸들링하는 코드인 getPromise이다.

 

const getPromiseDataClosure = () => {
	const promiseMem = new Map();

	return (key: string, axiosRequest: Promise<any>, force: boolean) => {
		if (promiseMem.has(key) && !force) {
			return promiseMem.get(key);
		}
		const promiseData = {
			promise: axiosRequest,
			status: "pending",
			result: null,
			suspender: axiosRequest.then(
				(response) => {
					promiseData.status = "success";
					promiseData.result = response;
				},
				(error) => {
					promiseData.status = "error";
					promiseData.result = error;
				}
			),
		};

		promiseMem.set(key, promiseData);
		return promiseData;
	};
};

const getPromiseData = getPromiseDataClosure();

기능은 심플한데 아까 맨 처음 나온 코드에 key값을 통한 메모리제이션 기능이 추가되었다.

 

1초마다 count가 올라가고, refresh 누를때마다 testurl/{count} 주소 로 request를 보내는 방식으로 구현해봤는데 실험 결과 잘 작동하는것 같다.

 

최종 코드는 다음 주소에서 볼 수 있다.

 

https://codesandbox.io/s/zealous-hugle-ozz7j0?file=/src/App.tsx

 

 

반응형

'Front-end > React' 카테고리의 다른 글

react-flow에 undo, redo 구현 (2) - 함수형 패러다임  (0) 2022.09.17
react-flow에 undo, redo 구현  (0) 2022.09.03
react 17(개인 공부용)  (1) 2022.06.09
React18(개인 공부 메모용)  (0) 2022.06.09
react TMI  (0) 2022.04.13
반응형

태그

 

Map tag

HTML CSS JavaScript MDN infographic

 

HTML <map> 요소 <area> 요소와 함께 이미지 맵(클릭 가능한 링크 영역)을 정의할 때 사용

 

https://developer.mozilla.org/ko/docs/Web/HTML/Element/map

 

속성 

role

 

<!-- role example -->
<li role="menuitem">Open file…</li>

Role 은 HTML 요소의 역할을 약속

기본 tag만으로 해당 요소의 역할을 정의하기 어려울때 사용

 

        <div role="button">Place Order</div>

해당 <div>의 작성자가 버튼에 기대되는 키보드 인터랙션을 제공하는 JavaScript도 구현했다는 약속

(반대로 아래에서 설명할 ARIA 역할(role)은 브라우저가 키보드 동작이나 스타일링을 제공하지 않습니다.)

 

 

area 계열

 

속성 (Property) 는 해당 요소의 특징이나 상황을 정의하며 aria- 라는 접두사를 사용

 

 

 

aria-label

<button aria-label="menu" class="button"></button>

aria-label 은 우리가 흔히 알고 있는 Label 목적을 위한 속성

특정 요소에 대한 설명을 그대로

 

 

... 공부하는대로 계속 업데이트중 

 

 

CSS들 

 

https://www.notion.so/css-851342264b4c4bde9f96a6f8f8e765bf

 

 

 

 

reference

 

https://bcp0109.tistory.com/348

 

웹 접근성과 WAI-ARIA

Overview HTML 페이지를 만들 때 고려해야 하는 것 중 하나가 웹 접근성입니다. 웹 접근성이란 시각장애인들이 웹 페이지를 원활하게 이용할 수 있도록 알려주는 가이드라인이라고 생각하면 됩니다

bcp0109.tistory.com

https://mulder21c.github.io/aria-practices/

 

WAI-ARIA 작성 방법 1.2

유저 인터페이스가 적절한 접근 가능한 이름을 제공하는데 사용될 수 있는 보이는 텍스트를 포함하는 경우, 접근 가능한 이름에 대한 보이는 텍스트를 사용하는 것이 유지보수를 간소화하고,

mulder21c.github.io

 

반응형
반응형

tabIndex

 

tabIndex 전역 특성은 요소가 포커스 가능함을 나타내며,

Tab키를 사용한 연속적인 키보드 탐색에서 어느 순서에 위치할지 지정해준다.

 

tabIndex는 3개의 상태를 가진다.

 

음의 정수값 : 연속 키보드 탐색(이하 tab)으로 접근할 수 없는 값

 

0 : tab을 사용해 접근할 수 있는값, 위에서 아래 순서대로 접근 가능해짐

 

양의 정수값 : tab을 사용해 접근 가능하며 숫자가 큰 순서대로 우선순위가 정해진다. 최대값은 32767이다.

0부터 시작해서 다음에 1이 있고, 그다음 2가 되고 이런 식이다.

 

대화형 컨텐츠(<a>, <button>, <details>, <input>, <select>, <textarea> 등)은 tabIndex의 기본값이 0이고,

비대화형 컨텐츠(div 등)은 기본값이 -1이라 지정 해주지 않으면 tab키로 접근하지 않는다.

 

예시)

 

tabIndex test

1
2
10
1

(tistory에서 다른 tab때문에 엉키게 보임)

 

코드) 

 

<h2>tabIndex test</h2>
<div>
   <div>1</div>
   <div tabindex="3">2</div>
   <div tabindex="1"> 10</div>
   <button tabindex="2" type="button">abc</button>
   <div>1</div>
</div>

 

tabIndex를 임의대로 사용하면 사용자 입장에서 의도치 않은 결과를 낼 수 있는것 같으니

가능하면 양수 값 사용은 자제하는게 좋을 것 같다.

 

modal 열릴때 다른 곳 tab focus 방지를 위해 tabIndex를 활용하면 좋다는데

이건 한번 써보고 이어 쓰기로.. 

 

reference 

 

https://developer.mozilla.org/ko/docs/Web/HTML/Global_attributes/tabindex

 

tabindex - HTML: Hypertext Markup Language | MDN

tabindex 전역 특성은 요소가 포커스 가능함을 나타내며, 이름에서도 알 수 있듯, 주로 Tab 키를 사용하는 연속적인 키보드 탐색에서 어느 순서에 위치할지 지정합니다.

developer.mozilla.org

 

https://mygumi.tistory.com/53

 

웹 접근성 tabindex 속성 :: 마이구미

이번 글은 tabindex 속성에 대한 웹 접근성에 대해 알아볼 것이다. 참고 자료 - http://nuli.navercorp.com/sharing/blog/post/1132726 tabindex 를 활용한 하나의 테크닉 - https://mygumi.tistory.com/372 tab..

mygumi.tistory.com

 

반응형
반응형

박스의 width와 height를 어떻게 구하는지 정하는 방식

 

box-sizing = content-box | border-box | initial | inherit

 

값 

content-box 기본값. width와 height가 콘텐츠 영역만 포함하고 안팎 여백 & 테두리를 포함하지 않는다
border-box width와 height가 안팎 여백 & 테두리를 포함한다. 
initial 초기 값으로 지정 
inherit 부모로부터 상속

 

예제

 

border-box

content-box

 

 

<!DOCTYPE html>
<html>
<head>
<style> 
div.container {
  width: 100%;
  border: 2px solid black;
}

.content-box {
  box-sizing: content-box;
  width: 50%;
  border: 5px solid red;
  float: left;
}

.border-box {
  box-sizing: border-box;
  width: 50%;
  border: 5px solid red;
  float: left;
}


</style>
</head>
<body>

<div class="container">
  <div class="border-box">border-box</div>
  <br>
  
  <div class="content-box">content-box</div>
  <div style="clear:both;"></div>
</div>

</body>
</html>

 

가로 세로 길이의 정확한 값을 원하면 border-box 설정을 해두자 

 

reference 

 

https://www.w3schools.com/cssref/css3_pr_box-sizing.asp

반응형

'Front-end > CSS' 카테고리의 다른 글

font-family 상속 시키기  (0) 2022.11.04
css 챌린지  (0) 2022.10.05
[CSS] Gap  (0) 2022.06.13
loading spinner  (2) 2022.02.13
CSS 애니메이션 성능 개선  (0) 2022.01.27
반응형

Gap

'내부의 children 요소'들이 일정한 간격으로 떨어져 위치할 수 있게 사용하는 요소

grid 혹은 flex에서 사용 가능하다.

 

margin과의 차이점 

 

-> gap 스타일 속성은 인접한 요소가 있을때만 Gap을 만든다

 

See the Pen Untitled by lodado (@lodado) on CodePen.

 

호환성

 

출처 : https://caniuse.com/?search=GAP

메이저 브라우저는 전부 지원하는거 같은데 앞으로 호환성이 더 좋아지면 편리하게 쓸 수 있을것으로 기대된다.

 

ref

 

https://webisfree.com/2020-12-05/[css]-%EC%8A%A4%ED%83%80%EC%9D%BC%EC%86%8D%EC%84%B1-gap%EC%9D%84-flex%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0 

 

[css] 스타일속성 gap을 flex에서 사용하기

CSS 스타일 속성 gap에 대하여 알아보려고 합니다.

webisfree.com

반응형

'Front-end > CSS' 카테고리의 다른 글

font-family 상속 시키기  (0) 2022.11.04
css 챌린지  (0) 2022.10.05
[CSS] box-sizing  (0) 2022.07.11
loading spinner  (2) 2022.02.13
CSS 애니메이션 성능 개선  (0) 2022.01.27
반응형

react 17은 큰 변화는 없고 점진적인 upgrade를 위한 단계라고 한다.

 

 

이벤트 위임의 변경점

 

기존에는 document에 이벤트 위임을 붙여 이벤트를 처리했다면

17부터는 root에 이벤트를 붙여서 처리

 

https://ko.reactjs.org/blog/2020/08/10/react-v17-rc.html#changes-to-event-delegation

ex) 

<button onClick={handleClick}>

 

 

위 코드같은경우엔 내부적으로 

 

const rootNode = document.getElementById('root');
ReactDOM.render(<App />, rootNode);

 

root에 이벤트 위임을 넣어 처리

기존에는 (예시로) 리엑트 내부 e.stopPropagation() 코드가 혼용되서 쓰여지고 있는 JQuery 이벤트를 prevent하는 일이 있었는데

이런일을 방지하게 변경

또한 React17에서 event propagation이 보다 regular dom에 가깝게 변경

 

 

브라우저 최적화 (잡다한 변경점)

 

1. onScroll이 네이티브에선 버블링되지 않는데 React onScroll은 버블링되서 이벤트 버블링 제거 

 

2.

onFocus = focusin

onBlur = focusout 이벤트 사용하도록 변경?

(focusin focusout은 버블링 발생) 

 

3. Capture phase events (e.g. onClickCapture) 가 이제 진짜 캡처 페이즈 리스너 사용 (이전엔 어케했누?)

 

이벤트 풀링 최적화 제거 (No Event Pooling)

 

“event pooling” optimization이 별 의미없어서 삭제

비동기적으로 이벤트 객체에 접근 불가능했던게 삭제됨

 

function handleChange(e) {
  setData(data => ({
    ...data,
    // This crashes in React 16 and earlier:
    text: e.target.value <- before v17 : null
  }));
}

 

난 2021 입문자라(react 17 이후 사용) event pooling이 무슨 역할이었는지 정확히는 모르겠다

SyntheticEvent 객체는 이벤트 핸들러가 호출된 후 초기화되기에, 비동기적으로 이벤트 객체에 접근할 수 없었던 것이 수정되었다고 한다.

 

아마 setData 실행시기와 e의 생명주기가 일치하지 않았던것(초기화되버려서)으로 추론된다.

 

Effect Cleanup Timing

useEffect(() => {
  // This is the effect itself.
  return () => {    // This is its cleanup.  };});

 

useEffect가 보다 효율적으로 변경 

 

기본 컨셉 : 

 

Most effects don’t need to delay screen updates, so React runs them asynchronously soon after the update has been reflected on the screen.

 

(For the rare cases where you need an effect to block paint, e.g. to measure and position a tooltip, prefer useLayoutEffect.)

 

그러나 보통은 unmounting 될때 cleanup function은 보통 동기적으로 실행되게 사용되어 왔다.

(similar to componentWillUnmount being synchronous in classes)

이것은 탭 변경과 같은 큰 페이지 변경시 속도 저하를 초래했다. 

 

그래서 React 17에서는 항상 cleanup function은 비동기적으로 실행되게 변경되었다.

 

(In React 17, the effect cleanup function always runs asynchronously — for example, if the component is unmounting, the cleanup runs after the screen has been updated.)

 

동기적 상황을 원하면 useLayoutEffect를 쓰시라

 

(you might want to rely on the synchronous execution, you can switch to useLayoutEffect instead)

 

추가적으로 cleanup function은 언제나 new Effect가 실행하기 전에 실행된다. 

useEffect(() => {
  const instance = someRef.current;
  instance.someSetupMethod();
  return () => {
    instance.someCleanupMethod();
  };
});

 

Consistent Errors for Returning Undefined

let Button = forwardRef(() => {
  // We forgot to write return, so this component returns undefined.
  // React 17 surfaces this as an error instead of ignoring it.
  <button />;
});

let Button = memo(() => {
  // We forgot to write return, so this component returns undefined.
  // React 17 surfaces this as an error instead of ignoring it.
  <button />;
});

return undefined하면 에러

(제대로 return 안하면 에러) 

 

만약 아무것도 return 하지 않는 것을 명시적으로 나타내고 싶다면 return null 사용

 

ref

https://han7096.medium.com/react-v17-release-candidate-톺아보기-6a4b091965c4

 

React v17 Release Candidate 톺아보기

변경사항과 유의할점을 React 공식 문서와, 개인적인 지식을 덧붙여 정리한 글

han7096.medium.com

https://reactjs.org/blog/2020/08/10/react-v17-rc.html

 

React v17.0 Release Candidate: No New Features – React Blog

Today, we are publishing the first Release Candidate for React 17. It has been two and a half years since the previous major release of React, which is a long time even by our standards! In this blog post, we will describe the role of this major release, w

reactjs.org

 

반응형

'Front-end > React' 카테고리의 다른 글

react-flow에 undo, redo 구현  (0) 2022.09.03
Suspense를 위한 wrapper 만들기  (0) 2022.07.28
React18(개인 공부 메모용)  (0) 2022.06.09
react TMI  (0) 2022.04.13
drag and drop으로 창 크기 조절  (0) 2022.03.18
반응형

notion link

https://silver-blue-23c.notion.site/React18-0d46d803f3be437dbe7f90348dbecd09

 

React18

React 18의 알파 버전이 출시

* 바로 막 쓰기엔 무리일듯

자동 배치(Automatic Batching)가 도입되어 배치가 개선

 

배치 : React가 더 나은 성능을 위해 여러 개의 state 업데이트를 하나의 리렌더링 (re-render)로 묶는 것을 의미)

React 18의 createRoot를 통해, 모든 업데이트들은 어디서 왔는가와 무관하게 자동으로 배칭되게 된다.

 

이 뜻은, 추가적으로 timeout, promise, native 이벤트 핸들러와 모든 여타 이벤트는 React에서 제공하는 이벤트와 동일하게 state 업데이트를 배칭할 수 있다. 이를 통해 우리는 렌더링을 최소화하고, 나아가 애플리케이션에서 더 나은 성능을 기대한다.

상태 업데이트에 배치가 적용되지 않았으면 하는 경우엔 새롭게 추가된ReactDOM.flushSync함수를 사용

 

useTransition

React 17 까지는 상태 업데이트를 긴급 혹은 전환으로 명시하는 방법 X

 

 모든 상태 업데이트는 긴급 업데이트 → setTimeout이나 throttle, debounce 등의 테크닉을 써서 긴급 업데이트 방해를 우회하는 것이 최선이였다

 

useTransition을 쓰면

일부 상태 업데이트를 긴급하지 않은 것 (not urgent)로 표시

이것으로 표시되지 않은 상태 업데이트는 긴급한 것으로 간주

긴급한 상태 업데이트 (input text 등)가 긴급하지 않은 상태 업데이트 (검색 결과 목록 렌더링)을 중단할 수 있다.

 

쓰는 이유 : 느린 렌더링 & 느린 네트워크

업데이트를 긴급한 것과 긴급하지 않은 것으로 나누어 개발자에게 렌더링 성능을 튜닝하는데 많은 자유를 주었다고 볼 수 있다.

 

useId

 

useId는 클라이언트와 서버간의 hydration의 mismatch를 피하면서 유니크 아이디를 생성할 수 있는 새로운 훅이다. 이는 주로 고유한 id가 필요한 접근성 API와 사용되는 컴포넌트에 유용할 것으로 기대

 

useDeferredValue

 

useDeferredValue를 사용하면, 트리에서 급하지 않은 부분의 재렌더링을 지연

지연된 렌더링을 인터럽트하고 사용자 입력을 차단할때 사용

이는 debounce와 비슷하지만, 몇가지 더 장점이 있다.

지연된 렌더링은 인터럽트가 가능하며, 사용자 입력을 차단하지 않는다.

 

***** 아래는 Library author들에게 권장하는 hook들*****

(아직 어디에 쓰는진 잘..? library 제작자한테 추천한다고 한다 특히 useSyncExternalStore)

 

useSyncExternalStore

 

스토어에 대한 업데이트를 강제로 동기화 하여 외부 스토어가 concurrent read를 지원할 수 있도록 하는 새로운 훅

**useEffect**가 필요하지 않고, 이는 리액트 외부 상태와 통합되는 모든 라이브러리에 권장된다.

 

 

용어 설명 :

  • External Store: 외부 스토어라는 것은 우리가 subscribe하는 무언가를 의미한다. 예를 들어 리덕스 스토어, 글로벌 변수, dom 상태 등이 될 수 있다.
  • Internal Storeprops context useState useReducer 등 리액트가 관리하는 상태를 의미한다.
  • Tearing: 시각적인 비일치를 의미한다. 예를 들어, 하나의 상태에 대해 UI가 여러 상태로 보여지고 있는, (= 각 컴포넌트 별로 업데이트 속도가 달라서 발생하는) UI가 찢어진 상태를 의미한다.

**useExternalStore**는 리액트 18에서 스토어 내 데이터를 올바르게 가져올 수 있도록 도와준다.

 

useInsertionEffect

 

css-in-js 라이브러리가 렌더링 도중에 스타일을 삽입할 때 성능 문제를 해결할 수 있는 새로운 훅

 

The signature is identical to useEffect, but it fires synchronously before all DOM mutations.

 

리액트가 DOM을 변환한경우, 레이아웃에서 무언가를 읽기전 (**clientWidth**와 같이) 또는 페인트를 위해 브라우저에 값을 전달하기 전에 DOM에 대한 다른 변경과 동일한 타이밍에 작업을 하면 된다.

 

→ We must ensure to manipulate the CSS rules at the same time as other changes to the DOM. It can be when React mutates the DOM, before anything is read from the layout and before the content is visible in the browser.

useLayoutEffect 과 비슷,

hook is used to read the layout from the DOM and synchronously re-render.

 

순서 UseInsertionEffect → useLayoutEffect → useEffect

 

this hook does not have access to refs and cannot schedule updates.

 

선택적(Selective) Hydartion 등의 동시성(Concurrent) 기능이 추가

 

이 파트는 알아두면 좋을듯

 

 

새로운 서버 사이드 렌더링 아키텍처가 도입(<Suspense>와 React.lazy를 지원)

및 부분 SSR 지원

React 18에는 렌더링을 위한 3가지 API가 존재

  • renderToString: 유지 (제한된 Suspense 지원)
  • renderToNodeStream: Deprecated (Full Suspense를 지원하나, 스트리밍되지 않음)
  • pipeToNodeWritable: 신규 API, 사용 추천(스트리밍으로 Full Suspense) 지원

 

ref

 

https://medium.com/naver-place-dev/react-18을-준비하세요-8603c36ddb25

 

React 18을 준비하세요.

요약

medium.com

https://yceffort.kr/2022/04/react-18-changelog#starttransition-usetransition

 

Home

yceffort

yceffort.kr

 

반응형

'Front-end > React' 카테고리의 다른 글

Suspense를 위한 wrapper 만들기  (0) 2022.07.28
react 17(개인 공부용)  (1) 2022.06.09
react TMI  (0) 2022.04.13
drag and drop으로 창 크기 조절  (0) 2022.03.18
redux, redux-saga로 로딩, 에러 페이지 구현  (0) 2022.02.12

+ Recent posts